Merge pull request #5689 from guggero/remote-signer
Support remote signing over RPC
This commit is contained in:
51 files changed
+3331
-788
No files matched your search
@@ -427,6 +427,39 @@ func (n *NetworkHarness) newNodeWithSeed(name string, extraArgs []string,
|
||||
return node, genSeedResp.CipherSeedMnemonic, response.AdminMacaroon, nil
|
||||
}
|
||||
|
||||
func (n *NetworkHarness) NewNodeRemoteSigner(name string, extraArgs []string,
|
||||
password []byte, watchOnly *lnrpc.WatchOnly) (*HarnessNode, error) {
|
||||
|
||||
node, err := n.newNode(
|
||||
name, extraArgs, true, password, n.dbBackend, true,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ctxb := context.Background()
|
||||
|
||||
// With the seed created, construct the init request to the node,
|
||||
// including the newly generated seed.
|
||||
initReq := &lnrpc.InitWalletRequest{
|
||||
WalletPassword: password,
|
||||
WatchOnly: watchOnly,
|
||||
}
|
||||
|
||||
// Pass the init request via rpc to finish unlocking the node. This will
|
||||
// also initialize the macaroon-authenticated LightningClient.
|
||||
_, err = node.Init(ctxb, initReq)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// With the node started, we can now record its public key within the
|
||||
// global mapping.
|
||||
n.RegisterNode(node)
|
||||
|
||||
return node, nil
|
||||
}
|
||||
|
||||
// RestoreNodeWithSeed fully initializes a HarnessNode using a chosen mnemonic,
|
||||
// password, recovery window, and optionally a set of static channel backups.
|
||||
// After providing the initialization request to unlock the node, this method
|
||||
|
||||
@@ -22,6 +22,14 @@ import (
|
||||
//
|
||||
// TODO(wilmer): Add RBF case once btcd supports it.
|
||||
func testCPFP(net *lntest.NetworkHarness, t *harnessTest) {
|
||||
runCPFP(net, t, net.Alice, net.Bob)
|
||||
}
|
||||
|
||||
// runCPFP ensures that the daemon can bump an unconfirmed transaction's fee
|
||||
// rate by broadcasting a Child-Pays-For-Parent (CPFP) transaction.
|
||||
func runCPFP(net *lntest.NetworkHarness, t *harnessTest,
|
||||
alice, bob *lntest.HarnessNode) {
|
||||
|
||||
// Skip this test for neutrino, as it's not aware of mempool
|
||||
// transactions.
|
||||
if net.BackendCfg.Name() == lntest.NeutrinoBackendName {
|
||||
@@ -31,14 +39,14 @@ func testCPFP(net *lntest.NetworkHarness, t *harnessTest) {
|
||||
// We'll start the test by sending Alice some coins, which she'll use to
|
||||
// send to Bob.
|
||||
ctxb := context.Background()
|
||||
net.SendCoins(t.t, btcutil.SatoshiPerBitcoin, net.Alice)
|
||||
net.SendCoins(t.t, btcutil.SatoshiPerBitcoin, alice)
|
||||
|
||||
// Create an address for Bob to send the coins to.
|
||||
addrReq := &lnrpc.NewAddressRequest{
|
||||
Type: lnrpc.AddressType_WITNESS_PUBKEY_HASH,
|
||||
}
|
||||
ctxt, _ := context.WithTimeout(ctxb, defaultTimeout)
|
||||
resp, err := net.Bob.NewAddress(ctxt, addrReq)
|
||||
resp, err := bob.NewAddress(ctxt, addrReq)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to get new address for bob: %v", err)
|
||||
}
|
||||
@@ -50,7 +58,7 @@ func testCPFP(net *lntest.NetworkHarness, t *harnessTest) {
|
||||
Amount: btcutil.SatoshiPerBitcoin,
|
||||
}
|
||||
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
||||
if _, err = net.Alice.SendCoins(ctxt, sendReq); err != nil {
|
||||
if _, err = alice.SendCoins(ctxt, sendReq); err != nil {
|
||||
t.Fatalf("unable to send coins to bob: %v", err)
|
||||
}
|
||||
|
||||
@@ -88,7 +96,7 @@ func testCPFP(net *lntest.NetworkHarness, t *harnessTest) {
|
||||
TxidBytes: txid[:],
|
||||
OutputIndex: uint32(bobOutputIdx),
|
||||
}
|
||||
assertWalletUnspent(t, net.Bob, op)
|
||||
assertWalletUnspent(t, bob, op)
|
||||
|
||||
// We'll attempt to bump the fee of this transaction by performing a
|
||||
// CPFP from Alice's point of view.
|
||||
@@ -99,7 +107,7 @@ func testCPFP(net *lntest.NetworkHarness, t *harnessTest) {
|
||||
),
|
||||
}
|
||||
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
||||
_, err = net.Bob.WalletKitClient.BumpFee(ctxt, bumpFeeReq)
|
||||
_, err = bob.WalletKitClient.BumpFee(ctxt, bumpFeeReq)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to bump fee: %v", err)
|
||||
}
|
||||
@@ -115,7 +123,7 @@ func testCPFP(net *lntest.NetworkHarness, t *harnessTest) {
|
||||
// UtxoSweeper. We'll ensure it's using the fee rate specified.
|
||||
pendingSweepsReq := &walletrpc.PendingSweepsRequest{}
|
||||
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
||||
pendingSweepsResp, err := net.Bob.WalletKitClient.PendingSweeps(
|
||||
pendingSweepsResp, err := bob.WalletKitClient.PendingSweeps(
|
||||
ctxt, pendingSweepsReq,
|
||||
)
|
||||
if err != nil {
|
||||
@@ -146,7 +154,7 @@ func testCPFP(net *lntest.NetworkHarness, t *harnessTest) {
|
||||
err = wait.NoError(func() error {
|
||||
req := &walletrpc.PendingSweepsRequest{}
|
||||
ctxt, _ = context.WithTimeout(ctxb, defaultTimeout)
|
||||
resp, err := net.Bob.WalletKitClient.PendingSweeps(ctxt, req)
|
||||
resp, err := bob.WalletKitClient.PendingSweeps(ctxt, req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to retrieve bob's pending "+
|
||||
"sweeps: %v", err)
|
||||
|
||||
@@ -259,6 +259,15 @@ func testOpenChannelAfterReorg(net *lntest.NetworkHarness, t *harnessTest) {
|
||||
// and ensures that if a node is subscribed to channel updates they will be
|
||||
// received correctly for both cooperative and force closed channels.
|
||||
func testBasicChannelCreationAndUpdates(net *lntest.NetworkHarness, t *harnessTest) {
|
||||
runBasicChannelCreationAndUpdates(net, t, net.Alice, net.Bob)
|
||||
}
|
||||
|
||||
// runBasicChannelCreationAndUpdates tests multiple channel opening and closing,
|
||||
// and ensures that if a node is subscribed to channel updates they will be
|
||||
// received correctly for both cooperative and force closed channels.
|
||||
func runBasicChannelCreationAndUpdates(net *lntest.NetworkHarness,
|
||||
t *harnessTest, alice, bob *lntest.HarnessNode) {
|
||||
|
||||
ctxb := context.Background()
|
||||
const (
|
||||
numChannels = 2
|
||||
@@ -266,10 +275,10 @@ func testBasicChannelCreationAndUpdates(net *lntest.NetworkHarness, t *harnessTe
|
||||
)
|
||||
|
||||
// Subscribe Bob and Alice to channel event notifications.
|
||||
bobChanSub := subscribeChannelNotifications(ctxb, t, net.Bob)
|
||||
bobChanSub := subscribeChannelNotifications(ctxb, t, bob)
|
||||
defer close(bobChanSub.quit)
|
||||
|
||||
aliceChanSub := subscribeChannelNotifications(ctxb, t, net.Alice)
|
||||
aliceChanSub := subscribeChannelNotifications(ctxb, t, alice)
|
||||
defer close(aliceChanSub.quit)
|
||||
|
||||
// Open the channels between Alice and Bob, asserting that the channels
|
||||
@@ -277,8 +286,7 @@ func testBasicChannelCreationAndUpdates(net *lntest.NetworkHarness, t *harnessTe
|
||||
chanPoints := make([]*lnrpc.ChannelPoint, numChannels)
|
||||
for i := 0; i < numChannels; i++ {
|
||||
chanPoints[i] = openChannelAndAssert(
|
||||
t, net, net.Alice, net.Bob,
|
||||
lntest.OpenChannelParams{
|
||||
t, net, alice, bob, lntest.OpenChannelParams{
|
||||
Amt: amount,
|
||||
},
|
||||
)
|
||||
@@ -346,9 +354,9 @@ func testBasicChannelCreationAndUpdates(net *lntest.NetworkHarness, t *harnessTe
|
||||
for i, chanPoint := range chanPoints {
|
||||
// Force close the first of the two channels.
|
||||
force := i%2 == 0
|
||||
closeChannelAndAssert(t, net, net.Alice, chanPoint, force)
|
||||
closeChannelAndAssert(t, net, alice, chanPoint, force)
|
||||
if force {
|
||||
cleanupForceClose(t, net, net.Alice, chanPoint)
|
||||
cleanupForceClose(t, net, alice, chanPoint)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -243,6 +243,13 @@ func testPaymentFollowingChannelOpen(net *lntest.NetworkHarness, t *harnessTest)
|
||||
|
||||
// testAsyncPayments tests the performance of the async payments.
|
||||
func testAsyncPayments(net *lntest.NetworkHarness, t *harnessTest) {
|
||||
runAsyncPayments(net, t, net.Alice, net.Bob)
|
||||
}
|
||||
|
||||
// runAsyncPayments tests the performance of the async payments.
|
||||
func runAsyncPayments(net *lntest.NetworkHarness, t *harnessTest, alice,
|
||||
bob *lntest.HarnessNode) {
|
||||
|
||||
ctxb := context.Background()
|
||||
|
||||
const (
|
||||
@@ -254,13 +261,13 @@ func testAsyncPayments(net *lntest.NetworkHarness, t *harnessTest) {
|
||||
// Alice should send all money from her side to Bob.
|
||||
channelCapacity := btcutil.Amount(paymentAmt * 2000)
|
||||
chanPoint := openChannelAndAssert(
|
||||
t, net, net.Alice, net.Bob,
|
||||
t, net, alice, bob,
|
||||
lntest.OpenChannelParams{
|
||||
Amt: channelCapacity,
|
||||
},
|
||||
)
|
||||
|
||||
info, err := getChanInfo(net.Alice)
|
||||
info, err := getChanInfo(alice)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to get alice channel info: %v", err)
|
||||
}
|
||||
@@ -278,7 +285,7 @@ func testAsyncPayments(net *lntest.NetworkHarness, t *harnessTest) {
|
||||
// With the channel open, we'll create invoices for Bob that Alice
|
||||
// will pay to in order to advance the state of the channel.
|
||||
bobPayReqs, _, _, err := createPayReqs(
|
||||
net.Bob, paymentAmt, numInvoices,
|
||||
bob, paymentAmt, numInvoices,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create pay reqs: %v", err)
|
||||
@@ -286,7 +293,7 @@ func testAsyncPayments(net *lntest.NetworkHarness, t *harnessTest) {
|
||||
|
||||
// Wait for Alice to receive the channel edge from the funding manager.
|
||||
ctxt, _ := context.WithTimeout(ctxb, defaultTimeout)
|
||||
err = net.Alice.WaitForNetworkChannelOpen(ctxt, chanPoint)
|
||||
err = alice.WaitForNetworkChannelOpen(ctxt, chanPoint)
|
||||
if err != nil {
|
||||
t.Fatalf("alice didn't see the alice->bob channel before "+
|
||||
"timeout: %v", err)
|
||||
@@ -301,7 +308,7 @@ func testAsyncPayments(net *lntest.NetworkHarness, t *harnessTest) {
|
||||
payReq := bobPayReqs[i]
|
||||
go func() {
|
||||
ctxt, _ = context.WithTimeout(ctxb, lntest.AsyncBenchmarkTimeout)
|
||||
stream, err := net.Alice.RouterClient.SendPaymentV2(
|
||||
stream, err := alice.RouterClient.SendPaymentV2(
|
||||
ctxt,
|
||||
&routerrpc.SendPaymentRequest{
|
||||
PaymentRequest: payReq,
|
||||
@@ -344,7 +351,7 @@ func testAsyncPayments(net *lntest.NetworkHarness, t *harnessTest) {
|
||||
// htlcs listed and has correct balances. This is needed due to the fact
|
||||
// that we now pipeline the settles.
|
||||
err = wait.Predicate(func() bool {
|
||||
aliceChan, err := getChanInfo(net.Alice)
|
||||
aliceChan, err := getChanInfo(alice)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
@@ -366,7 +373,7 @@ func testAsyncPayments(net *lntest.NetworkHarness, t *harnessTest) {
|
||||
|
||||
// Wait for Bob to receive revocation from Alice.
|
||||
err = wait.NoError(func() error {
|
||||
bobChan, err := getChanInfo(net.Bob)
|
||||
bobChan, err := getChanInfo(bob)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to get bob's channel info: %v", err)
|
||||
}
|
||||
@@ -399,7 +406,7 @@ func testAsyncPayments(net *lntest.NetworkHarness, t *harnessTest) {
|
||||
// Finally, immediately close the channel. This function will also
|
||||
// block until the channel is closed and will additionally assert the
|
||||
// relevant channel closing post conditions.
|
||||
closeChannelAndAssert(t, net, net.Alice, chanPoint, false)
|
||||
closeChannelAndAssert(t, net, alice, chanPoint, false)
|
||||
}
|
||||
|
||||
// testBidirectionalAsyncPayments tests that nodes are able to send the
|
||||
|
||||
@@ -19,9 +19,6 @@ import (
|
||||
// by using a Partially Signed Bitcoin Transaction that funds the channel
|
||||
// multisig funding output.
|
||||
func testPsbtChanFunding(net *lntest.NetworkHarness, t *harnessTest) {
|
||||
ctxb := context.Background()
|
||||
const chanSize = funding.MaxBtcFundingAmount
|
||||
|
||||
// First, we'll create two new nodes that we'll use to open channels
|
||||
// between for this test. Dave gets some coins that will be used to
|
||||
// fund the PSBT, just to make sure that Carol has an empty wallet.
|
||||
@@ -31,6 +28,17 @@ func testPsbtChanFunding(net *lntest.NetworkHarness, t *harnessTest) {
|
||||
dave := net.NewNode(t.t, "dave", nil)
|
||||
defer shutdownAndAssert(net, t, dave)
|
||||
|
||||
runPsbtChanFunding(net, t, carol, dave)
|
||||
}
|
||||
|
||||
// runPsbtChanFunding makes sure a channel can be opened between carol and dave
|
||||
// by using a Partially Signed Bitcoin Transaction that funds the channel
|
||||
// multisig funding output.
|
||||
func runPsbtChanFunding(net *lntest.NetworkHarness, t *harnessTest, carol,
|
||||
dave *lntest.HarnessNode) {
|
||||
|
||||
ctxb := context.Background()
|
||||
const chanSize = funding.MaxBtcFundingAmount
|
||||
net.SendCoins(t.t, btcutil.SatoshiPerBitcoin, dave)
|
||||
|
||||
// Before we start the test, we'll ensure both sides are connected so
|
||||
|
||||
@@ -0,0 +1,266 @@
|
||||
package itest
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/btcsuite/btcutil"
|
||||
"github.com/btcsuite/btcwallet/waddrmgr"
|
||||
"github.com/lightningnetwork/lnd/keychain"
|
||||
"github.com/lightningnetwork/lnd/lnrpc"
|
||||
"github.com/lightningnetwork/lnd/lnrpc/walletrpc"
|
||||
"github.com/lightningnetwork/lnd/lntest"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var (
|
||||
rootKey = "tprv8ZgxMBicQKsPe6jS4vDm2n7s42Q6MpvghUQqMmSKG7bTZvGKtjrcU3" +
|
||||
"PGzMNG37yzxywrcdvgkwrr8eYXJmbwdvUNVT4Ucv7ris4jvA7BUmg"
|
||||
|
||||
nodePubKey = "033f55d436d4f7d24aeffb1b976647380f22ebf9e74390e8c76dcff" +
|
||||
"9fea0093b7a"
|
||||
|
||||
accounts = []*lnrpc.WatchOnlyAccount{{
|
||||
Purpose: waddrmgr.KeyScopeBIP0049Plus.Purpose,
|
||||
// We always use the mainnet coin type for our BIP49/84
|
||||
// addresses!
|
||||
CoinType: 0,
|
||||
Account: 0,
|
||||
Xpub: "tpubDDXEYWvGCTytEF6hBog9p4qr2QBUvJhh4P2wM4qHHv9N489khk" +
|
||||
"QoGkBXDVoquuiyBf8SKBwrYseYdtq9j2v2nttPpE8qbuW3sE2MCk" +
|
||||
"FPhTq",
|
||||
}, {
|
||||
Purpose: waddrmgr.KeyScopeBIP0084.Purpose,
|
||||
// We always use the mainnet coin type for our BIP49/84
|
||||
// addresses!
|
||||
CoinType: 0,
|
||||
Account: 0,
|
||||
Xpub: "tpubDDWAWrSLRSFrG1KdqXMQQyTKYGSKLKaY7gxpvK7RdV3e3Dkhvu" +
|
||||
"W2GgsFvsPN4RGmuoYtUgZ1LHZE8oftz7T4mzc1BxGt5rt8zJcVQi" +
|
||||
"KTPPV",
|
||||
}, {
|
||||
Purpose: keychain.BIP0043Purpose,
|
||||
CoinType: harnessNetParams.HDCoinType,
|
||||
Account: uint32(keychain.KeyFamilyMultiSig),
|
||||
Xpub: "tpubDDXFHr67Ro2tHKVWG2gNjjijKUH1Lyv5NKFYdJnuaLGVNBVwyV" +
|
||||
"5AbykhR43iy8wYozEMbw2QfmAqZhb8gnuL5mm9sZh8YsR6FjGAbe" +
|
||||
"w1xoT",
|
||||
}, {
|
||||
Purpose: keychain.BIP0043Purpose,
|
||||
CoinType: harnessNetParams.HDCoinType,
|
||||
Account: uint32(keychain.KeyFamilyRevocationBase),
|
||||
Xpub: "tpubDDXFHr67Ro2tKkccDqNfDqZpd5wCs2n6XRV2Uh185DzCTbkDaE" +
|
||||
"d9v7P837zZTYBNVfaRriuxgGVgxbGjDui4CKxyzBzwz4aAZxjn2P" +
|
||||
"hNcQy",
|
||||
}, {
|
||||
Purpose: keychain.BIP0043Purpose,
|
||||
CoinType: harnessNetParams.HDCoinType,
|
||||
Account: uint32(keychain.KeyFamilyHtlcBase),
|
||||
Xpub: "tpubDDXFHr67Ro2tNH4KH41i4oTsWfRjFigoH1Ee7urvHow51opH9x" +
|
||||
"J7mu1qSPMPVtkVqQZ5tE4NTuFJPrbDqno7TQietyUDmPTwyVviJb" +
|
||||
"GCwXk",
|
||||
}, {
|
||||
Purpose: keychain.BIP0043Purpose,
|
||||
CoinType: harnessNetParams.HDCoinType,
|
||||
Account: uint32(keychain.KeyFamilyPaymentBase),
|
||||
Xpub: "tpubDDXFHr67Ro2tQj5Zvav2ALhkU6dRQAhEtNPnYJVBC8hs2U1A9e" +
|
||||
"cqxRY3XTiJKBDD7e8tudhmTRs8aGWJAiAXJN5kXy3Hi6cmiwGWjX" +
|
||||
"K5Cv5",
|
||||
}, {
|
||||
Purpose: keychain.BIP0043Purpose,
|
||||
CoinType: harnessNetParams.HDCoinType,
|
||||
Account: uint32(keychain.KeyFamilyDelayBase),
|
||||
Xpub: "tpubDDXFHr67Ro2tSSR2LLBJtotxx2U45cuESLWKA72YT9td3SzVKH" +
|
||||
"AptzDEx5chsUNZ4WRMY5h6HJxRSebjRatxQKX1uUsux1LvKS1wsf" +
|
||||
"NJ2PH",
|
||||
}, {
|
||||
Purpose: keychain.BIP0043Purpose,
|
||||
CoinType: harnessNetParams.HDCoinType,
|
||||
Account: uint32(keychain.KeyFamilyRevocationRoot),
|
||||
Xpub: "tpubDDXFHr67Ro2tTwzfWvNoMoPpZbxdMEfe1WhbXJxvXikGixPa4g" +
|
||||
"gSRZeGx6T5yxVHTVT3rjVh35Veqsowj7emX8SZfXKDKDKcLduXCe" +
|
||||
"WPUU3",
|
||||
}, {
|
||||
Purpose: keychain.BIP0043Purpose,
|
||||
CoinType: harnessNetParams.HDCoinType,
|
||||
Account: uint32(keychain.KeyFamilyNodeKey),
|
||||
Xpub: "tpubDDXFHr67Ro2tYEDS2EByRedfsUoEwBtrzVbS1qdPrX6sAkUYGL" +
|
||||
"rZWvMmQv8KZDZ4zd9r8WzM9bJ2nGp7XuNVC4w2EBtWg7i76gbrmu" +
|
||||
"EWjQh",
|
||||
}, {
|
||||
Purpose: keychain.BIP0043Purpose,
|
||||
CoinType: harnessNetParams.HDCoinType,
|
||||
Account: uint32(keychain.KeyFamilyStaticBackup),
|
||||
Xpub: "tpubDDXFHr67Ro2tYpwnFJEQaM8eAPM2UV5uY6gFgXeSzS5aC5T9Tf" +
|
||||
"zXuawYKBbQMZJn8qHXLafY4tAutoda1aKP5h6Nbgy3swPbnhWbFj" +
|
||||
"S5wnX",
|
||||
}, {
|
||||
Purpose: keychain.BIP0043Purpose,
|
||||
CoinType: harnessNetParams.HDCoinType,
|
||||
Account: uint32(keychain.KeyFamilyTowerSession),
|
||||
Xpub: "tpubDDXFHr67Ro2tddKpAjUegXqt7EGxRXnHkeLbUkfuFMGbLJYgRp" +
|
||||
"G4ew5pMmGg2nmcGmHFQ29w3juNhd8N5ZZ8HwJdymC4f5ukQLJ4yg" +
|
||||
"9rEr3",
|
||||
}, {
|
||||
Purpose: keychain.BIP0043Purpose,
|
||||
CoinType: harnessNetParams.HDCoinType,
|
||||
Account: uint32(keychain.KeyFamilyTowerID),
|
||||
Xpub: "tpubDDXFHr67Ro2tgE89V8ZdgMytC2Jq1iT9ttGhdzR1X7haQJNBmX" +
|
||||
"t8kau6taC6DGASYzbrjmo9z9w6JQFcaLNqbhS2h2PVSzKf79j265" +
|
||||
"Zi8hF",
|
||||
}}
|
||||
)
|
||||
|
||||
// testRemoteSigner tests that a watch-only wallet can use a remote signing
|
||||
// wallet to perform any signing or ECDH operations.
|
||||
func testRemoteSigner(net *lntest.NetworkHarness, t *harnessTest) {
|
||||
ctxb := context.Background()
|
||||
|
||||
subTests := []struct {
|
||||
name string
|
||||
randomSeed bool
|
||||
sendCoins bool
|
||||
fn func(tt *harnessTest, wo, carol *lntest.HarnessNode)
|
||||
}{{
|
||||
name: "random seed",
|
||||
randomSeed: true,
|
||||
fn: func(tt *harnessTest, wo, carol *lntest.HarnessNode) {
|
||||
// Nothing more to test here.
|
||||
},
|
||||
}, {
|
||||
name: "account import",
|
||||
fn: func(tt *harnessTest, wo, carol *lntest.HarnessNode) {
|
||||
runWalletImportAccountScenario(
|
||||
net, tt,
|
||||
walletrpc.AddressType_WITNESS_PUBKEY_HASH,
|
||||
carol, wo,
|
||||
)
|
||||
},
|
||||
}, {
|
||||
name: "basic channel open close",
|
||||
sendCoins: true,
|
||||
fn: func(tt *harnessTest, wo, carol *lntest.HarnessNode) {
|
||||
runBasicChannelCreationAndUpdates(
|
||||
net, tt, wo, carol,
|
||||
)
|
||||
},
|
||||
}, {
|
||||
name: "async payments",
|
||||
sendCoins: true,
|
||||
fn: func(tt *harnessTest, wo, carol *lntest.HarnessNode) {
|
||||
runAsyncPayments(net, tt, wo, carol)
|
||||
},
|
||||
}, {
|
||||
name: "shared key",
|
||||
fn: func(tt *harnessTest, wo, carol *lntest.HarnessNode) {
|
||||
runDeriveSharedKey(tt, wo)
|
||||
},
|
||||
}, {
|
||||
name: "cpfp",
|
||||
sendCoins: true,
|
||||
fn: func(tt *harnessTest, wo, carol *lntest.HarnessNode) {
|
||||
runCPFP(net, tt, wo, carol)
|
||||
},
|
||||
}, {
|
||||
name: "psbt",
|
||||
fn: func(tt *harnessTest, wo, carol *lntest.HarnessNode) {
|
||||
runPsbtChanFunding(net, tt, carol, wo)
|
||||
},
|
||||
}}
|
||||
|
||||
for _, st := range subTests {
|
||||
subTest := st
|
||||
|
||||
// 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 = accounts
|
||||
signer *lntest.HarnessNode
|
||||
err error
|
||||
)
|
||||
if !subTest.randomSeed {
|
||||
signer, err = net.RestoreNodeWithSeed(
|
||||
"Signer", nil, password, nil, rootKey, 0, nil,
|
||||
)
|
||||
require.NoError(t.t, err)
|
||||
} else {
|
||||
signer = net.NewNode(t.t, "Signer", nil)
|
||||
signerNodePubKey = signer.PubKeyStr
|
||||
|
||||
rpcAccts, err := signer.WalletKitClient.ListAccounts(
|
||||
ctxb, &walletrpc.ListAccountsRequest{},
|
||||
)
|
||||
require.NoError(t.t, err)
|
||||
|
||||
watchOnlyAccounts, err = walletrpc.AccountsToWatchOnly(
|
||||
rpcAccts.Accounts,
|
||||
)
|
||||
require.NoError(t.t, err)
|
||||
}
|
||||
|
||||
// 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, err := net.NewNodeRemoteSigner(
|
||||
"WatchOnly", []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,
|
||||
),
|
||||
}, password, &lnrpc.WatchOnly{
|
||||
MasterKeyBirthdayTimestamp: 0,
|
||||
MasterKeyFingerprint: nil,
|
||||
Accounts: watchOnlyAccounts,
|
||||
},
|
||||
)
|
||||
require.NoError(t.t, err)
|
||||
|
||||
resp, err := watchOnly.GetInfo(ctxb, &lnrpc.GetInfoRequest{})
|
||||
require.NoError(t.t, err)
|
||||
|
||||
require.Equal(t.t, signerNodePubKey, resp.IdentityPubkey)
|
||||
|
||||
if subTest.sendCoins {
|
||||
net.SendCoins(t.t, btcutil.SatoshiPerBitcoin, watchOnly)
|
||||
assertAccountBalance(
|
||||
t.t, signer, "default",
|
||||
btcutil.SatoshiPerBitcoin, 0,
|
||||
)
|
||||
assertAccountBalance(
|
||||
t.t, watchOnly, "default",
|
||||
btcutil.SatoshiPerBitcoin, 0,
|
||||
)
|
||||
}
|
||||
|
||||
carol := net.NewNode(t.t, "carol", nil)
|
||||
net.EnsureConnected(t.t, watchOnly, carol)
|
||||
|
||||
success := t.t.Run(subTest.name, func(tt *testing.T) {
|
||||
ht := newHarnessTest(tt, net)
|
||||
subTest.fn(ht, watchOnly, carol)
|
||||
})
|
||||
|
||||
shutdownAndAssert(net, t, carol)
|
||||
shutdownAndAssert(net, t, watchOnly)
|
||||
shutdownAndAssert(net, t, signer)
|
||||
|
||||
if !success {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,14 @@ import (
|
||||
// the node's pubkey and a customized public key to check the validity of the
|
||||
// result.
|
||||
func testDeriveSharedKey(net *lntest.NetworkHarness, t *harnessTest) {
|
||||
runDeriveSharedKey(t, net.Alice)
|
||||
}
|
||||
|
||||
// runDeriveSharedKey checks the ECDH performed by the endpoint
|
||||
// DeriveSharedKey. It creates an ephemeral private key, performing an ECDH with
|
||||
// the node's pubkey and a customized public key to check the validity of the
|
||||
// result.
|
||||
func runDeriveSharedKey(t *harnessTest, alice *lntest.HarnessNode) {
|
||||
ctxb := context.Background()
|
||||
|
||||
// Create an ephemeral key, extracts its public key, and make a
|
||||
@@ -32,7 +40,7 @@ func testDeriveSharedKey(net *lntest.NetworkHarness, t *harnessTest) {
|
||||
req *signrpc.SharedKeyRequest) {
|
||||
|
||||
ctxt, _ := context.WithTimeout(ctxb, defaultTimeout)
|
||||
resp, err := net.Alice.SignerClient.DeriveSharedKey(ctxt, req)
|
||||
resp, err := alice.SignerClient.DeriveSharedKey(ctxt, req)
|
||||
require.NoError(t.t, err, "calling DeriveSharedKey failed")
|
||||
|
||||
sharedKey, _ := privKeyECDH.ECDH(pub)
|
||||
@@ -42,13 +50,13 @@ func testDeriveSharedKey(net *lntest.NetworkHarness, t *harnessTest) {
|
||||
)
|
||||
}
|
||||
|
||||
nodePub, err := btcec.ParsePubKey(net.Alice.PubKey[:], btcec.S256())
|
||||
nodePub, err := btcec.ParsePubKey(alice.PubKey[:], btcec.S256())
|
||||
require.NoError(t.t, err, "failed to parse node pubkey")
|
||||
|
||||
customizedKeyFamily := int32(keychain.KeyFamilyMultiSig)
|
||||
customizedIndex := int32(1)
|
||||
customizedPub, err := deriveCustomizedKey(
|
||||
ctxb, net.Alice, customizedKeyFamily, customizedIndex,
|
||||
ctxb, alice, customizedKeyFamily, customizedIndex,
|
||||
)
|
||||
require.NoError(t.t, err, "failed to create customized pubkey")
|
||||
|
||||
@@ -85,7 +93,7 @@ func testDeriveSharedKey(net *lntest.NetworkHarness, t *harnessTest) {
|
||||
req = &signrpc.SharedKeyRequest{
|
||||
EphemeralPubkey: ephemeralPubBytes,
|
||||
KeyDesc: &signrpc.KeyDescriptor{
|
||||
RawKeyBytes: net.Alice.PubKey[:],
|
||||
RawKeyBytes: alice.PubKey[:],
|
||||
KeyLoc: &signrpc.KeyLocator{
|
||||
KeyFamily: int32(keychain.KeyFamilyNodeKey),
|
||||
},
|
||||
@@ -135,7 +143,7 @@ func testDeriveSharedKey(net *lntest.NetworkHarness, t *harnessTest) {
|
||||
// params, the expected error is returned.
|
||||
assertErrorMatch := func(match string, req *signrpc.SharedKeyRequest) {
|
||||
ctxt, _ := context.WithTimeout(ctxb, defaultTimeout)
|
||||
_, err := net.Alice.SignerClient.DeriveSharedKey(ctxt, req)
|
||||
_, err := alice.SignerClient.DeriveSharedKey(ctxt, req)
|
||||
require.Error(t.t, err, "expected to have an error")
|
||||
require.Contains(
|
||||
t.t, err.Error(), match, "error failed to match",
|
||||
@@ -163,7 +171,7 @@ func testDeriveSharedKey(net *lntest.NetworkHarness, t *harnessTest) {
|
||||
req = &signrpc.SharedKeyRequest{
|
||||
EphemeralPubkey: ephemeralPubBytes,
|
||||
KeyDesc: &signrpc.KeyDescriptor{
|
||||
RawKeyBytes: net.Alice.PubKey[:],
|
||||
RawKeyBytes: alice.PubKey[:],
|
||||
},
|
||||
}
|
||||
assertErrorMatch("key_desc.key_loc must also be set", req)
|
||||
|
||||
@@ -355,4 +355,8 @@ var allTestCases = []*testCase{
|
||||
name: "wipe forwarding packages",
|
||||
test: testWipeForwardingPackages,
|
||||
},
|
||||
{
|
||||
name: "remote signer",
|
||||
test: testRemoteSigner,
|
||||
},
|
||||
}
|
||||
@@ -594,9 +594,6 @@ func testWalletImportAccount(net *lntest.NetworkHarness, t *harnessTest) {
|
||||
func testWalletImportAccountScenario(net *lntest.NetworkHarness, t *harnessTest,
|
||||
addrType walletrpc.AddressType) {
|
||||
|
||||
ctxb := context.Background()
|
||||
const utxoAmt int64 = btcutil.SatoshiPerBitcoin
|
||||
|
||||
// We'll start our test by having two nodes, Carol and Dave. Carol's
|
||||
// default wallet account will be imported into Dave's node.
|
||||
carol := net.NewNode(t.t, "carol", nil)
|
||||
@@ -605,6 +602,15 @@ func testWalletImportAccountScenario(net *lntest.NetworkHarness, t *harnessTest,
|
||||
dave := net.NewNode(t.t, "dave", nil)
|
||||
defer shutdownAndAssert(net, t, dave)
|
||||
|
||||
runWalletImportAccountScenario(net, t, addrType, carol, dave)
|
||||
}
|
||||
|
||||
func runWalletImportAccountScenario(net *lntest.NetworkHarness, t *harnessTest,
|
||||
addrType walletrpc.AddressType, carol, dave *lntest.HarnessNode) {
|
||||
|
||||
ctxb := context.Background()
|
||||
const utxoAmt int64 = btcutil.SatoshiPerBitcoin
|
||||
|
||||
ctxt, cancel := context.WithTimeout(ctxb, defaultTimeout)
|
||||
defer cancel()
|
||||
listReq := &walletrpc.ListAccountsRequest{
|
||||
|
||||
@@ -116,7 +116,7 @@ func (s *SingleSigner) ComputeInputScript(tx *wire.MsgTx,
|
||||
// SignMessage takes a public key and a message and only signs the message
|
||||
// with the stored private key if the public key matches the private key.
|
||||
func (s *SingleSigner) SignMessage(keyLoc keychain.KeyLocator,
|
||||
msg []byte) (*btcec.Signature, error) {
|
||||
msg []byte, doubleHash bool) (*btcec.Signature, error) {
|
||||
|
||||
mockKeyLoc := s.KeyLoc
|
||||
if s.KeyLoc.IsEmpty() {
|
||||
@@ -127,7 +127,12 @@ func (s *SingleSigner) SignMessage(keyLoc keychain.KeyLocator,
|
||||
return nil, fmt.Errorf("unknown public key")
|
||||
}
|
||||
|
||||
digest := chainhash.DoubleHashB(msg)
|
||||
var digest []byte
|
||||
if doubleHash {
|
||||
digest = chainhash.DoubleHashB(msg)
|
||||
} else {
|
||||
digest = chainhash.HashB(msg)
|
||||
}
|
||||
sign, err := s.Privkey.Sign(digest)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't sign the message: %v", err)
|
||||
|
||||
@@ -169,7 +169,7 @@ func (w *WalletController) ListLeasedOutputs() ([]*wtxmgr.LockedOutput, error) {
|
||||
}
|
||||
|
||||
// FundPsbt currently does nothing.
|
||||
func (w *WalletController) FundPsbt(_ *psbt.Packet,
|
||||
func (w *WalletController) FundPsbt(_ *psbt.Packet, _ int32,
|
||||
_ chainfee.SatPerKWeight, _ string) (int32, error) {
|
||||
|
||||
return 0, nil
|
||||
|
||||
Reference in new issue
Block a user