lntest/node: add ability to unlock/init HarnessNode
This commit is contained in:
1 file changed
+106
-18
+106
-18
@@ -98,6 +98,8 @@ type nodeConfig struct {
|
|||||||
ReadMacPath string
|
ReadMacPath string
|
||||||
InvoiceMacPath string
|
InvoiceMacPath string
|
||||||
|
|
||||||
|
HasSeed bool
|
||||||
|
|
||||||
P2PPort int
|
P2PPort int
|
||||||
RPCPort int
|
RPCPort int
|
||||||
RESTPort int
|
RESTPort int
|
||||||
@@ -136,7 +138,6 @@ func (cfg nodeConfig) genArgs() []string {
|
|||||||
encodedCert := hex.EncodeToString(cfg.RPCConfig.Certificates)
|
encodedCert := hex.EncodeToString(cfg.RPCConfig.Certificates)
|
||||||
args = append(args, "--bitcoin.active")
|
args = append(args, "--bitcoin.active")
|
||||||
args = append(args, "--nobootstrap")
|
args = append(args, "--nobootstrap")
|
||||||
args = append(args, "--noencryptwallet")
|
|
||||||
args = append(args, "--debuglevel=debug")
|
args = append(args, "--debuglevel=debug")
|
||||||
args = append(args, "--bitcoin.defaultchanconfs=1")
|
args = append(args, "--bitcoin.defaultchanconfs=1")
|
||||||
args = append(args, "--bitcoin.defaultremotedelay=4")
|
args = append(args, "--bitcoin.defaultremotedelay=4")
|
||||||
@@ -159,6 +160,10 @@ func (cfg nodeConfig) genArgs() []string {
|
|||||||
args = append(args, fmt.Sprintf("--externalip=%s", cfg.P2PAddr()))
|
args = append(args, fmt.Sprintf("--externalip=%s", cfg.P2PAddr()))
|
||||||
args = append(args, fmt.Sprintf("--trickledelay=%v", trickleDelay))
|
args = append(args, fmt.Sprintf("--trickledelay=%v", trickleDelay))
|
||||||
|
|
||||||
|
if !cfg.HasSeed {
|
||||||
|
args = append(args, "--noencryptwallet")
|
||||||
|
}
|
||||||
|
|
||||||
if cfg.ExtraArgs != nil {
|
if cfg.ExtraArgs != nil {
|
||||||
args = append(args, cfg.ExtraArgs...)
|
args = append(args, cfg.ExtraArgs...)
|
||||||
}
|
}
|
||||||
@@ -195,10 +200,13 @@ type HarnessNode struct {
|
|||||||
wg sync.WaitGroup
|
wg sync.WaitGroup
|
||||||
|
|
||||||
lnrpc.LightningClient
|
lnrpc.LightningClient
|
||||||
|
|
||||||
|
lnrpc.WalletUnlockerClient
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assert *HarnessNode implements the lnrpc.LightningClient interface.
|
// Assert *HarnessNode implements the lnrpc.LightningClient interface.
|
||||||
var _ lnrpc.LightningClient = (*HarnessNode)(nil)
|
var _ lnrpc.LightningClient = (*HarnessNode)(nil)
|
||||||
|
var _ lnrpc.WalletUnlockerClient = (*HarnessNode)(nil)
|
||||||
|
|
||||||
// newNode creates a new test lightning node instance from the passed config.
|
// newNode creates a new test lightning node instance from the passed config.
|
||||||
func newNode(cfg nodeConfig) (*HarnessNode, error) {
|
func newNode(cfg nodeConfig) (*HarnessNode, error) {
|
||||||
@@ -301,13 +309,78 @@ func (hn *HarnessNode) start(lndError chan<- error) error {
|
|||||||
|
|
||||||
// Since Stop uses the LightningClient to stop the node, if we fail to get a
|
// Since Stop uses the LightningClient to stop the node, if we fail to get a
|
||||||
// connected client, we have to kill the process.
|
// connected client, we have to kill the process.
|
||||||
conn, err := hn.connectRPC()
|
useMacaroons := !hn.cfg.HasSeed
|
||||||
|
conn, err := hn.ConnectRPC(useMacaroons)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
hn.cmd.Process.Kill()
|
hn.cmd.Process.Kill()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If the node was created with a seed, we will need to perform an
|
||||||
|
// additional step to unlock the wallet. The connection returned will
|
||||||
|
// only use the TLS certs, and can only perform operations necessary to
|
||||||
|
// unlock the daemon.
|
||||||
|
if hn.cfg.HasSeed {
|
||||||
|
hn.WalletUnlockerClient = lnrpc.NewWalletUnlockerClient(conn)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return hn.initLightningClient(conn)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init initializes a harness node by passing the init request via rpc. After
|
||||||
|
// the request is submitted, this method will block until an
|
||||||
|
// macaroon-authenticated rpc connection can be established to the harness node.
|
||||||
|
// Once established, the new connection is used to initialize the
|
||||||
|
// LightningClient and subscribes the HarnessNode to topology changes.
|
||||||
|
func (hn *HarnessNode) Init(ctx context.Context,
|
||||||
|
initReq *lnrpc.InitWalletRequest) error {
|
||||||
|
|
||||||
|
timeout := time.Duration(time.Second * 15)
|
||||||
|
ctxt, _ := context.WithTimeout(ctx, timeout)
|
||||||
|
_, err := hn.InitWallet(ctxt, initReq)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for the wallet to finish unlocking, such that we can connect to
|
||||||
|
// it via a macaroon-authenticated rpc connection.
|
||||||
|
var conn *grpc.ClientConn
|
||||||
|
if err = WaitPredicate(func() bool {
|
||||||
|
conn, err = hn.ConnectRPC(true)
|
||||||
|
return err == nil
|
||||||
|
}, 5*time.Second); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return hn.initLightningClient(conn)
|
||||||
|
}
|
||||||
|
|
||||||
|
// initLightningClient constructs the grpc LightningClient from the given client
|
||||||
|
// connection and subscribes the harness node to graph topology updates.
|
||||||
|
func (hn *HarnessNode) initLightningClient(conn *grpc.ClientConn) error {
|
||||||
|
// Construct the LightningClient that will allow us to use the
|
||||||
|
// HarnessNode directly for normal rpc operations.
|
||||||
hn.LightningClient = lnrpc.NewLightningClient(conn)
|
hn.LightningClient = lnrpc.NewLightningClient(conn)
|
||||||
|
|
||||||
|
// Set the harness node's pubkey to what the node claims in GetInfo.
|
||||||
|
err := hn.FetchNodeInfo()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Launch the watcher that will hook into graph related topology change
|
||||||
|
// from the PoV of this node.
|
||||||
|
hn.wg.Add(1)
|
||||||
|
go hn.lightningNetworkWatcher()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// FetchNodeInfo queries an unlocked node to retrieve its public key. This
|
||||||
|
// method also spawns a lightning network watcher for this node, which watches
|
||||||
|
// for topology changes.
|
||||||
|
func (hn *HarnessNode) FetchNodeInfo() error {
|
||||||
// Obtain the lnid of this node for quick identification purposes.
|
// Obtain the lnid of this node for quick identification purposes.
|
||||||
ctxb := context.Background()
|
ctxb := context.Background()
|
||||||
info, err := hn.GetInfo(ctxb, &lnrpc.GetInfoRequest{})
|
info, err := hn.GetInfo(ctxb, &lnrpc.GetInfoRequest{})
|
||||||
@@ -323,11 +396,6 @@ func (hn *HarnessNode) start(lndError chan<- error) error {
|
|||||||
}
|
}
|
||||||
copy(hn.PubKey[:], pubkey)
|
copy(hn.PubKey[:], pubkey)
|
||||||
|
|
||||||
// Launch the watcher that will hook into graph related topology change
|
|
||||||
// from the PoV of this node.
|
|
||||||
hn.wg.Add(1)
|
|
||||||
go hn.lightningNetworkWatcher()
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -365,24 +433,45 @@ func (hn *HarnessNode) writePidFile() error {
|
|||||||
|
|
||||||
// connectRPC uses the TLS certificate and admin macaroon files written by the
|
// connectRPC uses the TLS certificate and admin macaroon files written by the
|
||||||
// lnd node to create a gRPC client connection.
|
// lnd node to create a gRPC client connection.
|
||||||
func (hn *HarnessNode) connectRPC() (*grpc.ClientConn, error) {
|
func (hn *HarnessNode) ConnectRPC(useMacs bool) (*grpc.ClientConn, error) {
|
||||||
// Wait until TLS certificate and admin macaroon are created before
|
// Wait until TLS certificate and admin macaroon are created before
|
||||||
// using them, up to 20 sec.
|
// using them, up to 20 sec.
|
||||||
tlsTimeout := time.After(30 * time.Second)
|
tlsTimeout := time.After(30 * time.Second)
|
||||||
for !fileExists(hn.cfg.TLSCertPath) || !fileExists(hn.cfg.AdminMacPath) {
|
for !fileExists(hn.cfg.TLSCertPath) {
|
||||||
select {
|
select {
|
||||||
case <-tlsTimeout:
|
case <-tlsTimeout:
|
||||||
return nil, fmt.Errorf("timeout waiting for TLS cert file " +
|
return nil, fmt.Errorf("timeout waiting for TLS cert " +
|
||||||
"and admin macaroon file to be created after " +
|
"file to be created after 30 seconds")
|
||||||
"20 seconds")
|
|
||||||
case <-time.After(100 * time.Millisecond):
|
case <-time.After(100 * time.Millisecond):
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
opts := []grpc.DialOption{
|
||||||
|
grpc.WithBlock(),
|
||||||
|
grpc.WithTimeout(time.Second * 20),
|
||||||
|
}
|
||||||
|
|
||||||
tlsCreds, err := credentials.NewClientTLSFromFile(hn.cfg.TLSCertPath, "")
|
tlsCreds, err := credentials.NewClientTLSFromFile(hn.cfg.TLSCertPath, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
opts = append(opts, grpc.WithTransportCredentials(tlsCreds))
|
||||||
|
|
||||||
|
if !useMacs {
|
||||||
|
return grpc.Dial(hn.cfg.RPCAddr(), opts...)
|
||||||
|
}
|
||||||
|
|
||||||
|
macTimeout := time.After(30 * time.Second)
|
||||||
|
for !fileExists(hn.cfg.AdminMacPath) {
|
||||||
|
select {
|
||||||
|
case <-macTimeout:
|
||||||
|
return nil, fmt.Errorf("timeout waiting for admin " +
|
||||||
|
"macaroon file to be created after 30 seconds")
|
||||||
|
case <-time.After(100 * time.Millisecond):
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
macBytes, err := ioutil.ReadFile(hn.cfg.AdminMacPath)
|
macBytes, err := ioutil.ReadFile(hn.cfg.AdminMacPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -391,12 +480,10 @@ func (hn *HarnessNode) connectRPC() (*grpc.ClientConn, error) {
|
|||||||
if err = mac.UnmarshalBinary(macBytes); err != nil {
|
if err = mac.UnmarshalBinary(macBytes); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
opts := []grpc.DialOption{
|
|
||||||
grpc.WithTransportCredentials(tlsCreds),
|
macCred := macaroons.NewMacaroonCredential(mac)
|
||||||
grpc.WithPerRPCCredentials(macaroons.NewMacaroonCredential(mac)),
|
opts = append(opts, grpc.WithPerRPCCredentials(macCred))
|
||||||
grpc.WithBlock(),
|
|
||||||
grpc.WithTimeout(time.Second * 20),
|
|
||||||
}
|
|
||||||
return grpc.Dial(hn.cfg.RPCAddr(), opts...)
|
return grpc.Dial(hn.cfg.RPCAddr(), opts...)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -441,6 +528,7 @@ func (hn *HarnessNode) stop() error {
|
|||||||
hn.quit = nil
|
hn.quit = nil
|
||||||
hn.processExit = nil
|
hn.processExit = nil
|
||||||
hn.LightningClient = nil
|
hn.LightningClient = nil
|
||||||
|
hn.WalletUnlockerClient = nil
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user