From ca91c0a3b82d100da7b3291a57596eea3418b835 Mon Sep 17 00:00:00 2001 From: joanestebanr <129153821+joanestebanr@users.noreply.github.com> Date: Wed, 5 Feb 2025 11:46:14 +0100 Subject: [PATCH] fix: bridgesync e2e test, prevent reorg a finalized block --- bridgesync/bridgesync.go | 22 ++++++++++++++++++-- bridgesync/e2e_test.go | 45 +++++++++++++++++++++++++++++++--------- sync/evmdownloader.go | 8 +++++++ 3 files changed, 63 insertions(+), 12 deletions(-) diff --git a/bridgesync/bridgesync.go b/bridgesync/bridgesync.go index 4ebbd127..a0b9ca84 100644 --- a/bridgesync/bridgesync.go +++ b/bridgesync/bridgesync.go @@ -23,8 +23,9 @@ type ReorgDetector interface { // BridgeSync manages the state of the exit tree for the bridge contract by processing Ethereum blockchain events. type BridgeSync struct { - processor *processor - driver *sync.EVMDriver + processor *processor + driver *sync.EVMDriver + downloader *sync.EVMDownloader originNetwork uint32 reorgDetector ReorgDetector @@ -188,6 +189,7 @@ func newBridgeSync( return &BridgeSync{ processor: processor, driver: driver, + downloader: downloader, originNetwork: originNetwork, reorgDetector: rd, }, nil @@ -205,6 +207,22 @@ func (s *BridgeSync) GetLastProcessedBlock(ctx context.Context) (uint64, error) return s.processor.GetLastProcessedBlock(ctx) } +func (s *BridgeSync) GetLastRequestedBlock(ctx context.Context) (uint64, error) { + if s.processor.isHalted() { + return 0, sync.ErrInconsistentState + } + storageLastBlock, err := s.processor.GetLastProcessedBlock(ctx) + if err != nil { + return 0, err + } + downloaderLastBlock := s.downloader.LastBlockNumberRequested() + log.Infof("storage: %d downloader: %d", storageLastBlock, downloaderLastBlock) + if downloaderLastBlock > storageLastBlock { + return downloaderLastBlock, nil + } + return storageLastBlock, nil +} + func (s *BridgeSync) GetBridgeRootByHash(ctx context.Context, root common.Hash) (*tree.Root, error) { if s.processor.isHalted() { return nil, sync.ErrInconsistentState diff --git a/bridgesync/e2e_test.go b/bridgesync/e2e_test.go index 976fd54c..12fef04f 100644 --- a/bridgesync/e2e_test.go +++ b/bridgesync/e2e_test.go @@ -7,10 +7,12 @@ import ( "time" "github.com/agglayer/aggkit/bridgesync" + "github.com/agglayer/aggkit/etherman" "github.com/agglayer/aggkit/log" "github.com/agglayer/aggkit/test/helpers" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/ethclient/simulated" "github.com/stretchr/testify/require" ) @@ -56,13 +58,19 @@ func TestBridgeEventE2E(t *testing.T) { require.NoError(t, err) require.Equal(t, receipt.Status, types.ReceiptStatusSuccessful) expectedBridges = append(expectedBridges, bridge) + expectedRoot, err := setup.L1Environment.BridgeContract.GetRoot(nil) + require.NoError(t, err) + finalizedBlock := GetFinalizedBlockNumber(t, ctx, setup.L1Environment.SimBackend.Client()) + log.Infof("*** iteration: %d, Bridge Root: %s latestBlock:%d finalizedBlock:%d", i, common.Hash(expectedRoot).Hex(), bn, finalizedBlock) bridgesSent++ - - // Trigger reorg - if i%reorgEveryXIterations == 0 { - blocksToReorg := 1 + i%maxReorgDepth - bn, err := setup.L1Environment.SimBackend.Client().BlockNumber(ctx) - require.NoError(t, err) + bn, err = setup.L1Environment.SimBackend.Client().BlockNumber(ctx) + require.NoError(t, err) + finalizedBlockNumber := GetFinalizedBlockNumber(t, ctx, setup.L1Environment.SimBackend.Client()) + blocksToReorg := 1 + i%maxReorgDepth + // Trigger reorg but prevent to reorg a finalized block + if i%reorgEveryXIterations == 0 && bn-uint64(blocksToReorg) > finalizedBlockNumber { + log.Infof("*** Reorg iteration: %d, Reorging %d blocks. From block: %d to %d. finalizedBlockNumber: %d", + i, blocksToReorg, bn, bn-uint64(blocksToReorg), finalizedBlockNumber) helpers.Reorg(t, setup.L1Environment.SimBackend, uint64(blocksToReorg)) // Clean expected bridges lastValidBlock := bn - uint64(blocksToReorg) @@ -92,21 +100,38 @@ func TestBridgeEventE2E(t *testing.T) { // Wait for syncer to catch up time.Sleep(time.Second * 2) // sleeping since the processor could be up to date, but have pending reorgs - lb, err := setup.L1Environment.SimBackend.Client().BlockNumber(ctx) - require.NoError(t, err) + + lb := GetFinalizedBlockNumber(t, ctx, setup.L1Environment.SimBackend.Client()) helpers.RequireProcessorUpdated(t, setup.L1Environment.BridgeSync, lb) // Get bridges lastBlock, err := setup.L1Environment.SimBackend.Client().BlockNumber(ctx) require.NoError(t, err) - actualBridges, err := setup.L1Environment.BridgeSync.GetBridges(ctx, 0, lastBlock) + lastProcessedBlock, err := setup.L1Environment.BridgeSync.GetLastProcessedBlock(ctx) require.NoError(t, err) - + actualBridges, err := setup.L1Environment.BridgeSync.GetBridges(ctx, 0, lastProcessedBlock) + require.NoError(t, err) + log.Infof("lastBlockOnChain:%d lastProcessedBlock: %d, len(actualBridges): %d", lb, lastProcessedBlock, len(actualBridges)) // Assert bridges expectedRoot, err := setup.L1Environment.BridgeContract.GetRoot(nil) require.NoError(t, err) root, err := setup.L1Environment.BridgeSync.GetExitRootByIndex(ctx, expectedBridges[len(expectedBridges)-1].DepositCount) require.NoError(t, err) + log.Infof("expectedRoot: %s lastBlock: %d lastFinalized:%d DepositCount:%d ", common.Hash(expectedRoot).Hex(), lastBlock, lb, expectedBridges[len(expectedBridges)-1].DepositCount) + for i := 119; i >= 00; i-- { + root, err := setup.L1Environment.BridgeSync.GetExitRootByIndex(ctx, uint32(i)) + require.NoError(t, err) + log.Infof("DepositCount:%d root: %s", i, root.Hash.Hex()) + } require.Equal(t, common.Hash(expectedRoot).Hex(), root.Hash.Hex()) require.Equal(t, expectedBridges, actualBridges) } + +func GetFinalizedBlockNumber(t *testing.T, ctx context.Context, client simulated.Client) uint64 { + t.Helper() + lastBlockFinalityType, err := etherman.FinalizedBlock.ToBlockNum() + require.NoError(t, err) + lastBlockHeader, err := client.HeaderByNumber(ctx, lastBlockFinalityType) + require.NoError(t, err) + return lastBlockHeader.Number.Uint64() +} diff --git a/sync/evmdownloader.go b/sync/evmdownloader.go index 04a411a2..bcee2985 100644 --- a/sync/evmdownloader.go +++ b/sync/evmdownloader.go @@ -42,6 +42,7 @@ type EVMDownloader struct { log *log.Logger finalizedBlockType etherman.BlockNumberFinality stopDownloaderOnIterationN int + lastBlockNumberRequested uint64 } func NewEVMDownloader( @@ -107,6 +108,12 @@ func (d *EVMDownloader) setStopDownloaderOnIterationN(iteration int) { d.stopDownloaderOnIterationN = iteration } +// LastBlockNumberRequested returns the last block number requested by the downloader +// maybe doesnt have data and are in unsafe zone, so no block have emitted +func (d *EVMDownloader) LastBlockNumberRequested() uint64 { + return d.lastBlockNumberRequested +} + func (d *EVMDownloader) Download(ctx context.Context, fromBlock uint64, downloadedCh chan EVMBlock) { lastBlock := d.WaitForNewBlocks(ctx, 0) toBlock := fromBlock + d.syncBlockChunkSize @@ -153,6 +160,7 @@ func (d *EVMDownloader) Download(ctx context.Context, fromBlock uint64, download blocks := d.GetEventsByBlockRange(ctx, fromBlock, requestToBlock) d.log.Debugf("result events from blocks [%d to %d] -> len(blocks)=%d", fromBlock, requestToBlock, len(blocks)) + d.lastBlockNumberRequested = requestToBlock if toBlock <= lastFinalizedBlockNumber { d.reportBlocks(downloadedCh, blocks, lastFinalizedBlockNumber) if blocks.Len() == 0 || blocks[blocks.Len()-1].Num < toBlock {