github.com/btcsuite/btcd@v0.24.0/integration/rpctest/utils.go (about)

     1  // Copyright (c) 2016 The btcsuite developers
     2  // Use of this source code is governed by an ISC
     3  // license that can be found in the LICENSE file.
     4  
     5  package rpctest
     6  
     7  import (
     8  	"reflect"
     9  	"time"
    10  
    11  	"github.com/btcsuite/btcd/chaincfg/chainhash"
    12  	"github.com/btcsuite/btcd/rpcclient"
    13  )
    14  
    15  // JoinType is an enum representing a particular type of "node join". A node
    16  // join is a synchronization tool used to wait until a subset of nodes have a
    17  // consistent state with respect to an attribute.
    18  type JoinType uint8
    19  
    20  const (
    21  	// Blocks is a JoinType which waits until all nodes share the same
    22  	// block height.
    23  	Blocks JoinType = iota
    24  
    25  	// Mempools is a JoinType which blocks until all nodes have identical
    26  	// mempool.
    27  	Mempools
    28  )
    29  
    30  // JoinNodes is a synchronization tool used to block until all passed nodes are
    31  // fully synced with respect to an attribute. This function will block for a
    32  // period of time, finally returning once all nodes are synced according to the
    33  // passed JoinType. This function be used to to ensure all active test
    34  // harnesses are at a consistent state before proceeding to an assertion or
    35  // check within rpc tests.
    36  func JoinNodes(nodes []*Harness, joinType JoinType) error {
    37  	switch joinType {
    38  	case Blocks:
    39  		return syncBlocks(nodes)
    40  	case Mempools:
    41  		return syncMempools(nodes)
    42  	}
    43  	return nil
    44  }
    45  
    46  // syncMempools blocks until all nodes have identical mempools.
    47  func syncMempools(nodes []*Harness) error {
    48  	poolsMatch := false
    49  
    50  retry:
    51  	for !poolsMatch {
    52  		firstPool, err := nodes[0].Client.GetRawMempool()
    53  		if err != nil {
    54  			return err
    55  		}
    56  
    57  		// If all nodes have an identical mempool with respect to the
    58  		// first node, then we're done. Otherwise, drop back to the top
    59  		// of the loop and retry after a short wait period.
    60  		for _, node := range nodes[1:] {
    61  			nodePool, err := node.Client.GetRawMempool()
    62  			if err != nil {
    63  				return err
    64  			}
    65  
    66  			if !reflect.DeepEqual(firstPool, nodePool) {
    67  				time.Sleep(time.Millisecond * 100)
    68  				continue retry
    69  			}
    70  		}
    71  
    72  		poolsMatch = true
    73  	}
    74  
    75  	return nil
    76  }
    77  
    78  // syncBlocks blocks until all nodes report the same best chain.
    79  func syncBlocks(nodes []*Harness) error {
    80  	blocksMatch := false
    81  
    82  retry:
    83  	for !blocksMatch {
    84  		var prevHash *chainhash.Hash
    85  		var prevHeight int32
    86  		for _, node := range nodes {
    87  			blockHash, blockHeight, err := node.Client.GetBestBlock()
    88  			if err != nil {
    89  				return err
    90  			}
    91  			if prevHash != nil && (*blockHash != *prevHash ||
    92  				blockHeight != prevHeight) {
    93  
    94  				time.Sleep(time.Millisecond * 100)
    95  				continue retry
    96  			}
    97  			prevHash, prevHeight = blockHash, blockHeight
    98  		}
    99  
   100  		blocksMatch = true
   101  	}
   102  
   103  	return nil
   104  }
   105  
   106  // ConnectNode establishes a new peer-to-peer connection between the "from"
   107  // harness and the "to" harness.  The connection made is flagged as persistent,
   108  // therefore in the case of disconnects, "from" will attempt to reestablish a
   109  // connection to the "to" harness.
   110  func ConnectNode(from *Harness, to *Harness) error {
   111  	peerInfo, err := from.Client.GetPeerInfo()
   112  	if err != nil {
   113  		return err
   114  	}
   115  	numPeers := len(peerInfo)
   116  
   117  	targetAddr := to.node.config.listen
   118  	if err := from.Client.AddNode(targetAddr, rpcclient.ANAdd); err != nil {
   119  		return err
   120  	}
   121  
   122  	// Block until a new connection has been established.
   123  	peerInfo, err = from.Client.GetPeerInfo()
   124  	if err != nil {
   125  		return err
   126  	}
   127  	for len(peerInfo) <= numPeers {
   128  		peerInfo, err = from.Client.GetPeerInfo()
   129  		if err != nil {
   130  			return err
   131  		}
   132  	}
   133  
   134  	return nil
   135  }
   136  
   137  // TearDownAll tears down all active test harnesses.
   138  func TearDownAll() error {
   139  	harnessStateMtx.Lock()
   140  	defer harnessStateMtx.Unlock()
   141  
   142  	for _, harness := range testInstances {
   143  		if err := harness.tearDown(); err != nil {
   144  			return err
   145  		}
   146  	}
   147  
   148  	return nil
   149  }
   150  
   151  // ActiveHarnesses returns a slice of all currently active test harnesses. A
   152  // test harness if considered "active" if it has been created, but not yet torn
   153  // down.
   154  func ActiveHarnesses() []*Harness {
   155  	harnessStateMtx.RLock()
   156  	defer harnessStateMtx.RUnlock()
   157  
   158  	activeNodes := make([]*Harness, 0, len(testInstances))
   159  	for _, harness := range testInstances {
   160  		activeNodes = append(activeNodes, harness)
   161  	}
   162  
   163  	return activeNodes
   164  }