From 8baa1a3d73bcaf51d626af4414bd96ae877a7060 Mon Sep 17 00:00:00 2001 From: yyforyongyu Date: Fri, 8 Nov 2024 15:41:14 +0800 Subject: [PATCH 01/32] itest: optimize blocks mined in `testGarbageCollectLinkNodes` There's no need to mine 80ish blocks here. --- itest/lnd_misc_test.go | 7 ------- 1 file changed, 7 deletions(-) diff --git a/itest/lnd_misc_test.go b/itest/lnd_misc_test.go index f983b39e91..30dba0a878 100644 --- a/itest/lnd_misc_test.go +++ b/itest/lnd_misc_test.go @@ -10,7 +10,6 @@ import ( "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcwallet/wallet" - "github.com/lightningnetwork/lnd/chainreg" "github.com/lightningnetwork/lnd/funding" "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/lncfg" @@ -506,12 +505,6 @@ func testGarbageCollectLinkNodes(ht *lntest.HarnessTest) { // close the channel instead. ht.ForceCloseChannel(alice, forceCloseChanPoint) - // We'll need to mine some blocks in order to mark the channel fully - // closed. - ht.MineBlocks( - chainreg.DefaultBitcoinTimeLockDelta - defaultCSV, - ) - // Before we test reconnection, we'll ensure that the channel has been // fully cleaned up for both Carol and Alice. ht.AssertNumPendingForceClose(alice, 0) From 8f4e260889dd6d50318e64a03d0d0c280ee575c5 Mon Sep 17 00:00:00 2001 From: yyforyongyu Date: Fri, 8 Nov 2024 18:56:18 +0800 Subject: [PATCH 02/32] itest: break remote signer into independent cases So the test can run faster. --- itest/list_on_test.go | 5 +- itest/lnd_remote_signer_test.go | 358 +++++++++++++++++++++----------- 2 files changed, 238 insertions(+), 125 deletions(-) diff --git a/itest/list_on_test.go b/itest/list_on_test.go index 3a5a10a9fa..da525a8630 100644 --- a/itest/list_on_test.go +++ b/itest/list_on_test.go @@ -508,10 +508,6 @@ var allTestCases = []*lntest.TestCase{ Name: "async payments benchmark", TestFunc: testAsyncPayments, }, - { - Name: "remote signer", - TestFunc: testRemoteSigner, - }, { Name: "taproot coop close", TestFunc: testTaprootCoopClose, @@ -695,4 +691,5 @@ func init() { allTestCases = append(allTestCases, multiHopForceCloseTestCases...) allTestCases = append(allTestCases, watchtowerTestCases...) allTestCases = append(allTestCases, psbtFundingTestCases...) + allTestCases = append(allTestCases, remoteSignerTestCases...) } diff --git a/itest/lnd_remote_signer_test.go b/itest/lnd_remote_signer_test.go index bd92ba37c8..6cbda365aa 100644 --- a/itest/lnd_remote_signer_test.go +++ b/itest/lnd_remote_signer_test.go @@ -16,6 +16,59 @@ import ( "github.com/stretchr/testify/require" ) +// remoteSignerTestCases defines a set of test cases to run against the remote +// signer. +var remoteSignerTestCases = []*lntest.TestCase{ + { + Name: "remote signer random seed", + TestFunc: testRemoteSignerRadomSeed, + }, + { + Name: "remote signer account import", + TestFunc: testRemoteSignerAccountImport, + }, + { + Name: "remote signer channel open", + TestFunc: testRemoteSignerChannelOpen, + }, + { + Name: "remote signer funding input types", + TestFunc: testRemoteSignerChannelFundingInputTypes, + }, + { + Name: "remote signer funding async payments", + TestFunc: testRemoteSignerAsyncPayments, + }, + { + Name: "remote signer funding async payments taproot", + TestFunc: testRemoteSignerAsyncPaymentsTaproot, + }, + { + Name: "remote signer shared key", + TestFunc: testRemoteSignerSharedKey, + }, + { + Name: "remote signer bump fee", + TestFunc: testRemoteSignerBumpFee, + }, + { + Name: "remote signer psbt", + TestFunc: testRemoteSignerPSBT, + }, + { + Name: "remote signer sign output raw", + TestFunc: testRemoteSignerSignOutputRaw, + }, + { + Name: "remote signer verify msg", + TestFunc: testRemoteSignerSignVerifyMsg, + }, + { + Name: "remote signer taproot", + TestFunc: testRemoteSignerTaproot, + }, +} + var ( rootKey = "tprv8ZgxMBicQKsPe6jS4vDm2n7s42Q6MpvghUQqMmSKG7bTZvGKtjrcU3" + "PGzMNG37yzxywrcdvgkwrr8eYXJmbwdvUNVT4Ucv7ris4jvA7BUmg" @@ -53,25 +106,115 @@ var ( }} ) -// testRemoteSigner tests that a watch-only wallet can use a remote signing -// wallet to perform any signing or ECDH operations. -func testRemoteSigner(ht *lntest.HarnessTest) { - type testCase struct { - name string - randomSeed bool - sendCoins bool - commitType lnrpc.CommitmentType - fn func(tt *lntest.HarnessTest, - wo, carol *node.HarnessNode) +// remoteSignerTestCase defines a test case for the remote signer test suite. +type remoteSignerTestCase struct { + name string + randomSeed bool + sendCoins bool + commitType lnrpc.CommitmentType + fn func(tt *lntest.HarnessTest, wo, carol *node.HarnessNode) +} + +// prepareRemoteSignerTest prepares a test case for the remote signer test +// suite by creating three nodes. +func prepareRemoteSignerTest(ht *lntest.HarnessTest, tc remoteSignerTestCase) ( + *node.HarnessNode, *node.HarnessNode, *node.HarnessNode) { + + // Signer is our signing node and has the wallet with the full master + // private key. We test that we can create the watch-only wallet from + // the exported accounts but also from a static key to make sure the + // derivation of the account public keys is correct in both cases. + password := []byte("itestpassword") + var ( + signerNodePubKey = nodePubKey + watchOnlyAccounts = deriveCustomScopeAccounts(ht.T) + signer *node.HarnessNode + err error + ) + if !tc.randomSeed { + signer = ht.RestoreNodeWithSeed( + "Signer", nil, password, nil, rootKey, 0, nil, + ) + } else { + signer = ht.NewNode("Signer", nil) + signerNodePubKey = signer.PubKeyStr + + rpcAccts := signer.RPC.ListAccounts( + &walletrpc.ListAccountsRequest{}, + ) + + watchOnlyAccounts, err = walletrpc.AccountsToWatchOnly( + rpcAccts.Accounts, + ) + require.NoError(ht, err) } - subTests := []testCase{{ + var commitArgs []string + if tc.commitType == lnrpc.CommitmentType_SIMPLE_TAPROOT { + commitArgs = lntest.NodeArgsForCommitType( + tc.commitType, + ) + } + + // WatchOnly is the node that has a watch-only wallet and uses the + // Signer node for any operation that requires access to private keys. + watchOnly := ht.NewNodeRemoteSigner( + "WatchOnly", append([]string{ + "--remotesigner.enable", + fmt.Sprintf( + "--remotesigner.rpchost=localhost:%d", + signer.Cfg.RPCPort, + ), + fmt.Sprintf( + "--remotesigner.tlscertpath=%s", + signer.Cfg.TLSCertPath, + ), + fmt.Sprintf( + "--remotesigner.macaroonpath=%s", + signer.Cfg.AdminMacPath, + ), + }, commitArgs...), + password, &lnrpc.WatchOnly{ + MasterKeyBirthdayTimestamp: 0, + MasterKeyFingerprint: nil, + Accounts: watchOnlyAccounts, + }, + ) + + resp := watchOnly.RPC.GetInfo() + require.Equal(ht, signerNodePubKey, resp.IdentityPubkey) + + if tc.sendCoins { + ht.FundCoins(btcutil.SatoshiPerBitcoin, watchOnly) + ht.AssertWalletAccountBalance( + watchOnly, "default", + btcutil.SatoshiPerBitcoin, 0, + ) + } + + carol := ht.NewNode("carol", commitArgs) + ht.EnsureConnected(watchOnly, carol) + + return signer, watchOnly, carol +} + +// testRemoteSignerRadomSeed tests that a watch-only wallet can use a remote +// signing wallet to perform any signing or ECDH operations. +func testRemoteSignerRadomSeed(ht *lntest.HarnessTest) { + tc := remoteSignerTestCase{ name: "random seed", randomSeed: true, fn: func(tt *lntest.HarnessTest, wo, carol *node.HarnessNode) { // Nothing more to test here. }, - }, { + } + + _, watchOnly, carol := prepareRemoteSignerTest(ht, tc) + tc.fn(ht, watchOnly, carol) +} + +func testRemoteSignerAccountImport(ht *lntest.HarnessTest) { + tc := remoteSignerTestCase{ name: "account import", fn: func(tt *lntest.HarnessTest, wo, carol *node.HarnessNode) { runWalletImportAccountScenario( @@ -79,25 +222,53 @@ func testRemoteSigner(ht *lntest.HarnessTest) { carol, wo, ) }, - }, { + } + + _, watchOnly, carol := prepareRemoteSignerTest(ht, tc) + tc.fn(ht, watchOnly, carol) +} + +func testRemoteSignerChannelOpen(ht *lntest.HarnessTest) { + tc := remoteSignerTestCase{ name: "basic channel open close", sendCoins: true, fn: func(tt *lntest.HarnessTest, wo, carol *node.HarnessNode) { runBasicChannelCreationAndUpdates(tt, wo, carol) }, - }, { + } + + _, watchOnly, carol := prepareRemoteSignerTest(ht, tc) + tc.fn(ht, watchOnly, carol) +} + +func testRemoteSignerChannelFundingInputTypes(ht *lntest.HarnessTest) { + tc := remoteSignerTestCase{ name: "channel funding input types", sendCoins: false, fn: func(tt *lntest.HarnessTest, wo, carol *node.HarnessNode) { runChannelFundingInputTypes(tt, carol, wo) }, - }, { + } + + _, watchOnly, carol := prepareRemoteSignerTest(ht, tc) + tc.fn(ht, watchOnly, carol) +} + +func testRemoteSignerAsyncPayments(ht *lntest.HarnessTest) { + tc := remoteSignerTestCase{ name: "async payments", sendCoins: true, fn: func(tt *lntest.HarnessTest, wo, carol *node.HarnessNode) { runAsyncPayments(tt, wo, carol, nil) }, - }, { + } + + _, watchOnly, carol := prepareRemoteSignerTest(ht, tc) + tc.fn(ht, watchOnly, carol) +} + +func testRemoteSignerAsyncPaymentsTaproot(ht *lntest.HarnessTest) { + tc := remoteSignerTestCase{ name: "async payments taproot", sendCoins: true, fn: func(tt *lntest.HarnessTest, wo, carol *node.HarnessNode) { @@ -108,18 +279,39 @@ func testRemoteSigner(ht *lntest.HarnessTest) { ) }, commitType: lnrpc.CommitmentType_SIMPLE_TAPROOT, - }, { + } + + _, watchOnly, carol := prepareRemoteSignerTest(ht, tc) + tc.fn(ht, watchOnly, carol) +} + +func testRemoteSignerSharedKey(ht *lntest.HarnessTest) { + tc := remoteSignerTestCase{ name: "shared key", fn: func(tt *lntest.HarnessTest, wo, carol *node.HarnessNode) { runDeriveSharedKey(tt, wo) }, - }, { + } + + _, watchOnly, carol := prepareRemoteSignerTest(ht, tc) + tc.fn(ht, watchOnly, carol) +} + +func testRemoteSignerBumpFee(ht *lntest.HarnessTest) { + tc := remoteSignerTestCase{ name: "bumpfee", sendCoins: true, fn: func(tt *lntest.HarnessTest, wo, carol *node.HarnessNode) { runBumpFee(tt, wo) }, - }, { + } + + _, watchOnly, carol := prepareRemoteSignerTest(ht, tc) + tc.fn(ht, watchOnly, carol) +} + +func testRemoteSignerPSBT(ht *lntest.HarnessTest) { + tc := remoteSignerTestCase{ name: "psbt", randomSeed: true, fn: func(tt *lntest.HarnessTest, wo, carol *node.HarnessNode) { @@ -137,19 +329,40 @@ func testRemoteSigner(ht *lntest.HarnessTest) { // sure we can fund and then sign PSBTs from our wallet. runFundAndSignPsbt(ht, wo) }, - }, { + } + + _, watchOnly, carol := prepareRemoteSignerTest(ht, tc) + tc.fn(ht, watchOnly, carol) +} + +func testRemoteSignerSignOutputRaw(ht *lntest.HarnessTest) { + tc := remoteSignerTestCase{ name: "sign output raw", sendCoins: true, fn: func(tt *lntest.HarnessTest, wo, carol *node.HarnessNode) { runSignOutputRaw(tt, wo) }, - }, { + } + + _, watchOnly, carol := prepareRemoteSignerTest(ht, tc) + tc.fn(ht, watchOnly, carol) +} + +func testRemoteSignerSignVerifyMsg(ht *lntest.HarnessTest) { + tc := remoteSignerTestCase{ name: "sign verify msg", sendCoins: true, fn: func(tt *lntest.HarnessTest, wo, carol *node.HarnessNode) { runSignVerifyMessage(tt, wo) }, - }, { + } + + _, watchOnly, carol := prepareRemoteSignerTest(ht, tc) + tc.fn(ht, watchOnly, carol) +} + +func testRemoteSignerTaproot(ht *lntest.HarnessTest) { + tc := remoteSignerTestCase{ name: "taproot", sendCoins: true, randomSeed: true, @@ -175,107 +388,10 @@ func testRemoteSigner(ht *lntest.HarnessTest) { ) } }, - }} - - prepareTest := func(st *lntest.HarnessTest, - subTest testCase) (*node.HarnessNode, - *node.HarnessNode, *node.HarnessNode) { - - // Signer is our signing node and has the wallet with the full - // master private key. We test that we can create the watch-only - // wallet from the exported accounts but also from a static key - // to make sure the derivation of the account public keys is - // correct in both cases. - password := []byte("itestpassword") - var ( - signerNodePubKey = nodePubKey - watchOnlyAccounts = deriveCustomScopeAccounts(ht.T) - signer *node.HarnessNode - err error - ) - if !subTest.randomSeed { - signer = st.RestoreNodeWithSeed( - "Signer", nil, password, nil, rootKey, 0, nil, - ) - } else { - signer = st.NewNode("Signer", nil) - signerNodePubKey = signer.PubKeyStr - - rpcAccts := signer.RPC.ListAccounts( - &walletrpc.ListAccountsRequest{}, - ) - - watchOnlyAccounts, err = walletrpc.AccountsToWatchOnly( - rpcAccts.Accounts, - ) - require.NoError(st, err) - } - - var commitArgs []string - if subTest.commitType == lnrpc.CommitmentType_SIMPLE_TAPROOT { - commitArgs = lntest.NodeArgsForCommitType( - subTest.commitType, - ) - } - - // WatchOnly is the node that has a watch-only wallet and uses - // the Signer node for any operation that requires access to - // private keys. - watchOnly := st.NewNodeRemoteSigner( - "WatchOnly", append([]string{ - "--remotesigner.enable", - fmt.Sprintf( - "--remotesigner.rpchost=localhost:%d", - signer.Cfg.RPCPort, - ), - fmt.Sprintf( - "--remotesigner.tlscertpath=%s", - signer.Cfg.TLSCertPath, - ), - fmt.Sprintf( - "--remotesigner.macaroonpath=%s", - signer.Cfg.AdminMacPath, - ), - }, commitArgs...), - password, &lnrpc.WatchOnly{ - MasterKeyBirthdayTimestamp: 0, - MasterKeyFingerprint: nil, - Accounts: watchOnlyAccounts, - }, - ) - - resp := watchOnly.RPC.GetInfo() - require.Equal(st, signerNodePubKey, resp.IdentityPubkey) - - if subTest.sendCoins { - st.FundCoins(btcutil.SatoshiPerBitcoin, watchOnly) - ht.AssertWalletAccountBalance( - watchOnly, "default", - btcutil.SatoshiPerBitcoin, 0, - ) - } - - carol := st.NewNode("carol", commitArgs) - st.EnsureConnected(watchOnly, carol) - - return signer, watchOnly, carol } - for _, testCase := range subTests { - subTest := testCase - - success := ht.Run(subTest.name, func(tt *testing.T) { - // Skip the cleanup here as no standby node is used. - st := ht.Subtest(tt) - - _, watchOnly, carol := prepareTest(st, subTest) - subTest.fn(st, watchOnly, carol) - }) - - if !success { - return - } - } + _, watchOnly, carol := prepareRemoteSignerTest(ht, tc) + tc.fn(ht, watchOnly, carol) } // deriveCustomScopeAccounts derives the first 255 default accounts of the custom lnd From 6d1c3c29d7f631249e43beecb9a4968e157fccd2 Mon Sep 17 00:00:00 2001 From: yyforyongyu Date: Fri, 8 Nov 2024 19:10:02 +0800 Subject: [PATCH 03/32] itest: break down channel restore commit types cases --- itest/list_on_test.go | 5 +- itest/lnd_channel_backup_test.go | 137 +++++++++++++++---------------- 2 files changed, 65 insertions(+), 77 deletions(-) diff --git a/itest/list_on_test.go b/itest/list_on_test.go index da525a8630..0d618108f9 100644 --- a/itest/list_on_test.go +++ b/itest/list_on_test.go @@ -27,10 +27,6 @@ var allTestCases = []*lntest.TestCase{ Name: "channel backup restore unconfirmed", TestFunc: testChannelBackupRestoreUnconfirmed, }, - { - Name: "channel backup restore commit types", - TestFunc: testChannelBackupRestoreCommitTypes, - }, { Name: "channel backup restore force close", TestFunc: testChannelBackupRestoreForceClose, @@ -692,4 +688,5 @@ func init() { allTestCases = append(allTestCases, watchtowerTestCases...) allTestCases = append(allTestCases, psbtFundingTestCases...) allTestCases = append(allTestCases, remoteSignerTestCases...) + allTestCases = append(allTestCases, channelRestoreTestCases...) } diff --git a/itest/lnd_channel_backup_test.go b/itest/lnd_channel_backup_test.go index 36f2cb7987..5925c0a1ec 100644 --- a/itest/lnd_channel_backup_test.go +++ b/itest/lnd_channel_backup_test.go @@ -23,6 +23,70 @@ import ( "github.com/stretchr/testify/require" ) +// channelRestoreTestCases contains the test cases for the channel restore +// scenario. +var channelRestoreTestCases = []*lntest.TestCase{ + { + // Restore the backup from the on-disk file, using the RPC + // interface, for anchor commitment channels. + Name: "channel backup restore anchor", + TestFunc: func(ht *lntest.HarnessTest) { + runChanRestoreScenarioCommitTypes( + ht, lnrpc.CommitmentType_ANCHORS, false, + ) + }, + }, + { + // Restore the backup from the on-disk file, using the RPC + // interface, for script-enforced leased channels. + Name: "channel backup restore leased", + TestFunc: func(ht *lntest.HarnessTest) { + runChanRestoreScenarioCommitTypes( + ht, leasedType, false, + ) + }, + }, + { + // Restore the backup from the on-disk file, using the RPC + // interface, for zero-conf anchor channels. + Name: "channel backup restore anchor zero conf", + TestFunc: func(ht *lntest.HarnessTest) { + runChanRestoreScenarioCommitTypes( + ht, lnrpc.CommitmentType_ANCHORS, true, + ) + }, + }, + { + // Restore the backup from the on-disk file, using the RPC + // interface for a zero-conf script-enforced leased channel. + Name: "channel backup restore leased zero conf", + TestFunc: func(ht *lntest.HarnessTest) { + runChanRestoreScenarioCommitTypes( + ht, leasedType, true, + ) + }, + }, + { + // Restore a channel back up of a taproot channel that was + // confirmed. + Name: "channel backup restore simple taproot", + TestFunc: func(ht *lntest.HarnessTest) { + runChanRestoreScenarioCommitTypes( + ht, lnrpc.CommitmentType_SIMPLE_TAPROOT, false, + ) + }, + }, + { + // Restore a channel back up of an unconfirmed taproot channel. + Name: "channel backup restore simple taproot zero conf", + TestFunc: func(ht *lntest.HarnessTest) { + runChanRestoreScenarioCommitTypes( + ht, lnrpc.CommitmentType_SIMPLE_TAPROOT, true, + ) + }, + }, +} + type ( // nodeRestorer is a function closure that allows each test case to // control exactly *how* the prior node is restored. This might be @@ -540,79 +604,6 @@ func runChanRestoreScenarioUnConfirmed(ht *lntest.HarnessTest, useFile bool) { crs.testScenario(ht, restoredNodeFunc) } -// testChannelBackupRestoreCommitTypes tests that we're able to recover from, -// and initiate the DLP protocol for different channel commitment types and -// zero-conf channel. -func testChannelBackupRestoreCommitTypes(ht *lntest.HarnessTest) { - var testCases = []struct { - name string - ct lnrpc.CommitmentType - zeroConf bool - }{ - // Restore the backup from the on-disk file, using the RPC - // interface, for anchor commitment channels. - { - name: "restore from backup file anchors", - ct: lnrpc.CommitmentType_ANCHORS, - }, - - // Restore the backup from the on-disk file, using the RPC - // interface, for script-enforced leased channels. - { - name: "restore from backup file script " + - "enforced lease", - ct: lnrpc.CommitmentType_SCRIPT_ENFORCED_LEASE, - }, - - // Restore the backup from the on-disk file, using the RPC - // interface, for zero-conf anchor channels. - { - name: "restore from backup file for zero-conf " + - "anchors channel", - ct: lnrpc.CommitmentType_ANCHORS, - zeroConf: true, - }, - - // Restore the backup from the on-disk file, using the RPC - // interface for a zero-conf script-enforced leased channel. - { - name: "restore from backup file zero-conf " + - "script-enforced leased channel", - ct: lnrpc.CommitmentType_SCRIPT_ENFORCED_LEASE, - zeroConf: true, - }, - - // Restore a channel back up of a taproot channel that was - // confirmed. - { - name: "restore from backup taproot", - ct: lnrpc.CommitmentType_SIMPLE_TAPROOT, - zeroConf: false, - }, - - // Restore a channel back up of an unconfirmed taproot channel. - { - name: "restore from backup taproot zero conf", - ct: lnrpc.CommitmentType_SIMPLE_TAPROOT, - zeroConf: true, - }, - } - - for _, testCase := range testCases { - tc := testCase - success := ht.Run(tc.name, func(t *testing.T) { - h := ht.Subtest(t) - - runChanRestoreScenarioCommitTypes( - h, tc.ct, tc.zeroConf, - ) - }) - if !success { - break - } - } -} - // runChanRestoreScenarioCommitTypes tests that the DLP is applied for // different channel commitment types and zero-conf channel. func runChanRestoreScenarioCommitTypes(ht *lntest.HarnessTest, From a9dbd36fcf546da06920a3996e925dbe177e5cfc Mon Sep 17 00:00:00 2001 From: yyforyongyu Date: Fri, 8 Nov 2024 20:39:44 +0800 Subject: [PATCH 04/32] itest: break down utxo selection funding tests --- itest/list_on_test.go | 5 +- ...lnd_channel_funding_utxo_selection_test.go | 405 +++++++++++++----- 2 files changed, 287 insertions(+), 123 deletions(-) diff --git a/itest/list_on_test.go b/itest/list_on_test.go index 0d618108f9..218471e027 100644 --- a/itest/list_on_test.go +++ b/itest/list_on_test.go @@ -556,10 +556,6 @@ var allTestCases = []*lntest.TestCase{ Name: "custom features", TestFunc: testCustomFeatures, }, - { - Name: "utxo selection funding", - TestFunc: testChannelUtxoSelection, - }, { Name: "update pending open channels on funder side", TestFunc: testUpdateOnFunderPendingOpenChannels, @@ -689,4 +685,5 @@ func init() { allTestCases = append(allTestCases, psbtFundingTestCases...) allTestCases = append(allTestCases, remoteSignerTestCases...) allTestCases = append(allTestCases, channelRestoreTestCases...) + allTestCases = append(allTestCases, fundUtxoSelectionTestCases...) } diff --git a/itest/lnd_channel_funding_utxo_selection_test.go b/itest/lnd_channel_funding_utxo_selection_test.go index f840b8ac99..7868b73338 100644 --- a/itest/lnd_channel_funding_utxo_selection_test.go +++ b/itest/lnd_channel_funding_utxo_selection_test.go @@ -15,6 +15,37 @@ import ( "github.com/stretchr/testify/require" ) +var fundUtxoSelectionTestCases = []*lntest.TestCase{ + { + Name: "utxo selection funding error", + TestFunc: testChannelUtxoSelectionError, + }, + { + Name: "utxo selection selected valid chan size", + TestFunc: testUtxoSelectionSelectedValidChanSize, + }, + { + Name: "utxo selection selected valid chan reserve", + TestFunc: testUtxoSelectionSelectedValidChanReserve, + }, + { + Name: "utxo selection selected reserve from selected", + TestFunc: testUtxoSelectionReserveFromSelected, + }, + { + Name: "utxo selection fundmax", + TestFunc: testUtxoSelectionFundmax, + }, + { + Name: "utxo selection fundmax reserve", + TestFunc: testUtxoSelectionFundmaxReserve, + }, + { + Name: "utxo selection reused utxo", + TestFunc: testUtxoSelectionReuseUTXO, + }, +} + type chanFundUtxoSelectionTestCase struct { // name is the name of the target test case. name string @@ -57,9 +88,10 @@ type chanFundUtxoSelectionTestCase struct { reuseUtxo bool } -// testChannelUtxoSelection checks various channel funding scenarios where the -// user instructed the wallet to use a selection funds available in the wallet. -func testChannelUtxoSelection(ht *lntest.HarnessTest) { +// testChannelUtxoSelectionError checks various channel funding error scenarios +// where the user instructed the wallet to use a selection funds available in +// the wallet. +func testChannelUtxoSelectionError(ht *lntest.HarnessTest) { // Create two new nodes that open a channel between each other for these // tests. args := lntest.NodeArgsForCommitType(lnrpc.CommitmentType_ANCHORS) @@ -115,73 +147,6 @@ func testChannelUtxoSelection(ht *lntest.HarnessTest) { "create funding transaction, need 0.00210337 " + "BTC only have 0.00100000 BTC available", }, - // We are spending two selected coins partially out of three - // available in the wallet and expect a change output and the - // unselected coin as remaining wallet balance. - { - name: "selected, local amount > " + - "min chan size", - initialCoins: []btcutil.Amount{ - 200_000, 50_000, 100_000, - }, - selectedCoins: []btcutil.Amount{ - 200_000, 100_000, - }, - localAmt: btcutil.Amount(250_000), - expectedBalance: btcutil.Amount(250_000), - remainingWalletBalance: btcutil.Amount(350_000) - - btcutil.Amount(250_000) - fundingFee(2, true), - }, - // We are spending the entirety of two selected coins out of - // three available in the wallet and expect no change output and - // the unselected coin as remaining wallet balance. - { - name: "fundmax, local amount > min " + - "chan size", - initialCoins: []btcutil.Amount{ - 200_000, 100_000, 50_000, - }, - selectedCoins: []btcutil.Amount{ - 200_000, 50_000, - }, - expectedBalance: btcutil.Amount(200_000) + - btcutil.Amount(50_000) - fundingFee(2, false), - remainingWalletBalance: btcutil.Amount(100_000), - }, - // Select all coins in wallet and use the maximum available - // local amount to fund an anchor channel. - { - name: "selected, local amount leaves sufficient " + - "reserve", - initialCoins: []btcutil.Amount{ - 200_000, 100_000, - }, - selectedCoins: []btcutil.Amount{200_000, 100_000}, - commitmentType: lnrpc.CommitmentType_ANCHORS, - localAmt: btcutil.Amount(300_000) - - reserveAmount - fundingFee(2, true), - expectedBalance: btcutil.Amount(300_000) - - reserveAmount - fundingFee(2, true), - remainingWalletBalance: reserveAmount, - }, - // Select all coins in wallet towards local amount except for an - // anchor reserve portion. Because the UTXOs are sorted by size - // by default, the reserve amount is just left in the wallet. - { - name: "selected, reserve from selected", - initialCoins: []btcutil.Amount{ - 200_000, reserveAmount, 100_000, - }, - selectedCoins: []btcutil.Amount{ - 200_000, reserveAmount, 100_000, - }, - commitmentType: lnrpc.CommitmentType_ANCHORS, - localAmt: btcutil.Amount(300_000) - - fundingFee(2, true), - expectedBalance: btcutil.Amount(300_000) - - fundingFee(2, true), - remainingWalletBalance: reserveAmount, - }, // Select all coins in wallet and use more than the maximum // available local amount to fund an anchor channel. { @@ -200,43 +165,6 @@ func testChannelUtxoSelection(ht *lntest.HarnessTest) { "insufficient funds for fee bumping anchor " + "channel closings", }, - // We fund an anchor channel with a single coin and just keep - // enough funds in the wallet to cover for the anchor reserve. - { - name: "fundmax, sufficient reserve", - initialCoins: []btcutil.Amount{ - 200_000, reserveAmount, - }, - selectedCoins: []btcutil.Amount{200_000}, - commitmentType: lnrpc.CommitmentType_ANCHORS, - expectedBalance: btcutil.Amount(200_000) - - fundingFee(1, false), - remainingWalletBalance: reserveAmount, - }, - // We fund an anchor channel with a single coin and expect the - // reserve amount left in the wallet. - { - name: "fundmax, sufficient reserve from channel " + - "balance carve out", - initialCoins: []btcutil.Amount{ - 200_000, - }, - selectedCoins: []btcutil.Amount{200_000}, - commitmentType: lnrpc.CommitmentType_ANCHORS, - expectedBalance: btcutil.Amount(200_000) - - reserveAmount - fundingFee(1, true), - remainingWalletBalance: reserveAmount, - }, - // Confirm that already spent outputs can't be reused to fund - // another channel. - { - name: "output already spent", - initialCoins: []btcutil.Amount{ - 200_000, - }, - selectedCoins: []btcutil.Amount{200_000}, - reuseUtxo: true, - }, } for _, tc := range tcs { @@ -255,24 +183,258 @@ func testChannelUtxoSelection(ht *lntest.HarnessTest) { } } +// testChannelUtxoSelection checks various channel funding scenarios where the +// user instructed the wallet to use a selection funds available in the wallet. +func testUtxoSelectionSelectedValidChanSize(ht *lntest.HarnessTest) { + // Create two new nodes that open a channel between each other for these + // tests. + args := lntest.NodeArgsForCommitType(lnrpc.CommitmentType_ANCHORS) + alice := ht.NewNode("Alice", args) + bob := ht.NewNode("Bob", args) + + // Ensure both sides are connected so the funding flow can be properly + // executed. + ht.EnsureConnected(alice, bob) + + // Calculate reserve amount for one channel. + reserveResp, _ := alice.RPC.WalletKit.RequiredReserve( + context.Background(), &walletrpc.RequiredReserveRequest{ + AdditionalPublicChannels: 1, + }, + ) + + reserveAmount := btcutil.Amount(reserveResp.RequiredReserve) + + // We are spending two selected coins partially out of three available + // in the wallet and expect a change output and the unselected coin as + // remaining wallet balance. + tc := &chanFundUtxoSelectionTestCase{ + name: "selected, local amount > min chan size", + initialCoins: []btcutil.Amount{ + 200_000, 50_000, 100_000, + }, + selectedCoins: []btcutil.Amount{ + 200_000, 100_000, + }, + localAmt: btcutil.Amount(250_000), + expectedBalance: btcutil.Amount(250_000), + remainingWalletBalance: btcutil.Amount(350_000) - + btcutil.Amount(250_000) - fundingFee(2, true), + } + + runUtxoSelectionTestCase(ht, alice, bob, tc, reserveAmount) +} + +// testChannelUtxoSelection checks various channel funding scenarios where the +// user instructed the wallet to use a selection funds available in the wallet. +func testUtxoSelectionSelectedValidChanReserve(ht *lntest.HarnessTest) { + // Create two new nodes that open a channel between each other for these + // tests. + args := lntest.NodeArgsForCommitType(lnrpc.CommitmentType_ANCHORS) + alice := ht.NewNode("Alice", args) + bob := ht.NewNode("Bob", args) + + // Ensure both sides are connected so the funding flow can be properly + // executed. + ht.EnsureConnected(alice, bob) + + // Calculate reserve amount for one channel. + reserveResp, _ := alice.RPC.WalletKit.RequiredReserve( + context.Background(), &walletrpc.RequiredReserveRequest{ + AdditionalPublicChannels: 1, + }, + ) + + reserveAmount := btcutil.Amount(reserveResp.RequiredReserve) + + // Select all coins in wallet and use the maximum available + // local amount to fund an anchor channel. + tc := &chanFundUtxoSelectionTestCase{ + name: "selected, local amount leaves sufficient reserve", + initialCoins: []btcutil.Amount{ + 200_000, 100_000, + }, + selectedCoins: []btcutil.Amount{200_000, 100_000}, + commitmentType: lnrpc.CommitmentType_ANCHORS, + localAmt: btcutil.Amount(300_000) - + reserveAmount - fundingFee(2, true), + expectedBalance: btcutil.Amount(300_000) - + reserveAmount - fundingFee(2, true), + remainingWalletBalance: reserveAmount, + } + + runUtxoSelectionTestCase(ht, alice, bob, tc, reserveAmount) +} + +// testChannelUtxoSelection checks various channel funding scenarios where the +// user instructed the wallet to use a selection funds available in the wallet. +func testUtxoSelectionReserveFromSelected(ht *lntest.HarnessTest) { + // Create two new nodes that open a channel between each other for these + // tests. + args := lntest.NodeArgsForCommitType(lnrpc.CommitmentType_ANCHORS) + alice := ht.NewNode("Alice", args) + bob := ht.NewNode("Bob", args) + + // Ensure both sides are connected so the funding flow can be properly + // executed. + ht.EnsureConnected(alice, bob) + + // Calculate reserve amount for one channel. + reserveResp, _ := alice.RPC.WalletKit.RequiredReserve( + context.Background(), &walletrpc.RequiredReserveRequest{ + AdditionalPublicChannels: 1, + }, + ) + + reserveAmount := btcutil.Amount(reserveResp.RequiredReserve) + + // Select all coins in wallet towards local amount except for an anchor + // reserve portion. Because the UTXOs are sorted by size by default, + // the reserve amount is just left in the wallet. + tc := &chanFundUtxoSelectionTestCase{ + name: "selected, reserve from selected", + initialCoins: []btcutil.Amount{ + 200_000, reserveAmount, 100_000, + }, + selectedCoins: []btcutil.Amount{ + 200_000, reserveAmount, 100_000, + }, + commitmentType: lnrpc.CommitmentType_ANCHORS, + localAmt: btcutil.Amount(300_000) - + fundingFee(2, true), + expectedBalance: btcutil.Amount(300_000) - + fundingFee(2, true), + remainingWalletBalance: reserveAmount, + } + + runUtxoSelectionTestCase(ht, alice, bob, tc, reserveAmount) +} + +// testChannelUtxoSelection checks various channel funding scenarios where the +// user instructed the wallet to use a selection funds available in the wallet. +func testUtxoSelectionFundmax(ht *lntest.HarnessTest) { + // Create two new nodes that open a channel between each other for these + // tests. + args := lntest.NodeArgsForCommitType(lnrpc.CommitmentType_ANCHORS) + alice := ht.NewNode("Alice", args) + bob := ht.NewNode("Bob", args) + + // Ensure both sides are connected so the funding flow can be properly + // executed. + ht.EnsureConnected(alice, bob) + + // Calculate reserve amount for one channel. + reserveResp, _ := alice.RPC.WalletKit.RequiredReserve( + context.Background(), &walletrpc.RequiredReserveRequest{ + AdditionalPublicChannels: 1, + }, + ) + + reserveAmount := btcutil.Amount(reserveResp.RequiredReserve) + + // We fund an anchor channel with a single coin and just keep enough + // funds in the wallet to cover for the anchor reserve. + tc := &chanFundUtxoSelectionTestCase{ + name: "fundmax, sufficient reserve", + initialCoins: []btcutil.Amount{ + 200_000, reserveAmount, + }, + selectedCoins: []btcutil.Amount{200_000}, + commitmentType: lnrpc.CommitmentType_ANCHORS, + expectedBalance: btcutil.Amount(200_000) - + fundingFee(1, false), + remainingWalletBalance: reserveAmount, + } + + runUtxoSelectionTestCase(ht, alice, bob, tc, reserveAmount) +} + +// testChannelUtxoSelection checks various channel funding scenarios where the +// user instructed the wallet to use a selection funds available in the wallet. +func testUtxoSelectionFundmaxReserve(ht *lntest.HarnessTest) { + // Create two new nodes that open a channel between each other for these + // tests. + args := lntest.NodeArgsForCommitType(lnrpc.CommitmentType_ANCHORS) + alice := ht.NewNode("Alice", args) + bob := ht.NewNode("Bob", args) + + // Ensure both sides are connected so the funding flow can be properly + // executed. + ht.EnsureConnected(alice, bob) + + // Calculate reserve amount for one channel. + reserveResp, _ := alice.RPC.WalletKit.RequiredReserve( + context.Background(), &walletrpc.RequiredReserveRequest{ + AdditionalPublicChannels: 1, + }, + ) + + reserveAmount := btcutil.Amount(reserveResp.RequiredReserve) + + // We fund an anchor channel with a single coin and expect the reserve + // amount left in the wallet. + tc := &chanFundUtxoSelectionTestCase{ + name: "fundmax, sufficient reserve from channel " + + "balance carve out", + initialCoins: []btcutil.Amount{ + 200_000, + }, + selectedCoins: []btcutil.Amount{200_000}, + commitmentType: lnrpc.CommitmentType_ANCHORS, + expectedBalance: btcutil.Amount(200_000) - + reserveAmount - fundingFee(1, true), + remainingWalletBalance: reserveAmount, + } + + runUtxoSelectionTestCase(ht, alice, bob, tc, reserveAmount) +} + +// testChannelUtxoSelection checks various channel funding scenarios where the +// user instructed the wallet to use a selection funds available in the wallet. +func testUtxoSelectionReuseUTXO(ht *lntest.HarnessTest) { + // Create two new nodes that open a channel between each other for these + // tests. + args := lntest.NodeArgsForCommitType(lnrpc.CommitmentType_ANCHORS) + alice := ht.NewNode("Alice", args) + bob := ht.NewNode("Bob", args) + + // Ensure both sides are connected so the funding flow can be properly + // executed. + ht.EnsureConnected(alice, bob) + + // Calculate reserve amount for one channel. + reserveResp, _ := alice.RPC.WalletKit.RequiredReserve( + context.Background(), &walletrpc.RequiredReserveRequest{ + AdditionalPublicChannels: 1, + }, + ) + + reserveAmount := btcutil.Amount(reserveResp.RequiredReserve) + + // Confirm that already spent outputs can't be reused to fund another + // channel. + tc := &chanFundUtxoSelectionTestCase{ + name: "output already spent", + initialCoins: []btcutil.Amount{ + 200_000, + }, + selectedCoins: []btcutil.Amount{200_000}, + reuseUtxo: true, + } + + runUtxoSelectionTestCase(ht, alice, bob, tc, reserveAmount) +} + // runUtxoSelectionTestCase runs a single test case asserting that test // conditions are met. func runUtxoSelectionTestCase(ht *lntest.HarnessTest, alice, bob *node.HarnessNode, tc *chanFundUtxoSelectionTestCase, reserveAmount btcutil.Amount) { - // fund initial coins + // Fund initial coins. for _, initialCoin := range tc.initialCoins { ht.FundCoins(initialCoin, alice) } - defer func() { - // Fund additional coins to sweep in case the wallet contains - // dust. - ht.FundCoins(100_000, alice) - - // Remove all funds from Alice. - sweepNodeWalletAndAssert(ht, alice) - }() // Create an outpoint lookup for each unique amount. lookup := make(map[int64]*lnrpc.OutPoint) @@ -314,9 +476,14 @@ func runUtxoSelectionTestCase(ht *lntest.HarnessTest, alice, // successful, simply check for an error. if tc.chanOpenShouldFail { expectedErr := errors.New(tc.expectedErrStr) - ht.OpenChannelAssertErr( - alice, bob, chanParams, expectedErr, - ) + ht.OpenChannelAssertErr(alice, bob, chanParams, expectedErr) + + // Fund additional coins to sweep in case the wallet contains + // dust. + ht.FundCoins(100_000, alice) + + // Remove all funds from Alice. + sweepNodeWalletAndAssert(ht, alice) return } From 6f261863f184fd7c39e98f970006dea11590d680 Mon Sep 17 00:00:00 2001 From: yyforyongyu Date: Fri, 8 Nov 2024 22:30:57 +0800 Subject: [PATCH 05/32] itest: break all multihop test cases --- itest/lnd_multi-hop_force_close_test.go | 1486 +++++++++++------------ 1 file changed, 717 insertions(+), 769 deletions(-) diff --git a/itest/lnd_multi-hop_force_close_test.go b/itest/lnd_multi-hop_force_close_test.go index f35ad8f47c..dc18792ea4 100644 --- a/itest/lnd_multi-hop_force_close_test.go +++ b/itest/lnd_multi-hop_force_close_test.go @@ -1,8 +1,6 @@ package itest import ( - "testing" - "github.com/btcsuite/btcd/btcutil" "github.com/lightningnetwork/lnd/lncfg" "github.com/lightningnetwork/lnd/lnrpc" @@ -34,130 +32,207 @@ var multiHopForceCloseTestCases = []*lntest.TestCase{ Name: "multihop local claim outgoing htlc anchor", TestFunc: testLocalClaimOutgoingHTLCAnchor, }, + { + Name: "multihop local claim outgoing htlc anchor zero conf", + TestFunc: testLocalClaimOutgoingHTLCAnchorZeroConf, + }, { Name: "multihop local claim outgoing htlc simple taproot", TestFunc: testLocalClaimOutgoingHTLCSimpleTaproot, }, + { + Name: "multihop local claim outgoing htlc simple taproot zero conf", + TestFunc: testLocalClaimOutgoingHTLCSimpleTaprootZeroConf, + }, { Name: "multihop local claim outgoing htlc leased", TestFunc: testLocalClaimOutgoingHTLCLeased, }, + { + Name: "multihop local claim outgoing htlc leased zero conf", + TestFunc: testLocalClaimOutgoingHTLCLeasedZeroConf, + }, { Name: "multihop receiver preimage claim anchor", TestFunc: testMultiHopReceiverPreimageClaimAnchor, }, + { + Name: "multihop receiver preimage claim anchor zero conf", + TestFunc: testMultiHopReceiverPreimageClaimAnchorZeroConf, + }, { Name: "multihop receiver preimage claim simple taproot", TestFunc: testMultiHopReceiverPreimageClaimSimpleTaproot, }, + { + Name: "multihop receiver preimage claim simple taproot zero conf", + TestFunc: testMultiHopReceiverPreimageClaimSimpleTaprootZeroConf, + }, { Name: "multihop receiver preimage claim leased", TestFunc: testMultiHopReceiverPreimageClaimLeased, }, + { + Name: "multihop receiver preimage claim leased zero conf", + TestFunc: testMultiHopReceiverPreimageClaimLeasedZeroConf, + }, { Name: "multihop local force close before timeout anchor", TestFunc: testLocalForceCloseBeforeTimeoutAnchor, }, + { + Name: "multihop local force close before timeout anchor zero conf", + TestFunc: testLocalForceCloseBeforeTimeoutAnchorZeroConf, + }, { Name: "multihop local force close before timeout simple taproot", TestFunc: testLocalForceCloseBeforeTimeoutSimpleTaproot, }, + { + Name: "multihop local force close before timeout simple taproot zero conf", + TestFunc: testLocalForceCloseBeforeTimeoutSimpleTaprootZeroConf, + }, { Name: "multihop local force close before timeout leased", TestFunc: testLocalForceCloseBeforeTimeoutLeased, }, + { + Name: "multihop local force close before timeout leased zero conf", + TestFunc: testLocalForceCloseBeforeTimeoutLeasedZeroConf, + }, { Name: "multihop remote force close before timeout anchor", TestFunc: testRemoteForceCloseBeforeTimeoutAnchor, }, + { + Name: "multihop remote force close before timeout anchor zero conf", + TestFunc: testRemoteForceCloseBeforeTimeoutAnchorZeroConf, + }, { Name: "multihop remote force close before timeout simple taproot", TestFunc: testRemoteForceCloseBeforeTimeoutSimpleTaproot, }, + { + Name: "multihop remote force close before timeout simple taproot zero conf", + TestFunc: testRemoteForceCloseBeforeTimeoutSimpleTaprootZeroConf, + }, { Name: "multihop remote force close before timeout leased", TestFunc: testRemoteForceCloseBeforeTimeoutLeased, }, + { + Name: "multihop remote force close before timeout leased zero conf", + TestFunc: testRemoteForceCloseBeforeTimeoutLeasedZeroConf, + }, { Name: "multihop local claim incoming htlc anchor", TestFunc: testLocalClaimIncomingHTLCAnchor, }, + { + Name: "multihop local claim incoming htlc anchor zero conf", + TestFunc: testLocalClaimIncomingHTLCAnchorZeroConf, + }, { Name: "multihop local claim incoming htlc simple taproot", TestFunc: testLocalClaimIncomingHTLCSimpleTaproot, }, + { + Name: "multihop local claim incoming htlc simple taproot zero conf", + TestFunc: testLocalClaimIncomingHTLCSimpleTaprootZeroConf, + }, { Name: "multihop local claim incoming htlc leased", TestFunc: testLocalClaimIncomingHTLCLeased, }, + { + Name: "multihop local claim incoming htlc leased zero conf", + TestFunc: testLocalClaimIncomingHTLCLeasedZeroConf, + }, { Name: "multihop local preimage claim anchor", TestFunc: testLocalPreimageClaimAnchor, }, + { + Name: "multihop local preimage claim anchor zero conf", + TestFunc: testLocalPreimageClaimAnchorZeroConf, + }, { Name: "multihop local preimage claim simple taproot", TestFunc: testLocalPreimageClaimSimpleTaproot, }, + { + Name: "multihop local preimage claim simple taproot zero conf", + TestFunc: testLocalPreimageClaimSimpleTaprootZeroConf, + }, { Name: "multihop local preimage claim leased", TestFunc: testLocalPreimageClaimLeased, }, + { + Name: "multihop local preimage claim leased zero conf", + TestFunc: testLocalPreimageClaimLeasedZeroConf, + }, { Name: "multihop htlc aggregation anchor", TestFunc: testHtlcAggregaitonAnchor, }, + { + Name: "multihop htlc aggregation anchor zero conf", + TestFunc: testHtlcAggregaitonAnchorZeroConf, + }, { Name: "multihop htlc aggregation simple taproot", TestFunc: testHtlcAggregaitonSimpleTaproot, }, + { + Name: "multihop htlc aggregation simple taproot zero conf", + TestFunc: testHtlcAggregaitonSimpleTaprootZeroConf, + }, { Name: "multihop htlc aggregation leased", TestFunc: testHtlcAggregaitonLeased, }, + { + Name: "multihop htlc aggregation leased zero conf", + TestFunc: testHtlcAggregaitonLeasedZeroConf, + }, } // testLocalClaimOutgoingHTLCAnchor tests `runLocalClaimOutgoingHTLC` with // anchor channel. func testLocalClaimOutgoingHTLCAnchor(ht *lntest.HarnessTest) { - success := ht.Run("no zero conf", func(t *testing.T) { - st := ht.Subtest(t) + // Create a three hop network: Alice -> Bob -> Carol, using anchor + // channels. + // + // Prepare params. + openChannelParams := lntest.OpenChannelParams{Amt: chanAmt} - // Create a three hop network: Alice -> Bob -> Carol, using - // anchor channels. - // - // Prepare params. - openChannelParams := lntest.OpenChannelParams{Amt: chanAmt} + cfg := node.CfgAnchor + cfgCarol := append([]string{"--hodl.exit-settle"}, cfg...) + cfgs := [][]string{cfg, cfg, cfgCarol} - cfg := node.CfgAnchor - cfgCarol := append([]string{"--hodl.exit-settle"}, cfg...) - cfgs := [][]string{cfg, cfg, cfgCarol} + runLocalClaimOutgoingHTLC(ht, cfgs, openChannelParams) +} - runLocalClaimOutgoingHTLC(st, cfgs, openChannelParams) - }) - if !success { - return +// testLocalClaimOutgoingHTLCAnchorZeroConf tests `runLocalClaimOutgoingHTLC` +// with zero conf anchor channel. +func testLocalClaimOutgoingHTLCAnchorZeroConf(ht *lntest.HarnessTest) { + // Create a three hop network: Alice -> Bob -> Carol, using zero-conf + // anchor channels. + // + // Prepare params. + openChannelParams := lntest.OpenChannelParams{ + Amt: chanAmt, + ZeroConf: true, + CommitmentType: lnrpc.CommitmentType_ANCHORS, } - ht.Run("zero conf", func(t *testing.T) { - st := ht.Subtest(t) - - // Create a three hop network: Alice -> Bob -> Carol, using - // zero-conf anchor channels. - // - // Prepare params. - openChannelParams := lntest.OpenChannelParams{ - Amt: chanAmt, - ZeroConf: true, - CommitmentType: lnrpc.CommitmentType_ANCHORS, - } - - // Prepare Carol's node config to enable zero-conf and anchor. - cfg := node.CfgZeroConf - cfgCarol := append([]string{"--hodl.exit-settle"}, cfg...) - cfgs := [][]string{cfg, cfg, cfgCarol} + // Prepare Carol's node config to enable zero-conf and anchor. + cfg := node.CfgZeroConf + cfgCarol := append([]string{"--hodl.exit-settle"}, cfg...) + cfgs := [][]string{cfg, cfg, cfgCarol} - runLocalClaimOutgoingHTLC(st, cfgs, openChannelParams) - }) + runLocalClaimOutgoingHTLC(ht, cfgs, openChannelParams) } // testLocalClaimOutgoingHTLCSimpleTaproot tests `runLocalClaimOutgoingHTLC` @@ -165,101 +240,87 @@ func testLocalClaimOutgoingHTLCAnchor(ht *lntest.HarnessTest) { func testLocalClaimOutgoingHTLCSimpleTaproot(ht *lntest.HarnessTest) { c := lnrpc.CommitmentType_SIMPLE_TAPROOT - success := ht.Run("no zero conf", func(t *testing.T) { - st := ht.Subtest(t) - - // Create a three hop network: Alice -> Bob -> Carol, using - // simple taproot channels. - // - // Prepare params. - openChannelParams := lntest.OpenChannelParams{ - Amt: chanAmt, - CommitmentType: c, - Private: true, - } + // Create a three hop network: Alice -> Bob -> Carol, using simple + // taproot channels. + // + // Prepare params. + openChannelParams := lntest.OpenChannelParams{ + Amt: chanAmt, + CommitmentType: c, + Private: true, + } - cfg := node.CfgSimpleTaproot - cfgCarol := append([]string{"--hodl.exit-settle"}, cfg...) - cfgs := [][]string{cfg, cfg, cfgCarol} + cfg := node.CfgSimpleTaproot + cfgCarol := append([]string{"--hodl.exit-settle"}, cfg...) + cfgs := [][]string{cfg, cfg, cfgCarol} - runLocalClaimOutgoingHTLC(st, cfgs, openChannelParams) - }) - if !success { - return - } + runLocalClaimOutgoingHTLC(ht, cfgs, openChannelParams) +} - ht.Run("zero conf", func(t *testing.T) { - st := ht.Subtest(t) +// testLocalClaimOutgoingHTLCSimpleTaprootZeroConf tests +// `runLocalClaimOutgoingHTLC` with zero-conf simple taproot channel. +func testLocalClaimOutgoingHTLCSimpleTaprootZeroConf(ht *lntest.HarnessTest) { + c := lnrpc.CommitmentType_SIMPLE_TAPROOT - // Create a three hop network: Alice -> Bob -> Carol, using - // zero-conf simple taproot channels. - // - // Prepare params. - openChannelParams := lntest.OpenChannelParams{ - Amt: chanAmt, - ZeroConf: true, - CommitmentType: c, - Private: true, - } + // Create a three hop network: Alice -> Bob -> Carol, using zero-conf + // simple taproot channels. + // + // Prepare params. + openChannelParams := lntest.OpenChannelParams{ + Amt: chanAmt, + ZeroConf: true, + CommitmentType: c, + Private: true, + } - // Prepare Carol's node config to enable zero-conf and leased - // channel. - cfg := node.CfgSimpleTaproot - cfg = append(cfg, node.CfgZeroConf...) - cfgCarol := append([]string{"--hodl.exit-settle"}, cfg...) - cfgs := [][]string{cfg, cfg, cfgCarol} + // Prepare Carol's node config to enable zero-conf and leased channel. + cfg := node.CfgSimpleTaproot + cfg = append(cfg, node.CfgZeroConf...) + cfgCarol := append([]string{"--hodl.exit-settle"}, cfg...) + cfgs := [][]string{cfg, cfg, cfgCarol} - runLocalClaimOutgoingHTLC(st, cfgs, openChannelParams) - }) + runLocalClaimOutgoingHTLC(ht, cfgs, openChannelParams) } // testLocalClaimOutgoingHTLCLeased tests `runLocalClaimOutgoingHTLC` with // script enforced lease channel. func testLocalClaimOutgoingHTLCLeased(ht *lntest.HarnessTest) { - success := ht.Run("no zero conf", func(t *testing.T) { - st := ht.Subtest(t) + // Create a three hop network: Alice -> Bob -> Carol, using leased + // channels. + // + // Prepare params. + openChannelParams := lntest.OpenChannelParams{ + Amt: chanAmt, + CommitmentType: leasedType, + } - // Create a three hop network: Alice -> Bob -> Carol, using - // leased channels. - // - // Prepare params. - openChannelParams := lntest.OpenChannelParams{ - Amt: chanAmt, - CommitmentType: leasedType, - } + cfg := node.CfgLeased + cfgCarol := append([]string{"--hodl.exit-settle"}, cfg...) + cfgs := [][]string{cfg, cfg, cfgCarol} - cfg := node.CfgLeased - cfgCarol := append([]string{"--hodl.exit-settle"}, cfg...) - cfgs := [][]string{cfg, cfg, cfgCarol} + runLocalClaimOutgoingHTLC(ht, cfgs, openChannelParams) +} - runLocalClaimOutgoingHTLC(st, cfgs, openChannelParams) - }) - if !success { - return +// testLocalClaimOutgoingHTLCLeasedZeroConf tests `runLocalClaimOutgoingHTLC` +// with zero-conf script enforced lease channel. +func testLocalClaimOutgoingHTLCLeasedZeroConf(ht *lntest.HarnessTest) { + // Create a three hop network: Alice -> Bob -> Carol, using zero-conf + // anchor channels. + // + // Prepare params. + openChannelParams := lntest.OpenChannelParams{ + Amt: chanAmt, + ZeroConf: true, + CommitmentType: leasedType, } - ht.Run("zero conf", func(t *testing.T) { - st := ht.Subtest(t) - - // Create a three hop network: Alice -> Bob -> Carol, using - // zero-conf anchor channels. - // - // Prepare params. - openChannelParams := lntest.OpenChannelParams{ - Amt: chanAmt, - ZeroConf: true, - CommitmentType: leasedType, - } + // Prepare Carol's node config to enable zero-conf and leased channel. + cfg := node.CfgLeased + cfg = append(cfg, node.CfgZeroConf...) + cfgCarol := append([]string{"--hodl.exit-settle"}, cfg...) + cfgs := [][]string{cfg, cfg, cfgCarol} - // Prepare Carol's node config to enable zero-conf and leased - // channel. - cfg := node.CfgLeased - cfg = append(cfg, node.CfgZeroConf...) - cfgCarol := append([]string{"--hodl.exit-settle"}, cfg...) - cfgs := [][]string{cfg, cfg, cfgCarol} - - runLocalClaimOutgoingHTLC(st, cfgs, openChannelParams) - }) + runLocalClaimOutgoingHTLC(ht, cfgs, openChannelParams) } // runLocalClaimOutgoingHTLC tests that in a multi-hop scenario, if the @@ -476,43 +537,36 @@ func runLocalClaimOutgoingHTLC(ht *lntest.HarnessTest, // testMultiHopReceiverPreimageClaimAnchor tests // `runMultiHopReceiverPreimageClaim` with anchor channels. func testMultiHopReceiverPreimageClaimAnchor(ht *lntest.HarnessTest) { - success := ht.Run("no zero conf", func(t *testing.T) { - st := ht.Subtest(t) + // Create a three hop network: Alice -> Bob -> Carol, using anchor + // channels. + // + // Prepare params. + openChannelParams := lntest.OpenChannelParams{Amt: chanAmt} - // Create a three hop network: Alice -> Bob -> Carol, using - // anchor channels. - // - // Prepare params. - openChannelParams := lntest.OpenChannelParams{Amt: chanAmt} + cfg := node.CfgAnchor + cfgs := [][]string{cfg, cfg, cfg} - cfg := node.CfgAnchor - cfgs := [][]string{cfg, cfg, cfg} + runMultiHopReceiverPreimageClaim(ht, cfgs, openChannelParams) +} - runMultiHopReceiverPreimageClaim(st, cfgs, openChannelParams) - }) - if !success { - return +// testMultiHopReceiverPreimageClaimAnchorZeroConf tests +// `runMultiHopReceiverPreimageClaim` with zero-conf anchor channels. +func testMultiHopReceiverPreimageClaimAnchorZeroConf(ht *lntest.HarnessTest) { + // Create a three hop network: Alice -> Bob -> Carol, using zero-conf + // anchor channels. + // + // Prepare params. + openChannelParams := lntest.OpenChannelParams{ + Amt: chanAmt, + ZeroConf: true, + CommitmentType: lnrpc.CommitmentType_ANCHORS, } - ht.Run("zero conf", func(t *testing.T) { - st := ht.Subtest(t) - - // Create a three hop network: Alice -> Bob -> Carol, using - // zero-conf anchor channels. - // - // Prepare params. - openChannelParams := lntest.OpenChannelParams{ - Amt: chanAmt, - ZeroConf: true, - CommitmentType: lnrpc.CommitmentType_ANCHORS, - } - - // Prepare Carol's node config to enable zero-conf and anchor. - cfg := node.CfgZeroConf - cfgs := [][]string{cfg, cfg, cfg} + // Prepare Carol's node config to enable zero-conf and anchor. + cfg := node.CfgZeroConf + cfgs := [][]string{cfg, cfg, cfg} - runMultiHopReceiverPreimageClaim(st, cfgs, openChannelParams) - }) + runMultiHopReceiverPreimageClaim(ht, cfgs, openChannelParams) } // testMultiHopReceiverPreimageClaimSimpleTaproot tests @@ -520,97 +574,88 @@ func testMultiHopReceiverPreimageClaimAnchor(ht *lntest.HarnessTest) { func testMultiHopReceiverPreimageClaimSimpleTaproot(ht *lntest.HarnessTest) { c := lnrpc.CommitmentType_SIMPLE_TAPROOT - success := ht.Run("no zero conf", func(t *testing.T) { - st := ht.Subtest(t) + // Create a three hop network: Alice -> Bob -> Carol, using simple + // taproot channels. + // + // Prepare params. + openChannelParams := lntest.OpenChannelParams{ + Amt: chanAmt, + CommitmentType: c, + Private: true, + } - // Create a three hop network: Alice -> Bob -> Carol, using - // simple taproot channels. - // - // Prepare params. - openChannelParams := lntest.OpenChannelParams{ - Amt: chanAmt, - CommitmentType: c, - Private: true, - } + cfg := node.CfgSimpleTaproot + cfgs := [][]string{cfg, cfg, cfg} - cfg := node.CfgSimpleTaproot - cfgs := [][]string{cfg, cfg, cfg} + runMultiHopReceiverPreimageClaim(ht, cfgs, openChannelParams) +} - runMultiHopReceiverPreimageClaim(st, cfgs, openChannelParams) - }) - if !success { - return - } +// testMultiHopReceiverPreimageClaimSimpleTaproot tests +// `runMultiHopReceiverPreimageClaim` with zero-conf simple taproot channels. +func testMultiHopReceiverPreimageClaimSimpleTaprootZeroConf( + ht *lntest.HarnessTest) { - ht.Run("zero conf", func(t *testing.T) { - st := ht.Subtest(t) + c := lnrpc.CommitmentType_SIMPLE_TAPROOT - // Create a three hop network: Alice -> Bob -> Carol, using - // zero-conf simple taproot channels. - // - // Prepare params. - openChannelParams := lntest.OpenChannelParams{ - Amt: chanAmt, - ZeroConf: true, - CommitmentType: c, - Private: true, - } + // Create a three hop network: Alice -> Bob -> Carol, using zero-conf + // simple taproot channels. + // + // Prepare params. + openChannelParams := lntest.OpenChannelParams{ + Amt: chanAmt, + ZeroConf: true, + CommitmentType: c, + Private: true, + } - // Prepare Carol's node config to enable zero-conf and leased - // channel. - cfg := node.CfgSimpleTaproot - cfg = append(cfg, node.CfgZeroConf...) - cfgs := [][]string{cfg, cfg, cfg} + // Prepare Carol's node config to enable zero-conf and leased + // channel. + cfg := node.CfgSimpleTaproot + cfg = append(cfg, node.CfgZeroConf...) + cfgs := [][]string{cfg, cfg, cfg} - runMultiHopReceiverPreimageClaim(st, cfgs, openChannelParams) - }) + runMultiHopReceiverPreimageClaim(ht, cfgs, openChannelParams) } // testMultiHopReceiverPreimageClaimLeased tests // `runMultiHopReceiverPreimageClaim` with script enforce lease channels. func testMultiHopReceiverPreimageClaimLeased(ht *lntest.HarnessTest) { - success := ht.Run("no zero conf", func(t *testing.T) { - st := ht.Subtest(t) + // Create a three hop network: Alice -> Bob -> Carol, using leased + // channels. + // + // Prepare params. + openChannelParams := lntest.OpenChannelParams{ + Amt: chanAmt, + CommitmentType: leasedType, + } - // Create a three hop network: Alice -> Bob -> Carol, using - // leased channels. - // - // Prepare params. - openChannelParams := lntest.OpenChannelParams{ - Amt: chanAmt, - CommitmentType: leasedType, - } + cfg := node.CfgLeased + cfgs := [][]string{cfg, cfg, cfg} - cfg := node.CfgLeased - cfgs := [][]string{cfg, cfg, cfg} + runMultiHopReceiverPreimageClaim(ht, cfgs, openChannelParams) +} - runMultiHopReceiverPreimageClaim(st, cfgs, openChannelParams) - }) - if !success { - return +// testMultiHopReceiverPreimageClaimLeased tests +// `runMultiHopReceiverPreimageClaim` with zero-conf script enforce lease +// channels. +func testMultiHopReceiverPreimageClaimLeasedZeroConf(ht *lntest.HarnessTest) { + // Create a three hop network: Alice -> Bob -> Carol, using zero-conf + // anchor channels. + // + // Prepare params. + openChannelParams := lntest.OpenChannelParams{ + Amt: chanAmt, + ZeroConf: true, + CommitmentType: leasedType, } - ht.Run("zero conf", func(t *testing.T) { - st := ht.Subtest(t) - - // Create a three hop network: Alice -> Bob -> Carol, using - // zero-conf anchor channels. - // - // Prepare params. - openChannelParams := lntest.OpenChannelParams{ - Amt: chanAmt, - ZeroConf: true, - CommitmentType: leasedType, - } - - // Prepare Carol's node config to enable zero-conf and leased - // channel. - cfg := node.CfgLeased - cfg = append(cfg, node.CfgZeroConf...) - cfgs := [][]string{cfg, cfg, cfg} + // Prepare Carol's node config to enable zero-conf and leased + // channel. + cfg := node.CfgLeased + cfg = append(cfg, node.CfgZeroConf...) + cfgs := [][]string{cfg, cfg, cfg} - runMultiHopReceiverPreimageClaim(st, cfgs, openChannelParams) - }) + runMultiHopReceiverPreimageClaim(ht, cfgs, openChannelParams) } // runMultiHopReceiverClaim tests that in the multi-hop setting, if the @@ -878,45 +923,38 @@ func runMultiHopReceiverPreimageClaim(ht *lntest.HarnessTest, // testLocalForceCloseBeforeTimeoutAnchor tests // `runLocalForceCloseBeforeHtlcTimeout` with anchor channel. func testLocalForceCloseBeforeTimeoutAnchor(ht *lntest.HarnessTest) { - success := ht.Run("no zero conf", func(t *testing.T) { - st := ht.Subtest(t) + // Create a three hop network: Alice -> Bob -> Carol, using anchor + // channels. + // + // Prepare params. + params := lntest.OpenChannelParams{Amt: chanAmt} - // Create a three hop network: Alice -> Bob -> Carol, using - // anchor channels. - // - // Prepare params. - params := lntest.OpenChannelParams{Amt: chanAmt} + cfg := node.CfgAnchor + cfgCarol := append([]string{"--hodl.exit-settle"}, cfg...) + cfgs := [][]string{cfg, cfg, cfgCarol} - cfg := node.CfgAnchor - cfgCarol := append([]string{"--hodl.exit-settle"}, cfg...) - cfgs := [][]string{cfg, cfg, cfgCarol} + runLocalForceCloseBeforeHtlcTimeout(ht, cfgs, params) +} - runLocalForceCloseBeforeHtlcTimeout(st, cfgs, params) - }) - if !success { - return +// testLocalForceCloseBeforeTimeoutAnchorZeroConf tests +// `runLocalForceCloseBeforeHtlcTimeout` with zero-conf anchor channel. +func testLocalForceCloseBeforeTimeoutAnchorZeroConf(ht *lntest.HarnessTest) { + // Create a three hop network: Alice -> Bob -> Carol, using zero-conf + // anchor channels. + // + // Prepare params. + params := lntest.OpenChannelParams{ + Amt: chanAmt, + ZeroConf: true, + CommitmentType: lnrpc.CommitmentType_ANCHORS, } - ht.Run("zero conf", func(t *testing.T) { - st := ht.Subtest(t) - - // Create a three hop network: Alice -> Bob -> Carol, using - // zero-conf anchor channels. - // - // Prepare params. - params := lntest.OpenChannelParams{ - Amt: chanAmt, - ZeroConf: true, - CommitmentType: lnrpc.CommitmentType_ANCHORS, - } - - // Prepare Carol's node config to enable zero-conf and anchor. - cfg := node.CfgZeroConf - cfgCarol := append([]string{"--hodl.exit-settle"}, cfg...) - cfgs := [][]string{cfg, cfg, cfgCarol} + // Prepare Carol's node config to enable zero-conf and anchor. + cfg := node.CfgZeroConf + cfgCarol := append([]string{"--hodl.exit-settle"}, cfg...) + cfgs := [][]string{cfg, cfg, cfgCarol} - runLocalForceCloseBeforeHtlcTimeout(st, cfgs, params) - }) + runLocalForceCloseBeforeHtlcTimeout(ht, cfgs, params) } // testLocalForceCloseBeforeTimeoutSimpleTaproot tests @@ -924,101 +962,90 @@ func testLocalForceCloseBeforeTimeoutAnchor(ht *lntest.HarnessTest) { func testLocalForceCloseBeforeTimeoutSimpleTaproot(ht *lntest.HarnessTest) { c := lnrpc.CommitmentType_SIMPLE_TAPROOT - success := ht.Run("no zero conf", func(t *testing.T) { - st := ht.Subtest(t) + // Create a three hop network: Alice -> Bob -> Carol, using simple + // taproot channels. + // + // Prepare params. + params := lntest.OpenChannelParams{ + Amt: chanAmt, + CommitmentType: c, + Private: true, + } - // Create a three hop network: Alice -> Bob -> Carol, using - // simple taproot channels. - // - // Prepare params. - params := lntest.OpenChannelParams{ - Amt: chanAmt, - CommitmentType: c, - Private: true, - } + cfg := node.CfgSimpleTaproot + cfgCarol := append([]string{"--hodl.exit-settle"}, cfg...) + cfgs := [][]string{cfg, cfg, cfgCarol} - cfg := node.CfgSimpleTaproot - cfgCarol := append([]string{"--hodl.exit-settle"}, cfg...) - cfgs := [][]string{cfg, cfg, cfgCarol} + runLocalForceCloseBeforeHtlcTimeout(ht, cfgs, params) +} - runLocalForceCloseBeforeHtlcTimeout(st, cfgs, params) - }) - if !success { - return - } +// testLocalForceCloseBeforeTimeoutSimpleTaproot tests +// `runLocalForceCloseBeforeHtlcTimeout` with zero-conf simple taproot channel. +func testLocalForceCloseBeforeTimeoutSimpleTaprootZeroConf( + ht *lntest.HarnessTest) { - ht.Run("zero conf", func(t *testing.T) { - st := ht.Subtest(t) + c := lnrpc.CommitmentType_SIMPLE_TAPROOT - // Create a three hop network: Alice -> Bob -> Carol, using - // zero-conf simple taproot channels. - // - // Prepare params. - params := lntest.OpenChannelParams{ - Amt: chanAmt, - ZeroConf: true, - CommitmentType: c, - Private: true, - } + // Create a three hop network: Alice -> Bob -> Carol, using zero-conf + // simple taproot channels. + // + // Prepare params. + params := lntest.OpenChannelParams{ + Amt: chanAmt, + ZeroConf: true, + CommitmentType: c, + Private: true, + } - // Prepare Carol's node config to enable zero-conf and leased - // channel. - cfg := node.CfgSimpleTaproot - cfg = append(cfg, node.CfgZeroConf...) - cfgCarol := append([]string{"--hodl.exit-settle"}, cfg...) - cfgs := [][]string{cfg, cfg, cfgCarol} + // Prepare Carol's node config to enable zero-conf and leased channel. + cfg := node.CfgSimpleTaproot + cfg = append(cfg, node.CfgZeroConf...) + cfgCarol := append([]string{"--hodl.exit-settle"}, cfg...) + cfgs := [][]string{cfg, cfg, cfgCarol} - runLocalForceCloseBeforeHtlcTimeout(st, cfgs, params) - }) + runLocalForceCloseBeforeHtlcTimeout(ht, cfgs, params) } // testLocalForceCloseBeforeTimeoutLeased tests // `runLocalForceCloseBeforeHtlcTimeout` with script enforced lease channel. func testLocalForceCloseBeforeTimeoutLeased(ht *lntest.HarnessTest) { - success := ht.Run("no zero conf", func(t *testing.T) { - st := ht.Subtest(t) + // Create a three hop network: Alice -> Bob -> Carol, using leased + // channels. + // + // Prepare params. + params := lntest.OpenChannelParams{ + Amt: chanAmt, + CommitmentType: leasedType, + } - // Create a three hop network: Alice -> Bob -> Carol, using - // leased channels. - // - // Prepare params. - params := lntest.OpenChannelParams{ - Amt: chanAmt, - CommitmentType: leasedType, - } + cfg := node.CfgLeased + cfgCarol := append([]string{"--hodl.exit-settle"}, cfg...) + cfgs := [][]string{cfg, cfg, cfgCarol} - cfg := node.CfgLeased - cfgCarol := append([]string{"--hodl.exit-settle"}, cfg...) - cfgs := [][]string{cfg, cfg, cfgCarol} + runLocalForceCloseBeforeHtlcTimeout(ht, cfgs, params) +} - runLocalForceCloseBeforeHtlcTimeout(st, cfgs, params) - }) - if !success { - return +// testLocalForceCloseBeforeTimeoutLeased tests +// `runLocalForceCloseBeforeHtlcTimeout` with zero-conf script enforced lease +// channel. +func testLocalForceCloseBeforeTimeoutLeasedZeroConf(ht *lntest.HarnessTest) { + // Create a three hop network: Alice -> Bob -> Carol, using zero-conf + // anchor channels. + // + // Prepare params. + params := lntest.OpenChannelParams{ + Amt: chanAmt, + ZeroConf: true, + CommitmentType: leasedType, } - ht.Run("zero conf", func(t *testing.T) { - st := ht.Subtest(t) - - // Create a three hop network: Alice -> Bob -> Carol, using - // zero-conf anchor channels. - // - // Prepare params. - params := lntest.OpenChannelParams{ - Amt: chanAmt, - ZeroConf: true, - CommitmentType: leasedType, - } - - // Prepare Carol's node config to enable zero-conf and leased - // channel. - cfg := node.CfgLeased - cfg = append(cfg, node.CfgZeroConf...) - cfgCarol := append([]string{"--hodl.exit-settle"}, cfg...) - cfgs := [][]string{cfg, cfg, cfgCarol} + // Prepare Carol's node config to enable zero-conf and leased channel. + cfg := node.CfgLeased + cfg = append(cfg, node.CfgZeroConf...) + cfgCarol := append([]string{"--hodl.exit-settle"}, cfg...) + cfgs := [][]string{cfg, cfg, cfgCarol} - runLocalForceCloseBeforeHtlcTimeout(st, cfgs, params) - }) + runLocalForceCloseBeforeHtlcTimeout(ht, cfgs, params) } // runLocalForceCloseBeforeHtlcTimeout tests that in a multi-hop HTLC scenario, @@ -1210,45 +1237,65 @@ func runLocalForceCloseBeforeHtlcTimeout(ht *lntest.HarnessTest, // testRemoteForceCloseBeforeTimeoutAnchor tests // `runRemoteForceCloseBeforeHtlcTimeout` with anchor channel. func testRemoteForceCloseBeforeTimeoutAnchor(ht *lntest.HarnessTest) { - success := ht.Run("no zero conf", func(t *testing.T) { - st := ht.Subtest(t) + // Create a three hop network: Alice -> Bob -> Carol, using anchor + // channels. + // + // Prepare params. + params := lntest.OpenChannelParams{Amt: chanAmt} - // Create a three hop network: Alice -> Bob -> Carol, using - // anchor channels. - // - // Prepare params. - params := lntest.OpenChannelParams{Amt: chanAmt} + cfg := node.CfgAnchor + cfgCarol := append([]string{"--hodl.exit-settle"}, cfg...) + cfgs := [][]string{cfg, cfg, cfgCarol} - cfg := node.CfgAnchor - cfgCarol := append([]string{"--hodl.exit-settle"}, cfg...) - cfgs := [][]string{cfg, cfg, cfgCarol} + runRemoteForceCloseBeforeHtlcTimeout(ht, cfgs, params) +} - runRemoteForceCloseBeforeHtlcTimeout(st, cfgs, params) - }) - if !success { - return +// testRemoteForceCloseBeforeTimeoutAnchor tests +// `runRemoteForceCloseBeforeHtlcTimeout` with zero-conf anchor channel. +func testRemoteForceCloseBeforeTimeoutAnchorZeroConf(ht *lntest.HarnessTest) { + // Create a three hop network: Alice -> Bob -> Carol, using zero-conf + // anchor channels. + // + // Prepare params. + params := lntest.OpenChannelParams{ + Amt: chanAmt, + ZeroConf: true, + CommitmentType: lnrpc.CommitmentType_ANCHORS, } - ht.Run("zero conf", func(t *testing.T) { - st := ht.Subtest(t) + // Prepare Carol's node config to enable zero-conf and anchor. + cfg := node.CfgZeroConf + cfgCarol := append([]string{"--hodl.exit-settle"}, cfg...) + cfgs := [][]string{cfg, cfg, cfgCarol} - // Create a three hop network: Alice -> Bob -> Carol, using - // zero-conf anchor channels. - // - // Prepare params. - params := lntest.OpenChannelParams{ - Amt: chanAmt, - ZeroConf: true, - CommitmentType: lnrpc.CommitmentType_ANCHORS, - } + runRemoteForceCloseBeforeHtlcTimeout(ht, cfgs, params) +} - // Prepare Carol's node config to enable zero-conf and anchor. - cfg := node.CfgZeroConf - cfgCarol := append([]string{"--hodl.exit-settle"}, cfg...) - cfgs := [][]string{cfg, cfg, cfgCarol} +// testRemoteForceCloseBeforeTimeoutSimpleTaproot tests +// `runLocalForceCloseBeforeHtlcTimeout` with zero-conf simple taproot channel. +func testRemoteForceCloseBeforeTimeoutSimpleTaprootZeroConf( + ht *lntest.HarnessTest) { + + c := lnrpc.CommitmentType_SIMPLE_TAPROOT - runRemoteForceCloseBeforeHtlcTimeout(st, cfgs, params) - }) + // Create a three hop network: Alice -> Bob -> Carol, using zero-conf + // simple taproot channels. + // + // Prepare params. + params := lntest.OpenChannelParams{ + Amt: chanAmt, + ZeroConf: true, + CommitmentType: c, + Private: true, + } + + // Prepare Carol's node config to enable zero-conf and leased channel. + cfg := node.CfgSimpleTaproot + cfg = append(cfg, node.CfgZeroConf...) + cfgCarol := append([]string{"--hodl.exit-settle"}, cfg...) + cfgs := [][]string{cfg, cfg, cfgCarol} + + runRemoteForceCloseBeforeHtlcTimeout(ht, cfgs, params) } // testRemoteForceCloseBeforeTimeoutSimpleTaproot tests @@ -1256,101 +1303,64 @@ func testRemoteForceCloseBeforeTimeoutAnchor(ht *lntest.HarnessTest) { func testRemoteForceCloseBeforeTimeoutSimpleTaproot(ht *lntest.HarnessTest) { c := lnrpc.CommitmentType_SIMPLE_TAPROOT - success := ht.Run("no zero conf", func(t *testing.T) { - st := ht.Subtest(t) + // Create a three hop network: Alice -> Bob -> Carol, using simple + // taproot channels. + // + // Prepare params. + params := lntest.OpenChannelParams{ + Amt: chanAmt, + CommitmentType: c, + Private: true, + } - // Create a three hop network: Alice -> Bob -> Carol, using - // simple taproot channels. - // - // Prepare params. - params := lntest.OpenChannelParams{ - Amt: chanAmt, - CommitmentType: c, - Private: true, - } + cfg := node.CfgSimpleTaproot + cfgCarol := append([]string{"--hodl.exit-settle"}, cfg...) + cfgs := [][]string{cfg, cfg, cfgCarol} - cfg := node.CfgSimpleTaproot - cfgCarol := append([]string{"--hodl.exit-settle"}, cfg...) - cfgs := [][]string{cfg, cfg, cfgCarol} + runRemoteForceCloseBeforeHtlcTimeout(ht, cfgs, params) +} - runRemoteForceCloseBeforeHtlcTimeout(st, cfgs, params) - }) - if !success { - return +// testRemoteForceCloseBeforeTimeoutLeasedZeroConf tests +// `runRemoteForceCloseBeforeHtlcTimeout` with zero-conf script enforced lease +// channel. +func testRemoteForceCloseBeforeTimeoutLeasedZeroConf(ht *lntest.HarnessTest) { + // Create a three hop network: Alice -> Bob -> Carol, using zero-conf + // anchor channels. + // + // Prepare params. + params := lntest.OpenChannelParams{ + Amt: chanAmt, + ZeroConf: true, + CommitmentType: leasedType, } - ht.Run("zero conf", func(t *testing.T) { - st := ht.Subtest(t) + // Prepare Carol's node config to enable zero-conf and leased + // channel. + cfg := node.CfgLeased + cfg = append(cfg, node.CfgZeroConf...) + cfgCarol := append([]string{"--hodl.exit-settle"}, cfg...) + cfgs := [][]string{cfg, cfg, cfgCarol} - // Create a three hop network: Alice -> Bob -> Carol, using - // zero-conf simple taproot channels. - // - // Prepare params. - params := lntest.OpenChannelParams{ - Amt: chanAmt, - ZeroConf: true, - CommitmentType: c, - Private: true, - } - - // Prepare Carol's node config to enable zero-conf and leased - // channel. - cfg := node.CfgSimpleTaproot - cfg = append(cfg, node.CfgZeroConf...) - cfgCarol := append([]string{"--hodl.exit-settle"}, cfg...) - cfgs := [][]string{cfg, cfg, cfgCarol} - - runRemoteForceCloseBeforeHtlcTimeout(st, cfgs, params) - }) + runRemoteForceCloseBeforeHtlcTimeout(ht, cfgs, params) } // testRemoteForceCloseBeforeTimeoutLeased tests // `runRemoteForceCloseBeforeHtlcTimeout` with script enforced lease channel. func testRemoteForceCloseBeforeTimeoutLeased(ht *lntest.HarnessTest) { - success := ht.Run("no zero conf", func(t *testing.T) { - st := ht.Subtest(t) - - // Create a three hop network: Alice -> Bob -> Carol, using - // leased channels. - // - // Prepare params. - params := lntest.OpenChannelParams{ - Amt: chanAmt, - CommitmentType: leasedType, - } - - cfg := node.CfgLeased - cfgCarol := append([]string{"--hodl.exit-settle"}, cfg...) - cfgs := [][]string{cfg, cfg, cfgCarol} - - runRemoteForceCloseBeforeHtlcTimeout(st, cfgs, params) - }) - if !success { - return + // Create a three hop network: Alice -> Bob -> Carol, using leased + // channels. + // + // Prepare params. + params := lntest.OpenChannelParams{ + Amt: chanAmt, + CommitmentType: leasedType, } - ht.Run("zero conf", func(t *testing.T) { - st := ht.Subtest(t) - - // Create a three hop network: Alice -> Bob -> Carol, using - // zero-conf anchor channels. - // - // Prepare params. - params := lntest.OpenChannelParams{ - Amt: chanAmt, - ZeroConf: true, - CommitmentType: leasedType, - } - - // Prepare Carol's node config to enable zero-conf and leased - // channel. - cfg := node.CfgLeased - cfg = append(cfg, node.CfgZeroConf...) - cfgCarol := append([]string{"--hodl.exit-settle"}, cfg...) - cfgs := [][]string{cfg, cfg, cfgCarol} + cfg := node.CfgLeased + cfgCarol := append([]string{"--hodl.exit-settle"}, cfg...) + cfgs := [][]string{cfg, cfg, cfgCarol} - runRemoteForceCloseBeforeHtlcTimeout(st, cfgs, params) - }) + runRemoteForceCloseBeforeHtlcTimeout(ht, cfgs, params) } // runRemoteForceCloseBeforeHtlcTimeout tests that if we extend a multi-hop @@ -1521,46 +1531,63 @@ func runRemoteForceCloseBeforeHtlcTimeout(ht *lntest.HarnessTest, ht.AssertInvoiceState(stream, lnrpc.Invoice_CANCELED) } +// testLocalClaimIncomingHTLCAnchorZeroConf tests `runLocalClaimIncomingHTLC` +// with zero-conf anchor channel. +func testLocalClaimIncomingHTLCAnchorZeroConf(ht *lntest.HarnessTest) { + // Create a three hop network: Alice -> Bob -> Carol, using zero-conf + // anchor channels. + // + // Prepare params. + params := lntest.OpenChannelParams{ + Amt: chanAmt, + ZeroConf: true, + CommitmentType: lnrpc.CommitmentType_ANCHORS, + } + + // Prepare Carol's node config to enable zero-conf and anchor. + cfg := node.CfgZeroConf + cfgs := [][]string{cfg, cfg, cfg} + + runLocalClaimIncomingHTLC(ht, cfgs, params) +} + // testLocalClaimIncomingHTLCAnchor tests `runLocalClaimIncomingHTLC` with // anchor channel. func testLocalClaimIncomingHTLCAnchor(ht *lntest.HarnessTest) { - success := ht.Run("no zero conf", func(t *testing.T) { - st := ht.Subtest(t) - - // Create a three hop network: Alice -> Bob -> Carol, using - // anchor channels. - // - // Prepare params. - params := lntest.OpenChannelParams{Amt: chanAmt} + // Create a three hop network: Alice -> Bob -> Carol, using anchor + // channels. + // + // Prepare params. + params := lntest.OpenChannelParams{Amt: chanAmt} - cfg := node.CfgAnchor - cfgs := [][]string{cfg, cfg, cfg} + cfg := node.CfgAnchor + cfgs := [][]string{cfg, cfg, cfg} - runLocalClaimIncomingHTLC(st, cfgs, params) - }) - if !success { - return - } + runLocalClaimIncomingHTLC(ht, cfgs, params) +} - ht.Run("zero conf", func(t *testing.T) { - st := ht.Subtest(t) +// testLocalClaimIncomingHTLCSimpleTaprootZeroConf tests +// `runLocalClaimIncomingHTLC` with zero-conf simple taproot channel. +func testLocalClaimIncomingHTLCSimpleTaprootZeroConf(ht *lntest.HarnessTest) { + c := lnrpc.CommitmentType_SIMPLE_TAPROOT - // Create a three hop network: Alice -> Bob -> Carol, using - // zero-conf anchor channels. - // - // Prepare params. - params := lntest.OpenChannelParams{ - Amt: chanAmt, - ZeroConf: true, - CommitmentType: lnrpc.CommitmentType_ANCHORS, - } + // Create a three hop network: Alice -> Bob -> Carol, using zero-conf + // simple taproot channels. + // + // Prepare params. + params := lntest.OpenChannelParams{ + Amt: chanAmt, + ZeroConf: true, + CommitmentType: c, + Private: true, + } - // Prepare Carol's node config to enable zero-conf and anchor. - cfg := node.CfgZeroConf - cfgs := [][]string{cfg, cfg, cfg} + // Prepare Carol's node config to enable zero-conf and leased channel. + cfg := node.CfgSimpleTaproot + cfg = append(cfg, node.CfgZeroConf...) + cfgs := [][]string{cfg, cfg, cfg} - runLocalClaimIncomingHTLC(st, cfgs, params) - }) + runLocalClaimIncomingHTLC(ht, cfgs, params) } // testLocalClaimIncomingHTLCSimpleTaproot tests `runLocalClaimIncomingHTLC` @@ -1568,50 +1595,20 @@ func testLocalClaimIncomingHTLCAnchor(ht *lntest.HarnessTest) { func testLocalClaimIncomingHTLCSimpleTaproot(ht *lntest.HarnessTest) { c := lnrpc.CommitmentType_SIMPLE_TAPROOT - success := ht.Run("no zero conf", func(t *testing.T) { - st := ht.Subtest(t) - - // Create a three hop network: Alice -> Bob -> Carol, using - // simple taproot channels. - // - // Prepare params. - params := lntest.OpenChannelParams{ - Amt: chanAmt, - CommitmentType: c, - Private: true, - } - - cfg := node.CfgSimpleTaproot - cfgs := [][]string{cfg, cfg, cfg} - - runLocalClaimIncomingHTLC(st, cfgs, params) - }) - if !success { - return + // Create a three hop network: Alice -> Bob -> Carol, using simple + // taproot channels. + // + // Prepare params. + params := lntest.OpenChannelParams{ + Amt: chanAmt, + CommitmentType: c, + Private: true, } - ht.Run("zero conf", func(t *testing.T) { - st := ht.Subtest(t) + cfg := node.CfgSimpleTaproot + cfgs := [][]string{cfg, cfg, cfg} - // Create a three hop network: Alice -> Bob -> Carol, using - // zero-conf simple taproot channels. - // - // Prepare params. - params := lntest.OpenChannelParams{ - Amt: chanAmt, - ZeroConf: true, - CommitmentType: c, - Private: true, - } - - // Prepare Carol's node config to enable zero-conf and leased - // channel. - cfg := node.CfgSimpleTaproot - cfg = append(cfg, node.CfgZeroConf...) - cfgs := [][]string{cfg, cfg, cfg} - - runLocalClaimIncomingHTLC(st, cfgs, params) - }) + runLocalClaimIncomingHTLC(ht, cfgs, params) } // runLocalClaimIncomingHTLC tests that in a multi-hop HTLC scenario, if we @@ -1885,51 +1882,44 @@ func runLocalClaimIncomingHTLC(ht *lntest.HarnessTest, ht.AssertPaymentStatus(alice, preimage, lnrpc.Payment_SUCCEEDED) } -// testLocalClaimIncomingHTLCLeased tests `runLocalClaimIncomingHTLCLeased` -// with script enforced lease channel. -func testLocalClaimIncomingHTLCLeased(ht *lntest.HarnessTest) { - success := ht.Run("no zero conf", func(t *testing.T) { - st := ht.Subtest(t) +// testLocalClaimIncomingHTLCLeasedZeroConf tests +// `runLocalClaimIncomingHTLCLeased` with zero-conf script enforced lease +// channel. +func testLocalClaimIncomingHTLCLeasedZeroConf(ht *lntest.HarnessTest) { + // Create a three hop network: Alice -> Bob -> Carol, using zero-conf + // anchor channels. + // + // Prepare params. + params := lntest.OpenChannelParams{ + Amt: chanAmt, + ZeroConf: true, + CommitmentType: leasedType, + } - // Create a three hop network: Alice -> Bob -> Carol, using - // leased channels. - // - // Prepare params. - params := lntest.OpenChannelParams{ - Amt: chanAmt, - CommitmentType: leasedType, - } + // Prepare Carol's node config to enable zero-conf and leased channel. + cfg := node.CfgLeased + cfg = append(cfg, node.CfgZeroConf...) + cfgs := [][]string{cfg, cfg, cfg} - cfg := node.CfgLeased - cfgs := [][]string{cfg, cfg, cfg} + runLocalClaimIncomingHTLCLeased(ht, cfgs, params) +} - runLocalClaimIncomingHTLCLeased(st, cfgs, params) - }) - if !success { - return +// testLocalClaimIncomingHTLCLeased tests `runLocalClaimIncomingHTLCLeased` +// with script enforced lease channel. +func testLocalClaimIncomingHTLCLeased(ht *lntest.HarnessTest) { + // Create a three hop network: Alice -> Bob -> Carol, using leased + // channels. + // + // Prepare params. + params := lntest.OpenChannelParams{ + Amt: chanAmt, + CommitmentType: leasedType, } - ht.Run("zero conf", func(t *testing.T) { - st := ht.Subtest(t) + cfg := node.CfgLeased + cfgs := [][]string{cfg, cfg, cfg} - // Create a three hop network: Alice -> Bob -> Carol, using - // zero-conf anchor channels. - // - // Prepare params. - params := lntest.OpenChannelParams{ - Amt: chanAmt, - ZeroConf: true, - CommitmentType: leasedType, - } - - // Prepare Carol's node config to enable zero-conf and leased - // channel. - cfg := node.CfgLeased - cfg = append(cfg, node.CfgZeroConf...) - cfgs := [][]string{cfg, cfg, cfg} - - runLocalClaimIncomingHTLCLeased(st, cfgs, params) - }) + runLocalClaimIncomingHTLCLeased(ht, cfgs, params) } // runLocalClaimIncomingHTLCLeased tests that in a multi-hop HTLC scenario, if @@ -2196,46 +2186,63 @@ func runLocalClaimIncomingHTLCLeased(ht *lntest.HarnessTest, ht.AssertPaymentStatus(alice, preimage, lnrpc.Payment_SUCCEEDED) } +// testLocalPreimageClaimAnchorZeroConf tests `runLocalPreimageClaim` with +// zero-conf anchor channel. +func testLocalPreimageClaimAnchorZeroConf(ht *lntest.HarnessTest) { + // Create a three hop network: Alice -> Bob -> Carol, using zero-conf + // anchor channels. + // + // Prepare params. + params := lntest.OpenChannelParams{ + Amt: chanAmt, + ZeroConf: true, + CommitmentType: lnrpc.CommitmentType_ANCHORS, + } + + // Prepare Carol's node config to enable zero-conf and anchor. + cfg := node.CfgZeroConf + cfgs := [][]string{cfg, cfg, cfg} + + runLocalPreimageClaim(ht, cfgs, params) +} + // testLocalPreimageClaimAnchor tests `runLocalPreimageClaim` with anchor // channel. func testLocalPreimageClaimAnchor(ht *lntest.HarnessTest) { - success := ht.Run("no zero conf", func(t *testing.T) { - st := ht.Subtest(t) - - // Create a three hop network: Alice -> Bob -> Carol, using - // anchor channels. - // - // Prepare params. - params := lntest.OpenChannelParams{Amt: chanAmt} + // Create a three hop network: Alice -> Bob -> Carol, using anchor + // channels. + // + // Prepare params. + params := lntest.OpenChannelParams{Amt: chanAmt} - cfg := node.CfgAnchor - cfgs := [][]string{cfg, cfg, cfg} + cfg := node.CfgAnchor + cfgs := [][]string{cfg, cfg, cfg} - runLocalPreimageClaim(st, cfgs, params) - }) - if !success { - return - } + runLocalPreimageClaim(ht, cfgs, params) +} - ht.Run("zero conf", func(t *testing.T) { - st := ht.Subtest(t) +// testLocalPreimageClaimSimpleTaprootZeroConf tests +// `runLocalClaimIncomingHTLC` with zero-conf simple taproot channel. +func testLocalPreimageClaimSimpleTaprootZeroConf(ht *lntest.HarnessTest) { + c := lnrpc.CommitmentType_SIMPLE_TAPROOT - // Create a three hop network: Alice -> Bob -> Carol, using - // zero-conf anchor channels. - // - // Prepare params. - params := lntest.OpenChannelParams{ - Amt: chanAmt, - ZeroConf: true, - CommitmentType: lnrpc.CommitmentType_ANCHORS, - } + // Create a three hop network: Alice -> Bob -> Carol, using zero-conf + // simple taproot channels. + // + // Prepare params. + params := lntest.OpenChannelParams{ + Amt: chanAmt, + ZeroConf: true, + CommitmentType: c, + Private: true, + } - // Prepare Carol's node config to enable zero-conf and anchor. - cfg := node.CfgZeroConf - cfgs := [][]string{cfg, cfg, cfg} + // Prepare Carol's node config to enable zero-conf and leased channel. + cfg := node.CfgSimpleTaproot + cfg = append(cfg, node.CfgZeroConf...) + cfgs := [][]string{cfg, cfg, cfg} - runLocalPreimageClaim(st, cfgs, params) - }) + runLocalPreimageClaim(ht, cfgs, params) } // testLocalPreimageClaimSimpleTaproot tests `runLocalClaimIncomingHTLC` with @@ -2243,50 +2250,20 @@ func testLocalPreimageClaimAnchor(ht *lntest.HarnessTest) { func testLocalPreimageClaimSimpleTaproot(ht *lntest.HarnessTest) { c := lnrpc.CommitmentType_SIMPLE_TAPROOT - success := ht.Run("no zero conf", func(t *testing.T) { - st := ht.Subtest(t) - - // Create a three hop network: Alice -> Bob -> Carol, using - // simple taproot channels. - // - // Prepare params. - params := lntest.OpenChannelParams{ - Amt: chanAmt, - CommitmentType: c, - Private: true, - } - - cfg := node.CfgSimpleTaproot - cfgs := [][]string{cfg, cfg, cfg} - - runLocalPreimageClaim(st, cfgs, params) - }) - if !success { - return + // Create a three hop network: Alice -> Bob -> Carol, using simple + // taproot channels. + // + // Prepare params. + params := lntest.OpenChannelParams{ + Amt: chanAmt, + CommitmentType: c, + Private: true, } - ht.Run("zero conf", func(t *testing.T) { - st := ht.Subtest(t) - - // Create a three hop network: Alice -> Bob -> Carol, using - // zero-conf simple taproot channels. - // - // Prepare params. - params := lntest.OpenChannelParams{ - Amt: chanAmt, - ZeroConf: true, - CommitmentType: c, - Private: true, - } - - // Prepare Carol's node config to enable zero-conf and leased - // channel. - cfg := node.CfgSimpleTaproot - cfg = append(cfg, node.CfgZeroConf...) - cfgs := [][]string{cfg, cfg, cfg} + cfg := node.CfgSimpleTaproot + cfgs := [][]string{cfg, cfg, cfg} - runLocalPreimageClaim(st, cfgs, params) - }) + runLocalPreimageClaim(ht, cfgs, params) } // runLocalPreimageClaim tests that in the multi-hop HTLC scenario, if the @@ -2564,51 +2541,43 @@ func runLocalPreimageClaim(ht *lntest.HarnessTest, ht.AssertPaymentStatus(alice, preimage, lnrpc.Payment_SUCCEEDED) } -// testLocalPreimageClaimLeased tests `runLocalPreimageClaim` with script -// enforced lease channel. -func testLocalPreimageClaimLeased(ht *lntest.HarnessTest) { - success := ht.Run("no zero conf", func(t *testing.T) { - st := ht.Subtest(t) +// testLocalPreimageClaimLeasedZeroConf tests `runLocalPreimageClaim` with +// zero-conf script enforced lease channel. +func testLocalPreimageClaimLeasedZeroConf(ht *lntest.HarnessTest) { + // Create a three hop network: Alice -> Bob -> Carol, using zero-conf + // anchor channels. + // + // Prepare params. + params := lntest.OpenChannelParams{ + Amt: chanAmt, + ZeroConf: true, + CommitmentType: leasedType, + } - // Create a three hop network: Alice -> Bob -> Carol, using - // leased channels. - // - // Prepare params. - params := lntest.OpenChannelParams{ - Amt: chanAmt, - CommitmentType: leasedType, - } + // Prepare Carol's node config to enable zero-conf and leased channel. + cfg := node.CfgLeased + cfg = append(cfg, node.CfgZeroConf...) + cfgs := [][]string{cfg, cfg, cfg} - cfg := node.CfgLeased - cfgs := [][]string{cfg, cfg, cfg} + runLocalPreimageClaimLeased(ht, cfgs, params) +} - runLocalPreimageClaimLeased(st, cfgs, params) - }) - if !success { - return +// testLocalPreimageClaimLeased tests `runLocalPreimageClaim` with script +// enforced lease channel. +func testLocalPreimageClaimLeased(ht *lntest.HarnessTest) { + // Create a three hop network: Alice -> Bob -> Carol, using leased + // channels. + // + // Prepare params. + params := lntest.OpenChannelParams{ + Amt: chanAmt, + CommitmentType: leasedType, } - ht.Run("zero conf", func(t *testing.T) { - st := ht.Subtest(t) + cfg := node.CfgLeased + cfgs := [][]string{cfg, cfg, cfg} - // Create a three hop network: Alice -> Bob -> Carol, using - // zero-conf anchor channels. - // - // Prepare params. - params := lntest.OpenChannelParams{ - Amt: chanAmt, - ZeroConf: true, - CommitmentType: leasedType, - } - - // Prepare Carol's node config to enable zero-conf and leased - // channel. - cfg := node.CfgLeased - cfg = append(cfg, node.CfgZeroConf...) - cfgs := [][]string{cfg, cfg, cfg} - - runLocalPreimageClaimLeased(st, cfgs, params) - }) + runLocalPreimageClaimLeased(ht, cfgs, params) } // runLocalPreimageClaimLeased tests that in the multi-hop HTLC scenario, if @@ -2862,45 +2831,62 @@ func runLocalPreimageClaimLeased(ht *lntest.HarnessTest, ht.AssertNumPendingForceClose(bob, 0) } +// testHtlcAggregaitonAnchor tests `runHtlcAggregation` with zero-conf anchor +// channel. +func testHtlcAggregaitonAnchorZeroConf(ht *lntest.HarnessTest) { + // Create a three hop network: Alice -> Bob -> Carol, using zero-conf + // anchor channels. + // + // Prepare params. + params := lntest.OpenChannelParams{ + Amt: chanAmt, + ZeroConf: true, + CommitmentType: lnrpc.CommitmentType_ANCHORS, + } + + // Prepare Carol's node config to enable zero-conf and anchor. + cfg := node.CfgZeroConf + cfgs := [][]string{cfg, cfg, cfg} + + runHtlcAggregation(ht, cfgs, params) +} + // testHtlcAggregaitonAnchor tests `runHtlcAggregation` with anchor channel. func testHtlcAggregaitonAnchor(ht *lntest.HarnessTest) { - success := ht.Run("no zero conf", func(t *testing.T) { - st := ht.Subtest(t) - - // Create a three hop network: Alice -> Bob -> Carol, using - // anchor channels. - // - // Prepare params. - params := lntest.OpenChannelParams{Amt: chanAmt} + // Create a three hop network: Alice -> Bob -> Carol, using anchor + // channels. + // + // Prepare params. + params := lntest.OpenChannelParams{Amt: chanAmt} - cfg := node.CfgAnchor - cfgs := [][]string{cfg, cfg, cfg} + cfg := node.CfgAnchor + cfgs := [][]string{cfg, cfg, cfg} - runHtlcAggregation(st, cfgs, params) - }) - if !success { - return - } + runHtlcAggregation(ht, cfgs, params) +} - ht.Run("zero conf", func(t *testing.T) { - st := ht.Subtest(t) +// testHtlcAggregaitonSimpleTaprootZeroConf tests `runHtlcAggregation` with +// zero-conf simple taproot channel. +func testHtlcAggregaitonSimpleTaprootZeroConf(ht *lntest.HarnessTest) { + c := lnrpc.CommitmentType_SIMPLE_TAPROOT - // Create a three hop network: Alice -> Bob -> Carol, using - // zero-conf anchor channels. - // - // Prepare params. - params := lntest.OpenChannelParams{ - Amt: chanAmt, - ZeroConf: true, - CommitmentType: lnrpc.CommitmentType_ANCHORS, - } + // Create a three hop network: Alice -> Bob -> Carol, using zero-conf + // simple taproot channels. + // + // Prepare params. + params := lntest.OpenChannelParams{ + Amt: chanAmt, + ZeroConf: true, + CommitmentType: c, + Private: true, + } - // Prepare Carol's node config to enable zero-conf and anchor. - cfg := node.CfgZeroConf - cfgs := [][]string{cfg, cfg, cfg} + // Prepare Carol's node config to enable zero-conf and leased channel. + cfg := node.CfgSimpleTaproot + cfg = append(cfg, node.CfgZeroConf...) + cfgs := [][]string{cfg, cfg, cfg} - runHtlcAggregation(st, cfgs, params) - }) + runHtlcAggregation(ht, cfgs, params) } // testHtlcAggregaitonSimpleTaproot tests `runHtlcAggregation` with simple @@ -2908,97 +2894,59 @@ func testHtlcAggregaitonAnchor(ht *lntest.HarnessTest) { func testHtlcAggregaitonSimpleTaproot(ht *lntest.HarnessTest) { c := lnrpc.CommitmentType_SIMPLE_TAPROOT - success := ht.Run("no zero conf", func(t *testing.T) { - st := ht.Subtest(t) + // Create a three hop network: Alice -> Bob -> Carol, using simple + // taproot channels. + // + // Prepare params. + params := lntest.OpenChannelParams{ + Amt: chanAmt, + CommitmentType: c, + Private: true, + } - // Create a three hop network: Alice -> Bob -> Carol, using - // simple taproot channels. - // - // Prepare params. - params := lntest.OpenChannelParams{ - Amt: chanAmt, - CommitmentType: c, - Private: true, - } + cfg := node.CfgSimpleTaproot + cfgs := [][]string{cfg, cfg, cfg} - cfg := node.CfgSimpleTaproot - cfgs := [][]string{cfg, cfg, cfg} + runHtlcAggregation(ht, cfgs, params) +} - runHtlcAggregation(st, cfgs, params) - }) - if !success { - return +// testHtlcAggregaitonLeasedZeroConf tests `runHtlcAggregation` with zero-conf +// script enforced lease channel. +func testHtlcAggregaitonLeasedZeroConf(ht *lntest.HarnessTest) { + // Create a three hop network: Alice -> Bob -> Carol, using zero-conf + // anchor channels. + // + // Prepare params. + params := lntest.OpenChannelParams{ + Amt: chanAmt, + ZeroConf: true, + CommitmentType: leasedType, } - ht.Run("zero conf", func(t *testing.T) { - st := ht.Subtest(t) - - // Create a three hop network: Alice -> Bob -> Carol, using - // zero-conf simple taproot channels. - // - // Prepare params. - params := lntest.OpenChannelParams{ - Amt: chanAmt, - ZeroConf: true, - CommitmentType: c, - Private: true, - } - - // Prepare Carol's node config to enable zero-conf and leased - // channel. - cfg := node.CfgSimpleTaproot - cfg = append(cfg, node.CfgZeroConf...) - cfgs := [][]string{cfg, cfg, cfg} + // Prepare Carol's node config to enable zero-conf and leased channel. + cfg := node.CfgLeased + cfg = append(cfg, node.CfgZeroConf...) + cfgs := [][]string{cfg, cfg, cfg} - runHtlcAggregation(st, cfgs, params) - }) + runHtlcAggregation(ht, cfgs, params) } // testHtlcAggregaitonLeased tests `runHtlcAggregation` with script enforced // lease channel. func testHtlcAggregaitonLeased(ht *lntest.HarnessTest) { - success := ht.Run("no zero conf", func(t *testing.T) { - st := ht.Subtest(t) - - // Create a three hop network: Alice -> Bob -> Carol, using - // leased channels. - // - // Prepare params. - params := lntest.OpenChannelParams{ - Amt: chanAmt, - CommitmentType: leasedType, - } - - cfg := node.CfgLeased - cfgs := [][]string{cfg, cfg, cfg} - - runHtlcAggregation(st, cfgs, params) - }) - if !success { - return + // Create a three hop network: Alice -> Bob -> Carol, using leased + // channels. + // + // Prepare params. + params := lntest.OpenChannelParams{ + Amt: chanAmt, + CommitmentType: leasedType, } - ht.Run("zero conf", func(t *testing.T) { - st := ht.Subtest(t) - - // Create a three hop network: Alice -> Bob -> Carol, using - // zero-conf anchor channels. - // - // Prepare params. - params := lntest.OpenChannelParams{ - Amt: chanAmt, - ZeroConf: true, - CommitmentType: leasedType, - } - - // Prepare Carol's node config to enable zero-conf and leased - // channel. - cfg := node.CfgLeased - cfg = append(cfg, node.CfgZeroConf...) - cfgs := [][]string{cfg, cfg, cfg} + cfg := node.CfgLeased + cfgs := [][]string{cfg, cfg, cfg} - runHtlcAggregation(st, cfgs, params) - }) + runHtlcAggregation(ht, cfgs, params) } // runHtlcAggregation tests that in a multi-hop HTLC scenario, if we force From 428829a1c0a1ad27fd8b896d5b3b65fe944e2daf Mon Sep 17 00:00:00 2001 From: yyforyongyu Date: Sat, 9 Nov 2024 00:37:50 +0800 Subject: [PATCH 06/32] itest: break down scid alias channel update tests --- itest/list_on_test.go | 5 +- itest/lnd_zero_conf_test.go | 116 +++++++++++++++++++----------------- 2 files changed, 62 insertions(+), 59 deletions(-) diff --git a/itest/list_on_test.go b/itest/list_on_test.go index 218471e027..aaa21fe2fa 100644 --- a/itest/list_on_test.go +++ b/itest/list_on_test.go @@ -448,10 +448,6 @@ var allTestCases = []*lntest.TestCase{ Name: "option scid alias", TestFunc: testOptionScidAlias, }, - { - Name: "scid alias channel update", - TestFunc: testUpdateChannelPolicyScidAlias, - }, { Name: "scid alias upgrade", TestFunc: testOptionScidUpgrade, @@ -686,4 +682,5 @@ func init() { allTestCases = append(allTestCases, remoteSignerTestCases...) allTestCases = append(allTestCases, channelRestoreTestCases...) allTestCases = append(allTestCases, fundUtxoSelectionTestCases...) + allTestCases = append(allTestCases, zeroConfPolicyTestCases...) } diff --git a/itest/lnd_zero_conf_test.go b/itest/lnd_zero_conf_test.go index c7f4c59a78..5b87846be5 100644 --- a/itest/lnd_zero_conf_test.go +++ b/itest/lnd_zero_conf_test.go @@ -19,6 +19,67 @@ import ( "github.com/stretchr/testify/require" ) +// zeroConfPolicyTestCases checks that option-scid-alias, zero-conf +// channel-types, and option-scid-alias feature-bit-only channels have the +// expected graph and that payments work when updating the channel policy. +var zeroConfPolicyTestCases = []*lntest.TestCase{ + { + Name: "channel policy update private", + TestFunc: func(ht *lntest.HarnessTest) { + // zeroConf: false + // scidAlias: false + // private: true + testPrivateUpdateAlias( + ht, false, false, true, + ) + }, + }, + { + Name: "channel policy update private scid alias", + TestFunc: func(ht *lntest.HarnessTest) { + // zeroConf: false + // scidAlias: true + // private: true + testPrivateUpdateAlias( + ht, false, true, true, + ) + }, + }, + { + Name: "channel policy update private zero conf", + TestFunc: func(ht *lntest.HarnessTest) { + // zeroConf: true + // scidAlias: false + // private: true + testPrivateUpdateAlias( + ht, true, false, true, + ) + }, + }, + { + Name: "channel policy update public zero conf", + TestFunc: func(ht *lntest.HarnessTest) { + // zeroConf: true + // scidAlias: false + // private: false + testPrivateUpdateAlias( + ht, true, false, false, + ) + }, + }, + { + Name: "channel policy update public", + TestFunc: func(ht *lntest.HarnessTest) { + // zeroConf: false + // scidAlias: false + // private: false + testPrivateUpdateAlias( + ht, false, false, false, + ) + }, + }, +} + // testZeroConfChannelOpen tests that opening a zero-conf channel works and // sending payments also works. func testZeroConfChannelOpen(ht *lntest.HarnessTest) { @@ -395,61 +456,6 @@ func waitForZeroConfGraphChange(hn *node.HarnessNode, }, defaultTimeout) } -// testUpdateChannelPolicyScidAlias checks that option-scid-alias, zero-conf -// channel-types, and option-scid-alias feature-bit-only channels have the -// expected graph and that payments work when updating the channel policy. -func testUpdateChannelPolicyScidAlias(ht *lntest.HarnessTest) { - tests := []struct { - name string - - // The option-scid-alias channel type. - scidAliasType bool - - // The zero-conf channel type. - zeroConf bool - - private bool - }{ - { - name: "private scid-alias chantype update", - scidAliasType: true, - private: true, - }, - { - name: "private zero-conf update", - zeroConf: true, - private: true, - }, - { - name: "public zero-conf update", - zeroConf: true, - }, - { - name: "public no-chan-type update", - }, - { - name: "private no-chan-type update", - private: true, - }, - } - - for _, test := range tests { - test := test - - success := ht.Run(test.name, func(t *testing.T) { - st := ht.Subtest(t) - - testPrivateUpdateAlias( - st, test.zeroConf, test.scidAliasType, - test.private, - ) - }) - if !success { - return - } - } -} - func testPrivateUpdateAlias(ht *lntest.HarnessTest, zeroConf, scidAliasType, private bool) { From c8e6d74547d63c1d29c0d8f8c17f052104f1255a Mon Sep 17 00:00:00 2001 From: yyforyongyu Date: Sat, 9 Nov 2024 00:55:54 +0800 Subject: [PATCH 07/32] itest: break down open channel fee policy --- itest/list_on_test.go | 5 +- itest/lnd_open_channel_test.go | 407 +++++++++++++++++++++------------ 2 files changed, 268 insertions(+), 144 deletions(-) diff --git a/itest/list_on_test.go b/itest/list_on_test.go index aaa21fe2fa..0f6fa41ffc 100644 --- a/itest/list_on_test.go +++ b/itest/list_on_test.go @@ -512,10 +512,6 @@ var allTestCases = []*lntest.TestCase{ Name: "trackpayments compatible", TestFunc: testTrackPaymentsCompatible, }, - { - Name: "open channel fee policy", - TestFunc: testOpenChannelUpdateFeePolicy, - }, { Name: "custom message", TestFunc: testCustomMessage, @@ -683,4 +679,5 @@ func init() { allTestCases = append(allTestCases, channelRestoreTestCases...) allTestCases = append(allTestCases, fundUtxoSelectionTestCases...) allTestCases = append(allTestCases, zeroConfPolicyTestCases...) + allTestCases = append(allTestCases, channelFeePolicyTestCases...) } diff --git a/itest/lnd_open_channel_test.go b/itest/lnd_open_channel_test.go index 3142e09eb2..9d51fc5635 100644 --- a/itest/lnd_open_channel_test.go +++ b/itest/lnd_open_channel_test.go @@ -3,7 +3,6 @@ package itest import ( "fmt" "strings" - "testing" "github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/chaincfg/chainhash" @@ -18,6 +17,31 @@ import ( "github.com/stretchr/testify/require" ) +// channelFeePolicyTestCases defines a set of tests to check the update channel +// policy fee behavior. +var channelFeePolicyTestCases = []*lntest.TestCase{ + { + Name: "channel fee policy default", + TestFunc: testChannelFeePolicyDefault, + }, + { + Name: "channel fee policy base fee", + TestFunc: testChannelFeePolicyBaseFee, + }, + { + Name: "channel fee policy fee rate", + TestFunc: testChannelFeePolicyFeeRate, + }, + { + Name: "channel fee policy base fee and fee rate", + TestFunc: testChannelFeePolicyBaseFeeAndFeeRate, + }, + { + Name: "channel fee policy low base fee and fee rate", + TestFunc: testChannelFeePolicyLowBaseFeeAndFeeRate, + }, +} + // testOpenChannelAfterReorg tests that in the case where we have an open // channel where the funding tx gets reorged out, the channel will no // longer be present in the node's routing table. @@ -123,32 +147,103 @@ func testOpenChannelAfterReorg(ht *lntest.HarnessTest) { ht.AssertTxInBlock(block, *fundingTxID) } -// testOpenChannelFeePolicy checks if different channel fee scenarios are -// correctly handled when the optional channel fee parameters baseFee and -// feeRate are provided. If the OpenChannelRequest is not provided with a value -// for baseFee/feeRate the expectation is that the default baseFee/feeRate is -// applied. -// -// 1. No params provided to OpenChannelRequest: -// ChannelUpdate --> defaultBaseFee, defaultFeeRate -// 2. Only baseFee provided to OpenChannelRequest: -// ChannelUpdate --> provided baseFee, defaultFeeRate -// 3. Only feeRate provided to OpenChannelRequest: -// ChannelUpdate --> defaultBaseFee, provided FeeRate -// 4. baseFee and feeRate provided to OpenChannelRequest: -// ChannelUpdate --> provided baseFee, provided feeRate -// 5. Both baseFee and feeRate are set to a value lower than the default: -// ChannelUpdate --> provided baseFee, provided feeRate -func testOpenChannelUpdateFeePolicy(ht *lntest.HarnessTest) { +// testChannelFeePolicyDefault check when no params provided to +// OpenChannelRequest: ChannelUpdate --> defaultBaseFee, defaultFeeRate. +func testChannelFeePolicyDefault(ht *lntest.HarnessTest) { + const ( + defaultBaseFee = 1000 + defaultFeeRate = 1 + defaultTimeLockDelta = chainreg.DefaultBitcoinTimeLockDelta + defaultMinHtlc = 1000 + ) + + defaultMaxHtlc := lntest.CalculateMaxHtlc(funding.MaxBtcFundingAmount) + + chanAmt := funding.MaxBtcFundingAmount + pushAmt := chanAmt / 2 + + feeScenario := lntest.OpenChannelParams{ + Amt: chanAmt, + PushAmt: pushAmt, + UseBaseFee: false, + UseFeeRate: false, + } + + expectedPolicy := lnrpc.RoutingPolicy{ + FeeBaseMsat: defaultBaseFee, + FeeRateMilliMsat: defaultFeeRate, + TimeLockDelta: defaultTimeLockDelta, + MinHtlc: defaultMinHtlc, + MaxHtlcMsat: defaultMaxHtlc, + } + + bobExpectedPolicy := lnrpc.RoutingPolicy{ + FeeBaseMsat: defaultBaseFee, + FeeRateMilliMsat: defaultFeeRate, + TimeLockDelta: defaultTimeLockDelta, + MinHtlc: defaultMinHtlc, + MaxHtlcMsat: defaultMaxHtlc, + } + + runChannelFeePolicyTest( + ht, feeScenario, &expectedPolicy, &bobExpectedPolicy, + ) +} + +// testChannelFeePolicyBaseFee checks only baseFee provided to +// OpenChannelRequest: ChannelUpdate --> provided baseFee, defaultFeeRate. +func testChannelFeePolicyBaseFee(ht *lntest.HarnessTest) { const ( defaultBaseFee = 1000 defaultFeeRate = 1 defaultTimeLockDelta = chainreg.DefaultBitcoinTimeLockDelta defaultMinHtlc = 1000 optionalBaseFee = 1337 + ) + + defaultMaxHtlc := lntest.CalculateMaxHtlc(funding.MaxBtcFundingAmount) + + chanAmt := funding.MaxBtcFundingAmount + pushAmt := chanAmt / 2 + + feeScenario := lntest.OpenChannelParams{ + Amt: chanAmt, + PushAmt: pushAmt, + BaseFee: optionalBaseFee, + UseBaseFee: true, + UseFeeRate: false, + } + + expectedPolicy := lnrpc.RoutingPolicy{ + FeeBaseMsat: optionalBaseFee, + FeeRateMilliMsat: defaultFeeRate, + TimeLockDelta: defaultTimeLockDelta, + MinHtlc: defaultMinHtlc, + MaxHtlcMsat: defaultMaxHtlc, + } + + bobExpectedPolicy := lnrpc.RoutingPolicy{ + FeeBaseMsat: defaultBaseFee, + FeeRateMilliMsat: defaultFeeRate, + TimeLockDelta: defaultTimeLockDelta, + MinHtlc: defaultMinHtlc, + MaxHtlcMsat: defaultMaxHtlc, + } + + runChannelFeePolicyTest( + ht, feeScenario, &expectedPolicy, &bobExpectedPolicy, + ) +} + +// testChannelFeePolicyFeeRate checks if only feeRate provided to +// OpenChannelRequest: ChannelUpdate --> defaultBaseFee, provided FeeRate. +func testChannelFeePolicyFeeRate(ht *lntest.HarnessTest) { + const ( + defaultBaseFee = 1000 + defaultFeeRate = 1 + defaultTimeLockDelta = chainreg.DefaultBitcoinTimeLockDelta + defaultMinHtlc = 1000 optionalFeeRate = 1337 - lowBaseFee = 0 - lowFeeRate = 900 ) defaultMaxHtlc := lntest.CalculateMaxHtlc(funding.MaxBtcFundingAmount) @@ -156,81 +251,20 @@ func testOpenChannelUpdateFeePolicy(ht *lntest.HarnessTest) { chanAmt := funding.MaxBtcFundingAmount pushAmt := chanAmt / 2 - feeScenarios := []lntest.OpenChannelParams{ - { - Amt: chanAmt, - PushAmt: pushAmt, - UseBaseFee: false, - UseFeeRate: false, - }, - { - Amt: chanAmt, - PushAmt: pushAmt, - BaseFee: optionalBaseFee, - UseBaseFee: true, - UseFeeRate: false, - }, - { - Amt: chanAmt, - PushAmt: pushAmt, - FeeRate: optionalFeeRate, - UseBaseFee: false, - UseFeeRate: true, - }, - { - Amt: chanAmt, - PushAmt: pushAmt, - BaseFee: optionalBaseFee, - FeeRate: optionalFeeRate, - UseBaseFee: true, - UseFeeRate: true, - }, - { - Amt: chanAmt, - PushAmt: pushAmt, - BaseFee: lowBaseFee, - FeeRate: lowFeeRate, - UseBaseFee: true, - UseFeeRate: true, - }, + feeScenario := lntest.OpenChannelParams{ + Amt: chanAmt, + PushAmt: pushAmt, + FeeRate: optionalFeeRate, + UseBaseFee: false, + UseFeeRate: true, } - expectedPolicies := []lnrpc.RoutingPolicy{ - { - FeeBaseMsat: defaultBaseFee, - FeeRateMilliMsat: defaultFeeRate, - TimeLockDelta: defaultTimeLockDelta, - MinHtlc: defaultMinHtlc, - MaxHtlcMsat: defaultMaxHtlc, - }, - { - FeeBaseMsat: optionalBaseFee, - FeeRateMilliMsat: defaultFeeRate, - TimeLockDelta: defaultTimeLockDelta, - MinHtlc: defaultMinHtlc, - MaxHtlcMsat: defaultMaxHtlc, - }, - { - FeeBaseMsat: defaultBaseFee, - FeeRateMilliMsat: optionalFeeRate, - TimeLockDelta: defaultTimeLockDelta, - MinHtlc: defaultMinHtlc, - MaxHtlcMsat: defaultMaxHtlc, - }, - { - FeeBaseMsat: optionalBaseFee, - FeeRateMilliMsat: optionalFeeRate, - TimeLockDelta: defaultTimeLockDelta, - MinHtlc: defaultMinHtlc, - MaxHtlcMsat: defaultMaxHtlc, - }, - { - FeeBaseMsat: lowBaseFee, - FeeRateMilliMsat: lowFeeRate, - TimeLockDelta: defaultTimeLockDelta, - MinHtlc: defaultMinHtlc, - MaxHtlcMsat: defaultMaxHtlc, - }, + expectedPolicy := lnrpc.RoutingPolicy{ + FeeBaseMsat: defaultBaseFee, + FeeRateMilliMsat: optionalFeeRate, + TimeLockDelta: defaultTimeLockDelta, + MinHtlc: defaultMinHtlc, + MaxHtlcMsat: defaultMaxHtlc, } bobExpectedPolicy := lnrpc.RoutingPolicy{ @@ -241,65 +275,158 @@ func testOpenChannelUpdateFeePolicy(ht *lntest.HarnessTest) { MaxHtlcMsat: defaultMaxHtlc, } - runTestCase := func(ht *lntest.HarnessTest, - chanParams lntest.OpenChannelParams, - alicePolicy, bobPolicy *lnrpc.RoutingPolicy) { + runChannelFeePolicyTest( + ht, feeScenario, &expectedPolicy, &bobExpectedPolicy, + ) +} - // In this basic test, we'll need a third node, Carol, so we - // can forward a payment through the channel we'll open with - // the different fee policies. - alice := ht.NewNodeWithCoins("Alice", nil) - bob := ht.NewNode("Bob", nil) - carol := ht.NewNodeWithCoins("Carol", nil) +// testChannelFeePolicyBaseFeeAndFeeRate checks if baseFee and feeRate provided +// to OpenChannelRequest: ChannelUpdate --> provided baseFee, provided feeRate. +func testChannelFeePolicyBaseFeeAndFeeRate(ht *lntest.HarnessTest) { + const ( + defaultBaseFee = 1000 + defaultFeeRate = 1 + defaultTimeLockDelta = chainreg.DefaultBitcoinTimeLockDelta + defaultMinHtlc = 1000 + optionalBaseFee = 1337 + optionalFeeRate = 1337 + ) - ht.EnsureConnected(alice, bob) - ht.EnsureConnected(alice, carol) + defaultMaxHtlc := lntest.CalculateMaxHtlc(funding.MaxBtcFundingAmount) - nodes := []*node.HarnessNode{alice, bob, carol} + chanAmt := funding.MaxBtcFundingAmount + pushAmt := chanAmt / 2 - // Create a channel Alice->Bob. - chanPoint := ht.OpenChannel(alice, bob, chanParams) + feeScenario := lntest.OpenChannelParams{ + Amt: chanAmt, + PushAmt: pushAmt, + BaseFee: optionalBaseFee, + FeeRate: optionalFeeRate, + UseBaseFee: true, + UseFeeRate: true, + } - // Create a channel Carol->Alice. - ht.OpenChannel( - carol, alice, lntest.OpenChannelParams{ - Amt: 500000, - }, - ) + expectedPolicy := lnrpc.RoutingPolicy{ + FeeBaseMsat: optionalBaseFee, + FeeRateMilliMsat: optionalFeeRate, + TimeLockDelta: defaultTimeLockDelta, + MinHtlc: defaultMinHtlc, + MaxHtlcMsat: defaultMaxHtlc, + } - // Alice and Bob should see each other's ChannelUpdates, - // advertising the preferred routing policies. - assertNodesPolicyUpdate( - ht, nodes, alice, alicePolicy, chanPoint, - ) - assertNodesPolicyUpdate(ht, nodes, bob, bobPolicy, chanPoint) + bobExpectedPolicy := lnrpc.RoutingPolicy{ + FeeBaseMsat: defaultBaseFee, + FeeRateMilliMsat: defaultFeeRate, + TimeLockDelta: defaultTimeLockDelta, + MinHtlc: defaultMinHtlc, + MaxHtlcMsat: defaultMaxHtlc, + } - // They should now know about the default policies. - for _, n := range nodes { - ht.AssertChannelPolicy( - n, alice.PubKeyStr, alicePolicy, chanPoint, - ) - ht.AssertChannelPolicy( - n, bob.PubKeyStr, bobPolicy, chanPoint, - ) - } + runChannelFeePolicyTest( + ht, feeScenario, &expectedPolicy, &bobExpectedPolicy, + ) +} + +// testChannelFeePolicyLowBaseFeeAndFeeRate checks if both baseFee and feeRate +// are set to a value lower than the default: ChannelUpdate --> provided +// baseFee, provided feeRate. +func testChannelFeePolicyLowBaseFeeAndFeeRate(ht *lntest.HarnessTest) { + const ( + defaultBaseFee = 1000 + defaultFeeRate = 1 + defaultTimeLockDelta = chainreg.DefaultBitcoinTimeLockDelta + defaultMinHtlc = 1000 + lowBaseFee = 0 + lowFeeRate = 900 + ) - // We should be able to forward a payment from Carol to Bob - // through the new channel we opened. - payReqs, _, _ := ht.CreatePayReqs(bob, paymentAmt, 1) - ht.CompletePaymentRequests(carol, payReqs) + defaultMaxHtlc := lntest.CalculateMaxHtlc(funding.MaxBtcFundingAmount) + + chanAmt := funding.MaxBtcFundingAmount + pushAmt := chanAmt / 2 + + feeScenario := lntest.OpenChannelParams{ + Amt: chanAmt, + PushAmt: pushAmt, + BaseFee: lowBaseFee, + FeeRate: lowFeeRate, + UseBaseFee: true, + UseFeeRate: true, } - for i, feeScenario := range feeScenarios { - ht.Run(fmt.Sprintf("%d", i), func(t *testing.T) { - st := ht.Subtest(t) + expectedPolicy := lnrpc.RoutingPolicy{ + FeeBaseMsat: lowBaseFee, + FeeRateMilliMsat: lowFeeRate, + TimeLockDelta: defaultTimeLockDelta, + MinHtlc: defaultMinHtlc, + MaxHtlcMsat: defaultMaxHtlc, + } - runTestCase( - st, feeScenario, - &expectedPolicies[i], &bobExpectedPolicy, - ) - }) + bobExpectedPolicy := lnrpc.RoutingPolicy{ + FeeBaseMsat: defaultBaseFee, + FeeRateMilliMsat: defaultFeeRate, + TimeLockDelta: defaultTimeLockDelta, + MinHtlc: defaultMinHtlc, + MaxHtlcMsat: defaultMaxHtlc, } + + runChannelFeePolicyTest( + ht, feeScenario, &expectedPolicy, &bobExpectedPolicy, + ) +} + +// runChannelFeePolicyTest checks if different channel fee scenarios are +// correctly handled when the optional channel fee parameters baseFee and +// feeRate are provided. If the OpenChannelRequest is not provided with a value +// for baseFee/feeRate the expectation is that the default baseFee/feeRate is +// applied. +func runChannelFeePolicyTest(ht *lntest.HarnessTest, + chanParams lntest.OpenChannelParams, + alicePolicy, bobPolicy *lnrpc.RoutingPolicy) { + + // In this basic test, we'll need a third node, Carol, so we can + // forward a payment through the channel we'll open with the different + // fee policies. + alice := ht.NewNodeWithCoins("Alice", nil) + bob := ht.NewNode("Bob", nil) + carol := ht.NewNodeWithCoins("Carol", nil) + + ht.EnsureConnected(alice, bob) + ht.EnsureConnected(alice, carol) + + nodes := []*node.HarnessNode{alice, bob, carol} + + // Create a channel Alice->Bob. + chanPoint := ht.OpenChannel(alice, bob, chanParams) + + // Create a channel Carol->Alice. + ht.OpenChannel( + carol, alice, lntest.OpenChannelParams{ + Amt: 500000, + }, + ) + + // Alice and Bob should see each other's ChannelUpdates, advertising + // the preferred routing policies. + assertNodesPolicyUpdate( + ht, nodes, alice, alicePolicy, chanPoint, + ) + assertNodesPolicyUpdate(ht, nodes, bob, bobPolicy, chanPoint) + + // They should now know about the default policies. + for _, n := range nodes { + ht.AssertChannelPolicy( + n, alice.PubKeyStr, alicePolicy, chanPoint, + ) + ht.AssertChannelPolicy( + n, bob.PubKeyStr, bobPolicy, chanPoint, + ) + } + + // We should be able to forward a payment from Carol to Bob + // through the new channel we opened. + payReqs, _, _ := ht.CreatePayReqs(bob, paymentAmt, 1) + ht.CompletePaymentRequests(carol, payReqs) } // testBasicChannelCreationAndUpdates tests multiple channel opening and From 41ae04c12068d3d6d5058ea1ac48b269378bdf3a Mon Sep 17 00:00:00 2001 From: yyforyongyu Date: Sat, 9 Nov 2024 00:59:15 +0800 Subject: [PATCH 08/32] itest: break down payment failed tests --- itest/list_on_test.go | 8 +++++++ itest/lnd_payment_test.go | 50 +++++++++++++++++++-------------------- 2 files changed, 32 insertions(+), 26 deletions(-) diff --git a/itest/list_on_test.go b/itest/list_on_test.go index 0f6fa41ffc..040fea1b69 100644 --- a/itest/list_on_test.go +++ b/itest/list_on_test.go @@ -648,6 +648,10 @@ var allTestCases = []*lntest.TestCase{ Name: "payment failed htlc local swept", TestFunc: testPaymentFailedHTLCLocalSwept, }, + { + Name: "payment failed htlc local swept resumed", + TestFunc: testPaymentFailedHTLCLocalSweptResumed, + }, { Name: "payment succeeded htlc remote swept", TestFunc: testPaymentSucceededHTLCRemoteSwept, @@ -656,6 +660,10 @@ var allTestCases = []*lntest.TestCase{ Name: "send to route failed htlc timeout", TestFunc: testSendToRouteFailHTLCTimeout, }, + { + Name: "send to route failed htlc timeout resumed", + TestFunc: testSendToRouteFailHTLCTimeoutResumed, + }, { Name: "debuglevel show", TestFunc: testDebuglevelShow, diff --git a/itest/lnd_payment_test.go b/itest/lnd_payment_test.go index 8f9b2bc748..72a2c16030 100644 --- a/itest/lnd_payment_test.go +++ b/itest/lnd_payment_test.go @@ -179,21 +179,19 @@ func testPaymentSucceededHTLCRemoteSwept(ht *lntest.HarnessTest) { // out and claimed onchain via the timeout path, the payment will be marked as // failed. This test creates a topology from Alice -> Bob, and let Alice send // payments to Bob. Bob then goes offline, such that Alice's outgoing HTLC will -// time out. Alice will also be restarted to make sure resumed payments are -// also marked as failed. +// time out. func testPaymentFailedHTLCLocalSwept(ht *lntest.HarnessTest) { - success := ht.Run("fail payment", func(t *testing.T) { - st := ht.Subtest(t) - runTestPaymentHTLCTimeout(st, false) - }) - if !success { - return - } + runTestPaymentHTLCTimeout(ht, false) +} - ht.Run("fail resumed payment", func(t *testing.T) { - st := ht.Subtest(t) - runTestPaymentHTLCTimeout(st, true) - }) +// testPaymentFailedHTLCLocalSweptResumed checks that when an outgoing HTLC is +// timed out and claimed onchain via the timeout path, the payment will be +// marked as failed. This test creates a topology from Alice -> Bob, and let +// Alice send payments to Bob. Bob then goes offline, such that Alice's +// outgoing HTLC will time out. Alice will be restarted to make sure resumed +// payments are also marked as failed. +func testPaymentFailedHTLCLocalSweptResumed(ht *lntest.HarnessTest) { + runTestPaymentHTLCTimeout(ht, true) } // runTestPaymentHTLCTimeout is the helper function that actually runs the @@ -1181,21 +1179,21 @@ func sendPaymentInterceptAndCancel(ht *lntest.HarnessTest, // out and claimed onchain via the timeout path, the payment will be marked as // failed. This test creates a topology from Alice -> Bob, and let Alice send // payments to Bob. Bob then goes offline, such that Alice's outgoing HTLC will -// time out. Alice will also be restarted to make sure resumed payments are -// also marked as failed. +// time out. func testSendToRouteFailHTLCTimeout(ht *lntest.HarnessTest) { - success := ht.Run("fail payment", func(t *testing.T) { - st := ht.Subtest(t) - runSendToRouteFailHTLCTimeout(st, false) - }) - if !success { - return - } + runSendToRouteFailHTLCTimeout(ht, false) +} - ht.Run("fail resumed payment", func(t *testing.T) { - st := ht.Subtest(t) - runTestPaymentHTLCTimeout(st, true) - }) +// testSendToRouteFailHTLCTimeout is similar to +// testPaymentFailedHTLCLocalSwept. The only difference is the `SendPayment` is +// replaced with `SendToRouteV2`. It checks that when an outgoing HTLC is timed +// out and claimed onchain via the timeout path, the payment will be marked as +// failed. This test creates a topology from Alice -> Bob, and let Alice send +// payments to Bob. Bob then goes offline, such that Alice's outgoing HTLC will +// time out. Alice will be restarted to make sure resumed payments are also +// marked as failed. +func testSendToRouteFailHTLCTimeoutResumed(ht *lntest.HarnessTest) { + runTestPaymentHTLCTimeout(ht, true) } // runSendToRouteFailHTLCTimeout is the helper function that actually runs the From b69e214df2ade1d387ccc324e588eb13f1c68082 Mon Sep 17 00:00:00 2001 From: yyforyongyu Date: Sat, 9 Nov 2024 01:12:49 +0800 Subject: [PATCH 09/32] itest: break down channel backup restore tests --- itest/list_on_test.go | 4 - itest/lnd_channel_backup_test.go | 351 +++++++++++++++---------------- 2 files changed, 168 insertions(+), 187 deletions(-) diff --git a/itest/list_on_test.go b/itest/list_on_test.go index 040fea1b69..6ded67143b 100644 --- a/itest/list_on_test.go +++ b/itest/list_on_test.go @@ -19,10 +19,6 @@ var allTestCases = []*lntest.TestCase{ Name: "external channel funding", TestFunc: testExternalFundingChanPoint, }, - { - Name: "channel backup restore basic", - TestFunc: testChannelBackupRestoreBasic, - }, { Name: "channel backup restore unconfirmed", TestFunc: testChannelBackupRestoreUnconfirmed, diff --git a/itest/lnd_channel_backup_test.go b/itest/lnd_channel_backup_test.go index 5925c0a1ec..5dc5a1729d 100644 --- a/itest/lnd_channel_backup_test.go +++ b/itest/lnd_channel_backup_test.go @@ -85,6 +85,26 @@ var channelRestoreTestCases = []*lntest.TestCase{ ) }, }, + { + Name: "channel backup restore from rpc", + TestFunc: testChannelBackupRestoreFromRPC, + }, + { + Name: "channel backup restore from file", + TestFunc: testChannelBackupRestoreFromFile, + }, + { + Name: "channel backup restore during creation", + TestFunc: testChannelBackupRestoreDuringCreation, + }, + { + Name: "channel backup restore during unlock", + TestFunc: testChannelBackupRestoreDuringUnlock, + }, + { + Name: "channel backup restore twice", + TestFunc: testChannelBackupRestoreTwice, + }, } type ( @@ -298,202 +318,167 @@ func (c *chanRestoreScenario) testScenario(ht *lntest.HarnessTest, ) } -// testChannelBackupRestore tests that we're able to recover from, and initiate -// the DLP protocol via: the RPC restore command, restoring on unlock, and -// restoring from initial wallet creation. We'll also alternate between -// restoring form the on disk file, and restoring from the exported RPC command -// as well. -func testChannelBackupRestoreBasic(ht *lntest.HarnessTest) { - var testCases = []struct { - name string - restoreMethod restoreMethodType - }{ - // Restore from backups obtained via the RPC interface. Dave - // was the initiator, of the non-advertised channel. - { - name: "restore from RPC backup", - restoreMethod: func(st *lntest.HarnessTest, - oldNode *node.HarnessNode, - backupFilePath string, - password []byte, - mnemonic []string) nodeRestorer { - - // For this restoration method, we'll grab the - // current multi-channel backup from the old - // node, and use it to restore a new node - // within the closure. - chanBackup := oldNode.RPC.ExportAllChanBackups() - - multi := chanBackup.MultiChanBackup. - MultiChanBackup - - // In our nodeRestorer function, we'll restore - // the node from seed, then manually recover - // the channel backup. - return chanRestoreViaRPC( - st, password, mnemonic, multi, - ) - }, - }, +// testChannelBackupRestoreFromRPC tests that we're able to recover from, and +// initiate the DLP protocol via the RPC restore command. +func testChannelBackupRestoreFromRPC(ht *lntest.HarnessTest) { + // Restore from backups obtained via the RPC interface. Dave was the + // initiator, of the non-advertised channel. + restoreMethod := func(st *lntest.HarnessTest, oldNode *node.HarnessNode, + backupFilePath string, password []byte, + mnemonic []string) nodeRestorer { - // Restore the backup from the on-disk file, using the RPC - // interface. - { - name: "restore from backup file", - restoreMethod: func(st *lntest.HarnessTest, - oldNode *node.HarnessNode, - backupFilePath string, - password []byte, - mnemonic []string) nodeRestorer { - - // Read the entire Multi backup stored within - // this node's channel.backup file. - multi, err := os.ReadFile(backupFilePath) - require.NoError(st, err) - - // Now that we have Dave's backup file, we'll - // create a new nodeRestorer that will restore - // using the on-disk channel.backup. - return chanRestoreViaRPC( - st, password, mnemonic, multi, - ) - }, - }, + // For this restoration method, we'll grab the current + // multi-channel backup from the old node, and use it to + // restore a new node within the closure. + chanBackup := oldNode.RPC.ExportAllChanBackups() - // Restore the backup as part of node initialization with the - // prior mnemonic and new backup seed. - { - name: "restore during creation", - restoreMethod: func(st *lntest.HarnessTest, - oldNode *node.HarnessNode, - backupFilePath string, - password []byte, - mnemonic []string) nodeRestorer { - - // First, fetch the current backup state as is, - // to obtain our latest Multi. - chanBackup := oldNode.RPC.ExportAllChanBackups() - backupSnapshot := &lnrpc.ChanBackupSnapshot{ - MultiChanBackup: chanBackup. - MultiChanBackup, - } + multi := chanBackup.MultiChanBackup. + MultiChanBackup - // Create a new nodeRestorer that will restore - // the node using the Multi backup we just - // obtained above. - return func() *node.HarnessNode { - return st.RestoreNodeWithSeed( - "dave", nil, password, mnemonic, - "", revocationWindow, - backupSnapshot, - ) - } - }, - }, + // In our nodeRestorer function, we'll restore the node from + // seed, then manually recover the channel backup. + return chanRestoreViaRPC( + st, password, mnemonic, multi, + ) + } - // Restore the backup once the node has already been - // re-created, using the Unlock call. - { - name: "restore during unlock", - restoreMethod: func(st *lntest.HarnessTest, - oldNode *node.HarnessNode, - backupFilePath string, - password []byte, - mnemonic []string) nodeRestorer { - - // First, fetch the current backup state as is, - // to obtain our latest Multi. - chanBackup := oldNode.RPC.ExportAllChanBackups() - backupSnapshot := &lnrpc.ChanBackupSnapshot{ - MultiChanBackup: chanBackup. - MultiChanBackup, - } + runChanRestoreScenarioBasic(ht, restoreMethod) +} - // Create a new nodeRestorer that will restore - // the node with its seed, but no channel - // backup, shutdown this initialized node, then - // restart it again using Unlock. - return func() *node.HarnessNode { - newNode := st.RestoreNodeWithSeed( - "dave", nil, password, mnemonic, - "", revocationWindow, nil, - ) - st.RestartNodeWithChanBackups( - newNode, backupSnapshot, - ) - - return newNode - } - }, - }, +// testChannelBackupRestoreFromFile tests that we're able to recover from, and +// initiate the DLP protocol via the backup file. +func testChannelBackupRestoreFromFile(ht *lntest.HarnessTest) { + // Restore the backup from the on-disk file, using the RPC interface. + restoreMethod := func(st *lntest.HarnessTest, oldNode *node.HarnessNode, + backupFilePath string, password []byte, + mnemonic []string) nodeRestorer { - // Restore the backup from the on-disk file a second time to - // make sure imports can be canceled and later resumed. - { - name: "restore from backup file twice", - restoreMethod: func(st *lntest.HarnessTest, - oldNode *node.HarnessNode, - backupFilePath string, - password []byte, - mnemonic []string) nodeRestorer { - - // Read the entire Multi backup stored within - // this node's channel.backup file. - multi, err := os.ReadFile(backupFilePath) - require.NoError(st, err) - - // Now that we have Dave's backup file, we'll - // create a new nodeRestorer that will restore - // using the on-disk channel.backup. - // - //nolint:ll - backup := &lnrpc.RestoreChanBackupRequest_MultiChanBackup{ - MultiChanBackup: multi, - } + // Read the entire Multi backup stored within this node's + // channel.backup file. + multi, err := os.ReadFile(backupFilePath) + require.NoError(st, err) + + // Now that we have Dave's backup file, we'll create a new + // nodeRestorer that will restore using the on-disk + // channel.backup. + return chanRestoreViaRPC( + st, password, mnemonic, multi, + ) + } - return func() *node.HarnessNode { - newNode := st.RestoreNodeWithSeed( - "dave", nil, password, mnemonic, - "", revocationWindow, nil, - ) + runChanRestoreScenarioBasic(ht, restoreMethod) +} - req := &lnrpc.RestoreChanBackupRequest{ - Backup: backup, - } - res := newNode.RPC.RestoreChanBackups( - req, - ) - require.EqualValues( - st, 1, res.NumRestored, - ) - - req = &lnrpc.RestoreChanBackupRequest{ - Backup: backup, - } - res = newNode.RPC.RestoreChanBackups( - req, - ) - require.EqualValues( - st, 0, res.NumRestored, - ) - - return newNode - } - }, - }, +// testChannelBackupRestoreFromFile tests that we're able to recover from, and +// initiate the DLP protocol via restoring from initial wallet creation. +func testChannelBackupRestoreDuringCreation(ht *lntest.HarnessTest) { + // Restore the backup as part of node initialization with the prior + // mnemonic and new backup seed. + restoreMethod := func(st *lntest.HarnessTest, oldNode *node.HarnessNode, + backupFilePath string, password []byte, + mnemonic []string) nodeRestorer { + + // First, fetch the current backup state as is, to obtain our + // latest Multi. + chanBackup := oldNode.RPC.ExportAllChanBackups() + backupSnapshot := &lnrpc.ChanBackupSnapshot{ + MultiChanBackup: chanBackup. + MultiChanBackup, + } + + // Create a new nodeRestorer that will restore the node using + // the Multi backup we just obtained above. + return func() *node.HarnessNode { + return st.RestoreNodeWithSeed( + "dave", nil, password, mnemonic, + "", revocationWindow, + backupSnapshot, + ) + } + } + + runChanRestoreScenarioBasic(ht, restoreMethod) +} + +// testChannelBackupRestoreFromFile tests that we're able to recover from, and +// initiate the DLP protocol via restoring on unlock. +func testChannelBackupRestoreDuringUnlock(ht *lntest.HarnessTest) { + // Restore the backup once the node has already been re-created, using + // the Unlock call. + restoreMethod := func(st *lntest.HarnessTest, oldNode *node.HarnessNode, + backupFilePath string, password []byte, + mnemonic []string) nodeRestorer { + + // First, fetch the current backup state as is, to obtain our + // latest Multi. + chanBackup := oldNode.RPC.ExportAllChanBackups() + backupSnapshot := &lnrpc.ChanBackupSnapshot{ + MultiChanBackup: chanBackup. + MultiChanBackup, + } + + // Create a new nodeRestorer that will restore the node with + // its seed, but no channel backup, shutdown this initialized + // node, then restart it again using Unlock. + return func() *node.HarnessNode { + newNode := st.RestoreNodeWithSeed( + "dave", nil, password, mnemonic, + "", revocationWindow, nil, + ) + st.RestartNodeWithChanBackups( + newNode, backupSnapshot, + ) + + return newNode + } } - for _, testCase := range testCases { - tc := testCase - success := ht.Run(tc.name, func(t *testing.T) { - h := ht.Subtest(t) + runChanRestoreScenarioBasic(ht, restoreMethod) +} + +// testChannelBackupRestoreTwice tests that we're able to recover from, and +// initiate the DLP protocol twice by alternating between restoring form the on +// disk file, and restoring from the exported RPC command. +func testChannelBackupRestoreTwice(ht *lntest.HarnessTest) { + // Restore the backup from the on-disk file a second time to make sure + // imports can be canceled and later resumed. + restoreMethod := func(st *lntest.HarnessTest, oldNode *node.HarnessNode, + backupFilePath string, password []byte, + mnemonic []string) nodeRestorer { + + // Read the entire Multi backup stored within this node's + // channel.backup file. + multi, err := os.ReadFile(backupFilePath) + require.NoError(st, err) + + // Now that we have Dave's backup file, we'll create a new + // nodeRestorer that will restore using the on-disk + // channel.backup. + backup := &lnrpc.RestoreChanBackupRequest_MultiChanBackup{ + MultiChanBackup: multi, + } + + return func() *node.HarnessNode { + newNode := st.RestoreNodeWithSeed( + "dave", nil, password, mnemonic, + "", revocationWindow, nil, + ) + + req := &lnrpc.RestoreChanBackupRequest{ + Backup: backup, + } + newNode.RPC.RestoreChanBackups(req) + + req = &lnrpc.RestoreChanBackupRequest{ + Backup: backup, + } + newNode.RPC.RestoreChanBackups(req) - runChanRestoreScenarioBasic(h, tc.restoreMethod) - }) - if !success { - break + return newNode } } + + runChanRestoreScenarioBasic(ht, restoreMethod) } // runChanRestoreScenarioBasic executes a given test case from end to end, From c82610c6d4bd242d3d72225fc0e128680c1b7c78 Mon Sep 17 00:00:00 2001 From: yyforyongyu Date: Sat, 9 Nov 2024 01:19:43 +0800 Subject: [PATCH 10/32] itest: break down wallet import account tests --- itest/list_on_test.go | 5 +- itest/lnd_wallet_import_test.go | 108 +++++++++++++++----------------- 2 files changed, 50 insertions(+), 63 deletions(-) diff --git a/itest/list_on_test.go b/itest/list_on_test.go index 6ded67143b..a70d67f4c9 100644 --- a/itest/list_on_test.go +++ b/itest/list_on_test.go @@ -484,10 +484,6 @@ var allTestCases = []*lntest.TestCase{ Name: "simple taproot channel activation", TestFunc: testSimpleTaprootChannelActivation, }, - { - Name: "wallet import account", - TestFunc: testWalletImportAccount, - }, { Name: "wallet import pubkey", TestFunc: testWalletImportPubKey, @@ -684,4 +680,5 @@ func init() { allTestCases = append(allTestCases, fundUtxoSelectionTestCases...) allTestCases = append(allTestCases, zeroConfPolicyTestCases...) allTestCases = append(allTestCases, channelFeePolicyTestCases...) + allTestCases = append(allTestCases, walletImportAccountTestCases...) } diff --git a/itest/lnd_wallet_import_test.go b/itest/lnd_wallet_import_test.go index eab0f2c04f..f861390337 100644 --- a/itest/lnd_wallet_import_test.go +++ b/itest/lnd_wallet_import_test.go @@ -23,6 +23,55 @@ import ( "github.com/stretchr/testify/require" ) +// walletImportAccountTestCases tests that an imported account can fund +// transactions and channels through PSBTs, by having one node (the one with +// the imported account) craft the transactions and another node act as the +// signer. +// +//nolint:ll +var walletImportAccountTestCases = []*lntest.TestCase{ + { + Name: "wallet import account standard BIP-0044", + TestFunc: func(ht *lntest.HarnessTest) { + testWalletImportAccountScenario( + ht, walletrpc.AddressType_WITNESS_PUBKEY_HASH, + ) + }, + }, + { + Name: "wallet import account standard BIP-0049", + TestFunc: func(ht *lntest.HarnessTest) { + testWalletImportAccountScenario( + ht, walletrpc.AddressType_NESTED_WITNESS_PUBKEY_HASH, + ) + }, + }, + { + Name: "wallet import account lnd BIP-0049 variant", + TestFunc: func(ht *lntest.HarnessTest) { + testWalletImportAccountScenario( + ht, walletrpc.AddressType_HYBRID_NESTED_WITNESS_PUBKEY_HASH, + ) + }, + }, + { + Name: "wallet import account standard BIP-0084", + TestFunc: func(ht *lntest.HarnessTest) { + testWalletImportAccountScenario( + ht, walletrpc.AddressType_WITNESS_PUBKEY_HASH, + ) + }, + }, + { + Name: "wallet import account standard BIP-0086", + TestFunc: func(ht *lntest.HarnessTest) { + testWalletImportAccountScenario( + ht, walletrpc.AddressType_TAPROOT_PUBKEY, + ) + }, + }, +} + const ( defaultAccount = lnwallet.DefaultAccountName defaultImportedAccount = waddrmgr.ImportedAddrAccountName @@ -452,65 +501,6 @@ func fundChanAndCloseFromImportedAccount(ht *lntest.HarnessTest, srcNode, } } -// testWalletImportAccount tests that an imported account can fund transactions -// and channels through PSBTs, by having one node (the one with the imported -// account) craft the transactions and another node act as the signer. -func testWalletImportAccount(ht *lntest.HarnessTest) { - testCases := []struct { - name string - addrType walletrpc.AddressType - }{ - { - name: "standard BIP-0044", - addrType: walletrpc.AddressType_WITNESS_PUBKEY_HASH, - }, - { - name: "standard BIP-0049", - addrType: walletrpc. - AddressType_NESTED_WITNESS_PUBKEY_HASH, - }, - { - name: "lnd BIP-0049 variant", - addrType: walletrpc. - AddressType_HYBRID_NESTED_WITNESS_PUBKEY_HASH, - }, - { - name: "standard BIP-0084", - addrType: walletrpc.AddressType_WITNESS_PUBKEY_HASH, - }, - { - name: "standard BIP-0086", - addrType: walletrpc.AddressType_TAPROOT_PUBKEY, - }, - } - - for _, tc := range testCases { - tc := tc - success := ht.Run(tc.name, func(tt *testing.T) { - testFunc := func(ht *lntest.HarnessTest) { - testWalletImportAccountScenario( - ht, tc.addrType, - ) - } - - st := ht.Subtest(tt) - - st.RunTestCase(&lntest.TestCase{ - Name: tc.name, - TestFunc: testFunc, - }) - }) - if !success { - // Log failure time to help relate the lnd logs to the - // failure. - ht.Logf("Failure time: %v", time.Now().Format( - "2006-01-02 15:04:05.000", - )) - break - } - } -} - func testWalletImportAccountScenario(ht *lntest.HarnessTest, addrType walletrpc.AddressType) { From 6bef51d60b5099e071819053fd3332223a0e58ab Mon Sep 17 00:00:00 2001 From: yyforyongyu Date: Sat, 9 Nov 2024 14:56:57 +0800 Subject: [PATCH 11/32] itest: break down basic funding flow tests --- itest/list_on_test.go | 5 +- itest/lnd_funding_test.go | 332 +++++++++++++++++++++++--------------- 2 files changed, 201 insertions(+), 136 deletions(-) diff --git a/itest/list_on_test.go b/itest/list_on_test.go index a70d67f4c9..73016dd92a 100644 --- a/itest/list_on_test.go +++ b/itest/list_on_test.go @@ -11,10 +11,6 @@ var allTestCases = []*lntest.TestCase{ Name: "update channel status", TestFunc: testUpdateChanStatus, }, - { - Name: "basic funding flow", - TestFunc: testBasicChannelFunding, - }, { Name: "external channel funding", TestFunc: testExternalFundingChanPoint, @@ -681,4 +677,5 @@ func init() { allTestCases = append(allTestCases, zeroConfPolicyTestCases...) allTestCases = append(allTestCases, channelFeePolicyTestCases...) allTestCases = append(allTestCases, walletImportAccountTestCases...) + allTestCases = append(allTestCases, basicFundingTestCases...) } diff --git a/itest/lnd_funding_test.go b/itest/lnd_funding_test.go index a309213e05..c034b1751d 100644 --- a/itest/lnd_funding_test.go +++ b/itest/lnd_funding_test.go @@ -25,162 +25,230 @@ import ( "github.com/stretchr/testify/require" ) -// testBasicChannelFunding performs a test exercising expected behavior from a -// basic funding workflow. The test creates a new channel between Alice and -// Bob, then immediately closes the channel after asserting some expected post -// conditions. Finally, the chain itself is checked to ensure the closing -// transaction was mined. -func testBasicChannelFunding(ht *lntest.HarnessTest) { - // Run through the test with combinations of all the different - // commitment types. - allTypes := []lnrpc.CommitmentType{ - lnrpc.CommitmentType_STATIC_REMOTE_KEY, - lnrpc.CommitmentType_ANCHORS, - lnrpc.CommitmentType_SIMPLE_TAPROOT, - } +// basicFundingTestCases defines the test cases for the basic funding test. +var basicFundingTestCases = []*lntest.TestCase{ + { + Name: "basic funding flow static key remote", + TestFunc: testBasicChannelFundingStaticRemote, + }, + { + Name: "basic funding flow anchor", + TestFunc: testBasicChannelFundingAnchor, + }, + { + Name: "basic funding flow simple taproot", + TestFunc: testBasicChannelFundingSimpleTaproot, + }, +} + +// allFundingTypes defines the channel types to test for the basic funding +// test. +var allFundingTypes = []lnrpc.CommitmentType{ + lnrpc.CommitmentType_STATIC_REMOTE_KEY, + lnrpc.CommitmentType_ANCHORS, + lnrpc.CommitmentType_SIMPLE_TAPROOT, +} - // testFunding is a function closure that takes Carol and Dave's - // commitment types and test the funding flow. - testFunding := func(ht *lntest.HarnessTest, carolCommitType, - daveCommitType lnrpc.CommitmentType) { +// testBasicChannelFundingStaticRemote performs a test exercising expected +// behavior from a basic funding workflow. The test creates a new channel +// between Carol and Dave, with Carol using the static remote key commitment +// type, and Dave using allFundingTypes. +func testBasicChannelFundingStaticRemote(ht *lntest.HarnessTest) { + carolCommitType := lnrpc.CommitmentType_STATIC_REMOTE_KEY - // Based on the current tweak variable for Carol, we'll - // preferentially signal the legacy commitment format. We do - // the same for Dave shortly below. - carolArgs := lntest.NodeArgsForCommitType(carolCommitType) - carol := ht.NewNode("Carol", carolArgs) + // We'll test all possible combinations of the feature bit presence + // that both nodes can signal for this new channel type. We'll make a + // new Carol+Dave for each test instance as well. + for _, daveCommitType := range allFundingTypes { + cc := carolCommitType + dc := daveCommitType - // Each time, we'll send Carol a new set of coins in order to - // fund the channel. - ht.FundCoins(btcutil.SatoshiPerBitcoin, carol) + testName := fmt.Sprintf( + "carol_commit=%v,dave_commit=%v", cc, dc, + ) - daveArgs := lntest.NodeArgsForCommitType(daveCommitType) - dave := ht.NewNode("Dave", daveArgs) + success := ht.Run(testName, func(t *testing.T) { + st := ht.Subtest(t) + runBasicFundingTest(st, cc, dc) + }) - // Before we start the test, we'll ensure both sides are - // connected to the funding flow can properly be executed. - ht.EnsureConnected(carol, dave) + if !success { + break + } + } +} - var privateChan bool +// testBasicChannelFundingAnchor performs a test exercising expected behavior +// from a basic funding workflow. The test creates a new channel between Carol +// and Dave, with Carol using the anchor commitment type, and Dave using +// allFundingTypes. +func testBasicChannelFundingAnchor(ht *lntest.HarnessTest) { + carolCommitType := lnrpc.CommitmentType_ANCHORS - // If this is to be a taproot channel type, then it needs to be - // private, otherwise it'll be rejected by Dave. - // - // TODO(roasbeef): lift after gossip 1.75 - if carolCommitType == lnrpc.CommitmentType_SIMPLE_TAPROOT { - privateChan = true + // We'll test all possible combinations of the feature bit presence + // that both nodes can signal for this new channel type. We'll make a + // new Carol+Dave for each test instance as well. + for _, daveCommitType := range allFundingTypes { + cc := carolCommitType + dc := daveCommitType + + testName := fmt.Sprintf( + "carol_commit=%v,dave_commit=%v", cc, dc, + ) + + success := ht.Run(testName, func(t *testing.T) { + st := ht.Subtest(t) + runBasicFundingTest(st, cc, dc) + }) + + if !success { + break } + } +} + +// testBasicChannelFundingSimpleTaproot performs a test exercising expected +// behavior from a basic funding workflow. The test creates a new channel +// between Carol and Dave, with Carol using the simple taproot commitment type, +// and Dave using allFundingTypes. +func testBasicChannelFundingSimpleTaproot(ht *lntest.HarnessTest) { + carolCommitType := lnrpc.CommitmentType_SIMPLE_TAPROOT + + // We'll test all possible combinations of the feature bit presence + // that both nodes can signal for this new channel type. We'll make a + // new Carol+Dave for each test instance as well. + for _, daveCommitType := range allFundingTypes { + cc := carolCommitType + dc := daveCommitType + + testName := fmt.Sprintf( + "carol_commit=%v,dave_commit=%v", cc, dc, + ) + + success := ht.Run(testName, func(t *testing.T) { + st := ht.Subtest(t) + runBasicFundingTest(st, cc, dc) + }) - // If carol wants taproot, but dave wants something - // else, then we'll assert that the channel negotiation - // attempt fails. - if carolCommitType == lnrpc.CommitmentType_SIMPLE_TAPROOT && - daveCommitType != lnrpc.CommitmentType_SIMPLE_TAPROOT { - - expectedErr := fmt.Errorf("requested channel type " + - "not supported") - amt := funding.MaxBtcFundingAmount - ht.OpenChannelAssertErr( - carol, dave, lntest.OpenChannelParams{ - Private: privateChan, - Amt: amt, - CommitmentType: carolCommitType, - }, expectedErr, - ) - - return + if !success { + break } + } +} + +// runBasicFundingTest is a helper function that takes Carol and Dave's +// commitment types and test the funding flow. +func runBasicFundingTest(ht *lntest.HarnessTest, carolCommitType, + daveCommitType lnrpc.CommitmentType) { + + // Based on the current tweak variable for Carol, we'll preferentially + // signal the legacy commitment format. We do the same for Dave + // shortly below. + carolArgs := lntest.NodeArgsForCommitType(carolCommitType) + carol := ht.NewNode("Carol", carolArgs) + + // Each time, we'll send Carol a new set of coins in order to fund the + // channel. + ht.FundCoins(btcutil.SatoshiPerBitcoin, carol) - carolChan, daveChan := basicChannelFundingTest( - ht, carol, dave, nil, privateChan, &carolCommitType, + daveArgs := lntest.NodeArgsForCommitType(daveCommitType) + dave := ht.NewNode("Dave", daveArgs) + + // Before we start the test, we'll ensure both sides are connected to + // the funding flow can properly be executed. + ht.EnsureConnected(carol, dave) + + var privateChan bool + + // If this is to be a taproot channel type, then it needs to be + // private, otherwise it'll be rejected by Dave. + // + // TODO(roasbeef): lift after gossip 1.75 + if carolCommitType == lnrpc.CommitmentType_SIMPLE_TAPROOT { + privateChan = true + } + + // If carol wants taproot, but dave wants something else, then we'll + // assert that the channel negotiation attempt fails. + if carolCommitType == lnrpc.CommitmentType_SIMPLE_TAPROOT && + daveCommitType != lnrpc.CommitmentType_SIMPLE_TAPROOT { + + expectedErr := fmt.Errorf("requested channel type " + + "not supported") + amt := funding.MaxBtcFundingAmount + ht.OpenChannelAssertErr( + carol, dave, lntest.OpenChannelParams{ + Private: privateChan, + Amt: amt, + CommitmentType: carolCommitType, + }, expectedErr, ) - // Both nodes should report the same commitment - // type. - chansCommitType := carolChan.CommitmentType - require.Equal(ht, chansCommitType, daveChan.CommitmentType, - "commit types don't match") + return + } - // Now check that the commitment type reported by both nodes is - // what we expect. It will be the minimum of the two nodes' - // preference, in the order Legacy, Tweakless, Anchors. - expType := carolCommitType + carolChan, daveChan := basicChannelFundingTest( + ht, carol, dave, nil, privateChan, &carolCommitType, + ) - switch daveCommitType { - // Dave supports taproot, type will be what Carol supports. - case lnrpc.CommitmentType_SIMPLE_TAPROOT: + // Both nodes should report the same commitment type. + chansCommitType := carolChan.CommitmentType + require.Equal(ht, chansCommitType, daveChan.CommitmentType, + "commit types don't match") + + // Now check that the commitment type reported by both nodes is what we + // expect. It will be the minimum of the two nodes' preference, in the + // order Legacy, Tweakless, Anchors. + expType := carolCommitType + + switch daveCommitType { + // Dave supports taproot, type will be what Carol supports. + case lnrpc.CommitmentType_SIMPLE_TAPROOT: + + // Dave supports anchors, type will be what Carol supports. + case lnrpc.CommitmentType_ANCHORS: + // However if Alice wants taproot chans, then we downgrade to + // anchors as this is still using implicit negotiation. + if expType == lnrpc.CommitmentType_SIMPLE_TAPROOT { + expType = lnrpc.CommitmentType_ANCHORS + } - // Dave supports anchors, type will be what Carol supports. + // Dave only supports tweakless, channel will be downgraded to this + // type if Carol supports anchors. + case lnrpc.CommitmentType_STATIC_REMOTE_KEY: + switch expType { case lnrpc.CommitmentType_ANCHORS: - // However if Alice wants taproot chans, then we - // downgrade to anchors as this is still using implicit - // negotiation. - if expType == lnrpc.CommitmentType_SIMPLE_TAPROOT { - expType = lnrpc.CommitmentType_ANCHORS - } - - // Dave only supports tweakless, channel will be downgraded to - // this type if Carol supports anchors. - case lnrpc.CommitmentType_STATIC_REMOTE_KEY: - switch expType { - case lnrpc.CommitmentType_ANCHORS: - expType = lnrpc.CommitmentType_STATIC_REMOTE_KEY - case lnrpc.CommitmentType_SIMPLE_TAPROOT: - expType = lnrpc.CommitmentType_STATIC_REMOTE_KEY - } - - // Dave only supports legacy type, channel will be downgraded - // to this type. - case lnrpc.CommitmentType_LEGACY: - expType = lnrpc.CommitmentType_LEGACY - - default: - ht.Fatalf("invalid commit type %v", daveCommitType) + expType = lnrpc.CommitmentType_STATIC_REMOTE_KEY + case lnrpc.CommitmentType_SIMPLE_TAPROOT: + expType = lnrpc.CommitmentType_STATIC_REMOTE_KEY } - // Check that the signalled type matches what we expect. - switch { - case expType == lnrpc.CommitmentType_ANCHORS && - chansCommitType == lnrpc.CommitmentType_ANCHORS: + // Dave only supports legacy type, channel will be downgraded to this + // type. + case lnrpc.CommitmentType_LEGACY: + expType = lnrpc.CommitmentType_LEGACY - case expType == lnrpc.CommitmentType_STATIC_REMOTE_KEY && - chansCommitType == lnrpc.CommitmentType_STATIC_REMOTE_KEY: //nolint:ll + default: + ht.Fatalf("invalid commit type %v", daveCommitType) + } - case expType == lnrpc.CommitmentType_LEGACY && - chansCommitType == lnrpc.CommitmentType_LEGACY: + // Check that the signalled type matches what we expect. + switch { + case expType == lnrpc.CommitmentType_ANCHORS && + chansCommitType == lnrpc.CommitmentType_ANCHORS: - case expType == lnrpc.CommitmentType_SIMPLE_TAPROOT && - chansCommitType == lnrpc.CommitmentType_SIMPLE_TAPROOT: + case expType == lnrpc.CommitmentType_STATIC_REMOTE_KEY && + chansCommitType == lnrpc.CommitmentType_STATIC_REMOTE_KEY: - default: - ht.Fatalf("expected nodes to signal "+ - "commit type %v, instead got "+ - "%v", expType, chansCommitType) - } - } + case expType == lnrpc.CommitmentType_LEGACY && + chansCommitType == lnrpc.CommitmentType_LEGACY: -test: - // We'll test all possible combinations of the feature bit presence - // that both nodes can signal for this new channel type. We'll make a - // new Carol+Dave for each test instance as well. - for _, carolCommitType := range allTypes { - for _, daveCommitType := range allTypes { - cc := carolCommitType - dc := daveCommitType - - testName := fmt.Sprintf( - "carol_commit=%v,dave_commit=%v", cc, dc, - ) - - success := ht.Run(testName, func(t *testing.T) { - st := ht.Subtest(t) - testFunding(st, cc, dc) - }) - - if !success { - break test - } - } + case expType == lnrpc.CommitmentType_SIMPLE_TAPROOT && + chansCommitType == lnrpc.CommitmentType_SIMPLE_TAPROOT: + + default: + ht.Fatalf("expected nodes to signal commit type %v, instead "+ + "got %v", expType, chansCommitType) } } From f42b1082b7dd16bf227af5fd02e26af4626a5212 Mon Sep 17 00:00:00 2001 From: yyforyongyu Date: Sat, 9 Nov 2024 15:14:53 +0800 Subject: [PATCH 12/32] itest: break down single hop send to route Also removed the duplicate test cases. --- itest/list_on_test.go | 5 +-- itest/lnd_routing_test.go | 70 +++++++++++++-------------------------- 2 files changed, 24 insertions(+), 51 deletions(-) diff --git a/itest/list_on_test.go b/itest/list_on_test.go index 73016dd92a..b0146f16bf 100644 --- a/itest/list_on_test.go +++ b/itest/list_on_test.go @@ -300,10 +300,6 @@ var allTestCases = []*lntest.TestCase{ Name: "revoked uncooperative close retribution remote hodl", TestFunc: testRevokedCloseRetributionRemoteHodl, }, - { - Name: "single-hop send to route", - TestFunc: testSingleHopSendToRoute, - }, { Name: "multi-hop send to route", TestFunc: testMultiHopSendToRoute, @@ -678,4 +674,5 @@ func init() { allTestCases = append(allTestCases, channelFeePolicyTestCases...) allTestCases = append(allTestCases, walletImportAccountTestCases...) allTestCases = append(allTestCases, basicFundingTestCases...) + allTestCases = append(allTestCases, sendToRouteTestCases...) } diff --git a/itest/lnd_routing_test.go b/itest/lnd_routing_test.go index 950b24d24b..c32e021d44 100644 --- a/itest/lnd_routing_test.go +++ b/itest/lnd_routing_test.go @@ -20,46 +20,33 @@ import ( "google.golang.org/protobuf/proto" ) -type singleHopSendToRouteCase struct { - name string - - // streaming tests streaming SendToRoute if true, otherwise tests - // synchronous SenToRoute. - streaming bool - - // routerrpc submits the request to the routerrpc subserver if true, - // otherwise submits to the main rpc server. - routerrpc bool -} - -var singleHopSendToRouteCases = []singleHopSendToRouteCase{ - { - name: "regular main sync", - }, - { - name: "regular main stream", - streaming: true, - }, - { - name: "regular routerrpc sync", - routerrpc: true, - }, +var sendToRouteTestCases = []*lntest.TestCase{ { - name: "mpp main sync", + Name: "single hop send to route sync", + TestFunc: func(ht *lntest.HarnessTest) { + // useStream: false, routerrpc: false. + testSingleHopSendToRouteCase(ht, false, false) + }, }, { - name: "mpp main stream", - streaming: true, + Name: "single hop send to route stream", + TestFunc: func(ht *lntest.HarnessTest) { + // useStream: true, routerrpc: false. + testSingleHopSendToRouteCase(ht, true, false) + }, }, { - name: "mpp routerrpc sync", - routerrpc: true, + Name: "single hop send to route v2", + TestFunc: func(ht *lntest.HarnessTest) { + // useStream: false, routerrpc: true. + testSingleHopSendToRouteCase(ht, false, true) + }, }, } -// testSingleHopSendToRoute tests that payments are properly processed through a -// provided route with a single hop. We'll create the following network -// topology: +// testSingleHopSendToRouteCase tests that payments are properly processed +// through a provided route with a single hop. We'll create the following +// network topology: // // Carol --100k--> Dave // @@ -67,19 +54,8 @@ var singleHopSendToRouteCases = []singleHopSendToRouteCase{ // by feeding the route back into the various SendToRoute RPC methods. Here we // test all three SendToRoute endpoints, forcing each to perform both a regular // payment and an MPP payment. -func testSingleHopSendToRoute(ht *lntest.HarnessTest) { - for _, test := range singleHopSendToRouteCases { - test := test - - ht.Run(test.name, func(t1 *testing.T) { - st := ht.Subtest(t1) - testSingleHopSendToRouteCase(st, test) - }) - } -} - func testSingleHopSendToRouteCase(ht *lntest.HarnessTest, - test singleHopSendToRouteCase) { + useStream, useRPC bool) { const chanAmt = btcutil.Amount(100000) const paymentAmtSat = 1000 @@ -199,11 +175,11 @@ func testSingleHopSendToRouteCase(ht *lntest.HarnessTest, // synchronously via the routerrpc's SendToRoute, or via the main RPC // server's SendToRoute streaming or sync calls. switch { - case !test.routerrpc && test.streaming: + case !useRPC && useStream: sendToRouteStream() - case !test.routerrpc && !test.streaming: + case !useRPC && !useStream: sendToRouteSync() - case test.routerrpc && !test.streaming: + case useRPC && !useStream: sendToRouteRouterRPC() default: require.Fail(ht, "routerrpc does not support "+ From 80895beb6e2230c3d53cd2bd549d390ba5284e63 Mon Sep 17 00:00:00 2001 From: yyforyongyu Date: Sat, 9 Nov 2024 15:18:40 +0800 Subject: [PATCH 13/32] itest: break down taproot tests --- itest/list_on_test.go | 12 ++++++++++-- itest/lnd_taproot_test.go | 17 ++++++++++++++--- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/itest/list_on_test.go b/itest/list_on_test.go index b0146f16bf..e8ae3fbe0e 100644 --- a/itest/list_on_test.go +++ b/itest/list_on_test.go @@ -469,8 +469,16 @@ var allTestCases = []*lntest.TestCase{ TestFunc: testBumpForceCloseFee, }, { - Name: "taproot", - TestFunc: testTaproot, + Name: "taproot spend", + TestFunc: testTaprootSpend, + }, + { + Name: "taproot musig2", + TestFunc: testTaprootMuSig2, + }, + { + Name: "taproot import scripts", + TestFunc: testTaprootImportScripts, }, { Name: "simple taproot channel activation", diff --git a/itest/lnd_taproot_test.go b/itest/lnd_taproot_test.go index 2b68105fdb..ddbfb6983a 100644 --- a/itest/lnd_taproot_test.go +++ b/itest/lnd_taproot_test.go @@ -46,9 +46,9 @@ var ( )) ) -// testTaproot ensures that the daemon can send to and spend from taproot (p2tr) -// outputs. -func testTaproot(ht *lntest.HarnessTest) { +// testTaprootSpend ensures that the daemon can send to and spend from taproot +// (p2tr) outputs. +func testTaprootSpend(ht *lntest.HarnessTest) { alice := ht.NewNode("Alice", nil) testTaprootSendCoinsKeySpendBip86(ht, alice) @@ -62,6 +62,12 @@ func testTaproot(ht *lntest.HarnessTest) { ht, alice, txscript.SigHashSingle, ) testTaprootSignOutputRawKeySpendRootHash(ht, alice) +} + +// testTaprootMuSig2 ensures that the daemon can send to and spend from taproot +// (p2tr) outputs using musig2. +func testTaprootMuSig2(ht *lntest.HarnessTest) { + alice := ht.NewNodeWithCoins("Alice", nil) muSig2Versions := []signrpc.MuSig2Version{ signrpc.MuSig2Version_MUSIG2_VERSION_V040, @@ -74,6 +80,11 @@ func testTaproot(ht *lntest.HarnessTest) { testTaprootMuSig2CombinedLeafKeySpend(ht, alice, version) testMuSig2CombineKey(ht, alice, version) } +} + +// testTaprootImportScripts ensures that the daemon can import taproot scripts. +func testTaprootImportScripts(ht *lntest.HarnessTest) { + alice := ht.NewNodeWithCoins("Alice", nil) testTaprootImportTapscriptFullTree(ht, alice) testTaprootImportTapscriptPartialReveal(ht, alice) From c0ffd294452585de5ad1716932e573c9208bdce3 Mon Sep 17 00:00:00 2001 From: yyforyongyu Date: Sat, 9 Nov 2024 15:41:48 +0800 Subject: [PATCH 14/32] itest: break down channel fundmax tests --- itest/list_on_test.go | 12 ++- itest/lnd_channel_funding_fund_max_test.go | 120 +++++++++++++++++---- 2 files changed, 111 insertions(+), 21 deletions(-) diff --git a/itest/list_on_test.go b/itest/list_on_test.go index e8ae3fbe0e..7562350fff 100644 --- a/itest/list_on_test.go +++ b/itest/list_on_test.go @@ -525,8 +525,16 @@ var allTestCases = []*lntest.TestCase{ TestFunc: testLookupHtlcResolution, }, { - Name: "channel fundmax", - TestFunc: testChannelFundMax, + Name: "channel fundmax error", + TestFunc: testChannelFundMaxError, + }, + { + Name: "channel fundmax wallet amount", + TestFunc: testChannelFundMaxWalletAmount, + }, + { + Name: "channel fundmax anchor reserve", + TestFunc: testChannelFundMaxAnchorReserve, }, { Name: "htlc timeout resolver extract preimage remote", diff --git a/itest/lnd_channel_funding_fund_max_test.go b/itest/lnd_channel_funding_fund_max_test.go index c93ab5fdc7..5a19ccdebd 100644 --- a/itest/lnd_channel_funding_fund_max_test.go +++ b/itest/lnd_channel_funding_fund_max_test.go @@ -50,9 +50,9 @@ type chanFundMaxTestCase struct { private bool } -// testChannelFundMax checks various channel funding scenarios where the user -// instructed the wallet to use all remaining funds. -func testChannelFundMax(ht *lntest.HarnessTest) { +// testChannelFundMaxError checks various error channel funding scenarios where +// the user instructed the wallet to use all remaining funds. +func testChannelFundMaxError(ht *lntest.HarnessTest) { // Create two new nodes that open a channel between each other for these // tests. args := lntest.NodeArgsForCommitType(lnrpc.CommitmentType_ANCHORS) @@ -92,22 +92,6 @@ func testChannelFundMax(ht *lntest.HarnessTest) { expectedErrStr: "available funds(0.00017877 BTC) " + "below the minimum amount(0.00020000 BTC)", }, - { - name: "wallet amount > min chan " + - "size (37000sat)", - initialWalletBalance: 37_000, - // The transaction fee to open the channel must be - // subtracted from Alice's balance. - // (since wallet balance < max-chan-size) - expectedBalanceAlice: btcutil.Amount(37_000) - - fundingFee(1, false), - }, - { - name: "wallet amount > max chan size " + - "(20000000sat)", - initialWalletBalance: 20_000_000, - expectedBalanceAlice: lnd.MaxFundingAmount, - }, // Expects, that if the maximum funding amount for a channel is // pushed to the remote side, then the funding flow is failing // because the push amount has to be less than the local channel @@ -137,6 +121,63 @@ func testChannelFundMax(ht *lntest.HarnessTest) { expectedErrStr: "funder balance too small (-8050000) " + "with fee=9050 sat, minimum=708 sat required", }, + } + + for _, testCase := range testCases { + success := ht.Run( + testCase.name, func(tt *testing.T) { + runFundMaxTestCase( + ht, alice, bob, testCase, reserveAmount, + ) + }, + ) + + // Stop at the first failure. Mimic behavior of original test + // framework. + if !success { + break + } + } +} + +// testChannelFundMaxWalletAmount checks various channel funding scenarios +// where the user instructed the wallet to use all remaining funds and succeed. +func testChannelFundMaxWalletAmount(ht *lntest.HarnessTest) { + // Create two new nodes that open a channel between each other for these + // tests. + args := lntest.NodeArgsForCommitType(lnrpc.CommitmentType_ANCHORS) + alice := ht.NewNode("Alice", args) + bob := ht.NewNode("Bob", args) + + // Ensure both sides are connected so the funding flow can be properly + // executed. + ht.EnsureConnected(alice, bob) + + // Calculate reserve amount for one channel. + reserveResp, _ := alice.RPC.WalletKit.RequiredReserve( + context.Background(), &walletrpc.RequiredReserveRequest{ + AdditionalPublicChannels: 1, + }, + ) + reserveAmount := btcutil.Amount(reserveResp.RequiredReserve) + + var testCases = []*chanFundMaxTestCase{ + { + name: "wallet amount > min chan " + + "size (37000sat)", + initialWalletBalance: 37_000, + // The transaction fee to open the channel must be + // subtracted from Alice's balance. + // (since wallet balance < max-chan-size) + expectedBalanceAlice: btcutil.Amount(37_000) - + fundingFee(1, false), + }, + { + name: "wallet amount > max chan size " + + "(20000000sat)", + initialWalletBalance: 20_000_000, + expectedBalanceAlice: lnd.MaxFundingAmount, + }, { name: "wallet amount > max chan size, " + "push amount 16766000", @@ -144,7 +185,48 @@ func testChannelFundMax(ht *lntest.HarnessTest) { pushAmt: 16_766_000, expectedBalanceAlice: lnd.MaxFundingAmount - 16_766_000, }, + } + for _, testCase := range testCases { + success := ht.Run( + testCase.name, func(tt *testing.T) { + runFundMaxTestCase( + ht, alice, bob, testCase, reserveAmount, + ) + }, + ) + + // Stop at the first failure. Mimic behavior of original test + // framework. + if !success { + break + } + } +} + +// testChannelFundMaxAnchorReserve checks various channel funding scenarios +// where the user instructed the wallet to use all remaining funds and its +// impact on anchor reserve. +func testChannelFundMaxAnchorReserve(ht *lntest.HarnessTest) { + // Create two new nodes that open a channel between each other for these + // tests. + args := lntest.NodeArgsForCommitType(lnrpc.CommitmentType_ANCHORS) + alice := ht.NewNode("Alice", args) + bob := ht.NewNode("Bob", args) + + // Ensure both sides are connected so the funding flow can be properly + // executed. + ht.EnsureConnected(alice, bob) + + // Calculate reserve amount for one channel. + reserveResp, _ := alice.RPC.WalletKit.RequiredReserve( + context.Background(), &walletrpc.RequiredReserveRequest{ + AdditionalPublicChannels: 1, + }, + ) + reserveAmount := btcutil.Amount(reserveResp.RequiredReserve) + + var testCases = []*chanFundMaxTestCase{ { name: "anchor reserved value", initialWalletBalance: 100_000, From 93765f23dca412f0f4e46d893b16e098262c0ef2 Mon Sep 17 00:00:00 2001 From: yyforyongyu Date: Thu, 5 Dec 2024 08:49:56 +0800 Subject: [PATCH 15/32] itest: breakdown `testSendDirectPayment` Also fixes a wrong usage of `ht.Subtest`. --- itest/list_on_test.go | 8 +- itest/lnd_payment_test.go | 207 ++++++++++++++++---------------------- 2 files changed, 91 insertions(+), 124 deletions(-) diff --git a/itest/list_on_test.go b/itest/list_on_test.go index 7562350fff..22bcd5866f 100644 --- a/itest/list_on_test.go +++ b/itest/list_on_test.go @@ -168,8 +168,12 @@ var allTestCases = []*lntest.TestCase{ TestFunc: testListPayments, }, { - Name: "send direct payment", - TestFunc: testSendDirectPayment, + Name: "send direct payment anchor", + TestFunc: testSendDirectPaymentAnchor, + }, + { + Name: "send direct payment simple taproot", + TestFunc: testSendDirectPaymentSimpleTaproot, }, { Name: "immediate payment after channel opened", diff --git a/itest/lnd_payment_test.go b/itest/lnd_payment_test.go index 72a2c16030..ecd0cb06bd 100644 --- a/itest/lnd_payment_test.go +++ b/itest/lnd_payment_test.go @@ -339,135 +339,98 @@ func runTestPaymentHTLCTimeout(ht *lntest.HarnessTest, restartAlice bool) { ht.AssertPaymentStatusFromStream(payStream, lnrpc.Payment_FAILED) } -// testSendDirectPayment creates a topology Alice->Bob and then tests that -// Alice can send a direct payment to Bob. This test modifies the fee estimator -// to return floor fee rate(1 sat/vb). -func testSendDirectPayment(ht *lntest.HarnessTest) { - // Grab Alice and Bob's nodes for convenience. - alice := ht.NewNodeWithCoins("Alice", nil) - bob := ht.NewNodeWithCoins("Bob", nil) +// runSendDirectPayment opens a channel between Alice and Bob using the +// specified params. It then sends a payment from Alice to Bob and asserts it +// being successful. +func runSendDirectPayment(ht *lntest.HarnessTest, cfgs [][]string, + params lntest.OpenChannelParams) { - // Create a list of commitment types we want to test. - commitmentTypes := []lnrpc.CommitmentType{ - lnrpc.CommitmentType_ANCHORS, - lnrpc.CommitmentType_SIMPLE_TAPROOT, - } + // Set the fee estimate to 1sat/vbyte. + ht.SetFeeEstimate(250) - // testSendPayment opens a channel between Alice and Bob using the - // specified params. It then sends a payment from Alice to Bob and - // asserts it being successful. - testSendPayment := func(ht *lntest.HarnessTest, - params lntest.OpenChannelParams) { - - // Check that there are no payments before test. - chanPoint := ht.OpenChannel(alice, bob, params) - - // Now that the channel is open, create an invoice for Bob - // which expects a payment of 1000 satoshis from Alice paid via - // a particular preimage. - const paymentAmt = 1000 - preimage := ht.Random32Bytes() - invoice := &lnrpc.Invoice{ - RPreimage: preimage, - Value: paymentAmt, - } - invoiceResp := bob.RPC.AddInvoice(invoice) - - // With the invoice for Bob added, send a payment towards Alice - // paying to the above generated invoice. - payReqs := []string{invoiceResp.PaymentRequest} - ht.CompletePaymentRequests(alice, payReqs) - - p := ht.AssertNumPayments(alice, 1)[0] - path := p.Htlcs[len(p.Htlcs)-1].Route.Hops - - // Ensure that the stored path shows a direct payment to Bob - // with no other nodes in-between. - require.Len(ht, path, 1, "wrong number of routes in path") - require.Equal(ht, bob.PubKeyStr, path[0].PubKey, "wrong pubkey") - - // The payment amount should also match our previous payment - // directly. - require.EqualValues(ht, paymentAmt, p.ValueSat, - "incorrect sat amount") - require.EqualValues(ht, paymentAmt*1000, p.ValueMsat, - "incorrect msat amount") - - // The payment hash (or r-hash) should have been stored - // correctly. - correctRHash := hex.EncodeToString(invoiceResp.RHash) - require.Equal(ht, correctRHash, p.PaymentHash, "incorrect hash") - - // As we made a single-hop direct payment, there should have - // been no fee applied. - require.Zero(ht, p.FeeSat, "fee should be 0") - require.Zero(ht, p.FeeMsat, "fee should be 0") - - // Now verify that the payment request returned by the rpc - // matches the invoice that we paid. - require.Equal(ht, invoiceResp.PaymentRequest, p.PaymentRequest, - "incorrect payreq") - - // Delete all payments from Alice. DB should have no payments. - alice.RPC.DeleteAllPayments() - ht.AssertNumPayments(alice, 0) - - // TODO(yy): remove the sleep once the following bug is fixed. - // When the invoice is reported settled, the commitment dance - // is not yet finished, which can cause an error when closing - // the channel, saying there's active HTLCs. We need to - // investigate this issue and reverse the order to, first - // finish the commitment dance, then report the invoice as - // settled. - time.Sleep(2 * time.Second) - - // Close the channel. - // - // NOTE: This implicitly tests that the channel link is active - // before closing this channel. The above payment will trigger - // a commitment dance in both of the nodes. If the node fails - // to update the commitment state, we will fail to close the - // channel as the link won't be active. - ht.CloseChannel(alice, chanPoint) + // Create a two-hop network: Alice -> Bob. + _, nodes := ht.CreateSimpleNetwork(cfgs, params) + alice, bob := nodes[0], nodes[1] + + // Now that the channel is open, create an invoice for Bob + // which expects a payment of 1000 satoshis from Alice paid via + // a particular preimage. + const paymentAmt = 1000 + preimage := ht.Random32Bytes() + invoice := &lnrpc.Invoice{ + RPreimage: preimage, + Value: paymentAmt, } + invoiceResp := bob.RPC.AddInvoice(invoice) - // Run the test cases. - for _, ct := range commitmentTypes { - ht.Run(ct.String(), func(t *testing.T) { - st := ht.Subtest(t) - - // Set the fee estimate to 1sat/vbyte. - st.SetFeeEstimate(250) - - // Restart the nodes with the specified commitment type. - args := lntest.NodeArgsForCommitType(ct) - st.RestartNodeWithExtraArgs(alice, args) - st.RestartNodeWithExtraArgs(bob, args) - - // Make sure they are connected. - st.EnsureConnected(alice, bob) - - // There's a bug that causes the funding to be failed - // due to the `ListCoins` cannot find the utxos. - // - // TODO(yy): remove this line to fix the ListCoins bug. - st.FundCoins(btcutil.SatoshiPerBitcoin, alice) - - // Open a channel with 100k satoshis between Alice and - // Bob with Alice being the sole funder of the channel. - params := lntest.OpenChannelParams{ - Amt: 100_000, - CommitmentType: ct, - } + // With the invoice for Bob added, send a payment towards Alice + // paying to the above generated invoice. + payReqs := []string{invoiceResp.PaymentRequest} + ht.CompletePaymentRequests(alice, payReqs) - // Open private channel for taproot channels. - if ct == lnrpc.CommitmentType_SIMPLE_TAPROOT { - params.Private = true - } + p := ht.AssertNumPayments(alice, 1)[0] + path := p.Htlcs[len(p.Htlcs)-1].Route.Hops - testSendPayment(st, params) - }) + // Ensure that the stored path shows a direct payment to Bob + // with no other nodes in-between. + require.Len(ht, path, 1, "wrong number of routes in path") + require.Equal(ht, bob.PubKeyStr, path[0].PubKey, "wrong pubkey") + + // The payment amount should also match our previous payment + // directly. + require.EqualValues(ht, paymentAmt, p.ValueSat, + "incorrect sat amount") + require.EqualValues(ht, paymentAmt*1000, p.ValueMsat, + "incorrect msat amount") + + // The payment hash (or r-hash) should have been stored + // correctly. + correctRHash := hex.EncodeToString(invoiceResp.RHash) + require.Equal(ht, correctRHash, p.PaymentHash, "incorrect hash") + + // As we made a single-hop direct payment, there should have + // been no fee applied. + require.Zero(ht, p.FeeSat, "fee should be 0") + require.Zero(ht, p.FeeMsat, "fee should be 0") + + // Now verify that the payment request returned by the rpc + // matches the invoice that we paid. + require.Equal(ht, invoiceResp.PaymentRequest, p.PaymentRequest, + "incorrect payreq") +} + +// testSendDirectPaymentAnchor creates a topology Alice->Bob using anchor +// channel and then tests that Alice can send a direct payment to Bob. +func testSendDirectPaymentAnchor(ht *lntest.HarnessTest) { + // Create a two-hop network: Alice -> Bob using anchor channel. + // + // Prepare params. + params := lntest.OpenChannelParams{Amt: chanAmt} + cfg := node.CfgAnchor + cfgs := [][]string{cfg, cfg} + + runSendDirectPayment(ht, cfgs, params) +} + +// testSendDirectPaymentSimpleTaproot creates a topology Alice->Bob using +// simple taproot channel and then tests that Alice can send a direct payment +// to Bob. +func testSendDirectPaymentSimpleTaproot(ht *lntest.HarnessTest) { + c := lnrpc.CommitmentType_SIMPLE_TAPROOT + + // Create a two-hop network: Alice -> Bob using simple taproot channel. + // + // Prepare params. + params := lntest.OpenChannelParams{ + Amt: chanAmt, + CommitmentType: c, + Private: true, } + + cfg := node.CfgSimpleTaproot + cfgs := [][]string{cfg, cfg} + + runSendDirectPayment(ht, cfgs, params) } func testListPayments(ht *lntest.HarnessTest) { From b3f99c3bdeec2814dfc08893b6dc2d8b6156ebdc Mon Sep 17 00:00:00 2001 From: yyforyongyu Date: Sat, 9 Nov 2024 14:39:12 +0800 Subject: [PATCH 16/32] itest: further reduce block mined in tests --- itest/lnd_channel_backup_test.go | 1 - itest/lnd_estimate_route_fee_test.go | 36 ++++++++++--------------- itest/lnd_hold_invoice_force_test.go | 2 +- itest/lnd_route_blinding_test.go | 39 ++++++++++++---------------- itest/lnd_wipe_fwdpkgs_test.go | 3 --- 5 files changed, 30 insertions(+), 51 deletions(-) diff --git a/itest/lnd_channel_backup_test.go b/itest/lnd_channel_backup_test.go index 5dc5a1729d..3d3e014355 100644 --- a/itest/lnd_channel_backup_test.go +++ b/itest/lnd_channel_backup_test.go @@ -1499,7 +1499,6 @@ func assertTimeLockSwept(ht *lntest.HarnessTest, carol, dave *node.HarnessNode, ht.AssertNumPendingSweeps(dave, 2) // Mine a block to trigger the sweeps. - ht.MineEmptyBlocks(1) daveSweep := ht.AssertNumTxsInMempool(1)[0] block := ht.MineBlocksAndAssertNumTxes(1, 1)[0] ht.AssertTxInBlock(block, daveSweep) diff --git a/itest/lnd_estimate_route_fee_test.go b/itest/lnd_estimate_route_fee_test.go index 80d19fc043..713cfe1ed8 100644 --- a/itest/lnd_estimate_route_fee_test.go +++ b/itest/lnd_estimate_route_fee_test.go @@ -94,21 +94,17 @@ func testEstimateRouteFee(ht *lntest.HarnessTest) { // added to the invoice always have enough liquidity, but here we check // that the prober uses the more expensive route. ht.EnsureConnected(mts.bob, paula) - channelPointBobPaula := ht.OpenChannel( - mts.bob, paula, lntest.OpenChannelParams{ - Private: true, - Amt: 90_000, - PushAmt: 69_000, - }, - ) + ht.OpenChannel(mts.bob, paula, lntest.OpenChannelParams{ + Private: true, + Amt: 90_000, + PushAmt: 69_000, + }) ht.EnsureConnected(mts.eve, paula) - channelPointEvePaula := ht.OpenChannel( - mts.eve, paula, lntest.OpenChannelParams{ - Private: true, - Amt: 1_000_000, - }, - ) + ht.OpenChannel(mts.eve, paula, lntest.OpenChannelParams{ + Private: true, + Amt: 1_000_000, + }) bobsPrivChannels := mts.bob.RPC.ListChannels(&lnrpc.ListChannelsRequest{ PrivateOnly: true, @@ -243,6 +239,8 @@ func testEstimateRouteFee(ht *lntest.HarnessTest) { locktime := initialBlockHeight + defaultTimelock + int64(routing.BlockPadding) + noChanNode := ht.NewNode("ImWithoutChannels", nil) + var testCases = []*estimateRouteFeeTestCase{ // Single hop payment is free. { @@ -304,10 +302,8 @@ func testEstimateRouteFee(ht *lntest.HarnessTest) { { name: "single hop hint, destination " + "without channels", - probing: true, - destination: ht.NewNode( - "ImWithoutChannels", nil, - ), + probing: true, + destination: noChanNode, routeHints: singleRouteHint, expectedRoutingFeesMsat: feeACBP, expectedCltvDelta: locktime + deltaACBP, @@ -357,12 +353,6 @@ func testEstimateRouteFee(ht *lntest.HarnessTest) { break } } - - mts.ht.CloseChannelAssertPending(mts.bob, channelPointBobPaula, false) - mts.ht.CloseChannelAssertPending(mts.eve, channelPointEvePaula, false) - ht.MineBlocksAndAssertNumTxes(1, 2) - - mts.closeChannels() } // runTestCase runs a single test case asserting that test conditions are met. diff --git a/itest/lnd_hold_invoice_force_test.go b/itest/lnd_hold_invoice_force_test.go index 19e44a8375..5fe8ea0daa 100644 --- a/itest/lnd_hold_invoice_force_test.go +++ b/itest/lnd_hold_invoice_force_test.go @@ -30,7 +30,7 @@ func testHoldInvoiceForceClose(ht *lntest.HarnessTest) { ) invoiceReq := &invoicesrpc.AddHoldInvoiceRequest{ Value: 30000, - CltvExpiry: 40, + CltvExpiry: finalCltvDelta, Hash: payHash[:], } bobInvoice := bob.RPC.AddHoldInvoice(invoiceReq) diff --git a/itest/lnd_route_blinding_test.go b/itest/lnd_route_blinding_test.go index 109a2db442..4b6f98d18e 100644 --- a/itest/lnd_route_blinding_test.go +++ b/itest/lnd_route_blinding_test.go @@ -5,6 +5,7 @@ import ( "crypto/sha256" "encoding/hex" "errors" + "fmt" "time" "github.com/btcsuite/btcd/btcec/v2" @@ -19,6 +20,10 @@ import ( "github.com/stretchr/testify/require" ) +// toLocalCSV is the CSV delay for the node's to_local output. We use a small +// value to save us from mining blocks. +var toLocalCSV = 2 + // testQueryBlindedRoutes tests querying routes to blinded routes. To do this, // it sets up a nework of Alice - Bob - Carol and creates a mock blinded route // that uses Carol as the introduction node (plus dummy hops to cover multiple @@ -346,14 +351,18 @@ func newBlindedForwardTest(ht *lntest.HarnessTest) (context.Context, func (b *blindedForwardTest) setupNetwork(ctx context.Context, withInterceptor bool) { - const chanAmt = btcutil.Amount(100000) - - carolArgs := []string{"--bitcoin.timelockdelta=18"} + carolArgs := []string{ + "--bitcoin.timelockdelta=18", + fmt.Sprintf("--bitcoin.defaultremotedelay=%v", toLocalCSV), + } if withInterceptor { carolArgs = append(carolArgs, "--requireinterceptor") } - daveArgs := []string{"--bitcoin.timelockdelta=18"} + daveArgs := []string{ + "--bitcoin.timelockdelta=18", + fmt.Sprintf("--bitcoin.defaultremotedelay=%v", toLocalCSV), + } cfgs := [][]string{nil, nil, carolArgs, daveArgs} param := lntest.OpenChannelParams{ Amt: chanAmt, @@ -780,11 +789,11 @@ func testErrorHandlingOnChainFailure(ht *lntest.HarnessTest) { // SuspendCarol so that she can't interfere with the resolution of the // HTLC from now on. - restartCarol := ht.SuspendNode(testCase.carol) + ht.SuspendNode(testCase.carol) // Mine blocks so that Bob will claim his CSV delayed local commitment, // we've already mined 1 block so we need one less than our CSV. - ht.MineBlocks(node.DefaultCSV - 1) + ht.MineBlocks(toLocalCSV - 1) ht.AssertNumPendingSweeps(bob, 1) ht.MineBlocksAndAssertNumTxes(1, 1) @@ -797,6 +806,7 @@ func testErrorHandlingOnChainFailure(ht *lntest.HarnessTest) { // value. info := bob.RPC.GetInfo() target := carolHTLC.IncomingExpiry - info.BlockHeight + ht.Log(carolHTLC.IncomingExpiry, info.BlockHeight, target) ht.MineBlocks(int(target)) // Wait for Bob's timeout transaction in the mempool, since we've @@ -819,23 +829,6 @@ func testErrorHandlingOnChainFailure(ht *lntest.HarnessTest) { lnrpc.Failure_INVALID_ONION_BLINDING, ) - // Clean up the rest of our force close: mine blocks so that Bob's CSV - // expires to trigger his sweep and then mine it. - ht.MineBlocks(node.DefaultCSV) - ht.AssertNumPendingSweeps(bob, 1) - ht.MineBlocksAndAssertNumTxes(1, 1) - - // Bring carol back up so that we can close out the rest of our - // channels cooperatively. She requires an interceptor to start up - // so we just re-register our interceptor. - require.NoError(ht, restartCarol()) - _, err = testCase.carol.RPC.Router.HtlcInterceptor(ctx) - require.NoError(ht, err, "interceptor") - - // Assert that Carol has started up and reconnected to dave so that - // we can close out channels cooperatively. - ht.EnsureConnected(testCase.carol, testCase.dave) - // Manually close out the rest of our channels and cancel (don't use // built in cleanup which will try close the already-force-closed // channel). diff --git a/itest/lnd_wipe_fwdpkgs_test.go b/itest/lnd_wipe_fwdpkgs_test.go index bd567f7a00..9aafd960a0 100644 --- a/itest/lnd_wipe_fwdpkgs_test.go +++ b/itest/lnd_wipe_fwdpkgs_test.go @@ -106,7 +106,4 @@ func testWipeForwardingPackages(ht *lntest.HarnessTest) { // Mine 1 block to get Alice's sweeping tx confirmed. ht.MineBlocksAndAssertNumTxes(1, 1) - - // Clean up the force closed channel. - ht.CleanupForceClose(bob) } From 5326b1ba4351e5d8695ef58bf4459f8a9ffb4e44 Mon Sep 17 00:00:00 2001 From: yyforyongyu Date: Thu, 7 Nov 2024 23:19:19 +0800 Subject: [PATCH 17/32] itest: track and skip flaky tests for windows To make the CI indicative, we now starting tracking the flaky tests found when running on Windows. As a starting point, rather than ignore the windows CI entirely, we now identify there are cases where lnd can be buggy when running in windows. We should fix the tests in the future, otherwise the windows build should be deleted. --- itest/list_exclude_test.go | 108 +++++++++++++++++++++++++++++++++++++ itest/list_on_test.go | 15 ++++++ itest/lnd_test.go | 5 ++ 3 files changed, 128 insertions(+) create mode 100644 itest/list_exclude_test.go diff --git a/itest/list_exclude_test.go b/itest/list_exclude_test.go new file mode 100644 index 0000000000..86567cf352 --- /dev/null +++ b/itest/list_exclude_test.go @@ -0,0 +1,108 @@ +//go:build integration + +package itest + +import ( + "fmt" + + "github.com/lightningnetwork/lnd/fn/v2" + "github.com/lightningnetwork/lnd/lntest" +) + +// excludedTestsWindows is a list of tests that are flaky on Windows and should +// be excluded from the test suite atm. +// +// TODO(yy): fix these tests and remove them from this list. +var excludedTestsWindows = []string{ + "batch channel funding", + "zero conf channel open", + "open channel with unstable utxos", + "funding flow persistence", + "channel policy update public zero conf", + + "listsweeps", + "sweep htlcs", + "sweep cpfp anchor incoming timeout", + "payment succeeded htlc remote swept", + "3rd party anchor spend", + + "send payment amp", + "async payments benchmark", + "async bidirectional payments", + + "multihop htlc aggregation leased", + "multihop htlc aggregation leased zero conf", + "multihop htlc aggregation anchor", + "multihop htlc aggregation anchor zero conf", + "multihop htlc aggregation simple taproot", + "multihop htlc aggregation simple taproot zero conf", + + "channel force closure anchor", + "channel force closure simple taproot", + "channel backup restore force close", + "wipe forwarding packages", + + "coop close with htlcs", + "coop close with external delivery", + + "forward interceptor restart", + "forward interceptor dedup htlcs", + "invoice HTLC modifier basic", + "lookup htlc resolution", + + "remote signer taproot", + "remote signer account import", + "remote signer bump fee", + "remote signer funding input types", + "remote signer funding async payments taproot", + "remote signer funding async payments", + "remote signer random seed", + "remote signer verify msg", + "remote signer channel open", + "remote signer shared key", + "remote signer psbt", + "remote signer sign output raw", + + "on chain to blinded", + "query blinded route", + + "data loss protection", +} + +// filterWindowsFlakyTests filters out the flaky tests that are excluded from +// the test suite on Windows. +func filterWindowsFlakyTests() []*lntest.TestCase { + // filteredTestCases is a substest of allTestCases that excludes the + // above flaky tests. + filteredTestCases := make([]*lntest.TestCase, 0, len(allTestCases)) + + // Create a set for the excluded test cases for fast lookup. + excludedSet := fn.NewSet(excludedTestsWindows...) + + // Remove the tests from the excludedSet if it's found in the list of + // all test cases. This is done to ensure the excluded tests are not + // pointing to a test case that doesn't exist. + for _, tc := range allTestCases { + if excludedSet.Contains(tc.Name) { + excludedSet.Remove(tc.Name) + + continue + } + + filteredTestCases = append(filteredTestCases, tc) + } + + // Exit early if all the excluded tests are found in allTestCases. + if excludedSet.IsEmpty() { + return filteredTestCases + } + + // Otherwise, print out the tests that are not found in allTestCases. + errStr := "\nThe following tests are not found, please make sure the " + + "test names are correct in `excludedTestsWindows`.\n" + for _, name := range excludedSet.ToSlice() { + errStr += fmt.Sprintf("Test not found in test suite: %v", name) + } + + panic(errStr) +} diff --git a/itest/list_on_test.go b/itest/list_on_test.go index 22bcd5866f..4b1ccafc12 100644 --- a/itest/list_on_test.go +++ b/itest/list_on_test.go @@ -695,4 +695,19 @@ func init() { allTestCases = append(allTestCases, walletImportAccountTestCases...) allTestCases = append(allTestCases, basicFundingTestCases...) allTestCases = append(allTestCases, sendToRouteTestCases...) + + // Prepare the test cases for windows to exclude some of the flaky + // ones. + // + // NOTE: We need to run this before the isWindowsOS check to make sure + // the excluded tests are found in allTestCases. Otherwise, if a + // non-existing test is included in excludedTestsWindows, we won't be + // able to find it until it's pushed to the CI, which creates a much + // longer feedback loop. + windowsTestCases := filterWindowsFlakyTests() + + // If this is Windows, we'll skip running some of the flaky tests. + if isWindowsOS() { + allTestCases = windowsTestCases + } } diff --git a/itest/lnd_test.go b/itest/lnd_test.go index 63462199ae..c1a43b4106 100644 --- a/itest/lnd_test.go +++ b/itest/lnd_test.go @@ -238,6 +238,11 @@ func isDarwin() bool { return runtime.GOOS == "darwin" } +// isWindowsOS returns true if the test is running on a Windows OS. +func isWindowsOS() bool { + return runtime.GOOS == "windows" +} + func init() { // Before we start any node, we need to make sure that any btcd node // that is started through the RPC harness uses a unique port as well From fc7f282d8472e035f8b71373236c1f6ef750b8eb Mon Sep 17 00:00:00 2001 From: yyforyongyu Date: Sat, 9 Nov 2024 22:39:34 +0800 Subject: [PATCH 18/32] lntest: increase node start timeout and payment benchmark timeout --- lntest/wait/timeouts_darwin.go | 16 +++++++++++++++- lntest/wait/timeouts_remote_db.go | 2 +- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/lntest/wait/timeouts_darwin.go b/lntest/wait/timeouts_darwin.go index f992d06b22..42b1d0dc6c 100644 --- a/lntest/wait/timeouts_darwin.go +++ b/lntest/wait/timeouts_darwin.go @@ -29,7 +29,21 @@ const ( // NodeStartTimeout is the timeout value when waiting for a node to // become fully started. - NodeStartTimeout = time.Minute * 2 + // + // TODO(yy): There is an optimization we can do to increase the time it + // takes to finish the initial wallet sync. Instead of finding the + // block birthday using binary search in btcwallet, we can instead + // search optimistically by looking at the chain tip minus X blocks to + // get the birthday block. This way in the test the node won't attempt + // to sync from the beginning of the chain, which is always the case + // due to how regtest blocks are mined. + // The other direction of optimization is to change the precision of + // the regtest block's median time. By consensus, we need to increase + // at least one second(?), this means in regtest when large amount of + // blocks are mined in a short time, the block time is actually in the + // future. We could instead allow the median time to increase by + // microseconds for itests. + NodeStartTimeout = time.Minute * 3 // SqliteBusyTimeout is the maximum time that a call to the sqlite db // will wait for the connection to become available. diff --git a/lntest/wait/timeouts_remote_db.go b/lntest/wait/timeouts_remote_db.go index 43cd6e022b..ae7043ff0e 100644 --- a/lntest/wait/timeouts_remote_db.go +++ b/lntest/wait/timeouts_remote_db.go @@ -29,7 +29,7 @@ const ( // AsyncBenchmarkTimeout is the timeout used when running the async // payments benchmark. - AsyncBenchmarkTimeout = time.Minute*2 + extraTimeout + AsyncBenchmarkTimeout = time.Minute*5 + extraTimeout // NodeStartTimeout is the timeout value when waiting for a node to // become fully started. From 5aec1ff157b2e69eb4cd872512662cb3dbbd4b47 Mon Sep 17 00:00:00 2001 From: yyforyongyu Date: Thu, 21 Nov 2024 22:31:13 +0800 Subject: [PATCH 19/32] lntest: make sure policies are populated in `AssertChannelInGraph` --- lntest/harness_assertion.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/lntest/harness_assertion.go b/lntest/harness_assertion.go index 0c14cd9e08..5cc6edeeda 100644 --- a/lntest/harness_assertion.go +++ b/lntest/harness_assertion.go @@ -1859,6 +1859,18 @@ func (h *HarnessTest) AssertChannelInGraph(hn *node.HarnessNode, op, err) } + // Make sure the policies are populated, otherwise this edge + // cannot be used for routing. + if resp.Node1Policy == nil { + return fmt.Errorf("channel %s has no policy1: %w", + op, err) + } + + if resp.Node2Policy == nil { + return fmt.Errorf("channel %s has no policy2: %w", + op, err) + } + edge = resp return nil From 9aea8520d770e74bad97847b8b6511647b5852d2 Mon Sep 17 00:00:00 2001 From: yyforyongyu Date: Sun, 10 Nov 2024 00:36:08 +0800 Subject: [PATCH 20/32] workflows: use `btcd` for macOS To increase the speed from 40m per run to roughly 20m per run. --- .github/workflows/main.yml | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 17d61f1746..8c5115146d 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -371,14 +371,8 @@ jobs: go-version: '${{ env.GO_VERSION }}' key-prefix: integration-test - - name: install bitcoind - run: | - wget https://bitcoincore.org/bin/bitcoin-core-${BITCOIN_VERSION}.0/bitcoin-${BITCOIN_VERSION}.0-arm64-apple-darwin.tar.gz - tar zxvf bitcoin-${BITCOIN_VERSION}.0-arm64-apple-darwin.tar.gz - mv bitcoin-${BITCOIN_VERSION}.0 /tmp/bitcoin - - name: run itest - run: PATH=$PATH:/tmp/bitcoin/bin make itest-parallel tranches=${{ env.TRANCHES }} backend=bitcoind shuffleseed=${{ github.run_id }} + run: make itest-parallel tranches=${{ env.TRANCHES }} shuffleseed=${{ github.run_id }} - name: Zip log files on failure if: ${{ failure() }} From 9a819b8c6d283a62a56cf465132e8c3a2390de0c Mon Sep 17 00:00:00 2001 From: yyforyongyu Date: Mon, 25 Nov 2024 21:56:47 +0800 Subject: [PATCH 21/32] itest+lntest: add new method `FundNumCoins` Most of the time we only need to fund the node with given number of UTXOs without concerning the amount, so we add the more efficient funding method as it mines a single block in the end. --- itest/lnd_sweep_test.go | 12 ++++++------ lntest/harness.go | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 6 deletions(-) diff --git a/itest/lnd_sweep_test.go b/itest/lnd_sweep_test.go index 21e8df679d..e920259949 100644 --- a/itest/lnd_sweep_test.go +++ b/itest/lnd_sweep_test.go @@ -771,24 +771,24 @@ func testSweepHTLCs(ht *lntest.HarnessTest) { // Bob needs two more wallet utxos: // - when sweeping anchors, he needs one utxo for each sweep. // - when sweeping HTLCs, he needs one utxo for each sweep. - ht.FundCoins(btcutil.SatoshiPerBitcoin, bob) - ht.FundCoins(btcutil.SatoshiPerBitcoin, bob) + numUTXOs := 2 // Bob should have enough wallet UTXOs here to sweep the HTLC in the // end of this test. However, due to a known issue, Bob's wallet may // report there's no UTXO available. For details, // - https://github.com/lightningnetwork/lnd/issues/8786 // - // TODO(yy): remove this step once the issue is resolved. - ht.FundCoins(btcutil.SatoshiPerBitcoin, bob) + // TODO(yy): remove this extra UTXO once the issue is resolved. + numUTXOs++ // For neutrino backend, we need two more UTXOs for Bob to create his // sweeping txns. if ht.IsNeutrinoBackend() { - ht.FundCoins(btcutil.SatoshiPerBitcoin, bob) - ht.FundCoins(btcutil.SatoshiPerBitcoin, bob) + numUTXOs += 2 } + ht.FundNumCoins(bob, numUTXOs) + // Subscribe the invoices. stream1 := carol.RPC.SubscribeSingleInvoice(payHashSettled[:]) stream2 := carol.RPC.SubscribeSingleInvoice(payHashHold[:]) diff --git a/lntest/harness.go b/lntest/harness.go index aca4ec7dd9..e9eb9898ec 100644 --- a/lntest/harness.go +++ b/lntest/harness.go @@ -1379,6 +1379,40 @@ func (h *HarnessTest) FundCoinsP2TR(amt btcutil.Amount, h.fundCoins(amt, target, lnrpc.AddressType_TAPROOT_PUBKEY, true) } +// FundNumCoins attempts to send the given number of UTXOs from the internal +// mining node to the targeted lightning node using a P2WKH address. Each UTXO +// has an amount of 1 BTC. 1 blocks are mined to confirm the tx. +func (h *HarnessTest) FundNumCoins(hn *node.HarnessNode, num int) { + // Get the initial balance first. + resp := hn.RPC.WalletBalance() + initialBalance := btcutil.Amount(resp.ConfirmedBalance) + + const fundAmount = 1 * btcutil.SatoshiPerBitcoin + + // Send out the outputs from the miner. + for i := 0; i < num; i++ { + h.createAndSendOutput( + hn, fundAmount, lnrpc.AddressType_WITNESS_PUBKEY_HASH, + ) + } + + // Wait for ListUnspent to show the correct number of unconfirmed + // UTXOs. + // + // Since neutrino doesn't support unconfirmed outputs, skip this check. + if !h.IsNeutrinoBackend() { + h.AssertNumUTXOsUnconfirmed(hn, num) + } + + // Mine a block to confirm the transactions. + h.MineBlocksAndAssertNumTxes(1, num) + + // Now block until the wallet have fully synced up. + totalAmount := btcutil.Amount(fundAmount * num) + expectedBalance := initialBalance + totalAmount + h.WaitForBalanceConfirmed(hn, expectedBalance) +} + // completePaymentRequestsAssertStatus sends payments from a node to complete // all payment requests. This function does not return until all payments // have reached the specified status. From ef54c92928e708ddc466dcde4040e8f74ebae83b Mon Sep 17 00:00:00 2001 From: yyforyongyu Date: Mon, 25 Nov 2024 17:15:34 +0800 Subject: [PATCH 22/32] lntest: limit the num of blocks mined in each test --- lntest/harness.go | 44 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 39 insertions(+), 5 deletions(-) diff --git a/lntest/harness.go b/lntest/harness.go index e9eb9898ec..d504c7e2a5 100644 --- a/lntest/harness.go +++ b/lntest/harness.go @@ -326,11 +326,8 @@ func (h *HarnessTest) Subtest(t *testing.T) *HarnessTest { startHeight := int32(h.CurrentHeight()) st.Cleanup(func() { - _, endHeight := h.GetBestBlock() - - st.Logf("finished test: %s, start height=%d, end height=%d, "+ - "mined blocks=%d", st.manager.currentTestCase, - startHeight, endHeight, endHeight-startHeight) + // Make sure the test is not consuming too many blocks. + st.checkAndLimitBlocksMined(startHeight) // Don't bother run the cleanups if the test is failed. if st.Failed() { @@ -368,6 +365,43 @@ func (h *HarnessTest) Subtest(t *testing.T) *HarnessTest { return st } +// checkAndLimitBlocksMined asserts that the blocks mined in a single test +// doesn't exceed 50, which implicitly discourage table-drive tests, which are +// hard to maintain and take a long time to run. +func (h *HarnessTest) checkAndLimitBlocksMined(startHeight int32) { + _, endHeight := h.GetBestBlock() + blocksMined := endHeight - startHeight + + h.Logf("finished test: %s, start height=%d, end height=%d, mined "+ + "blocks=%d", h.manager.currentTestCase, startHeight, endHeight, + blocksMined) + + // If the number of blocks is less than 40, we consider the test + // healthy. + if blocksMined < 40 { + return + } + + // Otherwise log a warning if it's mining more than 40 blocks. + desc := "!============================================!\n" + + desc += fmt.Sprintf("Too many blocks (%v) mined in one test! Tips:\n", + blocksMined) + + desc += "1. break test into smaller individual tests, especially if " + + "this is a table-drive test.\n" + + "2. use smaller CSV via `--bitcoin.defaultremotedelay=1.`\n" + + "3. use smaller CLTV via `--bitcoin.timelockdelta=18.`\n" + + "4. remove unnecessary CloseChannel when test ends.\n" + + "5. use `CreateSimpleNetwork` for efficient channel creation.\n" + h.Log(desc) + + // We enforce that the test should not mine more than 50 blocks, which + // is more than enough to test a multi hop force close scenario. + require.LessOrEqual(h, int(blocksMined), 50, "cannot mine more than "+ + "50 blocks in one test") +} + // shutdownAllNodes will shutdown all running nodes. func (h *HarnessTest) shutdownAllNodes() { for _, node := range h.manager.activeNodes { From 72b0985c0dee3b5b50f6eda2b7e0a9298c158f82 Mon Sep 17 00:00:00 2001 From: yyforyongyu Date: Tue, 26 Nov 2024 17:45:12 +0800 Subject: [PATCH 23/32] docs: update release notes --- docs/release-notes/release-notes-0.19.0.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/release-notes/release-notes-0.19.0.md b/docs/release-notes/release-notes-0.19.0.md index d2ef485d6b..a09d60566d 100644 --- a/docs/release-notes/release-notes-0.19.0.md +++ b/docs/release-notes/release-notes-0.19.0.md @@ -217,6 +217,10 @@ The underlying functionality between those two options remain the same. estimator provided by bitcoind or btcd in regtest and simnet modes instead of static fee estimator if feeurl is not provided. +* The integration tests CI have been optimized to run faster and all flakes are + now documented and + [fixedo](https://github.com/lightningnetwork/lnd/pull/9260). + ## Database * [Migrate the mission control From b0a1f9034982a13e0e0e05acf5691cdae7898c84 Mon Sep 17 00:00:00 2001 From: yyforyongyu Date: Wed, 4 Dec 2024 06:29:22 +0800 Subject: [PATCH 24/32] itest: add a prefix before appending a subtest case --- itest/list_exclude_test.go | 43 +++++----- itest/list_on_test.go | 63 +++++++++++--- itest/lnd_channel_backup_test.go | 22 ++--- ...lnd_channel_funding_utxo_selection_test.go | 14 ++-- itest/lnd_funding_test.go | 6 +- itest/lnd_multi-hop_force_close_test.go | 84 +++++++++---------- itest/lnd_open_channel_test.go | 10 +-- itest/lnd_remote_signer_test.go | 24 +++--- itest/lnd_routing_test.go | 6 +- itest/lnd_wallet_import_test.go | 16 +--- itest/lnd_watchtower_test.go | 6 +- 11 files changed, 165 insertions(+), 129 deletions(-) diff --git a/itest/list_exclude_test.go b/itest/list_exclude_test.go index 86567cf352..aba4e79b8d 100644 --- a/itest/list_exclude_test.go +++ b/itest/list_exclude_test.go @@ -18,7 +18,9 @@ var excludedTestsWindows = []string{ "zero conf channel open", "open channel with unstable utxos", "funding flow persistence", - "channel policy update public zero conf", + + // Gives "channel link not found" error. + "zero conf-channel policy update public zero conf", "listsweeps", "sweep htlcs", @@ -30,12 +32,12 @@ var excludedTestsWindows = []string{ "async payments benchmark", "async bidirectional payments", - "multihop htlc aggregation leased", - "multihop htlc aggregation leased zero conf", - "multihop htlc aggregation anchor", - "multihop htlc aggregation anchor zero conf", - "multihop htlc aggregation simple taproot", - "multihop htlc aggregation simple taproot zero conf", + "multihop-htlc aggregation leased", + "multihop-htlc aggregation leased zero conf", + "multihop-htlc aggregation anchor", + "multihop-htlc aggregation anchor zero conf", + "multihop-htlc aggregation simple taproot", + "multihop-htlc aggregation simple taproot zero conf", "channel force closure anchor", "channel force closure simple taproot", @@ -50,18 +52,18 @@ var excludedTestsWindows = []string{ "invoice HTLC modifier basic", "lookup htlc resolution", - "remote signer taproot", - "remote signer account import", - "remote signer bump fee", - "remote signer funding input types", - "remote signer funding async payments taproot", - "remote signer funding async payments", - "remote signer random seed", - "remote signer verify msg", - "remote signer channel open", - "remote signer shared key", - "remote signer psbt", - "remote signer sign output raw", + "remote signer-taproot", + "remote signer-account import", + "remote signer-bump fee", + "remote signer-funding input types", + "remote signer-funding async payments taproot", + "remote signer-funding async payments", + "remote signer-random seed", + "remote signer-verify msg", + "remote signer-channel open", + "remote signer-shared key", + "remote signer-psbt", + "remote signer-sign output raw", "on chain to blinded", "query blinded route", @@ -101,7 +103,8 @@ func filterWindowsFlakyTests() []*lntest.TestCase { errStr := "\nThe following tests are not found, please make sure the " + "test names are correct in `excludedTestsWindows`.\n" for _, name := range excludedSet.ToSlice() { - errStr += fmt.Sprintf("Test not found in test suite: %v", name) + errStr += fmt.Sprintf("Test not found in test suite: %v\n", + name) } panic(errStr) diff --git a/itest/list_on_test.go b/itest/list_on_test.go index 4b1ccafc12..be3244fe5e 100644 --- a/itest/list_on_test.go +++ b/itest/list_on_test.go @@ -3,6 +3,8 @@ package itest import ( + "fmt" + "github.com/lightningnetwork/lnd/lntest" ) @@ -682,19 +684,58 @@ var allTestCases = []*lntest.TestCase{ }, } +// appendPrefixed is used to add a prefix to each test name in the subtests +// before appending them to the main test cases. +func appendPrefixed(prefix string, testCases, + subtestCases []*lntest.TestCase) []*lntest.TestCase { + + for _, tc := range subtestCases { + name := fmt.Sprintf("%s-%s", prefix, tc.Name) + testCases = append(testCases, &lntest.TestCase{ + Name: name, + TestFunc: tc.TestFunc, + }) + } + + return testCases +} + func init() { // Register subtests. - allTestCases = append(allTestCases, multiHopForceCloseTestCases...) - allTestCases = append(allTestCases, watchtowerTestCases...) - allTestCases = append(allTestCases, psbtFundingTestCases...) - allTestCases = append(allTestCases, remoteSignerTestCases...) - allTestCases = append(allTestCases, channelRestoreTestCases...) - allTestCases = append(allTestCases, fundUtxoSelectionTestCases...) - allTestCases = append(allTestCases, zeroConfPolicyTestCases...) - allTestCases = append(allTestCases, channelFeePolicyTestCases...) - allTestCases = append(allTestCases, walletImportAccountTestCases...) - allTestCases = append(allTestCases, basicFundingTestCases...) - allTestCases = append(allTestCases, sendToRouteTestCases...) + allTestCases = appendPrefixed( + "multihop", allTestCases, multiHopForceCloseTestCases, + ) + allTestCases = appendPrefixed( + "watchtower", allTestCases, watchtowerTestCases, + ) + allTestCases = appendPrefixed( + "psbt", allTestCases, psbtFundingTestCases, + ) + allTestCases = appendPrefixed( + "remote signer", allTestCases, remoteSignerTestCases, + ) + allTestCases = appendPrefixed( + "channel backup", allTestCases, channelRestoreTestCases, + ) + allTestCases = appendPrefixed( + "utxo selection", allTestCases, fundUtxoSelectionTestCases, + ) + allTestCases = appendPrefixed( + "zero conf", allTestCases, zeroConfPolicyTestCases, + ) + allTestCases = appendPrefixed( + "channel fee policy", allTestCases, channelFeePolicyTestCases, + ) + allTestCases = appendPrefixed( + "wallet import account", allTestCases, + walletImportAccountTestCases, + ) + allTestCases = appendPrefixed( + "funding", allTestCases, basicFundingTestCases, + ) + allTestCases = appendPrefixed( + "send to route", allTestCases, sendToRouteTestCases, + ) // Prepare the test cases for windows to exclude some of the flaky // ones. diff --git a/itest/lnd_channel_backup_test.go b/itest/lnd_channel_backup_test.go index 3d3e014355..f44f468884 100644 --- a/itest/lnd_channel_backup_test.go +++ b/itest/lnd_channel_backup_test.go @@ -29,7 +29,7 @@ var channelRestoreTestCases = []*lntest.TestCase{ { // Restore the backup from the on-disk file, using the RPC // interface, for anchor commitment channels. - Name: "channel backup restore anchor", + Name: "restore anchor", TestFunc: func(ht *lntest.HarnessTest) { runChanRestoreScenarioCommitTypes( ht, lnrpc.CommitmentType_ANCHORS, false, @@ -39,7 +39,7 @@ var channelRestoreTestCases = []*lntest.TestCase{ { // Restore the backup from the on-disk file, using the RPC // interface, for script-enforced leased channels. - Name: "channel backup restore leased", + Name: "restore leased", TestFunc: func(ht *lntest.HarnessTest) { runChanRestoreScenarioCommitTypes( ht, leasedType, false, @@ -49,7 +49,7 @@ var channelRestoreTestCases = []*lntest.TestCase{ { // Restore the backup from the on-disk file, using the RPC // interface, for zero-conf anchor channels. - Name: "channel backup restore anchor zero conf", + Name: "restore anchor zero conf", TestFunc: func(ht *lntest.HarnessTest) { runChanRestoreScenarioCommitTypes( ht, lnrpc.CommitmentType_ANCHORS, true, @@ -59,7 +59,7 @@ var channelRestoreTestCases = []*lntest.TestCase{ { // Restore the backup from the on-disk file, using the RPC // interface for a zero-conf script-enforced leased channel. - Name: "channel backup restore leased zero conf", + Name: "restore leased zero conf", TestFunc: func(ht *lntest.HarnessTest) { runChanRestoreScenarioCommitTypes( ht, leasedType, true, @@ -69,7 +69,7 @@ var channelRestoreTestCases = []*lntest.TestCase{ { // Restore a channel back up of a taproot channel that was // confirmed. - Name: "channel backup restore simple taproot", + Name: "restore simple taproot", TestFunc: func(ht *lntest.HarnessTest) { runChanRestoreScenarioCommitTypes( ht, lnrpc.CommitmentType_SIMPLE_TAPROOT, false, @@ -78,7 +78,7 @@ var channelRestoreTestCases = []*lntest.TestCase{ }, { // Restore a channel back up of an unconfirmed taproot channel. - Name: "channel backup restore simple taproot zero conf", + Name: "restore simple taproot zero conf", TestFunc: func(ht *lntest.HarnessTest) { runChanRestoreScenarioCommitTypes( ht, lnrpc.CommitmentType_SIMPLE_TAPROOT, true, @@ -86,23 +86,23 @@ var channelRestoreTestCases = []*lntest.TestCase{ }, }, { - Name: "channel backup restore from rpc", + Name: "restore from rpc", TestFunc: testChannelBackupRestoreFromRPC, }, { - Name: "channel backup restore from file", + Name: "restore from file", TestFunc: testChannelBackupRestoreFromFile, }, { - Name: "channel backup restore during creation", + Name: "restore during creation", TestFunc: testChannelBackupRestoreDuringCreation, }, { - Name: "channel backup restore during unlock", + Name: "restore during unlock", TestFunc: testChannelBackupRestoreDuringUnlock, }, { - Name: "channel backup restore twice", + Name: "restore twice", TestFunc: testChannelBackupRestoreTwice, }, } diff --git a/itest/lnd_channel_funding_utxo_selection_test.go b/itest/lnd_channel_funding_utxo_selection_test.go index 7868b73338..c5ee72e6a0 100644 --- a/itest/lnd_channel_funding_utxo_selection_test.go +++ b/itest/lnd_channel_funding_utxo_selection_test.go @@ -17,31 +17,31 @@ import ( var fundUtxoSelectionTestCases = []*lntest.TestCase{ { - Name: "utxo selection funding error", + Name: "funding error", TestFunc: testChannelUtxoSelectionError, }, { - Name: "utxo selection selected valid chan size", + Name: "selected valid chan size", TestFunc: testUtxoSelectionSelectedValidChanSize, }, { - Name: "utxo selection selected valid chan reserve", + Name: "selected valid chan reserve", TestFunc: testUtxoSelectionSelectedValidChanReserve, }, { - Name: "utxo selection selected reserve from selected", + Name: "selected reserve from selected", TestFunc: testUtxoSelectionReserveFromSelected, }, { - Name: "utxo selection fundmax", + Name: "fundmax", TestFunc: testUtxoSelectionFundmax, }, { - Name: "utxo selection fundmax reserve", + Name: "fundmax reserve", TestFunc: testUtxoSelectionFundmaxReserve, }, { - Name: "utxo selection reused utxo", + Name: "reused utxo", TestFunc: testUtxoSelectionReuseUTXO, }, } diff --git a/itest/lnd_funding_test.go b/itest/lnd_funding_test.go index c034b1751d..b1c8b9278b 100644 --- a/itest/lnd_funding_test.go +++ b/itest/lnd_funding_test.go @@ -28,15 +28,15 @@ import ( // basicFundingTestCases defines the test cases for the basic funding test. var basicFundingTestCases = []*lntest.TestCase{ { - Name: "basic funding flow static key remote", + Name: "basic flow static key remote", TestFunc: testBasicChannelFundingStaticRemote, }, { - Name: "basic funding flow anchor", + Name: "basic flow anchor", TestFunc: testBasicChannelFundingAnchor, }, { - Name: "basic funding flow simple taproot", + Name: "basic flow simple taproot", TestFunc: testBasicChannelFundingSimpleTaproot, }, } diff --git a/itest/lnd_multi-hop_force_close_test.go b/itest/lnd_multi-hop_force_close_test.go index dc18792ea4..fde18c67db 100644 --- a/itest/lnd_multi-hop_force_close_test.go +++ b/itest/lnd_multi-hop_force_close_test.go @@ -29,171 +29,171 @@ var leasedType = lnrpc.CommitmentType_SCRIPT_ENFORCED_LEASE //nolint:ll var multiHopForceCloseTestCases = []*lntest.TestCase{ { - Name: "multihop local claim outgoing htlc anchor", + Name: "local claim outgoing htlc anchor", TestFunc: testLocalClaimOutgoingHTLCAnchor, }, { - Name: "multihop local claim outgoing htlc anchor zero conf", + Name: "local claim outgoing htlc anchor zero conf", TestFunc: testLocalClaimOutgoingHTLCAnchorZeroConf, }, { - Name: "multihop local claim outgoing htlc simple taproot", + Name: "local claim outgoing htlc simple taproot", TestFunc: testLocalClaimOutgoingHTLCSimpleTaproot, }, { - Name: "multihop local claim outgoing htlc simple taproot zero conf", + Name: "local claim outgoing htlc simple taproot zero conf", TestFunc: testLocalClaimOutgoingHTLCSimpleTaprootZeroConf, }, { - Name: "multihop local claim outgoing htlc leased", + Name: "local claim outgoing htlc leased", TestFunc: testLocalClaimOutgoingHTLCLeased, }, { - Name: "multihop local claim outgoing htlc leased zero conf", + Name: "local claim outgoing htlc leased zero conf", TestFunc: testLocalClaimOutgoingHTLCLeasedZeroConf, }, { - Name: "multihop receiver preimage claim anchor", + Name: "receiver preimage claim anchor", TestFunc: testMultiHopReceiverPreimageClaimAnchor, }, { - Name: "multihop receiver preimage claim anchor zero conf", + Name: "receiver preimage claim anchor zero conf", TestFunc: testMultiHopReceiverPreimageClaimAnchorZeroConf, }, { - Name: "multihop receiver preimage claim simple taproot", + Name: "receiver preimage claim simple taproot", TestFunc: testMultiHopReceiverPreimageClaimSimpleTaproot, }, { - Name: "multihop receiver preimage claim simple taproot zero conf", + Name: "receiver preimage claim simple taproot zero conf", TestFunc: testMultiHopReceiverPreimageClaimSimpleTaprootZeroConf, }, { - Name: "multihop receiver preimage claim leased", + Name: "receiver preimage claim leased", TestFunc: testMultiHopReceiverPreimageClaimLeased, }, { - Name: "multihop receiver preimage claim leased zero conf", + Name: "receiver preimage claim leased zero conf", TestFunc: testMultiHopReceiverPreimageClaimLeasedZeroConf, }, { - Name: "multihop local force close before timeout anchor", + Name: "local force close before timeout anchor", TestFunc: testLocalForceCloseBeforeTimeoutAnchor, }, { - Name: "multihop local force close before timeout anchor zero conf", + Name: "local force close before timeout anchor zero conf", TestFunc: testLocalForceCloseBeforeTimeoutAnchorZeroConf, }, { - Name: "multihop local force close before timeout simple taproot", + Name: "local force close before timeout simple taproot", TestFunc: testLocalForceCloseBeforeTimeoutSimpleTaproot, }, { - Name: "multihop local force close before timeout simple taproot zero conf", + Name: "local force close before timeout simple taproot zero conf", TestFunc: testLocalForceCloseBeforeTimeoutSimpleTaprootZeroConf, }, { - Name: "multihop local force close before timeout leased", + Name: "local force close before timeout leased", TestFunc: testLocalForceCloseBeforeTimeoutLeased, }, { - Name: "multihop local force close before timeout leased zero conf", + Name: "local force close before timeout leased zero conf", TestFunc: testLocalForceCloseBeforeTimeoutLeasedZeroConf, }, { - Name: "multihop remote force close before timeout anchor", + Name: "remote force close before timeout anchor", TestFunc: testRemoteForceCloseBeforeTimeoutAnchor, }, { - Name: "multihop remote force close before timeout anchor zero conf", + Name: "remote force close before timeout anchor zero conf", TestFunc: testRemoteForceCloseBeforeTimeoutAnchorZeroConf, }, { - Name: "multihop remote force close before timeout simple taproot", + Name: "remote force close before timeout simple taproot", TestFunc: testRemoteForceCloseBeforeTimeoutSimpleTaproot, }, { - Name: "multihop remote force close before timeout simple taproot zero conf", + Name: "remote force close before timeout simple taproot zero conf", TestFunc: testRemoteForceCloseBeforeTimeoutSimpleTaprootZeroConf, }, { - Name: "multihop remote force close before timeout leased", + Name: "remote force close before timeout leased", TestFunc: testRemoteForceCloseBeforeTimeoutLeased, }, { - Name: "multihop remote force close before timeout leased zero conf", + Name: "remote force close before timeout leased zero conf", TestFunc: testRemoteForceCloseBeforeTimeoutLeasedZeroConf, }, { - Name: "multihop local claim incoming htlc anchor", + Name: "local claim incoming htlc anchor", TestFunc: testLocalClaimIncomingHTLCAnchor, }, { - Name: "multihop local claim incoming htlc anchor zero conf", + Name: "local claim incoming htlc anchor zero conf", TestFunc: testLocalClaimIncomingHTLCAnchorZeroConf, }, { - Name: "multihop local claim incoming htlc simple taproot", + Name: "local claim incoming htlc simple taproot", TestFunc: testLocalClaimIncomingHTLCSimpleTaproot, }, { - Name: "multihop local claim incoming htlc simple taproot zero conf", + Name: "local claim incoming htlc simple taproot zero conf", TestFunc: testLocalClaimIncomingHTLCSimpleTaprootZeroConf, }, { - Name: "multihop local claim incoming htlc leased", + Name: "local claim incoming htlc leased", TestFunc: testLocalClaimIncomingHTLCLeased, }, { - Name: "multihop local claim incoming htlc leased zero conf", + Name: "local claim incoming htlc leased zero conf", TestFunc: testLocalClaimIncomingHTLCLeasedZeroConf, }, { - Name: "multihop local preimage claim anchor", + Name: "local preimage claim anchor", TestFunc: testLocalPreimageClaimAnchor, }, { - Name: "multihop local preimage claim anchor zero conf", + Name: "local preimage claim anchor zero conf", TestFunc: testLocalPreimageClaimAnchorZeroConf, }, { - Name: "multihop local preimage claim simple taproot", + Name: "local preimage claim simple taproot", TestFunc: testLocalPreimageClaimSimpleTaproot, }, { - Name: "multihop local preimage claim simple taproot zero conf", + Name: "local preimage claim simple taproot zero conf", TestFunc: testLocalPreimageClaimSimpleTaprootZeroConf, }, { - Name: "multihop local preimage claim leased", + Name: "local preimage claim leased", TestFunc: testLocalPreimageClaimLeased, }, { - Name: "multihop local preimage claim leased zero conf", + Name: "local preimage claim leased zero conf", TestFunc: testLocalPreimageClaimLeasedZeroConf, }, { - Name: "multihop htlc aggregation anchor", + Name: "htlc aggregation anchor", TestFunc: testHtlcAggregaitonAnchor, }, { - Name: "multihop htlc aggregation anchor zero conf", + Name: "htlc aggregation anchor zero conf", TestFunc: testHtlcAggregaitonAnchorZeroConf, }, { - Name: "multihop htlc aggregation simple taproot", + Name: "htlc aggregation simple taproot", TestFunc: testHtlcAggregaitonSimpleTaproot, }, { - Name: "multihop htlc aggregation simple taproot zero conf", + Name: "htlc aggregation simple taproot zero conf", TestFunc: testHtlcAggregaitonSimpleTaprootZeroConf, }, { - Name: "multihop htlc aggregation leased", + Name: "htlc aggregation leased", TestFunc: testHtlcAggregaitonLeased, }, { - Name: "multihop htlc aggregation leased zero conf", + Name: "htlc aggregation leased zero conf", TestFunc: testHtlcAggregaitonLeasedZeroConf, }, } diff --git a/itest/lnd_open_channel_test.go b/itest/lnd_open_channel_test.go index 9d51fc5635..84bf8cc353 100644 --- a/itest/lnd_open_channel_test.go +++ b/itest/lnd_open_channel_test.go @@ -21,23 +21,23 @@ import ( // policy fee behavior. var channelFeePolicyTestCases = []*lntest.TestCase{ { - Name: "channel fee policy default", + Name: "default", TestFunc: testChannelFeePolicyDefault, }, { - Name: "channel fee policy base fee", + Name: "base fee", TestFunc: testChannelFeePolicyBaseFee, }, { - Name: "channel fee policy fee rate", + Name: "fee rate", TestFunc: testChannelFeePolicyFeeRate, }, { - Name: "channel fee policy base fee and fee rate", + Name: "base fee and fee rate", TestFunc: testChannelFeePolicyBaseFeeAndFeeRate, }, { - Name: "channel fee policy low base fee and fee rate", + Name: "low base fee and fee rate", TestFunc: testChannelFeePolicyLowBaseFeeAndFeeRate, }, } diff --git a/itest/lnd_remote_signer_test.go b/itest/lnd_remote_signer_test.go index 6cbda365aa..fd48df6432 100644 --- a/itest/lnd_remote_signer_test.go +++ b/itest/lnd_remote_signer_test.go @@ -20,51 +20,51 @@ import ( // signer. var remoteSignerTestCases = []*lntest.TestCase{ { - Name: "remote signer random seed", + Name: "random seed", TestFunc: testRemoteSignerRadomSeed, }, { - Name: "remote signer account import", + Name: "account import", TestFunc: testRemoteSignerAccountImport, }, { - Name: "remote signer channel open", + Name: "channel open", TestFunc: testRemoteSignerChannelOpen, }, { - Name: "remote signer funding input types", + Name: "funding input types", TestFunc: testRemoteSignerChannelFundingInputTypes, }, { - Name: "remote signer funding async payments", + Name: "funding async payments", TestFunc: testRemoteSignerAsyncPayments, }, { - Name: "remote signer funding async payments taproot", + Name: "funding async payments taproot", TestFunc: testRemoteSignerAsyncPaymentsTaproot, }, { - Name: "remote signer shared key", + Name: "shared key", TestFunc: testRemoteSignerSharedKey, }, { - Name: "remote signer bump fee", + Name: "bump fee", TestFunc: testRemoteSignerBumpFee, }, { - Name: "remote signer psbt", + Name: "psbt", TestFunc: testRemoteSignerPSBT, }, { - Name: "remote signer sign output raw", + Name: "sign output raw", TestFunc: testRemoteSignerSignOutputRaw, }, { - Name: "remote signer verify msg", + Name: "verify msg", TestFunc: testRemoteSignerSignVerifyMsg, }, { - Name: "remote signer taproot", + Name: "taproot", TestFunc: testRemoteSignerTaproot, }, } diff --git a/itest/lnd_routing_test.go b/itest/lnd_routing_test.go index c32e021d44..802f1831bf 100644 --- a/itest/lnd_routing_test.go +++ b/itest/lnd_routing_test.go @@ -22,21 +22,21 @@ import ( var sendToRouteTestCases = []*lntest.TestCase{ { - Name: "single hop send to route sync", + Name: "single hop with sync", TestFunc: func(ht *lntest.HarnessTest) { // useStream: false, routerrpc: false. testSingleHopSendToRouteCase(ht, false, false) }, }, { - Name: "single hop send to route stream", + Name: "single hop with stream", TestFunc: func(ht *lntest.HarnessTest) { // useStream: true, routerrpc: false. testSingleHopSendToRouteCase(ht, true, false) }, }, { - Name: "single hop send to route v2", + Name: "single hop with v2", TestFunc: func(ht *lntest.HarnessTest) { // useStream: false, routerrpc: true. testSingleHopSendToRouteCase(ht, false, true) diff --git a/itest/lnd_wallet_import_test.go b/itest/lnd_wallet_import_test.go index f861390337..4a08fb2e6c 100644 --- a/itest/lnd_wallet_import_test.go +++ b/itest/lnd_wallet_import_test.go @@ -31,15 +31,7 @@ import ( //nolint:ll var walletImportAccountTestCases = []*lntest.TestCase{ { - Name: "wallet import account standard BIP-0044", - TestFunc: func(ht *lntest.HarnessTest) { - testWalletImportAccountScenario( - ht, walletrpc.AddressType_WITNESS_PUBKEY_HASH, - ) - }, - }, - { - Name: "wallet import account standard BIP-0049", + Name: "standard BIP-0049", TestFunc: func(ht *lntest.HarnessTest) { testWalletImportAccountScenario( ht, walletrpc.AddressType_NESTED_WITNESS_PUBKEY_HASH, @@ -47,7 +39,7 @@ var walletImportAccountTestCases = []*lntest.TestCase{ }, }, { - Name: "wallet import account lnd BIP-0049 variant", + Name: "lnd BIP-0049 variant", TestFunc: func(ht *lntest.HarnessTest) { testWalletImportAccountScenario( ht, walletrpc.AddressType_HYBRID_NESTED_WITNESS_PUBKEY_HASH, @@ -55,7 +47,7 @@ var walletImportAccountTestCases = []*lntest.TestCase{ }, }, { - Name: "wallet import account standard BIP-0084", + Name: "standard BIP-0084", TestFunc: func(ht *lntest.HarnessTest) { testWalletImportAccountScenario( ht, walletrpc.AddressType_WITNESS_PUBKEY_HASH, @@ -63,7 +55,7 @@ var walletImportAccountTestCases = []*lntest.TestCase{ }, }, { - Name: "wallet import account standard BIP-0086", + Name: "standard BIP-0086", TestFunc: func(ht *lntest.HarnessTest) { testWalletImportAccountScenario( ht, walletrpc.AddressType_TAPROOT_PUBKEY, diff --git a/itest/lnd_watchtower_test.go b/itest/lnd_watchtower_test.go index 1f5ad6ea31..73857bab93 100644 --- a/itest/lnd_watchtower_test.go +++ b/itest/lnd_watchtower_test.go @@ -23,15 +23,15 @@ import ( // watchtower client and server. var watchtowerTestCases = []*lntest.TestCase{ { - Name: "watchtower revoked close retribution altruist", + Name: "revoked close retribution altruist", TestFunc: testRevokedCloseRetributionAltruistWatchtower, }, { - Name: "watchtower client session deletion", + Name: "client session deletion", TestFunc: testTowerClientSessionDeletion, }, { - Name: "watchtower client tower and session management", + Name: "client tower and session management", TestFunc: testTowerClientTowerAndSessionManagement, }, } From 0ac77d57998562d22314b88ef41a12fb42f839bf Mon Sep 17 00:00:00 2001 From: yyforyongyu Date: Wed, 4 Dec 2024 09:51:10 +0800 Subject: [PATCH 25/32] itest: even out num of tests per tranche Previous splitting logic simply put all the remainder in the last tranche, which could make the last tranche run significantly more test cases. We now change it so the remainder is evened out across tranches. --- itest/lnd_test.go | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/itest/lnd_test.go b/itest/lnd_test.go index c1a43b4106..07b8554f2f 100644 --- a/itest/lnd_test.go +++ b/itest/lnd_test.go @@ -174,6 +174,30 @@ func maybeShuffleTestCases() { }) } +// createIndices divides the number of test cases into pairs of indices that +// specify the start and end of a tranche. +func createIndices(numCases, numTranches uint) [][2]uint { + // Calculate base value and remainder. + base := numCases / numTranches + remainder := numCases % numTranches + + // Generate indices. + indices := make([][2]uint, numTranches) + start := uint(0) + + for i := uint(0); i < numTranches; i++ { + end := start + base + if i < remainder { + // Add one for the remainder. + end++ + } + indices[i] = [2]uint{start, end} + start = end + } + + return indices +} + // getTestCaseSplitTranche returns the sub slice of the test cases that should // be run as the current split tranche as well as the index and slice offset of // the tranche. @@ -200,12 +224,9 @@ func getTestCaseSplitTranche() ([]*lntest.TestCase, uint, uint) { maybeShuffleTestCases() numCases := uint(len(allTestCases)) - testsPerTranche := numCases / numTranches - trancheOffset := runTranche * testsPerTranche - trancheEnd := trancheOffset + testsPerTranche - if trancheEnd > numCases || runTranche == numTranches-1 { - trancheEnd = numCases - } + indices := createIndices(numCases, numTranches) + index := indices[runTranche] + trancheOffset, trancheEnd := index[0], index[1] return allTestCases[trancheOffset:trancheEnd], threadID, trancheOffset From 90e84c826822abce50019778a893c9704b25e087 Mon Sep 17 00:00:00 2001 From: yyforyongyu Date: Wed, 4 Dec 2024 10:46:25 +0800 Subject: [PATCH 26/32] lntest: increase port timeout --- lntest/port/port.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lntest/port/port.go b/lntest/port/port.go index 28db1918dc..28cd92d69b 100644 --- a/lntest/port/port.go +++ b/lntest/port/port.go @@ -8,6 +8,8 @@ import ( "strconv" "sync" "time" + + "github.com/lightningnetwork/lnd/lntest/wait" ) const ( @@ -45,7 +47,7 @@ func NextAvailablePort() int { defer portFileMutex.Unlock() lockFile := filepath.Join(os.TempDir(), uniquePortFile+".lock") - timeout := time.After(time.Second) + timeout := time.After(wait.DefaultTimeout) var ( lockFileHandle *os.File From e39ba4d7709c7c185cc9bb472c5da101c432b779 Mon Sep 17 00:00:00 2001 From: yyforyongyu Date: Wed, 4 Dec 2024 13:44:32 +0800 Subject: [PATCH 27/32] lntest: add timeouts for windows For Windows the tests run much slower so we create customized timeouts for them. --- lntest/wait/timeouts.go | 4 ++-- lntest/wait/timeouts_windows.go | 39 +++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 2 deletions(-) create mode 100644 lntest/wait/timeouts_windows.go diff --git a/lntest/wait/timeouts.go b/lntest/wait/timeouts.go index f8239799b6..312c788c42 100644 --- a/lntest/wait/timeouts.go +++ b/lntest/wait/timeouts.go @@ -1,5 +1,5 @@ -//go:build !darwin && !kvdb_etcd && !kvdb_postgres -// +build !darwin,!kvdb_etcd,!kvdb_postgres +//go:build !darwin && !windows && !kvdb_etcd && !kvdb_postgres +// +build !darwin,!windows,!kvdb_etcd,!kvdb_postgres package wait diff --git a/lntest/wait/timeouts_windows.go b/lntest/wait/timeouts_windows.go new file mode 100644 index 0000000000..4fba516f1e --- /dev/null +++ b/lntest/wait/timeouts_windows.go @@ -0,0 +1,39 @@ +//go:build windows && !kvdb_etcd && !kvdb_postgres +// +build windows,!kvdb_etcd,!kvdb_postgres + +package wait + +import "time" + +const ( + // MinerMempoolTimeout is the max time we will wait for a transaction + // to propagate to the mining node's mempool. + MinerMempoolTimeout = time.Minute + + // ChannelOpenTimeout is the max time we will wait before a channel to + // be considered opened. + ChannelOpenTimeout = time.Second * 30 + + // ChannelCloseTimeout is the max time we will wait before a channel is + // considered closed. + ChannelCloseTimeout = time.Second * 30 + + // DefaultTimeout is a timeout that will be used for various wait + // scenarios where no custom timeout value is defined. + DefaultTimeout = time.Second * 60 + + // AsyncBenchmarkTimeout is the timeout used when running the async + // payments benchmark. + AsyncBenchmarkTimeout = time.Minute * 5 + + // NodeStartTimeout is the timeout value when waiting for a node to + // become fully started. + NodeStartTimeout = time.Minute * 3 + + // SqliteBusyTimeout is the maximum time that a call to the sqlite db + // will wait for the connection to become available. + SqliteBusyTimeout = time.Second * 10 + + // PaymentTimeout is the timeout used when sending payments. + PaymentTimeout = time.Second * 120 +) From 0a0ab90897aafff95901e5a68d82a0a4aaae3168 Mon Sep 17 00:00:00 2001 From: yyforyongyu Date: Thu, 5 Dec 2024 10:25:23 +0800 Subject: [PATCH 28/32] lntest: properly handle shutdown error This commit removes the panic used in checking the shutdown log. Instead, the error is returned and asserted in `shutdownAllNodes` so it's easier to check which node failed in which test. We also catch all the errors returned from `StopDaemon` call to properly access the shutdown behavior. --- lntest/harness.go | 32 ++++++++-------- lntest/harness_node_manager.go | 5 ++- lntest/node/harness_node.go | 68 +++++++++++++++++----------------- 3 files changed, 56 insertions(+), 49 deletions(-) diff --git a/lntest/harness.go b/lntest/harness.go index d504c7e2a5..33f7863c7b 100644 --- a/lntest/harness.go +++ b/lntest/harness.go @@ -332,7 +332,7 @@ func (h *HarnessTest) Subtest(t *testing.T) *HarnessTest { // Don't bother run the cleanups if the test is failed. if st.Failed() { st.Log("test failed, skipped cleanup") - st.shutdownAllNodes() + st.shutdownNodesNoAssert() return } @@ -402,16 +402,20 @@ func (h *HarnessTest) checkAndLimitBlocksMined(startHeight int32) { "50 blocks in one test") } +// shutdownNodesNoAssert will shutdown all running nodes without assertions. +// This is used when the test has already failed, we don't want to log more +// errors but focusing on the original error. +func (h *HarnessTest) shutdownNodesNoAssert() { + for _, node := range h.manager.activeNodes { + _ = h.manager.shutdownNode(node) + } +} + // shutdownAllNodes will shutdown all running nodes. func (h *HarnessTest) shutdownAllNodes() { + var err error for _, node := range h.manager.activeNodes { - // The process may not be in a state to always shutdown - // immediately, so we'll retry up to a hard limit to ensure we - // eventually shutdown. - err := wait.NoError(func() error { - return h.manager.shutdownNode(node) - }, DefaultTimeout) - + err = h.manager.shutdownNode(node) if err == nil { continue } @@ -421,6 +425,8 @@ func (h *HarnessTest) shutdownAllNodes() { // processes. h.Logf("unable to shutdown %s, got err: %v", node.Name(), err) } + + require.NoError(h, err, "failed to shutdown all nodes") } // cleanupStandbyNode is a function should be called with defer whenever a @@ -516,12 +522,7 @@ func (h *HarnessTest) NewNodeWithCoins(name string, // Shutdown shuts down the given node and asserts that no errors occur. func (h *HarnessTest) Shutdown(node *node.HarnessNode) { - // The process may not be in a state to always shutdown immediately, so - // we'll retry up to a hard limit to ensure we eventually shutdown. - err := wait.NoError(func() error { - return h.manager.shutdownNode(node) - }, DefaultTimeout) - + err := h.manager.shutdownNode(node) require.NoErrorf(h, err, "unable to shutdown %v in %v", node.Name(), h.manager.currentTestCase) } @@ -764,9 +765,10 @@ func (h *HarnessTest) NewNodeRemoteSigner(name string, extraArgs []string, // KillNode kills the node and waits for the node process to stop. func (h *HarnessTest) KillNode(hn *node.HarnessNode) { + delete(h.manager.activeNodes, hn.Cfg.NodeID) + h.Logf("Manually killing the node %s", hn.Name()) require.NoErrorf(h, hn.KillAndWait(), "%s: kill got error", hn.Name()) - delete(h.manager.activeNodes, hn.Cfg.NodeID) } // SetFeeEstimate sets a fee rate to be returned from fee estimator. diff --git a/lntest/harness_node_manager.go b/lntest/harness_node_manager.go index 2040acec7f..48afa6b358 100644 --- a/lntest/harness_node_manager.go +++ b/lntest/harness_node_manager.go @@ -112,11 +112,14 @@ func (nm *nodeManager) registerNode(node *node.HarnessNode) { // ShutdownNode stops an active lnd process and returns when the process has // exited and any temporary directories have been cleaned up. func (nm *nodeManager) shutdownNode(node *node.HarnessNode) error { + // Remove the node from the active nodes map even if the shutdown + // fails as the shutdown cannot be retried in that case. + delete(nm.activeNodes, node.Cfg.NodeID) + if err := node.Shutdown(); err != nil { return err } - delete(nm.activeNodes, node.Cfg.NodeID) return nil } diff --git a/lntest/node/harness_node.go b/lntest/node/harness_node.go index d1353c25cf..0ac74f8779 100644 --- a/lntest/node/harness_node.go +++ b/lntest/node/harness_node.go @@ -636,12 +636,11 @@ func (hn *HarnessNode) cleanup() error { // waitForProcessExit Launch a new goroutine which that bubbles up any // potential fatal process errors to the goroutine running the tests. func (hn *HarnessNode) WaitForProcessExit() error { - var err error + var errReturned error errChan := make(chan error, 1) go func() { - err = hn.cmd.Wait() - errChan <- err + errChan <- hn.cmd.Wait() }() select { @@ -656,24 +655,36 @@ func (hn *HarnessNode) WaitForProcessExit() error { return nil } + // The process may have already been killed in the test, in + // that case we will skip the error and continue processing + // the logs. + if strings.Contains(err.Error(), "signal: killed") { + break + } + // Otherwise, we print the error, break the select and save // logs. hn.printErrf("wait process exit got err: %v", err) - - break + errReturned = err case <-time.After(wait.DefaultTimeout): hn.printErrf("timeout waiting for process to exit") } // Make sure log file is closed and renamed if necessary. - finalizeLogfile(hn) + filename := finalizeLogfile(hn) - // Rename the etcd.log file if the node was running on embedded - // etcd. + // Assert the node has shut down from the log file. + err1 := assertNodeShutdown(filename) + if err1 != nil { + return fmt.Errorf("[%s]: assert shutdown failed in log[%s]: %w", + hn.Name(), filename, err1) + } + + // Rename the etcd.log file if the node was running on embedded etcd. finalizeEtcdLog(hn) - return err + return errReturned } // Stop attempts to stop the active lnd process. @@ -700,23 +711,21 @@ func (hn *HarnessNode) Stop() error { err := wait.NoError(func() error { _, err := hn.RPC.LN.StopDaemon(ctxt, &req) - - switch { - case err == nil: + if err == nil { return nil + } - // Try again if a recovery/rescan is in progress. - case strings.Contains( - err.Error(), "recovery in progress", - ): - return err - - default: + // If the connection is already closed, we can exit + // early as the node has already been shut down in the + // test, e.g., in etcd leader health check test. + if strings.Contains(err.Error(), "connection refused") { return nil } + + return err }, wait.DefaultTimeout) if err != nil { - return err + return fmt.Errorf("shutdown timeout: %w", err) } // Wait for goroutines to be finished. @@ -724,6 +733,7 @@ func (hn *HarnessNode) Stop() error { go func() { hn.Watcher.wg.Wait() close(done) + hn.Watcher = nil }() // If the goroutines fail to finish before timeout, we'll print @@ -966,31 +976,23 @@ func getFinalizedLogFilePrefix(hn *HarnessNode) string { // finalizeLogfile makes sure the log file cleanup function is initialized, // even if no log file is created. -func finalizeLogfile(hn *HarnessNode) { +func finalizeLogfile(hn *HarnessNode) string { // Exit early if there's no log file. if hn.logFile == nil { - return + return "" } hn.logFile.Close() // If logoutput flag is not set, return early. if !*logOutput { - return + return "" } - newFileName := fmt.Sprintf("%v.log", - getFinalizedLogFilePrefix(hn), - ) + newFileName := fmt.Sprintf("%v.log", getFinalizedLogFilePrefix(hn)) renameFile(hn.filename, newFileName) - // Assert the node has shut down from the log file. - err := assertNodeShutdown(newFileName) - if err != nil { - err := fmt.Errorf("[%s]: assert shutdown failed in log[%s]: %w", - hn.Name(), newFileName, err) - panic(err) - } + return newFileName } // assertNodeShutdown asserts that the node has shut down properly by checking From 2088e36613c0349bb5da6fd47ca4fc3448d8c472 Mon Sep 17 00:00:00 2001 From: yyforyongyu Date: Thu, 5 Dec 2024 17:29:54 +0800 Subject: [PATCH 29/32] workflows: increase num of tranches to 16 Keep the SQL, etcd, bitcoin rpcpolling builds and non-ubuntu builds at 8 since they are less stable. --- .github/workflows/main.yml | 96 +++++++++++++++++++++++++++++++------- 1 file changed, 79 insertions(+), 17 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 8c5115146d..4646693f74 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -23,7 +23,14 @@ defaults: env: BITCOIN_VERSION: "28" - TRANCHES: 8 + # TRANCHES defines the number of tranches used in the itests. + TRANCHES: 16 + + # SMALL_TRANCHES defines the number of tranches used in the less stable itest + # builds + # + # TODO(yy): remove this value and use TRANCHES. + SMALL_TRANCHES: 8 # If you change this please also update GO_VERSION in Makefile (then run # `make lint` to see where else it needs to be updated as well). @@ -229,10 +236,10 @@ jobs: ######################## - # run ubuntu integration tests + # run integration tests with TRANCHES ######################## - ubuntu-integration-test: - name: run ubuntu itests + basic-integration-test: + name: basic itests runs-on: ubuntu-latest if: '!contains(github.event.pull_request.labels.*.name, ''no-itest'')' strategy: @@ -246,20 +253,75 @@ jobs: args: backend=bitcoind cover=1 - name: bitcoind-notxindex args: backend="bitcoind notxindex" + - name: neutrino + args: backend=neutrino cover=1 + steps: + - name: git checkout + uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: fetch and rebase on ${{ github.base_ref }} + if: github.event_name == 'pull_request' + uses: ./.github/actions/rebase + + - name: setup go ${{ env.GO_VERSION }} + uses: ./.github/actions/setup-go + with: + go-version: '${{ env.GO_VERSION }}' + key-prefix: integration-test + + - name: install bitcoind + run: ./scripts/install_bitcoind.sh $BITCOIN_VERSION + + - name: run ${{ matrix.name }} + run: make itest-parallel tranches=${{ env.TRANCHES }} ${{ matrix.args }} shuffleseed=${{ github.run_id }}${{ strategy.job-index }} + + - name: Send coverage + if: ${{ contains(matrix.args, 'cover=1') }} + uses: shogo82148/actions-goveralls@v1 + with: + path-to-profile: coverage.txt + flag-name: 'itest-${{ matrix.name }}' + parallel: true + + - name: Zip log files on failure + if: ${{ failure() }} + timeout-minutes: 5 # timeout after 5 minute + run: 7z a logs-itest-${{ matrix.name }}.zip itest/**/*.log + + - name: Upload log files on failure + uses: actions/upload-artifact@v3 + if: ${{ failure() }} + with: + name: logs-itest-${{ matrix.name }} + path: logs-itest-${{ matrix.name }}.zip + retention-days: 5 + + ######################## + # run integration tests with SMALL_TRANCHES + ######################## + integration-test: + name: itests + runs-on: ubuntu-latest + if: '!contains(github.event.pull_request.labels.*.name, ''no-itest'')' + strategy: + # Allow other tests in the matrix to continue if one fails. + fail-fast: false + matrix: + include: - name: bitcoind-rpcpolling - args: backend="bitcoind rpcpolling" cover=1 + args: backend="bitcoind rpcpolling" - name: bitcoind-etcd args: backend=bitcoind dbbackend=etcd - - name: bitcoind-postgres - args: backend=bitcoind dbbackend=postgres - name: bitcoind-sqlite args: backend=bitcoind dbbackend=sqlite - - name: bitcoind-postgres-nativesql - args: backend=bitcoind dbbackend=postgres nativesql=true - name: bitcoind-sqlite-nativesql args: backend=bitcoind dbbackend=sqlite nativesql=true - - name: neutrino - args: backend=neutrino cover=1 + - name: bitcoind-postgres + args: backend=bitcoind dbbackend=postgres + - name: bitcoind-postgres-nativesql + args: backend=bitcoind dbbackend=postgres nativesql=true steps: - name: git checkout uses: actions/checkout@v3 @@ -280,7 +342,7 @@ jobs: run: ./scripts/install_bitcoind.sh $BITCOIN_VERSION - name: run ${{ matrix.name }} - run: make itest-parallel tranches=${{ env.TRANCHES }} ${{ matrix.args }} shuffleseed=${{ github.run_id }}${{ strategy.job-index }} + run: make itest-parallel tranches=${{ env.SMALL_TRANCHES }} ${{ matrix.args }} shuffleseed=${{ github.run_id }}${{ strategy.job-index }} - name: Send coverage if: ${{ contains(matrix.args, 'cover=1') }} @@ -308,7 +370,7 @@ jobs: # run windows integration test ######################## windows-integration-test: - name: run windows itest + name: windows itest runs-on: windows-latest if: '!contains(github.event.pull_request.labels.*.name, ''no-itest'')' steps: @@ -328,7 +390,7 @@ jobs: key-prefix: integration-test - name: run itest - run: make itest-parallel tranches=${{ env.TRANCHES }} windows=1 shuffleseed=${{ github.run_id }} + run: make itest-parallel tranches=${{ env.SMALL_TRANCHES }} windows=1 shuffleseed=${{ github.run_id }} - name: kill any remaining lnd processes if: ${{ failure() }} @@ -352,7 +414,7 @@ jobs: # run macOS integration test ######################## macos-integration-test: - name: run macOS itest + name: macOS itest runs-on: macos-14 if: '!contains(github.event.pull_request.labels.*.name, ''no-itest'')' steps: @@ -372,7 +434,7 @@ jobs: key-prefix: integration-test - name: run itest - run: make itest-parallel tranches=${{ env.TRANCHES }} shuffleseed=${{ github.run_id }} + run: make itest-parallel tranches=${{ env.SMALL_TRANCHES }} shuffleseed=${{ github.run_id }} - name: Zip log files on failure if: ${{ failure() }} @@ -425,7 +487,7 @@ jobs: # Notify about the completion of all coverage collecting jobs. finish: if: ${{ always() }} - needs: [unit-test, ubuntu-integration-test] + needs: [unit-test, basic-integration-test] runs-on: ubuntu-latest steps: - uses: shogo82148/actions-goveralls@v1 From e31c412f1c2c70f81c400b9c2a5b513d42cd457e Mon Sep 17 00:00:00 2001 From: yyforyongyu Date: Sat, 7 Dec 2024 18:02:59 +0800 Subject: [PATCH 30/32] lntest: make sure chain backend is synced to miner We sometimes see `timeout waiting for UTXOs` error from bitcoind-related itests due to the chain backend not synced to the miner. We now assert it's synced before continue. --- lntest/harness.go | 15 +++++++++------ lntest/harness_assertion.go | 6 +++--- lntest/harness_miner.go | 6 +++--- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/lntest/harness.go b/lntest/harness.go index 33f7863c7b..dc8033c39c 100644 --- a/lntest/harness.go +++ b/lntest/harness.go @@ -481,6 +481,14 @@ func (h *HarnessTest) NewNode(name string, err = node.Start(h.runCtx) require.NoError(h, err, "failed to start node %s", node.Name()) + // Get the miner's best block hash. + bestBlock, err := h.miner.Client.GetBestBlockHash() + require.NoError(h, err, "unable to get best block hash") + + // Wait until the node's chain backend is synced to the miner's best + // block. + h.WaitForBlockchainSyncTo(node, *bestBlock) + return node } @@ -490,12 +498,7 @@ func (h *HarnessTest) NewNode(name string, func (h *HarnessTest) NewNodeWithCoins(name string, extraArgs []string) *node.HarnessNode { - node, err := h.manager.newNode(h.T, name, extraArgs, nil, false) - require.NoErrorf(h, err, "unable to create new node for %s", name) - - // Start the node. - err = node.Start(h.runCtx) - require.NoError(h, err, "failed to start node %s", node.Name()) + node := h.NewNode(name, extraArgs) // Load up the wallets of the node with 5 outputs of 1 BTC each. const ( diff --git a/lntest/harness_assertion.go b/lntest/harness_assertion.go index 5cc6edeeda..9e30604fe4 100644 --- a/lntest/harness_assertion.go +++ b/lntest/harness_assertion.go @@ -61,9 +61,9 @@ func (h *HarnessTest) WaitForBlockchainSync(hn *node.HarnessNode) { // WaitForBlockchainSyncTo waits until the node is synced to bestBlock. func (h *HarnessTest) WaitForBlockchainSyncTo(hn *node.HarnessNode, - bestBlock *wire.MsgBlock) { + bestBlock chainhash.Hash) { - bestBlockHash := bestBlock.BlockHash().String() + bestBlockHash := bestBlock.String() err := wait.NoError(func() error { resp := hn.RPC.GetInfo() if resp.SyncedToChain { @@ -1629,7 +1629,7 @@ func (h *HarnessTest) AssertActiveNodesSynced() { // AssertActiveNodesSyncedTo asserts all active nodes have synced to the // provided bestBlock. -func (h *HarnessTest) AssertActiveNodesSyncedTo(bestBlock *wire.MsgBlock) { +func (h *HarnessTest) AssertActiveNodesSyncedTo(bestBlock chainhash.Hash) { for _, node := range h.manager.activeNodes { h.WaitForBlockchainSyncTo(node, bestBlock) } diff --git a/lntest/harness_miner.go b/lntest/harness_miner.go index 17fd864ed7..cde9663c4f 100644 --- a/lntest/harness_miner.go +++ b/lntest/harness_miner.go @@ -41,7 +41,7 @@ func (h *HarnessTest) MineBlocks(num int) { // Check the block doesn't have any txns except the coinbase. if len(block.Transactions) <= 1 { // Make sure all the active nodes are synced. - h.AssertActiveNodesSyncedTo(block) + h.AssertActiveNodesSyncedTo(block.BlockHash()) // Mine the next block. continue @@ -116,7 +116,7 @@ func (h *HarnessTest) MineBlocksAndAssertNumTxes(num uint32, // Finally, make sure all the active nodes are synced. bestBlock := blocks[len(blocks)-1] - h.AssertActiveNodesSyncedTo(bestBlock) + h.AssertActiveNodesSyncedTo(bestBlock.BlockHash()) return blocks } @@ -157,7 +157,7 @@ func (h *HarnessTest) cleanMempool() { bestBlock = blocks[len(blocks)-1] // Make sure all the active nodes are synced. - h.AssertActiveNodesSyncedTo(bestBlock) + h.AssertActiveNodesSyncedTo(bestBlock.BlockHash()) return fmt.Errorf("still have %d txes in mempool", len(mem)) }, wait.MinerMempoolTimeout) From e1407ffef60da28984861f0459f95149603f5965 Mon Sep 17 00:00:00 2001 From: yyforyongyu Date: Sat, 7 Dec 2024 18:04:13 +0800 Subject: [PATCH 31/32] itest: document and fix wallet UTXO flake --- itest/lnd_multi-hop_force_close_test.go | 34 +++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/itest/lnd_multi-hop_force_close_test.go b/itest/lnd_multi-hop_force_close_test.go index fde18c67db..4284631e86 100644 --- a/itest/lnd_multi-hop_force_close_test.go +++ b/itest/lnd_multi-hop_force_close_test.go @@ -679,8 +679,6 @@ func runMultiHopReceiverPreimageClaim(ht *lntest.HarnessTest, alice, bob, carol := nodes[0], nodes[1], nodes[2] bobChanPoint := chanPoints[1] - ht.FundCoins(btcutil.SatoshiPerBitcoin, carol) - // For neutrino backend, we need to one more UTXO for Carol so she can // sweep her outputs. if ht.IsNeutrinoBackend() { @@ -690,6 +688,14 @@ func runMultiHopReceiverPreimageClaim(ht *lntest.HarnessTest, // Fund Carol one UTXO so she can sweep outputs. ht.FundCoins(btcutil.SatoshiPerBitcoin, carol) + // Carol should have enough wallet UTXOs here to sweep the HTLC in the + // end of this test. However, due to a known issue, Carol's wallet may + // report there's no UTXO available. For details, + // - https://github.com/lightningnetwork/lnd/issues/8786 + // + // TODO(yy): remove this step once the issue is resolved. + ht.FundCoins(btcutil.SatoshiPerBitcoin, carol) + // If this is a taproot channel, then we'll need to make some manual // route hints so Alice can actually find a route. var routeHints []*lnrpc.RouteHint @@ -1633,6 +1639,22 @@ func runLocalClaimIncomingHTLC(ht *lntest.HarnessTest, // Fund Carol one UTXO so she can sweep outputs. ht.FundCoins(btcutil.SatoshiPerBitcoin, carol) + // Carol should have enough wallet UTXOs here to sweep the HTLC in the + // end of this test. However, due to a known issue, Carol's wallet may + // report there's no UTXO available. For details, + // - https://github.com/lightningnetwork/lnd/issues/8786 + // + // TODO(yy): remove this step once the issue is resolved. + ht.FundCoins(btcutil.SatoshiPerBitcoin, carol) + + // Bob should have enough wallet UTXOs here to sweep the HTLC in the + // end of this test. However, due to a known issue, Bob's wallet may + // report there's no UTXO available. For details, + // - https://github.com/lightningnetwork/lnd/issues/8786 + // + // TODO(yy): remove this step once the issue is resolved. + ht.FundCoins(btcutil.SatoshiPerBitcoin, bob) + // If this is a taproot channel, then we'll need to make some manual // route hints so Alice can actually find a route. var routeHints []*lnrpc.RouteHint @@ -2603,6 +2625,14 @@ func runLocalPreimageClaimLeased(ht *lntest.HarnessTest, // Fund Carol one UTXO so she can sweep outputs. ht.FundCoins(btcutil.SatoshiPerBitcoin, carol) + // Carol should have enough wallet UTXOs here to sweep the HTLC in the + // end of this test. However, due to a known issue, Carol's wallet may + // report there's no UTXO available. For details, + // - https://github.com/lightningnetwork/lnd/issues/8786 + // + // TODO(yy): remove this step once the issue is resolved. + ht.FundCoins(btcutil.SatoshiPerBitcoin, carol) + // With the network active, we'll now add a new hodl invoice at Carol's // end. Make sure the cltv expiry delta is large enough, otherwise Bob // won't send out the outgoing htlc. From cc89b327816bec518c1c72806adcd43d6bd97470 Mon Sep 17 00:00:00 2001 From: yyforyongyu Date: Tue, 10 Dec 2024 15:43:49 +0800 Subject: [PATCH 32/32] itest: fix flake in `testCoopCloseWithExternalDeliveryImpl` The response from `ClosedChannels` may not be up-to-date, so we wrap it inside a wait closure. --- .../lnd_coop_close_external_delivery_test.go | 25 +++++++++++++------ 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/itest/lnd_coop_close_external_delivery_test.go b/itest/lnd_coop_close_external_delivery_test.go index 8341a2f650..eea09a48d9 100644 --- a/itest/lnd_coop_close_external_delivery_test.go +++ b/itest/lnd_coop_close_external_delivery_test.go @@ -1,11 +1,13 @@ package itest import ( + "fmt" "testing" "github.com/btcsuite/btcd/btcutil" "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lntest" + "github.com/lightningnetwork/lnd/lntest/wait" "github.com/stretchr/testify/require" ) @@ -114,11 +116,20 @@ func testCoopCloseWithExternalDeliveryImpl(ht *lntest.HarnessTest, // assertion. We want to ensure that even though alice's delivery // address is set to an address in bob's wallet, we should still show // the balance as settled. - closed := alice.RPC.ClosedChannels(&lnrpc.ClosedChannelsRequest{ - Cooperative: true, - }) - - // The settled balance should never be zero at this point. - require.NotZero(ht, len(closed.Channels)) - require.NotZero(ht, closed.Channels[0].SettledBalance) + err = wait.NoError(func() error { + closed := alice.RPC.ClosedChannels(&lnrpc.ClosedChannelsRequest{ + Cooperative: true, + }) + + if len(closed.Channels) == 0 { + return fmt.Errorf("expected closed channel not found") + } + + if closed.Channels[0].SettledBalance == 0 { + return fmt.Errorf("expected settled balance to be zero") + } + + return nil + }, defaultTimeout) + require.NoError(ht, err, "timeout checking closed channels") }