func ActiveHarnesses() []*Harness {
	harnessStateMtx.RLock()
	defer harnessStateMtx.RUnlock()
	activeNodes := make([]*Harness, 0, len(testInstances))
	for _, harness := range testInstances {
		activeNodes = append(activeNodes, harness)
	}
	return activeNodes
}
ActiveHarnesses returns a slice of all currently active test harnesses. A
test harness if considered "active" if it has been created, but not yet torn
down.
package rpctest
import (
	"bytes"
	"fmt"
	"github.com/soteria-dag/soterd/soterutil"
	"github.com/wcharczuk/go-chart"
	"io/ioutil"
	"reflect"
	"time"
	"github.com/soteria-dag/soterd/chaincfg/chainhash"
	"github.com/soteria-dag/soterd/rpcclient"
	"github.com/soteria-dag/soterd/soterjson"
	"github.com/soteria-dag/soterd/wire"
)
type JoinType uint8
const (
	Blocks JoinType = iota
	Mempools
)
func JoinNodes(nodes []*Harness, joinType JoinType) error {
	switch joinType {
	case Blocks:
		return syncBlocks(nodes)
	case Mempools:
		return syncMempools(nodes)
	}
	return nil
}
func absInt32(x int32) int32 {
	if x < 0 {
		return -x
	} else {
		return x
	}
}
func colorPicker (v int) string {
	color := chart.GetAlternateColor(v)
	return fmt.Sprintf("#%x%x%x", []byte{color.R}, []byte{color.G}, []byte{color.B})
}
func stylePicker (v bool) string {
	if (v) {
		return "filled"
	} 	
	return "filled, dashed"
}
func keys(m map[int32]int) []int32 {
	k := make([]int32, 0)
	for v := range m {
		k = append(k, v)
	}
	return k
}
func meanInterval(values []int32) float64 {
	if len(values) < 2 {
		return float64(0)
	}
	diffs := make([]int32, 0)
	for i := 0; i < (len(values) - 1); i++ {
		d := absInt32(values[i + 1] - values[i])
		diffs = append(diffs, d)
	}
	mean := float64(sumInt32(diffs)) / float64(len(diffs))
	return mean
}
func save(bytes []byte, pattern string) (string, error) {
	t, err := ioutil.TempFile("", pattern)
	if err != nil {
		return "", err
	}
	_, err = t.Write(bytes)
	if err != nil {
		return "", err
	}
	err = t.Close()
	if err != nil {
		return "", err
	}
	return t.Name(), nil
}
func sumInt32(x []int32) int32 {
	total := int32(0)
	for _, v := range x {
		total += v
	}
	return total
}
func CountDAGBlocks(node *Harness) (int, float64, error) {
	count := 0
	mpbHeights := make(map[int32]int)
	tips, err := node.Node.GetDAGTips()
	if err != nil {
		return count, meanInterval(keys(mpbHeights)), err
	}
	for height := int32(0); height <= tips.MaxHeight; height++ {
		hashes, err := node.Node.GetBlockHash(int64(height))
		if err != nil {
			return count, meanInterval(keys(mpbHeights)), err
		}
		for _, hash := range hashes {
			block, err := node.Node.GetBlock(hash)
			if err != nil {
				return count, meanInterval(keys(mpbHeights)), err
			}
			if (*block).Parents.Size > 1 {
				count++
				mpbHeights[height] = 1
			}
		}
	}
	return count, meanInterval(keys(mpbHeights)), nil
}
func DumpDAG(i int, node *Harness) error {
	tips, err := node.Node.GetDAGTips()
	if err != nil {
		return err
	}
	for height := int32(0); height <= tips.MaxHeight; height++ {
		hashes, err := node.Node.GetBlockHash(int64(height))
		if err != nil {
			return err
		}
		for _, hash := range hashes {
			block, err := node.Node.GetBlock(hash)
			if err != nil {
				return err
			}
			fmt.Printf("miner %v\theight %v\tblock %v\tparents %v\n", i, height, hash, block.Parents.ParentHashes())
		}
	}
	return nil
}
func RenderDagsDot(nodes []*Harness, rankdir string) ([]byte, error) {
	var dot bytes.Buffer
	smallHashLen := 7
	blockCreator := make(map[string]int)
	for i, n := range nodes {
		resp, err := n.Node.GetBlockMetrics()
		if err != nil {
			continue
		}
		for _, hash := range resp.BlkHashes {
			blockCreator[hash] = i
		}
	}
	node := nodes[0]
	tips, err := node.Node.GetDAGTips()
	if err != nil {
		return dot.Bytes(), err
	}
	dag := make([][]*wire.MsgBlock, 0)
	blockIndex := make(map[string]*wire.MsgBlock)
	for height := int32(0); height <= tips.MaxHeight; height++ {
		blocks := make([]*wire.MsgBlock, 0)
		hashes, err := node.Node.GetBlockHash(int64(height))
		if err != nil {
			return dot.Bytes(), err
		}
		for _, hash := range hashes {
			block, err := node.Node.GetBlock(hash)
			if err != nil {
				return dot.Bytes(), err
			}
			blockIndex[block.BlockHash().String()] = block
			blocks = append(blocks, block)
		}
		dag = append(dag, blocks)
	}
	dagcoloring, err := node.Node.GetDAGColoring()
	if err != nil {
		return dot.Bytes(), err
	}
	blockcoloring := make(map[string]bool)
	for _, dagNode := range dagcoloring {
		hash := dagNode.Hash
		coloring := dagNode.IsBlue
		blockcoloring[hash] = coloring
	}
	graphIndex := make(map[string]int)
	n := 0
	_, err = fmt.Fprintln(&dot, "digraph dag {")
	if err != nil {
		return dot.Bytes(), err
	}
	_, err = fmt.Fprintln(&dot, "ordering=out;")
	if err != nil {
		return dot.Bytes(), err
	}
	_, err = fmt.Fprintf(&dot, "rankdir=\"%s\";", rankdir)
	if err != nil {
		return dot.Bytes(), err
	}
	for height, blocks := range dag {
		for _, block := range blocks {
			hash := block.BlockHash().String()
			smallHashIndex := len(hash) - smallHashLen
			graphIndex[hash] = n
			dagcoloring := blockcoloring[hash]
			style := stylePicker(dagcoloring)
			creator, exists := blockCreator[hash]
			var err error
			if exists {
				color := colorPicker(creator)
				_, err = fmt.Fprintf(&dot, "n%d [label=\"%s\", tooltip=\"node %d height %d hash %s\", fillcolor=\"%s\", style=\"%s\"];\n",
					n, hash[smallHashIndex:], creator, height, hash, color, style)
			} else {
				_, err = fmt.Fprintf(&dot, "n%d [label=\"%s\", tooltip=\"height %d hash %s\", style=\"%s\"];\n",
					n, hash[smallHashIndex:], height, hash, style)
			}
			if err != nil {
				return dot.Bytes(), err
			}
			n++
		}
	}
	for _, blocks := range dag {
		for _, block := range blocks {
			blockN := graphIndex[block.BlockHash().String()]
			for _, parent := range block.Parents.Parents {
				parentN := graphIndex[parent.Hash.String()]
				_, err := fmt.Fprintf(&dot, "n%d -> n%d;\n", blockN, parentN)
				if err != nil {
					return dot.Bytes(), err
				}
			}
		}
	}
	dot.WriteString("}")
	return dot.Bytes(), nil
}
func SaveDagHTML(r *Harness) (string, error) {
	dot, err := r.Node.RenderDag()
	if err != nil {
		return "", err
	}
	svg, err := soterutil.DotToSvg([]byte(dot.Dot))
	if err != nil {
		return "", err
	}
	svgEmbed, err := soterutil.StripSvgXmlDecl(svg)
	if err != nil {
		return "", err
	}
	h, err := soterutil.RenderSvgHTML(svgEmbed, "dag")
	if err != nil {
		return "", err
	}
	return save(h, "dag_*.html")
}
func checkHeaderEqual(headerA, headerB wire.BlockHeader) error {
	if headerA.Version != headerB.Version {
		return fmt.Errorf("block header Version mismatch: %v != %v", headerA.Version, headerB.Version)
	}
	if !headerA.PrevBlock.IsEqual(&headerB.PrevBlock) {
		return fmt.Errorf("block header PrevBlock mismatch: %v != %v", headerA.PrevBlock, headerB.PrevBlock)
	}
	if !headerA.MerkleRoot.IsEqual(&headerB.MerkleRoot) {
		return fmt.Errorf("block header MerkleRoot mismatch: %v != %v", headerA.MerkleRoot, headerB.MerkleRoot)
	}
	if !headerA.Timestamp.Equal(headerB.Timestamp) {
		return fmt.Errorf("block header Timestamp mismatch: %v != %v", headerA.Timestamp, headerB.Timestamp)
	}
	if headerA.Bits != headerB.Bits {
		return fmt.Errorf("block header Bits mismatch: %v != %v", headerA.Bits, headerB.Bits)
	}
	if headerA.Nonce != headerB.Nonce {
		return fmt.Errorf("block header Nonce mismatch: %v != %v", headerA.Bits, headerB.Bits)
	}
	return nil
}
func assertParentIdentical(parentA, parentB *wire.Parent) error {
	if !parentA.Hash.IsEqual(&parentB.Hash) {
		return fmt.Errorf("block parent Hash mismatch: %v != %v", parentA.Hash, parentB.Hash)
	}
	if parentA.Data != parentB.Data {
		return fmt.Errorf("block parent Data mismatch: %v != %v", parentA.Data, parentB.Data)
	}
	return nil
}
func checkParentSubHeaderEqual(parentsA, parentsB wire.ParentSubHeader) error {
	if parentsA.Version != parentsB.Version {
		return fmt.Errorf("block parent sub-header Version mismatch: %v != %v", parentsA.Version, parentsB.Version)
	}
	if parentsA.Size != parentsB.Size {
		return fmt.Errorf("block parent sub-header Size (claimed number of parents) mismatch: %v != %v", parentsA.Size, parentsB.Size)
	}
	if len(parentsA.Parents) != len(parentsB.Parents) {
		return fmt.Errorf("block parent sub-header actual number of parents mismatch: %v != %v", len(parentsA.Parents), len(parentsB.Parents))
	}
	parentIndexA := make(map[string]int)
	parentIndexB := make(map[string]int)
	for i, hash := range parentsA.ParentHashes() {
		parentIndexA[hash.String()] = i
	}
	for i, hash := range parentsB.ParentHashes() {
		parentIndexB[hash.String()] = i
	}
	for hash, indexA := range parentIndexA {
		indexB, ok := parentIndexB[hash]
		if !ok {
			return fmt.Errorf("block parent sub-header B missing A parent: %v", hash)
		}
		err := assertParentIdentical(parentsA.Parents[indexA], parentsB.Parents[indexB])
		if err != nil {
			return err
		}
	}
	for hash, _ := range parentIndexB {
		_, ok := parentIndexA[hash]
		if !ok {
			return fmt.Errorf("block parent sub-header A missing B parent: %v", hash)
		}
	}
	return nil
}
func checkTransactionsEqual(transA, transB []*wire.MsgTx) error {
	if len(transA) != len(transB) {
		return fmt.Errorf("block transactions count mismatch: %v != %v", len(transA), len(transB))
	}
	txIndexA := make(map[string]int)
	txIndexB := make(map[string]int)
	for i, tx := range transA {
		txHash := tx.TxHash().String()
		txIndexA[txHash] = i
	}
	for i, tx := range transB {
		txHash := tx.TxHash().String()
		txIndexB[txHash] = i
	}
	for hash, _ := range txIndexA {
		_, ok := txIndexB[hash]
		if !ok {
			return fmt.Errorf("block transaction from A missing from or different in B: %v", hash)
		}
	}
	for hash, _ := range txIndexB {
		_, ok := txIndexA[hash]
		if !ok {
			return fmt.Errorf("block transaction from B missing from or different in A: %v", hash)
		}
	}
	return nil
}
func syncMempools(nodes []*Harness) error {
	poolsMatch := false
retry:
	for !poolsMatch {
		firstPool, err := nodes[0].Node.GetRawMempool()
		if err != nil {
			return err
		}
		for _, node := range nodes[1:] {
			nodePool, err := node.Node.GetRawMempool()
			if err != nil {
				return err
			}
			if !reflect.DeepEqual(firstPool, nodePool) {
				time.Sleep(time.Millisecond * 100)
				continue retry
			}
		}
		poolsMatch = true
	}
	return nil
}
func syncBlocks(nodes []*Harness) error {
	blocksMatch := false
retry:
	for !blocksMatch {
		var prevHash *chainhash.Hash
		var prevHeight int32
		for _, node := range nodes {
			blockHash, blockHeight, err := node.Node.GetBestBlock()
			if err != nil {
				return err
			}
			if prevHash != nil && (*blockHash != *prevHash ||
				blockHeight != prevHeight) {
				time.Sleep(time.Millisecond * 100)
				continue retry
			}
			prevHash, prevHeight = blockHash, blockHeight
		}
		blocksMatch = true
	}
	return nil
}
func CheckBlocksEqual(blockA, blockB *wire.MsgBlock) error {
	err := checkHeaderEqual(blockA.Header, blockB.Header)
	if err != nil {
		return err
	}
	err = checkParentSubHeaderEqual(blockA.Parents, blockB.Parents)
	if err != nil {
		return err
	}
	err = checkTransactionsEqual(blockA.Transactions, blockB.Transactions)
	if err != nil {
		return err
	}
	return nil
}
func CompareDAG(nodes []*Harness) error {
	targetNodeIndex := 0
	targetNode := nodes[targetNodeIndex]
	tips, err := targetNode.Node.GetDAGTips()
	if err != nil {
		return err
	}
	dag := [][]*chainhash.Hash{}
	for height := int32(0); height <= tips.MaxHeight; height++ {
		hashes, err := targetNode.Node.GetBlockHash(int64(height))
		if err != nil {
			return err
		}
		dag = append(dag, hashes)
	}
	for i, node := range nodes {
		if i == targetNodeIndex {
			continue
		}
		for height, hashes := range dag {
			otherHashes, err := node.Node.GetBlockHash(int64(height))
			if err != nil {
				return err
			}
			if len(otherHashes) != len(hashes) {
				err := fmt.Errorf("node %v has different number of hashes at height %v than node %v: %v != %v",
					i, height, targetNodeIndex, len(otherHashes), len(hashes))
				return err
			}
			for _, hash := range hashes {
				match := false
				for _, otherHash := range otherHashes {
					if hash.IsEqual(otherHash) {
						match = true
						break
					}
				}
				if !match {
					err := fmt.Errorf("node %v has missing hash %v at height %v", i, hash, height)
					return err
				}
			}
			for _, otherHash := range otherHashes {
				match := false
				for _, hash := range hashes {
					if otherHash.IsEqual(hash) {
						match = true
						break
					}
				}
				if !match {
					err := fmt.Errorf("node %v has extra hash %v at height %v", i, otherHash, height)
					return err
				}
			}
			for _, hash := range hashes {
				block, err := targetNode.Node.GetBlock(hash)
				if err != nil {
					err := fmt.Errorf("failed to get block %v from target node %v", hash, targetNodeIndex)
					return err
				}
				otherBlock, err := nodes[i].Node.GetBlock(hash)
				if err != nil {
					err := fmt.Errorf("failed to get block %v from node %v", hash, i)
					return err
				}
				err = CheckBlocksEqual(block, otherBlock)
				if err != nil {
					return err
				}
			}
		}
	}
	return nil
}
func IsConnected(from *Harness, to *Harness) (bool, error) {
	toAddr := to.P2PAddress()
	fromPeers, err := from.Node.GetPeerInfo()
	if err != nil {
		return false, err
	}
	for _, peerInfo := range fromPeers {
		if peerInfo.Addr == toAddr {
			return true, nil
		}
	}
	return false, nil
}
func ConnectNode(from *Harness, to *Harness) error {
	peerInfo, err := from.Node.GetPeerInfo()
	if err != nil {
		return err
	}
	numPeers := len(peerInfo)
	targetAddr := to.P2PAddress()
	if err := from.Node.AddNode(targetAddr, rpcclient.ANAdd); err != nil {
		return err
	}
	peerInfo, err = from.Node.GetPeerInfo()
	if err != nil {
		return err
	}
	for len(peerInfo) <= numPeers {
		peerInfo, err = from.Node.GetPeerInfo()
		if err != nil {
			return err
		}
	}
	return nil
}
func ConnectNodes(nodes []*Harness) error {
	for i, node := range nodes {
		for j, peer := range nodes {
			if i == j {
				continue
			}
			connected, err := IsConnected(peer, node)
			if err != nil {
				return err
			}
			if connected {
				continue
			}
			connected, err = IsConnected(node, peer)
			if err != nil {
				return err
			}
			if connected {
				continue
			}
			err = ConnectNode(peer, node)
			if err != nil {
				return err
			}
		}
	}
	return nil
}
func TearDownAll() error {
	harnessStateMtx.Lock()
	defer harnessStateMtx.Unlock()
	for _, harness := range testInstances {
		if err := harness.tearDown(); err != nil {
			return err
		}
	}
	return nil
}
func WaitForBlock(h *Harness, hash *chainhash.Hash, wait time.Duration) (*wire.MsgBlock, error) {
	pollInterval := time.Duration(time.Millisecond * 500)
	waitThreshold := time.Now().Add(wait)
	for {
		block, err := h.Node.GetBlock(hash)
		switch errType := err.(type) {
		case nil:
			return block, err
		case *soterjson.RPCError:
			if errType.Code == soterjson.ErrRPCBlockNotFound {
				if time.Now().Before(waitThreshold) {
					time.Sleep(pollInterval)
				} else {
					return block, err
				}
			} else {
				return block, err
			}
		default:
			return block, err
		}
	}
}
func WaitForBlocks(nodes []*Harness, hashes []*chainhash.Hash, wait time.Duration) error {
	pollInterval := time.Duration(time.Millisecond * 500)
	waitThreshold := time.Now().Add(wait)
	for {
		matchingNodes := 0
		for _, node := range nodes {
			matching := 0
			for _, hash := range hashes {
				_, err := WaitForBlock(node, hash, wait)
				if err != nil {
					continue
				}
				matching++
			}
			if matching == len(hashes) {
				matchingNodes++
			}
		}
		if matchingNodes == len(nodes) {
			return nil
		} else if time.Now().Before(waitThreshold){
			time.Sleep(pollInterval)
		} else {
			var hashStrings []string
			for _, hash := range hashes {
				hashStr := (*hash).String()
				hashStrings = append(hashStrings, hashStr)
			}
			err := fmt.Errorf("Timeout while waiting for nodes to sync blocks %v", hashStrings)
			return err
		}
	}
}
func WaitForDAG(nodes []*Harness, wait time.Duration) error {
	pollInterval := time.Duration(time.Second)
	waitThreshold := time.Now().Add(wait)
	if len(nodes) < 2 {
		return nil
	}
	for {
		err := CompareDAG(nodes)
		if err != nil {
			if time.Now().Before(waitThreshold) {
				time.Sleep(pollInterval)
			} else {
				timeout := fmt.Errorf("Timeout while waiting for nodes to sync dag")
				return timeout
			}
		} else {
			return nil
		}
	}
}