From c9b597434177e62198f80ff7202bee067b0067a8 Mon Sep 17 00:00:00 2001 From: Boris Nagaev Date: Tue, 22 Apr 2025 11:03:18 -0300 Subject: [PATCH] itest: test imported address in coop close Make sure that an address imported to LND via ImportTapscript or ImportPublicKey can be used as a delivery address in coop close. New test cases: - P2TR address imported with ImportTapscript - P2TR address imported with ImportPublicKey - P2WPKH address imported with ImportPublicKey Safeguard against https://github.com/lightninglabs/loop/issues/923 --- .../lnd_coop_close_external_delivery_test.go | 160 ++++++++++++++++-- 1 file changed, 149 insertions(+), 11 deletions(-) diff --git a/itest/lnd_coop_close_external_delivery_test.go b/itest/lnd_coop_close_external_delivery_test.go index e2c84fab1..4c2f0a5c4 100644 --- a/itest/lnd_coop_close_external_delivery_test.go +++ b/itest/lnd_coop_close_external_delivery_test.go @@ -4,7 +4,9 @@ import ( "fmt" "github.com/btcsuite/btcd/btcutil" + "github.com/btcsuite/btcd/txscript" "github.com/lightningnetwork/lnd/lnrpc" + "github.com/lightningnetwork/lnd/lnrpc/walletrpc" "github.com/lightningnetwork/lnd/lntest" "github.com/lightningnetwork/lnd/lntest/wait" "github.com/stretchr/testify/require" @@ -15,7 +17,7 @@ var coopCloseWithExternalTestCases = []*lntest.TestCase{ Name: "set P2WPKH delivery address at open", TestFunc: func(ht *lntest.HarnessTest) { testCoopCloseWithExternalDelivery( - ht, true, + ht, true, false, false, lnrpc.AddressType_UNUSED_WITNESS_PUBKEY_HASH, ) }, @@ -24,7 +26,7 @@ var coopCloseWithExternalTestCases = []*lntest.TestCase{ Name: "set P2WPKH delivery address at close", TestFunc: func(ht *lntest.HarnessTest) { testCoopCloseWithExternalDelivery( - ht, false, + ht, false, false, false, lnrpc.AddressType_UNUSED_WITNESS_PUBKEY_HASH, ) }, @@ -33,7 +35,7 @@ var coopCloseWithExternalTestCases = []*lntest.TestCase{ Name: "set P2TR delivery address at open", TestFunc: func(ht *lntest.HarnessTest) { testCoopCloseWithExternalDelivery( - ht, true, + ht, true, false, false, lnrpc.AddressType_UNUSED_TAPROOT_PUBKEY, ) }, @@ -42,7 +44,61 @@ var coopCloseWithExternalTestCases = []*lntest.TestCase{ Name: "set P2TR delivery address at close", TestFunc: func(ht *lntest.HarnessTest) { testCoopCloseWithExternalDelivery( - ht, false, + ht, false, false, false, + lnrpc.AddressType_UNUSED_TAPROOT_PUBKEY, + ) + }, + }, + { + Name: "set imported P2TR address (ImportTapscript) at open", + TestFunc: func(ht *lntest.HarnessTest) { + testCoopCloseWithExternalDelivery( + ht, true, true, false, + lnrpc.AddressType_UNUSED_TAPROOT_PUBKEY, + ) + }, + }, + { + Name: "set imported P2TR address (ImportTapscript) at close", + TestFunc: func(ht *lntest.HarnessTest) { + testCoopCloseWithExternalDelivery( + ht, false, true, false, + lnrpc.AddressType_UNUSED_TAPROOT_PUBKEY, + ) + }, + }, + { + Name: "set imported P2WPKH address at open", + TestFunc: func(ht *lntest.HarnessTest) { + testCoopCloseWithExternalDelivery( + ht, true, false, true, + lnrpc.AddressType_UNUSED_WITNESS_PUBKEY_HASH, + ) + }, + }, + { + Name: "set imported P2WPKH address at close", + TestFunc: func(ht *lntest.HarnessTest) { + testCoopCloseWithExternalDelivery( + ht, false, false, true, + lnrpc.AddressType_UNUSED_WITNESS_PUBKEY_HASH, + ) + }, + }, + { + Name: "set imported P2TR address (ImportPublicKey) at open", + TestFunc: func(ht *lntest.HarnessTest) { + testCoopCloseWithExternalDelivery( + ht, true, false, true, + lnrpc.AddressType_UNUSED_TAPROOT_PUBKEY, + ) + }, + }, + { + Name: "set imported P2TR address (ImportPublicKey) at close", + TestFunc: func(ht *lntest.HarnessTest) { + testCoopCloseWithExternalDelivery( + ht, false, false, true, lnrpc.AddressType_UNUSED_TAPROOT_PUBKEY, ) }, @@ -53,20 +109,102 @@ var coopCloseWithExternalTestCases = []*lntest.TestCase{ // balance irrespective of whether the delivery address is in LND's wallet or // not. Some users set this value to be an address in a different wallet and // this should not affect our ability to accurately report the settled balance. +// +// If importTapscript is set, it imports a Taproot script and internal key to +// Alice's LND using ImportTapscript to make sure it doesn't interfere with +// delivery address. If importPubkey is set, the address is imported using +// ImportPublicKey. func testCoopCloseWithExternalDelivery(ht *lntest.HarnessTest, - upfrontShutdown bool, deliveryAddressType lnrpc.AddressType) { + upfrontShutdown, importTapscript, importPubkey bool, + deliveryAddressType lnrpc.AddressType) { alice := ht.NewNodeWithCoins("Alice", nil) bob := ht.NewNodeWithCoins("bob", nil) ht.ConnectNodes(alice, bob) + // Make fake taproot internal public key (equal to the final). + // This is only used if importTapscript or importPubkey is set. + taprootPubkey := [32]byte{1, 2, 3} + if upfrontShutdown { + // Make new address for second sub-test not to import + // the same address twice causing an error. + taprootPubkey[3] = 1 + } + pkScriptBytes := append( + []byte{txscript.OP_1, txscript.OP_DATA_32}, + taprootPubkey[:]..., + ) + pkScript, err := txscript.ParsePkScript(pkScriptBytes) + require.NoError(ht, err) + taprootAddress, err := pkScript.Address(harnessNetParams) + require.NoError(ht, err) + + var addr string + switch { + // Use ImportTapscript. + case importTapscript: + addr = taprootAddress.String() + + // Import the taproot address to LND. + req := &walletrpc.ImportTapscriptRequest{ + InternalPublicKey: taprootPubkey[:], + Script: &walletrpc.ImportTapscriptRequest_FullKeyOnly{ + FullKeyOnly: true, + }, + } + res := alice.RPC.ImportTapscript(req) + require.Equal(ht, addr, res.P2TrAddress) + + // Use ImportPublicKey. + case importPubkey: + var ( + address btcutil.Address + pubKey []byte + addressType walletrpc.AddressType + ) + switch deliveryAddressType { + case lnrpc.AddressType_UNUSED_WITNESS_PUBKEY_HASH: + // Make fake public key hash. + pk := [33]byte{2, 3, 4} + if upfrontShutdown { + // Make new address for second sub-test. + pk[1]++ + } + address, err = btcutil.NewAddressWitnessPubKeyHash( + btcutil.Hash160(pk[:]), harnessNetParams, + ) + require.NoError(ht, err) + pubKey = pk[:] + addressType = walletrpc.AddressType_WITNESS_PUBKEY_HASH + + case lnrpc.AddressType_UNUSED_TAPROOT_PUBKEY: + address = taprootAddress + pubKey = taprootPubkey[:] + addressType = walletrpc.AddressType_TAPROOT_PUBKEY + + default: + ht.Fatalf("not allowed address type: %v", + deliveryAddressType) + } + + addr = address.String() + + // Import the address to LND. + alice.RPC.ImportPublicKey(&walletrpc.ImportPublicKeyRequest{ + PublicKey: pubKey, + AddressType: addressType, + }) + // Here we generate a final delivery address in bob's wallet but set by // alice. We do this to ensure that the address is not in alice's LND // wallet. We already correctly track settled balances when the address // is in the LND wallet. - addr := bob.RPC.NewAddress(&lnrpc.NewAddressRequest{ - Type: deliveryAddressType, - }) + default: + res := bob.RPC.NewAddress(&lnrpc.NewAddressRequest{ + Type: deliveryAddressType, + }) + addr = res.Address + } // Prepare for channel open. openParams := lntest.OpenChannelParams{ @@ -76,7 +214,7 @@ func testCoopCloseWithExternalDelivery(ht *lntest.HarnessTest, // If we are testing the case where we set it on open then we'll set the // upfront shutdown script in the channel open parameters. if upfrontShutdown { - openParams.CloseAddress = addr.Address + openParams.CloseAddress = addr } // Open the channel! @@ -91,14 +229,14 @@ func testCoopCloseWithExternalDelivery(ht *lntest.HarnessTest, // If we are testing the case where we set the delivery address on // channel close then we will set it in the channel close parameters. if !upfrontShutdown { - closeParams.DeliveryAddress = addr.Address + closeParams.DeliveryAddress = addr } // Close the channel! closeClient := alice.RPC.CloseChannel(&closeParams) // Assert that we got a channel update when we get a closing txid. - _, err := closeClient.Recv() + _, err = closeClient.Recv() require.NoError(ht, err) // Mine the closing transaction.