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 }