Skip to content

Commit

Permalink
fix: bridgesync e2e test, prevent reorg a finalized block
Browse files Browse the repository at this point in the history
  • Loading branch information
joanestebanr committed Feb 5, 2025
1 parent 925baba commit ca91c0a
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 12 deletions.
22 changes: 20 additions & 2 deletions bridgesync/bridgesync.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -188,6 +189,7 @@ func newBridgeSync(
return &BridgeSync{
processor: processor,
driver: driver,
downloader: downloader,
originNetwork: originNetwork,
reorgDetector: rd,
}, nil
Expand All @@ -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
Expand Down
45 changes: 35 additions & 10 deletions bridgesync/e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)

Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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()
}
8 changes: 8 additions & 0 deletions sync/evmdownloader.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ type EVMDownloader struct {
log *log.Logger
finalizedBlockType etherman.BlockNumberFinality
stopDownloaderOnIterationN int
lastBlockNumberRequested uint64
}

func NewEVMDownloader(
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 {
Expand Down

0 comments on commit ca91c0a

Please sign in to comment.