123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212 |
- package lntest
- import (
- "context"
- "fmt"
- "sync"
- "sync/atomic"
- "testing"
- "github.com/lightningnetwork/lnd/lnrpc"
- "github.com/lightningnetwork/lnd/lntest/node"
- "github.com/lightningnetwork/lnd/lntest/wait"
- )
- // nodeManager is responsible for handling the start and stop of a given node.
- // It also keeps track of the running nodes.
- type nodeManager struct {
- sync.Mutex
- // chainBackend houses the information necessary to use a node as LND
- // chain backend, such as rpc configuration, P2P information etc.
- chainBackend node.BackendConfig
- // currentTestCase holds the name for the currently run test case.
- currentTestCase string
- // lndBinary is the full path to the lnd binary that was specifically
- // compiled with all required itest flags.
- lndBinary string
- // dbBackend sets the database backend to use.
- dbBackend node.DatabaseBackend
- // nativeSQL sets the database backend to use native SQL when
- // applicable.
- nativeSQL bool
- // activeNodes is a map of all running nodes, format:
- // {pubkey: *HarnessNode}.
- activeNodes map[uint32]*node.HarnessNode
- // standbyNodes is a map of all the standby nodes, format:
- // {pubkey: *HarnessNode}.
- standbyNodes map[uint32]*node.HarnessNode
- // nodeCounter is a monotonically increasing counter that's used as the
- // node's unique ID.
- nodeCounter uint32
- // feeServiceURL is the url of the fee service.
- feeServiceURL string
- }
- // newNodeManager creates a new node manager instance.
- func newNodeManager(lndBinary string, dbBackend node.DatabaseBackend,
- nativeSQL bool) *nodeManager {
- return &nodeManager{
- lndBinary: lndBinary,
- dbBackend: dbBackend,
- nativeSQL: nativeSQL,
- activeNodes: make(map[uint32]*node.HarnessNode),
- standbyNodes: make(map[uint32]*node.HarnessNode),
- }
- }
- // nextNodeID generates a unique sequence to be used as the node's ID.
- func (nm *nodeManager) nextNodeID() uint32 {
- nodeID := atomic.AddUint32(&nm.nodeCounter, 1)
- return nodeID - 1
- }
- // newNode initializes a new HarnessNode, supporting the ability to initialize
- // a wallet with or without a seed. If useSeed is false, the returned harness
- // node can be used immediately. Otherwise, the node will require an additional
- // initialization phase where the wallet is either created or restored.
- func (nm *nodeManager) newNode(t *testing.T, name string, extraArgs []string,
- password []byte, noAuth bool) (*node.HarnessNode, error) {
- cfg := &node.BaseNodeConfig{
- Name: name,
- LogFilenamePrefix: nm.currentTestCase,
- Password: password,
- BackendCfg: nm.chainBackend,
- ExtraArgs: extraArgs,
- FeeURL: nm.feeServiceURL,
- DBBackend: nm.dbBackend,
- NativeSQL: nm.nativeSQL,
- NodeID: nm.nextNodeID(),
- LndBinary: nm.lndBinary,
- NetParams: harnessNetParams,
- SkipUnlock: noAuth,
- }
- node, err := node.NewHarnessNode(t, cfg)
- if err != nil {
- return nil, err
- }
- // Put node in activeNodes to ensure Shutdown is called even if start
- // returns an error.
- nm.registerNode(node)
- return node, nil
- }
- // RegisterNode records a new HarnessNode in the NetworkHarnesses map of known
- // nodes. This method should only be called with nodes that have successfully
- // retrieved their public keys via FetchNodeInfo.
- func (nm *nodeManager) registerNode(node *node.HarnessNode) {
- nm.Lock()
- nm.activeNodes[node.Cfg.NodeID] = node
- nm.Unlock()
- }
- // ShutdownNode stops an active lnd process and returns when the process has
- // exited and any temporary directories have been cleaned up.
- func (nm *nodeManager) shutdownNode(node *node.HarnessNode) error {
- if err := node.Shutdown(); err != nil {
- return err
- }
- delete(nm.activeNodes, node.Cfg.NodeID)
- return nil
- }
- // restartNode attempts to restart a lightning node by shutting it down
- // cleanly, then restarting the process. This function is fully blocking. Upon
- // restart, the RPC connection to the node will be re-attempted, continuing iff
- // the connection attempt is successful. If the callback parameter is non-nil,
- // then the function will be executed after the node shuts down, but *before*
- // the process has been started up again.
- func (nm *nodeManager) restartNode(ctxt context.Context,
- hn *node.HarnessNode, callback func() error) error {
- // Stop the node.
- if err := hn.Stop(); err != nil {
- return fmt.Errorf("restart node got error: %w", err)
- }
- if callback != nil {
- if err := callback(); err != nil {
- return err
- }
- }
- // Start the node without unlocking the wallet.
- if hn.Cfg.SkipUnlock {
- return hn.StartWithNoAuth(ctxt)
- }
- return hn.Start(ctxt)
- }
- // unlockNode unlocks the node's wallet if the password is configured.
- // Additionally, each time the node is unlocked, the caller can pass a set of
- // SCBs to pass in via the Unlock method allowing them to restore channels
- // during restart.
- func (nm *nodeManager) unlockNode(hn *node.HarnessNode,
- chanBackups ...*lnrpc.ChanBackupSnapshot) error {
- // If the node doesn't have a password set, then we can exit here as we
- // don't need to unlock it.
- if len(hn.Cfg.Password) == 0 {
- return nil
- }
- // Otherwise, we'll unlock the wallet, then complete the final steps
- // for the node initialization process.
- unlockReq := &lnrpc.UnlockWalletRequest{
- WalletPassword: hn.Cfg.Password,
- }
- if len(chanBackups) != 0 {
- unlockReq.ChannelBackups = chanBackups[0]
- unlockReq.RecoveryWindow = 100
- }
- err := wait.NoError(func() error {
- return hn.Unlock(unlockReq)
- }, DefaultTimeout)
- if err != nil {
- return fmt.Errorf("%s: failed to unlock: %w", hn.Name(), err)
- }
- return nil
- }
- // initWalletAndNode will unlock the node's wallet and finish setting up the
- // node so it's ready to take RPC requests.
- func (nm *nodeManager) initWalletAndNode(hn *node.HarnessNode,
- req *lnrpc.InitWalletRequest) ([]byte, error) {
- // Pass the init request via rpc to finish unlocking the node.
- resp := hn.RPC.InitWallet(req)
- // Now that the wallet is unlocked, before creating an authed
- // connection we will close the old unauthed connection.
- if err := hn.CloseConn(); err != nil {
- return nil, fmt.Errorf("close unauthed conn failed")
- }
- // Init the node, which will create the authed grpc conn and all its
- // rpc clients.
- err := hn.InitNode(resp.AdminMacaroon)
- // In stateless initialization mode we get a macaroon back that we have
- // to return to the test, otherwise gRPC calls won't be possible since
- // there are no macaroon files created in that mode.
- // In stateful init the admin macaroon will just be nil.
- return resp.AdminMacaroon, err
- }
|