// connectRPCClient attempts to establish an RPC connection to the created // dcrd process belonging to this Harness instance. If the initial connection // attempt fails, this function will retry h.maxConnRetries times, backing off // the time between subsequent attempts. If after h.maxConnRetries attempts, // we're not able to establish a connection, this function returns with an error. func (h *Harness) connectRPCClient() error { var client *rpc.Client var err error rpcConf := h.node.config.rpcConnConfig() for i := 0; i < h.maxConnRetries; i++ { if client, err = rpc.New(&rpcConf, h.handlers); err != nil { time.Sleep(time.Duration(math.Log(float64(i+3))) * 50 * time.Millisecond) continue } break } if client == nil { return fmt.Errorf("connection timed out: %v", err) } err = client.NotifyBlocks() if err != nil { return err } h.Node = client return nil }
// makeDestinationScriptSource creates a ChangeSource which is used to receive // all correlated previous input value. A non-change address is created by this // function. func makeDestinationScriptSource(rpcClient *dcrrpcclient.Client, accountName string) txauthor.ChangeSource { return func() ([]byte, error) { destinationAddress, err := rpcClient.GetNewAddress(accountName) if err != nil { return nil, err } return txscript.PayToAddrScript(destinationAddress) } }
// SetUp initializes the rpc test state. Initialization includes: starting up a // simnet node, creating a websocket client and connecting to the started node, // and finally: optionally generating and submitting a testchain with a configurable // number of mature coinbase outputs coinbase outputs. func (h *Harness) SetUp(createTestChain bool, numMatureOutputs uint32) error { var err error // Start the dcrd node itself. This spawns a new process which will be // managed if err = h.node.Start(); err != nil { return err } time.Sleep(200 * time.Millisecond) if err = h.connectRPCClient(); err != nil { return err } fmt.Println("Node RPC client connected.") // Start dcrwallet. This spawns a new process which will be managed if err = h.wallet.Start(); err != nil { return err } time.Sleep(1 * time.Second) // Connect walletClient so we can get the mining address var walletClient *rpc.Client walletRPCConf := h.wallet.config.rpcConnConfig() for i := 0; i < 400; i++ { if walletClient, err = rpc.New(&walletRPCConf, nil); err != nil { time.Sleep(time.Duration(math.Log(float64(i+3))) * 50 * time.Millisecond) continue } break } if walletClient == nil { return fmt.Errorf("walletClient connection timedout") } fmt.Println("Wallet RPC client connected.") h.WalletRPC = walletClient // Get a new address from the wallet to be set with dcrd's --miningaddr time.Sleep(5 * time.Second) var miningAddr dcrutil.Address for i := 0; i < 100; i++ { if miningAddr, err = walletClient.GetNewAddress("default"); err != nil { time.Sleep(time.Duration(math.Log(float64(i+3))) * 50 * time.Millisecond) continue } break } if miningAddr == nil { return fmt.Errorf("RPC not up for mining addr %v %v", h.testNodeDir, h.testWalletDir) } h.miningAddr = miningAddr var extraArgs []string miningArg := fmt.Sprintf("--miningaddr=%s", miningAddr) extraArgs = append(extraArgs, miningArg) // Stop node so we can restart it with --miningaddr if err = h.node.Stop(); err != nil { return err } config, err := newConfig(h.node.config.appDataDir, h.node.config.certFile, h.node.config.keyFile, extraArgs) if err != nil { return err } config.listen = h.node.config.listen config.rpcListen = h.node.config.rpcListen // Create the testing node bounded to the simnet. node, err := newNode(config, h.testNodeDir) if err != nil { return err } h.node = node // Restart node with mining address set if err = h.node.Start(); err != nil { return err } time.Sleep(1 * time.Second) if err := h.connectRPCClient(); err != nil { return err } fmt.Printf("Node RPC client connected, miningaddr: %v.\n", miningAddr) // Create a test chain with the desired number of mature coinbase outputs if createTestChain { numToGenerate := uint32(h.ActiveNet.CoinbaseMaturity) + numMatureOutputs fmt.Printf("Generating %v blocks...\n", numToGenerate) _, err := h.Node.Generate(numToGenerate) if err != nil { return err } fmt.Println("Block generation complete.") } // Wait for the wallet to sync up to the current height. // TODO: Figure out why this is the longest wait, about 60 sec, when it // should be almost immediate. fmt.Println("Waiting for wallet to sync to current height.") ticker := time.NewTicker(time.Millisecond * 500) desiredHeight := int64(numMatureOutputs + uint32(h.ActiveNet.CoinbaseMaturity)) out: for range ticker.C { count, err := h.WalletRPC.GetBlockCount() if err != nil { return err } if count == desiredHeight { break out } } ticker.Stop() fmt.Println("Wallet sync complete.") return nil }
// traceDevPremineOuts returns a list of outpoints that are part of the dev // premine coins and are ancestors of the inputs to the passed transaction hash. func traceDevPremineOuts(client *dcrrpcclient.Client, txHash *chainhash.Hash) ([]wire.OutPoint, error) { // Trace the lineage of all inputs to the provided transaction back to // the coinbase outputs that generated them and add those outpoints to // a list. Also, keep track of all of the processed transactions in // order to avoid processing duplicates. knownCoinbases := make(map[chainhash.Hash]struct{}) processedHashes := make(map[chainhash.Hash]struct{}) coinbaseOuts := make([]wire.OutPoint, 0, 10) processOuts := []wire.OutPoint{{Hash: *txHash}} for len(processOuts) > 0 { // Grab the first outpoint to process and skip it if it has // already been traced. outpoint := processOuts[0] processOuts = processOuts[1:] if _, exists := processedHashes[outpoint.Hash]; exists { if _, exists := knownCoinbases[outpoint.Hash]; exists { coinbaseOuts = append(coinbaseOuts, outpoint) } continue } processedHashes[outpoint.Hash] = struct{}{} // Request the transaction for the outpoint from the server. tx, err := client.GetRawTransaction(&outpoint.Hash) if err != nil { return nil, fmt.Errorf("failed to get transaction %v: %v", &outpoint.Hash, err) } // Add the outpoint to the coinbase outputs list when it is part // of a coinbase transaction. Also, keep track of the fact the // transaction is a coinbase to use when avoiding duplicate // checks. if blockchain.IsCoinBase(tx) { knownCoinbases[outpoint.Hash] = struct{}{} coinbaseOuts = append(coinbaseOuts, outpoint) continue } // Add the inputs to the transaction to the list of transactions // to load and continue tracing. // // However, skip the first input to stake generation txns since // they are creating new coins. The remaining inputs to a // stake generation transaction still need to be traced since // they represent the coins that purchased the ticket. txIns := tx.MsgTx().TxIn isSSGen, _ := stake.IsSSGen(tx.MsgTx()) if isSSGen { txIns = txIns[1:] } for _, txIn := range txIns { processOuts = append(processOuts, txIn.PreviousOutPoint) } } // Add any of the outputs that are dev premine outputs to a list. var devPremineOuts []wire.OutPoint for _, coinbaseOut := range coinbaseOuts { if isDevPremineOut(coinbaseOut) { devPremineOuts = append(devPremineOuts, coinbaseOut) } } return devPremineOuts, nil }