@@ -3,16 +3,20 @@ package itest
33import (
44 "fmt"
55 "testing"
6+ "time"
67
78 "github.com/btcsuite/btcd/btcutil"
89 "github.com/btcsuite/btcd/btcutil/hdkeychain"
910 "github.com/btcsuite/btcwallet/waddrmgr"
1011 "github.com/lightningnetwork/lnd/keychain"
12+ "github.com/lightningnetwork/lnd/lncfg"
1113 "github.com/lightningnetwork/lnd/lnrpc"
1214 "github.com/lightningnetwork/lnd/lnrpc/signrpc"
1315 "github.com/lightningnetwork/lnd/lnrpc/walletrpc"
1416 "github.com/lightningnetwork/lnd/lntest"
1517 "github.com/lightningnetwork/lnd/lntest/node"
18+ "github.com/lightningnetwork/lnd/lntest/wait"
19+ "github.com/lightningnetwork/lnd/lnwallet/rpcwallet"
1620 "github.com/stretchr/testify/require"
1721)
1822
@@ -324,7 +328,7 @@ func testOutboundRemoteSigner(ht *lntest.HarnessTest) {
324328 "--remotesigner.timeout=30s" ,
325329 "--remotesigner.requesttimeout=30s" ,
326330 }, commitArgs ... ),
327- password ,
331+ password , true ,
328332 )
329333
330334 // As the signer node will make an outbound connection to the
@@ -412,6 +416,107 @@ func testOutboundRemoteSigner(ht *lntest.HarnessTest) {
412416 }
413417}
414418
419+ // testOutboundRSMacaroonEnforcement tests that a valid macaroon including
420+ // the `remotesigner` entity is required to connect to a watch-only node that
421+ // uses an outbound remote signer, while the watch-only node is in the state
422+ // where it waits for the signer to connect.
423+ func testOutboundRSMacaroonEnforcement (ht * lntest.HarnessTest ) {
424+ // Ensure that the watch-only node uses a configuration that requires an
425+ // outbound remote signer during startup.
426+ watchOnlyArgs := []string {
427+ "--remotesigner.enable" ,
428+ "--remotesigner.signertype=outbound" ,
429+ "--remotesigner.timeout=15s" ,
430+ "--remotesigner.requesttimeout=15s" ,
431+ }
432+
433+ // Create the watch-only node. Note that we require authentication for
434+ // the watch-only node, as we want to test that the macaroon enforcement
435+ // works as expected.
436+ watchOnly := ht .CreateNewNode ("WatchOnly" , watchOnlyArgs , nil , false )
437+
438+ startChan := make (chan error )
439+
440+ // Start the watch-only node in a goroutine as it requires a remote
441+ // signer to connect before it can fully start.
442+ go func () {
443+ startChan <- watchOnly .Start (ht .Context ())
444+ }()
445+
446+ // Wait and ensure that the watch-only node reaches the state where
447+ // it waits for the remote signer to connect, as this is the state where
448+ // we want to test the macaroon enforcement.
449+ err := wait .Predicate (func () bool {
450+ if watchOnly .RPC == nil {
451+ return false
452+ }
453+
454+ state , err := watchOnly .RPC .State .GetState (
455+ ht .Context (), & lnrpc.GetStateRequest {},
456+ )
457+ if err != nil {
458+ return false
459+ }
460+
461+ return state .State == lnrpc .WalletState_ALLOW_REMOTE_SIGNER
462+ }, 5 * time .Second )
463+ require .NoError (ht , err )
464+
465+ // Set up a connection to the watch-only node. However, instead of using
466+ // the watch-only node's admin macaroon, we'll use the invoice macaroon.
467+ // The connection should not be allowed using this macaroon because it
468+ // lacks the `remotesigner` entity required when the signer node
469+ // connects to the watch-only node.
470+ cfg := & lncfg.RemoteSigner {
471+ SignerType : lncfg .SignerClientType ,
472+ RPCHost : watchOnly .Cfg .RPCAddr (),
473+ MacaroonPath : watchOnly .Cfg .InvoiceMacPath ,
474+ TLSCertPath : watchOnly .Cfg .TLSCertPath ,
475+ Timeout : 10 * time .Second ,
476+ }
477+ streamFeeder := rpcwallet .NewStreamFeeder (
478+ cfg .RPCHost , cfg .MacaroonPath , cfg .TLSCertPath ,
479+ cfg .RequestTimeout ,
480+ )
481+
482+ stream , cleanup , err := streamFeeder .GetStream (ht .Context ())
483+ require .NoError (ht , err )
484+
485+ defer cleanup ()
486+
487+ // Since we're using an unauthorized macaroon, we should expect to be
488+ // denied access to the watch-only node.
489+ _ , err = stream .Recv ()
490+ require .ErrorContains (ht , err , "permission denied" )
491+
492+ // Finally, connect a real signer to the watch-only node so that
493+ // it can start up properly.
494+ signerArgs := []string {
495+ "--remotesigner.signertype=signer" ,
496+ "--remotesigner.timeout=30s" ,
497+ "--remotesigner.requesttimeout=10s" ,
498+ fmt .Sprintf (
499+ "--remotesigner.rpchost=localhost:%d" ,
500+ watchOnly .Cfg .RPCPort ,
501+ ),
502+ fmt .Sprintf (
503+ "--remotesigner.tlscertpath=%s" ,
504+ watchOnly .Cfg .TLSCertPath ,
505+ ),
506+ fmt .Sprintf (
507+ "--remotesigner.macaroonpath=%s" ,
508+ watchOnly .Cfg .AdminMacPath , // An authorized macaroon.
509+ ),
510+ }
511+
512+ _ = ht .NewNode ("Signer" , signerArgs )
513+
514+ // Finally, wait and ensure that the watch-only node is able to start
515+ // up properly.
516+ err = <- startChan
517+ require .NoError (ht , err , "Shouldn't error on watch-only node startup" )
518+ }
519+
415520// deriveCustomScopeAccounts derives the first 255 default accounts of the custom lnd
416521// internal key scope.
417522func deriveCustomScopeAccounts (t * testing.T ) []* lnrpc.WatchOnlyAccount {
0 commit comments