From bc55d6515adfe21c2c6cb11be0c7723fa5f5e171 Mon Sep 17 00:00:00 2001 From: lesterli Date: Sun, 28 Jul 2024 19:41:34 +0800 Subject: [PATCH 01/15] finality range --- op-node/rollup/finality/finalizer.go | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/op-node/rollup/finality/finalizer.go b/op-node/rollup/finality/finalizer.go index 8c94db0a80ce..14ba3ea69e0f 100644 --- a/op-node/rollup/finality/finalizer.go +++ b/op-node/rollup/finality/finalizer.go @@ -248,14 +248,11 @@ func (fi *Finalizer) tryFinalize() { lastFinalizedBlock := fi.findLastFinalizedL2BlockWithConsecutiveQuorom(fd.L2Block.Number, finalizedL2.Number) // set finalized block(s) - if lastFinalizedBlock != nil { - finalizedL2, finalizedDerivedFrom = fi.updateFinalized(*lastFinalizedBlock, lastFinalizedBlock.L1Origin) + if lastFinalizedBlock != nil && lastFinalizedBlock.Number == fd.L2Block.Number { + finalizedL2 = *lastFinalizedBlock + finalizedDerivedFrom = fd.L1Block } - // some blocks in the queried range is not BTC finalized, stop iterating to honor consecutive quorom - if lastFinalizedBlock == nil || lastFinalizedBlock.Number != fd.L2Block.Number { - break - } // keep iterating, there may be later L2 blocks that can also be finalized } } @@ -366,13 +363,6 @@ func (fi *Finalizer) findLastFinalizedL2BlockWithConsecutiveQuorom( return nil } -func (fi *Finalizer) updateFinalized(lastFinalizedBlock eth.L2BlockRef, fdL1Block eth.BlockID) (eth.L2BlockRef, eth.BlockID) { - finalizedL2 := lastFinalizedBlock - finalizedDerivedFrom := fdL1Block - fi.log.Debug("set finalized block", "l2_block", finalizedL2, "derived_from", finalizedDerivedFrom) - return finalizedL2, finalizedDerivedFrom -} - // onDerivedSafeBlock buffers the L1 block the safe head was fully derived from, // to finalize it once the derived-from L1 block, or a later L1 block, finalizes. func (fi *Finalizer) onDerivedSafeBlock(l2Safe eth.L2BlockRef, derivedFrom eth.L1BlockRef) { From 32f9c52aa11b4b69fcdbc341b6e329217ce2b20d Mon Sep 17 00:00:00 2001 From: lesterli Date: Sun, 28 Jul 2024 19:42:03 +0800 Subject: [PATCH 02/15] fix basic --- op-node/rollup/finality/finalizer_test.go | 73 +++++++++++++++++++++-- 1 file changed, 68 insertions(+), 5 deletions(-) diff --git a/op-node/rollup/finality/finalizer_test.go b/op-node/rollup/finality/finalizer_test.go index d4dc76fb4ca5..3da7051d25e8 100644 --- a/op-node/rollup/finality/finalizer_test.go +++ b/op-node/rollup/finality/finalizer_test.go @@ -11,12 +11,15 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/log" + "github.com/babylonchain/babylon-finality-gadget/sdk/cwclient" + "github.com/babylonchain/babylon-finality-gadget/testutil/mocks" "github.com/ethereum-optimism/optimism/op-node/rollup" "github.com/ethereum-optimism/optimism/op-node/rollup/derive" "github.com/ethereum-optimism/optimism/op-node/rollup/engine" "github.com/ethereum-optimism/optimism/op-service/eth" "github.com/ethereum-optimism/optimism/op-service/testlog" "github.com/ethereum-optimism/optimism/op-service/testutils" + "go.uber.org/mock/gomock" ) func TestEngineQueue_Finalize(t *testing.T) { @@ -82,6 +85,7 @@ func TestEngineQueue_Finalize(t *testing.T) { L1Origin: refA.ID(), SequenceNumber: 0, } + cfg := &rollup.Config{ Genesis: rollup.Genesis{ L1: refA.ID(), @@ -91,6 +95,13 @@ func TestEngineQueue_Finalize(t *testing.T) { BlockTime: 1, SeqWindowSize: 2, } + babylonCfg := &rollup.BabylonConfig{ + ChainID: "chain-test", + ContractAddress: "bbn1eyfccmjm6732k7wp4p6gdjwhxjwsvje44j0hfx8nkgrm8fs7vqfsa3n3gc", + BitcoinRpc: "https://rpc.this-is-a-mock.com/btc", + } + cfg.BabylonConfig = babylonCfg + refA1 := eth.L2BlockRef{ Hash: testutils.RandomHash(rng), Number: refA0.Number + 1, @@ -190,8 +201,48 @@ func TestEngineQueue_Finalize(t *testing.T) { l1F.ExpectL1BlockRefByNumber(refD.Number, refD, nil) l1F.ExpectL1BlockRefByNumber(refD.Number, refD, nil) + l2F := &testutils.MockL2Client{} + defer l2F.AssertExpectations(t) + l2F.ExpectL2BlockRefByNumber(refA1.Number, refA1, nil) + l2F.ExpectL2BlockRefByNumber(refB0.Number, refB0, nil) + l2F.ExpectL2BlockRefByNumber(refB1.Number, refB1, nil) + l2F.ExpectL2BlockRefByNumber(refC0.Number, refC0, nil) + l2F.ExpectL2BlockRefByNumber(refC1.Number, refC1, nil) + + ctl := gomock.NewController(t) + defer ctl.Finish() + sdkClient := mocks.NewMockISdkClient(ctl) + queryBlocks := make([]*cwclient.L2Block, refC1.Number) + queryBlocks[0] = &cwclient.L2Block{ + BlockHeight: refA1.Number, + BlockHash: refA1.Hash.String(), + BlockTimestamp: refA1.Time, + } + queryBlocks[1] = &cwclient.L2Block{ + BlockHeight: refB0.Number, + BlockHash: refB0.Hash.String(), + BlockTimestamp: refB0.Time, + } + queryBlocks[2] = &cwclient.L2Block{ + BlockHeight: refB1.Number, + BlockHash: refB1.Hash.String(), + BlockTimestamp: refB1.Time, + } + queryBlocks[3] = &cwclient.L2Block{ + BlockHeight: refC0.Number, + BlockHash: refC0.Hash.String(), + BlockTimestamp: refC0.Time, + } + queryBlocks[4] = &cwclient.L2Block{ + BlockHeight: refC1.Number, + BlockHash: refC1.Hash.String(), + BlockTimestamp: refC1.Time, + } + sdkClient.EXPECT().QueryBlockRangeBabylonFinalized(queryBlocks).Return(&refC1.Number, nil) + emitter := &testutils.MockEmitter{} - fi := NewFinalizer(context.Background(), logger, &rollup.Config{}, l1F, emitter) + fi := NewFinalizer(context.Background(), logger, &rollup.Config{BabylonConfig: babylonCfg}, l1F, l2F, emitter) + fi.babylonFinalityClient = sdkClient // now say C1 was included in D and became the new safe head fi.OnEvent(engine.SafeDerivedEvent{Safe: refC1, DerivedFrom: refD}) @@ -224,8 +275,11 @@ func TestEngineQueue_Finalize(t *testing.T) { l1F.ExpectL1BlockRefByNumber(refD.Number, refD, nil) // to check finality signal l1F.ExpectL1BlockRefByNumber(refD.Number, refD, nil) // to check what was derived from (same in this case) + l2F := &testutils.MockL2Client{} + defer l2F.AssertExpectations(t) + emitter := &testutils.MockEmitter{} - fi := NewFinalizer(context.Background(), logger, &rollup.Config{}, l1F, emitter) + fi := NewFinalizer(context.Background(), logger, &rollup.Config{}, l1F, l2F, emitter) // now say C1 was included in D and became the new safe head fi.OnEvent(engine.SafeDerivedEvent{Safe: refC1, DerivedFrom: refD}) @@ -263,8 +317,11 @@ func TestEngineQueue_Finalize(t *testing.T) { l1F := &testutils.MockL1Source{} defer l1F.AssertExpectations(t) + l2F := &testutils.MockL2Client{} + defer l2F.AssertExpectations(t) + emitter := &testutils.MockEmitter{} - fi := NewFinalizer(context.Background(), logger, &rollup.Config{}, l1F, emitter) + fi := NewFinalizer(context.Background(), logger, &rollup.Config{}, l1F, l2F, emitter) fi.OnEvent(engine.SafeDerivedEvent{Safe: refC1, DerivedFrom: refD}) fi.OnEvent(derive.DeriverIdleEvent{Origin: refD}) @@ -348,8 +405,11 @@ func TestEngineQueue_Finalize(t *testing.T) { l1F.ExpectL1BlockRefByNumber(refD.Number, refD, nil) // check the signal l1F.ExpectL1BlockRefByNumber(refC.Number, refC, nil) // check what we derived the L2 block from + l2F := &testutils.MockL2Client{} + defer l2F.AssertExpectations(t) + emitter := &testutils.MockEmitter{} - fi := NewFinalizer(context.Background(), logger, &rollup.Config{}, l1F, emitter) + fi := NewFinalizer(context.Background(), logger, &rollup.Config{}, l1F, l2F, emitter) // now say B1 was included in C and became the new safe head fi.OnEvent(engine.SafeDerivedEvent{Safe: refB1, DerivedFrom: refC}) @@ -384,8 +444,11 @@ func TestEngineQueue_Finalize(t *testing.T) { l1F.ExpectL1BlockRefByNumber(refF.Number, refF, nil) // check signal l1F.ExpectL1BlockRefByNumber(refE.Number, refE, nil) // post-reorg + l2F := &testutils.MockL2Client{} + defer l2F.AssertExpectations(t) + emitter := &testutils.MockEmitter{} - fi := NewFinalizer(context.Background(), logger, &rollup.Config{}, l1F, emitter) + fi := NewFinalizer(context.Background(), logger, &rollup.Config{}, l1F, l2F, emitter) // now say B1 was included in C and became the new safe head fi.OnEvent(engine.SafeDerivedEvent{Safe: refB1, DerivedFrom: refC}) From cf2e7051d2ce928c8966b28488d76fd776150ff1 Mon Sep 17 00:00:00 2001 From: lesterli Date: Sun, 28 Jul 2024 19:45:22 +0800 Subject: [PATCH 03/15] fix retry --- op-node/rollup/finality/finalizer_test.go | 44 ++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/op-node/rollup/finality/finalizer_test.go b/op-node/rollup/finality/finalizer_test.go index 3da7051d25e8..af14c716ffd2 100644 --- a/op-node/rollup/finality/finalizer_test.go +++ b/op-node/rollup/finality/finalizer_test.go @@ -277,9 +277,51 @@ func TestEngineQueue_Finalize(t *testing.T) { l2F := &testutils.MockL2Client{} defer l2F.AssertExpectations(t) + l2F.ExpectL2BlockRefByNumber(refA1.Number, refA1, nil) + l2F.ExpectL2BlockRefByNumber(refB0.Number, refB0, nil) + l2F.ExpectL2BlockRefByNumber(refB1.Number, refB1, nil) + l2F.ExpectL2BlockRefByNumber(refC0.Number, refC0, nil) + l2F.ExpectL2BlockRefByNumber(refC1.Number, refC1, nil) + l2F.ExpectL2BlockRefByNumber(refA1.Number, refA1, nil) + l2F.ExpectL2BlockRefByNumber(refB0.Number, refB0, nil) + l2F.ExpectL2BlockRefByNumber(refB1.Number, refB1, nil) + l2F.ExpectL2BlockRefByNumber(refC0.Number, refC0, nil) + l2F.ExpectL2BlockRefByNumber(refC1.Number, refC1, nil) + + ctl := gomock.NewController(t) + defer ctl.Finish() + sdkClient := mocks.NewMockISdkClient(ctl) + queryBlocks := make([]*cwclient.L2Block, refC1.Number) + queryBlocks[0] = &cwclient.L2Block{ + BlockHeight: refA1.Number, + BlockHash: refA1.Hash.String(), + BlockTimestamp: refA1.Time, + } + queryBlocks[1] = &cwclient.L2Block{ + BlockHeight: refB0.Number, + BlockHash: refB0.Hash.String(), + BlockTimestamp: refB0.Time, + } + queryBlocks[2] = &cwclient.L2Block{ + BlockHeight: refB1.Number, + BlockHash: refB1.Hash.String(), + BlockTimestamp: refB1.Time, + } + queryBlocks[3] = &cwclient.L2Block{ + BlockHeight: refC0.Number, + BlockHash: refC0.Hash.String(), + BlockTimestamp: refC0.Time, + } + queryBlocks[4] = &cwclient.L2Block{ + BlockHeight: refC1.Number, + BlockHash: refC1.Hash.String(), + BlockTimestamp: refC1.Time, + } + sdkClient.EXPECT().QueryBlockRangeBabylonFinalized(queryBlocks).Return(&refC1.Number, nil).AnyTimes() emitter := &testutils.MockEmitter{} - fi := NewFinalizer(context.Background(), logger, &rollup.Config{}, l1F, l2F, emitter) + fi := NewFinalizer(context.Background(), logger, &rollup.Config{BabylonConfig: babylonCfg}, l1F, l2F, emitter) + fi.babylonFinalityClient = sdkClient // now say C1 was included in D and became the new safe head fi.OnEvent(engine.SafeDerivedEvent{Safe: refC1, DerivedFrom: refD}) From 52f814899fa0d0315411e3ff550c5c0c2d93183f Mon Sep 17 00:00:00 2001 From: lesterli Date: Sun, 28 Jul 2024 19:50:00 +0800 Subject: [PATCH 04/15] fix repeat --- op-node/rollup/finality/finalizer_test.go | 87 ++++++++++++++++++++++- 1 file changed, 86 insertions(+), 1 deletion(-) diff --git a/op-node/rollup/finality/finalizer_test.go b/op-node/rollup/finality/finalizer_test.go index af14c716ffd2..2e41a94d2f59 100644 --- a/op-node/rollup/finality/finalizer_test.go +++ b/op-node/rollup/finality/finalizer_test.go @@ -362,8 +362,13 @@ func TestEngineQueue_Finalize(t *testing.T) { l2F := &testutils.MockL2Client{} defer l2F.AssertExpectations(t) + ctl := gomock.NewController(t) + defer ctl.Finish() + sdkClient := mocks.NewMockISdkClient(ctl) + emitter := &testutils.MockEmitter{} - fi := NewFinalizer(context.Background(), logger, &rollup.Config{}, l1F, l2F, emitter) + fi := NewFinalizer(context.Background(), logger, &rollup.Config{BabylonConfig: babylonCfg}, l1F, l2F, emitter) + fi.babylonFinalityClient = sdkClient fi.OnEvent(engine.SafeDerivedEvent{Safe: refC1, DerivedFrom: refD}) fi.OnEvent(derive.DeriverIdleEvent{Origin: refD}) @@ -378,10 +383,43 @@ func TestEngineQueue_Finalize(t *testing.T) { fi.OnEvent(FinalizeL1Event{FinalizedL1: refD}) emitter.AssertExpectations(t) + queryBlocks := make([]*cwclient.L2Block, refC1.Number) + queryBlocks[0] = &cwclient.L2Block{ + BlockHeight: refA1.Number, + BlockHash: refA1.Hash.String(), + BlockTimestamp: refA1.Time, + } + queryBlocks[1] = &cwclient.L2Block{ + BlockHeight: refB0.Number, + BlockHash: refB0.Hash.String(), + BlockTimestamp: refB0.Time, + } + queryBlocks[2] = &cwclient.L2Block{ + BlockHeight: refB1.Number, + BlockHash: refB1.Hash.String(), + BlockTimestamp: refB1.Time, + } + queryBlocks[3] = &cwclient.L2Block{ + BlockHeight: refC0.Number, + BlockHash: refC0.Hash.String(), + BlockTimestamp: refC0.Time, + } + queryBlocks[4] = &cwclient.L2Block{ + BlockHeight: refC1.Number, + BlockHash: refC1.Hash.String(), + BlockTimestamp: refC1.Time, + } + sdkClient.EXPECT().QueryBlockRangeBabylonFinalized(queryBlocks).Return(&refC1.Number, nil).AnyTimes() + // C1 was included in D, and should be finalized now emitter.ExpectOnce(engine.PromoteFinalizedEvent{Ref: refC1}) l1F.ExpectL1BlockRefByNumber(refD.Number, refD, nil) l1F.ExpectL1BlockRefByNumber(refD.Number, refD, nil) + l2F.ExpectL2BlockRefByNumber(refA1.Number, refA1, nil) + l2F.ExpectL2BlockRefByNumber(refB0.Number, refB0, nil) + l2F.ExpectL2BlockRefByNumber(refB1.Number, refB1, nil) + l2F.ExpectL2BlockRefByNumber(refC0.Number, refC0, nil) + l2F.ExpectL2BlockRefByNumber(refC1.Number, refC1, nil) fi.OnEvent(TryFinalizeEvent{}) emitter.AssertExpectations(t) l1F.AssertExpectations(t) @@ -391,10 +429,24 @@ func TestEngineQueue_Finalize(t *testing.T) { fi.OnEvent(FinalizeL1Event{FinalizedL1: refE}) emitter.AssertExpectations(t) + queryBlocksTwo := make([]*cwclient.L2Block, 1) + queryBlocksTwo[0] = &cwclient.L2Block{ + BlockHeight: refD0.Number, + BlockHash: refD0.Hash.String(), + BlockTimestamp: refD0.Time, + } + sdkClient.EXPECT().QueryBlockRangeBabylonFinalized(queryBlocksTwo).Return(&refD0.Number, nil).AnyTimes() + // D0 was included in E, and should be finalized now emitter.ExpectOnce(engine.PromoteFinalizedEvent{Ref: refD0}) l1F.ExpectL1BlockRefByNumber(refE.Number, refE, nil) l1F.ExpectL1BlockRefByNumber(refE.Number, refE, nil) + l2F.ExpectL2BlockRefByNumber(refA1.Number, refA1, nil) + l2F.ExpectL2BlockRefByNumber(refB0.Number, refB0, nil) + l2F.ExpectL2BlockRefByNumber(refB1.Number, refB1, nil) + l2F.ExpectL2BlockRefByNumber(refC0.Number, refC0, nil) + l2F.ExpectL2BlockRefByNumber(refC1.Number, refC1, nil) + l2F.ExpectL2BlockRefByNumber(refD0.Number, refD0, nil) fi.OnEvent(TryFinalizeEvent{}) emitter.AssertExpectations(t) l1F.AssertExpectations(t) @@ -429,10 +481,43 @@ func TestEngineQueue_Finalize(t *testing.T) { fi.OnEvent(FinalizeL1Event{FinalizedL1: refH}) emitter.AssertExpectations(t) + queryBlocksThree := make([]*cwclient.L2Block, 5) + queryBlocksThree[0] = &cwclient.L2Block{ + BlockHeight: refD1.Number, + BlockHash: refD1.Hash.String(), + BlockTimestamp: refD1.Time, + } + queryBlocksThree[1] = &cwclient.L2Block{ + BlockHeight: refE0.Number, + BlockHash: refE0.Hash.String(), + BlockTimestamp: refE0.Time, + } + queryBlocksThree[2] = &cwclient.L2Block{ + BlockHeight: refE1.Number, + BlockHash: refE1.Hash.String(), + BlockTimestamp: refE1.Time, + } + queryBlocksThree[3] = &cwclient.L2Block{ + BlockHeight: refF0.Number, + BlockHash: refF0.Hash.String(), + BlockTimestamp: refF0.Time, + } + queryBlocksThree[4] = &cwclient.L2Block{ + BlockHeight: refF1.Number, + BlockHash: refF1.Hash.String(), + BlockTimestamp: refF1.Time, + } + sdkClient.EXPECT().QueryBlockRangeBabylonFinalized(queryBlocksThree).Return(&refF1.Number, nil).AnyTimes() + // F1 should be finalized now, since it was included in H emitter.ExpectOnce(engine.PromoteFinalizedEvent{Ref: refF1}) l1F.ExpectL1BlockRefByNumber(refH.Number, refH, nil) l1F.ExpectL1BlockRefByNumber(refH.Number, refH, nil) + l2F.ExpectL2BlockRefByNumber(refD1.Number, refD1, nil) + l2F.ExpectL2BlockRefByNumber(refE0.Number, refE0, nil) + l2F.ExpectL2BlockRefByNumber(refE1.Number, refE1, nil) + l2F.ExpectL2BlockRefByNumber(refF0.Number, refF0, nil) + l2F.ExpectL2BlockRefByNumber(refF1.Number, refF1, nil) fi.OnEvent(TryFinalizeEvent{}) emitter.AssertExpectations(t) l1F.AssertExpectations(t) From 52c28c00182af560feec91cb03dc17b092f929da Mon Sep 17 00:00:00 2001 From: lesterli Date: Sun, 28 Jul 2024 19:51:58 +0800 Subject: [PATCH 05/15] fix older-data --- op-node/rollup/finality/finalizer_test.go | 27 ++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/op-node/rollup/finality/finalizer_test.go b/op-node/rollup/finality/finalizer_test.go index 2e41a94d2f59..d0d442c523cf 100644 --- a/op-node/rollup/finality/finalizer_test.go +++ b/op-node/rollup/finality/finalizer_test.go @@ -534,9 +534,34 @@ func TestEngineQueue_Finalize(t *testing.T) { l2F := &testutils.MockL2Client{} defer l2F.AssertExpectations(t) + l2F.ExpectL2BlockRefByNumber(refA1.Number, refA1, nil) + l2F.ExpectL2BlockRefByNumber(refB0.Number, refB0, nil) + l2F.ExpectL2BlockRefByNumber(refB1.Number, refB1, nil) + + ctl := gomock.NewController(t) + defer ctl.Finish() + sdkClient := mocks.NewMockISdkClient(ctl) + queryBlocks := make([]*cwclient.L2Block, refB1.Number) + queryBlocks[0] = &cwclient.L2Block{ + BlockHeight: refA1.Number, + BlockHash: refA1.Hash.String(), + BlockTimestamp: refA1.Time, + } + queryBlocks[1] = &cwclient.L2Block{ + BlockHeight: refB0.Number, + BlockHash: refB0.Hash.String(), + BlockTimestamp: refB0.Time, + } + queryBlocks[2] = &cwclient.L2Block{ + BlockHeight: refB1.Number, + BlockHash: refB1.Hash.String(), + BlockTimestamp: refB1.Time, + } + sdkClient.EXPECT().QueryBlockRangeBabylonFinalized(queryBlocks).Return(&refB1.Number, nil) emitter := &testutils.MockEmitter{} - fi := NewFinalizer(context.Background(), logger, &rollup.Config{}, l1F, l2F, emitter) + fi := NewFinalizer(context.Background(), logger, &rollup.Config{BabylonConfig: babylonCfg}, l1F, l2F, emitter) + fi.babylonFinalityClient = sdkClient // now say B1 was included in C and became the new safe head fi.OnEvent(engine.SafeDerivedEvent{Safe: refB1, DerivedFrom: refC}) From ffa6329b4a291b1a7108ff1f781f1195e62fa94f Mon Sep 17 00:00:00 2001 From: lesterli Date: Sun, 28 Jul 2024 21:31:15 +0800 Subject: [PATCH 06/15] fix reorg-safe --- op-node/rollup/finality/finalizer.go | 2 + op-node/rollup/finality/finalizer_test.go | 78 ++++++++++++++++++++++- 2 files changed, 79 insertions(+), 1 deletion(-) diff --git a/op-node/rollup/finality/finalizer.go b/op-node/rollup/finality/finalizer.go index 14ba3ea69e0f..21fd1393f764 100644 --- a/op-node/rollup/finality/finalizer.go +++ b/op-node/rollup/finality/finalizer.go @@ -243,9 +243,11 @@ func (fi *Finalizer) tryFinalize() { finalizedL2 := fi.lastFinalizedL2 // may be zeroed if nothing was finalized since startup. var finalizedDerivedFrom eth.BlockID // go through the latest inclusion data, and find the last L2 block that was derived from a finalized L1 block + fi.log.Debug("tryFinalize", "finalityData", fi.finalityData, "finalizedL2", finalizedL2) for _, fd := range fi.finalityData { if fd.L2Block.Number > finalizedL2.Number && fd.L1Block.Number <= fi.finalizedL1.Number { lastFinalizedBlock := fi.findLastFinalizedL2BlockWithConsecutiveQuorom(fd.L2Block.Number, finalizedL2.Number) + fi.log.Debug("finalizing L2 block", "l2_block", fd.L2Block, "derived_from", fd.L1Block, "last_finalized_block", lastFinalizedBlock) // set finalized block(s) if lastFinalizedBlock != nil && lastFinalizedBlock.Number == fd.L2Block.Number { diff --git a/op-node/rollup/finality/finalizer_test.go b/op-node/rollup/finality/finalizer_test.go index d0d442c523cf..5222a8052dfa 100644 --- a/op-node/rollup/finality/finalizer_test.go +++ b/op-node/rollup/finality/finalizer_test.go @@ -599,8 +599,13 @@ func TestEngineQueue_Finalize(t *testing.T) { l2F := &testutils.MockL2Client{} defer l2F.AssertExpectations(t) + ctl := gomock.NewController(t) + defer ctl.Finish() + sdkClient := mocks.NewMockISdkClient(ctl) + emitter := &testutils.MockEmitter{} - fi := NewFinalizer(context.Background(), logger, &rollup.Config{}, l1F, l2F, emitter) + fi := NewFinalizer(context.Background(), logger, &rollup.Config{BabylonConfig: babylonCfg}, l1F, l2F, emitter) + fi.babylonFinalityClient = sdkClient // now say B1 was included in C and became the new safe head fi.OnEvent(engine.SafeDerivedEvent{Safe: refB1, DerivedFrom: refC}) @@ -633,6 +638,49 @@ func TestEngineQueue_Finalize(t *testing.T) { fi.OnEvent(engine.SafeDerivedEvent{Safe: refC0Alt, DerivedFrom: refDAlt}) fi.OnEvent(engine.SafeDerivedEvent{Safe: refC1Alt, DerivedFrom: refDAlt}) + l2F.ExpectL2BlockRefByNumber(refA1.Number, refA1, nil) + l2F.ExpectL2BlockRefByNumber(refB0.Number, refB0, nil) + l2F.ExpectL2BlockRefByNumber(refB1.Number, refB1, nil) + l2F.ExpectL2BlockRefByNumber(refC0Alt.Number, refC0Alt, nil) + l2F.ExpectL2BlockRefByNumber(refC1Alt.Number, refC1Alt, nil) + + l2F.ExpectL2BlockRefByNumber(refA1.Number, refA1, nil) + l2F.ExpectL2BlockRefByNumber(refB0.Number, refB0, nil) + l2F.ExpectL2BlockRefByNumber(refB1.Number, refB1, nil) + l2F.ExpectL2BlockRefByNumber(refC0Alt.Number, refC0Alt, nil) + l2F.ExpectL2BlockRefByNumber(refC1Alt.Number, refC1Alt, nil) + + queryBlocks := make([]*cwclient.L2Block, refB1.Number) + queryBlocks[0] = &cwclient.L2Block{ + BlockHeight: refA1.Number, + BlockHash: refA1.Hash.String(), + BlockTimestamp: refA1.Time, + } + queryBlocks[1] = &cwclient.L2Block{ + BlockHeight: refB0.Number, + BlockHash: refB0.Hash.String(), + BlockTimestamp: refB0.Time, + } + queryBlocks[2] = &cwclient.L2Block{ + BlockHeight: refB1.Number, + BlockHash: refB1.Hash.String(), + BlockTimestamp: refB1.Time, + } + sdkClient.EXPECT().QueryBlockRangeBabylonFinalized(queryBlocks).Return(&refB1.Number, nil).AnyTimes() + + queryBlocksTwo := make([]*cwclient.L2Block, 2) + queryBlocksTwo[0] = &cwclient.L2Block{ + BlockHeight: refC0Alt.Number, + BlockHash: refC0Alt.Hash.String(), + BlockTimestamp: refC0Alt.Time, + } + queryBlocksTwo[1] = &cwclient.L2Block{ + BlockHeight: refC1Alt.Number, + BlockHash: refC1Alt.Hash.String(), + BlockTimestamp: refC1Alt.Time, + } + sdkClient.EXPECT().QueryBlockRangeBabylonFinalized(queryBlocksTwo).Return(&refC1Alt.Number, nil).AnyTimes() + // We get an early finality signal for F, of the chain that did not include refC0Alt and refC1Alt, // as L1 block F does not build on DAlt. // The finality signal was for a new chain, while derivation is on an old stale chain. @@ -673,6 +721,34 @@ func TestEngineQueue_Finalize(t *testing.T) { // and don't expect a finality attempt. emitter.AssertExpectations(t) + l2F.ExpectL2BlockRefByNumber(refA1.Number, refA1, nil) + l2F.ExpectL2BlockRefByNumber(refB0.Number, refB0, nil) + l2F.ExpectL2BlockRefByNumber(refB1.Number, refB1, nil) + l2F.ExpectL2BlockRefByNumber(refC0.Number, refC0, nil) + + queryBlocksThree := make([]*cwclient.L2Block, refC0.Number) + queryBlocksThree[0] = &cwclient.L2Block{ + BlockHeight: refA1.Number, + BlockHash: refA1.Hash.String(), + BlockTimestamp: refA1.Time, + } + queryBlocksThree[1] = &cwclient.L2Block{ + BlockHeight: refB0.Number, + BlockHash: refB0.Hash.String(), + BlockTimestamp: refB0.Time, + } + queryBlocksThree[2] = &cwclient.L2Block{ + BlockHeight: refB1.Number, + BlockHash: refB1.Hash.String(), + BlockTimestamp: refB1.Time, + } + queryBlocksThree[3] = &cwclient.L2Block{ + BlockHeight: refC0.Number, + BlockHash: refC0.Hash.String(), + BlockTimestamp: refC0.Time, + } + sdkClient.EXPECT().QueryBlockRangeBabylonFinalized(queryBlocksThree).Return(&refC0.Number, nil) + // if we reset the attempt, then we can finalize however. fi.triedFinalizeAt = 0 emitter.ExpectOnce(TryFinalizeEvent{}) From 6a6bfade83788731418d0ef4d0e2f922474fb5ac Mon Sep 17 00:00:00 2001 From: lesterli Date: Mon, 29 Jul 2024 11:10:02 +0800 Subject: [PATCH 07/15] revert finalize range --- op-node/rollup/finality/finalizer.go | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/op-node/rollup/finality/finalizer.go b/op-node/rollup/finality/finalizer.go index 21fd1393f764..00df39e56c97 100644 --- a/op-node/rollup/finality/finalizer.go +++ b/op-node/rollup/finality/finalizer.go @@ -243,17 +243,22 @@ func (fi *Finalizer) tryFinalize() { finalizedL2 := fi.lastFinalizedL2 // may be zeroed if nothing was finalized since startup. var finalizedDerivedFrom eth.BlockID // go through the latest inclusion data, and find the last L2 block that was derived from a finalized L1 block - fi.log.Debug("tryFinalize", "finalityData", fi.finalityData, "finalizedL2", finalizedL2) + fi.log.Debug("try finalize", "finality_data", fi.finalityData, "last_finalized_l2", finalizedL2) for _, fd := range fi.finalityData { if fd.L2Block.Number > finalizedL2.Number && fd.L1Block.Number <= fi.finalizedL1.Number { lastFinalizedBlock := fi.findLastFinalizedL2BlockWithConsecutiveQuorom(fd.L2Block.Number, finalizedL2.Number) - fi.log.Debug("finalizing L2 block", "l2_block", fd.L2Block, "derived_from", fd.L1Block, "last_finalized_block", lastFinalizedBlock) // set finalized block(s) - if lastFinalizedBlock != nil && lastFinalizedBlock.Number == fd.L2Block.Number { + if lastFinalizedBlock != nil { finalizedL2 = *lastFinalizedBlock finalizedDerivedFrom = fd.L1Block } + fi.log.Debug("set finalized block", "finalized_l2", finalizedL2, "finalized_derived_from", finalizedDerivedFrom, "fd_l2_block", fd.L2Block) + + // some blocks in the queried range is not BTC finalized, stop iterating to honor consecutive quorom + if lastFinalizedBlock == nil || finalizedL2.Number != fd.L2Block.Number { + break + } // keep iterating, there may be later L2 blocks that can also be finalized } From c5eac9f0ca07e5f115ec4058be16b15741d1828a Mon Sep 17 00:00:00 2001 From: lesterli Date: Mon, 29 Jul 2024 16:35:33 +0800 Subject: [PATCH 08/15] update for comments: use helper functions --- op-node/rollup/finality/finalizer_test.go | 301 ++++------------------ op-service/testutils/mock_l2.go | 4 + 2 files changed, 50 insertions(+), 255 deletions(-) diff --git a/op-node/rollup/finality/finalizer_test.go b/op-node/rollup/finality/finalizer_test.go index 5222a8052dfa..6225ef805aad 100644 --- a/op-node/rollup/finality/finalizer_test.go +++ b/op-node/rollup/finality/finalizer_test.go @@ -85,7 +85,6 @@ func TestEngineQueue_Finalize(t *testing.T) { L1Origin: refA.ID(), SequenceNumber: 0, } - cfg := &rollup.Config{ Genesis: rollup.Genesis{ L1: refA.ID(), @@ -101,7 +100,6 @@ func TestEngineQueue_Finalize(t *testing.T) { BitcoinRpc: "https://rpc.this-is-a-mock.com/btc", } cfg.BabylonConfig = babylonCfg - refA1 := eth.L2BlockRef{ Hash: testutils.RandomHash(rng), Number: refA0.Number + 1, @@ -203,46 +201,16 @@ func TestEngineQueue_Finalize(t *testing.T) { l2F := &testutils.MockL2Client{} defer l2F.AssertExpectations(t) - l2F.ExpectL2BlockRefByNumber(refA1.Number, refA1, nil) - l2F.ExpectL2BlockRefByNumber(refB0.Number, refB0, nil) - l2F.ExpectL2BlockRefByNumber(refB1.Number, refB1, nil) - l2F.ExpectL2BlockRefByNumber(refC0.Number, refC0, nil) - l2F.ExpectL2BlockRefByNumber(refC1.Number, refC1, nil) + helperL2BlockRefByNumberWithTimes(t, l2F, 1, refA1, refB0, refB1, refC0, refC1) + + emitter := &testutils.MockEmitter{} + fi := NewFinalizer(context.Background(), logger, &rollup.Config{BabylonConfig: babylonCfg}, l1F, l2F, emitter) ctl := gomock.NewController(t) defer ctl.Finish() sdkClient := mocks.NewMockISdkClient(ctl) - queryBlocks := make([]*cwclient.L2Block, refC1.Number) - queryBlocks[0] = &cwclient.L2Block{ - BlockHeight: refA1.Number, - BlockHash: refA1.Hash.String(), - BlockTimestamp: refA1.Time, - } - queryBlocks[1] = &cwclient.L2Block{ - BlockHeight: refB0.Number, - BlockHash: refB0.Hash.String(), - BlockTimestamp: refB0.Time, - } - queryBlocks[2] = &cwclient.L2Block{ - BlockHeight: refB1.Number, - BlockHash: refB1.Hash.String(), - BlockTimestamp: refB1.Time, - } - queryBlocks[3] = &cwclient.L2Block{ - BlockHeight: refC0.Number, - BlockHash: refC0.Hash.String(), - BlockTimestamp: refC0.Time, - } - queryBlocks[4] = &cwclient.L2Block{ - BlockHeight: refC1.Number, - BlockHash: refC1.Hash.String(), - BlockTimestamp: refC1.Time, - } - sdkClient.EXPECT().QueryBlockRangeBabylonFinalized(queryBlocks).Return(&refC1.Number, nil) - - emitter := &testutils.MockEmitter{} - fi := NewFinalizer(context.Background(), logger, &rollup.Config{BabylonConfig: babylonCfg}, l1F, l2F, emitter) fi.babylonFinalityClient = sdkClient + helperSdkClientQueryBlockRangeBabylonFinalized(t, sdkClient, 1, refA1, refB0, refB1, refC0, refC1) // now say C1 was included in D and became the new safe head fi.OnEvent(engine.SafeDerivedEvent{Safe: refC1, DerivedFrom: refD}) @@ -277,51 +245,15 @@ func TestEngineQueue_Finalize(t *testing.T) { l2F := &testutils.MockL2Client{} defer l2F.AssertExpectations(t) - l2F.ExpectL2BlockRefByNumber(refA1.Number, refA1, nil) - l2F.ExpectL2BlockRefByNumber(refB0.Number, refB0, nil) - l2F.ExpectL2BlockRefByNumber(refB1.Number, refB1, nil) - l2F.ExpectL2BlockRefByNumber(refC0.Number, refC0, nil) - l2F.ExpectL2BlockRefByNumber(refC1.Number, refC1, nil) - l2F.ExpectL2BlockRefByNumber(refA1.Number, refA1, nil) - l2F.ExpectL2BlockRefByNumber(refB0.Number, refB0, nil) - l2F.ExpectL2BlockRefByNumber(refB1.Number, refB1, nil) - l2F.ExpectL2BlockRefByNumber(refC0.Number, refC0, nil) - l2F.ExpectL2BlockRefByNumber(refC1.Number, refC1, nil) + helperL2BlockRefByNumberWithTimes(t, l2F, 2, refA1, refB0, refB1, refC0, refC1) + emitter := &testutils.MockEmitter{} + fi := NewFinalizer(context.Background(), logger, &rollup.Config{BabylonConfig: babylonCfg}, l1F, l2F, emitter) ctl := gomock.NewController(t) defer ctl.Finish() sdkClient := mocks.NewMockISdkClient(ctl) - queryBlocks := make([]*cwclient.L2Block, refC1.Number) - queryBlocks[0] = &cwclient.L2Block{ - BlockHeight: refA1.Number, - BlockHash: refA1.Hash.String(), - BlockTimestamp: refA1.Time, - } - queryBlocks[1] = &cwclient.L2Block{ - BlockHeight: refB0.Number, - BlockHash: refB0.Hash.String(), - BlockTimestamp: refB0.Time, - } - queryBlocks[2] = &cwclient.L2Block{ - BlockHeight: refB1.Number, - BlockHash: refB1.Hash.String(), - BlockTimestamp: refB1.Time, - } - queryBlocks[3] = &cwclient.L2Block{ - BlockHeight: refC0.Number, - BlockHash: refC0.Hash.String(), - BlockTimestamp: refC0.Time, - } - queryBlocks[4] = &cwclient.L2Block{ - BlockHeight: refC1.Number, - BlockHash: refC1.Hash.String(), - BlockTimestamp: refC1.Time, - } - sdkClient.EXPECT().QueryBlockRangeBabylonFinalized(queryBlocks).Return(&refC1.Number, nil).AnyTimes() - - emitter := &testutils.MockEmitter{} - fi := NewFinalizer(context.Background(), logger, &rollup.Config{BabylonConfig: babylonCfg}, l1F, l2F, emitter) fi.babylonFinalityClient = sdkClient + helperSdkClientQueryBlockRangeBabylonFinalized(t, sdkClient, 2, refA1, refB0, refB1, refC0, refC1) // now say C1 was included in D and became the new safe head fi.OnEvent(engine.SafeDerivedEvent{Safe: refC1, DerivedFrom: refD}) @@ -362,12 +294,11 @@ func TestEngineQueue_Finalize(t *testing.T) { l2F := &testutils.MockL2Client{} defer l2F.AssertExpectations(t) + emitter := &testutils.MockEmitter{} + fi := NewFinalizer(context.Background(), logger, &rollup.Config{BabylonConfig: babylonCfg}, l1F, l2F, emitter) ctl := gomock.NewController(t) defer ctl.Finish() sdkClient := mocks.NewMockISdkClient(ctl) - - emitter := &testutils.MockEmitter{} - fi := NewFinalizer(context.Background(), logger, &rollup.Config{BabylonConfig: babylonCfg}, l1F, l2F, emitter) fi.babylonFinalityClient = sdkClient fi.OnEvent(engine.SafeDerivedEvent{Safe: refC1, DerivedFrom: refD}) @@ -383,43 +314,12 @@ func TestEngineQueue_Finalize(t *testing.T) { fi.OnEvent(FinalizeL1Event{FinalizedL1: refD}) emitter.AssertExpectations(t) - queryBlocks := make([]*cwclient.L2Block, refC1.Number) - queryBlocks[0] = &cwclient.L2Block{ - BlockHeight: refA1.Number, - BlockHash: refA1.Hash.String(), - BlockTimestamp: refA1.Time, - } - queryBlocks[1] = &cwclient.L2Block{ - BlockHeight: refB0.Number, - BlockHash: refB0.Hash.String(), - BlockTimestamp: refB0.Time, - } - queryBlocks[2] = &cwclient.L2Block{ - BlockHeight: refB1.Number, - BlockHash: refB1.Hash.String(), - BlockTimestamp: refB1.Time, - } - queryBlocks[3] = &cwclient.L2Block{ - BlockHeight: refC0.Number, - BlockHash: refC0.Hash.String(), - BlockTimestamp: refC0.Time, - } - queryBlocks[4] = &cwclient.L2Block{ - BlockHeight: refC1.Number, - BlockHash: refC1.Hash.String(), - BlockTimestamp: refC1.Time, - } - sdkClient.EXPECT().QueryBlockRangeBabylonFinalized(queryBlocks).Return(&refC1.Number, nil).AnyTimes() - // C1 was included in D, and should be finalized now emitter.ExpectOnce(engine.PromoteFinalizedEvent{Ref: refC1}) l1F.ExpectL1BlockRefByNumber(refD.Number, refD, nil) l1F.ExpectL1BlockRefByNumber(refD.Number, refD, nil) - l2F.ExpectL2BlockRefByNumber(refA1.Number, refA1, nil) - l2F.ExpectL2BlockRefByNumber(refB0.Number, refB0, nil) - l2F.ExpectL2BlockRefByNumber(refB1.Number, refB1, nil) - l2F.ExpectL2BlockRefByNumber(refC0.Number, refC0, nil) - l2F.ExpectL2BlockRefByNumber(refC1.Number, refC1, nil) + helperL2BlockRefByNumberWithTimes(t, l2F, 2, refA1, refB0, refB1, refC0, refC1) + helperSdkClientQueryBlockRangeBabylonFinalized(t, sdkClient, 2, refA1, refB0, refB1, refC0, refC1) fi.OnEvent(TryFinalizeEvent{}) emitter.AssertExpectations(t) l1F.AssertExpectations(t) @@ -429,24 +329,12 @@ func TestEngineQueue_Finalize(t *testing.T) { fi.OnEvent(FinalizeL1Event{FinalizedL1: refE}) emitter.AssertExpectations(t) - queryBlocksTwo := make([]*cwclient.L2Block, 1) - queryBlocksTwo[0] = &cwclient.L2Block{ - BlockHeight: refD0.Number, - BlockHash: refD0.Hash.String(), - BlockTimestamp: refD0.Time, - } - sdkClient.EXPECT().QueryBlockRangeBabylonFinalized(queryBlocksTwo).Return(&refD0.Number, nil).AnyTimes() - // D0 was included in E, and should be finalized now emitter.ExpectOnce(engine.PromoteFinalizedEvent{Ref: refD0}) l1F.ExpectL1BlockRefByNumber(refE.Number, refE, nil) l1F.ExpectL1BlockRefByNumber(refE.Number, refE, nil) - l2F.ExpectL2BlockRefByNumber(refA1.Number, refA1, nil) - l2F.ExpectL2BlockRefByNumber(refB0.Number, refB0, nil) - l2F.ExpectL2BlockRefByNumber(refB1.Number, refB1, nil) - l2F.ExpectL2BlockRefByNumber(refC0.Number, refC0, nil) - l2F.ExpectL2BlockRefByNumber(refC1.Number, refC1, nil) - l2F.ExpectL2BlockRefByNumber(refD0.Number, refD0, nil) + helperL2BlockRefByNumberWithTimes(t, l2F, 1, refD0) + helperSdkClientQueryBlockRangeBabylonFinalized(t, sdkClient, 1, refD0) fi.OnEvent(TryFinalizeEvent{}) emitter.AssertExpectations(t) l1F.AssertExpectations(t) @@ -481,43 +369,12 @@ func TestEngineQueue_Finalize(t *testing.T) { fi.OnEvent(FinalizeL1Event{FinalizedL1: refH}) emitter.AssertExpectations(t) - queryBlocksThree := make([]*cwclient.L2Block, 5) - queryBlocksThree[0] = &cwclient.L2Block{ - BlockHeight: refD1.Number, - BlockHash: refD1.Hash.String(), - BlockTimestamp: refD1.Time, - } - queryBlocksThree[1] = &cwclient.L2Block{ - BlockHeight: refE0.Number, - BlockHash: refE0.Hash.String(), - BlockTimestamp: refE0.Time, - } - queryBlocksThree[2] = &cwclient.L2Block{ - BlockHeight: refE1.Number, - BlockHash: refE1.Hash.String(), - BlockTimestamp: refE1.Time, - } - queryBlocksThree[3] = &cwclient.L2Block{ - BlockHeight: refF0.Number, - BlockHash: refF0.Hash.String(), - BlockTimestamp: refF0.Time, - } - queryBlocksThree[4] = &cwclient.L2Block{ - BlockHeight: refF1.Number, - BlockHash: refF1.Hash.String(), - BlockTimestamp: refF1.Time, - } - sdkClient.EXPECT().QueryBlockRangeBabylonFinalized(queryBlocksThree).Return(&refF1.Number, nil).AnyTimes() - // F1 should be finalized now, since it was included in H emitter.ExpectOnce(engine.PromoteFinalizedEvent{Ref: refF1}) l1F.ExpectL1BlockRefByNumber(refH.Number, refH, nil) l1F.ExpectL1BlockRefByNumber(refH.Number, refH, nil) - l2F.ExpectL2BlockRefByNumber(refD1.Number, refD1, nil) - l2F.ExpectL2BlockRefByNumber(refE0.Number, refE0, nil) - l2F.ExpectL2BlockRefByNumber(refE1.Number, refE1, nil) - l2F.ExpectL2BlockRefByNumber(refF0.Number, refF0, nil) - l2F.ExpectL2BlockRefByNumber(refF1.Number, refF1, nil) + helperL2BlockRefByNumberWithTimes(t, l2F, 1, refD1, refE0, refE1, refF0, refF1) + helperSdkClientQueryBlockRangeBabylonFinalized(t, sdkClient, 1, refD1, refE0, refE1, refF0, refF1) fi.OnEvent(TryFinalizeEvent{}) emitter.AssertExpectations(t) l1F.AssertExpectations(t) @@ -534,34 +391,15 @@ func TestEngineQueue_Finalize(t *testing.T) { l2F := &testutils.MockL2Client{} defer l2F.AssertExpectations(t) - l2F.ExpectL2BlockRefByNumber(refA1.Number, refA1, nil) - l2F.ExpectL2BlockRefByNumber(refB0.Number, refB0, nil) - l2F.ExpectL2BlockRefByNumber(refB1.Number, refB1, nil) + helperL2BlockRefByNumberWithTimes(t, l2F, 1, refA1, refB0, refB1) + emitter := &testutils.MockEmitter{} + fi := NewFinalizer(context.Background(), logger, &rollup.Config{BabylonConfig: babylonCfg}, l1F, l2F, emitter) ctl := gomock.NewController(t) defer ctl.Finish() sdkClient := mocks.NewMockISdkClient(ctl) - queryBlocks := make([]*cwclient.L2Block, refB1.Number) - queryBlocks[0] = &cwclient.L2Block{ - BlockHeight: refA1.Number, - BlockHash: refA1.Hash.String(), - BlockTimestamp: refA1.Time, - } - queryBlocks[1] = &cwclient.L2Block{ - BlockHeight: refB0.Number, - BlockHash: refB0.Hash.String(), - BlockTimestamp: refB0.Time, - } - queryBlocks[2] = &cwclient.L2Block{ - BlockHeight: refB1.Number, - BlockHash: refB1.Hash.String(), - BlockTimestamp: refB1.Time, - } - sdkClient.EXPECT().QueryBlockRangeBabylonFinalized(queryBlocks).Return(&refB1.Number, nil) - - emitter := &testutils.MockEmitter{} - fi := NewFinalizer(context.Background(), logger, &rollup.Config{BabylonConfig: babylonCfg}, l1F, l2F, emitter) fi.babylonFinalityClient = sdkClient + helperSdkClientQueryBlockRangeBabylonFinalized(t, sdkClient, 1, refA1, refB0, refB1) // now say B1 was included in C and became the new safe head fi.OnEvent(engine.SafeDerivedEvent{Safe: refB1, DerivedFrom: refC}) @@ -599,12 +437,11 @@ func TestEngineQueue_Finalize(t *testing.T) { l2F := &testutils.MockL2Client{} defer l2F.AssertExpectations(t) + emitter := &testutils.MockEmitter{} + fi := NewFinalizer(context.Background(), logger, &rollup.Config{BabylonConfig: babylonCfg}, l1F, l2F, emitter) ctl := gomock.NewController(t) defer ctl.Finish() sdkClient := mocks.NewMockISdkClient(ctl) - - emitter := &testutils.MockEmitter{} - fi := NewFinalizer(context.Background(), logger, &rollup.Config{BabylonConfig: babylonCfg}, l1F, l2F, emitter) fi.babylonFinalityClient = sdkClient // now say B1 was included in C and became the new safe head @@ -638,48 +475,9 @@ func TestEngineQueue_Finalize(t *testing.T) { fi.OnEvent(engine.SafeDerivedEvent{Safe: refC0Alt, DerivedFrom: refDAlt}) fi.OnEvent(engine.SafeDerivedEvent{Safe: refC1Alt, DerivedFrom: refDAlt}) - l2F.ExpectL2BlockRefByNumber(refA1.Number, refA1, nil) - l2F.ExpectL2BlockRefByNumber(refB0.Number, refB0, nil) - l2F.ExpectL2BlockRefByNumber(refB1.Number, refB1, nil) - l2F.ExpectL2BlockRefByNumber(refC0Alt.Number, refC0Alt, nil) - l2F.ExpectL2BlockRefByNumber(refC1Alt.Number, refC1Alt, nil) - - l2F.ExpectL2BlockRefByNumber(refA1.Number, refA1, nil) - l2F.ExpectL2BlockRefByNumber(refB0.Number, refB0, nil) - l2F.ExpectL2BlockRefByNumber(refB1.Number, refB1, nil) - l2F.ExpectL2BlockRefByNumber(refC0Alt.Number, refC0Alt, nil) - l2F.ExpectL2BlockRefByNumber(refC1Alt.Number, refC1Alt, nil) - - queryBlocks := make([]*cwclient.L2Block, refB1.Number) - queryBlocks[0] = &cwclient.L2Block{ - BlockHeight: refA1.Number, - BlockHash: refA1.Hash.String(), - BlockTimestamp: refA1.Time, - } - queryBlocks[1] = &cwclient.L2Block{ - BlockHeight: refB0.Number, - BlockHash: refB0.Hash.String(), - BlockTimestamp: refB0.Time, - } - queryBlocks[2] = &cwclient.L2Block{ - BlockHeight: refB1.Number, - BlockHash: refB1.Hash.String(), - BlockTimestamp: refB1.Time, - } - sdkClient.EXPECT().QueryBlockRangeBabylonFinalized(queryBlocks).Return(&refB1.Number, nil).AnyTimes() - - queryBlocksTwo := make([]*cwclient.L2Block, 2) - queryBlocksTwo[0] = &cwclient.L2Block{ - BlockHeight: refC0Alt.Number, - BlockHash: refC0Alt.Hash.String(), - BlockTimestamp: refC0Alt.Time, - } - queryBlocksTwo[1] = &cwclient.L2Block{ - BlockHeight: refC1Alt.Number, - BlockHash: refC1Alt.Hash.String(), - BlockTimestamp: refC1Alt.Time, - } - sdkClient.EXPECT().QueryBlockRangeBabylonFinalized(queryBlocksTwo).Return(&refC1Alt.Number, nil).AnyTimes() + helperL2BlockRefByNumberWithTimes(t, l2F, 2, refA1, refB0, refB1, refC0Alt, refC1Alt) + helperSdkClientQueryBlockRangeBabylonFinalized(t, sdkClient, 2, refA1, refB0, refB1) + helperSdkClientQueryBlockRangeBabylonFinalized(t, sdkClient, 2, refC0Alt, refC1Alt) // We get an early finality signal for F, of the chain that did not include refC0Alt and refC1Alt, // as L1 block F does not build on DAlt. @@ -721,33 +519,8 @@ func TestEngineQueue_Finalize(t *testing.T) { // and don't expect a finality attempt. emitter.AssertExpectations(t) - l2F.ExpectL2BlockRefByNumber(refA1.Number, refA1, nil) - l2F.ExpectL2BlockRefByNumber(refB0.Number, refB0, nil) - l2F.ExpectL2BlockRefByNumber(refB1.Number, refB1, nil) - l2F.ExpectL2BlockRefByNumber(refC0.Number, refC0, nil) - - queryBlocksThree := make([]*cwclient.L2Block, refC0.Number) - queryBlocksThree[0] = &cwclient.L2Block{ - BlockHeight: refA1.Number, - BlockHash: refA1.Hash.String(), - BlockTimestamp: refA1.Time, - } - queryBlocksThree[1] = &cwclient.L2Block{ - BlockHeight: refB0.Number, - BlockHash: refB0.Hash.String(), - BlockTimestamp: refB0.Time, - } - queryBlocksThree[2] = &cwclient.L2Block{ - BlockHeight: refB1.Number, - BlockHash: refB1.Hash.String(), - BlockTimestamp: refB1.Time, - } - queryBlocksThree[3] = &cwclient.L2Block{ - BlockHeight: refC0.Number, - BlockHash: refC0.Hash.String(), - BlockTimestamp: refC0.Time, - } - sdkClient.EXPECT().QueryBlockRangeBabylonFinalized(queryBlocksThree).Return(&refC0.Number, nil) + helperL2BlockRefByNumberWithTimes(t, l2F, 1, refA1, refB0, refB1, refC0) + helperSdkClientQueryBlockRangeBabylonFinalized(t, sdkClient, 1, refA1, refB0, refB1, refC0) // if we reset the attempt, then we can finalize however. fi.triedFinalizeAt = 0 @@ -759,3 +532,21 @@ func TestEngineQueue_Finalize(t *testing.T) { emitter.AssertExpectations(t) }) } + +func helperL2BlockRefByNumberWithTimes(t *testing.T, l2F *testutils.MockL2Client, times int, refs ...eth.L2BlockRef) { + for _, ref := range refs { + l2F.ExpectL2BlockRefByNumberWithTimes(ref.Number, ref, times, nil) + } +} + +func helperSdkClientQueryBlockRangeBabylonFinalized(t *testing.T, sdkClient *mocks.MockISdkClient, times int, refs ...eth.L2BlockRef) { + queryBlocks := make([]*cwclient.L2Block, len(refs)) + for i, ref := range refs { + queryBlocks[i] = &cwclient.L2Block{ + BlockHeight: ref.Number, + BlockHash: ref.Hash.String(), + BlockTimestamp: ref.Time, + } + } + sdkClient.EXPECT().QueryBlockRangeBabylonFinalized(queryBlocks).Return(&refs[len(refs)-1].Number, nil).Times(times) +} diff --git a/op-service/testutils/mock_l2.go b/op-service/testutils/mock_l2.go index f6de3a44b905..650f39e49369 100644 --- a/op-service/testutils/mock_l2.go +++ b/op-service/testutils/mock_l2.go @@ -29,6 +29,10 @@ func (m *MockL2Client) ExpectL2BlockRefByNumber(num uint64, ref eth.L2BlockRef, m.Mock.On("L2BlockRefByNumber", num).Once().Return(ref, &err) } +func (m *MockL2Client) ExpectL2BlockRefByNumberWithTimes(num uint64, ref eth.L2BlockRef, times int, err error) { + m.Mock.On("L2BlockRefByNumber", num).Times(times).Return(ref, &err) +} + func (c *MockL2Client) L2BlockRefByHash(ctx context.Context, hash common.Hash) (eth.L2BlockRef, error) { out := c.Mock.Called(hash) return out.Get(0).(eth.L2BlockRef), out.Error(1) From cd52f36f2a5ae419fd7b1ee3ede3fb69ecedd8f7 Mon Sep 17 00:00:00 2001 From: lesterli Date: Mon, 29 Jul 2024 21:21:24 +0800 Subject: [PATCH 09/15] fix plasma test --- op-node/rollup/finality/plasma_test.go | 370 ++++++++++++++----------- 1 file changed, 208 insertions(+), 162 deletions(-) diff --git a/op-node/rollup/finality/plasma_test.go b/op-node/rollup/finality/plasma_test.go index 6e7da6773f0e..072914b88213 100644 --- a/op-node/rollup/finality/plasma_test.go +++ b/op-node/rollup/finality/plasma_test.go @@ -4,8 +4,23 @@ import ( // nosemgrep + "context" + "math/rand" + "testing" + + "github.com/babylonchain/babylon-finality-gadget/sdk/cwclient" + "github.com/babylonchain/babylon-finality-gadget/testutil/mocks" + "github.com/ethereum-optimism/optimism/op-node/rollup" + "github.com/ethereum-optimism/optimism/op-node/rollup/derive" + "github.com/ethereum-optimism/optimism/op-node/rollup/engine" plasma "github.com/ethereum-optimism/optimism/op-plasma" "github.com/ethereum-optimism/optimism/op-service/eth" + "github.com/ethereum-optimism/optimism/op-service/testlog" + "github.com/ethereum-optimism/optimism/op-service/testutils" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/log" + "github.com/stretchr/testify/require" + "go.uber.org/mock/gomock" ) type fakePlasmaBackend struct { @@ -23,165 +38,196 @@ func (b *fakePlasmaBackend) OnFinalizedHeadSignal(f plasma.HeadSignalFn) { var _ PlasmaBackend = (*fakePlasmaBackend)(nil) -// TODO: Fix this test -// func TestPlasmaFinalityData(t *testing.T) { -// logger := testlog.Logger(t, log.LevelInfo) -// l1F := &testutils.MockL1Source{} -// l2F := &testutils.MockL2Client{} - -// rng := rand.New(rand.NewSource(1234)) - -// refA := testutils.RandomBlockRef(rng) -// refA0 := eth.L2BlockRef{ -// Hash: testutils.RandomHash(rng), -// Number: 0, -// ParentHash: common.Hash{}, -// Time: refA.Time, -// L1Origin: refA.ID(), -// SequenceNumber: 0, -// } - -// cfg := &rollup.Config{ -// Genesis: rollup.Genesis{ -// L1: refA.ID(), -// L2: refA0.ID(), -// L2Time: refA0.Time, -// SystemConfig: eth.SystemConfig{ -// BatcherAddr: common.Address{42}, -// Overhead: [32]byte{123}, -// Scalar: [32]byte{42}, -// GasLimit: 20_000_000, -// }, -// }, -// BlockTime: 1, -// SeqWindowSize: 2, -// } -// plasmaCfg := &rollup.PlasmaConfig{ -// DAChallengeWindow: 90, -// DAResolveWindow: 90, -// } -// babylonCfg := &rollup.BabylonConfig{ -// ChainID: "chain-test", -// ContractAddress: "bbn1eyfccmjm6732k7wp4p6gdjwhxjwsvje44j0hfx8nkgrm8fs7vqfsa3n3gc", -// BitcoinRpc: "https://rpc.ankr.com/btc", -// } -// cfg.BabylonConfig = babylonCfg -// // shoud return l1 finality if plasma is not enabled -// require.Equal(t, uint64(defaultFinalityLookback), calcFinalityLookback(cfg)) - -// cfg.PlasmaConfig = plasmaCfg -// expFinalityLookback := 181 -// require.Equal(t, uint64(expFinalityLookback), calcFinalityLookback(cfg)) - -// refA1 := eth.L2BlockRef{ -// Hash: testutils.RandomHash(rng), -// Number: refA0.Number + 1, -// ParentHash: refA0.Hash, -// Time: refA0.Time + cfg.BlockTime, -// L1Origin: refA.ID(), -// SequenceNumber: 1, -// } - -// // Simulate plasma finality by waiting for the finalized-inclusion -// // of a commitment to turn into undisputed finalized data. -// commitmentInclusionFinalized := eth.L1BlockRef{} -// plasmaBackend := &fakePlasmaBackend{ -// plasmaFn: func(ref eth.L1BlockRef) { -// commitmentInclusionFinalized = ref -// }, -// forwardTo: nil, -// } - -// emitter := &testutils.MockEmitter{} -// fi := NewPlasmaFinalizer(context.Background(), logger, cfg, l1F, l2F, emitter, plasmaBackend) -// require.NotNil(t, plasmaBackend.forwardTo, "plasma backend must have access to underlying standard finalizer") - -// require.Equal(t, expFinalityLookback, cap(fi.finalityData)) - -// l1parent := refA -// l2parent := refA1 - -// // advance over 200 l1 origins each time incrementing new l2 safe heads -// // and post processing. -// for i := uint64(0); i < 200; i++ { -// if i == 10 { // finalize a L1 commitment -// fi.OnEvent(FinalizeL1Event{FinalizedL1: l1parent}) -// emitter.AssertExpectations(t) // no events emitted upon L1 finality -// require.Equal(t, l1parent, commitmentInclusionFinalized, "plasma backend received L1 signal") -// } - -// previous := l1parent -// l1parent = eth.L1BlockRef{ -// Hash: testutils.RandomHash(rng), -// Number: previous.Number + 1, -// ParentHash: previous.Hash, -// Time: previous.Time + 12, -// } - -// for j := uint64(0); j < 2; j++ { -// l2parent = eth.L2BlockRef{ -// Hash: testutils.RandomHash(rng), -// Number: l2parent.Number + 1, -// ParentHash: l2parent.Hash, -// Time: l2parent.Time + cfg.BlockTime, -// L1Origin: previous.ID(), // reference previous origin, not the block the batch was included in -// SequenceNumber: j, -// } -// fi.OnEvent(engine.SafeDerivedEvent{Safe: l2parent, DerivedFrom: l1parent}) -// emitter.AssertExpectations(t) -// } -// // might trigger finalization attempt, if expired finality delay -// emitter.ExpectMaybeRun(func(ev rollup.Event) { -// require.IsType(t, TryFinalizeEvent{}, ev) -// }) -// fi.OnEvent(derive.DeriverIdleEvent{}) -// emitter.AssertExpectations(t) -// // clear expectations -// emitter.Mock.ExpectedCalls = nil - -// // no L2 finalize event, as no L1 finality signal has been forwarded by plasma backend yet -// fi.OnEvent(TryFinalizeEvent{}) -// emitter.AssertExpectations(t) - -// // Pretend to be the plasma backend, -// // send the original finalization signal to the underlying finalizer, -// // now that we are sure the commitment itself is not just finalized, -// // but the referenced data cannot be disputed anymore. -// plasmaFinalization := commitmentInclusionFinalized.Number + cfg.PlasmaConfig.DAChallengeWindow -// if commitmentInclusionFinalized != (eth.L1BlockRef{}) && l1parent.Number == plasmaFinalization { -// // When the signal is forwarded, a finalization attempt will be scheduled -// emitter.ExpectOnce(TryFinalizeEvent{}) -// plasmaBackend.forwardTo(commitmentInclusionFinalized) -// emitter.AssertExpectations(t) -// require.Equal(t, commitmentInclusionFinalized, fi.finalizedL1, "finality signal now made its way in regular finalizer") - -// // As soon as a finalization attempt is made, after the finality signal was triggered by plasma backend, -// // we should get an attempt to get a finalized L2 block. -// // In this test the L1 origin of the simulated L2 blocks lags 1 behind the block the L2 block is included in on L1. -// // So to check the L2 finality progress, we check if the next L1 block after the L1 origin -// // of the safe block matches that of the finalized L1 block. -// l1F.ExpectL1BlockRefByNumber(commitmentInclusionFinalized.Number, commitmentInclusionFinalized, nil) -// l1F.ExpectL1BlockRefByNumber(commitmentInclusionFinalized.Number, commitmentInclusionFinalized, nil) -// var finalizedL2 eth.L2BlockRef -// emitter.ExpectOnceRun(func(ev rollup.Event) { -// if x, ok := ev.(engine.PromoteFinalizedEvent); ok { -// finalizedL2 = x.Ref -// } else { -// t.Fatalf("expected L2 finalization, but got: %s", ev) -// } -// }) -// fi.OnEvent(TryFinalizeEvent{}) -// l1F.AssertExpectations(t) -// l2F.AssertExpectations(t) -// emitter.AssertExpectations(t) -// require.Equal(t, commitmentInclusionFinalized.Number, finalizedL2.L1Origin.Number+1) -// // Confirm finalization, so there will be no repeats of the PromoteFinalizedEvent -// fi.OnEvent(engine.ForkchoiceUpdateEvent{FinalizedL2Head: finalizedL2}) -// emitter.AssertExpectations(t) -// } -// } - -// // finality data does not go over challenge + resolve windows + 1 capacity -// // (prunes down to 180 then adds the extra 1 each time) -// require.Equal(t, expFinalityLookback, len(fi.finalityData)) -// } +func TestPlasmaFinalityData(t *testing.T) { + logger := testlog.Logger(t, log.LevelInfo) + l1F := &testutils.MockL1Source{} + l2F := &testutils.MockL2Client{} + + rng := rand.New(rand.NewSource(1234)) + + refA := testutils.RandomBlockRef(rng) + refA0 := eth.L2BlockRef{ + Hash: testutils.RandomHash(rng), + Number: 0, + ParentHash: common.Hash{}, + Time: refA.Time, + L1Origin: refA.ID(), + SequenceNumber: 0, + } + + cfg := &rollup.Config{ + Genesis: rollup.Genesis{ + L1: refA.ID(), + L2: refA0.ID(), + L2Time: refA0.Time, + SystemConfig: eth.SystemConfig{ + BatcherAddr: common.Address{42}, + Overhead: [32]byte{123}, + Scalar: [32]byte{42}, + GasLimit: 20_000_000, + }, + }, + BlockTime: 1, + SeqWindowSize: 2, + } + plasmaCfg := &rollup.PlasmaConfig{ + DAChallengeWindow: 90, + DAResolveWindow: 90, + } + babylonCfg := &rollup.BabylonConfig{ + ChainID: "chain-test", + ContractAddress: "bbn1eyfccmjm6732k7wp4p6gdjwhxjwsvje44j0hfx8nkgrm8fs7vqfsa3n3gc", + BitcoinRpc: "https://rpc.this-is-a-mock.com/btc", + } + cfg.BabylonConfig = babylonCfg + // shoud return l1 finality if plasma is not enabled + require.Equal(t, uint64(defaultFinalityLookback), calcFinalityLookback(cfg)) + + cfg.PlasmaConfig = plasmaCfg + expFinalityLookback := 181 + require.Equal(t, uint64(expFinalityLookback), calcFinalityLookback(cfg)) + + refA1 := eth.L2BlockRef{ + Hash: testutils.RandomHash(rng), + Number: refA0.Number + 1, + ParentHash: refA0.Hash, + Time: refA0.Time + cfg.BlockTime, + L1Origin: refA.ID(), + SequenceNumber: 1, + } + + // Simulate plasma finality by waiting for the finalized-inclusion + // of a commitment to turn into undisputed finalized data. + commitmentInclusionFinalized := eth.L1BlockRef{} + plasmaBackend := &fakePlasmaBackend{ + plasmaFn: func(ref eth.L1BlockRef) { + commitmentInclusionFinalized = ref + }, + forwardTo: nil, + } + + emitter := &testutils.MockEmitter{} + fi := NewPlasmaFinalizer(context.Background(), logger, cfg, l1F, l2F, emitter, plasmaBackend) + require.NotNil(t, plasmaBackend.forwardTo, "plasma backend must have access to underlying standard finalizer") + ctl := gomock.NewController(t) + defer ctl.Finish() + sdkClient := mocks.NewMockISdkClient(ctl) + fi.babylonFinalityClient = sdkClient + + require.Equal(t, expFinalityLookback, cap(fi.finalityData)) + + l1parent := refA + l2parent := refA1 + + // advance over 200 l1 origins each time incrementing new l2 safe heads + // and post processing. + for i := uint64(0); i < 200; i++ { + if i == 10 { // finalize a L1 commitment + fi.OnEvent(FinalizeL1Event{FinalizedL1: l1parent}) + emitter.AssertExpectations(t) // no events emitted upon L1 finality + require.Equal(t, l1parent, commitmentInclusionFinalized, "plasma backend received L1 signal") + } + + previous := l1parent + l1parent = eth.L1BlockRef{ + Hash: testutils.RandomHash(rng), + Number: previous.Number + 1, + ParentHash: previous.Hash, + Time: previous.Time + 12, + } + + mockL2Refs := []eth.L2BlockRef{} + if i == 0 { + mockL2Refs = append(mockL2Refs, l2parent) + l2F.ExpectL2BlockRefByNumber(l2parent.Number, l2parent, nil) + } + for j := uint64(0); j < 2; j++ { + l2parent = eth.L2BlockRef{ + Hash: testutils.RandomHash(rng), + Number: l2parent.Number + 1, + ParentHash: l2parent.Hash, + Time: l2parent.Time + cfg.BlockTime, + L1Origin: previous.ID(), // reference previous origin, not the block the batch was included in + SequenceNumber: j, + } + + if i < 10 { + mockL2Refs = append(mockL2Refs, l2parent) + l2F.ExpectL2BlockRefByNumber(l2parent.Number, l2parent, nil) + } + fi.OnEvent(engine.SafeDerivedEvent{Safe: l2parent, DerivedFrom: l1parent}) + emitter.AssertExpectations(t) + } + if i < 10 { + mockSdkClientQueryBlockRangeBabylonFinalized(t, sdkClient, mockL2Refs) + } + + // might trigger finalization attempt, if expired finality delay + emitter.ExpectMaybeRun(func(ev rollup.Event) { + require.IsType(t, TryFinalizeEvent{}, ev) + }) + fi.OnEvent(derive.DeriverIdleEvent{}) + emitter.AssertExpectations(t) + // clear expectations + emitter.Mock.ExpectedCalls = nil + + // no L2 finalize event, as no L1 finality signal has been forwarded by plasma backend yet + fi.OnEvent(TryFinalizeEvent{}) + emitter.AssertExpectations(t) + + // Pretend to be the plasma backend, + // send the original finalization signal to the underlying finalizer, + // now that we are sure the commitment itself is not just finalized, + // but the referenced data cannot be disputed anymore. + plasmaFinalization := commitmentInclusionFinalized.Number + cfg.PlasmaConfig.DAChallengeWindow + if commitmentInclusionFinalized != (eth.L1BlockRef{}) && l1parent.Number == plasmaFinalization { + // When the signal is forwarded, a finalization attempt will be scheduled + emitter.ExpectOnce(TryFinalizeEvent{}) + plasmaBackend.forwardTo(commitmentInclusionFinalized) + emitter.AssertExpectations(t) + require.Equal(t, commitmentInclusionFinalized, fi.finalizedL1, "finality signal now made its way in regular finalizer") + + // As soon as a finalization attempt is made, after the finality signal was triggered by plasma backend, + // we should get an attempt to get a finalized L2 block. + // In this test the L1 origin of the simulated L2 blocks lags 1 behind the block the L2 block is included in on L1. + // So to check the L2 finality progress, we check if the next L1 block after the L1 origin + // of the safe block matches that of the finalized L1 block. + l1F.ExpectL1BlockRefByNumber(commitmentInclusionFinalized.Number, commitmentInclusionFinalized, nil) + l1F.ExpectL1BlockRefByNumber(commitmentInclusionFinalized.Number, commitmentInclusionFinalized, nil) + + var finalizedL2 eth.L2BlockRef + emitter.ExpectOnceRun(func(ev rollup.Event) { + if x, ok := ev.(engine.PromoteFinalizedEvent); ok { + finalizedL2 = x.Ref + } else { + t.Fatalf("expected L2 finalization, but got: %s", ev) + } + }) + + fi.OnEvent(TryFinalizeEvent{}) + l1F.AssertExpectations(t) + l2F.AssertExpectations(t) + emitter.AssertExpectations(t) + require.Equal(t, commitmentInclusionFinalized.Number, finalizedL2.L1Origin.Number+1) + // Confirm finalization, so there will be no repeats of the PromoteFinalizedEvent + fi.OnEvent(engine.ForkchoiceUpdateEvent{FinalizedL2Head: finalizedL2}) + emitter.AssertExpectations(t) + } + } + + // finality data does not go over challenge + resolve windows + 1 capacity + // (prunes down to 180 then adds the extra 1 each time) + require.Equal(t, expFinalityLookback, len(fi.finalityData)) +} + +func mockSdkClientQueryBlockRangeBabylonFinalized(t *testing.T, sdkClient *mocks.MockISdkClient, refs []eth.L2BlockRef) { + queryBlocks := make([]*cwclient.L2Block, len(refs)) + for i, ref := range refs { + queryBlocks[i] = &cwclient.L2Block{ + BlockHeight: ref.Number, + BlockHash: ref.Hash.String(), + BlockTimestamp: ref.Time, + } + } + sdkClient.EXPECT().QueryBlockRangeBabylonFinalized(queryBlocks).Return(&refs[len(refs)-1].Number, nil) +} From 7c4ba8d7de28de38c113289ad25f3bc7b7d3765a Mon Sep 17 00:00:00 2001 From: lesterli Date: Mon, 29 Jul 2024 21:39:29 +0800 Subject: [PATCH 10/15] fix typo --- op-node/rollup/finality/finalizer.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/op-node/rollup/finality/finalizer.go b/op-node/rollup/finality/finalizer.go index 00df39e56c97..e7a2e35b1609 100644 --- a/op-node/rollup/finality/finalizer.go +++ b/op-node/rollup/finality/finalizer.go @@ -253,10 +253,10 @@ func (fi *Finalizer) tryFinalize() { finalizedL2 = *lastFinalizedBlock finalizedDerivedFrom = fd.L1Block } - fi.log.Debug("set finalized block", "finalized_l2", finalizedL2, "finalized_derived_from", finalizedDerivedFrom, "fd_l2_block", fd.L2Block) + fi.log.Debug("set finalized block", "finalized_derived_from", finalizedDerivedFrom, "fd_l2_block", fd.L2Block) // some blocks in the queried range is not BTC finalized, stop iterating to honor consecutive quorom - if lastFinalizedBlock == nil || finalizedL2.Number != fd.L2Block.Number { + if lastFinalizedBlock == nil || lastFinalizedBlock.Number != fd.L2Block.Number { break } From e22733785870ad9af025aa8ad3e6532d6bc6925d Mon Sep 17 00:00:00 2001 From: lesterli Date: Mon, 29 Jul 2024 21:49:46 +0800 Subject: [PATCH 11/15] rename and remove unused t --- op-node/rollup/finality/finalizer_test.go | 38 +++++++++++------------ 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/op-node/rollup/finality/finalizer_test.go b/op-node/rollup/finality/finalizer_test.go index 6225ef805aad..9277b9f17383 100644 --- a/op-node/rollup/finality/finalizer_test.go +++ b/op-node/rollup/finality/finalizer_test.go @@ -201,7 +201,7 @@ func TestEngineQueue_Finalize(t *testing.T) { l2F := &testutils.MockL2Client{} defer l2F.AssertExpectations(t) - helperL2BlockRefByNumberWithTimes(t, l2F, 1, refA1, refB0, refB1, refC0, refC1) + mockL2BlockRefByNumberWithTimes(l2F, 1, refA1, refB0, refB1, refC0, refC1) emitter := &testutils.MockEmitter{} fi := NewFinalizer(context.Background(), logger, &rollup.Config{BabylonConfig: babylonCfg}, l1F, l2F, emitter) @@ -210,7 +210,7 @@ func TestEngineQueue_Finalize(t *testing.T) { defer ctl.Finish() sdkClient := mocks.NewMockISdkClient(ctl) fi.babylonFinalityClient = sdkClient - helperSdkClientQueryBlockRangeBabylonFinalized(t, sdkClient, 1, refA1, refB0, refB1, refC0, refC1) + mockQueryBlockRangeBabylonFinalizedWithTimes(sdkClient, 1, refA1, refB0, refB1, refC0, refC1) // now say C1 was included in D and became the new safe head fi.OnEvent(engine.SafeDerivedEvent{Safe: refC1, DerivedFrom: refD}) @@ -245,7 +245,7 @@ func TestEngineQueue_Finalize(t *testing.T) { l2F := &testutils.MockL2Client{} defer l2F.AssertExpectations(t) - helperL2BlockRefByNumberWithTimes(t, l2F, 2, refA1, refB0, refB1, refC0, refC1) + mockL2BlockRefByNumberWithTimes(l2F, 2, refA1, refB0, refB1, refC0, refC1) emitter := &testutils.MockEmitter{} fi := NewFinalizer(context.Background(), logger, &rollup.Config{BabylonConfig: babylonCfg}, l1F, l2F, emitter) @@ -253,7 +253,7 @@ func TestEngineQueue_Finalize(t *testing.T) { defer ctl.Finish() sdkClient := mocks.NewMockISdkClient(ctl) fi.babylonFinalityClient = sdkClient - helperSdkClientQueryBlockRangeBabylonFinalized(t, sdkClient, 2, refA1, refB0, refB1, refC0, refC1) + mockQueryBlockRangeBabylonFinalizedWithTimes(sdkClient, 2, refA1, refB0, refB1, refC0, refC1) // now say C1 was included in D and became the new safe head fi.OnEvent(engine.SafeDerivedEvent{Safe: refC1, DerivedFrom: refD}) @@ -318,8 +318,8 @@ func TestEngineQueue_Finalize(t *testing.T) { emitter.ExpectOnce(engine.PromoteFinalizedEvent{Ref: refC1}) l1F.ExpectL1BlockRefByNumber(refD.Number, refD, nil) l1F.ExpectL1BlockRefByNumber(refD.Number, refD, nil) - helperL2BlockRefByNumberWithTimes(t, l2F, 2, refA1, refB0, refB1, refC0, refC1) - helperSdkClientQueryBlockRangeBabylonFinalized(t, sdkClient, 2, refA1, refB0, refB1, refC0, refC1) + mockL2BlockRefByNumberWithTimes(l2F, 2, refA1, refB0, refB1, refC0, refC1) + mockQueryBlockRangeBabylonFinalizedWithTimes(sdkClient, 2, refA1, refB0, refB1, refC0, refC1) fi.OnEvent(TryFinalizeEvent{}) emitter.AssertExpectations(t) l1F.AssertExpectations(t) @@ -333,8 +333,8 @@ func TestEngineQueue_Finalize(t *testing.T) { emitter.ExpectOnce(engine.PromoteFinalizedEvent{Ref: refD0}) l1F.ExpectL1BlockRefByNumber(refE.Number, refE, nil) l1F.ExpectL1BlockRefByNumber(refE.Number, refE, nil) - helperL2BlockRefByNumberWithTimes(t, l2F, 1, refD0) - helperSdkClientQueryBlockRangeBabylonFinalized(t, sdkClient, 1, refD0) + mockL2BlockRefByNumberWithTimes(l2F, 1, refD0) + mockQueryBlockRangeBabylonFinalizedWithTimes(sdkClient, 1, refD0) fi.OnEvent(TryFinalizeEvent{}) emitter.AssertExpectations(t) l1F.AssertExpectations(t) @@ -373,8 +373,8 @@ func TestEngineQueue_Finalize(t *testing.T) { emitter.ExpectOnce(engine.PromoteFinalizedEvent{Ref: refF1}) l1F.ExpectL1BlockRefByNumber(refH.Number, refH, nil) l1F.ExpectL1BlockRefByNumber(refH.Number, refH, nil) - helperL2BlockRefByNumberWithTimes(t, l2F, 1, refD1, refE0, refE1, refF0, refF1) - helperSdkClientQueryBlockRangeBabylonFinalized(t, sdkClient, 1, refD1, refE0, refE1, refF0, refF1) + mockL2BlockRefByNumberWithTimes(l2F, 1, refD1, refE0, refE1, refF0, refF1) + mockQueryBlockRangeBabylonFinalizedWithTimes(sdkClient, 1, refD1, refE0, refE1, refF0, refF1) fi.OnEvent(TryFinalizeEvent{}) emitter.AssertExpectations(t) l1F.AssertExpectations(t) @@ -391,7 +391,7 @@ func TestEngineQueue_Finalize(t *testing.T) { l2F := &testutils.MockL2Client{} defer l2F.AssertExpectations(t) - helperL2BlockRefByNumberWithTimes(t, l2F, 1, refA1, refB0, refB1) + mockL2BlockRefByNumberWithTimes(l2F, 1, refA1, refB0, refB1) emitter := &testutils.MockEmitter{} fi := NewFinalizer(context.Background(), logger, &rollup.Config{BabylonConfig: babylonCfg}, l1F, l2F, emitter) @@ -399,7 +399,7 @@ func TestEngineQueue_Finalize(t *testing.T) { defer ctl.Finish() sdkClient := mocks.NewMockISdkClient(ctl) fi.babylonFinalityClient = sdkClient - helperSdkClientQueryBlockRangeBabylonFinalized(t, sdkClient, 1, refA1, refB0, refB1) + mockQueryBlockRangeBabylonFinalizedWithTimes(sdkClient, 1, refA1, refB0, refB1) // now say B1 was included in C and became the new safe head fi.OnEvent(engine.SafeDerivedEvent{Safe: refB1, DerivedFrom: refC}) @@ -475,9 +475,9 @@ func TestEngineQueue_Finalize(t *testing.T) { fi.OnEvent(engine.SafeDerivedEvent{Safe: refC0Alt, DerivedFrom: refDAlt}) fi.OnEvent(engine.SafeDerivedEvent{Safe: refC1Alt, DerivedFrom: refDAlt}) - helperL2BlockRefByNumberWithTimes(t, l2F, 2, refA1, refB0, refB1, refC0Alt, refC1Alt) - helperSdkClientQueryBlockRangeBabylonFinalized(t, sdkClient, 2, refA1, refB0, refB1) - helperSdkClientQueryBlockRangeBabylonFinalized(t, sdkClient, 2, refC0Alt, refC1Alt) + mockL2BlockRefByNumberWithTimes(l2F, 2, refA1, refB0, refB1, refC0Alt, refC1Alt) + mockQueryBlockRangeBabylonFinalizedWithTimes(sdkClient, 2, refA1, refB0, refB1) + mockQueryBlockRangeBabylonFinalizedWithTimes(sdkClient, 2, refC0Alt, refC1Alt) // We get an early finality signal for F, of the chain that did not include refC0Alt and refC1Alt, // as L1 block F does not build on DAlt. @@ -519,8 +519,8 @@ func TestEngineQueue_Finalize(t *testing.T) { // and don't expect a finality attempt. emitter.AssertExpectations(t) - helperL2BlockRefByNumberWithTimes(t, l2F, 1, refA1, refB0, refB1, refC0) - helperSdkClientQueryBlockRangeBabylonFinalized(t, sdkClient, 1, refA1, refB0, refB1, refC0) + mockL2BlockRefByNumberWithTimes(l2F, 1, refA1, refB0, refB1, refC0) + mockQueryBlockRangeBabylonFinalizedWithTimes(sdkClient, 1, refA1, refB0, refB1, refC0) // if we reset the attempt, then we can finalize however. fi.triedFinalizeAt = 0 @@ -533,13 +533,13 @@ func TestEngineQueue_Finalize(t *testing.T) { }) } -func helperL2BlockRefByNumberWithTimes(t *testing.T, l2F *testutils.MockL2Client, times int, refs ...eth.L2BlockRef) { +func mockL2BlockRefByNumberWithTimes(l2F *testutils.MockL2Client, times int, refs ...eth.L2BlockRef) { for _, ref := range refs { l2F.ExpectL2BlockRefByNumberWithTimes(ref.Number, ref, times, nil) } } -func helperSdkClientQueryBlockRangeBabylonFinalized(t *testing.T, sdkClient *mocks.MockISdkClient, times int, refs ...eth.L2BlockRef) { +func mockQueryBlockRangeBabylonFinalizedWithTimes(sdkClient *mocks.MockISdkClient, times int, refs ...eth.L2BlockRef) { queryBlocks := make([]*cwclient.L2Block, len(refs)) for i, ref := range refs { queryBlocks[i] = &cwclient.L2Block{ From 65948d0c6961d43f1353656af97b876b1c51b0bd Mon Sep 17 00:00:00 2001 From: lesterli Date: Mon, 29 Jul 2024 21:54:33 +0800 Subject: [PATCH 12/15] rename --- op-node/rollup/finality/plasma_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/op-node/rollup/finality/plasma_test.go b/op-node/rollup/finality/plasma_test.go index 072914b88213..2181bba70ca0 100644 --- a/op-node/rollup/finality/plasma_test.go +++ b/op-node/rollup/finality/plasma_test.go @@ -159,7 +159,7 @@ func TestPlasmaFinalityData(t *testing.T) { emitter.AssertExpectations(t) } if i < 10 { - mockSdkClientQueryBlockRangeBabylonFinalized(t, sdkClient, mockL2Refs) + mockQueryBlockRangeBabylonFinalized(sdkClient, mockL2Refs) } // might trigger finalization attempt, if expired finality delay @@ -220,7 +220,7 @@ func TestPlasmaFinalityData(t *testing.T) { require.Equal(t, expFinalityLookback, len(fi.finalityData)) } -func mockSdkClientQueryBlockRangeBabylonFinalized(t *testing.T, sdkClient *mocks.MockISdkClient, refs []eth.L2BlockRef) { +func mockQueryBlockRangeBabylonFinalized(sdkClient *mocks.MockISdkClient, refs []eth.L2BlockRef) { queryBlocks := make([]*cwclient.L2Block, len(refs)) for i, ref := range refs { queryBlocks[i] = &cwclient.L2Block{ From b6fb74ba950306e0c33f1b99d512171c9aa18192 Mon Sep 17 00:00:00 2001 From: lesterli Date: Mon, 29 Jul 2024 22:37:23 +0800 Subject: [PATCH 13/15] fix log --- op-node/rollup/finality/finalizer.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/op-node/rollup/finality/finalizer.go b/op-node/rollup/finality/finalizer.go index e7a2e35b1609..d4d13275127e 100644 --- a/op-node/rollup/finality/finalizer.go +++ b/op-node/rollup/finality/finalizer.go @@ -252,8 +252,8 @@ func (fi *Finalizer) tryFinalize() { if lastFinalizedBlock != nil { finalizedL2 = *lastFinalizedBlock finalizedDerivedFrom = fd.L1Block + fi.log.Debug("set finalized block", "finalized_l2", finalizedL2, "finalized_derived_from", finalizedDerivedFrom, "fd_l2_block", fd.L2Block) } - fi.log.Debug("set finalized block", "finalized_derived_from", finalizedDerivedFrom, "fd_l2_block", fd.L2Block) // some blocks in the queried range is not BTC finalized, stop iterating to honor consecutive quorom if lastFinalizedBlock == nil || lastFinalizedBlock.Number != fd.L2Block.Number { From 952ade3460917734132d5a0e8e9570ef462ba20a Mon Sep 17 00:00:00 2001 From: lesterli Date: Tue, 13 Aug 2024 16:05:10 +0800 Subject: [PATCH 14/15] update for gadget rpc --- op-node/rollup/finality/plasma_test.go | 37 +++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/op-node/rollup/finality/plasma_test.go b/op-node/rollup/finality/plasma_test.go index f5cd5fc02da0..6319215b0cc1 100644 --- a/op-node/rollup/finality/plasma_test.go +++ b/op-node/rollup/finality/plasma_test.go @@ -10,14 +10,17 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/log" + fgtypes "github.com/babylonlabs-io/finality-gadget/types" "github.com/ethereum-optimism/optimism/op-node/rollup" "github.com/ethereum-optimism/optimism/op-node/rollup/derive" "github.com/ethereum-optimism/optimism/op-node/rollup/engine" "github.com/ethereum-optimism/optimism/op-node/rollup/event" + fgmocks "github.com/ethereum-optimism/optimism/op-node/testutils" plasma "github.com/ethereum-optimism/optimism/op-plasma" "github.com/ethereum-optimism/optimism/op-service/eth" "github.com/ethereum-optimism/optimism/op-service/testlog" "github.com/ethereum-optimism/optimism/op-service/testutils" + "go.uber.org/mock/gomock" ) type fakePlasmaBackend struct { @@ -36,10 +39,6 @@ func (b *fakePlasmaBackend) OnFinalizedHeadSignal(f plasma.HeadSignalFn) { var _ PlasmaBackend = (*fakePlasmaBackend)(nil) func TestPlasmaFinalityData(t *testing.T) { - if true { - // TODO(snapchain): to fix - return - } logger := testlog.Logger(t, log.LevelInfo) l1F := &testutils.MockL1Source{} l2F := &testutils.MockL2Client{} @@ -75,6 +74,7 @@ func TestPlasmaFinalityData(t *testing.T) { DAChallengeWindow: 90, DAResolveWindow: 90, } + cfg.BabylonFinalityGadgetRpc = "https://mock-finality-gadget-rpc.com" // shoud return l1 finality if plasma is not enabled require.Equal(t, uint64(defaultFinalityLookback), calcFinalityLookback(cfg)) @@ -105,6 +105,11 @@ func TestPlasmaFinalityData(t *testing.T) { fi := NewPlasmaFinalizer(context.Background(), logger, cfg, l1F, l2F, plasmaBackend) fi.AttachEmitter(emitter) require.NotNil(t, plasmaBackend.forwardTo, "plasma backend must have access to underlying standard finalizer") + ctl := gomock.NewController(t) + defer ctl.Finish() + fgclient := fgmocks.NewMockIFinalityGadgetClient(ctl) + fi.babylonFinalityClient = fgclient + mockActivatedTimestamp(fgclient) require.Equal(t, expFinalityLookback, cap(fi.finalityData)) @@ -128,6 +133,11 @@ func TestPlasmaFinalityData(t *testing.T) { Time: previous.Time + 12, } + mockL2Refs := []eth.L2BlockRef{} + if i == 0 { + mockL2Refs = append(mockL2Refs, l2parent) + l2F.ExpectL2BlockRefByNumber(l2parent.Number, l2parent, nil) + } for j := uint64(0); j < 2; j++ { l2parent = eth.L2BlockRef{ Hash: testutils.RandomHash(rng), @@ -137,9 +147,16 @@ func TestPlasmaFinalityData(t *testing.T) { L1Origin: previous.ID(), // reference previous origin, not the block the batch was included in SequenceNumber: j, } + if i < 10 { + mockL2Refs = append(mockL2Refs, l2parent) + l2F.ExpectL2BlockRefByNumber(l2parent.Number, l2parent, nil) + } fi.OnEvent(engine.SafeDerivedEvent{Safe: l2parent, DerivedFrom: l1parent}) emitter.AssertExpectations(t) } + if i < 10 { + mockQueryBlockRangeBabylonFinalized(fgclient, mockL2Refs) + } // might trigger finalization attempt, if expired finality delay emitter.ExpectMaybeRun(func(ev event.Event) { require.IsType(t, TryFinalizeEvent{}, ev) @@ -194,3 +211,15 @@ func TestPlasmaFinalityData(t *testing.T) { // (prunes down to 180 then adds the extra 1 each time) require.Equal(t, expFinalityLookback, len(fi.finalityData)) } + +func mockQueryBlockRangeBabylonFinalized(fgclient *fgmocks.MockIFinalityGadgetClient, refs []eth.L2BlockRef) { + queryBlocks := make([]*fgtypes.Block, len(refs)) + for i, ref := range refs { + queryBlocks[i] = &fgtypes.Block{ + BlockHeight: ref.Number, + BlockHash: ref.Hash.String(), + BlockTimestamp: ref.Time, + } + } + fgclient.EXPECT().QueryBlockRangeBabylonFinalized(queryBlocks).Return(&refs[len(refs)-1].Number, nil) +} From 19c5f7008e16386fafc78cffa331cf8ce4fa3c5c Mon Sep 17 00:00:00 2001 From: lesterli Date: Tue, 13 Aug 2024 23:33:09 +0800 Subject: [PATCH 15/15] update for comments --- op-node/rollup/finality/plasma_test.go | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/op-node/rollup/finality/plasma_test.go b/op-node/rollup/finality/plasma_test.go index 6319215b0cc1..79004275548c 100644 --- a/op-node/rollup/finality/plasma_test.go +++ b/op-node/rollup/finality/plasma_test.go @@ -67,14 +67,14 @@ func TestPlasmaFinalityData(t *testing.T) { GasLimit: 20_000_000, }, }, - BlockTime: 1, - SeqWindowSize: 2, + BlockTime: 1, + SeqWindowSize: 2, + BabylonFinalityGadgetRpc: "https://mock-finality-gadget-rpc.com", } plasmaCfg := &rollup.PlasmaConfig{ DAChallengeWindow: 90, DAResolveWindow: 90, } - cfg.BabylonFinalityGadgetRpc = "https://mock-finality-gadget-rpc.com" // shoud return l1 finality if plasma is not enabled require.Equal(t, uint64(defaultFinalityLookback), calcFinalityLookback(cfg)) @@ -118,8 +118,9 @@ func TestPlasmaFinalityData(t *testing.T) { // advance over 200 l1 origins each time incrementing new l2 safe heads // and post processing. + l1FinalityBlockNumber := uint64(10) for i := uint64(0); i < 200; i++ { - if i == 10 { // finalize a L1 commitment + if i == l1FinalityBlockNumber { // finalize a L1 commitment fi.OnEvent(FinalizeL1Event{FinalizedL1: l1parent}) emitter.AssertExpectations(t) // no events emitted upon L1 finality require.Equal(t, l1parent, commitmentInclusionFinalized, "plasma backend received L1 signal") @@ -147,14 +148,14 @@ func TestPlasmaFinalityData(t *testing.T) { L1Origin: previous.ID(), // reference previous origin, not the block the batch was included in SequenceNumber: j, } - if i < 10 { + if i < l1FinalityBlockNumber { mockL2Refs = append(mockL2Refs, l2parent) l2F.ExpectL2BlockRefByNumber(l2parent.Number, l2parent, nil) } fi.OnEvent(engine.SafeDerivedEvent{Safe: l2parent, DerivedFrom: l1parent}) emitter.AssertExpectations(t) } - if i < 10 { + if i < l1FinalityBlockNumber { mockQueryBlockRangeBabylonFinalized(fgclient, mockL2Refs) } // might trigger finalization attempt, if expired finality delay