diff --git a/aggsender/aggsender.go b/aggsender/aggsender.go index aabc9d15b..0dd68a02f 100644 --- a/aggsender/aggsender.go +++ b/aggsender/aggsender.go @@ -182,7 +182,8 @@ func newAggsender( return nil, fmt.Errorf("error creating verifier flow: %w", err) } - l1InfoTreeQuerier, err := query.NewL1InfoTreeDataQuerier(l1Client, cfg.GlobalExitRootL1Addr, l1InfoTreeSyncer) + l1InfoTreeQuerier, err := query.NewL1InfoTreeDataQuerier(l1Client, cfg.GlobalExitRootL1Addr, l1InfoTreeSyncer, + cfg.BlockFinalityForL1InfoTree) if err != nil { return nil, fmt.Errorf("error creating L1 Info tree data querier: %w", err) } diff --git a/aggsender/aggsender_test.go b/aggsender/aggsender_test.go index 7ff5be89f..f34bafb20 100644 --- a/aggsender/aggsender_test.go +++ b/aggsender/aggsender_test.go @@ -30,6 +30,7 @@ import ( "github.com/agglayer/aggkit/grpc" "github.com/agglayer/aggkit/log" treetypes "github.com/agglayer/aggkit/tree/types" + aggkittypes "github.com/agglayer/aggkit/types" "github.com/agglayer/go_signer/signer" signertypes "github.com/agglayer/go_signer/signer/types" "github.com/ethereum/go-ethereum/common" @@ -51,6 +52,7 @@ func TestConfigString(t *testing.T) { EpochNotificationPercentage: 50, Mode: "PP", SovereignRollupAddr: common.HexToAddress("0x1"), + BlockFinalityForL1InfoTree: aggkittypes.FinalizedBlock, } expected := fmt.Sprintf("StoragePath: /path/to/storage\n"+ @@ -67,7 +69,8 @@ func TestConfigString(t *testing.T) { "SovereignRollupAddr: 0x0000000000000000000000000000000000000001\n"+ "RequireNoFEPBlockGap: false\n"+ "RetriesToBuildAndSendCertificate: RetryPolicyConfig{Mode: , Config: RetryDelaysConfig{Delays: [], MaxRetries: NO RETRIES}}\n"+ - "StorageRetainCertificatesPolicy: retain all certificates, keep history: false\n", + "StorageRetainCertificatesPolicy: retain all certificates, keep history: false\n"+ + "BlockFinalityForL1InfoTree: FinalizedBlock\n", config.AgglayerClient.String()) require.Equal(t, expected, config.String()) diff --git a/aggsender/config/config.go b/aggsender/config/config.go index e3a99deb5..70e9ac1fa 100644 --- a/aggsender/config/config.go +++ b/aggsender/config/config.go @@ -11,6 +11,7 @@ import ( "github.com/agglayer/aggkit/common" "github.com/agglayer/aggkit/config/types" "github.com/agglayer/aggkit/grpc" + aggkittypes "github.com/agglayer/aggkit/types" signertypes "github.com/agglayer/go_signer/signer/types" ethCommon "github.com/ethereum/go-ethereum/common" ) @@ -96,6 +97,8 @@ type Config struct { CommitteeOverride query.CommitteeOverride `mapstructure:"CommitteeOverride"` // AgglayerBridgeL2Addr is the address of the bridge L2 sovereign contract on L2 sovereign chain AgglayerBridgeL2Addr ethCommon.Address `mapstructure:"AgglayerBridgeL2Addr"` + // BlockFinalityForL1InfoTree indicates the block finality to use when querying for L1InfoRoot to use + BlockFinalityForL1InfoTree aggkittypes.BlockNumberFinality `jsonschema:"enum=LatestBlock, enum=SafeBlock, enum=PendingBlock, enum=FinalizedBlock, enum=EarliestBlock" mapstructure:"BlockFinalityForL1InfoTree"` //nolint:lll } func (c Config) CheckCertConfigBriefString() string { @@ -118,7 +121,8 @@ func (c Config) String() string { "SovereignRollupAddr: " + c.SovereignRollupAddr.Hex() + "\n" + "RequireNoFEPBlockGap: " + fmt.Sprintf("%t", c.RequireNoFEPBlockGap) + "\n" + "RetriesToBuildAndSendCertificate: " + c.RetriesToBuildAndSendCertificate.String() + "\n" + - "StorageRetainCertificatesPolicy: " + c.StorageRetainCertificatesPolicy.String() + "\n" + "StorageRetainCertificatesPolicy: " + c.StorageRetainCertificatesPolicy.String() + "\n" + + "BlockFinalityForL1InfoTree: " + c.BlockFinalityForL1InfoTree.String() + "\n" } // Validate checks if the configuration is valid @@ -138,5 +142,8 @@ func (c Config) Validate() error { if err := c.StorageRetainCertificatesPolicy.Validate(); err != nil { return fmt.Errorf("invalid StorageRetainCertificatesPolicy config: %w", err) } + if err := c.BlockFinalityForL1InfoTree.Validate(); err != nil { + return fmt.Errorf("invalid BlockFinalityForL1InfoTree configuration: %w", err) + } return nil } diff --git a/aggsender/config/config_test.go b/aggsender/config/config_test.go index c9548c2f2..4de12c054 100644 --- a/aggsender/config/config_test.go +++ b/aggsender/config/config_test.go @@ -9,6 +9,7 @@ import ( "github.com/agglayer/aggkit/common" "github.com/agglayer/aggkit/config/types" "github.com/agglayer/aggkit/grpc" + aggkittypes "github.com/agglayer/aggkit/types" signertypes "github.com/agglayer/go_signer/signer/types" ethCommon "github.com/ethereum/go-ethereum/common" "github.com/stretchr/testify/require" @@ -29,6 +30,7 @@ func TestValidate(t *testing.T) { URL: "", }, }, + BlockFinalityForL1InfoTree: aggkittypes.FinalizedBlock, }, expectedErr: "invalid agglayer client config", }, @@ -44,6 +46,7 @@ func TestValidate(t *testing.T) { AggkitProverClient: &grpc.ClientConfig{ URL: "", }, + BlockFinalityForL1InfoTree: aggkittypes.FinalizedBlock, }, expectedErr: "invalid aggkit prover client config", }, @@ -59,8 +62,24 @@ func TestValidate(t *testing.T) { AggkitProverClient: &grpc.ClientConfig{ URL: "", }, + BlockFinalityForL1InfoTree: aggkittypes.FinalizedBlock, }, }, + { + name: "nBlockFinalityForL1InfoTree not set", + config: Config{ + Mode: aggsendertypes.PessimisticProofMode, + AgglayerClient: agglayer.ClientConfig{GRPC: &grpc.ClientConfig{ + URL: "http://localhost:9090", + MinConnectTimeout: types.NewDuration(5 * time.Second), + }, + }, + AggkitProverClient: &grpc.ClientConfig{ + URL: "", + }, + }, + expectedErr: "BlockFinalityForL1InfoTree", + }, } for _, tc := range testCases { diff --git a/aggsender/flows/builder_flow_factory.go b/aggsender/flows/builder_flow_factory.go index 8dec8f47a..b61c13fe0 100644 --- a/aggsender/flows/builder_flow_factory.go +++ b/aggsender/flows/builder_flow_factory.go @@ -52,6 +52,7 @@ func NewBuilderFlow( cfg.RequireCommitteeMembershipCheck, cfg.AgglayerBridgeL2Addr, cfg.GlobalExitRootL1Addr, + cfg.BlockFinalityForL1InfoTree, ) if err != nil { return nil, fmt.Errorf("failed to create common flow components: %w", err) @@ -100,6 +101,7 @@ func NewBuilderFlow( cfg.RequireCommitteeMembershipCheck, cfg.AgglayerBridgeL2Addr, cfg.GlobalExitRootL1Addr, + cfg.BlockFinalityForL1InfoTree, ) if err != nil { return nil, fmt.Errorf("failed to create common flow components: %w", err) @@ -166,6 +168,7 @@ func CreateCommonFlowComponents( requireCommitteeMembershipCheck bool, agglayerBridgeL2Addr ethCommon.Address, globalExitRootL1Addr ethCommon.Address, + blockFinalityForL1InfoTree aggkittypes.BlockNumberFinality, ) (*CommonFlowComponents, error) { l2ChainID, err := rollupDataQuerier.GetRollupChainID() if err != nil { @@ -184,7 +187,8 @@ func CreateCommonFlowComponents( } l2BridgeQuerier := query.NewBridgeDataQuerier(logger, l2Syncer, delayBetweenRetries, agglayerBridgeL2Reader) - l1InfoTreeQuerier, err := query.NewL1InfoTreeDataQuerier(l1Client, globalExitRootL1Addr, l1InfoTreeSyncer) + l1InfoTreeQuerier, err := query.NewL1InfoTreeDataQuerier(l1Client, globalExitRootL1Addr, l1InfoTreeSyncer, + blockFinalityForL1InfoTree) if err != nil { return nil, fmt.Errorf("error creating L1 Info tree data querier: %w", err) } diff --git a/aggsender/flows/verifier_flow_factory.go b/aggsender/flows/verifier_flow_factory.go index 40a322698..15aebd297 100644 --- a/aggsender/flows/verifier_flow_factory.go +++ b/aggsender/flows/verifier_flow_factory.go @@ -38,6 +38,7 @@ func NewVerifierFlow( cfg.RequireCommitteeMembershipCheck, cfg.AgglayerBridgeL2Addr, cfg.GlobalExitRootL1Addr, + cfg.BlockFinalityForL1InfoTree, ) if err != nil { return nil, nil, fmt.Errorf("failed to create common flow components: %w", err) @@ -67,6 +68,7 @@ func NewVerifierFlow( cfg.RequireCommitteeMembershipCheck, cfg.AgglayerBridgeL2Addr, cfg.GlobalExitRootL1Addr, + cfg.BlockFinalityForL1InfoTree, ) if err != nil { return nil, nil, fmt.Errorf("failed to create common flow components: %w", err) diff --git a/aggsender/prover/proof_generation_tool.go b/aggsender/prover/proof_generation_tool.go index 9720373ed..bd30fa247 100644 --- a/aggsender/prover/proof_generation_tool.go +++ b/aggsender/prover/proof_generation_tool.go @@ -87,6 +87,12 @@ func NewAggchainProofGenerationTool( return nil, fmt.Errorf("failed to create AggchainProofClient: %w", err) } + l1InfoTreeQuerier, err := query.NewL1InfoTreeDataQuerier(l1Client, cfg.GlobalExitRootL1Addr, l1InfoTreeSyncer, + aggkittypes.FinalizedBlock) + if err != nil { + return nil, fmt.Errorf("error creating L1 Info tree data querier: %w", err) + } + l2GERReader, err := l2gersync.NewL2EVMGERReader(cfg.GlobalExitRootL2Addr, l2Client, l1InfoTreeSyncer) if err != nil { return nil, fmt.Errorf("error creating L2 GER reader: %w", err) @@ -97,10 +103,6 @@ func NewAggchainProofGenerationTool( return nil, fmt.Errorf("failed to create bridge L2 sovereign reader: %w", err) } - l1InfoTreeQuerier, err := query.NewL1InfoTreeDataQuerier(l1Client, cfg.GlobalExitRootL1Addr, l1InfoTreeSyncer) - if err != nil { - return nil, fmt.Errorf("error creating L1 Info tree data querier: %w", err) - } l2BridgeQuerier := query.NewBridgeDataQuerier(logger, l2Syncer, time.Second, agglayerBridgeL2Reader) baseFlow := flows.NewBaseFlow( diff --git a/aggsender/prover/proof_generation_tool_test.go b/aggsender/prover/proof_generation_tool_test.go index df75c3c63..1c84fa22a 100644 --- a/aggsender/prover/proof_generation_tool_test.go +++ b/aggsender/prover/proof_generation_tool_test.go @@ -11,6 +11,7 @@ import ( aggkitgrpc "github.com/agglayer/aggkit/grpc" "github.com/agglayer/aggkit/log" aggkittypesmocks "github.com/agglayer/aggkit/types/mocks" + "github.com/ethereum/go-ethereum/common" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" ) @@ -156,4 +157,13 @@ func TestNewAggchainProofGenerationTool(t *testing.T) { _, err := NewAggchainProofGenerationTool(context.TODO(), log.WithFields("module", "test"), Config{AggkitProverClient: aggkitgrpc.DefaultConfig()}, mockL1Client, mockL2Client, mockL2Syncer, nil) require.Error(t, err) + + cfg := Config{ + AggkitProverClient: aggkitgrpc.DefaultConfig(), + GlobalExitRootL2Addr: common.HexToAddress("0xbeef"), + } + mockL1InfoTreeSyncer := mocks.NewL1InfoTreeSyncer(t) + _, err = NewAggchainProofGenerationTool(context.TODO(), log.WithFields("module", "test"), + cfg, mockL1Client, mockL2Client, mockL2Syncer, mockL1InfoTreeSyncer) + require.ErrorContains(t, err, "L2 GER reader") } diff --git a/aggsender/query/l1info_tree_data_query.go b/aggsender/query/l1info_tree_data_query.go index 47601efa1..54cddf37e 100644 --- a/aggsender/query/l1info_tree_data_query.go +++ b/aggsender/query/l1info_tree_data_query.go @@ -15,30 +15,31 @@ import ( "github.com/ethereum/go-ethereum/common" ) -var finalizedBlockBigInt = big.NewInt(int64(aggkittypes.Finalized)) - var _ types.L1InfoTreeDataQuerier = (*L1InfoTreeDataQuerier)(nil) // L1InfoTreeDataQuerier is a struct that holds the logic to query the L1 Info tree data type L1InfoTreeDataQuerier struct { - l1Client aggkittypes.BaseEthereumClienter - l1GERManager *agglayerger.Agglayerger - l1InfoTreeSyncer types.L1InfoTreeSyncer + l1Client aggkittypes.BaseEthereumClienter + l1GERManager *agglayerger.Agglayerger + l1InfoTreeSyncer types.L1InfoTreeSyncer + blockFinalityForL1InfoTree aggkittypes.BlockNumberFinality } // NewL1InfoTreeDataQuerier returns a new instance of the L1InfoTreeDataQuery func NewL1InfoTreeDataQuerier( l1Client aggkittypes.BaseEthereumClienter, l1GERAddr common.Address, - l1InfoTreeSyncer types.L1InfoTreeSyncer) (*L1InfoTreeDataQuerier, error) { + l1InfoTreeSyncer types.L1InfoTreeSyncer, + blockFinalityForL1InfoTree aggkittypes.BlockNumberFinality) (*L1InfoTreeDataQuerier, error) { l1GERManager, err := agglayerger.NewAgglayerger(l1GERAddr, l1Client) if err != nil { return nil, err } return &L1InfoTreeDataQuerier{ - l1Client: l1Client, - l1GERManager: l1GERManager, - l1InfoTreeSyncer: l1InfoTreeSyncer, + l1Client: l1Client, + l1GERManager: l1GERManager, + l1InfoTreeSyncer: l1InfoTreeSyncer, + blockFinalityForL1InfoTree: blockFinalityForL1InfoTree, }, nil } @@ -143,7 +144,7 @@ func (l *L1InfoTreeDataQuerier) GetProofForGER( // getLatestProcessedFinalizedBlock returns the latest processed finalized block from the l1infotreesyncer func (l *L1InfoTreeDataQuerier) getLatestProcessedFinalizedBlock(ctx context.Context) (uint64, error) { - lastFinalizedL1Block, err := l.l1Client.HeaderByNumber(ctx, finalizedBlockBigInt) + lastFinalizedL1Block, err := l.blockFinalityForL1InfoTree.BlockHeader(ctx, l.l1Client) if err != nil { return 0, fmt.Errorf("error getting latest finalized L1 block: %w", err) } diff --git a/aggsender/query/l1info_tree_data_query_test.go b/aggsender/query/l1info_tree_data_query_test.go index 3bad0ab39..07d7d62c7 100644 --- a/aggsender/query/l1info_tree_data_query_test.go +++ b/aggsender/query/l1info_tree_data_query_test.go @@ -9,12 +9,15 @@ import ( "github.com/agglayer/aggkit/aggsender/mocks" "github.com/agglayer/aggkit/l1infotreesync" treetypes "github.com/agglayer/aggkit/tree/types" + aggkittypes "github.com/agglayer/aggkit/types" aggkittypesmocks "github.com/agglayer/aggkit/types/mocks" "github.com/ethereum/go-ethereum/common" gethtypes "github.com/ethereum/go-ethereum/core/types" "github.com/stretchr/testify/require" ) +var finalizedBlockBigInt = big.NewInt(int64(aggkittypes.Finalized)) + func Test_GetFinalizedL1InfoTreeData(t *testing.T) { t.Parallel() @@ -83,7 +86,7 @@ func Test_GetFinalizedL1InfoTreeData(t *testing.T) { mockL1InfoTreeSyncer := mocks.NewL1InfoTreeSyncer(t) mockL1Client := aggkittypesmocks.NewBaseEthereumClienter(t) - l1InfoTreeDataQuery, err := NewL1InfoTreeDataQuerier(mockL1Client, common.Address{}, mockL1InfoTreeSyncer) + l1InfoTreeDataQuery, err := NewL1InfoTreeDataQuerier(mockL1Client, common.Address{}, mockL1InfoTreeSyncer, aggkittypes.FinalizedBlock) require.NoError(t, err) tc.mockFn(mockL1InfoTreeSyncer) @@ -179,7 +182,7 @@ func Test_AggchainProverFlow_GetLatestProcessedFinalizedBlock(t *testing.T) { mockL1InfoTreeSyncer := mocks.NewL1InfoTreeSyncer(t) mockL1Client := aggkittypesmocks.NewBaseEthereumClienter(t) - l1InfoTreeDataQuery, err := NewL1InfoTreeDataQuerier(mockL1Client, common.Address{}, mockL1InfoTreeSyncer) + l1InfoTreeDataQuery, err := NewL1InfoTreeDataQuerier(mockL1Client, common.Address{}, mockL1InfoTreeSyncer, aggkittypes.FinalizedBlock) require.NoError(t, err) tc.mockFn(mockL1InfoTreeSyncer, mockL1Client) @@ -263,7 +266,7 @@ func Test_GetProofForGER(t *testing.T) { t.Parallel() mockL1InfoTreeSyncer := mocks.NewL1InfoTreeSyncer(t) - l1InfoTreeDataQuery, err := NewL1InfoTreeDataQuerier(nil, common.Address{}, mockL1InfoTreeSyncer) + l1InfoTreeDataQuery, err := NewL1InfoTreeDataQuerier(nil, common.Address{}, mockL1InfoTreeSyncer, aggkittypes.FinalizedBlock) require.NoError(t, err) tc.mockFn(mockL1InfoTreeSyncer) @@ -374,7 +377,7 @@ func Test_IsGERFinalized(t *testing.T) { t.Parallel() mockL1InfoTreeSyncer := mocks.NewL1InfoTreeSyncer(t) - l1InfoTreeDataQuery, err := NewL1InfoTreeDataQuerier(nil, common.Address{}, mockL1InfoTreeSyncer) + l1InfoTreeDataQuery, err := NewL1InfoTreeDataQuerier(nil, common.Address{}, mockL1InfoTreeSyncer, aggkittypes.FinalizedBlock) require.NoError(t, err) tc.mockFn(mockL1InfoTreeSyncer) diff --git a/aggsender/validator/config.go b/aggsender/validator/config.go index 63111dd9c..7ab0d443f 100644 --- a/aggsender/validator/config.go +++ b/aggsender/validator/config.go @@ -8,6 +8,7 @@ import ( aggkitcommon "github.com/agglayer/aggkit/common" "github.com/agglayer/aggkit/config/types" aggkitgrpc "github.com/agglayer/aggkit/grpc" + aggkittypes "github.com/agglayer/aggkit/types" signertypes "github.com/agglayer/go_signer/signer/types" ethCommon "github.com/ethereum/go-ethereum/common" ) @@ -48,6 +49,9 @@ type Config struct { AgglayerBridgeL2Addr ethCommon.Address `mapstructure:"AgglayerBridgeL2Addr"` // GlobalExitRootL1Addr is the address of the GlobalExitRootManager contract on L1 GlobalExitRootL1Addr ethCommon.Address `mapstructure:"GlobalExitRootL1Addr"` + // BlockFinalityForL1InfoTree indicates the block finality to use when querying for L1InfoRoot to use + BlockFinalityForL1InfoTree aggkittypes.BlockNumberFinality `jsonschema:"enum=LatestBlock, enum=SafeBlock, enum=PendingBlock, enum=FinalizedBlock, enum=EarliestBlock" mapstructure:"BlockFinalityForL1InfoTree"` //nolint:lll + } type PPConfig struct { @@ -90,5 +94,9 @@ func (c *Config) Validate() error { return fmt.Errorf("invalid agglayer client config: %w", err) } + if err := c.BlockFinalityForL1InfoTree.Validate(); err != nil { + return fmt.Errorf("invalid BlockFinalityForL1InfoTree configuration: %w", err) + } + return nil } diff --git a/aggsender/validator/config_test.go b/aggsender/validator/config_test.go index efdd55023..ee5fd9db5 100644 --- a/aggsender/validator/config_test.go +++ b/aggsender/validator/config_test.go @@ -8,6 +8,7 @@ import ( aggsendertypes "github.com/agglayer/aggkit/aggsender/types" "github.com/agglayer/aggkit/config/types" "github.com/agglayer/aggkit/grpc" + aggkittypes "github.com/agglayer/aggkit/types" "github.com/ethereum/go-ethereum/common" "github.com/stretchr/testify/require" ) @@ -28,6 +29,7 @@ func TestValidatorConfigValidate(t *testing.T) { URL: "http://localhost:9090", MinConnectTimeout: types.NewDuration(5 * time.Second), }}, + BlockFinalityForL1InfoTree: aggkittypes.FinalizedBlock, }, }, { @@ -41,6 +43,7 @@ func TestValidatorConfigValidate(t *testing.T) { URL: "http://localhost:9090", MinConnectTimeout: types.NewDuration(5 * time.Second), }}, + BlockFinalityForL1InfoTree: aggkittypes.FinalizedBlock, }, }, { @@ -54,6 +57,7 @@ func TestValidatorConfigValidate(t *testing.T) { URL: "http://localhost:9090", MinConnectTimeout: types.NewDuration(5 * time.Second), }}, + BlockFinalityForL1InfoTree: aggkittypes.FinalizedBlock, }, expectedErr: errInvalidSovereignRollupAddr.Error(), }, @@ -65,6 +69,7 @@ func TestValidatorConfigValidate(t *testing.T) { URL: "http://localhost:9090", MinConnectTimeout: types.NewDuration(5 * time.Second), }}, + BlockFinalityForL1InfoTree: aggkittypes.FinalizedBlock, }, expectedErr: "invalid mode invalid-mode, must be one of", }, @@ -76,6 +81,7 @@ func TestValidatorConfigValidate(t *testing.T) { URL: "http://localhost:9090", MinConnectTimeout: types.NewDuration(5 * time.Second), }}, + BlockFinalityForL1InfoTree: aggkittypes.FinalizedBlock, }, expectedErr: "invalid mode , must be one of", }, @@ -86,6 +92,7 @@ func TestValidatorConfigValidate(t *testing.T) { AgglayerClient: agglayer.ClientConfig{GRPC: &grpc.ClientConfig{ URL: "", }}, + BlockFinalityForL1InfoTree: aggkittypes.FinalizedBlock, }, expectedErr: "invalid agglayer client config", }, diff --git a/config/default.go b/config/default.go index e6a893287..c9f0ac9ec 100644 --- a/config/default.go +++ b/config/default.go @@ -212,6 +212,7 @@ MaxL2BlockNumber = 0 StopOnFinishedSendingAllCertificates = false RequireCommitteeMembershipCheck = false AgglayerBridgeL2Addr = "{{L2Config.BridgeAddr}}" +BlockFinalityForL1InfoTree = "FinalizedBlock" [AggSender.RetriesToBuildAndSendCertificate] RetryMode = "delays" Delays = [ "1m", "1m", "2m", "5m", "5m", "8m" ] @@ -293,6 +294,7 @@ Mode = "{{AggSender.Mode}}" RequireCommitteeMembershipCheck = {{AggSender.RequireCommitteeMembershipCheck}} AgglayerBridgeL2Addr = "{{L2Config.BridgeAddr}}" GlobalExitRootL1Addr = "{{L1Config.polygonZkEVMGlobalExitRootAddress}}" +BlockFinalityForL1InfoTree = "{{AggSender.BlockFinalityForL1InfoTree}}" [Validator.ServerConfig] Host = "0.0.0.0" Port = 5578