mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-03-04 09:48:19 +01:00
itest: refactor testPsbtChanFunding
This commit is contained in:
parent
309ba1fcf4
commit
8a04127659
3 changed files with 58 additions and 84 deletions
|
@ -275,6 +275,10 @@ var allTestCasesTemp = []*lntemp.TestCase{
|
||||||
Name: "open channel reorg test",
|
Name: "open channel reorg test",
|
||||||
TestFunc: testOpenChannelAfterReorg,
|
TestFunc: testOpenChannelAfterReorg,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Name: "psbt channel funding",
|
||||||
|
TestFunc: testPsbtChanFunding,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
Name: "psbt channel funding external",
|
Name: "psbt channel funding external",
|
||||||
TestFunc: testPsbtChanFundingExternal,
|
TestFunc: testPsbtChanFundingExternal,
|
||||||
|
|
|
@ -3,7 +3,6 @@ package itest
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"crypto/rand"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -22,6 +21,7 @@ import (
|
||||||
"github.com/lightningnetwork/lnd/lnrpc/signrpc"
|
"github.com/lightningnetwork/lnd/lnrpc/signrpc"
|
||||||
"github.com/lightningnetwork/lnd/lnrpc/walletrpc"
|
"github.com/lightningnetwork/lnd/lnrpc/walletrpc"
|
||||||
"github.com/lightningnetwork/lnd/lntemp"
|
"github.com/lightningnetwork/lnd/lntemp"
|
||||||
|
"github.com/lightningnetwork/lnd/lntemp/node"
|
||||||
"github.com/lightningnetwork/lnd/lntest"
|
"github.com/lightningnetwork/lnd/lntest"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
@ -29,80 +29,64 @@ import (
|
||||||
// testPsbtChanFunding makes sure a channel can be opened between carol and dave
|
// testPsbtChanFunding makes sure a channel can be opened between carol and dave
|
||||||
// by using a Partially Signed Bitcoin Transaction that funds the channel
|
// by using a Partially Signed Bitcoin Transaction that funds the channel
|
||||||
// multisig funding output.
|
// multisig funding output.
|
||||||
func testPsbtChanFunding(net *lntest.NetworkHarness, t *harnessTest) {
|
func testPsbtChanFunding(ht *lntemp.HarnessTest) {
|
||||||
// First, we'll create two new nodes that we'll use to open channels
|
// First, we'll create two new nodes that we'll use to open channels
|
||||||
// between for this test. Dave gets some coins that will be used to
|
// between for this test. Dave gets some coins that will be used to
|
||||||
// fund the PSBT, just to make sure that Carol has an empty wallet.
|
// fund the PSBT, just to make sure that Carol has an empty wallet.
|
||||||
carol := net.NewNode(t.t, "carol", nil)
|
carol := ht.NewNode("carol", nil)
|
||||||
defer shutdownAndAssert(net, t, carol)
|
dave := ht.NewNode("dave", nil)
|
||||||
|
|
||||||
dave := net.NewNode(t.t, "dave", nil)
|
runPsbtChanFunding(ht, carol, dave)
|
||||||
defer shutdownAndAssert(net, t, dave)
|
|
||||||
|
|
||||||
runPsbtChanFunding(net, t, carol, dave)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// runPsbtChanFunding makes sure a channel can be opened between carol and dave
|
// runPsbtChanFunding makes sure a channel can be opened between carol and dave
|
||||||
// by using a Partially Signed Bitcoin Transaction that funds the channel
|
// by using a Partially Signed Bitcoin Transaction that funds the channel
|
||||||
// multisig funding output.
|
// multisig funding output.
|
||||||
func runPsbtChanFunding(net *lntest.NetworkHarness, t *harnessTest, carol,
|
func runPsbtChanFunding(ht *lntemp.HarnessTest, carol, dave *node.HarnessNode) {
|
||||||
dave *lntest.HarnessNode) {
|
|
||||||
|
|
||||||
ctxb := context.Background()
|
|
||||||
|
|
||||||
// Everything we do here should be done within a second or two, so we
|
|
||||||
// can just keep a single timeout context around for all calls.
|
|
||||||
ctxt, cancel := context.WithTimeout(ctxb, defaultTimeout)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
const chanSize = funding.MaxBtcFundingAmount
|
const chanSize = funding.MaxBtcFundingAmount
|
||||||
net.SendCoins(t.t, btcutil.SatoshiPerBitcoin, dave)
|
ht.FundCoins(btcutil.SatoshiPerBitcoin, dave)
|
||||||
|
|
||||||
// Before we start the test, we'll ensure both sides are connected so
|
// Before we start the test, we'll ensure both sides are connected so
|
||||||
// the funding flow can be properly executed.
|
// the funding flow can be properly executed.
|
||||||
net.EnsureConnected(t.t, carol, dave)
|
alice := ht.Alice
|
||||||
net.EnsureConnected(t.t, carol, net.Alice)
|
ht.EnsureConnected(carol, dave)
|
||||||
|
ht.EnsureConnected(carol, alice)
|
||||||
|
|
||||||
// At this point, we can begin our PSBT channel funding workflow. We'll
|
// At this point, we can begin our PSBT channel funding workflow. We'll
|
||||||
// start by generating a pending channel ID externally that will be used
|
// start by generating a pending channel ID externally that will be used
|
||||||
// to track this new funding type.
|
// to track this new funding type.
|
||||||
var pendingChanID [32]byte
|
pendingChanID := ht.Random32Bytes()
|
||||||
_, err := rand.Read(pendingChanID[:])
|
|
||||||
require.NoError(t.t, err)
|
|
||||||
|
|
||||||
// We'll also test batch funding of two channels so we need another ID.
|
// We'll also test batch funding of two channels so we need another ID.
|
||||||
var pendingChanID2 [32]byte
|
pendingChanID2 := ht.Random32Bytes()
|
||||||
_, err = rand.Read(pendingChanID2[:])
|
|
||||||
require.NoError(t.t, err)
|
|
||||||
|
|
||||||
// Now that we have the pending channel ID, Carol will open the channel
|
// Now that we have the pending channel ID, Carol will open the channel
|
||||||
// by specifying a PSBT shim. We use the NoPublish flag here to avoid
|
// by specifying a PSBT shim. We use the NoPublish flag here to avoid
|
||||||
// publishing the whole batch TX too early.
|
// publishing the whole batch TX too early.
|
||||||
chanUpdates, tempPsbt, err := openChannelPsbt(
|
chanUpdates, tempPsbt := ht.OpenChannelPsbt(
|
||||||
ctxt, carol, dave, lntest.OpenChannelParams{
|
carol, dave, lntemp.OpenChannelParams{
|
||||||
Amt: chanSize,
|
Amt: chanSize,
|
||||||
FundingShim: &lnrpc.FundingShim{
|
FundingShim: &lnrpc.FundingShim{
|
||||||
Shim: &lnrpc.FundingShim_PsbtShim{
|
Shim: &lnrpc.FundingShim_PsbtShim{
|
||||||
PsbtShim: &lnrpc.PsbtShim{
|
PsbtShim: &lnrpc.PsbtShim{
|
||||||
PendingChanId: pendingChanID[:],
|
PendingChanId: pendingChanID,
|
||||||
NoPublish: true,
|
NoPublish: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
require.NoError(t.t, err)
|
|
||||||
|
|
||||||
// Let's add a second channel to the batch. This time between Carol and
|
// Let's add a second channel to the batch. This time between Carol and
|
||||||
// Alice. We will publish the batch TX once this channel funding is
|
// Alice. We will publish the batch TX once this channel funding is
|
||||||
// complete.
|
// complete.
|
||||||
chanUpdates2, psbtBytes2, err := openChannelPsbt(
|
chanUpdates2, psbtBytes2 := ht.OpenChannelPsbt(
|
||||||
ctxt, carol, net.Alice, lntest.OpenChannelParams{
|
carol, alice, lntemp.OpenChannelParams{
|
||||||
Amt: chanSize,
|
Amt: chanSize,
|
||||||
FundingShim: &lnrpc.FundingShim{
|
FundingShim: &lnrpc.FundingShim{
|
||||||
Shim: &lnrpc.FundingShim_PsbtShim{
|
Shim: &lnrpc.FundingShim_PsbtShim{
|
||||||
PsbtShim: &lnrpc.PsbtShim{
|
PsbtShim: &lnrpc.PsbtShim{
|
||||||
PendingChanId: pendingChanID2[:],
|
PendingChanId: pendingChanID2,
|
||||||
NoPublish: false,
|
NoPublish: false,
|
||||||
BasePsbt: tempPsbt,
|
BasePsbt: tempPsbt,
|
||||||
},
|
},
|
||||||
|
@ -110,7 +94,6 @@ func runPsbtChanFunding(net *lntest.NetworkHarness, t *harnessTest, carol,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
require.NoError(t.t, err)
|
|
||||||
|
|
||||||
// We'll now ask Dave's wallet to fund the PSBT for us. This will return
|
// We'll now ask Dave's wallet to fund the PSBT for us. This will return
|
||||||
// a packet with inputs and outputs set but without any witness data.
|
// a packet with inputs and outputs set but without any witness data.
|
||||||
|
@ -123,55 +106,49 @@ func runPsbtChanFunding(net *lntest.NetworkHarness, t *harnessTest, carol,
|
||||||
SatPerVbyte: 2,
|
SatPerVbyte: 2,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
fundResp, err := dave.WalletKitClient.FundPsbt(ctxt, fundReq)
|
fundResp := dave.RPC.FundPsbt(fundReq)
|
||||||
require.NoError(t.t, err)
|
|
||||||
|
|
||||||
// We have a PSBT that has no witness data yet, which is exactly what we
|
// We have a PSBT that has no witness data yet, which is exactly what we
|
||||||
// need for the next step: Verify the PSBT with the funding intents.
|
// need for the next step: Verify the PSBT with the funding intents.
|
||||||
_, err = carol.FundingStateStep(ctxb, &lnrpc.FundingTransitionMsg{
|
carol.RPC.FundingStateStep(&lnrpc.FundingTransitionMsg{
|
||||||
Trigger: &lnrpc.FundingTransitionMsg_PsbtVerify{
|
Trigger: &lnrpc.FundingTransitionMsg_PsbtVerify{
|
||||||
PsbtVerify: &lnrpc.FundingPsbtVerify{
|
PsbtVerify: &lnrpc.FundingPsbtVerify{
|
||||||
PendingChanId: pendingChanID[:],
|
PendingChanId: pendingChanID,
|
||||||
FundedPsbt: fundResp.FundedPsbt,
|
FundedPsbt: fundResp.FundedPsbt,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
require.NoError(t.t, err)
|
carol.RPC.FundingStateStep(&lnrpc.FundingTransitionMsg{
|
||||||
_, err = carol.FundingStateStep(ctxb, &lnrpc.FundingTransitionMsg{
|
|
||||||
Trigger: &lnrpc.FundingTransitionMsg_PsbtVerify{
|
Trigger: &lnrpc.FundingTransitionMsg_PsbtVerify{
|
||||||
PsbtVerify: &lnrpc.FundingPsbtVerify{
|
PsbtVerify: &lnrpc.FundingPsbtVerify{
|
||||||
PendingChanId: pendingChanID2[:],
|
PendingChanId: pendingChanID2,
|
||||||
FundedPsbt: fundResp.FundedPsbt,
|
FundedPsbt: fundResp.FundedPsbt,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
require.NoError(t.t, err)
|
|
||||||
|
|
||||||
// Now we'll ask Dave's wallet to sign the PSBT so we can finish the
|
// Now we'll ask Dave's wallet to sign the PSBT so we can finish the
|
||||||
// funding flow.
|
// funding flow.
|
||||||
finalizeReq := &walletrpc.FinalizePsbtRequest{
|
finalizeReq := &walletrpc.FinalizePsbtRequest{
|
||||||
FundedPsbt: fundResp.FundedPsbt,
|
FundedPsbt: fundResp.FundedPsbt,
|
||||||
}
|
}
|
||||||
finalizeRes, err := dave.WalletKitClient.FinalizePsbt(ctxt, finalizeReq)
|
finalizeRes := dave.RPC.FinalizePsbt(finalizeReq)
|
||||||
require.NoError(t.t, err)
|
|
||||||
|
|
||||||
// We've signed our PSBT now, let's pass it to the intent again.
|
// We've signed our PSBT now, let's pass it to the intent again.
|
||||||
_, err = carol.FundingStateStep(ctxb, &lnrpc.FundingTransitionMsg{
|
carol.RPC.FundingStateStep(&lnrpc.FundingTransitionMsg{
|
||||||
Trigger: &lnrpc.FundingTransitionMsg_PsbtFinalize{
|
Trigger: &lnrpc.FundingTransitionMsg_PsbtFinalize{
|
||||||
PsbtFinalize: &lnrpc.FundingPsbtFinalize{
|
PsbtFinalize: &lnrpc.FundingPsbtFinalize{
|
||||||
PendingChanId: pendingChanID[:],
|
PendingChanId: pendingChanID,
|
||||||
SignedPsbt: finalizeRes.SignedPsbt,
|
SignedPsbt: finalizeRes.SignedPsbt,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
require.NoError(t.t, err)
|
|
||||||
|
|
||||||
// Consume the "channel pending" update. This waits until the funding
|
// Consume the "channel pending" update. This waits until the funding
|
||||||
// transaction was fully compiled.
|
// transaction was fully compiled.
|
||||||
updateResp, err := receiveChanUpdate(ctxt, chanUpdates)
|
updateResp := ht.ReceiveOpenChannelUpdate(chanUpdates)
|
||||||
require.NoError(t.t, err)
|
|
||||||
upd, ok := updateResp.Update.(*lnrpc.OpenStatusUpdate_ChanPending)
|
upd, ok := updateResp.Update.(*lnrpc.OpenStatusUpdate_ChanPending)
|
||||||
require.True(t.t, ok)
|
require.True(ht, ok)
|
||||||
chanPoint := &lnrpc.ChannelPoint{
|
chanPoint := &lnrpc.ChannelPoint{
|
||||||
FundingTxid: &lnrpc.ChannelPoint_FundingTxidBytes{
|
FundingTxid: &lnrpc.ChannelPoint_FundingTxidBytes{
|
||||||
FundingTxidBytes: upd.ChanPending.Txid,
|
FundingTxidBytes: upd.ChanPending.Txid,
|
||||||
|
@ -180,30 +157,25 @@ func runPsbtChanFunding(net *lntest.NetworkHarness, t *harnessTest, carol,
|
||||||
}
|
}
|
||||||
|
|
||||||
// No transaction should have been published yet.
|
// No transaction should have been published yet.
|
||||||
mempool, err := net.Miner.Client.GetRawMempool()
|
ht.Miner.AssertNumTxsInMempool(0)
|
||||||
require.NoError(t.t, err)
|
|
||||||
require.Equal(t.t, 0, len(mempool))
|
|
||||||
|
|
||||||
// Let's progress the second channel now. This time we'll use the raw
|
// Let's progress the second channel now. This time we'll use the raw
|
||||||
// wire format transaction directly.
|
// wire format transaction directly.
|
||||||
require.NoError(t.t, err)
|
carol.RPC.FundingStateStep(&lnrpc.FundingTransitionMsg{
|
||||||
_, err = carol.FundingStateStep(ctxb, &lnrpc.FundingTransitionMsg{
|
|
||||||
Trigger: &lnrpc.FundingTransitionMsg_PsbtFinalize{
|
Trigger: &lnrpc.FundingTransitionMsg_PsbtFinalize{
|
||||||
PsbtFinalize: &lnrpc.FundingPsbtFinalize{
|
PsbtFinalize: &lnrpc.FundingPsbtFinalize{
|
||||||
PendingChanId: pendingChanID2[:],
|
PendingChanId: pendingChanID2,
|
||||||
FinalRawTx: finalizeRes.RawFinalTx,
|
FinalRawTx: finalizeRes.RawFinalTx,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
require.NoError(t.t, err)
|
|
||||||
|
|
||||||
// Consume the "channel pending" update for the second channel. This
|
// Consume the "channel pending" update for the second channel. This
|
||||||
// waits until the funding transaction was fully compiled and in this
|
// waits until the funding transaction was fully compiled and in this
|
||||||
// case published.
|
// case published.
|
||||||
updateResp2, err := receiveChanUpdate(ctxt, chanUpdates2)
|
updateResp2 := ht.ReceiveOpenChannelUpdate(chanUpdates2)
|
||||||
require.NoError(t.t, err)
|
|
||||||
upd2, ok := updateResp2.Update.(*lnrpc.OpenStatusUpdate_ChanPending)
|
upd2, ok := updateResp2.Update.(*lnrpc.OpenStatusUpdate_ChanPending)
|
||||||
require.True(t.t, ok)
|
require.True(ht, ok)
|
||||||
chanPoint2 := &lnrpc.ChannelPoint{
|
chanPoint2 := &lnrpc.ChannelPoint{
|
||||||
FundingTxid: &lnrpc.ChannelPoint_FundingTxidBytes{
|
FundingTxid: &lnrpc.ChannelPoint_FundingTxidBytes{
|
||||||
FundingTxidBytes: upd2.ChanPending.Txid,
|
FundingTxidBytes: upd2.ChanPending.Txid,
|
||||||
|
@ -214,23 +186,19 @@ func runPsbtChanFunding(net *lntest.NetworkHarness, t *harnessTest, carol,
|
||||||
// Great, now we can mine a block to get the transaction confirmed, then
|
// Great, now we can mine a block to get the transaction confirmed, then
|
||||||
// wait for the new channel to be propagated through the network.
|
// wait for the new channel to be propagated through the network.
|
||||||
var finalTx wire.MsgTx
|
var finalTx wire.MsgTx
|
||||||
err = finalTx.Deserialize(bytes.NewReader(finalizeRes.RawFinalTx))
|
err := finalTx.Deserialize(bytes.NewReader(finalizeRes.RawFinalTx))
|
||||||
require.NoError(t.t, err)
|
require.NoError(ht, err)
|
||||||
|
|
||||||
txHash := finalTx.TxHash()
|
txHash := finalTx.TxHash()
|
||||||
block := mineBlocks(t, net, 6, 1)[0]
|
block := ht.MineBlocksAndAssertNumTxes(6, 1)[0]
|
||||||
assertTxInBlock(t, block, &txHash)
|
ht.Miner.AssertTxInBlock(block, &txHash)
|
||||||
err = carol.WaitForNetworkChannelOpen(chanPoint)
|
ht.AssertTopologyChannelOpen(carol, chanPoint)
|
||||||
require.NoError(t.t, err)
|
ht.AssertTopologyChannelOpen(carol, chanPoint2)
|
||||||
err = carol.WaitForNetworkChannelOpen(chanPoint2)
|
|
||||||
require.NoError(t.t, err)
|
|
||||||
|
|
||||||
// With the channel open, ensure that it is counted towards Carol's
|
// With the channel open, ensure that it is counted towards Carol's
|
||||||
// total channel balance.
|
// total channel balance.
|
||||||
balReq := &lnrpc.ChannelBalanceRequest{}
|
balRes := carol.RPC.ChannelBalance()
|
||||||
balRes, err := carol.ChannelBalance(ctxt, balReq)
|
require.NotZero(ht, balRes.LocalBalance.Sat)
|
||||||
require.NoError(t.t, err)
|
|
||||||
require.NotEqual(t.t, int64(0), balRes.LocalBalance.Sat)
|
|
||||||
|
|
||||||
// Next, to make sure the channel functions as normal, we'll make some
|
// Next, to make sure the channel functions as normal, we'll make some
|
||||||
// payments within the channel.
|
// payments within the channel.
|
||||||
|
@ -239,18 +207,24 @@ func runPsbtChanFunding(net *lntest.NetworkHarness, t *harnessTest, carol,
|
||||||
Memo: "new chans",
|
Memo: "new chans",
|
||||||
Value: int64(payAmt),
|
Value: int64(payAmt),
|
||||||
}
|
}
|
||||||
resp, err := dave.AddInvoice(ctxt, invoice)
|
resp := dave.RPC.AddInvoice(invoice)
|
||||||
require.NoError(t.t, err)
|
ht.CompletePaymentRequests(carol, []string{resp.PaymentRequest})
|
||||||
err = completePaymentRequests(
|
|
||||||
carol, carol.RouterClient, []string{resp.PaymentRequest}, true,
|
// TODO(yy): remove the sleep once the following bug is fixed. When the
|
||||||
)
|
// payment is reported as settled by Carol, it's expected the
|
||||||
require.NoError(t.t, err)
|
// commitment dance is finished and all subsequent states have been
|
||||||
|
// updated. Yet we'd receive the error `cannot co-op close channel with
|
||||||
|
// active htlcs` or `link failed to shutdown` if we close the channel.
|
||||||
|
// We need to investigate the order of settling the payments and
|
||||||
|
// updating commitments to understand and fix .
|
||||||
|
time.Sleep(2 * time.Second)
|
||||||
|
|
||||||
// To conclude, we'll close the newly created channel between Carol and
|
// To conclude, we'll close the newly created channel between Carol and
|
||||||
// Dave. This function will also block until the channel is closed and
|
// Dave. This function will also block until the channel is closed and
|
||||||
// will additionally assert the relevant channel closing post
|
// will additionally assert the relevant channel closing post
|
||||||
// conditions.
|
// conditions.
|
||||||
closeChannelAndAssert(t, net, carol, chanPoint, false)
|
ht.CloseChannel(carol, chanPoint)
|
||||||
|
ht.CloseChannel(carol, chanPoint2)
|
||||||
}
|
}
|
||||||
|
|
||||||
// testPsbtChanFundingExternal makes sure a channel can be opened between carol
|
// testPsbtChanFundingExternal makes sure a channel can be opened between carol
|
||||||
|
|
|
@ -12,10 +12,6 @@ var allTestCases = []*testCase{
|
||||||
name: "async bidirectional payments",
|
name: "async bidirectional payments",
|
||||||
test: testBidirectionalAsyncPayments,
|
test: testBidirectionalAsyncPayments,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: "psbt channel funding",
|
|
||||||
test: testPsbtChanFunding,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: "sign psbt",
|
name: "sign psbt",
|
||||||
test: testSignPsbt,
|
test: testSignPsbt,
|
||||||
|
|
Loading…
Add table
Reference in a new issue