itest: refactor testPsbtChanFunding

This commit is contained in:
yyforyongyu 2022-08-11 12:49:11 +08:00
parent 309ba1fcf4
commit 8a04127659
No known key found for this signature in database
GPG key ID: 9BCD95C4FF296868
3 changed files with 58 additions and 84 deletions

View file

@ -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,

View file

@ -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

View file

@ -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,