Skip to content

Commit b5d7696

Browse files
committed
itest: add outbound remote signer itest
1 parent 53748bf commit b5d7696

File tree

2 files changed

+153
-15
lines changed

2 files changed

+153
-15
lines changed

itest/list_on_test.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -492,7 +492,11 @@ var allTestCases = []*lntest.TestCase{
492492
},
493493
{
494494
Name: "remote signer",
495-
TestFunc: testRemoteSigner,
495+
TestFunc: testInboundRemoteSigner,
496+
},
497+
{
498+
Name: "outbound remote signer",
499+
TestFunc: testOutboundRemoteSigner,
496500
},
497501
{
498502
Name: "taproot coop close",

itest/lnd_remote_signer_test.go

Lines changed: 148 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -53,19 +53,17 @@ var (
5353
}}
5454
)
5555

56-
// testRemoteSigner tests that a watch-only wallet can use a remote signing
57-
// wallet to perform any signing or ECDH operations.
58-
func testRemoteSigner(ht *lntest.HarnessTest) {
59-
type testCase struct {
60-
name string
61-
randomSeed bool
62-
sendCoins bool
63-
commitType lnrpc.CommitmentType
64-
fn func(tt *lntest.HarnessTest,
65-
wo, carol *node.HarnessNode)
66-
}
56+
type remoteSignerTestCase struct {
57+
name string
58+
randomSeed bool
59+
sendCoins bool
60+
commitType lnrpc.CommitmentType
61+
fn func(tt *lntest.HarnessTest,
62+
wo, carol *node.HarnessNode)
63+
}
6764

68-
subTests := []testCase{{
65+
func getRemoteSignerTestCases(ht *lntest.HarnessTest) []remoteSignerTestCase {
66+
return []remoteSignerTestCase{{
6967
name: "random seed",
7068
randomSeed: true,
7169
fn: func(tt *lntest.HarnessTest, wo, carol *node.HarnessNode) {
@@ -176,9 +174,15 @@ func testRemoteSigner(ht *lntest.HarnessTest) {
176174
}
177175
},
178176
}}
177+
}
179178

179+
// testInboundRemoteSigner tests that a watch-only wallet can use a remote
180+
// signing wallet to perform any signing or ECDH operations. The test
181+
// specifically uses an inbound remote signer, meaning that the watch-only node
182+
// will make an outbound connection to the remote signer.
183+
func testInboundRemoteSigner(ht *lntest.HarnessTest) {
180184
prepareTest := func(st *lntest.HarnessTest,
181-
subTest testCase) (*node.HarnessNode,
185+
subTest remoteSignerTestCase) (*node.HarnessNode,
182186
*node.HarnessNode, *node.HarnessNode) {
183187

184188
// Signer is our signing node and has the wallet with the full
@@ -224,6 +228,7 @@ func testRemoteSigner(ht *lntest.HarnessTest) {
224228
watchOnly := st.NewNodeWatchOnly(
225229
"WatchOnly", append([]string{
226230
"--remotesigner.enable",
231+
"--remotesigner.signertype=inbound",
227232
fmt.Sprintf(
228233
"--remotesigner.rpchost=localhost:%d",
229234
signer.Cfg.RPCPort,
@@ -261,7 +266,136 @@ func testRemoteSigner(ht *lntest.HarnessTest) {
261266
return signer, watchOnly, carol
262267
}
263268

264-
for _, testCase := range subTests {
269+
for _, testCase := range getRemoteSignerTestCases(ht) {
270+
subTest := testCase
271+
272+
success := ht.Run(subTest.name, func(tt *testing.T) {
273+
// Skip the cleanup here as no standby node is used.
274+
st := ht.Subtest(tt)
275+
276+
_, watchOnly, carol := prepareTest(st, subTest)
277+
subTest.fn(st, watchOnly, carol)
278+
})
279+
280+
if !success {
281+
return
282+
}
283+
}
284+
}
285+
286+
// testOutboundRemoteSigner tests that a watch-only wallet can use a remote
287+
// signing wallet to perform any signing or ECDH operations. The test
288+
// specifically uses an outbound remote signer, meaning that the remote signer
289+
// node will make an outbound connection to the watch-only node.
290+
func testOutboundRemoteSigner(ht *lntest.HarnessTest) {
291+
prepareTest := func(st *lntest.HarnessTest,
292+
subTest remoteSignerTestCase) (*node.HarnessNode,
293+
*node.HarnessNode, *node.HarnessNode) {
294+
295+
// Signer is our signing node and has the wallet with the full
296+
// master private key. We test that we can create the watch-only
297+
// wallet from the exported accounts but also from a static key
298+
// to make sure the derivation of the account public keys is
299+
// correct in both cases.
300+
password := []byte("itestpassword")
301+
var (
302+
signerNodePubKey = nodePubKey
303+
watchOnlyAccounts = deriveCustomScopeAccounts(ht.T)
304+
signer *node.HarnessNode
305+
err error
306+
)
307+
308+
var commitArgs []string
309+
if subTest.commitType == lnrpc.CommitmentType_SIMPLE_TAPROOT {
310+
commitArgs = lntest.NodeArgsForCommitType(
311+
subTest.commitType,
312+
)
313+
}
314+
315+
// WatchOnly is the node that has a watch-only wallet and uses
316+
// the Signer node for any operation that requires access to
317+
// private keys. We use the outbound signer type here, meaning
318+
// that the watch-only node expects the signer to make an
319+
// outbound connection to it.
320+
watchOnly := st.CreateNewNode(
321+
"WatchOnly", append([]string{
322+
"--remotesigner.enable",
323+
"--remotesigner.signertype=outbound",
324+
"--remotesigner.timeout=30s",
325+
"--remotesigner.requesttimeout=30s",
326+
}, commitArgs...),
327+
password,
328+
)
329+
330+
// As the signer node will make an outbound connection to the
331+
// watch-only node, we must specify the watch-only node's RPC
332+
// connection details in the signer's configuration.
333+
signerArgs := []string{
334+
"--remotesigner.signertype=signer", // Outbound signer.
335+
"--remotesigner.timeout=30s",
336+
"--remotesigner.requesttimeout=10s",
337+
fmt.Sprintf(
338+
"--remotesigner.rpchost=localhost:%d",
339+
watchOnly.Cfg.RPCPort,
340+
),
341+
fmt.Sprintf(
342+
"--remotesigner.tlscertpath=%s",
343+
watchOnly.Cfg.TLSCertPath,
344+
),
345+
fmt.Sprintf(
346+
"--remotesigner.macaroonpath=%s",
347+
watchOnly.Cfg.AdminMacPath,
348+
),
349+
}
350+
351+
if !subTest.randomSeed {
352+
signer = st.RestoreNodeWithSeed(
353+
"Signer", signerArgs, password, nil, rootKey, 0,
354+
nil,
355+
)
356+
} else {
357+
signer = st.NewNode("Signer", signerArgs)
358+
signerNodePubKey = signer.PubKeyStr
359+
360+
rpcAccts := signer.RPC.ListAccounts(
361+
&walletrpc.ListAccountsRequest{},
362+
)
363+
364+
watchOnlyAccounts, err = walletrpc.AccountsToWatchOnly(
365+
rpcAccts.Accounts,
366+
)
367+
require.NoError(st, err)
368+
}
369+
370+
// As the watch-only node will not fully start until the signer
371+
// node connects to it, we need to start the watch-only node
372+
// after having started the signer node.
373+
st.StartWatchOnly(watchOnly, "WatchOnly", password,
374+
&lnrpc.WatchOnly{
375+
MasterKeyBirthdayTimestamp: 0,
376+
MasterKeyFingerprint: nil,
377+
Accounts: watchOnlyAccounts,
378+
},
379+
)
380+
381+
resp := watchOnly.RPC.GetInfo()
382+
require.Equal(st, signerNodePubKey, resp.IdentityPubkey)
383+
384+
if subTest.sendCoins {
385+
st.FundCoins(btcutil.SatoshiPerBitcoin, watchOnly)
386+
ht.AssertWalletAccountBalance(
387+
watchOnly, "default",
388+
btcutil.SatoshiPerBitcoin, 0,
389+
)
390+
}
391+
392+
carol := st.NewNode("carol", commitArgs)
393+
st.EnsureConnected(watchOnly, carol)
394+
395+
return signer, watchOnly, carol
396+
}
397+
398+
for _, testCase := range getRemoteSignerTestCases(ht) {
265399
subTest := testCase
266400

267401
success := ht.Run(subTest.name, func(tt *testing.T) {

0 commit comments

Comments
 (0)