diff --git a/agglayer/client.go b/agglayer/client.go index e60c1c7c..8396fc9e 100644 --- a/agglayer/client.go +++ b/agglayer/client.go @@ -15,7 +15,14 @@ import ( const errCodeAgglayerRateLimitExceeded int = -10007 -var ErrAgglayerRateLimitExceeded = fmt.Errorf("agglayer rate limit exceeded") +var ( + ErrAgglayerRateLimitExceeded = fmt.Errorf("agglayer rate limit exceeded") + jSONRPCCall = rpc.JSONRPCCall +) + +type AggLayerClientGetEpochConfiguration interface { + GetEpochConfiguration() (*ClockConfiguration, error) +} // AgglayerClientInterface is the interface that defines the methods that the AggLayerClient will implement type AgglayerClientInterface interface { @@ -23,6 +30,7 @@ type AgglayerClientInterface interface { WaitTxToBeMined(hash common.Hash, ctx context.Context) error SendCertificate(certificate *SignedCertificate) (common.Hash, error) GetCertificateHeader(certificateHash common.Hash) (*CertificateHeader, error) + AggLayerClientGetEpochConfiguration } // AggLayerClient is the client that will be used to interact with the AggLayer @@ -130,3 +138,23 @@ func (c *AggLayerClient) GetCertificateHeader(certificateHash common.Hash) (*Cer return result, nil } + +// GetEpochConfiguration returns the clock configuration of AggLayer +func (c *AggLayerClient) GetEpochConfiguration() (*ClockConfiguration, error) { + response, err := jSONRPCCall(c.url, "interop_getEpochConfiguration") + if err != nil { + return nil, err + } + + if response.Error != nil { + return nil, fmt.Errorf("GetEpochConfiguration code=%d msg=%s", response.Error.Code, response.Error.Message) + } + + var result *ClockConfiguration + err = json.Unmarshal(response.Result, &result) + if err != nil { + return nil, err + } + + return result, nil +} diff --git a/agglayer/client_test.go b/agglayer/client_test.go new file mode 100644 index 00000000..82baea85 --- /dev/null +++ b/agglayer/client_test.go @@ -0,0 +1,76 @@ +package agglayer + +import ( + "fmt" + "testing" + + "github.com/0xPolygon/cdk-rpc/rpc" + "github.com/stretchr/testify/require" +) + +const ( + testURL = "http://localhost:8080" +) + +func TestExploratoryClient(t *testing.T) { + t.Skip("This test is for exploratory purposes only") + sut := NewAggLayerClient("http://127.0.0.1:32853") + config, err := sut.GetEpochConfiguration() + require.NoError(t, err) + require.NotNil(t, config) + fmt.Printf("Config: %s", config.String()) +} + +func TestGetEpochConfigurationResponseWithError(t *testing.T) { + sut := NewAggLayerClient(testURL) + response := rpc.Response{ + Error: &rpc.ErrorObject{}, + } + jSONRPCCall = func(url, method string, params ...interface{}) (rpc.Response, error) { + return response, nil + } + clockConfig, err := sut.GetEpochConfiguration() + require.Nil(t, clockConfig) + require.Error(t, err) +} + +func TestGetEpochConfigurationResponseBadJson(t *testing.T) { + sut := NewAggLayerClient(testURL) + response := rpc.Response{ + Result: []byte(`{`), + } + jSONRPCCall = func(url, method string, params ...interface{}) (rpc.Response, error) { + return response, nil + } + clockConfig, err := sut.GetEpochConfiguration() + require.Nil(t, clockConfig) + require.Error(t, err) +} + +func TestGetEpochConfigurationErrorResponse(t *testing.T) { + sut := NewAggLayerClient(testURL) + + jSONRPCCall = func(url, method string, params ...interface{}) (rpc.Response, error) { + return rpc.Response{}, fmt.Errorf("unittest error") + } + clockConfig, err := sut.GetEpochConfiguration() + require.Nil(t, clockConfig) + require.Error(t, err) +} + +func TestGetEpochConfigurationOkResponse(t *testing.T) { + sut := NewAggLayerClient(testURL) + response := rpc.Response{ + Result: []byte(`{"epoch_duration": 1, "genesis_block": 1}`), + } + jSONRPCCall = func(url, method string, params ...interface{}) (rpc.Response, error) { + return response, nil + } + clockConfig, err := sut.GetEpochConfiguration() + require.NotNil(t, clockConfig) + require.NoError(t, err) + require.Equal(t, ClockConfiguration{ + EpochDuration: 1, + GenesisBlock: 1, + }, *clockConfig) +} diff --git a/agglayer/errors_test.go b/agglayer/errors_test.go new file mode 100644 index 00000000..3ca7b7ed --- /dev/null +++ b/agglayer/errors_test.go @@ -0,0 +1,270 @@ +package agglayer + +import ( + "encoding/json" + "fmt" + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestErrorVectors(t *testing.T) { + t.Parallel() + + type testCase struct { + TestName string `json:"test_name"` + ExpectedError string `json:"expected_error"` + CertificateHeaderJSON string `json:"certificate_header"` + } + + files, err := filepath.Glob("testdata/*/*.json") + require.NoError(t, err) + + for _, file := range files { + file := file + + t.Run(file, func(t *testing.T) { + t.Parallel() + + data, err := os.ReadFile(file) + require.NoError(t, err) + + var testCases []*testCase + + require.NoError(t, json.Unmarshal(data, &testCases)) + + for _, tc := range testCases { + certificateHeader := &CertificateHeader{} + err = json.Unmarshal([]byte(tc.CertificateHeaderJSON), certificateHeader) + + if tc.ExpectedError == "" { + require.NoError(t, err, "Test: %s not expected any unmarshal error, but got: %v", tc.TestName, err) + require.NotNil(t, certificateHeader.Error, "Test: %s unpacked error is nil", tc.TestName) + fmt.Println(certificateHeader.Error.String()) + } else { + require.ErrorContains(t, err, tc.ExpectedError, "Test: %s expected error: %s. Got: %v", tc.TestName, tc.ExpectedError, err) + } + } + }) + } +} + +func TestConvertMapValue_String(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + data map[string]interface{} + key string + want string + errString string + }{ + { + name: "Key exists and type matches", + data: map[string]interface{}{ + "key1": "value1", + }, + key: "key1", + want: "value1", + }, + { + name: "Key exists but type does not match", + data: map[string]interface{}{ + "key1": 1, + }, + key: "key1", + want: "", + errString: "is not of type", + }, + { + name: "Key does not exist", + data: map[string]interface{}{ + "key1": "value1", + }, + key: "key2", + want: "", + errString: "key key2 not found in map", + }, + } + + for _, tt := range tests { + tt := tt + + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + got, err := convertMapValue[string](tt.data, tt.key) + if tt.errString != "" { + require.ErrorContains(t, err, tt.errString) + } else { + require.Equal(t, tt.want, got) + } + }) + } +} + +//nolint:dupl +func TestConvertMapValue_Uint32(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + data map[string]interface{} + key string + want uint32 + errString string + }{ + { + name: "Key exists and type matches", + data: map[string]interface{}{ + "key1": uint32(123), + }, + key: "key1", + want: uint32(123), + }, + { + name: "Key exists but type does not match", + data: map[string]interface{}{ + "key1": "value1", + }, + key: "key1", + want: 0, + errString: "is not of type", + }, + { + name: "Key does not exist", + data: map[string]interface{}{ + "key1": uint32(123), + }, + key: "key2", + want: 0, + errString: "key key2 not found in map", + }, + } + + for _, tt := range tests { + tt := tt + + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + got, err := convertMapValue[uint32](tt.data, tt.key) + if tt.errString != "" { + require.ErrorContains(t, err, tt.errString) + } else { + require.Equal(t, tt.want, got) + } + }) + } +} + +//nolint:dupl +func TestConvertMapValue_Uint64(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + data map[string]interface{} + key string + want uint64 + errString string + }{ + { + name: "Key exists and type matches", + data: map[string]interface{}{ + "key1": uint64(3411), + }, + key: "key1", + want: uint64(3411), + }, + { + name: "Key exists but type does not match", + data: map[string]interface{}{ + "key1": "not a number", + }, + key: "key1", + want: 0, + errString: "is not of type", + }, + { + name: "Key does not exist", + data: map[string]interface{}{ + "key1": uint64(123555), + }, + key: "key22", + want: 0, + errString: "key key22 not found in map", + }, + } + + for _, tt := range tests { + tt := tt + + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + got, err := convertMapValue[uint64](tt.data, tt.key) + if tt.errString != "" { + require.ErrorContains(t, err, tt.errString) + } else { + require.Equal(t, tt.want, got) + } + }) + } +} + +func TestConvertMapValue_Bool(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + data map[string]interface{} + key string + want bool + errString string + }{ + { + name: "Key exists and type matches", + data: map[string]interface{}{ + "key1": true, + }, + key: "key1", + want: true, + }, + { + name: "Key exists but type does not match", + data: map[string]interface{}{ + "key1": "value1", + }, + key: "key1", + want: false, + errString: "is not of type", + }, + { + name: "Key does not exist", + data: map[string]interface{}{ + "key1": true, + }, + key: "key2", + want: false, + errString: "key key2 not found in map", + }, + } + + for _, tt := range tests { + tt := tt + + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + got, err := convertMapValue[bool](tt.data, tt.key) + if tt.errString != "" { + require.ErrorContains(t, err, tt.errString) + } else { + require.Equal(t, tt.want, got) + } + }) + } +} diff --git a/agglayer/mock_agglayer_client.go b/agglayer/mock_agglayer_client.go index 43100a2e..1b756713 100644 --- a/agglayer/mock_agglayer_client.go +++ b/agglayer/mock_agglayer_client.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.45.0. DO NOT EDIT. +// Code generated by mockery v2.39.0. DO NOT EDIT. package agglayer @@ -45,6 +45,36 @@ func (_m *AgglayerClientMock) GetCertificateHeader(certificateHash common.Hash) return r0, r1 } +// GetEpochConfiguration provides a mock function with given fields: +func (_m *AgglayerClientMock) GetEpochConfiguration() (*ClockConfiguration, error) { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for GetEpochConfiguration") + } + + var r0 *ClockConfiguration + var r1 error + if rf, ok := ret.Get(0).(func() (*ClockConfiguration, error)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() *ClockConfiguration); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*ClockConfiguration) + } + } + + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // SendCertificate provides a mock function with given fields: certificate func (_m *AgglayerClientMock) SendCertificate(certificate *SignedCertificate) (common.Hash, error) { ret := _m.Called(certificate) diff --git a/agglayer/proof_generation_error.go b/agglayer/proof_generation_error.go new file mode 100644 index 00000000..fa7012f7 --- /dev/null +++ b/agglayer/proof_generation_error.go @@ -0,0 +1,657 @@ +package agglayer + +import ( + "errors" + "fmt" + "reflect" + + "github.com/ethereum/go-ethereum/common" +) + +var errNotMap = errors.New("inner error is not a map") + +const ( + InvalidSignerErrorType = "InvalidSigner" + InvalidPreviousLERErrorType = "InvalidPreviousLocalExitRoot" + InvalidPreviousBalanceRootErrorType = "InvalidPreviousBalanceRoot" + InvalidPreviousNullifierRootErrorType = "InvalidPreviousNullifierRoot" + InvalidNewLocalExitRootErrorType = "InvalidNewLocalExitRoot" + InvalidNewBalanceRootErrorType = "InvalidNewBalanceRoot" + InvalidNewNullifierRootErrorType = "InvalidNewNullifierRoot" + InvalidImportedExitsRootErrorType = "InvalidImportedExitsRoot" + MismatchImportedExitsRootErrorType = "MismatchImportedExitsRoot" + InvalidNullifierPathErrorType = "InvalidNullifierPath" + InvalidBalancePathErrorType = "InvalidBalancePath" + BalanceOverflowInBridgeExitErrorType = "BalanceOverflowInBridgeExit" + BalanceUnderflowInBridgeExitErrorType = "BalanceUnderflowInBridgeExit" + CannotExitToSameNetworkErrorType = "CannotExitToSameNetwork" + InvalidMessageOriginNetworkErrorType = "InvalidMessageOriginNetwork" + InvalidL1TokenInfoErrorType = "InvalidL1TokenInfo" + MissingTokenBalanceProofErrorType = "MissingTokenBalanceProof" + DuplicateTokenBalanceProofErrorType = "DuplicateTokenBalanceProof" + InvalidSignatureErrorType = "InvalidSignature" + InvalidImportedBridgeExitErrorType = "InvalidImportedBridgeExit" + UnknownErrorType = "UnknownError" +) + +type PPError interface { + String() string +} + +// ProofGenerationError is a struct that represents an error that occurs when generating a proof. +type ProofGenerationError struct { + GenerationType string + InnerErrors []PPError +} + +// String is the implementation of the Error interface +func (p *ProofGenerationError) String() string { + return fmt.Sprintf("Proof generation error: %s. %s", p.GenerationType, p.InnerErrors) +} + +// Unmarshal unmarshals the data from a map into a ProofGenerationError struct. +func (p *ProofGenerationError) Unmarshal(data interface{}) error { + dataMap, ok := data.(map[string]interface{}) + if !ok { + return errNotMap + } + + generationType, err := convertMapValue[string](dataMap, "generation_type") + if err != nil { + return err + } + + p.GenerationType = generationType + + getPPErrFn := func(key string, value interface{}) (PPError, error) { + switch key { + case InvalidSignerErrorType: + invalidSigner := &InvalidSignerError{} + if err := invalidSigner.UnmarshalFromMap(value); err != nil { + return nil, err + } + return invalidSigner, nil + case InvalidPreviousLERErrorType: + invalidPreviousLER := NewInvalidPreviousLocalExitRoot() + if err := invalidPreviousLER.UnmarshalFromMap(value); err != nil { + return nil, err + } + p.InnerErrors = append(p.InnerErrors, invalidPreviousLER) + case InvalidPreviousBalanceRootErrorType: + invalidPreviousBalanceRoot := NewInvalidPreviousBalanceRoot() + if err := invalidPreviousBalanceRoot.UnmarshalFromMap(value); err != nil { + return nil, err + } + p.InnerErrors = append(p.InnerErrors, invalidPreviousBalanceRoot) + case InvalidPreviousNullifierRootErrorType: + invalidPreviousNullifierRoot := NewInvalidPreviousNullifierRoot() + if err := invalidPreviousNullifierRoot.UnmarshalFromMap(value); err != nil { + return nil, err + } + p.InnerErrors = append(p.InnerErrors, invalidPreviousNullifierRoot) + case InvalidNewLocalExitRootErrorType: + invalidNewLocalExitRoot := NewInvalidNewLocalExitRoot() + if err := invalidNewLocalExitRoot.UnmarshalFromMap(value); err != nil { + return nil, err + } + p.InnerErrors = append(p.InnerErrors, invalidNewLocalExitRoot) + case InvalidNewBalanceRootErrorType: + invalidNewBalanceRoot := NewInvalidNewBalanceRoot() + if err := invalidNewBalanceRoot.UnmarshalFromMap(value); err != nil { + return nil, err + } + p.InnerErrors = append(p.InnerErrors, invalidNewBalanceRoot) + case InvalidNewNullifierRootErrorType: + invalidNewNullifierRoot := NewInvalidNewNullifierRoot() + if err := invalidNewNullifierRoot.UnmarshalFromMap(value); err != nil { + return nil, err + } + p.InnerErrors = append(p.InnerErrors, invalidNewNullifierRoot) + case InvalidImportedExitsRootErrorType: + invalidImportedExitsRoot := NewInvalidImportedExitsRoot() + if err := invalidImportedExitsRoot.UnmarshalFromMap(value); err != nil { + return nil, err + } + p.InnerErrors = append(p.InnerErrors, invalidImportedExitsRoot) + case MismatchImportedExitsRootErrorType: + p.InnerErrors = append(p.InnerErrors, &MismatchImportedExitsRoot{}) + case InvalidNullifierPathErrorType: + p.InnerErrors = append(p.InnerErrors, &InvalidNullifierPath{}) + case InvalidBalancePathErrorType: + p.InnerErrors = append(p.InnerErrors, &InvalidBalancePath{}) + case BalanceOverflowInBridgeExitErrorType: + p.InnerErrors = append(p.InnerErrors, &BalanceOverflowInBridgeExit{}) + case BalanceUnderflowInBridgeExitErrorType: + p.InnerErrors = append(p.InnerErrors, &BalanceUnderflowInBridgeExit{}) + case CannotExitToSameNetworkErrorType: + p.InnerErrors = append(p.InnerErrors, &CannotExitToSameNetwork{}) + case InvalidMessageOriginNetworkErrorType: + p.InnerErrors = append(p.InnerErrors, &InvalidMessageOriginNetwork{}) + case InvalidL1TokenInfoErrorType: + invalidL1TokenInfo := NewInvalidL1TokenInfo() + if err := invalidL1TokenInfo.UnmarshalFromMap(value); err != nil { + return nil, err + } + p.InnerErrors = append(p.InnerErrors, invalidL1TokenInfo) + case MissingTokenBalanceProofErrorType: + missingTokenBalanceProof := NewMissingTokenBalanceProof() + if err := missingTokenBalanceProof.UnmarshalFromMap(value); err != nil { + return nil, err + } + p.InnerErrors = append(p.InnerErrors, missingTokenBalanceProof) + case DuplicateTokenBalanceProofErrorType: + duplicateTokenBalanceProof := NewDuplicateTokenBalanceProof() + if err := duplicateTokenBalanceProof.UnmarshalFromMap(value); err != nil { + return nil, err + } + p.InnerErrors = append(p.InnerErrors, duplicateTokenBalanceProof) + case InvalidSignatureErrorType: + p.InnerErrors = append(p.InnerErrors, &InvalidSignature{}) + case InvalidImportedBridgeExitErrorType: + invalidImportedBridgeExit := &InvalidImportedBridgeExit{} + if err := invalidImportedBridgeExit.UnmarshalFromMap(value); err != nil { + return nil, err + } + p.InnerErrors = append(p.InnerErrors, invalidImportedBridgeExit) + case UnknownErrorType: + p.InnerErrors = append(p.InnerErrors, &UnknownError{}) + default: + return nil, fmt.Errorf("unknown proof generation error type: %s", key) + } + + return nil, nil + } + + errorSourceMap, err := convertMapValue[map[string]interface{}](dataMap, "source") + if err != nil { + // it can be a single error + errSourceString, err := convertMapValue[string](dataMap, "source") + if err != nil { + return err + } + + ppErr, err := getPPErrFn(errSourceString, nil) + if err != nil { + return err + } + + if ppErr != nil { + p.InnerErrors = append(p.InnerErrors, ppErr) + } + + return nil + } + + // there will always be only one key in the source map + for key, value := range errorSourceMap { + ppErr, err := getPPErrFn(key, value) + if err != nil { + return err + } + + if ppErr != nil { + p.InnerErrors = append(p.InnerErrors, ppErr) + } + } + + return nil +} + +// InvalidSignerError is a struct that represents an error that occurs when +// the signer of the certificate is invalid, or the hash that was signed was not valid. +type InvalidSignerError struct { + Declared common.Address `json:"declared"` + Recovered common.Address `json:"recovered"` +} + +// String is the implementation of the Error interface +func (e *InvalidSignerError) String() string { + return fmt.Sprintf("%s. Declared: %s, Computed: %s", + InvalidSignerErrorType, e.Declared.String(), e.Recovered.String()) +} + +func (e *InvalidSignerError) UnmarshalFromMap(data interface{}) error { + dataMap, ok := data.(map[string]interface{}) + if !ok { + return errNotMap + } + + declared, err := convertMapValue[string](dataMap, "declared") + if err != nil { + return err + } + + recovered, err := convertMapValue[string](dataMap, "recovered") + if err != nil { + return err + } + + e.Declared = common.HexToAddress(declared) + e.Recovered = common.HexToAddress(recovered) + + return nil +} + +// DeclaredComputedError is a base struct for errors that have both declared and computed values. +type DeclaredComputedError struct { + Declared common.Hash `json:"declared"` + Computed common.Hash `json:"computed"` + ErrType string +} + +// String is the implementation of the Error interface +func (e *DeclaredComputedError) String() string { + return fmt.Sprintf("%s. Declared: %s, Computed: %s", + e.ErrType, e.Declared.String(), e.Computed.String()) +} + +// UnmarshalFromMap is the implementation of the Error interface +func (e *DeclaredComputedError) UnmarshalFromMap(data interface{}) error { + dataMap, ok := data.(map[string]interface{}) + if !ok { + return errNotMap + } + + declared, err := convertMapValue[string](dataMap, "declared") + if err != nil { + return err + } + + computed, err := convertMapValue[string](dataMap, "computed") + if err != nil { + return err + } + + e.Declared = common.HexToHash(declared) + e.Computed = common.HexToHash(computed) + + return nil +} + +// InvalidPreviousLocalExitRoot is a struct that represents an error that occurs when +// the previous local exit root is invalid. +type InvalidPreviousLocalExitRoot struct { + *DeclaredComputedError +} + +func NewInvalidPreviousLocalExitRoot() *InvalidPreviousLocalExitRoot { + return &InvalidPreviousLocalExitRoot{ + DeclaredComputedError: &DeclaredComputedError{ErrType: InvalidPreviousLERErrorType}, + } +} + +// InvalidPreviousBalanceRoot is a struct that represents an error that occurs when +// the previous balance root is invalid. +type InvalidPreviousBalanceRoot struct { + *DeclaredComputedError +} + +func NewInvalidPreviousBalanceRoot() *InvalidPreviousBalanceRoot { + return &InvalidPreviousBalanceRoot{ + DeclaredComputedError: &DeclaredComputedError{ErrType: InvalidPreviousBalanceRootErrorType}, + } +} + +// InvalidPreviousNullifierRoot is a struct that represents an error that occurs when +// the previous nullifier root is invalid. +type InvalidPreviousNullifierRoot struct { + *DeclaredComputedError +} + +func NewInvalidPreviousNullifierRoot() *InvalidPreviousNullifierRoot { + return &InvalidPreviousNullifierRoot{ + DeclaredComputedError: &DeclaredComputedError{ErrType: InvalidPreviousNullifierRootErrorType}, + } +} + +// InvalidNewLocalExitRoot is a struct that represents an error that occurs when +// the new local exit root is invalid. +type InvalidNewLocalExitRoot struct { + *DeclaredComputedError +} + +func NewInvalidNewLocalExitRoot() *InvalidNewLocalExitRoot { + return &InvalidNewLocalExitRoot{ + DeclaredComputedError: &DeclaredComputedError{ErrType: InvalidNewLocalExitRootErrorType}, + } +} + +// InvalidNewBalanceRoot is a struct that represents an error that occurs when +// the new balance root is invalid. +type InvalidNewBalanceRoot struct { + *DeclaredComputedError +} + +func NewInvalidNewBalanceRoot() *InvalidNewBalanceRoot { + return &InvalidNewBalanceRoot{ + DeclaredComputedError: &DeclaredComputedError{ErrType: InvalidNewBalanceRootErrorType}, + } +} + +// InvalidNewNullifierRoot is a struct that represents an error that occurs when +// the new nullifier root is invalid. +type InvalidNewNullifierRoot struct { + *DeclaredComputedError +} + +func NewInvalidNewNullifierRoot() *InvalidNewNullifierRoot { + return &InvalidNewNullifierRoot{ + DeclaredComputedError: &DeclaredComputedError{ErrType: InvalidNewNullifierRootErrorType}, + } +} + +// InvalidImportedExitsRoot is a struct that represents an error that occurs when +// the imported exits root is invalid. +type InvalidImportedExitsRoot struct { + *DeclaredComputedError +} + +func NewInvalidImportedExitsRoot() *InvalidImportedExitsRoot { + return &InvalidImportedExitsRoot{ + DeclaredComputedError: &DeclaredComputedError{ErrType: InvalidImportedExitsRootErrorType}, + } +} + +// MismatchImportedExitsRoot is a struct that represents an error that occurs when +// the commitment to the list of imported bridge exits but the list of imported bridge exits is empty. +type MismatchImportedExitsRoot struct{} + +// String is the implementation of the Error interface +func (e *MismatchImportedExitsRoot) String() string { + return fmt.Sprintf(`%s: The commitment to the list of imported bridge exits + should be Some if and only if this list is non-empty, should be None otherwise.`, + MismatchImportedExitsRootErrorType) +} + +// InvalidNullifierPath is a struct that represents an error that occurs when +// the provided nullifier path is invalid. +type InvalidNullifierPath struct{} + +// String is the implementation of the Error interface +func (e *InvalidNullifierPath) String() string { + return fmt.Sprintf("%s: The provided nullifier path is invalid", InvalidNullifierPathErrorType) +} + +// InvalidBalancePath is a struct that represents an error that occurs when +// the provided balance path is invalid. +type InvalidBalancePath struct{} + +// String is the implementation of the Error interface +func (e *InvalidBalancePath) String() string { + return fmt.Sprintf("%s: The provided balance path is invalid", InvalidBalancePathErrorType) +} + +// BalanceOverflowInBridgeExit is a struct that represents an error that occurs when +// bridge exit led to balance overflow. +type BalanceOverflowInBridgeExit struct{} + +// String is the implementation of the Error interface +func (e *BalanceOverflowInBridgeExit) String() string { + return fmt.Sprintf("%s: The imported bridge exit led to balance overflow.", BalanceOverflowInBridgeExitErrorType) +} + +// BalanceUnderflowInBridgeExit is a struct that represents an error that occurs when +// bridge exit led to balance underflow. +type BalanceUnderflowInBridgeExit struct{} + +// String is the implementation of the Error interface +func (e *BalanceUnderflowInBridgeExit) String() string { + return fmt.Sprintf("%s: The imported bridge exit led to balance underflow.", BalanceUnderflowInBridgeExitErrorType) +} + +// CannotExitToSameNetwork is a struct that represents an error that occurs when +// the user tries to exit to the same network. +type CannotExitToSameNetwork struct{} + +// String is the implementation of the Error interface +func (e *CannotExitToSameNetwork) String() string { + return fmt.Sprintf("%s: The provided bridge exit goes to the sender’s own network which is not permitted.", + CannotExitToSameNetworkErrorType) +} + +// InvalidMessageOriginNetwork is a struct that represents an error that occurs when +// the origin network of the message is invalid. +type InvalidMessageOriginNetwork struct{} + +// String is the implementation of the Error interface +func (e *InvalidMessageOriginNetwork) String() string { + return fmt.Sprintf("%s: The origin network of the message is invalid.", InvalidMessageOriginNetworkErrorType) +} + +// TokenInfoError is a struct inherited by other errors that have a TokenInfo field. +type TokenInfoError struct { + TokenInfo *TokenInfo `json:"token_info"` + isNested bool +} + +func (e *TokenInfoError) UnmarshalFromMap(data interface{}) error { + dataMap, ok := data.(map[string]interface{}) + if !ok { + return errNotMap + } + + var ( + err error + tokenInfoMap map[string]interface{} + ) + + if e.isNested { + tokenInfoMap, err = convertMapValue[map[string]interface{}](dataMap, "TokenInfo") + if err != nil { + return err + } + } else { + tokenInfoMap = dataMap + } + + originNetwork, err := convertMapValue[uint32](tokenInfoMap, "origin_network") + if err != nil { + return err + } + + originAddress, err := convertMapValue[string](tokenInfoMap, "origin_token_address") + if err != nil { + return err + } + + e.TokenInfo = &TokenInfo{ + OriginNetwork: originNetwork, + OriginTokenAddress: common.HexToAddress(originAddress), + } + + return nil +} + +// InvalidL1TokenInfo is a struct that represents an error that occurs when +// the L1 token info is invalid. +type InvalidL1TokenInfo struct { + *TokenInfoError +} + +// NewInvalidL1TokenInfo returns a new instance of InvalidL1TokenInfo. +func NewInvalidL1TokenInfo() *InvalidL1TokenInfo { + return &InvalidL1TokenInfo{ + TokenInfoError: &TokenInfoError{isNested: true}, + } +} + +// String is the implementation of the Error interface +func (e *InvalidL1TokenInfo) String() string { + return fmt.Sprintf("%s: The L1 token info is invalid. %s", + InvalidL1TokenInfoErrorType, e.TokenInfo.String()) +} + +// MissingTokenBalanceProof is a struct that represents an error that occurs when +// the token balance proof is missing. +type MissingTokenBalanceProof struct { + *TokenInfoError +} + +// NewMissingTokenBalanceProof returns a new instance of MissingTokenBalanceProof. +func NewMissingTokenBalanceProof() *MissingTokenBalanceProof { + return &MissingTokenBalanceProof{ + TokenInfoError: &TokenInfoError{isNested: true}, + } +} + +// String is the implementation of the Error interface +func (e *MissingTokenBalanceProof) String() string { + return fmt.Sprintf("%s: The provided token is missing a balance proof. %s", + MissingTokenBalanceProofErrorType, e.TokenInfo.String()) +} + +// DuplicateTokenBalanceProof is a struct that represents an error that occurs when +// the token balance proof is duplicated. +type DuplicateTokenBalanceProof struct { + *TokenInfoError +} + +// NewDuplicateTokenBalanceProof returns a new instance of DuplicateTokenBalanceProof. +func NewDuplicateTokenBalanceProof() *DuplicateTokenBalanceProof { + return &DuplicateTokenBalanceProof{ + TokenInfoError: &TokenInfoError{isNested: true}, + } +} + +// String is the implementation of the Error interface +func (e *DuplicateTokenBalanceProof) String() string { + return fmt.Sprintf("%s: The provided token comes with multiple balance proofs. %s", + DuplicateTokenBalanceProofErrorType, e.TokenInfo.String()) +} + +// InvalidSignature is a struct that represents an error that occurs when +// the signature is invalid. +type InvalidSignature struct{} + +// String is the implementation of the Error interface +func (e *InvalidSignature) String() string { + return fmt.Sprintf("%s: The provided signature is invalid.", InvalidSignatureErrorType) +} + +// UnknownError is a struct that represents an error that occurs when +// an unknown error is encountered. +type UnknownError struct{} + +// String is the implementation of the Error interface +func (e *UnknownError) String() string { + return fmt.Sprintf("%s: An unknown error occurred.", UnknownErrorType) +} + +// InvalidImportedBridgeExit is a struct that represents an error that occurs when +// the imported bridge exit is invalid. +type InvalidImportedBridgeExit struct { + GlobalIndex *GlobalIndex `json:"global_index"` + ErrorType string `json:"error_type"` +} + +// String is the implementation of the Error interface +func (e *InvalidImportedBridgeExit) String() string { + var errorDescription string + switch e.ErrorType { + case "MismatchGlobalIndexInclusionProof": + errorDescription = "The global index and the inclusion proof do not both correspond " + + "to the same network type: mainnet or rollup." + case "MismatchL1Root": + errorDescription = "The provided L1 info root does not match the one provided in the inclusion proof." + case "MismatchMER": + errorDescription = "The provided MER does not match the one provided in the inclusion proof." + case "MismatchRER": + errorDescription = "The provided RER does not match the one provided in the inclusion proof." + case "InvalidMerklePathLeafToLER": + errorDescription = "The inclusion proof from the leaf to the LER is invalid." + case "InvalidMerklePathLERToRER": + errorDescription = "The inclusion proof from the LER to the RER is invalid." + case "InvalidMerklePathGERToL1Root": + errorDescription = "The inclusion proof from the GER to the L1 info Root is invalid." + case "InvalidExitNetwork": + errorDescription = "The provided imported bridge exit does not target the right destination network." + default: + errorDescription = "An unknown error occurred." + } + + return fmt.Sprintf("%s: Global index: %s. Error type: %s. %s", + InvalidImportedBridgeExitErrorType, e.GlobalIndex.String(), e.ErrorType, errorDescription) +} + +func (e *InvalidImportedBridgeExit) UnmarshalFromMap(data interface{}) error { + dataMap, ok := data.(map[string]interface{}) + if !ok { + return errNotMap + } + + sourceErr, err := convertMapValue[string](dataMap, "source") + if err != nil { + return err + } + + e.ErrorType = sourceErr + + globalIndexMap, err := convertMapValue[map[string]interface{}](dataMap, "global_index") + if err != nil { + return err + } + + e.GlobalIndex = &GlobalIndex{} + return e.GlobalIndex.UnmarshalFromMap(globalIndexMap) +} + +// convertMapValue converts the value of a key in a map to a target type. +func convertMapValue[T any](data map[string]interface{}, key string) (T, error) { + value, ok := data[key] + if !ok { + var zero T + return zero, fmt.Errorf("key %s not found in map", key) + } + + // Try a direct type assertion + if convertedValue, ok := value.(T); ok { + return convertedValue, nil + } + + // If direct assertion fails, handle numeric type conversions + var target T + targetType := reflect.TypeOf(target) + + // Check if value is a float64 (default JSON number type) and target is a numeric type + if floatValue, ok := value.(float64); ok && targetType.Kind() >= reflect.Int && targetType.Kind() <= reflect.Uint64 { + convertedValue, err := convertNumeric(floatValue, targetType) + if err != nil { + return target, fmt.Errorf("conversion error for key %s: %w", key, err) + } + return convertedValue.(T), nil //nolint:forcetypeassert + } + + return target, fmt.Errorf("value of key %s is not of type %T", key, target) +} + +// convertNumeric converts a float64 to the specified numeric type. +func convertNumeric(value float64, targetType reflect.Type) (interface{}, error) { + switch targetType.Kind() { + case reflect.Int: + return int(value), nil + case reflect.Int8: + return int8(value), nil + case reflect.Int16: + return int16(value), nil + case reflect.Int32: + return int32(value), nil + case reflect.Int64: + return int64(value), nil + case reflect.Uint: + return uint(value), nil + case reflect.Uint8: + return uint8(value), nil + case reflect.Uint16: + return uint16(value), nil + case reflect.Uint32: + return uint32(value), nil + case reflect.Uint64: + return uint64(value), nil + case reflect.Float32: + return float32(value), nil + case reflect.Float64: + return value, nil + default: + return nil, errors.New("unsupported target type") + } +} diff --git a/agglayer/proof_verification_error.go b/agglayer/proof_verification_error.go new file mode 100644 index 00000000..dd5c5f74 --- /dev/null +++ b/agglayer/proof_verification_error.go @@ -0,0 +1,164 @@ +package agglayer + +import "fmt" + +const ( + VersionMismatchErrorType = "VersionMismatch" + CoreErrorType = "Core" + RecursionErrorType = "Recursion" + PlankErrorType = "Plank" + Groth16ErrorType = "Groth16" + InvalidPublicValuesErrorType = "InvalidPublicValues" +) + +// ProofVerificationError is an error that is returned when verifying a proof +type ProofVerificationError struct { + InnerErrors []PPError +} + +// String is the implementation of the Error interface +func (p *ProofVerificationError) String() string { + return fmt.Sprintf("Proof verification error: %v", p.InnerErrors) +} + +// Unmarshal unmarshals the data from a map into a ProofVerificationError struct. +func (p *ProofVerificationError) Unmarshal(data interface{}) error { + getPPErrFn := func(key string, value interface{}) (PPError, error) { + switch key { + case VersionMismatchErrorType: + versionMismatch := &VersionMismatch{} + if err := versionMismatch.Unmarshal(value); err != nil { + return nil, err + } + return versionMismatch, nil + case CoreErrorType: + core := &Core{} + if err := core.Unmarshal(value); err != nil { + return nil, err + } + return core, nil + case RecursionErrorType: + recursion := &Recursion{} + if err := recursion.Unmarshal(value); err != nil { + return nil, err + } + return recursion, nil + case PlankErrorType: + plank := &Plank{} + if err := plank.Unmarshal(value); err != nil { + return nil, err + } + return plank, nil + case Groth16ErrorType: + groth16 := &Groth16{} + if err := groth16.Unmarshal(value); err != nil { + return nil, err + } + return groth16, nil + case InvalidPublicValuesErrorType: + return &InvalidPublicValues{}, nil + default: + return nil, fmt.Errorf("unknown proof verification error type: %v", key) + } + } + + getAndAddInnerErrorFn := func(key string, value interface{}) error { + ppErr, err := getPPErrFn(key, value) + if err != nil { + return err + } + + if ppErr != nil { + p.InnerErrors = append(p.InnerErrors, ppErr) + } + + return nil + } + + dataMap, ok := data.(map[string]interface{}) + if !ok { + // it can be a single error + return getAndAddInnerErrorFn(data.(string), nil) //nolint:forcetypeassert + } + + for key, value := range dataMap { + if err := getAndAddInnerErrorFn(key, value); err != nil { + return err + } + } + + return nil +} + +// StringError is an error that is inherited by other errors that expect a string +// field in the data. +type StringError string + +// Unmarshal unmarshals the data from an interface{} into a StringError. +func (e *StringError) Unmarshal(data interface{}) error { + str, ok := data.(string) + if !ok { + return fmt.Errorf("expected string for StringError, got %T", data) + } + *e = StringError(str) + return nil +} + +// VersionMismatch is an error that is returned when the version of the proof is +// different from the version of the core. +type VersionMismatch struct { + StringError +} + +// String is the implementation of the Error interface +func (e *VersionMismatch) String() string { + return fmt.Sprintf("%s: %s", VersionMismatchErrorType, e.StringError) +} + +// Core is an error that is returned when the core machine verification fails. +type Core struct { + StringError +} + +// String is the implementation of the Error interface +func (e *Core) String() string { + return fmt.Sprintf("%s: Core machine verification error: %s", CoreErrorType, e.StringError) +} + +// Recursion is an error that is returned when the recursion verification fails. +type Recursion struct { + StringError +} + +// String is the implementation of the Error interface +func (e *Recursion) String() string { + return fmt.Sprintf("%s: Recursion verification error: %s", RecursionErrorType, e.StringError) +} + +// Plank is an error that is returned when the plank verification fails. +type Plank struct { + StringError +} + +// String is the implementation of the Error interface +func (e *Plank) String() string { + return fmt.Sprintf("%s: Plank verification error: %s", PlankErrorType, e.StringError) +} + +// Groth16 is an error that is returned when the Groth16 verification fails. +type Groth16 struct { + StringError +} + +// String is the implementation of the Error interface +func (e *Groth16) String() string { + return fmt.Sprintf("%s: Groth16 verification error: %s", Groth16ErrorType, e.StringError) +} + +// InvalidPublicValues is an error that is returned when the public values are invalid. +type InvalidPublicValues struct{} + +// String is the implementation of the Error interface +func (e *InvalidPublicValues) String() string { + return fmt.Sprintf("%s: Invalid public values", InvalidPublicValuesErrorType) +} diff --git a/agglayer/testdata/proof_generation_errors/errors_with_declared_computed_data.json b/agglayer/testdata/proof_generation_errors/errors_with_declared_computed_data.json new file mode 100644 index 00000000..4b1b4029 --- /dev/null +++ b/agglayer/testdata/proof_generation_errors/errors_with_declared_computed_data.json @@ -0,0 +1,45 @@ +[ + { + "test_name": "InvalidImportedExitsRoot", + "certificate_header": "{\"network_id\":14,\"height\":1,\"epoch_number\":null,\"certificate_index\":null,\"certificate_id\":\"0xa80cd4abb016bbb3c3058f923a88be1ad49d68277366c55554d6f13d62428a1f\",\"new_local_exit_root\":\"0x566244fbf813b6926f6895142979f61ed6706184909cb8c819cd0216202a8aa9\",\"metadata\":\"0x000000000000000000000000000000000000000000000000000000000000001f\",\"status\":{\"InError\":{\"error\":{\"ProofGenerationError\":{\"generation_type\":\"Native\",\"source\":{\"InvalidImportedExitsRoot\":{\"declared\":\"0x1116837a43bdc3dd9f114558daf4b26ed4eeeeec\",\"computed\":\"0x20222bfbb55589f7fd0bec3666e3de469111ce3c\"}}}}}}}" + }, + { + "test_name": "InvalidNewBalanceRoot", + "certificate_header": "{\"network_id\":11,\"height\":31,\"epoch_number\":null,\"certificate_index\":null,\"certificate_id\":\"0xa80cd4abb016bbb3c3058f923a88be1ad49d68277366c55554d6f13d62428a1f\",\"new_local_exit_root\":\"0x566244fbf813b6926f6895142979f61ed6706184909cb8c819cd0216202a8aa9\",\"metadata\":\"0x000000000000000000000000000000000000000000000000000000000000001f\",\"status\":{\"InError\":{\"error\":{\"ProofGenerationError\":{\"generation_type\":\"Native\",\"source\":{\"InvalidNewBalanceRoot\":{\"declared\":\"0x5b06837a43bdc3dd9f114558daf4b26ed4eeeeec\",\"computed\":\"0x20e92bfbb55589f7fd0bec3666e3de469111ce3c\"}}}}}}}" + }, + { + "test_name": "InvalidNewLocalExitRoot", + "certificate_header": "{\"network_id\":3,\"height\":22,\"epoch_number\":null,\"certificate_index\":null,\"certificate_id\":\"0xa80cd4abb016bbb3c3058f923a88be1ad49d68277366c55554d6f13d62428a1f\",\"new_local_exit_root\":\"0x566244fbf813b6926f6895142979f61ed6706184909cb8c819cd0216202a8aa9\",\"metadata\":\"0x000000000000000000000000000000000000000000000000000000000000001f\",\"status\":{\"InError\":{\"error\":{\"ProofGenerationError\":{\"generation_type\":\"Native\",\"source\":{\"InvalidNewLocalExitRoot\":{\"declared\":\"0x5b06837a43bdc3dd9f114558daf4b26ed49831ec\",\"computed\":\"0x20e92bfbb55589f7fd0bec3666e3de469525ce3c\"}}}}}}}" + }, + { + "test_name": "InvalidNewNullifierRoot", + "certificate_header": "{\"network_id\":2,\"height\":12,\"epoch_number\":null,\"certificate_index\":null,\"certificate_id\":\"0xa80cd4abb016bbb3c3058f923a88be1ad49d68277366c55554d6f13d62428a1f\",\"new_local_exit_root\":\"0x566244fbf813b6926f6895142979f61ed6706184909cb8c819cd0216202a8aa9\",\"metadata\":\"0x000000000000000000000000000000000000000000000000000000000000001f\",\"status\":{\"InError\":{\"error\":{\"ProofGenerationError\":{\"generation_type\":\"Native\",\"source\":{\"InvalidNewNullifierRoot\":{\"declared\":\"0x5b06837a43bdc3dd9f114558daf4b26ed4ccceec\",\"computed\":\"0x20e92bfbb55589f7fd0bec3666e3de222111ce3c\"}}}}}}}" + }, + { + "test_name": "InvalidPreviousBalanceRoot", + "certificate_header": "{\"network_id\":2,\"height\":11,\"epoch_number\":null,\"certificate_index\":null,\"certificate_id\":\"0xa80cd4abb016bbb3c3058f923a88be1ad49d68277366c55554d6f13d62428a1f\",\"new_local_exit_root\":\"0x566244fbf813b6926f6895142979f61ed6706184909cb8c819cd0216202a8aa9\",\"metadata\":\"0x000000000000000000000000000000000000000000000000000000000000001f\",\"status\":{\"InError\":{\"error\":{\"ProofGenerationError\":{\"generation_type\":\"Native\",\"source\":{\"InvalidPreviousBalanceRoot\":{\"declared\":\"0x5b06837a43bdc3dd9f114558daf4b26ed49842ec\",\"computed\":\"0x20e92bfbb55589f7fd0bec3666e3de469526de3c\"}}}}}}}" + }, + { + "test_name": "InvalidPreviousLocalExitRoot", + "certificate_header": "{\"network_id\":1,\"height\":0,\"epoch_number\":null,\"certificate_index\":null,\"certificate_id\":\"0xa80cd4abb016bbb3c3058f923a88be1ad49d68277366c55554d6f13d62428a1f\",\"new_local_exit_root\":\"0x566244fbf813b6926f6895142979f61ed6706184909cb8c819cd0216202a8aa9\",\"metadata\":\"0x000000000000000000000000000000000000000000000000000000000000001f\",\"status\":{\"InError\":{\"error\":{\"ProofGenerationError\":{\"generation_type\":\"Native\",\"source\":{\"InvalidPreviousLocalExitRoot\":{\"declared\":\"0x5b06837a43bdc3dd9f114558daf4b26ed49842ed\",\"computed\":\"0x20e92bfbb55589f7fd0bec3666e3de469526de3e\"}}}}}}}" + }, + { + "test_name": "InvalidPreviousNullifierRoot", + "certificate_header": "{\"network_id\":21,\"height\":111,\"epoch_number\":null,\"certificate_index\":null,\"certificate_id\":\"0xa80cd4abb016bbb3c3058f923a88be1ad49d68277366c55554d6f13d62428a1f\",\"new_local_exit_root\":\"0x566244fbf813b6926f6895142979f61ed6706184909cb8c819cd0216202a8aa9\",\"metadata\":\"0x000000000000000000000000000000000000000000000000000000000000001f\",\"status\":{\"InError\":{\"error\":{\"ProofGenerationError\":{\"generation_type\":\"Native\",\"source\":{\"InvalidPreviousNullifierRoot\":{\"declared\":\"0x5b06837a43bdc3dd9f114558daf4b26ed49842ee\",\"computed\":\"0x20e92bfbb55589f7fd0bec3666e3de469526de3e\"}}}}}}}" + }, + { + "test_name": "InvalidPreviousNullifierRoot_missing_declared", + "expected_error": "key declared not found in map", + "certificate_header": "{\"network_id\":21,\"height\":111,\"epoch_number\":null,\"certificate_index\":null,\"certificate_id\":\"0xa80cd4abb016bbb3c3058f923a88be1ad49d68277366c55554d6f13d62428a1f\",\"new_local_exit_root\":\"0x566244fbf813b6926f6895142979f61ed6706184909cb8c819cd0216202a8aa9\",\"metadata\":\"0x000000000000000000000000000000000000000000000000000000000000001f\",\"status\":{\"InError\":{\"error\":{\"ProofGenerationError\":{\"generation_type\":\"Native\",\"source\":{\"InvalidPreviousNullifierRoot\":{\"computed\":\"0x20e92bfbb55589f7fd0bec3666e3de469526de3e\"}}}}}}}" + }, + { + "test_name": "InvalidPreviousNullifierRoot_missing_computed", + "expected_error": "key computed not found in map", + "certificate_header": "{\"network_id\":21,\"height\":111,\"epoch_number\":null,\"certificate_index\":null,\"certificate_id\":\"0xa80cd4abb016bbb3c3058f923a88be1ad49d68277366c55554d6f13d62428a1f\",\"new_local_exit_root\":\"0x566244fbf813b6926f6895142979f61ed6706184909cb8c819cd0216202a8aa9\",\"metadata\":\"0x000000000000000000000000000000000000000000000000000000000000001f\",\"status\":{\"InError\":{\"error\":{\"ProofGenerationError\":{\"generation_type\":\"Native\",\"source\":{\"InvalidPreviousNullifierRoot\":{\"declared\":\"0x5b06837a43bdc3dd9f114558daf4b26ed49842ee\"}}}}}}}" + }, + { + "test_name": "InvalidPreviousNullifierRoot_missing_inner_error", + "expected_error": "not a map", + "certificate_header": "{\"network_id\":21,\"height\":111,\"epoch_number\":null,\"certificate_index\":null,\"certificate_id\":\"0xa80cd4abb016bbb3c3058f923a88be1ad49d68277366c55554d6f13d62428a1f\",\"new_local_exit_root\":\"0x566244fbf813b6926f6895142979f61ed6706184909cb8c819cd0216202a8aa9\",\"metadata\":\"0x000000000000000000000000000000000000000000000000000000000000001f\",\"status\":{\"InError\":{\"error\":{\"ProofGenerationError\":{\"generation_type\":\"Native\",\"source\":\"InvalidPreviousNullifierRoot\"}}}}}" + } +] \ No newline at end of file diff --git a/agglayer/testdata/proof_generation_errors/errors_with_token_info.json b/agglayer/testdata/proof_generation_errors/errors_with_token_info.json new file mode 100644 index 00000000..6884676a --- /dev/null +++ b/agglayer/testdata/proof_generation_errors/errors_with_token_info.json @@ -0,0 +1,29 @@ +[ + { + "test_name": "InvalidL1TokenInfo", + "certificate_header":"{\"network_id\":1,\"height\":0,\"epoch_number\":null,\"certificate_index\":null,\"certificate_id\":\"0xa80cd4abb016bbb3c3058f923a88be1ad49d68277366c55554d6f13d62428a1f\",\"new_local_exit_root\":\"0x566244fbf813b6926f6895142979f61ed6706184909cb8c819cd0216202a8aa9\",\"metadata\":\"0x000000000000000000000000000000000000000000000000000000000000001f\",\"status\":{\"InError\":{\"error\":{\"ProofGenerationError\":{\"generation_type\":\"Native\",\"source\":{\"InvalidL1TokenInfo\":{\"TokenInfo\":{\"origin_network\":1,\"origin_token_address\":\"0x566244fbf813b6926f6895142979f61ed6706184909cb8c819cd0216202a8aa2\"}}}}}}}}" + }, + { + "test_name": "MissingTokenBalanceProof", + "certificate_header": "{\"network_id\":2111,\"height\":1,\"epoch_number\":null,\"certificate_index\":null,\"certificate_id\":\"0xa80cd4abb016bbb3c3058f923a88be1ad49d68277366c55554d6f13d62428a1f\",\"new_local_exit_root\":\"0x566244fbf813b6926f6895142979f61ed6706184909cb8c819cd0216202a8aa9\",\"metadata\":\"0x000000000000000000000000000000000000000000000000000000000000001f\",\"status\":{\"InError\":{\"error\":{\"ProofGenerationError\":{\"generation_type\":\"Native\",\"source\":{\"MissingTokenBalanceProof\":{\"TokenInfo\":{\"origin_network\":2111,\"origin_token_address\":\"0x566244fbf813b6926f6895142979f61ed6706184909cb8c819cd0216202a8aa2\"}}}}}}}}" + }, + { + "test_name": "DuplicateTokenBalanceProof", + "certificate_header": "{\"network_id\":100000000,\"height\":18446744073709551615,\"epoch_number\":null,\"certificate_index\":null,\"certificate_id\":\"0xa80cd4abb016bbb3c3058f923a88be1ad49d68277366c55554d6f13d62428a1f\",\"new_local_exit_root\":\"0x566244fbf813b6926f6895142979f61ed6706184909cb8c819cd0216202a8aa9\",\"metadata\":\"0x000000000000000000000000000000000000000000000000000000000000001f\",\"status\":{\"InError\":{\"error\":{\"ProofGenerationError\":{\"generation_type\":\"Native\",\"source\":{\"DuplicateTokenBalanceProof\":{\"TokenInfo\":{\"origin_network\":10000000000,\"origin_token_address\":\"0x566244fbf813b6926f6895142979f61ed6706184909cb8c819cd0216202a8aa2\"}}}}}}}}" + }, + { + "test_name": "DuplicateTokenBalanceProof_missing_token_info", + "expected_error": "key TokenInfo not found in map", + "certificate_header": "{\"network_id\":100000000,\"height\":18446744073709551615,\"epoch_number\":null,\"certificate_index\":null,\"certificate_id\":\"0xa80cd4abb016bbb3c3058f923a88be1ad49d68277366c55554d6f13d62428a1f\",\"new_local_exit_root\":\"0x566244fbf813b6926f6895142979f61ed6706184909cb8c819cd0216202a8aa9\",\"metadata\":\"0x000000000000000000000000000000000000000000000000000000000000001f\",\"status\":{\"InError\":{\"error\":{\"ProofGenerationError\":{\"generation_type\":\"Native\",\"source\":{\"DuplicateTokenBalanceProof\":{}}}}}}}" + }, + { + "test_name": "DuplicateTokenBalanceProof_missing_origin_network", + "expected_error": "key origin_network not found in map", + "certificate_header": "{\"network_id\":100000000,\"height\":18446744073709551615,\"epoch_number\":null,\"certificate_index\":null,\"certificate_id\":\"0xa80cd4abb016bbb3c3058f923a88be1ad49d68277366c55554d6f13d62428a1f\",\"new_local_exit_root\":\"0x566244fbf813b6926f6895142979f61ed6706184909cb8c819cd0216202a8aa9\",\"metadata\":\"0x000000000000000000000000000000000000000000000000000000000000001f\",\"status\":{\"InError\":{\"error\":{\"ProofGenerationError\":{\"generation_type\":\"Native\",\"source\":{\"DuplicateTokenBalanceProof\":{\"TokenInfo\":{\"origin_token_address\":\"0x566244fbf813b6926f6895142979f61ed6706184909cb8c819cd0216202a8aa2\"}}}}}}}}" + }, + { + "test_name": "DuplicateTokenBalanceProof_missing_origin_token_address", + "expected_error": "key origin_token_address not found in map", + "certificate_header": "{\"network_id\":100000000,\"height\":18446744073709551615,\"epoch_number\":null,\"certificate_index\":null,\"certificate_id\":\"0xa80cd4abb016bbb3c3058f923a88be1ad49d68277366c55554d6f13d62428a1f\",\"new_local_exit_root\":\"0x566244fbf813b6926f6895142979f61ed6706184909cb8c819cd0216202a8aa9\",\"metadata\":\"0x000000000000000000000000000000000000000000000000000000000000001f\",\"status\":{\"InError\":{\"error\":{\"ProofGenerationError\":{\"generation_type\":\"Native\",\"source\":{\"DuplicateTokenBalanceProof\":{\"TokenInfo\":{\"origin_network\":10000000000}}}}}}}}" + } +] \ No newline at end of file diff --git a/agglayer/testdata/proof_generation_errors/errors_without_inner_data.json b/agglayer/testdata/proof_generation_errors/errors_without_inner_data.json new file mode 100644 index 00000000..87946f16 --- /dev/null +++ b/agglayer/testdata/proof_generation_errors/errors_without_inner_data.json @@ -0,0 +1,38 @@ +[ + { + "test_name": "MismatchImportedExitsRoot", + "certificate_header": "{\"network_id\":14,\"height\":1,\"epoch_number\":null,\"certificate_index\":null,\"certificate_id\":\"0xa80cd4abb016bbb3c3058f923a88be1ad49d68277366c55554d6f13d62428a1f\",\"new_local_exit_root\":\"0x566244fbf813b6926f6895142979f61ed6706184909cb8c819cd0216202a8aa9\",\"metadata\":\"0x000000000000000000000000000000000000000000000000000000000000001f\",\"status\":{\"InError\":{\"error\":{\"ProofGenerationError\":{\"generation_type\":\"Native\",\"source\":\"MismatchImportedExitsRoot\"}}}}}" + }, + { + "test_name": "InvalidNullifierPath", + "certificate_header": "{\"network_id\":15,\"height\":2,\"epoch_number\":null,\"certificate_index\":null,\"certificate_id\":\"0xa80cd4abb016bbb3c3058f923a88be1ad49d68277366c55554d6f13d62428a1f\",\"new_local_exit_root\":\"0x566244fbf813b6926f6895142979f61ed6706184909cb8c819cd0216202a8aa9\",\"metadata\":\"0x000000000000000000000000000000000000000000000000000000000000001f\",\"status\":{\"InError\":{\"error\":{\"ProofGenerationError\":{\"generation_type\":\"Native\",\"source\":\"InvalidNullifierPath\"}}}}}" + }, + { + "test_name": "InvalidBalancePath", + "certificate_header": "{\"network_id\":16,\"height\":3,\"epoch_number\":null,\"certificate_index\":null,\"certificate_id\":\"0xa80cd4abb016bbb3c3058f923a88be1ad49d68277366c55554d6f13d62428a1f\",\"new_local_exit_root\":\"0x566244fbf813b6926f6895142979f61ed6706184909cb8c819cd0216202a8aa9\",\"metadata\":\"0x000000000000000000000000000000000000000000000000000000000000001f\",\"status\":{\"InError\":{\"error\":{\"ProofGenerationError\":{\"generation_type\":\"Native\",\"source\":\"InvalidBalancePath\"}}}}}" + }, + { + "test_name": "BalanceOverflowInBridgeExit", + "certificate_header": "{\"network_id\":17,\"height\":4,\"epoch_number\":null,\"certificate_index\":null,\"certificate_id\":\"0xa80cd4abb016bbb3c3058f923a88be1ad49d68277366c55554d6f13d62428a1f\",\"new_local_exit_root\":\"0x566244fbf813b6926f6895142979f61ed6706184909cb8c819cd0216202a8aa9\",\"metadata\":\"0x000000000000000000000000000000000000000000000000000000000000001f\",\"status\":{\"InError\":{\"error\":{\"ProofGenerationError\":{\"generation_type\":\"Native\",\"source\":\"BalanceOverflowInBridgeExit\"}}}}}" + }, + { + "test_name": "BalanceUnderflowInBridgeExit", + "certificate_header": "{\"network_id\":18,\"height\":5,\"epoch_number\":null,\"certificate_index\":null,\"certificate_id\":\"0xa80cd4abb016bbb3c3058f923a88be1ad49d68277366c55554d6f13d62428a1f\",\"new_local_exit_root\":\"0x566244fbf813b6926f6895142979f61ed6706184909cb8c819cd0216202a8aa9\",\"metadata\":\"0x000000000000000000000000000000000000000000000000000000000000001f\",\"status\":{\"InError\":{\"error\":{\"ProofGenerationError\":{\"generation_type\":\"Native\",\"source\":\"BalanceUnderflowInBridgeExit\"}}}}}" + }, + { + "test_name": "CannotExitToSameNetwork", + "certificate_header": "{\"network_id\":19,\"height\":6,\"epoch_number\":null,\"certificate_index\":null,\"certificate_id\":\"0xa80cd4abb016bbb3c3058f923a88be1ad49d68277366c55554d6f13d62428a1f\",\"new_local_exit_root\":\"0x566244fbf813b6926f6895142979f61ed6706184909cb8c819cd0216202a8aa9\",\"metadata\":\"0x000000000000000000000000000000000000000000000000000000000000001f\",\"status\":{\"InError\":{\"error\":{\"ProofGenerationError\":{\"generation_type\":\"Native\",\"source\":\"CannotExitToSameNetwork\"}}}}}" + }, + { + "test_name": "InvalidMessageOriginNetwork", + "certificate_header": "{\"network_id\":20,\"height\":7,\"epoch_number\":null,\"certificate_index\":null,\"certificate_id\":\"0xa80cd4abb016bbb3c3058f923a88be1ad49d68277366c55554d6f13d62428a1f\",\"new_local_exit_root\":\"0x566244fbf813b6926f6895142979f61ed6706184909cb8c819cd0216202a8aa9\",\"metadata\":\"0x000000000000000000000000000000000000000000000000000000000000001f\",\"status\":{\"InError\":{\"error\":{\"ProofGenerationError\":{\"generation_type\":\"Native\",\"source\":\"InvalidMessageOriginNetwork\"}}}}}" + }, + { + "test_name": "UnknownError", + "certificate_header": "{\"network_id\":21,\"height\":8,\"epoch_number\":null,\"certificate_index\":null,\"certificate_id\":\"0xa80cd4abb016bbb3c3058f923a88be1ad49d68277366c55554d6f13d62428a1f\",\"new_local_exit_root\":\"0x566244fbf813b6926f6895142979f61ed6706184909cb8c819cd0216202a8aa9\",\"metadata\":\"0x000000000000000000000000000000000000000000000000000000000000001f\",\"status\":{\"InError\":{\"error\":{\"ProofGenerationError\":{\"generation_type\":\"Native\",\"source\":\"UnknownError\"}}}}}" + }, + { + "test_name": "InvalidSignature", + "certificate_header": "{\"network_id\":22,\"height\":9,\"epoch_number\":null,\"certificate_index\":null,\"certificate_id\":\"0xa80cd4abb016bbb3c3058f923a88be1ad49d68277366c55554d6f13d62428a1f\",\"new_local_exit_root\":\"0x566244fbf813b6926f6895142979f61ed6706184909cb8c819cd0216202a8aa9\",\"metadata\":\"0x000000000000000000000000000000000000000000000000000000000000001f\",\"status\":{\"InError\":{\"error\":{\"ProofGenerationError\":{\"generation_type\":\"Native\",\"source\":\"InvalidSignature\"}}}}}" + } +] \ No newline at end of file diff --git a/agglayer/testdata/proof_generation_errors/invalid_imported_bridge_exit_errors.json b/agglayer/testdata/proof_generation_errors/invalid_imported_bridge_exit_errors.json new file mode 100644 index 00000000..dc6b8cad --- /dev/null +++ b/agglayer/testdata/proof_generation_errors/invalid_imported_bridge_exit_errors.json @@ -0,0 +1,48 @@ +[ + { + "test_name": "MismatchGlobalIndexInclusionProof", + "certificate_header": "{\"network_id\":1,\"height\":0,\"epoch_number\":null,\"certificate_index\":null,\"certificate_id\":\"0xa80cd4abb016bbb3c3058f923a88be1ad49d68277366c55554d6f13d62428a1f\",\"new_local_exit_root\":\"0x566244fbf813b6926f6895142979f61ed6706184909cb8c819cd0216202a8aa9\",\"metadata\":\"0x000000000000000000000000000000000000000000000000000000000000001f\",\"status\":{\"InError\":{\"error\":{\"ProofGenerationError\":{\"generation_type\":\"Native\",\"source\":{\"InvalidImportedBridgeExit\":{\"source\":\"MismatchGlobalIndexInclusionProof\",\"global_index\":{\"mainnet_flag\":true,\"rollup_index\":0,\"leaf_index\":1}}}}}}}}" + }, + { + "test_name": "MismatchL1Root", + "certificate_header": "{\"network_id\":1,\"height\":1,\"epoch_number\":null,\"certificate_index\":null,\"certificate_id\":\"0xa80cd4abb016bbb3c3058f923a88be1ad49d68277366c55554d6f13d62428a1f\",\"new_local_exit_root\":\"0x566244fbf813b6926f6895142979f61ed6706184909cb8c819cd0216202a8aa9\",\"metadata\":\"0x000000000000000000000000000000000000000000000000000000000000001f\",\"status\":{\"InError\":{\"error\":{\"ProofGenerationError\":{\"generation_type\":\"Native\",\"source\":{\"InvalidImportedBridgeExit\":{\"source\":\"MismatchL1Root\",\"global_index\":{\"mainnet_flag\":true,\"rollup_index\":0,\"leaf_index\":2}}}}}}}}" + }, + { + "test_name": "MismatchMER", + "certificate_header": "{\"network_id\":1,\"height\":2,\"epoch_number\":null,\"certificate_index\":null,\"certificate_id\":\"0xa80cd4abb016bbb3c3058f923a88be1ad49d68277366c55554d6f13d62428a1f\",\"new_local_exit_root\":\"0x566244fbf813b6926f6895142979f61ed6706184909cb8c819cd0216202a8aa9\",\"metadata\":\"0x000000000000000000000000000000000000000000000000000000000000001f\",\"status\":{\"InError\":{\"error\":{\"ProofGenerationError\":{\"generation_type\":\"Native\",\"source\":{\"InvalidImportedBridgeExit\":{\"source\":\"MismatchMER\",\"global_index\":{\"mainnet_flag\":true,\"rollup_index\":0,\"leaf_index\":3}}}}}}}}" + }, + { + "test_name": "MismatchRER", + "certificate_header": "{\"network_id\":1,\"height\":3,\"epoch_number\":null,\"certificate_index\":null,\"certificate_id\":\"0xa80cd4abb016bbb3c3058f923a88be1ad49d68277366c55554d6f13d62428a1f\",\"new_local_exit_root\":\"0x566244fbf813b6926f6895142979f61ed6706184909cb8c819cd0216202a8aa9\",\"metadata\":\"0x000000000000000000000000000000000000000000000000000000000000001f\",\"status\":{\"InError\":{\"error\":{\"ProofGenerationError\":{\"generation_type\":\"Native\",\"source\":{\"InvalidImportedBridgeExit\":{\"source\":\"MismatchRER\",\"global_index\":{\"mainnet_flag\":false,\"rollup_index\":1,\"leaf_index\":4}}}}}}}}" + }, + { + "test_name": "InvalidMerklePathLeafToLER", + "certificate_header": "{\"network_id\":1,\"height\":4,\"epoch_number\":null,\"certificate_index\":null,\"certificate_id\":\"0xa80cd4abb016bbb3c3058f923a88be1ad49d68277366c55554d6f13d62428a1f\",\"new_local_exit_root\":\"0x566244fbf813b6926f6895142979f61ed6706184909cb8c819cd0216202a8aa9\",\"metadata\":\"0x000000000000000000000000000000000000000000000000000000000000001f\",\"status\":{\"InError\":{\"error\":{\"ProofGenerationError\":{\"generation_type\":\"Native\",\"source\":{\"InvalidImportedBridgeExit\":{\"source\":\"InvalidMerklePathLeafToLER\",\"global_index\":{\"mainnet_flag\":true,\"rollup_index\":0,\"leaf_index\":5}}}}}}}}" + }, + { + "test_name": "InvalidMerklePathLERToRER", + "certificate_header": "{\"network_id\":1,\"height\":5,\"epoch_number\":null,\"certificate_index\":null,\"certificate_id\":\"0xa80cd4abb016bbb3c3058f923a88be1ad49d68277366c55554d6f13d62428a1f\",\"new_local_exit_root\":\"0x566244fbf813b6926f6895142979f61ed6706184909cb8c819cd0216202a8aa9\",\"metadata\":\"0x000000000000000000000000000000000000000000000000000000000000001f\",\"status\":{\"InError\":{\"error\":{\"ProofGenerationError\":{\"generation_type\":\"Native\",\"source\":{\"InvalidImportedBridgeExit\":{\"source\":\"InvalidMerklePathLERToRER\",\"global_index\":{\"mainnet_flag\":false,\"rollup_index\":2,\"leaf_index\":6}}}}}}}}" + }, + { + "test_name": "InvalidMerklePathGERToL1Root", + "certificate_header": "{\"network_id\":1,\"height\":6,\"epoch_number\":null,\"certificate_index\":null,\"certificate_id\":\"0xa80cd4abb016bbb3c3058f923a88be1ad49d68277366c55554d6f13d62428a1f\",\"new_local_exit_root\":\"0x566244fbf813b6926f6895142979f61ed6706184909cb8c819cd0216202a8aa9\",\"metadata\":\"0x000000000000000000000000000000000000000000000000000000000000001f\",\"status\":{\"InError\":{\"error\":{\"ProofGenerationError\":{\"generation_type\":\"Native\",\"source\":{\"InvalidImportedBridgeExit\":{\"source\":\"InvalidMerklePathGERToL1Root\",\"global_index\":{\"mainnet_flag\":true,\"rollup_index\":0,\"leaf_index\":7}}}}}}}}" + }, + { + "test_name": "InvalidExitNetwork", + "certificate_header": "{\"network_id\":1,\"height\":7,\"epoch_number\":null,\"certificate_index\":null,\"certificate_id\":\"0xa80cd4abb016bbb3c3058f923a88be1ad49d68277366c55554d6f13d62428a1f\",\"new_local_exit_root\":\"0x566244fbf813b6926f6895142979f61ed6706184909cb8c819cd0216202a8aa9\",\"metadata\":\"0x000000000000000000000000000000000000000000000000000000000000001f\",\"status\":{\"InError\":{\"error\":{\"ProofGenerationError\":{\"generation_type\":\"Native\",\"source\":{\"InvalidImportedBridgeExit\":{\"source\":\"InvalidExitNetwork\",\"global_index\":{\"mainnet_flag\":false,\"rollup_index\":1,\"leaf_index\":8}}}}}}}}" + }, + { + "test_name": "InvalidExitNetwork_missing_source", + "expected_error": "key source not found in map", + "certificate_header": "{\"network_id\":1,\"height\":7,\"epoch_number\":null,\"certificate_index\":null,\"certificate_id\":\"0xa80cd4abb016bbb3c3058f923a88be1ad49d68277366c55554d6f13d62428a1f\",\"new_local_exit_root\":\"0x566244fbf813b6926f6895142979f61ed6706184909cb8c819cd0216202a8aa9\",\"metadata\":\"0x000000000000000000000000000000000000000000000000000000000000001f\",\"status\":{\"InError\":{\"error\":{\"ProofGenerationError\":{\"generation_type\":\"Native\",\"source\":{\"InvalidImportedBridgeExit\":{\"global_index\":{\"mainnet_flag\":false,\"rollup_index\":1,\"leaf_index\":8}}}}}}}}" + }, + { + "test_name": "InvalidExitNetwork_missing_global_index", + "expected_error": "key global_index not found in map", + "certificate_header": "{\"network_id\":1,\"height\":7,\"epoch_number\":null,\"certificate_index\":null,\"certificate_id\":\"0xa80cd4abb016bbb3c3058f923a88be1ad49d68277366c55554d6f13d62428a1f\",\"new_local_exit_root\":\"0x566244fbf813b6926f6895142979f61ed6706184909cb8c819cd0216202a8aa9\",\"metadata\":\"0x000000000000000000000000000000000000000000000000000000000000001f\",\"status\":{\"InError\":{\"error\":{\"ProofGenerationError\":{\"generation_type\":\"Native\",\"source\":{\"InvalidImportedBridgeExit\":{\"source\":\"InvalidExitNetwork\"}}}}}}}" + }, + { + "test_name": "UnknownError", + "certificate_header": "{\"network_id\":1,\"height\":7,\"epoch_number\":null,\"certificate_index\":null,\"certificate_id\":\"0xa80cd4abb016bbb3c3058f923a88be1ad49d68277366c55554d6f13d62428a1f\",\"new_local_exit_root\":\"0x566244fbf813b6926f6895142979f61ed6706184909cb8c819cd0216202a8aa9\",\"metadata\":\"0x000000000000000000000000000000000000000000000000000000000000001f\",\"status\":{\"InError\":{\"error\":{\"ProofGenerationError\":{\"generation_type\":\"Native\",\"source\":{\"InvalidImportedBridgeExit\":{\"source\":\"UnknownError\",\"global_index\":{\"mainnet_flag\":false,\"rollup_index\":1,\"leaf_index\":8}}}}}}}}" + } +] \ No newline at end of file diff --git a/agglayer/testdata/proof_generation_errors/invalid_signer_error.json b/agglayer/testdata/proof_generation_errors/invalid_signer_error.json new file mode 100644 index 00000000..62c5578c --- /dev/null +++ b/agglayer/testdata/proof_generation_errors/invalid_signer_error.json @@ -0,0 +1,21 @@ +[ + { + "test_name": "InvalidSignerError", + "certificate_header": "{\"network_id\":1,\"height\":0,\"epoch_number\":null,\"certificate_index\":null,\"certificate_id\":\"0xa80cd4abb016bbb3c3058f923a88be1ad49d68277366c55554d6f13d62428a1f\",\"new_local_exit_root\":\"0x566244fbf813b6926f6895142979f61ed6706184909cb8c819cd0216202a8aa9\",\"metadata\":\"0x000000000000000000000000000000000000000000000000000000000000001f\",\"status\":{\"InError\":{\"error\":{\"ProofGenerationError\":{\"generation_type\":\"Native\",\"source\":{\"InvalidSigner\":{\"declared\":\"0x5b06837a43bdc3dd9f114558daf4b26ed49842ed\",\"recovered\":\"0x20e92bfbb55589f7fd0bec3666e3de469526de3e\"}}}}}}}" + }, + { + "test_name": "InvalidSignerError_missing_declared", + "expected_error": "key declared not found in map", + "certificate_header": "{\"network_id\":1,\"height\":0,\"epoch_number\":null,\"certificate_index\":null,\"certificate_id\":\"0xa80cd4abb016bbb3c3058f923a88be1ad49d68277366c55554d6f13d62428a1f\",\"new_local_exit_root\":\"0x566244fbf813b6926f6895142979f61ed6706184909cb8c819cd0216202a8aa9\",\"metadata\":\"0x000000000000000000000000000000000000000000000000000000000000001f\",\"status\":{\"InError\":{\"error\":{\"ProofGenerationError\":{\"generation_type\":\"Native\",\"source\":{\"InvalidSigner\":{\"recovered\":\"0x20e92bfbb55589f7fd0bec3666e3de469526de3e\"}}}}}}}" + }, + { + "test_name": "InvalidSignerError_missing_recovered", + "expected_error": "key recovered not found in map", + "certificate_header": "{\"network_id\":1,\"height\":0,\"epoch_number\":null,\"certificate_index\":null,\"certificate_id\":\"0xa80cd4abb016bbb3c3058f923a88be1ad49d68277366c55554d6f13d62428a1f\",\"new_local_exit_root\":\"0x566244fbf813b6926f6895142979f61ed6706184909cb8c819cd0216202a8aa9\",\"metadata\":\"0x000000000000000000000000000000000000000000000000000000000000001f\",\"status\":{\"InError\":{\"error\":{\"ProofGenerationError\":{\"generation_type\":\"Native\",\"source\":{\"InvalidSigner\":{\"declared\":\"0x5b06837a43bdc3dd9f114558daf4b26ed49842ed\"}}}}}}}" + }, + { + "test_name": "InvalidSignerError_missing_inner_error", + "expected_error": "not a map", + "certificate_header": "{\"network_id\":1,\"height\":0,\"epoch_number\":null,\"certificate_index\":null,\"certificate_id\":\"0xa80cd4abb016bbb3c3058f923a88be1ad49d68277366c55554d6f13d62428a1f\",\"new_local_exit_root\":\"0x566244fbf813b6926f6895142979f61ed6706184909cb8c819cd0216202a8aa9\",\"metadata\":\"0x000000000000000000000000000000000000000000000000000000000000001f\",\"status\":{\"InError\":{\"error\":{\"ProofGenerationError\":{\"generation_type\":\"Native\",\"source\":\"InvalidSigner\"}}}}}" + } +] \ No newline at end of file diff --git a/agglayer/testdata/proof_generation_errors/random_unmarshal_errors.json b/agglayer/testdata/proof_generation_errors/random_unmarshal_errors.json new file mode 100644 index 00000000..680370e2 --- /dev/null +++ b/agglayer/testdata/proof_generation_errors/random_unmarshal_errors.json @@ -0,0 +1,12 @@ +[ + { + "test_name": "missing_proof_generation_type", + "certificate_header": "{\"network_id\":14,\"height\":1,\"epoch_number\":null,\"certificate_index\":null,\"certificate_id\":\"0xa80cd4abb016bbb3c3058f923a88be1ad49d68277366c55554d6f13d62428a1f\",\"new_local_exit_root\":\"0x566244fbf813b6926f6895142979f61ed6706184909cb8c819cd0216202a8aa9\",\"metadata\":\"0x000000000000000000000000000000000000000000000000000000000000001f\",\"status\":{\"InError\":{\"error\":{\"ProofGenerationError\":{\"source\":{\"InvalidImportedExitsRoot\":{\"declared\":\"0x1116837a43bdc3dd9f114558daf4b26ed4eeeeec\",\"computed\":\"0x20222bfbb55589f7fd0bec3666e3de469111ce3c\"}}}}}}}", + "expected_error": "key generation_type not found in map" + }, + { + "test_name": "missing_source", + "certificate_header": "{\"network_id\":14,\"height\":1,\"epoch_number\":null,\"certificate_index\":null,\"certificate_id\":\"0xa80cd4abb016bbb3c3058f923a88be1ad49d68277366c55554d6f13d62428a1f\",\"new_local_exit_root\":\"0x566244fbf813b6926f6895142979f61ed6706184909cb8c819cd0216202a8aa9\",\"metadata\":\"0x000000000000000000000000000000000000000000000000000000000000001f\",\"status\":{\"InError\":{\"error\":{\"ProofGenerationError\":{\"generation_type\":\"Native\",\"unknown\":{\"InvalidImportedExitsRoot\":{\"declared\":\"0x1116837a43bdc3dd9f114558daf4b26ed4eeeeec\",\"computed\":\"0x20222bfbb55589f7fd0bec3666e3de469111ce3c\"}}}}}}}", + "expected_error": "key source not found in map" + } +] \ No newline at end of file diff --git a/agglayer/testdata/proof_verification_errors/errors_with_inner_data.json b/agglayer/testdata/proof_verification_errors/errors_with_inner_data.json new file mode 100644 index 00000000..2060d2ee --- /dev/null +++ b/agglayer/testdata/proof_verification_errors/errors_with_inner_data.json @@ -0,0 +1,22 @@ +[ + { + "test_name": "VersionMismatch", + "certificate_header": "{\"network_id\":1,\"height\":4,\"epoch_number\":null,\"certificate_index\":null,\"certificate_id\":\"0xa80cd4abb016bbb3c3058f923a88be1ad49d68277366c55554d6f13d62428a1f\",\"new_local_exit_root\":\"0x566244fbf813b6926f6895142979f61ed6706184909cb8c819cd0216202a8aa9\",\"metadata\":\"0x000000000000000000000000000000000000000000000000000000000000001f\",\"status\":{\"InError\":{\"error\":{\"ProofVerificationError\":{\"VersionMismatch\":\"version1-1\"}}}}}" + }, + { + "test_name": "Core", + "certificate_header": "{\"network_id\":1,\"height\":4,\"epoch_number\":null,\"certificate_index\":null,\"certificate_id\":\"0xa80cd4abb016bbb3c3058f923a88be1ad49d68277366c55554d6f13d62428a1f\",\"new_local_exit_root\":\"0x566244fbf813b6926f6895142979f61ed6706184909cb8c819cd0216202a8aa9\",\"metadata\":\"0x000000000000000000000000000000000000000000000000000000000000001f\",\"status\":{\"InError\":{\"error\":{\"ProofVerificationError\":{\"Core\":\"coreexample\"}}}}}" + }, + { + "test_name": "Recursion", + "certificate_header": "{\"network_id\":1,\"height\":4,\"epoch_number\":null,\"certificate_index\":null,\"certificate_id\":\"0xa80cd4abb016bbb3c3058f923a88be1ad49d68277366c55554d6f13d62428a1f\",\"new_local_exit_root\":\"0x566244fbf813b6926f6895142979f61ed6706184909cb8c819cd0216202a8aa9\",\"metadata\":\"0x000000000000000000000000000000000000000000000000000000000000001f\",\"status\":{\"InError\":{\"error\":{\"ProofVerificationError\":{\"Recursion\":\"recursion error\"}}}}}" + }, + { + "test_name": "Plank", + "certificate_header": "{\"network_id\":1,\"height\":4,\"epoch_number\":null,\"certificate_index\":null,\"certificate_id\":\"0xa80cd4abb016bbb3c3058f923a88be1ad49d68277366c55554d6f13d62428a1f\",\"new_local_exit_root\":\"0x566244fbf813b6926f6895142979f61ed6706184909cb8c819cd0216202a8aa9\",\"metadata\":\"0x000000000000000000000000000000000000000000000000000000000000001f\",\"status\":{\"InError\":{\"error\":{\"ProofVerificationError\":{\"Plank\":\"plank error\"}}}}}" + }, + { + "test_name": "Groth16", + "certificate_header": "{\"network_id\":1,\"height\":4,\"epoch_number\":null,\"certificate_index\":null,\"certificate_id\":\"0xa80cd4abb016bbb3c3058f923a88be1ad49d68277366c55554d6f13d62428a1f\",\"new_local_exit_root\":\"0x566244fbf813b6926f6895142979f61ed6706184909cb8c819cd0216202a8aa9\",\"metadata\":\"0x000000000000000000000000000000000000000000000000000000000000001f\",\"status\":{\"InError\":{\"error\":{\"ProofVerificationError\":{\"Groth16\":\"Groth16 error\"}}}}}" + } +] \ No newline at end of file diff --git a/agglayer/testdata/proof_verification_errors/errors_without_inner_data.json b/agglayer/testdata/proof_verification_errors/errors_without_inner_data.json new file mode 100644 index 00000000..458b07c0 --- /dev/null +++ b/agglayer/testdata/proof_verification_errors/errors_without_inner_data.json @@ -0,0 +1,6 @@ +[ + { + "test_name": "InvalidPublicValues", + "certificate_header": "{\"network_id\":1,\"height\":4,\"epoch_number\":null,\"certificate_index\":null,\"certificate_id\":\"0xa80cd4abb016bbb3c3058f923a88be1ad49d68277366c55554d6f13d62428a1f\",\"new_local_exit_root\":\"0x566244fbf813b6926f6895142979f61ed6706184909cb8c819cd0216202a8aa9\",\"metadata\":\"0x000000000000000000000000000000000000000000000000000000000000001f\",\"status\":{\"InError\":{\"error\":{\"ProofVerificationError\":\"InvalidPublicValues\"}}}}" + } +] \ No newline at end of file diff --git a/agglayer/testdata/type_conversion_errors/errors_with_declared_computed_data.json b/agglayer/testdata/type_conversion_errors/errors_with_declared_computed_data.json new file mode 100644 index 00000000..348ffa5f --- /dev/null +++ b/agglayer/testdata/type_conversion_errors/errors_with_declared_computed_data.json @@ -0,0 +1,6 @@ +[ + { + "test_name": "MultipleL1InfoRoot", + "certificate_header": "{\"network_id\":1,\"height\":4,\"epoch_number\":null,\"certificate_index\":null,\"certificate_id\":\"0xa80cd4abb016bbb3c3058f923a88be1ad49d68277366c55554d6f13d62428a1f\",\"new_local_exit_root\":\"0x566244fbf813b6926f6895142979f61ed6706184909cb8c819cd0216202a8aa9\",\"metadata\":\"0x000000000000000000000000000000000000000000000000000000000000001f\",\"status\":{\"InError\":{\"error\":{\"TypeConversionError\":{\"MismatchNewLocalExitRoot\":{\"declared\":\"0xa80cd4abb016bbb3c3058f923a88be1ad49d68277366c55554d6f13d62428a1f\",\"computed\":\"0x5b06837a43bdc3dd9f114558daf4b26ed49842ee\"}}}}}}" + } +] \ No newline at end of file diff --git a/agglayer/testdata/type_conversion_errors/errors_with_token_info.json b/agglayer/testdata/type_conversion_errors/errors_with_token_info.json new file mode 100644 index 00000000..06d739a9 --- /dev/null +++ b/agglayer/testdata/type_conversion_errors/errors_with_token_info.json @@ -0,0 +1,26 @@ +[ + { + "test_name": "MultipleL1InfoRoot", + "certificate_header": "{\"network_id\":1,\"height\":0,\"epoch_number\":null,\"certificate_index\":null,\"certificate_id\":\"0xa80cd4abb016bbb3c3058f923a88be1ad49d68277366c55554d6f13d62428a1f\",\"new_local_exit_root\":\"0x566244fbf813b6926f6895142979f61ed6706184909cb8c819cd0216202a8aa9\",\"metadata\":\"0x000000000000000000000000000000000000000000000000000000000000001f\",\"status\":{\"InError\":{\"error\":{\"TypeConversionError\":{\"MultipleL1InfoRoot\":{\"origin_network\":1,\"origin_token_address\":\"0x566244fbf813b6926f6895142979f61ed6706184909cb8c819cd0216202a8aa2\"}}}}}}" + }, + { + "test_name": "MismatchNewLocalExitRoot", + "certificate_header": "{\"network_id\":1,\"height\":1,\"epoch_number\":null,\"certificate_index\":null,\"certificate_id\":\"0xa80cd4abb016bbb3c3058f923a88be1ad49d68277366c55554d6f13d62428a1f\",\"new_local_exit_root\":\"0x566244fbf813b6926f6895142979f61ed6706184909cb8c819cd0216202a8aa9\",\"metadata\":\"0x000000000000000000000000000000000000000000000000000000000000001f\",\"status\":{\"InError\":{\"error\":{\"TypeConversionError\":{\"MismatchNewLocalExitRoot\":{\"origin_network\":1,\"origin_token_address\":\"0x566244fbf813b6926f6895142979f61ed6706184909cb8c819cd0216202a8aa2\"}}}}}}" + }, + { + "test_name": "BalanceOverflow", + "certificate_header": "{\"network_id\":1,\"height\":2,\"epoch_number\":null,\"certificate_index\":null,\"certificate_id\":\"0xa80cd4abb016bbb3c3058f923a88be1ad49d68277366c55554d6f13d62428a1f\",\"new_local_exit_root\":\"0x566244fbf813b6926f6895142979f61ed6706184909cb8c819cd0216202a8aa9\",\"metadata\":\"0x000000000000000000000000000000000000000000000000000000000000001f\",\"status\":{\"InError\":{\"error\":{\"TypeConversionError\":{\"BalanceOverflow\":{\"origin_network\":1,\"origin_token_address\":\"0x566244fbf813b6926f6895142979f61ed6706184909cb8c819cd0216202a8aa2\"}}}}}}" + }, + { + "test_name": "BalanceUnderflow", + "certificate_header": "{\"network_id\":1,\"height\":3,\"epoch_number\":null,\"certificate_index\":null,\"certificate_id\":\"0xa80cd4abb016bbb3c3058f923a88be1ad49d68277366c55554d6f13d62428a1f\",\"new_local_exit_root\":\"0x566244fbf813b6926f6895142979f61ed6706184909cb8c819cd0216202a8aa9\",\"metadata\":\"0x000000000000000000000000000000000000000000000000000000000000001f\",\"status\":{\"InError\":{\"error\":{\"TypeConversionError\":{\"BalanceUnderflow\":{\"origin_network\":1,\"origin_token_address\":\"0x566244fbf813b6926f6895142979f61ed6706184909cb8c819cd0216202a8aa2\"}}}}}}" + }, + { + "test_name": "BalanceProofGenerationFailed - KeyAlreadyPresent", + "certificate_header": "{\"network_id\":1,\"height\":4,\"epoch_number\":null,\"certificate_index\":null,\"certificate_id\":\"0xa80cd4abb016bbb3c3058f923a88be1ad49d68277366c55554d6f13d62428a1f\",\"new_local_exit_root\":\"0x566244fbf813b6926f6895142979f61ed6706184909cb8c819cd0216202a8aa9\",\"metadata\":\"0x000000000000000000000000000000000000000000000000000000000000001f\",\"status\":{\"InError\":{\"error\":{\"TypeConversionError\":{\"BalanceProofGenerationFailed\":{\"source\":\"KeyAlreadyPresent\",\"token\":{\"origin_network\":1,\"origin_token_address\":\"0x566244fbf813b6926f6895142979f61ed6706184909cb8c819cd0216202a8aa2\"}}}}}}}" + }, + { + "test_name": "BalanceProofGenerationFailed - KeyNotPresent", + "certificate_header": "{\"network_id\":1,\"height\":5,\"epoch_number\":null,\"certificate_index\":null,\"certificate_id\":\"0xa80cd4abb016bbb3c3058f923a88be1ad49d68277366c55554d6f13d62428a1f\",\"new_local_exit_root\":\"0x566244fbf813b6926f6895142979f61ed6706184909cb8c819cd0216202a8aa9\",\"metadata\":\"0x000000000000000000000000000000000000000000000000000000000000001f\",\"status\":{\"InError\":{\"error\":{\"TypeConversionError\":{\"BalanceProofGenerationFailed\":{\"source\":\"KeyNotPresent\",\"token\":{\"origin_network\":11,\"origin_token_address\":\"0x566244fbf813b6926f6895142979f61ed6706184909cb8c819cd0216202a8aa2\"}}}}}}}" + } +] \ No newline at end of file diff --git a/agglayer/testdata/type_conversion_errors/errors_without_inner_data.json b/agglayer/testdata/type_conversion_errors/errors_without_inner_data.json new file mode 100644 index 00000000..a92aca80 --- /dev/null +++ b/agglayer/testdata/type_conversion_errors/errors_without_inner_data.json @@ -0,0 +1,6 @@ +[ + { + "test_name": "MultipleL1InfoRoot", + "certificate_header": "{\"network_id\":1,\"height\":0,\"epoch_number\":null,\"certificate_index\":null,\"certificate_id\":\"0xa80cd4abb016bbb3c3058f923a88be1ad49d68277366c55554d6f13d62428a1f\",\"new_local_exit_root\":\"0x566244fbf813b6926f6895142979f61ed6706184909cb8c819cd0216202a8aa9\",\"metadata\":\"0x000000000000000000000000000000000000000000000000000000000000001f\",\"status\":{\"InError\":{\"error\":{\"TypeConversionError\":\"MultipleL1InfoRoot\"}}}}" + } +] \ No newline at end of file diff --git a/agglayer/testdata/type_conversion_errors/nullifier_path_generation_failed_error.json b/agglayer/testdata/type_conversion_errors/nullifier_path_generation_failed_error.json new file mode 100644 index 00000000..b52cd73f --- /dev/null +++ b/agglayer/testdata/type_conversion_errors/nullifier_path_generation_failed_error.json @@ -0,0 +1,20 @@ +[ + { + "test_name": "NullifierPathGenerationFailed - KeyPresent", + "certificate_header": "{\"network_id\":1,\"height\":6,\"epoch_number\":null,\"certificate_index\":null,\"certificate_id\":\"0xa80cd4abb016bbb3c3058f923a88be1ad49d68277366c55554d6f13d62428a1f\",\"new_local_exit_root\":\"0x566244fbf813b6926f6895142979f61ed6706184909cb8c819cd0216202a8aa9\",\"metadata\":\"0x000000000000000000000000000000000000000000000000000000000000001f\",\"status\":{\"InError\":{\"error\":{\"TypeConversionError\":{\"NullifierPathGenerationFailed\":{\"source\":\"KeyPresent\",\"global_index\":{\"mainnet_flag\":true,\"rollup_index\":0,\"leaf_index\":1}}}}}}}" + }, + { + "test_name": "NullifierPathGenerationFailed - DepthOutOfBounds", + "certificate_header": "{\"network_id\":1,\"height\":7,\"epoch_number\":null,\"certificate_index\":null,\"certificate_id\":\"0xa80cd4abb016bbb3c3058f923a88be1ad49d68277366c55554d6f13d62428a1f\",\"new_local_exit_root\":\"0x566244fbf813b6926f6895142979f61ed6706184909cb8c819cd0216202a8aa9\",\"metadata\":\"0x000000000000000000000000000000000000000000000000000000000000001f\",\"status\":{\"InError\":{\"error\":{\"TypeConversionError\":{\"NullifierPathGenerationFailed\":{\"source\":\"DepthOutOfBounds\",\"global_index\":{\"mainnet_flag\":false,\"rollup_index\":11,\"leaf_index\":123}}}}}}}" + }, + { + "test_name": "NullifierPathGenerationFailed_unknown_SMT_error_code", + "expected_error": "unknown SMT error code", + "certificate_header": "{\"network_id\":1,\"height\":7,\"epoch_number\":null,\"certificate_index\":null,\"certificate_id\":\"0xa80cd4abb016bbb3c3058f923a88be1ad49d68277366c55554d6f13d62428a1f\",\"new_local_exit_root\":\"0x566244fbf813b6926f6895142979f61ed6706184909cb8c819cd0216202a8aa9\",\"metadata\":\"0x000000000000000000000000000000000000000000000000000000000000001f\",\"status\":{\"InError\":{\"error\":{\"TypeConversionError\":{\"NullifierPathGenerationFailed\":{\"source\":\"UnknownCode\",\"global_index\":{\"mainnet_flag\":false,\"rollup_index\":11,\"leaf_index\":123}}}}}}}" + }, + { + "test_name": "NullifierPathGenerationFailed_missing_SMT_source", + "expected_error": "error code is not a string", + "certificate_header": "{\"network_id\":1,\"height\":7,\"epoch_number\":null,\"certificate_index\":null,\"certificate_id\":\"0xa80cd4abb016bbb3c3058f923a88be1ad49d68277366c55554d6f13d62428a1f\",\"new_local_exit_root\":\"0x566244fbf813b6926f6895142979f61ed6706184909cb8c819cd0216202a8aa9\",\"metadata\":\"0x000000000000000000000000000000000000000000000000000000000000001f\",\"status\":{\"InError\":{\"error\":{\"TypeConversionError\":{\"NullifierPathGenerationFailed\":{\"unknown\":\"DepthOutOfBounds\",\"global_index\":{\"mainnet_flag\":false,\"rollup_index\":11,\"leaf_index\":123}}}}}}}" + } +] \ No newline at end of file diff --git a/agglayer/type_conversion_error.go b/agglayer/type_conversion_error.go new file mode 100644 index 00000000..89129253 --- /dev/null +++ b/agglayer/type_conversion_error.go @@ -0,0 +1,255 @@ +package agglayer + +import ( + "errors" + "fmt" +) + +const ( + MultipleL1InfoRootErrorType = "MultipleL1InfoRoot" + MismatchNewLocalExitRootErrorType = "MismatchNewLocalExitRoot" + BalanceOverflowErrorType = "BalanceOverflow" + BalanceUnderflowErrorType = "BalanceUnderflow" + BalanceProofGenerationFailedErrorType = "BalanceProofGenerationFailed" + NullifierPathGenerationFailedErrorType = "NullifierPathGenerationFailed" +) + +// TypeConversionError is an error that is returned when verifying a certficate +// before generating its proof. +type TypeConversionError struct { + InnerErrors []PPError +} + +// String is the implementation of the Error interface +func (p *TypeConversionError) String() string { + return fmt.Sprintf("Type conversion error: %v", p.InnerErrors) +} + +// Unmarshal unmarshals the data from a map into a ProofGenerationError struct. +func (p *TypeConversionError) Unmarshal(data interface{}) error { + getPPErrFn := func(key string, value interface{}) (PPError, error) { + switch key { + case MultipleL1InfoRootErrorType: + p.InnerErrors = append(p.InnerErrors, &MultipleL1InfoRoot{}) + case MismatchNewLocalExitRootErrorType: + p.InnerErrors = append(p.InnerErrors, NewMismatchNewLocalExitRoot()) + case BalanceOverflowErrorType: + balanceOverflow := NewBalanceOverflow() + if err := balanceOverflow.UnmarshalFromMap(value); err != nil { + return nil, err + } + return balanceOverflow, nil + case BalanceUnderflowErrorType: + balanceUnderflow := NewBalanceUnderflow() + if err := balanceUnderflow.UnmarshalFromMap(value); err != nil { + return nil, err + } + return balanceUnderflow, nil + case BalanceProofGenerationFailedErrorType: + balanceProofGenerationFailed := NewBalanceProofGenerationFailed() + if err := balanceProofGenerationFailed.UnmarshalFromMap(value); err != nil { + return nil, err + } + return balanceProofGenerationFailed, nil + case NullifierPathGenerationFailedErrorType: + nullifierPathGenerationFailed := NewNullifierPathGenerationFailed() + if err := nullifierPathGenerationFailed.UnmarshalFromMap(value); err != nil { + return nil, err + } + return nullifierPathGenerationFailed, nil + default: + return nil, fmt.Errorf("unknown type conversion error type: %v", key) + } + + return nil, nil + } + + getAndAddInnerErrorFn := func(key string, value interface{}) error { + ppErr, err := getPPErrFn(key, value) + if err != nil { + return err + } + + if ppErr != nil { + p.InnerErrors = append(p.InnerErrors, ppErr) + } + + return nil + } + + errorSourceMap, ok := data.(map[string]interface{}) + if !ok { + // it can be a single error + return getAndAddInnerErrorFn(data.(string), nil) //nolint:forcetypeassert + } + + for key, value := range errorSourceMap { + if err := getAndAddInnerErrorFn(key, value); err != nil { + return err + } + } + + return nil +} + +// MultipleL1InfoRoot is an error that is returned when the imported bridge exits +// refer to different L1 info roots. +type MultipleL1InfoRoot struct{} + +// String is the implementation of the Error interface +func (e *MultipleL1InfoRoot) String() string { + return fmt.Sprintf(`%s: The imported bridge exits should refer to one and the same L1 info root.`, + MultipleL1InfoRootErrorType) +} + +// MissingNewLocalExitRoot is an error that is returned when the certificate refers to +// a new local exit root which differ from the one computed by the agglayer. +type MismatchNewLocalExitRoot struct { + *DeclaredComputedError +} + +func NewMismatchNewLocalExitRoot() *MismatchNewLocalExitRoot { + return &MismatchNewLocalExitRoot{ + DeclaredComputedError: &DeclaredComputedError{ErrType: MismatchNewLocalExitRootErrorType}, + } +} + +// BalanceOverflow is an error that is returned when the given token balance cannot overflow. +type BalanceOverflow struct { + *TokenInfoError +} + +// NewBalanceOverflow returns a new BalanceOverflow error. +func NewBalanceOverflow() *BalanceOverflow { + return &BalanceOverflow{ + TokenInfoError: &TokenInfoError{}, + } +} + +// String is the implementation of the Error interface +func (e *BalanceOverflow) String() string { + return fmt.Sprintf("%s: The given token balance cannot overflow. %s", + BalanceOverflowErrorType, e.TokenInfo.String()) +} + +// BalanceUnderflow is an error that is returned when the given token balance cannot be negative. +type BalanceUnderflow struct { + *TokenInfoError +} + +// NewBalanceOverflow returns a new BalanceOverflow error. +func NewBalanceUnderflow() *BalanceUnderflow { + return &BalanceUnderflow{ + TokenInfoError: &TokenInfoError{}, + } +} + +// String is the implementation of the Error interface +func (e *BalanceUnderflow) String() string { + return fmt.Sprintf("%s: The given token balance cannot be negative. %s", + BalanceUnderflowErrorType, e.TokenInfo.String()) +} + +// SmtError is a type that is inherited by all errors that occur during SMT operations. +type SmtError struct { + ErrorCode string + Error string +} + +func (e *SmtError) Unmarshal(data interface{}) error { + errCode, ok := data.(string) + if !ok { + return errors.New("error code is not a string") + } + + e.ErrorCode = errCode + + switch errCode { + case "KeyAlreadyPresent": + e.Error = "trying to insert a key already in the SMT" + case "KeyNotPresent": + e.Error = "trying to generate a Merkle proof for a key not in the SMT" + case "KeyPresent": + e.Error = "trying to generate a non-inclusion proof for a key present in the SMT" + case "DepthOutOfBounds": + e.Error = "depth out of bounds" + default: + return fmt.Errorf("unknown SMT error code: %s", errCode) + } + + return nil +} + +// BalanceProofGenerationFailed is a struct that represents an error that occurs when +// the balance proof for the given token cannot be generated. +type BalanceProofGenerationFailed struct { + *TokenInfoError + *SmtError +} + +func NewBalanceProofGenerationFailed() *BalanceProofGenerationFailed { + return &BalanceProofGenerationFailed{ + TokenInfoError: &TokenInfoError{}, + SmtError: &SmtError{}, + } +} + +// String is the implementation of the Error interface +func (e *BalanceProofGenerationFailed) String() string { + return fmt.Sprintf("%s: The balance proof for the given token cannot be generated. TokenInfo: %s. Error type: %s. %s", + BalanceProofGenerationFailedErrorType, e.TokenInfo.String(), + e.SmtError.ErrorCode, e.SmtError.Error) +} + +func (e *BalanceProofGenerationFailed) UnmarshalFromMap(data interface{}) error { + dataMap, ok := data.(map[string]interface{}) + if !ok { + return errNotMap + } + + if err := e.TokenInfoError.UnmarshalFromMap(dataMap["token"]); err != nil { + return err + } + + return e.SmtError.Unmarshal(dataMap["source"]) +} + +// NullifierPathGenerationFailed is a struct that represents an error that occurs when +// the nullifier path for the given imported bridge exit cannot be generated.. +type NullifierPathGenerationFailed struct { + GlobalIndex *GlobalIndex `json:"global_index"` + *SmtError +} + +func NewNullifierPathGenerationFailed() *NullifierPathGenerationFailed { + return &NullifierPathGenerationFailed{ + SmtError: &SmtError{}, + } +} + +// String is the implementation of the Error interface +func (e *NullifierPathGenerationFailed) String() string { + return fmt.Sprintf("%s: The nullifier path for the given imported bridge exit cannot be generated. "+ + "GlobalIndex: %s. Error type: %s. %s", + NullifierPathGenerationFailedErrorType, e.GlobalIndex.String(), + e.SmtError.ErrorCode, e.SmtError.Error) +} + +func (e *NullifierPathGenerationFailed) UnmarshalFromMap(data interface{}) error { + dataMap, ok := data.(map[string]interface{}) + if !ok { + return errNotMap + } + + if err := e.SmtError.Unmarshal(dataMap["source"]); err != nil { + return err + } + + globalIndexMap, err := convertMapValue[map[string]interface{}](dataMap, "global_index") + if err != nil { + return err + } + + e.GlobalIndex = &GlobalIndex{} + return e.GlobalIndex.UnmarshalFromMap(globalIndexMap) +} diff --git a/agglayer/types.go b/agglayer/types.go index 9350e791..b6a3198e 100644 --- a/agglayer/types.go +++ b/agglayer/types.go @@ -2,6 +2,7 @@ package agglayer import ( "encoding/json" + "errors" "fmt" "math/big" "strings" @@ -36,10 +37,7 @@ func (c *CertificateStatus) UnmarshalJSON(data []byte) error { if strings.Contains(dataStr, "InError") { status = "InError" } else { - err := json.Unmarshal(data, &status) - if err != nil { - return err - } + status = string(data) } switch status { @@ -199,6 +197,7 @@ type TokenInfo struct { OriginTokenAddress common.Address `json:"origin_token_address"` } +// String returns a string representation of the TokenInfo struct func (t *TokenInfo) String() string { return fmt.Sprintf("OriginNetwork: %d, OriginTokenAddress: %s", t.OriginNetwork, t.OriginTokenAddress.String()) } @@ -210,6 +209,11 @@ type GlobalIndex struct { LeafIndex uint32 `json:"leaf_index"` } +// String returns a string representation of the GlobalIndex struct +func (g *GlobalIndex) String() string { + return fmt.Sprintf("MainnetFlag: %t, RollupIndex: %d, LeafIndex: %d", g.MainnetFlag, g.RollupIndex, g.LeafIndex) +} + func (g *GlobalIndex) Hash() common.Hash { return crypto.Keccak256Hash( cdkcommon.BigIntToLittleEndianBytes( @@ -218,9 +222,27 @@ func (g *GlobalIndex) Hash() common.Hash { ) } -func (g *GlobalIndex) String() string { - return fmt.Sprintf("MainnetFlag: %t, RollupIndex: %d, LeafIndex: %d", - g.MainnetFlag, g.RollupIndex, g.LeafIndex) +func (g *GlobalIndex) UnmarshalFromMap(data map[string]interface{}) error { + rollupIndex, err := convertMapValue[uint32](data, "rollup_index") + if err != nil { + return err + } + + leafIndex, err := convertMapValue[uint32](data, "leaf_index") + if err != nil { + return err + } + + mainnetFlag, err := convertMapValue[bool](data, "mainnet_flag") + if err != nil { + return err + } + + g.RollupIndex = rollupIndex + g.LeafIndex = leafIndex + g.MainnetFlag = mainnetFlag + + return nil } // BridgeExit represents a token bridge exit @@ -525,9 +547,96 @@ type CertificateHeader struct { NewLocalExitRoot common.Hash `json:"new_local_exit_root"` Status CertificateStatus `json:"status"` Metadata common.Hash `json:"metadata"` + Error PPError `json:"-"` } func (c CertificateHeader) String() string { - return fmt.Sprintf("Height: %d, CertificateID: %s, NewLocalExitRoot: %s", - c.Height, c.CertificateID.String(), c.NewLocalExitRoot.String()) + errors := "" + if c.Error != nil { + errors = c.Error.String() + } + + return fmt.Sprintf("Height: %d, CertificateID: %s, NewLocalExitRoot: %s. Status: %s. Errors: %s", + c.Height, c.CertificateID.String(), c.NewLocalExitRoot.String(), c.Status.String(), errors) +} + +func (c *CertificateHeader) UnmarshalJSON(data []byte) error { + // we define an alias to avoid infinite recursion + type Alias CertificateHeader + aux := &struct { + Status interface{} `json:"status"` + *Alias + }{ + Alias: (*Alias)(c), + } + + if err := json.Unmarshal(data, &aux); err != nil { + return err + } + + // Process Status field + switch status := aux.Status.(type) { + case string: // certificate not InError + if err := c.Status.UnmarshalJSON([]byte(status)); err != nil { + return err + } + case map[string]interface{}: // certificate has errors + inErrMap, err := convertMapValue[map[string]interface{}](status, "InError") + if err != nil { + return err + } + + inErrDataMap, err := convertMapValue[map[string]interface{}](inErrMap, "error") + if err != nil { + return err + } + + var ppError PPError + + for key, value := range inErrDataMap { + switch key { + case "ProofGenerationError": + p := &ProofGenerationError{} + if err := p.Unmarshal(value); err != nil { + return err + } + + ppError = p + case "TypeConversionError": + t := &TypeConversionError{} + if err := t.Unmarshal(value); err != nil { + return err + } + + ppError = t + case "ProofVerificationError": + p := &ProofVerificationError{} + if err := p.Unmarshal(value); err != nil { + return err + } + + ppError = p + default: + return fmt.Errorf("invalid error type: %s", key) + } + } + + c.Status = InError + c.Error = ppError + default: + return errors.New("invalid status type") + } + + return nil +} + +// ClockConfiguration represents the configuration of the epoch clock +// returned by the interop_GetEpochConfiguration RPC call +type ClockConfiguration struct { + EpochDuration uint64 `json:"epoch_duration"` + GenesisBlock uint64 `json:"genesis_block"` +} + +func (c ClockConfiguration) String() string { + return fmt.Sprintf("EpochDuration: %d, GenesisBlock: %d", c.EpochDuration, c.GenesisBlock) } diff --git a/agglayer/types_test.go b/agglayer/types_test.go index 95033141..f2133923 100644 --- a/agglayer/types_test.go +++ b/agglayer/types_test.go @@ -152,3 +152,102 @@ func TestSignedCertificate_Copy(t *testing.T) { require.Empty(t, certificateCopy.ImportedBridgeExits) }) } + +func TestGlobalIndex_UnmarshalFromMap(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + data map[string]interface{} + want *GlobalIndex + wantErr bool + }{ + { + name: "valid data", + data: map[string]interface{}{ + "rollup_index": uint32(0), + "leaf_index": uint32(2), + "mainnet_flag": true, + }, + want: &GlobalIndex{ + RollupIndex: 0, + LeafIndex: 2, + MainnetFlag: true, + }, + wantErr: false, + }, + { + name: "missing rollup_index", + data: map[string]interface{}{ + "leaf_index": uint32(2), + "mainnet_flag": true, + }, + want: &GlobalIndex{}, + wantErr: true, + }, + { + name: "invalid rollup_index type", + data: map[string]interface{}{ + "rollup_index": "invalid", + "leaf_index": uint32(2), + "mainnet_flag": true, + }, + want: &GlobalIndex{}, + wantErr: true, + }, + { + name: "missing leaf_index", + data: map[string]interface{}{ + "rollup_index": uint32(1), + "mainnet_flag": true, + }, + want: &GlobalIndex{}, + wantErr: true, + }, + { + name: "invalid leaf_index type", + data: map[string]interface{}{ + "rollup_index": uint32(1), + "leaf_index": "invalid", + "mainnet_flag": true, + }, + want: &GlobalIndex{}, + wantErr: true, + }, + { + name: "missing mainnet_flag", + data: map[string]interface{}{ + "rollup_index": uint32(1), + "leaf_index": uint32(2), + }, + want: &GlobalIndex{}, + wantErr: true, + }, + { + name: "invalid mainnet_flag type", + data: map[string]interface{}{ + "rollup_index": uint32(1), + "leaf_index": uint32(2), + "mainnet_flag": "invalid", + }, + want: &GlobalIndex{}, + wantErr: true, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + g := &GlobalIndex{} + err := g.UnmarshalFromMap(tt.data) + if tt.wantErr { + require.Error(t, err) + } else { + require.NoError(t, err) + require.Equal(t, tt.want, g) + } + }) + } +} diff --git a/aggsender/aggsender.go b/aggsender/aggsender.go index e3242bdf..dcbbc268 100644 --- a/aggsender/aggsender.go +++ b/aggsender/aggsender.go @@ -8,11 +8,12 @@ import ( "fmt" "math/big" "os" + "slices" "time" "github.com/0xPolygon/cdk/agglayer" "github.com/0xPolygon/cdk/aggsender/db" - aggsendertypes "github.com/0xPolygon/cdk/aggsender/types" + "github.com/0xPolygon/cdk/aggsender/types" "github.com/0xPolygon/cdk/bridgesync" cdkcommon "github.com/0xPolygon/cdk/common" "github.com/0xPolygon/cdk/l1infotreesync" @@ -33,10 +34,11 @@ var ( // AggSender is a component that will send certificates to the aggLayer type AggSender struct { - log aggsendertypes.Logger + log types.Logger - l2Syncer aggsendertypes.L2BridgeSyncer - l1infoTreeSyncer aggsendertypes.L1InfoTreeSyncer + l2Syncer types.L2BridgeSyncer + l1infoTreeSyncer types.L1InfoTreeSyncer + epochNotifier types.EpochNotifier storage db.AggSenderStorage aggLayerClient agglayer.AgglayerClientInterface @@ -53,7 +55,8 @@ func New( cfg Config, aggLayerClient agglayer.AgglayerClientInterface, l1InfoTreeSyncer *l1infotreesync.L1InfoTreeSync, - l2Syncer *bridgesync.BridgeSync) (*AggSender, error) { + l2Syncer *bridgesync.BridgeSync, + epochNotifier types.EpochNotifier) (*AggSender, error) { storage, err := db.NewAggSenderSQLStorage(logger, cfg.StoragePath) if err != nil { return nil, err @@ -74,24 +77,30 @@ func New( aggLayerClient: aggLayerClient, l1infoTreeSyncer: l1InfoTreeSyncer, sequencerKey: sequencerPrivateKey, + epochNotifier: epochNotifier, }, nil } // Start starts the AggSender func (a *AggSender) Start(ctx context.Context) { - go a.sendCertificates(ctx) - go a.checkIfCertificatesAreSettled(ctx) + a.sendCertificates(ctx) } // sendCertificates sends certificates to the aggLayer func (a *AggSender) sendCertificates(ctx context.Context) { - ticker := time.NewTicker(a.cfg.BlockGetInterval.Duration) - + chEpoch := a.epochNotifier.Subscribe("aggsender") for { select { - case <-ticker.C: - if _, err := a.sendCertificate(ctx); err != nil { - log.Error(err) + case epoch := <-chEpoch: + a.log.Infof("Epoch received: %s", epoch.String()) + thereArePendingCerts, err := a.checkPendingCertificatesStatus(ctx) + if err == nil && !thereArePendingCerts { + if _, err := a.sendCertificate(ctx); err != nil { + log.Error(err) + } + } else { + log.Warnf("Skipping epoch %s because there are pending certificates %v or error: %w", + epoch.String(), thereArePendingCerts, err) } case <-ctx.Done(): a.log.Info("AggSender stopped") @@ -183,7 +192,7 @@ func (a *AggSender) sendCertificate(ctx context.Context) (*agglayer.SignedCertif } createdTime := time.Now().UTC().UnixMilli() - certInfo := aggsendertypes.CertificateInfo{ + certInfo := types.CertificateInfo{ Height: certificate.Height, CertificateID: certificateHash, NewLocalExitRoot: certificate.NewLocalExitRoot, @@ -224,7 +233,7 @@ func (a *AggSender) saveCertificateToFile(signedCertificate *agglayer.SignedCert // getNextHeightAndPreviousLER returns the height and previous LER for the new certificate func (a *AggSender) getNextHeightAndPreviousLER( - lastSentCertificateInfo *aggsendertypes.CertificateInfo) (uint64, common.Hash) { + lastSentCertificateInfo *types.CertificateInfo) (uint64, common.Hash) { height := lastSentCertificateInfo.Height + 1 if lastSentCertificateInfo.Status == agglayer.InError { // previous certificate was in error, so we need to resend it @@ -247,7 +256,7 @@ func (a *AggSender) getNextHeightAndPreviousLER( func (a *AggSender) buildCertificate(ctx context.Context, bridges []bridgesync.Bridge, claims []bridgesync.Claim, - lastSentCertificateInfo aggsendertypes.CertificateInfo, + lastSentCertificateInfo types.CertificateInfo, toBlock uint64) (*agglayer.Certificate, error) { if len(bridges) == 0 && len(claims) == 0 { return nil, errNoBridgesAndClaims @@ -475,34 +484,30 @@ func (a *AggSender) signCertificate(certificate *agglayer.Certificate) (*agglaye }, nil } -// checkIfCertificatesAreSettled checks if certificates are settled -func (a *AggSender) checkIfCertificatesAreSettled(ctx context.Context) { - ticker := time.NewTicker(a.cfg.CheckSettledInterval.Duration) - for { - select { - case <-ticker.C: - a.checkPendingCertificatesStatus(ctx) - case <-ctx.Done(): - return - } - } -} - // checkPendingCertificatesStatus checks the status of pending certificates // and updates in the storage if it changed on agglayer -func (a *AggSender) checkPendingCertificatesStatus(ctx context.Context) { +// It returns: +// bool -> if there are pending certificates +// error -> if there was an error +func (a *AggSender) checkPendingCertificatesStatus(ctx context.Context) (bool, error) { pendingCertificates, err := a.storage.GetCertificatesByStatus(nonSettledStatuses) if err != nil { - a.log.Errorf("error getting pending certificates: %w", err) - return + err = fmt.Errorf("error getting pending certificates: %w", err) + a.log.Error(err) + return true, err } + thereArePendingCertificates := false a.log.Debugf("checkPendingCertificatesStatus num of pendingCertificates: %d", len(pendingCertificates)) for _, certificate := range pendingCertificates { certificateHeader, err := a.aggLayerClient.GetCertificateHeader(certificate.CertificateID) if err != nil { - a.log.Errorf("error getting certificate header of %s from agglayer: %w", - certificate.String(), err) - continue + err = fmt.Errorf("error getting certificate header of %d/%s from agglayer: %w", + certificate.Height, certificate.String(), err) + a.log.Error(err) + return true, err + } + if slices.Contains(nonSettledStatuses, certificateHeader.Status) { + thereArePendingCertificates = true } a.log.Debugf("aggLayerClient.GetCertificateHeader status [%s] of certificate %s ", certificateHeader.Status, @@ -516,11 +521,13 @@ func (a *AggSender) checkPendingCertificatesStatus(ctx context.Context) { certificate.UpdatedAt = time.Now().UTC().UnixMilli() if err := a.storage.UpdateCertificateStatus(ctx, *certificate); err != nil { - a.log.Errorf("error updating certificate %s status in storage: %w", certificateHeader.String(), err) - continue + err = fmt.Errorf("error updating certificate %s status in storage: %w", certificateHeader.String(), err) + a.log.Error(err) + return true, err } } } + return thereArePendingCertificates, nil } // shouldSendCertificate checks if a certificate should be sent at given time diff --git a/aggsender/aggsender_test.go b/aggsender/aggsender_test.go index e55422e0..0d071e76 100644 --- a/aggsender/aggsender_test.go +++ b/aggsender/aggsender_test.go @@ -27,12 +27,19 @@ import ( func TestExploratoryGetCertificateHeader(t *testing.T) { t.Skip("This test is exploratory and should be skipped") - aggLayerClient := agglayer.NewAggLayerClient("http://localhost:32795") + aggLayerClient := agglayer.NewAggLayerClient("http://localhost:32796") certificateID := common.HexToHash("0xf153e75e24591432ac5deafaeaafba3fec0fd851261c86051b9c0d540b38c369") certificateHeader, err := aggLayerClient.GetCertificateHeader(certificateID) require.NoError(t, err) fmt.Print(certificateHeader) } +func TestExploratoryGetEpochConfiguration(t *testing.T) { + t.Skip("This test is exploratory and should be skipped") + aggLayerClient := agglayer.NewAggLayerClient("http://localhost:32796") + clockConfig, err := aggLayerClient.GetEpochConfiguration() + require.NoError(t, err) + fmt.Print(clockConfig) +} func TestConfigString(t *testing.T) { config := Config{ @@ -42,6 +49,8 @@ func TestConfigString(t *testing.T) { CheckSettledInterval: types.Duration{Duration: 20 * time.Second}, AggsenderPrivateKey: types.KeystoreFileConfig{Path: "/path/to/key", Password: "password"}, URLRPCL2: "http://l2.rpc.url", + BlockFinality: "latestBlock", + EpochNotificationPercentage: 50, SaveCertificatesToFilesPath: "/path/to/certificates", } @@ -52,6 +61,8 @@ func TestConfigString(t *testing.T) { "AggsenderPrivateKeyPath: /path/to/key\n" + "AggsenderPrivateKeyPassword: password\n" + "URLRPCL2: http://l2.rpc.url\n" + + "BlockFinality: latestBlock\n" + + "EpochNotificationPercentage: 50\n" + "SaveCertificatesToFilesPath: /path/to/certificates\n" require.Equal(t, expected, config.String()) @@ -274,7 +285,8 @@ func TestGetImportedBridgeExits(t *testing.T) { t.Parallel() mockProof := generateTestProof(t) - mockL1InfoTreeSyncer := mocks.NewL1InfoTreeSyncerMock(t) + + mockL1InfoTreeSyncer := mocks.NewL1InfoTreeSyncer(t) mockL1InfoTreeSyncer.On("GetInfoByGlobalExitRoot", mock.Anything).Return(&l1infotreesync.L1InfoTreeLeaf{ L1InfoTreeIndex: 1, Timestamp: 123456789, @@ -507,8 +519,8 @@ func TestGetImportedBridgeExits(t *testing.T) { } func TestBuildCertificate(t *testing.T) { - mockL2BridgeSyncer := mocks.NewL2BridgeSyncerMock(t) - mockL1InfoTreeSyncer := mocks.NewL1InfoTreeSyncerMock(t) + mockL2BridgeSyncer := mocks.NewL2BridgeSyncer(t) + mockL1InfoTreeSyncer := mocks.NewL1InfoTreeSyncer(t) mockProof := generateTestProof(t) tests := []struct { @@ -738,17 +750,17 @@ func generateTestProof(t *testing.T) treeTypes.Proof { } func TestCheckIfCertificatesAreSettled(t *testing.T) { - t.Parallel() - tests := []struct { - name string - pendingCertificates []*aggsendertypes.CertificateInfo - certificateHeaders map[common.Hash]*agglayer.CertificateHeader - getFromDBError error - clientError error - updateDBError error - expectedErrorLogMessages []string - expectedInfoMessages []string + name string + pendingCertificates []*aggsendertypes.CertificateInfo + certificateHeaders map[common.Hash]*agglayer.CertificateHeader + getFromDBError error + clientError error + updateDBError error + expectedErrorLogMessages []string + expectedInfoMessages []string + expectedThereArePendingCerts bool + expectedError bool }{ { name: "All certificates settled - update successful", @@ -784,6 +796,8 @@ func TestCheckIfCertificatesAreSettled(t *testing.T) { expectedErrorLogMessages: []string{ "error getting pending certificates: %w", }, + expectedThereArePendingCerts: true, + expectedError: true, }, { name: "Error getting certificate header", @@ -797,6 +811,8 @@ func TestCheckIfCertificatesAreSettled(t *testing.T) { expectedErrorLogMessages: []string{ "error getting header of certificate %s with height: %d from agglayer: %w", }, + expectedThereArePendingCerts: true, + expectedError: true, }, { name: "Error updating certificate status", @@ -813,6 +829,8 @@ func TestCheckIfCertificatesAreSettled(t *testing.T) { expectedInfoMessages: []string{ "certificate %s changed status to %s", }, + expectedThereArePendingCerts: true, + expectedError: true, }, } @@ -820,9 +838,7 @@ func TestCheckIfCertificatesAreSettled(t *testing.T) { tt := tt t.Run(tt.name, func(t *testing.T) { - t.Parallel() - - mockStorage := mocks.NewAggSenderStorageMock(t) + mockStorage := mocks.NewAggSenderStorage(t) mockAggLayerClient := agglayer.NewAgglayerClientMock(t) mockLogger := log.WithFields("test", "unittest") @@ -847,14 +863,10 @@ func TestCheckIfCertificatesAreSettled(t *testing.T) { }, } - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - go aggSender.checkIfCertificatesAreSettled(ctx) - - time.Sleep(2 * time.Second) - cancel() - + ctx := context.TODO() + thereArePendingCerts, err := aggSender.checkPendingCertificatesStatus(ctx) + require.Equal(t, tt.expectedThereArePendingCerts, thereArePendingCerts) + require.Equal(t, tt.expectedError, err != nil) mockAggLayerClient.AssertExpectations(t) mockStorage.AssertExpectations(t) }) @@ -885,23 +897,23 @@ func TestSendCertificate(t *testing.T) { expectedError string } - setupTest := func(cfg testCfg) (*AggSender, *mocks.AggSenderStorageMock, *mocks.L2BridgeSyncerMock, - *agglayer.AgglayerClientMock, *mocks.L1InfoTreeSyncerMock) { + setupTest := func(cfg testCfg) (*AggSender, *mocks.AggSenderStorage, *mocks.L2BridgeSyncer, + *agglayer.AgglayerClientMock, *mocks.L1InfoTreeSyncer) { var ( aggsender = &AggSender{ log: log.WithFields("aggsender", 1), cfg: Config{}, sequencerKey: cfg.sequencerKey, } - mockStorage *mocks.AggSenderStorageMock - mockL2Syncer *mocks.L2BridgeSyncerMock + mockStorage *mocks.AggSenderStorage + mockL2Syncer *mocks.L2BridgeSyncer mockAggLayerClient *agglayer.AgglayerClientMock - mockL1InfoTreeSyncer *mocks.L1InfoTreeSyncerMock + mockL1InfoTreeSyncer *mocks.L1InfoTreeSyncer ) if cfg.shouldSendCertificate != nil || cfg.getLastSentCertificate != nil || cfg.saveLastSentCertificate != nil { - mockStorage = mocks.NewAggSenderStorageMock(t) + mockStorage = mocks.NewAggSenderStorage(t) mockStorage.On("GetCertificatesByStatus", nonSettledStatuses). Return(cfg.shouldSendCertificate...).Once() @@ -918,7 +930,7 @@ func TestSendCertificate(t *testing.T) { if cfg.lastL2BlockProcessed != nil || cfg.originNetwork != nil || cfg.getBridges != nil || cfg.getClaims != nil || cfg.getInfoByGlobalExitRoot != nil { - mockL2Syncer = mocks.NewL2BridgeSyncerMock(t) + mockL2Syncer = mocks.NewL2BridgeSyncer(t) mockL2Syncer.On("GetLastProcessedBlock", mock.Anything).Return(cfg.lastL2BlockProcessed...).Once() @@ -950,7 +962,7 @@ func TestSendCertificate(t *testing.T) { if cfg.getInfoByGlobalExitRoot != nil || cfg.getL1InfoTreeRootByIndex != nil || cfg.getL1InfoTreeMerkleProofFromIndexToRoot != nil { - mockL1InfoTreeSyncer = mocks.NewL1InfoTreeSyncerMock(t) + mockL1InfoTreeSyncer = mocks.NewL1InfoTreeSyncer(t) mockL1InfoTreeSyncer.On("GetInfoByGlobalExitRoot", mock.Anything).Return(cfg.getInfoByGlobalExitRoot...).Once() if cfg.getL1InfoTreeRootByIndex != nil { @@ -1481,10 +1493,10 @@ func TestSendCertificate_NoClaims(t *testing.T) { require.NoError(t, err) ctx := context.Background() - mockStorage := mocks.NewAggSenderStorageMock(t) - mockL2Syncer := mocks.NewL2BridgeSyncerMock(t) + mockStorage := mocks.NewAggSenderStorage(t) + mockL2Syncer := mocks.NewL2BridgeSyncer(t) mockAggLayerClient := agglayer.NewAgglayerClientMock(t) - mockL1InfoTreeSyncer := mocks.NewL1InfoTreeSyncerMock(t) + mockL1InfoTreeSyncer := mocks.NewL1InfoTreeSyncer(t) aggSender := &AggSender{ log: log.WithFields("aggsender-test", "no claims test"), diff --git a/aggsender/block_notifier_polling.go b/aggsender/block_notifier_polling.go new file mode 100644 index 00000000..17dafefa --- /dev/null +++ b/aggsender/block_notifier_polling.go @@ -0,0 +1,219 @@ +package aggsender + +import ( + "context" + "fmt" + "math/big" + "sync" + "time" + + "github.com/0xPolygon/cdk/aggsender/types" + "github.com/0xPolygon/cdk/etherman" +) + +var ( + timeNowFunc = time.Now +) + +const ( + AutomaticBlockInterval = time.Second * 0 + // minBlockInterval is the minimum interval at which the AggSender will check for new blocks + minBlockInterval = time.Second + // maxBlockInterval is the maximum interval at which the AggSender will check for new blocks + maxBlockInterval = time.Minute +) + +type ConfigBlockNotifierPolling struct { + // BlockFinalityType is the finality of the block to be notified + BlockFinalityType etherman.BlockNumberFinality + // CheckNewBlockInterval is the interval at which the AggSender will check for new blocks + // if is 0 it will be calculated automatically + CheckNewBlockInterval time.Duration +} + +type BlockNotifierPolling struct { + ethClient types.EthClient + blockFinality *big.Int + logger types.Logger + config ConfigBlockNotifierPolling + mu sync.Mutex + lastStatus *blockNotifierPollingInternalStatus + types.GenericSubscriber[types.EventNewBlock] +} + +// NewBlockNotifierPolling creates a new BlockNotifierPolling. +// if param `subscriber` is nil a new GenericSubscriberImpl[types.EventNewBlock] will be created. +// To use this class you need to subscribe and each time that a new block appear the subscriber +// will be notified through the channel. (check unit tests TestExploratoryBlockNotifierPolling +// for more information) +func NewBlockNotifierPolling(ethClient types.EthClient, + config ConfigBlockNotifierPolling, + logger types.Logger, + subscriber types.GenericSubscriber[types.EventNewBlock]) (*BlockNotifierPolling, error) { + if subscriber == nil { + subscriber = NewGenericSubscriberImpl[types.EventNewBlock]() + } + finality, err := config.BlockFinalityType.ToBlockNum() + if err != nil { + return nil, fmt.Errorf("failed to convert block finality type to block number: %w", err) + } + + return &BlockNotifierPolling{ + ethClient: ethClient, + blockFinality: finality, + logger: logger, + config: config, + GenericSubscriber: subscriber, + }, nil +} + +func (b *BlockNotifierPolling) String() string { + status := b.getGlobalStatus() + res := fmt.Sprintf("BlockNotifierPolling: finality=%s", b.config.BlockFinalityType) + if status != nil { + res += fmt.Sprintf(" lastBlockSeen=%d", status.lastBlockSeen) + } else { + res += " lastBlockSeen=none" + } + return res +} + +// Start starts the BlockNotifierPolling blocking the current goroutine +func (b *BlockNotifierPolling) Start(ctx context.Context) { + ticker := time.NewTimer(b.config.CheckNewBlockInterval) + defer ticker.Stop() + + var status *blockNotifierPollingInternalStatus = nil + + for { + select { + case <-ctx.Done(): + return + case <-ticker.C: + delay, newStatus, event := b.step(ctx, status) + status = newStatus + b.setGlobalStatus(status) + if event != nil { + b.Publish(*event) + } + ticker.Reset(delay) + } + } +} + +func (b *BlockNotifierPolling) setGlobalStatus(status *blockNotifierPollingInternalStatus) { + b.mu.Lock() + defer b.mu.Unlock() + b.lastStatus = status +} + +func (b *BlockNotifierPolling) getGlobalStatus() *blockNotifierPollingInternalStatus { + b.mu.Lock() + defer b.mu.Unlock() + if b.lastStatus == nil { + return nil + } + copyStatus := *b.lastStatus + return ©Status +} + +// step is the main function of the BlockNotifierPolling, it checks if there is a new block +// it returns: +// - the delay for the next check +// - the new status +// - the new even to emit or nil +func (b *BlockNotifierPolling) step(ctx context.Context, + previousState *blockNotifierPollingInternalStatus) (time.Duration, + *blockNotifierPollingInternalStatus, *types.EventNewBlock) { + currentBlock, err := b.ethClient.HeaderByNumber(ctx, b.blockFinality) + if err == nil && currentBlock == nil { + err = fmt.Errorf("failed to get block number: return a nil block") + } + if err != nil { + b.logger.Errorf("Failed to get block number: %v", err) + newState := previousState.clear() + return b.nextBlockRequestDelay(nil, err), newState, nil + } + if previousState == nil { + newState := previousState.intialBlock(currentBlock.Number.Uint64()) + return b.nextBlockRequestDelay(previousState, nil), newState, nil + } + if currentBlock.Number.Uint64() == previousState.lastBlockSeen { + // No new block, so no changes on state + return b.nextBlockRequestDelay(previousState, nil), previousState, nil + } + // New blockNumber! + eventToEmit := &types.EventNewBlock{ + BlockNumber: currentBlock.Number.Uint64(), + BlockFinalityType: b.config.BlockFinalityType, + } + + if currentBlock.Number.Uint64()-previousState.lastBlockSeen != 1 { + b.logger.Warnf("Missed block(s) [finality:%s]: %d -> %d", + b.config.BlockFinalityType, previousState.lastBlockSeen, currentBlock.Number.Uint64()) + // It start from scratch because something fails in calculation of block period + newState := previousState.intialBlock(currentBlock.Number.Uint64()) + return b.nextBlockRequestDelay(nil, nil), newState, eventToEmit + } + newState := previousState.incommingNewBlock(currentBlock.Number.Uint64()) + b.logger.Debugf("New block seen [finality:%s]: %d. blockRate:%s", + b.config.BlockFinalityType, currentBlock.Number.Uint64(), newState.previousBlockTime) + + return b.nextBlockRequestDelay(newState, nil), newState, eventToEmit +} + +func (b *BlockNotifierPolling) nextBlockRequestDelay(status *blockNotifierPollingInternalStatus, + err error) time.Duration { + if b.config.CheckNewBlockInterval == AutomaticBlockInterval { + return b.config.CheckNewBlockInterval + } + // Initial stages wait the minimum interval to increas accuracy + if status == nil || status.previousBlockTime == nil { + return minBlockInterval + } + if err != nil { + // If error we wait twice the min interval + return minBlockInterval * 2 //nolint:mnd // 2 times the interval + } + // we have a previous block time so we can calculate the interval + now := timeNowFunc() + expectedTimeNextBlock := status.lastBlockTime.Add(*status.previousBlockTime) + distanceToNextBlock := expectedTimeNextBlock.Sub(now) + interval := distanceToNextBlock * 4 / 5 //nolint:mnd // 80% of for reach the next block + return max(minBlockInterval, min(maxBlockInterval, interval)) +} + +type blockNotifierPollingInternalStatus struct { + lastBlockSeen uint64 + lastBlockTime time.Time // first appear of block lastBlockSeen + previousBlockTime *time.Duration // time of the previous block to appear +} + +func (s *blockNotifierPollingInternalStatus) String() string { + if s == nil { + return "nil" + } + return fmt.Sprintf("lastBlockSeen=%d lastBlockTime=%s previousBlockTime=%s", + s.lastBlockSeen, s.lastBlockTime, s.previousBlockTime) +} + +func (s *blockNotifierPollingInternalStatus) clear() *blockNotifierPollingInternalStatus { + return &blockNotifierPollingInternalStatus{} +} + +func (s *blockNotifierPollingInternalStatus) intialBlock(block uint64) *blockNotifierPollingInternalStatus { + return &blockNotifierPollingInternalStatus{ + lastBlockSeen: block, + lastBlockTime: timeNowFunc(), + } +} + +func (s *blockNotifierPollingInternalStatus) incommingNewBlock(block uint64) *blockNotifierPollingInternalStatus { + now := timeNowFunc() + timePreviousBlock := now.Sub(s.lastBlockTime) + return &blockNotifierPollingInternalStatus{ + lastBlockSeen: block, + lastBlockTime: now, + previousBlockTime: &timePreviousBlock, + } +} diff --git a/aggsender/block_notifier_polling_test.go b/aggsender/block_notifier_polling_test.go new file mode 100644 index 00000000..83b3b643 --- /dev/null +++ b/aggsender/block_notifier_polling_test.go @@ -0,0 +1,211 @@ +package aggsender + +import ( + "context" + "fmt" + "math/big" + "os" + "testing" + "time" + + "github.com/0xPolygon/cdk/aggsender/mocks" + aggsendertypes "github.com/0xPolygon/cdk/aggsender/types" + "github.com/0xPolygon/cdk/etherman" + "github.com/0xPolygon/cdk/log" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/ethclient" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" +) + +func TestExploratoryBlockNotifierPolling(t *testing.T) { + t.Skip() + urlRPCL1 := os.Getenv("L1URL") + fmt.Println("URL=", urlRPCL1) + ethClient, err := ethclient.Dial(urlRPCL1) + require.NoError(t, err) + + sut, errSut := NewBlockNotifierPolling(ethClient, + ConfigBlockNotifierPolling{ + BlockFinalityType: etherman.LatestBlock, + }, log.WithFields("test", "test"), nil) + require.NoError(t, errSut) + go sut.Start(context.Background()) + ch := sut.Subscribe("test") + for { + select { + case block := <-ch: + fmt.Println(block) + } + } +} + +func TestBlockNotifierPollingStep(t *testing.T) { + time0 := time.Unix(1731322117, 0) + period0 := time.Second * 10 + period0_80percent := time.Second * 8 + time1 := time0.Add(period0) + tests := []struct { + name string + previousStatus *blockNotifierPollingInternalStatus + HeaderByNumberError bool + HeaderByNumberErrorNumber uint64 + forcedTime time.Time + expectedStatus *blockNotifierPollingInternalStatus + expectedDelay time.Duration + expectedEvent *aggsendertypes.EventNewBlock + }{ + { + name: "initial->receive block", + previousStatus: nil, + HeaderByNumberError: false, + HeaderByNumberErrorNumber: 100, + forcedTime: time0, + expectedStatus: &blockNotifierPollingInternalStatus{ + lastBlockSeen: 100, + lastBlockTime: time0, + }, + expectedDelay: minBlockInterval, + expectedEvent: nil, + }, + { + name: "received block->error", + previousStatus: nil, + HeaderByNumberError: true, + forcedTime: time0, + expectedStatus: &blockNotifierPollingInternalStatus{}, + expectedDelay: minBlockInterval, + expectedEvent: nil, + }, + + { + name: "have block period->receive new block", + previousStatus: &blockNotifierPollingInternalStatus{ + lastBlockSeen: 100, + lastBlockTime: time0, + previousBlockTime: &period0, + }, + HeaderByNumberError: false, + HeaderByNumberErrorNumber: 101, + forcedTime: time1, + expectedStatus: &blockNotifierPollingInternalStatus{ + lastBlockSeen: 101, + lastBlockTime: time1, + previousBlockTime: &period0, + }, + expectedDelay: period0_80percent, + expectedEvent: &aggsendertypes.EventNewBlock{ + BlockNumber: 101, + }, + }, + } + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + testData := newBlockNotifierPollingTestData(t, nil) + + timeNowFunc = func() time.Time { + return tt.forcedTime + } + + if tt.HeaderByNumberError == false { + hdr1 := &types.Header{ + Number: big.NewInt(int64(tt.HeaderByNumberErrorNumber)), + } + testData.ethClientMock.EXPECT().HeaderByNumber(mock.Anything, mock.Anything).Return(hdr1, nil).Once() + } else { + testData.ethClientMock.EXPECT().HeaderByNumber(mock.Anything, mock.Anything).Return(nil, fmt.Errorf("error")).Once() + } + delay, newStatus, event := testData.sut.step(context.TODO(), tt.previousStatus) + require.Equal(t, tt.expectedDelay, delay, "delay") + require.Equal(t, tt.expectedStatus, newStatus, "new_status") + if tt.expectedEvent == nil { + require.Nil(t, event, "send_event") + } else { + require.Equal(t, tt.expectedEvent.BlockNumber, event.BlockNumber, "send_event") + } + }) + } +} + +func TestDelayNoPreviousBLock(t *testing.T) { + testData := newBlockNotifierPollingTestData(t, nil) + status := blockNotifierPollingInternalStatus{ + lastBlockSeen: 100, + } + delay := testData.sut.nextBlockRequestDelay(&status, nil) + require.Equal(t, minBlockInterval, delay) +} + +func TestDelayBLock(t *testing.T) { + testData := newBlockNotifierPollingTestData(t, nil) + pt := time.Second * 10 + status := blockNotifierPollingInternalStatus{ + lastBlockSeen: 100, + previousBlockTime: &pt, + } + delay := testData.sut.nextBlockRequestDelay(&status, nil) + require.Equal(t, minBlockInterval, delay) +} + +func TestNewBlockNotifierPolling(t *testing.T) { + testData := newBlockNotifierPollingTestData(t, nil) + require.NotNil(t, testData.sut) + _, err := NewBlockNotifierPolling(testData.ethClientMock, ConfigBlockNotifierPolling{ + BlockFinalityType: etherman.BlockNumberFinality("invalid"), + }, log.WithFields("test", "test"), nil) + require.Error(t, err) +} + +func TestBlockNotifierPollingString(t *testing.T) { + testData := newBlockNotifierPollingTestData(t, nil) + require.NotEmpty(t, testData.sut.String()) + testData.sut.lastStatus = &blockNotifierPollingInternalStatus{ + lastBlockSeen: 100, + } + require.NotEmpty(t, testData.sut.String()) +} + +func TestBlockNotifierPollingStart(t *testing.T) { + testData := newBlockNotifierPollingTestData(t, nil) + ch := testData.sut.Subscribe("test") + hdr1 := &types.Header{ + Number: big.NewInt(100), + } + testData.ethClientMock.EXPECT().HeaderByNumber(mock.Anything, mock.Anything).Return(hdr1, nil).Once() + hdr2 := &types.Header{ + Number: big.NewInt(101), + } + testData.ethClientMock.EXPECT().HeaderByNumber(mock.Anything, mock.Anything).Return(hdr2, nil).Once() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + go testData.sut.Start(ctx) + block := <-ch + require.NotNil(t, block) + require.Equal(t, uint64(101), block.BlockNumber) +} + +type blockNotifierPollingTestData struct { + sut *BlockNotifierPolling + ethClientMock *mocks.EthClient + ctx context.Context +} + +func newBlockNotifierPollingTestData(t *testing.T, config *ConfigBlockNotifierPolling) blockNotifierPollingTestData { + t.Helper() + if config == nil { + config = &ConfigBlockNotifierPolling{ + BlockFinalityType: etherman.LatestBlock, + CheckNewBlockInterval: time.Second, + } + } + EthClientMock := mocks.NewEthClient(t) + logger := log.WithFields("test", "BlockNotifierPolling") + sut, err := NewBlockNotifierPolling(EthClientMock, *config, logger, nil) + require.NoError(t, err) + return blockNotifierPollingTestData{ + sut: sut, + ethClientMock: EthClientMock, + ctx: context.TODO(), + } +} diff --git a/aggsender/config.go b/aggsender/config.go index 4ff78f96..8ae0b759 100644 --- a/aggsender/config.go +++ b/aggsender/config.go @@ -1,6 +1,8 @@ package aggsender import ( + "fmt" + "github.com/0xPolygon/cdk/config/types" ) @@ -18,6 +20,13 @@ type Config struct { AggsenderPrivateKey types.KeystoreFileConfig `mapstructure:"AggsenderPrivateKey"` // URLRPCL2 is the URL of the L2 RPC node URLRPCL2 string `mapstructure:"URLRPCL2"` + // BlockFinality indicates which finality follows AggLayer + BlockFinality string `jsonschema:"enum=LatestBlock, enum=SafeBlock, enum=PendingBlock, enum=FinalizedBlock, enum=EarliestBlock" mapstructure:"BlockFinality"` //nolint:lll + // EpochNotificationPercentage indicates the percentage of the epoch + // the AggSender should send the certificate + // 0 -> Begin + // 50 -> Middle + EpochNotificationPercentage uint `mapstructure:"EpochNotificationPercentage"` // SaveCertificatesToFilesPath if != "" tells the AggSender to save the certificates to a file in this path SaveCertificatesToFilesPath string `mapstructure:"SaveCertificatesToFilesPath"` } @@ -31,5 +40,7 @@ func (c Config) String() string { "AggsenderPrivateKeyPath: " + c.AggsenderPrivateKey.Path + "\n" + "AggsenderPrivateKeyPassword: " + c.AggsenderPrivateKey.Password + "\n" + "URLRPCL2: " + c.URLRPCL2 + "\n" + + "BlockFinality: " + c.BlockFinality + "\n" + + "EpochNotificationPercentage: " + fmt.Sprintf("%d", c.EpochNotificationPercentage) + "\n" + "SaveCertificatesToFilesPath: " + c.SaveCertificatesToFilesPath + "\n" } diff --git a/aggsender/epoch_notifier_per_block.go b/aggsender/epoch_notifier_per_block.go new file mode 100644 index 00000000..3b560731 --- /dev/null +++ b/aggsender/epoch_notifier_per_block.go @@ -0,0 +1,204 @@ +package aggsender + +import ( + "context" + "fmt" + + "github.com/0xPolygon/cdk/agglayer" + "github.com/0xPolygon/cdk/aggsender/types" +) + +const ( + maxPercent = 100.0 +) + +type ExtraInfoEventEpoch struct { + PendingBlocks int +} + +func (e *ExtraInfoEventEpoch) String() string { + return fmt.Sprintf("ExtraInfoEventEpoch: pendingBlocks=%d", e.PendingBlocks) +} + +type ConfigEpochNotifierPerBlock struct { + StartingEpochBlock uint64 + NumBlockPerEpoch uint + + // EpochNotificationPercentage + // 0 -> begin new Epoch + // 50 -> middle of epoch + // 100 -> end of epoch (same as 0) + EpochNotificationPercentage uint +} + +func NewConfigEpochNotifierPerBlock(aggLayer agglayer.AggLayerClientGetEpochConfiguration, + epochNotificationPercentage uint) (*ConfigEpochNotifierPerBlock, error) { + if aggLayer == nil { + return nil, fmt.Errorf("newConfigEpochNotifierPerBlock: aggLayerClient is required") + } + clockConfig, err := aggLayer.GetEpochConfiguration() + if err != nil { + return nil, fmt.Errorf("newConfigEpochNotifierPerBlock: error getting clock configuration from AggLayer: %w", err) + } + return &ConfigEpochNotifierPerBlock{ + StartingEpochBlock: clockConfig.GenesisBlock, + NumBlockPerEpoch: uint(clockConfig.EpochDuration), + EpochNotificationPercentage: epochNotificationPercentage, + }, nil +} + +func (c *ConfigEpochNotifierPerBlock) Validate() error { + if c.NumBlockPerEpoch == 0 { + return fmt.Errorf("numBlockPerEpoch: num block per epoch is required > 0 ") + } + if c.EpochNotificationPercentage >= maxPercent { + return fmt.Errorf("epochNotificationPercentage: must be between 0 and 99") + } + return nil +} + +type EpochNotifierPerBlock struct { + blockNotifier types.BlockNotifier + logger types.Logger + + lastStartingEpochBlock uint64 + + Config ConfigEpochNotifierPerBlock + types.GenericSubscriber[types.EpochEvent] +} + +func NewEpochNotifierPerBlock(blockNotifier types.BlockNotifier, + logger types.Logger, + config ConfigEpochNotifierPerBlock, + subscriber types.GenericSubscriber[types.EpochEvent]) (*EpochNotifierPerBlock, error) { + if subscriber == nil { + subscriber = NewGenericSubscriberImpl[types.EpochEvent]() + } + + err := config.Validate() + if err != nil { + return nil, fmt.Errorf("invalid config: %w", err) + } + return &EpochNotifierPerBlock{ + blockNotifier: blockNotifier, + logger: logger, + lastStartingEpochBlock: config.StartingEpochBlock, + Config: config, + GenericSubscriber: subscriber, + }, nil +} + +func (e *EpochNotifierPerBlock) String() string { + return fmt.Sprintf("EpochNotifierPerBlock: startingEpochBlock=%d, numBlockPerEpoch=%d,"+ + " EpochNotificationPercentage=%d", + e.Config.StartingEpochBlock, e.Config.NumBlockPerEpoch, e.Config.EpochNotificationPercentage) +} + +// StartAsync starts the notifier in a goroutine +func (e *EpochNotifierPerBlock) StartAsync(ctx context.Context) { + eventNewBlockChannel := e.blockNotifier.Subscribe("EpochNotifierPerBlock") + go e.startInternal(ctx, eventNewBlockChannel) +} + +// Start starts the notifier synchronously +func (e *EpochNotifierPerBlock) Start(ctx context.Context) { + eventNewBlockChannel := e.blockNotifier.Subscribe("EpochNotifierPerBlock") + e.startInternal(ctx, eventNewBlockChannel) +} + +func (e *EpochNotifierPerBlock) startInternal(ctx context.Context, eventNewBlockChannel <-chan types.EventNewBlock) { + status := internalStatus{ + lastBlockSeen: e.Config.StartingEpochBlock, + waitingForEpoch: e.epochNumber(e.Config.StartingEpochBlock), + } + for { + select { + case <-ctx.Done(): + return + case newBlock := <-eventNewBlockChannel: + var event *types.EpochEvent + status, event = e.step(status, newBlock) + if event != nil { + e.logger.Debugf("new Epoch Event: %s", event.String()) + e.GenericSubscriber.Publish(*event) + } + } + } +} + +type internalStatus struct { + lastBlockSeen uint64 + waitingForEpoch uint64 +} + +func (e *EpochNotifierPerBlock) step(status internalStatus, + newBlock types.EventNewBlock) (internalStatus, *types.EpochEvent) { + currentBlock := newBlock.BlockNumber + if currentBlock < e.Config.StartingEpochBlock { + // This is a bit strange, the first epoch is in the future + e.logger.Warnf("Block number %d is before the starting first epoch block %d."+ + " Please check your config", currentBlock, e.Config.StartingEpochBlock) + return status, nil + } + // No new block + if currentBlock <= status.lastBlockSeen { + return status, nil + } + status.lastBlockSeen = currentBlock + + needNotify, closingEpoch := e.isNotificationRequired(currentBlock, status.waitingForEpoch) + if needNotify { + // Notify the epoch has started + info := e.infoEpoch(currentBlock, closingEpoch) + status.waitingForEpoch = closingEpoch + 1 + return status, &types.EpochEvent{ + Epoch: closingEpoch, + ExtraInfo: info, + } + } + return status, nil +} + +func (e *EpochNotifierPerBlock) infoEpoch(currentBlock, newEpochNotified uint64) *ExtraInfoEventEpoch { + nextBlockStartingEpoch := e.endBlockEpoch(newEpochNotified) + return &ExtraInfoEventEpoch{ + PendingBlocks: int(nextBlockStartingEpoch - currentBlock), + } +} +func (e *EpochNotifierPerBlock) percentEpoch(currentBlock uint64) float64 { + epoch := e.epochNumber(currentBlock) + startingBlock := e.startingBlockEpoch(epoch) + elapsedBlocks := currentBlock - startingBlock + return float64(elapsedBlocks) / float64(e.Config.NumBlockPerEpoch) +} +func (e *EpochNotifierPerBlock) isNotificationRequired(currentBlock, lastEpochNotified uint64) (bool, uint64) { + percentEpoch := e.percentEpoch(currentBlock) + thresholdPercent := float64(e.Config.EpochNotificationPercentage) / maxPercent + maxTresholdPercent := float64(e.Config.NumBlockPerEpoch-1) / float64(e.Config.NumBlockPerEpoch) + if thresholdPercent > maxTresholdPercent { + thresholdPercent = maxTresholdPercent + } + if percentEpoch < thresholdPercent { + e.logger.Debugf("Block %d is at %f%% of the epoch no notify", currentBlock, percentEpoch*maxPercent) + return false, e.epochNumber(currentBlock) + } + nextEpoch := e.epochNumber(currentBlock) + 1 + return nextEpoch > lastEpochNotified, e.epochNumber(currentBlock) +} + +func (e *EpochNotifierPerBlock) startingBlockEpoch(epoch uint64) uint64 { + if epoch == 0 { + return e.Config.StartingEpochBlock - 1 + } + return e.Config.StartingEpochBlock + ((epoch - 1) * uint64(e.Config.NumBlockPerEpoch)) +} + +func (e *EpochNotifierPerBlock) endBlockEpoch(epoch uint64) uint64 { + return e.startingBlockEpoch(epoch + 1) +} +func (e *EpochNotifierPerBlock) epochNumber(currentBlock uint64) uint64 { + if currentBlock < e.Config.StartingEpochBlock { + return 0 + } + return 1 + ((currentBlock - e.Config.StartingEpochBlock) / uint64(e.Config.NumBlockPerEpoch)) +} diff --git a/aggsender/epoch_notifier_per_block_test.go b/aggsender/epoch_notifier_per_block_test.go new file mode 100644 index 00000000..203116d0 --- /dev/null +++ b/aggsender/epoch_notifier_per_block_test.go @@ -0,0 +1,219 @@ +package aggsender + +import ( + "context" + "fmt" + "testing" + + "github.com/0xPolygon/cdk/agglayer" + "github.com/0xPolygon/cdk/aggsender/mocks" + "github.com/0xPolygon/cdk/aggsender/types" + "github.com/0xPolygon/cdk/etherman" + "github.com/0xPolygon/cdk/log" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" +) + +func TestStartingBlockEpoch(t *testing.T) { + testData := newNotifierPerBlockTestData(t, &ConfigEpochNotifierPerBlock{ + StartingEpochBlock: 9, + NumBlockPerEpoch: 10, + EpochNotificationPercentage: 80, + }) + // EPOCH: ---0 ----+----1 -----+----2 ----+----3 ----+----4 ----+----5 ----+---- + // BLOCK: 9 19 29 39 49 + require.Equal(t, uint64(8), testData.sut.startingBlockEpoch(0)) + require.Equal(t, uint64(9), testData.sut.startingBlockEpoch(1)) + require.Equal(t, uint64(19), testData.sut.startingBlockEpoch(2)) +} + +func TestEpochNotifyPercentageEdgeCase0(t *testing.T) { + testData := newNotifierPerBlockTestData(t, nil) + testData.sut.Config.EpochNotificationPercentage = 0 + notify, epoch := testData.sut.isNotificationRequired(9, 0) + require.True(t, notify) + require.Equal(t, uint64(1), epoch) +} + +// if percent is 99 means at end of epoch, so in a config 0, epoch-size=10, +// 99% means last block of epoch +func TestEpochNotifyPercentageEdgeCase99(t *testing.T) { + testData := newNotifierPerBlockTestData(t, nil) + testData.sut.Config.EpochNotificationPercentage = 99 + notify, epoch := testData.sut.isNotificationRequired(9, 0) + require.True(t, notify) + require.Equal(t, uint64(1), epoch) +} + +func TestEpochStep(t *testing.T) { + testData := newNotifierPerBlockTestData(t, &ConfigEpochNotifierPerBlock{ + StartingEpochBlock: 9, + NumBlockPerEpoch: 10, + EpochNotificationPercentage: 50, + }) + // EPOCH: ---0 ----+----1 -----+----2 ----+----3 ----+----4 ----+----5 ----+---- + // BLOCK: 9 19 29 39 49 + // start EPOCH#1 -> 9 + // end EPOCH#1 -> 19 + // start EPOCH#2 -> 19 + + tests := []struct { + name string + initialStatus internalStatus + blockNumber uint64 + expectedEvent bool + expectedEventEpoch uint64 + expectedEventPendingBlocks int + }{ + { + name: "First block of epoch, no notification until close to end", + initialStatus: internalStatus{lastBlockSeen: 8, waitingForEpoch: 0}, + blockNumber: 9, + expectedEvent: false, + expectedEventEpoch: 1, + expectedEventPendingBlocks: 0, + }, + { + name: "epoch#1 close to end, notify it!", + initialStatus: internalStatus{lastBlockSeen: 17, waitingForEpoch: 0}, + blockNumber: 18, + expectedEvent: true, + expectedEventEpoch: 1, // Finishing epoch 0 + expectedEventPendingBlocks: 1, // 19 - 18 + }, + { + name: "epoch#1 close to end, but already notified", + initialStatus: internalStatus{lastBlockSeen: 17, waitingForEpoch: 2}, + blockNumber: 18, + expectedEvent: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + _, event := testData.sut.step(tt.initialStatus, types.EventNewBlock{BlockNumber: tt.blockNumber, BlockFinalityType: etherman.LatestBlock}) + require.Equal(t, tt.expectedEvent, event != nil) + if event != nil { + require.Equal(t, tt.expectedEventEpoch, event.Epoch, "Epoch") + extraInfo, ok := event.ExtraInfo.(*ExtraInfoEventEpoch) + require.True(t, ok, "ExtraInfo") + require.Equal(t, tt.expectedEventPendingBlocks, extraInfo.PendingBlocks, "PendingBlocks") + } + }) + } +} + +func TestNewConfigEpochNotifierPerBlock(t *testing.T) { + _, err := NewConfigEpochNotifierPerBlock(nil, 1) + require.Error(t, err) + aggLayerMock := agglayer.NewAgglayerClientMock(t) + aggLayerMock.On("GetEpochConfiguration").Return(nil, fmt.Errorf("error")).Once() + _, err = NewConfigEpochNotifierPerBlock(aggLayerMock, 1) + require.Error(t, err) + cfgAggLayer := &agglayer.ClockConfiguration{ + GenesisBlock: 123, + EpochDuration: 456, + } + aggLayerMock.On("GetEpochConfiguration").Return(cfgAggLayer, nil).Once() + cfg, err := NewConfigEpochNotifierPerBlock(aggLayerMock, 1) + require.NoError(t, err) + require.Equal(t, uint64(123), cfg.StartingEpochBlock) + require.Equal(t, uint(456), cfg.NumBlockPerEpoch) +} + +func TestNotifyEpoch(t *testing.T) { + testData := newNotifierPerBlockTestData(t, nil) + ch := testData.sut.Subscribe("test") + chBlocks := make(chan types.EventNewBlock) + testData.blockNotifierMock.EXPECT().Subscribe(mock.Anything).Return(chBlocks) + testData.sut.StartAsync(testData.ctx) + chBlocks <- types.EventNewBlock{BlockNumber: 109, BlockFinalityType: etherman.LatestBlock} + epochEvent := <-ch + require.Equal(t, uint64(11), epochEvent.Epoch) + testData.ctx.Done() +} + +func TestStepSameEpoch(t *testing.T) { + testData := newNotifierPerBlockTestData(t, nil) + status := internalStatus{ + lastBlockSeen: 100, + waitingForEpoch: testData.sut.epochNumber(100), + } + newStatus, _ := testData.sut.step(status, types.EventNewBlock{BlockNumber: 103, BlockFinalityType: etherman.LatestBlock}) + require.Equal(t, uint64(103), newStatus.lastBlockSeen) + require.Equal(t, status.waitingForEpoch, newStatus.waitingForEpoch) +} + +func TestStepNotifyEpoch(t *testing.T) { + testData := newNotifierPerBlockTestData(t, nil) + status := internalStatus{ + lastBlockSeen: 100, + waitingForEpoch: testData.sut.epochNumber(100), + } + status, _ = testData.sut.step(status, types.EventNewBlock{BlockNumber: 109, BlockFinalityType: etherman.LatestBlock}) + require.Equal(t, uint64(109), status.lastBlockSeen) + require.Equal(t, uint64(12), status.waitingForEpoch) +} + +func TestBlockEpochNumber(t *testing.T) { + testData := newNotifierPerBlockTestData(t, &ConfigEpochNotifierPerBlock{ + StartingEpochBlock: 105, + NumBlockPerEpoch: 10, + EpochNotificationPercentage: 1, + }) + require.Equal(t, uint64(0), testData.sut.epochNumber(0)) + require.Equal(t, uint64(0), testData.sut.epochNumber(104)) + require.Equal(t, uint64(1), testData.sut.epochNumber(105)) + require.Equal(t, uint64(1), testData.sut.epochNumber(114)) + require.Equal(t, uint64(2), testData.sut.epochNumber(115)) + require.Equal(t, uint64(2), testData.sut.epochNumber(116)) + require.Equal(t, uint64(2), testData.sut.epochNumber(124)) + require.Equal(t, uint64(3), testData.sut.epochNumber(125)) +} + +func TestBlockBeforeEpoch(t *testing.T) { + testData := newNotifierPerBlockTestData(t, &ConfigEpochNotifierPerBlock{ + StartingEpochBlock: 105, + NumBlockPerEpoch: 10, + EpochNotificationPercentage: 1, + }) + status := internalStatus{ + lastBlockSeen: 104, + waitingForEpoch: testData.sut.epochNumber(104), + } + newStatus, _ := testData.sut.step(status, types.EventNewBlock{BlockNumber: 104, BlockFinalityType: etherman.LatestBlock}) + // We are previous block of first epoch, so we should do nothing + require.Equal(t, status, newStatus) + status = newStatus + // First block of first epoch + newStatus, _ = testData.sut.step(status, types.EventNewBlock{BlockNumber: 105, BlockFinalityType: etherman.LatestBlock}) + require.Equal(t, uint64(105), newStatus.lastBlockSeen) + // Near end first epoch + newStatus, _ = testData.sut.step(status, types.EventNewBlock{BlockNumber: 114, BlockFinalityType: etherman.LatestBlock}) + require.Equal(t, uint64(114), newStatus.lastBlockSeen) +} + +type notifierPerBlockTestData struct { + sut *EpochNotifierPerBlock + blockNotifierMock *mocks.BlockNotifier + ctx context.Context +} + +func newNotifierPerBlockTestData(t *testing.T, config *ConfigEpochNotifierPerBlock) notifierPerBlockTestData { + t.Helper() + if config == nil { + config = &ConfigEpochNotifierPerBlock{ + StartingEpochBlock: 0, + NumBlockPerEpoch: 10, + EpochNotificationPercentage: 50, + } + } + blockNotifierMock := mocks.NewBlockNotifier(t) + logger := log.WithFields("test", "EpochNotifierPerBlock") + sut, err := NewEpochNotifierPerBlock(blockNotifierMock, logger, *config, nil) + require.NoError(t, err) + return notifierPerBlockTestData{ + sut: sut, + blockNotifierMock: blockNotifierMock, + ctx: context.TODO(), + } +} diff --git a/aggsender/generic_subscriber_impl.go b/aggsender/generic_subscriber_impl.go new file mode 100644 index 00000000..e4251449 --- /dev/null +++ b/aggsender/generic_subscriber_impl.go @@ -0,0 +1,33 @@ +package aggsender + +import "sync" + +type GenericSubscriberImpl[T any] struct { + // map of subscribers with names + subs map[chan T]string + mu sync.RWMutex +} + +func NewGenericSubscriberImpl[T any]() *GenericSubscriberImpl[T] { + return &GenericSubscriberImpl[T]{ + subs: make(map[chan T]string), + } +} + +func (g *GenericSubscriberImpl[T]) Subscribe(subscriberName string) <-chan T { + ch := make(chan T) + g.mu.Lock() + defer g.mu.Unlock() + g.subs[ch] = subscriberName + return ch +} + +func (g *GenericSubscriberImpl[T]) Publish(data T) { + g.mu.RLock() + defer g.mu.RUnlock() + for ch := range g.subs { + go func(ch chan T) { + ch <- data + }(ch) + } +} diff --git a/aggsender/mocks/agg_sender_storage.go b/aggsender/mocks/agg_sender_storage.go new file mode 100644 index 00000000..1816d4a3 --- /dev/null +++ b/aggsender/mocks/agg_sender_storage.go @@ -0,0 +1,351 @@ +// Code generated by mockery. DO NOT EDIT. + +package mocks + +import ( + agglayer "github.com/0xPolygon/cdk/agglayer" + common "github.com/ethereum/go-ethereum/common" + + context "context" + + mock "github.com/stretchr/testify/mock" + + types "github.com/0xPolygon/cdk/aggsender/types" +) + +// AggSenderStorage is an autogenerated mock type for the AggSenderStorage type +type AggSenderStorage struct { + mock.Mock +} + +type AggSenderStorage_Expecter struct { + mock *mock.Mock +} + +func (_m *AggSenderStorage) EXPECT() *AggSenderStorage_Expecter { + return &AggSenderStorage_Expecter{mock: &_m.Mock} +} + +// DeleteCertificate provides a mock function with given fields: ctx, certificateID +func (_m *AggSenderStorage) DeleteCertificate(ctx context.Context, certificateID common.Hash) error { + ret := _m.Called(ctx, certificateID) + + if len(ret) == 0 { + panic("no return value specified for DeleteCertificate") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, common.Hash) error); ok { + r0 = rf(ctx, certificateID) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// AggSenderStorage_DeleteCertificate_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DeleteCertificate' +type AggSenderStorage_DeleteCertificate_Call struct { + *mock.Call +} + +// DeleteCertificate is a helper method to define mock.On call +// - ctx context.Context +// - certificateID common.Hash +func (_e *AggSenderStorage_Expecter) DeleteCertificate(ctx interface{}, certificateID interface{}) *AggSenderStorage_DeleteCertificate_Call { + return &AggSenderStorage_DeleteCertificate_Call{Call: _e.mock.On("DeleteCertificate", ctx, certificateID)} +} + +func (_c *AggSenderStorage_DeleteCertificate_Call) Run(run func(ctx context.Context, certificateID common.Hash)) *AggSenderStorage_DeleteCertificate_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(common.Hash)) + }) + return _c +} + +func (_c *AggSenderStorage_DeleteCertificate_Call) Return(_a0 error) *AggSenderStorage_DeleteCertificate_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *AggSenderStorage_DeleteCertificate_Call) RunAndReturn(run func(context.Context, common.Hash) error) *AggSenderStorage_DeleteCertificate_Call { + _c.Call.Return(run) + return _c +} + +// GetCertificateByHeight provides a mock function with given fields: height +func (_m *AggSenderStorage) GetCertificateByHeight(height uint64) (types.CertificateInfo, error) { + ret := _m.Called(height) + + if len(ret) == 0 { + panic("no return value specified for GetCertificateByHeight") + } + + var r0 types.CertificateInfo + var r1 error + if rf, ok := ret.Get(0).(func(uint64) (types.CertificateInfo, error)); ok { + return rf(height) + } + if rf, ok := ret.Get(0).(func(uint64) types.CertificateInfo); ok { + r0 = rf(height) + } else { + r0 = ret.Get(0).(types.CertificateInfo) + } + + if rf, ok := ret.Get(1).(func(uint64) error); ok { + r1 = rf(height) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// AggSenderStorage_GetCertificateByHeight_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetCertificateByHeight' +type AggSenderStorage_GetCertificateByHeight_Call struct { + *mock.Call +} + +// GetCertificateByHeight is a helper method to define mock.On call +// - height uint64 +func (_e *AggSenderStorage_Expecter) GetCertificateByHeight(height interface{}) *AggSenderStorage_GetCertificateByHeight_Call { + return &AggSenderStorage_GetCertificateByHeight_Call{Call: _e.mock.On("GetCertificateByHeight", height)} +} + +func (_c *AggSenderStorage_GetCertificateByHeight_Call) Run(run func(height uint64)) *AggSenderStorage_GetCertificateByHeight_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(uint64)) + }) + return _c +} + +func (_c *AggSenderStorage_GetCertificateByHeight_Call) Return(_a0 types.CertificateInfo, _a1 error) *AggSenderStorage_GetCertificateByHeight_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *AggSenderStorage_GetCertificateByHeight_Call) RunAndReturn(run func(uint64) (types.CertificateInfo, error)) *AggSenderStorage_GetCertificateByHeight_Call { + _c.Call.Return(run) + return _c +} + +// GetCertificatesByStatus provides a mock function with given fields: status +func (_m *AggSenderStorage) GetCertificatesByStatus(status []agglayer.CertificateStatus) ([]*types.CertificateInfo, error) { + ret := _m.Called(status) + + if len(ret) == 0 { + panic("no return value specified for GetCertificatesByStatus") + } + + var r0 []*types.CertificateInfo + var r1 error + if rf, ok := ret.Get(0).(func([]agglayer.CertificateStatus) ([]*types.CertificateInfo, error)); ok { + return rf(status) + } + if rf, ok := ret.Get(0).(func([]agglayer.CertificateStatus) []*types.CertificateInfo); ok { + r0 = rf(status) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*types.CertificateInfo) + } + } + + if rf, ok := ret.Get(1).(func([]agglayer.CertificateStatus) error); ok { + r1 = rf(status) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// AggSenderStorage_GetCertificatesByStatus_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetCertificatesByStatus' +type AggSenderStorage_GetCertificatesByStatus_Call struct { + *mock.Call +} + +// GetCertificatesByStatus is a helper method to define mock.On call +// - status []agglayer.CertificateStatus +func (_e *AggSenderStorage_Expecter) GetCertificatesByStatus(status interface{}) *AggSenderStorage_GetCertificatesByStatus_Call { + return &AggSenderStorage_GetCertificatesByStatus_Call{Call: _e.mock.On("GetCertificatesByStatus", status)} +} + +func (_c *AggSenderStorage_GetCertificatesByStatus_Call) Run(run func(status []agglayer.CertificateStatus)) *AggSenderStorage_GetCertificatesByStatus_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].([]agglayer.CertificateStatus)) + }) + return _c +} + +func (_c *AggSenderStorage_GetCertificatesByStatus_Call) Return(_a0 []*types.CertificateInfo, _a1 error) *AggSenderStorage_GetCertificatesByStatus_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *AggSenderStorage_GetCertificatesByStatus_Call) RunAndReturn(run func([]agglayer.CertificateStatus) ([]*types.CertificateInfo, error)) *AggSenderStorage_GetCertificatesByStatus_Call { + _c.Call.Return(run) + return _c +} + +// GetLastSentCertificate provides a mock function with given fields: +func (_m *AggSenderStorage) GetLastSentCertificate() (types.CertificateInfo, error) { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for GetLastSentCertificate") + } + + var r0 types.CertificateInfo + var r1 error + if rf, ok := ret.Get(0).(func() (types.CertificateInfo, error)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() types.CertificateInfo); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(types.CertificateInfo) + } + + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// AggSenderStorage_GetLastSentCertificate_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetLastSentCertificate' +type AggSenderStorage_GetLastSentCertificate_Call struct { + *mock.Call +} + +// GetLastSentCertificate is a helper method to define mock.On call +func (_e *AggSenderStorage_Expecter) GetLastSentCertificate() *AggSenderStorage_GetLastSentCertificate_Call { + return &AggSenderStorage_GetLastSentCertificate_Call{Call: _e.mock.On("GetLastSentCertificate")} +} + +func (_c *AggSenderStorage_GetLastSentCertificate_Call) Run(run func()) *AggSenderStorage_GetLastSentCertificate_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *AggSenderStorage_GetLastSentCertificate_Call) Return(_a0 types.CertificateInfo, _a1 error) *AggSenderStorage_GetLastSentCertificate_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *AggSenderStorage_GetLastSentCertificate_Call) RunAndReturn(run func() (types.CertificateInfo, error)) *AggSenderStorage_GetLastSentCertificate_Call { + _c.Call.Return(run) + return _c +} + +// SaveLastSentCertificate provides a mock function with given fields: ctx, certificate +func (_m *AggSenderStorage) SaveLastSentCertificate(ctx context.Context, certificate types.CertificateInfo) error { + ret := _m.Called(ctx, certificate) + + if len(ret) == 0 { + panic("no return value specified for SaveLastSentCertificate") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, types.CertificateInfo) error); ok { + r0 = rf(ctx, certificate) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// AggSenderStorage_SaveLastSentCertificate_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SaveLastSentCertificate' +type AggSenderStorage_SaveLastSentCertificate_Call struct { + *mock.Call +} + +// SaveLastSentCertificate is a helper method to define mock.On call +// - ctx context.Context +// - certificate types.CertificateInfo +func (_e *AggSenderStorage_Expecter) SaveLastSentCertificate(ctx interface{}, certificate interface{}) *AggSenderStorage_SaveLastSentCertificate_Call { + return &AggSenderStorage_SaveLastSentCertificate_Call{Call: _e.mock.On("SaveLastSentCertificate", ctx, certificate)} +} + +func (_c *AggSenderStorage_SaveLastSentCertificate_Call) Run(run func(ctx context.Context, certificate types.CertificateInfo)) *AggSenderStorage_SaveLastSentCertificate_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(types.CertificateInfo)) + }) + return _c +} + +func (_c *AggSenderStorage_SaveLastSentCertificate_Call) Return(_a0 error) *AggSenderStorage_SaveLastSentCertificate_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *AggSenderStorage_SaveLastSentCertificate_Call) RunAndReturn(run func(context.Context, types.CertificateInfo) error) *AggSenderStorage_SaveLastSentCertificate_Call { + _c.Call.Return(run) + return _c +} + +// UpdateCertificateStatus provides a mock function with given fields: ctx, certificate +func (_m *AggSenderStorage) UpdateCertificateStatus(ctx context.Context, certificate types.CertificateInfo) error { + ret := _m.Called(ctx, certificate) + + if len(ret) == 0 { + panic("no return value specified for UpdateCertificateStatus") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, types.CertificateInfo) error); ok { + r0 = rf(ctx, certificate) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// AggSenderStorage_UpdateCertificateStatus_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpdateCertificateStatus' +type AggSenderStorage_UpdateCertificateStatus_Call struct { + *mock.Call +} + +// UpdateCertificateStatus is a helper method to define mock.On call +// - ctx context.Context +// - certificate types.CertificateInfo +func (_e *AggSenderStorage_Expecter) UpdateCertificateStatus(ctx interface{}, certificate interface{}) *AggSenderStorage_UpdateCertificateStatus_Call { + return &AggSenderStorage_UpdateCertificateStatus_Call{Call: _e.mock.On("UpdateCertificateStatus", ctx, certificate)} +} + +func (_c *AggSenderStorage_UpdateCertificateStatus_Call) Run(run func(ctx context.Context, certificate types.CertificateInfo)) *AggSenderStorage_UpdateCertificateStatus_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(types.CertificateInfo)) + }) + return _c +} + +func (_c *AggSenderStorage_UpdateCertificateStatus_Call) Return(_a0 error) *AggSenderStorage_UpdateCertificateStatus_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *AggSenderStorage_UpdateCertificateStatus_Call) RunAndReturn(run func(context.Context, types.CertificateInfo) error) *AggSenderStorage_UpdateCertificateStatus_Call { + _c.Call.Return(run) + return _c +} + +// NewAggSenderStorage creates a new instance of AggSenderStorage. 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 NewAggSenderStorage(t interface { + mock.TestingT + Cleanup(func()) +}) *AggSenderStorage { + mock := &AggSenderStorage{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/aggsender/mocks/block_notifier.go b/aggsender/mocks/block_notifier.go new file mode 100644 index 00000000..f8fc556d --- /dev/null +++ b/aggsender/mocks/block_notifier.go @@ -0,0 +1,128 @@ +// Code generated by mockery. DO NOT EDIT. + +package mocks + +import ( + types "github.com/0xPolygon/cdk/aggsender/types" + mock "github.com/stretchr/testify/mock" +) + +// BlockNotifier is an autogenerated mock type for the BlockNotifier type +type BlockNotifier struct { + mock.Mock +} + +type BlockNotifier_Expecter struct { + mock *mock.Mock +} + +func (_m *BlockNotifier) EXPECT() *BlockNotifier_Expecter { + return &BlockNotifier_Expecter{mock: &_m.Mock} +} + +// String provides a mock function with given fields: +func (_m *BlockNotifier) String() string { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for String") + } + + var r0 string + if rf, ok := ret.Get(0).(func() string); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(string) + } + + return r0 +} + +// BlockNotifier_String_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'String' +type BlockNotifier_String_Call struct { + *mock.Call +} + +// String is a helper method to define mock.On call +func (_e *BlockNotifier_Expecter) String() *BlockNotifier_String_Call { + return &BlockNotifier_String_Call{Call: _e.mock.On("String")} +} + +func (_c *BlockNotifier_String_Call) Run(run func()) *BlockNotifier_String_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *BlockNotifier_String_Call) Return(_a0 string) *BlockNotifier_String_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *BlockNotifier_String_Call) RunAndReturn(run func() string) *BlockNotifier_String_Call { + _c.Call.Return(run) + return _c +} + +// Subscribe provides a mock function with given fields: id +func (_m *BlockNotifier) Subscribe(id string) <-chan types.EventNewBlock { + ret := _m.Called(id) + + if len(ret) == 0 { + panic("no return value specified for Subscribe") + } + + var r0 <-chan types.EventNewBlock + if rf, ok := ret.Get(0).(func(string) <-chan types.EventNewBlock); ok { + r0 = rf(id) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(<-chan types.EventNewBlock) + } + } + + return r0 +} + +// BlockNotifier_Subscribe_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Subscribe' +type BlockNotifier_Subscribe_Call struct { + *mock.Call +} + +// Subscribe is a helper method to define mock.On call +// - id string +func (_e *BlockNotifier_Expecter) Subscribe(id interface{}) *BlockNotifier_Subscribe_Call { + return &BlockNotifier_Subscribe_Call{Call: _e.mock.On("Subscribe", id)} +} + +func (_c *BlockNotifier_Subscribe_Call) Run(run func(id string)) *BlockNotifier_Subscribe_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(string)) + }) + return _c +} + +func (_c *BlockNotifier_Subscribe_Call) Return(_a0 <-chan types.EventNewBlock) *BlockNotifier_Subscribe_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *BlockNotifier_Subscribe_Call) RunAndReturn(run func(string) <-chan types.EventNewBlock) *BlockNotifier_Subscribe_Call { + _c.Call.Return(run) + return _c +} + +// NewBlockNotifier creates a new instance of BlockNotifier. 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 NewBlockNotifier(t interface { + mock.TestingT + Cleanup(func()) +}) *BlockNotifier { + mock := &BlockNotifier{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/aggsender/mocks/epoch_notifier.go b/aggsender/mocks/epoch_notifier.go new file mode 100644 index 00000000..fb8bf35f --- /dev/null +++ b/aggsender/mocks/epoch_notifier.go @@ -0,0 +1,163 @@ +// Code generated by mockery. DO NOT EDIT. + +package mocks + +import ( + context "context" + + types "github.com/0xPolygon/cdk/aggsender/types" + mock "github.com/stretchr/testify/mock" +) + +// EpochNotifier is an autogenerated mock type for the EpochNotifier type +type EpochNotifier struct { + mock.Mock +} + +type EpochNotifier_Expecter struct { + mock *mock.Mock +} + +func (_m *EpochNotifier) EXPECT() *EpochNotifier_Expecter { + return &EpochNotifier_Expecter{mock: &_m.Mock} +} + +// Start provides a mock function with given fields: ctx +func (_m *EpochNotifier) Start(ctx context.Context) { + _m.Called(ctx) +} + +// EpochNotifier_Start_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Start' +type EpochNotifier_Start_Call struct { + *mock.Call +} + +// Start is a helper method to define mock.On call +// - ctx context.Context +func (_e *EpochNotifier_Expecter) Start(ctx interface{}) *EpochNotifier_Start_Call { + return &EpochNotifier_Start_Call{Call: _e.mock.On("Start", ctx)} +} + +func (_c *EpochNotifier_Start_Call) Run(run func(ctx context.Context)) *EpochNotifier_Start_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context)) + }) + return _c +} + +func (_c *EpochNotifier_Start_Call) Return() *EpochNotifier_Start_Call { + _c.Call.Return() + return _c +} + +func (_c *EpochNotifier_Start_Call) RunAndReturn(run func(context.Context)) *EpochNotifier_Start_Call { + _c.Call.Return(run) + return _c +} + +// String provides a mock function with given fields: +func (_m *EpochNotifier) String() string { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for String") + } + + var r0 string + if rf, ok := ret.Get(0).(func() string); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(string) + } + + return r0 +} + +// EpochNotifier_String_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'String' +type EpochNotifier_String_Call struct { + *mock.Call +} + +// String is a helper method to define mock.On call +func (_e *EpochNotifier_Expecter) String() *EpochNotifier_String_Call { + return &EpochNotifier_String_Call{Call: _e.mock.On("String")} +} + +func (_c *EpochNotifier_String_Call) Run(run func()) *EpochNotifier_String_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *EpochNotifier_String_Call) Return(_a0 string) *EpochNotifier_String_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *EpochNotifier_String_Call) RunAndReturn(run func() string) *EpochNotifier_String_Call { + _c.Call.Return(run) + return _c +} + +// Subscribe provides a mock function with given fields: id +func (_m *EpochNotifier) Subscribe(id string) <-chan types.EpochEvent { + ret := _m.Called(id) + + if len(ret) == 0 { + panic("no return value specified for Subscribe") + } + + var r0 <-chan types.EpochEvent + if rf, ok := ret.Get(0).(func(string) <-chan types.EpochEvent); ok { + r0 = rf(id) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(<-chan types.EpochEvent) + } + } + + return r0 +} + +// EpochNotifier_Subscribe_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Subscribe' +type EpochNotifier_Subscribe_Call struct { + *mock.Call +} + +// Subscribe is a helper method to define mock.On call +// - id string +func (_e *EpochNotifier_Expecter) Subscribe(id interface{}) *EpochNotifier_Subscribe_Call { + return &EpochNotifier_Subscribe_Call{Call: _e.mock.On("Subscribe", id)} +} + +func (_c *EpochNotifier_Subscribe_Call) Run(run func(id string)) *EpochNotifier_Subscribe_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(string)) + }) + return _c +} + +func (_c *EpochNotifier_Subscribe_Call) Return(_a0 <-chan types.EpochEvent) *EpochNotifier_Subscribe_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *EpochNotifier_Subscribe_Call) RunAndReturn(run func(string) <-chan types.EpochEvent) *EpochNotifier_Subscribe_Call { + _c.Call.Return(run) + return _c +} + +// NewEpochNotifier creates a new instance of EpochNotifier. 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 NewEpochNotifier(t interface { + mock.TestingT + Cleanup(func()) +}) *EpochNotifier { + mock := &EpochNotifier{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/aggsender/mocks/mock_eth_client.go b/aggsender/mocks/eth_client.go similarity index 50% rename from aggsender/mocks/mock_eth_client.go rename to aggsender/mocks/eth_client.go index ebf618bf..6a68de41 100644 --- a/aggsender/mocks/mock_eth_client.go +++ b/aggsender/mocks/eth_client.go @@ -11,21 +11,21 @@ import ( mock "github.com/stretchr/testify/mock" ) -// EthClientMock is an autogenerated mock type for the EthClient type -type EthClientMock struct { +// EthClient is an autogenerated mock type for the EthClient type +type EthClient struct { mock.Mock } -type EthClientMock_Expecter struct { +type EthClient_Expecter struct { mock *mock.Mock } -func (_m *EthClientMock) EXPECT() *EthClientMock_Expecter { - return &EthClientMock_Expecter{mock: &_m.Mock} +func (_m *EthClient) EXPECT() *EthClient_Expecter { + return &EthClient_Expecter{mock: &_m.Mock} } // BlockNumber provides a mock function with given fields: ctx -func (_m *EthClientMock) BlockNumber(ctx context.Context) (uint64, error) { +func (_m *EthClient) BlockNumber(ctx context.Context) (uint64, error) { ret := _m.Called(ctx) if len(ret) == 0 { @@ -52,36 +52,36 @@ func (_m *EthClientMock) BlockNumber(ctx context.Context) (uint64, error) { return r0, r1 } -// EthClientMock_BlockNumber_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'BlockNumber' -type EthClientMock_BlockNumber_Call struct { +// EthClient_BlockNumber_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'BlockNumber' +type EthClient_BlockNumber_Call struct { *mock.Call } // BlockNumber is a helper method to define mock.On call // - ctx context.Context -func (_e *EthClientMock_Expecter) BlockNumber(ctx interface{}) *EthClientMock_BlockNumber_Call { - return &EthClientMock_BlockNumber_Call{Call: _e.mock.On("BlockNumber", ctx)} +func (_e *EthClient_Expecter) BlockNumber(ctx interface{}) *EthClient_BlockNumber_Call { + return &EthClient_BlockNumber_Call{Call: _e.mock.On("BlockNumber", ctx)} } -func (_c *EthClientMock_BlockNumber_Call) Run(run func(ctx context.Context)) *EthClientMock_BlockNumber_Call { +func (_c *EthClient_BlockNumber_Call) Run(run func(ctx context.Context)) *EthClient_BlockNumber_Call { _c.Call.Run(func(args mock.Arguments) { run(args[0].(context.Context)) }) return _c } -func (_c *EthClientMock_BlockNumber_Call) Return(_a0 uint64, _a1 error) *EthClientMock_BlockNumber_Call { +func (_c *EthClient_BlockNumber_Call) Return(_a0 uint64, _a1 error) *EthClient_BlockNumber_Call { _c.Call.Return(_a0, _a1) return _c } -func (_c *EthClientMock_BlockNumber_Call) RunAndReturn(run func(context.Context) (uint64, error)) *EthClientMock_BlockNumber_Call { +func (_c *EthClient_BlockNumber_Call) RunAndReturn(run func(context.Context) (uint64, error)) *EthClient_BlockNumber_Call { _c.Call.Return(run) return _c } // HeaderByNumber provides a mock function with given fields: ctx, number -func (_m *EthClientMock) HeaderByNumber(ctx context.Context, number *big.Int) (*coretypes.Header, error) { +func (_m *EthClient) HeaderByNumber(ctx context.Context, number *big.Int) (*coretypes.Header, error) { ret := _m.Called(ctx, number) if len(ret) == 0 { @@ -110,42 +110,42 @@ func (_m *EthClientMock) HeaderByNumber(ctx context.Context, number *big.Int) (* return r0, r1 } -// EthClientMock_HeaderByNumber_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'HeaderByNumber' -type EthClientMock_HeaderByNumber_Call struct { +// EthClient_HeaderByNumber_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'HeaderByNumber' +type EthClient_HeaderByNumber_Call struct { *mock.Call } // HeaderByNumber is a helper method to define mock.On call // - ctx context.Context // - number *big.Int -func (_e *EthClientMock_Expecter) HeaderByNumber(ctx interface{}, number interface{}) *EthClientMock_HeaderByNumber_Call { - return &EthClientMock_HeaderByNumber_Call{Call: _e.mock.On("HeaderByNumber", ctx, number)} +func (_e *EthClient_Expecter) HeaderByNumber(ctx interface{}, number interface{}) *EthClient_HeaderByNumber_Call { + return &EthClient_HeaderByNumber_Call{Call: _e.mock.On("HeaderByNumber", ctx, number)} } -func (_c *EthClientMock_HeaderByNumber_Call) Run(run func(ctx context.Context, number *big.Int)) *EthClientMock_HeaderByNumber_Call { +func (_c *EthClient_HeaderByNumber_Call) Run(run func(ctx context.Context, number *big.Int)) *EthClient_HeaderByNumber_Call { _c.Call.Run(func(args mock.Arguments) { run(args[0].(context.Context), args[1].(*big.Int)) }) return _c } -func (_c *EthClientMock_HeaderByNumber_Call) Return(_a0 *coretypes.Header, _a1 error) *EthClientMock_HeaderByNumber_Call { +func (_c *EthClient_HeaderByNumber_Call) Return(_a0 *coretypes.Header, _a1 error) *EthClient_HeaderByNumber_Call { _c.Call.Return(_a0, _a1) return _c } -func (_c *EthClientMock_HeaderByNumber_Call) RunAndReturn(run func(context.Context, *big.Int) (*coretypes.Header, error)) *EthClientMock_HeaderByNumber_Call { +func (_c *EthClient_HeaderByNumber_Call) RunAndReturn(run func(context.Context, *big.Int) (*coretypes.Header, error)) *EthClient_HeaderByNumber_Call { _c.Call.Return(run) return _c } -// NewEthClientMock creates a new instance of EthClientMock. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// NewEthClient creates a new instance of EthClient. 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 NewEthClientMock(t interface { +func NewEthClient(t interface { mock.TestingT Cleanup(func()) -}) *EthClientMock { - mock := &EthClientMock{} +}) *EthClient { + mock := &EthClient{} mock.Mock.Test(t) t.Cleanup(func() { mock.AssertExpectations(t) }) diff --git a/aggsender/mocks/generic_subscriber.go b/aggsender/mocks/generic_subscriber.go new file mode 100644 index 00000000..b4bee4b4 --- /dev/null +++ b/aggsender/mocks/generic_subscriber.go @@ -0,0 +1,113 @@ +// Code generated by mockery. DO NOT EDIT. + +package mocks + +import mock "github.com/stretchr/testify/mock" + +// GenericSubscriber is an autogenerated mock type for the GenericSubscriber type +type GenericSubscriber[T interface{}] struct { + mock.Mock +} + +type GenericSubscriber_Expecter[T interface{}] struct { + mock *mock.Mock +} + +func (_m *GenericSubscriber[T]) EXPECT() *GenericSubscriber_Expecter[T] { + return &GenericSubscriber_Expecter[T]{mock: &_m.Mock} +} + +// Publish provides a mock function with given fields: data +func (_m *GenericSubscriber[T]) Publish(data T) { + _m.Called(data) +} + +// GenericSubscriber_Publish_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Publish' +type GenericSubscriber_Publish_Call[T interface{}] struct { + *mock.Call +} + +// Publish is a helper method to define mock.On call +// - data T +func (_e *GenericSubscriber_Expecter[T]) Publish(data interface{}) *GenericSubscriber_Publish_Call[T] { + return &GenericSubscriber_Publish_Call[T]{Call: _e.mock.On("Publish", data)} +} + +func (_c *GenericSubscriber_Publish_Call[T]) Run(run func(data T)) *GenericSubscriber_Publish_Call[T] { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(T)) + }) + return _c +} + +func (_c *GenericSubscriber_Publish_Call[T]) Return() *GenericSubscriber_Publish_Call[T] { + _c.Call.Return() + return _c +} + +func (_c *GenericSubscriber_Publish_Call[T]) RunAndReturn(run func(T)) *GenericSubscriber_Publish_Call[T] { + _c.Call.Return(run) + return _c +} + +// Subscribe provides a mock function with given fields: subscriberName +func (_m *GenericSubscriber[T]) Subscribe(subscriberName string) <-chan T { + ret := _m.Called(subscriberName) + + if len(ret) == 0 { + panic("no return value specified for Subscribe") + } + + var r0 <-chan T + if rf, ok := ret.Get(0).(func(string) <-chan T); ok { + r0 = rf(subscriberName) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(<-chan T) + } + } + + return r0 +} + +// GenericSubscriber_Subscribe_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Subscribe' +type GenericSubscriber_Subscribe_Call[T interface{}] struct { + *mock.Call +} + +// Subscribe is a helper method to define mock.On call +// - subscriberName string +func (_e *GenericSubscriber_Expecter[T]) Subscribe(subscriberName interface{}) *GenericSubscriber_Subscribe_Call[T] { + return &GenericSubscriber_Subscribe_Call[T]{Call: _e.mock.On("Subscribe", subscriberName)} +} + +func (_c *GenericSubscriber_Subscribe_Call[T]) Run(run func(subscriberName string)) *GenericSubscriber_Subscribe_Call[T] { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(string)) + }) + return _c +} + +func (_c *GenericSubscriber_Subscribe_Call[T]) Return(_a0 <-chan T) *GenericSubscriber_Subscribe_Call[T] { + _c.Call.Return(_a0) + return _c +} + +func (_c *GenericSubscriber_Subscribe_Call[T]) RunAndReturn(run func(string) <-chan T) *GenericSubscriber_Subscribe_Call[T] { + _c.Call.Return(run) + return _c +} + +// NewGenericSubscriber creates a new instance of GenericSubscriber. 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 NewGenericSubscriber[T interface{}](t interface { + mock.TestingT + Cleanup(func()) +}) *GenericSubscriber[T] { + mock := &GenericSubscriber[T]{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/aggsender/mocks/l1_info_tree_syncer.go b/aggsender/mocks/l1_info_tree_syncer.go new file mode 100644 index 00000000..70ac97de --- /dev/null +++ b/aggsender/mocks/l1_info_tree_syncer.go @@ -0,0 +1,217 @@ +// Code generated by mockery. DO NOT EDIT. + +package mocks + +import ( + context "context" + + common "github.com/ethereum/go-ethereum/common" + + l1infotreesync "github.com/0xPolygon/cdk/l1infotreesync" + + mock "github.com/stretchr/testify/mock" + + treetypes "github.com/0xPolygon/cdk/tree/types" +) + +// L1InfoTreeSyncer is an autogenerated mock type for the L1InfoTreeSyncer type +type L1InfoTreeSyncer struct { + mock.Mock +} + +type L1InfoTreeSyncer_Expecter struct { + mock *mock.Mock +} + +func (_m *L1InfoTreeSyncer) EXPECT() *L1InfoTreeSyncer_Expecter { + return &L1InfoTreeSyncer_Expecter{mock: &_m.Mock} +} + +// GetInfoByGlobalExitRoot provides a mock function with given fields: globalExitRoot +func (_m *L1InfoTreeSyncer) GetInfoByGlobalExitRoot(globalExitRoot common.Hash) (*l1infotreesync.L1InfoTreeLeaf, error) { + ret := _m.Called(globalExitRoot) + + if len(ret) == 0 { + panic("no return value specified for GetInfoByGlobalExitRoot") + } + + var r0 *l1infotreesync.L1InfoTreeLeaf + var r1 error + if rf, ok := ret.Get(0).(func(common.Hash) (*l1infotreesync.L1InfoTreeLeaf, error)); ok { + return rf(globalExitRoot) + } + if rf, ok := ret.Get(0).(func(common.Hash) *l1infotreesync.L1InfoTreeLeaf); ok { + r0 = rf(globalExitRoot) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*l1infotreesync.L1InfoTreeLeaf) + } + } + + if rf, ok := ret.Get(1).(func(common.Hash) error); ok { + r1 = rf(globalExitRoot) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// L1InfoTreeSyncer_GetInfoByGlobalExitRoot_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetInfoByGlobalExitRoot' +type L1InfoTreeSyncer_GetInfoByGlobalExitRoot_Call struct { + *mock.Call +} + +// GetInfoByGlobalExitRoot is a helper method to define mock.On call +// - globalExitRoot common.Hash +func (_e *L1InfoTreeSyncer_Expecter) GetInfoByGlobalExitRoot(globalExitRoot interface{}) *L1InfoTreeSyncer_GetInfoByGlobalExitRoot_Call { + return &L1InfoTreeSyncer_GetInfoByGlobalExitRoot_Call{Call: _e.mock.On("GetInfoByGlobalExitRoot", globalExitRoot)} +} + +func (_c *L1InfoTreeSyncer_GetInfoByGlobalExitRoot_Call) Run(run func(globalExitRoot common.Hash)) *L1InfoTreeSyncer_GetInfoByGlobalExitRoot_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(common.Hash)) + }) + return _c +} + +func (_c *L1InfoTreeSyncer_GetInfoByGlobalExitRoot_Call) Return(_a0 *l1infotreesync.L1InfoTreeLeaf, _a1 error) *L1InfoTreeSyncer_GetInfoByGlobalExitRoot_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *L1InfoTreeSyncer_GetInfoByGlobalExitRoot_Call) RunAndReturn(run func(common.Hash) (*l1infotreesync.L1InfoTreeLeaf, error)) *L1InfoTreeSyncer_GetInfoByGlobalExitRoot_Call { + _c.Call.Return(run) + return _c +} + +// GetL1InfoTreeMerkleProofFromIndexToRoot provides a mock function with given fields: ctx, index, root +func (_m *L1InfoTreeSyncer) GetL1InfoTreeMerkleProofFromIndexToRoot(ctx context.Context, index uint32, root common.Hash) (treetypes.Proof, error) { + ret := _m.Called(ctx, index, root) + + if len(ret) == 0 { + panic("no return value specified for GetL1InfoTreeMerkleProofFromIndexToRoot") + } + + var r0 treetypes.Proof + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, uint32, common.Hash) (treetypes.Proof, error)); ok { + return rf(ctx, index, root) + } + if rf, ok := ret.Get(0).(func(context.Context, uint32, common.Hash) treetypes.Proof); ok { + r0 = rf(ctx, index, root) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(treetypes.Proof) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, uint32, common.Hash) error); ok { + r1 = rf(ctx, index, root) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// L1InfoTreeSyncer_GetL1InfoTreeMerkleProofFromIndexToRoot_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetL1InfoTreeMerkleProofFromIndexToRoot' +type L1InfoTreeSyncer_GetL1InfoTreeMerkleProofFromIndexToRoot_Call struct { + *mock.Call +} + +// GetL1InfoTreeMerkleProofFromIndexToRoot is a helper method to define mock.On call +// - ctx context.Context +// - index uint32 +// - root common.Hash +func (_e *L1InfoTreeSyncer_Expecter) GetL1InfoTreeMerkleProofFromIndexToRoot(ctx interface{}, index interface{}, root interface{}) *L1InfoTreeSyncer_GetL1InfoTreeMerkleProofFromIndexToRoot_Call { + return &L1InfoTreeSyncer_GetL1InfoTreeMerkleProofFromIndexToRoot_Call{Call: _e.mock.On("GetL1InfoTreeMerkleProofFromIndexToRoot", ctx, index, root)} +} + +func (_c *L1InfoTreeSyncer_GetL1InfoTreeMerkleProofFromIndexToRoot_Call) Run(run func(ctx context.Context, index uint32, root common.Hash)) *L1InfoTreeSyncer_GetL1InfoTreeMerkleProofFromIndexToRoot_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(uint32), args[2].(common.Hash)) + }) + return _c +} + +func (_c *L1InfoTreeSyncer_GetL1InfoTreeMerkleProofFromIndexToRoot_Call) Return(_a0 treetypes.Proof, _a1 error) *L1InfoTreeSyncer_GetL1InfoTreeMerkleProofFromIndexToRoot_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *L1InfoTreeSyncer_GetL1InfoTreeMerkleProofFromIndexToRoot_Call) RunAndReturn(run func(context.Context, uint32, common.Hash) (treetypes.Proof, error)) *L1InfoTreeSyncer_GetL1InfoTreeMerkleProofFromIndexToRoot_Call { + _c.Call.Return(run) + return _c +} + +// GetL1InfoTreeRootByIndex provides a mock function with given fields: ctx, index +func (_m *L1InfoTreeSyncer) GetL1InfoTreeRootByIndex(ctx context.Context, index uint32) (treetypes.Root, error) { + ret := _m.Called(ctx, index) + + if len(ret) == 0 { + panic("no return value specified for GetL1InfoTreeRootByIndex") + } + + var r0 treetypes.Root + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, uint32) (treetypes.Root, error)); ok { + return rf(ctx, index) + } + if rf, ok := ret.Get(0).(func(context.Context, uint32) treetypes.Root); ok { + r0 = rf(ctx, index) + } else { + r0 = ret.Get(0).(treetypes.Root) + } + + if rf, ok := ret.Get(1).(func(context.Context, uint32) error); ok { + r1 = rf(ctx, index) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// L1InfoTreeSyncer_GetL1InfoTreeRootByIndex_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetL1InfoTreeRootByIndex' +type L1InfoTreeSyncer_GetL1InfoTreeRootByIndex_Call struct { + *mock.Call +} + +// GetL1InfoTreeRootByIndex is a helper method to define mock.On call +// - ctx context.Context +// - index uint32 +func (_e *L1InfoTreeSyncer_Expecter) GetL1InfoTreeRootByIndex(ctx interface{}, index interface{}) *L1InfoTreeSyncer_GetL1InfoTreeRootByIndex_Call { + return &L1InfoTreeSyncer_GetL1InfoTreeRootByIndex_Call{Call: _e.mock.On("GetL1InfoTreeRootByIndex", ctx, index)} +} + +func (_c *L1InfoTreeSyncer_GetL1InfoTreeRootByIndex_Call) Run(run func(ctx context.Context, index uint32)) *L1InfoTreeSyncer_GetL1InfoTreeRootByIndex_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(uint32)) + }) + return _c +} + +func (_c *L1InfoTreeSyncer_GetL1InfoTreeRootByIndex_Call) Return(_a0 treetypes.Root, _a1 error) *L1InfoTreeSyncer_GetL1InfoTreeRootByIndex_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *L1InfoTreeSyncer_GetL1InfoTreeRootByIndex_Call) RunAndReturn(run func(context.Context, uint32) (treetypes.Root, error)) *L1InfoTreeSyncer_GetL1InfoTreeRootByIndex_Call { + _c.Call.Return(run) + return _c +} + +// NewL1InfoTreeSyncer creates a new instance of L1InfoTreeSyncer. 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 NewL1InfoTreeSyncer(t interface { + mock.TestingT + Cleanup(func()) +}) *L1InfoTreeSyncer { + mock := &L1InfoTreeSyncer{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/aggsender/mocks/l2_bridge_syncer.go b/aggsender/mocks/l2_bridge_syncer.go new file mode 100644 index 00000000..800007ff --- /dev/null +++ b/aggsender/mocks/l2_bridge_syncer.go @@ -0,0 +1,423 @@ +// Code generated by mockery. DO NOT EDIT. + +package mocks + +import ( + bridgesync "github.com/0xPolygon/cdk/bridgesync" + common "github.com/ethereum/go-ethereum/common" + + context "context" + + etherman "github.com/0xPolygon/cdk/etherman" + + mock "github.com/stretchr/testify/mock" + + treetypes "github.com/0xPolygon/cdk/tree/types" +) + +// L2BridgeSyncer is an autogenerated mock type for the L2BridgeSyncer type +type L2BridgeSyncer struct { + mock.Mock +} + +type L2BridgeSyncer_Expecter struct { + mock *mock.Mock +} + +func (_m *L2BridgeSyncer) EXPECT() *L2BridgeSyncer_Expecter { + return &L2BridgeSyncer_Expecter{mock: &_m.Mock} +} + +// BlockFinality provides a mock function with given fields: +func (_m *L2BridgeSyncer) BlockFinality() etherman.BlockNumberFinality { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for BlockFinality") + } + + var r0 etherman.BlockNumberFinality + if rf, ok := ret.Get(0).(func() etherman.BlockNumberFinality); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(etherman.BlockNumberFinality) + } + + return r0 +} + +// L2BridgeSyncer_BlockFinality_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'BlockFinality' +type L2BridgeSyncer_BlockFinality_Call struct { + *mock.Call +} + +// BlockFinality is a helper method to define mock.On call +func (_e *L2BridgeSyncer_Expecter) BlockFinality() *L2BridgeSyncer_BlockFinality_Call { + return &L2BridgeSyncer_BlockFinality_Call{Call: _e.mock.On("BlockFinality")} +} + +func (_c *L2BridgeSyncer_BlockFinality_Call) Run(run func()) *L2BridgeSyncer_BlockFinality_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *L2BridgeSyncer_BlockFinality_Call) Return(_a0 etherman.BlockNumberFinality) *L2BridgeSyncer_BlockFinality_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *L2BridgeSyncer_BlockFinality_Call) RunAndReturn(run func() etherman.BlockNumberFinality) *L2BridgeSyncer_BlockFinality_Call { + _c.Call.Return(run) + return _c +} + +// GetBlockByLER provides a mock function with given fields: ctx, ler +func (_m *L2BridgeSyncer) GetBlockByLER(ctx context.Context, ler common.Hash) (uint64, error) { + ret := _m.Called(ctx, ler) + + if len(ret) == 0 { + panic("no return value specified for GetBlockByLER") + } + + var r0 uint64 + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, common.Hash) (uint64, error)); ok { + return rf(ctx, ler) + } + if rf, ok := ret.Get(0).(func(context.Context, common.Hash) uint64); ok { + r0 = rf(ctx, ler) + } else { + r0 = ret.Get(0).(uint64) + } + + if rf, ok := ret.Get(1).(func(context.Context, common.Hash) error); ok { + r1 = rf(ctx, ler) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// L2BridgeSyncer_GetBlockByLER_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetBlockByLER' +type L2BridgeSyncer_GetBlockByLER_Call struct { + *mock.Call +} + +// GetBlockByLER is a helper method to define mock.On call +// - ctx context.Context +// - ler common.Hash +func (_e *L2BridgeSyncer_Expecter) GetBlockByLER(ctx interface{}, ler interface{}) *L2BridgeSyncer_GetBlockByLER_Call { + return &L2BridgeSyncer_GetBlockByLER_Call{Call: _e.mock.On("GetBlockByLER", ctx, ler)} +} + +func (_c *L2BridgeSyncer_GetBlockByLER_Call) Run(run func(ctx context.Context, ler common.Hash)) *L2BridgeSyncer_GetBlockByLER_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(common.Hash)) + }) + return _c +} + +func (_c *L2BridgeSyncer_GetBlockByLER_Call) Return(_a0 uint64, _a1 error) *L2BridgeSyncer_GetBlockByLER_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *L2BridgeSyncer_GetBlockByLER_Call) RunAndReturn(run func(context.Context, common.Hash) (uint64, error)) *L2BridgeSyncer_GetBlockByLER_Call { + _c.Call.Return(run) + return _c +} + +// GetBridgesPublished provides a mock function with given fields: ctx, fromBlock, toBlock +func (_m *L2BridgeSyncer) GetBridgesPublished(ctx context.Context, fromBlock uint64, toBlock uint64) ([]bridgesync.Bridge, error) { + ret := _m.Called(ctx, fromBlock, toBlock) + + if len(ret) == 0 { + panic("no return value specified for GetBridgesPublished") + } + + var r0 []bridgesync.Bridge + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, uint64, uint64) ([]bridgesync.Bridge, error)); ok { + return rf(ctx, fromBlock, toBlock) + } + if rf, ok := ret.Get(0).(func(context.Context, uint64, uint64) []bridgesync.Bridge); ok { + r0 = rf(ctx, fromBlock, toBlock) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]bridgesync.Bridge) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, uint64, uint64) error); ok { + r1 = rf(ctx, fromBlock, toBlock) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// L2BridgeSyncer_GetBridgesPublished_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetBridgesPublished' +type L2BridgeSyncer_GetBridgesPublished_Call struct { + *mock.Call +} + +// GetBridgesPublished is a helper method to define mock.On call +// - ctx context.Context +// - fromBlock uint64 +// - toBlock uint64 +func (_e *L2BridgeSyncer_Expecter) GetBridgesPublished(ctx interface{}, fromBlock interface{}, toBlock interface{}) *L2BridgeSyncer_GetBridgesPublished_Call { + return &L2BridgeSyncer_GetBridgesPublished_Call{Call: _e.mock.On("GetBridgesPublished", ctx, fromBlock, toBlock)} +} + +func (_c *L2BridgeSyncer_GetBridgesPublished_Call) Run(run func(ctx context.Context, fromBlock uint64, toBlock uint64)) *L2BridgeSyncer_GetBridgesPublished_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(uint64), args[2].(uint64)) + }) + return _c +} + +func (_c *L2BridgeSyncer_GetBridgesPublished_Call) Return(_a0 []bridgesync.Bridge, _a1 error) *L2BridgeSyncer_GetBridgesPublished_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *L2BridgeSyncer_GetBridgesPublished_Call) RunAndReturn(run func(context.Context, uint64, uint64) ([]bridgesync.Bridge, error)) *L2BridgeSyncer_GetBridgesPublished_Call { + _c.Call.Return(run) + return _c +} + +// GetClaims provides a mock function with given fields: ctx, fromBlock, toBlock +func (_m *L2BridgeSyncer) GetClaims(ctx context.Context, fromBlock uint64, toBlock uint64) ([]bridgesync.Claim, error) { + ret := _m.Called(ctx, fromBlock, toBlock) + + if len(ret) == 0 { + panic("no return value specified for GetClaims") + } + + var r0 []bridgesync.Claim + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, uint64, uint64) ([]bridgesync.Claim, error)); ok { + return rf(ctx, fromBlock, toBlock) + } + if rf, ok := ret.Get(0).(func(context.Context, uint64, uint64) []bridgesync.Claim); ok { + r0 = rf(ctx, fromBlock, toBlock) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]bridgesync.Claim) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, uint64, uint64) error); ok { + r1 = rf(ctx, fromBlock, toBlock) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// L2BridgeSyncer_GetClaims_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetClaims' +type L2BridgeSyncer_GetClaims_Call struct { + *mock.Call +} + +// GetClaims is a helper method to define mock.On call +// - ctx context.Context +// - fromBlock uint64 +// - toBlock uint64 +func (_e *L2BridgeSyncer_Expecter) GetClaims(ctx interface{}, fromBlock interface{}, toBlock interface{}) *L2BridgeSyncer_GetClaims_Call { + return &L2BridgeSyncer_GetClaims_Call{Call: _e.mock.On("GetClaims", ctx, fromBlock, toBlock)} +} + +func (_c *L2BridgeSyncer_GetClaims_Call) Run(run func(ctx context.Context, fromBlock uint64, toBlock uint64)) *L2BridgeSyncer_GetClaims_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(uint64), args[2].(uint64)) + }) + return _c +} + +func (_c *L2BridgeSyncer_GetClaims_Call) Return(_a0 []bridgesync.Claim, _a1 error) *L2BridgeSyncer_GetClaims_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *L2BridgeSyncer_GetClaims_Call) RunAndReturn(run func(context.Context, uint64, uint64) ([]bridgesync.Claim, error)) *L2BridgeSyncer_GetClaims_Call { + _c.Call.Return(run) + return _c +} + +// GetExitRootByIndex provides a mock function with given fields: ctx, index +func (_m *L2BridgeSyncer) GetExitRootByIndex(ctx context.Context, index uint32) (treetypes.Root, error) { + ret := _m.Called(ctx, index) + + if len(ret) == 0 { + panic("no return value specified for GetExitRootByIndex") + } + + var r0 treetypes.Root + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, uint32) (treetypes.Root, error)); ok { + return rf(ctx, index) + } + if rf, ok := ret.Get(0).(func(context.Context, uint32) treetypes.Root); ok { + r0 = rf(ctx, index) + } else { + r0 = ret.Get(0).(treetypes.Root) + } + + if rf, ok := ret.Get(1).(func(context.Context, uint32) error); ok { + r1 = rf(ctx, index) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// L2BridgeSyncer_GetExitRootByIndex_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetExitRootByIndex' +type L2BridgeSyncer_GetExitRootByIndex_Call struct { + *mock.Call +} + +// GetExitRootByIndex is a helper method to define mock.On call +// - ctx context.Context +// - index uint32 +func (_e *L2BridgeSyncer_Expecter) GetExitRootByIndex(ctx interface{}, index interface{}) *L2BridgeSyncer_GetExitRootByIndex_Call { + return &L2BridgeSyncer_GetExitRootByIndex_Call{Call: _e.mock.On("GetExitRootByIndex", ctx, index)} +} + +func (_c *L2BridgeSyncer_GetExitRootByIndex_Call) Run(run func(ctx context.Context, index uint32)) *L2BridgeSyncer_GetExitRootByIndex_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(uint32)) + }) + return _c +} + +func (_c *L2BridgeSyncer_GetExitRootByIndex_Call) Return(_a0 treetypes.Root, _a1 error) *L2BridgeSyncer_GetExitRootByIndex_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *L2BridgeSyncer_GetExitRootByIndex_Call) RunAndReturn(run func(context.Context, uint32) (treetypes.Root, error)) *L2BridgeSyncer_GetExitRootByIndex_Call { + _c.Call.Return(run) + return _c +} + +// GetLastProcessedBlock provides a mock function with given fields: ctx +func (_m *L2BridgeSyncer) GetLastProcessedBlock(ctx context.Context) (uint64, error) { + ret := _m.Called(ctx) + + if len(ret) == 0 { + panic("no return value specified for GetLastProcessedBlock") + } + + var r0 uint64 + var r1 error + if rf, ok := ret.Get(0).(func(context.Context) (uint64, error)); ok { + return rf(ctx) + } + if rf, ok := ret.Get(0).(func(context.Context) uint64); ok { + r0 = rf(ctx) + } else { + r0 = ret.Get(0).(uint64) + } + + if rf, ok := ret.Get(1).(func(context.Context) error); ok { + r1 = rf(ctx) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// L2BridgeSyncer_GetLastProcessedBlock_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetLastProcessedBlock' +type L2BridgeSyncer_GetLastProcessedBlock_Call struct { + *mock.Call +} + +// GetLastProcessedBlock is a helper method to define mock.On call +// - ctx context.Context +func (_e *L2BridgeSyncer_Expecter) GetLastProcessedBlock(ctx interface{}) *L2BridgeSyncer_GetLastProcessedBlock_Call { + return &L2BridgeSyncer_GetLastProcessedBlock_Call{Call: _e.mock.On("GetLastProcessedBlock", ctx)} +} + +func (_c *L2BridgeSyncer_GetLastProcessedBlock_Call) Run(run func(ctx context.Context)) *L2BridgeSyncer_GetLastProcessedBlock_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context)) + }) + return _c +} + +func (_c *L2BridgeSyncer_GetLastProcessedBlock_Call) Return(_a0 uint64, _a1 error) *L2BridgeSyncer_GetLastProcessedBlock_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *L2BridgeSyncer_GetLastProcessedBlock_Call) RunAndReturn(run func(context.Context) (uint64, error)) *L2BridgeSyncer_GetLastProcessedBlock_Call { + _c.Call.Return(run) + return _c +} + +// OriginNetwork provides a mock function with given fields: +func (_m *L2BridgeSyncer) OriginNetwork() uint32 { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for OriginNetwork") + } + + var r0 uint32 + if rf, ok := ret.Get(0).(func() uint32); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(uint32) + } + + return r0 +} + +// L2BridgeSyncer_OriginNetwork_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'OriginNetwork' +type L2BridgeSyncer_OriginNetwork_Call struct { + *mock.Call +} + +// OriginNetwork is a helper method to define mock.On call +func (_e *L2BridgeSyncer_Expecter) OriginNetwork() *L2BridgeSyncer_OriginNetwork_Call { + return &L2BridgeSyncer_OriginNetwork_Call{Call: _e.mock.On("OriginNetwork")} +} + +func (_c *L2BridgeSyncer_OriginNetwork_Call) Run(run func()) *L2BridgeSyncer_OriginNetwork_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *L2BridgeSyncer_OriginNetwork_Call) Return(_a0 uint32) *L2BridgeSyncer_OriginNetwork_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *L2BridgeSyncer_OriginNetwork_Call) RunAndReturn(run func() uint32) *L2BridgeSyncer_OriginNetwork_Call { + _c.Call.Return(run) + return _c +} + +// NewL2BridgeSyncer creates a new instance of L2BridgeSyncer. 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 NewL2BridgeSyncer(t interface { + mock.TestingT + Cleanup(func()) +}) *L2BridgeSyncer { + mock := &L2BridgeSyncer{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/aggsender/mocks/logger.go b/aggsender/mocks/logger.go new file mode 100644 index 00000000..bb26739e --- /dev/null +++ b/aggsender/mocks/logger.go @@ -0,0 +1,376 @@ +// Code generated by mockery. DO NOT EDIT. + +package mocks + +import mock "github.com/stretchr/testify/mock" + +// Logger is an autogenerated mock type for the Logger type +type Logger struct { + mock.Mock +} + +type Logger_Expecter struct { + mock *mock.Mock +} + +func (_m *Logger) EXPECT() *Logger_Expecter { + return &Logger_Expecter{mock: &_m.Mock} +} + +// Debug provides a mock function with given fields: args +func (_m *Logger) Debug(args ...interface{}) { + var _ca []interface{} + _ca = append(_ca, args...) + _m.Called(_ca...) +} + +// Logger_Debug_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Debug' +type Logger_Debug_Call struct { + *mock.Call +} + +// Debug is a helper method to define mock.On call +// - args ...interface{} +func (_e *Logger_Expecter) Debug(args ...interface{}) *Logger_Debug_Call { + return &Logger_Debug_Call{Call: _e.mock.On("Debug", + append([]interface{}{}, args...)...)} +} + +func (_c *Logger_Debug_Call) Run(run func(args ...interface{})) *Logger_Debug_Call { + _c.Call.Run(func(args mock.Arguments) { + variadicArgs := make([]interface{}, len(args)-0) + for i, a := range args[0:] { + if a != nil { + variadicArgs[i] = a.(interface{}) + } + } + run(variadicArgs...) + }) + return _c +} + +func (_c *Logger_Debug_Call) Return() *Logger_Debug_Call { + _c.Call.Return() + return _c +} + +func (_c *Logger_Debug_Call) RunAndReturn(run func(...interface{})) *Logger_Debug_Call { + _c.Call.Return(run) + return _c +} + +// Debugf provides a mock function with given fields: format, args +func (_m *Logger) Debugf(format string, args ...interface{}) { + var _ca []interface{} + _ca = append(_ca, format) + _ca = append(_ca, args...) + _m.Called(_ca...) +} + +// Logger_Debugf_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Debugf' +type Logger_Debugf_Call struct { + *mock.Call +} + +// Debugf is a helper method to define mock.On call +// - format string +// - args ...interface{} +func (_e *Logger_Expecter) Debugf(format interface{}, args ...interface{}) *Logger_Debugf_Call { + return &Logger_Debugf_Call{Call: _e.mock.On("Debugf", + append([]interface{}{format}, args...)...)} +} + +func (_c *Logger_Debugf_Call) Run(run func(format string, args ...interface{})) *Logger_Debugf_Call { + _c.Call.Run(func(args mock.Arguments) { + variadicArgs := make([]interface{}, len(args)-1) + for i, a := range args[1:] { + if a != nil { + variadicArgs[i] = a.(interface{}) + } + } + run(args[0].(string), variadicArgs...) + }) + return _c +} + +func (_c *Logger_Debugf_Call) Return() *Logger_Debugf_Call { + _c.Call.Return() + return _c +} + +func (_c *Logger_Debugf_Call) RunAndReturn(run func(string, ...interface{})) *Logger_Debugf_Call { + _c.Call.Return(run) + return _c +} + +// Error provides a mock function with given fields: args +func (_m *Logger) Error(args ...interface{}) { + var _ca []interface{} + _ca = append(_ca, args...) + _m.Called(_ca...) +} + +// Logger_Error_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Error' +type Logger_Error_Call struct { + *mock.Call +} + +// Error is a helper method to define mock.On call +// - args ...interface{} +func (_e *Logger_Expecter) Error(args ...interface{}) *Logger_Error_Call { + return &Logger_Error_Call{Call: _e.mock.On("Error", + append([]interface{}{}, args...)...)} +} + +func (_c *Logger_Error_Call) Run(run func(args ...interface{})) *Logger_Error_Call { + _c.Call.Run(func(args mock.Arguments) { + variadicArgs := make([]interface{}, len(args)-0) + for i, a := range args[0:] { + if a != nil { + variadicArgs[i] = a.(interface{}) + } + } + run(variadicArgs...) + }) + return _c +} + +func (_c *Logger_Error_Call) Return() *Logger_Error_Call { + _c.Call.Return() + return _c +} + +func (_c *Logger_Error_Call) RunAndReturn(run func(...interface{})) *Logger_Error_Call { + _c.Call.Return(run) + return _c +} + +// Errorf provides a mock function with given fields: format, args +func (_m *Logger) Errorf(format string, args ...interface{}) { + var _ca []interface{} + _ca = append(_ca, format) + _ca = append(_ca, args...) + _m.Called(_ca...) +} + +// Logger_Errorf_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Errorf' +type Logger_Errorf_Call struct { + *mock.Call +} + +// Errorf is a helper method to define mock.On call +// - format string +// - args ...interface{} +func (_e *Logger_Expecter) Errorf(format interface{}, args ...interface{}) *Logger_Errorf_Call { + return &Logger_Errorf_Call{Call: _e.mock.On("Errorf", + append([]interface{}{format}, args...)...)} +} + +func (_c *Logger_Errorf_Call) Run(run func(format string, args ...interface{})) *Logger_Errorf_Call { + _c.Call.Run(func(args mock.Arguments) { + variadicArgs := make([]interface{}, len(args)-1) + for i, a := range args[1:] { + if a != nil { + variadicArgs[i] = a.(interface{}) + } + } + run(args[0].(string), variadicArgs...) + }) + return _c +} + +func (_c *Logger_Errorf_Call) Return() *Logger_Errorf_Call { + _c.Call.Return() + return _c +} + +func (_c *Logger_Errorf_Call) RunAndReturn(run func(string, ...interface{})) *Logger_Errorf_Call { + _c.Call.Return(run) + return _c +} + +// Info provides a mock function with given fields: args +func (_m *Logger) Info(args ...interface{}) { + var _ca []interface{} + _ca = append(_ca, args...) + _m.Called(_ca...) +} + +// Logger_Info_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Info' +type Logger_Info_Call struct { + *mock.Call +} + +// Info is a helper method to define mock.On call +// - args ...interface{} +func (_e *Logger_Expecter) Info(args ...interface{}) *Logger_Info_Call { + return &Logger_Info_Call{Call: _e.mock.On("Info", + append([]interface{}{}, args...)...)} +} + +func (_c *Logger_Info_Call) Run(run func(args ...interface{})) *Logger_Info_Call { + _c.Call.Run(func(args mock.Arguments) { + variadicArgs := make([]interface{}, len(args)-0) + for i, a := range args[0:] { + if a != nil { + variadicArgs[i] = a.(interface{}) + } + } + run(variadicArgs...) + }) + return _c +} + +func (_c *Logger_Info_Call) Return() *Logger_Info_Call { + _c.Call.Return() + return _c +} + +func (_c *Logger_Info_Call) RunAndReturn(run func(...interface{})) *Logger_Info_Call { + _c.Call.Return(run) + return _c +} + +// Infof provides a mock function with given fields: format, args +func (_m *Logger) Infof(format string, args ...interface{}) { + var _ca []interface{} + _ca = append(_ca, format) + _ca = append(_ca, args...) + _m.Called(_ca...) +} + +// Logger_Infof_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Infof' +type Logger_Infof_Call struct { + *mock.Call +} + +// Infof is a helper method to define mock.On call +// - format string +// - args ...interface{} +func (_e *Logger_Expecter) Infof(format interface{}, args ...interface{}) *Logger_Infof_Call { + return &Logger_Infof_Call{Call: _e.mock.On("Infof", + append([]interface{}{format}, args...)...)} +} + +func (_c *Logger_Infof_Call) Run(run func(format string, args ...interface{})) *Logger_Infof_Call { + _c.Call.Run(func(args mock.Arguments) { + variadicArgs := make([]interface{}, len(args)-1) + for i, a := range args[1:] { + if a != nil { + variadicArgs[i] = a.(interface{}) + } + } + run(args[0].(string), variadicArgs...) + }) + return _c +} + +func (_c *Logger_Infof_Call) Return() *Logger_Infof_Call { + _c.Call.Return() + return _c +} + +func (_c *Logger_Infof_Call) RunAndReturn(run func(string, ...interface{})) *Logger_Infof_Call { + _c.Call.Return(run) + return _c +} + +// Warn provides a mock function with given fields: args +func (_m *Logger) Warn(args ...interface{}) { + var _ca []interface{} + _ca = append(_ca, args...) + _m.Called(_ca...) +} + +// Logger_Warn_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Warn' +type Logger_Warn_Call struct { + *mock.Call +} + +// Warn is a helper method to define mock.On call +// - args ...interface{} +func (_e *Logger_Expecter) Warn(args ...interface{}) *Logger_Warn_Call { + return &Logger_Warn_Call{Call: _e.mock.On("Warn", + append([]interface{}{}, args...)...)} +} + +func (_c *Logger_Warn_Call) Run(run func(args ...interface{})) *Logger_Warn_Call { + _c.Call.Run(func(args mock.Arguments) { + variadicArgs := make([]interface{}, len(args)-0) + for i, a := range args[0:] { + if a != nil { + variadicArgs[i] = a.(interface{}) + } + } + run(variadicArgs...) + }) + return _c +} + +func (_c *Logger_Warn_Call) Return() *Logger_Warn_Call { + _c.Call.Return() + return _c +} + +func (_c *Logger_Warn_Call) RunAndReturn(run func(...interface{})) *Logger_Warn_Call { + _c.Call.Return(run) + return _c +} + +// Warnf provides a mock function with given fields: format, args +func (_m *Logger) Warnf(format string, args ...interface{}) { + var _ca []interface{} + _ca = append(_ca, format) + _ca = append(_ca, args...) + _m.Called(_ca...) +} + +// Logger_Warnf_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Warnf' +type Logger_Warnf_Call struct { + *mock.Call +} + +// Warnf is a helper method to define mock.On call +// - format string +// - args ...interface{} +func (_e *Logger_Expecter) Warnf(format interface{}, args ...interface{}) *Logger_Warnf_Call { + return &Logger_Warnf_Call{Call: _e.mock.On("Warnf", + append([]interface{}{format}, args...)...)} +} + +func (_c *Logger_Warnf_Call) Run(run func(format string, args ...interface{})) *Logger_Warnf_Call { + _c.Call.Run(func(args mock.Arguments) { + variadicArgs := make([]interface{}, len(args)-1) + for i, a := range args[1:] { + if a != nil { + variadicArgs[i] = a.(interface{}) + } + } + run(args[0].(string), variadicArgs...) + }) + return _c +} + +func (_c *Logger_Warnf_Call) Return() *Logger_Warnf_Call { + _c.Call.Return() + return _c +} + +func (_c *Logger_Warnf_Call) RunAndReturn(run func(string, ...interface{})) *Logger_Warnf_Call { + _c.Call.Return(run) + return _c +} + +// NewLogger creates a new instance of Logger. 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 NewLogger(t interface { + mock.TestingT + Cleanup(func()) +}) *Logger { + mock := &Logger{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/aggsender/mocks/mock_aggsender_storage.go b/aggsender/mocks/mock_aggsender_storage.go deleted file mode 100644 index 17f8d227..00000000 --- a/aggsender/mocks/mock_aggsender_storage.go +++ /dev/null @@ -1,351 +0,0 @@ -// Code generated by mockery. DO NOT EDIT. - -package mocks - -import ( - agglayer "github.com/0xPolygon/cdk/agglayer" - common "github.com/ethereum/go-ethereum/common" - - context "context" - - mock "github.com/stretchr/testify/mock" - - types "github.com/0xPolygon/cdk/aggsender/types" -) - -// AggSenderStorageMock is an autogenerated mock type for the AggSenderStorage type -type AggSenderStorageMock struct { - mock.Mock -} - -type AggSenderStorageMock_Expecter struct { - mock *mock.Mock -} - -func (_m *AggSenderStorageMock) EXPECT() *AggSenderStorageMock_Expecter { - return &AggSenderStorageMock_Expecter{mock: &_m.Mock} -} - -// DeleteCertificate provides a mock function with given fields: ctx, certificateID -func (_m *AggSenderStorageMock) DeleteCertificate(ctx context.Context, certificateID common.Hash) error { - ret := _m.Called(ctx, certificateID) - - if len(ret) == 0 { - panic("no return value specified for DeleteCertificate") - } - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, common.Hash) error); ok { - r0 = rf(ctx, certificateID) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// AggSenderStorageMock_DeleteCertificate_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DeleteCertificate' -type AggSenderStorageMock_DeleteCertificate_Call struct { - *mock.Call -} - -// DeleteCertificate is a helper method to define mock.On call -// - ctx context.Context -// - certificateID common.Hash -func (_e *AggSenderStorageMock_Expecter) DeleteCertificate(ctx interface{}, certificateID interface{}) *AggSenderStorageMock_DeleteCertificate_Call { - return &AggSenderStorageMock_DeleteCertificate_Call{Call: _e.mock.On("DeleteCertificate", ctx, certificateID)} -} - -func (_c *AggSenderStorageMock_DeleteCertificate_Call) Run(run func(ctx context.Context, certificateID common.Hash)) *AggSenderStorageMock_DeleteCertificate_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(common.Hash)) - }) - return _c -} - -func (_c *AggSenderStorageMock_DeleteCertificate_Call) Return(_a0 error) *AggSenderStorageMock_DeleteCertificate_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *AggSenderStorageMock_DeleteCertificate_Call) RunAndReturn(run func(context.Context, common.Hash) error) *AggSenderStorageMock_DeleteCertificate_Call { - _c.Call.Return(run) - return _c -} - -// GetCertificateByHeight provides a mock function with given fields: height -func (_m *AggSenderStorageMock) GetCertificateByHeight(height uint64) (types.CertificateInfo, error) { - ret := _m.Called(height) - - if len(ret) == 0 { - panic("no return value specified for GetCertificateByHeight") - } - - var r0 types.CertificateInfo - var r1 error - if rf, ok := ret.Get(0).(func(uint64) (types.CertificateInfo, error)); ok { - return rf(height) - } - if rf, ok := ret.Get(0).(func(uint64) types.CertificateInfo); ok { - r0 = rf(height) - } else { - r0 = ret.Get(0).(types.CertificateInfo) - } - - if rf, ok := ret.Get(1).(func(uint64) error); ok { - r1 = rf(height) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// AggSenderStorageMock_GetCertificateByHeight_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetCertificateByHeight' -type AggSenderStorageMock_GetCertificateByHeight_Call struct { - *mock.Call -} - -// GetCertificateByHeight is a helper method to define mock.On call -// - height uint64 -func (_e *AggSenderStorageMock_Expecter) GetCertificateByHeight(height interface{}) *AggSenderStorageMock_GetCertificateByHeight_Call { - return &AggSenderStorageMock_GetCertificateByHeight_Call{Call: _e.mock.On("GetCertificateByHeight", height)} -} - -func (_c *AggSenderStorageMock_GetCertificateByHeight_Call) Run(run func(height uint64)) *AggSenderStorageMock_GetCertificateByHeight_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(uint64)) - }) - return _c -} - -func (_c *AggSenderStorageMock_GetCertificateByHeight_Call) Return(_a0 types.CertificateInfo, _a1 error) *AggSenderStorageMock_GetCertificateByHeight_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *AggSenderStorageMock_GetCertificateByHeight_Call) RunAndReturn(run func(uint64) (types.CertificateInfo, error)) *AggSenderStorageMock_GetCertificateByHeight_Call { - _c.Call.Return(run) - return _c -} - -// GetCertificatesByStatus provides a mock function with given fields: status -func (_m *AggSenderStorageMock) GetCertificatesByStatus(status []agglayer.CertificateStatus) ([]*types.CertificateInfo, error) { - ret := _m.Called(status) - - if len(ret) == 0 { - panic("no return value specified for GetCertificatesByStatus") - } - - var r0 []*types.CertificateInfo - var r1 error - if rf, ok := ret.Get(0).(func([]agglayer.CertificateStatus) ([]*types.CertificateInfo, error)); ok { - return rf(status) - } - if rf, ok := ret.Get(0).(func([]agglayer.CertificateStatus) []*types.CertificateInfo); ok { - r0 = rf(status) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]*types.CertificateInfo) - } - } - - if rf, ok := ret.Get(1).(func([]agglayer.CertificateStatus) error); ok { - r1 = rf(status) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// AggSenderStorageMock_GetCertificatesByStatus_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetCertificatesByStatus' -type AggSenderStorageMock_GetCertificatesByStatus_Call struct { - *mock.Call -} - -// GetCertificatesByStatus is a helper method to define mock.On call -// - status []agglayer.CertificateStatus -func (_e *AggSenderStorageMock_Expecter) GetCertificatesByStatus(status interface{}) *AggSenderStorageMock_GetCertificatesByStatus_Call { - return &AggSenderStorageMock_GetCertificatesByStatus_Call{Call: _e.mock.On("GetCertificatesByStatus", status)} -} - -func (_c *AggSenderStorageMock_GetCertificatesByStatus_Call) Run(run func(status []agglayer.CertificateStatus)) *AggSenderStorageMock_GetCertificatesByStatus_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].([]agglayer.CertificateStatus)) - }) - return _c -} - -func (_c *AggSenderStorageMock_GetCertificatesByStatus_Call) Return(_a0 []*types.CertificateInfo, _a1 error) *AggSenderStorageMock_GetCertificatesByStatus_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *AggSenderStorageMock_GetCertificatesByStatus_Call) RunAndReturn(run func([]agglayer.CertificateStatus) ([]*types.CertificateInfo, error)) *AggSenderStorageMock_GetCertificatesByStatus_Call { - _c.Call.Return(run) - return _c -} - -// GetLastSentCertificate provides a mock function with given fields: -func (_m *AggSenderStorageMock) GetLastSentCertificate() (types.CertificateInfo, error) { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetLastSentCertificate") - } - - var r0 types.CertificateInfo - var r1 error - if rf, ok := ret.Get(0).(func() (types.CertificateInfo, error)); ok { - return rf() - } - if rf, ok := ret.Get(0).(func() types.CertificateInfo); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(types.CertificateInfo) - } - - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// AggSenderStorageMock_GetLastSentCertificate_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetLastSentCertificate' -type AggSenderStorageMock_GetLastSentCertificate_Call struct { - *mock.Call -} - -// GetLastSentCertificate is a helper method to define mock.On call -func (_e *AggSenderStorageMock_Expecter) GetLastSentCertificate() *AggSenderStorageMock_GetLastSentCertificate_Call { - return &AggSenderStorageMock_GetLastSentCertificate_Call{Call: _e.mock.On("GetLastSentCertificate")} -} - -func (_c *AggSenderStorageMock_GetLastSentCertificate_Call) Run(run func()) *AggSenderStorageMock_GetLastSentCertificate_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *AggSenderStorageMock_GetLastSentCertificate_Call) Return(_a0 types.CertificateInfo, _a1 error) *AggSenderStorageMock_GetLastSentCertificate_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *AggSenderStorageMock_GetLastSentCertificate_Call) RunAndReturn(run func() (types.CertificateInfo, error)) *AggSenderStorageMock_GetLastSentCertificate_Call { - _c.Call.Return(run) - return _c -} - -// SaveLastSentCertificate provides a mock function with given fields: ctx, certificate -func (_m *AggSenderStorageMock) SaveLastSentCertificate(ctx context.Context, certificate types.CertificateInfo) error { - ret := _m.Called(ctx, certificate) - - if len(ret) == 0 { - panic("no return value specified for SaveLastSentCertificate") - } - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, types.CertificateInfo) error); ok { - r0 = rf(ctx, certificate) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// AggSenderStorageMock_SaveLastSentCertificate_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SaveLastSentCertificate' -type AggSenderStorageMock_SaveLastSentCertificate_Call struct { - *mock.Call -} - -// SaveLastSentCertificate is a helper method to define mock.On call -// - ctx context.Context -// - certificate types.CertificateInfo -func (_e *AggSenderStorageMock_Expecter) SaveLastSentCertificate(ctx interface{}, certificate interface{}) *AggSenderStorageMock_SaveLastSentCertificate_Call { - return &AggSenderStorageMock_SaveLastSentCertificate_Call{Call: _e.mock.On("SaveLastSentCertificate", ctx, certificate)} -} - -func (_c *AggSenderStorageMock_SaveLastSentCertificate_Call) Run(run func(ctx context.Context, certificate types.CertificateInfo)) *AggSenderStorageMock_SaveLastSentCertificate_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(types.CertificateInfo)) - }) - return _c -} - -func (_c *AggSenderStorageMock_SaveLastSentCertificate_Call) Return(_a0 error) *AggSenderStorageMock_SaveLastSentCertificate_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *AggSenderStorageMock_SaveLastSentCertificate_Call) RunAndReturn(run func(context.Context, types.CertificateInfo) error) *AggSenderStorageMock_SaveLastSentCertificate_Call { - _c.Call.Return(run) - return _c -} - -// UpdateCertificateStatus provides a mock function with given fields: ctx, certificate -func (_m *AggSenderStorageMock) UpdateCertificateStatus(ctx context.Context, certificate types.CertificateInfo) error { - ret := _m.Called(ctx, certificate) - - if len(ret) == 0 { - panic("no return value specified for UpdateCertificateStatus") - } - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, types.CertificateInfo) error); ok { - r0 = rf(ctx, certificate) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// AggSenderStorageMock_UpdateCertificateStatus_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpdateCertificateStatus' -type AggSenderStorageMock_UpdateCertificateStatus_Call struct { - *mock.Call -} - -// UpdateCertificateStatus is a helper method to define mock.On call -// - ctx context.Context -// - certificate types.CertificateInfo -func (_e *AggSenderStorageMock_Expecter) UpdateCertificateStatus(ctx interface{}, certificate interface{}) *AggSenderStorageMock_UpdateCertificateStatus_Call { - return &AggSenderStorageMock_UpdateCertificateStatus_Call{Call: _e.mock.On("UpdateCertificateStatus", ctx, certificate)} -} - -func (_c *AggSenderStorageMock_UpdateCertificateStatus_Call) Run(run func(ctx context.Context, certificate types.CertificateInfo)) *AggSenderStorageMock_UpdateCertificateStatus_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(types.CertificateInfo)) - }) - return _c -} - -func (_c *AggSenderStorageMock_UpdateCertificateStatus_Call) Return(_a0 error) *AggSenderStorageMock_UpdateCertificateStatus_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *AggSenderStorageMock_UpdateCertificateStatus_Call) RunAndReturn(run func(context.Context, types.CertificateInfo) error) *AggSenderStorageMock_UpdateCertificateStatus_Call { - _c.Call.Return(run) - return _c -} - -// NewAggSenderStorageMock creates a new instance of AggSenderStorageMock. 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 NewAggSenderStorageMock(t interface { - mock.TestingT - Cleanup(func()) -}) *AggSenderStorageMock { - mock := &AggSenderStorageMock{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/aggsender/mocks/mock_l1infotree_syncer.go b/aggsender/mocks/mock_l1infotree_syncer.go deleted file mode 100644 index e113d4ed..00000000 --- a/aggsender/mocks/mock_l1infotree_syncer.go +++ /dev/null @@ -1,217 +0,0 @@ -// Code generated by mockery. DO NOT EDIT. - -package mocks - -import ( - context "context" - - common "github.com/ethereum/go-ethereum/common" - - l1infotreesync "github.com/0xPolygon/cdk/l1infotreesync" - - mock "github.com/stretchr/testify/mock" - - treetypes "github.com/0xPolygon/cdk/tree/types" -) - -// L1InfoTreeSyncerMock is an autogenerated mock type for the L1InfoTreeSyncer type -type L1InfoTreeSyncerMock struct { - mock.Mock -} - -type L1InfoTreeSyncerMock_Expecter struct { - mock *mock.Mock -} - -func (_m *L1InfoTreeSyncerMock) EXPECT() *L1InfoTreeSyncerMock_Expecter { - return &L1InfoTreeSyncerMock_Expecter{mock: &_m.Mock} -} - -// GetInfoByGlobalExitRoot provides a mock function with given fields: globalExitRoot -func (_m *L1InfoTreeSyncerMock) GetInfoByGlobalExitRoot(globalExitRoot common.Hash) (*l1infotreesync.L1InfoTreeLeaf, error) { - ret := _m.Called(globalExitRoot) - - if len(ret) == 0 { - panic("no return value specified for GetInfoByGlobalExitRoot") - } - - var r0 *l1infotreesync.L1InfoTreeLeaf - var r1 error - if rf, ok := ret.Get(0).(func(common.Hash) (*l1infotreesync.L1InfoTreeLeaf, error)); ok { - return rf(globalExitRoot) - } - if rf, ok := ret.Get(0).(func(common.Hash) *l1infotreesync.L1InfoTreeLeaf); ok { - r0 = rf(globalExitRoot) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*l1infotreesync.L1InfoTreeLeaf) - } - } - - if rf, ok := ret.Get(1).(func(common.Hash) error); ok { - r1 = rf(globalExitRoot) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// L1InfoTreeSyncerMock_GetInfoByGlobalExitRoot_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetInfoByGlobalExitRoot' -type L1InfoTreeSyncerMock_GetInfoByGlobalExitRoot_Call struct { - *mock.Call -} - -// GetInfoByGlobalExitRoot is a helper method to define mock.On call -// - globalExitRoot common.Hash -func (_e *L1InfoTreeSyncerMock_Expecter) GetInfoByGlobalExitRoot(globalExitRoot interface{}) *L1InfoTreeSyncerMock_GetInfoByGlobalExitRoot_Call { - return &L1InfoTreeSyncerMock_GetInfoByGlobalExitRoot_Call{Call: _e.mock.On("GetInfoByGlobalExitRoot", globalExitRoot)} -} - -func (_c *L1InfoTreeSyncerMock_GetInfoByGlobalExitRoot_Call) Run(run func(globalExitRoot common.Hash)) *L1InfoTreeSyncerMock_GetInfoByGlobalExitRoot_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(common.Hash)) - }) - return _c -} - -func (_c *L1InfoTreeSyncerMock_GetInfoByGlobalExitRoot_Call) Return(_a0 *l1infotreesync.L1InfoTreeLeaf, _a1 error) *L1InfoTreeSyncerMock_GetInfoByGlobalExitRoot_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *L1InfoTreeSyncerMock_GetInfoByGlobalExitRoot_Call) RunAndReturn(run func(common.Hash) (*l1infotreesync.L1InfoTreeLeaf, error)) *L1InfoTreeSyncerMock_GetInfoByGlobalExitRoot_Call { - _c.Call.Return(run) - return _c -} - -// GetL1InfoTreeMerkleProofFromIndexToRoot provides a mock function with given fields: ctx, index, root -func (_m *L1InfoTreeSyncerMock) GetL1InfoTreeMerkleProofFromIndexToRoot(ctx context.Context, index uint32, root common.Hash) (treetypes.Proof, error) { - ret := _m.Called(ctx, index, root) - - if len(ret) == 0 { - panic("no return value specified for GetL1InfoTreeMerkleProofFromIndexToRoot") - } - - var r0 treetypes.Proof - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, uint32, common.Hash) (treetypes.Proof, error)); ok { - return rf(ctx, index, root) - } - if rf, ok := ret.Get(0).(func(context.Context, uint32, common.Hash) treetypes.Proof); ok { - r0 = rf(ctx, index, root) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(treetypes.Proof) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, uint32, common.Hash) error); ok { - r1 = rf(ctx, index, root) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// L1InfoTreeSyncerMock_GetL1InfoTreeMerkleProofFromIndexToRoot_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetL1InfoTreeMerkleProofFromIndexToRoot' -type L1InfoTreeSyncerMock_GetL1InfoTreeMerkleProofFromIndexToRoot_Call struct { - *mock.Call -} - -// GetL1InfoTreeMerkleProofFromIndexToRoot is a helper method to define mock.On call -// - ctx context.Context -// - index uint32 -// - root common.Hash -func (_e *L1InfoTreeSyncerMock_Expecter) GetL1InfoTreeMerkleProofFromIndexToRoot(ctx interface{}, index interface{}, root interface{}) *L1InfoTreeSyncerMock_GetL1InfoTreeMerkleProofFromIndexToRoot_Call { - return &L1InfoTreeSyncerMock_GetL1InfoTreeMerkleProofFromIndexToRoot_Call{Call: _e.mock.On("GetL1InfoTreeMerkleProofFromIndexToRoot", ctx, index, root)} -} - -func (_c *L1InfoTreeSyncerMock_GetL1InfoTreeMerkleProofFromIndexToRoot_Call) Run(run func(ctx context.Context, index uint32, root common.Hash)) *L1InfoTreeSyncerMock_GetL1InfoTreeMerkleProofFromIndexToRoot_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(uint32), args[2].(common.Hash)) - }) - return _c -} - -func (_c *L1InfoTreeSyncerMock_GetL1InfoTreeMerkleProofFromIndexToRoot_Call) Return(_a0 treetypes.Proof, _a1 error) *L1InfoTreeSyncerMock_GetL1InfoTreeMerkleProofFromIndexToRoot_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *L1InfoTreeSyncerMock_GetL1InfoTreeMerkleProofFromIndexToRoot_Call) RunAndReturn(run func(context.Context, uint32, common.Hash) (treetypes.Proof, error)) *L1InfoTreeSyncerMock_GetL1InfoTreeMerkleProofFromIndexToRoot_Call { - _c.Call.Return(run) - return _c -} - -// GetL1InfoTreeRootByIndex provides a mock function with given fields: ctx, index -func (_m *L1InfoTreeSyncerMock) GetL1InfoTreeRootByIndex(ctx context.Context, index uint32) (treetypes.Root, error) { - ret := _m.Called(ctx, index) - - if len(ret) == 0 { - panic("no return value specified for GetL1InfoTreeRootByIndex") - } - - var r0 treetypes.Root - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, uint32) (treetypes.Root, error)); ok { - return rf(ctx, index) - } - if rf, ok := ret.Get(0).(func(context.Context, uint32) treetypes.Root); ok { - r0 = rf(ctx, index) - } else { - r0 = ret.Get(0).(treetypes.Root) - } - - if rf, ok := ret.Get(1).(func(context.Context, uint32) error); ok { - r1 = rf(ctx, index) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// L1InfoTreeSyncerMock_GetL1InfoTreeRootByIndex_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetL1InfoTreeRootByIndex' -type L1InfoTreeSyncerMock_GetL1InfoTreeRootByIndex_Call struct { - *mock.Call -} - -// GetL1InfoTreeRootByIndex is a helper method to define mock.On call -// - ctx context.Context -// - index uint32 -func (_e *L1InfoTreeSyncerMock_Expecter) GetL1InfoTreeRootByIndex(ctx interface{}, index interface{}) *L1InfoTreeSyncerMock_GetL1InfoTreeRootByIndex_Call { - return &L1InfoTreeSyncerMock_GetL1InfoTreeRootByIndex_Call{Call: _e.mock.On("GetL1InfoTreeRootByIndex", ctx, index)} -} - -func (_c *L1InfoTreeSyncerMock_GetL1InfoTreeRootByIndex_Call) Run(run func(ctx context.Context, index uint32)) *L1InfoTreeSyncerMock_GetL1InfoTreeRootByIndex_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(uint32)) - }) - return _c -} - -func (_c *L1InfoTreeSyncerMock_GetL1InfoTreeRootByIndex_Call) Return(_a0 treetypes.Root, _a1 error) *L1InfoTreeSyncerMock_GetL1InfoTreeRootByIndex_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *L1InfoTreeSyncerMock_GetL1InfoTreeRootByIndex_Call) RunAndReturn(run func(context.Context, uint32) (treetypes.Root, error)) *L1InfoTreeSyncerMock_GetL1InfoTreeRootByIndex_Call { - _c.Call.Return(run) - return _c -} - -// NewL1InfoTreeSyncerMock creates a new instance of L1InfoTreeSyncerMock. 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 NewL1InfoTreeSyncerMock(t interface { - mock.TestingT - Cleanup(func()) -}) *L1InfoTreeSyncerMock { - mock := &L1InfoTreeSyncerMock{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/aggsender/mocks/mock_l2bridge_syncer.go b/aggsender/mocks/mock_l2bridge_syncer.go deleted file mode 100644 index 725184c3..00000000 --- a/aggsender/mocks/mock_l2bridge_syncer.go +++ /dev/null @@ -1,423 +0,0 @@ -// Code generated by mockery. DO NOT EDIT. - -package mocks - -import ( - bridgesync "github.com/0xPolygon/cdk/bridgesync" - common "github.com/ethereum/go-ethereum/common" - - context "context" - - etherman "github.com/0xPolygon/cdk/etherman" - - mock "github.com/stretchr/testify/mock" - - treetypes "github.com/0xPolygon/cdk/tree/types" -) - -// L2BridgeSyncerMock is an autogenerated mock type for the L2BridgeSyncer type -type L2BridgeSyncerMock struct { - mock.Mock -} - -type L2BridgeSyncerMock_Expecter struct { - mock *mock.Mock -} - -func (_m *L2BridgeSyncerMock) EXPECT() *L2BridgeSyncerMock_Expecter { - return &L2BridgeSyncerMock_Expecter{mock: &_m.Mock} -} - -// BlockFinality provides a mock function with given fields: -func (_m *L2BridgeSyncerMock) BlockFinality() etherman.BlockNumberFinality { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for BlockFinality") - } - - var r0 etherman.BlockNumberFinality - if rf, ok := ret.Get(0).(func() etherman.BlockNumberFinality); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(etherman.BlockNumberFinality) - } - - return r0 -} - -// L2BridgeSyncerMock_BlockFinality_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'BlockFinality' -type L2BridgeSyncerMock_BlockFinality_Call struct { - *mock.Call -} - -// BlockFinality is a helper method to define mock.On call -func (_e *L2BridgeSyncerMock_Expecter) BlockFinality() *L2BridgeSyncerMock_BlockFinality_Call { - return &L2BridgeSyncerMock_BlockFinality_Call{Call: _e.mock.On("BlockFinality")} -} - -func (_c *L2BridgeSyncerMock_BlockFinality_Call) Run(run func()) *L2BridgeSyncerMock_BlockFinality_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *L2BridgeSyncerMock_BlockFinality_Call) Return(_a0 etherman.BlockNumberFinality) *L2BridgeSyncerMock_BlockFinality_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *L2BridgeSyncerMock_BlockFinality_Call) RunAndReturn(run func() etherman.BlockNumberFinality) *L2BridgeSyncerMock_BlockFinality_Call { - _c.Call.Return(run) - return _c -} - -// GetBlockByLER provides a mock function with given fields: ctx, ler -func (_m *L2BridgeSyncerMock) GetBlockByLER(ctx context.Context, ler common.Hash) (uint64, error) { - ret := _m.Called(ctx, ler) - - if len(ret) == 0 { - panic("no return value specified for GetBlockByLER") - } - - var r0 uint64 - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, common.Hash) (uint64, error)); ok { - return rf(ctx, ler) - } - if rf, ok := ret.Get(0).(func(context.Context, common.Hash) uint64); ok { - r0 = rf(ctx, ler) - } else { - r0 = ret.Get(0).(uint64) - } - - if rf, ok := ret.Get(1).(func(context.Context, common.Hash) error); ok { - r1 = rf(ctx, ler) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// L2BridgeSyncerMock_GetBlockByLER_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetBlockByLER' -type L2BridgeSyncerMock_GetBlockByLER_Call struct { - *mock.Call -} - -// GetBlockByLER is a helper method to define mock.On call -// - ctx context.Context -// - ler common.Hash -func (_e *L2BridgeSyncerMock_Expecter) GetBlockByLER(ctx interface{}, ler interface{}) *L2BridgeSyncerMock_GetBlockByLER_Call { - return &L2BridgeSyncerMock_GetBlockByLER_Call{Call: _e.mock.On("GetBlockByLER", ctx, ler)} -} - -func (_c *L2BridgeSyncerMock_GetBlockByLER_Call) Run(run func(ctx context.Context, ler common.Hash)) *L2BridgeSyncerMock_GetBlockByLER_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(common.Hash)) - }) - return _c -} - -func (_c *L2BridgeSyncerMock_GetBlockByLER_Call) Return(_a0 uint64, _a1 error) *L2BridgeSyncerMock_GetBlockByLER_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *L2BridgeSyncerMock_GetBlockByLER_Call) RunAndReturn(run func(context.Context, common.Hash) (uint64, error)) *L2BridgeSyncerMock_GetBlockByLER_Call { - _c.Call.Return(run) - return _c -} - -// GetBridgesPublished provides a mock function with given fields: ctx, fromBlock, toBlock -func (_m *L2BridgeSyncerMock) GetBridgesPublished(ctx context.Context, fromBlock uint64, toBlock uint64) ([]bridgesync.Bridge, error) { - ret := _m.Called(ctx, fromBlock, toBlock) - - if len(ret) == 0 { - panic("no return value specified for GetBridgesPublished") - } - - var r0 []bridgesync.Bridge - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, uint64, uint64) ([]bridgesync.Bridge, error)); ok { - return rf(ctx, fromBlock, toBlock) - } - if rf, ok := ret.Get(0).(func(context.Context, uint64, uint64) []bridgesync.Bridge); ok { - r0 = rf(ctx, fromBlock, toBlock) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]bridgesync.Bridge) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, uint64, uint64) error); ok { - r1 = rf(ctx, fromBlock, toBlock) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// L2BridgeSyncerMock_GetBridgesPublished_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetBridgesPublished' -type L2BridgeSyncerMock_GetBridgesPublished_Call struct { - *mock.Call -} - -// GetBridgesPublished is a helper method to define mock.On call -// - ctx context.Context -// - fromBlock uint64 -// - toBlock uint64 -func (_e *L2BridgeSyncerMock_Expecter) GetBridgesPublished(ctx interface{}, fromBlock interface{}, toBlock interface{}) *L2BridgeSyncerMock_GetBridgesPublished_Call { - return &L2BridgeSyncerMock_GetBridgesPublished_Call{Call: _e.mock.On("GetBridgesPublished", ctx, fromBlock, toBlock)} -} - -func (_c *L2BridgeSyncerMock_GetBridgesPublished_Call) Run(run func(ctx context.Context, fromBlock uint64, toBlock uint64)) *L2BridgeSyncerMock_GetBridgesPublished_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(uint64), args[2].(uint64)) - }) - return _c -} - -func (_c *L2BridgeSyncerMock_GetBridgesPublished_Call) Return(_a0 []bridgesync.Bridge, _a1 error) *L2BridgeSyncerMock_GetBridgesPublished_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *L2BridgeSyncerMock_GetBridgesPublished_Call) RunAndReturn(run func(context.Context, uint64, uint64) ([]bridgesync.Bridge, error)) *L2BridgeSyncerMock_GetBridgesPublished_Call { - _c.Call.Return(run) - return _c -} - -// GetClaims provides a mock function with given fields: ctx, fromBlock, toBlock -func (_m *L2BridgeSyncerMock) GetClaims(ctx context.Context, fromBlock uint64, toBlock uint64) ([]bridgesync.Claim, error) { - ret := _m.Called(ctx, fromBlock, toBlock) - - if len(ret) == 0 { - panic("no return value specified for GetClaims") - } - - var r0 []bridgesync.Claim - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, uint64, uint64) ([]bridgesync.Claim, error)); ok { - return rf(ctx, fromBlock, toBlock) - } - if rf, ok := ret.Get(0).(func(context.Context, uint64, uint64) []bridgesync.Claim); ok { - r0 = rf(ctx, fromBlock, toBlock) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]bridgesync.Claim) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, uint64, uint64) error); ok { - r1 = rf(ctx, fromBlock, toBlock) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// L2BridgeSyncerMock_GetClaims_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetClaims' -type L2BridgeSyncerMock_GetClaims_Call struct { - *mock.Call -} - -// GetClaims is a helper method to define mock.On call -// - ctx context.Context -// - fromBlock uint64 -// - toBlock uint64 -func (_e *L2BridgeSyncerMock_Expecter) GetClaims(ctx interface{}, fromBlock interface{}, toBlock interface{}) *L2BridgeSyncerMock_GetClaims_Call { - return &L2BridgeSyncerMock_GetClaims_Call{Call: _e.mock.On("GetClaims", ctx, fromBlock, toBlock)} -} - -func (_c *L2BridgeSyncerMock_GetClaims_Call) Run(run func(ctx context.Context, fromBlock uint64, toBlock uint64)) *L2BridgeSyncerMock_GetClaims_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(uint64), args[2].(uint64)) - }) - return _c -} - -func (_c *L2BridgeSyncerMock_GetClaims_Call) Return(_a0 []bridgesync.Claim, _a1 error) *L2BridgeSyncerMock_GetClaims_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *L2BridgeSyncerMock_GetClaims_Call) RunAndReturn(run func(context.Context, uint64, uint64) ([]bridgesync.Claim, error)) *L2BridgeSyncerMock_GetClaims_Call { - _c.Call.Return(run) - return _c -} - -// GetExitRootByIndex provides a mock function with given fields: ctx, index -func (_m *L2BridgeSyncerMock) GetExitRootByIndex(ctx context.Context, index uint32) (treetypes.Root, error) { - ret := _m.Called(ctx, index) - - if len(ret) == 0 { - panic("no return value specified for GetExitRootByIndex") - } - - var r0 treetypes.Root - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, uint32) (treetypes.Root, error)); ok { - return rf(ctx, index) - } - if rf, ok := ret.Get(0).(func(context.Context, uint32) treetypes.Root); ok { - r0 = rf(ctx, index) - } else { - r0 = ret.Get(0).(treetypes.Root) - } - - if rf, ok := ret.Get(1).(func(context.Context, uint32) error); ok { - r1 = rf(ctx, index) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// L2BridgeSyncerMock_GetExitRootByIndex_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetExitRootByIndex' -type L2BridgeSyncerMock_GetExitRootByIndex_Call struct { - *mock.Call -} - -// GetExitRootByIndex is a helper method to define mock.On call -// - ctx context.Context -// - index uint32 -func (_e *L2BridgeSyncerMock_Expecter) GetExitRootByIndex(ctx interface{}, index interface{}) *L2BridgeSyncerMock_GetExitRootByIndex_Call { - return &L2BridgeSyncerMock_GetExitRootByIndex_Call{Call: _e.mock.On("GetExitRootByIndex", ctx, index)} -} - -func (_c *L2BridgeSyncerMock_GetExitRootByIndex_Call) Run(run func(ctx context.Context, index uint32)) *L2BridgeSyncerMock_GetExitRootByIndex_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(uint32)) - }) - return _c -} - -func (_c *L2BridgeSyncerMock_GetExitRootByIndex_Call) Return(_a0 treetypes.Root, _a1 error) *L2BridgeSyncerMock_GetExitRootByIndex_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *L2BridgeSyncerMock_GetExitRootByIndex_Call) RunAndReturn(run func(context.Context, uint32) (treetypes.Root, error)) *L2BridgeSyncerMock_GetExitRootByIndex_Call { - _c.Call.Return(run) - return _c -} - -// GetLastProcessedBlock provides a mock function with given fields: ctx -func (_m *L2BridgeSyncerMock) GetLastProcessedBlock(ctx context.Context) (uint64, error) { - ret := _m.Called(ctx) - - if len(ret) == 0 { - panic("no return value specified for GetLastProcessedBlock") - } - - var r0 uint64 - var r1 error - if rf, ok := ret.Get(0).(func(context.Context) (uint64, error)); ok { - return rf(ctx) - } - if rf, ok := ret.Get(0).(func(context.Context) uint64); ok { - r0 = rf(ctx) - } else { - r0 = ret.Get(0).(uint64) - } - - if rf, ok := ret.Get(1).(func(context.Context) error); ok { - r1 = rf(ctx) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// L2BridgeSyncerMock_GetLastProcessedBlock_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetLastProcessedBlock' -type L2BridgeSyncerMock_GetLastProcessedBlock_Call struct { - *mock.Call -} - -// GetLastProcessedBlock is a helper method to define mock.On call -// - ctx context.Context -func (_e *L2BridgeSyncerMock_Expecter) GetLastProcessedBlock(ctx interface{}) *L2BridgeSyncerMock_GetLastProcessedBlock_Call { - return &L2BridgeSyncerMock_GetLastProcessedBlock_Call{Call: _e.mock.On("GetLastProcessedBlock", ctx)} -} - -func (_c *L2BridgeSyncerMock_GetLastProcessedBlock_Call) Run(run func(ctx context.Context)) *L2BridgeSyncerMock_GetLastProcessedBlock_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context)) - }) - return _c -} - -func (_c *L2BridgeSyncerMock_GetLastProcessedBlock_Call) Return(_a0 uint64, _a1 error) *L2BridgeSyncerMock_GetLastProcessedBlock_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *L2BridgeSyncerMock_GetLastProcessedBlock_Call) RunAndReturn(run func(context.Context) (uint64, error)) *L2BridgeSyncerMock_GetLastProcessedBlock_Call { - _c.Call.Return(run) - return _c -} - -// OriginNetwork provides a mock function with given fields: -func (_m *L2BridgeSyncerMock) OriginNetwork() uint32 { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for OriginNetwork") - } - - var r0 uint32 - if rf, ok := ret.Get(0).(func() uint32); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(uint32) - } - - return r0 -} - -// L2BridgeSyncerMock_OriginNetwork_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'OriginNetwork' -type L2BridgeSyncerMock_OriginNetwork_Call struct { - *mock.Call -} - -// OriginNetwork is a helper method to define mock.On call -func (_e *L2BridgeSyncerMock_Expecter) OriginNetwork() *L2BridgeSyncerMock_OriginNetwork_Call { - return &L2BridgeSyncerMock_OriginNetwork_Call{Call: _e.mock.On("OriginNetwork")} -} - -func (_c *L2BridgeSyncerMock_OriginNetwork_Call) Run(run func()) *L2BridgeSyncerMock_OriginNetwork_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *L2BridgeSyncerMock_OriginNetwork_Call) Return(_a0 uint32) *L2BridgeSyncerMock_OriginNetwork_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *L2BridgeSyncerMock_OriginNetwork_Call) RunAndReturn(run func() uint32) *L2BridgeSyncerMock_OriginNetwork_Call { - _c.Call.Return(run) - return _c -} - -// NewL2BridgeSyncerMock creates a new instance of L2BridgeSyncerMock. 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 NewL2BridgeSyncerMock(t interface { - mock.TestingT - Cleanup(func()) -}) *L2BridgeSyncerMock { - mock := &L2BridgeSyncerMock{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/aggsender/mocks/mock_logger.go b/aggsender/mocks/mock_logger.go deleted file mode 100644 index 5b0eb4e9..00000000 --- a/aggsender/mocks/mock_logger.go +++ /dev/null @@ -1,290 +0,0 @@ -// Code generated by mockery. DO NOT EDIT. - -package mocks - -import mock "github.com/stretchr/testify/mock" - -// LoggerMock is an autogenerated mock type for the Logger type -type LoggerMock struct { - mock.Mock -} - -type LoggerMock_Expecter struct { - mock *mock.Mock -} - -func (_m *LoggerMock) EXPECT() *LoggerMock_Expecter { - return &LoggerMock_Expecter{mock: &_m.Mock} -} - -// Debug provides a mock function with given fields: args -func (_m *LoggerMock) Debug(args ...interface{}) { - var _ca []interface{} - _ca = append(_ca, args...) - _m.Called(_ca...) -} - -// LoggerMock_Debug_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Debug' -type LoggerMock_Debug_Call struct { - *mock.Call -} - -// Debug is a helper method to define mock.On call -// - args ...interface{} -func (_e *LoggerMock_Expecter) Debug(args ...interface{}) *LoggerMock_Debug_Call { - return &LoggerMock_Debug_Call{Call: _e.mock.On("Debug", - append([]interface{}{}, args...)...)} -} - -func (_c *LoggerMock_Debug_Call) Run(run func(args ...interface{})) *LoggerMock_Debug_Call { - _c.Call.Run(func(args mock.Arguments) { - variadicArgs := make([]interface{}, len(args)-0) - for i, a := range args[0:] { - if a != nil { - variadicArgs[i] = a.(interface{}) - } - } - run(variadicArgs...) - }) - return _c -} - -func (_c *LoggerMock_Debug_Call) Return() *LoggerMock_Debug_Call { - _c.Call.Return() - return _c -} - -func (_c *LoggerMock_Debug_Call) RunAndReturn(run func(...interface{})) *LoggerMock_Debug_Call { - _c.Call.Return(run) - return _c -} - -// Debugf provides a mock function with given fields: format, args -func (_m *LoggerMock) Debugf(format string, args ...interface{}) { - var _ca []interface{} - _ca = append(_ca, format) - _ca = append(_ca, args...) - _m.Called(_ca...) -} - -// LoggerMock_Debugf_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Debugf' -type LoggerMock_Debugf_Call struct { - *mock.Call -} - -// Debugf is a helper method to define mock.On call -// - format string -// - args ...interface{} -func (_e *LoggerMock_Expecter) Debugf(format interface{}, args ...interface{}) *LoggerMock_Debugf_Call { - return &LoggerMock_Debugf_Call{Call: _e.mock.On("Debugf", - append([]interface{}{format}, args...)...)} -} - -func (_c *LoggerMock_Debugf_Call) Run(run func(format string, args ...interface{})) *LoggerMock_Debugf_Call { - _c.Call.Run(func(args mock.Arguments) { - variadicArgs := make([]interface{}, len(args)-1) - for i, a := range args[1:] { - if a != nil { - variadicArgs[i] = a.(interface{}) - } - } - run(args[0].(string), variadicArgs...) - }) - return _c -} - -func (_c *LoggerMock_Debugf_Call) Return() *LoggerMock_Debugf_Call { - _c.Call.Return() - return _c -} - -func (_c *LoggerMock_Debugf_Call) RunAndReturn(run func(string, ...interface{})) *LoggerMock_Debugf_Call { - _c.Call.Return(run) - return _c -} - -// Error provides a mock function with given fields: args -func (_m *LoggerMock) Error(args ...interface{}) { - var _ca []interface{} - _ca = append(_ca, args...) - _m.Called(_ca...) -} - -// LoggerMock_Error_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Error' -type LoggerMock_Error_Call struct { - *mock.Call -} - -// Error is a helper method to define mock.On call -// - args ...interface{} -func (_e *LoggerMock_Expecter) Error(args ...interface{}) *LoggerMock_Error_Call { - return &LoggerMock_Error_Call{Call: _e.mock.On("Error", - append([]interface{}{}, args...)...)} -} - -func (_c *LoggerMock_Error_Call) Run(run func(args ...interface{})) *LoggerMock_Error_Call { - _c.Call.Run(func(args mock.Arguments) { - variadicArgs := make([]interface{}, len(args)-0) - for i, a := range args[0:] { - if a != nil { - variadicArgs[i] = a.(interface{}) - } - } - run(variadicArgs...) - }) - return _c -} - -func (_c *LoggerMock_Error_Call) Return() *LoggerMock_Error_Call { - _c.Call.Return() - return _c -} - -func (_c *LoggerMock_Error_Call) RunAndReturn(run func(...interface{})) *LoggerMock_Error_Call { - _c.Call.Return(run) - return _c -} - -// Errorf provides a mock function with given fields: format, args -func (_m *LoggerMock) Errorf(format string, args ...interface{}) { - var _ca []interface{} - _ca = append(_ca, format) - _ca = append(_ca, args...) - _m.Called(_ca...) -} - -// LoggerMock_Errorf_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Errorf' -type LoggerMock_Errorf_Call struct { - *mock.Call -} - -// Errorf is a helper method to define mock.On call -// - format string -// - args ...interface{} -func (_e *LoggerMock_Expecter) Errorf(format interface{}, args ...interface{}) *LoggerMock_Errorf_Call { - return &LoggerMock_Errorf_Call{Call: _e.mock.On("Errorf", - append([]interface{}{format}, args...)...)} -} - -func (_c *LoggerMock_Errorf_Call) Run(run func(format string, args ...interface{})) *LoggerMock_Errorf_Call { - _c.Call.Run(func(args mock.Arguments) { - variadicArgs := make([]interface{}, len(args)-1) - for i, a := range args[1:] { - if a != nil { - variadicArgs[i] = a.(interface{}) - } - } - run(args[0].(string), variadicArgs...) - }) - return _c -} - -func (_c *LoggerMock_Errorf_Call) Return() *LoggerMock_Errorf_Call { - _c.Call.Return() - return _c -} - -func (_c *LoggerMock_Errorf_Call) RunAndReturn(run func(string, ...interface{})) *LoggerMock_Errorf_Call { - _c.Call.Return(run) - return _c -} - -// Info provides a mock function with given fields: args -func (_m *LoggerMock) Info(args ...interface{}) { - var _ca []interface{} - _ca = append(_ca, args...) - _m.Called(_ca...) -} - -// LoggerMock_Info_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Info' -type LoggerMock_Info_Call struct { - *mock.Call -} - -// Info is a helper method to define mock.On call -// - args ...interface{} -func (_e *LoggerMock_Expecter) Info(args ...interface{}) *LoggerMock_Info_Call { - return &LoggerMock_Info_Call{Call: _e.mock.On("Info", - append([]interface{}{}, args...)...)} -} - -func (_c *LoggerMock_Info_Call) Run(run func(args ...interface{})) *LoggerMock_Info_Call { - _c.Call.Run(func(args mock.Arguments) { - variadicArgs := make([]interface{}, len(args)-0) - for i, a := range args[0:] { - if a != nil { - variadicArgs[i] = a.(interface{}) - } - } - run(variadicArgs...) - }) - return _c -} - -func (_c *LoggerMock_Info_Call) Return() *LoggerMock_Info_Call { - _c.Call.Return() - return _c -} - -func (_c *LoggerMock_Info_Call) RunAndReturn(run func(...interface{})) *LoggerMock_Info_Call { - _c.Call.Return(run) - return _c -} - -// Infof provides a mock function with given fields: format, args -func (_m *LoggerMock) Infof(format string, args ...interface{}) { - var _ca []interface{} - _ca = append(_ca, format) - _ca = append(_ca, args...) - _m.Called(_ca...) -} - -// LoggerMock_Infof_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Infof' -type LoggerMock_Infof_Call struct { - *mock.Call -} - -// Infof is a helper method to define mock.On call -// - format string -// - args ...interface{} -func (_e *LoggerMock_Expecter) Infof(format interface{}, args ...interface{}) *LoggerMock_Infof_Call { - return &LoggerMock_Infof_Call{Call: _e.mock.On("Infof", - append([]interface{}{format}, args...)...)} -} - -func (_c *LoggerMock_Infof_Call) Run(run func(format string, args ...interface{})) *LoggerMock_Infof_Call { - _c.Call.Run(func(args mock.Arguments) { - variadicArgs := make([]interface{}, len(args)-1) - for i, a := range args[1:] { - if a != nil { - variadicArgs[i] = a.(interface{}) - } - } - run(args[0].(string), variadicArgs...) - }) - return _c -} - -func (_c *LoggerMock_Infof_Call) Return() *LoggerMock_Infof_Call { - _c.Call.Return() - return _c -} - -func (_c *LoggerMock_Infof_Call) RunAndReturn(run func(string, ...interface{})) *LoggerMock_Infof_Call { - _c.Call.Return(run) - return _c -} - -// NewLoggerMock creates a new instance of LoggerMock. 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 NewLoggerMock(t interface { - mock.TestingT - Cleanup(func()) -}) *LoggerMock { - mock := &LoggerMock{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/aggsender/types/block_notifier.go b/aggsender/types/block_notifier.go new file mode 100644 index 00000000..475abc1b --- /dev/null +++ b/aggsender/types/block_notifier.go @@ -0,0 +1,15 @@ +package types + +import "github.com/0xPolygon/cdk/etherman" + +type EventNewBlock struct { + BlockNumber uint64 + BlockFinalityType etherman.BlockNumberFinality +} + +// BlockNotifier is the interface that wraps the basic methods to notify a new block. +type BlockNotifier interface { + // NotifyEpochStarted notifies the epoch has started. + Subscribe(id string) <-chan EventNewBlock + String() string +} diff --git a/aggsender/types/epoch_notifier.go b/aggsender/types/epoch_notifier.go new file mode 100644 index 00000000..426ad362 --- /dev/null +++ b/aggsender/types/epoch_notifier.go @@ -0,0 +1,25 @@ +package types + +import ( + "context" + "fmt" +) + +// EpochEvent is the event that notifies the neear end epoch +type EpochEvent struct { + Epoch uint64 + // ExtraInfo if a detailed information about the epoch that depends on implementation + ExtraInfo fmt.Stringer +} + +func (e EpochEvent) String() string { + return fmt.Sprintf("EpochEvent: epoch=%d extra=%s", e.Epoch, e.ExtraInfo) +} + +type EpochNotifier interface { + // NotifyEpochStarted notifies the epoch is close to end. + Subscribe(id string) <-chan EpochEvent + // Start starts the notifier synchronously + Start(ctx context.Context) + String() string +} diff --git a/aggsender/types/generic_subscriber.go b/aggsender/types/generic_subscriber.go new file mode 100644 index 00000000..67038c5c --- /dev/null +++ b/aggsender/types/generic_subscriber.go @@ -0,0 +1,6 @@ +package types + +type GenericSubscriber[T any] interface { + Subscribe(subscriberName string) <-chan T + Publish(data T) +} diff --git a/aggsender/types/types.go b/aggsender/types/types.go index 46d31176..d9e0b2e7 100644 --- a/aggsender/types/types.go +++ b/aggsender/types/types.go @@ -47,6 +47,8 @@ type Logger interface { Infof(format string, args ...interface{}) Error(args ...interface{}) Errorf(format string, args ...interface{}) + Warn(args ...interface{}) + Warnf(format string, args ...interface{}) Debug(args ...interface{}) Debugf(format string, args ...interface{}) } diff --git a/cmd/run.go b/cmd/run.go index c30da739..6042e935 100644 --- a/cmd/run.go +++ b/cmd/run.go @@ -125,6 +125,7 @@ func start(cliCtx *cli.Context) error { aggsender, err := createAggSender( cliCtx.Context, c.AggSender, + l1Client, l1InfoTreeSync, l2BridgeSync, ) @@ -144,13 +145,35 @@ func start(cliCtx *cli.Context) error { func createAggSender( ctx context.Context, cfg aggsender.Config, + l1EthClient *ethclient.Client, l1InfoTreeSync *l1infotreesync.L1InfoTreeSync, - l2Syncer *bridgesync.BridgeSync, -) (*aggsender.AggSender, error) { + l2Syncer *bridgesync.BridgeSync) (*aggsender.AggSender, error) { logger := log.WithFields("module", cdkcommon.AGGSENDER) agglayerClient := agglayer.NewAggLayerClient(cfg.AggLayerURL) + blockNotifier, err := aggsender.NewBlockNotifierPolling(l1EthClient, aggsender.ConfigBlockNotifierPolling{ + BlockFinalityType: etherman.BlockNumberFinality(cfg.BlockFinality), + CheckNewBlockInterval: aggsender.AutomaticBlockInterval, + }, logger, nil) + if err != nil { + return nil, err + } - return aggsender.New(ctx, logger, cfg, agglayerClient, l1InfoTreeSync, l2Syncer) + notifierCfg, err := aggsender.NewConfigEpochNotifierPerBlock(agglayerClient, cfg.EpochNotificationPercentage) + if err != nil { + return nil, fmt.Errorf("cant generate config for Epoch Notifier because: %w", err) + } + epochNotifier, err := aggsender.NewEpochNotifierPerBlock( + blockNotifier, + logger, + *notifierCfg, nil) + if err != nil { + return nil, err + } + log.Infof("Starting blockNotifier: %s", blockNotifier.String()) + go blockNotifier.Start(ctx) + log.Infof("Starting epochNotifier: %s", epochNotifier.String()) + go epochNotifier.Start(ctx) + return aggsender.New(ctx, logger, cfg, agglayerClient, l1InfoTreeSync, l2Syncer, epochNotifier) } func createAggregator(ctx context.Context, c config.Config, runMigrations bool) *aggregator.Aggregator { diff --git a/config/default.go b/config/default.go index bbf4d2e0..d7188e43 100644 --- a/config/default.go +++ b/config/default.go @@ -7,6 +7,7 @@ L1URL = "http://localhost:8545" L2URL = "http://localhost:8123" AggLayerURL = "https://agglayer-dev.polygon.technology" + ForkId = 9 ContractVersions = "elderberry" IsValidiumMode = false @@ -215,7 +216,7 @@ DBPath = "{{PathRWData}}/reorgdetectorl2.sqlite" DBPath = "{{PathRWData}}/L1InfoTreeSync.sqlite" GlobalExitRootAddr="{{NetworkConfig.L1.GlobalExitRootManagerAddr}}" RollupManagerAddr = "{{NetworkConfig.L1.RollupManagerAddr}}" -SyncBlockChunkSize=10 +SyncBlockChunkSize=100 BlockFinality="LatestBlock" URLRPCL1="{{L1URL}}" WaitForNewBlocksPeriod="100ms" @@ -340,5 +341,7 @@ AggsenderPrivateKey = {Path = "{{SequencerPrivateKeyPath}}", Password = "{{Seque BlockGetInterval = "2s" URLRPCL2="{{L2URL}}" CheckSettledInterval = "2s" +BlockFinality = "LatestBlock" +EpochNotificationPercentage = 50 SaveCertificatesToFiles = false ` diff --git a/dataavailability/datacommittee/datacommittee.go b/dataavailability/datacommittee/datacommittee.go index 474c5934..369fc0fe 100644 --- a/dataavailability/datacommittee/datacommittee.go +++ b/dataavailability/datacommittee/datacommittee.go @@ -105,53 +105,40 @@ func (d *Backend) Init() error { return nil } -// GetSequence gets backend data one hash at a time. This should be optimized on the DAC side to get them all at once. +// GetSequence retrieves backend data by querying committee members for each hash concurrently. func (d *Backend) GetSequence(_ context.Context, hashes []common.Hash, _ []byte) ([][]byte, error) { - intialMember := d.selectedCommitteeMember + initialMember := d.selectedCommitteeMember - var found bool - for !found && intialMember != -1 { + var batchData [][]byte + for retries := 0; retries < len(d.committeeMembers); retries++ { member := d.committeeMembers[d.selectedCommitteeMember] d.logger.Infof("trying to get data from %s at %s", member.Addr.Hex(), member.URL) c := d.dataCommitteeClientFactory.New(member.URL) - dataMap, err := c.ListOffChainData(d.ctx, hashes) if err != nil { - d.logger.Warnf( - "error getting data from DAC node %s at %s: %s", - member.Addr.Hex(), member.URL, err, - ) + d.logger.Warnf("error getting data from DAC node %s at %s: %s", member.Addr.Hex(), member.URL, err) d.selectedCommitteeMember = (d.selectedCommitteeMember + 1) % len(d.committeeMembers) - if d.selectedCommitteeMember == intialMember { + if d.selectedCommitteeMember == initialMember { break } - continue } - batchData := make([][]byte, 0, len(hashes)) + batchData = make([][]byte, 0, len(hashes)) for _, hash := range hashes { actualTransactionsHash := crypto.Keccak256Hash(dataMap[hash]) if actualTransactionsHash != hash { - unexpectedHash := fmt.Errorf( - unexpectedHashTemplate, hash, actualTransactionsHash, - ) - d.logger.Warnf( - "error getting data from DAC node %s at %s: %s", - member.Addr.Hex(), member.URL, unexpectedHash, - ) + unexpectedHash := fmt.Errorf(unexpectedHashTemplate, hash, actualTransactionsHash) + d.logger.Warnf("error getting data from DAC node %s at %s: %s", member.Addr.Hex(), member.URL, unexpectedHash) d.selectedCommitteeMember = (d.selectedCommitteeMember + 1) % len(d.committeeMembers) - if d.selectedCommitteeMember == intialMember { + if d.selectedCommitteeMember == initialMember { break } - continue } - batchData = append(batchData, dataMap[hash]) } - return batchData, nil } diff --git a/go.mod b/go.mod index 0061c72f..430e8326 100644 --- a/go.mod +++ b/go.mod @@ -33,7 +33,7 @@ require ( go.uber.org/zap v1.27.0 golang.org/x/crypto v0.27.0 golang.org/x/net v0.29.0 - golang.org/x/sync v0.8.0 + golang.org/x/sync v0.9.0 google.golang.org/grpc v1.64.0 google.golang.org/protobuf v1.34.2 modernc.org/sqlite v1.32.0 @@ -151,7 +151,7 @@ require ( github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect go.opentelemetry.io/otel/trace v1.24.0 // indirect go.uber.org/multierr v1.10.0 // indirect - golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa // indirect + golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f // indirect golang.org/x/sys v0.25.0 // indirect golang.org/x/text v0.18.0 // indirect golang.org/x/time v0.5.0 // indirect diff --git a/go.sum b/go.sum index ceb905ac..3ad80938 100644 --- a/go.sum +++ b/go.sum @@ -6,6 +6,7 @@ github.com/0xPolygon/cdk-rpc v0.0.0-20241004114257-6c3cb6eebfb6 h1:FXL/rcO7/GtZ3 github.com/0xPolygon/cdk-rpc v0.0.0-20241004114257-6c3cb6eebfb6/go.mod h1:2scWqMMufrQXu7TikDgQ3BsyaKoX8qP26D6E262vSOg= github.com/0xPolygon/zkevm-ethtx-manager v0.2.1 h1:2Yb+KdJFMpVrS9LIkd658XiWuN+MCTs7SgeWaopXScg= github.com/0xPolygon/zkevm-ethtx-manager v0.2.1/go.mod h1:lqQmzSo2OXEZItD0R4Cd+lqKFxphXEWgqHefVcGDZZc= +github.com/0xPolygonHermez/zkevm-data-streamer v0.2.7/go.mod h1:7nM7Ihk+fTG1TQPwdZoGOYd3wprqqyIyjtS514uHzWE= github.com/0xPolygonHermez/zkevm-synchronizer-l1 v1.0.5 h1:YmnhuCl349MoNASN0fMeGKU1o9HqJhiZkfMsA/1cTRA= github.com/0xPolygonHermez/zkevm-synchronizer-l1 v1.0.5/go.mod h1:X4Su/M/+hSISqdl9yomKlRsbTyuZHsRohporyHsP8gg= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= @@ -482,6 +483,8 @@ golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ= golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE= +golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f h1:XdNn9LlyWAhLVp6P/i8QYBW+hlyhrhei9uErw2B5GJo= +golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f/go.mod h1:D5SMRVC3C2/4+F/DB1wZsLRnSNimn2Sp/NPsCrsv8ak= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= @@ -489,6 +492,7 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8= golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -510,6 +514,8 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ= +golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -571,6 +577,7 @@ golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.23.0 h1:SGsXPZ+2l4JsgaCKkx+FQ9YZ5XEtA1GZYuoDjenLjvg= golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI= +golang.org/x/tools v0.27.0 h1:qEKojBykQkQ4EynWy4S8Weg69NumxKdn40Fce3uc/8o= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/scripts/local_config b/scripts/local_config index 09e0167a..5830b6e6 100755 --- a/scripts/local_config +++ b/scripts/local_config @@ -447,4 +447,4 @@ EOF echo " -----------------------------------------------------------" echo " " echo " - rembember to clean previous execution data: " -echo " rm -Rf ${path_rw_data}/*" +echo " rm -Rf ${zkevm_path_rw_data}/*" diff --git a/sonar-project.properties b/sonar-project.properties index a6245819..3b6ddc8a 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -7,11 +7,11 @@ sonar.projectName=cdk sonar.organization=0xpolygon sonar.sources=. -sonar.exclusions=**/test/**,**/vendor/**,**/mocks/**,**/build/**,**/target/**,**/proto/include/**,**/*.pb.go,**/docs/**,**/*.sql,**/mocks_*/*,scripts/**,**/mock_*.go,**/agglayer/**,**/cmd/** +sonar.exclusions=**/test/**,**/vendor/**,**/mocks/**,**/build/**,**/target/**,**/proto/include/**,**/*.pb.go,**/docs/**,**/*.sql,**/mocks_*/*,scripts/**,**/mock_*.go,**/cmd/** sonar.tests=. sonar.test.inclusions=**/*_test.go -sonar.test.exclusions=test/contracts/**,**/vendor/**,**/docs/**,**/mocks/**,**/*.pb.go,**/*.yml,**/*.yaml,**/*.json,**/*.xml,**/*.toml,**/mocks_*/*,**/mock_*.go,**/agglayer/**,**/cmd/** +sonar.test.exclusions=test/contracts/**,**/vendor/**,**/docs/**,**/mocks/**,**/*.pb.go,**/*.yml,**/*.yaml,**/*.json,**/*.xml,**/*.toml,**/mocks_*/*,**/mock_*.go,**/cmd/** sonar.issue.enforceSemantic=true # ===================================================== diff --git a/test/Makefile b/test/Makefile index 51a475ed..2435730c 100644 --- a/test/Makefile +++ b/test/Makefile @@ -3,6 +3,8 @@ generate-mocks: generate-mocks-bridgesync generate-mocks-reorgdetector generate- generate-mocks-da generate-mocks-l1infotreesync generate-mocks-helpers \ generate-mocks-sync generate-mocks-l1infotreesync generate-mocks-aggregator \ generate-mocks-aggsender generate-mocks-agglayer generate-mocks-bridgesync + generate-mocks-sync generate-mocks-l1infotreesync generate-mocks-aggregator \ + generate-mocks-aggsender generate-mocks-agglayer generate-mocks-bridgesync .PHONY: generate-mocks-bridgesync generate-mocks-bridgesync: ## Generates mocks for bridgesync, using mockery tool @@ -61,11 +63,8 @@ generate-mocks-aggregator: ## Generates mocks for aggregator, using mockery tool .PHONY: generate-mocks-aggsender generate-mocks-aggsender: ## Generates mocks for aggsender, using mockery tool - export "GOROOT=$$(go env GOROOT)" && $$(go env GOPATH)/bin/mockery --name=L1InfoTreeSyncer --dir=../aggsender/types --output=../aggsender/mocks --outpkg=mocks --structname=L1InfoTreeSyncerMock --filename=mock_l1infotree_syncer.go ${COMMON_MOCKERY_PARAMS} - export "GOROOT=$$(go env GOROOT)" && $$(go env GOPATH)/bin/mockery --name=L2BridgeSyncer --dir=../aggsender/types --output=../aggsender/mocks --outpkg=mocks --structname=L2BridgeSyncerMock --filename=mock_l2bridge_syncer.go ${COMMON_MOCKERY_PARAMS} - export "GOROOT=$$(go env GOROOT)" && $$(go env GOPATH)/bin/mockery --name=Logger --dir=../aggsender/types --output=../aggsender/mocks --outpkg=mocks --structname=LoggerMock --filename=mock_logger.go ${COMMON_MOCKERY_PARAMS} - export "GOROOT=$$(go env GOROOT)" && $$(go env GOPATH)/bin/mockery --name=AggSenderStorage --dir=../aggsender/db --output=../aggsender/mocks --outpkg=mocks --structname=AggSenderStorageMock --filename=mock_aggsender_storage.go ${COMMON_MOCKERY_PARAMS} - export "GOROOT=$$(go env GOROOT)" && $$(go env GOPATH)/bin/mockery --name=EthClient --dir=../aggsender/types --output=../aggsender/mocks --outpkg=mocks --structname=EthClientMock --filename=mock_eth_client.go ${COMMON_MOCKERY_PARAMS} + rm -Rf ../aggsender/mocks + export "GOROOT=$$(go env GOROOT)" && $$(go env GOPATH)/bin/mockery --all --case snake --dir ../aggsender --output ../aggsender/mocks --outpkg mocks ${COMMON_MOCKERY_PARAMS} .PHONY: generate-mocks-agglayer generate-mocks-agglayer: ## Generates mocks for agglayer, using mockery tool diff --git a/test/bridge-e2e.bats b/test/bridge-e2e.bats index ed599c7d..e754ef70 100644 --- a/test/bridge-e2e.bats +++ b/test/bridge-e2e.bats @@ -48,23 +48,35 @@ setup() { } @test "Native gas token deposit to WETH" { + destination_addr=$sender_addr local initial_receiver_balance=$(cast call --rpc-url "$l2_rpc_url" "$weth_token_addr" "$balance_of_fn_sig" "$destination_addr" | awk '{print $1}') echo "Initial receiver balance of native token on L2 $initial_receiver_balance" >&3 - echo "Running LxLy deposit" >&3 + echo "=== Running LxLy deposit on L1 to network: $l2_rpc_network_id native_token: $native_token_addr" >&3 + + destination_net=$l2_rpc_network_id run bridgeAsset "$native_token_addr" "$l1_rpc_url" assert_success - echo "Running LxLy claim" >&3 + echo "=== Running LxLy claim on L2" >&3 timeout="120" claim_frequency="10" run wait_for_claim "$timeout" "$claim_frequency" "$l2_rpc_url" assert_success run verify_balance "$l2_rpc_url" "$weth_token_addr" "$destination_addr" "$initial_receiver_balance" "$ether_value" - if [ $status -eq 0 ]; then - break - fi + assert_success + + echo "=== bridgeAsset L2 WETH: $weth_token_addr to L1 ETH" >&3 + destination_addr=$sender_addr + destination_net=0 + run bridgeAsset "$weth_token_addr" "$l2_rpc_url" + assert_success + + echo "=== Claim in L1 ETH" >&3 + timeout="400" + claim_frequency="60" + run wait_for_claim "$timeout" "$claim_frequency" "$l1_rpc_url" assert_success } diff --git a/test/config/kurtosis-cdk-node-config.toml.template b/test/config/kurtosis-cdk-node-config.toml.template index 508c1286..4069b350 100644 --- a/test/config/kurtosis-cdk-node-config.toml.template +++ b/test/config/kurtosis-cdk-node-config.toml.template @@ -25,8 +25,6 @@ AggregatorPrivateKeyPassword = "{{.zkevm_l2_keystore_password}}" SenderProofToL1Addr = "{{.zkevm_l2_agglayer_address}}" polygonBridgeAddr = "{{.zkevm_bridge_address}}" - -RPCURL = "http://{{.l2_rpc_name}}{{.deployment_suffix}}:{{.zkevm_rpc_http_port}}" WitnessURL = "http://{{.l2_rpc_name}}{{.deployment_suffix}}:{{.zkevm_rpc_http_port}}" diff --git a/test/helpers/lxly-bridge-test.bash b/test/helpers/lxly-bridge-test.bash index 7b3cb008..ad5ab943 100644 --- a/test/helpers/lxly-bridge-test.bash +++ b/test/helpers/lxly-bridge-test.bash @@ -36,6 +36,7 @@ function claim() { readonly bridge_deposit_file=$(mktemp) readonly claimable_deposit_file=$(mktemp) echo "Getting full list of deposits" >&3 + echo " curl -s \"$bridge_api_url/bridges/$destination_addr?limit=100&offset=0\"" >&3 curl -s "$bridge_api_url/bridges/$destination_addr?limit=100&offset=0" | jq '.' | tee $bridge_deposit_file echo "Looking for claimable deposits" >&3