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 }