diff --git a/aggsender/aggsender.go b/aggsender/aggsender.go index 91190cb6..cfc610ea 100644 --- a/aggsender/aggsender.go +++ b/aggsender/aggsender.go @@ -187,14 +187,25 @@ func (a *AggSender) sendCertificate(ctx context.Context) (*agglayer.SignedCertif return nil, nil } - toBlock, bridges, claims, err := a.limitCertSize(ctx, fromBlock, toBlock) + claims, err := a.l2Syncer.GetClaims(ctx, fromBlock, toBlock) + if err != nil { + return nil, fmt.Errorf("error getting claims: %w", err) + } + certificateParams := &types.CertificateLocalParams{ + FromBlock: fromBlock, + ToBlock: toBlock, + Bridges: bridges, + Claims: claims, + } + + certificateParams, err = a.limitCertSize(certificateParams) if err != nil { return nil, fmt.Errorf("error limitCertSize: %w", err) } - a.log.Infof("building certificate for block: %d to block: %d, numBridges=%d, numClaims=%d estimatedSize=%d", - fromBlock, toBlock, len(bridges), len(claims), a.estimateSizeCert(len(bridges), len(claims))) + a.log.Infof("building certificate for %s estimatedSize=%d", + certificateParams.String(), certificateParams.EstimatedSize()) - certificate, err := a.buildCertificate(ctx, bridges, claims, lastSentCertificateInfo, toBlock) + certificate, err := a.buildCertificate(ctx, certificateParams, lastSentCertificateInfo) if err != nil { return nil, fmt.Errorf("error building certificate: %w", err) } @@ -265,57 +276,36 @@ func (a *AggSender) saveCertificateToStorage(ctx context.Context, cert types.Cer return nil } -const ( - estimatedSizeBridgeExit = 250 - estimatedSizeClaim = 44000 -) - -func (a *AggSender) limitCertSize(ctx context.Context, fromBlock, toBlock uint64) (uint64, - []bridgesync.Bridge, []bridgesync.Claim, error) { +func (a *AggSender) limitCertSize(fullCert *types.CertificateLocalParams) (*types.CertificateLocalParams, error) { + currentCert := fullCert + var previousCert *types.CertificateLocalParams var err error - var bridges, previousBridges []bridgesync.Bridge - var claims, previousClaims []bridgesync.Claim - if toBlock < fromBlock { - return toBlock, nil, nil, fmt.Errorf("invalid call, toBlock(%d)max size: %d", - a.estimateSizeCert(len(previousBridges), len(previousClaims)), a.cfg.MaxCertSize) - return toBlock + 1, previousBridges, previousClaims, nil + previousCert.EstimatedSize(), a.cfg.MaxCertSize) + return previousCert, nil } - if a.cfg.MaxCertSize == 0 || a.estimateSizeCert(len(bridges), len(claims)) < a.cfg.MaxCertSize { - return toBlock, bridges, claims, nil + if a.cfg.MaxCertSize == 0 || currentCert.EstimatedSize() < a.cfg.MaxCertSize { + return currentCert, nil } + // Minimum size of the certificate - if fromBlock == toBlock { + if currentCert.NumberOfBlocks() <= 1 { a.log.Warnf("reach the minium num blocks [%d to %d].Certificate size: %d >max size: %d", - fromBlock, toBlock, a.estimateSizeCert(len(bridges), len(claims)), a.cfg.MaxCertSize) - return toBlock, bridges, claims, nil + currentCert.FromBlock, currentCert.ToBlock, currentCert.EstimatedSize(), a.cfg.MaxCertSize) + return currentCert, nil + } + previousCert = currentCert + currentCert, err = currentCert.Range(currentCert.FromBlock, currentCert.ToBlock-1) + if err != nil { + return nil, fmt.Errorf("error reducing certificate: %w", err) } - - // We have to reduce the number of blocks - toBlock-- - previousBridges = bridges - previousClaims = claims } } -func (a *AggSender) estimateSizeCert(numBridges, numClaims int) uint { - return uint(numBridges*estimatedSizeBridgeExit + numClaims*estimatedSizeClaim) -} - // saveCertificate saves the certificate to a tmp file func (a *AggSender) saveCertificateToFile(signedCertificate *agglayer.SignedCertificate) { if signedCertificate == nil || a.cfg.SaveCertificatesToFilesPath == "" { @@ -380,25 +370,19 @@ func (a *AggSender) getNextHeightAndPreviousLER( // buildCertificate builds a certificate from the bridge events func (a *AggSender) buildCertificate(ctx context.Context, - bridges []bridgesync.Bridge, - claims []bridgesync.Claim, - lastSentCertificateInfo *types.CertificateInfo, - toBlock uint64) (*agglayer.Certificate, error) { - if len(bridges) == 0 && len(claims) == 0 { + certParams *types.CertificateLocalParams, + lastSentCertificateInfo *types.CertificateInfo) (*agglayer.Certificate, error) { + if certParams.IsEmpty() { return nil, errNoBridgesAndClaims } - bridgeExits := a.getBridgeExits(bridges) - importedBridgeExits, err := a.getImportedBridgeExits(ctx, claims) + bridgeExits := a.getBridgeExits(certParams.Bridges) + importedBridgeExits, err := a.getImportedBridgeExits(ctx, certParams.Claims) if err != nil { return nil, fmt.Errorf("error getting imported bridge exits: %w", err) } - var depositCount uint32 - - if len(bridges) > 0 { - depositCount = bridges[len(bridges)-1].DepositCount - } + depositCount := certParams.MaxDepoitCount() exitRoot, err := a.l2Syncer.GetExitRootByIndex(ctx, depositCount) if err != nil { @@ -417,7 +401,7 @@ func (a *AggSender) buildCertificate(ctx context.Context, BridgeExits: bridgeExits, ImportedBridgeExits: importedBridgeExits, Height: height, - Metadata: createCertificateMetadata(toBlock), + Metadata: createCertificateMetadata(certParams.ToBlock), }, nil } diff --git a/aggsender/aggsender_test.go b/aggsender/aggsender_test.go index a54853ad..f9495b99 100644 --- a/aggsender/aggsender_test.go +++ b/aggsender/aggsender_test.go @@ -787,7 +787,13 @@ func TestBuildCertificate(t *testing.T) { l1infoTreeSyncer: mockL1InfoTreeSyncer, log: log.WithFields("test", "unittest"), } - cert, err := aggSender.buildCertificate(context.Background(), tt.bridges, tt.claims, &tt.lastSentCertificateInfo, tt.toBlock) + + certParam := &aggsendertypes.CertificateLocalParams{ + ToBlock: tt.toBlock, + Bridges: tt.bridges, + Claims: tt.claims, + } + cert, err := aggSender.buildCertificate(context.Background(), certParam, &tt.lastSentCertificateInfo) if tt.expectedError { require.Error(t, err) @@ -1917,83 +1923,53 @@ func TestCheckLastCertificateFromAgglayer_Case4ErrorUpdateStatus(t *testing.T) { func TestLimitSize_FirstOneFit(t *testing.T) { testData := newAggsenderTestData(t, testDataFlagMockStorage) - fromBlock := uint64(1) - toBlock := uint64(20) - // It returns 1 bridge - testData.l2syncerMock.EXPECT().GetBridgesPublished(testData.ctx, fromBlock, toBlock).Return([]bridgesync.Bridge{ - {}, - }, nil) - testData.l2syncerMock.EXPECT().GetClaims(testData.ctx, fromBlock, toBlock).Return([]bridgesync.Claim{}, nil) - newToBlock, _, _, err := testData.sut.limitCertSize(testData.ctx, fromBlock, toBlock) + certParams := &aggsendertypes.CertificateLocalParams{ + FromBlock: uint64(1), + ToBlock: uint64(20), + Bridges: NewBridgesData(1, []uint64{1}), + } + newCert, err := testData.sut.limitCertSize(certParams) require.NoError(t, err) - require.Equal(t, toBlock, newToBlock) + require.Equal(t, certParams, newCert) } func TestLimitSize_FirstMinusOneFit(t *testing.T) { testData := newAggsenderTestData(t, testDataFlagMockStorage) - testData.sut.cfg.MaxCertSize = (estimatedSizeBridgeExit * 3) + 1 - fromBlock := uint64(1) - toBlock := uint64(20) - // It returns 4 bridge - testData.l2syncerMock.EXPECT().GetBridgesPublished(testData.ctx, fromBlock, toBlock).Return([]bridgesync.Bridge{ - {}, {}, {}, {}, - }, nil).Once() - - // fromBlock is 1, toBlock is 20, so it will return 3 - testData.l2syncerMock.EXPECT().GetBridgesPublished(testData.ctx, fromBlock, toBlock-1).Return([]bridgesync.Bridge{ - {}, {}, {}, - }, nil).Once() - testData.l2syncerMock.EXPECT().GetClaims(testData.ctx, fromBlock, mock.Anything).Return([]bridgesync.Claim{}, nil) - newToBlock, _, _, err := testData.sut.limitCertSize(testData.ctx, fromBlock, toBlock) + testData.sut.cfg.MaxCertSize = (aggsendertypes.EstimatedSizeBridgeExit * 3) + 1 + certParams := &aggsendertypes.CertificateLocalParams{ + FromBlock: uint64(1), + ToBlock: uint64(20), + Bridges: NewBridgesData(4, []uint64{19, 19, 19, 20}), + } + newCert, err := testData.sut.limitCertSize(certParams) require.NoError(t, err) - require.Equal(t, toBlock-1, newToBlock) + require.Equal(t, uint64(19), newCert.ToBlock) } func TestLimitSize_NoWayToFitInMaxSize(t *testing.T) { testData := newAggsenderTestData(t, testDataFlagMockStorage) - testData.sut.cfg.MaxCertSize = (estimatedSizeBridgeExit * 2) + 1 - fromBlock := uint64(1) - toBlock := uint64(20) - // It returns 4 bridge - testData.l2syncerMock.EXPECT().GetBridgesPublished(testData.ctx, fromBlock, toBlock).Return([]bridgesync.Bridge{ - {}, {}, {}, {}, - }, nil).Once() - - // fromBlock is 1, toBlock is 19, so it will return 3 that is bigger hat maxSize - testData.l2syncerMock.EXPECT().GetBridgesPublished(testData.ctx, fromBlock, toBlock-1).Return([]bridgesync.Bridge{ - {}, {}, {}, - }, nil).Once() - - // fromBlock is 1, toBlock is 18, so it will return 0. So must use previous one even that is > maxSize - testData.l2syncerMock.EXPECT().GetBridgesPublished(testData.ctx, fromBlock, toBlock-2).Return([]bridgesync.Bridge{}, nil).Once() - testData.l2syncerMock.EXPECT().GetClaims(testData.ctx, fromBlock, mock.Anything).Return([]bridgesync.Claim{}, nil) - newToBlock, _, _, err := testData.sut.limitCertSize(testData.ctx, fromBlock, toBlock) + testData.sut.cfg.MaxCertSize = (aggsendertypes.EstimatedSizeBridgeExit * 2) + 1 + certParams := &aggsendertypes.CertificateLocalParams{ + FromBlock: uint64(1), + ToBlock: uint64(20), + Bridges: NewBridgesData(4, []uint64{19, 19, 19, 20}), + } + newCert, err := testData.sut.limitCertSize(certParams) require.NoError(t, err) - require.Equal(t, toBlock-1, newToBlock) -} - -func TestLimitSize_InvalidCall(t *testing.T) { - testData := newAggsenderTestData(t, testDataFlagMockStorage) - _, _, _, err := testData.sut.limitCertSize(testData.ctx, 10, 5) //nolint: dogsled - require.Error(t, err) + require.Equal(t, uint64(19), newCert.ToBlock) } func TestLimitSize_MinNumBlocks(t *testing.T) { testData := newAggsenderTestData(t, testDataFlagMockStorage) - testData.sut.cfg.MaxCertSize = (estimatedSizeBridgeExit * 2) + 1 - // It returns 4 bridge - testData.l2syncerMock.EXPECT().GetBridgesPublished(testData.ctx, uint64(1), uint64(2)).Return([]bridgesync.Bridge{ - {}, {}, {}, {}, - }, nil).Once() - - // fromBlock is 1, toBlock is 19, so it will return 3 that is bigger hat maxSize - testData.l2syncerMock.EXPECT().GetBridgesPublished(testData.ctx, uint64(1), uint64(1)).Return([]bridgesync.Bridge{ - {}, {}, {}, - }, nil).Once() - testData.l2syncerMock.EXPECT().GetClaims(testData.ctx, uint64(1), mock.Anything).Return([]bridgesync.Claim{}, nil) - newToBlock, _, _, err := testData.sut.limitCertSize(testData.ctx, 1, 2) + testData.sut.cfg.MaxCertSize = (aggsendertypes.EstimatedSizeBridgeExit * 2) + 1 + certParams := &aggsendertypes.CertificateLocalParams{ + FromBlock: uint64(1), + ToBlock: uint64(2), + Bridges: NewBridgesData(6, []uint64{1, 1, 1, 2, 2, 2}), + } + newCert, err := testData.sut.limitCertSize(certParams) require.NoError(t, err) - require.Equal(t, uint64(1), newToBlock) + require.Equal(t, uint64(1), newCert.ToBlock) } type testDataFlags = int @@ -2013,6 +1989,31 @@ type aggsenderTestData struct { testCerts []aggsendertypes.CertificateInfo } +func NewBridgesData(num int, blockNum []uint64) []bridgesync.Bridge { + res := make([]bridgesync.Bridge, 0) + for i := 0; i < num; i++ { + res = append(res, bridgesync.Bridge{ + BlockNum: blockNum[i%len(blockNum)], + BlockPos: 0, + LeafType: agglayer.LeafTypeAsset.Uint8(), + OriginNetwork: 1, + }) + } + return res +} + +func NewClaimData(num int, blockNum []uint64) []bridgesync.Claim { + res := make([]bridgesync.Claim, 0) + for i := 0; i < num; i++ { + res = append(res, bridgesync.Claim{ + BlockNum: blockNum[i%len(blockNum)], + BlockPos: 0, + }) + } + return res + +} + func certInfoToCertHeader(certInfo *aggsendertypes.CertificateInfo, networkID uint32) *agglayer.CertificateHeader { if certInfo == nil { return nil diff --git a/aggsender/types/certificate_params.go b/aggsender/types/certificate_params.go new file mode 100644 index 00000000..bb118e88 --- /dev/null +++ b/aggsender/types/certificate_params.go @@ -0,0 +1,92 @@ +package types + +import ( + "fmt" + + "github.com/0xPolygon/cdk/bridgesync" +) + +const ( + EstimatedSizeBridgeExit = 250 + EstimatedSizeClaim = 44000 +) + +type CertificateLocalParams struct { + FromBlock uint64 + ToBlock uint64 + Bridges []bridgesync.Bridge + Claims []bridgesync.Claim +} + +func (c *CertificateLocalParams) String() string { + return fmt.Sprintf("FromBlock: %d, ToBlock: %d, numBridges: %d, numClaims: %d", c.FromBlock, c.ToBlock, c.NumberOfBridges(), c.NumberOfClaims()) +} + +func (c *CertificateLocalParams) Range(fromBlock, toBlock uint64) (*CertificateLocalParams, error) { + if c.FromBlock == fromBlock && c.ToBlock == toBlock { + return c, nil + } + if c.FromBlock > fromBlock || c.ToBlock < toBlock { + return nil, fmt.Errorf("invalid range") + } + newCert := &CertificateLocalParams{ + FromBlock: fromBlock, + ToBlock: toBlock, + Bridges: make([]bridgesync.Bridge, 0), + Claims: make([]bridgesync.Claim, 0), + } + + for _, bridge := range c.Bridges { + if bridge.BlockNum >= fromBlock && bridge.BlockNum <= toBlock { + newCert.Bridges = append(newCert.Bridges, bridge) + } + } + + for _, claim := range c.Claims { + if claim.BlockNum >= fromBlock && claim.BlockNum <= toBlock { + newCert.Claims = append(newCert.Claims, claim) + } + } + return newCert, nil +} + +func (c *CertificateLocalParams) NumberOfBridges() int { + if c == nil { + return 0 + } + return len(c.Bridges) +} + +func (c *CertificateLocalParams) NumberOfClaims() int { + if c == nil { + return 0 + } + return len(c.Claims) +} + +func (c *CertificateLocalParams) NumberOfBlocks() int { + if c == nil { + return 0 + } + return int(c.ToBlock - c.FromBlock + 1) +} + +func (c *CertificateLocalParams) EstimatedSize() uint { + if c == nil { + return 0 + } + numBridges := len(c.Bridges) + numClaims := len(c.Claims) + return uint(numBridges*EstimatedSizeBridgeExit + numClaims*EstimatedSizeClaim) +} + +func (c *CertificateLocalParams) IsEmpty() bool { + return c.NumberOfBridges() == 0 && c.NumberOfClaims() == 0 +} + +func (c *CertificateLocalParams) MaxDepoitCount() uint32 { + if c == nil || c.NumberOfBridges() == 0 { + return 0 + } + return c.Bridges[len(c.Bridges)-1].DepositCount +}