mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-06-30 18:43:42 +02:00
lntest: add itest for remote signing
This commit is contained in:
@ -427,6 +427,39 @@ func (n *NetworkHarness) newNodeWithSeed(name string, extraArgs []string,
|
|||||||
return node, genSeedResp.CipherSeedMnemonic, response.AdminMacaroon, nil
|
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,
|
// RestoreNodeWithSeed fully initializes a HarnessNode using a chosen mnemonic,
|
||||||
// password, recovery window, and optionally a set of static channel backups.
|
// password, recovery window, and optionally a set of static channel backups.
|
||||||
// After providing the initialization request to unlock the node, this method
|
// After providing the initialization request to unlock the node, this method
|
||||||
|
266
lntest/itest/lnd_remote_signer_test.go
Normal file
266
lntest/itest/lnd_remote_signer_test.go
Normal file
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -355,4 +355,8 @@ var allTestCases = []*testCase{
|
|||||||
name: "wipe forwarding packages",
|
name: "wipe forwarding packages",
|
||||||
test: testWipeForwardingPackages,
|
test: testWipeForwardingPackages,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "remote signer",
|
||||||
|
test: testRemoteSigner,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user