diff --git a/aggsender/aggsender_test.go b/aggsender/aggsender_test.go index 0d73d5cc6..afbd1c620 100644 --- a/aggsender/aggsender_test.go +++ b/aggsender/aggsender_test.go @@ -26,7 +26,6 @@ import ( mocksdb "github.com/agglayer/aggkit/db/compatibility/mocks" "github.com/agglayer/aggkit/grpc" "github.com/agglayer/aggkit/log" - treetypes "github.com/agglayer/aggkit/tree/types" "github.com/agglayer/go_signer/signer" signertypes "github.com/agglayer/go_signer/signer/types" "github.com/ethereum/go-ethereum/common" @@ -196,73 +195,6 @@ func TestExploratoryGenerateCert(t *testing.T) { require.NoError(t, encoder.Encode(certificate)) } -func TestSendCertificate_NoClaims(t *testing.T) { - privateKey, err := crypto.GenerateKey() - require.NoError(t, err) - - ctx := context.Background() - mockStorage := mocks.NewAggSenderStorage(t) - mockL2BridgeQuerier := mocks.NewBridgeQuerier(t) - mockL1Querier := mocks.NewL1InfoTreeDataQuerier(t) - mockAggLayerClient := agglayer.NewAgglayerClientMock(t) - mockEpochNotifier := mocks.NewEpochNotifier(t) - mockLERQuerier := mocks.NewLERQuerier(t) - logger := log.WithFields("aggsender-test", "no claims test") - signer := signer.NewLocalSignFromPrivateKey("ut", log.WithFields("aggsender", 1), privateKey, 0) - aggSender := &AggSender{ - log: logger, - storage: mockStorage, - l2OriginNetwork: 1, - aggLayerClient: mockAggLayerClient, - epochNotifier: mockEpochNotifier, - cfg: config.Config{}, - flow: flows.NewPPFlow(logger, - flows.NewBaseFlow(logger, mockL2BridgeQuerier, mockStorage, - mockL1Querier, mockLERQuerier, flows.NewBaseFlowConfigDefault()), - mockStorage, mockL1Querier, mockL2BridgeQuerier, signer, true, 0), - rateLimiter: aggkitcommon.NewRateLimit(aggkitcommon.RateLimitConfig{}), - } - - mockStorage.EXPECT().GetLastSentCertificateHeader().Return(&aggsendertypes.CertificateHeader{ - NewLocalExitRoot: common.HexToHash("0x123"), - Height: 1, - FromBlock: 0, - ToBlock: 10, - Status: agglayertypes.Settled, - }, nil).Once() - mockStorage.EXPECT().SaveLastSentCertificate(mock.Anything, mock.Anything).Return(nil).Once() - mockL2BridgeQuerier.EXPECT().GetLastProcessedBlock(mock.Anything).Return(uint64(50), nil) - mockL2BridgeQuerier.EXPECT().GetBridgesAndClaims(mock.Anything, uint64(11), uint64(50)).Return([]bridgesync.Bridge{ - { - BlockNum: 30, - BlockPos: 0, - LeafType: agglayertypes.LeafTypeAsset.Uint8(), - OriginNetwork: 1, - OriginAddress: common.HexToAddress("0x1"), - DestinationNetwork: 2, - DestinationAddress: common.HexToAddress("0x2"), - Amount: big.NewInt(100), - Metadata: []byte("metadata"), - DepositCount: 1, - }, - }, []bridgesync.Claim{}, nil).Once() - mockL1Querier.EXPECT().GetLatestFinalizedL1InfoRoot(ctx).Return(&treetypes.Root{}, nil, nil).Once() - mockL2BridgeQuerier.EXPECT().GetExitRootByIndex(mock.Anything, uint32(1)).Return(common.Hash{}, nil).Once() - mockL2BridgeQuerier.EXPECT().OriginNetwork().Return(uint32(1)).Once() - mockAggLayerClient.EXPECT().SendCertificate(mock.Anything, mock.Anything, mock.Anything).Return(common.Hash{}, nil).Once() - mockEpochNotifier.EXPECT().GetEpochStatus().Return(aggsendertypes.EpochStatus{}) - signedCertificate, err := aggSender.sendCertificate(ctx) - require.NoError(t, err) - require.NotNil(t, signedCertificate) - require.NotNil(t, signedCertificate.AggchainData) - require.NotNil(t, signedCertificate.ImportedBridgeExits) - require.Len(t, signedCertificate.BridgeExits, 1) - - mockStorage.AssertExpectations(t) - mockL2BridgeQuerier.AssertExpectations(t) - mockAggLayerClient.AssertExpectations(t) -} - func TestExtractFromCertificateMetadataToBlock(t *testing.T) { t.Parallel() @@ -747,8 +679,9 @@ func newAggsenderTestData(t *testing.T, creationFlags testDataFlags) *aggsenderT l2BridgeQuerier := mocks.NewBridgeQuerier(t) agglayerClientMock := agglayer.NewAgglayerClientMock(t) l1InfoTreeQuerierMock := mocks.NewL1InfoTreeDataQuerier(t) - lerQuerier := mocks.NewLERQuerier(t) epochNotifierMock := mocks.NewEpochNotifier(t) + paramsBuilderMock := mocks.NewCommonCertParamsBuilder(t) + paramsVerifierMock := mocks.NewCommonCertParamsVerifier(t) logger := log.WithFields("aggsender-test", "checkLastCertificateFromAgglayer") var storageMock *mocks.AggSenderStorage var storage db.AggSenderStorage @@ -783,8 +716,7 @@ func newAggsenderTestData(t *testing.T, creationFlags testDataFlags) *aggsenderT rateLimiter: aggkitcommon.NewRateLimit(aggkitcommon.RateLimitConfig{}), epochNotifier: epochNotifierMock, flow: flows.NewPPFlow(logger, - flows.NewBaseFlow(logger, l2BridgeQuerier, storage, - l1InfoTreeQuerierMock, lerQuerier, flows.NewBaseFlowConfigDefault()), + paramsBuilderMock, paramsVerifierMock, storage, l1InfoTreeQuerierMock, l2BridgeQuerier, signer, true, 0), } var flowMock *mocks.AggsenderFlow diff --git a/aggsender/certificatebuild/common_params_builder.go b/aggsender/certificatebuild/common_params_builder.go new file mode 100644 index 000000000..5e69eaed0 --- /dev/null +++ b/aggsender/certificatebuild/common_params_builder.go @@ -0,0 +1,444 @@ +package certificatebuild + +import ( + "context" + "errors" + "fmt" + "time" + + agglayertypes "github.com/agglayer/aggkit/agglayer/types" + "github.com/agglayer/aggkit/aggsender/converters" + "github.com/agglayer/aggkit/aggsender/db" + "github.com/agglayer/aggkit/aggsender/types" + aggkitcommon "github.com/agglayer/aggkit/common" + "github.com/ethereum/go-ethereum/common" +) + +var ( + errNoBridgesAndClaims = errors.New("no bridges and claims to build certificate") + ErrNoNewBlocks = errors.New("no new blocks to send a certificate") + + EmptyLER = common.HexToHash("0x27ae5ba08d7291c96c8cbddcc148bf48a6d68c7974b94356f53754ef6171d757") +) + +// timeNowUTC returns the current time in UTC as a uint32 timestamp. +func timeNowUTC() uint32 { + // Use a more precise time function to avoid collisions in tests + // and ensure that the time is always in UTC. + return uint32(time.Now().UTC().Unix()) +} + +// CommonBuildConfig is a struct that holds the configuration for the certificate building process +type CommonBuildConfig struct { + // MaxCertSize is the maximum size of the certificate in bytes. 0 means no limit + MaxCertSize uint + // StartL2Block is the L2 block number from which to start sending certificates. + // It is used to determine the first block to include in the certificate. + // It can be 0 + StartL2Block uint64 + // RequireNoFEPBlockGap indicates whether the certificate building process requires no gap between the + // first FEP block and last settled certificate. + RequireNoFEPBlockGap bool +} + +// NewCommonBuildConfigDefault returns a CommonBuildConfig with default values +func NewCommonBuildConfigDefault() CommonBuildConfig { + return CommonBuildConfig{ + MaxCertSize: 0, // 0 means no limit + StartL2Block: 0, // 0 means start from the first block + RequireNoFEPBlockGap: false, + } +} + +// NewCommonBuildConfig returns a CommonBuildConfig with the specified maxCertSize, startL2Block, +// and requireNoFEPBlockGap values +func NewCommonBuildConfig( + maxCertSize uint, + startL2Block uint64, + requireNoFEPBlockGap bool) CommonBuildConfig { + return CommonBuildConfig{ + MaxCertSize: maxCertSize, + StartL2Block: startL2Block, + RequireNoFEPBlockGap: requireNoFEPBlockGap, + } +} + +var _ types.CommonCertParamsBuilder = (*commonParamsBuilder)(nil) + +// commonParamsBuilder is responsible for constructing common certificate-related data and operations. +// It manages dependencies such as logging, storage, bridge querying, and data conversion +// for bridge exits and imported bridge exits. The builder is configured via +// CommonParamsBuilderConfig to customize its behavior. +type commonParamsBuilder struct { + log types.Logger + storage db.AggSenderStorage + l2BridgeQuerier types.BridgeQuerier + l1InfoTreeQuerier types.L1InfoTreeDataQuerier + lerQuerier types.LERQuerier + + cfg CommonBuildConfig + // TimeNowFunc is a function that returns the current time as a uint32 timestamp. + timeNowFunc func() uint32 +} + +// NewCommonParamsBuilder creates a new instance of CommonParamsBuilder. +func NewCommonParamsBuilder( + log types.Logger, + storage db.AggSenderStorage, + l1InfoTreeDataQuerier types.L1InfoTreeDataQuerier, + l2BridgeQuerier types.BridgeQuerier, + lerQuerier types.LERQuerier, + cfg CommonBuildConfig, +) types.CommonCertParamsBuilder { + return &commonParamsBuilder{ + log: log, + cfg: cfg, + storage: storage, + lerQuerier: lerQuerier, + timeNowFunc: timeNowUTC, + l2BridgeQuerier: l2BridgeQuerier, + l1InfoTreeQuerier: l1InfoTreeDataQuerier, + } +} + +// GeneratePreBuildParams prepares the parameters required before building a certificate. +// It retrieves the last sent certificate header, determines the next block range for the certificate, +// fetches the latest finalized L1 info root, and assembles all necessary data into a CertificatePreBuildParams struct. +// Returns the constructed CertificatePreBuildParams and an error if any step fails. +// +// Parameters: +// - ctx: Context for controlling cancellation and deadlines. +// - certType: The type of certificate to be built. +// +// Returns: +// - *types.CertificatePreBuildParams: The parameters needed for certificate pre-building. +// - error: Non-nil if any retrieval or computation fails. +func (c *commonParamsBuilder) GeneratePreBuildParams(ctx context.Context, + certType types.CertificateType) (*types.CertificatePreBuildParams, error) { + lastSentCertificate, err := c.storage.GetLastSentCertificateHeader() + if err != nil { + return nil, fmt.Errorf("error getting last sent certificate: %w", err) + } + + nextBlocks, retryCount, err := c.nextCertificateBlockRange(ctx, lastSentCertificate) + if err != nil { + return nil, fmt.Errorf("error getting next certificate block range: %w", err) + } + l1InfoRoot, _, err := c.l1InfoTreeQuerier.GetLatestFinalizedL1InfoRoot(ctx) + if err != nil { + return nil, fmt.Errorf("error getting latest finalized L1 info root: %w", err) + } + + return &types.CertificatePreBuildParams{ + BlockRange: nextBlocks, + RetryCount: retryCount, + LastSentCertificate: lastSentCertificate, + CertificateType: certType, + L1InfoTreeToProve: &types.CertificateL1InfoTreeData{ + L1InfoTreeRootToProve: l1InfoRoot.Hash, + L1InfoTreeLeafCount: l1InfoRoot.Index + 1, + }, + CreatedAt: c.timeNowFunc(), + }, nil +} + +// GenerateBuildParams constructs a CertificateBuildParams object based on the provided +// CertificatePreBuildParams. It validates the input parameters, retrieves bridge and claim +// data for the specified block range using the l2BridgeQuerier, and populates the build +// parameters accordingly. Returns an error if required parameters are missing or if data +// retrieval fails. +// +// Parameters: +// - ctx: context.Context for controlling cancellation and deadlines. +// - preParams: types.CertificatePreBuildParams containing the necessary input data. +// +// Returns: +// - *types.CertificateBuildParams: The populated build parameters for certificate creation. +// - error: An error if input validation fails or bridge/claim retrieval encounters an issue. +func (c *commonParamsBuilder) GenerateBuildParams(ctx context.Context, + preParams types.CertificatePreBuildParams) (*types.CertificateBuildParams, error) { + if preParams.L1InfoTreeToProve == nil { + return nil, fmt.Errorf("L1InfoTreeWhichToProve should be not nil for GenerateBuildParams") + } + + bridges, claims, err := c.l2BridgeQuerier.GetBridgesAndClaims(ctx, + preParams.BlockRange.FromBlock, preParams.BlockRange.ToBlock) + if err != nil { + return nil, fmt.Errorf("generateBulidParams fails getting bridges and claims. Err: %w", err) + } + + buildParams := &types.CertificateBuildParams{ + FromBlock: preParams.BlockRange.FromBlock, + ToBlock: preParams.BlockRange.ToBlock, + RetryCount: preParams.RetryCount, + LastSentCertificate: preParams.LastSentCertificate, + Bridges: bridges, + Claims: claims, + CreatedAt: preParams.CreatedAt, + CertificateType: preParams.CertificateType, + L1InfoTreeRootFromWhichToProve: preParams.L1InfoTreeToProve.L1InfoTreeRootToProve, + L1InfoTreeLeafCount: preParams.L1InfoTreeToProve.L1InfoTreeLeafCount, + } + + return buildParams, nil +} + +// GetCommonCertificateBuildParams returns the common parameters to build a certificate +func (c *commonParamsBuilder) GetCommonCertificateBuildParams( + ctx context.Context, certType types.CertificateType) (*types.CertificateBuildParams, error) { + preParams, err := c.GeneratePreBuildParams(ctx, certType) + if err != nil { + return nil, fmt.Errorf("error generating pre build params: %w", err) + } + + params, err := c.GenerateBuildParams(ctx, *preParams) + if err != nil { + return nil, fmt.Errorf("error generating build params: %w", err) + } + + params, err = c.LimitCertSize(params) + if err != nil { + return nil, fmt.Errorf("error applying limit size: %w", err) + } + + return params, nil +} + +// LimitCertSize limits certificate size based on the max size configuration parameter +// size is expressed in bytes +func (c *commonParamsBuilder) LimitCertSize( + certParams *types.CertificateBuildParams) (*types.CertificateBuildParams, error) { + currentCert := certParams + var err error + maxCertSize := c.cfg.MaxCertSize + for { + if maxCertSize == 0 || currentCert.EstimatedSize() <= maxCertSize { + return currentCert, nil + } + + if currentCert.NumberOfBlocks() <= 1 { + c.log.Warnf("Minimum number of blocks reached [%d to %d]. Estimated size: %d > max size: %d", + currentCert.FromBlock, currentCert.ToBlock, currentCert.EstimatedSize(), maxCertSize) + return currentCert, nil + } + + currentCert, err = currentCert.Range(currentCert.FromBlock, currentCert.ToBlock-1) + if err != nil { + return nil, fmt.Errorf("error reducing certificate: %w", err) + } + } +} + +// BuildCertificate constructs a new agglayertypes.Certificate based on the provided CertificateBuildParams, +// the last sent certificate, and a flag indicating whether empty certificates are allowed. +// It performs the following steps: +// - Logs the certificate build attempt with relevant parameters. +// - Validates that the certificate is not empty if allowEmptyCert is false. +// - Converts bridge and claim data into bridge exits and imported bridge exits. +// - Determines the next certificate height and previous local exit root (LER). +// - Computes the new local exit root based on the provided parameters. +// - Assembles certificate metadata and returns the constructed certificate. +// +// Parameters: +// - ctx: Context for controlling cancellation and deadlines. +// - certParams: Parameters required to build the certificate. +// - lastSentCertificate: The most recently sent certificate, used to determine the next height and previous LER. +// - allowEmptyCert: If false, prevents building a certificate with no bridges or claims. +// +// Returns: +// - Pointer to the constructed agglayertypes.Certificate on success. +// - An error if any step in the process fails. +func (c *commonParamsBuilder) BuildCertificate(ctx context.Context, + certParams *types.CertificateBuildParams, + lastSentCertificate *types.CertificateHeader, + allowEmptyCert bool) (*agglayertypes.Certificate, error) { + c.log.Infof("building certificate for %s estimatedSize=%d", certParams.String(), certParams.EstimatedSize()) + + if !allowEmptyCert && certParams.IsEmpty() { + return nil, errNoBridgesAndClaims + } + + bridgeExits := converters.ConvertToBridgeExits(certParams.Bridges) + importedBridgeExits, err := converters.ConvertToImportedBridgeExits( + ctx, certParams.Claims, certParams.L1InfoTreeRootFromWhichToProve, c.l1InfoTreeQuerier) + if err != nil { + return nil, fmt.Errorf("error getting imported bridge exits: %w", err) + } + + height, previousLER, err := c.getNextHeightAndPreviousLER(lastSentCertificate) + if err != nil { + return nil, fmt.Errorf("error getting next height and previous LER: %w", err) + } + + newLER, err := c.getNewLocalExitRootForParams(ctx, certParams, previousLER) + if err != nil { + return nil, fmt.Errorf("error getting new local exit root: %w", err) + } + + meta := types.NewCertificateMetadata( + certParams.FromBlock, + uint32(certParams.ToBlock-certParams.FromBlock), + certParams.CreatedAt, + certParams.CertificateType.ToInt(), + ) + + return &agglayertypes.Certificate{ + NetworkID: c.l2BridgeQuerier.OriginNetwork(), + PrevLocalExitRoot: previousLER, + NewLocalExitRoot: newLER, + BridgeExits: bridgeExits, + ImportedBridgeExits: importedBridgeExits, + Height: height, + Metadata: meta.ToHash(), + L1InfoTreeLeafCount: certParams.L1InfoTreeLeafCount, + }, nil +} + +// nextCertificateBlockRange returns the block range and retryCount for the next certificate +func (c *commonParamsBuilder) nextCertificateBlockRange(ctx context.Context, + lastSentCertificate *types.CertificateHeader) (types.BlockRange, int, error) { + lastL2BlockSynced, err := c.l2BridgeQuerier.GetLastProcessedBlock(ctx) + if err != nil { + return types.BlockRangeZero, 0, fmt.Errorf("error getting last processed block from l2: %w", err) + } + + previousToBlock, retryCount := c.getLastSentBlockAndRetryCount(lastSentCertificate) + if previousToBlock >= lastL2BlockSynced { + c.log.Warnf("no new blocks to send a certificate, last certificate block: %d, last L2 block: %d", + previousToBlock, lastL2BlockSynced) + return types.BlockRangeZero, 0, ErrNoNewBlocks + } + + fromBlock := previousToBlock + 1 + toBlock := lastL2BlockSynced + + return types.NewBlockRange(fromBlock, toBlock), retryCount, nil +} + +// getLastSentBlockAndRetryCount returns the last sent block of the last sent certificate +// if there is no previously sent certificate, it returns startL2Block and 0 +func (c *commonParamsBuilder) getLastSentBlockAndRetryCount( + lastSentCertificateInfo *types.CertificateHeader) (uint64, int) { + if lastSentCertificateInfo == nil { + // this is the first certificate so we start from what we have set in start L2 block + return c.cfg.StartL2Block, 0 + } + + retryCount := 0 + lastSentBlock := lastSentCertificateInfo.ToBlock + + if lastSentCertificateInfo.Status == agglayertypes.InError { + // if the last certificate was in error, we need to resend it + // from the block before the error + if lastSentCertificateInfo.FromBlock > 0 { + lastSentBlock = lastSentCertificateInfo.FromBlock - 1 + } + + retryCount = lastSentCertificateInfo.RetryCount + 1 + } + return lastSentBlock, retryCount +} + +// GetNewLocalExitRoot gets the new local exit root for the new certificate +func (c *commonParamsBuilder) GetNewLocalExitRoot(ctx context.Context, + certParams *types.CertificateBuildParams) (common.Hash, error) { + if certParams == nil { + return common.Hash{}, + fmt.Errorf("commonParamsBuilder.GetNewLocalExitRoot. certificate build parameters cannot be nil") + } + + _, previousLER, err := c.getNextHeightAndPreviousLER(certParams.LastSentCertificate) + if err != nil { + return common.Hash{}, + fmt.Errorf("commonParamsBuilder.GetNewLocalExitRoot. error getting next height and previous LER: %w", err) + } + + newLER, err := c.getNewLocalExitRootForParams(ctx, certParams, previousLER) + if err != nil { + return common.Hash{}, + fmt.Errorf("commonParamsBuilder.GetNewLocalExitRoot. error getting new local exit root: %w", err) + } + + return newLER, nil +} + +// getNewLocalExitRootForParams gets the new local exit root for the new certificate build params +func (c *commonParamsBuilder) getNewLocalExitRootForParams( + ctx context.Context, + certParams *types.CertificateBuildParams, + previousLER common.Hash) (common.Hash, error) { + if certParams.NumberOfBridges() == 0 { + // if there is no bridge exits we return the previous LER + // since there was no change in the local exit root + return previousLER, nil + } + + depositCount := certParams.MaxDepositCount() + + exitRoot, err := c.l2BridgeQuerier.GetExitRootByIndex(ctx, depositCount) + if err != nil { + return common.Hash{}, fmt.Errorf("error getting exit root by index: %d. Error: %w", depositCount, err) + } + + return exitRoot, nil +} + +// getNextHeightAndPreviousLER returns the height and previous LER for the new certificate +func (c *commonParamsBuilder) getNextHeightAndPreviousLER( + lastSentCertificateInfo *types.CertificateHeader) (uint64, common.Hash, error) { + if lastSentCertificateInfo == nil { + ler, err := c.getStartLER() + return uint64(0), ler, err + } + if !lastSentCertificateInfo.Status.IsClosed() { + return 0, aggkitcommon.ZeroHash, fmt.Errorf("last certificate %s is not closed (status: %s)", + lastSentCertificateInfo.ID(), lastSentCertificateInfo.Status.String()) + } + if lastSentCertificateInfo.Status.IsSettled() { + return lastSentCertificateInfo.Height + 1, lastSentCertificateInfo.NewLocalExitRoot, nil + } + + if lastSentCertificateInfo.Status.IsInError() { + // We can reuse last one of lastCert? + if lastSentCertificateInfo.PreviousLocalExitRoot != nil { + return lastSentCertificateInfo.Height, *lastSentCertificateInfo.PreviousLocalExitRoot, nil + } + // Is the first one, so we can set the zeroLER + if lastSentCertificateInfo.Height == 0 { + ler, err := c.getStartLER() + return uint64(0), ler, err + } + // We get previous certificate that must be settled + c.log.Debugf("last certificate %s is in error, getting previous settled certificate height:%d", + lastSentCertificateInfo.Height-1) + lastSettleCert, err := c.storage.GetCertificateHeaderByHeight(lastSentCertificateInfo.Height - 1) + if err != nil { + return 0, aggkitcommon.ZeroHash, fmt.Errorf("error getting last settled certificate: %w", err) + } + if lastSettleCert == nil { + return 0, aggkitcommon.ZeroHash, fmt.Errorf("none settled certificate: %w", err) + } + if !lastSettleCert.Status.IsSettled() { + return 0, aggkitcommon.ZeroHash, fmt.Errorf("last settled certificate %s is not settled (status: %s)", + lastSettleCert.ID(), lastSettleCert.Status.String()) + } + + return lastSentCertificateInfo.Height, lastSettleCert.NewLocalExitRoot, nil + } + return 0, aggkitcommon.ZeroHash, fmt.Errorf("last certificate %s has an unknown status: %s", + lastSentCertificateInfo.ID(), lastSentCertificateInfo.Status.String()) +} + +// getStartLER returns the last local exit root (LER) based on the configuration +func (c *commonParamsBuilder) getStartLER() (common.Hash, error) { + ler, err := c.lerQuerier.GetLastLocalExitRoot() + if err != nil { + return common.Hash{}, fmt.Errorf("error getting last local exit root: %w", err) + } + + if ler == aggkitcommon.ZeroHash { + return EmptyLER, nil + } + + return ler, nil +} diff --git a/aggsender/flows/flow_base_test.go b/aggsender/certificatebuild/common_params_builder_test.go similarity index 55% rename from aggsender/flows/flow_base_test.go rename to aggsender/certificatebuild/common_params_builder_test.go index 48e9d9c0d..78cf277cb 100644 --- a/aggsender/flows/flow_base_test.go +++ b/aggsender/certificatebuild/common_params_builder_test.go @@ -1,4 +1,4 @@ -package flows +package certificatebuild import ( "context" @@ -12,12 +12,245 @@ import ( aggkitcommon "github.com/agglayer/aggkit/common" "github.com/agglayer/aggkit/db" "github.com/agglayer/aggkit/log" + treetypes "github.com/agglayer/aggkit/tree/types" "github.com/ethereum/go-ethereum/common" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" ) -func Test_baseFlow_limitCertSize(t *testing.T) { +func timeNowUTCForTest() uint32 { + return uint32(12345) +} + +func Test_GeneratePreBuildParams(t *testing.T) { + t.Parallel() + + testCases := []struct { + name string + certType types.CertificateType + mockFn func(*mocks.BridgeQuerier, *mocks.AggSenderStorage, *mocks.L1InfoTreeDataQuerier) + expectedParams *types.CertificatePreBuildParams + expectedError string + }{ + { + name: "storage returns an error", + certType: types.CertificateTypePP, + mockFn: func(mockL2BridgeQuerier *mocks.BridgeQuerier, + mockStorage *mocks.AggSenderStorage, + mockL1InfoTreeDataQuerier *mocks.L1InfoTreeDataQuerier) { + mockStorage.EXPECT().GetLastSentCertificateHeader().Return(nil, errors.New("storage error")) + }, + expectedError: "error getting last sent certificate: storage error", + }, + { + name: "get last processed block error", + certType: types.CertificateTypePP, + mockFn: func(mockL2BridgeQuerier *mocks.BridgeQuerier, + mockStorage *mocks.AggSenderStorage, + mockL1InfoTreeDataQuerier *mocks.L1InfoTreeDataQuerier) { + mockStorage.EXPECT().GetLastSentCertificateHeader().Return(nil, nil) + mockL2BridgeQuerier.EXPECT().GetLastProcessedBlock(t.Context()).Return(uint64(0), errors.New("querier error")) + }, + expectedError: "error getting last processed block from l2: querier error", + }, + { + name: "get latest finalized block error", + certType: types.CertificateTypePP, + mockFn: func(mockL2BridgeQuerier *mocks.BridgeQuerier, + mockStorage *mocks.AggSenderStorage, + mockL1InfoTreeDataQuerier *mocks.L1InfoTreeDataQuerier) { + mockStorage.EXPECT().GetLastSentCertificateHeader().Return(nil, nil) + mockL2BridgeQuerier.EXPECT().GetLastProcessedBlock(t.Context()).Return(uint64(100), nil) + mockL1InfoTreeDataQuerier.EXPECT().GetLatestFinalizedL1InfoRoot(t.Context()).Return(nil, nil, errors.New("querier error")) + }, + expectedError: "error getting latest finalized L1 info root: querier error", + }, + { + name: "success", + certType: types.CertificateTypePP, + mockFn: func(mockL2BridgeQuerier *mocks.BridgeQuerier, + mockStorage *mocks.AggSenderStorage, + mockL1InfoTreeDataQuerier *mocks.L1InfoTreeDataQuerier) { + mockStorage.EXPECT().GetLastSentCertificateHeader().Return(nil, nil) + mockL2BridgeQuerier.EXPECT().GetLastProcessedBlock(t.Context()).Return(uint64(100), nil) + mockL1InfoTreeDataQuerier.EXPECT().GetLatestFinalizedL1InfoRoot(t.Context()).Return(&treetypes.Root{ + Hash: common.HexToHash("0x1"), + Index: 1, + BlockNum: 55, + }, nil, nil) + }, + expectedParams: &types.CertificatePreBuildParams{ + CertificateType: types.CertificateTypePP, + BlockRange: types.NewBlockRange(1, 100), + RetryCount: 0, + LastSentCertificate: nil, + L1InfoTreeToProve: &types.CertificateL1InfoTreeData{ + L1InfoTreeRootToProve: common.HexToHash("0x1"), + L1InfoTreeLeafCount: 2, + }, + CreatedAt: timeNowUTCForTest(), + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + mockL1InfoTreeQuerier := mocks.NewL1InfoTreeDataQuerier(t) + mockL2BridgeQuerier := mocks.NewBridgeQuerier(t) + mockStorage := mocks.NewAggSenderStorage(t) + + if tc.mockFn != nil { + tc.mockFn(mockL2BridgeQuerier, mockStorage, mockL1InfoTreeQuerier) + } + + builder := NewCommonParamsBuilder( + log.WithFields("test", tc.name), + mockStorage, + mockL1InfoTreeQuerier, + mockL2BridgeQuerier, + nil, // lerQuerier + NewCommonBuildConfig(0, 0, false), + ) + bl, ok := builder.(*commonParamsBuilder) + require.True(t, ok, "builder should be of type *commonParamsBuilder") + bl.timeNowFunc = timeNowUTCForTest + + result, err := builder.GeneratePreBuildParams(t.Context(), tc.certType) + + if tc.expectedError != "" { + require.ErrorContains(t, err, tc.expectedError) + } else { + require.NoError(t, err) + require.Equal(t, tc.expectedParams, result) + } + + mockL1InfoTreeQuerier.AssertExpectations(t) + mockL2BridgeQuerier.AssertExpectations(t) + mockStorage.AssertExpectations(t) + }) + } +} + +func Test_GenerateBuildParams(t *testing.T) { + t.Parallel() + + testCases := []struct { + name string + preParams types.CertificatePreBuildParams + mockFn func(*mocks.BridgeQuerier) + expectedParams *types.CertificateBuildParams + expectedError string + }{ + { + name: "L1InfoTreeToProve is nil", + preParams: types.CertificatePreBuildParams{ + BlockRange: types.NewBlockRange(1, 10), + RetryCount: 2, + LastSentCertificate: &types.CertificateHeader{Height: 5}, + CertificateType: types.CertificateTypePP, + L1InfoTreeToProve: nil, + CreatedAt: timeNowUTCForTest(), + }, + expectedError: "L1InfoTreeWhichToProve should be not nil for GenerateBuildParams", + }, + { + name: "GetBridgesAndClaims returns error", + preParams: types.CertificatePreBuildParams{ + BlockRange: types.NewBlockRange(1, 10), + RetryCount: 2, + LastSentCertificate: &types.CertificateHeader{Height: 5}, + CertificateType: types.CertificateTypePP, + L1InfoTreeToProve: &types.CertificateL1InfoTreeData{ + L1InfoTreeRootToProve: common.HexToHash("0xabc"), + L1InfoTreeLeafCount: 42, + }, + CreatedAt: timeNowUTCForTest(), + }, + mockFn: func(mockL2BridgeQuerier *mocks.BridgeQuerier) { + mockL2BridgeQuerier.EXPECT(). + GetBridgesAndClaims(t.Context(), uint64(1), uint64(10)). + Return(nil, nil, errors.New("bridge error")) + }, + expectedError: "generateBulidParams fails getting bridges and claims. Err: bridge error", + }, + { + name: "Success", + preParams: types.CertificatePreBuildParams{ + BlockRange: types.NewBlockRange(1, 10), + RetryCount: 2, + LastSentCertificate: &types.CertificateHeader{Height: 5}, + CertificateType: types.CertificateTypePP, + L1InfoTreeToProve: &types.CertificateL1InfoTreeData{ + L1InfoTreeRootToProve: common.HexToHash("0xabc"), + L1InfoTreeLeafCount: 42, + }, + CreatedAt: timeNowUTCForTest(), + }, + mockFn: func(mockL2BridgeQuerier *mocks.BridgeQuerier) { + mockL2BridgeQuerier.EXPECT(). + GetBridgesAndClaims(t.Context(), uint64(1), uint64(10)). + Return( + []bridgesync.Bridge{{BlockNum: 2}, {BlockNum: 3}}, + []bridgesync.Claim{{BlockNum: 4}}, + nil, + ) + }, + expectedParams: &types.CertificateBuildParams{ + FromBlock: 1, + ToBlock: 10, + RetryCount: 2, + LastSentCertificate: &types.CertificateHeader{Height: 5}, + Bridges: []bridgesync.Bridge{{BlockNum: 2}, {BlockNum: 3}}, + Claims: []bridgesync.Claim{{BlockNum: 4}}, + CreatedAt: timeNowUTCForTest(), + CertificateType: types.CertificateTypePP, + L1InfoTreeRootFromWhichToProve: common.HexToHash("0xabc"), + L1InfoTreeLeafCount: 42, + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + mockL2BridgeQuerier := mocks.NewBridgeQuerier(t) + + if tc.mockFn != nil { + tc.mockFn(mockL2BridgeQuerier) + } + + builder := NewCommonParamsBuilder( + log.WithFields("test", tc.name), + nil, // storage + nil, // l1InfoTreeDataQuerier + mockL2BridgeQuerier, + nil, // lerQuerier + NewCommonBuildConfig(0, 0, false), + ) + bl, ok := builder.(*commonParamsBuilder) + require.True(t, ok, "builder should be of type *commonParamsBuilder") + bl.timeNowFunc = timeNowUTCForTest + + result, err := builder.GenerateBuildParams(t.Context(), tc.preParams) + + if tc.expectedError != "" { + require.ErrorContains(t, err, tc.expectedError) + } else { + require.NoError(t, err) + require.Equal(t, tc.expectedParams, result) + } + + mockL2BridgeQuerier.AssertExpectations(t) + }) + } +} + +func Test_LimitCertSize(t *testing.T) { + t.Parallel() + tests := []struct { name string maxCertSize uint @@ -117,15 +350,18 @@ func Test_baseFlow_limitCertSize(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - f := NewBaseFlow( - log.WithFields("test", t.Name()), - nil, - nil, - nil, - nil, - NewBaseFlowConfig(tt.maxCertSize, 0, false)) + t.Parallel() + + builder := NewCommonParamsBuilder( + log.WithFields("test", tt.name), + nil, // storage + nil, // l1InfoTreeDataQuerier + nil, // l2BridgeQuerier + nil, // lerQuerier + NewCommonBuildConfig(tt.maxCertSize, 0, false), + ) - result, err := f.LimitCertSize(tt.fullCert) + result, err := builder.LimitCertSize(tt.fullCert) if tt.expectedError != "" { require.Error(t, err) @@ -138,66 +374,59 @@ func Test_baseFlow_limitCertSize(t *testing.T) { } } -func Test_baseFlow_getNewLocalExitRoot(t *testing.T) { +func Test_GetNewLocalExitRoot(t *testing.T) { t.Parallel() tests := []struct { - name string - certParams *types.CertificateBuildParams - mockFn func(mockL2BridgeQuerier *mocks.BridgeQuerier) - previousLER common.Hash - expectedLER common.Hash - expectedError string - numberOfBridges int + name string + certParams *types.CertificateBuildParams + mockFn func(mockL2BridgeQuerier *mocks.BridgeQuerier, mockStorage *mocks.AggSenderStorage) + expectedLER common.Hash + expectedError string + getNextHeightErr error + getNewLERMockErr error }{ { - name: "no bridges, return previous LER", - certParams: &types.CertificateBuildParams{ - Bridges: []bridgesync.Bridge{}, - }, - previousLER: common.HexToHash("0x123"), - expectedLER: common.HexToHash("0x123"), + name: "certificate parameters are nil", + certParams: nil, + expectedLER: common.Hash{}, + expectedError: "certificate build parameters cannot be nil", }, { - name: "exit root found, return new exit root", + name: "error getting next height and previous LER", certParams: &types.CertificateBuildParams{ - Bridges: []bridgesync.Bridge{{}, {}}, - ToBlock: 10, - }, - previousLER: common.HexToHash("0x123"), - expectedLER: common.HexToHash("0x456"), - mockFn: func(mockL2BridgeQuerier *mocks.BridgeQuerier) { - mockL2BridgeQuerier.EXPECT().GetExitRootByIndex(mock.Anything, mock.Anything). - Return(common.HexToHash("0x456"), nil) + LastSentCertificate: &types.CertificateHeader{ + Status: agglayertypes.Pending, + }, }, + getNextHeightErr: errors.New("mock error"), + expectedLER: common.Hash{}, + expectedError: "error getting next height and previous LER", }, { - name: "exit root not found, return previous LER", + name: "error getting new local exit root", certParams: &types.CertificateBuildParams{ + LastSentCertificate: &types.CertificateHeader{ + Status: agglayertypes.Settled, + }, Bridges: []bridgesync.Bridge{{}, {}}, - ToBlock: 10, }, - previousLER: common.HexToHash("0x123"), - expectedLER: common.HexToHash("0x123"), - expectedError: "not found", - mockFn: func(mockL2BridgeQuerier *mocks.BridgeQuerier) { + getNewLERMockErr: errors.New("mock error"), + expectedLER: common.Hash{}, + expectedError: "error getting new local exit root", + mockFn: func(mockL2BridgeQuerier *mocks.BridgeQuerier, mockStorage *mocks.AggSenderStorage) { mockL2BridgeQuerier.EXPECT().GetExitRootByIndex(mock.Anything, mock.Anything). - Return(common.Hash{}, db.ErrNotFound) + Return(common.Hash{}, errors.New("mock error")) }, }, { - name: "error fetching exit root, return error", + name: "successfully get new local exit root", certParams: &types.CertificateBuildParams{ - Bridges: []bridgesync.Bridge{{}, {}}, - ToBlock: 10, - }, - previousLER: common.HexToHash("0x123"), - expectedLER: common.Hash{}, - expectedError: "error getting exit root by index: 0. Error: unexpected error", - mockFn: func(mockL2BridgeQuerier *mocks.BridgeQuerier) { - mockL2BridgeQuerier.EXPECT().GetExitRootByIndex(mock.Anything, mock.Anything). - Return(common.Hash{}, errors.New("unexpected error")) + LastSentCertificate: &types.CertificateHeader{ + Status: agglayertypes.Settled, + }, }, + expectedLER: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000000"), }, } @@ -208,15 +437,19 @@ func Test_baseFlow_getNewLocalExitRoot(t *testing.T) { t.Parallel() mockL2BridgeQuerier := mocks.NewBridgeQuerier(t) + mockStorage := mocks.NewAggSenderStorage(t) + if tt.mockFn != nil { - tt.mockFn(mockL2BridgeQuerier) + tt.mockFn(mockL2BridgeQuerier, mockStorage) } - f := &baseFlow{ + builder := &commonParamsBuilder{ l2BridgeQuerier: mockL2BridgeQuerier, + storage: mockStorage, } - result, err := f.getNewLocalExitRoot(context.Background(), tt.certParams, tt.previousLER) + ctx := context.TODO() + result, err := builder.GetNewLocalExitRoot(ctx, tt.certParams) if tt.expectedError != "" { require.Error(t, err) @@ -228,59 +461,189 @@ func Test_baseFlow_getNewLocalExitRoot(t *testing.T) { }) } } -func Test_baseFlow_GetNewLocalExitRoot(t *testing.T) { + +func Test_GetCommonCertificateBuildParams(t *testing.T) { t.Parallel() + type mocksSetup struct { + storage *mocks.AggSenderStorage + l2BridgeQuerier *mocks.BridgeQuerier + l1InfoTreeQuerier *mocks.L1InfoTreeDataQuerier + lerQuerier *mocks.LERQuerier + } + + newMocks := func(t *testing.T) mocksSetup { + t.Helper() + + return mocksSetup{ + storage: mocks.NewAggSenderStorage(t), + l2BridgeQuerier: mocks.NewBridgeQuerier(t), + l1InfoTreeQuerier: mocks.NewL1InfoTreeDataQuerier(t), + lerQuerier: mocks.NewLERQuerier(t), + } + } + tests := []struct { - name string - certParams *types.CertificateBuildParams - mockFn func(mockL2BridgeQuerier *mocks.BridgeQuerier, mockStorage *mocks.AggSenderStorage) - expectedLER common.Hash - expectedError string - getNextHeightErr error - getNewLERMockErr error + name string + certType types.CertificateType + cfg CommonBuildConfig + setupMocks func(m mocksSetup) + expectedError string + expectedParams *types.CertificateBuildParams }{ { - name: "certificate parameters are nil", - certParams: nil, - expectedLER: common.Hash{}, - expectedError: "certificate build parameters cannot be nil", + name: "error from GeneratePreBuildParams propagates", + certType: types.CertificateTypePP, + setupMocks: func(m mocksSetup) { + m.storage.EXPECT().GetLastSentCertificateHeader().Return(nil, errors.New("storage error")) + }, + expectedError: "error generating pre build params: error getting last sent certificate: storage error", + }, + { + name: "error from GenerateBuildParams propagates", + certType: types.CertificateTypePP, + setupMocks: func(m mocksSetup) { + m.storage.EXPECT().GetLastSentCertificateHeader().Return(nil, nil) + m.l2BridgeQuerier.EXPECT().GetLastProcessedBlock(mock.Anything).Return(uint64(10), nil) + m.l1InfoTreeQuerier.EXPECT().GetLatestFinalizedL1InfoRoot(mock.Anything).Return(&treetypes.Root{ + Hash: common.HexToHash("0x1"), + Index: 1, + }, nil, nil) + m.l2BridgeQuerier.EXPECT().GetBridgesAndClaims(mock.Anything, uint64(1), uint64(10)). + Return(nil, nil, errors.New("bridge error")) + }, + expectedError: "error generating build params: generateBulidParams fails getting bridges and claims. Err: bridge error", + }, + { + name: "success returns build params", + certType: types.CertificateTypePP, + setupMocks: func(m mocksSetup) { + m.storage.EXPECT().GetLastSentCertificateHeader().Return(nil, nil) + m.l2BridgeQuerier.EXPECT().GetLastProcessedBlock(mock.Anything).Return(uint64(10), nil) + m.l1InfoTreeQuerier.EXPECT().GetLatestFinalizedL1InfoRoot(mock.Anything).Return(&treetypes.Root{ + Hash: common.HexToHash("0x1"), + Index: 1, + }, nil, nil) + m.l2BridgeQuerier.EXPECT().GetBridgesAndClaims(mock.Anything, uint64(1), uint64(10)). + Return([]bridgesync.Bridge{{BlockNum: 2}, {BlockNum: 3}}, []bridgesync.Claim{{BlockNum: 4}}, nil) + }, + expectedParams: &types.CertificateBuildParams{ + FromBlock: 1, + ToBlock: 10, + RetryCount: 0, + LastSentCertificate: nil, + Bridges: []bridgesync.Bridge{{BlockNum: 2}, {BlockNum: 3}}, + Claims: []bridgesync.Claim{{BlockNum: 4}}, + CreatedAt: timeNowUTCForTest(), + CertificateType: types.CertificateTypePP, + L1InfoTreeRootFromWhichToProve: common.HexToHash("0x1"), + L1InfoTreeLeafCount: 2, + }, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + m := newMocks(t) + if tt.setupMocks != nil { + tt.setupMocks(m) + } + cfg := tt.cfg + if cfg == (CommonBuildConfig{}) { + cfg = NewCommonBuildConfigDefault() + } + builder := NewCommonParamsBuilder( + log.WithFields("test", tt.name), + m.storage, + m.l1InfoTreeQuerier, + m.l2BridgeQuerier, + m.lerQuerier, + cfg, + ) + bl, ok := builder.(*commonParamsBuilder) + require.True(t, ok, "builder should be of type *commonParamsBuilder") + bl.timeNowFunc = timeNowUTCForTest + + result, err := builder.GetCommonCertificateBuildParams(context.Background(), tt.certType) + if tt.expectedError != "" { + require.Error(t, err) + require.Contains(t, err.Error(), tt.expectedError) + } else { + require.NoError(t, err) + require.Equal(t, tt.expectedParams, result) + } + m.storage.AssertExpectations(t) + m.l2BridgeQuerier.AssertExpectations(t) + m.l1InfoTreeQuerier.AssertExpectations(t) + if m.lerQuerier != nil { + m.lerQuerier.AssertExpectations(t) + } + }) + } +} + +func Test_getNewLocalExitRootForParams(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + certParams *types.CertificateBuildParams + mockFn func(mockL2BridgeQuerier *mocks.BridgeQuerier) + previousLER common.Hash + expectedLER common.Hash + expectedError string + numberOfBridges int + }{ + { + name: "no bridges, return previous LER", + certParams: &types.CertificateBuildParams{ + Bridges: []bridgesync.Bridge{}, + }, + previousLER: common.HexToHash("0x123"), + expectedLER: common.HexToHash("0x123"), }, { - name: "error getting next height and previous LER", + name: "exit root found, return new exit root", certParams: &types.CertificateBuildParams{ - LastSentCertificate: &types.CertificateHeader{ - Status: agglayertypes.Pending, - }, + Bridges: []bridgesync.Bridge{{}, {}}, + ToBlock: 10, + }, + previousLER: common.HexToHash("0x123"), + expectedLER: common.HexToHash("0x456"), + mockFn: func(mockL2BridgeQuerier *mocks.BridgeQuerier) { + mockL2BridgeQuerier.EXPECT().GetExitRootByIndex(mock.Anything, mock.Anything). + Return(common.HexToHash("0x456"), nil) }, - getNextHeightErr: errors.New("mock error"), - expectedLER: common.Hash{}, - expectedError: "error getting next height and previous LER", }, { - name: "error getting new local exit root", + name: "exit root not found, return previous LER", certParams: &types.CertificateBuildParams{ - LastSentCertificate: &types.CertificateHeader{ - Status: agglayertypes.Settled, - }, Bridges: []bridgesync.Bridge{{}, {}}, + ToBlock: 10, }, - getNewLERMockErr: errors.New("mock error"), - expectedLER: common.Hash{}, - expectedError: "error getting new local exit root", - mockFn: func(mockL2BridgeQuerier *mocks.BridgeQuerier, mockStorage *mocks.AggSenderStorage) { + previousLER: common.HexToHash("0x123"), + expectedLER: common.HexToHash("0x123"), + expectedError: "not found", + mockFn: func(mockL2BridgeQuerier *mocks.BridgeQuerier) { mockL2BridgeQuerier.EXPECT().GetExitRootByIndex(mock.Anything, mock.Anything). - Return(common.Hash{}, errors.New("mock error")) + Return(common.Hash{}, db.ErrNotFound) }, }, { - name: "successfully get new local exit root", + name: "error fetching exit root, return error", certParams: &types.CertificateBuildParams{ - LastSentCertificate: &types.CertificateHeader{ - Status: agglayertypes.Settled, - }, + Bridges: []bridgesync.Bridge{{}, {}}, + ToBlock: 10, + }, + previousLER: common.HexToHash("0x123"), + expectedLER: common.Hash{}, + expectedError: "error getting exit root by index: 0. Error: unexpected error", + mockFn: func(mockL2BridgeQuerier *mocks.BridgeQuerier) { + mockL2BridgeQuerier.EXPECT().GetExitRootByIndex(mock.Anything, mock.Anything). + Return(common.Hash{}, errors.New("unexpected error")) }, - expectedLER: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000000"), }, } @@ -291,18 +654,15 @@ func Test_baseFlow_GetNewLocalExitRoot(t *testing.T) { t.Parallel() mockL2BridgeQuerier := mocks.NewBridgeQuerier(t) - mockStorage := mocks.NewAggSenderStorage(t) - if tt.mockFn != nil { - tt.mockFn(mockL2BridgeQuerier, mockStorage) + tt.mockFn(mockL2BridgeQuerier) } - f := &baseFlow{ + builder := &commonParamsBuilder{ l2BridgeQuerier: mockL2BridgeQuerier, - storage: mockStorage, } - ctx := context.TODO() - result, err := f.GetNewLocalExitRoot(ctx, tt.certParams) + + result, err := builder.getNewLocalExitRootForParams(context.Background(), tt.certParams, tt.previousLER) if tt.expectedError != "" { require.Error(t, err) @@ -315,7 +675,7 @@ func Test_baseFlow_GetNewLocalExitRoot(t *testing.T) { } } -func Test_baseFlow_getNextHeightAndPreviousLER(t *testing.T) { +func Test_getNextHeightAndPreviousLER(t *testing.T) { t.Parallel() previousLER := common.HexToHash("0x123") @@ -332,7 +692,7 @@ func Test_baseFlow_getNextHeightAndPreviousLER(t *testing.T) { name: "no last sent certificate - zero start LER", lastSentCert: nil, expectedHeight: 0, - expectedLER: emptyLER, + expectedLER: EmptyLER, mockFn: func(mockLERQuerier *mocks.LERQuerier, mockStorage *mocks.AggSenderStorage) { mockLERQuerier.EXPECT().GetLastLocalExitRoot().Return(aggkitcommon.ZeroHash, nil) }, @@ -395,9 +755,9 @@ func Test_baseFlow_getNextHeightAndPreviousLER(t *testing.T) { NewLocalExitRoot: common.HexToHash("0x789"), }, expectedHeight: 0, - expectedLER: emptyLER, + expectedLER: EmptyLER, mockFn: func(mockLERQuerier *mocks.LERQuerier, mockStorage *mocks.AggSenderStorage) { - mockLERQuerier.EXPECT().GetLastLocalExitRoot().Return(emptyLER, nil) + mockLERQuerier.EXPECT().GetLastLocalExitRoot().Return(EmptyLER, nil) }, }, { @@ -477,13 +837,13 @@ func Test_baseFlow_getNextHeightAndPreviousLER(t *testing.T) { } log := log.WithFields("test", t.Name()) - f := &baseFlow{ + builder := &commonParamsBuilder{ lerQuerier: mockLERQuerier, storage: mockStorage, log: log, } - height, ler, err := f.getNextHeightAndPreviousLER(tc.lastSentCert) + height, ler, err := builder.getNextHeightAndPreviousLER(tc.lastSentCert) if tc.expectedError != "" { require.ErrorContains(t, err, tc.expectedError) } else { @@ -494,252 +854,3 @@ func Test_baseFlow_getNextHeightAndPreviousLER(t *testing.T) { }) } } - -func Test_baseFlow_VerifyBuildParams(t *testing.T) { - t.Parallel() - - ctx := context.Background() - - testCases := []struct { - name string - buildParams *types.CertificateBuildParams - mockFn func(*mocks.BridgeQuerier) - expectedError string - }{ - { - name: "invalid retry starting block", - buildParams: &types.CertificateBuildParams{ - FromBlock: 10, - ToBlock: 15, - RetryCount: 1, - LastSentCertificate: &types.CertificateHeader{ - Height: 1, - Status: agglayertypes.InError, - FromBlock: 5, - ToBlock: 10, - }, - }, - expectedError: "retry certificate fromBlock 10 != last sent certificate fromBlock 5", - }, - { - name: "invalid claim GER", - buildParams: &types.CertificateBuildParams{ - FromBlock: 1, - ToBlock: 10, - Claims: []bridgesync.Claim{ - {GlobalExitRoot: common.HexToHash("0x123"), MainnetExitRoot: common.HexToHash("0x456"), RollupExitRoot: common.HexToHash("0x789")}, - }, - }, - expectedError: "GER mismatch", - }, - { - name: "success", - buildParams: &types.CertificateBuildParams{ - FromBlock: 1, - ToBlock: 10, - }, - }, - } - - for _, tc := range testCases { - tc := tc - - t.Run(tc.name, func(t *testing.T) { - t.Parallel() - mockL2BridgeQuerier := mocks.NewBridgeQuerier(t) - if tc.mockFn != nil { - tc.mockFn(mockL2BridgeQuerier) - } - - log := log.WithFields("test", t.Name()) - f := &baseFlow{ - log: log, - l2BridgeQuerier: mockL2BridgeQuerier, - } - - err := f.VerifyBuildParams(ctx, tc.buildParams) - if tc.expectedError != "" { - require.ErrorContains(t, err, tc.expectedError) - } else { - require.NoError(t, err) - } - }) - } -} - -func Test_baseFlow_VerifyBlockRangeGaps(t *testing.T) { - t.Parallel() - - ctx := context.Background() - - type args struct { - lastSentCertificate *types.CertificateHeader - newFromBlock uint64 - newToBlock uint64 - } - tests := []struct { - name string - args args - requireNoFEPGap bool - mockFn func(mockL2BridgeQuerier *mocks.BridgeQuerier) - expectedError string - }{ - { - name: "lastSentCertificate is nil", - args: args{ - lastSentCertificate: nil, - newFromBlock: 10, - newToBlock: 20, - }, - }, - { - name: "no gap between certificates", - args: args{ - lastSentCertificate: &types.CertificateHeader{ - Status: agglayertypes.Settled, - FromBlock: 10, - ToBlock: 20, - }, - newFromBlock: 21, - newToBlock: 30, - }, - }, - { - name: "gap exists but no bridges or claims in gap", - args: args{ - lastSentCertificate: &types.CertificateHeader{ - Status: agglayertypes.Settled, - FromBlock: 10, - ToBlock: 15, - }, - newFromBlock: 17, - newToBlock: 20, - }, - mockFn: func(mockL2BridgeQuerier *mocks.BridgeQuerier) { - // gap is [16,16] - mockL2BridgeQuerier.EXPECT(). - GetBridgesAndClaims(ctx, uint64(16), uint64(16)). - Return([]bridgesync.Bridge{}, []bridgesync.Claim{}, nil) - }, - }, - { - name: "gap exists and bridges in gap returns error", - args: args{ - lastSentCertificate: &types.CertificateHeader{ - Status: agglayertypes.Settled, - FromBlock: 10, - ToBlock: 15, - }, - newFromBlock: 17, - newToBlock: 20, - }, - mockFn: func(mockL2BridgeQuerier *mocks.BridgeQuerier) { - mockL2BridgeQuerier.EXPECT(). - GetBridgesAndClaims(ctx, uint64(16), uint64(16)). - Return([]bridgesync.Bridge{{}}, []bridgesync.Claim{}, nil) - }, - expectedError: "there are new bridges or claims in the gap", - }, - { - name: "gap exists and claims in gap returns error", - args: args{ - lastSentCertificate: &types.CertificateHeader{ - Status: agglayertypes.Settled, - FromBlock: 10, - ToBlock: 15, - }, - newFromBlock: 17, - newToBlock: 20, - }, - mockFn: func(mockL2BridgeQuerier *mocks.BridgeQuerier) { - mockL2BridgeQuerier.EXPECT(). - GetBridgesAndClaims(ctx, uint64(16), uint64(16)). - Return([]bridgesync.Bridge{}, []bridgesync.Claim{{}}, nil) - }, - expectedError: "there are new bridges or claims in the gap", - }, - { - name: "gap exists, no bridges/claims, RequireNoFEPBlockGap true returns error", - args: args{ - lastSentCertificate: &types.CertificateHeader{ - Status: agglayertypes.Settled, - FromBlock: 10, - ToBlock: 15, - }, - newFromBlock: 17, - newToBlock: 20, - }, - requireNoFEPGap: true, - mockFn: func(mockL2BridgeQuerier *mocks.BridgeQuerier) { - mockL2BridgeQuerier.EXPECT(). - GetBridgesAndClaims(ctx, uint64(16), uint64(16)). - Return([]bridgesync.Bridge{}, []bridgesync.Claim{}, nil) - }, - expectedError: "block gap detected", - }, - { - name: "gap exists, GetBridgesAndClaims returns error", - args: args{ - lastSentCertificate: &types.CertificateHeader{ - Status: agglayertypes.Settled, - FromBlock: 10, - ToBlock: 15, - }, - newFromBlock: 17, - newToBlock: 20, - }, - mockFn: func(mockL2BridgeQuerier *mocks.BridgeQuerier) { - mockL2BridgeQuerier.EXPECT(). - GetBridgesAndClaims(ctx, uint64(16), uint64(16)). - Return(nil, nil, errors.New("db error")) - }, - expectedError: "error getting bridges and claims in the gap", - }, - { - name: "lastSentCertificate is InError, gap logic uses FromBlock-1", - args: args{ - lastSentCertificate: &types.CertificateHeader{ - Status: agglayertypes.InError, - FromBlock: 5, - ToBlock: 10, - }, - newFromBlock: 7, - newToBlock: 10, - }, - mockFn: func(mockL2BridgeQuerier *mocks.BridgeQuerier) { - // lastSettledToBlock = 4, so gap is [5,6] - mockL2BridgeQuerier.EXPECT(). - GetBridgesAndClaims(ctx, uint64(5), uint64(6)). - Return([]bridgesync.Bridge{}, []bridgesync.Claim{}, nil) - }, - }, - } - - for _, tt := range tests { - tt := tt - - t.Run(tt.name, func(t *testing.T) { - t.Parallel() - - mockL2BridgeQuerier := mocks.NewBridgeQuerier(t) - if tt.mockFn != nil { - tt.mockFn(mockL2BridgeQuerier) - } - - f := &baseFlow{ - l2BridgeQuerier: mockL2BridgeQuerier, - cfg: BaseFlowConfig{ - RequireNoFEPBlockGap: tt.requireNoFEPGap, - }, - } - - err := f.VerifyBlockRangeGaps(ctx, tt.args.lastSentCertificate, tt.args.newFromBlock, tt.args.newToBlock) - if tt.expectedError != "" { - require.Error(t, err) - require.Contains(t, err.Error(), tt.expectedError) - } else { - require.NoError(t, err) - } - }) - } -} diff --git a/aggsender/certificatebuild/common_params_verifier.go b/aggsender/certificatebuild/common_params_verifier.go new file mode 100644 index 000000000..565ff04f8 --- /dev/null +++ b/aggsender/certificatebuild/common_params_verifier.go @@ -0,0 +1,134 @@ +package certificatebuild + +import ( + "context" + "fmt" + + "github.com/agglayer/aggkit/aggsender/types" + "github.com/agglayer/aggkit/bridgesync" + "github.com/ethereum/go-ethereum/common" + "golang.org/x/crypto/sha3" +) + +var _ types.CommonCertParamsVerifier = (*CommonParamsVerifier)(nil) + +// CommonParamsVerifier is responsible for verifying common parameters related to certificate building. +// It utilizes a BridgeQuerier to interact with the L2 bridge and can enforce a requirement that there +// is no gap between FEP (Finalized Epoch Proof) blocks, depending on the requireNoFEPBlockGap flag. +type CommonParamsVerifier struct { + l2BridgeQuerier types.BridgeQuerier + requireNoFEPBlockGap bool +} + +// NewCommonParamsVerifier creates a new CommonParamsVerifier instance +func NewCommonParamsVerifier( + l2BridgeQuerier types.BridgeQuerier, + requireNoFEPBlockGap bool, +) *CommonParamsVerifier { + return &CommonParamsVerifier{ + l2BridgeQuerier: l2BridgeQuerier, + requireNoFEPBlockGap: requireNoFEPBlockGap, + } +} + +// VerifyBuildParams verifies the build parameters +func (c *CommonParamsVerifier) VerifyBuildParams(ctx context.Context, fullCert *types.CertificateBuildParams) error { + if err := c.verifyRetryCertStartingBlock(fullCert); err != nil { + return fmt.Errorf("error verifying retry certificate starting block: %w", err) + } + + if err := c.verifyClaimGERs(fullCert.Claims); err != nil { + return err + } + + return nil +} + +// VerifyBlockRangeGaps checks if there are any gaps in the block range of the certificate +// and verifies that there are no new bridges or claims in the gap. +func (c *CommonParamsVerifier) VerifyBlockRangeGaps( + ctx context.Context, + lastSentCertificate *types.CertificateHeader, + newFromBlock, newToBlock uint64) error { + if lastSentCertificate == nil { + return nil + } + + lastSettledFromBlock := uint64(0) + lastSettledToBlock := uint64(0) + if lastSentCertificate.Status.IsInError() { + // if the last certificate was in error, we need to check the last + // settled range to be correct + // we will leave the from block as 0, since we only require the to block + // to check the gap between the last sent certificate and the new one + if lastSentCertificate.FromBlock > 0 { + lastSettledToBlock = lastSentCertificate.FromBlock - 1 + } + } else { + lastSettledFromBlock = lastSentCertificate.FromBlock + lastSettledToBlock = lastSentCertificate.ToBlock + } + + nextBlockRange := types.NewBlockRange(newFromBlock, newToBlock) + lastBlockRange := types.NewBlockRange(lastSettledFromBlock, lastSettledToBlock) + + // case 2: is a new cert but is not contiguous to previous one + gap := nextBlockRange.Gap(lastBlockRange) + if gap.IsEmpty() { + return nil + } + + bridgeDataInTheGap, claimDataInTheGap, err := c.l2BridgeQuerier.GetBridgesAndClaims( + ctx, gap.FromBlock, gap.ToBlock) + if err != nil { + return fmt.Errorf("error getting bridges and claims in the gap %s: %w", gap.String(), err) + } + if len(bridgeDataInTheGap) > 0 || len(claimDataInTheGap) > 0 { + return fmt.Errorf("there are new bridges or claims in the gap %s, len(bridges)=%d. len(claims)=%d", + gap.String(), len(bridgeDataInTheGap), len(claimDataInTheGap)) + } + + if !gap.IsEmpty() && c.requireNoFEPBlockGap { + // even though we do not have bridge transactions in the gap, + // we need to return an error if RequireNoFEPBlockGap is true + return fmt.Errorf("block gap detected: %s without bridge transactions, but RequireNoFEPBlockGap is true", + gap.String()) + } + + return nil +} + +// verifyRetryCertStartingBlock verifies that the starting block of a retry certificate +// matches the last sent (InError) certificate's starting block. +func (c *CommonParamsVerifier) verifyRetryCertStartingBlock(buildParams *types.CertificateBuildParams) error { + if buildParams.IsARetry() && buildParams.FromBlock != buildParams.LastSentCertificate.FromBlock { + return fmt.Errorf("retry certificate fromBlock %d != last sent certificate fromBlock %d", + buildParams.FromBlock, buildParams.LastSentCertificate.FromBlock) + } + + return nil +} + +// verifyClaimGERs verifies the correctnes GERs of the claims +func (c *CommonParamsVerifier) verifyClaimGERs(claims []bridgesync.Claim) error { + for _, claim := range claims { + ger := calculateGER(claim.MainnetExitRoot, claim.RollupExitRoot) + if ger != claim.GlobalExitRoot { + return fmt.Errorf("claim[GlobalIndex: %s, BlockNum: %d]: GER mismatch. Expected: %s, got: %s", + claim.GlobalIndex.String(), claim.BlockNum, claim.GlobalExitRoot.String(), ger.String()) + } + } + + return nil +} + +// calculateGER calculates the GER hash based on the mainnet exit root and the rollup exit root +func calculateGER(mainnetExitRoot, rollupExitRoot common.Hash) common.Hash { + var gerBytes [common.HashLength]byte + hasher := sha3.NewLegacyKeccak256() + hasher.Write(mainnetExitRoot.Bytes()) + hasher.Write(rollupExitRoot.Bytes()) + copy(gerBytes[:], hasher.Sum(nil)) + + return gerBytes +} diff --git a/aggsender/certificatebuild/common_params_verifier_test.go b/aggsender/certificatebuild/common_params_verifier_test.go new file mode 100644 index 000000000..eb82b211f --- /dev/null +++ b/aggsender/certificatebuild/common_params_verifier_test.go @@ -0,0 +1,254 @@ +package certificatebuild + +import ( + "context" + "errors" + "testing" + + agglayertypes "github.com/agglayer/aggkit/agglayer/types" + "github.com/agglayer/aggkit/aggsender/mocks" + "github.com/agglayer/aggkit/aggsender/types" + "github.com/agglayer/aggkit/bridgesync" + "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/require" +) + +func Test_baseFlow_VerifyBuildParams(t *testing.T) { + t.Parallel() + + ctx := context.Background() + + testCases := []struct { + name string + buildParams *types.CertificateBuildParams + mockFn func(*mocks.BridgeQuerier) + expectedError string + }{ + { + name: "invalid retry starting block", + buildParams: &types.CertificateBuildParams{ + FromBlock: 10, + ToBlock: 15, + RetryCount: 1, + LastSentCertificate: &types.CertificateHeader{ + Height: 1, + Status: agglayertypes.InError, + FromBlock: 5, + ToBlock: 10, + }, + }, + expectedError: "retry certificate fromBlock 10 != last sent certificate fromBlock 5", + }, + { + name: "invalid claim GER", + buildParams: &types.CertificateBuildParams{ + FromBlock: 1, + ToBlock: 10, + Claims: []bridgesync.Claim{ + {GlobalExitRoot: common.HexToHash("0x123"), MainnetExitRoot: common.HexToHash("0x456"), RollupExitRoot: common.HexToHash("0x789")}, + }, + }, + expectedError: "GER mismatch", + }, + { + name: "success", + buildParams: &types.CertificateBuildParams{ + FromBlock: 1, + ToBlock: 10, + }, + }, + } + + for _, tc := range testCases { + tc := tc + + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + mockL2BridgeQuerier := mocks.NewBridgeQuerier(t) + if tc.mockFn != nil { + tc.mockFn(mockL2BridgeQuerier) + } + + verifier := NewCommonParamsVerifier(mockL2BridgeQuerier, false) + + err := verifier.VerifyBuildParams(ctx, tc.buildParams) + if tc.expectedError != "" { + require.ErrorContains(t, err, tc.expectedError) + } else { + require.NoError(t, err) + } + }) + } +} + +func Test_VerifyBlockRangeGaps(t *testing.T) { + t.Parallel() + + ctx := context.Background() + + type args struct { + lastSentCertificate *types.CertificateHeader + newFromBlock uint64 + newToBlock uint64 + } + tests := []struct { + name string + args args + requireNoFEPGap bool + mockFn func(mockL2BridgeQuerier *mocks.BridgeQuerier) + expectedError string + }{ + { + name: "lastSentCertificate is nil", + args: args{ + lastSentCertificate: nil, + newFromBlock: 10, + newToBlock: 20, + }, + }, + { + name: "no gap between certificates", + args: args{ + lastSentCertificate: &types.CertificateHeader{ + Status: agglayertypes.Settled, + FromBlock: 10, + ToBlock: 20, + }, + newFromBlock: 21, + newToBlock: 30, + }, + }, + { + name: "gap exists but no bridges or claims in gap", + args: args{ + lastSentCertificate: &types.CertificateHeader{ + Status: agglayertypes.Settled, + FromBlock: 10, + ToBlock: 15, + }, + newFromBlock: 17, + newToBlock: 20, + }, + mockFn: func(mockL2BridgeQuerier *mocks.BridgeQuerier) { + // gap is [16,16] + mockL2BridgeQuerier.EXPECT(). + GetBridgesAndClaims(ctx, uint64(16), uint64(16)). + Return([]bridgesync.Bridge{}, []bridgesync.Claim{}, nil) + }, + }, + { + name: "gap exists and bridges in gap returns error", + args: args{ + lastSentCertificate: &types.CertificateHeader{ + Status: agglayertypes.Settled, + FromBlock: 10, + ToBlock: 15, + }, + newFromBlock: 17, + newToBlock: 20, + }, + mockFn: func(mockL2BridgeQuerier *mocks.BridgeQuerier) { + mockL2BridgeQuerier.EXPECT(). + GetBridgesAndClaims(ctx, uint64(16), uint64(16)). + Return([]bridgesync.Bridge{{}}, []bridgesync.Claim{}, nil) + }, + expectedError: "there are new bridges or claims in the gap", + }, + { + name: "gap exists and claims in gap returns error", + args: args{ + lastSentCertificate: &types.CertificateHeader{ + Status: agglayertypes.Settled, + FromBlock: 10, + ToBlock: 15, + }, + newFromBlock: 17, + newToBlock: 20, + }, + mockFn: func(mockL2BridgeQuerier *mocks.BridgeQuerier) { + mockL2BridgeQuerier.EXPECT(). + GetBridgesAndClaims(ctx, uint64(16), uint64(16)). + Return([]bridgesync.Bridge{}, []bridgesync.Claim{{}}, nil) + }, + expectedError: "there are new bridges or claims in the gap", + }, + { + name: "gap exists, no bridges/claims, RequireNoFEPBlockGap true returns error", + args: args{ + lastSentCertificate: &types.CertificateHeader{ + Status: agglayertypes.Settled, + FromBlock: 10, + ToBlock: 15, + }, + newFromBlock: 17, + newToBlock: 20, + }, + requireNoFEPGap: true, + mockFn: func(mockL2BridgeQuerier *mocks.BridgeQuerier) { + mockL2BridgeQuerier.EXPECT(). + GetBridgesAndClaims(ctx, uint64(16), uint64(16)). + Return([]bridgesync.Bridge{}, []bridgesync.Claim{}, nil) + }, + expectedError: "block gap detected", + }, + { + name: "gap exists, GetBridgesAndClaims returns error", + args: args{ + lastSentCertificate: &types.CertificateHeader{ + Status: agglayertypes.Settled, + FromBlock: 10, + ToBlock: 15, + }, + newFromBlock: 17, + newToBlock: 20, + }, + mockFn: func(mockL2BridgeQuerier *mocks.BridgeQuerier) { + mockL2BridgeQuerier.EXPECT(). + GetBridgesAndClaims(ctx, uint64(16), uint64(16)). + Return(nil, nil, errors.New("db error")) + }, + expectedError: "error getting bridges and claims in the gap", + }, + { + name: "lastSentCertificate is InError, gap logic uses FromBlock-1", + args: args{ + lastSentCertificate: &types.CertificateHeader{ + Status: agglayertypes.InError, + FromBlock: 5, + ToBlock: 10, + }, + newFromBlock: 7, + newToBlock: 10, + }, + mockFn: func(mockL2BridgeQuerier *mocks.BridgeQuerier) { + // lastSettledToBlock = 4, so gap is [5,6] + mockL2BridgeQuerier.EXPECT(). + GetBridgesAndClaims(ctx, uint64(5), uint64(6)). + Return([]bridgesync.Bridge{}, []bridgesync.Claim{}, nil) + }, + }, + } + + for _, tt := range tests { + tt := tt + + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + mockL2BridgeQuerier := mocks.NewBridgeQuerier(t) + if tt.mockFn != nil { + tt.mockFn(mockL2BridgeQuerier) + } + + verifier := NewCommonParamsVerifier(mockL2BridgeQuerier, tt.requireNoFEPGap) + + err := verifier.VerifyBlockRangeGaps(ctx, tt.args.lastSentCertificate, tt.args.newFromBlock, tt.args.newToBlock) + if tt.expectedError != "" { + require.Error(t, err) + require.Contains(t, err.Error(), tt.expectedError) + } else { + require.NoError(t, err) + } + }) + } +} diff --git a/aggsender/flows/factory.go b/aggsender/flows/factory.go index afef04509..9230ed6bf 100644 --- a/aggsender/flows/factory.go +++ b/aggsender/flows/factory.go @@ -5,6 +5,7 @@ import ( "fmt" "github.com/agglayer/aggkit/aggsender/aggchainproofclient" + "github.com/agglayer/aggkit/aggsender/certificatebuild" "github.com/agglayer/aggkit/aggsender/config" "github.com/agglayer/aggkit/aggsender/db" "github.com/agglayer/aggkit/aggsender/optimistic" @@ -46,22 +47,34 @@ func NewFlow( } logger.Infof("Initializing RollupManager contract at address: %s. Genesis block: %d", cfg.RollupManagerAddr, cfg.RollupCreationBlockL1) - lerQuerier, err := query.NewLERDataQuerier( - cfg.RollupManagerAddr, cfg.RollupCreationBlockL1, rollupDataQuerier) + logger.Infof("Aggsender signer address: %s", signer.PublicAddress().Hex()) + + lerQuerier, err := query.NewLERDataQuerier(cfg.RollupCreationBlockL1, rollupDataQuerier) if err != nil { return nil, fmt.Errorf("error creating LER data querier: %w", err) } l2BridgeQuerier := query.NewBridgeDataQuerier(logger, l2Syncer, cfg.DelayBetweenRetries.Duration) l1InfoTreeQuerier := query.NewL1InfoTreeDataQuerier(l1Client, l1InfoTreeSyncer) - logger.Infof("Aggsender signer address: %s", signer.PublicAddress().Hex()) - baseFlow := NewBaseFlow( - logger, l2BridgeQuerier, storage, l1InfoTreeQuerier, lerQuerier, - NewBaseFlowConfig(cfg.MaxCertSize, 0, false), + commonParamsBuilder := certificatebuild.NewCommonParamsBuilder( + logger, + storage, + l1InfoTreeQuerier, + l2BridgeQuerier, + lerQuerier, + certificatebuild.NewCommonBuildConfig( + cfg.MaxCertSize, + 0, // on PP networks we always start from block 0 + false, // no FEP block gap on PP networks + ), ) + commonParamsVerifier := certificatebuild.NewCommonParamsVerifier( + l2BridgeQuerier, false) // no FEP block gap on PP networks + return NewPPFlow( logger, - baseFlow, + commonParamsBuilder, + commonParamsVerifier, storage, l1InfoTreeQuerier, l2BridgeQuerier, @@ -85,8 +98,6 @@ func NewFlow( return nil, fmt.Errorf("aggchainProverFlow - error creating aggkit prover client: %w", err) } - l1InfoTreeQuerier := query.NewL1InfoTreeDataQuerier(l1Client, l1InfoTreeSyncer) - startL2Block, err := funcGetL2StartBlock(cfg.SovereignRollupAddr, l1Client) if err != nil { return nil, fmt.Errorf("aggchainProverFlow - error reading sovereign rollup: %w", err) @@ -97,42 +108,48 @@ func NewFlow( return nil, fmt.Errorf("aggchainProverFlow - error creating optimistic mode querier: %w", err) } - lerQuerier, err := query.NewLERDataQuerier( - cfg.RollupManagerAddr, cfg.RollupCreationBlockL1, rollupDataQuerier) + lerQuerier, err := query.NewLERDataQuerier(cfg.RollupCreationBlockL1, rollupDataQuerier) if err != nil { return nil, fmt.Errorf("aggchainProverFlow - error creating LER data querier: %w", err) } - l2BridgeQuerier := query.NewBridgeDataQuerier(logger, l2Syncer, cfg.DelayBetweenRetries.Duration) - baseFlow := NewBaseFlow( - logger, l2BridgeQuerier, storage, l1InfoTreeQuerier, lerQuerier, - NewBaseFlowConfig(cfg.MaxCertSize, startL2Block, cfg.RequireNoFEPBlockGap), - ) - l2GERReader, err := l2GERReaderFactory(cfg.GlobalExitRootL2Addr, l2Client, l1InfoTreeSyncer) if err != nil { return nil, fmt.Errorf("failed to create L2 GER reader: %w", err) } + l1InfoTreeQuerier := query.NewL1InfoTreeDataQuerier(l1Client, l1InfoTreeSyncer) + l2BridgeQuerier := query.NewBridgeDataQuerier(logger, l2Syncer, cfg.DelayBetweenRetries.Duration) gerQuerier := query.NewGERDataQuerier(l1InfoTreeQuerier, l2GERReader) - + commonParamsBuilder := certificatebuild.NewCommonParamsBuilder( + logger, + storage, + l1InfoTreeQuerier, + l2BridgeQuerier, + lerQuerier, + certificatebuild.NewCommonBuildConfig( + cfg.MaxCertSize, + startL2Block, + cfg.RequireNoFEPBlockGap, + ), + ) + commonParamsVerifier := certificatebuild.NewCommonParamsVerifier(l2BridgeQuerier, cfg.RequireNoFEPBlockGap) aggchainProofQuerier := query.NewAggchainProofQuery( logger, aggchainProofClient, l1InfoTreeQuerier, optimisticSigner, - baseFlow, + commonParamsBuilder, gerQuerier, ) return NewAggchainProverFlow( logger, - NewAggchainProverFlowConfig(cfg.MaxL2BlockNumber), - baseFlow, + NewAggchainProverFlowConfig(cfg.MaxL2BlockNumber, startL2Block), + commonParamsBuilder, + commonParamsVerifier, storage, - l1InfoTreeQuerier, l2BridgeQuerier, - gerQuerier, l1Client, signer, optimisticModeQuerier, diff --git a/aggsender/flows/flow_aggchain_prover.go b/aggsender/flows/flow_aggchain_prover.go index 405926b89..e0bcc303d 100644 --- a/aggsender/flows/flow_aggchain_prover.go +++ b/aggsender/flows/flow_aggchain_prover.go @@ -7,6 +7,7 @@ import ( "github.com/0xPolygon/cdk-contracts-tooling/contracts/pp/l2-sovereign-chain/aggchainfep" agglayertypes "github.com/agglayer/aggkit/agglayer/types" + "github.com/agglayer/aggkit/aggsender/certificatebuild" "github.com/agglayer/aggkit/aggsender/db" "github.com/agglayer/aggkit/aggsender/query" "github.com/agglayer/aggkit/aggsender/types" @@ -17,19 +18,18 @@ import ( // AggchainProverFlow is a struct that holds the logic for the AggchainProver prover type flow type AggchainProverFlow struct { - baseFlow types.AggsenderFlowBaser + log types.Logger + cfg AggchainProverFlowConfig + storage db.AggSenderStorage + l2BridgeQuerier types.BridgeQuerier - log types.Logger - storage db.AggSenderStorage - l1InfoTreeDataQuerier types.L1InfoTreeDataQuerier - l2BridgeQuerier types.BridgeQuerier - - gerQuerier types.GERQuerier certificateSigner signertypes.Signer optimisticModeQuerier types.OptimisticModeQuerier aggchainProofQuerier types.AggchainProofQuerier - config AggchainProverFlowConfig featureMaxL2Block types.MaxL2BlockNumberLimiterInterface + + commonParamsBuilder types.CommonCertParamsBuilder + commonParamsVerifier types.CommonCertParamsVerifier } func getL2StartBlock(sovereignRollupAddr common.Address, l1Client aggkittypes.BaseEthereumClienter) (uint64, error) { @@ -51,20 +51,24 @@ func getL2StartBlock(sovereignRollupAddr common.Address, l1Client aggkittypes.Ba // AggchainProverFlowConfig holds the configuration for the AggchainProverFlow type AggchainProverFlowConfig struct { maxL2BlockNumber uint64 + startL2Block uint64 } // NewAggchainProverFlowConfigDefault returns a default configuration for the AggchainProverFlow func NewAggchainProverFlowConfigDefault() AggchainProverFlowConfig { return AggchainProverFlowConfig{ maxL2BlockNumber: 0, + startL2Block: 0, } } // NewAggchainProverFlowConfig creates a new AggchainProverFlowConfig with the given base flow config func NewAggchainProverFlowConfig( - maxL2BlockNumber uint64) AggchainProverFlowConfig { + maxL2BlockNumber uint64, + startL2Block uint64) AggchainProverFlowConfig { return AggchainProverFlowConfig{ maxL2BlockNumber: maxL2BlockNumber, + startL2Block: startL2Block, } } @@ -72,35 +76,33 @@ func NewAggchainProverFlowConfig( // creating it func NewAggchainProverFlow( log types.Logger, - aggChainProverConfig AggchainProverFlowConfig, - baseFlow types.AggsenderFlowBaser, + cfg AggchainProverFlowConfig, + commonParamsBuilder types.CommonCertParamsBuilder, + commonParamsVerifier types.CommonCertParamsVerifier, storage db.AggSenderStorage, - l1InfoTreeQuerier types.L1InfoTreeDataQuerier, l2BridgeQuerier types.BridgeQuerier, - gerQuerier types.GERQuerier, l1Client aggkittypes.BaseEthereumClienter, signer signertypes.Signer, optimisticModeQuerier types.OptimisticModeQuerier, aggchainProofQuerier types.AggchainProofQuerier, ) *AggchainProverFlow { feature := NewMaxL2BlockNumberLimiter( - aggChainProverConfig.maxL2BlockNumber, + cfg.maxL2BlockNumber, log, false, // AggchainProverFlow allows to resize retry certs false, // AggchainProverFlow allows to send no bridges certs ) return &AggchainProverFlow{ log: log, + cfg: cfg, storage: storage, - l1InfoTreeDataQuerier: l1InfoTreeQuerier, l2BridgeQuerier: l2BridgeQuerier, - gerQuerier: gerQuerier, - config: aggChainProverConfig, certificateSigner: signer, optimisticModeQuerier: optimisticModeQuerier, aggchainProofQuerier: aggchainProofQuerier, - baseFlow: baseFlow, featureMaxL2Block: feature, + commonParamsBuilder: commonParamsBuilder, + commonParamsVerifier: commonParamsVerifier, } } @@ -114,7 +116,7 @@ func (a *AggchainProverFlow) CheckInitialStatus(ctx context.Context) error { // we check if there are gaps between start L2 block and last sent certificate on startup // if there are gaps with bridge transactions, we can not allow the start of aggsender - startL2Block := a.baseFlow.StartL2Block() + startL2Block := a.cfg.startL2Block // we need to wait for the syncer to catch up to the start L2 block (start FEP block) // in order to check if there are any bridge transactions in the gap @@ -122,7 +124,7 @@ func (a *AggchainProverFlow) CheckInitialStatus(ctx context.Context) error { return fmt.Errorf("aggchainProverFlow - error waiting for syncer to catch up: %w", err) } - if err := a.baseFlow.VerifyBlockRangeGaps( + if err := a.commonParamsVerifier.VerifyBlockRangeGaps( ctx, lastSentCertificate, startL2Block, startL2Block); err != nil { return fmt.Errorf("aggchainProverFlow - error verifying block range gaps on startup. Err: %w", err) } @@ -225,9 +227,9 @@ func (a *AggchainProverFlow) GetCertificateBuildParams(ctx context.Context) (*ty lastSentCert.CertType, typeCert) } - buildParams, err := a.baseFlow.GetCertificateBuildParamsInternal(ctx, typeCert) + buildParams, err := a.commonParamsBuilder.GetCommonCertificateBuildParams(ctx, typeCert) if err != nil { - if errors.Is(err, errNoNewBlocks) { + if errors.Is(err, certificatebuild.ErrNoNewBlocks) { // no new blocks to send a certificate // this is a valid case, so just return nil without error return nil, nil @@ -256,7 +258,7 @@ func (a *AggchainProverFlow) GetCertificateBuildParams(ctx context.Context) (*ty // it also calls the prover to get the aggchain proof func (a *AggchainProverFlow) verifyBuildParamsAndGenerateProof( ctx context.Context, buildParams *types.CertificateBuildParams) (*types.CertificateBuildParams, error) { - if err := a.baseFlow.VerifyBuildParams(ctx, buildParams); err != nil { + if err := a.commonParamsVerifier.VerifyBuildParams(ctx, buildParams); err != nil { return nil, fmt.Errorf("aggchainProverFlow - error verifying build params: %w", err) } @@ -291,7 +293,7 @@ func (a *AggchainProverFlow) verifyBuildParamsAndGenerateProof( // this function is the implementation of the FlowManager interface func (a *AggchainProverFlow) BuildCertificate(ctx context.Context, buildParams *types.CertificateBuildParams) (*agglayertypes.Certificate, error) { - cert, err := a.baseFlow.BuildCertificate(ctx, buildParams, buildParams.LastSentCertificate, true) + cert, err := a.commonParamsBuilder.BuildCertificate(ctx, buildParams, buildParams.LastSentCertificate, true) if err != nil { return nil, fmt.Errorf("aggchainProverFlow - error building certificate: %w", err) } @@ -335,20 +337,20 @@ func (a *AggchainProverFlow) getLastProvenBlock(fromBlock uint64, lastCertificat // if this is the first certificate, we need to start from the starting L2 block // that we got from the sovereign rollup a.log.Infof("aggchainProverFlow - getLastProvenBlock - fromBlock is 0, returns startL2Block: %d", - a.baseFlow.StartL2Block()) - return a.baseFlow.StartL2Block() + a.cfg.startL2Block) + return a.cfg.startL2Block } - if lastCertificate != nil && lastCertificate.ToBlock < a.baseFlow.StartL2Block() { + if lastCertificate != nil && lastCertificate.ToBlock < a.cfg.startL2Block { // if the last certificate is settled on PP, the last proven block is the starting L2 block a.log.Infof("aggchainProverFlow - getLastProvenBlock. Last certificate block: %d < startL2Block: %d", - lastCertificate.ToBlock, a.baseFlow.StartL2Block()) - return a.baseFlow.StartL2Block() + lastCertificate.ToBlock, a.cfg.startL2Block) + return a.cfg.startL2Block } - if fromBlock-1 < a.baseFlow.StartL2Block() { + if fromBlock-1 < a.cfg.startL2Block { // if the fromBlock is less than the starting L2 block, we need to start from the starting L2 block a.log.Infof("aggchainProverFlow - getLastProvenBlock. FromBlock: %d < startL2Block: %d", - fromBlock, a.baseFlow.StartL2Block()) - return a.baseFlow.StartL2Block() + fromBlock, a.cfg.startL2Block) + return a.cfg.startL2Block } return fromBlock - 1 diff --git a/aggsender/flows/flow_aggchain_prover_optimistic_test.go b/aggsender/flows/flow_aggchain_prover_optimistic_test.go index 9d0f51738..996b5e7f8 100644 --- a/aggsender/flows/flow_aggchain_prover_optimistic_test.go +++ b/aggsender/flows/flow_aggchain_prover_optimistic_test.go @@ -48,7 +48,7 @@ func Test_AggchainProverFlow_getCertificateTypeToGenerate(t *testing.T) { t.Parallel() data := NewAggchainProverFlowTestData(t, - NewBaseFlowConfigDefault()) + NewAggchainProverFlowConfigDefault()) data.mockOptimisticModeQuerier.EXPECT().IsOptimisticModeOn().Return(tc.optimisticModeReturn, tc.optimisticModeError).Once() certificateType, err := data.sut.getCertificateTypeToGenerate() if tc.optimisticModeError != nil { @@ -65,7 +65,7 @@ func Test_AggchainProverFlow_getCertificateTypeToGenerate(t *testing.T) { // the key part of it is the call to GetCertificateBuildParamsInternal that means that are getting // a new block range and is not taking advantage of previous proofs func Test_AggchainProverFlow_PreviousCertNotSameTypeItRecalculateCertificate(t *testing.T) { - data := NewAggchainProverFlowTestData(t, NewBaseFlowConfigDefault()) + data := NewAggchainProverFlowTestData(t, NewAggchainProverFlowConfigDefault()) lastCert := &types.CertificateHeader{ Height: 3, FromBlock: 10, @@ -86,10 +86,10 @@ func Test_AggchainProverFlow_PreviousCertNotSameTypeItRecalculateCertificate(t * data.mockOptimisticModeQuerier.EXPECT().IsOptimisticModeOn().Return(false, nil).Once() // then because last cert type doesnt match is going to act as a new one // requesting to GetCertificateBuildParamsInternal to create a new cert - data.mockFlowBase.EXPECT().GetCertificateBuildParamsInternal(data.ctx, types.CertificateTypeFEP).Return( + data.mockParamsBuilder.EXPECT().GetCommonCertificateBuildParams(data.ctx, types.CertificateTypeFEP).Return( nextCert, nil).Once() // After the function verifyBuildParamsAndGenerateProof calls to baseFlow.VerifyBuildParams() - data.mockFlowBase.EXPECT().VerifyBuildParams(mock.Anything, mock.Anything).Return(nil).Once() + data.mockParamsVerifier.EXPECT().VerifyBuildParams(mock.Anything, mock.Anything).Return(nil).Once() // Now calls to aggkit-prover service: data.mockAggchainProofQuerier.EXPECT().GenerateAggchainProof(mock.Anything, uint64(9), uint64(70), mock.Anything).Return(&types.AggchainProof{ SP1StarkProof: &types.SP1StarkProof{ @@ -106,45 +106,39 @@ func Test_AggchainProverFlow_PreviousCertNotSameTypeItRecalculateCertificate(t * type AggchainProverFlowTestData struct { mockStorage *mocks.AggSenderStorage mockL2BridgeQuerier *mocks.BridgeQuerier - mockL1InfoTreeQuerier *mocks.L1InfoTreeDataQuerier - mockGERQuerier *mocks.GERQuerier mockL1Client *aggkittypesmocks.BaseEthereumClienter mockOptimisticModeQuerier *mocks.OptimisticModeQuerier mockSigner *mocks.Signer - mockFlowBase *mocks.AggsenderFlowBaser mockAggchainProofQuerier *mocks.AggchainProofQuerier + mockParamsBuilder *mocks.CommonCertParamsBuilder + mockParamsVerifier *mocks.CommonCertParamsVerifier ctx context.Context sut *AggchainProverFlow } -func NewAggchainProverFlowTestData(t *testing.T, cfgBase BaseFlowConfig) *AggchainProverFlowTestData { +func NewAggchainProverFlowTestData(t *testing.T, cfgBase AggchainProverFlowConfig) *AggchainProverFlowTestData { t.Helper() res := &AggchainProverFlowTestData{ mockStorage: mocks.NewAggSenderStorage(t), mockL2BridgeQuerier: mocks.NewBridgeQuerier(t), - mockL1InfoTreeQuerier: mocks.NewL1InfoTreeDataQuerier(t), - mockGERQuerier: mocks.NewGERQuerier(t), mockL1Client: aggkittypesmocks.NewBaseEthereumClienter(t), mockOptimisticModeQuerier: mocks.NewOptimisticModeQuerier(t), mockSigner: mocks.NewSigner(t), mockAggchainProofQuerier: mocks.NewAggchainProofQuerier(t), - mockFlowBase: mocks.NewAggsenderFlowBaser(t), - ctx: context.TODO(), + mockParamsBuilder: mocks.NewCommonCertParamsBuilder(t), + mockParamsVerifier: mocks.NewCommonCertParamsVerifier(t), + ctx: t.Context(), } - // Simulate the access to baseFlow variables - res.mockFlowBase.EXPECT().StartL2Block().Return(cfgBase.StartL2Block).Maybe() - res.sut = NewAggchainProverFlow( log.WithFields("flowManager", "AggchainProverFlowTestData"), NewAggchainProverFlowConfigDefault(), - res.mockFlowBase, + res.mockParamsBuilder, + res.mockParamsVerifier, res.mockStorage, - res.mockL1InfoTreeQuerier, res.mockL2BridgeQuerier, - res.mockGERQuerier, res.mockL1Client, res.mockSigner, res.mockOptimisticModeQuerier, diff --git a/aggsender/flows/flow_aggchain_prover_test.go b/aggsender/flows/flow_aggchain_prover_test.go index b02ccfda0..e44fa693c 100644 --- a/aggsender/flows/flow_aggchain_prover_test.go +++ b/aggsender/flows/flow_aggchain_prover_test.go @@ -11,6 +11,7 @@ import ( "github.com/0xPolygon/cdk-contracts-tooling/contracts/pp/l2-sovereign-chain/aggchainfep" agglayertypes "github.com/agglayer/aggkit/agglayer/types" + "github.com/agglayer/aggkit/aggsender/certificatebuild" "github.com/agglayer/aggkit/aggsender/mocks" "github.com/agglayer/aggkit/aggsender/query" "github.com/agglayer/aggkit/aggsender/types" @@ -26,18 +27,17 @@ import ( func Test_AggchainProverFlow_GetCertificateBuildParams(t *testing.T) { t.Parallel() - // Set up the test context - ctx := context.Background() finalizedL1Root := common.HexToHash("0x1") + ctx := t.Context() testCases := []struct { name string mockFn func(*mocks.AggSenderStorage, *mocks.BridgeQuerier, *mocks.AggchainProofQuerier, - *mocks.L1InfoTreeDataQuerier, - *mocks.GERQuerier, + *mocks.CommonCertParamsVerifier, + *mocks.CommonCertParamsBuilder, ) expectedParams *types.CertificateBuildParams expectedError string @@ -47,8 +47,8 @@ func Test_AggchainProverFlow_GetCertificateBuildParams(t *testing.T) { mockFn: func(mockStorage *mocks.AggSenderStorage, mockL2BridgeQuerier *mocks.BridgeQuerier, mockAggchainProofQuerier *mocks.AggchainProofQuerier, - mockL1InfoDataQuery *mocks.L1InfoTreeDataQuerier, - mockGERQuerier *mocks.GERQuerier) { + mockParamsVerifier *mocks.CommonCertParamsVerifier, + mockParamsBuilder *mocks.CommonCertParamsBuilder) { mockStorage.EXPECT().GetLastSentCertificateHeaderWithProofIfInError(ctx).Return(nil, nil, errors.New("some error")) }, expectedError: "some error", @@ -58,11 +58,10 @@ func Test_AggchainProverFlow_GetCertificateBuildParams(t *testing.T) { mockFn: func(mockStorage *mocks.AggSenderStorage, mockL2BridgeQuerier *mocks.BridgeQuerier, mockAggchainProofQuerier *mocks.AggchainProofQuerier, - mockL1InfoDataQuery *mocks.L1InfoTreeDataQuerier, - mockGERQuerier *mocks.GERQuerier) { + mockParamsVerifier *mocks.CommonCertParamsVerifier, + mockParamsBuilder *mocks.CommonCertParamsBuilder) { rer := common.HexToHash("0x1") mer := common.HexToHash("0x2") - ger := calculateGER(mer, rer) mockStorage.EXPECT().GetLastSentCertificateHeaderWithProofIfInError(ctx).Return(&types.CertificateHeader{ Height: 0, FromBlock: 1, @@ -80,7 +79,6 @@ func Test_AggchainProverFlow_GetCertificateBuildParams(t *testing.T) { mockL2BridgeQuerier.EXPECT().GetBridgesAndClaims(ctx, uint64(1), uint64(10)).Return([]bridgesync.Bridge{{}}, []bridgesync.Claim{ { GlobalIndex: big.NewInt(1), - GlobalExitRoot: ger, MainnetExitRoot: mer, RollupExitRoot: rer, }}, nil) @@ -94,7 +92,6 @@ func Test_AggchainProverFlow_GetCertificateBuildParams(t *testing.T) { GlobalIndex: big.NewInt(1), RollupExitRoot: common.HexToHash("0x1"), MainnetExitRoot: common.HexToHash("0x2"), - GlobalExitRoot: calculateGER(common.HexToHash("0x2"), common.HexToHash("0x1")), }}, L1InfoTreeRootFromWhichToProve: common.HexToHash("0x1"), AggchainProof: &types.AggchainProof{ @@ -118,11 +115,10 @@ func Test_AggchainProverFlow_GetCertificateBuildParams(t *testing.T) { mockFn: func(mockStorage *mocks.AggSenderStorage, mockL2BridgeQuerier *mocks.BridgeQuerier, mockAggchainProofQuerier *mocks.AggchainProofQuerier, - mockL1InfoDataQuery *mocks.L1InfoTreeDataQuerier, - mockGERQuerier *mocks.GERQuerier) { + mockParamsVerifier *mocks.CommonCertParamsVerifier, + mockParamsBuilder *mocks.CommonCertParamsBuilder) { rer := common.HexToHash("0x1") mer := common.HexToHash("0x2") - ger := calculateGER(mer, rer) mockStorage.EXPECT().GetLastSentCertificateHeaderWithProofIfInError(ctx).Return(&types.CertificateHeader{ Height: 0, FromBlock: 1, @@ -134,11 +130,11 @@ func Test_AggchainProverFlow_GetCertificateBuildParams(t *testing.T) { mockL2BridgeQuerier.EXPECT().GetBridgesAndClaims(ctx, uint64(1), uint64(10)).Return([]bridgesync.Bridge{{}}, []bridgesync.Claim{ { GlobalIndex: big.NewInt(1), - GlobalExitRoot: ger, MainnetExitRoot: mer, RollupExitRoot: rer, }}, nil) - mockAggchainProofQuerier.EXPECT().GenerateAggchainProof(context.Background(), uint64(0), uint64(10), mock.Anything). + mockParamsVerifier.EXPECT().VerifyBuildParams(ctx, mock.Anything).Return(nil).Once() + mockAggchainProofQuerier.EXPECT().GenerateAggchainProof(ctx, uint64(0), uint64(10), mock.Anything). Return(&types.AggchainProof{ SP1StarkProof: &types.SP1StarkProof{Proof: []byte("some-proof")}, LastProvenBlock: 0, @@ -163,7 +159,6 @@ func Test_AggchainProverFlow_GetCertificateBuildParams(t *testing.T) { GlobalIndex: big.NewInt(1), RollupExitRoot: common.HexToHash("0x1"), MainnetExitRoot: common.HexToHash("0x2"), - GlobalExitRoot: calculateGER(common.HexToHash("0x2"), common.HexToHash("0x1")), }}, L1InfoTreeRootFromWhichToProve: common.HexToHash("0x1"), AggchainProof: &types.AggchainProof{ @@ -178,24 +173,16 @@ func Test_AggchainProverFlow_GetCertificateBuildParams(t *testing.T) { mockFn: func(mockStorage *mocks.AggSenderStorage, mockL2BridgeQuerier *mocks.BridgeQuerier, mockAggchainProofQuerier *mocks.AggchainProofQuerier, - mockL1InfoDataQuery *mocks.L1InfoTreeDataQuerier, - mockGERQuerier *mocks.GERQuerier) { - rer := common.HexToHash("0x1") - mer := common.HexToHash("0x2") - ger := calculateGER(mer, rer) + mockParamsVerifier *mocks.CommonCertParamsVerifier, + mockParamsBuilder *mocks.CommonCertParamsBuilder) { mockStorage.EXPECT().GetLastSentCertificateHeaderWithProofIfInError(ctx).Return(nil, nil, nil).Once() - mockStorage.EXPECT().GetLastSentCertificateHeader().Return(nil, nil).Once() - mockL1InfoDataQuery.EXPECT().GetLatestFinalizedL1InfoRoot(mock.Anything).Return( - &treetypes.Root{Hash: common.HexToHash("0x123"), BlockNum: 10}, nil, nil) - mockL2BridgeQuerier.On("GetLastProcessedBlock", ctx).Return(uint64(10), nil) - mockL2BridgeQuerier.EXPECT().GetBridgesAndClaims(ctx, uint64(1), uint64(10)).Return([]bridgesync.Bridge{{}}, []bridgesync.Claim{ - { - GlobalIndex: big.NewInt(1), - GlobalExitRoot: ger, - MainnetExitRoot: mer, - RollupExitRoot: rer, - }}, nil) - mockAggchainProofQuerier.EXPECT().GenerateAggchainProof(context.Background(), uint64(0), uint64(10), mock.Anything). + mockParamsBuilder.EXPECT().GetCommonCertificateBuildParams(ctx, types.CertificateTypeFEP).Return(&types.CertificateBuildParams{ + FromBlock: 1, + ToBlock: 10, + CertificateType: types.CertificateTypeFEP, + }, nil).Once() + mockParamsVerifier.EXPECT().VerifyBuildParams(ctx, mock.Anything).Return(nil).Once() + mockAggchainProofQuerier.EXPECT().GenerateAggchainProof(ctx, uint64(0), uint64(10), mock.Anything). Return(nil, nil, errors.New("some error")) }, expectedError: "aggchainProverFlow - error generating aggchain proof: some error", @@ -205,16 +192,17 @@ func Test_AggchainProverFlow_GetCertificateBuildParams(t *testing.T) { mockFn: func(mockStorage *mocks.AggSenderStorage, mockL2BridgeQuerier *mocks.BridgeQuerier, mockAggchainProofQuerier *mocks.AggchainProofQuerier, - mockL1InfoDataQuery *mocks.L1InfoTreeDataQuerier, - mockGERQuerier *mocks.GERQuerier) { + mockParamsVerifier *mocks.CommonCertParamsVerifier, + mockParamsBuilder *mocks.CommonCertParamsBuilder) { mockStorage.EXPECT().GetLastSentCertificateHeaderWithProofIfInError(ctx).Return(nil, nil, nil).Once() - mockStorage.EXPECT().GetLastSentCertificateHeader().Return(nil, nil).Once() - mockL2BridgeQuerier.EXPECT().GetLastProcessedBlock(ctx).Return(uint64(10), nil) - mockL1InfoDataQuery.EXPECT().GetLatestFinalizedL1InfoRoot(mock.Anything).Return( - &treetypes.Root{Hash: common.HexToHash("0x123"), BlockNum: 10}, nil, nil) - mockL2BridgeQuerier.EXPECT().GetBridgesAndClaims(ctx, uint64(1), uint64(10)).Return([]bridgesync.Bridge{}, []bridgesync.Claim{}, nil) + mockParamsBuilder.EXPECT().GetCommonCertificateBuildParams(ctx, types.CertificateTypeFEP).Return(&types.CertificateBuildParams{ + FromBlock: 1, + ToBlock: 10, + CertificateType: types.CertificateTypeFEP, + }, nil).Once() + mockParamsVerifier.EXPECT().VerifyBuildParams(ctx, mock.Anything).Return(nil).Once() wrappedErr := fmt.Errorf("wrapped error: %w", query.ErrNoProofBuiltYet) - mockAggchainProofQuerier.EXPECT().GenerateAggchainProof(context.Background(), uint64(0), uint64(10), mock.Anything). + mockAggchainProofQuerier.EXPECT().GenerateAggchainProof(ctx, uint64(0), uint64(10), mock.Anything). Return(nil, nil, wrappedErr) }, expectedError: "", @@ -225,23 +213,22 @@ func Test_AggchainProverFlow_GetCertificateBuildParams(t *testing.T) { mockFn: func(mockStorage *mocks.AggSenderStorage, mockL2BridgeQuerier *mocks.BridgeQuerier, mockAggchainProofQuerier *mocks.AggchainProofQuerier, - mockL1InfoDataQuery *mocks.L1InfoTreeDataQuerier, - mockGERQuerier *mocks.GERQuerier) { - rer := common.HexToHash("0x1") - mer := common.HexToHash("0x2") - ger := calculateGER(mer, rer) + mockParamsVerifier *mocks.CommonCertParamsVerifier, + mockParamsBuilder *mocks.CommonCertParamsBuilder) { mockStorage.EXPECT().GetLastSentCertificateHeaderWithProofIfInError(ctx).Return(&types.CertificateHeader{ToBlock: 5, Status: agglayertypes.Settled}, nil, nil).Once() - mockStorage.EXPECT().GetLastSentCertificateHeader().Return(&types.CertificateHeader{ToBlock: 5}, nil).Once() - mockL1InfoDataQuery.EXPECT().GetLatestFinalizedL1InfoRoot(mock.Anything).Return( - &treetypes.Root{Hash: common.HexToHash("0x123"), BlockNum: 10}, nil, nil) - mockL2BridgeQuerier.On("GetLastProcessedBlock", ctx).Return(uint64(10), nil) - mockL2BridgeQuerier.EXPECT().GetBridgesAndClaims(ctx, uint64(6), uint64(10)).Return([]bridgesync.Bridge{{}}, []bridgesync.Claim{{ - GlobalIndex: big.NewInt(1), - GlobalExitRoot: ger, - MainnetExitRoot: mer, - RollupExitRoot: rer, - }}, nil) - mockAggchainProofQuerier.EXPECT().GenerateAggchainProof(context.Background(), uint64(5), uint64(10), mock.Anything). + mockParamsBuilder.EXPECT().GetCommonCertificateBuildParams(ctx, types.CertificateTypeFEP).Return(&types.CertificateBuildParams{ + FromBlock: 6, + ToBlock: 10, + CertificateType: types.CertificateTypeFEP, + Bridges: []bridgesync.Bridge{}, + Claims: []bridgesync.Claim{}, + LastSentCertificate: &types.CertificateHeader{ + ToBlock: 5, + Status: agglayertypes.Settled, + }, + }, nil).Once() + mockParamsVerifier.EXPECT().VerifyBuildParams(ctx, mock.Anything).Return(nil).Once() + mockAggchainProofQuerier.EXPECT().GenerateAggchainProof(ctx, uint64(5), uint64(10), mock.Anything). Return(&types.AggchainProof{ SP1StarkProof: &types.SP1StarkProof{Proof: []byte("some-proof")}, LastProvenBlock: 6, @@ -254,22 +241,17 @@ func Test_AggchainProverFlow_GetCertificateBuildParams(t *testing.T) { RetryCount: 0, LastSentCertificate: &types.CertificateHeader{ ToBlock: 5, + Status: agglayertypes.Settled, }, - Bridges: []bridgesync.Bridge{{}}, - L1InfoTreeLeafCount: 11, - Claims: []bridgesync.Claim{{ - GlobalIndex: big.NewInt(1), - RollupExitRoot: common.HexToHash("0x1"), - MainnetExitRoot: common.HexToHash("0x2"), - GlobalExitRoot: calculateGER(common.HexToHash("0x2"), common.HexToHash("0x1")), - }}, + Bridges: []bridgesync.Bridge{}, + Claims: []bridgesync.Claim{}, + L1InfoTreeLeafCount: 11, L1InfoTreeRootFromWhichToProve: common.HexToHash("0x1"), AggchainProof: &types.AggchainProof{ SP1StarkProof: &types.SP1StarkProof{Proof: []byte("some-proof")}, LastProvenBlock: 6, EndBlock: 10, }, - CreatedAt: timeNowUTCForTest(), CertificateType: types.CertificateTypeFEP, }, }, @@ -278,23 +260,27 @@ func Test_AggchainProverFlow_GetCertificateBuildParams(t *testing.T) { mockFn: func(mockStorage *mocks.AggSenderStorage, mockL2BridgeQuerier *mocks.BridgeQuerier, mockAggchainProofQuerier *mocks.AggchainProofQuerier, - mockL1InfoDataQuery *mocks.L1InfoTreeDataQuerier, - mockGERQuerier *mocks.GERQuerier) { - rer := common.HexToHash("0x1") - mer := common.HexToHash("0x2") - ger := calculateGER(mer, rer) + mockParamsVerifier *mocks.CommonCertParamsVerifier, + mockParamsBuilder *mocks.CommonCertParamsBuilder) { mockStorage.EXPECT().GetLastSentCertificateHeaderWithProofIfInError(ctx).Return(&types.CertificateHeader{ToBlock: 5, Status: agglayertypes.Settled}, nil, nil).Once() - mockStorage.EXPECT().GetLastSentCertificateHeader().Return(&types.CertificateHeader{ToBlock: 5}, nil).Once() - mockL1InfoDataQuery.EXPECT().GetLatestFinalizedL1InfoRoot(mock.Anything).Return( - &treetypes.Root{Hash: common.HexToHash("0x123"), BlockNum: 10}, nil, nil) - mockL2BridgeQuerier.On("GetLastProcessedBlock", ctx).Return(uint64(10), nil) - mockL2BridgeQuerier.EXPECT().GetBridgesAndClaims(ctx, uint64(6), uint64(10)).Return( - []bridgesync.Bridge{{BlockNum: 6}, {BlockNum: 10}}, - []bridgesync.Claim{ - {BlockNum: 8, GlobalIndex: big.NewInt(1), GlobalExitRoot: ger, MainnetExitRoot: mer, RollupExitRoot: rer}, - {BlockNum: 9, GlobalIndex: big.NewInt(2), GlobalExitRoot: ger, MainnetExitRoot: mer, RollupExitRoot: rer}}, - nil) - mockAggchainProofQuerier.EXPECT().GenerateAggchainProof(context.Background(), uint64(5), uint64(10), mock.Anything). + mockParamsBuilder.EXPECT().GetCommonCertificateBuildParams(ctx, types.CertificateTypeFEP).Return(&types.CertificateBuildParams{ + FromBlock: 6, + ToBlock: 10, + CertificateType: types.CertificateTypeFEP, + Bridges: []bridgesync.Bridge{{BlockNum: 6}, {BlockNum: 10}}, + Claims: []bridgesync.Claim{{ + BlockNum: 8, + GlobalIndex: big.NewInt(1), + RollupExitRoot: common.HexToHash("0x1"), + MainnetExitRoot: common.HexToHash("0x2"), + }}, + LastSentCertificate: &types.CertificateHeader{ + ToBlock: 5, + Status: agglayertypes.Settled, + }, + }, nil).Once() + mockParamsVerifier.EXPECT().VerifyBuildParams(ctx, mock.Anything).Return(nil).Once() + mockAggchainProofQuerier.EXPECT().GenerateAggchainProof(ctx, uint64(5), uint64(10), mock.Anything). Return(&types.AggchainProof{ SP1StarkProof: &types.SP1StarkProof{Proof: []byte("some-proof")}, LastProvenBlock: 6, @@ -308,6 +294,7 @@ func Test_AggchainProverFlow_GetCertificateBuildParams(t *testing.T) { L1InfoTreeLeafCount: 11, LastSentCertificate: &types.CertificateHeader{ ToBlock: 5, + Status: agglayertypes.Settled, }, Bridges: []bridgesync.Bridge{{BlockNum: 6}}, Claims: []bridgesync.Claim{{ @@ -315,7 +302,6 @@ func Test_AggchainProverFlow_GetCertificateBuildParams(t *testing.T) { GlobalIndex: big.NewInt(1), RollupExitRoot: common.HexToHash("0x1"), MainnetExitRoot: common.HexToHash("0x2"), - GlobalExitRoot: calculateGER(common.HexToHash("0x2"), common.HexToHash("0x1")), }}, L1InfoTreeRootFromWhichToProve: common.HexToHash("0x1"), AggchainProof: &types.AggchainProof{ @@ -323,7 +309,6 @@ func Test_AggchainProverFlow_GetCertificateBuildParams(t *testing.T) { LastProvenBlock: 6, EndBlock: 8, }, - CreatedAt: timeNowUTCForTest(), CertificateType: types.CertificateTypeFEP, }, }, @@ -337,37 +322,27 @@ func Test_AggchainProverFlow_GetCertificateBuildParams(t *testing.T) { mockAggchainProofQuerier := mocks.NewAggchainProofQuerier(t) mockStorage := mocks.NewAggSenderStorage(t) mockL2BridgeQuerier := mocks.NewBridgeQuerier(t) - mockGERQuerier := mocks.NewGERQuerier(t) mockOptimistic := mocks.NewOptimisticModeQuerier(t) - mockL1InfoTreeDataQuerier := mocks.NewL1InfoTreeDataQuerier(t) - mockLERQuerier := mocks.NewLERQuerier(t) mockSigner := mocks.NewSigner(t) + mockParamsBuilder := mocks.NewCommonCertParamsBuilder(t) + mockParamsVerifier := mocks.NewCommonCertParamsVerifier(t) logger := log.WithFields("flowManager", "Test_AggchainProverFlow_GetCertificateBuildParams") - flowBase := NewBaseFlow( - logger, - mockL2BridgeQuerier, - mockStorage, - mockL1InfoTreeDataQuerier, - mockLERQuerier, - NewBaseFlowConfigDefault()) - flowBase.timeNowFunc = timeNowUTCForTest aggchainFlow := NewAggchainProverFlow( logger, NewAggchainProverFlowConfigDefault(), - flowBase, + mockParamsBuilder, + mockParamsVerifier, mockStorage, - mockL1InfoTreeDataQuerier, mockL2BridgeQuerier, - mockGERQuerier, - nil, + nil, // l1Client mockSigner, mockOptimistic, mockAggchainProofQuerier, ) mockOptimistic.EXPECT().IsOptimisticModeOn().Return(false, nil).Maybe() - tc.mockFn(mockStorage, mockL2BridgeQuerier, mockAggchainProofQuerier, mockL1InfoTreeDataQuerier, mockGERQuerier) + tc.mockFn(mockStorage, mockL2BridgeQuerier, mockAggchainProofQuerier, mockParamsVerifier, mockParamsBuilder) - params, err := aggchainFlow.GetCertificateBuildParams(ctx) + params, err := aggchainFlow.GetCertificateBuildParams(t.Context()) if tc.expectedError != "" { require.ErrorContains(t, err, tc.expectedError) } else { @@ -377,9 +352,11 @@ func Test_AggchainProverFlow_GetCertificateBuildParams(t *testing.T) { mockStorage.AssertExpectations(t) mockL2BridgeQuerier.AssertExpectations(t) - mockL1InfoTreeDataQuerier.AssertExpectations(t) - mockL1InfoTreeDataQuerier.AssertExpectations(t) mockAggchainProofQuerier.AssertExpectations(t) + mockParamsBuilder.AssertExpectations(t) + mockParamsVerifier.AssertExpectations(t) + mockSigner.AssertExpectations(t) + mockOptimistic.AssertExpectations(t) }) } } @@ -463,22 +440,13 @@ func Test_AggchainProverFlow_getLastProvenBlock(t *testing.T) { t.Parallel() logger := log.WithFields("flowManager", "Test_AggchainProverFlow_GetCertificateBuildParams") - flowBase := NewBaseFlow( - logger, - nil, // l2BridgeQuerier - nil, // sotrage - nil, // l1InfoTreeDataQuerier, - nil, // lerQuerier - NewBaseFlowConfig(0, tc.startL2Block, false), - ) flow := NewAggchainProverFlow( logger, - NewAggchainProverFlowConfigDefault(), - flowBase, + NewAggchainProverFlowConfig(0, tc.startL2Block), + nil, // mockParamsBuilder + nil, // mockParamsVerifier nil, // mockStorage - nil, // mockL1InfoTreeDataQuerier nil, // mockL2BridgeQuerier - nil, // mockGERQuerier nil, // mockOptimistic nil, // mockSigner nil, // optimisticModeQuerier @@ -499,16 +467,15 @@ func Test_AggchainProverFlow_BuildCertificate(t *testing.T) { testCases := []struct { name string - mockFn func(*mocks.BridgeQuerier, *mocks.LERQuerier, *mocks.Signer) + mockFn func(*mocks.CommonCertParamsBuilder, *mocks.Signer) buildParams *types.CertificateBuildParams expectedError string expectedResult *agglayertypes.Certificate }{ { name: "error building certificate", - mockFn: func(mockL2BridgeQuerier *mocks.BridgeQuerier, mockLERQuerier *mocks.LERQuerier, mockSigner *mocks.Signer) { - mockLERQuerier.EXPECT().GetLastLocalExitRoot().Return(emptyLER, nil) - mockL2BridgeQuerier.EXPECT().GetExitRootByIndex(mock.Anything, uint32(0)).Return(common.Hash{}, errors.New("some error")) + mockFn: func(mockParamsBuilder *mocks.CommonCertParamsBuilder, mockSigner *mocks.Signer) { + mockParamsBuilder.EXPECT().BuildCertificate(ctx, mock.Anything, mock.Anything, true).Return(nil, errors.New("build error")).Once() }, buildParams: &types.CertificateBuildParams{ FromBlock: 1, @@ -517,15 +484,23 @@ func Test_AggchainProverFlow_BuildCertificate(t *testing.T) { Claims: []bridgesync.Claim{}, L1InfoTreeRootFromWhichToProve: common.HexToHash("0x1"), }, - expectedError: "error getting exit root by index", + expectedError: "aggchainProverFlow - error building certificate", }, { name: "success building certificate", - mockFn: func(mockL2BridgeQuerier *mocks.BridgeQuerier, mockLERQuerier *mocks.LERQuerier, mockSigner *mocks.Signer) { - mockL2BridgeQuerier.EXPECT().OriginNetwork().Return(uint32(1)) + mockFn: func(mockParamsBuilder *mocks.CommonCertParamsBuilder, mockSigner *mocks.Signer) { + mockParamsBuilder.EXPECT().BuildCertificate(ctx, mock.Anything, mock.Anything, true).Return(&agglayertypes.Certificate{ + NetworkID: 1, + Height: 0, + NewLocalExitRoot: certificatebuild.EmptyLER, + Metadata: types.NewCertificateMetadata(1, 9, uint32(createdAt.Unix()), types.CertificateTypeFEP.ToInt()).ToHash(), + BridgeExits: []*agglayertypes.BridgeExit{}, + ImportedBridgeExits: []*agglayertypes.ImportedBridgeExit{}, + PrevLocalExitRoot: certificatebuild.EmptyLER, + L1InfoTreeLeafCount: 0, + }, nil).Once() mockSigner.EXPECT().PublicAddress().Return(common.HexToAddress("0x123")) mockSigner.EXPECT().SignHash(mock.Anything, mock.Anything).Return([]byte("signature"), nil) - mockLERQuerier.EXPECT().GetLastLocalExitRoot().Return(emptyLER, nil) }, buildParams: &types.CertificateBuildParams{ FromBlock: 1, @@ -554,12 +529,12 @@ func Test_AggchainProverFlow_BuildCertificate(t *testing.T) { expectedResult: &agglayertypes.Certificate{ NetworkID: 1, Height: 0, - NewLocalExitRoot: emptyLER, + NewLocalExitRoot: certificatebuild.EmptyLER, CustomChainData: []byte("some-data"), Metadata: types.NewCertificateMetadata(1, 9, uint32(createdAt.Unix()), types.CertificateTypeFEP.ToInt()).ToHash(), BridgeExits: []*agglayertypes.BridgeExit{}, ImportedBridgeExits: []*agglayertypes.ImportedBridgeExit{}, - PrevLocalExitRoot: emptyLER, + PrevLocalExitRoot: certificatebuild.EmptyLER, L1InfoTreeLeafCount: 0, AggchainData: &agglayertypes.AggchainDataProof{ Proof: []byte("some-proof"), @@ -581,27 +556,18 @@ func Test_AggchainProverFlow_BuildCertificate(t *testing.T) { t.Parallel() logger := log.WithFields("flowManager", "Test_AggchainProverFlow_BuildCertificate") mockSigner := mocks.NewSigner(t) - mockL2BridgeQuerier := mocks.NewBridgeQuerier(t) - mockLERQuerier := mocks.NewLERQuerier(t) + mockParamsBuilder := mocks.NewCommonCertParamsBuilder(t) if tc.mockFn != nil { - tc.mockFn(mockL2BridgeQuerier, mockLERQuerier, mockSigner) + tc.mockFn(mockParamsBuilder, mockSigner) } - flowBase := NewBaseFlow( - logger, - mockL2BridgeQuerier, - nil, // mockStorage - nil, // mockL1InfoTreeDataQuerier - mockLERQuerier, - NewBaseFlowConfigDefault(), - ) + aggchainFlow := NewAggchainProverFlow( logger, NewAggchainProverFlowConfigDefault(), - flowBase, + mockParamsBuilder, + nil, // mockParamsVerifier nil, // mockStorage - nil, // mockL1InfoTreeDataQuerier - mockL2BridgeQuerier, - nil, // mockGERQuerier + nil, // mockL2BridgeQuerier nil, // mockOptimistic mockSigner, nil, // optimisticModeQuerier @@ -616,6 +582,9 @@ func Test_AggchainProverFlow_BuildCertificate(t *testing.T) { require.NotNil(t, certificate) require.Equal(t, tc.expectedResult, certificate) } + + mockSigner.AssertExpectations(t) + mockParamsBuilder.AssertExpectations(t) }) } } @@ -696,9 +665,10 @@ func Test_AggchainProverFlow_CheckInitialStatus(t *testing.T) { testCases := []struct { name string requireNoFEPBlockGap bool + startL2Block uint64 mockFn func( mockStorage *mocks.AggSenderStorage, - mockBaseFlow *mocks.AggsenderFlowBaser, + mockBaseFlow *mocks.CommonCertParamsVerifier, mockL2BridgeSyncer *mocks.BridgeQuerier, ) expectedError string @@ -707,7 +677,7 @@ func Test_AggchainProverFlow_CheckInitialStatus(t *testing.T) { name: "error getting last sent certificate", mockFn: func( mockStorage *mocks.AggSenderStorage, - mockBaseFlow *mocks.AggsenderFlowBaser, + mockBaseFlow *mocks.CommonCertParamsVerifier, mockL2BridgeSyncer *mocks.BridgeQuerier, ) { mockStorage.EXPECT().GetLastSentCertificateHeader().Return(nil, errors.New("db error")).Once() @@ -715,29 +685,29 @@ func Test_AggchainProverFlow_CheckInitialStatus(t *testing.T) { expectedError: "aggchainProverFlow - error getting last sent certificate: db error", }, { - name: "error waiting for syncer to catch up", + name: "error waiting for syncer to catch up", + startL2Block: 15, mockFn: func( mockStorage *mocks.AggSenderStorage, - mockBaseFlow *mocks.AggsenderFlowBaser, + mockBaseFlow *mocks.CommonCertParamsVerifier, mockL2BridgeSyncer *mocks.BridgeQuerier, ) { lastCert := &types.CertificateHeader{ToBlock: 10} mockStorage.EXPECT().GetLastSentCertificateHeader().Return(lastCert, nil).Once() - mockBaseFlow.EXPECT().StartL2Block().Return(uint64(15)).Once() mockL2BridgeSyncer.EXPECT().WaitForSyncerToCatchUp(ctx, uint64(15)).Return(errors.New("sync error")).Once() }, expectedError: "aggchainProverFlow - error waiting for syncer to catch up: sync error", }, { - name: "error verifying block range gaps - has bridge transactions in gap", + name: "error verifying block range gaps - has bridge transactions in gap", + startL2Block: 15, mockFn: func( mockStorage *mocks.AggSenderStorage, - mockBaseFlow *mocks.AggsenderFlowBaser, + mockBaseFlow *mocks.CommonCertParamsVerifier, mockL2BridgeSyncer *mocks.BridgeQuerier, ) { lastCert := &types.CertificateHeader{ToBlock: 10} mockStorage.EXPECT().GetLastSentCertificateHeader().Return(lastCert, nil).Once() - mockBaseFlow.EXPECT().StartL2Block().Return(uint64(15)).Once() mockL2BridgeSyncer.EXPECT().WaitForSyncerToCatchUp(ctx, uint64(15)).Return(nil).Once() mockBaseFlow.EXPECT().VerifyBlockRangeGaps(ctx, lastCert, uint64(15), uint64(15)). Return(errors.New("gap error")).Once() @@ -746,15 +716,15 @@ func Test_AggchainProverFlow_CheckInitialStatus(t *testing.T) { }, { name: "success ", + startL2Block: 11, requireNoFEPBlockGap: true, mockFn: func( mockStorage *mocks.AggSenderStorage, - mockBaseFlow *mocks.AggsenderFlowBaser, + mockBaseFlow *mocks.CommonCertParamsVerifier, mockL2BridgeSyncer *mocks.BridgeQuerier, ) { lastCert := &types.CertificateHeader{ToBlock: 10} mockStorage.EXPECT().GetLastSentCertificateHeader().Return(lastCert, nil).Once() - mockBaseFlow.EXPECT().StartL2Block().Return(uint64(11)).Once() mockL2BridgeSyncer.EXPECT().WaitForSyncerToCatchUp(ctx, uint64(11)).Return(nil).Once() mockBaseFlow.EXPECT().VerifyBlockRangeGaps(ctx, lastCert, uint64(11), uint64(11)). Return(nil).Once() @@ -767,18 +737,19 @@ func Test_AggchainProverFlow_CheckInitialStatus(t *testing.T) { t.Run(tc.name, func(t *testing.T) { t.Parallel() mockStorage := mocks.NewAggSenderStorage(t) - mockBaseFlow := mocks.NewAggsenderFlowBaser(t) + mockParamsVerifier := mocks.NewCommonCertParamsVerifier(t) mockL2BridgeSyncer := mocks.NewBridgeQuerier(t) logger := log.WithFields("flowManager", "Test_AggchainProverFlow_CheckInitialStatus") flow := &AggchainProverFlow{ - log: logger, - storage: mockStorage, - baseFlow: mockBaseFlow, - l2BridgeQuerier: mockL2BridgeSyncer, + log: logger, + storage: mockStorage, + commonParamsVerifier: mockParamsVerifier, + l2BridgeQuerier: mockL2BridgeSyncer, + cfg: NewAggchainProverFlowConfig(0, tc.startL2Block), } - tc.mockFn(mockStorage, mockBaseFlow, mockL2BridgeSyncer) + tc.mockFn(mockStorage, mockParamsVerifier, mockL2BridgeSyncer) err := flow.CheckInitialStatus(ctx) if tc.expectedError != "" { @@ -788,7 +759,7 @@ func Test_AggchainProverFlow_CheckInitialStatus(t *testing.T) { } mockStorage.AssertExpectations(t) - mockBaseFlow.AssertExpectations(t) + mockParamsVerifier.AssertExpectations(t) mockL2BridgeSyncer.AssertExpectations(t) }) } diff --git a/aggsender/flows/flow_base.go b/aggsender/flows/flow_base.go deleted file mode 100644 index d05a5ce6e..000000000 --- a/aggsender/flows/flow_base.go +++ /dev/null @@ -1,514 +0,0 @@ -package flows - -import ( - "context" - "errors" - "fmt" - "time" - - agglayertypes "github.com/agglayer/aggkit/agglayer/types" - "github.com/agglayer/aggkit/aggsender/converters" - "github.com/agglayer/aggkit/aggsender/db" - "github.com/agglayer/aggkit/aggsender/types" - "github.com/agglayer/aggkit/bridgesync" - aggkitcommon "github.com/agglayer/aggkit/common" - "github.com/ethereum/go-ethereum/common" - "golang.org/x/crypto/sha3" -) - -var ( - errNoBridgesAndClaims = errors.New("no bridges and claims to build certificate") - errNoNewBlocks = errors.New("no new blocks to send a certificate") - - emptyLER = common.HexToHash("0x27ae5ba08d7291c96c8cbddcc148bf48a6d68c7974b94356f53754ef6171d757") -) - -// TimeNowUTC returns the current time in UTC as a uint32 timestamp. -func TimeNowUTC() uint32 { - // Use a more precise time function to avoid collisions in tests - // and ensure that the time is always in UTC. - return uint32(time.Now().UTC().Unix()) -} - -// BaseFlowConfig is a struct that holds the configuration for the base flow -type BaseFlowConfig struct { - // MaxCertSize is the maximum size of the certificate in bytes. 0 means no limit - MaxCertSize uint - // StartL2Block is the L2 block number from which to start sending certificates. - // It is used to determine the first block to include in the certificate. - // It can be 0 - StartL2Block uint64 - // RequireNoFEPBlockGap indicates whether the flow requires no gap between the - // first FEP block and last settled certificate. - RequireNoFEPBlockGap bool -} - -// NewBaseFlowConfigDefault returns a BaseFlowConfig with default values -func NewBaseFlowConfigDefault() BaseFlowConfig { - return BaseFlowConfig{ - MaxCertSize: 0, // 0 means no limit - StartL2Block: 0, // 0 means start from the first block - RequireNoFEPBlockGap: false, // default is false, can be set to true if needed - } -} - -// NewBaseFlowConfig returns a BaseFlowConfig with the specified maxCertSize and startL2Block -func NewBaseFlowConfig(maxCertSize uint, startL2Block uint64, requireNoFEPBlockGap bool) BaseFlowConfig { - return BaseFlowConfig{ - MaxCertSize: maxCertSize, - StartL2Block: startL2Block, - RequireNoFEPBlockGap: requireNoFEPBlockGap, - } -} - -// baseFlow is a struct that holds the common logic for the different prover types -type baseFlow struct { - l2BridgeQuerier types.BridgeQuerier - storage db.AggSenderStorage - l1InfoTreeDataQuerier types.L1InfoTreeDataQuerier - lerQuerier types.LERQuerier - cfg BaseFlowConfig - log types.Logger - // TimeNowFunc is a function that returns the current time as a uint32 timestamp. - timeNowFunc func() uint32 -} - -// NewBaseFlow creates a new instance of the base flow -func NewBaseFlow( - log types.Logger, - l2BridgeQuerier types.BridgeQuerier, - storage db.AggSenderStorage, - l1InfoTreeDataQuerier types.L1InfoTreeDataQuerier, - lerQuerier types.LERQuerier, - cfg BaseFlowConfig, -) *baseFlow { - return &baseFlow{ - log: log, - l2BridgeQuerier: l2BridgeQuerier, - storage: storage, - l1InfoTreeDataQuerier: l1InfoTreeDataQuerier, - lerQuerier: lerQuerier, - cfg: cfg, - timeNowFunc: TimeNowUTC, - } -} - -// StartL2Block returns the L2 block number from which to start sending certificates. -func (f *baseFlow) StartL2Block() uint64 { - return f.cfg.StartL2Block -} - -// NextCertificateBlockRange returns the block range and retryCount for the next certificate -func (f *baseFlow) NextCertificateBlockRange(ctx context.Context, - lastSentCertificate *types.CertificateHeader) (types.BlockRange, int, error) { - lastL2BlockSynced, err := f.l2BridgeQuerier.GetLastProcessedBlock(ctx) - if err != nil { - return types.BlockRangeZero, 0, fmt.Errorf("error getting last processed block from l2: %w", err) - } - - previousToBlock, retryCount := f.getLastSentBlockAndRetryCount(lastSentCertificate) - if previousToBlock >= lastL2BlockSynced { - f.log.Warnf("no new blocks to send a certificate, last certificate block: %d, last L2 block: %d", - previousToBlock, lastL2BlockSynced) - return types.BlockRangeZero, 0, errNoNewBlocks - } - fromBlock := previousToBlock + 1 - toBlock := lastL2BlockSynced - return types.NewBlockRange(fromBlock, toBlock), retryCount, nil -} - -// GetLastCertificate returns latest certificate in local database -func (f *baseFlow) GetLastCertificate(ctx context.Context) (*types.CertificateHeader, error) { - lastSentCertificate, err := f.storage.GetLastSentCertificateHeader() - if err != nil { - return nil, fmt.Errorf("fails to GetLastCertificate. Err: %w", err) - } - return lastSentCertificate, nil -} - -func (f *baseFlow) GeneratePreBuildParams(ctx context.Context, - certType types.CertificateType) (*types.CertificatePreBuildParams, error) { - lastSentCertificate, err := f.GetLastCertificate(ctx) - if err != nil { - return nil, fmt.Errorf("error getting last sent certificate: %w", err) - } - - nextBlocks, retryCount, err := f.NextCertificateBlockRange(ctx, lastSentCertificate) - if err != nil { - return nil, fmt.Errorf("error getting next certificate block range: %w", err) - } - l1InfoRoot, _, err := f.l1InfoTreeDataQuerier.GetLatestFinalizedL1InfoRoot(ctx) - if err != nil { - return nil, fmt.Errorf("error getting latest finalized L1 info root: %w", err) - } - - return &types.CertificatePreBuildParams{ - BlockRange: nextBlocks, - RetryCount: retryCount, - LastSentCertificate: lastSentCertificate, - CertificateType: certType, - L1InfoTreeToProve: &types.CertificateL1InfoTreeData{ - L1InfoTreeRootToProve: l1InfoRoot.Hash, - L1InfoTreeLeafCount: l1InfoRoot.Index + 1, - }, - CreatedAt: f.timeNowFunc(), - }, nil -} - -func (f *baseFlow) GenerateBuildParams(ctx context.Context, - preParams types.CertificatePreBuildParams) (*types.CertificateBuildParams, error) { - if preParams.L1InfoTreeToProve == nil { - return nil, fmt.Errorf("L1InfoTreeWhichToProve should be not nil for GenerateBuildParams") - } - - bridges, claims, err := f.l2BridgeQuerier.GetBridgesAndClaims(ctx, - preParams.BlockRange.FromBlock, preParams.BlockRange.ToBlock) - if err != nil { - return nil, fmt.Errorf("generateBulidParams fails getting bridges and claims. Err: %w", err) - } - - buildParams := &types.CertificateBuildParams{ - FromBlock: preParams.BlockRange.FromBlock, - ToBlock: preParams.BlockRange.ToBlock, - RetryCount: preParams.RetryCount, - LastSentCertificate: preParams.LastSentCertificate, - Bridges: bridges, - Claims: claims, - CreatedAt: preParams.CreatedAt, - CertificateType: preParams.CertificateType, - L1InfoTreeRootFromWhichToProve: preParams.L1InfoTreeToProve.L1InfoTreeRootToProve, - L1InfoTreeLeafCount: preParams.L1InfoTreeToProve.L1InfoTreeLeafCount, - } - return buildParams, nil -} - -// GetCertificateBuildParamsInternal returns the parameters to build a certificate -func (f *baseFlow) GetCertificateBuildParamsInternal( - ctx context.Context, certType types.CertificateType) (*types.CertificateBuildParams, error) { - preParams, err := f.GeneratePreBuildParams(ctx, certType) - if err != nil { - return nil, fmt.Errorf("error generating pre build params: %w", err) - } - params, err := f.GenerateBuildParams(ctx, *preParams) - if err != nil { - return nil, fmt.Errorf("error generating build params: %w", err) - } - params, err = f.LimitCertSize(params) - if err != nil { - return nil, fmt.Errorf("error applying limit size: %w", err) - } - return params, nil -} - -// VerifyBuildParams verifies the build parameters -func (f *baseFlow) VerifyBuildParams(ctx context.Context, fullCert *types.CertificateBuildParams) error { - if err := f.verifyRetryCertStartingBlock(fullCert); err != nil { - return fmt.Errorf("error verifying retry certificate starting block: %w", err) - } - - if err := f.verifyClaimGERs(fullCert.Claims); err != nil { - return err - } - - return nil -} - -// LimitCertSize limits certificate size based on the max size configuration parameter -// size is expressed in bytes -func (f *baseFlow) LimitCertSize( - certParams *types.CertificateBuildParams) (*types.CertificateBuildParams, error) { - currentCert := certParams - var err error - maxCertSize := f.cfg.MaxCertSize - for { - if maxCertSize == 0 || currentCert.EstimatedSize() <= maxCertSize { - return currentCert, nil - } - - if currentCert.NumberOfBlocks() <= 1 { - f.log.Warnf("Minimum number of blocks reached [%d to %d]. Estimated size: %d > max size: %d", - currentCert.FromBlock, currentCert.ToBlock, currentCert.EstimatedSize(), maxCertSize) - return currentCert, nil - } - - currentCert, err = currentCert.Range(currentCert.FromBlock, currentCert.ToBlock-1) - if err != nil { - return nil, fmt.Errorf("error reducing certificate: %w", err) - } - } -} - -// GetNewLocalExitRoot gets the new local exit root for the certificate -func (f *baseFlow) GetNewLocalExitRoot(ctx context.Context, - certParams *types.CertificateBuildParams) (common.Hash, error) { - if certParams == nil { - return common.Hash{}, fmt.Errorf("baseFlow.GetNewLocalExitRoot. certificate build parameters cannot be nil") - } - _, previousLER, err := f.getNextHeightAndPreviousLER(certParams.LastSentCertificate) - if err != nil { - return common.Hash{}, fmt.Errorf("baseFlow.GetNewLocalExitRoot. error getting next height and previous LER: %w", err) - } - - newLER, err := f.getNewLocalExitRoot(ctx, certParams, previousLER) - if err != nil { - return common.Hash{}, fmt.Errorf("baseFlow.GetNewLocalExitRoot. error getting new local exit root: %w", err) - } - return newLER, nil -} - -func (f *baseFlow) BuildCertificate(ctx context.Context, - certParams *types.CertificateBuildParams, - lastSentCertificate *types.CertificateHeader, - allowEmptyCert bool) (*agglayertypes.Certificate, error) { - f.log.Infof("building certificate for %s estimatedSize=%d", certParams.String(), certParams.EstimatedSize()) - - if !allowEmptyCert && certParams.IsEmpty() { - return nil, errNoBridgesAndClaims - } - - bridgeExits := f.getBridgeExits(certParams.Bridges) - importedBridgeExits, err := f.getImportedBridgeExits(ctx, certParams.Claims, certParams.L1InfoTreeRootFromWhichToProve) - if err != nil { - return nil, fmt.Errorf("error getting imported bridge exits: %w", err) - } - - height, previousLER, err := f.getNextHeightAndPreviousLER(lastSentCertificate) - if err != nil { - return nil, fmt.Errorf("error getting next height and previous LER: %w", err) - } - - newLER, err := f.getNewLocalExitRoot(ctx, certParams, previousLER) - if err != nil { - return nil, fmt.Errorf("error getting new local exit root: %w", err) - } - - meta := types.NewCertificateMetadata( - certParams.FromBlock, - uint32(certParams.ToBlock-certParams.FromBlock), - certParams.CreatedAt, - certParams.CertificateType.ToInt(), - ) - - return &agglayertypes.Certificate{ - NetworkID: f.l2BridgeQuerier.OriginNetwork(), - PrevLocalExitRoot: previousLER, - NewLocalExitRoot: newLER, - BridgeExits: bridgeExits, - ImportedBridgeExits: importedBridgeExits, - Height: height, - Metadata: meta.ToHash(), - L1InfoTreeLeafCount: certParams.L1InfoTreeLeafCount, - }, nil -} - -// getNewLocalExitRoot gets the new local exit root for the certificate -func (f *baseFlow) getNewLocalExitRoot( - ctx context.Context, - certParams *types.CertificateBuildParams, - previousLER common.Hash) (common.Hash, error) { - if certParams.NumberOfBridges() == 0 { - // if there is no bridge exits we return the previous LER - // since there was no change in the local exit root - return previousLER, nil - } - - depositCount := certParams.MaxDepositCount() - - exitRoot, err := f.l2BridgeQuerier.GetExitRootByIndex(ctx, depositCount) - if err != nil { - return common.Hash{}, fmt.Errorf("error getting exit root by index: %d. Error: %w", depositCount, err) - } - - return exitRoot, nil -} - -// ConvertClaimToImportedBridgeExit converts a claim to an ImportedBridgeExit object -func (f *baseFlow) ConvertClaimToImportedBridgeExit(claim bridgesync.Claim) (*agglayertypes.ImportedBridgeExit, error) { - return converters.ConvertToImportedBridgeExitWithoutClaimData(claim) -} - -// getBridgeExits converts bridges to agglayer.BridgeExit objects -func (f *baseFlow) getBridgeExits(bridges []bridgesync.Bridge) []*agglayertypes.BridgeExit { - return converters.ConvertToBridgeExits(bridges) -} - -// getImportedBridgeExits converts claims to agglayertypes.ImportedBridgeExit objects and calculates necessary proofs -func (f *baseFlow) getImportedBridgeExits( - ctx context.Context, claims []bridgesync.Claim, - rootFromWhichToProve common.Hash, -) ([]*agglayertypes.ImportedBridgeExit, error) { - return converters.ConvertToImportedBridgeExits( - ctx, claims, rootFromWhichToProve, f.l1InfoTreeDataQuerier) -} - -// getStartLER returns the last local exit root (LER) based on the configuration -func (f *baseFlow) getStartLER() (common.Hash, error) { - ler, err := f.lerQuerier.GetLastLocalExitRoot() - if err != nil { - return common.Hash{}, fmt.Errorf("error getting last local exit root: %w", err) - } - - if ler == aggkitcommon.ZeroHash { - return emptyLER, nil - } - - return ler, nil -} - -// getNextHeightAndPreviousLER returns the height and previous LER for the new certificate -func (f *baseFlow) getNextHeightAndPreviousLER( - lastSentCertificateInfo *types.CertificateHeader) (uint64, common.Hash, error) { - if lastSentCertificateInfo == nil { - ler, err := f.getStartLER() - return uint64(0), ler, err - } - if !lastSentCertificateInfo.Status.IsClosed() { - return 0, aggkitcommon.ZeroHash, fmt.Errorf("last certificate %s is not closed (status: %s)", - lastSentCertificateInfo.ID(), lastSentCertificateInfo.Status.String()) - } - if lastSentCertificateInfo.Status.IsSettled() { - return lastSentCertificateInfo.Height + 1, lastSentCertificateInfo.NewLocalExitRoot, nil - } - - if lastSentCertificateInfo.Status.IsInError() { - // We can reuse last one of lastCert? - if lastSentCertificateInfo.PreviousLocalExitRoot != nil { - return lastSentCertificateInfo.Height, *lastSentCertificateInfo.PreviousLocalExitRoot, nil - } - // Is the first one, so we can set the zeroLER - if lastSentCertificateInfo.Height == 0 { - ler, err := f.getStartLER() - return uint64(0), ler, err - } - // We get previous certificate that must be settled - f.log.Debugf("last certificate %s is in error, getting previous settled certificate height:%d", - lastSentCertificateInfo.Height-1) - lastSettleCert, err := f.storage.GetCertificateHeaderByHeight(lastSentCertificateInfo.Height - 1) - if err != nil { - return 0, aggkitcommon.ZeroHash, fmt.Errorf("error getting last settled certificate: %w", err) - } - if lastSettleCert == nil { - return 0, aggkitcommon.ZeroHash, fmt.Errorf("none settled certificate: %w", err) - } - if !lastSettleCert.Status.IsSettled() { - return 0, aggkitcommon.ZeroHash, fmt.Errorf("last settled certificate %s is not settled (status: %s)", - lastSettleCert.ID(), lastSettleCert.Status.String()) - } - - return lastSentCertificateInfo.Height, lastSettleCert.NewLocalExitRoot, nil - } - return 0, aggkitcommon.ZeroHash, fmt.Errorf("last certificate %s has an unknown status: %s", - lastSentCertificateInfo.ID(), lastSentCertificateInfo.Status.String()) -} - -// verifyClaimGERs verifies the correctnes GERs of the claims -func (f *baseFlow) verifyClaimGERs(claims []bridgesync.Claim) error { - for _, claim := range claims { - ger := calculateGER(claim.MainnetExitRoot, claim.RollupExitRoot) - if ger != claim.GlobalExitRoot { - return fmt.Errorf("claim[GlobalIndex: %s, BlockNum: %d]: GER mismatch. Expected: %s, got: %s", - claim.GlobalIndex.String(), claim.BlockNum, claim.GlobalExitRoot.String(), ger.String()) - } - } - - return nil -} - -// verifyRetryCertStartingBlock verifies that the starting block of a retry certificate -// matches the last sent (InError) certificate's starting block. -func (f *baseFlow) verifyRetryCertStartingBlock(buildParams *types.CertificateBuildParams) error { - if buildParams.IsARetry() && buildParams.FromBlock != buildParams.LastSentCertificate.FromBlock { - return fmt.Errorf("retry certificate fromBlock %d != last sent certificate fromBlock %d", - buildParams.FromBlock, buildParams.LastSentCertificate.FromBlock) - } - - return nil -} - -// VerifyBlockRangeGaps checks if there are any gaps in the block range of the certificate -// and verifies that there are no new bridges or claims in the gap. -func (f *baseFlow) VerifyBlockRangeGaps( - ctx context.Context, - lastSentCertificate *types.CertificateHeader, - newFromBlock, newToBlock uint64) error { - if lastSentCertificate == nil { - return nil - } - - lastSettledFromBlock := uint64(0) - lastSettledToBlock := uint64(0) - if lastSentCertificate.Status.IsInError() { - // if the last certificate was in error, we need to check the last - // settled range to be correct - // we will leave the from block as 0, since we only require the to block - // to check the gap between the last sent certificate and the new one - if lastSentCertificate.FromBlock > 0 { - lastSettledToBlock = lastSentCertificate.FromBlock - 1 - } - } else { - lastSettledFromBlock = lastSentCertificate.FromBlock - lastSettledToBlock = lastSentCertificate.ToBlock - } - - nextBlockRange := types.NewBlockRange(newFromBlock, newToBlock) - lastBlockRange := types.NewBlockRange(lastSettledFromBlock, lastSettledToBlock) - - // case 2: is a new cert but is not contiguous to previous one - gap := nextBlockRange.Gap(lastBlockRange) - if gap.IsEmpty() { - return nil - } - - bridgeDataInTheGap, claimDataInTheGap, err := f.l2BridgeQuerier.GetBridgesAndClaims( - ctx, gap.FromBlock, gap.ToBlock) - if err != nil { - return fmt.Errorf("error getting bridges and claims in the gap %s: %w", gap.String(), err) - } - if len(bridgeDataInTheGap) > 0 || len(claimDataInTheGap) > 0 { - return fmt.Errorf("there are new bridges or claims in the gap %s, len(bridges)=%d. len(claims)=%d", - gap.String(), len(bridgeDataInTheGap), len(claimDataInTheGap)) - } - - if !gap.IsEmpty() && f.cfg.RequireNoFEPBlockGap { - // even though we do not have bridge transactions in the gap, - // we need to return an error if RequireNoFEPBlockGap is true - return fmt.Errorf("block gap detected: %s without bridge transactions, but RequireNoFEPBlockGap is true", - gap.String()) - } - - return nil -} - -// getLastSentBlockAndRetryCount returns the last sent block of the last sent certificate -// if there is no previosly sent certificate, it returns startL2Block and 0 -func (f *baseFlow) getLastSentBlockAndRetryCount(lastSentCertificateInfo *types.CertificateHeader) (uint64, int) { - if lastSentCertificateInfo == nil { - // this is the first certificate so we start from what we have set in start L2 block - return f.StartL2Block(), 0 - } - - retryCount := 0 - lastSentBlock := lastSentCertificateInfo.ToBlock - - if lastSentCertificateInfo.Status == agglayertypes.InError { - // if the last certificate was in error, we need to resend it - // from the block before the error - if lastSentCertificateInfo.FromBlock > 0 { - lastSentBlock = lastSentCertificateInfo.FromBlock - 1 - } - - retryCount = lastSentCertificateInfo.RetryCount + 1 - } - return lastSentBlock, retryCount -} - -// calculateGER calculates the GER hash based on the mainnet exit root and the rollup exit root -func calculateGER(mainnetExitRoot, rollupExitRoot common.Hash) common.Hash { - var gerBytes [common.HashLength]byte - hasher := sha3.NewLegacyKeccak256() - hasher.Write(mainnetExitRoot.Bytes()) - hasher.Write(rollupExitRoot.Bytes()) - copy(gerBytes[:], hasher.Sum(nil)) - - return gerBytes -} diff --git a/aggsender/flows/flow_pp.go b/aggsender/flows/flow_pp.go index bd02e9153..7f3a4729d 100644 --- a/aggsender/flows/flow_pp.go +++ b/aggsender/flows/flow_pp.go @@ -6,6 +6,7 @@ import ( "fmt" agglayertypes "github.com/agglayer/aggkit/agglayer/types" + "github.com/agglayer/aggkit/aggsender/certificatebuild" "github.com/agglayer/aggkit/aggsender/db" "github.com/agglayer/aggkit/aggsender/types" signertypes "github.com/agglayer/go_signer/signer/types" @@ -14,18 +15,21 @@ import ( // PPFlow is a struct that holds the logic for the regular pessimistic proof flow type PPFlow struct { - baseFlow types.AggsenderFlowBaser signer signertypes.Signer log types.Logger l1InfoTreeDataQuerier types.L1InfoTreeDataQuerier forceOneBridgeExit bool maxL2BlockLimiter types.MaxL2BlockNumberLimiterInterface + + commonParamsBuilder types.CommonCertParamsBuilder + commonParamsVerifier types.CommonCertParamsVerifier } // NewPPFlow returns a new instance of the PPFlow func NewPPFlow(log types.Logger, - baseFlow types.AggsenderFlowBaser, + commonParamsBuilder types.CommonCertParamsBuilder, + commonParamsVerifier types.CommonCertParamsVerifier, storage db.AggSenderStorage, l1InfoTreeQuerier types.L1InfoTreeDataQuerier, l2BridgeQuerier types.BridgeQuerier, @@ -39,10 +43,11 @@ func NewPPFlow(log types.Logger, forceOneBridgeExit, ) return &PPFlow{ + commonParamsBuilder: commonParamsBuilder, + commonParamsVerifier: commonParamsVerifier, signer: signer, log: log, l1InfoTreeDataQuerier: l1InfoTreeQuerier, - baseFlow: baseFlow, forceOneBridgeExit: forceOneBridgeExit, maxL2BlockLimiter: feature, } @@ -55,7 +60,7 @@ func (p *PPFlow) CheckInitialStatus(ctx context.Context) error { } func (p *PPFlow) GeneratePreBuildParams(ctx context.Context) (*types.CertificatePreBuildParams, error) { - return p.baseFlow.GeneratePreBuildParams(ctx, types.CertificateTypePP) + return p.commonParamsBuilder.GeneratePreBuildParams(ctx, types.CertificateTypePP) } func (p *PPFlow) GenerateBuildParams(ctx context.Context, @@ -63,11 +68,11 @@ func (p *PPFlow) GenerateBuildParams(ctx context.Context, if preParams == nil { return nil, fmt.Errorf("ppFlow - preParams is nil") } - params, err := p.baseFlow.GenerateBuildParams(ctx, *preParams) + params, err := p.commonParamsBuilder.GenerateBuildParams(ctx, *preParams) if err != nil { return nil, fmt.Errorf("ppFlow - error generating build params: %w", err) } - params, err = p.baseFlow.LimitCertSize(params) + params, err = p.commonParamsBuilder.LimitCertSize(params) if err != nil { return nil, fmt.Errorf("error applying limit size: %w", err) } @@ -77,9 +82,9 @@ func (p *PPFlow) GenerateBuildParams(ctx context.Context, // GetCertificateBuildParams returns the parameters to build a certificate // this function is the implementation of the FlowManager interface func (p *PPFlow) GetCertificateBuildParams(ctx context.Context) (*types.CertificateBuildParams, error) { - buildParams, err := p.baseFlow.GetCertificateBuildParamsInternal(ctx, types.CertificateTypePP) + buildParams, err := p.commonParamsBuilder.GetCommonCertificateBuildParams(ctx, types.CertificateTypePP) if err != nil { - if errors.Is(err, errNoNewBlocks) { + if errors.Is(err, certificatebuild.ErrNoNewBlocks) { // no new blocks to send a certificate, // this is a valid case, so just return nil without error return nil, nil @@ -101,6 +106,7 @@ func (p *PPFlow) GetCertificateBuildParams(ctx context.Context) (*types.Certific buildParams.FromBlock, buildParams.ToBlock) return nil, nil } + if p.maxL2BlockLimiter != nil { // If the feature is enabled, we need to adapt the build params buildParams, err = p.maxL2BlockLimiter.AdaptCertificate(buildParams) @@ -109,9 +115,10 @@ func (p *PPFlow) GetCertificateBuildParams(ctx context.Context) (*types.Certific } } - if err := p.baseFlow.VerifyBuildParams(ctx, buildParams); err != nil { + if err := p.commonParamsVerifier.VerifyBuildParams(ctx, buildParams); err != nil { return nil, fmt.Errorf("ppFlow - error verifying build params: %w", err) } + return buildParams, nil } @@ -119,7 +126,7 @@ func (p *PPFlow) GetCertificateBuildParams(ctx context.Context) (*types.Certific // this function is the implementation of the FlowManager interface func (p *PPFlow) BuildCertificate(ctx context.Context, buildParams *types.CertificateBuildParams) (*agglayertypes.Certificate, error) { - certificate, err := p.baseFlow.BuildCertificate(ctx, buildParams, buildParams.LastSentCertificate, false) + certificate, err := p.commonParamsBuilder.BuildCertificate(ctx, buildParams, buildParams.LastSentCertificate, false) if err != nil { return nil, fmt.Errorf("ppFlow - error building certificate: %w", err) } diff --git a/aggsender/flows/flow_pp_test.go b/aggsender/flows/flow_pp_test.go index df609c528..a2d13747a 100644 --- a/aggsender/flows/flow_pp_test.go +++ b/aggsender/flows/flow_pp_test.go @@ -1,446 +1,117 @@ package flows import ( - "context" "errors" - "fmt" - "math/big" "testing" agglayertypes "github.com/agglayer/aggkit/agglayer/types" + "github.com/agglayer/aggkit/aggsender/certificatebuild" "github.com/agglayer/aggkit/aggsender/mocks" "github.com/agglayer/aggkit/aggsender/types" "github.com/agglayer/aggkit/bridgesync" - "github.com/agglayer/aggkit/l1infotreesync" "github.com/agglayer/aggkit/log" - treetypes "github.com/agglayer/aggkit/tree/types" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/crypto" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" ) -func timeNowUTCForTest() uint32 { - return uint32(12345) -} - -func TestBuildCertificate(t *testing.T) { - mockL2BridgeQuerier := mocks.NewBridgeQuerier(t) - mockL1InfoTreeQuerier := mocks.NewL1InfoTreeDataQuerier(t) - mockProof := generateTestProof(t) - - tests := []struct { - name string - bridges []bridgesync.Bridge - claims []bridgesync.Claim - lastSentCertificate types.CertificateHeader - fromBlock uint64 - toBlock uint64 - mockFn func() - expectedCert *agglayertypes.Certificate - expectedError bool - }{ - { - name: "Valid certificate with bridges and claims", - bridges: []bridgesync.Bridge{ - { - LeafType: agglayertypes.LeafTypeAsset.Uint8(), - OriginNetwork: 1, - OriginAddress: common.HexToAddress("0x123"), - DestinationNetwork: 2, - DestinationAddress: common.HexToAddress("0x456"), - Amount: big.NewInt(100), - Metadata: []byte("metadata"), - DepositCount: 1, - }, - }, - claims: []bridgesync.Claim{ - { - IsMessage: false, - OriginNetwork: 1, - OriginAddress: common.HexToAddress("0x1234"), - DestinationNetwork: 2, - DestinationAddress: common.HexToAddress("0x4567"), - Amount: big.NewInt(111), - Metadata: []byte("metadata1"), - GlobalIndex: big.NewInt(1), - GlobalExitRoot: common.HexToHash("0x7891"), - RollupExitRoot: common.HexToHash("0xaaab"), - MainnetExitRoot: common.HexToHash("0xbbba"), - ProofLocalExitRoot: mockProof, - ProofRollupExitRoot: mockProof, - }, - }, - lastSentCertificate: types.CertificateHeader{ - NewLocalExitRoot: common.HexToHash("0x123"), - Height: 1, - Status: agglayertypes.Settled, - }, - fromBlock: 0, - toBlock: 10, - expectedCert: &agglayertypes.Certificate{ - NetworkID: 1, - PrevLocalExitRoot: common.HexToHash("0x123"), - NewLocalExitRoot: common.HexToHash("0x789"), - Metadata: types.NewCertificateMetadata(0, 10, 0, types.CertificateTypePP.ToInt()).ToHash(), - BridgeExits: []*agglayertypes.BridgeExit{ - { - LeafType: agglayertypes.LeafTypeAsset, - TokenInfo: &agglayertypes.TokenInfo{ - OriginNetwork: 1, - OriginTokenAddress: common.HexToAddress("0x123"), - }, - DestinationNetwork: 2, - DestinationAddress: common.HexToAddress("0x456"), - Amount: big.NewInt(100), - Metadata: crypto.Keccak256([]byte("metadata")), - }, - }, - ImportedBridgeExits: []*agglayertypes.ImportedBridgeExit{ - { - BridgeExit: &agglayertypes.BridgeExit{ - LeafType: agglayertypes.LeafTypeAsset, - TokenInfo: &agglayertypes.TokenInfo{ - OriginNetwork: 1, - OriginTokenAddress: common.HexToAddress("0x1234"), - }, - DestinationNetwork: 2, - DestinationAddress: common.HexToAddress("0x4567"), - Amount: big.NewInt(111), - Metadata: crypto.Keccak256([]byte("metadata1")), - }, - GlobalIndex: &agglayertypes.GlobalIndex{ - MainnetFlag: false, - RollupIndex: 0, - LeafIndex: 1, - }, - ClaimData: &agglayertypes.ClaimFromRollup{ - L1Leaf: &agglayertypes.L1InfoTreeLeaf{ - L1InfoTreeIndex: 1, - RollupExitRoot: common.HexToHash("0xaaab"), - MainnetExitRoot: common.HexToHash("0xbbba"), - Inner: &agglayertypes.L1InfoTreeLeafInner{ - GlobalExitRoot: common.HexToHash("0x7891"), - Timestamp: 123456789, - BlockHash: common.HexToHash("0xabc"), - }, - }, - ProofLeafLER: &agglayertypes.MerkleProof{ - Root: common.HexToHash("0xc52019815b51acf67a715cae6794a20083d63fd9af45783b7adf69123dae92c8"), - Proof: mockProof, - }, - ProofLERToRER: &agglayertypes.MerkleProof{ - Root: common.HexToHash("0xaaab"), - Proof: mockProof, - }, - ProofGERToL1Root: &agglayertypes.MerkleProof{ - Root: common.HexToHash("0x7891"), - Proof: mockProof, - }, - }, - }, - }, - Height: 2, - }, - mockFn: func() { - mockL2BridgeQuerier.EXPECT().OriginNetwork().Return(uint32(1)) - mockL2BridgeQuerier.EXPECT().GetExitRootByIndex(mock.Anything, mock.Anything).Return(common.HexToHash("0x789"), nil) - mockL1InfoTreeQuerier.EXPECT().GetProofForGER(mock.Anything, mock.Anything, mock.Anything).Return(&l1infotreesync.L1InfoTreeLeaf{ - L1InfoTreeIndex: 1, - Timestamp: 123456789, - PreviousBlockHash: common.HexToHash("0xabc"), - GlobalExitRoot: common.HexToHash("0x7891"), - }, mockProof, nil) - }, - expectedError: false, - }, - { - name: "No bridges or claims", - bridges: []bridgesync.Bridge{}, - claims: []bridgesync.Claim{}, - lastSentCertificate: types.CertificateHeader{ - NewLocalExitRoot: common.HexToHash("0x123"), - Height: 1, - }, - expectedCert: nil, - expectedError: true, - }, - { - name: "Error getting imported bridge exits", - bridges: []bridgesync.Bridge{ - { - LeafType: agglayertypes.LeafTypeAsset.Uint8(), - OriginNetwork: 1, - OriginAddress: common.HexToAddress("0x123"), - DestinationNetwork: 2, - DestinationAddress: common.HexToAddress("0x456"), - Amount: big.NewInt(100), - Metadata: []byte("metadata"), - DepositCount: 1, - }, - }, - claims: []bridgesync.Claim{ - { - IsMessage: false, - OriginNetwork: 1, - OriginAddress: common.HexToAddress("0x1234"), - DestinationNetwork: 2, - DestinationAddress: common.HexToAddress("0x4567"), - Amount: big.NewInt(111), - Metadata: []byte("metadata1"), - GlobalIndex: new(big.Int).SetBytes([]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}), - GlobalExitRoot: common.HexToHash("0x7891"), - RollupExitRoot: common.HexToHash("0xaaab"), - MainnetExitRoot: common.HexToHash("0xbbba"), - ProofLocalExitRoot: mockProof, - }, - }, - lastSentCertificate: types.CertificateHeader{ - NewLocalExitRoot: common.HexToHash("0x123"), - Height: 1, - }, - mockFn: func() { - mockL1InfoTreeQuerier.EXPECT().GetProofForGER(mock.Anything, mock.Anything, mock.Anything).Return(&l1infotreesync.L1InfoTreeLeaf{ - L1InfoTreeIndex: 1, - Timestamp: 123456789, - PreviousBlockHash: common.HexToHash("0xabc"), - GlobalExitRoot: common.HexToHash("0x7891"), - }, mockProof, nil) - }, - expectedCert: nil, - expectedError: true, - }, - } - - for _, tt := range tests { - tt := tt - - t.Run(tt.name, func(t *testing.T) { - mockL1InfoTreeQuerier.ExpectedCalls = nil - mockL2BridgeQuerier.ExpectedCalls = nil - - if tt.mockFn != nil { - tt.mockFn() - } - - flow := &baseFlow{ - l2BridgeQuerier: mockL2BridgeQuerier, - l1InfoTreeDataQuerier: mockL1InfoTreeQuerier, - log: log.WithFields("test", "unittest"), - } - - certParam := &types.CertificateBuildParams{ - ToBlock: tt.toBlock, - Bridges: tt.bridges, - Claims: tt.claims, - CertificateType: types.CertificateTypePP, - L1InfoTreeRootFromWhichToProve: common.HexToHash("0x7891"), - } - cert, err := flow.BuildCertificate(context.Background(), certParam, &tt.lastSentCertificate, false) - - if tt.expectedError { - require.Error(t, err) - require.Nil(t, cert) - } else { - require.NoError(t, err) - require.Equal(t, tt.expectedCert, cert) - } - }) - } -} - -func generateTestProof(t *testing.T) treetypes.Proof { - t.Helper() - - proof := treetypes.Proof{} - - for i := 0; i < int(treetypes.DefaultHeight) && i < 10; i++ { - proof[i] = common.HexToHash(fmt.Sprintf("0x%d", i)) - } - - return proof -} - func Test_PPFlow_GetCertificateBuildParams(t *testing.T) { t.Parallel() - ctx := context.Background() testCases := []struct { name string - mockFn func(*mocks.AggSenderStorage, *mocks.BridgeQuerier, *mocks.L1InfoTreeDataQuerier) + mockFn func(*mocks.CommonCertParamsBuilder, *mocks.CommonCertParamsVerifier) forceOneBridgeExit bool expectedParams *types.CertificateBuildParams expectedError string }{ { - name: "error getting last processed block", - mockFn: func(mockStorage *mocks.AggSenderStorage, - mockL2BridgeQuerier *mocks.BridgeQuerier, - mockL1InfoTreeQuerier *mocks.L1InfoTreeDataQuerier) { - mockStorage.EXPECT().GetLastSentCertificateHeader().Return(&types.CertificateHeader{ToBlock: 5}, nil) - mockL2BridgeQuerier.EXPECT().GetLastProcessedBlock(ctx).Return(uint64(0), errors.New("some error")) - }, - expectedError: "error getting last processed block from l2: some error", - }, - { - name: "error getting last sent certificate", - mockFn: func(mockStorage *mocks.AggSenderStorage, - mockL2BridgeQuerier *mocks.BridgeQuerier, - mockL1InfoTreeQuerier *mocks.L1InfoTreeDataQuerier) { - mockStorage.EXPECT().GetLastSentCertificateHeader().Return(nil, errors.New("some error")) + name: "error getting common certificate build params", + mockFn: func(mockBuilder *mocks.CommonCertParamsBuilder, + mockVerifier *mocks.CommonCertParamsVerifier) { + mockBuilder.EXPECT().GetCommonCertificateBuildParams(t.Context(), types.CertificateTypePP).Return(nil, errors.New("some error")).Once() }, expectedError: "some error", }, { name: "no new blocks to send a certificate", - mockFn: func(mockStorage *mocks.AggSenderStorage, - mockL2BridgeQuerier *mocks.BridgeQuerier, - mockL1InfoTreeQuerier *mocks.L1InfoTreeDataQuerier) { - mockL2BridgeQuerier.EXPECT().GetLastProcessedBlock(ctx).Return(uint64(10), nil) - mockStorage.EXPECT().GetLastSentCertificateHeader().Return(&types.CertificateHeader{ToBlock: 10}, nil) + mockFn: func(mockBuilder *mocks.CommonCertParamsBuilder, + mockVerifier *mocks.CommonCertParamsVerifier) { + mockBuilder.EXPECT().GetCommonCertificateBuildParams(t.Context(), types.CertificateTypePP).Return(nil, certificatebuild.ErrNoNewBlocks).Once() }, expectedParams: nil, }, { - name: "error getting bridges and claims", - mockFn: func(mockStorage *mocks.AggSenderStorage, - mockL2BridgeQuerier *mocks.BridgeQuerier, - mockL1InfoTreeQuerier *mocks.L1InfoTreeDataQuerier) { - mockL1InfoTreeQuerier.EXPECT().GetLatestFinalizedL1InfoRoot(mock.Anything).Return( - &treetypes.Root{Hash: common.HexToHash("0x123"), BlockNum: 10}, nil, nil) - mockL2BridgeQuerier.EXPECT().GetLastProcessedBlock(ctx).Return(uint64(10), nil) - mockStorage.EXPECT().GetLastSentCertificateHeader().Return(&types.CertificateHeader{ToBlock: 5}, nil) - mockL2BridgeQuerier.EXPECT().GetBridgesAndClaims(ctx, uint64(6), uint64(10)).Return(nil, nil, errors.New("some error")) - }, - expectedError: "some error", - }, - { - name: "no bridges and claims", - mockFn: func(mockStorage *mocks.AggSenderStorage, - mockL2BridgeQuerier *mocks.BridgeQuerier, - mockL1InfoTreeQuerier *mocks.L1InfoTreeDataQuerier) { - mockL1InfoTreeQuerier.EXPECT().GetLatestFinalizedL1InfoRoot(mock.Anything).Return( - &treetypes.Root{Hash: common.HexToHash("0x123"), BlockNum: 10}, nil, nil) - mockL2BridgeQuerier.EXPECT().GetLastProcessedBlock(ctx).Return(uint64(10), nil) - mockStorage.EXPECT().GetLastSentCertificateHeader().Return(&types.CertificateHeader{ToBlock: 5}, nil) - mockL2BridgeQuerier.EXPECT().GetBridgesAndClaims(ctx, uint64(6), uint64(10)).Return([]bridgesync.Bridge{}, []bridgesync.Claim{}, nil) + name: "no bridges when forceOneBridgeExit is true", + forceOneBridgeExit: true, + mockFn: func(mockBuilder *mocks.CommonCertParamsBuilder, + mockVerifier *mocks.CommonCertParamsVerifier) { + mockBuilder.EXPECT().GetCommonCertificateBuildParams(t.Context(), types.CertificateTypePP).Return(&types.CertificateBuildParams{}, nil).Once() }, expectedParams: nil, }, { - name: "no bridges when forceOneBridgeExit is true", - forceOneBridgeExit: true, - mockFn: func(mockStorage *mocks.AggSenderStorage, - mockL2BridgeQuerier *mocks.BridgeQuerier, - mockL1InfoTreeQuerier *mocks.L1InfoTreeDataQuerier) { - mockL1InfoTreeQuerier.EXPECT().GetLatestFinalizedL1InfoRoot(mock.Anything).Return( - &treetypes.Root{Hash: common.HexToHash("0x123"), BlockNum: 10}, nil, nil) - mockL2BridgeQuerier.EXPECT().GetLastProcessedBlock(ctx).Return(uint64(10), nil) - mockStorage.EXPECT().GetLastSentCertificateHeader().Return(&types.CertificateHeader{ToBlock: 5}, nil) - mockL2BridgeQuerier.EXPECT().GetBridgesAndClaims(ctx, uint64(6), uint64(10)).Return([]bridgesync.Bridge{}, []bridgesync.Claim{{}}, nil) + name: "no bridges and claims when forceOneBridgeExit is false", + forceOneBridgeExit: false, + mockFn: func(mockBuilder *mocks.CommonCertParamsBuilder, + mockVerifier *mocks.CommonCertParamsVerifier) { + mockBuilder.EXPECT().GetCommonCertificateBuildParams(t.Context(), types.CertificateTypePP).Return(&types.CertificateBuildParams{}, nil).Once() }, expectedParams: nil, }, { name: "no bridges when forceOneBridgeExit is false, but has claims", forceOneBridgeExit: false, - mockFn: func(mockStorage *mocks.AggSenderStorage, - mockL2BridgeQuerier *mocks.BridgeQuerier, - mockL1InfoTreeQuerier *mocks.L1InfoTreeDataQuerier) { - mockL2BridgeQuerier.EXPECT().GetLastProcessedBlock(ctx).Return(uint64(10), nil) - mockStorage.EXPECT().GetLastSentCertificateHeader().Return(&types.CertificateHeader{ToBlock: 5}, nil) - rer := common.HexToHash("0x1") - mer := common.HexToHash("0x2") - ger := calculateGER(mer, rer) - mockL2BridgeQuerier.EXPECT().GetBridgesAndClaims(ctx, uint64(6), uint64(10)).Return([]bridgesync.Bridge{}, []bridgesync.Claim{ - { - BlockNum: 1, - GlobalExitRoot: ger, - RollupExitRoot: rer, - MainnetExitRoot: mer, - }}, nil) - mockL1InfoTreeQuerier.EXPECT().GetLatestFinalizedL1InfoRoot(ctx).Return( - &treetypes.Root{Hash: common.HexToHash("0x123"), BlockNum: 1}, nil, nil) + mockFn: func(mockBuilder *mocks.CommonCertParamsBuilder, + mockVerifier *mocks.CommonCertParamsVerifier) { + params := &types.CertificateBuildParams{ + FromBlock: 0, + ToBlock: 10, + Claims: []bridgesync.Claim{{BlockNum: 10}}, + } + mockBuilder.EXPECT().GetCommonCertificateBuildParams(t.Context(), types.CertificateTypePP).Return(params, nil).Once() + mockVerifier.EXPECT().VerifyBuildParams(t.Context(), params).Return(nil).Once() }, expectedParams: &types.CertificateBuildParams{ - FromBlock: 6, - ToBlock: 10, - RetryCount: 0, - L1InfoTreeLeafCount: 1, - CertificateType: types.CertificateTypePP, - LastSentCertificate: &types.CertificateHeader{ToBlock: 5}, - Bridges: []bridgesync.Bridge{}, - Claims: []bridgesync.Claim{ - { - BlockNum: 1, - RollupExitRoot: common.HexToHash("0x1"), - MainnetExitRoot: common.HexToHash("0x2"), - GlobalExitRoot: calculateGER(common.HexToHash("0x2"), common.HexToHash("0x1")), - }}, - CreatedAt: timeNowUTCForTest(), - L1InfoTreeRootFromWhichToProve: common.HexToHash("0x123"), - }, - }, - { - name: "error claim GER invalid", - mockFn: func(mockStorage *mocks.AggSenderStorage, - mockL2BridgeQuerier *mocks.BridgeQuerier, - mockL1InfoTreeQuerier *mocks.L1InfoTreeDataQuerier) { - mockL1InfoTreeQuerier.EXPECT().GetLatestFinalizedL1InfoRoot(mock.Anything).Return( - &treetypes.Root{Hash: common.HexToHash("0x123"), BlockNum: 10}, nil, nil) - mockL2BridgeQuerier.EXPECT().GetLastProcessedBlock(ctx).Return(uint64(10), nil) - mockStorage.EXPECT().GetLastSentCertificateHeader().Return(&types.CertificateHeader{ToBlock: 5}, nil) - mockL2BridgeQuerier.EXPECT().GetBridgesAndClaims(ctx, uint64(6), uint64(10)).Return( - []bridgesync.Bridge{{}}, []bridgesync.Claim{{GlobalExitRoot: common.HexToHash("0x1")}}, nil) + FromBlock: 0, + ToBlock: 10, + Claims: []bridgesync.Claim{{BlockNum: 10}}, }, - expectedError: "GER mismatch", }, { - name: "error GetLatestFinalizedL1InfoRoot", - mockFn: func(mockStorage *mocks.AggSenderStorage, - mockL2BridgeQuerier *mocks.BridgeQuerier, - mockL1InfoTreeQuerier *mocks.L1InfoTreeDataQuerier) { - mockL2BridgeQuerier.EXPECT().GetLastProcessedBlock(ctx).Return(uint64(10), nil) - mockStorage.EXPECT().GetLastSentCertificateHeader().Return(&types.CertificateHeader{ToBlock: 5}, nil) - mockL1InfoTreeQuerier.On("GetLatestFinalizedL1InfoRoot", ctx).Return(nil, nil, errors.New("some error")) + name: "error on verifying build params", + mockFn: func(mockBuilder *mocks.CommonCertParamsBuilder, + mockVerifier *mocks.CommonCertParamsVerifier) { + params := &types.CertificateBuildParams{ + FromBlock: 0, + ToBlock: 10, + Bridges: []bridgesync.Bridge{{BlockNum: 5}}, + Claims: []bridgesync.Claim{{BlockNum: 10}}, + } + mockBuilder.EXPECT().GetCommonCertificateBuildParams(t.Context(), types.CertificateTypePP). + Return(params, nil).Once() + mockVerifier.EXPECT().VerifyBuildParams(t.Context(), params).Return(errors.New("verification error")).Once() }, - expectedError: "error generating pre build params: error getting latest finalized L1 info root: some error", + expectedError: "ppFlow - error verifying build params", }, { - name: "success", - mockFn: func(mockStorage *mocks.AggSenderStorage, - mockL2BridgeQuerier *mocks.BridgeQuerier, - mockL1InfoTreeQuerier *mocks.L1InfoTreeDataQuerier) { - rer := common.HexToHash("0x1") - mer := common.HexToHash("0x2") - ger := calculateGER(mer, rer) - mockL2BridgeQuerier.EXPECT().GetLastProcessedBlock(ctx).Return(uint64(10), nil) - mockStorage.EXPECT().GetLastSentCertificateHeader().Return(&types.CertificateHeader{ToBlock: 5}, nil) - mockL2BridgeQuerier.EXPECT().GetBridgesAndClaims(ctx, uint64(6), uint64(10)).Return([]bridgesync.Bridge{{}}, []bridgesync.Claim{ - { - GlobalExitRoot: ger, - RollupExitRoot: rer, - MainnetExitRoot: mer, - }}, nil) - mockL1InfoTreeQuerier.EXPECT().GetLatestFinalizedL1InfoRoot(ctx).Return( - &treetypes.Root{Hash: common.HexToHash("0x123"), BlockNum: 10}, nil, nil) + name: "success - has bridges and claims", + mockFn: func(mockBuilder *mocks.CommonCertParamsBuilder, + mockVerifier *mocks.CommonCertParamsVerifier) { + params := &types.CertificateBuildParams{ + FromBlock: 0, + ToBlock: 10, + Bridges: []bridgesync.Bridge{{BlockNum: 5}}, + Claims: []bridgesync.Claim{{BlockNum: 10}}, + } + mockBuilder.EXPECT().GetCommonCertificateBuildParams(t.Context(), types.CertificateTypePP).Return(params, nil).Once() + mockVerifier.EXPECT().VerifyBuildParams(t.Context(), params).Return(nil).Once() }, expectedParams: &types.CertificateBuildParams{ - FromBlock: 6, - ToBlock: 10, - RetryCount: 0, - L1InfoTreeLeafCount: 1, - CertificateType: types.CertificateTypePP, - LastSentCertificate: &types.CertificateHeader{ToBlock: 5}, - Bridges: []bridgesync.Bridge{{}}, - Claims: []bridgesync.Claim{ - { - RollupExitRoot: common.HexToHash("0x1"), - MainnetExitRoot: common.HexToHash("0x2"), - GlobalExitRoot: calculateGER(common.HexToHash("0x2"), common.HexToHash("0x1")), - }}, - CreatedAt: timeNowUTCForTest(), - L1InfoTreeRootFromWhichToProve: common.HexToHash("0x123"), + FromBlock: 0, + ToBlock: 10, + Bridges: []bridgesync.Bridge{{BlockNum: 5}}, + Claims: []bridgesync.Claim{{BlockNum: 10}}, }, }, } @@ -449,115 +120,148 @@ func Test_PPFlow_GetCertificateBuildParams(t *testing.T) { tc := tc t.Run(tc.name, func(t *testing.T) { t.Parallel() - mockStorage := mocks.NewAggSenderStorage(t) - mockL2BridgeQuerier := mocks.NewBridgeQuerier(t) - mockL1InfoTreeQuerier := mocks.NewL1InfoTreeDataQuerier(t) - mockLERQuerier := mocks.NewLERQuerier(t) + mockParamsBuilder := mocks.NewCommonCertParamsBuilder(t) + mockParamsVerifier := mocks.NewCommonCertParamsVerifier(t) + logger := log.WithFields("test", "Test_PPFlow_GetCertificateBuildParams") - baseFlow := NewBaseFlow(logger, mockL2BridgeQuerier, - mockStorage, mockL1InfoTreeQuerier, mockLERQuerier, NewBaseFlowConfigDefault()) - baseFlow.timeNowFunc = timeNowUTCForTest ppFlow := NewPPFlow( logger, - baseFlow, - mockStorage, mockL1InfoTreeQuerier, mockL2BridgeQuerier, nil, tc.forceOneBridgeExit, 0) + mockParamsBuilder, + mockParamsVerifier, + nil, // mockStorage + nil, // mockL1InfoTreeQuerier + nil, // mockL2BridgeQuerier + nil, // mockSigner + tc.forceOneBridgeExit, + 0, + ) - tc.mockFn(mockStorage, mockL2BridgeQuerier, mockL1InfoTreeQuerier) + tc.mockFn(mockParamsBuilder, mockParamsVerifier) - params, err := ppFlow.GetCertificateBuildParams(ctx) + params, err := ppFlow.GetCertificateBuildParams(t.Context()) if tc.expectedError != "" { require.ErrorContains(t, err, tc.expectedError) } else { require.NoError(t, err) require.Equal(t, tc.expectedParams, params) } + + mockParamsBuilder.AssertExpectations(t) + mockParamsVerifier.AssertExpectations(t) }) } } -func TestGetLastSentBlockAndRetryCount(t *testing.T) { +func Test_PPFlow_CheckInitialStatus(t *testing.T) { + sut := &PPFlow{} + require.Nil(t, sut.CheckInitialStatus(t.Context())) +} + +func Test_PPFlow_BuildCertificate(t *testing.T) { t.Parallel() - tests := []struct { - name string - lastSentCertificate *types.CertificateHeader - expectedBlock uint64 - startL2Block uint64 - expectedRetryCount int + testCases := []struct { + name string + buildParams *types.CertificateBuildParams + mockFn func(*mocks.CommonCertParamsBuilder, *mocks.Signer) + expectedCert *agglayertypes.Certificate + expectedError string }{ { - name: "No last sent certificate, start block is 0", - lastSentCertificate: nil, - expectedBlock: 0, - startL2Block: 0, - expectedRetryCount: 0, - }, - { - name: "No last sent certificate, start block is 1000", - lastSentCertificate: nil, - expectedBlock: 1000, - startL2Block: 1000, - expectedRetryCount: 0, - }, - { - name: "Last sent certificate with no error", - lastSentCertificate: &types.CertificateHeader{ - ToBlock: 10, - Status: agglayertypes.Settled, + name: "error building common certificate", + buildParams: &types.CertificateBuildParams{ + FromBlock: 0, + ToBlock: 10, + Bridges: []bridgesync.Bridge{{BlockNum: 5}}, + Claims: []bridgesync.Claim{{BlockNum: 10}}, + LastSentCertificate: nil, + }, + mockFn: func(mockBuilder *mocks.CommonCertParamsBuilder, mockSigner *mocks.Signer) { + mockBuilder.EXPECT().BuildCertificate(t.Context(), mock.Anything, (*types.CertificateHeader)(nil), false). + Return(nil, errors.New("build error")).Once() }, - expectedBlock: 10, - expectedRetryCount: 0, + expectedError: "ppFlow - error building certificate: build error", }, { - name: "Last sent certificate with error and non-zero FromBlock", - lastSentCertificate: &types.CertificateHeader{ - FromBlock: 5, - ToBlock: 10, - Status: agglayertypes.InError, - RetryCount: 1, + name: "error signing certificate", + buildParams: &types.CertificateBuildParams{ + FromBlock: 0, + ToBlock: 10, + Bridges: []bridgesync.Bridge{{BlockNum: 5}}, + Claims: []bridgesync.Claim{{BlockNum: 10}}, + LastSentCertificate: nil, }, - expectedBlock: 4, - expectedRetryCount: 2, + mockFn: func(mockBuilder *mocks.CommonCertParamsBuilder, mockSigner *mocks.Signer) { + mockBuilder.EXPECT().BuildCertificate(t.Context(), mock.Anything, (*types.CertificateHeader)(nil), false). + Return(&agglayertypes.Certificate{NewLocalExitRoot: common.HexToHash("0x456")}, nil).Once() + mockSigner.EXPECT().SignHash(t.Context(), mock.Anything).Return(nil, errors.New("signing error")).Once() + }, + expectedError: "ppFlow - error signing certificate: signing error", }, { - name: "Last sent certificate with error and zero FromBlock", - lastSentCertificate: &types.CertificateHeader{ - FromBlock: 0, - ToBlock: 10, - Status: agglayertypes.InError, - RetryCount: 1, + name: "successfully builds and signs certificate", + buildParams: &types.CertificateBuildParams{ + FromBlock: 0, + ToBlock: 9, + Bridges: []bridgesync.Bridge{{BlockNum: 5}}, + Claims: []bridgesync.Claim{{BlockNum: 8}}, + }, + mockFn: func(mockBuilder *mocks.CommonCertParamsBuilder, mockSigner *mocks.Signer) { + mockBuilder.EXPECT().BuildCertificate(t.Context(), mock.Anything, (*types.CertificateHeader)(nil), false). + Return(&agglayertypes.Certificate{NewLocalExitRoot: common.HexToHash("0x456")}, nil).Once() + mockSigner.EXPECT().SignHash(t.Context(), mock.Anything).Return([]byte("mock_signature"), nil).Once() + mockSigner.EXPECT().PublicAddress().Return(common.HexToAddress("0x123")).Once() + }, + expectedCert: &agglayertypes.Certificate{ + NewLocalExitRoot: common.HexToHash("0x456"), + AggchainData: &agglayertypes.AggchainDataSignature{ + Signature: []byte("mock_signature"), + }, }, - expectedBlock: 10, - expectedRetryCount: 2, }, } - for _, tt := range tests { - tt := tt - - t.Run(tt.name, func(t *testing.T) { + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { t.Parallel() - baseFlow := &baseFlow{cfg: NewBaseFlowConfig(0, tt.startL2Block, false)} + mockParamsBuilder := mocks.NewCommonCertParamsBuilder(t) + mockSigner := mocks.NewSigner(t) + + if tc.mockFn != nil { + tc.mockFn(mockParamsBuilder, mockSigner) + } + + logger := log.WithFields("test", "Test_PPFlow_BuildCertificate") + ppFlow := NewPPFlow( + logger, + mockParamsBuilder, + nil, // commonParamsVerifier + nil, // storage + nil, // l1InfoTreeQuerier + nil, // l2BridgeQuerier + mockSigner, + false, // forceOneBridgeExit + 0, // maxL2BlockNumber + ) - block, retryCount := baseFlow.getLastSentBlockAndRetryCount(tt.lastSentCertificate) + cert, err := ppFlow.BuildCertificate(t.Context(), tc.buildParams) + if tc.expectedError != "" { + require.ErrorContains(t, err, tc.expectedError) + } else { + require.NoError(t, err) + require.NotNil(t, cert) + } - require.Equal(t, tt.expectedBlock, block) - require.Equal(t, tt.expectedRetryCount, retryCount) + mockParamsBuilder.AssertExpectations(t) + mockSigner.AssertExpectations(t) }) } } -func Test_PPFlow_CheckInitialStatus(t *testing.T) { - sut := &PPFlow{} - require.Nil(t, sut.CheckInitialStatus(context.TODO())) -} - func Test_PPFlow_SignCertificate(t *testing.T) { t.Parallel() - ctx := context.Background() - tests := []struct { name string mockSignerFn func(*mocks.Signer) @@ -568,7 +272,7 @@ func Test_PPFlow_SignCertificate(t *testing.T) { { name: "successfully signs certificate", mockSignerFn: func(mockSigner *mocks.Signer) { - mockSigner.EXPECT().SignHash(ctx, mock.Anything).Return([]byte("mock_signature"), nil) + mockSigner.EXPECT().SignHash(t.Context(), mock.Anything).Return([]byte("mock_signature"), nil) mockSigner.EXPECT().PublicAddress().Return(common.HexToAddress("0x123")) }, certificate: &agglayertypes.Certificate{ @@ -584,7 +288,7 @@ func Test_PPFlow_SignCertificate(t *testing.T) { { name: "error signing certificate", mockSignerFn: func(mockSigner *mocks.Signer) { - mockSigner.EXPECT().SignHash(ctx, mock.Anything).Return(nil, errors.New("signing error")) + mockSigner.EXPECT().SignHash(t.Context(), mock.Anything).Return(nil, errors.New("signing error")) }, certificate: &agglayertypes.Certificate{ NewLocalExitRoot: common.HexToHash("0x456"), @@ -604,17 +308,11 @@ func Test_PPFlow_SignCertificate(t *testing.T) { tt.mockSignerFn(mockSigner) } logger := log.WithFields("test", "Test_PPFlow_SignCertificate") - flowBase := NewBaseFlow( - logger, - nil, // mockL2BridgeQuerier, - nil, // mockStorage, - nil, // mockL1InfoTreeDataQuerier, - nil, // mockLERQuerier, - NewBaseFlowConfigDefault()) ppFlow := NewPPFlow( logger, - flowBase, + nil, // commonParamsBuilder + nil, // commonParamsVerifier nil, // storage nil, // l1InfoTreeDataQuerier nil, // l2BridgeQuerier @@ -623,7 +321,7 @@ func Test_PPFlow_SignCertificate(t *testing.T) { 0, // maxL2BlockNumber ) - signedCert, err := ppFlow.signCertificate(ctx, tt.certificate) + signedCert, err := ppFlow.signCertificate(t.Context(), tt.certificate) if tt.expectedError != "" { require.ErrorContains(t, err, tt.expectedError) @@ -632,6 +330,8 @@ func Test_PPFlow_SignCertificate(t *testing.T) { require.NoError(t, err) require.Equal(t, tt.expectedCert, signedCert) } + + mockSigner.AssertExpectations(t) }) } } diff --git a/aggsender/mocks/mock_aggsender_flow_baser.go b/aggsender/mocks/mock_aggsender_flow_baser.go deleted file mode 100644 index ab816984c..000000000 --- a/aggsender/mocks/mock_aggsender_flow_baser.go +++ /dev/null @@ -1,597 +0,0 @@ -// Code generated by mockery. DO NOT EDIT. - -package mocks - -import ( - agglayertypes "github.com/agglayer/aggkit/agglayer/types" - bridgesync "github.com/agglayer/aggkit/bridgesync" - - common "github.com/ethereum/go-ethereum/common" - - context "context" - - mock "github.com/stretchr/testify/mock" - - types "github.com/agglayer/aggkit/aggsender/types" -) - -// AggsenderFlowBaser is an autogenerated mock type for the AggsenderFlowBaser type -type AggsenderFlowBaser struct { - mock.Mock -} - -type AggsenderFlowBaser_Expecter struct { - mock *mock.Mock -} - -func (_m *AggsenderFlowBaser) EXPECT() *AggsenderFlowBaser_Expecter { - return &AggsenderFlowBaser_Expecter{mock: &_m.Mock} -} - -// BuildCertificate provides a mock function with given fields: ctx, certParams, lastSentCertificate, allowEmptyCert -func (_m *AggsenderFlowBaser) BuildCertificate(ctx context.Context, certParams *types.CertificateBuildParams, lastSentCertificate *types.CertificateHeader, allowEmptyCert bool) (*agglayertypes.Certificate, error) { - ret := _m.Called(ctx, certParams, lastSentCertificate, allowEmptyCert) - - if len(ret) == 0 { - panic("no return value specified for BuildCertificate") - } - - var r0 *agglayertypes.Certificate - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, *types.CertificateBuildParams, *types.CertificateHeader, bool) (*agglayertypes.Certificate, error)); ok { - return rf(ctx, certParams, lastSentCertificate, allowEmptyCert) - } - if rf, ok := ret.Get(0).(func(context.Context, *types.CertificateBuildParams, *types.CertificateHeader, bool) *agglayertypes.Certificate); ok { - r0 = rf(ctx, certParams, lastSentCertificate, allowEmptyCert) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*agglayertypes.Certificate) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, *types.CertificateBuildParams, *types.CertificateHeader, bool) error); ok { - r1 = rf(ctx, certParams, lastSentCertificate, allowEmptyCert) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// AggsenderFlowBaser_BuildCertificate_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'BuildCertificate' -type AggsenderFlowBaser_BuildCertificate_Call struct { - *mock.Call -} - -// BuildCertificate is a helper method to define mock.On call -// - ctx context.Context -// - certParams *types.CertificateBuildParams -// - lastSentCertificate *types.CertificateHeader -// - allowEmptyCert bool -func (_e *AggsenderFlowBaser_Expecter) BuildCertificate(ctx interface{}, certParams interface{}, lastSentCertificate interface{}, allowEmptyCert interface{}) *AggsenderFlowBaser_BuildCertificate_Call { - return &AggsenderFlowBaser_BuildCertificate_Call{Call: _e.mock.On("BuildCertificate", ctx, certParams, lastSentCertificate, allowEmptyCert)} -} - -func (_c *AggsenderFlowBaser_BuildCertificate_Call) Run(run func(ctx context.Context, certParams *types.CertificateBuildParams, lastSentCertificate *types.CertificateHeader, allowEmptyCert bool)) *AggsenderFlowBaser_BuildCertificate_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(*types.CertificateBuildParams), args[2].(*types.CertificateHeader), args[3].(bool)) - }) - return _c -} - -func (_c *AggsenderFlowBaser_BuildCertificate_Call) Return(_a0 *agglayertypes.Certificate, _a1 error) *AggsenderFlowBaser_BuildCertificate_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *AggsenderFlowBaser_BuildCertificate_Call) RunAndReturn(run func(context.Context, *types.CertificateBuildParams, *types.CertificateHeader, bool) (*agglayertypes.Certificate, error)) *AggsenderFlowBaser_BuildCertificate_Call { - _c.Call.Return(run) - return _c -} - -// ConvertClaimToImportedBridgeExit provides a mock function with given fields: claim -func (_m *AggsenderFlowBaser) ConvertClaimToImportedBridgeExit(claim bridgesync.Claim) (*agglayertypes.ImportedBridgeExit, error) { - ret := _m.Called(claim) - - if len(ret) == 0 { - panic("no return value specified for ConvertClaimToImportedBridgeExit") - } - - var r0 *agglayertypes.ImportedBridgeExit - var r1 error - if rf, ok := ret.Get(0).(func(bridgesync.Claim) (*agglayertypes.ImportedBridgeExit, error)); ok { - return rf(claim) - } - if rf, ok := ret.Get(0).(func(bridgesync.Claim) *agglayertypes.ImportedBridgeExit); ok { - r0 = rf(claim) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*agglayertypes.ImportedBridgeExit) - } - } - - if rf, ok := ret.Get(1).(func(bridgesync.Claim) error); ok { - r1 = rf(claim) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// AggsenderFlowBaser_ConvertClaimToImportedBridgeExit_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ConvertClaimToImportedBridgeExit' -type AggsenderFlowBaser_ConvertClaimToImportedBridgeExit_Call struct { - *mock.Call -} - -// ConvertClaimToImportedBridgeExit is a helper method to define mock.On call -// - claim bridgesync.Claim -func (_e *AggsenderFlowBaser_Expecter) ConvertClaimToImportedBridgeExit(claim interface{}) *AggsenderFlowBaser_ConvertClaimToImportedBridgeExit_Call { - return &AggsenderFlowBaser_ConvertClaimToImportedBridgeExit_Call{Call: _e.mock.On("ConvertClaimToImportedBridgeExit", claim)} -} - -func (_c *AggsenderFlowBaser_ConvertClaimToImportedBridgeExit_Call) Run(run func(claim bridgesync.Claim)) *AggsenderFlowBaser_ConvertClaimToImportedBridgeExit_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(bridgesync.Claim)) - }) - return _c -} - -func (_c *AggsenderFlowBaser_ConvertClaimToImportedBridgeExit_Call) Return(_a0 *agglayertypes.ImportedBridgeExit, _a1 error) *AggsenderFlowBaser_ConvertClaimToImportedBridgeExit_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *AggsenderFlowBaser_ConvertClaimToImportedBridgeExit_Call) RunAndReturn(run func(bridgesync.Claim) (*agglayertypes.ImportedBridgeExit, error)) *AggsenderFlowBaser_ConvertClaimToImportedBridgeExit_Call { - _c.Call.Return(run) - return _c -} - -// GenerateBuildParams provides a mock function with given fields: ctx, preParams -func (_m *AggsenderFlowBaser) GenerateBuildParams(ctx context.Context, preParams types.CertificatePreBuildParams) (*types.CertificateBuildParams, error) { - ret := _m.Called(ctx, preParams) - - if len(ret) == 0 { - panic("no return value specified for GenerateBuildParams") - } - - var r0 *types.CertificateBuildParams - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, types.CertificatePreBuildParams) (*types.CertificateBuildParams, error)); ok { - return rf(ctx, preParams) - } - if rf, ok := ret.Get(0).(func(context.Context, types.CertificatePreBuildParams) *types.CertificateBuildParams); ok { - r0 = rf(ctx, preParams) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*types.CertificateBuildParams) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, types.CertificatePreBuildParams) error); ok { - r1 = rf(ctx, preParams) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// AggsenderFlowBaser_GenerateBuildParams_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GenerateBuildParams' -type AggsenderFlowBaser_GenerateBuildParams_Call struct { - *mock.Call -} - -// GenerateBuildParams is a helper method to define mock.On call -// - ctx context.Context -// - preParams types.CertificatePreBuildParams -func (_e *AggsenderFlowBaser_Expecter) GenerateBuildParams(ctx interface{}, preParams interface{}) *AggsenderFlowBaser_GenerateBuildParams_Call { - return &AggsenderFlowBaser_GenerateBuildParams_Call{Call: _e.mock.On("GenerateBuildParams", ctx, preParams)} -} - -func (_c *AggsenderFlowBaser_GenerateBuildParams_Call) Run(run func(ctx context.Context, preParams types.CertificatePreBuildParams)) *AggsenderFlowBaser_GenerateBuildParams_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(types.CertificatePreBuildParams)) - }) - return _c -} - -func (_c *AggsenderFlowBaser_GenerateBuildParams_Call) Return(_a0 *types.CertificateBuildParams, _a1 error) *AggsenderFlowBaser_GenerateBuildParams_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *AggsenderFlowBaser_GenerateBuildParams_Call) RunAndReturn(run func(context.Context, types.CertificatePreBuildParams) (*types.CertificateBuildParams, error)) *AggsenderFlowBaser_GenerateBuildParams_Call { - _c.Call.Return(run) - return _c -} - -// GeneratePreBuildParams provides a mock function with given fields: ctx, certType -func (_m *AggsenderFlowBaser) GeneratePreBuildParams(ctx context.Context, certType types.CertificateType) (*types.CertificatePreBuildParams, error) { - ret := _m.Called(ctx, certType) - - if len(ret) == 0 { - panic("no return value specified for GeneratePreBuildParams") - } - - var r0 *types.CertificatePreBuildParams - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, types.CertificateType) (*types.CertificatePreBuildParams, error)); ok { - return rf(ctx, certType) - } - if rf, ok := ret.Get(0).(func(context.Context, types.CertificateType) *types.CertificatePreBuildParams); ok { - r0 = rf(ctx, certType) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*types.CertificatePreBuildParams) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, types.CertificateType) error); ok { - r1 = rf(ctx, certType) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// AggsenderFlowBaser_GeneratePreBuildParams_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GeneratePreBuildParams' -type AggsenderFlowBaser_GeneratePreBuildParams_Call struct { - *mock.Call -} - -// GeneratePreBuildParams is a helper method to define mock.On call -// - ctx context.Context -// - certType types.CertificateType -func (_e *AggsenderFlowBaser_Expecter) GeneratePreBuildParams(ctx interface{}, certType interface{}) *AggsenderFlowBaser_GeneratePreBuildParams_Call { - return &AggsenderFlowBaser_GeneratePreBuildParams_Call{Call: _e.mock.On("GeneratePreBuildParams", ctx, certType)} -} - -func (_c *AggsenderFlowBaser_GeneratePreBuildParams_Call) Run(run func(ctx context.Context, certType types.CertificateType)) *AggsenderFlowBaser_GeneratePreBuildParams_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(types.CertificateType)) - }) - return _c -} - -func (_c *AggsenderFlowBaser_GeneratePreBuildParams_Call) Return(_a0 *types.CertificatePreBuildParams, _a1 error) *AggsenderFlowBaser_GeneratePreBuildParams_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *AggsenderFlowBaser_GeneratePreBuildParams_Call) RunAndReturn(run func(context.Context, types.CertificateType) (*types.CertificatePreBuildParams, error)) *AggsenderFlowBaser_GeneratePreBuildParams_Call { - _c.Call.Return(run) - return _c -} - -// GetCertificateBuildParamsInternal provides a mock function with given fields: ctx, certType -func (_m *AggsenderFlowBaser) GetCertificateBuildParamsInternal(ctx context.Context, certType types.CertificateType) (*types.CertificateBuildParams, error) { - ret := _m.Called(ctx, certType) - - if len(ret) == 0 { - panic("no return value specified for GetCertificateBuildParamsInternal") - } - - var r0 *types.CertificateBuildParams - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, types.CertificateType) (*types.CertificateBuildParams, error)); ok { - return rf(ctx, certType) - } - if rf, ok := ret.Get(0).(func(context.Context, types.CertificateType) *types.CertificateBuildParams); ok { - r0 = rf(ctx, certType) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*types.CertificateBuildParams) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, types.CertificateType) error); ok { - r1 = rf(ctx, certType) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// AggsenderFlowBaser_GetCertificateBuildParamsInternal_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetCertificateBuildParamsInternal' -type AggsenderFlowBaser_GetCertificateBuildParamsInternal_Call struct { - *mock.Call -} - -// GetCertificateBuildParamsInternal is a helper method to define mock.On call -// - ctx context.Context -// - certType types.CertificateType -func (_e *AggsenderFlowBaser_Expecter) GetCertificateBuildParamsInternal(ctx interface{}, certType interface{}) *AggsenderFlowBaser_GetCertificateBuildParamsInternal_Call { - return &AggsenderFlowBaser_GetCertificateBuildParamsInternal_Call{Call: _e.mock.On("GetCertificateBuildParamsInternal", ctx, certType)} -} - -func (_c *AggsenderFlowBaser_GetCertificateBuildParamsInternal_Call) Run(run func(ctx context.Context, certType types.CertificateType)) *AggsenderFlowBaser_GetCertificateBuildParamsInternal_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(types.CertificateType)) - }) - return _c -} - -func (_c *AggsenderFlowBaser_GetCertificateBuildParamsInternal_Call) Return(_a0 *types.CertificateBuildParams, _a1 error) *AggsenderFlowBaser_GetCertificateBuildParamsInternal_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *AggsenderFlowBaser_GetCertificateBuildParamsInternal_Call) RunAndReturn(run func(context.Context, types.CertificateType) (*types.CertificateBuildParams, error)) *AggsenderFlowBaser_GetCertificateBuildParamsInternal_Call { - _c.Call.Return(run) - return _c -} - -// GetNewLocalExitRoot provides a mock function with given fields: ctx, certParams -func (_m *AggsenderFlowBaser) GetNewLocalExitRoot(ctx context.Context, certParams *types.CertificateBuildParams) (common.Hash, error) { - ret := _m.Called(ctx, certParams) - - if len(ret) == 0 { - panic("no return value specified for GetNewLocalExitRoot") - } - - var r0 common.Hash - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, *types.CertificateBuildParams) (common.Hash, error)); ok { - return rf(ctx, certParams) - } - if rf, ok := ret.Get(0).(func(context.Context, *types.CertificateBuildParams) common.Hash); ok { - r0 = rf(ctx, certParams) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(common.Hash) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, *types.CertificateBuildParams) error); ok { - r1 = rf(ctx, certParams) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// AggsenderFlowBaser_GetNewLocalExitRoot_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetNewLocalExitRoot' -type AggsenderFlowBaser_GetNewLocalExitRoot_Call struct { - *mock.Call -} - -// GetNewLocalExitRoot is a helper method to define mock.On call -// - ctx context.Context -// - certParams *types.CertificateBuildParams -func (_e *AggsenderFlowBaser_Expecter) GetNewLocalExitRoot(ctx interface{}, certParams interface{}) *AggsenderFlowBaser_GetNewLocalExitRoot_Call { - return &AggsenderFlowBaser_GetNewLocalExitRoot_Call{Call: _e.mock.On("GetNewLocalExitRoot", ctx, certParams)} -} - -func (_c *AggsenderFlowBaser_GetNewLocalExitRoot_Call) Run(run func(ctx context.Context, certParams *types.CertificateBuildParams)) *AggsenderFlowBaser_GetNewLocalExitRoot_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(*types.CertificateBuildParams)) - }) - return _c -} - -func (_c *AggsenderFlowBaser_GetNewLocalExitRoot_Call) Return(_a0 common.Hash, _a1 error) *AggsenderFlowBaser_GetNewLocalExitRoot_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *AggsenderFlowBaser_GetNewLocalExitRoot_Call) RunAndReturn(run func(context.Context, *types.CertificateBuildParams) (common.Hash, error)) *AggsenderFlowBaser_GetNewLocalExitRoot_Call { - _c.Call.Return(run) - return _c -} - -// LimitCertSize provides a mock function with given fields: certParams -func (_m *AggsenderFlowBaser) LimitCertSize(certParams *types.CertificateBuildParams) (*types.CertificateBuildParams, error) { - ret := _m.Called(certParams) - - if len(ret) == 0 { - panic("no return value specified for LimitCertSize") - } - - var r0 *types.CertificateBuildParams - var r1 error - if rf, ok := ret.Get(0).(func(*types.CertificateBuildParams) (*types.CertificateBuildParams, error)); ok { - return rf(certParams) - } - if rf, ok := ret.Get(0).(func(*types.CertificateBuildParams) *types.CertificateBuildParams); ok { - r0 = rf(certParams) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*types.CertificateBuildParams) - } - } - - if rf, ok := ret.Get(1).(func(*types.CertificateBuildParams) error); ok { - r1 = rf(certParams) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// AggsenderFlowBaser_LimitCertSize_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'LimitCertSize' -type AggsenderFlowBaser_LimitCertSize_Call struct { - *mock.Call -} - -// LimitCertSize is a helper method to define mock.On call -// - certParams *types.CertificateBuildParams -func (_e *AggsenderFlowBaser_Expecter) LimitCertSize(certParams interface{}) *AggsenderFlowBaser_LimitCertSize_Call { - return &AggsenderFlowBaser_LimitCertSize_Call{Call: _e.mock.On("LimitCertSize", certParams)} -} - -func (_c *AggsenderFlowBaser_LimitCertSize_Call) Run(run func(certParams *types.CertificateBuildParams)) *AggsenderFlowBaser_LimitCertSize_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(*types.CertificateBuildParams)) - }) - return _c -} - -func (_c *AggsenderFlowBaser_LimitCertSize_Call) Return(_a0 *types.CertificateBuildParams, _a1 error) *AggsenderFlowBaser_LimitCertSize_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *AggsenderFlowBaser_LimitCertSize_Call) RunAndReturn(run func(*types.CertificateBuildParams) (*types.CertificateBuildParams, error)) *AggsenderFlowBaser_LimitCertSize_Call { - _c.Call.Return(run) - return _c -} - -// StartL2Block provides a mock function with no fields -func (_m *AggsenderFlowBaser) StartL2Block() uint64 { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for StartL2Block") - } - - var r0 uint64 - if rf, ok := ret.Get(0).(func() uint64); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(uint64) - } - - return r0 -} - -// AggsenderFlowBaser_StartL2Block_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'StartL2Block' -type AggsenderFlowBaser_StartL2Block_Call struct { - *mock.Call -} - -// StartL2Block is a helper method to define mock.On call -func (_e *AggsenderFlowBaser_Expecter) StartL2Block() *AggsenderFlowBaser_StartL2Block_Call { - return &AggsenderFlowBaser_StartL2Block_Call{Call: _e.mock.On("StartL2Block")} -} - -func (_c *AggsenderFlowBaser_StartL2Block_Call) Run(run func()) *AggsenderFlowBaser_StartL2Block_Call { - _c.Call.Run(func(args mock.Arguments) { - run() - }) - return _c -} - -func (_c *AggsenderFlowBaser_StartL2Block_Call) Return(_a0 uint64) *AggsenderFlowBaser_StartL2Block_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *AggsenderFlowBaser_StartL2Block_Call) RunAndReturn(run func() uint64) *AggsenderFlowBaser_StartL2Block_Call { - _c.Call.Return(run) - return _c -} - -// VerifyBlockRangeGaps provides a mock function with given fields: ctx, lastSentCertificate, newFromBlock, newToBlock -func (_m *AggsenderFlowBaser) VerifyBlockRangeGaps(ctx context.Context, lastSentCertificate *types.CertificateHeader, newFromBlock uint64, newToBlock uint64) error { - ret := _m.Called(ctx, lastSentCertificate, newFromBlock, newToBlock) - - if len(ret) == 0 { - panic("no return value specified for VerifyBlockRangeGaps") - } - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, *types.CertificateHeader, uint64, uint64) error); ok { - r0 = rf(ctx, lastSentCertificate, newFromBlock, newToBlock) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// AggsenderFlowBaser_VerifyBlockRangeGaps_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'VerifyBlockRangeGaps' -type AggsenderFlowBaser_VerifyBlockRangeGaps_Call struct { - *mock.Call -} - -// VerifyBlockRangeGaps is a helper method to define mock.On call -// - ctx context.Context -// - lastSentCertificate *types.CertificateHeader -// - newFromBlock uint64 -// - newToBlock uint64 -func (_e *AggsenderFlowBaser_Expecter) VerifyBlockRangeGaps(ctx interface{}, lastSentCertificate interface{}, newFromBlock interface{}, newToBlock interface{}) *AggsenderFlowBaser_VerifyBlockRangeGaps_Call { - return &AggsenderFlowBaser_VerifyBlockRangeGaps_Call{Call: _e.mock.On("VerifyBlockRangeGaps", ctx, lastSentCertificate, newFromBlock, newToBlock)} -} - -func (_c *AggsenderFlowBaser_VerifyBlockRangeGaps_Call) Run(run func(ctx context.Context, lastSentCertificate *types.CertificateHeader, newFromBlock uint64, newToBlock uint64)) *AggsenderFlowBaser_VerifyBlockRangeGaps_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(*types.CertificateHeader), args[2].(uint64), args[3].(uint64)) - }) - return _c -} - -func (_c *AggsenderFlowBaser_VerifyBlockRangeGaps_Call) Return(_a0 error) *AggsenderFlowBaser_VerifyBlockRangeGaps_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *AggsenderFlowBaser_VerifyBlockRangeGaps_Call) RunAndReturn(run func(context.Context, *types.CertificateHeader, uint64, uint64) error) *AggsenderFlowBaser_VerifyBlockRangeGaps_Call { - _c.Call.Return(run) - return _c -} - -// VerifyBuildParams provides a mock function with given fields: ctx, fullCert -func (_m *AggsenderFlowBaser) VerifyBuildParams(ctx context.Context, fullCert *types.CertificateBuildParams) error { - ret := _m.Called(ctx, fullCert) - - if len(ret) == 0 { - panic("no return value specified for VerifyBuildParams") - } - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, *types.CertificateBuildParams) error); ok { - r0 = rf(ctx, fullCert) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// AggsenderFlowBaser_VerifyBuildParams_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'VerifyBuildParams' -type AggsenderFlowBaser_VerifyBuildParams_Call struct { - *mock.Call -} - -// VerifyBuildParams is a helper method to define mock.On call -// - ctx context.Context -// - fullCert *types.CertificateBuildParams -func (_e *AggsenderFlowBaser_Expecter) VerifyBuildParams(ctx interface{}, fullCert interface{}) *AggsenderFlowBaser_VerifyBuildParams_Call { - return &AggsenderFlowBaser_VerifyBuildParams_Call{Call: _e.mock.On("VerifyBuildParams", ctx, fullCert)} -} - -func (_c *AggsenderFlowBaser_VerifyBuildParams_Call) Run(run func(ctx context.Context, fullCert *types.CertificateBuildParams)) *AggsenderFlowBaser_VerifyBuildParams_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(*types.CertificateBuildParams)) - }) - return _c -} - -func (_c *AggsenderFlowBaser_VerifyBuildParams_Call) Return(_a0 error) *AggsenderFlowBaser_VerifyBuildParams_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *AggsenderFlowBaser_VerifyBuildParams_Call) RunAndReturn(run func(context.Context, *types.CertificateBuildParams) error) *AggsenderFlowBaser_VerifyBuildParams_Call { - _c.Call.Return(run) - return _c -} - -// NewAggsenderFlowBaser creates a new instance of AggsenderFlowBaser. 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 NewAggsenderFlowBaser(t interface { - mock.TestingT - Cleanup(func()) -}) *AggsenderFlowBaser { - mock := &AggsenderFlowBaser{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/aggsender/mocks/mock_common_cert_params_builder.go b/aggsender/mocks/mock_common_cert_params_builder.go new file mode 100644 index 000000000..124f22122 --- /dev/null +++ b/aggsender/mocks/mock_common_cert_params_builder.go @@ -0,0 +1,396 @@ +// Code generated by mockery. DO NOT EDIT. + +package mocks + +import ( + agglayertypes "github.com/agglayer/aggkit/agglayer/types" + common "github.com/ethereum/go-ethereum/common" + + context "context" + + mock "github.com/stretchr/testify/mock" + + types "github.com/agglayer/aggkit/aggsender/types" +) + +// CommonCertParamsBuilder is an autogenerated mock type for the CommonCertParamsBuilder type +type CommonCertParamsBuilder struct { + mock.Mock +} + +type CommonCertParamsBuilder_Expecter struct { + mock *mock.Mock +} + +func (_m *CommonCertParamsBuilder) EXPECT() *CommonCertParamsBuilder_Expecter { + return &CommonCertParamsBuilder_Expecter{mock: &_m.Mock} +} + +// BuildCertificate provides a mock function with given fields: ctx, certParams, lastSentCertificate, allowEmptyCert +func (_m *CommonCertParamsBuilder) BuildCertificate(ctx context.Context, certParams *types.CertificateBuildParams, lastSentCertificate *types.CertificateHeader, allowEmptyCert bool) (*agglayertypes.Certificate, error) { + ret := _m.Called(ctx, certParams, lastSentCertificate, allowEmptyCert) + + if len(ret) == 0 { + panic("no return value specified for BuildCertificate") + } + + var r0 *agglayertypes.Certificate + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *types.CertificateBuildParams, *types.CertificateHeader, bool) (*agglayertypes.Certificate, error)); ok { + return rf(ctx, certParams, lastSentCertificate, allowEmptyCert) + } + if rf, ok := ret.Get(0).(func(context.Context, *types.CertificateBuildParams, *types.CertificateHeader, bool) *agglayertypes.Certificate); ok { + r0 = rf(ctx, certParams, lastSentCertificate, allowEmptyCert) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*agglayertypes.Certificate) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, *types.CertificateBuildParams, *types.CertificateHeader, bool) error); ok { + r1 = rf(ctx, certParams, lastSentCertificate, allowEmptyCert) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// CommonCertParamsBuilder_BuildCertificate_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'BuildCertificate' +type CommonCertParamsBuilder_BuildCertificate_Call struct { + *mock.Call +} + +// BuildCertificate is a helper method to define mock.On call +// - ctx context.Context +// - certParams *types.CertificateBuildParams +// - lastSentCertificate *types.CertificateHeader +// - allowEmptyCert bool +func (_e *CommonCertParamsBuilder_Expecter) BuildCertificate(ctx interface{}, certParams interface{}, lastSentCertificate interface{}, allowEmptyCert interface{}) *CommonCertParamsBuilder_BuildCertificate_Call { + return &CommonCertParamsBuilder_BuildCertificate_Call{Call: _e.mock.On("BuildCertificate", ctx, certParams, lastSentCertificate, allowEmptyCert)} +} + +func (_c *CommonCertParamsBuilder_BuildCertificate_Call) Run(run func(ctx context.Context, certParams *types.CertificateBuildParams, lastSentCertificate *types.CertificateHeader, allowEmptyCert bool)) *CommonCertParamsBuilder_BuildCertificate_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(*types.CertificateBuildParams), args[2].(*types.CertificateHeader), args[3].(bool)) + }) + return _c +} + +func (_c *CommonCertParamsBuilder_BuildCertificate_Call) Return(_a0 *agglayertypes.Certificate, _a1 error) *CommonCertParamsBuilder_BuildCertificate_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *CommonCertParamsBuilder_BuildCertificate_Call) RunAndReturn(run func(context.Context, *types.CertificateBuildParams, *types.CertificateHeader, bool) (*agglayertypes.Certificate, error)) *CommonCertParamsBuilder_BuildCertificate_Call { + _c.Call.Return(run) + return _c +} + +// GenerateBuildParams provides a mock function with given fields: ctx, preParams +func (_m *CommonCertParamsBuilder) GenerateBuildParams(ctx context.Context, preParams types.CertificatePreBuildParams) (*types.CertificateBuildParams, error) { + ret := _m.Called(ctx, preParams) + + if len(ret) == 0 { + panic("no return value specified for GenerateBuildParams") + } + + var r0 *types.CertificateBuildParams + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, types.CertificatePreBuildParams) (*types.CertificateBuildParams, error)); ok { + return rf(ctx, preParams) + } + if rf, ok := ret.Get(0).(func(context.Context, types.CertificatePreBuildParams) *types.CertificateBuildParams); ok { + r0 = rf(ctx, preParams) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.CertificateBuildParams) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, types.CertificatePreBuildParams) error); ok { + r1 = rf(ctx, preParams) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// CommonCertParamsBuilder_GenerateBuildParams_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GenerateBuildParams' +type CommonCertParamsBuilder_GenerateBuildParams_Call struct { + *mock.Call +} + +// GenerateBuildParams is a helper method to define mock.On call +// - ctx context.Context +// - preParams types.CertificatePreBuildParams +func (_e *CommonCertParamsBuilder_Expecter) GenerateBuildParams(ctx interface{}, preParams interface{}) *CommonCertParamsBuilder_GenerateBuildParams_Call { + return &CommonCertParamsBuilder_GenerateBuildParams_Call{Call: _e.mock.On("GenerateBuildParams", ctx, preParams)} +} + +func (_c *CommonCertParamsBuilder_GenerateBuildParams_Call) Run(run func(ctx context.Context, preParams types.CertificatePreBuildParams)) *CommonCertParamsBuilder_GenerateBuildParams_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(types.CertificatePreBuildParams)) + }) + return _c +} + +func (_c *CommonCertParamsBuilder_GenerateBuildParams_Call) Return(_a0 *types.CertificateBuildParams, _a1 error) *CommonCertParamsBuilder_GenerateBuildParams_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *CommonCertParamsBuilder_GenerateBuildParams_Call) RunAndReturn(run func(context.Context, types.CertificatePreBuildParams) (*types.CertificateBuildParams, error)) *CommonCertParamsBuilder_GenerateBuildParams_Call { + _c.Call.Return(run) + return _c +} + +// GeneratePreBuildParams provides a mock function with given fields: ctx, certType +func (_m *CommonCertParamsBuilder) GeneratePreBuildParams(ctx context.Context, certType types.CertificateType) (*types.CertificatePreBuildParams, error) { + ret := _m.Called(ctx, certType) + + if len(ret) == 0 { + panic("no return value specified for GeneratePreBuildParams") + } + + var r0 *types.CertificatePreBuildParams + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, types.CertificateType) (*types.CertificatePreBuildParams, error)); ok { + return rf(ctx, certType) + } + if rf, ok := ret.Get(0).(func(context.Context, types.CertificateType) *types.CertificatePreBuildParams); ok { + r0 = rf(ctx, certType) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.CertificatePreBuildParams) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, types.CertificateType) error); ok { + r1 = rf(ctx, certType) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// CommonCertParamsBuilder_GeneratePreBuildParams_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GeneratePreBuildParams' +type CommonCertParamsBuilder_GeneratePreBuildParams_Call struct { + *mock.Call +} + +// GeneratePreBuildParams is a helper method to define mock.On call +// - ctx context.Context +// - certType types.CertificateType +func (_e *CommonCertParamsBuilder_Expecter) GeneratePreBuildParams(ctx interface{}, certType interface{}) *CommonCertParamsBuilder_GeneratePreBuildParams_Call { + return &CommonCertParamsBuilder_GeneratePreBuildParams_Call{Call: _e.mock.On("GeneratePreBuildParams", ctx, certType)} +} + +func (_c *CommonCertParamsBuilder_GeneratePreBuildParams_Call) Run(run func(ctx context.Context, certType types.CertificateType)) *CommonCertParamsBuilder_GeneratePreBuildParams_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(types.CertificateType)) + }) + return _c +} + +func (_c *CommonCertParamsBuilder_GeneratePreBuildParams_Call) Return(_a0 *types.CertificatePreBuildParams, _a1 error) *CommonCertParamsBuilder_GeneratePreBuildParams_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *CommonCertParamsBuilder_GeneratePreBuildParams_Call) RunAndReturn(run func(context.Context, types.CertificateType) (*types.CertificatePreBuildParams, error)) *CommonCertParamsBuilder_GeneratePreBuildParams_Call { + _c.Call.Return(run) + return _c +} + +// GetCommonCertificateBuildParams provides a mock function with given fields: ctx, certType +func (_m *CommonCertParamsBuilder) GetCommonCertificateBuildParams(ctx context.Context, certType types.CertificateType) (*types.CertificateBuildParams, error) { + ret := _m.Called(ctx, certType) + + if len(ret) == 0 { + panic("no return value specified for GetCommonCertificateBuildParams") + } + + var r0 *types.CertificateBuildParams + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, types.CertificateType) (*types.CertificateBuildParams, error)); ok { + return rf(ctx, certType) + } + if rf, ok := ret.Get(0).(func(context.Context, types.CertificateType) *types.CertificateBuildParams); ok { + r0 = rf(ctx, certType) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.CertificateBuildParams) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, types.CertificateType) error); ok { + r1 = rf(ctx, certType) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// CommonCertParamsBuilder_GetCommonCertificateBuildParams_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetCommonCertificateBuildParams' +type CommonCertParamsBuilder_GetCommonCertificateBuildParams_Call struct { + *mock.Call +} + +// GetCommonCertificateBuildParams is a helper method to define mock.On call +// - ctx context.Context +// - certType types.CertificateType +func (_e *CommonCertParamsBuilder_Expecter) GetCommonCertificateBuildParams(ctx interface{}, certType interface{}) *CommonCertParamsBuilder_GetCommonCertificateBuildParams_Call { + return &CommonCertParamsBuilder_GetCommonCertificateBuildParams_Call{Call: _e.mock.On("GetCommonCertificateBuildParams", ctx, certType)} +} + +func (_c *CommonCertParamsBuilder_GetCommonCertificateBuildParams_Call) Run(run func(ctx context.Context, certType types.CertificateType)) *CommonCertParamsBuilder_GetCommonCertificateBuildParams_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(types.CertificateType)) + }) + return _c +} + +func (_c *CommonCertParamsBuilder_GetCommonCertificateBuildParams_Call) Return(_a0 *types.CertificateBuildParams, _a1 error) *CommonCertParamsBuilder_GetCommonCertificateBuildParams_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *CommonCertParamsBuilder_GetCommonCertificateBuildParams_Call) RunAndReturn(run func(context.Context, types.CertificateType) (*types.CertificateBuildParams, error)) *CommonCertParamsBuilder_GetCommonCertificateBuildParams_Call { + _c.Call.Return(run) + return _c +} + +// GetNewLocalExitRoot provides a mock function with given fields: ctx, certParams +func (_m *CommonCertParamsBuilder) GetNewLocalExitRoot(ctx context.Context, certParams *types.CertificateBuildParams) (common.Hash, error) { + ret := _m.Called(ctx, certParams) + + if len(ret) == 0 { + panic("no return value specified for GetNewLocalExitRoot") + } + + var r0 common.Hash + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *types.CertificateBuildParams) (common.Hash, error)); ok { + return rf(ctx, certParams) + } + if rf, ok := ret.Get(0).(func(context.Context, *types.CertificateBuildParams) common.Hash); ok { + r0 = rf(ctx, certParams) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(common.Hash) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, *types.CertificateBuildParams) error); ok { + r1 = rf(ctx, certParams) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// CommonCertParamsBuilder_GetNewLocalExitRoot_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetNewLocalExitRoot' +type CommonCertParamsBuilder_GetNewLocalExitRoot_Call struct { + *mock.Call +} + +// GetNewLocalExitRoot is a helper method to define mock.On call +// - ctx context.Context +// - certParams *types.CertificateBuildParams +func (_e *CommonCertParamsBuilder_Expecter) GetNewLocalExitRoot(ctx interface{}, certParams interface{}) *CommonCertParamsBuilder_GetNewLocalExitRoot_Call { + return &CommonCertParamsBuilder_GetNewLocalExitRoot_Call{Call: _e.mock.On("GetNewLocalExitRoot", ctx, certParams)} +} + +func (_c *CommonCertParamsBuilder_GetNewLocalExitRoot_Call) Run(run func(ctx context.Context, certParams *types.CertificateBuildParams)) *CommonCertParamsBuilder_GetNewLocalExitRoot_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(*types.CertificateBuildParams)) + }) + return _c +} + +func (_c *CommonCertParamsBuilder_GetNewLocalExitRoot_Call) Return(_a0 common.Hash, _a1 error) *CommonCertParamsBuilder_GetNewLocalExitRoot_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *CommonCertParamsBuilder_GetNewLocalExitRoot_Call) RunAndReturn(run func(context.Context, *types.CertificateBuildParams) (common.Hash, error)) *CommonCertParamsBuilder_GetNewLocalExitRoot_Call { + _c.Call.Return(run) + return _c +} + +// LimitCertSize provides a mock function with given fields: certParams +func (_m *CommonCertParamsBuilder) LimitCertSize(certParams *types.CertificateBuildParams) (*types.CertificateBuildParams, error) { + ret := _m.Called(certParams) + + if len(ret) == 0 { + panic("no return value specified for LimitCertSize") + } + + var r0 *types.CertificateBuildParams + var r1 error + if rf, ok := ret.Get(0).(func(*types.CertificateBuildParams) (*types.CertificateBuildParams, error)); ok { + return rf(certParams) + } + if rf, ok := ret.Get(0).(func(*types.CertificateBuildParams) *types.CertificateBuildParams); ok { + r0 = rf(certParams) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.CertificateBuildParams) + } + } + + if rf, ok := ret.Get(1).(func(*types.CertificateBuildParams) error); ok { + r1 = rf(certParams) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// CommonCertParamsBuilder_LimitCertSize_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'LimitCertSize' +type CommonCertParamsBuilder_LimitCertSize_Call struct { + *mock.Call +} + +// LimitCertSize is a helper method to define mock.On call +// - certParams *types.CertificateBuildParams +func (_e *CommonCertParamsBuilder_Expecter) LimitCertSize(certParams interface{}) *CommonCertParamsBuilder_LimitCertSize_Call { + return &CommonCertParamsBuilder_LimitCertSize_Call{Call: _e.mock.On("LimitCertSize", certParams)} +} + +func (_c *CommonCertParamsBuilder_LimitCertSize_Call) Run(run func(certParams *types.CertificateBuildParams)) *CommonCertParamsBuilder_LimitCertSize_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*types.CertificateBuildParams)) + }) + return _c +} + +func (_c *CommonCertParamsBuilder_LimitCertSize_Call) Return(_a0 *types.CertificateBuildParams, _a1 error) *CommonCertParamsBuilder_LimitCertSize_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *CommonCertParamsBuilder_LimitCertSize_Call) RunAndReturn(run func(*types.CertificateBuildParams) (*types.CertificateBuildParams, error)) *CommonCertParamsBuilder_LimitCertSize_Call { + _c.Call.Return(run) + return _c +} + +// NewCommonCertParamsBuilder creates a new instance of CommonCertParamsBuilder. 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 NewCommonCertParamsBuilder(t interface { + mock.TestingT + Cleanup(func()) +}) *CommonCertParamsBuilder { + mock := &CommonCertParamsBuilder{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/aggsender/mocks/mock_common_cert_params_verifier.go b/aggsender/mocks/mock_common_cert_params_verifier.go new file mode 100644 index 000000000..1bc122fa9 --- /dev/null +++ b/aggsender/mocks/mock_common_cert_params_verifier.go @@ -0,0 +1,133 @@ +// Code generated by mockery. DO NOT EDIT. + +package mocks + +import ( + context "context" + + types "github.com/agglayer/aggkit/aggsender/types" + mock "github.com/stretchr/testify/mock" +) + +// CommonCertParamsVerifier is an autogenerated mock type for the CommonCertParamsVerifier type +type CommonCertParamsVerifier struct { + mock.Mock +} + +type CommonCertParamsVerifier_Expecter struct { + mock *mock.Mock +} + +func (_m *CommonCertParamsVerifier) EXPECT() *CommonCertParamsVerifier_Expecter { + return &CommonCertParamsVerifier_Expecter{mock: &_m.Mock} +} + +// VerifyBlockRangeGaps provides a mock function with given fields: ctx, lastSentCertificate, newFromBlock, newToBlock +func (_m *CommonCertParamsVerifier) VerifyBlockRangeGaps(ctx context.Context, lastSentCertificate *types.CertificateHeader, newFromBlock uint64, newToBlock uint64) error { + ret := _m.Called(ctx, lastSentCertificate, newFromBlock, newToBlock) + + if len(ret) == 0 { + panic("no return value specified for VerifyBlockRangeGaps") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, *types.CertificateHeader, uint64, uint64) error); ok { + r0 = rf(ctx, lastSentCertificate, newFromBlock, newToBlock) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// CommonCertParamsVerifier_VerifyBlockRangeGaps_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'VerifyBlockRangeGaps' +type CommonCertParamsVerifier_VerifyBlockRangeGaps_Call struct { + *mock.Call +} + +// VerifyBlockRangeGaps is a helper method to define mock.On call +// - ctx context.Context +// - lastSentCertificate *types.CertificateHeader +// - newFromBlock uint64 +// - newToBlock uint64 +func (_e *CommonCertParamsVerifier_Expecter) VerifyBlockRangeGaps(ctx interface{}, lastSentCertificate interface{}, newFromBlock interface{}, newToBlock interface{}) *CommonCertParamsVerifier_VerifyBlockRangeGaps_Call { + return &CommonCertParamsVerifier_VerifyBlockRangeGaps_Call{Call: _e.mock.On("VerifyBlockRangeGaps", ctx, lastSentCertificate, newFromBlock, newToBlock)} +} + +func (_c *CommonCertParamsVerifier_VerifyBlockRangeGaps_Call) Run(run func(ctx context.Context, lastSentCertificate *types.CertificateHeader, newFromBlock uint64, newToBlock uint64)) *CommonCertParamsVerifier_VerifyBlockRangeGaps_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(*types.CertificateHeader), args[2].(uint64), args[3].(uint64)) + }) + return _c +} + +func (_c *CommonCertParamsVerifier_VerifyBlockRangeGaps_Call) Return(_a0 error) *CommonCertParamsVerifier_VerifyBlockRangeGaps_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *CommonCertParamsVerifier_VerifyBlockRangeGaps_Call) RunAndReturn(run func(context.Context, *types.CertificateHeader, uint64, uint64) error) *CommonCertParamsVerifier_VerifyBlockRangeGaps_Call { + _c.Call.Return(run) + return _c +} + +// VerifyBuildParams provides a mock function with given fields: ctx, fullCert +func (_m *CommonCertParamsVerifier) VerifyBuildParams(ctx context.Context, fullCert *types.CertificateBuildParams) error { + ret := _m.Called(ctx, fullCert) + + if len(ret) == 0 { + panic("no return value specified for VerifyBuildParams") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, *types.CertificateBuildParams) error); ok { + r0 = rf(ctx, fullCert) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// CommonCertParamsVerifier_VerifyBuildParams_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'VerifyBuildParams' +type CommonCertParamsVerifier_VerifyBuildParams_Call struct { + *mock.Call +} + +// VerifyBuildParams is a helper method to define mock.On call +// - ctx context.Context +// - fullCert *types.CertificateBuildParams +func (_e *CommonCertParamsVerifier_Expecter) VerifyBuildParams(ctx interface{}, fullCert interface{}) *CommonCertParamsVerifier_VerifyBuildParams_Call { + return &CommonCertParamsVerifier_VerifyBuildParams_Call{Call: _e.mock.On("VerifyBuildParams", ctx, fullCert)} +} + +func (_c *CommonCertParamsVerifier_VerifyBuildParams_Call) Run(run func(ctx context.Context, fullCert *types.CertificateBuildParams)) *CommonCertParamsVerifier_VerifyBuildParams_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(*types.CertificateBuildParams)) + }) + return _c +} + +func (_c *CommonCertParamsVerifier_VerifyBuildParams_Call) Return(_a0 error) *CommonCertParamsVerifier_VerifyBuildParams_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *CommonCertParamsVerifier_VerifyBuildParams_Call) RunAndReturn(run func(context.Context, *types.CertificateBuildParams) error) *CommonCertParamsVerifier_VerifyBuildParams_Call { + _c.Call.Return(run) + return _c +} + +// NewCommonCertParamsVerifier creates a new instance of CommonCertParamsVerifier. 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 NewCommonCertParamsVerifier(t interface { + mock.TestingT + Cleanup(func()) +}) *CommonCertParamsVerifier { + mock := &CommonCertParamsVerifier{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/aggsender/prover/proof_generation_tool.go b/aggsender/prover/proof_generation_tool.go index d46c439fb..90c1af596 100644 --- a/aggsender/prover/proof_generation_tool.go +++ b/aggsender/prover/proof_generation_tool.go @@ -7,7 +7,7 @@ import ( "github.com/0xPolygon/cdk-rpc/rpc" "github.com/agglayer/aggkit/aggsender/aggchainproofclient" - "github.com/agglayer/aggkit/aggsender/flows" + "github.com/agglayer/aggkit/aggsender/certificatebuild" "github.com/agglayer/aggkit/aggsender/query" "github.com/agglayer/aggkit/aggsender/types" aggkitgrpc "github.com/agglayer/aggkit/grpc" @@ -43,6 +43,9 @@ type Config struct { // SovereignRollupAddr is the address of the sovereign rollup contract on L1 SovereignRollupAddr common.Address `mapstructure:"SovereignRollupAddr"` + + // RollupCreationBlockL1 is the block number when the rollup was created on L1 + RollupCreationBlockL1 uint64 `mapstructure:"RollupCreationBlockL1"` } // AggchainProofGenerationTool is a tool to generate Aggchain proofs @@ -71,6 +74,7 @@ func NewAggchainProofGenerationTool( l2Client aggkittypes.BaseEthereumClienter, l2Syncer types.L2BridgeSyncer, l1InfoTreeSyncer types.L1InfoTreeSyncer, + rollupDataQuerier types.RollupDataQuerier, ) (*AggchainProofGenerationTool, error) { if err := cfg.AggkitProverClient.Validate(); err != nil { return nil, fmt.Errorf("invalid aggkit prover client config: %w", err) @@ -86,23 +90,28 @@ func NewAggchainProofGenerationTool( return nil, fmt.Errorf("error creating L2 GER reader: %w", err) } + lerQuerier, err := query.NewLERDataQuerier(cfg.RollupCreationBlockL1, rollupDataQuerier) + if err != nil { + return nil, fmt.Errorf("error creating LER data querier: %w", err) + } + l1InfoTreeQuerier := query.NewL1InfoTreeDataQuerier(l1Client, l1InfoTreeSyncer) l2BridgeQuerier := query.NewBridgeDataQuerier(logger, l2Syncer, time.Second) - baseFlow := flows.NewBaseFlow( + certBuilder := certificatebuild.NewCommonParamsBuilder( logger, - l2BridgeQuerier, - nil, // storage + nil, // storage is not used in the tool, so we pass nil l1InfoTreeQuerier, - nil, // lerQuerier - flows.NewBaseFlowConfigDefault(), + l2BridgeQuerier, + lerQuerier, + certificatebuild.NewCommonBuildConfigDefault(), ) aggchainProofQuerier := query.NewAggchainProofQuery( logger, aggchainProofClient, l1InfoTreeQuerier, nil, // optimistic signer is not used in the tool, so we pass nil - baseFlow, + certBuilder, query.NewGERDataQuerier(l1InfoTreeQuerier, l2GERReader), ) diff --git a/aggsender/prover/proof_generation_tool_test.go b/aggsender/prover/proof_generation_tool_test.go index 986e61538..5434ddeaf 100644 --- a/aggsender/prover/proof_generation_tool_test.go +++ b/aggsender/prover/proof_generation_tool_test.go @@ -146,14 +146,13 @@ func TestGetRPCServices(t *testing.T) { } func TestNewAggchainProofGenerationTool(t *testing.T) { - mockL2Syncer := mocks.NewL2BridgeSyncer(t) mockL1Client := aggkittypesmocks.NewBaseEthereumClienter(t) mockL2Client := aggkittypesmocks.NewBaseEthereumClienter(t) mockL1Client.EXPECT().CallContract(mock.Anything, mock.Anything, mock.Anything).Return(nil, nil).Maybe() mockL1Client.EXPECT().CodeAt(mock.Anything, mock.Anything, mock.Anything).Return(nil, nil).Maybe() mockL2Client.EXPECT().CallContract(mock.Anything, mock.Anything, mock.Anything).Return(nil, nil).Maybe() mockL2Client.EXPECT().CodeAt(mock.Anything, mock.Anything, mock.Anything).Return(nil, nil).Maybe() - _, err := NewAggchainProofGenerationTool(context.TODO(), log.WithFields("module", "test"), - Config{AggkitProverClient: aggkitgrpc.DefaultConfig()}, mockL1Client, mockL2Client, mockL2Syncer, nil) + _, err := NewAggchainProofGenerationTool(t.Context(), log.WithFields("module", "test"), + Config{AggkitProverClient: aggkitgrpc.DefaultConfig()}, mockL1Client, mockL2Client, nil, nil, nil) require.Error(t, err) } diff --git a/aggsender/query/ler_query.go b/aggsender/query/ler_query.go index f41eb9b2b..7dcd77dda 100644 --- a/aggsender/query/ler_query.go +++ b/aggsender/query/ler_query.go @@ -23,7 +23,6 @@ type lerDataQuerier struct { // It initializes the RollupManager contract using the provided address and Ethereum client. // // Parameters: -// - rollupManagerAddr: The Ethereum address of the RollupManager contract. // - l1GenesisBlock: The block number of the Layer 1 genesis block. // - l1Client: An implementation of BaseEthereumClienter for interacting with the Ethereum network. // @@ -31,7 +30,6 @@ type lerDataQuerier struct { // - types.LERQuerier: An initialized LERQuerier for querying rollup data. // - error: An error if the RollupManager contract could not be created. func NewLERDataQuerier( - rollupManagerAddr common.Address, l1GenesisBlock uint64, rollupDataQuerier types.RollupDataQuerier) (types.LERQuerier, error) { return &lerDataQuerier{ diff --git a/aggsender/query/ler_query_test.go b/aggsender/query/ler_query_test.go index b7d040b4c..dc03caa57 100644 --- a/aggsender/query/ler_query_test.go +++ b/aggsender/query/ler_query_test.go @@ -48,7 +48,7 @@ func TestGetLastLocalExitRoot(t *testing.T) { tc.mockFn(mockRollupQuerier) } - querier, err := NewLERDataQuerier(common.Address{}, 0, mockRollupQuerier) + querier, err := NewLERDataQuerier(0, mockRollupQuerier) require.NoError(t, err) result, err := querier.GetLastLocalExitRoot() diff --git a/aggsender/types/interfaces.go b/aggsender/types/interfaces.go index beffc9710..a11ed6e13 100644 --- a/aggsender/types/interfaces.go +++ b/aggsender/types/interfaces.go @@ -30,30 +30,6 @@ type AggsenderFlow interface { preParams *CertificatePreBuildParams) (*CertificateBuildParams, error) } -type AggsenderFlowBaser interface { - GetCertificateBuildParamsInternal( - ctx context.Context, certType CertificateType) (*CertificateBuildParams, error) - BuildCertificate(ctx context.Context, - certParams *CertificateBuildParams, - lastSentCertificate *CertificateHeader, - allowEmptyCert bool) (*agglayertypes.Certificate, error) - GetNewLocalExitRoot(ctx context.Context, - certParams *CertificateBuildParams) (common.Hash, error) - VerifyBuildParams(ctx context.Context, fullCert *CertificateBuildParams) error - VerifyBlockRangeGaps( - ctx context.Context, - lastSentCertificate *CertificateHeader, - newFromBlock, newToBlock uint64) error - ConvertClaimToImportedBridgeExit(claim bridgesync.Claim) (*agglayertypes.ImportedBridgeExit, error) - StartL2Block() uint64 - - GeneratePreBuildParams(ctx context.Context, - certType CertificateType) (*CertificatePreBuildParams, error) - GenerateBuildParams(ctx context.Context, - preParams CertificatePreBuildParams) (*CertificateBuildParams, error) - LimitCertSize(certParams *CertificateBuildParams) (*CertificateBuildParams, error) -} - // L1InfoTreeSyncer is an interface defining functions that an L1InfoTreeSyncer should implement type L1InfoTreeSyncer interface { GetInfoByGlobalExitRoot(globalExitRoot common.Hash) (*l1infotreesync.L1InfoTreeLeaf, error) @@ -247,3 +223,25 @@ type AggchainProofQuerier interface { certBuildParams *CertificateBuildParams, ) (*AggchainProof, *treetypes.Root, error) } + +// CommonCertParamsBuilder is an interface defining functions that a CommonCertParamsBuilder should implement +type CommonCertParamsBuilder interface { + GeneratePreBuildParams(ctx context.Context, certType CertificateType) (*CertificatePreBuildParams, error) + GenerateBuildParams(ctx context.Context, preParams CertificatePreBuildParams) (*CertificateBuildParams, error) + GetCommonCertificateBuildParams(ctx context.Context, certType CertificateType) (*CertificateBuildParams, error) + LimitCertSize(certParams *CertificateBuildParams) (*CertificateBuildParams, error) + GetNewLocalExitRoot(ctx context.Context, certParams *CertificateBuildParams) (common.Hash, error) + BuildCertificate(ctx context.Context, + certParams *CertificateBuildParams, + lastSentCertificate *CertificateHeader, + allowEmptyCert bool) (*agglayertypes.Certificate, error) +} + +// CommonCertParamsVerifier is an interface defining functions that a CommonCertParamsVerifier should implement +type CommonCertParamsVerifier interface { + VerifyBuildParams(ctx context.Context, fullCert *CertificateBuildParams) error + VerifyBlockRangeGaps( + ctx context.Context, + lastSentCertificate *CertificateHeader, + newFromBlock, newToBlock uint64) error +} diff --git a/aggsender/validator/validate_db_test.go b/aggsender/validator/validate_db_test.go index 327f99023..8114d06bc 100644 --- a/aggsender/validator/validate_db_test.go +++ b/aggsender/validator/validate_db_test.go @@ -7,6 +7,7 @@ import ( "testing" "github.com/0xPolygon/cdk-contracts-tooling/contracts/pp/l2-sovereign-chain/polygonrollupmanager" + "github.com/agglayer/aggkit/aggsender/certificatebuild" "github.com/agglayer/aggkit/aggsender/flows" "github.com/agglayer/aggkit/aggsender/mocks" "github.com/agglayer/aggkit/aggsender/query" @@ -16,7 +17,6 @@ import ( mocksethclient "github.com/agglayer/aggkit/types/mocks" "github.com/agglayer/go_signer/signer" signerTypes "github.com/agglayer/go_signer/signer/types" - "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" @@ -48,19 +48,19 @@ func TestValidateFullAggsenderDB(t *testing.T) { ) require.NoError(t, err) l1InfoTreeDataQuerier := query.NewL1InfoTreeDataQuerier(mockL1EthClient, l1InfoTreeSync) - mockRollupDataQuerier := mocks.NewRollupDataQuerier(t) - lerQuerier, err := query.NewLERDataQuerier(common.Address{}, 1, mockRollupDataQuerier) + lerQuerier, err := query.NewLERDataQuerier(1, mockRollupDataQuerier) require.NoError(t, err) + signer, err := signer.NewSigner(ctx, 0, signerTypes.SignerConfig{ Method: signerTypes.MethodMock, }, "test", logger) require.NoError(t, err) - cfgBase := flows.NewBaseFlowConfigDefault() ppFlow := flows.NewPPFlow( logger, - flows.NewBaseFlow(logger, l2BridgeQuerier, nil, l1InfoTreeDataQuerier, lerQuerier, cfgBase), + certificatebuild.NewCommonParamsBuilder(logger, nil, l1InfoTreeDataQuerier, l2BridgeQuerier, lerQuerier, certificatebuild.NewCommonBuildConfigDefault()), + certificatebuild.NewCommonParamsVerifier(l2BridgeQuerier, false), nil, // storage l1InfoTreeDataQuerier, l2BridgeQuerier, diff --git a/cmd/run.go b/cmd/run.go index 7cb306a44..813ed7314 100644 --- a/cmd/run.go +++ b/cmd/run.go @@ -19,6 +19,7 @@ import ( "github.com/agglayer/aggkit/aggoracle" "github.com/agglayer/aggkit/aggoracle/chaingersender" "github.com/agglayer/aggkit/aggsender" + "github.com/agglayer/aggkit/aggsender/certificatebuild" aggsendercfg "github.com/agglayer/aggkit/aggsender/config" "github.com/agglayer/aggkit/aggsender/flows" "github.com/agglayer/aggkit/aggsender/prover" @@ -143,6 +144,7 @@ func start(cliCtx *cli.Context) error { l2Client, l1InfoTreeSync, l2BridgeSync, + rollupDataQuerier, ) if err != nil { log.Fatal(err) @@ -196,6 +198,7 @@ func createAggchainProofGen( l2Client aggkittypes.BaseEthereumClienter, l1InfoTreeSync *l1infotreesync.L1InfoTreeSync, l2Syncer *bridgesync.BridgeSync, + rollupDataQuerier *etherman.RollupDataQuerier, ) (*prover.AggchainProofGenerationTool, error) { logger := log.WithFields("module", aggkitcommon.AGGCHAINPROOFGEN) @@ -207,6 +210,7 @@ func createAggchainProofGen( l2Client, l2Syncer, l1InfoTreeSync, + rollupDataQuerier, ) if err != nil { return nil, fmt.Errorf("failed to create AggchainProofGenerationTool: %w", err) @@ -233,8 +237,7 @@ func createAggSenderValidator(ctx context.Context, l2BridgeQuerier := query.NewBridgeDataQuerier(logger, l2Syncer, cfg.DelayBetweenRetries.Duration) l1InfoTreeQuerier := query.NewL1InfoTreeDataQuerier(l1Client, l1InfoTreeSync) - lerQuerier, err := query.NewLERDataQuerier( - cfg.LerQuerier.RollupManagerAddr, cfg.LerQuerier.RollupCreationBlockL1, rollupDataQuerier) + lerQuerier, err := query.NewLERDataQuerier(cfg.LerQuerier.RollupCreationBlockL1, rollupDataQuerier) if err != nil { return nil, fmt.Errorf("error creating LER data querier: %w", err) } @@ -244,19 +247,20 @@ func createAggSenderValidator(ctx context.Context, } flowPP := flows.NewPPFlow( logger, - flows.NewBaseFlow( + certificatebuild.NewCommonParamsBuilder( logger, - l2BridgeQuerier, - nil, // storage + nil, // storage is not used on validator l1InfoTreeQuerier, + l2BridgeQuerier, lerQuerier, - flows.BaseFlowConfig{ - MaxCertSize: cfg.MaxCertSize, - StartL2Block: 0, - RequireNoFEPBlockGap: false, // for PP doesnt apply it - }, + certificatebuild.NewCommonBuildConfig( + cfg.MaxCertSize, + 0, // on PP networks we always start from block 0 + false, // no FEP block gap on PP networks + ), ), - nil, // storage + certificatebuild.NewCommonParamsVerifier(l2BridgeQuerier, false), // no FEP block gap on PP networks + nil, // storage is not used on validator l1InfoTreeQuerier, l2BridgeQuerier, signer, // we reuse the signer, but the PP signature is not use diff --git a/config/default.go b/config/default.go index dd6a971e3..a57094520 100644 --- a/config/default.go +++ b/config/default.go @@ -249,6 +249,7 @@ Port = 9091 [AggchainProofGen] SovereignRollupAddr = "{{L1Config.polygonZkEVMAddress}}" GlobalExitRootL2 = "{{L2Config.GlobalExitRootAddr}}" +RollupCreationBlockL1 = {{rollupCreationBlockNumber}} [AggchainProofGen.AggkitProverClient] URL = "{{AggchainProofURL}}" MinConnectTimeout = "5s"