Skip to content
Merged
3 changes: 2 additions & 1 deletion aggsender/aggsender.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down
21 changes: 16 additions & 5 deletions aggsender/aggsender_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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"+
Expand All @@ -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())
Expand All @@ -79,6 +82,8 @@ func TestAggSenderStart(t *testing.T) {
bridgeL2SyncerMock := mocks.NewL2BridgeSyncer(t)
rollupQuerierMock := mocks.NewRollupDataQuerier(t)
committeQuerierMock := mocks.NewMultisigQuerier(t)
mockL1InfoTreeSyncer := mocks.NewL1InfoTreeSyncer(t)
mockL1InfoTreeSyncer.EXPECT().Finality().Return(aggkittypes.FinalizedBlock).Maybe()
sendTrigger := mocks.NewCertificateSendTrigger(t)
sendTrigger.EXPECT().Setup(ctx)
ch := make(chan aggsendertypes.CertificateTriggerEvent)
Expand All @@ -102,9 +107,10 @@ func TestAggSenderStart(t *testing.T) {
AggsenderPrivateKey: signertypes.SignerConfig{
Method: signertypes.MethodNone,
},
BlockFinalityForL1InfoTree: aggkittypes.FinalizedBlock,
},
aggLayerMock,
nil, // l1 info tree syncer
mockL1InfoTreeSyncer, // l1 info tree syncer
bridgeL2SyncerMock,
nil, // l1 client
nil, // l2 client
Expand Down Expand Up @@ -266,7 +272,7 @@ func TestSendCertificate_NoClaims(t *testing.T) {
},
}, []bridgesync.Claim{}, nil).Once()
mockL2BridgeQuerier.EXPECT().GetUnsetClaimsForBlockRange(mock.Anything, uint64(11), uint64(50)).Return([]bridgetypes.Unclaim{}, nil).Once()
mockL1Querier.EXPECT().GetLatestFinalizedL1InfoRoot(ctx).Return(&treetypes.Root{}, nil, nil).Once()
mockL1Querier.EXPECT().GetTargetL1InfoRoot(ctx).Return(&treetypes.Root{}, nil, nil).Once()
mockL2BridgeQuerier.EXPECT().GetExitRootByIndex(mock.Anything, uint32(1)).Return(common.Hash{}, nil).Once()
mockL2BridgeQuerier.EXPECT().OriginNetwork().Return(uint32(1)).Once()
mockAggLayerClient.EXPECT().SendCertificate(mock.Anything, mock.Anything).Return(common.Hash{}, nil).Once()
Expand Down Expand Up @@ -520,6 +526,10 @@ func TestNewAggSender(t *testing.T) {
mockBridgeSyncer := mocks.NewL2BridgeSyncer(t)
mockRollupQuerier := mocks.NewRollupDataQuerier(t)
mockCommitteeQuerier := mocks.NewMultisigQuerier(t)

mockL1InfoTreeSyncer := mocks.NewL1InfoTreeSyncer(t)
mockL1InfoTreeSyncer.EXPECT().Finality().Return(aggkittypes.FinalizedBlock).Maybe()

mockBridgeSyncer.EXPECT().OriginNetwork().Return(uint32(1)).Times(2)
mockRollupQuerier.EXPECT().GetRollupChainID().Return(uint64(1234), nil)
committee, err := aggsendertypes.NewMultisigCommittee([]*aggsendertypes.SignerInfo{aggsendertypes.NewSignerInfo("", common.Address{})},
Expand All @@ -532,10 +542,11 @@ func TestNewAggSender(t *testing.T) {
AggsenderPrivateKey: signertypes.SignerConfig{
Method: signertypes.MethodNone,
},
Mode: aggsendertypes.PessimisticProofMode,
Mode: aggsendertypes.PessimisticProofMode,
BlockFinalityForL1InfoTree: aggkittypes.FinalizedBlock,
},
mockAgglayerClient,
nil, // l1 info tree syncer
mockL1InfoTreeSyncer, // l1 info tree syncer
mockBridgeSyncer,
nil, // l1 client
nil, // l2 client
Expand Down
9 changes: 8 additions & 1 deletion aggsender/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)
Expand Down Expand Up @@ -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 {
Expand All @@ -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
Expand All @@ -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
}
19 changes: 19 additions & 0 deletions aggsender/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -29,6 +30,7 @@ func TestValidate(t *testing.T) {
URL: "",
},
},
BlockFinalityForL1InfoTree: aggkittypes.FinalizedBlock,
},
expectedErr: "invalid agglayer client config",
},
Expand All @@ -44,6 +46,7 @@ func TestValidate(t *testing.T) {
AggkitProverClient: &grpc.ClientConfig{
URL: "",
},
BlockFinalityForL1InfoTree: aggkittypes.FinalizedBlock,
},
expectedErr: "invalid aggkit prover client config",
},
Expand All @@ -59,8 +62,24 @@ func TestValidate(t *testing.T) {
AggkitProverClient: &grpc.ClientConfig{
URL: "",
},
BlockFinalityForL1InfoTree: aggkittypes.FinalizedBlock,
},
},
{
name: "BlockFinalityForL1InfoTree 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 {
Expand Down
8 changes: 4 additions & 4 deletions aggsender/flows/builder_flow_aggchain_prover_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ func Test_AggchainProverFlow_GetCertificateBuildParams(t *testing.T) {
ger := l1infotreesync.CalculateGER(mer, rer)
mockStorage.EXPECT().GetLastSentCertificateHeaderWithProofIfInError(ctx).Return(nil, nil, nil).Once()
mockStorage.EXPECT().GetLastSentCertificateHeader().Return(nil, nil).Once()
mockL1InfoDataQuery.EXPECT().GetLatestFinalizedL1InfoRoot(mock.Anything).Return(
mockL1InfoDataQuery.EXPECT().GetTargetL1InfoRoot(mock.Anything).Return(
&treetypes.Root{Hash: finalizedL1Root, BlockNum: 10}, nil, nil)
mockL2BridgeQuerier.On("GetLastProcessedBlock", ctx).Return(uint64(10), nil)
mockL2BridgeQuerier.EXPECT().GetBridgesAndClaims(ctx, uint64(1), uint64(10)).Return([]bridgesync.Bridge{{}}, []bridgesync.Claim{
Expand All @@ -217,7 +217,7 @@ func Test_AggchainProverFlow_GetCertificateBuildParams(t *testing.T) {
mockStorage.EXPECT().GetLastSentCertificateHeaderWithProofIfInError(ctx).Return(nil, nil, nil).Once()
mockStorage.EXPECT().GetLastSentCertificateHeader().Return(nil, nil).Once()
mockL2BridgeQuerier.EXPECT().GetLastProcessedBlock(ctx).Return(uint64(10), nil)
mockL1InfoDataQuery.EXPECT().GetLatestFinalizedL1InfoRoot(mock.Anything).Return(
mockL1InfoDataQuery.EXPECT().GetTargetL1InfoRoot(mock.Anything).Return(
&treetypes.Root{Hash: finalizedL1Root, BlockNum: 10}, nil, nil)
mockL2BridgeQuerier.EXPECT().GetBridgesAndClaims(ctx, uint64(1), uint64(10)).Return([]bridgesync.Bridge{}, []bridgesync.Claim{}, nil)
mockL2BridgeQuerier.EXPECT().GetUnsetClaimsForBlockRange(ctx, uint64(1), uint64(10)).Return([]bridgesynctypes.Unclaim{}, nil)
Expand All @@ -239,7 +239,7 @@ func Test_AggchainProverFlow_GetCertificateBuildParams(t *testing.T) {
ger := l1infotreesync.CalculateGER(mer, rer)
mockStorage.EXPECT().GetLastSentCertificateHeaderWithProofIfInError(ctx).Return(&types.CertificateHeader{ToBlock: 5, Status: agglayertypes.Settled}, nil, nil).Once()
mockStorage.EXPECT().GetLastSentCertificateHeader().Return(&types.CertificateHeader{ToBlock: 5}, nil).Once()
mockL1InfoDataQuery.EXPECT().GetLatestFinalizedL1InfoRoot(mock.Anything).Return(
mockL1InfoDataQuery.EXPECT().GetTargetL1InfoRoot(mock.Anything).Return(
&treetypes.Root{Hash: finalizedL1Root, BlockNum: 10, Index: 10}, nil, nil)
mockL2BridgeQuerier.On("GetLastProcessedBlock", ctx).Return(uint64(10), nil)
mockL2BridgeQuerier.EXPECT().GetBridgesAndClaims(ctx, uint64(6), uint64(10)).Return([]bridgesync.Bridge{{}}, []bridgesync.Claim{{
Expand Down Expand Up @@ -294,7 +294,7 @@ func Test_AggchainProverFlow_GetCertificateBuildParams(t *testing.T) {
ger := l1infotreesync.CalculateGER(mer, rer)
mockStorage.EXPECT().GetLastSentCertificateHeaderWithProofIfInError(ctx).Return(&types.CertificateHeader{ToBlock: 5, Status: agglayertypes.Settled}, nil, nil).Once()
mockStorage.EXPECT().GetLastSentCertificateHeader().Return(&types.CertificateHeader{ToBlock: 5}, nil).Once()
mockL1InfoDataQuery.EXPECT().GetLatestFinalizedL1InfoRoot(mock.Anything).Return(
mockL1InfoDataQuery.EXPECT().GetTargetL1InfoRoot(mock.Anything).Return(
&treetypes.Root{Hash: finalizedL1Root, BlockNum: 10, Index: 10}, nil, nil)
mockL2BridgeQuerier.On("GetLastProcessedBlock", ctx).Return(uint64(10), nil)
mockL2BridgeQuerier.EXPECT().GetBridgesAndClaims(ctx, uint64(6), uint64(10)).Return(
Expand Down
6 changes: 5 additions & 1 deletion aggsender/flows/builder_flow_factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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 {
Expand All @@ -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)
}
Expand Down
20 changes: 14 additions & 6 deletions aggsender/flows/builder_flow_factory_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
cfgtypes "github.com/agglayer/aggkit/config/types"
aggkitgrpc "github.com/agglayer/aggkit/grpc"
"github.com/agglayer/aggkit/log"
aggkittypes "github.com/agglayer/aggkit/types"
typesmocks "github.com/agglayer/aggkit/types/mocks"
signertypes "github.com/agglayer/go_signer/signer/types"
"github.com/ethereum/go-ethereum/common"
Expand All @@ -33,10 +34,11 @@ func TestNewFlow(t *testing.T) {
{
name: "success with PessimisticProofMode",
cfg: config.Config{
Mode: types.PessimisticProofMode,
AggsenderPrivateKey: signertypes.SignerConfig{Method: signertypes.MethodNone},
MaxCertSize: 100,
AggkitProverClient: aggkitgrpc.DefaultConfig(),
Mode: types.PessimisticProofMode,
AggsenderPrivateKey: signertypes.SignerConfig{Method: signertypes.MethodNone},
MaxCertSize: 100,
AggkitProverClient: aggkitgrpc.DefaultConfig(),
BlockFinalityForL1InfoTree: aggkittypes.FinalizedBlock,
},
mockFn: func(mockCommittee *mocks.MultisigQuerier) {
committee, err := types.NewMultisigCommittee([]*types.SignerInfo{types.NewSignerInfo("", common.Address{})}, 1)
Expand All @@ -53,6 +55,7 @@ func TestNewFlow(t *testing.T) {
MaxCertSize: 100,
AggkitProverClient: aggkitgrpc.DefaultConfig(),
RequireCommitteeMembershipCheck: true,
BlockFinalityForL1InfoTree: aggkittypes.FinalizedBlock,
},
mockFn: func(mockCommittee *mocks.MultisigQuerier) {
mockCommittee.EXPECT().GetMultisigCommittee(mock.Anything, mock.Anything).Return(nil, errors.New("test error")).Maybe()
Expand All @@ -67,6 +70,7 @@ func TestNewFlow(t *testing.T) {
MaxCertSize: 100,
AggkitProverClient: aggkitgrpc.DefaultConfig(),
RequireCommitteeMembershipCheck: false,
BlockFinalityForL1InfoTree: aggkittypes.FinalizedBlock,
},
mockFn: func(mockCommittee *mocks.MultisigQuerier) {
mockCommittee.EXPECT().GetMultisigCommittee(mock.Anything, mock.Anything).Return(nil, errors.New("test error")).Maybe()
Expand All @@ -80,6 +84,7 @@ func TestNewFlow(t *testing.T) {
MaxCertSize: 100,
AggkitProverClient: aggkitgrpc.DefaultConfig(),
RequireCommitteeMembershipCheck: false,
BlockFinalityForL1InfoTree: aggkittypes.FinalizedBlock,
},
mockFn: func(mockCommittee *mocks.MultisigQuerier) {
signers := []*types.SignerInfo{
Expand All @@ -101,6 +106,7 @@ func TestNewFlow(t *testing.T) {
MaxCertSize: 100,
AggkitProverClient: aggkitgrpc.DefaultConfig(),
RequireCommitteeMembershipCheck: true,
BlockFinalityForL1InfoTree: aggkittypes.FinalizedBlock,
},
mockFn: func(mockCommittee *mocks.MultisigQuerier) {
signers := []*types.SignerInfo{
Expand All @@ -122,7 +128,8 @@ func TestNewFlow(t *testing.T) {
AggsenderPrivateKey: signertypes.SignerConfig{
Method: signertypes.MethodLocal,
},
AggkitProverClient: aggkitgrpc.DefaultConfig(),
AggkitProverClient: aggkitgrpc.DefaultConfig(),
BlockFinalityForL1InfoTree: aggkittypes.FinalizedBlock,
},
expectedError: "error signer.Initialize",
},
Expand All @@ -146,6 +153,7 @@ func TestNewFlow(t *testing.T) {
TrustedSequencerKey: keyConfig,
RequireKeyMatchTrustedSequencer: true,
},
BlockFinalityForL1InfoTree: aggkittypes.FinalizedBlock,
},
expectedError: "failed to fetch the aggchain signers from the AggchainFEP contract",
},
Expand All @@ -166,7 +174,7 @@ func TestNewFlow(t *testing.T) {

mockL2BridgeSyncer.EXPECT().OriginNetwork().Return(1).Maybe()
mockLogger := log.WithFields("test", "NewFlow")

mockL1InfoTreeSyncer.EXPECT().Finality().Return(aggkittypes.FinalizedBlock).Maybe()
mockL1Client.EXPECT().CallContract(mock.Anything, mock.Anything, mock.Anything).Return([]byte{1, 2, 3}, nil).Maybe()
mockL1Client.EXPECT().CodeAt(mock.Anything, mock.Anything, mock.Anything).Return([]byte{1, 2, 3}, nil).Maybe()
mockL2Client.EXPECT().CallContract(mock.Anything, mock.Anything, mock.Anything).Return([]byte{1, 2, 3}, nil).Maybe()
Expand Down
Loading
Loading