Browse Source

e2e: tweak semantics of waitForHeight (#6943)

pull/6948/head
Callum Waters 3 years ago
committed by GitHub
parent
commit
e932b469ed
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 89 additions and 17 deletions
  1. +3
    -1
      light/client.go
  2. +27
    -1
      light/client_test.go
  3. +1
    -1
      test/e2e/runner/benchmark.go
  4. +57
    -13
      test/e2e/runner/rpc.go
  5. +1
    -1
      test/e2e/runner/wait.go

+ 3
- 1
light/client.go View File

@ -379,6 +379,7 @@ func (c *Client) Update(ctx context.Context, now time.Time) (*types.LightBlock,
return nil, err return nil, err
} }
// If there is a new light block then verify it
if latestBlock.Height > lastTrustedHeight { if latestBlock.Height > lastTrustedHeight {
err = c.verifyLightBlock(ctx, latestBlock, now) err = c.verifyLightBlock(ctx, latestBlock, now)
if err != nil { if err != nil {
@ -388,7 +389,8 @@ func (c *Client) Update(ctx context.Context, now time.Time) (*types.LightBlock,
return latestBlock, nil return latestBlock, nil
} }
return nil, nil
// else return the latestTrustedBlock
return c.latestTrustedBlock, nil
} }
// VerifyLightBlockAtHeight fetches the light block at the given height // VerifyLightBlockAtHeight fetches the light block at the given height


+ 27
- 1
light/client_test.go View File

@ -644,7 +644,7 @@ func TestClientReplacesPrimaryWithWitnessIfPrimaryIsUnavailable(t *testing.T) {
chainID, chainID,
trustOptions, trustOptions,
mockDeadNode, mockDeadNode,
[]provider.Provider{mockFullNode, mockFullNode},
[]provider.Provider{mockDeadNode, mockFullNode},
dbs.New(dbm.NewMemDB()), dbs.New(dbm.NewMemDB()),
light.Logger(log.TestingLogger()), light.Logger(log.TestingLogger()),
) )
@ -663,6 +663,32 @@ func TestClientReplacesPrimaryWithWitnessIfPrimaryIsUnavailable(t *testing.T) {
mockFullNode.AssertExpectations(t) mockFullNode.AssertExpectations(t)
} }
func TestClientReplacesPrimaryWithWitnessIfPrimaryDoesntHaveBlock(t *testing.T) {
mockFullNode := &provider_mocks.Provider{}
mockFullNode.On("LightBlock", mock.Anything, mock.Anything).Return(l1, nil)
mockDeadNode := &provider_mocks.Provider{}
mockDeadNode.On("LightBlock", mock.Anything, mock.Anything).Return(nil, provider.ErrLightBlockNotFound)
c, err := light.NewClient(
ctx,
chainID,
trustOptions,
mockDeadNode,
[]provider.Provider{mockDeadNode, mockFullNode},
dbs.New(dbm.NewMemDB()),
light.Logger(log.TestingLogger()),
)
require.NoError(t, err)
_, err = c.Update(ctx, bTime.Add(2*time.Hour))
require.NoError(t, err)
// we should still have the dead node as a witness because it
// hasn't repeatedly been unresponsive yet
assert.Equal(t, 2, len(c.Witnesses()))
mockDeadNode.AssertExpectations(t)
mockFullNode.AssertExpectations(t)
}
func TestClient_BackwardsVerification(t *testing.T) { func TestClient_BackwardsVerification(t *testing.T) {
{ {
headers, vals, _ := genLightBlocksWithKeys(chainID, 9, 3, 0, bTime) headers, vals, _ := genLightBlocksWithKeys(chainID, 9, 3, 0, bTime)


+ 1
- 1
test/e2e/runner/benchmark.go View File

@ -22,7 +22,7 @@ import (
// Metrics are based of the `benchmarkLength`, the amount of consecutive blocks // Metrics are based of the `benchmarkLength`, the amount of consecutive blocks
// sampled from in the testnet // sampled from in the testnet
func Benchmark(ctx context.Context, testnet *e2e.Testnet, benchmarkLength int64) error { func Benchmark(ctx context.Context, testnet *e2e.Testnet, benchmarkLength int64) error {
block, _, err := waitForHeight(ctx, testnet, 0)
block, err := getLatestBlock(ctx, testnet)
if err != nil { if err != nil {
return err return err
} }


+ 57
- 13
test/e2e/runner/rpc.go View File

@ -13,17 +13,22 @@ 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 block at the height seen. Errors if the network is not making
// progress at all. // progress at all.
// If height == 0, the initial height of the test network is used as the target.
func waitForHeight(ctx context.Context, 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
clients = map[string]*rpchttp.HTTP{} clients = map[string]*rpchttp.HTTP{}
lastHeight int64
lastIncrease = time.Now() lastIncrease = time.Now()
nodesAtHeight = map[string]struct{}{} nodesAtHeight = map[string]struct{}{}
numRunningNodes int numRunningNodes int
) )
if height == 0 {
height = testnet.InitialHeight
}
for _, node := range testnet.Nodes { for _, node := range testnet.Nodes {
if node.Stateless() { if node.Stateless() {
continue continue
@ -47,10 +52,10 @@ func waitForHeight(ctx context.Context, testnet *e2e.Testnet, height int64) (*ty
continue continue
} }
// skip nodes that don't have state or haven't started yet
if node.Stateless() { if node.Stateless() {
continue continue
} }
if !node.HasStarted { if !node.HasStarted {
continue continue
} }
@ -67,16 +72,16 @@ func waitForHeight(ctx context.Context, testnet *e2e.Testnet, height int64) (*ty
wctx, cancel := context.WithTimeout(ctx, 10*time.Second) wctx, cancel := context.WithTimeout(ctx, 10*time.Second)
defer cancel() defer cancel()
result, err := client.Block(wctx, nil)
result, err := client.Status(wctx)
if err != nil { if err != nil {
continue continue
} }
if result.Block != nil && (maxResult == nil || result.Block.Height > maxResult.Block.Height) {
maxResult = result
if result.SyncInfo.LatestBlockHeight > lastHeight {
lastHeight = result.SyncInfo.LatestBlockHeight
lastIncrease = time.Now() lastIncrease = time.Now()
} }
if maxResult != nil && maxResult.Block.Height >= height {
if result.SyncInfo.LatestBlockHeight >= height {
// the node has achieved the target height! // the node has achieved the target height!
// add this node to the set of target // add this node to the set of target
@ -90,9 +95,16 @@ func waitForHeight(ctx context.Context, testnet *e2e.Testnet, height int64) (*ty
continue continue
} }
// return once all nodes have reached
// the target height.
return maxResult.Block, &maxResult.BlockID, nil
// All nodes are at or above the target height. Now fetch the block for that target height
// and return it. We loop again through all clients because some may have pruning set but
// at least two of them should be archive nodes.
for _, c := range clients {
result, err := c.Block(ctx, &height)
if err != nil || result == nil || result.Block == nil {
continue
}
return result.Block, &result.BlockID, err
}
} }
} }
@ -100,12 +112,12 @@ func waitForHeight(ctx context.Context, testnet *e2e.Testnet, height int64) (*ty
return nil, nil, errors.New("unable to connect to any network nodes") return nil, nil, errors.New("unable to connect to any network nodes")
} }
if time.Since(lastIncrease) >= time.Minute { if time.Since(lastIncrease) >= time.Minute {
if maxResult == nil {
return nil, nil, errors.New("chain stalled at unknown height")
if lastHeight == 0 {
return nil, nil, errors.New("chain stalled at unknown height (most likely upon starting)")
} }
return nil, nil, fmt.Errorf("chain stalled at height %v [%d of %d nodes %+v]", return nil, nil, fmt.Errorf("chain stalled at height %v [%d of %d nodes %+v]",
maxResult.Block.Height,
lastHeight,
len(nodesAtHeight), len(nodesAtHeight),
numRunningNodes, numRunningNodes,
nodesAtHeight) nodesAtHeight)
@ -182,3 +194,35 @@ func waitForNode(ctx context.Context, node *e2e.Node, height int64) (*rpctypes.R
} }
} }
} }
// getLatestBlock returns the last block that all active nodes in the network have
// agreed upon i.e. the earlist of each nodes latest block
func getLatestBlock(ctx context.Context, testnet *e2e.Testnet) (*types.Block, error) {
var earliestBlock *types.Block
for _, node := range testnet.Nodes {
// skip nodes that don't have state or haven't started yet
if node.Stateless() {
continue
}
if !node.HasStarted {
continue
}
client, err := node.Client()
if err != nil {
return nil, err
}
wctx, cancel := context.WithTimeout(ctx, 10*time.Second)
defer cancel()
result, err := client.Block(wctx, nil)
if err != nil {
return nil, err
}
if result.Block != nil && (earliestBlock == nil || earliestBlock.Height > result.Block.Height) {
earliestBlock = result.Block
}
}
return earliestBlock, nil
}

+ 1
- 1
test/e2e/runner/wait.go View File

@ -10,7 +10,7 @@ import (
// Wait waits for a number of blocks to be produced, and for all nodes to catch // Wait waits for a number of blocks to be produced, and for all nodes to catch
// up with it. // up with it.
func Wait(ctx context.Context, testnet *e2e.Testnet, blocks int64) error { func Wait(ctx context.Context, testnet *e2e.Testnet, blocks int64) error {
block, _, err := waitForHeight(ctx, testnet, 0)
block, err := getLatestBlock(ctx, testnet)
if err != nil { if err != nil {
return err return err
} }


Loading…
Cancel
Save