diff --git a/agglayer/client.go b/agglayer/client.go index fb70925e..48d4bfe0 100644 --- a/agglayer/client.go +++ b/agglayer/client.go @@ -35,7 +35,6 @@ type AgglayerClientInterface interface { WaitTxToBeMined(hash common.Hash, ctx context.Context) error SendCertificate(certificate *SignedCertificate) (common.Hash, error) GetCertificateHeader(certificateHash common.Hash) (*CertificateHeader, error) - GetLatestKnownCertificateHeader(networkID uint32) (*CertificateHeader, error) AggLayerClientGetEpochConfiguration AggLayerClientRecoveryQuerier } @@ -166,27 +165,6 @@ func (c *AggLayerClient) GetEpochConfiguration() (*ClockConfiguration, error) { return result, nil } -// GetLatestKnownCertificateHeader returns the last certificate header submitted by networkID -func (c *AggLayerClient) GetLatestKnownCertificateHeader(networkID uint32) (*CertificateHeader, error) { - response, err := jSONRPCCall(c.url, "interop_getLatestKnownCertificateHeader", networkID) - if err != nil { - return nil, fmt.Errorf("GetLatestKnownCertificateHeader error jSONRPCCall. Err: %w", err) - } - - if response.Error != nil { - return nil, fmt.Errorf("GetLatestKnownCertificateHeader rpc returns an error: code=%d msg=%s", - response.Error.Code, response.Error.Message) - } - - var result *CertificateHeader - err = json.Unmarshal(response.Result, &result) - if err != nil { - return nil, fmt.Errorf("GetLatestKnownCertificateHeader error Unmashal. Err: %w", err) - } - - return result, nil -} - func (c *AggLayerClient) GetLatestSettledCertificateHeader(networkID uint32) (*CertificateHeader, error) { response, err := jSONRPCCall(c.url, "interop_getLatestSettledCertificateHeader", networkID) if err != nil { diff --git a/agglayer/mock_agglayer_client.go b/agglayer/mock_agglayer_client.go index 033d3b83..1497cc24 100644 --- a/agglayer/mock_agglayer_client.go +++ b/agglayer/mock_agglayer_client.go @@ -138,64 +138,6 @@ func (_c *AgglayerClientMock_GetEpochConfiguration_Call) RunAndReturn(run func() return _c } -// GetLatestKnownCertificateHeader provides a mock function with given fields: networkID -func (_m *AgglayerClientMock) GetLatestKnownCertificateHeader(networkID uint32) (*CertificateHeader, error) { - ret := _m.Called(networkID) - - if len(ret) == 0 { - panic("no return value specified for GetLatestKnownCertificateHeader") - } - - var r0 *CertificateHeader - var r1 error - if rf, ok := ret.Get(0).(func(uint32) (*CertificateHeader, error)); ok { - return rf(networkID) - } - if rf, ok := ret.Get(0).(func(uint32) *CertificateHeader); ok { - r0 = rf(networkID) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*CertificateHeader) - } - } - - if rf, ok := ret.Get(1).(func(uint32) error); ok { - r1 = rf(networkID) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// AgglayerClientMock_GetLatestKnownCertificateHeader_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetLatestKnownCertificateHeader' -type AgglayerClientMock_GetLatestKnownCertificateHeader_Call struct { - *mock.Call -} - -// GetLatestKnownCertificateHeader is a helper method to define mock.On call -// - networkID uint32 -func (_e *AgglayerClientMock_Expecter) GetLatestKnownCertificateHeader(networkID interface{}) *AgglayerClientMock_GetLatestKnownCertificateHeader_Call { - return &AgglayerClientMock_GetLatestKnownCertificateHeader_Call{Call: _e.mock.On("GetLatestKnownCertificateHeader", networkID)} -} - -func (_c *AgglayerClientMock_GetLatestKnownCertificateHeader_Call) Run(run func(networkID uint32)) *AgglayerClientMock_GetLatestKnownCertificateHeader_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(uint32)) - }) - return _c -} - -func (_c *AgglayerClientMock_GetLatestKnownCertificateHeader_Call) Return(_a0 *CertificateHeader, _a1 error) *AgglayerClientMock_GetLatestKnownCertificateHeader_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *AgglayerClientMock_GetLatestKnownCertificateHeader_Call) RunAndReturn(run func(uint32) (*CertificateHeader, error)) *AgglayerClientMock_GetLatestKnownCertificateHeader_Call { - _c.Call.Return(run) - return _c -} - // GetLatestPendingCertificateHeader provides a mock function with given fields: networkID func (_m *AgglayerClientMock) GetLatestPendingCertificateHeader(networkID uint32) (*CertificateHeader, error) { ret := _m.Called(networkID) diff --git a/aggsender/aggsender.go b/aggsender/aggsender.go index b74c25df..53f8efaa 100644 --- a/aggsender/aggsender.go +++ b/aggsender/aggsender.go @@ -128,6 +128,8 @@ func (a *AggSender) checkInitialStatus(ctx context.Context) { defer ticker.Stop() a.status.Status = types.StatusCheckingInitialStage for { + // + a.checkPendingCertificatesStatus(ctx) err := a.checkLastCertificateFromAgglayer(ctx) a.status.SetLastError(err) if err != nil { @@ -743,76 +745,30 @@ func (a *AggSender) checkLastCertificateFromAgglayer(ctx context.Context) error return fmt.Errorf("recovery: error retrieving initial status: %w", err) } initialStatus.LogData() - return nil -} - -// checkLastCertificateFromAgglayer checks the last certificate from agglayer -func (a *AggSender) checkLastCertificateFromAgglayerOld(ctx context.Context) error { - networkID := a.l2Syncer.OriginNetwork() - a.log.Infof("recovery: checking last certificate from AggLayer for network %d", networkID) - aggLayerLastCert, err := a.aggLayerClient.GetLatestKnownCertificateHeader(networkID) - if err != nil { - return fmt.Errorf("recovery: error getting latest known certificate header from agglayer: %w", err) - } - a.log.Infof("recovery: last certificate from AggLayer: %s", aggLayerLastCert.String()) - localLastCert, err := a.storage.GetLastSentCertificate() + action, err := initialStatus.Process2() if err != nil { - return fmt.Errorf("recovery: error getting last sent certificate from local storage: %w", err) + return fmt.Errorf("recovery: error processing initial status: %w", err) } - a.log.Infof("recovery: last certificate in storage: %s", localLastCert.String()) + err = a.executeInitialStatusAction(ctx, action, initialStatus.LocalCert) + return err +} - // CASE 1: No certificates in local storage and agglayer - if localLastCert == nil && aggLayerLastCert == nil { +func (a *AggSender) executeInitialStatusAction(ctx context.Context, action *InitialStatusResult, localCert *types.CertificateInfo) error { + a.log.Infof("recovery: action: %s", action.String()) + switch action.Action { + case InitialStatusActionNone: a.log.Info("recovery: No certificates in local storage and agglayer: initial state") - return nil - } - // CASE 2: No certificates in local storage but agglayer has one - if localLastCert == nil && aggLayerLastCert != nil { - a.log.Info("recovery: No certificates in local storage but agglayer have one: recovery aggSender cert: %s", - aggLayerLastCert.String()) - if _, err := a.updateLocalStorageWithAggLayerCert(ctx, aggLayerLastCert); err != nil { + case InitialStatusActionUpdateCurrentCert: + if err := a.updateCertificateStatus(ctx, localCert, action.Cert); err != nil { return fmt.Errorf("recovery: error updating local storage with agglayer certificate: %w", err) } - return nil - } - // CASE 2.1: certificate in storage but not in agglayer - // this is a non-sense, so throw an error - if localLastCert != nil && aggLayerLastCert == nil { - return fmt.Errorf("recovery: certificate exists in storage but not in agglayer. Inconsistency") - } - // CASE 3.1: the certificate on the agglayer has less height than the one stored in the local storage - if aggLayerLastCert.Height < localLastCert.Height { - return fmt.Errorf("recovery: the last certificate in the agglayer has less height (%d) "+ - "than the one in the local storage (%d)", aggLayerLastCert.Height, localLastCert.Height) - } - // CASE 3.2: aggsender stopped between sending to agglayer and storing to the local storage - if aggLayerLastCert.Height == localLastCert.Height+1 { - a.log.Infof("recovery: AggLayer has the next cert (height: %d), so is a recovery case: storing cert: %s", - aggLayerLastCert.Height, aggLayerLastCert.String()) - // we need to store the certificate in the local storage. - localLastCert, err = a.updateLocalStorageWithAggLayerCert(ctx, aggLayerLastCert) - if err != nil { - log.Errorf("recovery: error updating certificate: %s, reason: %w", aggLayerLastCert.String(), err) - return fmt.Errorf("recovery: error updating certificate: %w", err) + case InitialStatusActionInsertNewCert: + if _, err := a.updateLocalStorageWithAggLayerCert(ctx, action.Cert); err != nil { + return fmt.Errorf("recovery: error new local storage with agglayer certificate: %w", err) } + default: + return fmt.Errorf("recovery: unknown action: %s", action.Action) } - // CASE 4: AggSender and AggLayer are not on the same page - // note: we don't need to check individual fields of the certificate - // because CertificateID is a hash of all the fields - if localLastCert.CertificateID != aggLayerLastCert.CertificateID { - a.log.Errorf("recovery: Local certificate:\n %s \n is different from agglayer certificate:\n %s", - localLastCert.String(), aggLayerLastCert.String()) - return fmt.Errorf("recovery: mismatch between local and agglayer certificates") - } - // CASE 5: AggSender and AggLayer are at same page - // just update status - err = a.updateCertificateStatus(ctx, localLastCert, aggLayerLastCert) - if err != nil { - a.log.Errorf("recovery: error updating status certificate: %s status: %w", aggLayerLastCert.String(), err) - return fmt.Errorf("recovery: error updating certificate status: %w", err) - } - - a.log.Infof("recovery: successfully checked last certificate from AggLayer for network %d", networkID) return nil } diff --git a/aggsender/aggsender_initial_state.go b/aggsender/aggsender_initial_state.go index f8326618..795b4e57 100644 --- a/aggsender/aggsender_initial_state.go +++ b/aggsender/aggsender_initial_state.go @@ -9,10 +9,10 @@ import ( ) type InitialStatus struct { - AggLayerLastSettledCert *agglayer.CertificateHeader - AggLayerLastPendingCert *agglayer.CertificateHeader - LocalCert *types.CertificateInfo - log types.Logger + SettledCert *agglayer.CertificateHeader + PendingCert *agglayer.CertificateHeader + LocalCert *types.CertificateInfo + log types.Logger } type InitialStatusAction int @@ -23,6 +23,11 @@ const ( InitialStatusActionInsertNewCert ) +// String representation of the enum +func (i InitialStatusAction) String() string { + return [...]string{"None", "Update", "InsertNew"}[i] +} + var ( ErrorAgglayerInconsistence = fmt.Errorf("recovery: agglayer incosistence") ErrorMismatchStateAgglayerAndLocal = fmt.Errorf("recovery: mismatch between local and agglayer certificates") @@ -35,14 +40,29 @@ type InitialStatusResult struct { Cert *agglayer.CertificateHeader } +func (i *InitialStatusResult) String() string { + if i == nil { + return "nil" + } + res := fmt.Sprintf("Action: %d, Message: %s", i.Action, i.Message) + + if i.Cert != nil { + res += fmt.Sprintf(", Cert: %s", i.Cert.ID()) + } else { + res += ", Cert: nil" + } + return res +} + // NewInitialStatus creates a new InitialStatus object, get the data from AggLayer and local storage func NewInitialStatus(log types.Logger, networkID uint32, storage db.AggSenderStorage, aggLayerClient agglayer.AggLayerClientRecoveryQuerier) (*InitialStatus, error) { - log.Infof("recovery: checking last certificate from AggLayer for network %d", networkID) + log.Infof("recovery: checking last settled certificate from AggLayer for network %d", networkID) aggLayerLastSettledCert, err := aggLayerClient.GetLatestSettledCertificateHeader(networkID) if err != nil { return nil, fmt.Errorf("recovery: error getting GetLatestSettledCertificateHeader from agglayer: %w", err) } + log.Infof("recovery: checking last pending certificate from AggLayer for network %d", networkID) aggLayerLastPendingCert, err := aggLayerClient.GetLatestPendingCertificateHeader(networkID) if err != nil { return nil, fmt.Errorf("recovery: error getting GetLatestPendingCertificateHeader from agglayer: %w", err) @@ -53,20 +73,94 @@ func NewInitialStatus(log types.Logger, networkID uint32, storage db.AggSenderSt return nil, fmt.Errorf("recovery: error getting last sent certificate from local storage: %w", err) } return &InitialStatus{ - AggLayerLastSettledCert: aggLayerLastSettledCert, - AggLayerLastPendingCert: aggLayerLastPendingCert, - LocalCert: localLastCert, - log: log, + SettledCert: aggLayerLastSettledCert, // from Agglayer + PendingCert: aggLayerLastPendingCert, // from Agglayer + LocalCert: localLastCert, + log: log, }, nil } // LogData logs the data from the InitialStatus object func (i *InitialStatus) LogData() { - i.log.Infof("recovery: last settled certificate from AggLayer: %s", i.AggLayerLastSettledCert.ID()) - i.log.Infof("recovery: last pending certificate from AggLayer: %s / status: %s", i.AggLayerLastPendingCert.ID(), i.AggLayerLastPendingCert.StatusString()) + i.log.Infof("recovery: last settled certificate from AggLayer: %s", i.SettledCert.ID()) + i.log.Infof("recovery: last pending certificate from AggLayer: %s / status: %s", i.PendingCert.ID(), i.PendingCert.StatusString()) i.log.Infof("recovery: last certificate from Local : %s / status: %s", i.LocalCert.ID(), i.LocalCert.StatusString()) } +// checkLastCertificateFromAgglayer checks the last certificate from agglayer +func (i *InitialStatus) Process2() (*InitialStatusResult, error) { + // Check that agglayer data is consistent. + if err := i.checkAgglayerConsistenceCerts(); err != nil { + return nil, err + } + if i.LocalCert == nil && i.SettledCert == nil && i.PendingCert != nil { + if i.PendingCert.Height == 0 { + return &InitialStatusResult{Action: InitialStatusActionInsertNewCert, + Message: "no settled cert yet, and the pending cert have the righ height(0) so we use it", + Cert: i.PendingCert}, nil + } + + // We don't known if pendingCert is going to be settle or error. + // We can't use it becasue maybe is error wrong height + if !i.PendingCert.Status.IsInError() && i.PendingCert.Height > 0 { + return nil, fmt.Errorf("recovery: pendingCert %s is in state %s but have a suspicious height, so we wait to finish", + i.PendingCert.ID(), i.PendingCert.StatusString()) + } + if i.PendingCert.Status.IsInError() && i.PendingCert.Height > 0 { + return &InitialStatusResult{Action: InitialStatusActionNone, + Message: "the pending cert have wrong height and it's on error. We ignore it", + Cert: nil}, nil + } + } + aggLayerLastCert := i.getLastAggLayerCert() + i.log.Infof("recovery: last certificate from AggLayer: %s", aggLayerLastCert.String()) + localLastCert := i.LocalCert + + // CASE 1: No certificates in local storage and agglayer + if localLastCert == nil && aggLayerLastCert == nil { + return &InitialStatusResult{Action: InitialStatusActionNone, + Message: "no certificates in local storage and agglayer: initial state", + Cert: nil}, nil + } + // CASE 2: No certificates in local storage but agglayer has one + if localLastCert == nil && aggLayerLastCert != nil { + return &InitialStatusResult{Action: InitialStatusActionInsertNewCert, + Message: "no certificates in local storage but agglayer have one (no InError)", + Cert: aggLayerLastCert}, nil + } + // CASE 2.1: certificate in storage but not in agglayer + // this is a non-sense, so throw an error + if localLastCert != nil && aggLayerLastCert == nil { + return nil, fmt.Errorf("recovery: certificate exists in storage but not in agglayer. Inconsistency") + } + // CASE 3.1: the certificate on the agglayer has less height than the one stored in the local storage + if aggLayerLastCert.Height < localLastCert.Height { + return nil, fmt.Errorf("recovery: the last certificate in the agglayer has less height (%d) "+ + "than the one in the local storage (%d)", aggLayerLastCert.Height, localLastCert.Height) + } + // CASE 3.2: aggsender stopped between sending to agglayer and storing to the local storage + if aggLayerLastCert.Height == localLastCert.Height+1 { + // we need to store the certificate in the local storage. + return &InitialStatusResult{Action: InitialStatusActionInsertNewCert, + Message: fmt.Sprintf("aggsender stopped between sending to agglayer and storing to the local storage: storing cert: %s", + aggLayerLastCert.ID()), + Cert: aggLayerLastCert}, nil + } + // CASE 4: AggSender and AggLayer are not on the same page + // note: we don't need to check individual fields of the certificate + // because CertificateID is a hash of all the fields + if localLastCert.CertificateID != aggLayerLastCert.CertificateID { + return nil, fmt.Errorf("recovery: Local certificate:\n %s \n is different from agglayer certificate:\n %s", + localLastCert.String(), aggLayerLastCert.String()) + } + // CASE 5: AggSender and AggLayer are at same page + // just update status + return &InitialStatusResult{Action: InitialStatusActionUpdateCurrentCert, + Message: fmt.Sprintf("aggsender same cert, updating state: %s", + aggLayerLastCert.ID()), + Cert: aggLayerLastCert}, nil +} + // Process checks the data from the InitialStatus object and returns the action to be taken or error func (i *InitialStatus) Process() (*InitialStatusResult, error) { // CASE1: Same page @@ -140,18 +234,33 @@ func (i *InitialStatus) checkAgglayerConsistenceCerts() error { if i.isEmptyAggLayer() { return nil } - if i.AggLayerLastPendingCert != nil || i.AggLayerLastSettledCert == nil { - if !i.AggLayerLastPendingCert.Status.IsInError() && i.AggLayerLastPendingCert.Height != 0 { - return fmt.Errorf("no settled cert, and pending one is height %d and not in error. Err: %w", i.AggLayerLastPendingCert.Height, ErrorAgglayerInconsistence) + if i.PendingCert != nil && i.SettledCert == nil { + // If Height>0 and not inError, we have a problem. We should have a settled cert + if !i.PendingCert.Status.IsInError() && i.PendingCert.Height != 0 { + return fmt.Errorf("consistence: no settled cert, and pending one is height %d and not in error. Err: %w", i.PendingCert.Height, ErrorAgglayerInconsistence) } return nil } - if i.AggLayerLastSettledCert != nil || i.AggLayerLastPendingCert != nil { - if i.AggLayerLastPendingCert.Height == i.AggLayerLastSettledCert.Height && - i.AggLayerLastSettledCert.CertificateID != i.AggLayerLastPendingCert.CertificateID && - !i.AggLayerLastSettledCert.Status.IsInError() { - return fmt.Errorf("settled (%s) and pending (%s) certs are different for same height. Err: %w", - i.AggLayerLastSettledCert.ID(), i.AggLayerLastPendingCert.ID(), + + // If only settled cert, there no chance of inconsistency + if i.PendingCert == nil && i.SettledCert != nil { + return nil + } + + // If both settled and pending cert, that is the potential inconsistency + if i.SettledCert != nil && i.PendingCert != nil { + // This is there is a settled cert for a height but also a pending cert for the same height + if i.PendingCert.Height == i.SettledCert.Height && + i.SettledCert.CertificateID != i.PendingCert.CertificateID && + !i.SettledCert.Status.IsInError() { + return fmt.Errorf("consistence: settled (%s) and pending (%s) certs are different for same height. Err: %w", + i.SettledCert.ID(), i.PendingCert.ID(), + ErrorAgglayerInconsistence) + } + // + if i.SettledCert.Height > i.PendingCert.Height && !i.SettledCert.Status.IsInError() { + return fmt.Errorf("settled cert height %s is higher than pending cert height %s that is inNoError. Err: %w", + i.SettledCert.ID(), i.PendingCert.ID(), ErrorAgglayerInconsistence) } } @@ -181,7 +290,7 @@ func (i *InitialStatus) isLocalAndAggLayerEqual() bool { } func (i *InitialStatus) isEmptyAggLayer() bool { - return i.AggLayerLastPendingCert == nil && i.AggLayerLastSettledCert == nil + return i.PendingCert == nil && i.SettledCert == nil } func (i *InitialStatus) isEmptyLocal() bool { @@ -194,17 +303,18 @@ func (i *InitialStatus) isLocalAndAggLayerSameHeight() bool { } func (i *InitialStatus) getLastAggLayerCert() *agglayer.CertificateHeader { - if i.AggLayerLastPendingCert == nil { - return i.AggLayerLastSettledCert - } - // If the lastCert is not inError we return it as last one - if !i.AggLayerLastPendingCert.Status.IsInError() { - return i.AggLayerLastPendingCert - } - if i.AggLayerLastPendingCert.Status.IsInError() && i.LocalCert != nil && i.LocalCert.CertificateID == i.AggLayerLastPendingCert.CertificateID { - return i.AggLayerLastPendingCert + if i.PendingCert == nil { + return i.SettledCert } - return i.AggLayerLastSettledCert + return i.PendingCert + // // If the lastCert is not inError we return it as last one + // if !i.PendingCert.Status.IsInError() { + // return i.PendingCert + // } + // if i.PendingCert.Status.IsInError() && i.LocalCert != nil && i.LocalCert.CertificateID == i.PendingCert.CertificateID { + // return i.PendingCert + // } + //return i.SettledCert } func (i *InitialStatus) thereAreNoInErrorAggLayerCert() bool { @@ -212,11 +322,11 @@ func (i *InitialStatus) thereAreNoInErrorAggLayerCert() bool { } func (i *InitialStatus) getNoInErrorAggLayerCert() *agglayer.CertificateHeader { - if i.AggLayerLastPendingCert != nil && !i.AggLayerLastPendingCert.Status.IsInError() { - return i.AggLayerLastPendingCert + if i.PendingCert != nil && !i.PendingCert.Status.IsInError() { + return i.PendingCert } - if i.AggLayerLastSettledCert != nil && !i.AggLayerLastSettledCert.Status.IsInError() { - return i.AggLayerLastSettledCert + if i.SettledCert != nil && !i.SettledCert.Status.IsInError() { + return i.SettledCert } return nil } diff --git a/aggsender/aggsender_initial_state_test.go b/aggsender/aggsender_initial_state_test.go index e3454458..d550a84b 100644 --- a/aggsender/aggsender_initial_state_test.go +++ b/aggsender/aggsender_initial_state_test.go @@ -1,6 +1,7 @@ package aggsender import ( + "fmt" "testing" "github.com/agglayer/aggkit/agglayer" @@ -10,94 +11,235 @@ import ( "github.com/stretchr/testify/require" ) -// LOCAL | AGGLAYER SETTLED | AGGLAYER PENDING | ACTION -//-------------------------------------------------------------------------------------------------- -// ID | h | st | ID | h | st | ID | h | st | -// ------------------------------------------------------------------------------------------------- - -// nil | nil | nil | none -// nil | nil | ID1| NA | inError | none -// nil | nil | ID1| h0 | !=inError | store(PENDING) -// nil | ID1| h1 | NA | nil | store(SETTLE) -// nil | ID1| h1 | NA | ID2| h2 | inError | store(SETTLE) -// nil | ID1| h1 | NA | ID2| h2 | !=inError | store(SETTLE) - -// ID1| h1 | NA | nil | ID1| h1 | inError | update(PENDING) -// ID1| h1 | NA | nil | ID1| h1 | !=inError | update(PENDING) -// ID2| h2 | NA | ID1| h1 | N/A | ID2| h2 | N/A | update(PENDING) -// ID2| h2 | NA | ID1| h3 | N/A | nil | update(h2, ask_agglayer(h2)) + store(SETTLED) -// ID2| h2 | NA | ID1| h2 | settled | ID1| h3 | !=inError | update(SETTLED) + store(PENDING) -// ID2| h2 | NA | ID1| h2 | settled | ID1| h3 | inError | update(SETTLED) - type certTestData struct { CertificateID common.Hash Height uint64 Status agglayer.CertificateStatus } +type initialStateResultTest struct { + action InitialStatusAction + subMsg string + cert *certTestData +} + type testCaseData struct { name string localCert *certTestData agglayerSettled *certTestData agglayerPending *certTestData - resultError error - resultActions *InitialStatusResult + resultError bool + resultActions *initialStateResultTest } -// ID |LOCAL | AGGLAYER SETTLED | AGGLAYER PENDING | ACTION -// -// |-------------------------------------------------------------------------------------------------- -// |ID | h | st | ID | h | st | ID | h | st | +// ID|LOCAL | AGGLAYER SETTLED | AGGLAYER PENDING | ACTION +// |------------------------------------------------------------------------------------------------- +// |ID | h | st | ID | h | st | ID | h | st | // |------------------------------------------------------------------------------------------------- // 1|N/A | ID1| h1 | NA | ID2| h1 | !=inError | Agglayer incosistence // 2|N/A | ID1| h2 | NA | ID2| h1 | !=inError | Agglayer incosistence??? // 3|nil | nil | ID1| >h0 | !=inError | Agglayer incosistence??? -// 4|ID1| h1 | Inerror | nil | nil | AggSender incosistence -// 5|ID1| h1 | Settled | nil | nil | AggSender incosistence -// 6|ID1| h3 | NA | NA | ID2| h2 | N/A | AggSender incosistence -// 7|ID1| h3 | NA | ID2 | h2 |NA | NA | AggSender incosistence -// 8|ID2| h2 | NA | ID1| h3 | N/A | ID3| h4 | !=inError | AggSender incosistence (2cert jump) -// 9|ID2| h2 | NA | ID1| h3 | N/A | ID3| h4 | inError | store(SETTLED) we ignore pending inError +// 4|ID1| h1 | Inerror | nil | nil | AggSender incosistence +// 5|ID1| h1 | Settled | nil | nil | AggSender incosistence +// 6|ID1| h1 | !=closed | nil | nil | incosistence + +// 7|ID1| h3 | NA | NA | ID2| h2 | !=InError | AggSender incosistence +// 8|ID1| h3 | NA | ID2 | h2 |NA | NA | AggSender incosistence +// 9|ID2| h2 | NA | ID1| h3 | N/A | ID3| h4 | !=inError | AggSender incosistence (2cert jump) +// 10|ID2| h2 | NA | ID1| h3 | N/A | ID3| h4 | inError | AggSender incosistence (2cert jump) func TestInitialStateInconsistence(t *testing.T) { hash1 := common.HexToHash("0xdead") hash2 := common.HexToHash("0xbeef") - //hash3 := common.HexToHash("0xfeed") tests := []testCaseData{ { - name: "1| local:NA | settled: ID1| h1 | NA | pending:ID2| h1 | !=inError | Agglayer incosistence", + name: "1| local:NA | settled: {ID1| h1 | NA} | pending:{ID2| h1 | !=inError} | Agglayer incosistence", localCert: nil, agglayerSettled: &certTestData{hash1, 1, agglayer.Proven}, agglayerPending: &certTestData{hash2, 1, agglayer.Pending}, - resultError: ErrorAgglayerInconsistence, + resultError: true, }, { - name: "2| N/A | ID1| h2 | NA | ID2| h1 | !=inError | Agglayer incosistence???", + name: "2| local:NA | settled:{ID1| h2 | NA} | pending:{ID2| h1 | !=inError} | Agglayer incosistence???", localCert: nil, agglayerSettled: &certTestData{hash1, 2, agglayer.Proven}, agglayerPending: &certTestData{hash2, 1, agglayer.Pending}, - resultError: ErrorAgglayerInconsistence, + resultError: true, }, { - name: "3|nil | nil | ID1| >h0 | !=inError | Agglayer incosistence???", + name: "3|local:nil | settled:nil | pending:{ID1| >h0 | !=inError} | Agglayer incosistence???", localCert: nil, agglayerSettled: nil, agglayerPending: &certTestData{hash1, 1, agglayer.Pending}, - resultError: ErrorAgglayerInconsistence, + resultError: true, }, { - name: "4|ID1| h1 | Inerror | nil | nil | AggSender incosistence", + name: "4|local:{ID1| h1 | Inerror} | settled:nil | pending:nil | AggSender incosistence", localCert: &certTestData{hash1, 2, agglayer.InError}, agglayerSettled: nil, agglayerPending: nil, - resultError: ErrorMismatchStateAgglayerAndLocal, + resultError: true, }, { - name: "5|ID1| h1 | Settled | nil | nil | AggSender incosistence", + name: "5|ID1| h1 | Settled | settled:nil | pending:nil | AggSender incosistence", localCert: &certTestData{hash1, 2, agglayer.Settled}, agglayerSettled: nil, agglayerPending: nil, - resultError: ErrorMismatchStateAgglayerAndLocal, + resultError: true, + }, + { + name: "6|ID1| h0 | !=closed | nil | nil | incosistence", + localCert: &certTestData{hash1, 0, agglayer.Proven}, + agglayerSettled: nil, + agglayerPending: nil, + resultError: true, + }, + { + name: "7|ID1| h3 | NA | NA | ID2| h2 | !=InError | AggSender incosistence", + localCert: &certTestData{hash1, 3, agglayer.Proven}, + agglayerSettled: nil, + agglayerPending: &certTestData{hash2, 2, agglayer.Proven}, + resultError: true, + }, + { + name: "8|ID1| h3 | NA | ID2 | h2 |NA | NA | AggSender incosistence", + localCert: &certTestData{hash1, 3, agglayer.Proven}, + agglayerSettled: &certTestData{hash2, 2, agglayer.Proven}, + agglayerPending: nil, + resultError: true, + }, + { + name: "9|ID2| h2 | NA | ID1| h3 | N/A | ID3| h4 | !=inError | AggSender incosistence (2cert jump)", + localCert: &certTestData{hash1, 2, agglayer.Proven}, + agglayerSettled: &certTestData{hash2, 3, agglayer.Settled}, + agglayerPending: &certTestData{hash2, 4, agglayer.Proven}, + resultError: true, + }, + { + name: "10|ID2| h2 | NA | ID1| h3 | N/A | ID3| h4 | inError | AggSender incosistence (2cert jump)", + localCert: &certTestData{hash1, 2, agglayer.Proven}, + agglayerSettled: &certTestData{hash2, 3, agglayer.Settled}, + agglayerPending: &certTestData{hash2, 4, agglayer.InError}, + resultError: true, + }, + } + runTestCases(t, tests) +} + +// ID|LOCAL | AGGLAYER SETTLED | AGGLAYER PENDING | ACTION +// +// |------------------------------------------------------------------------------------------------- +// |ID , h , st | ID , h , st | ID , h , st | +// |------------------------------------------------------------------------------------------------- +// 1| nil | nil | nil | none +// 2| nil | nil | ID1, h0 , inError | store(PENDING) h0 so is next cert +// 3| nil | nil | ID1, h1 , inError | none +// 4| nil | nil | ID1, h0 , !=inError | store(PENDING) h0 so is next cert +// 5| nil | nil | ID1, h1 , !=inError | wait, h1 is not next cert but we wait until pass to inError +// 6| nil | ID1, h1 , NA | nil | store(SETTLE) +// 7| nil | ID1, h1 , NA | ID2, h2 , inError | store(PENDING) +// 8| nil | ID1, h1 , NA | ID2, h2 , !=inError | store(PENDING) h2 is next to h1 +// 9|ID1, h1 , NA | nil | ID1, h1 , inError | update(PENDING) +// 10|ID2, h2 , NA | ID1, h1 , N/A | ID2, h2 , N/A | update(PENDING) +// 11|ID2, h2 , NA | ID1, h3 , N/A | nil | store(SETTLED) +// 12|ID2, h2 , NA | ID1, h2 , settled | ID1, h3 , !=inError | store(PENDING) +// 13|ID2, h2 , NA | ID1, h2 , settled | ID1, h3 , inError | store(PENDING) +func TestRegularCases(t *testing.T) { + hash1 := common.HexToHash("0xdead") + hash2 := common.HexToHash("0xbeef") + + tests := []testCaseData{ + { + name: "01| nil | nil | nil | none", + localCert: nil, + agglayerSettled: nil, + agglayerPending: nil, + resultActions: &initialStateResultTest{InitialStatusActionNone, "", nil}, + }, + { + name: "02| nil | nil | ID1, h0 , inError |store(PENDING) h0 so is next cert", + localCert: nil, + agglayerSettled: nil, + agglayerPending: &certTestData{hash1, 0, agglayer.InError}, + resultActions: &initialStateResultTest{InitialStatusActionInsertNewCert, "", &certTestData{hash1, 0, agglayer.InError}}, + }, + { + name: "03| nil | nil | ID1, h1 , inError |none", + localCert: nil, + agglayerSettled: nil, + agglayerPending: &certTestData{hash1, 1, agglayer.InError}, + resultActions: &initialStateResultTest{InitialStatusActionNone, "", nil}, + }, + { + name: "04| nil | nil | ID1, h0 , !=inError | store(PENDING) h0 so is next cert", + localCert: nil, + agglayerSettled: nil, + agglayerPending: &certTestData{hash1, 0, agglayer.Proven}, + resultActions: &initialStateResultTest{InitialStatusActionInsertNewCert, "", &certTestData{hash1, 0, agglayer.Proven}}, + }, + { + name: "05| nil | nil | ID1, h1 , !=inError | wait, h1 is not next cert but we wait until pass to inError", + localCert: nil, + agglayerSettled: nil, + agglayerPending: &certTestData{hash1, 1, agglayer.Proven}, + resultError: true, + }, + { + name: "06| nil | ID1, h1 , NA | nil | store(SETTLE)", + localCert: nil, + agglayerSettled: &certTestData{hash1, 1, agglayer.Proven}, + agglayerPending: nil, + resultActions: &initialStateResultTest{InitialStatusActionInsertNewCert, "", &certTestData{hash1, 1, agglayer.Proven}}, + }, + { + name: "07| nil | ID1, h1 , NA | ID2, h2 , inError | store(PENDING)", + localCert: nil, + agglayerSettled: &certTestData{hash1, 1, agglayer.Proven}, + agglayerPending: &certTestData{hash2, 2, agglayer.InError}, + resultActions: &initialStateResultTest{InitialStatusActionInsertNewCert, "", &certTestData{hash2, 2, agglayer.InError}}, + }, + { + name: "08| nil | ID1, h1 , NA | ID2, h2 , !=inError | store(PENDING) h2 is next to h1", + localCert: nil, + agglayerSettled: &certTestData{hash1, 1, agglayer.Settled}, + agglayerPending: &certTestData{hash2, 2, agglayer.Pending}, + resultActions: &initialStateResultTest{InitialStatusActionInsertNewCert, "", &certTestData{hash2, 2, agglayer.Pending}}, + }, + { + name: "09|ID1, h1 , NA | nil | ID1, h1 , inError | update(PENDING)", + localCert: &certTestData{hash1, 1, agglayer.Proven}, + agglayerSettled: nil, + agglayerPending: &certTestData{hash1, 1, agglayer.InError}, + resultActions: &initialStateResultTest{InitialStatusActionUpdateCurrentCert, "", &certTestData{hash1, 1, agglayer.InError}}, + }, + + { + name: "10|ID2, h2 , NA | ID1, h1 , N/A | ID2, h2 , N/A | update(PENDING)", + localCert: &certTestData{hash2, 2, agglayer.Proven}, + agglayerSettled: &certTestData{hash1, 1, agglayer.Settled}, + agglayerPending: &certTestData{hash2, 2, agglayer.InError}, + resultActions: &initialStateResultTest{InitialStatusActionUpdateCurrentCert, "", &certTestData{hash2, 2, agglayer.InError}}, + }, + { + name: "11|ID2, h2 , NA | ID1, h3 , N/A | nil | store(SETTLED)", + localCert: &certTestData{hash2, 2, agglayer.Proven}, + agglayerSettled: &certTestData{hash1, 3, agglayer.Proven}, + agglayerPending: nil, + resultActions: &initialStateResultTest{InitialStatusActionInsertNewCert, "", &certTestData{hash1, 3, agglayer.Proven}}, + }, + { + name: "12|ID2, h2 , NA | ID1, h2 , settled | ID1, h3 , !=inError | store(PENDING)", + localCert: &certTestData{hash2, 2, agglayer.Proven}, + agglayerSettled: &certTestData{hash1, 2, agglayer.Settled}, + agglayerPending: &certTestData{hash1, 3, agglayer.Proven}, + resultActions: &initialStateResultTest{InitialStatusActionInsertNewCert, "", &certTestData{hash1, 3, agglayer.Proven}}, + }, + { + name: "13|ID2, h2 , NA | ID1, h2 , settled | ID1, h3 , inError | store(PENDING)", + localCert: &certTestData{hash2, 2, agglayer.Proven}, + agglayerSettled: &certTestData{hash1, 2, agglayer.Settled}, + agglayerPending: &certTestData{hash1, 3, agglayer.InError}, + resultActions: &initialStateResultTest{InitialStatusActionInsertNewCert, "", &certTestData{hash1, 3, agglayer.InError}}, }, } runTestCases(t, tests) @@ -118,26 +260,38 @@ func runTestCases(t *testing.T, tests []testCaseData) { } } if tt.agglayerSettled != nil { - sut.AggLayerLastSettledCert = &agglayer.CertificateHeader{ + sut.SettledCert = &agglayer.CertificateHeader{ CertificateID: tt.agglayerSettled.CertificateID, Height: tt.agglayerSettled.Height, Status: tt.agglayerSettled.Status, } } if tt.agglayerPending != nil { - sut.AggLayerLastPendingCert = &agglayer.CertificateHeader{ + sut.PendingCert = &agglayer.CertificateHeader{ CertificateID: tt.agglayerPending.CertificateID, Height: tt.agglayerPending.Height, Status: tt.agglayerPending.Status, } } - actions, err := sut.Process() - if tt.resultError != nil { + action, err := sut.Process2() + if tt.resultError { require.Error(t, err) - require.Nil(t, actions) + require.Nil(t, action) } else { require.NoError(t, err) + if tt.resultActions != nil { + fmt.Print("test:", tt.name) + fmt.Print("result:", action.String()) + require.Equal(t, tt.resultActions.action, action.Action) + require.Contains(t, action.Message, tt.resultActions.subMsg) + if tt.resultActions.cert != nil { + require.NotNil(t, action.Cert) + require.Equal(t, tt.resultActions.cert.CertificateID, action.Cert.CertificateID) + require.Equal(t, tt.resultActions.cert.Height, action.Cert.Height) + require.Equal(t, tt.resultActions.cert.Status, action.Cert.Status) + } + } } }) } diff --git a/aggsender/aggsender_test.go b/aggsender/aggsender_test.go index 5880972d..c1d0c867 100644 --- a/aggsender/aggsender_test.go +++ b/aggsender/aggsender_test.go @@ -293,7 +293,8 @@ func TestAggSenderStart(t *testing.T) { epochNotifierMock.EXPECT().Subscribe("aggsender").Return(ch) bridgeL2SyncerMock.EXPECT().OriginNetwork().Return(uint32(1)) bridgeL2SyncerMock.EXPECT().GetLastProcessedBlock(mock.Anything).Return(uint64(0), nil) - aggLayerMock.EXPECT().GetLatestKnownCertificateHeader(mock.Anything).Return(nil, nil) + aggLayerMock.EXPECT().GetLatestPendingCertificateHeader(mock.Anything).Return(nil, nil) + aggLayerMock.EXPECT().GetLatestSettledCertificateHeader(mock.Anything).Return(nil, nil) go aggSender.Start(ctx) ch <- aggsendertypes.EpochEvent{ @@ -1749,18 +1750,27 @@ func TestExtractFromCertificateMetadataToBlock(t *testing.T) { func TestCheckLastCertificateFromAgglayer_ErrorAggLayer(t *testing.T) { testData := newAggsenderTestData(t, testDataFlagMockStorage) - testData.l2syncerMock.EXPECT().OriginNetwork().Return(networkIDTest).Once() - testData.agglayerClientMock.EXPECT().GetLatestKnownCertificateHeader(networkIDTest).Return(nil, fmt.Errorf("unittest error")).Once() - - err := testData.sut.checkLastCertificateFromAgglayer(testData.ctx) - require.Error(t, err) + t.Run("error getting last settled cert", func(t *testing.T) { + testData.l2syncerMock.EXPECT().OriginNetwork().Return(networkIDTest).Maybe() + testData.agglayerClientMock.EXPECT().GetLatestSettledCertificateHeader(networkIDTest).Return(nil, fmt.Errorf("unittest error")).Once() + err := testData.sut.checkLastCertificateFromAgglayer(testData.ctx) + require.Error(t, err) + }) + t.Run("error getting last pending cert", func(t *testing.T) { + testData.l2syncerMock.EXPECT().OriginNetwork().Return(networkIDTest).Maybe() + testData.agglayerClientMock.EXPECT().GetLatestSettledCertificateHeader(networkIDTest).Return(nil, nil).Once() + testData.agglayerClientMock.EXPECT().GetLatestPendingCertificateHeader(networkIDTest).Return(nil, fmt.Errorf("unittest error")).Maybe() + err := testData.sut.checkLastCertificateFromAgglayer(testData.ctx) + require.Error(t, err) + }) } func TestCheckLastCertificateFromAgglayer_ErrorStorageGetLastSentCertificate(t *testing.T) { testData := newAggsenderTestData(t, testDataFlagMockStorage) - testData.l2syncerMock.EXPECT().OriginNetwork().Return(networkIDTest).Once() - testData.agglayerClientMock.EXPECT().GetLatestKnownCertificateHeader(networkIDTest).Return(nil, nil).Once() + testData.l2syncerMock.EXPECT().OriginNetwork().Return(networkIDTest).Maybe() + testData.agglayerClientMock.EXPECT().GetLatestSettledCertificateHeader(networkIDTest).Return(nil, nil).Maybe() + testData.agglayerClientMock.EXPECT().GetLatestPendingCertificateHeader(networkIDTest).Return(nil, nil).Maybe() testData.storageMock.EXPECT().GetLastSentCertificate().Return(nil, fmt.Errorf("unittest error")) err := testData.sut.checkLastCertificateFromAgglayer(testData.ctx) @@ -1774,7 +1784,8 @@ func TestCheckLastCertificateFromAgglayer_ErrorStorageGetLastSentCertificate(t * func TestCheckLastCertificateFromAgglayer_Case1NoCerts(t *testing.T) { testData := newAggsenderTestData(t, testDataFlagNone) testData.l2syncerMock.EXPECT().OriginNetwork().Return(networkIDTest).Once() - testData.agglayerClientMock.EXPECT().GetLatestKnownCertificateHeader(networkIDTest).Return(nil, nil).Once() + testData.agglayerClientMock.EXPECT().GetLatestSettledCertificateHeader(networkIDTest).Return(nil, nil).Maybe() + testData.agglayerClientMock.EXPECT().GetLatestPendingCertificateHeader(networkIDTest).Return(nil, nil).Maybe() err := testData.sut.checkLastCertificateFromAgglayer(testData.ctx) @@ -1787,7 +1798,8 @@ func TestCheckLastCertificateFromAgglayer_Case1NoCerts(t *testing.T) { func TestCheckLastCertificateFromAgglayer_Case2NoCertLocalCertRemote(t *testing.T) { testData := newAggsenderTestData(t, testDataFlagNone) testData.l2syncerMock.EXPECT().OriginNetwork().Return(networkIDTest).Once() - testData.agglayerClientMock.EXPECT().GetLatestKnownCertificateHeader(networkIDTest). + testData.agglayerClientMock.EXPECT().GetLatestSettledCertificateHeader(mock.Anything).Return(nil, nil) + testData.agglayerClientMock.EXPECT().GetLatestPendingCertificateHeader(networkIDTest). Return(certInfoToCertHeader(t, &testData.testCerts[0], networkIDTest), nil).Once() err := testData.sut.checkLastCertificateFromAgglayer(testData.ctx) @@ -1803,8 +1815,10 @@ func TestCheckLastCertificateFromAgglayer_Case2NoCertLocalCertRemote(t *testing. func TestCheckLastCertificateFromAgglayer_Case2NoCertLocalCertRemoteErrorStorage(t *testing.T) { testData := newAggsenderTestData(t, testDataFlagMockStorage) testData.l2syncerMock.EXPECT().OriginNetwork().Return(networkIDTest).Once() - testData.agglayerClientMock.EXPECT().GetLatestKnownCertificateHeader(networkIDTest). + testData.agglayerClientMock.EXPECT().GetLatestSettledCertificateHeader(mock.Anything).Return(nil, nil) + testData.agglayerClientMock.EXPECT().GetLatestPendingCertificateHeader(networkIDTest). Return(certInfoToCertHeader(t, &testData.testCerts[0], networkIDTest), nil).Once() + testData.storageMock.EXPECT().GetLastSentCertificate().Return(nil, nil) testData.storageMock.EXPECT().SaveLastSentCertificate(mock.Anything, mock.Anything).Return(errTest).Once() err := testData.sut.checkLastCertificateFromAgglayer(testData.ctx) @@ -1817,8 +1831,8 @@ func TestCheckLastCertificateFromAgglayer_Case2NoCertLocalCertRemoteErrorStorage func TestCheckLastCertificateFromAgglayer_Case2_1NoCertRemoteButCertLocal(t *testing.T) { testData := newAggsenderTestData(t, testDataFlagMockStorage) testData.l2syncerMock.EXPECT().OriginNetwork().Return(networkIDTest).Once() - testData.agglayerClientMock.EXPECT().GetLatestKnownCertificateHeader(networkIDTest). - Return(nil, nil).Once() + testData.agglayerClientMock.EXPECT().GetLatestSettledCertificateHeader(mock.Anything).Return(nil, nil) + testData.agglayerClientMock.EXPECT().GetLatestPendingCertificateHeader(mock.Anything).Return(nil, nil) testData.storageMock.EXPECT().GetLastSentCertificate().Return(&testData.testCerts[0], nil) err := testData.sut.checkLastCertificateFromAgglayer(testData.ctx) @@ -1826,23 +1840,28 @@ func TestCheckLastCertificateFromAgglayer_Case2_1NoCertRemoteButCertLocal(t *tes } // CASE 3.1: the certificate on the agglayer has less height than the one stored in the local storage + func TestCheckLastCertificateFromAgglayer_Case3_1LessHeight(t *testing.T) { testData := newAggsenderTestData(t, testDataFlagMockStorage) testData.l2syncerMock.EXPECT().OriginNetwork().Return(networkIDTest).Once() - testData.agglayerClientMock.EXPECT().GetLatestKnownCertificateHeader(networkIDTest). + testData.agglayerClientMock.EXPECT().GetLatestSettledCertificateHeader(mock.Anything).Return(nil, nil) + testData.agglayerClientMock.EXPECT().GetLatestPendingCertificateHeader(networkIDTest). Return(certInfoToCertHeader(t, &testData.testCerts[0], networkIDTest), nil).Once() testData.storageMock.EXPECT().GetLastSentCertificate().Return(&testData.testCerts[1], nil) err := testData.sut.checkLastCertificateFromAgglayer(testData.ctx) - require.ErrorContains(t, err, "recovery: the last certificate in the agglayer has less height (1) than the one in the local storage (2)") + require.ErrorContains(t, err, "recovery: the last certificate in the agglayer has less height (0) than the one in the local storage (1)") } // CASE 3.2: AggSender and AggLayer not same height. AggLayer has a new certificate + func TestCheckLastCertificateFromAgglayer_Case3_2Mismatch(t *testing.T) { testData := newAggsenderTestData(t, testDataFlagMockStorage) testData.l2syncerMock.EXPECT().OriginNetwork().Return(networkIDTest).Once() - testData.agglayerClientMock.EXPECT().GetLatestKnownCertificateHeader(networkIDTest). + testData.agglayerClientMock.EXPECT().GetLatestSettledCertificateHeader(mock.Anything). + Return(certInfoToCertHeader(t, &testData.testCerts[0], networkIDTest), nil).Once() + testData.agglayerClientMock.EXPECT().GetLatestPendingCertificateHeader(networkIDTest). Return(certInfoToCertHeader(t, &testData.testCerts[1], networkIDTest), nil).Once() testData.storageMock.EXPECT().GetLastSentCertificate().Return(&testData.testCerts[0], nil) testData.storageMock.EXPECT().SaveLastSentCertificate(mock.Anything, mock.Anything).Return(nil).Once() @@ -1853,10 +1872,12 @@ func TestCheckLastCertificateFromAgglayer_Case3_2Mismatch(t *testing.T) { } // CASE 4: AggSender and AggLayer not same certificateID + func TestCheckLastCertificateFromAgglayer_Case4Mismatch(t *testing.T) { testData := newAggsenderTestData(t, testDataFlagMockStorage) testData.l2syncerMock.EXPECT().OriginNetwork().Return(networkIDTest).Once() - testData.agglayerClientMock.EXPECT().GetLatestKnownCertificateHeader(networkIDTest). + testData.agglayerClientMock.EXPECT().GetLatestSettledCertificateHeader(mock.Anything).Return(nil, nil) + testData.agglayerClientMock.EXPECT().GetLatestPendingCertificateHeader(networkIDTest). Return(certInfoToCertHeader(t, &testData.testCerts[0], networkIDTest), nil).Once() testData.storageMock.EXPECT().GetLastSentCertificate().Return(&testData.testCerts[1], nil) @@ -1866,10 +1887,12 @@ func TestCheckLastCertificateFromAgglayer_Case4Mismatch(t *testing.T) { } // CASE 5: AggSender and AggLayer same certificateID and same status + func TestCheckLastCertificateFromAgglayer_Case5SameStatus(t *testing.T) { testData := newAggsenderTestData(t, testDataFlagMockStorage) testData.l2syncerMock.EXPECT().OriginNetwork().Return(networkIDTest).Once() - testData.agglayerClientMock.EXPECT().GetLatestKnownCertificateHeader(networkIDTest). + testData.agglayerClientMock.EXPECT().GetLatestSettledCertificateHeader(mock.Anything).Return(nil, nil) + testData.agglayerClientMock.EXPECT().GetLatestPendingCertificateHeader(networkIDTest). Return(certInfoToCertHeader(t, &testData.testCerts[0], networkIDTest), nil).Once() testData.storageMock.EXPECT().GetLastSentCertificate().Return(&testData.testCerts[0], nil) @@ -1879,13 +1902,16 @@ func TestCheckLastCertificateFromAgglayer_Case5SameStatus(t *testing.T) { } // CASE 5: AggSender and AggLayer same certificateID and differ on status + func TestCheckLastCertificateFromAgglayer_Case5UpdateStatus(t *testing.T) { testData := newAggsenderTestData(t, testDataFlagMockStorage) testData.l2syncerMock.EXPECT().OriginNetwork().Return(networkIDTest).Once() aggLayerCert := certInfoToCertHeader(t, &testData.testCerts[0], networkIDTest) aggLayerCert.Status = agglayer.Settled - testData.agglayerClientMock.EXPECT().GetLatestKnownCertificateHeader(networkIDTest). + testData.agglayerClientMock.EXPECT().GetLatestSettledCertificateHeader(mock.Anything).Return(nil, nil) + testData.agglayerClientMock.EXPECT().GetLatestPendingCertificateHeader(networkIDTest). Return(aggLayerCert, nil).Once() + testData.storageMock.EXPECT().GetLastSentCertificate().Return(&testData.testCerts[0], nil) testData.storageMock.EXPECT().UpdateCertificate(mock.Anything, mock.Anything).Return(nil).Once() @@ -1895,13 +1921,16 @@ func TestCheckLastCertificateFromAgglayer_Case5UpdateStatus(t *testing.T) { } // CASE 4: AggSender and AggLayer same certificateID and differ on status but fails update + func TestCheckLastCertificateFromAgglayer_Case4ErrorUpdateStatus(t *testing.T) { testData := newAggsenderTestData(t, testDataFlagMockStorage) testData.l2syncerMock.EXPECT().OriginNetwork().Return(networkIDTest).Once() aggLayerCert := certInfoToCertHeader(t, &testData.testCerts[0], networkIDTest) aggLayerCert.Status = agglayer.Settled - testData.agglayerClientMock.EXPECT().GetLatestKnownCertificateHeader(networkIDTest). + testData.agglayerClientMock.EXPECT().GetLatestSettledCertificateHeader(mock.Anything).Return(nil, nil) + testData.agglayerClientMock.EXPECT().GetLatestPendingCertificateHeader(networkIDTest). Return(aggLayerCert, nil).Once() + testData.storageMock.EXPECT().GetLastSentCertificate().Return(&testData.testCerts[0], nil) testData.storageMock.EXPECT().UpdateCertificate(mock.Anything, mock.Anything).Return(errTest).Once() @@ -2126,13 +2155,13 @@ func newAggsenderTestData(t *testing.T, creationFlags testDataFlags) *aggsenderT } testCerts := []aggsendertypes.CertificateInfo{ { - Height: 1, + Height: 0, CertificateID: common.HexToHash("0x1"), NewLocalExitRoot: common.HexToHash("0x2"), Status: agglayer.Pending, }, { - Height: 2, + Height: 1, CertificateID: common.HexToHash("0x1a111"), NewLocalExitRoot: common.HexToHash("0x2a2"), Status: agglayer.Pending,