Skip to content

Commit d072ae2

Browse files
committed
Refactor tests in preparation for sparse blocks
1 parent 715d9ba commit d072ae2

2 files changed

Lines changed: 79 additions & 66 deletions

File tree

pkg/logpoller/helper_test.go

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -176,16 +176,6 @@ func (th *TestHarness) assertDontHave(t *testing.T, start, end int) {
176176
}
177177
}
178178

179-
func (th *TestHarness) assertHaveCanonical(t *testing.T, start, end int) {
180-
for i := start; i < end; i++ {
181-
blk, err := th.ORM.SelectBlockByNumber(testutils.Context(t), int64(i))
182-
require.NoError(t, err, "block %v", i)
183-
chainBlk, err := th.Client.BlockByNumber(testutils.Context(t), big.NewInt(int64(i)))
184-
require.NoError(t, err)
185-
assert.Equal(t, chainBlk.Hash().String(), blk.BlockHash.String(), "block %v", i)
186-
}
187-
}
188-
189179
// Simulates an RPC failover event to an alternate rpc server. This can also be used to
190180
// simulate switching back to the primary rpc after it recovers.
191181
func (th *TestHarness) SetActiveClient(backend evmtypes.Backend, chainType chaintype.ChainType) {

pkg/logpoller/log_poller_test.go

Lines changed: 79 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -723,23 +723,7 @@ func TestLogPoller_SynchronizedWithGeth(t *testing.T) {
723723
lp.PollAndSaveLogs(testutils.Context(t), currentBlockNumber, false)
724724
currentBlock, err := lp.LatestBlock(testutils.Context(t))
725725
require.NoError(t, err)
726-
matchesGeth := func() bool {
727-
// Check every block is identical
728-
latest, err1 := ec.BlockByNumber(testutils.Context(t), nil)
729-
require.NoError(t, err1)
730-
for i := 1; i < int(latest.NumberU64()); i++ {
731-
ourBlock, err1 := lp.BlockByNumber(testutils.Context(t), int64(i))
732-
require.NoError(t, err1)
733-
gethBlock, err1 := ec.BlockByNumber(testutils.Context(t), big.NewInt(int64(i)))
734-
require.NoError(t, err1)
735-
if ourBlock.BlockHash != gethBlock.Hash() {
736-
t.Logf("Initial poll our block differs at height %d got %x want %x\n", i, ourBlock.BlockHash, gethBlock.Hash())
737-
return false
738-
}
739-
}
740-
return true
741-
}
742-
if !matchesGeth() {
726+
if !checkDBMatchesGeth(t, orm, simulatedClient) {
743727
return false
744728
}
745729
// Randomly pick to mine or reorg
@@ -776,7 +760,7 @@ func TestLogPoller_SynchronizedWithGeth(t *testing.T) {
776760
currentBlock, err = lp.LatestBlock(testutils.Context(t))
777761
require.NoError(t, err)
778762
}
779-
return matchesGeth()
763+
return checkDBMatchesGeth(t, orm, simulatedClient)
780764
}, gen.SliceOfN(numChainInserts, gen.UInt64Range(1, uint64(finalityDepth-1))))) // Max reorg depth is finality depth - 1
781765
p.TestingRun(t)
782766
}
@@ -841,15 +825,15 @@ func TestLogPoller_PollAndSaveLogs(t *testing.T) {
841825
lgs, err := th.ORM.SelectLogsByBlockRange(testutils.Context(t), 1, 1)
842826
require.NoError(t, err)
843827
assert.Empty(t, lgs)
844-
th.assertHaveCanonical(t, 1, 1)
828+
requireDBMatchesGeth(t, th.ORM, th.Client)
845829

846830
// Polling again should be a noop, since we are at the latest.
847831
newStart = th.PollAndSaveLogs(testutils.Context(t), newStart)
848832
assert.Equal(t, int64(2), newStart)
849833
latest, err := th.ORM.SelectLatestBlock(testutils.Context(t))
850834
require.NoError(t, err)
851835
assert.Equal(t, int64(1), latest.BlockNumber)
852-
th.assertHaveCanonical(t, 1, 1)
836+
requireDBMatchesGeth(t, th.ORM, th.Client)
853837

854838
// Test scenario: one log 2 block chain.
855839
// Chain gen <- 1 <- 2 (L1)
@@ -901,7 +885,7 @@ func TestLogPoller_PollAndSaveLogs(t *testing.T) {
901885
require.NoError(t, err)
902886
require.Len(t, lgs, 1)
903887
assert.Equal(t, hexutil.MustDecode(`0x0000000000000000000000000000000000000000000000000000000000000002`), lgs[0].Data)
904-
th.assertHaveCanonical(t, 1, 3)
888+
requireDBMatchesGeth(t, th.ORM, th.Client)
905889

906890
parent, err := th.Client.BlockByNumber(testutils.Context(t), big.NewInt(1))
907891
require.NoError(t, err)
@@ -936,8 +920,7 @@ func TestLogPoller_PollAndSaveLogs(t *testing.T) {
936920
assert.Equal(t, hexutil.MustDecode(`0x0000000000000000000000000000000000000000000000000000000000000001`), lgs[0].Data)
937921
assert.Equal(t, int64(3), lgs[1].BlockNumber)
938922
assert.Equal(t, hexutil.MustDecode(`0x0000000000000000000000000000000000000000000000000000000000000003`), lgs[1].Data)
939-
th.assertHaveCanonical(t, 1, 1)
940-
th.assertHaveCanonical(t, 3, 4)
923+
requireDBMatchesGeth(t, th.ORM, th.Client)
941924
th.assertDontHave(t, 2, 2) // 2 gets backfilled
942925

943926
// Test scenario: multiple logs per block for many blocks (also after reorg).
@@ -968,9 +951,7 @@ func TestLogPoller_PollAndSaveLogs(t *testing.T) {
968951
assert.Equal(t, th.EmitterAddress2, lgs[1].Address)
969952
assert.Equal(t, hexutil.MustDecode(`0x0000000000000000000000000000000000000000000000000000000000000006`), lgs[2].Data)
970953
assert.Equal(t, th.EmitterAddress1, lgs[2].Address)
971-
th.assertHaveCanonical(t, 1, 1)
972-
th.assertDontHave(t, 2, 2) // 2 gets backfilled
973-
th.assertHaveCanonical(t, 3, 6)
954+
requireDBMatchesGeth(t, th.ORM, th.Client)
974955

975956
// Test scenario: node down for exactly finality + 2 blocks
976957
// Note we only backfill up to finalized - 1 blocks, because we need to save the
@@ -996,7 +977,7 @@ func TestLogPoller_PollAndSaveLogs(t *testing.T) {
996977
assert.Equal(t, int64(8), lgs[1].BlockNumber)
997978
assert.Equal(t, hexutil.MustDecode(`0x0000000000000000000000000000000000000000000000000000000000000009`), lgs[2].Data)
998979
assert.Equal(t, int64(9), lgs[2].BlockNumber)
999-
th.assertHaveCanonical(t, 8, 10)
980+
requireDBMatchesGeth(t, th.ORM, th.Client)
1000981

1001982
// Test scenario large backfill (multiple batches)
1002983
// Chain gen <- 1 <- 2 (L1_1) <- 3' L1_3 <- 4 <- 5 (L1_4, L2_5) <- 6 (L1_6) <- 7 (L1_7) <- 8 (L1_8) <- 9 (L1_9) <- 10..32
@@ -1017,9 +998,9 @@ func TestLogPoller_PollAndSaveLogs(t *testing.T) {
1017998
lgs, err = th.ORM.SelectLogsByBlockRange(testutils.Context(t), 11, 36)
1018999
require.NoError(t, err)
10191000
assert.Len(t, lgs, 25)
1020-
th.assertHaveCanonical(t, 32, 36) // Should have last finalized block plus unfinalized blocks
1021-
th.assertDontHave(t, 11, 13) // Should not have older finalized blocks
1022-
th.assertDontHave(t, 14, 16) // Should not have older finalized blocks
1001+
requireDBMatchesGeth(t, th.ORM, th.Client)
1002+
th.assertDontHave(t, 11, 13) // Should not have older finalized blocks
1003+
th.assertDontHave(t, 14, 16) // Should not have older finalized blocks
10231004

10241005
// Verify that a custom block timestamp will get written to db correctly also
10251006
b, err = th.Client.BlockByNumber(testutils.Context(t), nil)
@@ -1206,9 +1187,8 @@ func TestLogPoller_PollAndSaveLogsDeepReorg(t *testing.T) {
12061187
require.NoError(t, err)
12071188
require.Len(t, lgs, 30)
12081189
assert.Equal(t, hexutil.MustDecode(`0x0000000000000000000000000000000000000000000000000000000000000002`), lgs[0].Data)
1209-
th.assertHaveCanonical(t, 1, 2)
1210-
th.assertDontHave(t, 2, 31) // These blocks are backfilled
1211-
th.assertHaveCanonical(t, 32, 36)
1190+
requireLBBlockIsFinalized(t, th.ORM, 30)
1191+
requireDBMatchesGeth(t, th.ORM, th.Client)
12121192
})
12131193
}
12141194
}
@@ -1557,27 +1537,25 @@ func TestTooManyLogResults(t *testing.T) {
15571537
}
15581538

15591539
var filterLogsCall *mock.Call
1560-
head := &evmtypes.Head{}
1561-
finalized := &evmtypes.Head{}
15621540

15631541
ec.On("HeadByNumber", mock.Anything, mock.Anything).Return(func(ctx context.Context, blockNumber *big.Int) (*evmtypes.Head, error) {
15641542
if blockNumber == nil {
15651543
require.FailNow(t, "unexpected call to get current head")
15661544
}
1567-
return &evmtypes.Head{Number: blockNumber.Int64(), ParentHash: common.HexToHash(fmt.Sprintf("0x%x", blockNumber.Int64()-1))}, nil
1545+
return newHead(blockNumber.Int64()), nil
15681546
})
15691547

15701548
t.Run("halves size until small enough, then succeeds", func(t *testing.T) {
15711549
// Simulate latestBlock = 300
1572-
head.Number = 300
1573-
head.Hash = common.HexToHash("0x1234") // needed to satisfy validation in fetchBlocks()
1574-
finalized.Number = head.Number - lpOpts.FinalityDepth
1550+
head := newHead(300)
1551+
finalized := newHead(head.Number - lpOpts.FinalityDepth)
15751552

15761553
headTracker.On("LatestAndFinalizedBlock", mock.Anything).Return(head, finalized, nil).Once()
15771554
headTracker.On("LatestSafeBlock", mock.Anything).Return(finalized, nil).Once()
15781555

15791556
headByHash := ec.On("HeadByHash", mock.Anything, mock.Anything).Return(func(ctx context.Context, blockHash common.Hash) (*evmtypes.Head, error) {
1580-
return &evmtypes.Head{Hash: blockHash}, nil
1557+
num := new(big.Int).SetBytes(blockHash.Bytes()).Int64()
1558+
return newHead(num), nil
15811559
})
15821560

15831561
batchCallContext := ec.On("BatchCallContext", mock.Anything, mock.Anything).Return(
@@ -1591,11 +1569,7 @@ func TestTooManyLogResults(t *testing.T) {
15911569
blockNumber, ok := new(big.Int).SetString(blockNumberHex[2:], 16)
15921570
require.True(t, ok, blockNumberHex)
15931571

1594-
calls[i].Result = &evmtypes.Head{
1595-
Number: blockNumber.Int64(),
1596-
Hash: common.HexToHash(fmt.Sprintf("0x%x", blockNumber.Int64())),
1597-
ParentHash: common.HexToHash(fmt.Sprintf("0x%x", blockNumber.Int64()-1)),
1598-
}
1572+
calls[i].Result = newHead(blockNumber.Int64())
15991573
}
16001574
return nil
16011575
},
@@ -1629,7 +1603,7 @@ func TestTooManyLogResults(t *testing.T) {
16291603
lp.PollAndSaveLogs(ctx, 5, false)
16301604
block, err2 := o.SelectLatestBlock(ctx)
16311605
require.NoError(t, err2)
1632-
assert.Equal(t, int64(298), block.BlockNumber)
1606+
assert.Equal(t, int64(300), block.BlockNumber)
16331607

16341608
logs := obs.FilterLevelExact(zapcore.WarnLevel).FilterMessageSnippet("halving block range batch size").FilterFieldKey("newBatchSize").All()
16351609
// Should have tried again 3 times--first reducing batch size to 10, then 5, then 2
@@ -1647,8 +1621,8 @@ func TestTooManyLogResults(t *testing.T) {
16471621

16481622
// Now jump to block 500, but return error no matter how small the block range gets.
16491623
// Should exit the loop with a critical error instead of hanging.
1650-
head.Number = 500
1651-
finalized.Number = head.Number - lpOpts.FinalityDepth
1624+
head := newHead(500)
1625+
finalized := newHead(head.Number - lpOpts.FinalityDepth)
16521626
headTracker.On("LatestAndFinalizedBlock", mock.Anything).Return(head, finalized, nil).Once()
16531627
headTracker.On("LatestSafeBlock", mock.Anything).Return(finalized, nil).Once()
16541628
filterLogsCall = ec.On("FilterLogs", mock.Anything, mock.Anything).Return(func(ctx context.Context, fq ethereum.FilterQuery) (logs []types.Log, err error) {
@@ -1663,7 +1637,7 @@ func TestTooManyLogResults(t *testing.T) {
16631637
if err != nil {
16641638
require.ErrorContains(t, err, "no rows") // In case this subtest is run by itself
16651639
} else {
1666-
assert.Equal(t, int64(298), block.BlockNumber)
1640+
assert.Equal(t, int64(300), block.BlockNumber)
16671641
}
16681642
warns := obs.FilterMessageSnippet("halving block range").FilterLevelExact(zapcore.WarnLevel).All()
16691643
crit := obs.FilterMessageSnippet("failed to retrieve logs").FilterLevelExact(zapcore.DPanicLevel).All()
@@ -1679,8 +1653,8 @@ func TestTooManyLogResults(t *testing.T) {
16791653

16801654
t.Run("Unrelated error are retried without adjusting size", func(t *testing.T) {
16811655
unrelatedError := errors.New("Unrelated to the size of the request")
1682-
head.Number = 500
1683-
finalized.Number = head.Number - lpOpts.FinalityDepth
1656+
head := newHead(500)
1657+
finalized := newHead(head.Number - lpOpts.FinalityDepth)
16841658

16851659
obs.TakeAll()
16861660
filterLogsCall = ec.On("FilterLogs", mock.Anything, mock.Anything).Return(func(ctx context.Context, fq ethereum.FilterQuery) (logs []types.Log, err error) {
@@ -1697,7 +1671,7 @@ func TestTooManyLogResults(t *testing.T) {
16971671
if err != nil {
16981672
require.ErrorContains(t, err, "no rows") // In case this subtest is run by itself
16991673
} else {
1700-
assert.Equal(t, int64(298), block.BlockNumber)
1674+
assert.Equal(t, int64(300), block.BlockNumber)
17011675
}
17021676
crit := obs.FilterLevelExact(zapcore.DPanicLevel).All()
17031677
errors := obs.FilterLevelExact(zapcore.ErrorLevel).All()
@@ -2261,10 +2235,59 @@ func TestLogPoller_Reorg_On_Replay(t *testing.T) {
22612235
require.Len(t, logs, 1)
22622236
require.Equal(t, newLogData, big.NewInt(0).SetBytes(logs[0].Data).Int64(), "Log data should match the log from the new block, indicating that the old block's log was properly removed during replay")
22632237
// Ensure reorged block was replaced by a new one
2264-
dbBlock, err := th.ORM.SelectBlockByNumber(testutils.Context(t), reorgedBlock.Number().Int64())
2265-
require.NoError(t, err)
2266-
require.Equal(t, reorgedBlock.Number().Int64(), dbBlock.BlockNumber)
2267-
require.NotEqual(t, reorgedBlock.Hash(), dbBlock.BlockHash)
2238+
requireDBMatchesGeth(t, th.ORM, th.Client)
22682239
})
22692240
}
22702241
}
2242+
2243+
func requireDBMatchesGeth(t *testing.T, orm logpoller.ORM, client logpoller.Client) {
2244+
require.True(t, checkDBMatchesGeth(t, orm, client), "DB state does not match geth canonical chain")
2245+
}
2246+
2247+
func checkDBMatchesGeth(t *testing.T, orm logpoller.ORM, client logpoller.Client) bool {
2248+
// Check every block is identical
2249+
latest, err1 := client.HeadByNumber(testutils.Context(t), nil)
2250+
require.NoError(t, err1)
2251+
dbBlocks, err := orm.SelectLogsByBlockRange(t.Context(), 0, latest.Number)
2252+
require.NoError(t, err)
2253+
// ensure all blocks present in db are on geth canonical chain
2254+
for _, ourBlock := range dbBlocks {
2255+
gethBlock, err1 := client.HeadByNumber(testutils.Context(t), big.NewInt(ourBlock.BlockNumber))
2256+
require.NoError(t, err1)
2257+
if ourBlock.BlockHash != gethBlock.Hash {
2258+
t.Logf("Initial poll our block differs at height %d got %x want %x\n", ourBlock.BlockNumber, ourBlock.BlockHash, gethBlock.Hash)
2259+
return false
2260+
}
2261+
}
2262+
2263+
latestDB, err := orm.SelectLatestBlock(t.Context())
2264+
require.NoError(t, err)
2265+
require.Equal(t, latest.Number, latestDB.BlockNumber, "latest block number in db should match geth")
2266+
2267+
// ensure all logs present in db are on geth canonical chain
2268+
logs, err1 := orm.SelectLogsByBlockRange(t.Context(), 0, latest.Number)
2269+
require.NoError(t, err1)
2270+
for _, log := range logs {
2271+
gethBlock, err1 := client.HeadByNumber(testutils.Context(t), big.NewInt(log.BlockNumber))
2272+
require.NoError(t, err1)
2273+
if log.BlockHash != gethBlock.Hash {
2274+
t.Logf("Log present in db, is not present in canonical chain. Log block number %d, Log block Hash: %s, block hash %s\n", log.BlockNumber, log.BlockHash, gethBlock.Hash)
2275+
return false
2276+
}
2277+
}
2278+
return true
2279+
}
2280+
2281+
func requireLBBlockIsFinalized(t *testing.T, orm logpoller.ORM, block int64) {
2282+
latest, err := orm.SelectLatestBlock(t.Context())
2283+
require.NoError(t, err)
2284+
require.GreaterOrEqual(t, latest.FinalizedBlockNumber, block, "specified block should be finalized")
2285+
}
2286+
2287+
func newHead(num int64) *evmtypes.Head {
2288+
return &evmtypes.Head{
2289+
Number: num,
2290+
Hash: common.BigToHash(big.NewInt(num)),
2291+
ParentHash: common.BigToHash(big.NewInt(num - 1)),
2292+
}
2293+
}

0 commit comments

Comments
 (0)