github.com/ari-anchor/sei-tendermint@v0.0.0-20230519144642-dc826b7b56bb/test/e2e/runner/rpc.go (about) 1 package main 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 "time" 8 9 "github.com/ari-anchor/sei-tendermint/libs/log" 10 rpchttp "github.com/ari-anchor/sei-tendermint/rpc/client/http" 11 rpctypes "github.com/ari-anchor/sei-tendermint/rpc/coretypes" 12 e2e "github.com/ari-anchor/sei-tendermint/test/e2e/pkg" 13 "github.com/ari-anchor/sei-tendermint/types" 14 ) 15 16 // waitForHeight waits for the network to reach a certain height (or above), 17 // returning the block at the height seen. Errors if the network is not making 18 // progress at all. 19 // If height == 0, the initial height of the test network is used as the target. 20 func waitForHeight(ctx context.Context, testnet *e2e.Testnet, height int64) (*types.Block, *types.BlockID, error) { 21 var ( 22 err error 23 clients = map[string]*rpchttp.HTTP{} 24 lastHeight int64 25 lastIncrease = time.Now() 26 nodesAtHeight = map[string]struct{}{} 27 numRunningNodes int 28 ) 29 if height == 0 { 30 height = testnet.InitialHeight 31 } 32 33 for _, node := range testnet.Nodes { 34 if node.Stateless() { 35 continue 36 } 37 38 if node.HasStarted { 39 numRunningNodes++ 40 } 41 } 42 43 timer := time.NewTimer(0) 44 defer timer.Stop() 45 for { 46 select { 47 case <-ctx.Done(): 48 return nil, nil, ctx.Err() 49 case <-timer.C: 50 for _, node := range testnet.Nodes { 51 // skip nodes that have reached the target height 52 if _, ok := nodesAtHeight[node.Name]; ok { 53 continue 54 } 55 56 // skip nodes that don't have state or haven't started yet 57 if node.Stateless() { 58 continue 59 } 60 if !node.HasStarted { 61 continue 62 } 63 64 // cache the clients 65 client, ok := clients[node.Name] 66 if !ok { 67 client, err = node.Client() 68 if err != nil { 69 continue 70 } 71 clients[node.Name] = client 72 } 73 74 result, err := client.Status(ctx) 75 if err != nil { 76 continue 77 } 78 if result.SyncInfo.LatestBlockHeight > lastHeight { 79 lastHeight = result.SyncInfo.LatestBlockHeight 80 lastIncrease = time.Now() 81 } 82 83 if result.SyncInfo.LatestBlockHeight >= height { 84 // the node has achieved the target height! 85 86 // add this node to the set of target 87 // height nodes 88 nodesAtHeight[node.Name] = struct{}{} 89 90 // if not all of the nodes that we 91 // have clients for have reached the 92 // target height, keep trying. 93 if numRunningNodes > len(nodesAtHeight) { 94 continue 95 } 96 97 // All nodes are at or above the target height. Now fetch the block for that target height 98 // and return it. We loop again through all clients because some may have pruning set but 99 // at least two of them should be archive nodes. 100 for _, c := range clients { 101 result, err := c.Block(ctx, &height) 102 if err != nil || result == nil || result.Block == nil { 103 continue 104 } 105 return result.Block, &result.BlockID, err 106 } 107 } 108 } 109 110 if len(clients) == 0 { 111 return nil, nil, errors.New("unable to connect to any network nodes") 112 } 113 if time.Since(lastIncrease) >= time.Minute { 114 if lastHeight == 0 { 115 return nil, nil, errors.New("chain stalled at unknown height (most likely upon starting)") 116 } 117 118 return nil, nil, fmt.Errorf("chain stalled at height %v [%d of %d nodes %+v]", 119 lastHeight, 120 len(nodesAtHeight), 121 numRunningNodes, 122 nodesAtHeight) 123 124 } 125 timer.Reset(1 * time.Second) 126 } 127 } 128 } 129 130 // waitForNode waits for a node to become available and catch up to the given block height. 131 func waitForNode(ctx context.Context, logger log.Logger, node *e2e.Node, height int64) (*rpctypes.ResultStatus, error) { 132 // If the node is the light client or seed note, we do not check for the last height. 133 // The light client and seed note can be behind the full node and validator 134 if node.Mode == e2e.ModeSeed { 135 return nil, nil 136 } 137 client, err := node.Client() 138 if err != nil { 139 return nil, err 140 } 141 142 timer := time.NewTimer(0) 143 defer timer.Stop() 144 145 var ( 146 lastFailed bool 147 counter int 148 ) 149 for { 150 counter++ 151 if lastFailed { 152 lastFailed = false 153 154 // if there was a problem with the request in 155 // the previous recreate the client to ensure 156 // reconnection 157 client, err = node.Client() 158 if err != nil { 159 return nil, err 160 } 161 } 162 163 select { 164 case <-ctx.Done(): 165 return nil, ctx.Err() 166 case <-timer.C: 167 status, err := client.Status(ctx) 168 switch { 169 case errors.Is(err, context.DeadlineExceeded): 170 return nil, fmt.Errorf("timed out waiting for %v to reach height %v", node.Name, height) 171 case errors.Is(err, context.Canceled): 172 return nil, err 173 // If the node is the light client, it is not essential to wait for it to catch up, but we must return status info 174 case err == nil && node.Mode == e2e.ModeLight: 175 return status, nil 176 case err == nil && node.Mode != e2e.ModeLight && status.SyncInfo.LatestBlockHeight >= height: 177 return status, nil 178 case counter%500 == 0: 179 switch { 180 case err != nil: 181 lastFailed = true 182 logger.Error("node not yet ready", 183 "iter", counter, 184 "node", node.Name, 185 "target", height, 186 "err", err, 187 ) 188 case status != nil: 189 logger.Info("node not yet ready", 190 "iter", counter, 191 "node", node.Name, 192 "height", status.SyncInfo.LatestBlockHeight, 193 "target", height, 194 ) 195 } 196 } 197 timer.Reset(250 * time.Millisecond) 198 } 199 } 200 } 201 202 // getLatestBlock returns the last block that all active nodes in the network have 203 // agreed upon i.e. the earlist of each nodes latest block 204 func getLatestBlock(ctx context.Context, testnet *e2e.Testnet) (*types.Block, error) { 205 var earliestBlock *types.Block 206 for _, node := range testnet.Nodes { 207 // skip nodes that don't have state or haven't started yet 208 if node.Stateless() { 209 continue 210 } 211 if !node.HasStarted { 212 continue 213 } 214 215 client, err := node.Client() 216 if err != nil { 217 return nil, err 218 } 219 220 wctx, cancel := context.WithTimeout(ctx, 10*time.Second) 221 defer cancel() 222 result, err := client.Block(wctx, nil) 223 if err != nil { 224 return nil, err 225 } 226 227 if result.Block != nil && (earliestBlock == nil || earliestBlock.Height > result.Block.Height) { 228 earliestBlock = result.Block 229 } 230 } 231 return earliestBlock, nil 232 }