lntest/node: add ability to unlock/init HarnessNode
This commit is contained in:
parent
329793d06b
commit
c977ebb8bd
124
lntest/node.go
124
lntest/node.go
@ -98,6 +98,8 @@ type nodeConfig struct {
|
||||
ReadMacPath string
|
||||
InvoiceMacPath string
|
||||
|
||||
HasSeed bool
|
||||
|
||||
P2PPort int
|
||||
RPCPort int
|
||||
RESTPort int
|
||||
@ -136,7 +138,6 @@ func (cfg nodeConfig) genArgs() []string {
|
||||
encodedCert := hex.EncodeToString(cfg.RPCConfig.Certificates)
|
||||
args = append(args, "--bitcoin.active")
|
||||
args = append(args, "--nobootstrap")
|
||||
args = append(args, "--noencryptwallet")
|
||||
args = append(args, "--debuglevel=debug")
|
||||
args = append(args, "--bitcoin.defaultchanconfs=1")
|
||||
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("--trickledelay=%v", trickleDelay))
|
||||
|
||||
if !cfg.HasSeed {
|
||||
args = append(args, "--noencryptwallet")
|
||||
}
|
||||
|
||||
if cfg.ExtraArgs != nil {
|
||||
args = append(args, cfg.ExtraArgs...)
|
||||
}
|
||||
@ -195,10 +200,13 @@ type HarnessNode struct {
|
||||
wg sync.WaitGroup
|
||||
|
||||
lnrpc.LightningClient
|
||||
|
||||
lnrpc.WalletUnlockerClient
|
||||
}
|
||||
|
||||
// Assert *HarnessNode implements the lnrpc.LightningClient interface.
|
||||
var _ lnrpc.LightningClient = (*HarnessNode)(nil)
|
||||
var _ lnrpc.WalletUnlockerClient = (*HarnessNode)(nil)
|
||||
|
||||
// newNode creates a new test lightning node instance from the passed config.
|
||||
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
|
||||
// connected client, we have to kill the process.
|
||||
conn, err := hn.connectRPC()
|
||||
useMacaroons := !hn.cfg.HasSeed
|
||||
conn, err := hn.ConnectRPC(useMacaroons)
|
||||
if err != nil {
|
||||
hn.cmd.Process.Kill()
|
||||
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)
|
||||
|
||||
// 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.
|
||||
ctxb := context.Background()
|
||||
info, err := hn.GetInfo(ctxb, &lnrpc.GetInfoRequest{})
|
||||
@ -323,11 +396,6 @@ func (hn *HarnessNode) start(lndError chan<- error) error {
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
@ -365,24 +433,45 @@ func (hn *HarnessNode) writePidFile() error {
|
||||
|
||||
// connectRPC uses the TLS certificate and admin macaroon files written by the
|
||||
// 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
|
||||
// using them, up to 20 sec.
|
||||
tlsTimeout := time.After(30 * time.Second)
|
||||
for !fileExists(hn.cfg.TLSCertPath) || !fileExists(hn.cfg.AdminMacPath) {
|
||||
for !fileExists(hn.cfg.TLSCertPath) {
|
||||
select {
|
||||
case <-tlsTimeout:
|
||||
return nil, fmt.Errorf("timeout waiting for TLS cert file " +
|
||||
"and admin macaroon file to be created after " +
|
||||
"20 seconds")
|
||||
return nil, fmt.Errorf("timeout waiting for TLS cert " +
|
||||
"file to be created after 30 seconds")
|
||||
case <-time.After(100 * time.Millisecond):
|
||||
}
|
||||
}
|
||||
|
||||
opts := []grpc.DialOption{
|
||||
grpc.WithBlock(),
|
||||
grpc.WithTimeout(time.Second * 20),
|
||||
}
|
||||
|
||||
tlsCreds, err := credentials.NewClientTLSFromFile(hn.cfg.TLSCertPath, "")
|
||||
if err != nil {
|
||||
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)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -391,12 +480,10 @@ func (hn *HarnessNode) connectRPC() (*grpc.ClientConn, error) {
|
||||
if err = mac.UnmarshalBinary(macBytes); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
opts := []grpc.DialOption{
|
||||
grpc.WithTransportCredentials(tlsCreds),
|
||||
grpc.WithPerRPCCredentials(macaroons.NewMacaroonCredential(mac)),
|
||||
grpc.WithBlock(),
|
||||
grpc.WithTimeout(time.Second * 20),
|
||||
}
|
||||
|
||||
macCred := macaroons.NewMacaroonCredential(mac)
|
||||
opts = append(opts, grpc.WithPerRPCCredentials(macCred))
|
||||
|
||||
return grpc.Dial(hn.cfg.RPCAddr(), opts...)
|
||||
}
|
||||
|
||||
@ -441,6 +528,7 @@ func (hn *HarnessNode) stop() error {
|
||||
hn.quit = nil
|
||||
hn.processExit = nil
|
||||
hn.LightningClient = nil
|
||||
hn.WalletUnlockerClient = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user