Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: limit cert by estimated size #217

Merged
merged 11 commits into from
Dec 3, 2024
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 50 additions & 15 deletions aggsender/aggsender.go
Original file line number Diff line number Diff line change
Expand Up @@ -191,10 +191,21 @@ func (a *AggSender) sendCertificate(ctx context.Context) (*agglayer.SignedCertif
if err != nil {
return nil, fmt.Errorf("error getting claims: %w", err)
}
certificateParams := &types.CertificateLocalParams{
FromBlock: fromBlock,
ToBlock: toBlock,
Bridges: bridges,
Claims: claims,
}

a.log.Infof("building certificate for block: %d to block: %d", fromBlock, toBlock)
certificateParams, err = a.limitCertSize(certificateParams)
if err != nil {
return nil, fmt.Errorf("error limitCertSize: %w", err)
}
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)
}
Expand Down Expand Up @@ -265,6 +276,36 @@ func (a *AggSender) saveCertificateToStorage(ctx context.Context, cert types.Cer
return nil
}

func (a *AggSender) limitCertSize(fullCert *types.CertificateLocalParams) (*types.CertificateLocalParams, error) {
currentCert := fullCert
var previousCert *types.CertificateLocalParams
var err error
for {
if currentCert.NumberOfBridges() == 0 {
// We can't reduce more the certificate, so this is the minium size
a.log.Warnf("We reach the minium size of bridge.Certificate size: %d >max size: %d",
previousCert.EstimatedSize(), a.cfg.MaxCertSize)
return previousCert, nil
}

if a.cfg.MaxCertSize == 0 || currentCert.EstimatedSize() < a.cfg.MaxCertSize {
return currentCert, nil
}

// Minimum size of the certificate
if currentCert.NumberOfBlocks() <= 1 {
a.log.Warnf("reach the minium num blocks [%d to %d].Certificate size: %d >max size: %d",
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)
}
}
}

// saveCertificate saves the certificate to a tmp file
func (a *AggSender) saveCertificateToFile(signedCertificate *agglayer.SignedCertificate) {
if signedCertificate == nil || a.cfg.SaveCertificatesToFilesPath == "" {
Expand Down Expand Up @@ -329,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 {
Expand All @@ -366,7 +401,7 @@ func (a *AggSender) buildCertificate(ctx context.Context,
BridgeExits: bridgeExits,
ImportedBridgeExits: importedBridgeExits,
Height: height,
Metadata: createCertificateMetadata(toBlock),
Metadata: createCertificateMetadata(certParams.ToBlock),
}, nil
}

Expand Down
95 changes: 89 additions & 6 deletions aggsender/aggsender_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -787,7 +787,13 @@
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)
Expand Down Expand Up @@ -993,7 +999,7 @@
mockL2Syncer.On("GetLastProcessedBlock", mock.Anything).Return(cfg.lastL2BlockProcessed...).Once()

if cfg.getBridges != nil {
mockL2Syncer.On("GetBridgesPublished", mock.Anything, mock.Anything, mock.Anything).Return(cfg.getBridges...).Once()
mockL2Syncer.On("GetBridgesPublished", mock.Anything, mock.Anything, mock.Anything).Return(cfg.getBridges...)
}

if cfg.getClaims != nil {
Expand Down Expand Up @@ -1643,8 +1649,6 @@
}

func TestSendCertificate_NoClaims(t *testing.T) {
t.Parallel()

privateKey, err := crypto.GenerateKey()
require.NoError(t, err)

Expand Down Expand Up @@ -1690,8 +1694,8 @@
Metadata: []byte("metadata"),
DepositCount: 1,
},
}, nil).Once()
mockL2Syncer.On("GetClaims", mock.Anything, uint64(11), uint64(50)).Return([]bridgesync.Claim{}, nil).Once()
}, nil)
mockL2Syncer.On("GetClaims", mock.Anything, uint64(11), uint64(50)).Return([]bridgesync.Claim{}, nil)
mockL2Syncer.On("GetExitRootByIndex", mock.Anything, uint32(1)).Return(treeTypes.Root{}, nil).Once()
mockL2Syncer.On("OriginNetwork").Return(uint32(1), nil).Once()
mockAggLayerClient.On("SendCertificate", mock.Anything).Return(common.Hash{}, nil).Once()
Expand Down Expand Up @@ -1917,6 +1921,57 @@
require.Error(t, err)
}

func TestLimitSize_FirstOneFit(t *testing.T) {
testData := newAggsenderTestData(t, testDataFlagMockStorage)
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, certParams, newCert)
}

func TestLimitSize_FirstMinusOneFit(t *testing.T) {
testData := newAggsenderTestData(t, testDataFlagMockStorage)
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, uint64(19), newCert.ToBlock)
}

func TestLimitSize_NoWayToFitInMaxSize(t *testing.T) {
testData := newAggsenderTestData(t, testDataFlagMockStorage)
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, uint64(19), newCert.ToBlock)
}

func TestLimitSize_MinNumBlocks(t *testing.T) {
testData := newAggsenderTestData(t, testDataFlagMockStorage)
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), newCert.ToBlock)
}

type testDataFlags = int

const (
Expand All @@ -1934,6 +1989,31 @@
testCerts []aggsendertypes.CertificateInfo
}

func NewBridgesData(num int, blockNum []uint64) []bridgesync.Bridge {
joanestebanr marked this conversation as resolved.
Show resolved Hide resolved
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 {
joanestebanr marked this conversation as resolved.
Show resolved Hide resolved
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

}

Check failure on line 2015 in aggsender/aggsender_test.go

View workflow job for this annotation

GitHub Actions / lint

unnecessary trailing newline (whitespace)

func certInfoToCertHeader(certInfo *aggsendertypes.CertificateInfo, networkID uint32) *agglayer.CertificateHeader {
if certInfo == nil {
return nil
Expand Down Expand Up @@ -1979,6 +2059,9 @@
aggLayerClient: agglayerClientMock,
storage: storage,
l1infoTreeSyncer: l1InfoTreeSyncerMock,
cfg: Config{
MaxCertSize: 1024 * 1024,
},
}
testCerts := []aggsendertypes.CertificateInfo{
{
Expand Down
3 changes: 3 additions & 0 deletions aggsender/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ type Config struct {
DelayBeetweenRetries types.Duration `mapstructure:"DelayBeetweenRetries"`
// KeepCertificatesHistory is a flag to keep the certificates history on storage
KeepCertificatesHistory bool `mapstructure:"KeepCertificatesHistory"`
// MaxCertSize is the maximum size of the certificate (the emitted certificate can be bigger that this size)
// 0 is infinite
MaxCertSize uint `mapstructure:"MaxCertSize"`
}

// String returns a string representation of the Config
Expand Down
92 changes: 92 additions & 0 deletions aggsender/types/certificate_params.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package types

import (
"fmt"

"github.com/0xPolygon/cdk/bridgesync"
)

const (
EstimatedSizeBridgeExit = 250
EstimatedSizeClaim = 44000
)

type CertificateLocalParams struct {
joanestebanr marked this conversation as resolved.
Show resolved Hide resolved
joanestebanr marked this conversation as resolved.
Show resolved Hide resolved
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
}
2 changes: 2 additions & 0 deletions config/default.go
Original file line number Diff line number Diff line change
Expand Up @@ -340,4 +340,6 @@ SaveCertificatesToFilesPath = ""
MaxRetriesStoreCertificate = 3
DelayBeetweenRetries = "60s"
KeepCertificatesHistory = true
# MaxSize of the certificate to 8Mb
MaxCertSize = 8388608
`
Loading