diff --git a/aggsender/aggsender.go b/aggsender/aggsender.go index 45350e734..61af9c8da 100644 --- a/aggsender/aggsender.go +++ b/aggsender/aggsender.go @@ -182,6 +182,13 @@ func newAggsender( return nil, fmt.Errorf("error creating verifier flow: %w", err) } + l1GERQuerier, err := query.NewL1GERDataQuerier( + cfg.GlobalExitRootL1Addr, aggkittypes.LatestBlock, l1Client, // TODO configurable finality + ) + if err != nil { + return nil, fmt.Errorf("error creating L1GER data querier: %w", err) + } + localValidator := validator.NewLocalValidator( logger, storage, @@ -191,6 +198,7 @@ func newAggsender( query.NewL1InfoTreeDataQuerier(l1Client, l1InfoTreeSyncer), certQuerier, query.NewLERDataQuerier(cfg.RollupCreationBlockL1, rollupDataQuerier), + l1GERQuerier, ), ) diff --git a/aggsender/aggsender_validator.go b/aggsender/aggsender_validator.go index fce0b50df..f6013ce78 100644 --- a/aggsender/aggsender_validator.go +++ b/aggsender/aggsender_validator.go @@ -11,6 +11,7 @@ import ( aggkitcommon "github.com/agglayer/aggkit/common" "github.com/agglayer/aggkit/grpc" signertypes "github.com/agglayer/go_signer/signer/types" + "github.com/ethereum/go-ethereum/common" ) var ( @@ -29,14 +30,15 @@ func NewAggsenderValidator(ctx context.Context, logger aggkitcommon.Logger, cfg validator.Config, flow types.AggsenderVerifierFlow, - l1InfoTreeDataQuerier validator.L1InfoTreeRootByLeafQuerier, + l1InfoTreeDataQuerier types.L1InfoTreeDataQuerier, aggLayerClient agglayer.AggLayerClientCertificateIDQuerier, certQuerier types.CertificateQuerier, aggchainFEPQuerier types.AggchainFEPRollupQuerier, lerQuerier types.LERQuerier, + l1GERQuerier types.L1GERQuerier, signer signertypes.Signer) (*AggsenderValidator, error) { validatorCert := validator.NewAggsenderValidator( - logger, flow, l1InfoTreeDataQuerier, certQuerier, lerQuerier) + logger, flow, l1InfoTreeDataQuerier, certQuerier, lerQuerier, l1GERQuerier) grpcServer, err := grpc.NewServer(cfg.ServerConfig) if err != nil { return nil, err @@ -63,3 +65,8 @@ func (a *AggsenderValidator) Start(ctx context.Context) { func (a *AggsenderValidator) ValidateCertificate(ctx context.Context, params types.VerifyIncomingRequest) error { return a.validator.ValidateCertificate(ctx, params) } + +// ValidateGER validates the GlobalExitRoot that needs to be injected. +func (a *AggsenderValidator) ValidateGER(ctx context.Context, ger common.Hash) error { + return a.validator.ValidateGER(ctx, ger) +} diff --git a/aggsender/config/config.go b/aggsender/config/config.go index d871ddab0..4973fa980 100644 --- a/aggsender/config/config.go +++ b/aggsender/config/config.go @@ -60,6 +60,8 @@ type Config struct { // GlobalExitRootL2Addr is the address of the GlobalExitRootManager contract on l2 sovereign chain // this address is needed for the AggchainProof mode of the AggSender GlobalExitRootL2Addr ethCommon.Address `mapstructure:"GlobalExitRootL2"` + // GlobalExitRootL1Addr is the address of the GlobalExitRootManager contract on l1 chain + GlobalExitRootL1Addr ethCommon.Address `mapstructure:"GlobalExitRootL1"` // SovereignRollupAddr is the address of the sovereign rollup contract on L1 SovereignRollupAddr ethCommon.Address `mapstructure:"SovereignRollupAddr"` // RequireStorageContentCompatibility is true it's mandatory that data stored in the database diff --git a/aggsender/flows/builder_flow_factory.go b/aggsender/flows/builder_flow_factory.go index ef759477d..040fa0d3c 100644 --- a/aggsender/flows/builder_flow_factory.go +++ b/aggsender/flows/builder_flow_factory.go @@ -114,7 +114,7 @@ func NewBuilderFlow( commonFlowComponents.L1InfoTreeDataQuerier, optimisticSigner, commonFlowComponents.BaseFlow, - query.NewGERDataQuerier(commonFlowComponents.L1InfoTreeDataQuerier, l2GERReader), + query.NewL2GERDataQuerier(commonFlowComponents.L1InfoTreeDataQuerier, l2GERReader), commonFlowComponents.L2BridgeQuerier, ) diff --git a/aggsender/mocks/mock_agglayer_ger.go b/aggsender/mocks/mock_agglayer_ger.go new file mode 100644 index 000000000..208d78ae0 --- /dev/null +++ b/aggsender/mocks/mock_agglayer_ger.go @@ -0,0 +1,96 @@ +// Code generated by mockery. DO NOT EDIT. + +package mocks + +import ( + big "math/big" + + bind "github.com/ethereum/go-ethereum/accounts/abi/bind" + mock "github.com/stretchr/testify/mock" +) + +// AgglayerGER is an autogenerated mock type for the AgglayerGER type +type AgglayerGER struct { + mock.Mock +} + +type AgglayerGER_Expecter struct { + mock *mock.Mock +} + +func (_m *AgglayerGER) EXPECT() *AgglayerGER_Expecter { + return &AgglayerGER_Expecter{mock: &_m.Mock} +} + +// GlobalExitRootMap provides a mock function with given fields: opts, ger +func (_m *AgglayerGER) GlobalExitRootMap(opts *bind.CallOpts, ger [32]byte) (*big.Int, error) { + ret := _m.Called(opts, ger) + + if len(ret) == 0 { + panic("no return value specified for GlobalExitRootMap") + } + + var r0 *big.Int + var r1 error + if rf, ok := ret.Get(0).(func(*bind.CallOpts, [32]byte) (*big.Int, error)); ok { + return rf(opts, ger) + } + if rf, ok := ret.Get(0).(func(*bind.CallOpts, [32]byte) *big.Int); ok { + r0 = rf(opts, ger) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*big.Int) + } + } + + if rf, ok := ret.Get(1).(func(*bind.CallOpts, [32]byte) error); ok { + r1 = rf(opts, ger) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// AgglayerGER_GlobalExitRootMap_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GlobalExitRootMap' +type AgglayerGER_GlobalExitRootMap_Call struct { + *mock.Call +} + +// GlobalExitRootMap is a helper method to define mock.On call +// - opts *bind.CallOpts +// - ger [32]byte +func (_e *AgglayerGER_Expecter) GlobalExitRootMap(opts interface{}, ger interface{}) *AgglayerGER_GlobalExitRootMap_Call { + return &AgglayerGER_GlobalExitRootMap_Call{Call: _e.mock.On("GlobalExitRootMap", opts, ger)} +} + +func (_c *AgglayerGER_GlobalExitRootMap_Call) Run(run func(opts *bind.CallOpts, ger [32]byte)) *AgglayerGER_GlobalExitRootMap_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*bind.CallOpts), args[1].([32]byte)) + }) + return _c +} + +func (_c *AgglayerGER_GlobalExitRootMap_Call) Return(_a0 *big.Int, _a1 error) *AgglayerGER_GlobalExitRootMap_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *AgglayerGER_GlobalExitRootMap_Call) RunAndReturn(run func(*bind.CallOpts, [32]byte) (*big.Int, error)) *AgglayerGER_GlobalExitRootMap_Call { + _c.Call.Return(run) + return _c +} + +// NewAgglayerGER creates a new instance of AgglayerGER. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewAgglayerGER(t interface { + mock.TestingT + Cleanup(func()) +}) *AgglayerGER { + mock := &AgglayerGER{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/aggsender/mocks/mock_certificate_validate_and_signer.go b/aggsender/mocks/mock_certificate_validate_and_signer.go index cd65e1de6..a09ac223d 100644 --- a/aggsender/mocks/mock_certificate_validate_and_signer.go +++ b/aggsender/mocks/mock_certificate_validate_and_signer.go @@ -326,6 +326,65 @@ func (_c *CertificateValidateAndSigner_ValidateAndSignCertificate_Call) RunAndRe return _c } +// ValidateAndSignGER provides a mock function with given fields: ctx, ger +func (_m *CertificateValidateAndSigner) ValidateAndSignGER(ctx context.Context, ger common.Hash) ([]byte, error) { + ret := _m.Called(ctx, ger) + + if len(ret) == 0 { + panic("no return value specified for ValidateAndSignGER") + } + + var r0 []byte + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, common.Hash) ([]byte, error)); ok { + return rf(ctx, ger) + } + if rf, ok := ret.Get(0).(func(context.Context, common.Hash) []byte); ok { + r0 = rf(ctx, ger) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]byte) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, common.Hash) error); ok { + r1 = rf(ctx, ger) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// CertificateValidateAndSigner_ValidateAndSignGER_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ValidateAndSignGER' +type CertificateValidateAndSigner_ValidateAndSignGER_Call struct { + *mock.Call +} + +// ValidateAndSignGER is a helper method to define mock.On call +// - ctx context.Context +// - ger common.Hash +func (_e *CertificateValidateAndSigner_Expecter) ValidateAndSignGER(ctx interface{}, ger interface{}) *CertificateValidateAndSigner_ValidateAndSignGER_Call { + return &CertificateValidateAndSigner_ValidateAndSignGER_Call{Call: _e.mock.On("ValidateAndSignGER", ctx, ger)} +} + +func (_c *CertificateValidateAndSigner_ValidateAndSignGER_Call) Run(run func(ctx context.Context, ger common.Hash)) *CertificateValidateAndSigner_ValidateAndSignGER_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(common.Hash)) + }) + return _c +} + +func (_c *CertificateValidateAndSigner_ValidateAndSignGER_Call) Return(_a0 []byte, _a1 error) *CertificateValidateAndSigner_ValidateAndSignGER_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *CertificateValidateAndSigner_ValidateAndSignGER_Call) RunAndReturn(run func(context.Context, common.Hash) ([]byte, error)) *CertificateValidateAndSigner_ValidateAndSignGER_Call { + _c.Call.Return(run) + return _c +} + // NewCertificateValidateAndSigner creates a new instance of CertificateValidateAndSigner. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. // The first argument is typically a *testing.T value. func NewCertificateValidateAndSigner(t interface { diff --git a/aggsender/mocks/mock_certificate_validator.go b/aggsender/mocks/mock_certificate_validator.go index ca88aa196..96ac27a97 100644 --- a/aggsender/mocks/mock_certificate_validator.go +++ b/aggsender/mocks/mock_certificate_validator.go @@ -5,8 +5,11 @@ package mocks import ( context "context" - types "github.com/agglayer/aggkit/aggsender/types" + common "github.com/ethereum/go-ethereum/common" + mock "github.com/stretchr/testify/mock" + + types "github.com/agglayer/aggkit/aggsender/types" ) // CertificateValidator is an autogenerated mock type for the CertificateValidator type @@ -69,6 +72,53 @@ func (_c *CertificateValidator_ValidateCertificate_Call) RunAndReturn(run func(c return _c } +// ValidateGER provides a mock function with given fields: ctx, ger +func (_m *CertificateValidator) ValidateGER(ctx context.Context, ger common.Hash) error { + ret := _m.Called(ctx, ger) + + if len(ret) == 0 { + panic("no return value specified for ValidateGER") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, common.Hash) error); ok { + r0 = rf(ctx, ger) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// CertificateValidator_ValidateGER_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ValidateGER' +type CertificateValidator_ValidateGER_Call struct { + *mock.Call +} + +// ValidateGER is a helper method to define mock.On call +// - ctx context.Context +// - ger common.Hash +func (_e *CertificateValidator_Expecter) ValidateGER(ctx interface{}, ger interface{}) *CertificateValidator_ValidateGER_Call { + return &CertificateValidator_ValidateGER_Call{Call: _e.mock.On("ValidateGER", ctx, ger)} +} + +func (_c *CertificateValidator_ValidateGER_Call) Run(run func(ctx context.Context, ger common.Hash)) *CertificateValidator_ValidateGER_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(common.Hash)) + }) + return _c +} + +func (_c *CertificateValidator_ValidateGER_Call) Return(_a0 error) *CertificateValidator_ValidateGER_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *CertificateValidator_ValidateGER_Call) RunAndReturn(run func(context.Context, common.Hash) error) *CertificateValidator_ValidateGER_Call { + _c.Call.Return(run) + return _c +} + // NewCertificateValidator creates a new instance of CertificateValidator. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. // The first argument is typically a *testing.T value. func NewCertificateValidator(t interface { diff --git a/aggsender/mocks/mock_l1_ger_querier.go b/aggsender/mocks/mock_l1_ger_querier.go new file mode 100644 index 000000000..da01d7c59 --- /dev/null +++ b/aggsender/mocks/mock_l1_ger_querier.go @@ -0,0 +1,95 @@ +// Code generated by mockery. DO NOT EDIT. + +package mocks + +import ( + context "context" + + common "github.com/ethereum/go-ethereum/common" + + mock "github.com/stretchr/testify/mock" +) + +// L1GERQuerier is an autogenerated mock type for the L1GERQuerier type +type L1GERQuerier struct { + mock.Mock +} + +type L1GERQuerier_Expecter struct { + mock *mock.Mock +} + +func (_m *L1GERQuerier) EXPECT() *L1GERQuerier_Expecter { + return &L1GERQuerier_Expecter{mock: &_m.Mock} +} + +// DoesGERExistOnContract provides a mock function with given fields: ctx, ger +func (_m *L1GERQuerier) DoesGERExistOnContract(ctx context.Context, ger common.Hash) (bool, error) { + ret := _m.Called(ctx, ger) + + if len(ret) == 0 { + panic("no return value specified for DoesGERExistOnContract") + } + + var r0 bool + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, common.Hash) (bool, error)); ok { + return rf(ctx, ger) + } + if rf, ok := ret.Get(0).(func(context.Context, common.Hash) bool); ok { + r0 = rf(ctx, ger) + } else { + r0 = ret.Get(0).(bool) + } + + if rf, ok := ret.Get(1).(func(context.Context, common.Hash) error); ok { + r1 = rf(ctx, ger) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// L1GERQuerier_DoesGERExistOnContract_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DoesGERExistOnContract' +type L1GERQuerier_DoesGERExistOnContract_Call struct { + *mock.Call +} + +// DoesGERExistOnContract is a helper method to define mock.On call +// - ctx context.Context +// - ger common.Hash +func (_e *L1GERQuerier_Expecter) DoesGERExistOnContract(ctx interface{}, ger interface{}) *L1GERQuerier_DoesGERExistOnContract_Call { + return &L1GERQuerier_DoesGERExistOnContract_Call{Call: _e.mock.On("DoesGERExistOnContract", ctx, ger)} +} + +func (_c *L1GERQuerier_DoesGERExistOnContract_Call) Run(run func(ctx context.Context, ger common.Hash)) *L1GERQuerier_DoesGERExistOnContract_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(common.Hash)) + }) + return _c +} + +func (_c *L1GERQuerier_DoesGERExistOnContract_Call) Return(_a0 bool, _a1 error) *L1GERQuerier_DoesGERExistOnContract_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *L1GERQuerier_DoesGERExistOnContract_Call) RunAndReturn(run func(context.Context, common.Hash) (bool, error)) *L1GERQuerier_DoesGERExistOnContract_Call { + _c.Call.Return(run) + return _c +} + +// NewL1GERQuerier creates a new instance of L1GERQuerier. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewL1GERQuerier(t interface { + mock.TestingT + Cleanup(func()) +}) *L1GERQuerier { + mock := &L1GERQuerier{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/aggsender/mocks/mock_ger_querier.go b/aggsender/mocks/mock_l2_ger_querier.go similarity index 52% rename from aggsender/mocks/mock_ger_querier.go rename to aggsender/mocks/mock_l2_ger_querier.go index fe05effd3..3240c9c4c 100644 --- a/aggsender/mocks/mock_ger_querier.go +++ b/aggsender/mocks/mock_l2_ger_querier.go @@ -12,21 +12,21 @@ import ( types "github.com/agglayer/aggkit/agglayer/types" ) -// GERQuerier is an autogenerated mock type for the GERQuerier type -type GERQuerier struct { +// L2GERQuerier is an autogenerated mock type for the L2GERQuerier type +type L2GERQuerier struct { mock.Mock } -type GERQuerier_Expecter struct { +type L2GERQuerier_Expecter struct { mock *mock.Mock } -func (_m *GERQuerier) EXPECT() *GERQuerier_Expecter { - return &GERQuerier_Expecter{mock: &_m.Mock} +func (_m *L2GERQuerier) EXPECT() *L2GERQuerier_Expecter { + return &L2GERQuerier_Expecter{mock: &_m.Mock} } // GetInjectedGERsProofs provides a mock function with given fields: ctx, finalizedL1InfoTreeRootHash, fromBlock, toBlock -func (_m *GERQuerier) GetInjectedGERsProofs(ctx context.Context, finalizedL1InfoTreeRootHash common.Hash, fromBlock uint64, toBlock uint64) (map[common.Hash]*types.ProvenInsertedGERWithBlockNumber, error) { +func (_m *L2GERQuerier) GetInjectedGERsProofs(ctx context.Context, finalizedL1InfoTreeRootHash common.Hash, fromBlock uint64, toBlock uint64) (map[common.Hash]*types.ProvenInsertedGERWithBlockNumber, error) { ret := _m.Called(ctx, finalizedL1InfoTreeRootHash, fromBlock, toBlock) if len(ret) == 0 { @@ -55,8 +55,8 @@ func (_m *GERQuerier) GetInjectedGERsProofs(ctx context.Context, finalizedL1Info return r0, r1 } -// GERQuerier_GetInjectedGERsProofs_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetInjectedGERsProofs' -type GERQuerier_GetInjectedGERsProofs_Call struct { +// L2GERQuerier_GetInjectedGERsProofs_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetInjectedGERsProofs' +type L2GERQuerier_GetInjectedGERsProofs_Call struct { *mock.Call } @@ -65,29 +65,29 @@ type GERQuerier_GetInjectedGERsProofs_Call struct { // - finalizedL1InfoTreeRootHash common.Hash // - fromBlock uint64 // - toBlock uint64 -func (_e *GERQuerier_Expecter) GetInjectedGERsProofs(ctx interface{}, finalizedL1InfoTreeRootHash interface{}, fromBlock interface{}, toBlock interface{}) *GERQuerier_GetInjectedGERsProofs_Call { - return &GERQuerier_GetInjectedGERsProofs_Call{Call: _e.mock.On("GetInjectedGERsProofs", ctx, finalizedL1InfoTreeRootHash, fromBlock, toBlock)} +func (_e *L2GERQuerier_Expecter) GetInjectedGERsProofs(ctx interface{}, finalizedL1InfoTreeRootHash interface{}, fromBlock interface{}, toBlock interface{}) *L2GERQuerier_GetInjectedGERsProofs_Call { + return &L2GERQuerier_GetInjectedGERsProofs_Call{Call: _e.mock.On("GetInjectedGERsProofs", ctx, finalizedL1InfoTreeRootHash, fromBlock, toBlock)} } -func (_c *GERQuerier_GetInjectedGERsProofs_Call) Run(run func(ctx context.Context, finalizedL1InfoTreeRootHash common.Hash, fromBlock uint64, toBlock uint64)) *GERQuerier_GetInjectedGERsProofs_Call { +func (_c *L2GERQuerier_GetInjectedGERsProofs_Call) Run(run func(ctx context.Context, finalizedL1InfoTreeRootHash common.Hash, fromBlock uint64, toBlock uint64)) *L2GERQuerier_GetInjectedGERsProofs_Call { _c.Call.Run(func(args mock.Arguments) { run(args[0].(context.Context), args[1].(common.Hash), args[2].(uint64), args[3].(uint64)) }) return _c } -func (_c *GERQuerier_GetInjectedGERsProofs_Call) Return(_a0 map[common.Hash]*types.ProvenInsertedGERWithBlockNumber, _a1 error) *GERQuerier_GetInjectedGERsProofs_Call { +func (_c *L2GERQuerier_GetInjectedGERsProofs_Call) Return(_a0 map[common.Hash]*types.ProvenInsertedGERWithBlockNumber, _a1 error) *L2GERQuerier_GetInjectedGERsProofs_Call { _c.Call.Return(_a0, _a1) return _c } -func (_c *GERQuerier_GetInjectedGERsProofs_Call) RunAndReturn(run func(context.Context, common.Hash, uint64, uint64) (map[common.Hash]*types.ProvenInsertedGERWithBlockNumber, error)) *GERQuerier_GetInjectedGERsProofs_Call { +func (_c *L2GERQuerier_GetInjectedGERsProofs_Call) RunAndReturn(run func(context.Context, common.Hash, uint64, uint64) (map[common.Hash]*types.ProvenInsertedGERWithBlockNumber, error)) *L2GERQuerier_GetInjectedGERsProofs_Call { _c.Call.Return(run) return _c } // GetRemovedGERsForRange provides a mock function with given fields: ctx, fromBlock, toBlock -func (_m *GERQuerier) GetRemovedGERsForRange(ctx context.Context, fromBlock uint64, toBlock uint64) ([]*types.RemovedGER, error) { +func (_m *L2GERQuerier) GetRemovedGERsForRange(ctx context.Context, fromBlock uint64, toBlock uint64) ([]*types.RemovedGER, error) { ret := _m.Called(ctx, fromBlock, toBlock) if len(ret) == 0 { @@ -116,8 +116,8 @@ func (_m *GERQuerier) GetRemovedGERsForRange(ctx context.Context, fromBlock uint return r0, r1 } -// GERQuerier_GetRemovedGERsForRange_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetRemovedGERsForRange' -type GERQuerier_GetRemovedGERsForRange_Call struct { +// L2GERQuerier_GetRemovedGERsForRange_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetRemovedGERsForRange' +type L2GERQuerier_GetRemovedGERsForRange_Call struct { *mock.Call } @@ -125,34 +125,34 @@ type GERQuerier_GetRemovedGERsForRange_Call struct { // - ctx context.Context // - fromBlock uint64 // - toBlock uint64 -func (_e *GERQuerier_Expecter) GetRemovedGERsForRange(ctx interface{}, fromBlock interface{}, toBlock interface{}) *GERQuerier_GetRemovedGERsForRange_Call { - return &GERQuerier_GetRemovedGERsForRange_Call{Call: _e.mock.On("GetRemovedGERsForRange", ctx, fromBlock, toBlock)} +func (_e *L2GERQuerier_Expecter) GetRemovedGERsForRange(ctx interface{}, fromBlock interface{}, toBlock interface{}) *L2GERQuerier_GetRemovedGERsForRange_Call { + return &L2GERQuerier_GetRemovedGERsForRange_Call{Call: _e.mock.On("GetRemovedGERsForRange", ctx, fromBlock, toBlock)} } -func (_c *GERQuerier_GetRemovedGERsForRange_Call) Run(run func(ctx context.Context, fromBlock uint64, toBlock uint64)) *GERQuerier_GetRemovedGERsForRange_Call { +func (_c *L2GERQuerier_GetRemovedGERsForRange_Call) Run(run func(ctx context.Context, fromBlock uint64, toBlock uint64)) *L2GERQuerier_GetRemovedGERsForRange_Call { _c.Call.Run(func(args mock.Arguments) { run(args[0].(context.Context), args[1].(uint64), args[2].(uint64)) }) return _c } -func (_c *GERQuerier_GetRemovedGERsForRange_Call) Return(_a0 []*types.RemovedGER, _a1 error) *GERQuerier_GetRemovedGERsForRange_Call { +func (_c *L2GERQuerier_GetRemovedGERsForRange_Call) Return(_a0 []*types.RemovedGER, _a1 error) *L2GERQuerier_GetRemovedGERsForRange_Call { _c.Call.Return(_a0, _a1) return _c } -func (_c *GERQuerier_GetRemovedGERsForRange_Call) RunAndReturn(run func(context.Context, uint64, uint64) ([]*types.RemovedGER, error)) *GERQuerier_GetRemovedGERsForRange_Call { +func (_c *L2GERQuerier_GetRemovedGERsForRange_Call) RunAndReturn(run func(context.Context, uint64, uint64) ([]*types.RemovedGER, error)) *L2GERQuerier_GetRemovedGERsForRange_Call { _c.Call.Return(run) return _c } -// NewGERQuerier creates a new instance of GERQuerier. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// NewL2GERQuerier creates a new instance of L2GERQuerier. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. // The first argument is typically a *testing.T value. -func NewGERQuerier(t interface { +func NewL2GERQuerier(t interface { mock.TestingT Cleanup(func()) -}) *GERQuerier { - mock := &GERQuerier{} +}) *L2GERQuerier { + mock := &L2GERQuerier{} mock.Mock.Test(t) t.Cleanup(func() { mock.AssertExpectations(t) }) diff --git a/aggsender/mocks/mock_validator_client.go b/aggsender/mocks/mock_validator_client.go index 0c1a96c47..68c819471 100644 --- a/aggsender/mocks/mock_validator_client.go +++ b/aggsender/mocks/mock_validator_client.go @@ -145,6 +145,65 @@ func (_c *ValidatorClient_ValidateCertificate_Call) RunAndReturn(run func(contex return _c } +// ValidateGER provides a mock function with given fields: ctx, ger +func (_m *ValidatorClient) ValidateGER(ctx context.Context, ger common.Hash) ([]byte, error) { + ret := _m.Called(ctx, ger) + + if len(ret) == 0 { + panic("no return value specified for ValidateGER") + } + + var r0 []byte + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, common.Hash) ([]byte, error)); ok { + return rf(ctx, ger) + } + if rf, ok := ret.Get(0).(func(context.Context, common.Hash) []byte); ok { + r0 = rf(ctx, ger) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]byte) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, common.Hash) error); ok { + r1 = rf(ctx, ger) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// ValidatorClient_ValidateGER_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ValidateGER' +type ValidatorClient_ValidateGER_Call struct { + *mock.Call +} + +// ValidateGER is a helper method to define mock.On call +// - ctx context.Context +// - ger common.Hash +func (_e *ValidatorClient_Expecter) ValidateGER(ctx interface{}, ger interface{}) *ValidatorClient_ValidateGER_Call { + return &ValidatorClient_ValidateGER_Call{Call: _e.mock.On("ValidateGER", ctx, ger)} +} + +func (_c *ValidatorClient_ValidateGER_Call) Run(run func(ctx context.Context, ger common.Hash)) *ValidatorClient_ValidateGER_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(common.Hash)) + }) + return _c +} + +func (_c *ValidatorClient_ValidateGER_Call) Return(_a0 []byte, _a1 error) *ValidatorClient_ValidateGER_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *ValidatorClient_ValidateGER_Call) RunAndReturn(run func(context.Context, common.Hash) ([]byte, error)) *ValidatorClient_ValidateGER_Call { + _c.Call.Return(run) + return _c +} + // NewValidatorClient creates a new instance of ValidatorClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. // The first argument is typically a *testing.T value. func NewValidatorClient(t interface { diff --git a/aggsender/prover/proof_generation_tool.go b/aggsender/prover/proof_generation_tool.go index 48cf9e2fe..fd0185e4e 100644 --- a/aggsender/prover/proof_generation_tool.go +++ b/aggsender/prover/proof_generation_tool.go @@ -111,7 +111,7 @@ func NewAggchainProofGenerationTool( l1InfoTreeQuerier, nil, // optimistic signer is not used in the tool, so we pass nil baseFlow, - query.NewGERDataQuerier(l1InfoTreeQuerier, l2GERReader), + query.NewL2GERDataQuerier(l1InfoTreeQuerier, l2GERReader), query.NewBridgeDataQuerier(logger, l2Syncer, time.Second, agglayerBridgeL2Reader), ) diff --git a/aggsender/query/aggchain_proof_query.go b/aggsender/query/aggchain_proof_query.go index ffc599978..f0bb1f1a3 100644 --- a/aggsender/query/aggchain_proof_query.go +++ b/aggsender/query/aggchain_proof_query.go @@ -32,7 +32,7 @@ type aggchainProofQuery struct { l1InfoTreeDataQuerier types.L1InfoTreeDataQuerier lerQuerier types.LocalExitRootQuery optimisticSigner types.OptimisticSigner - gerQuerier types.GERQuerier + gerQuerier types.L2GERQuerier bridgeQuerier types.BridgeQuerier } @@ -43,7 +43,7 @@ func NewAggchainProofQuery( l1InfoTreeDataQuerier types.L1InfoTreeDataQuerier, optimisticSigner types.OptimisticSigner, lerQuerier types.LocalExitRootQuery, - gerQuerier types.GERQuerier, + gerQuerier types.L2GERQuerier, bridgeQuerier types.BridgeQuerier, ) *aggchainProofQuery { return &aggchainProofQuery{ diff --git a/aggsender/query/aggchain_proof_query_test.go b/aggsender/query/aggchain_proof_query_test.go index 209d4f5fc..92d07e9ee 100644 --- a/aggsender/query/aggchain_proof_query_test.go +++ b/aggsender/query/aggchain_proof_query_test.go @@ -251,7 +251,7 @@ func TestGenerateAggchainProof(t *testing.T) { mockFn func( *mocks.AggchainProofClientInterface, *mocks.L1InfoTreeDataQuerier, - *mocks.GERQuerier, + *mocks.L2GERQuerier, *mocks.BridgeQuerier) lastProvenBlock uint64 buildParams *types.CertificateBuildParams @@ -264,7 +264,7 @@ func TestGenerateAggchainProof(t *testing.T) { buildParams: &types.CertificateBuildParams{L1InfoTreeLeafCount: 1, L1InfoTreeRootFromWhichToProve: common.HexToHash("0x1")}, mockFn: func(aggchainProofClient *mocks.AggchainProofClientInterface, l1InfoTreeDataQuerier *mocks.L1InfoTreeDataQuerier, - gerQuerier *mocks.GERQuerier, + gerQuerier *mocks.L2GERQuerier, bridgeQuerier *mocks.BridgeQuerier) { l1InfoTreeDataQuerier.EXPECT().GetFinalizedL1InfoTreeData(ctx, common.HexToHash("0x1"), uint32(1)).Return(treetypes.Proof{}, nil, errors.New("some error")) }, @@ -276,7 +276,7 @@ func TestGenerateAggchainProof(t *testing.T) { buildParams: &types.CertificateBuildParams{L1InfoTreeLeafCount: 2, L1InfoTreeRootFromWhichToProve: common.HexToHash("0x1"), ToBlock: 200, Claims: []bridgesync.Claim{{}}}, mockFn: func(aggchainProofClient *mocks.AggchainProofClientInterface, l1InfoTreeDataQuerier *mocks.L1InfoTreeDataQuerier, - gerQuerier *mocks.GERQuerier, + gerQuerier *mocks.L2GERQuerier, bridgeQuerier *mocks.BridgeQuerier) { l1InfoTreeDataQuerier.EXPECT().GetFinalizedL1InfoTreeData(ctx, common.HexToHash("0x1"), uint32(2)).Return( treetypes.Proof{}, @@ -292,7 +292,7 @@ func TestGenerateAggchainProof(t *testing.T) { buildParams: &types.CertificateBuildParams{L1InfoTreeLeafCount: 2, L1InfoTreeRootFromWhichToProve: common.HexToHash("0x123"), ToBlock: 200, Claims: []bridgesync.Claim{{GlobalIndex: big.NewInt(1)}}}, mockFn: func(aggchainProofClient *mocks.AggchainProofClientInterface, l1InfoTreeDataQuerier *mocks.L1InfoTreeDataQuerier, - gerQuerier *mocks.GERQuerier, + gerQuerier *mocks.L2GERQuerier, bridgeQuerier *mocks.BridgeQuerier) { l1InfoTreeDataQuerier.EXPECT().GetFinalizedL1InfoTreeData(ctx, common.HexToHash("0x123"), uint32(2)).Return( treetypes.Proof{}, @@ -311,7 +311,7 @@ func TestGenerateAggchainProof(t *testing.T) { buildParams: &types.CertificateBuildParams{L1InfoTreeLeafCount: 2, L1InfoTreeRootFromWhichToProve: common.HexToHash("0x123"), ToBlock: 200, Claims: []bridgesync.Claim{{GlobalIndex: big.NewInt(1)}}}, mockFn: func(aggchainProofClient *mocks.AggchainProofClientInterface, l1InfoTreeDataQuerier *mocks.L1InfoTreeDataQuerier, - gerQuerier *mocks.GERQuerier, + gerQuerier *mocks.L2GERQuerier, bridgeQuerier *mocks.BridgeQuerier) { l1InfoTreeDataQuerier.EXPECT().GetFinalizedL1InfoTreeData(ctx, common.HexToHash("0x123"), uint32(2)).Return( treetypes.Proof{}, @@ -342,7 +342,7 @@ func TestGenerateAggchainProof(t *testing.T) { aggchainProofClient := mocks.NewAggchainProofClientInterface(t) l1InfoTreeDataQuerier := mocks.NewL1InfoTreeDataQuerier(t) - gerQuerier := mocks.NewGERQuerier(t) + gerQuerier := mocks.NewL2GERQuerier(t) bridgeQuerier := mocks.NewBridgeQuerier(t) if tc.mockFn != nil { tc.mockFn(aggchainProofClient, l1InfoTreeDataQuerier, gerQuerier, bridgeQuerier) diff --git a/aggsender/query/ger_query.go b/aggsender/query/ger_query.go index 5824658bc..dd16b1973 100644 --- a/aggsender/query/ger_query.go +++ b/aggsender/query/ger_query.go @@ -3,25 +3,34 @@ package query import ( "context" "fmt" + "math/big" + "github.com/0xPolygon/cdk-contracts-tooling/contracts/aggchain-multisig/agglayerger" agglayertypes "github.com/agglayer/aggkit/agglayer/types" "github.com/agglayer/aggkit/aggsender/types" + aggkittypes "github.com/agglayer/aggkit/types" + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" ) -var _ types.GERQuerier = (*gerDataQuerier)(nil) +var createAgglayerGERL1func = func(gerAddr common.Address, + l1Client aggkittypes.BaseEthereumClienter) (types.AgglayerGER, error) { + return agglayerger.NewAgglayerger(gerAddr, l1Client) +} + +var _ types.L2GERQuerier = (*l2GERDataQuerier)(nil) -// gerDataQuerier is a struct that holds the logic to query the GER (Global Exit Root) data -type gerDataQuerier struct { +// l2GERDataQuerier is a struct that holds the logic to query the GER (Global Exit Root) data +type l2GERDataQuerier struct { l1InfoTreeQuerier types.L1InfoTreeDataQuerier chainGERReader types.ChainGERReader } -// NewGERDataQuerier returns a new instance of the GERQuerier -func NewGERDataQuerier( +// NewL2GERDataQuerier returns a new instance of the GERQuerier for L2 chains +func NewL2GERDataQuerier( l1InfoTreeQuerier types.L1InfoTreeDataQuerier, - chainGERReader types.ChainGERReader) types.GERQuerier { - return &gerDataQuerier{ + chainGERReader types.ChainGERReader) types.L2GERQuerier { + return &l2GERDataQuerier{ l1InfoTreeQuerier: l1InfoTreeQuerier, chainGERReader: chainGERReader, } @@ -44,7 +53,7 @@ func NewGERDataQuerier( // Errors: // - Returns an error if there is an issue querying the chain for injected GERs. // - Returns an error if there is an issue generating proofs for any GER. -func (g *gerDataQuerier) GetInjectedGERsProofs( +func (g *l2GERDataQuerier) GetInjectedGERsProofs( ctx context.Context, finalizedL1InfoTreeRootHash common.Hash, fromBlock, toBlock uint64) (map[common.Hash]*agglayertypes.ProvenInsertedGERWithBlockNumber, error) { @@ -89,7 +98,7 @@ func (g *gerDataQuerier) GetInjectedGERsProofs( } // GetRemovedGERsForRange returns the removed GlobalExitRoots for the given block range -func (g *gerDataQuerier) GetRemovedGERsForRange(ctx context.Context, +func (g *l2GERDataQuerier) GetRemovedGERsForRange(ctx context.Context, fromBlock, toBlock uint64) ([]*agglayertypes.RemovedGER, error) { removedGERs, err := g.chainGERReader.GetRemovedGERsForRange(ctx, fromBlock, toBlock) if err != nil { @@ -98,3 +107,52 @@ func (g *gerDataQuerier) GetRemovedGERsForRange(ctx context.Context, } return removedGERs, nil } + +var _ types.L1GERQuerier = (*l1GERDataQuerier)(nil) + +// l1GERDataQuerier is a struct that holds the logic to query the L1 GER (Global Exit Root) data +type l1GERDataQuerier struct { + blockFinality aggkittypes.BlockNumberFinality + agglayerGER types.AgglayerGER + l1Client aggkittypes.BaseEthereumClienter +} + +// NewL1GERDataQuerier returns a new instance of the L1GERDataQuerier +func NewL1GERDataQuerier( + l1AgglayerGERAddr common.Address, + blockFinality aggkittypes.BlockNumberFinality, + l1Client aggkittypes.BaseEthereumClienter, +) (types.L1GERQuerier, error) { + agglayerGER, err := createAgglayerGERL1func(l1AgglayerGERAddr, l1Client) + if err != nil { + return nil, fmt.Errorf("failed to initialize L1 GER manager contract: %w", err) + } + + return &l1GERDataQuerier{ + l1Client: l1Client, + agglayerGER: agglayerGER, + blockFinality: blockFinality, + }, nil +} + +// DoesGERExistOnContract checks if the given GER exists on the Agglayer GER contract +func (g *l1GERDataQuerier) DoesGERExistOnContract(ctx context.Context, ger common.Hash) (bool, error) { + // TODO - maybe get the header and use block hash instead? + blockNum, err := g.blockFinality.BlockNumber(ctx, g.l1Client) + if err != nil { + return false, fmt.Errorf("error getting block number for finality %s: %w", g.blockFinality.String(), err) + } + + timestamp, err := g.agglayerGER.GlobalExitRootMap( + &bind.CallOpts{ + Context: ctx, + BlockNumber: new(big.Int).SetUint64(blockNum), + }, + ger, + ) + if err != nil { + return false, fmt.Errorf("error querying GER existence on contract: %w", err) + } + + return timestamp.Cmp(common.Big0) != 0, nil +} diff --git a/aggsender/query/ger_query_test.go b/aggsender/query/ger_query_test.go index 53fb4f7ed..cbdcf0330 100644 --- a/aggsender/query/ger_query_test.go +++ b/aggsender/query/ger_query_test.go @@ -3,14 +3,21 @@ package query import ( "context" "errors" + "math/big" "testing" agglayertypes "github.com/agglayer/aggkit/agglayer/types" "github.com/agglayer/aggkit/aggsender/mocks" + "github.com/agglayer/aggkit/aggsender/types" "github.com/agglayer/aggkit/l1infotreesync" "github.com/agglayer/aggkit/l2gersync" 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/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" + ethtypes "github.com/ethereum/go-ethereum/core/types" + "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" ) @@ -88,13 +95,12 @@ func Test_GetInjectedGERsProofs(t *testing.T) { } for _, tc := range testCases { - tc := tc t.Run(tc.name, func(t *testing.T) { t.Parallel() mockGERReader := mocks.NewChainGERReader(t) mockL1InfoTreeQuery := mocks.NewL1InfoTreeDataQuerier(t) - gerQuerier := NewGERDataQuerier(mockL1InfoTreeQuery, mockGERReader) + gerQuerier := NewL2GERDataQuerier(mockL1InfoTreeQuery, mockGERReader) tc.mockFn(mockGERReader, mockL1InfoTreeQuery) @@ -198,13 +204,12 @@ func Test_GetRemovedGERsForRange(t *testing.T) { } for _, tc := range testCases { - tc := tc t.Run(tc.name, func(t *testing.T) { t.Parallel() mockGERReader := mocks.NewChainGERReader(t) mockL1InfoTreeQuery := mocks.NewL1InfoTreeDataQuerier(t) - gerQuerier := NewGERDataQuerier(mockL1InfoTreeQuery, mockGERReader) + gerQuerier := NewL2GERDataQuerier(mockL1InfoTreeQuery, mockGERReader) tc.mockFn(mockGERReader) @@ -220,3 +225,168 @@ func Test_GetRemovedGERsForRange(t *testing.T) { }) } } + +func Test_NewL1GERDataQuerier(t *testing.T) { + t.Run("error initializing L1 GER manager contract", func(t *testing.T) { + createAgglayerGERL1func = func(_ common.Address, _ aggkittypes.BaseEthereumClienter) (types.AgglayerGER, error) { + return nil, errors.New("some error") + } + + _, err := NewL1GERDataQuerier( + common.HexToAddress("0x1"), + aggkittypes.FinalizedBlock, + aggkittypesmocks.NewBaseEthereumClienter(t), + ) + require.ErrorContains(t, err, "failed to initialize L1 GER manager contract: some error") + }) + + t.Run("success", func(t *testing.T) { + createAgglayerGERL1func = func(_ common.Address, _ aggkittypes.BaseEthereumClienter) (types.AgglayerGER, error) { + return mocks.NewAgglayerGER(t), nil + } + + gerQuerier, err := NewL1GERDataQuerier( + common.HexToAddress("0x1"), + aggkittypes.FinalizedBlock, + aggkittypesmocks.NewBaseEthereumClienter(t), + ) + require.NoError(t, err) + require.NotNil(t, gerQuerier) + }) +} + +func Test_L1GERDataQuerier_DoesGERExistOnContract(t *testing.T) { + t.Parallel() + + testCases := []struct { + name string + ger common.Hash + blockFinality *aggkittypes.BlockNumberFinality + mockFn func(*mocks.AgglayerGER, *aggkittypesmocks.BaseEthereumClienter) + expectedExist bool + expectedError string + }{ + { + name: "error getting block number for finality", + ger: common.HexToHash("0x1"), + mockFn: func(mockAgglayerGER *mocks.AgglayerGER, mockL1Client *aggkittypesmocks.BaseEthereumClienter) { + mockL1Client.EXPECT().HeaderByNumber( + t.Context(), + big.NewInt(int64(aggkittypes.Finalized)), + ).Return(nil, errors.New("some error")) + }, + expectedError: "error getting block number for finality FinalizedBlock", + }, + { + name: "error querying GER existence on contract", + ger: common.HexToHash("0x1"), + mockFn: func(mockAgglayerGER *mocks.AgglayerGER, mockL1Client *aggkittypesmocks.BaseEthereumClienter) { + mockL1Client.EXPECT().HeaderByNumber( + t.Context(), + big.NewInt(int64(aggkittypes.Finalized)), + ).Return(ðtypes.Header{Number: big.NewInt(100)}, nil) + mockAgglayerGER.EXPECT().GlobalExitRootMap( + &bind.CallOpts{ + Context: t.Context(), + BlockNumber: big.NewInt(100), + }, + mock.Anything, + ).Return(nil, errors.New("some error")) + }, + expectedError: "error querying GER existence on contract: some error", + }, + { + name: "GER does not exist", + ger: common.HexToHash("0x1"), + mockFn: func(mockAgglayerGER *mocks.AgglayerGER, mockL1Client *aggkittypesmocks.BaseEthereumClienter) { + mockL1Client.EXPECT().HeaderByNumber( + t.Context(), + big.NewInt(int64(aggkittypes.Finalized)), + ).Return(ðtypes.Header{Number: big.NewInt(100)}, nil) + mockAgglayerGER.EXPECT().GlobalExitRootMap( + &bind.CallOpts{ + Context: t.Context(), + BlockNumber: big.NewInt(100), + }, + mock.Anything, + ).Return(common.Big0, nil) + }, + expectedExist: false, + }, + { + name: "GER exists", + ger: common.HexToHash("0x1"), + mockFn: func(mockAgglayerGER *mocks.AgglayerGER, mockL1Client *aggkittypesmocks.BaseEthereumClienter) { + mockL1Client.EXPECT().HeaderByNumber( + t.Context(), + big.NewInt(int64(aggkittypes.Finalized)), + ).Return(ðtypes.Header{Number: big.NewInt(100)}, nil) + mockAgglayerGER.EXPECT().GlobalExitRootMap( + &bind.CallOpts{ + Context: t.Context(), + BlockNumber: big.NewInt(100), + }, + mock.Anything, + ).Return(big.NewInt(12345), nil) + }, + expectedExist: true, + }, + { + name: "GER exists with block finality with offset", + ger: common.HexToHash("0x1"), + blockFinality: &aggkittypes.BlockNumberFinality{ + Block: aggkittypes.Latest, + Offset: -6, + }, + mockFn: func(mockAgglayerGER *mocks.AgglayerGER, mockL1Client *aggkittypesmocks.BaseEthereumClienter) { + mockL1Client.EXPECT().HeaderByNumber( + t.Context(), + (*big.Int)(nil), + ).Return(ðtypes.Header{Number: big.NewInt(200)}, nil) + mockAgglayerGER.EXPECT().GlobalExitRootMap( + &bind.CallOpts{ + Context: t.Context(), + BlockNumber: big.NewInt(194), + }, + mock.Anything, + ).Return(big.NewInt(67890), nil) + }, + expectedExist: true, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + mockAgglayerGER := mocks.NewAgglayerGER(t) + mockL1Client := aggkittypesmocks.NewBaseEthereumClienter(t) + + blockFinality := aggkittypes.FinalizedBlock + if tc.blockFinality != nil { + blockFinality = *tc.blockFinality + } + + gerQuerier := &l1GERDataQuerier{ + blockFinality: blockFinality, + agglayerGER: mockAgglayerGER, + l1Client: mockL1Client, + } + + if tc.mockFn != nil { + tc.mockFn(mockAgglayerGER, mockL1Client) + } + + exists, err := gerQuerier.DoesGERExistOnContract(t.Context(), tc.ger) + if tc.expectedError != "" { + require.ErrorContains(t, err, tc.expectedError) + } else { + require.NoError(t, err) + require.Equal(t, tc.expectedExist, exists) + } + + mockAgglayerGER.AssertExpectations(t) + mockL1Client.AssertExpectations(t) + }) + } +} diff --git a/aggsender/types/interfaces.go b/aggsender/types/interfaces.go index 5d0ac24b6..27f3f805d 100644 --- a/aggsender/types/interfaces.go +++ b/aggsender/types/interfaces.go @@ -168,8 +168,8 @@ type L1InfoTreeDataQuerier interface { ) (bool, error) } -// GERQuerier is an interface defining functions that an GERQuerier should implement -type GERQuerier interface { +// L2GERQuerier is an interface defining functions that an L2GERQuerier should implement +type L2GERQuerier interface { GetInjectedGERsProofs( ctx context.Context, finalizedL1InfoTreeRootHash common.Hash, @@ -178,6 +178,16 @@ type GERQuerier interface { fromBlock, toBlock uint64) ([]*agglayertypes.RemovedGER, error) } +// L1GERQuerier is an interface defining functions that an L1GERQuerier should implement +type L1GERQuerier interface { + DoesGERExistOnContract(ctx context.Context, ger common.Hash) (bool, error) +} + +// AgglayerGER is an interface defining functions that an AgglayerGER should implement +type AgglayerGER interface { + GlobalExitRootMap(opts *bind.CallOpts, ger [common.HashLength]byte) (*big.Int, error) +} + // Logger is an interface that defines the methods to log messages type Logger interface { Panicf(format string, args ...interface{}) @@ -257,6 +267,7 @@ func (h *HealthCheckResponse) String() string { type CertificateValidator interface { ValidateCertificate(ctx context.Context, params VerifyIncomingRequest) error + ValidateGER(ctx context.Context, ger common.Hash) error } // CertificateValidateAndSigner is an interface to attach a certificate validator and signer @@ -270,10 +281,19 @@ type CertificateValidateAndSigner interface { certificate *agglayertypes.Certificate, lastL2BlockInCert uint64, ) ([]byte, error) + // URL returns the URL of the validator service URL() string + // String returns a string representation of the validator service String() string + // Address returns the Ethereum address of the validator Address() common.Address + // Index returns the index of the validator in the signers list Index() uint32 + // ValidateAndSignGER validates the GlobalExitRoot that needs to be injected and signs it if valid. + ValidateAndSignGER( + ctx context.Context, + ger common.Hash, + ) ([]byte, error) } // ValidatorClient is an interface defining functions that a ValidatorClient should implement @@ -285,6 +305,10 @@ type ValidatorClient interface { certificate *agglayertypes.Certificate, lastL2BlockInCert uint64, ) ([]byte, error) + ValidateGER( + ctx context.Context, + ger common.Hash, + ) ([]byte, error) } // LocalExitRootQuery is an interface defining functions that a LocalExitRootQuery should implement diff --git a/aggsender/validator/config.go b/aggsender/validator/config.go index 70ef6b3f1..88af0ada0 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" ) @@ -46,14 +47,18 @@ type Config struct { RequireCommitteeMembershipCheck bool `mapstructure:"RequireCommitteeMembershipCheck"` // AgglayerBridgeL2Addr is the address of the bridge L2 sovereign contract on L2 sovereign chain AgglayerBridgeL2Addr ethCommon.Address `mapstructure:"AgglayerBridgeL2Addr"` + // GERValidateConfig contains the configuration for the validation of GlobalExitRoots + GERValidateConfig GERValidateConfig `mapstructure:"GERValidateConfig"` } +// PPConfig contains specific configuration for Pessimistic Proof mode type PPConfig struct { // RequireOneBridgeInPPCertificate is a flag to force the validator to have at least one bridge exit // for the Pessimistic Proof certificates RequireOneBridgeInPPCertificate bool `mapstructure:"RequireOneBridgeInPPCertificate"` } +// FEPConfig contains specific configuration for FEP (AggchainProof) mode type FEPConfig struct { // SovereignRollupAddr is the address of the sovereign rollup contract on L1 SovereignRollupAddr ethCommon.Address `mapstructure:"SovereignRollupAddr"` @@ -64,6 +69,7 @@ type FEPConfig struct { OpNodeURL string `mapstructure:"OpNodeURL"` } +// LerQuerierConfig contains the configuration for the LER querier type LerQuerierConfig struct { // RollupManagerAddr is the address of the RollupManager contract on L1 RollupManagerAddr ethCommon.Address `mapstructure:"RollupManagerAddr"` @@ -71,6 +77,17 @@ type LerQuerierConfig struct { RollupCreationBlockL1 uint64 `mapstructure:"RollupCreationBlockL1"` } +// GERValidateConfig contains the configuration for validating GlobalExitRoots +type GERValidateConfig struct { + // GlobalExitRootL1Addr is the address of the GlobalExitRootManager contract on l1 chain + GlobalExitRootL1Addr ethCommon.Address `mapstructure:"GlobalExitRootL1"` + // BlockFinality indicates the finality of the blocks that will be used to validate the GERs + // Possible values: PendingBlock, LatestBlock, SafeBlock, FinalizedBlock, EarliestBlock + // (with an optional offset, e.g. SafeBlock/-5) + // See also: aggkittypes.BlockNumberFinality + BlockFinality aggkittypes.BlockNumberFinality `jsonschema:"enum=LatestBlock, enum=SafeBlock, enum=PendingBlock, enum=FinalizedBlock, enum=EarliestBlock" mapstructure:"BlockFinality"` //nolint:lll +} + // Validate checks if the configuration is valid func (c *Config) Validate() error { err := c.Mode.Validate() diff --git a/aggsender/validator/local_validator.go b/aggsender/validator/local_validator.go index d73de4310..72efd24dc 100644 --- a/aggsender/validator/local_validator.go +++ b/aggsender/validator/local_validator.go @@ -99,6 +99,26 @@ func (a *LocalValidator) ValidateAndSignCertificate( return aggkitcommon.EmptySignature, nil } +// ValidateAndSignGER validates the GlobalExitRoot that needs to be injected. +// LocalValidator does not sign the GER, it just validates it. +func (a *LocalValidator) ValidateAndSignGER( + ctx context.Context, + ger common.Hash, +) ([]byte, error) { + a.log.Infof("GER validation: %s ....", ger.Hex()) + + err := a.validator.ValidateGER(ctx, ger) + if err != nil { + a.log.Errorf("GER %s validation failed: %s", ger.Hex(), err.Error()) + return nil, fmt.Errorf("GER %s validation failed: %w", ger.Hex(), err) + } + + a.log.Infof("GER %s validation passed", ger.Hex()) + + // local validator does not sign the GER, it just validates it + return aggkitcommon.EmptySignature, nil +} + // getPreviousCertificate retrieves the previous certificate based on the new certificate's height. func getPreviousCertificate( storage db.AggSenderStorage, diff --git a/aggsender/validator/mocks/mock_l1_info_tree_root_by_leaf_querier.go b/aggsender/validator/mocks/mock_l1_info_tree_root_by_leaf_querier.go deleted file mode 100644 index 135c8c722..000000000 --- a/aggsender/validator/mocks/mock_l1_info_tree_root_by_leaf_querier.go +++ /dev/null @@ -1,96 +0,0 @@ -// Code generated by mockery. DO NOT EDIT. - -package mocks - -import ( - context "context" - - types "github.com/agglayer/aggkit/tree/types" - mock "github.com/stretchr/testify/mock" -) - -// L1InfoTreeRootByLeafQuerier is an autogenerated mock type for the L1InfoTreeRootByLeafQuerier type -type L1InfoTreeRootByLeafQuerier struct { - mock.Mock -} - -type L1InfoTreeRootByLeafQuerier_Expecter struct { - mock *mock.Mock -} - -func (_m *L1InfoTreeRootByLeafQuerier) EXPECT() *L1InfoTreeRootByLeafQuerier_Expecter { - return &L1InfoTreeRootByLeafQuerier_Expecter{mock: &_m.Mock} -} - -// GetL1InfoRootByLeafIndex provides a mock function with given fields: ctx, leafCount -func (_m *L1InfoTreeRootByLeafQuerier) GetL1InfoRootByLeafIndex(ctx context.Context, leafCount uint32) (*types.Root, error) { - ret := _m.Called(ctx, leafCount) - - if len(ret) == 0 { - panic("no return value specified for GetL1InfoRootByLeafIndex") - } - - var r0 *types.Root - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, uint32) (*types.Root, error)); ok { - return rf(ctx, leafCount) - } - if rf, ok := ret.Get(0).(func(context.Context, uint32) *types.Root); ok { - r0 = rf(ctx, leafCount) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*types.Root) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, uint32) error); ok { - r1 = rf(ctx, leafCount) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// L1InfoTreeRootByLeafQuerier_GetL1InfoRootByLeafIndex_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetL1InfoRootByLeafIndex' -type L1InfoTreeRootByLeafQuerier_GetL1InfoRootByLeafIndex_Call struct { - *mock.Call -} - -// GetL1InfoRootByLeafIndex is a helper method to define mock.On call -// - ctx context.Context -// - leafCount uint32 -func (_e *L1InfoTreeRootByLeafQuerier_Expecter) GetL1InfoRootByLeafIndex(ctx interface{}, leafCount interface{}) *L1InfoTreeRootByLeafQuerier_GetL1InfoRootByLeafIndex_Call { - return &L1InfoTreeRootByLeafQuerier_GetL1InfoRootByLeafIndex_Call{Call: _e.mock.On("GetL1InfoRootByLeafIndex", ctx, leafCount)} -} - -func (_c *L1InfoTreeRootByLeafQuerier_GetL1InfoRootByLeafIndex_Call) Run(run func(ctx context.Context, leafCount uint32)) *L1InfoTreeRootByLeafQuerier_GetL1InfoRootByLeafIndex_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(uint32)) - }) - return _c -} - -func (_c *L1InfoTreeRootByLeafQuerier_GetL1InfoRootByLeafIndex_Call) Return(_a0 *types.Root, _a1 error) *L1InfoTreeRootByLeafQuerier_GetL1InfoRootByLeafIndex_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *L1InfoTreeRootByLeafQuerier_GetL1InfoRootByLeafIndex_Call) RunAndReturn(run func(context.Context, uint32) (*types.Root, error)) *L1InfoTreeRootByLeafQuerier_GetL1InfoRootByLeafIndex_Call { - _c.Call.Return(run) - return _c -} - -// NewL1InfoTreeRootByLeafQuerier creates a new instance of L1InfoTreeRootByLeafQuerier. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewL1InfoTreeRootByLeafQuerier(t interface { - mock.TestingT - Cleanup(func()) -}) *L1InfoTreeRootByLeafQuerier { - mock := &L1InfoTreeRootByLeafQuerier{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/aggsender/validator/proto/v1/validator.pb.go b/aggsender/validator/proto/v1/validator.pb.go index d0ad449c9..3bed20baa 100644 --- a/aggsender/validator/proto/v1/validator.pb.go +++ b/aggsender/validator/proto/v1/validator.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.32.0 +// protoc-gen-go v1.36.6 // protoc (unknown) // source: aggsender/validator/proto/v1/validator.proto @@ -14,6 +14,7 @@ import ( emptypb "google.golang.org/protobuf/types/known/emptypb" reflect "reflect" sync "sync" + unsafe "unsafe" ) const ( @@ -25,22 +26,19 @@ const ( // Response Status() type HealthCheckResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + Version string `protobuf:"bytes,1,opt,name=version,proto3" json:"version,omitempty"` // Version of the validator + Status string `protobuf:"bytes,2,opt,name=status,proto3" json:"status,omitempty"` // Status of the validator + Reason string `protobuf:"bytes,3,opt,name=reason,proto3" json:"reason,omitempty"` // Reason of the validator status unknownFields protoimpl.UnknownFields - - Version string `protobuf:"bytes,1,opt,name=version,proto3" json:"version,omitempty"` // Version of the validator - Status string `protobuf:"bytes,2,opt,name=status,proto3" json:"status,omitempty"` // Status of the validator - Reason string `protobuf:"bytes,3,opt,name=reason,proto3" json:"reason,omitempty"` // Reason of the validator status + sizeCache protoimpl.SizeCache } func (x *HealthCheckResponse) Reset() { *x = HealthCheckResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_aggsender_validator_proto_v1_validator_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_aggsender_validator_proto_v1_validator_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *HealthCheckResponse) String() string { @@ -51,7 +49,7 @@ func (*HealthCheckResponse) ProtoMessage() {} func (x *HealthCheckResponse) ProtoReflect() protoreflect.Message { mi := &file_aggsender_validator_proto_v1_validator_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -89,25 +87,22 @@ func (x *HealthCheckResponse) GetReason() string { // Request to validate a certificate type ValidateCertificateRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - + state protoimpl.MessageState `protogen:"open.v1"` // Previous certificate identifier PreviousCertificateId *v1.CertificateId `protobuf:"bytes,1,opt,name=previous_certificate_id,json=previousCertificateId,proto3" json:"previous_certificate_id,omitempty"` // Certificate to be validated Certificate *v1.Certificate `protobuf:"bytes,2,opt,name=certificate,proto3" json:"certificate,omitempty"` // Last L2 block number included in the certificate LastL2BlockInCert uint64 `protobuf:"varint,3,opt,name=last_l2_block_in_cert,json=lastL2BlockInCert,proto3" json:"last_l2_block_in_cert,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *ValidateCertificateRequest) Reset() { *x = ValidateCertificateRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_aggsender_validator_proto_v1_validator_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_aggsender_validator_proto_v1_validator_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *ValidateCertificateRequest) String() string { @@ -118,7 +113,7 @@ func (*ValidateCertificateRequest) ProtoMessage() {} func (x *ValidateCertificateRequest) ProtoReflect() protoreflect.Message { mi := &file_aggsender_validator_proto_v1_validator_proto_msgTypes[1] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -156,21 +151,18 @@ func (x *ValidateCertificateRequest) GetLastL2BlockInCert() uint64 { // Type used as response to a certificate validation request. type ValidateCertificateResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - + state protoimpl.MessageState `protogen:"open.v1"` // The signature provided by the aggsender validator. - Signature *v11.FixedBytes65 `protobuf:"bytes,1,opt,name=signature,proto3" json:"signature,omitempty"` + Signature *v11.FixedBytes65 `protobuf:"bytes,1,opt,name=signature,proto3" json:"signature,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *ValidateCertificateResponse) Reset() { *x = ValidateCertificateResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_aggsender_validator_proto_v1_validator_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_aggsender_validator_proto_v1_validator_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *ValidateCertificateResponse) String() string { @@ -181,7 +173,7 @@ func (*ValidateCertificateResponse) ProtoMessage() {} func (x *ValidateCertificateResponse) ProtoReflect() protoreflect.Message { mi := &file_aggsender_validator_proto_v1_validator_proto_msgTypes[2] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -205,21 +197,18 @@ func (x *ValidateCertificateResponse) GetSignature() *v11.FixedBytes65 { // Request to validate a GER. type ValidateGERRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - + state protoimpl.MessageState `protogen:"open.v1"` // GER to be validated - Ger []byte `protobuf:"bytes,1,opt,name=ger,proto3" json:"ger,omitempty"` + Ger *v11.FixedBytes32 `protobuf:"bytes,1,opt,name=ger,proto3" json:"ger,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *ValidateGERRequest) Reset() { *x = ValidateGERRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_aggsender_validator_proto_v1_validator_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_aggsender_validator_proto_v1_validator_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *ValidateGERRequest) String() string { @@ -230,7 +219,7 @@ func (*ValidateGERRequest) ProtoMessage() {} func (x *ValidateGERRequest) ProtoReflect() protoreflect.Message { mi := &file_aggsender_validator_proto_v1_validator_proto_msgTypes[3] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -245,7 +234,7 @@ func (*ValidateGERRequest) Descriptor() ([]byte, []int) { return file_aggsender_validator_proto_v1_validator_proto_rawDescGZIP(), []int{3} } -func (x *ValidateGERRequest) GetGer() []byte { +func (x *ValidateGERRequest) GetGer() *v11.FixedBytes32 { if x != nil { return x.Ger } @@ -254,21 +243,18 @@ func (x *ValidateGERRequest) GetGer() []byte { // Response to a GER validation request. type ValidateGERResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - + state protoimpl.MessageState `protogen:"open.v1"` // The signature provided by the aggsender validator. - Signature *v11.FixedBytes65 `protobuf:"bytes,1,opt,name=signature,proto3" json:"signature,omitempty"` + Signature *v11.FixedBytes65 `protobuf:"bytes,1,opt,name=signature,proto3" json:"signature,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *ValidateGERResponse) Reset() { *x = ValidateGERResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_aggsender_validator_proto_v1_validator_proto_msgTypes[4] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_aggsender_validator_proto_v1_validator_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *ValidateGERResponse) String() string { @@ -279,7 +265,7 @@ func (*ValidateGERResponse) ProtoMessage() {} func (x *ValidateGERResponse) ProtoReflect() protoreflect.Message { mi := &file_aggsender_validator_proto_v1_validator_proto_msgTypes[4] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -303,102 +289,42 @@ func (x *ValidateGERResponse) GetSignature() *v11.FixedBytes65 { var File_aggsender_validator_proto_v1_validator_proto protoreflect.FileDescriptor -var file_aggsender_validator_proto_v1_validator_proto_rawDesc = []byte{ - 0x0a, 0x2c, 0x61, 0x67, 0x67, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x2f, 0x76, 0x61, 0x6c, 0x69, - 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x76, 0x31, 0x2f, 0x76, - 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1d, - 0x61, 0x67, 0x67, 0x6b, 0x69, 0x74, 0x2e, 0x61, 0x67, 0x67, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, - 0x2e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x76, 0x31, 0x1a, 0x28, 0x61, - 0x67, 0x67, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x6e, 0x6f, 0x64, 0x65, 0x2f, 0x74, 0x79, 0x70, - 0x65, 0x73, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, - 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x2b, 0x61, 0x67, 0x67, 0x6c, 0x61, 0x79, 0x65, - 0x72, 0x2f, 0x6e, 0x6f, 0x64, 0x65, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x76, 0x31, 0x2f, - 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x5f, 0x69, 0x64, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x25, 0x61, 0x67, 0x67, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x69, - 0x6e, 0x74, 0x65, 0x72, 0x6f, 0x70, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x76, 0x31, 0x2f, - 0x62, 0x79, 0x74, 0x65, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1b, 0x67, 0x6f, 0x6f, - 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x65, 0x6d, 0x70, - 0x74, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x5f, 0x0a, 0x13, 0x48, 0x65, 0x61, 0x6c, - 0x74, 0x68, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, - 0x73, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x22, 0xf4, 0x01, 0x0a, 0x1a, 0x56, 0x61, - 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, - 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x5d, 0x0a, 0x17, 0x70, 0x72, 0x65, 0x76, - 0x69, 0x6f, 0x75, 0x73, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, - 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x61, 0x67, 0x67, 0x6c, - 0x61, 0x79, 0x65, 0x72, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, - 0x76, 0x31, 0x2e, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x49, 0x64, - 0x52, 0x15, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, - 0x69, 0x63, 0x61, 0x74, 0x65, 0x49, 0x64, 0x12, 0x45, 0x0a, 0x0b, 0x63, 0x65, 0x72, 0x74, 0x69, - 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x61, - 0x67, 0x67, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x74, 0x79, 0x70, - 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, - 0x65, 0x52, 0x0b, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x12, 0x30, - 0x0a, 0x15, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x6c, 0x32, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, - 0x69, 0x6e, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x11, 0x6c, - 0x61, 0x73, 0x74, 0x4c, 0x32, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x6e, 0x43, 0x65, 0x72, 0x74, - 0x22, 0x64, 0x0a, 0x1b, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x43, 0x65, 0x72, 0x74, - 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x45, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x61, 0x67, 0x67, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2e, 0x69, 0x6e, - 0x74, 0x65, 0x72, 0x6f, 0x70, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x46, - 0x69, 0x78, 0x65, 0x64, 0x42, 0x79, 0x74, 0x65, 0x73, 0x36, 0x35, 0x52, 0x09, 0x73, 0x69, 0x67, - 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0x26, 0x0a, 0x12, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, - 0x74, 0x65, 0x47, 0x45, 0x52, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, - 0x67, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x67, 0x65, 0x72, 0x22, 0x5c, - 0x0a, 0x13, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x47, 0x45, 0x52, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x45, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, - 0x72, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x61, 0x67, 0x67, 0x6c, 0x61, - 0x79, 0x65, 0x72, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6f, 0x70, 0x2e, 0x74, 0x79, 0x70, 0x65, - 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x69, 0x78, 0x65, 0x64, 0x42, 0x79, 0x74, 0x65, 0x73, 0x36, - 0x35, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x32, 0xf4, 0x02, 0x0a, - 0x12, 0x41, 0x67, 0x67, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, - 0x74, 0x6f, 0x72, 0x12, 0x59, 0x0a, 0x0b, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x43, 0x68, 0x65, - 0x63, 0x6b, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x32, 0x2e, 0x61, 0x67, 0x67, - 0x6b, 0x69, 0x74, 0x2e, 0x61, 0x67, 0x67, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x2e, 0x76, 0x61, - 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x65, 0x61, 0x6c, 0x74, - 0x68, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x8c, - 0x01, 0x0a, 0x13, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x43, 0x65, 0x72, 0x74, 0x69, - 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x12, 0x39, 0x2e, 0x61, 0x67, 0x67, 0x6b, 0x69, 0x74, 0x2e, - 0x61, 0x67, 0x67, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x2e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, - 0x74, 0x6f, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x43, - 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x3a, 0x2e, 0x61, 0x67, 0x67, 0x6b, 0x69, 0x74, 0x2e, 0x61, 0x67, 0x67, 0x73, 0x65, - 0x6e, 0x64, 0x65, 0x72, 0x2e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x76, - 0x31, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, - 0x69, 0x63, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x74, 0x0a, - 0x0b, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x47, 0x45, 0x52, 0x12, 0x31, 0x2e, 0x61, - 0x67, 0x67, 0x6b, 0x69, 0x74, 0x2e, 0x61, 0x67, 0x67, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x2e, - 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x61, 0x6c, - 0x69, 0x64, 0x61, 0x74, 0x65, 0x47, 0x45, 0x52, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x32, 0x2e, 0x61, 0x67, 0x67, 0x6b, 0x69, 0x74, 0x2e, 0x61, 0x67, 0x67, 0x73, 0x65, 0x6e, 0x64, - 0x65, 0x72, 0x2e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x76, 0x31, 0x2e, - 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x47, 0x45, 0x52, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x42, 0x39, 0x5a, 0x37, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, - 0x6d, 0x2f, 0x61, 0x67, 0x67, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2f, 0x61, 0x67, 0x67, 0x6b, 0x69, - 0x74, 0x2f, 0x61, 0x67, 0x67, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x2f, 0x76, 0x61, 0x6c, 0x69, - 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x76, 0x31, 0x62, 0x06, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, -} +const file_aggsender_validator_proto_v1_validator_proto_rawDesc = "" + + "\n" + + ",aggsender/validator/proto/v1/validator.proto\x12\x1daggkit.aggsender.validator.v1\x1a(agglayer/node/types/v1/certificate.proto\x1a+agglayer/node/types/v1/certificate_id.proto\x1a%agglayer/interop/types/v1/bytes.proto\x1a\x1bgoogle/protobuf/empty.proto\"_\n" + + "\x13HealthCheckResponse\x12\x18\n" + + "\aversion\x18\x01 \x01(\tR\aversion\x12\x16\n" + + "\x06status\x18\x02 \x01(\tR\x06status\x12\x16\n" + + "\x06reason\x18\x03 \x01(\tR\x06reason\"\xf4\x01\n" + + "\x1aValidateCertificateRequest\x12]\n" + + "\x17previous_certificate_id\x18\x01 \x01(\v2%.agglayer.node.types.v1.CertificateIdR\x15previousCertificateId\x12E\n" + + "\vcertificate\x18\x02 \x01(\v2#.agglayer.node.types.v1.CertificateR\vcertificate\x120\n" + + "\x15last_l2_block_in_cert\x18\x03 \x01(\x04R\x11lastL2BlockInCert\"d\n" + + "\x1bValidateCertificateResponse\x12E\n" + + "\tsignature\x18\x01 \x01(\v2'.agglayer.interop.types.v1.FixedBytes65R\tsignature\"O\n" + + "\x12ValidateGERRequest\x129\n" + + "\x03ger\x18\x01 \x01(\v2'.agglayer.interop.types.v1.FixedBytes32R\x03ger\"\\\n" + + "\x13ValidateGERResponse\x12E\n" + + "\tsignature\x18\x01 \x01(\v2'.agglayer.interop.types.v1.FixedBytes65R\tsignature2\xf4\x02\n" + + "\x12AggsenderValidator\x12Y\n" + + "\vHealthCheck\x12\x16.google.protobuf.Empty\x1a2.aggkit.aggsender.validator.v1.HealthCheckResponse\x12\x8c\x01\n" + + "\x13ValidateCertificate\x129.aggkit.aggsender.validator.v1.ValidateCertificateRequest\x1a:.aggkit.aggsender.validator.v1.ValidateCertificateResponse\x12t\n" + + "\vValidateGER\x121.aggkit.aggsender.validator.v1.ValidateGERRequest\x1a2.aggkit.aggsender.validator.v1.ValidateGERResponseB9Z7github.com/agglayer/aggkit/aggsender/validator/proto/v1b\x06proto3" var ( file_aggsender_validator_proto_v1_validator_proto_rawDescOnce sync.Once - file_aggsender_validator_proto_v1_validator_proto_rawDescData = file_aggsender_validator_proto_v1_validator_proto_rawDesc + file_aggsender_validator_proto_v1_validator_proto_rawDescData []byte ) func file_aggsender_validator_proto_v1_validator_proto_rawDescGZIP() []byte { file_aggsender_validator_proto_v1_validator_proto_rawDescOnce.Do(func() { - file_aggsender_validator_proto_v1_validator_proto_rawDescData = protoimpl.X.CompressGZIP(file_aggsender_validator_proto_v1_validator_proto_rawDescData) + file_aggsender_validator_proto_v1_validator_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_aggsender_validator_proto_v1_validator_proto_rawDesc), len(file_aggsender_validator_proto_v1_validator_proto_rawDesc))) }) return file_aggsender_validator_proto_v1_validator_proto_rawDescData } var file_aggsender_validator_proto_v1_validator_proto_msgTypes = make([]protoimpl.MessageInfo, 5) -var file_aggsender_validator_proto_v1_validator_proto_goTypes = []interface{}{ +var file_aggsender_validator_proto_v1_validator_proto_goTypes = []any{ (*HealthCheckResponse)(nil), // 0: aggkit.aggsender.validator.v1.HealthCheckResponse (*ValidateCertificateRequest)(nil), // 1: aggkit.aggsender.validator.v1.ValidateCertificateRequest (*ValidateCertificateResponse)(nil), // 2: aggkit.aggsender.validator.v1.ValidateCertificateResponse @@ -407,24 +333,26 @@ var file_aggsender_validator_proto_v1_validator_proto_goTypes = []interface{}{ (*v1.CertificateId)(nil), // 5: agglayer.node.types.v1.CertificateId (*v1.Certificate)(nil), // 6: agglayer.node.types.v1.Certificate (*v11.FixedBytes65)(nil), // 7: agglayer.interop.types.v1.FixedBytes65 - (*emptypb.Empty)(nil), // 8: google.protobuf.Empty + (*v11.FixedBytes32)(nil), // 8: agglayer.interop.types.v1.FixedBytes32 + (*emptypb.Empty)(nil), // 9: google.protobuf.Empty } var file_aggsender_validator_proto_v1_validator_proto_depIdxs = []int32{ 5, // 0: aggkit.aggsender.validator.v1.ValidateCertificateRequest.previous_certificate_id:type_name -> agglayer.node.types.v1.CertificateId 6, // 1: aggkit.aggsender.validator.v1.ValidateCertificateRequest.certificate:type_name -> agglayer.node.types.v1.Certificate 7, // 2: aggkit.aggsender.validator.v1.ValidateCertificateResponse.signature:type_name -> agglayer.interop.types.v1.FixedBytes65 - 7, // 3: aggkit.aggsender.validator.v1.ValidateGERResponse.signature:type_name -> agglayer.interop.types.v1.FixedBytes65 - 8, // 4: aggkit.aggsender.validator.v1.AggsenderValidator.HealthCheck:input_type -> google.protobuf.Empty - 1, // 5: aggkit.aggsender.validator.v1.AggsenderValidator.ValidateCertificate:input_type -> aggkit.aggsender.validator.v1.ValidateCertificateRequest - 3, // 6: aggkit.aggsender.validator.v1.AggsenderValidator.ValidateGER:input_type -> aggkit.aggsender.validator.v1.ValidateGERRequest - 0, // 7: aggkit.aggsender.validator.v1.AggsenderValidator.HealthCheck:output_type -> aggkit.aggsender.validator.v1.HealthCheckResponse - 2, // 8: aggkit.aggsender.validator.v1.AggsenderValidator.ValidateCertificate:output_type -> aggkit.aggsender.validator.v1.ValidateCertificateResponse - 4, // 9: aggkit.aggsender.validator.v1.AggsenderValidator.ValidateGER:output_type -> aggkit.aggsender.validator.v1.ValidateGERResponse - 7, // [7:10] is the sub-list for method output_type - 4, // [4:7] is the sub-list for method input_type - 4, // [4:4] is the sub-list for extension type_name - 4, // [4:4] is the sub-list for extension extendee - 0, // [0:4] is the sub-list for field type_name + 8, // 3: aggkit.aggsender.validator.v1.ValidateGERRequest.ger:type_name -> agglayer.interop.types.v1.FixedBytes32 + 7, // 4: aggkit.aggsender.validator.v1.ValidateGERResponse.signature:type_name -> agglayer.interop.types.v1.FixedBytes65 + 9, // 5: aggkit.aggsender.validator.v1.AggsenderValidator.HealthCheck:input_type -> google.protobuf.Empty + 1, // 6: aggkit.aggsender.validator.v1.AggsenderValidator.ValidateCertificate:input_type -> aggkit.aggsender.validator.v1.ValidateCertificateRequest + 3, // 7: aggkit.aggsender.validator.v1.AggsenderValidator.ValidateGER:input_type -> aggkit.aggsender.validator.v1.ValidateGERRequest + 0, // 8: aggkit.aggsender.validator.v1.AggsenderValidator.HealthCheck:output_type -> aggkit.aggsender.validator.v1.HealthCheckResponse + 2, // 9: aggkit.aggsender.validator.v1.AggsenderValidator.ValidateCertificate:output_type -> aggkit.aggsender.validator.v1.ValidateCertificateResponse + 4, // 10: aggkit.aggsender.validator.v1.AggsenderValidator.ValidateGER:output_type -> aggkit.aggsender.validator.v1.ValidateGERResponse + 8, // [8:11] is the sub-list for method output_type + 5, // [5:8] is the sub-list for method input_type + 5, // [5:5] is the sub-list for extension type_name + 5, // [5:5] is the sub-list for extension extendee + 0, // [0:5] is the sub-list for field type_name } func init() { file_aggsender_validator_proto_v1_validator_proto_init() } @@ -432,73 +360,11 @@ func file_aggsender_validator_proto_v1_validator_proto_init() { if File_aggsender_validator_proto_v1_validator_proto != nil { return } - if !protoimpl.UnsafeEnabled { - file_aggsender_validator_proto_v1_validator_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*HealthCheckResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_aggsender_validator_proto_v1_validator_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ValidateCertificateRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_aggsender_validator_proto_v1_validator_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ValidateCertificateResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_aggsender_validator_proto_v1_validator_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ValidateGERRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_aggsender_validator_proto_v1_validator_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ValidateGERResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_aggsender_validator_proto_v1_validator_proto_rawDesc, + RawDescriptor: unsafe.Slice(unsafe.StringData(file_aggsender_validator_proto_v1_validator_proto_rawDesc), len(file_aggsender_validator_proto_v1_validator_proto_rawDesc)), NumEnums: 0, NumMessages: 5, NumExtensions: 0, @@ -509,7 +375,6 @@ func file_aggsender_validator_proto_v1_validator_proto_init() { MessageInfos: file_aggsender_validator_proto_v1_validator_proto_msgTypes, }.Build() File_aggsender_validator_proto_v1_validator_proto = out.File - file_aggsender_validator_proto_v1_validator_proto_rawDesc = nil file_aggsender_validator_proto_v1_validator_proto_goTypes = nil file_aggsender_validator_proto_v1_validator_proto_depIdxs = nil } diff --git a/aggsender/validator/proto/v1/validator.proto b/aggsender/validator/proto/v1/validator.proto index e08f83402..a0705b4e6 100644 --- a/aggsender/validator/proto/v1/validator.proto +++ b/aggsender/validator/proto/v1/validator.proto @@ -44,7 +44,7 @@ message ValidateCertificateResponse { // Request to validate a GER. message ValidateGERRequest { // GER to be validated - bytes ger = 1; + agglayer.interop.types.v1.FixedBytes32 ger = 1; } // Response to a GER validation request. diff --git a/aggsender/validator/proto/v1/validator_grpc.pb.go b/aggsender/validator/proto/v1/validator_grpc.pb.go index 6459bac58..75f14ca70 100644 --- a/aggsender/validator/proto/v1/validator_grpc.pb.go +++ b/aggsender/validator/proto/v1/validator_grpc.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.3.0 +// - protoc-gen-go-grpc v1.4.0 // - protoc (unknown) // source: aggsender/validator/proto/v1/validator.proto @@ -16,8 +16,8 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.32.0 or later. -const _ = grpc.SupportPackageIsVersion7 +// Requires gRPC-Go v1.62.0 or later. +const _ = grpc.SupportPackageIsVersion8 const ( AggsenderValidator_HealthCheck_FullMethodName = "/aggkit.aggsender.validator.v1.AggsenderValidator/HealthCheck" @@ -28,6 +28,8 @@ const ( // AggsenderValidatorClient is the client API for AggsenderValidator service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +// +// Service for validating new certificates type AggsenderValidatorClient interface { // Method to get the status of the validator HealthCheck(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*HealthCheckResponse, error) @@ -46,8 +48,9 @@ func NewAggsenderValidatorClient(cc grpc.ClientConnInterface) AggsenderValidator } func (c *aggsenderValidatorClient) HealthCheck(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*HealthCheckResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(HealthCheckResponse) - err := c.cc.Invoke(ctx, AggsenderValidator_HealthCheck_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, AggsenderValidator_HealthCheck_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -55,8 +58,9 @@ func (c *aggsenderValidatorClient) HealthCheck(ctx context.Context, in *emptypb. } func (c *aggsenderValidatorClient) ValidateCertificate(ctx context.Context, in *ValidateCertificateRequest, opts ...grpc.CallOption) (*ValidateCertificateResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(ValidateCertificateResponse) - err := c.cc.Invoke(ctx, AggsenderValidator_ValidateCertificate_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, AggsenderValidator_ValidateCertificate_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -64,8 +68,9 @@ func (c *aggsenderValidatorClient) ValidateCertificate(ctx context.Context, in * } func (c *aggsenderValidatorClient) ValidateGER(ctx context.Context, in *ValidateGERRequest, opts ...grpc.CallOption) (*ValidateGERResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(ValidateGERResponse) - err := c.cc.Invoke(ctx, AggsenderValidator_ValidateGER_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, AggsenderValidator_ValidateGER_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } @@ -75,6 +80,8 @@ func (c *aggsenderValidatorClient) ValidateGER(ctx context.Context, in *Validate // AggsenderValidatorServer is the server API for AggsenderValidator service. // All implementations must embed UnimplementedAggsenderValidatorServer // for forward compatibility +// +// Service for validating new certificates type AggsenderValidatorServer interface { // Method to get the status of the validator HealthCheck(context.Context, *emptypb.Empty) (*HealthCheckResponse, error) diff --git a/aggsender/validator/remote_validator.go b/aggsender/validator/remote_validator.go index af996f279..ce6b31efb 100644 --- a/aggsender/validator/remote_validator.go +++ b/aggsender/validator/remote_validator.go @@ -107,16 +107,51 @@ func (v *RemoteValidator) ValidateAndSignCertificate( // Validate received signature // We do not support ethereum legacy v+27 signatures - recoveredPublicKey, err := crypto.SigToPub(certificateHash[:], signature) + err = v.validateSignature(certificateHash, signature) if err != nil { - return nil, fmt.Errorf("error validating remote validator signature: %w", err) + return nil, fmt.Errorf("error validating signature from remote validator: %w", err) + } + + return signature, nil +} + +// ValidateAndSignGER validates the GlobalExitRoot that needs to be injected and signs it. +func (v *RemoteValidator) ValidateAndSignGER( + ctx context.Context, + ger common.Hash, +) ([]byte, error) { + signature, err := v.client.ValidateGER(ctx, ger) + if err != nil { + return nil, fmt.Errorf("error validating GER on aggsender validator service: %w", err) + } + + // Validate received signature + // We do not support ethereum legacy v+27 signatures + + err = v.validateSignature(ger, signature) + if err != nil { + return nil, fmt.Errorf("error validating signature from remote validator: %w", err) + } + + return signature, nil +} + +// validateSignature checks if the signature corresponds to the given message hash +// and was created by the remote validator's address. +func (v *RemoteValidator) validateSignature( + messageHash common.Hash, + signature []byte, +) error { + recoveredPublicKey, err := crypto.SigToPub(messageHash.Bytes(), signature) + if err != nil { + return fmt.Errorf("error validating remote validator signature: %w", err) } recoveredAddress := crypto.PubkeyToAddress(*recoveredPublicKey) if v.address != recoveredAddress { - return nil, fmt.Errorf("error validating remote validator signature, mismatch expected:%v current:%v", + return fmt.Errorf("error validating remote validator signature, mismatch. Expected: %v current: %v", v.address, recoveredAddress) } - return signature, nil + return nil } diff --git a/aggsender/validator/remote_validator_test.go b/aggsender/validator/remote_validator_test.go index 1b1f36978..66adea2cf 100644 --- a/aggsender/validator/remote_validator_test.go +++ b/aggsender/validator/remote_validator_test.go @@ -95,7 +95,7 @@ func TestRemoteClient_ValidateCertificate(t *testing.T) { mock.Anything, ).Return(certAtHeight11Sig, nil) }, - expectedError: "error validating remote validator signature, mismatch expected:0x9D36c7795cC5F041010F1eb74f3e70306Ab9Ede5 current:0x38996100B11d9637C61c2d903A8dE79F26B01A9a", + expectedError: "error validating remote validator signature, mismatch. Expected: 0x9D36c7795cC5F041010F1eb74f3e70306Ab9Ede5 current: 0x38996100B11d9637C61c2d903A8dE79F26B01A9a", }, { name: "fail, empty signature", diff --git a/aggsender/validator/validate_certificate.go b/aggsender/validator/validate_certificate.go index f939c17cd..4f304c484 100644 --- a/aggsender/validator/validate_certificate.go +++ b/aggsender/validator/validate_certificate.go @@ -10,43 +10,70 @@ import ( "github.com/agglayer/aggkit/aggsender/converters" "github.com/agglayer/aggkit/aggsender/types" aggkitcommon "github.com/agglayer/aggkit/common" - treetypes "github.com/agglayer/aggkit/tree/types" "github.com/ethereum/go-ethereum/common" ) var ( ErrNilCertificate = errors.New("aggsender-validator nil certificate") ErrMetadataNotCompatible = errors.New("aggsender-validator metadata not compatible with the current version") + errGERNotExists = errors.New("GER does not exist on L1 GER contract") ) -type L1InfoTreeRootByLeafQuerier interface { - // GetL1InfoRootByLeafIndex returns the L1 Info tree root for the given leaf index - GetL1InfoRootByLeafIndex(ctx context.Context, leafCount uint32) (*treetypes.Root, error) -} - // CertificateValidator is a object to validate a certificate type CertificateValidator struct { log aggkitcommon.Logger flow types.AggsenderVerifierFlow - l1InfoTreeDataQuerier L1InfoTreeRootByLeafQuerier + l1InfoTreeDataQuerier types.L1InfoTreeDataQuerier certQuerier types.CertificateQuerier lerQuerier types.LERQuerier + + l1GERQuerier types.L1GERQuerier } +// NewAggsenderValidator creates a new CertificateValidator instance with the provided dependencies. +// It initializes the validator with a logger, verification flow, and various data queriers +// needed for certificate validation operations. +// +// Parameters: +// - logger: Logger instance for recording validation operations and errors +// - flow: AggsenderVerifierFlow that defines the verification workflow +// - l1InfoTreeDataQuerier: Querier for L1 info tree data +// - certQuerier: Querier for certificate data +// - lerQuerier: Querier for LER (Local Exit Root) data +// - l1GERQuerier: Querier for L1 GER (Global Exit Root) data +// +// Returns: +// - *CertificateValidator: A new validator instance ready for certificate validation func NewAggsenderValidator(logger aggkitcommon.Logger, flow types.AggsenderVerifierFlow, - l1InfoTreeDataQuerier L1InfoTreeRootByLeafQuerier, + l1InfoTreeDataQuerier types.L1InfoTreeDataQuerier, certQuerier types.CertificateQuerier, - lerQuerier types.LERQuerier) *CertificateValidator { + lerQuerier types.LERQuerier, + l1GERQuerier types.L1GERQuerier) *CertificateValidator { return &CertificateValidator{ log: logger, flow: flow, l1InfoTreeDataQuerier: l1InfoTreeDataQuerier, certQuerier: certQuerier, lerQuerier: lerQuerier, + l1GERQuerier: l1GERQuerier, } } +// ValidateGER validates the GlobalExitRoot that needs to be injected. +func (a *CertificateValidator) ValidateGER(ctx context.Context, ger common.Hash) error { + doesExist, err := a.l1GERQuerier.DoesGERExistOnContract(ctx, ger) + if err != nil { + return fmt.Errorf("error checking GER existence on contract for GER %s: %w", ger.String(), err) + } + + if !doesExist { + return fmt.Errorf("%w: %s", errGERNotExists, ger.String()) + } + + return nil +} + // ValidateCertificate validates the incoming certificate against the previous one. func (a *CertificateValidator) ValidateCertificate(ctx context.Context, params types.VerifyIncomingRequest) error { if params.Certificate == nil { diff --git a/aggsender/validator/validate_certificate_test.go b/aggsender/validator/validate_certificate_test.go index a12e9e0c7..0ab00b4be 100644 --- a/aggsender/validator/validate_certificate_test.go +++ b/aggsender/validator/validate_certificate_test.go @@ -350,6 +350,7 @@ type testDataCertificateValidator struct { mockL1InfoTreeQuerier *mocks.L1InfoTreeDataQuerier mockCertQuerier *mocks.CertificateQuerier mockLERQuerier *mocks.LERQuerier + mockL1GERQuerier *mocks.L1GERQuerier sut *CertificateValidator } @@ -359,7 +360,8 @@ func newTestDataCertificateValidator(t *testing.T) testDataCertificateValidator mockFlow := mocks.NewAggsenderVerifierFlow(t) mockL1InfoTreeQuerier := mocks.NewL1InfoTreeDataQuerier(t) mockCertQuerier := mocks.NewCertificateQuerier(t) - lerQuerier := mocks.NewLERQuerier(t) + mockLERQuerier := mocks.NewLERQuerier(t) + mockL1GERQuerier := mocks.NewL1GERQuerier(t) return testDataCertificateValidator{ ctx: context.TODO(), @@ -367,7 +369,8 @@ func newTestDataCertificateValidator(t *testing.T) testDataCertificateValidator mockFlow: mockFlow, mockL1InfoTreeQuerier: mockL1InfoTreeQuerier, mockCertQuerier: mockCertQuerier, - mockLERQuerier: lerQuerier, - sut: NewAggsenderValidator(mockLogger, mockFlow, mockL1InfoTreeQuerier, mockCertQuerier, lerQuerier), + mockLERQuerier: mockLERQuerier, + mockL1GERQuerier: mockL1GERQuerier, + sut: NewAggsenderValidator(mockLogger, mockFlow, mockL1InfoTreeQuerier, mockCertQuerier, mockLERQuerier, mockL1GERQuerier), } } diff --git a/aggsender/validator/validator_client.go b/aggsender/validator/validator_client.go index a4dc79c0c..12f1ee27a 100644 --- a/aggsender/validator/validator_client.go +++ b/aggsender/validator/validator_client.go @@ -75,6 +75,23 @@ func (v *ValidatorClient) ValidateCertificate( return response.Signature.Value, nil } +// ValidateGER sends a GlobalExitRoot to the AggsenderValidator service for validation. +func (v *ValidatorClient) ValidateGER( + ctx context.Context, + ger common.Hash, +) ([]byte, error) { + response, err := v.client.ValidateGER(ctx, &v1.ValidateGERRequest{ + Ger: &typesv1.FixedBytes32{ + Value: ger.Bytes(), + }, + }) + if err != nil { + return nil, fmt.Errorf("aggsender validator failed to successfully validate GER: %w", err) + } + + return response.Signature.Value, nil +} + // certIDToProtoNullable converts a common.Hash pointer to a nodev1.CertificateId proto message. func certIDToProtoNullable(certID *common.Hash) *nodev1.CertificateId { if certID == nil { diff --git a/aggsender/validator/validator_service.go b/aggsender/validator/validator_service.go index b8f7ac018..6c631c08a 100644 --- a/aggsender/validator/validator_service.go +++ b/aggsender/validator/validator_service.go @@ -2,6 +2,7 @@ package validator import ( "context" + "errors" "fmt" v1types "buf.build/gen/go/agglayer/interop/protocolbuffers/go/agglayer/interop/types/v1" @@ -19,6 +20,13 @@ import ( "google.golang.org/protobuf/types/known/emptypb" ) +var ( + errSignerNotInitialized = grpc.GRPCError{ + Code: codes.Internal, + Message: "Signer is not initialized", + } +) + // ValidatorService implements the gRPC server for the AggsenderValidator service. type ValidatorService struct { // Embed the generated server interface to ensure forward compatibility @@ -53,6 +61,53 @@ func (s *ValidatorService) HealthCheck(ctx context.Context, in *emptypb.Empty) ( }, nil } +func (s *ValidatorService) ValidateGER( + ctx context.Context, + req *v1.ValidateGERRequest, +) (*v1.ValidateGERResponse, error) { + if req == nil || req.Ger == nil { + return nil, grpc.GRPCError{ + Code: codes.InvalidArgument, + Message: "GlobalExitRoot is required", + } + } + + ger := common.BytesToHash(req.Ger.Value) + s.log.Infof("Received GER to validate and sign: %s", ger.Hex()) + + err := s.validator.ValidateGER(ctx, ger) + if err != nil { + s.log.Errorf("Error validating GER: %v", err) + + code := codes.Internal + if errors.Is(err, errGERNotExists) { + code = codes.NotFound + } + + return nil, grpc.GRPCError{ + Code: code, + Message: "Error validating GER: " + err.Error(), + } + } + + signature, err := s.signGER(ctx, ger) + if err != nil { + s.log.Errorf("Error signing GER: %v", err) + return nil, grpc.GRPCError{ + Code: codes.Internal, + Message: "Error signing GER: " + err.Error(), + } + } + + s.log.Infof("GER %s validated and signed successfully: %s", ger.Hex(), common.Bytes2Hex(signature)) + + return &v1.ValidateGERResponse{ + Signature: &v1types.FixedBytes65{ + Value: signature, + }, + }, nil +} + // ValidateCertificate validates a new certificate func (s *ValidatorService) ValidateCertificate( ctx context.Context, req *v1.ValidateCertificateRequest) (*v1.ValidateCertificateResponse, error) { @@ -126,10 +181,7 @@ func (s *ValidatorService) ValidateCertificate( func (s *ValidatorService) signCertificate(ctx context.Context, cert *agglayertypes.Certificate) ([]byte, error) { if s.signer == nil { - return nil, grpc.GRPCError{ - Code: codes.Internal, - Message: "Signer is not initialized", - } + return nil, errSignerNotInitialized } hashToSign, err := HashCertificateToSign(cert) if err != nil { @@ -140,3 +192,11 @@ func (s *ValidatorService) signCertificate(ctx context.Context, cert *agglayerty } return s.signer.SignHash(ctx, hashToSign) } + +func (s *ValidatorService) signGER(ctx context.Context, ger common.Hash) ([]byte, error) { + if s.signer == nil { + return nil, errSignerNotInitialized + } + + return s.signer.SignHash(ctx, ger) +} diff --git a/buf.lock b/buf.lock index 1edc89748..3ad500770 100644 --- a/buf.lock +++ b/buf.lock @@ -2,8 +2,8 @@ version: v2 deps: - name: buf.build/agglayer/agglayer - commit: 293e4b4c9be24896b6829f4e5fa19b08 + commit: a0b8bb4a87534651a15f94c92c09a446 digest: b5:e8bc39234f135b01b61ec95a4b16e754206c68af8eee40149d9fffae100ebfa8d0a95ed1615b4ad1f56d74b559ced15748a6882d45a95bc5eaace710d95cf3eb - name: buf.build/agglayer/interop - commit: 31a4887bc637444c819f92e05c0ebc93 + commit: 8a354cac370942aea7f58e486dd38f30 digest: b5:16cba44cc3991d20b7575ea30d6d9377d389e6616eb2a84b41900fa92b0a3c67545a32d3676011dc1f4dfda0c3bf1befd2d95428b83527913a981cf64a2dceae diff --git a/cmd/run.go b/cmd/run.go index 71bf753c9..1b3d20cf0 100644 --- a/cmd/run.go +++ b/cmd/run.go @@ -332,6 +332,15 @@ func createAggSenderValidator(ctx context.Context, return nil, fmt.Errorf("failed to create verifier flow: %w", err) } + l1GERQuerier, err := query.NewL1GERDataQuerier( + cfg.GERValidateConfig.GlobalExitRootL1Addr, + cfg.GERValidateConfig.BlockFinality, + l1Client, + ) + if err != nil { + return nil, fmt.Errorf("error creating L1GER data querier: %w", err) + } + return aggsender.NewAggsenderValidator( ctx, logger, cfg, flow, flowParams.L1InfoTreeDataQuerier, @@ -339,6 +348,7 @@ func createAggSenderValidator(ctx context.Context, certQuerier, aggchainFEPQuerier, flowParams.LERQuerier, + l1GERQuerier, flowParams.Signer, ) } diff --git a/config/default.go b/config/default.go index d46f8ee26..c74b64964 100644 --- a/config/default.go +++ b/config/default.go @@ -201,6 +201,7 @@ Mode = "Auto" CheckStatusCertificateInterval = "5m" RetryCertAfterInError = false GlobalExitRootL2 = "{{L2Config.GlobalExitRootAddr}}" +GlobalExitRootL1 = "{{L1Config.polygonZkEVMGlobalExitRootAddress}}" SovereignRollupAddr = "{{L1Config.polygonZkEVMAddress}}" RequireStorageContentCompatibility = {{RequireStorageContentCompatibility}} RequireNoFEPBlockGap = false @@ -304,6 +305,9 @@ AgglayerBridgeL2Addr = "{{L2Config.BridgeAddr}}" SovereignRollupAddr = "{{AggSender.SovereignRollupAddr}}" RequireNoBlockGap = "{{AggSender.RequireNoFEPBlockGap}}" OpNodeURL = "{{OpNodeURL}}" +[Validator.GERValidateConfig] + GlobalExitRootL1Addr = "{{L1NetworkConfig.GlobalExitRootManagerAddr}}" + BlockFinality = "{{L1InfoTreeSync.BlockFinality}}" [Validator.AgglayerClient] Cached = true [Validator.AgglayerClient.ConfigurationCache]