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 7 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
59 changes: 55 additions & 4 deletions aggsender/aggsender.go
Original file line number Diff line number Diff line change
Expand Up @@ -187,12 +187,12 @@ func (a *AggSender) sendCertificate(ctx context.Context) (*agglayer.SignedCertif
return nil, nil
}

claims, err := a.l2Syncer.GetClaims(ctx, fromBlock, toBlock)
toBlock, bridges, claims, err := a.limitCertSize(ctx, fromBlock, toBlock)
joanestebanr marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
return nil, fmt.Errorf("error getting claims: %w", err)
return nil, fmt.Errorf("error limitCertSize: %w", err)
}

a.log.Infof("building certificate for block: %d to block: %d", fromBlock, toBlock)
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)))

certificate, err := a.buildCertificate(ctx, bridges, claims, lastSentCertificateInfo, toBlock)
if err != nil {
Expand Down Expand Up @@ -265,6 +265,57 @@ 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,
joanestebanr marked this conversation as resolved.
Show resolved Hide resolved
[]bridgesync.Bridge, []bridgesync.Claim, error) {
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)<fromBlock(%d)", toBlock, fromBlock)
}
for {
bridges, err = a.l2Syncer.GetBridgesPublished(ctx, fromBlock, toBlock)
if err != nil {
return 0, nil, nil, fmt.Errorf("error getting bridges: %w", err)
}
claims, err = a.l2Syncer.GetClaims(ctx, fromBlock, toBlock)
if err != nil {
return 0, nil, nil, fmt.Errorf("error getting claims: %w", err)
}

if len(bridges) == 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",
a.estimateSizeCert(len(previousBridges), len(previousClaims)), a.cfg.MaxCertSize)
return toBlock + 1, previousBridges, previousClaims, nil
}

if a.cfg.MaxCertSize == 0 || a.estimateSizeCert(len(bridges), len(claims)) < a.cfg.MaxCertSize {
return toBlock, bridges, claims, nil
}
// Minimum size of the certificate
if fromBlock == toBlock {
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
}

// 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 == "" {
Expand Down
92 changes: 87 additions & 5 deletions aggsender/aggsender_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -993,7 +993,7 @@ func TestSendCertificate(t *testing.T) {
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 +1643,6 @@ func TestGetNextHeightAndPreviousLER(t *testing.T) {
}

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

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

Expand Down Expand Up @@ -1690,8 +1688,8 @@ func TestSendCertificate_NoClaims(t *testing.T) {
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 +1915,87 @@ func TestCheckLastCertificateFromAgglayer_Case4ErrorUpdateStatus(t *testing.T) {
require.Error(t, err)
}

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)
require.NoError(t, err)
require.Equal(t, toBlock, newToBlock)
}

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)
require.NoError(t, err)
require.Equal(t, toBlock-1, newToBlock)
}

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)
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)
}

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)
require.NoError(t, err)
require.Equal(t, uint64(1), newToBlock)
}

type testDataFlags = int

const (
Expand Down Expand Up @@ -1979,6 +2058,9 @@ func newAggsenderTestData(t *testing.T, creationFlags testDataFlags) *aggsenderT
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
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