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
}
connectRPCClient attempts to establish an RPC connection to the created
hcashd 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.
package rpctest
import (
	"fmt"
	"io/ioutil"
	"math"
	"net"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"sync"
	"time"
	"github.com/HcashOrg/hcashd/chaincfg"
	"github.com/HcashOrg/hcashd/chaincfg/chainhash"
	rpc "github.com/HcashOrg/hcashrpcclient"
	"github.com/HcashOrg/hcashutil"
)
var (
	numTestInstances = 0
	defaultP2pPort = 18555
	defaultRPCPort = 19556
	defaultWalletRPCPort = 19557
	testInstances map[string]*Harness
	harnessStateMtx sync.RWMutex
)
type Harness struct {
	ActiveNet *chaincfg.Params
	Node      *rpc.Client
	WalletRPC *rpc.Client
	node      *node
	handlers  *rpc.NotificationHandlers
	wallet *walletTest
	testNodeDir    string
	testWalletDir  string
	maxConnRetries int
	nodeNum        int
	miningAddr     hcashutil.Address
}
func NewHarness(activeNet *chaincfg.Params, handlers *rpc.NotificationHandlers,
	extraArgs []string) (*Harness, error) {
	harnessStateMtx.Lock()
	defer harnessStateMtx.Unlock()
	harnessID := strconv.Itoa(numTestInstances)
	testDataPath := "rpctest-" + harnessID
	testData, err := ioutil.TempDir("", testDataPath)
	if err != nil {
		return nil, err
	}
	nodeTestData, err := ioutil.TempDir(testData, "node")
	if err != nil {
		return nil, err
	}
	walletTestData, err := ioutil.TempDir(testData, "wallet")
	if err != nil {
		return nil, err
	}
	certFile := filepath.Join(nodeTestData, "rpc.cert")
	keyFile := filepath.Join(nodeTestData, "rpc.key")
	certFileWallet := filepath.Join(walletTestData, "rpc.cert")
	keyFileWallet := filepath.Join(walletTestData, "rpc.key")
	if err = genCertPair(certFile, keyFile, certFileWallet, keyFileWallet); err != nil {
		return nil, err
	}
	p2p, rpcPort, walletRPC := generateListeningAddresses()
	config, err := newConfig(nodeTestData, certFile, keyFile, extraArgs)
	if err != nil {
		return nil, err
	}
	config.listen = p2p
	config.rpcListen = rpcPort
	node, err := newNode(config, nodeTestData)
	if err != nil {
		return nil, err
	}
	walletConfig, err := newWalletConfig(walletTestData, certFile, certFileWallet, keyFileWallet, nil)
	if err != nil {
		return nil, err
	}
	walletConfig.rpcConnect = rpcPort
	walletConfig.rpcListen = walletRPC
	walletTest, err := newWallet(walletConfig, walletTestData)
	if err != nil {
		return nil, err
	}
	nodeNum := numTestInstances
	numTestInstances++
	h := &Harness{
		handlers:       handlers,
		node:           node,
		wallet:         walletTest,
		maxConnRetries: 20,
		testNodeDir:    nodeTestData,
		testWalletDir:  walletTestData,
		ActiveNet:      activeNet,
		nodeNum:        nodeNum,
	}
	testInstances[h.testNodeDir] = h
	return h, nil
}
func (h *Harness) SetUp(createTestChain bool, numMatureOutputs uint32) error {
	var err error
	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.")
	if err = h.wallet.Start(); err != nil {
		return err
	}
	time.Sleep(1 * time.Second)
	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
	time.Sleep(5 * time.Second)
	var miningAddr hcashutil.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)
	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
	node, err := newNode(config, h.testNodeDir)
	if err != nil {
		return err
	}
	h.node = node
	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)
	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.")
	}
	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
}
func (h *Harness) TearDown() error {
	if h == nil {
		return nil
	}
	if h.Node != nil {
		h.Node.Shutdown()
	}
	if err := h.node.Stop(); err != nil {
		return err
	}
	if err := h.node.Cleanup(); err != nil {
		return err
	}
	if err := h.wallet.Stop(); err != nil {
		return err
	}
	if err := h.wallet.Cleanup(); err != nil {
		return err
	}
	if err := os.RemoveAll(h.testNodeDir); err != nil {
		return err
	}
	if err := os.RemoveAll(h.testWalletDir); err != nil {
		return err
	}
	delete(testInstances, h.testNodeDir)
	return nil
}
func (h *Harness) IsUp() bool {
	if h == nil {
		return false
	}
	_, up := testInstances[h.testNodeDir]
	return up
}