|
@ -15,7 +15,7 @@ import ( |
|
|
// waitForHeight waits for the network to reach a certain height (or above),
|
|
|
// waitForHeight waits for the network to reach a certain height (or above),
|
|
|
// returning the highest height seen. Errors if the network is not making
|
|
|
// returning the highest height seen. Errors if the network is not making
|
|
|
// progress at all.
|
|
|
// progress at all.
|
|
|
func waitForHeight(testnet *e2e.Testnet, height int64) (*types.Block, *types.BlockID, error) { |
|
|
|
|
|
|
|
|
func waitForHeight(ctx context.Context, testnet *e2e.Testnet, height int64) (*types.Block, *types.BlockID, error) { |
|
|
var ( |
|
|
var ( |
|
|
err error |
|
|
err error |
|
|
maxResult *rpctypes.ResultBlock |
|
|
maxResult *rpctypes.ResultBlock |
|
@ -25,11 +25,7 @@ func waitForHeight(testnet *e2e.Testnet, height int64) (*types.Block, *types.Blo |
|
|
numRunningNodes int |
|
|
numRunningNodes int |
|
|
) |
|
|
) |
|
|
for _, node := range testnet.Nodes { |
|
|
for _, node := range testnet.Nodes { |
|
|
if node.Mode == e2e.ModeSeed { |
|
|
|
|
|
continue |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if node.Mode == e2e.ModeLight { |
|
|
|
|
|
|
|
|
if node.Stateless() { |
|
|
continue |
|
|
continue |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
@ -38,86 +34,90 @@ func waitForHeight(testnet *e2e.Testnet, height int64) (*types.Block, *types.Blo |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
timer := time.NewTimer(0) |
|
|
|
|
|
defer timer.Stop() |
|
|
for { |
|
|
for { |
|
|
for _, node := range testnet.Nodes { |
|
|
|
|
|
// skip nodes that have reached the target height
|
|
|
|
|
|
if _, ok := nodesAtHeight[node.Name]; ok { |
|
|
|
|
|
continue |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
select { |
|
|
|
|
|
case <-ctx.Done(): |
|
|
|
|
|
return nil, nil, ctx.Err() |
|
|
|
|
|
case <-timer.C: |
|
|
|
|
|
for _, node := range testnet.Nodes { |
|
|
|
|
|
// skip nodes that have reached the target height
|
|
|
|
|
|
if _, ok := nodesAtHeight[node.Name]; ok { |
|
|
|
|
|
continue |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
if node.Mode == e2e.ModeSeed { |
|
|
|
|
|
continue |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
if node.Stateless() { |
|
|
|
|
|
continue |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
if node.Mode == e2e.ModeLight { |
|
|
|
|
|
continue |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
if !node.HasStarted { |
|
|
|
|
|
continue |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
if !node.HasStarted { |
|
|
|
|
|
continue |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
// cache the clients
|
|
|
|
|
|
client, ok := clients[node.Name] |
|
|
|
|
|
if !ok { |
|
|
|
|
|
client, err = node.Client() |
|
|
|
|
|
if err != nil { |
|
|
|
|
|
continue |
|
|
|
|
|
} |
|
|
|
|
|
clients[node.Name] = client |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
// cache the clients
|
|
|
|
|
|
client, ok := clients[node.Name] |
|
|
|
|
|
if !ok { |
|
|
|
|
|
client, err = node.Client() |
|
|
|
|
|
|
|
|
wctx, cancel := context.WithTimeout(ctx, 10*time.Second) |
|
|
|
|
|
defer cancel() |
|
|
|
|
|
result, err := client.Block(wctx, nil) |
|
|
if err != nil { |
|
|
if err != nil { |
|
|
continue |
|
|
continue |
|
|
} |
|
|
} |
|
|
clients[node.Name] = client |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
if result.Block != nil && (maxResult == nil || result.Block.Height > maxResult.Block.Height) { |
|
|
|
|
|
maxResult = result |
|
|
|
|
|
lastIncrease = time.Now() |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) |
|
|
|
|
|
defer cancel() |
|
|
|
|
|
result, err := client.Block(ctx, nil) |
|
|
|
|
|
if err != nil { |
|
|
|
|
|
continue |
|
|
|
|
|
} |
|
|
|
|
|
if result.Block != nil && (maxResult == nil || result.Block.Height > maxResult.Block.Height) { |
|
|
|
|
|
maxResult = result |
|
|
|
|
|
lastIncrease = time.Now() |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
if maxResult != nil && maxResult.Block.Height >= height { |
|
|
|
|
|
// the node has achieved the target height!
|
|
|
|
|
|
|
|
|
if maxResult != nil && maxResult.Block.Height >= height { |
|
|
|
|
|
// the node has achieved the target height!
|
|
|
|
|
|
|
|
|
// add this node to the set of target
|
|
|
|
|
|
// height nodes
|
|
|
|
|
|
nodesAtHeight[node.Name] = struct{}{} |
|
|
|
|
|
|
|
|
// add this node to the set of target
|
|
|
|
|
|
// height nodes
|
|
|
|
|
|
nodesAtHeight[node.Name] = struct{}{} |
|
|
|
|
|
|
|
|
// if not all of the nodes that we
|
|
|
|
|
|
// have clients for have reached the
|
|
|
|
|
|
// target height, keep trying.
|
|
|
|
|
|
if numRunningNodes > len(nodesAtHeight) { |
|
|
|
|
|
continue |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
// if not all of the nodes that we
|
|
|
|
|
|
// have clients for have reached the
|
|
|
|
|
|
// target height, keep trying.
|
|
|
|
|
|
if numRunningNodes > len(nodesAtHeight) { |
|
|
|
|
|
continue |
|
|
|
|
|
|
|
|
// return once all nodes have reached
|
|
|
|
|
|
// the target height.
|
|
|
|
|
|
return maxResult.Block, &maxResult.BlockID, nil |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// return once all nodes have reached
|
|
|
|
|
|
// the target height.
|
|
|
|
|
|
return maxResult.Block, &maxResult.BlockID, nil |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if len(clients) == 0 { |
|
|
|
|
|
return nil, nil, errors.New("unable to connect to any network nodes") |
|
|
|
|
|
} |
|
|
|
|
|
if time.Since(lastIncrease) >= time.Minute { |
|
|
|
|
|
if maxResult == nil { |
|
|
|
|
|
return nil, nil, errors.New("chain stalled at unknown height") |
|
|
|
|
|
|
|
|
if len(clients) == 0 { |
|
|
|
|
|
return nil, nil, errors.New("unable to connect to any network nodes") |
|
|
} |
|
|
} |
|
|
|
|
|
if time.Since(lastIncrease) >= time.Minute { |
|
|
|
|
|
if maxResult == nil { |
|
|
|
|
|
return nil, nil, errors.New("chain stalled at unknown height") |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
return nil, nil, fmt.Errorf("chain stalled at height %v [%d of %d nodes]", |
|
|
|
|
|
maxResult.Block.Height, |
|
|
|
|
|
len(nodesAtHeight), |
|
|
|
|
|
numRunningNodes) |
|
|
|
|
|
|
|
|
return nil, nil, fmt.Errorf("chain stalled at height %v [%d of %d nodes %+v]", |
|
|
|
|
|
maxResult.Block.Height, |
|
|
|
|
|
len(nodesAtHeight), |
|
|
|
|
|
numRunningNodes, |
|
|
|
|
|
nodesAtHeight) |
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
timer.Reset(1 * time.Second) |
|
|
} |
|
|
} |
|
|
time.Sleep(1 * time.Second) |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// waitForNode waits for a node to become available and catch up to the given block height.
|
|
|
// waitForNode waits for a node to become available and catch up to the given block height.
|
|
|
func waitForNode(node *e2e.Node, height int64, timeout time.Duration) (*rpctypes.ResultStatus, error) { |
|
|
|
|
|
|
|
|
func waitForNode(ctx context.Context, node *e2e.Node, height int64) (*rpctypes.ResultStatus, error) { |
|
|
if node.Mode == e2e.ModeSeed { |
|
|
if node.Mode == e2e.ModeSeed { |
|
|
return nil, nil |
|
|
return nil, nil |
|
|
} |
|
|
} |
|
@ -126,42 +126,59 @@ func waitForNode(node *e2e.Node, height int64, timeout time.Duration) (*rpctypes |
|
|
return nil, err |
|
|
return nil, err |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), timeout) |
|
|
|
|
|
defer cancel() |
|
|
|
|
|
|
|
|
timer := time.NewTimer(0) |
|
|
|
|
|
defer timer.Stop() |
|
|
|
|
|
|
|
|
|
|
|
var ( |
|
|
|
|
|
lastFailed bool |
|
|
|
|
|
counter int |
|
|
|
|
|
) |
|
|
for { |
|
|
for { |
|
|
status, err := client.Status(ctx) |
|
|
|
|
|
switch { |
|
|
|
|
|
case errors.Is(err, context.DeadlineExceeded): |
|
|
|
|
|
return nil, fmt.Errorf("timed out waiting for %v to reach height %v", node.Name, height) |
|
|
|
|
|
case errors.Is(err, context.Canceled): |
|
|
|
|
|
return nil, err |
|
|
|
|
|
case err == nil && status.SyncInfo.LatestBlockHeight >= height: |
|
|
|
|
|
return status, nil |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
time.Sleep(300 * time.Millisecond) |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// waitForAllNodes waits for all nodes to become available and catch up to the given block height.
|
|
|
|
|
|
func waitForAllNodes(testnet *e2e.Testnet, height int64, timeout time.Duration) (int64, error) { |
|
|
|
|
|
var lastHeight int64 |
|
|
|
|
|
|
|
|
|
|
|
for _, node := range testnet.Nodes { |
|
|
|
|
|
if node.Mode == e2e.ModeSeed { |
|
|
|
|
|
continue |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
status, err := waitForNode(node, height, timeout) |
|
|
|
|
|
if err != nil { |
|
|
|
|
|
return 0, err |
|
|
|
|
|
|
|
|
counter++ |
|
|
|
|
|
if lastFailed { |
|
|
|
|
|
lastFailed = false |
|
|
|
|
|
|
|
|
|
|
|
// if there was a problem with the request in
|
|
|
|
|
|
// the previous recreate the client to ensure
|
|
|
|
|
|
// reconnection
|
|
|
|
|
|
client, err = node.Client() |
|
|
|
|
|
if err != nil { |
|
|
|
|
|
return nil, err |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if status.SyncInfo.LatestBlockHeight > lastHeight { |
|
|
|
|
|
lastHeight = status.SyncInfo.LatestBlockHeight |
|
|
|
|
|
|
|
|
select { |
|
|
|
|
|
case <-ctx.Done(): |
|
|
|
|
|
return nil, ctx.Err() |
|
|
|
|
|
case <-timer.C: |
|
|
|
|
|
status, err := client.Status(ctx) |
|
|
|
|
|
switch { |
|
|
|
|
|
case errors.Is(err, context.DeadlineExceeded): |
|
|
|
|
|
return nil, fmt.Errorf("timed out waiting for %v to reach height %v", node.Name, height) |
|
|
|
|
|
case errors.Is(err, context.Canceled): |
|
|
|
|
|
return nil, err |
|
|
|
|
|
case err == nil && status.SyncInfo.LatestBlockHeight >= height: |
|
|
|
|
|
return status, nil |
|
|
|
|
|
case counter%50 == 0: |
|
|
|
|
|
switch { |
|
|
|
|
|
case err != nil: |
|
|
|
|
|
lastFailed = true |
|
|
|
|
|
logger.Error("node not yet ready", |
|
|
|
|
|
"iter", counter, |
|
|
|
|
|
"node", node.Name, |
|
|
|
|
|
"err", err, |
|
|
|
|
|
"target", height, |
|
|
|
|
|
) |
|
|
|
|
|
case status != nil: |
|
|
|
|
|
logger.Error("node not yet ready", |
|
|
|
|
|
"iter", counter, |
|
|
|
|
|
"node", node.Name, |
|
|
|
|
|
"height", status.SyncInfo.LatestBlockHeight, |
|
|
|
|
|
"target", height, |
|
|
|
|
|
) |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
timer.Reset(250 * time.Millisecond) |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
return lastHeight, nil |
|
|
|
|
|
} |
|
|
} |