From ba90549d82bb0a4671a2c2f52b17e44480cc3874 Mon Sep 17 00:00:00 2001 From: Jeffery Walsh Date: Thu, 4 May 2023 19:34:36 -0700 Subject: [PATCH 01/25] ref --- .github/workflows/test.yml | 1 - integration_test/entrypoint.sh | 3 ++- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 0bffce707..4e1ab2480 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -45,7 +45,6 @@ jobs: with: repository: taikoxyz/taiko-mono path: ${{ env.TAIKO_MONO_DIR }} - ref: get_oracle_prover - name: Install Foundry uses: foundry-rs/foundry-toolchain@v1 diff --git a/integration_test/entrypoint.sh b/integration_test/entrypoint.sh index 8f10b1681..ec951f993 100755 --- a/integration_test/entrypoint.sh +++ b/integration_test/entrypoint.sh @@ -32,6 +32,7 @@ L1_SIGNAL_SERVICE_CONTRACT_ADDRESS=$(echo $DEPLOYMENT_JSON | jq '.signal_service trap "docker compose -f $TESTNET_CONFIG down -v" EXIT INT KILL ERR RUN_TESTS=${RUN_TESTS:-false} +PACKAGE=${PACKAGE:-...} echo "TAIKO_L1_CONTRACT_ADDRESS: $TAIKO_L1_CONTRACT_ADDRESS" echo "L1_SIGNAL_SERVICE_CONTRACT_ADDRESS: $L1_SIGNAL_SERVICE_CONTRACT_ADDRESS" @@ -50,7 +51,7 @@ if [ "$RUN_TESTS" == "true" ]; then L2_SUGGESTED_FEE_RECIPIENT=0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 \ L1_PROVER_PRIVATE_KEY=59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d \ JWT_SECRET=$DIR/nodes/jwt.hex \ - go test -v -p=1 ./... -coverprofile=coverage.out -covermode=atomic -timeout=300s + go test -v -p=1 ./$PACKAGE -coverprofile=coverage.out -covermode=atomic -timeout=300s else echo "💻 Local dev net started" docker compose -f $TESTNET_CONFIG logs -f l2_execution_engine From e6124e6ec9a193e98ed3ce1cea83d5b9b26c3ab7 Mon Sep 17 00:00:00 2001 From: Jeffery Walsh Date: Wed, 10 May 2023 12:17:25 -0700 Subject: [PATCH 02/25] handle reorg on events being removed --- pkg/chain_iterator/block_batch_iterator.go | 27 +++++++++++++++---- .../block_batch_iterator_test.go | 3 +++ .../event_iterator/block_proposed_iterator.go | 4 +-- .../event_iterator/block_proven_iterator.go | 4 +-- 4 files changed, 29 insertions(+), 9 deletions(-) diff --git a/pkg/chain_iterator/block_batch_iterator.go b/pkg/chain_iterator/block_batch_iterator.go index 4479b4e7e..5d84f0ea7 100644 --- a/pkg/chain_iterator/block_batch_iterator.go +++ b/pkg/chain_iterator/block_batch_iterator.go @@ -29,6 +29,7 @@ type OnBlocksFunc func( ctx context.Context, start, end *types.Header, updateCurrentFunc UpdateCurrentFunc, + onReorgFunc OnReorgFunc, endIterFunc EndIterFunc, ) error @@ -38,6 +39,9 @@ type UpdateCurrentFunc func(*types.Header) // EndIterFunc ends the current iteration. type EndIterFunc func() +// OnReorgFunc handles a reorganization from the source chain. +type OnReorgFunc func() error + // BlockBatchIterator iterates the blocks in batches between the given start and end heights, // with the awareness of reorganization. type BlockBatchIterator struct { @@ -205,7 +209,7 @@ func (i *BlockBatchIterator) iter() (err error) { return err } - if err := i.onBlocks(i.ctx, i.current, endHeader, i.updateCurrent, i.end); err != nil { + if err := i.onBlocks(i.ctx, i.current, endHeader, i.updateCurrent, i.rewindOnReorgDetected, i.end); err != nil { return err } @@ -252,7 +256,7 @@ func (i *BlockBatchIterator) reverseIter() (err error) { return err } - if err := i.onBlocks(i.ctx, startHeader, i.current, i.updateCurrent, i.end); err != nil { + if err := i.onBlocks(i.ctx, startHeader, i.current, i.updateCurrent, i.rewindOnReorgDetected, i.end); err != nil { return err } @@ -282,6 +286,8 @@ func (i *BlockBatchIterator) end() { // ensureCurrentNotReorged checks if the iterator.current cursor was reorged, if was, will // rewind back `ReorgRewindDepth` blocks. +// reorg is also detected on the iteration of the event later, by checking +// event.Raw.Removed, which will also call `i.rewindOnReorgDetected` to rewind back func (i *BlockBatchIterator) ensureCurrentNotReorged() error { current, err := i.client.HeaderByHash(i.ctx, i.current.Hash()) if err != nil && !errors.Is(err, ethereum.NotFound) { @@ -293,7 +299,13 @@ func (i *BlockBatchIterator) ensureCurrentNotReorged() error { return nil } - // Reorg detected, rewind back `ReorgRewindDepth` blocks + // reorged + return i.rewindOnReorgDetected() +} + +// rewindOnReorgDetected rewinds back `ReorgRewindDepth` blocks and sets i.current +// to a stable block +func (i *BlockBatchIterator) rewindOnReorgDetected() error { var newCurrentHeight uint64 if i.current.Number.Uint64() <= ReorgRewindDepth { newCurrentHeight = 0 @@ -301,6 +313,11 @@ func (i *BlockBatchIterator) ensureCurrentNotReorged() error { newCurrentHeight = i.current.Number.Uint64() - ReorgRewindDepth } - i.current, err = i.client.HeaderByNumber(i.ctx, new(big.Int).SetUint64(newCurrentHeight)) - return err + current, err := i.client.HeaderByNumber(i.ctx, new(big.Int).SetUint64(newCurrentHeight)) + if err != nil { + return err + } + + i.current = current + return nil } diff --git a/pkg/chain_iterator/block_batch_iterator_test.go b/pkg/chain_iterator/block_batch_iterator_test.go index fd809936d..760c50ec2 100644 --- a/pkg/chain_iterator/block_batch_iterator_test.go +++ b/pkg/chain_iterator/block_batch_iterator_test.go @@ -33,6 +33,7 @@ func (s *BlockBatchIteratorTestSuite) TestIter() { ctx context.Context, start, end *types.Header, updateCurrentFunc UpdateCurrentFunc, + onReorgFunc OnReorgFunc, endIterFunc EndIterFunc, ) error { s.Equal(lastEnd.Uint64(), start.Number.Uint64()) @@ -68,6 +69,7 @@ func (s *BlockBatchIteratorTestSuite) TestIterReverse() { ctx context.Context, start, end *types.Header, updateCurrentFunc UpdateCurrentFunc, + onReorgFunc OnReorgFunc, endIterFunc EndIterFunc, ) error { s.Equal(lastStart.Uint64(), end.Number.Uint64()) @@ -99,6 +101,7 @@ func (s *BlockBatchIteratorTestSuite) TestIterEndFunc() { ctx context.Context, start, end *types.Header, updateCurrentFunc UpdateCurrentFunc, + onReorgFunc OnReorgFunc, endIterFunc EndIterFunc, ) error { s.Equal(lastEnd.Uint64(), start.Number.Uint64()) diff --git a/pkg/chain_iterator/event_iterator/block_proposed_iterator.go b/pkg/chain_iterator/event_iterator/block_proposed_iterator.go index 6928f09a3..74658e3c8 100644 --- a/pkg/chain_iterator/event_iterator/block_proposed_iterator.go +++ b/pkg/chain_iterator/event_iterator/block_proposed_iterator.go @@ -105,6 +105,7 @@ func assembleBlockProposedIteratorCallback( ctx context.Context, start, end *types.Header, updateCurrentFunc chainIterator.UpdateCurrentFunc, + onReorgFunc chainIterator.OnReorgFunc, endFunc chainIterator.EndIterFunc, ) error { endHeight := end.Number.Uint64() @@ -120,9 +121,8 @@ func assembleBlockProposedIteratorCallback( for iter.Next() { event := iter.Event - // Skip if reorged. if event.Raw.Removed { - continue + return onReorgFunc() } if err := callback(ctx, event, eventIter.end); err != nil { diff --git a/pkg/chain_iterator/event_iterator/block_proven_iterator.go b/pkg/chain_iterator/event_iterator/block_proven_iterator.go index 39d8bf1fd..8228c39e6 100644 --- a/pkg/chain_iterator/event_iterator/block_proven_iterator.go +++ b/pkg/chain_iterator/event_iterator/block_proven_iterator.go @@ -101,6 +101,7 @@ func assembleBlockProvenIteratorCallback( ctx context.Context, start, end *types.Header, updateCurrentFunc chainIterator.UpdateCurrentFunc, + onReorgFunc chainIterator.OnReorgFunc, endFunc chainIterator.EndIterFunc, ) error { endHeight := end.Number.Uint64() @@ -116,9 +117,8 @@ func assembleBlockProvenIteratorCallback( for iter.Next() { event := iter.Event - // Skip if reorged. if event.Raw.Removed { - continue + return onReorgFunc() } if err := callback(ctx, event, eventIter.end); err != nil { From fca8bfb4fae9f2f9919a29ae525313cd1e504dd3 Mon Sep 17 00:00:00 2001 From: Jeffery Walsh Date: Wed, 10 May 2023 14:35:10 -0700 Subject: [PATCH 03/25] comment --- pkg/chain_iterator/block_batch_iterator.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/chain_iterator/block_batch_iterator.go b/pkg/chain_iterator/block_batch_iterator.go index 5d84f0ea7..fa7dd5d3f 100644 --- a/pkg/chain_iterator/block_batch_iterator.go +++ b/pkg/chain_iterator/block_batch_iterator.go @@ -304,7 +304,7 @@ func (i *BlockBatchIterator) ensureCurrentNotReorged() error { } // rewindOnReorgDetected rewinds back `ReorgRewindDepth` blocks and sets i.current -// to a stable block +// to a stable block, or 0 if it's less than `ReorgRewindDepth`. func (i *BlockBatchIterator) rewindOnReorgDetected() error { var newCurrentHeight uint64 if i.current.Number.Uint64() <= ReorgRewindDepth { From a7bbfe2056fd7630ad55b4bea6be5be504a83c62 Mon Sep 17 00:00:00 2001 From: Jeffery Walsh Date: Wed, 10 May 2023 15:32:13 -0700 Subject: [PATCH 04/25] block batch iterator test --- pkg/chain_iterator/block_batch_iterator.go | 36 +++++++++++---- .../block_batch_iterator_test.go | 46 +++++++++++++++++++ 2 files changed, 73 insertions(+), 9 deletions(-) diff --git a/pkg/chain_iterator/block_batch_iterator.go b/pkg/chain_iterator/block_batch_iterator.go index fa7dd5d3f..fe72e566e 100644 --- a/pkg/chain_iterator/block_batch_iterator.go +++ b/pkg/chain_iterator/block_batch_iterator.go @@ -16,7 +16,7 @@ import ( const ( DefaultBlocksReadPerEpoch = 1000 - ReorgRewindDepth = 20 + DefaultReorgRewindDepth = 20 ) var ( @@ -55,6 +55,8 @@ type BlockBatchIterator struct { onBlocks OnBlocksFunc isEnd bool reverse bool + reorgRewindDepth uint64 + onReorg OnReorgFunc } // BlockBatchIteratorConfig represents the configs of a block batch iterator. @@ -65,6 +67,8 @@ type BlockBatchIteratorConfig struct { EndHeight *big.Int OnBlocks OnBlocksFunc Reverse bool + ReorgRewindDepth *uint64 + OnReorg OnReorgFunc } // NewBlockBatchIterator creates a new block batch iterator instance. @@ -104,13 +108,27 @@ func NewBlockBatchIterator(ctx context.Context, cfg *BlockBatchIteratorConfig) ( } } + var reorgRewindDepth uint64 + if cfg.ReorgRewindDepth != nil { + reorgRewindDepth = *cfg.ReorgRewindDepth + } else { + reorgRewindDepth = DefaultReorgRewindDepth + } + iterator := &BlockBatchIterator{ - ctx: ctx, - client: cfg.Client, - chainID: chainID, - startHeight: cfg.StartHeight.Uint64(), - onBlocks: cfg.OnBlocks, - reverse: cfg.Reverse, + ctx: ctx, + client: cfg.Client, + chainID: chainID, + startHeight: cfg.StartHeight.Uint64(), + onBlocks: cfg.OnBlocks, + reverse: cfg.Reverse, + reorgRewindDepth: reorgRewindDepth, + } + + if cfg.OnReorg != nil { + iterator.onReorg = cfg.OnReorg + } else { + iterator.onReorg = iterator.rewindOnReorgDetected } if cfg.Reverse { @@ -307,10 +325,10 @@ func (i *BlockBatchIterator) ensureCurrentNotReorged() error { // to a stable block, or 0 if it's less than `ReorgRewindDepth`. func (i *BlockBatchIterator) rewindOnReorgDetected() error { var newCurrentHeight uint64 - if i.current.Number.Uint64() <= ReorgRewindDepth { + if i.current.Number.Uint64() <= i.reorgRewindDepth { newCurrentHeight = 0 } else { - newCurrentHeight = i.current.Number.Uint64() - ReorgRewindDepth + newCurrentHeight = i.current.Number.Uint64() - i.reorgRewindDepth } current, err := i.client.HeaderByNumber(i.ctx, new(big.Int).SetUint64(newCurrentHeight)) diff --git a/pkg/chain_iterator/block_batch_iterator_test.go b/pkg/chain_iterator/block_batch_iterator_test.go index 760c50ec2..39120c0df 100644 --- a/pkg/chain_iterator/block_batch_iterator_test.go +++ b/pkg/chain_iterator/block_batch_iterator_test.go @@ -116,6 +116,52 @@ func (s *BlockBatchIteratorTestSuite) TestIterEndFunc() { s.Equal(lastEnd.Uint64(), maxBlocksReadPerEpoch) } +func (s *BlockBatchIteratorTestSuite) TestIter_ReorgEncounteredWithRemovedEvent() { + var maxBlocksReadPerEpoch uint64 = 2 + var reorgRewindDepth uint64 = 1 + + var reorgedBlocks = 0 + + headHeight, err := s.RpcClient.L1.BlockNumber(context.Background()) + s.Nil(err) + s.Greater(headHeight, uint64(0)) + + lastEnd := common.Big0 + + iter, err := NewBlockBatchIterator(context.Background(), &BlockBatchIteratorConfig{ + Client: s.RpcClient.L1, + MaxBlocksReadPerEpoch: &maxBlocksReadPerEpoch, + StartHeight: common.Big0, + EndHeight: new(big.Int).SetUint64(headHeight), + ReorgRewindDepth: &reorgRewindDepth, + OnReorg: func() error { + reorgedBlocks++ + return nil + }, + OnBlocks: func( + ctx context.Context, + start, end *types.Header, + updateCurrentFunc UpdateCurrentFunc, + onReorgFunc OnReorgFunc, + endIterFunc EndIterFunc, + ) error { + // reorg every 2 blocks + if end.Number.Uint64()%2 == 0 { + return onReorgFunc() + } + + s.Equal(lastEnd.Uint64(), start.Number.Uint64()) + lastEnd = end.Number + return nil + }, + }) + + s.Nil(err) + s.Nil(iter.Iter()) + s.Equal(headHeight, lastEnd.Uint64()) + s.Greater(reorgedBlocks, (headHeight/2 - 1)) +} + func TestBlockBatchIteratorTestSuite(t *testing.T) { suite.Run(t, new(BlockBatchIteratorTestSuite)) } From 2b6b00aba5ecdbe29fab7c3aed7ea62dbcf2e175 Mon Sep 17 00:00:00 2001 From: Jeffery Walsh Date: Wed, 10 May 2023 16:10:00 -0700 Subject: [PATCH 05/25] test --- pkg/chain_iterator/block_batch_iterator_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/chain_iterator/block_batch_iterator_test.go b/pkg/chain_iterator/block_batch_iterator_test.go index 39120c0df..75a819223 100644 --- a/pkg/chain_iterator/block_batch_iterator_test.go +++ b/pkg/chain_iterator/block_batch_iterator_test.go @@ -159,7 +159,7 @@ func (s *BlockBatchIteratorTestSuite) TestIter_ReorgEncounteredWithRemovedEvent( s.Nil(err) s.Nil(iter.Iter()) s.Equal(headHeight, lastEnd.Uint64()) - s.Greater(reorgedBlocks, (headHeight/2 - 1)) + s.Equal(reorgedBlocks, (headHeight/2 - 1)) } func TestBlockBatchIteratorTestSuite(t *testing.T) { From 5a9fbc6d0e449a166a83433472dffa646a701c74 Mon Sep 17 00:00:00 2001 From: Jeffery Walsh Date: Wed, 10 May 2023 16:18:50 -0700 Subject: [PATCH 06/25] reorg --- pkg/chain_iterator/block_batch_iterator_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/chain_iterator/block_batch_iterator_test.go b/pkg/chain_iterator/block_batch_iterator_test.go index 75a819223..7c5d7de9e 100644 --- a/pkg/chain_iterator/block_batch_iterator_test.go +++ b/pkg/chain_iterator/block_batch_iterator_test.go @@ -136,6 +136,7 @@ func (s *BlockBatchIteratorTestSuite) TestIter_ReorgEncounteredWithRemovedEvent( ReorgRewindDepth: &reorgRewindDepth, OnReorg: func() error { reorgedBlocks++ + lastEnd = new(big.Int).Sub(lastEnd, new(big.Int).SetUint64(reorgRewindDepth)) return nil }, OnBlocks: func( From cac931677acb2c85a3f72e7ee38fb313f968e3b0 Mon Sep 17 00:00:00 2001 From: Jeffery Walsh Date: Wed, 10 May 2023 17:32:47 -0700 Subject: [PATCH 07/25] reorg test --- Makefile | 1 + pkg/chain_iterator/block_batch_iterator.go | 4 +-- .../block_batch_iterator_test.go | 26 ++++++++++++------- 3 files changed, 19 insertions(+), 12 deletions(-) diff --git a/Makefile b/Makefile index 0af1b1c0b..eda456931 100644 --- a/Makefile +++ b/Makefile @@ -19,6 +19,7 @@ lint: test: @TAIKO_MONO_DIR=${TAIKO_MONO_DIR} \ COMPILE_PROTOCOL=${COMPILE_PROTOCOL} \ + PACKAGE=${PACKAGE} \ RUN_TESTS=true \ ./integration_test/entrypoint.sh diff --git a/pkg/chain_iterator/block_batch_iterator.go b/pkg/chain_iterator/block_batch_iterator.go index fe72e566e..a8b192365 100644 --- a/pkg/chain_iterator/block_batch_iterator.go +++ b/pkg/chain_iterator/block_batch_iterator.go @@ -227,7 +227,7 @@ func (i *BlockBatchIterator) iter() (err error) { return err } - if err := i.onBlocks(i.ctx, i.current, endHeader, i.updateCurrent, i.rewindOnReorgDetected, i.end); err != nil { + if err := i.onBlocks(i.ctx, i.current, endHeader, i.updateCurrent, i.onReorg, i.end); err != nil { return err } @@ -274,7 +274,7 @@ func (i *BlockBatchIterator) reverseIter() (err error) { return err } - if err := i.onBlocks(i.ctx, startHeader, i.current, i.updateCurrent, i.rewindOnReorgDetected, i.end); err != nil { + if err := i.onBlocks(i.ctx, startHeader, i.current, i.updateCurrent, i.onReorg, i.end); err != nil { return err } diff --git a/pkg/chain_iterator/block_batch_iterator_test.go b/pkg/chain_iterator/block_batch_iterator_test.go index 7c5d7de9e..4d0556f52 100644 --- a/pkg/chain_iterator/block_batch_iterator_test.go +++ b/pkg/chain_iterator/block_batch_iterator_test.go @@ -116,11 +116,11 @@ func (s *BlockBatchIteratorTestSuite) TestIterEndFunc() { s.Equal(lastEnd.Uint64(), maxBlocksReadPerEpoch) } -func (s *BlockBatchIteratorTestSuite) TestIter_ReorgEncounteredWithRemovedEvent() { - var maxBlocksReadPerEpoch uint64 = 2 +func (s *BlockBatchIteratorTestSuite) TestIter_ReorgEncountered() { + var maxBlocksReadPerEpoch uint64 = 1 var reorgRewindDepth uint64 = 1 - - var reorgedBlocks = 0 + var reorgedBlocks uint64 = 0 + var rewindEveryNBlocks uint64 = 2 headHeight, err := s.RpcClient.L1.BlockNumber(context.Background()) s.Nil(err) @@ -136,7 +136,11 @@ func (s *BlockBatchIteratorTestSuite) TestIter_ReorgEncounteredWithRemovedEvent( ReorgRewindDepth: &reorgRewindDepth, OnReorg: func() error { reorgedBlocks++ - lastEnd = new(big.Int).Sub(lastEnd, new(big.Int).SetUint64(reorgRewindDepth)) + if lastEnd.Uint64() < reorgRewindDepth { + lastEnd = common.Big0 + } else { + lastEnd = new(big.Int).Sub(lastEnd, new(big.Int).SetUint64(reorgRewindDepth)) + } return nil }, OnBlocks: func( @@ -146,12 +150,15 @@ func (s *BlockBatchIteratorTestSuite) TestIter_ReorgEncounteredWithRemovedEvent( onReorgFunc OnReorgFunc, endIterFunc EndIterFunc, ) error { - // reorg every 2 blocks - if end.Number.Uint64()%2 == 0 { + // reorg every 2 blocks but not the first block + if lastEnd != common.Big0 && end.Number.Uint64()%rewindEveryNBlocks == 0 { return onReorgFunc() } - s.Equal(lastEnd.Uint64(), start.Number.Uint64()) + if lastEnd.Uint64() != 0 { + s.Equal(start.Number.Uint64(), lastEnd.Uint64()+rewindEveryNBlocks) + } + lastEnd = end.Number return nil }, @@ -159,8 +166,7 @@ func (s *BlockBatchIteratorTestSuite) TestIter_ReorgEncounteredWithRemovedEvent( s.Nil(err) s.Nil(iter.Iter()) - s.Equal(headHeight, lastEnd.Uint64()) - s.Equal(reorgedBlocks, (headHeight/2 - 1)) + s.Equal((headHeight / rewindEveryNBlocks), reorgedBlocks) } func TestBlockBatchIteratorTestSuite(t *testing.T) { From d530db83e5ff600922e2317543134784c321f3a9 Mon Sep 17 00:00:00 2001 From: Jeffery Walsh Date: Wed, 10 May 2023 17:49:10 -0700 Subject: [PATCH 08/25] abort handling blocks when reorg is detected --- pkg/chain_iterator/block_batch_iterator.go | 19 +++++++++++++++---- .../block_batch_iterator_test.go | 18 +++++++++--------- .../event_iterator/block_proposed_iterator.go | 14 +++++++------- .../event_iterator/block_proven_iterator.go | 14 +++++++------- 4 files changed, 38 insertions(+), 27 deletions(-) diff --git a/pkg/chain_iterator/block_batch_iterator.go b/pkg/chain_iterator/block_batch_iterator.go index a8b192365..a7d15c311 100644 --- a/pkg/chain_iterator/block_batch_iterator.go +++ b/pkg/chain_iterator/block_batch_iterator.go @@ -24,14 +24,14 @@ var ( ) // OnBlocksFunc represents the callback function which will be called when a batch of blocks in chain are -// iterated. +// iterated. It returns true if it reorged, and false if not. type OnBlocksFunc func( ctx context.Context, start, end *types.Header, updateCurrentFunc UpdateCurrentFunc, onReorgFunc OnReorgFunc, endIterFunc EndIterFunc, -) error +) (bool, error) // UpdateCurrentFunc updates the iterator.current cursor in the iterator. type UpdateCurrentFunc func(*types.Header) @@ -227,10 +227,16 @@ func (i *BlockBatchIterator) iter() (err error) { return err } - if err := i.onBlocks(i.ctx, i.current, endHeader, i.updateCurrent, i.onReorg, i.end); err != nil { + reorged, err := i.onBlocks(i.ctx, i.current, endHeader, i.updateCurrent, i.onReorg, i.end) + if err != nil { return err } + // if we reorged, we want to skip checking if we are at the end, and also skip updating i.current + if reorged { + return nil + } + if i.isEnd { return io.EOF } @@ -274,10 +280,15 @@ func (i *BlockBatchIterator) reverseIter() (err error) { return err } - if err := i.onBlocks(i.ctx, startHeader, i.current, i.updateCurrent, i.onReorg, i.end); err != nil { + reorged, err := i.onBlocks(i.ctx, startHeader, i.current, i.updateCurrent, i.onReorg, i.end) + if err != nil { return err } + if reorged { + return nil + } + i.current = startHeader if !isLastEpoch && !i.isEnd { diff --git a/pkg/chain_iterator/block_batch_iterator_test.go b/pkg/chain_iterator/block_batch_iterator_test.go index 4d0556f52..c49f7bc6f 100644 --- a/pkg/chain_iterator/block_batch_iterator_test.go +++ b/pkg/chain_iterator/block_batch_iterator_test.go @@ -35,10 +35,10 @@ func (s *BlockBatchIteratorTestSuite) TestIter() { updateCurrentFunc UpdateCurrentFunc, onReorgFunc OnReorgFunc, endIterFunc EndIterFunc, - ) error { + ) (bool, error) { s.Equal(lastEnd.Uint64(), start.Number.Uint64()) lastEnd = end.Number - return nil + return false, nil }, }) @@ -71,10 +71,10 @@ func (s *BlockBatchIteratorTestSuite) TestIterReverse() { updateCurrentFunc UpdateCurrentFunc, onReorgFunc OnReorgFunc, endIterFunc EndIterFunc, - ) error { + ) (bool, error) { s.Equal(lastStart.Uint64(), end.Number.Uint64()) lastStart = start.Number - return nil + return false, nil }, }) @@ -103,11 +103,11 @@ func (s *BlockBatchIteratorTestSuite) TestIterEndFunc() { updateCurrentFunc UpdateCurrentFunc, onReorgFunc OnReorgFunc, endIterFunc EndIterFunc, - ) error { + ) (bool, error) { s.Equal(lastEnd.Uint64(), start.Number.Uint64()) lastEnd = end.Number endIterFunc() - return nil + return false, nil }, }) @@ -149,10 +149,10 @@ func (s *BlockBatchIteratorTestSuite) TestIter_ReorgEncountered() { updateCurrentFunc UpdateCurrentFunc, onReorgFunc OnReorgFunc, endIterFunc EndIterFunc, - ) error { + ) (bool, error) { // reorg every 2 blocks but not the first block if lastEnd != common.Big0 && end.Number.Uint64()%rewindEveryNBlocks == 0 { - return onReorgFunc() + return true, onReorgFunc() } if lastEnd.Uint64() != 0 { @@ -160,7 +160,7 @@ func (s *BlockBatchIteratorTestSuite) TestIter_ReorgEncountered() { } lastEnd = end.Number - return nil + return false, nil }, }) diff --git a/pkg/chain_iterator/event_iterator/block_proposed_iterator.go b/pkg/chain_iterator/event_iterator/block_proposed_iterator.go index 74658e3c8..ff7b2bd69 100644 --- a/pkg/chain_iterator/event_iterator/block_proposed_iterator.go +++ b/pkg/chain_iterator/event_iterator/block_proposed_iterator.go @@ -107,14 +107,14 @@ func assembleBlockProposedIteratorCallback( updateCurrentFunc chainIterator.UpdateCurrentFunc, onReorgFunc chainIterator.OnReorgFunc, endFunc chainIterator.EndIterFunc, - ) error { + ) (bool, error) { endHeight := end.Number.Uint64() iter, err := taikoL1Client.FilterBlockProposed( &bind.FilterOpts{Start: start.Number.Uint64(), End: &endHeight, Context: ctx}, filterQuery, ) if err != nil { - return err + return false, err } defer iter.Close() @@ -122,26 +122,26 @@ func assembleBlockProposedIteratorCallback( event := iter.Event if event.Raw.Removed { - return onReorgFunc() + return true, onReorgFunc() } if err := callback(ctx, event, eventIter.end); err != nil { - return err + return false, err } if eventIter.isEnd { endFunc() - return nil + return false, nil } current, err := client.HeaderByHash(ctx, event.Raw.BlockHash) if err != nil { - return err + return false, err } updateCurrentFunc(current) } - return nil + return false, nil } } diff --git a/pkg/chain_iterator/event_iterator/block_proven_iterator.go b/pkg/chain_iterator/event_iterator/block_proven_iterator.go index 8228c39e6..43b4f5f12 100644 --- a/pkg/chain_iterator/event_iterator/block_proven_iterator.go +++ b/pkg/chain_iterator/event_iterator/block_proven_iterator.go @@ -103,14 +103,14 @@ func assembleBlockProvenIteratorCallback( updateCurrentFunc chainIterator.UpdateCurrentFunc, onReorgFunc chainIterator.OnReorgFunc, endFunc chainIterator.EndIterFunc, - ) error { + ) (bool, error) { endHeight := end.Number.Uint64() iter, err := taikoL1Client.FilterBlockProven( &bind.FilterOpts{Start: start.Number.Uint64(), End: &endHeight, Context: ctx}, filterQuery, ) if err != nil { - return err + return false, err } defer iter.Close() @@ -118,26 +118,26 @@ func assembleBlockProvenIteratorCallback( event := iter.Event if event.Raw.Removed { - return onReorgFunc() + return true, onReorgFunc() } if err := callback(ctx, event, eventIter.end); err != nil { - return err + return false, err } if eventIter.isEnd { endFunc() - return nil + return false, nil } current, err := client.HeaderByHash(ctx, event.Raw.BlockHash) if err != nil { - return err + return false, err } updateCurrentFunc(current) } - return nil + return false, nil } } From 42baf918c2ff46ffe72735d2e13a035da51c7122 Mon Sep 17 00:00:00 2001 From: Jeffery Walsh Date: Wed, 10 May 2023 18:12:36 -0700 Subject: [PATCH 09/25] tests --- pkg/chain_iterator/block_batch_iterator_test.go | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/pkg/chain_iterator/block_batch_iterator_test.go b/pkg/chain_iterator/block_batch_iterator_test.go index c49f7bc6f..7a2af0d98 100644 --- a/pkg/chain_iterator/block_batch_iterator_test.go +++ b/pkg/chain_iterator/block_batch_iterator_test.go @@ -2,6 +2,7 @@ package chainiterator import ( "context" + "fmt" "math/big" "testing" @@ -121,6 +122,7 @@ func (s *BlockBatchIteratorTestSuite) TestIter_ReorgEncountered() { var reorgRewindDepth uint64 = 1 var reorgedBlocks uint64 = 0 var rewindEveryNBlocks uint64 = 2 + var lastBlockReorged bool = false headHeight, err := s.RpcClient.L1.BlockNumber(context.Background()) s.Nil(err) @@ -135,12 +137,14 @@ func (s *BlockBatchIteratorTestSuite) TestIter_ReorgEncountered() { EndHeight: new(big.Int).SetUint64(headHeight), ReorgRewindDepth: &reorgRewindDepth, OnReorg: func() error { + fmt.Println("reorging", lastEnd.Uint64()) reorgedBlocks++ if lastEnd.Uint64() < reorgRewindDepth { lastEnd = common.Big0 } else { lastEnd = new(big.Int).Sub(lastEnd, new(big.Int).SetUint64(reorgRewindDepth)) } + lastBlockReorged = true return nil }, OnBlocks: func( @@ -151,15 +155,20 @@ func (s *BlockBatchIteratorTestSuite) TestIter_ReorgEncountered() { endIterFunc EndIterFunc, ) (bool, error) { // reorg every 2 blocks but not the first block - if lastEnd != common.Big0 && end.Number.Uint64()%rewindEveryNBlocks == 0 { + if lastEnd != common.Big0 && lastEnd.Uint64()%rewindEveryNBlocks == 0 { return true, onReorgFunc() } - if lastEnd.Uint64() != 0 { - s.Equal(start.Number.Uint64(), lastEnd.Uint64()+rewindEveryNBlocks) + fmt.Println("not reorging: ", "start", start.Number.Int64(), "lastEnd", lastEnd.Uint64(), "end", end.Number.Uint64()) + + if lastBlockReorged { + s.Equal(start.Number.Uint64(), lastEnd.Uint64()+reorgRewindDepth) + } else { + } lastEnd = end.Number + lastBlockReorged = false return false, nil }, }) From 4865f1fd32329ac3e1e4435fc7d9590de3dca1f0 Mon Sep 17 00:00:00 2001 From: Jeffery Walsh Date: Wed, 10 May 2023 18:12:46 -0700 Subject: [PATCH 10/25] comment --- pkg/chain_iterator/block_batch_iterator_test.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/pkg/chain_iterator/block_batch_iterator_test.go b/pkg/chain_iterator/block_batch_iterator_test.go index 7a2af0d98..dc306a1ea 100644 --- a/pkg/chain_iterator/block_batch_iterator_test.go +++ b/pkg/chain_iterator/block_batch_iterator_test.go @@ -2,7 +2,6 @@ package chainiterator import ( "context" - "fmt" "math/big" "testing" @@ -137,7 +136,6 @@ func (s *BlockBatchIteratorTestSuite) TestIter_ReorgEncountered() { EndHeight: new(big.Int).SetUint64(headHeight), ReorgRewindDepth: &reorgRewindDepth, OnReorg: func() error { - fmt.Println("reorging", lastEnd.Uint64()) reorgedBlocks++ if lastEnd.Uint64() < reorgRewindDepth { lastEnd = common.Big0 @@ -159,8 +157,6 @@ func (s *BlockBatchIteratorTestSuite) TestIter_ReorgEncountered() { return true, onReorgFunc() } - fmt.Println("not reorging: ", "start", start.Number.Int64(), "lastEnd", lastEnd.Uint64(), "end", end.Number.Uint64()) - if lastBlockReorged { s.Equal(start.Number.Uint64(), lastEnd.Uint64()+reorgRewindDepth) } else { From 75d4919ce401690a086509f6cc9a2fcd3e796346 Mon Sep 17 00:00:00 2001 From: Jeffery Walsh Date: Wed, 10 May 2023 18:20:12 -0700 Subject: [PATCH 11/25] tests --- pkg/chain_iterator/block_batch_iterator_test.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/pkg/chain_iterator/block_batch_iterator_test.go b/pkg/chain_iterator/block_batch_iterator_test.go index dc306a1ea..ebb243cae 100644 --- a/pkg/chain_iterator/block_batch_iterator_test.go +++ b/pkg/chain_iterator/block_batch_iterator_test.go @@ -119,7 +119,6 @@ func (s *BlockBatchIteratorTestSuite) TestIterEndFunc() { func (s *BlockBatchIteratorTestSuite) TestIter_ReorgEncountered() { var maxBlocksReadPerEpoch uint64 = 1 var reorgRewindDepth uint64 = 1 - var reorgedBlocks uint64 = 0 var rewindEveryNBlocks uint64 = 2 var lastBlockReorged bool = false @@ -136,7 +135,6 @@ func (s *BlockBatchIteratorTestSuite) TestIter_ReorgEncountered() { EndHeight: new(big.Int).SetUint64(headHeight), ReorgRewindDepth: &reorgRewindDepth, OnReorg: func() error { - reorgedBlocks++ if lastEnd.Uint64() < reorgRewindDepth { lastEnd = common.Big0 } else { @@ -160,7 +158,7 @@ func (s *BlockBatchIteratorTestSuite) TestIter_ReorgEncountered() { if lastBlockReorged { s.Equal(start.Number.Uint64(), lastEnd.Uint64()+reorgRewindDepth) } else { - + s.Equal(start.Number.Uint64(), lastEnd.Uint64()) } lastEnd = end.Number @@ -171,7 +169,6 @@ func (s *BlockBatchIteratorTestSuite) TestIter_ReorgEncountered() { s.Nil(err) s.Nil(iter.Iter()) - s.Equal((headHeight / rewindEveryNBlocks), reorgedBlocks) } func TestBlockBatchIteratorTestSuite(t *testing.T) { From 882f29ffacce4a654fc37cdd93ea7928040f70d8 Mon Sep 17 00:00:00 2001 From: Jeffery Walsh Date: Thu, 11 May 2023 18:33:16 -0700 Subject: [PATCH 12/25] comments --- driver/chain_syncer/calldata/syncer.go | 117 +++++++++++++++++++++++++ 1 file changed, 117 insertions(+) diff --git a/driver/chain_syncer/calldata/syncer.go b/driver/chain_syncer/calldata/syncer.go index cc165d1ce..d7b07ef91 100644 --- a/driver/chain_syncer/calldata/syncer.go +++ b/driver/chain_syncer/calldata/syncer.go @@ -7,6 +7,7 @@ import ( "math/big" "time" + "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/beacon/engine" "github.com/ethereum/go-ethereum/common" @@ -114,8 +115,14 @@ func (s *Syncer) onBlockProposed( "L1Height", event.Raw.BlockNumber, "L1Hash", event.Raw.BlockHash, "BlockID", event.Id, + "Removed", event.Raw.Removed, ) + // handle reorg + if event.Raw.Removed { + return s.handleReorg(ctx, event) + } + // Fetch the L2 parent block. var ( parent *types.Header @@ -223,6 +230,116 @@ func (s *Syncer) onBlockProposed( return nil } +// handleReorg detects reorg and rewinds the chain by 1 until we find a block that is still in the chain, +// then inserts that chain as the new head. +func (s *Syncer) handleReorg(ctx context.Context, event *bindings.TaikoL1ClientBlockProposed) error { + log.Info( + "Reorg detected", + "L1Height", event.Raw.BlockNumber, + "L1Hash", event.Raw.BlockHash, + "BlockID", event.Id, + "Removed", event.Raw.Removed, + ) + + // rewind chain by 1 until we find a block that is still in the chain + var lastKnownGoodBlockId *big.Int + var blockId *big.Int = s.lastInsertedBlockID + + for lastKnownGoodBlockId == nil { + block, err := s.rpc.L2.BlockByNumber(ctx, blockId) + if err != nil && !errors.Is(err, ethereum.NotFound) { + return err + } + + if block != nil { + // block exists, we can rewind to this block + lastKnownGoodBlockId = blockId + } else { + // otherwise, sub 1 from blockId and try again + blockId = new(big.Int).Sub(s.lastInsertedBlockID, big.NewInt(1)) + } + } + + log.Info( + "🔗 Last known good block ID before reorg found", + "blockID", event.Id, + ) + + parent, err := s.rpc.L2ParentByBlockId(ctx, lastKnownGoodBlockId) + if err != nil { + return fmt.Errorf("error getting l2 parent by block id: %w", err) + } + + tx, err := s.rpc.L1.TransactionInBlock( + ctx, + event.Raw.BlockHash, + event.Raw.TxIndex, + ) + if err != nil { + return fmt.Errorf("failed to fetch original TaikoL1.proposeBlock transaction: %w", err) + } + + txListBytes, hint, _, err := s.txListValidator.ValidateTxList(event.Id, tx.Data()) + if err != nil { + return fmt.Errorf("failed to validate transactions list: %w", err) + } + + if hint != txListValidator.HintOK { + log.Info("Invalid transactions list, insert an empty L2 block instead", "blockID", event.Id) + txListBytes = []byte{} + } + + l1Origin := &rawdb.L1Origin{ + BlockID: event.Id, + L2BlockHash: common.Hash{}, // Will be set by taiko-geth. + L1BlockHeight: new(big.Int).SetUint64(event.Raw.BlockNumber), + L1BlockHash: event.Raw.BlockHash, + } + + payloadData, rpcError, payloadError := s.insertNewHead( + ctx, + event, + parent, + s.state.GetHeadBlockID(), + txListBytes, + l1Origin, + ) + + if rpcError != nil { + return fmt.Errorf("failed to insert new head to L2 execution engine: %w", rpcError) + } + + if payloadError != nil { + log.Warn( + "Ignore invalid block context", "blockID", event.Id, "payloadError", payloadError, "payloadData", payloadData, + ) + return nil + } + + log.Debug("Payload data", "hash", payloadData.BlockHash, "txs", len(payloadData.Transactions)) + + log.Info( + "🔗 Rewound chain and inserted last known good block as new head", + "blockID", event.Id, + "height", payloadData.Number, + "hash", payloadData.BlockHash, + "latestVerifiedBlockHeight", s.state.GetLatestVerifiedBlock().Height, + "latestVerifiedBlockHash", s.state.GetLatestVerifiedBlock().Hash, + "transactions", len(payloadData.Transactions), + "baseFee", payloadData.BaseFeePerGas, + "withdrawals", len(payloadData.Withdrawals), + ) + + metrics.DriverL1CurrentHeightGauge.Update(int64(event.Raw.BlockNumber)) + s.lastInsertedBlockID = event.Id + + if s.progressTracker.Triggered() { + s.progressTracker.ClearMeta() + } + + return nil +} + // insertNewHead tries to insert a new head block to the L2 execution engine's local // block chain through Engine APIs. func (s *Syncer) insertNewHead( From 16711a3568ab0173f5009a76ef04144eaf3b2a86 Mon Sep 17 00:00:00 2001 From: Jeffery Walsh Date: Thu, 11 May 2023 18:34:00 -0700 Subject: [PATCH 13/25] detect if block id is 0 --- driver/chain_syncer/calldata/syncer.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/driver/chain_syncer/calldata/syncer.go b/driver/chain_syncer/calldata/syncer.go index d7b07ef91..3a250cee1 100644 --- a/driver/chain_syncer/calldata/syncer.go +++ b/driver/chain_syncer/calldata/syncer.go @@ -246,6 +246,11 @@ func (s *Syncer) handleReorg(ctx context.Context, event *bindings.TaikoL1ClientB var blockId *big.Int = s.lastInsertedBlockID for lastKnownGoodBlockId == nil { + if blockId.Cmp(big.NewInt(0)) == 0 { + lastKnownGoodBlockId = new(big.Int).SetUint64(0) + break + } + block, err := s.rpc.L2.BlockByNumber(ctx, blockId) if err != nil && !errors.Is(err, ethereum.NotFound) { return err From 6e76e34d671c666158f08c754c6b2b2c74967093 Mon Sep 17 00:00:00 2001 From: Jeffery Walsh Date: Thu, 11 May 2023 19:44:05 -0700 Subject: [PATCH 14/25] reset l1 current + get event --- driver/chain_syncer/calldata/syncer.go | 37 ++++++++++++++++++-------- driver/chain_syncer/chain_syncer.go | 2 +- driver/state/l1_current.go | 36 ++++++++++++------------- 3 files changed, 45 insertions(+), 30 deletions(-) diff --git a/driver/chain_syncer/calldata/syncer.go b/driver/chain_syncer/calldata/syncer.go index 3a250cee1..ef56fac14 100644 --- a/driver/chain_syncer/calldata/syncer.go +++ b/driver/chain_syncer/calldata/syncer.go @@ -244,14 +244,16 @@ func (s *Syncer) handleReorg(ctx context.Context, event *bindings.TaikoL1ClientB // rewind chain by 1 until we find a block that is still in the chain var lastKnownGoodBlockId *big.Int var blockId *big.Int = s.lastInsertedBlockID + var block *types.Block + var err error - for lastKnownGoodBlockId == nil { + for { if blockId.Cmp(big.NewInt(0)) == 0 { lastKnownGoodBlockId = new(big.Int).SetUint64(0) break } - block, err := s.rpc.L2.BlockByNumber(ctx, blockId) + block, err = s.rpc.L2.BlockByNumber(ctx, blockId) if err != nil && !errors.Is(err, ethereum.NotFound) { return err } @@ -259,12 +261,19 @@ func (s *Syncer) handleReorg(ctx context.Context, event *bindings.TaikoL1ClientB if block != nil { // block exists, we can rewind to this block lastKnownGoodBlockId = blockId + break } else { // otherwise, sub 1 from blockId and try again blockId = new(big.Int).Sub(s.lastInsertedBlockID, big.NewInt(1)) } } + // shouuldnt be able to reach this error because of the 0 check above + // but just in case + if lastKnownGoodBlockId == nil { + return fmt.Errorf("failed to find last known good block ID after reorg") + } + log.Info( "🔗 Last known good block ID before reorg found", "blockID", event.Id, @@ -275,35 +284,41 @@ func (s *Syncer) handleReorg(ctx context.Context, event *bindings.TaikoL1ClientB return fmt.Errorf("error getting l2 parent by block id: %w", err) } + // reset l1 current to when the last known good block was inserted, and return the event. + blockProposedEvent, _, err := s.state.ResetL1Current(ctx, &state.HeightOrID{Height: block.Number()}) + if err != nil { + return fmt.Errorf("faile to reset l1 current: %w", err) + } + tx, err := s.rpc.L1.TransactionInBlock( ctx, - event.Raw.BlockHash, - event.Raw.TxIndex, + block.Hash(), + blockProposedEvent.Raw.TxIndex, ) if err != nil { return fmt.Errorf("failed to fetch original TaikoL1.proposeBlock transaction: %w", err) } - txListBytes, hint, _, err := s.txListValidator.ValidateTxList(event.Id, tx.Data()) + txListBytes, hint, _, err := s.txListValidator.ValidateTxList(block.Number(), tx.Data()) if err != nil { return fmt.Errorf("failed to validate transactions list: %w", err) } if hint != txListValidator.HintOK { - log.Info("Invalid transactions list, insert an empty L2 block instead", "blockID", event.Id) + log.Info("Invalid transactions list, insert an empty L2 block instead", "blockID", block.NumberU64()) txListBytes = []byte{} } l1Origin := &rawdb.L1Origin{ - BlockID: event.Id, + BlockID: block.Number(), L2BlockHash: common.Hash{}, // Will be set by taiko-geth. - L1BlockHeight: new(big.Int).SetUint64(event.Raw.BlockNumber), - L1BlockHash: event.Raw.BlockHash, + L1BlockHeight: new(big.Int).SetUint64(blockProposedEvent.Raw.BlockNumber), + L1BlockHash: blockProposedEvent.Raw.BlockHash, } payloadData, rpcError, payloadError := s.insertNewHead( ctx, - event, + blockProposedEvent, parent, s.state.GetHeadBlockID(), txListBytes, @@ -336,7 +351,7 @@ func (s *Syncer) handleReorg(ctx context.Context, event *bindings.TaikoL1ClientB ) metrics.DriverL1CurrentHeightGauge.Update(int64(event.Raw.BlockNumber)) - s.lastInsertedBlockID = event.Id + s.lastInsertedBlockID = block.Number() if s.progressTracker.Triggered() { s.progressTracker.ClearMeta() diff --git a/driver/chain_syncer/chain_syncer.go b/driver/chain_syncer/chain_syncer.go index 268198970..798299433 100644 --- a/driver/chain_syncer/chain_syncer.go +++ b/driver/chain_syncer/chain_syncer.go @@ -119,7 +119,7 @@ func (s *L2ChainSyncer) Sync(l1End *types.Header) error { } // Reset the L1Current cursor. - blockID, err := s.state.ResetL1Current(s.ctx, heightOrID) + _, blockID, err := s.state.ResetL1Current(s.ctx, heightOrID) if err != nil { return err } diff --git a/driver/state/l1_current.go b/driver/state/l1_current.go index 5cf3f7956..b0a501790 100644 --- a/driver/state/l1_current.go +++ b/driver/state/l1_current.go @@ -29,33 +29,32 @@ func (s *State) SetL1Current(h *types.Header) { // ResetL1Current resets the l1Current cursor to the L1 height which emitted a // BlockProven event with given blockID / blockHash. -func (s *State) ResetL1Current(ctx context.Context, heightOrID *HeightOrID) (*big.Int, error) { +func (s *State) ResetL1Current(ctx context.Context, heightOrID *HeightOrID) (*bindings.TaikoL1ClientBlockProposed, *big.Int, error) { if !heightOrID.NotEmpty() { - return nil, fmt.Errorf("empty input %v", heightOrID) + return nil, nil, fmt.Errorf("empty input %v", heightOrID) } log.Info("Reset L1 current cursor", "heightOrID", heightOrID) var ( - l1CurrentHeight *big.Int - err error + err error ) if (heightOrID.ID != nil && heightOrID.ID.Cmp(common.Big0) == 0) || (heightOrID.Height != nil && heightOrID.Height.Cmp(common.Big0) == 0) { l1Current, err := s.rpc.L1.HeaderByNumber(ctx, s.GenesisL1Height) if err != nil { - return nil, err + return nil, nil, err } s.SetL1Current(l1Current) - return common.Big0, nil + return nil, common.Big0, nil } // Need to find the block ID at first, before filtering the BlockProposed events. if heightOrID.ID == nil { header, err := s.rpc.L2.HeaderByNumber(context.Background(), heightOrID.Height) if err != nil { - return nil, err + return nil, nil, err } targetHash := header.Hash() @@ -85,18 +84,19 @@ func (s *State) ResetL1Current(ctx context.Context, heightOrID *HeightOrID) (*bi ) if err != nil { - return nil, err + return nil, nil, err } if err := iter.Iter(); err != nil { - return nil, err + return nil, nil, err } if heightOrID.ID == nil { - return nil, fmt.Errorf("BlockProven event not found, hash: %s", targetHash) + return nil, nil, fmt.Errorf("BlockProven event not found, hash: %s", targetHash) } } + var event *bindings.TaikoL1ClientBlockProposed iter, err := eventIterator.NewBlockProposedIterator( ctx, &eventIterator.BlockProposedIteratorConfig{ @@ -111,7 +111,7 @@ func (s *State) ResetL1Current(ctx context.Context, heightOrID *HeightOrID) (*bi e *bindings.TaikoL1ClientBlockProposed, end eventIterator.EndBlockProposedEventIterFunc, ) error { - l1CurrentHeight = new(big.Int).SetUint64(e.Raw.BlockNumber) + event = e end() return nil }, @@ -119,24 +119,24 @@ func (s *State) ResetL1Current(ctx context.Context, heightOrID *HeightOrID) (*bi ) if err != nil { - return nil, err + return nil, nil, err } if err := iter.Iter(); err != nil { - return nil, err + return nil, nil, err } - if l1CurrentHeight == nil { - return nil, fmt.Errorf("BlockProposed event not found, blockID: %s", heightOrID.ID) + if event == nil { + return nil, nil, fmt.Errorf("BlockProposed event not found, blockID: %s", heightOrID.ID) } - l1Current, err := s.rpc.L1.HeaderByNumber(ctx, l1CurrentHeight) + l1Current, err := s.rpc.L1.HeaderByNumber(ctx, new(big.Int).SetUint64(event.Raw.BlockNumber)) if err != nil { - return nil, err + return nil, nil, err } s.SetL1Current(l1Current) log.Info("Reset L1 current cursor", "height", s.GetL1Current().Number) - return heightOrID.ID, nil + return event, heightOrID.ID, nil } From 2d4f88c163c18080e8403daa73d6799e4b3af27c Mon Sep 17 00:00:00 2001 From: Jeffery Walsh Date: Thu, 11 May 2023 19:44:31 -0700 Subject: [PATCH 15/25] tests --- driver/state/l1_current_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/driver/state/l1_current_test.go b/driver/state/l1_current_test.go index 31367cb3e..86b26b60d 100644 --- a/driver/state/l1_current_test.go +++ b/driver/state/l1_current_test.go @@ -19,15 +19,15 @@ func (s *DriverStateTestSuite) TestSetL1Current() { } func (s *DriverStateTestSuite) TestResetL1CurrentEmptyHeight() { - l1Current, err := s.s.ResetL1Current(context.Background(), &HeightOrID{ID: common.Big0}) + _, l1Current, err := s.s.ResetL1Current(context.Background(), &HeightOrID{ID: common.Big0}) s.Nil(err) s.Zero(l1Current.Uint64()) - _, err = s.s.ResetL1Current(context.Background(), &HeightOrID{Height: common.Big0}) + _, _, err = s.s.ResetL1Current(context.Background(), &HeightOrID{Height: common.Big0}) s.Nil(err) } func (s *DriverStateTestSuite) TestResetL1CurrentEmptyID() { - _, err := s.s.ResetL1Current(context.Background(), &HeightOrID{Height: common.Big1}) + _, _, err := s.s.ResetL1Current(context.Background(), &HeightOrID{Height: common.Big1}) s.NotNil(err) } From bd8b1c93e5c91788716e7a39729be0078cfeae23 Mon Sep 17 00:00:00 2001 From: Jeffery Walsh Date: Thu, 11 May 2023 19:56:27 -0700 Subject: [PATCH 16/25] lint --- driver/state/l1_current.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/driver/state/l1_current.go b/driver/state/l1_current.go index b0a501790..9fa016801 100644 --- a/driver/state/l1_current.go +++ b/driver/state/l1_current.go @@ -29,7 +29,10 @@ func (s *State) SetL1Current(h *types.Header) { // ResetL1Current resets the l1Current cursor to the L1 height which emitted a // BlockProven event with given blockID / blockHash. -func (s *State) ResetL1Current(ctx context.Context, heightOrID *HeightOrID) (*bindings.TaikoL1ClientBlockProposed, *big.Int, error) { +func (s *State) ResetL1Current( + ctx context.Context, + heightOrID *HeightOrID, +) (*bindings.TaikoL1ClientBlockProposed, *big.Int, error) { if !heightOrID.NotEmpty() { return nil, nil, fmt.Errorf("empty input %v", heightOrID) } From 5910819c19143a54c298d4143908ef455e5e6bda Mon Sep 17 00:00:00 2001 From: Jeffery Walsh Date: Thu, 11 May 2023 19:57:47 -0700 Subject: [PATCH 17/25] comment was incorrect, i belive ResetL1Current is for blockProposed, not blockProven --- driver/state/l1_current.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/driver/state/l1_current.go b/driver/state/l1_current.go index 9fa016801..e9eb90f94 100644 --- a/driver/state/l1_current.go +++ b/driver/state/l1_current.go @@ -28,7 +28,7 @@ func (s *State) SetL1Current(h *types.Header) { } // ResetL1Current resets the l1Current cursor to the L1 height which emitted a -// BlockProven event with given blockID / blockHash. +// BlockProposed event with given blockID / blockHash. func (s *State) ResetL1Current( ctx context.Context, heightOrID *HeightOrID, From d9db02d420333a0dc58adcfe8c2baaede4c71b6a Mon Sep 17 00:00:00 2001 From: jeff <113397187+cyberhorsey@users.noreply.github.com> Date: Mon, 15 May 2023 11:14:42 -0700 Subject: [PATCH 18/25] Update driver/chain_syncer/calldata/syncer.go Co-authored-by: David --- driver/chain_syncer/calldata/syncer.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/driver/chain_syncer/calldata/syncer.go b/driver/chain_syncer/calldata/syncer.go index ef56fac14..e1ebec2a7 100644 --- a/driver/chain_syncer/calldata/syncer.go +++ b/driver/chain_syncer/calldata/syncer.go @@ -231,7 +231,7 @@ func (s *Syncer) onBlockProposed( } // handleReorg detects reorg and rewinds the chain by 1 until we find a block that is still in the chain, -// then inserts that chain as the new head. +// then inserts that block as the new head. func (s *Syncer) handleReorg(ctx context.Context, event *bindings.TaikoL1ClientBlockProposed) error { log.Info( "Reorg detected", From e63464491d342657453d37b8533c2e8ac4e41107 Mon Sep 17 00:00:00 2001 From: jeff <113397187+cyberhorsey@users.noreply.github.com> Date: Mon, 15 May 2023 11:14:50 -0700 Subject: [PATCH 19/25] Update driver/chain_syncer/calldata/syncer.go Co-authored-by: David --- driver/chain_syncer/calldata/syncer.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/driver/chain_syncer/calldata/syncer.go b/driver/chain_syncer/calldata/syncer.go index e1ebec2a7..36f71ca5b 100644 --- a/driver/chain_syncer/calldata/syncer.go +++ b/driver/chain_syncer/calldata/syncer.go @@ -268,7 +268,7 @@ func (s *Syncer) handleReorg(ctx context.Context, event *bindings.TaikoL1ClientB } } - // shouuldnt be able to reach this error because of the 0 check above + // shouldn't be able to reach this error because of the 0 check above // but just in case if lastKnownGoodBlockId == nil { return fmt.Errorf("failed to find last known good block ID after reorg") From b00ac3941b420de3d2d8b6f671ae9bb9a8c1cd54 Mon Sep 17 00:00:00 2001 From: jeff <113397187+cyberhorsey@users.noreply.github.com> Date: Mon, 15 May 2023 11:15:01 -0700 Subject: [PATCH 20/25] Update driver/chain_syncer/calldata/syncer.go Co-authored-by: David --- driver/chain_syncer/calldata/syncer.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/driver/chain_syncer/calldata/syncer.go b/driver/chain_syncer/calldata/syncer.go index 36f71ca5b..0140014a2 100644 --- a/driver/chain_syncer/calldata/syncer.go +++ b/driver/chain_syncer/calldata/syncer.go @@ -287,7 +287,7 @@ func (s *Syncer) handleReorg(ctx context.Context, event *bindings.TaikoL1ClientB // reset l1 current to when the last known good block was inserted, and return the event. blockProposedEvent, _, err := s.state.ResetL1Current(ctx, &state.HeightOrID{Height: block.Number()}) if err != nil { - return fmt.Errorf("faile to reset l1 current: %w", err) + return fmt.Errorf("failed to reset l1 current: %w", err) } tx, err := s.rpc.L1.TransactionInBlock( From 092c717d178a07b0bb28d81bd3ca65a739bc336b Mon Sep 17 00:00:00 2001 From: Jeffery Walsh Date: Mon, 15 May 2023 12:05:57 -0700 Subject: [PATCH 21/25] compare to state vars num blocks, and handle 0 block for rewinding --- driver/chain_syncer/calldata/syncer.go | 69 +++++++++++++++------ driver/chain_syncer/calldata/syncer_test.go | 6 +- 2 files changed, 56 insertions(+), 19 deletions(-) diff --git a/driver/chain_syncer/calldata/syncer.go b/driver/chain_syncer/calldata/syncer.go index 0140014a2..7a08493b9 100644 --- a/driver/chain_syncer/calldata/syncer.go +++ b/driver/chain_syncer/calldata/syncer.go @@ -26,6 +26,14 @@ import ( txListValidator "github.com/taikoxyz/taiko-client/pkg/tx_list_validator" ) +// ParentBlockInfo is an abstraction between *types.Header and our code, to allow passing in +// a zero hash and other zero-value fields. +type ParentBlockInfo struct { + Hash common.Hash + Number *big.Int + GasUsed uint64 +} + // Syncer responsible for letting the L2 execution engine catching up with protocol's latest // pending block through deriving L1 calldata. type Syncer struct { @@ -188,7 +196,11 @@ func (s *Syncer) onBlockProposed( payloadData, rpcError, payloadError := s.insertNewHead( ctx, event, - parent, + &ParentBlockInfo{ + Hash: parent.Hash(), + Number: parent.Number, + GasUsed: parent.GasUsed, + }, s.state.GetHeadBlockID(), txListBytes, l1Origin, @@ -247,6 +259,11 @@ func (s *Syncer) handleReorg(ctx context.Context, event *bindings.TaikoL1ClientB var block *types.Block var err error + stateVars, err := s.rpc.GetProtocolStateVariables() + if err != nil { + return fmt.Errorf("failed to get state variables: %w", err) + } + for { if blockId.Cmp(big.NewInt(0)) == 0 { lastKnownGoodBlockId = new(big.Int).SetUint64(0) @@ -258,7 +275,7 @@ func (s *Syncer) handleReorg(ctx context.Context, event *bindings.TaikoL1ClientB return err } - if block != nil { + if block != nil && blockId.Uint64() < stateVars.NumBlocks { // block exists, we can rewind to this block lastKnownGoodBlockId = blockId break @@ -276,12 +293,28 @@ func (s *Syncer) handleReorg(ctx context.Context, event *bindings.TaikoL1ClientB log.Info( "🔗 Last known good block ID before reorg found", - "blockID", event.Id, + "blockID", lastKnownGoodBlockId, ) - parent, err := s.rpc.L2ParentByBlockId(ctx, lastKnownGoodBlockId) - if err != nil { - return fmt.Errorf("error getting l2 parent by block id: %w", err) + var parentBlockInfo *ParentBlockInfo + + if lastKnownGoodBlockId.Cmp(common.Big0) == 0 { + parentBlockInfo = &ParentBlockInfo{ + Hash: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000000"), + Number: big.NewInt(0), + GasUsed: 0, + } + } else { + parent, err := s.rpc.L2ParentByBlockId(ctx, lastKnownGoodBlockId) + if err != nil { + return fmt.Errorf("error getting l2 parent by block id: %w", err) + } + + parentBlockInfo = &ParentBlockInfo{ + Hash: parent.Hash(), + Number: parent.Number, + GasUsed: parent.GasUsed, + } } // reset l1 current to when the last known good block was inserted, and return the event. @@ -319,7 +352,7 @@ func (s *Syncer) handleReorg(ctx context.Context, event *bindings.TaikoL1ClientB payloadData, rpcError, payloadError := s.insertNewHead( ctx, blockProposedEvent, - parent, + parentBlockInfo, s.state.GetHeadBlockID(), txListBytes, l1Origin, @@ -365,15 +398,15 @@ func (s *Syncer) handleReorg(ctx context.Context, event *bindings.TaikoL1ClientB func (s *Syncer) insertNewHead( ctx context.Context, event *bindings.TaikoL1ClientBlockProposed, - parent *types.Header, + parentBlockInfo *ParentBlockInfo, headBlockID *big.Int, txListBytes []byte, l1Origin *rawdb.L1Origin, ) (*engine.ExecutableData, error, error) { log.Debug( "Try to insert a new L2 head block", - "parentNumber", parent.Number, - "parentHash", parent.Hash(), + "parentNumber", parentBlockInfo.Number, + "parentHash", parentBlockInfo.Hash, "headBlockID", headBlockID, "l1Origin", l1Origin, ) @@ -387,17 +420,17 @@ func (s *Syncer) insertNewHead( } } - parentTimestamp, err := s.rpc.TaikoL2.ParentTimestamp(&bind.CallOpts{BlockNumber: parent.Number}) + parentTimestamp, err := s.rpc.TaikoL2.ParentTimestamp(&bind.CallOpts{BlockNumber: parentBlockInfo.Number}) if err != nil { return nil, nil, err } // Get L2 baseFee baseFee, err := s.rpc.TaikoL2.GetBasefee( - &bind.CallOpts{BlockNumber: parent.Number}, + &bind.CallOpts{BlockNumber: parentBlockInfo.Number}, uint32(event.Meta.Timestamp-parentTimestamp), uint64(event.Meta.GasLimit+uint32(s.anchorConstructor.GasLimit())), - parent.GasUsed, + parentBlockInfo.GasUsed, ) if err != nil { return nil, nil, fmt.Errorf("failed to get L2 baseFee: %w", encoding.TryParsingCustomError(err)) @@ -408,7 +441,7 @@ func (s *Syncer) insertNewHead( "baseFee", baseFee, "timeSinceParent", uint32(event.Meta.Timestamp-parentTimestamp), "gasLimit", uint64(event.Meta.GasLimit+uint32(s.anchorConstructor.GasLimit())), - "parentGasUsed", parent.GasUsed, + "parentGasUsed", parentBlockInfo.GasUsed, ) // Get withdrawals @@ -422,9 +455,9 @@ func (s *Syncer) insertNewHead( ctx, new(big.Int).SetUint64(event.Meta.L1Height), event.Meta.L1Hash, - new(big.Int).Add(parent.Number, common.Big1), + new(big.Int).Add(parentBlockInfo.Number, common.Big1), baseFee, - parent.GasUsed, + parentBlockInfo.GasUsed, ) if err != nil { return nil, nil, fmt.Errorf("failed to create TaikoL2.anchor transaction: %w", err) @@ -440,7 +473,7 @@ func (s *Syncer) insertNewHead( payload, rpcErr, payloadErr := s.createExecutionPayloads( ctx, event, - parent.Hash(), + parentBlockInfo.Hash, l1Origin, headBlockID, txListBytes, @@ -452,7 +485,7 @@ func (s *Syncer) insertNewHead( return nil, rpcErr, payloadErr } - fc := &engine.ForkchoiceStateV1{HeadBlockHash: parent.Hash()} + fc := &engine.ForkchoiceStateV1{HeadBlockHash: parentBlockInfo.Hash} // Update the fork choice fc.HeadBlockHash = payload.BlockHash diff --git a/driver/chain_syncer/calldata/syncer_test.go b/driver/chain_syncer/calldata/syncer_test.go index cd5aed1ef..daf007bfd 100644 --- a/driver/chain_syncer/calldata/syncer_test.go +++ b/driver/chain_syncer/calldata/syncer_test.go @@ -69,7 +69,11 @@ func (s *CalldataSyncerTestSuite) TestInsertNewHead() { Timestamp: uint64(time.Now().Unix()), }, }, - parent, + &ParentBlockInfo{ + Hash: parent.Hash(), + Number: parent.Number, + GasUsed: parent.GasUsed, + }, common.Big2, []byte{}, &rawdb.L1Origin{ From dc0ebb6842df1fe7fa44018d09cbdc015a9b5103 Mon Sep 17 00:00:00 2001 From: Jeffery Walsh Date: Mon, 15 May 2023 12:33:04 -0700 Subject: [PATCH 22/25] typo --- driver/chain_syncer/calldata/syncer.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/driver/chain_syncer/calldata/syncer.go b/driver/chain_syncer/calldata/syncer.go index 7a08493b9..799a8a025 100644 --- a/driver/chain_syncer/calldata/syncer.go +++ b/driver/chain_syncer/calldata/syncer.go @@ -259,7 +259,7 @@ func (s *Syncer) handleReorg(ctx context.Context, event *bindings.TaikoL1ClientB var block *types.Block var err error - stateVars, err := s.rpc.GetProtocolStateVariables() + stateVars, err := s.rpc.GetProtocolStateVariables(nil) if err != nil { return fmt.Errorf("failed to get state variables: %w", err) } From 83fb81ab4914e0d127a41fe29bf9875de1fd6c56 Mon Sep 17 00:00:00 2001 From: David Date: Wed, 17 May 2023 01:40:38 +0800 Subject: [PATCH 23/25] feat: update some logic and add more tests based on #216 (#228) --- driver/chain_syncer/calldata/syncer.go | 114 ++++++-------------- driver/chain_syncer/calldata/syncer_test.go | 64 +++++++++++ integration_test/nodes/docker-compose.yml | 2 +- 3 files changed, 96 insertions(+), 84 deletions(-) diff --git a/driver/chain_syncer/calldata/syncer.go b/driver/chain_syncer/calldata/syncer.go index 799a8a025..7b91e57ec 100644 --- a/driver/chain_syncer/calldata/syncer.go +++ b/driver/chain_syncer/calldata/syncer.go @@ -254,10 +254,18 @@ func (s *Syncer) handleReorg(ctx context.Context, event *bindings.TaikoL1ClientB ) // rewind chain by 1 until we find a block that is still in the chain - var lastKnownGoodBlockId *big.Int - var blockId *big.Int = s.lastInsertedBlockID - var block *types.Block - var err error + var ( + lastKnownGoodBlockId *big.Int + blockId *big.Int + block *types.Block + err error + ) + + l2Head, err := s.rpc.L2.BlockByNumber(ctx, nil) + if err != nil { + return err + } + blockId = l2Head.Number() stateVars, err := s.rpc.GetProtocolStateVariables(nil) if err != nil { @@ -265,13 +273,15 @@ func (s *Syncer) handleReorg(ctx context.Context, event *bindings.TaikoL1ClientB } for { - if blockId.Cmp(big.NewInt(0)) == 0 { - lastKnownGoodBlockId = new(big.Int).SetUint64(0) + if blockId.Cmp(common.Big0) == 0 { + if block, err = s.rpc.L2.BlockByNumber(ctx, common.Big0); err != nil { + return err + } + lastKnownGoodBlockId = common.Big0 break } - block, err = s.rpc.L2.BlockByNumber(ctx, blockId) - if err != nil && !errors.Is(err, ethereum.NotFound) { + if block, err = s.rpc.L2.BlockByNumber(ctx, blockId); err != nil && !errors.Is(err, ethereum.NotFound) { return err } @@ -281,7 +291,7 @@ func (s *Syncer) handleReorg(ctx context.Context, event *bindings.TaikoL1ClientB break } else { // otherwise, sub 1 from blockId and try again - blockId = new(big.Int).Sub(s.lastInsertedBlockID, big.NewInt(1)) + blockId = new(big.Int).Sub(blockId, common.Big1) } } @@ -296,91 +306,29 @@ func (s *Syncer) handleReorg(ctx context.Context, event *bindings.TaikoL1ClientB "blockID", lastKnownGoodBlockId, ) - var parentBlockInfo *ParentBlockInfo - - if lastKnownGoodBlockId.Cmp(common.Big0) == 0 { - parentBlockInfo = &ParentBlockInfo{ - Hash: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000000"), - Number: big.NewInt(0), - GasUsed: 0, - } - } else { - parent, err := s.rpc.L2ParentByBlockId(ctx, lastKnownGoodBlockId) - if err != nil { - return fmt.Errorf("error getting l2 parent by block id: %w", err) - } - - parentBlockInfo = &ParentBlockInfo{ - Hash: parent.Hash(), - Number: parent.Number, - GasUsed: parent.GasUsed, - } - } - - // reset l1 current to when the last known good block was inserted, and return the event. - blockProposedEvent, _, err := s.state.ResetL1Current(ctx, &state.HeightOrID{Height: block.Number()}) - if err != nil { - return fmt.Errorf("failed to reset l1 current: %w", err) - } - - tx, err := s.rpc.L1.TransactionInBlock( - ctx, - block.Hash(), - blockProposedEvent.Raw.TxIndex, - ) + fcRes, err := s.rpc.L2Engine.ForkchoiceUpdate(ctx, &engine.ForkchoiceStateV1{HeadBlockHash: block.Hash()}, nil) if err != nil { - return fmt.Errorf("failed to fetch original TaikoL1.proposeBlock transaction: %w", err) - } - - txListBytes, hint, _, err := s.txListValidator.ValidateTxList(block.Number(), tx.Data()) - if err != nil { - return fmt.Errorf("failed to validate transactions list: %w", err) - } - - if hint != txListValidator.HintOK { - log.Info("Invalid transactions list, insert an empty L2 block instead", "blockID", block.NumberU64()) - txListBytes = []byte{} - } - - l1Origin := &rawdb.L1Origin{ - BlockID: block.Number(), - L2BlockHash: common.Hash{}, // Will be set by taiko-geth. - L1BlockHeight: new(big.Int).SetUint64(blockProposedEvent.Raw.BlockNumber), - L1BlockHash: blockProposedEvent.Raw.BlockHash, + return err } - - payloadData, rpcError, payloadError := s.insertNewHead( - ctx, - blockProposedEvent, - parentBlockInfo, - s.state.GetHeadBlockID(), - txListBytes, - l1Origin, - ) - - if rpcError != nil { - return fmt.Errorf("failed to insert new head to L2 execution engine: %w", rpcError) + if fcRes.PayloadStatus.Status != engine.VALID { + return fmt.Errorf("unexpected ForkchoiceUpdate response status: %s", fcRes.PayloadStatus.Status) } - if payloadError != nil { - log.Warn( - "Ignore invalid block context", "blockID", event.Id, "payloadError", payloadError, "payloadData", payloadData, - ) - return nil + // reset l1 current to when the last known good block was inserted, and return the event. + if _, _, err := s.state.ResetL1Current(ctx, &state.HeightOrID{ID: lastKnownGoodBlockId}); err != nil { + return fmt.Errorf("failed to reset L1 current: %w", err) } - log.Debug("Payload data", "hash", payloadData.BlockHash, "txs", len(payloadData.Transactions)) - log.Info( "🔗 Rewound chain and inserted last known good block as new head", "blockID", event.Id, - "height", payloadData.Number, - "hash", payloadData.BlockHash, + "height", block.Number(), + "hash", block.Hash(), "latestVerifiedBlockHeight", s.state.GetLatestVerifiedBlock().Height, "latestVerifiedBlockHash", s.state.GetLatestVerifiedBlock().Hash, - "transactions", len(payloadData.Transactions), - "baseFee", payloadData.BaseFeePerGas, - "withdrawals", len(payloadData.Withdrawals), + "transactions", len(block.Transactions()), + "baseFee", block.BaseFee(), + "withdrawals", len(block.Withdrawals()), ) metrics.DriverL1CurrentHeightGauge.Update(int64(event.Raw.BlockNumber)) diff --git a/driver/chain_syncer/calldata/syncer_test.go b/driver/chain_syncer/calldata/syncer_test.go index daf007bfd..530889f78 100644 --- a/driver/chain_syncer/calldata/syncer_test.go +++ b/driver/chain_syncer/calldata/syncer_test.go @@ -9,16 +9,20 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" "github.com/stretchr/testify/suite" "github.com/taikoxyz/taiko-client/bindings" "github.com/taikoxyz/taiko-client/driver/chain_syncer/beaconsync" "github.com/taikoxyz/taiko-client/driver/state" + "github.com/taikoxyz/taiko-client/proposer" "github.com/taikoxyz/taiko-client/testutils" ) type CalldataSyncerTestSuite struct { testutils.ClientTestSuite s *Syncer + p testutils.Proposer } func (s *CalldataSyncerTestSuite) SetupTest() { @@ -36,6 +40,22 @@ func (s *CalldataSyncerTestSuite) SetupTest() { ) s.Nil(err) s.s = syncer + + prop := new(proposer.Proposer) + l1ProposerPrivKey, err := crypto.ToECDSA(common.Hex2Bytes(os.Getenv("L1_PROPOSER_PRIVATE_KEY"))) + s.Nil(err) + proposeInterval := 1024 * time.Hour // No need to periodically propose transactions list in unit tests + s.Nil(proposer.InitFromConfig(context.Background(), prop, (&proposer.Config{ + L1Endpoint: os.Getenv("L1_NODE_WS_ENDPOINT"), + L2Endpoint: os.Getenv("L2_EXECUTION_ENGINE_WS_ENDPOINT"), + TaikoL1Address: common.HexToAddress(os.Getenv("TAIKO_L1_ADDRESS")), + TaikoL2Address: common.HexToAddress(os.Getenv("TAIKO_L2_ADDRESS")), + L1ProposerPrivKey: l1ProposerPrivKey, + L2SuggestedFeeRecipient: common.HexToAddress(os.Getenv("L2_SUGGESTED_FEE_RECIPIENT")), + ProposeInterval: &proposeInterval, + }))) + + s.p = prop } func (s *CalldataSyncerTestSuite) TestProcessL1Blocks() { @@ -86,6 +106,50 @@ func (s *CalldataSyncerTestSuite) TestInsertNewHead() { s.Nil(payloadErr) } +func (s *CalldataSyncerTestSuite) TestHandleReorgToGenesis() { + testutils.ProposeAndInsertEmptyBlocks(&s.ClientTestSuite, s.p, s.s) + + l2Head1, err := s.s.rpc.L2.BlockByNumber(context.Background(), nil) + s.Nil(err) + s.Greater(l2Head1.NumberU64(), uint64(0)) + s.NotZero(s.s.lastInsertedBlockID.Uint64()) + s.s.lastInsertedBlockID = common.Big0 // let the chain reorg to genesis + + s.Nil(s.s.handleReorg(context.Background(), &bindings.TaikoL1ClientBlockProposed{ + Id: l2Head1.Number(), + Raw: types.Log{Removed: true}, + })) + + l2Head2, err := s.s.rpc.L2.BlockByNumber(context.Background(), nil) + s.Nil(err) + s.Equal(uint64(0), l2Head2.NumberU64()) +} + +func (s *CalldataSyncerTestSuite) TestHandleReorgToNoneGenesis() { + testutils.ProposeAndInsertEmptyBlocks(&s.ClientTestSuite, s.p, s.s) + + l2Head1, err := s.s.rpc.L2.BlockByNumber(context.Background(), nil) + s.Nil(err) + s.Greater(l2Head1.NumberU64(), uint64(0)) + s.NotZero(s.s.lastInsertedBlockID.Uint64()) + s.s.lastInsertedBlockID = common.Big1 // let the chain reorg to height 1 + + s.Nil(s.s.handleReorg(context.Background(), &bindings.TaikoL1ClientBlockProposed{ + Id: l2Head1.Number(), + Raw: types.Log{Removed: true}, + })) + + l2Head2, err := s.s.rpc.L2.BlockByNumber(context.Background(), nil) + s.Nil(err) + s.Equal(uint64(1), l2Head2.NumberU64()) + + testutils.ProposeAndInsertEmptyBlocks(&s.ClientTestSuite, s.p, s.s) + l2Head3, err := s.s.rpc.L2.BlockByNumber(context.Background(), nil) + s.Nil(err) + s.Greater(l2Head3.NumberU64(), l2Head2.NumberU64()) + s.Greater(s.s.lastInsertedBlockID.Uint64(), uint64(1)) +} + func TestCalldataSyncerTestSuite(t *testing.T) { suite.Run(t, new(CalldataSyncerTestSuite)) } diff --git a/integration_test/nodes/docker-compose.yml b/integration_test/nodes/docker-compose.yml index 64a816b99..04f852f4b 100644 --- a/integration_test/nodes/docker-compose.yml +++ b/integration_test/nodes/docker-compose.yml @@ -14,7 +14,7 @@ services: - "0.0.0.0" l2_execution_engine: - image: gcr.io/evmchain/taiko-geth:taiko + image: gcr.io/evmchain/taiko-geth:sha-f8be24a # TODO: change back to taiko tag restart: unless-stopped pull_policy: always volumes: From 2d3c7473a69eff14a1c888b0d252b30d8e79f7f5 Mon Sep 17 00:00:00 2001 From: Jeffery Walsh Date: Tue, 16 May 2023 10:47:19 -0700 Subject: [PATCH 24/25] remove parentblockinfo --- driver/chain_syncer/calldata/syncer.go | 36 +++++++-------------- driver/chain_syncer/calldata/syncer_test.go | 6 +--- 2 files changed, 13 insertions(+), 29 deletions(-) diff --git a/driver/chain_syncer/calldata/syncer.go b/driver/chain_syncer/calldata/syncer.go index 7b91e57ec..7e11d785d 100644 --- a/driver/chain_syncer/calldata/syncer.go +++ b/driver/chain_syncer/calldata/syncer.go @@ -26,14 +26,6 @@ import ( txListValidator "github.com/taikoxyz/taiko-client/pkg/tx_list_validator" ) -// ParentBlockInfo is an abstraction between *types.Header and our code, to allow passing in -// a zero hash and other zero-value fields. -type ParentBlockInfo struct { - Hash common.Hash - Number *big.Int - GasUsed uint64 -} - // Syncer responsible for letting the L2 execution engine catching up with protocol's latest // pending block through deriving L1 calldata. type Syncer struct { @@ -196,11 +188,7 @@ func (s *Syncer) onBlockProposed( payloadData, rpcError, payloadError := s.insertNewHead( ctx, event, - &ParentBlockInfo{ - Hash: parent.Hash(), - Number: parent.Number, - GasUsed: parent.GasUsed, - }, + parent, s.state.GetHeadBlockID(), txListBytes, l1Origin, @@ -346,15 +334,15 @@ func (s *Syncer) handleReorg(ctx context.Context, event *bindings.TaikoL1ClientB func (s *Syncer) insertNewHead( ctx context.Context, event *bindings.TaikoL1ClientBlockProposed, - parentBlockInfo *ParentBlockInfo, + parent *types.Header, headBlockID *big.Int, txListBytes []byte, l1Origin *rawdb.L1Origin, ) (*engine.ExecutableData, error, error) { log.Debug( "Try to insert a new L2 head block", - "parentNumber", parentBlockInfo.Number, - "parentHash", parentBlockInfo.Hash, + "parentNumber", parent.Number, + "parentHash", parent.Hash(), "headBlockID", headBlockID, "l1Origin", l1Origin, ) @@ -368,17 +356,17 @@ func (s *Syncer) insertNewHead( } } - parentTimestamp, err := s.rpc.TaikoL2.ParentTimestamp(&bind.CallOpts{BlockNumber: parentBlockInfo.Number}) + parentTimestamp, err := s.rpc.TaikoL2.ParentTimestamp(&bind.CallOpts{BlockNumber: parent.Number}) if err != nil { return nil, nil, err } // Get L2 baseFee baseFee, err := s.rpc.TaikoL2.GetBasefee( - &bind.CallOpts{BlockNumber: parentBlockInfo.Number}, + &bind.CallOpts{BlockNumber: parent.Number}, uint32(event.Meta.Timestamp-parentTimestamp), uint64(event.Meta.GasLimit+uint32(s.anchorConstructor.GasLimit())), - parentBlockInfo.GasUsed, + parent.GasUsed, ) if err != nil { return nil, nil, fmt.Errorf("failed to get L2 baseFee: %w", encoding.TryParsingCustomError(err)) @@ -389,7 +377,7 @@ func (s *Syncer) insertNewHead( "baseFee", baseFee, "timeSinceParent", uint32(event.Meta.Timestamp-parentTimestamp), "gasLimit", uint64(event.Meta.GasLimit+uint32(s.anchorConstructor.GasLimit())), - "parentGasUsed", parentBlockInfo.GasUsed, + "parentGasUsed", parent.GasUsed, ) // Get withdrawals @@ -403,9 +391,9 @@ func (s *Syncer) insertNewHead( ctx, new(big.Int).SetUint64(event.Meta.L1Height), event.Meta.L1Hash, - new(big.Int).Add(parentBlockInfo.Number, common.Big1), + new(big.Int).Add(parent.Number, common.Big1), baseFee, - parentBlockInfo.GasUsed, + parent.GasUsed, ) if err != nil { return nil, nil, fmt.Errorf("failed to create TaikoL2.anchor transaction: %w", err) @@ -421,7 +409,7 @@ func (s *Syncer) insertNewHead( payload, rpcErr, payloadErr := s.createExecutionPayloads( ctx, event, - parentBlockInfo.Hash, + parent.Hash(), l1Origin, headBlockID, txListBytes, @@ -433,7 +421,7 @@ func (s *Syncer) insertNewHead( return nil, rpcErr, payloadErr } - fc := &engine.ForkchoiceStateV1{HeadBlockHash: parentBlockInfo.Hash} + fc := &engine.ForkchoiceStateV1{HeadBlockHash: parent.Hash()} // Update the fork choice fc.HeadBlockHash = payload.BlockHash diff --git a/driver/chain_syncer/calldata/syncer_test.go b/driver/chain_syncer/calldata/syncer_test.go index 530889f78..920c62ea7 100644 --- a/driver/chain_syncer/calldata/syncer_test.go +++ b/driver/chain_syncer/calldata/syncer_test.go @@ -89,11 +89,7 @@ func (s *CalldataSyncerTestSuite) TestInsertNewHead() { Timestamp: uint64(time.Now().Unix()), }, }, - &ParentBlockInfo{ - Hash: parent.Hash(), - Number: parent.Number, - GasUsed: parent.GasUsed, - }, + parent, common.Big2, []byte{}, &rawdb.L1Origin{ From 77baff877ab19b8eda7211e7381e899a48df9d8a Mon Sep 17 00:00:00 2001 From: David Date: Wed, 17 May 2023 01:54:55 +0800 Subject: [PATCH 25/25] feat: fix tests and rename Id to ID --- driver/chain_syncer/calldata/syncer.go | 33 ++++++++++++++------------ 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/driver/chain_syncer/calldata/syncer.go b/driver/chain_syncer/calldata/syncer.go index 7e11d785d..950845f1e 100644 --- a/driver/chain_syncer/calldata/syncer.go +++ b/driver/chain_syncer/calldata/syncer.go @@ -243,17 +243,20 @@ func (s *Syncer) handleReorg(ctx context.Context, event *bindings.TaikoL1ClientB // rewind chain by 1 until we find a block that is still in the chain var ( - lastKnownGoodBlockId *big.Int - blockId *big.Int + lastKnownGoodBlockID *big.Int + blockID *big.Int = s.lastInsertedBlockID block *types.Block err error ) - l2Head, err := s.rpc.L2.BlockByNumber(ctx, nil) - if err != nil { - return err + // if `lastInsertedBlockID` has not been set, we use current L2 chain head as blockID instead + if blockID == nil { + l2Head, err := s.rpc.L2.BlockByNumber(ctx, nil) + if err != nil { + return err + } + blockID = l2Head.Number() } - blockId = l2Head.Number() stateVars, err := s.rpc.GetProtocolStateVariables(nil) if err != nil { @@ -261,37 +264,37 @@ func (s *Syncer) handleReorg(ctx context.Context, event *bindings.TaikoL1ClientB } for { - if blockId.Cmp(common.Big0) == 0 { + if blockID.Cmp(common.Big0) == 0 { if block, err = s.rpc.L2.BlockByNumber(ctx, common.Big0); err != nil { return err } - lastKnownGoodBlockId = common.Big0 + lastKnownGoodBlockID = common.Big0 break } - if block, err = s.rpc.L2.BlockByNumber(ctx, blockId); err != nil && !errors.Is(err, ethereum.NotFound) { + if block, err = s.rpc.L2.BlockByNumber(ctx, blockID); err != nil && !errors.Is(err, ethereum.NotFound) { return err } - if block != nil && blockId.Uint64() < stateVars.NumBlocks { + if block != nil && blockID.Uint64() < stateVars.NumBlocks { // block exists, we can rewind to this block - lastKnownGoodBlockId = blockId + lastKnownGoodBlockID = blockID break } else { // otherwise, sub 1 from blockId and try again - blockId = new(big.Int).Sub(blockId, common.Big1) + blockID = new(big.Int).Sub(blockID, common.Big1) } } // shouldn't be able to reach this error because of the 0 check above // but just in case - if lastKnownGoodBlockId == nil { + if lastKnownGoodBlockID == nil { return fmt.Errorf("failed to find last known good block ID after reorg") } log.Info( "🔗 Last known good block ID before reorg found", - "blockID", lastKnownGoodBlockId, + "blockID", lastKnownGoodBlockID, ) fcRes, err := s.rpc.L2Engine.ForkchoiceUpdate(ctx, &engine.ForkchoiceStateV1{HeadBlockHash: block.Hash()}, nil) @@ -303,7 +306,7 @@ func (s *Syncer) handleReorg(ctx context.Context, event *bindings.TaikoL1ClientB } // reset l1 current to when the last known good block was inserted, and return the event. - if _, _, err := s.state.ResetL1Current(ctx, &state.HeightOrID{ID: lastKnownGoodBlockId}); err != nil { + if _, _, err := s.state.ResetL1Current(ctx, &state.HeightOrID{ID: lastKnownGoodBlockID}); err != nil { return fmt.Errorf("failed to reset L1 current: %w", err) }