github.com/decred/dcrlnd@v0.7.6/lntest/dcrlnd_harness.go (about)

     1  package lntest
     2  
     3  import (
     4  	"context"
     5  	"crypto/tls"
     6  	"crypto/x509"
     7  	"fmt"
     8  	"io/ioutil"
     9  	"os/exec"
    10  	"strings"
    11  	"time"
    12  
    13  	pb "decred.org/dcrwallet/v4/rpc/walletrpc"
    14  	"github.com/decred/dcrd/chaincfg/chainhash"
    15  	"github.com/decred/dcrd/dcrutil/v4"
    16  	"github.com/decred/dcrd/hdkeychain/v3"
    17  	"github.com/decred/dcrlnd/lnrpc"
    18  	"github.com/decred/dcrlnd/lntest/wait"
    19  	rpctest "github.com/decred/dcrtest/dcrdtest"
    20  	"google.golang.org/grpc"
    21  	"google.golang.org/grpc/backoff"
    22  	"google.golang.org/grpc/credentials"
    23  )
    24  
    25  // setupVotingWallet sets up a minimum voting wallet, so that the simnet used
    26  // for tests can advance past SVH.
    27  func (h *HarnessMiner) setupVotingWallet() error {
    28  	vwCtx, vwCancel := context.WithCancel(h.runCtx)
    29  	vw, err := rpctest.NewVotingWallet(vwCtx, h.Harness)
    30  	if err != nil {
    31  		vwCancel()
    32  		return err
    33  	}
    34  
    35  	// Use a custom miner on the voting wallet that ensures simnet blocks
    36  	// are generated as fast as possible without triggering PoW difficulty
    37  	// increases.
    38  	vw.SetMiner(func(ctx context.Context, nb uint32) ([]*chainhash.Hash, error) {
    39  		return rpctest.AdjustedSimnetMiner(ctx, h.Node, nb)
    40  	})
    41  
    42  	err = vw.Start(vwCtx)
    43  	if err != nil {
    44  		defer vwCancel()
    45  		return err
    46  	}
    47  
    48  	h.votingWallet = vw
    49  	h.votingWalletCancel = vwCancel
    50  	return nil
    51  }
    52  
    53  // SetUpChain performs the initial chain setup for integration tests. This
    54  // should be done only once.
    55  func (h *HarnessMiner) SetUpChain() error {
    56  	// Generate the premine block the usual way.
    57  	ctx, cancel := context.WithTimeout(h.runCtx, 30*time.Second)
    58  	defer cancel()
    59  	_, err := h.Node.Generate(ctx, 1)
    60  	if err != nil {
    61  		return fmt.Errorf("unable to generate premine: %v", err)
    62  	}
    63  
    64  	// Generate enough blocks so that the network harness can have funds to
    65  	// send to the voting wallet, Alice and Bob.
    66  	_, err = rpctest.AdjustedSimnetMiner(ctx, h.Node, 64)
    67  	if err != nil {
    68  		return fmt.Errorf("unable to init chain: %v", err)
    69  	}
    70  
    71  	// Setup a ticket buying/voting dcrwallet, so that the network advances
    72  	// past SVH.
    73  	err = h.setupVotingWallet()
    74  	if err != nil {
    75  		return err
    76  	}
    77  
    78  	return nil
    79  }
    80  
    81  // ModifyTestCaseName modifies the current test case name.
    82  func (n *NetworkHarness) ModifyTestCaseName(testCase string) {
    83  	n.currentTestCase = testCase
    84  }
    85  
    86  func (hn *HarnessNode) LogPrintf(format string, args ...interface{}) error {
    87  	now := time.Now().Format("2006-01-02 15:04:05.999")
    88  	f := now + " ----------: " + format
    89  	hn.AddToLog(f, args...)
    90  	return nil
    91  }
    92  
    93  func tlsCertFromFile(fname string) (*x509.CertPool, error) {
    94  	b, err := ioutil.ReadFile(fname)
    95  	if err != nil {
    96  		return nil, err
    97  	}
    98  	cp := x509.NewCertPool()
    99  	if !cp.AppendCertsFromPEM(b) {
   100  		return nil, fmt.Errorf("credentials: failed to append certificates")
   101  	}
   102  
   103  	return cp, nil
   104  }
   105  
   106  func (hn *HarnessNode) startRemoteWallet() error {
   107  	// Prepare and start the remote wallet process
   108  	walletArgs := hn.Cfg.genWalletArgs()
   109  	const dcrwalletExe = "dcrwallet-dcrlnd"
   110  	hn.walletCmd = exec.Command(dcrwalletExe, walletArgs...)
   111  
   112  	hn.walletCmd.Stdout = hn.logFile
   113  	hn.walletCmd.Stderr = hn.logFile
   114  
   115  	if err := hn.walletCmd.Start(); err != nil {
   116  		return fmt.Errorf("unable to start %s's wallet: %v", hn.Name(), err)
   117  	}
   118  
   119  	// Wait until the TLS cert file exists, so we can connect to the
   120  	// wallet.
   121  	var caCert *x509.CertPool
   122  	var clientCert tls.Certificate
   123  	err := wait.NoError(func() error {
   124  		var err error
   125  		caCert, err = tlsCertFromFile(hn.Cfg.TLSCertPath)
   126  		if err != nil {
   127  			return fmt.Errorf("unable to load wallet tls cert: %v", err)
   128  		}
   129  
   130  		clientCert, err = tls.LoadX509KeyPair(hn.Cfg.TLSCertPath, hn.Cfg.TLSKeyPath)
   131  		if err != nil {
   132  			return fmt.Errorf("unable to load wallet cert and key files: %v", err)
   133  		}
   134  
   135  		return nil
   136  	}, time.Second*30)
   137  	if err != nil {
   138  		return fmt.Errorf("error reading wallet TLS cert: %v", err)
   139  	}
   140  
   141  	// Setup the TLS config and credentials.
   142  	tlsCfg := &tls.Config{
   143  		ServerName:   "localhost",
   144  		RootCAs:      caCert,
   145  		Certificates: []tls.Certificate{clientCert},
   146  	}
   147  	creds := credentials.NewTLS(tlsCfg)
   148  
   149  	opts := []grpc.DialOption{
   150  		grpc.WithBlock(),
   151  		grpc.WithTimeout(time.Second * 40),
   152  		grpc.WithTransportCredentials(creds),
   153  		grpc.WithConnectParams(grpc.ConnectParams{
   154  			Backoff: backoff.Config{
   155  				BaseDelay:  time.Millisecond * 20,
   156  				Multiplier: 1,
   157  				Jitter:     0.2,
   158  				MaxDelay:   time.Millisecond * 20,
   159  			},
   160  			MinConnectTimeout: time.Millisecond * 20,
   161  		}),
   162  	}
   163  
   164  	hn.walletConn, err = grpc.Dial(
   165  		fmt.Sprintf("localhost:%d", hn.Cfg.WalletPort),
   166  		opts...,
   167  	)
   168  	if err != nil {
   169  		return fmt.Errorf("unable to connect to the wallet: %v", err)
   170  	}
   171  
   172  	password := hn.Cfg.Password
   173  	if len(password) == 0 {
   174  		password = []byte("private1")
   175  	}
   176  
   177  	// Open or create the wallet as necessary.
   178  	ctxb := context.Background()
   179  	loader := pb.NewWalletLoaderServiceClient(hn.walletConn)
   180  	respExists, err := loader.WalletExists(ctxb, &pb.WalletExistsRequest{})
   181  	if err != nil {
   182  		return err
   183  	}
   184  	if respExists.Exists {
   185  		_, err := loader.OpenWallet(ctxb, &pb.OpenWalletRequest{})
   186  		if err != nil {
   187  			return err
   188  		}
   189  
   190  		err = hn.Cfg.BackendCfg.StartWalletSync(loader, password)
   191  		if err != nil {
   192  			return err
   193  		}
   194  	} else if !hn.Cfg.HasSeed {
   195  		// If the test won't require or provide a seed, then initialize
   196  		// the wallet with a random one.
   197  		seed, err := hdkeychain.GenerateSeed(hdkeychain.RecommendedSeedLen)
   198  		if err != nil {
   199  			return err
   200  		}
   201  		reqCreate := &pb.CreateWalletRequest{
   202  			PrivatePassphrase: password,
   203  			Seed:              seed,
   204  		}
   205  		_, err = loader.CreateWallet(ctxb, reqCreate)
   206  		if err != nil {
   207  			return err
   208  		}
   209  
   210  		// Set the wallet to use per-account passphrases.
   211  		wallet := pb.NewWalletServiceClient(hn.walletConn)
   212  		reqSetAcctPwd := &pb.SetAccountPassphraseRequest{
   213  			AccountNumber:        0,
   214  			WalletPassphrase:     password,
   215  			NewAccountPassphrase: password,
   216  		}
   217  		_, err = wallet.SetAccountPassphrase(ctxb, reqSetAcctPwd)
   218  		if err != nil {
   219  			return err
   220  		}
   221  
   222  		// Wait for the wallet to be relocked. This is needed to avoid a
   223  		// wrongful lock event during the following syncing stage.
   224  		err = wait.NoError(func() error {
   225  			res, err := wallet.AccountUnlocked(ctxb, &pb.AccountUnlockedRequest{AccountNumber: 0})
   226  			if err != nil {
   227  				return err
   228  			}
   229  			if res.Unlocked {
   230  				return fmt.Errorf("wallet account still unlocked")
   231  			}
   232  			return nil
   233  		}, 30*time.Second)
   234  		if err != nil {
   235  			return err
   236  		}
   237  
   238  		err = hn.Cfg.BackendCfg.StartWalletSync(loader, password)
   239  		if err != nil {
   240  			return err
   241  		}
   242  	}
   243  
   244  	return nil
   245  }
   246  
   247  func (hn *HarnessNode) unlockRemoteWallet() error {
   248  	password := hn.Cfg.Password
   249  	if len(password) == 0 {
   250  		password = []byte("private1")
   251  	}
   252  
   253  	// Assemble the client key+cert buffer for gRPC auth into the wallet.
   254  	var clientKeyCert []byte
   255  	cert, err := ioutil.ReadFile(hn.Cfg.TLSCertPath)
   256  	if err != nil {
   257  		return fmt.Errorf("unable to load wallet client cert file: %v", err)
   258  	}
   259  	key, err := ioutil.ReadFile(hn.Cfg.TLSKeyPath)
   260  	if err != nil {
   261  		return fmt.Errorf("unable to load wallet client key file: %v", err)
   262  	}
   263  	clientKeyCert = append(key, cert...)
   264  
   265  	unlockReq := &lnrpc.UnlockWalletRequest{
   266  		WalletPassword:    password,
   267  		DcrwClientKeyCert: clientKeyCert,
   268  	}
   269  	err = hn.Unlock(unlockReq)
   270  	if err != nil {
   271  		return fmt.Errorf("unable to unlock remote wallet: %v", err)
   272  	}
   273  	return nil
   274  }
   275  
   276  // GenerateAndSubmitBlock publishes the transactions and generates a new blck.
   277  //
   278  // This function's signature is similar to the one in btcd's rpctest package.
   279  func (h *HarnessMiner) GenerateAndSubmitBlock(txs []*dcrutil.Tx, _ int32, _ time.Time) (*dcrutil.Block, error) {
   280  	for _, tx := range txs {
   281  		_, err := h.Node.SendRawTransaction(h.runCtx, tx.MsgTx(), true)
   282  		if err != nil && !strings.Contains(err.Error(), "already have transaction") {
   283  			return nil, err
   284  		}
   285  	}
   286  
   287  	bh, err := h.votingWallet.GenerateBlocks(h.runCtx, 1)
   288  	if err != nil {
   289  		return nil, err
   290  	}
   291  
   292  	block, err := h.Node.GetBlock(h.runCtx, bh[0])
   293  	if err != nil {
   294  		return nil, err
   295  	}
   296  
   297  	return dcrutil.NewBlock(block), nil
   298  }