lntest/node: add ability to unlock/init HarnessNode

This commit is contained in:
Conner Fromknecht 2018-04-02 16:57:25 -07:00
parent 329793d06b
commit c977ebb8bd
No known key found for this signature in database
GPG Key ID: 39DE78FBE6ACB0EF

@ -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
}