From 085f17609d538a8e94d836c73a11bf54ebf965de Mon Sep 17 00:00:00 2001 From: Victor Castell Date: Fri, 29 Nov 2024 12:47:47 +0100 Subject: [PATCH] feat(aggsender): add more data to certificate metadata (#200) New struct CertificateMetadata to represent the data serialized and deserialized to the Certificate.metadata field. The new data, FromBlock and CreatedAt is stored in the hash as bigints --- aggsender/aggsender.go | 38 ++++++++++++------------ aggsender/aggsender_test.go | 58 +++++++++++++++++++++++++------------ aggsender/types/types.go | 45 ++++++++++++++++++++++++++++ 3 files changed, 104 insertions(+), 37 deletions(-) diff --git a/aggsender/aggsender.go b/aggsender/aggsender.go index 42e518fd2..db4e4912c 100644 --- a/aggsender/aggsender.go +++ b/aggsender/aggsender.go @@ -6,7 +6,6 @@ import ( "encoding/json" "errors" "fmt" - "math/big" "os" "time" @@ -188,7 +187,8 @@ func (a *AggSender) sendCertificate(ctx context.Context) (*agglayer.SignedCertif a.log.Infof("building certificate for block: %d to block: %d", fromBlock, toBlock) - certificate, err := a.buildCertificate(ctx, bridges, claims, lastSentCertificateInfo, toBlock) + createdTime := uint64(time.Now().UTC().UnixMilli()) + certificate, err := a.buildCertificate(ctx, bridges, claims, lastSentCertificateInfo, fromBlock, toBlock, createdTime) if err != nil { return nil, fmt.Errorf("error building certificate: %w", err) } @@ -212,7 +212,6 @@ func (a *AggSender) sendCertificate(ctx context.Context) (*agglayer.SignedCertif return nil, fmt.Errorf("error marshalling signed certificate. Cert:%s. Err: %w", signedCertificate.Brief(), err) } - createdTime := time.Now().UTC().UnixMilli() prevLER := common.BytesToHash(certificate.PrevLocalExitRoot[:]) certInfo := types.CertificateInfo{ Height: certificate.Height, @@ -221,8 +220,8 @@ func (a *AggSender) sendCertificate(ctx context.Context) (*agglayer.SignedCertif PreviousLocalExitRoot: &prevLER, FromBlock: fromBlock, ToBlock: toBlock, - CreatedAt: createdTime, - UpdatedAt: createdTime, + CreatedAt: int64(createdTime), + UpdatedAt: int64(createdTime), SignedCertificate: string(raw), } // TODO: Improve this case, if a cert is not save in the storage, we are going to settle a unknown certificate @@ -325,7 +324,9 @@ func (a *AggSender) buildCertificate(ctx context.Context, bridges []bridgesync.Bridge, claims []bridgesync.Claim, lastSentCertificateInfo *types.CertificateInfo, - toBlock uint64) (*agglayer.Certificate, error) { + fromBlock, toBlock uint64, + createdAt uint64, +) (*agglayer.Certificate, error) { if len(bridges) == 0 && len(claims) == 0 { return nil, errNoBridgesAndClaims } @@ -352,6 +353,12 @@ func (a *AggSender) buildCertificate(ctx context.Context, return nil, fmt.Errorf("error getting next height and previous LER: %w", err) } + meta := &types.CertificateMetadata{ + FromBlock: fromBlock, + ToBlock: toBlock, + CreatedAt: createdAt, + } + return &agglayer.Certificate{ NetworkID: a.l2Syncer.OriginNetwork(), PrevLocalExitRoot: previousLER, @@ -359,7 +366,7 @@ func (a *AggSender) buildCertificate(ctx context.Context, BridgeExits: bridgeExits, ImportedBridgeExits: importedBridgeExits, Height: height, - Metadata: createCertificateMetadata(toBlock), + Metadata: meta.ToHash(), }, nil } @@ -722,28 +729,21 @@ func extractSignatureData(signature []byte) (r, s common.Hash, isOddParity bool, return } -// createCertificateMetadata creates a certificate metadata from given input -func createCertificateMetadata(toBlock uint64) common.Hash { - return common.BigToHash(new(big.Int).SetUint64(toBlock)) -} - -func extractFromCertificateMetadataToBlock(metadata common.Hash) uint64 { - return metadata.Big().Uint64() -} - func NewCertificateInfoFromAgglayerCertHeader(c *agglayer.CertificateHeader) *types.CertificateInfo { if c == nil { return nil } + meta := types.NewCertificateMetadataFromHash(c.Metadata) now := time.Now().UTC().UnixMilli() + res := &types.CertificateInfo{ Height: c.Height, CertificateID: c.CertificateID, NewLocalExitRoot: c.NewLocalExitRoot, - FromBlock: 0, - ToBlock: extractFromCertificateMetadataToBlock(c.Metadata), + FromBlock: meta.FromBlock, + ToBlock: meta.ToBlock, Status: c.Status, - CreatedAt: now, + CreatedAt: int64(meta.CreatedAt), UpdatedAt: now, SignedCertificate: "na/agglayer header", } diff --git a/aggsender/aggsender_test.go b/aggsender/aggsender_test.go index 79504f6a0..26320bd1c 100644 --- a/aggsender/aggsender_test.go +++ b/aggsender/aggsender_test.go @@ -590,6 +590,7 @@ func TestBuildCertificate(t *testing.T) { bridges []bridgesync.Bridge claims []bridgesync.Claim lastSentCertificateInfo aggsendertypes.CertificateInfo + fromBlock uint64 toBlock uint64 mockFn func() expectedCert *agglayer.Certificate @@ -631,12 +632,13 @@ func TestBuildCertificate(t *testing.T) { Height: 1, Status: agglayer.Settled, }, - toBlock: 10, + fromBlock: 0, + toBlock: 10, expectedCert: &agglayer.Certificate{ NetworkID: 1, PrevLocalExitRoot: common.HexToHash("0x123"), NewLocalExitRoot: common.HexToHash("0x789"), - Metadata: createCertificateMetadata(10), + Metadata: aggsendertypes.NewCertificateMetadata(0, 10, 0).ToHash(), BridgeExits: []*agglayer.BridgeExit{ { LeafType: agglayer.LeafTypeAsset, @@ -787,7 +789,15 @@ 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) + cert, err := aggSender.buildCertificate( + context.Background(), + tt.bridges, + tt.claims, + &tt.lastSentCertificateInfo, + tt.fromBlock, + tt.toBlock, + 0, + ) if tt.expectedError { require.Error(t, err) @@ -1711,10 +1721,15 @@ func TestSendCertificate_NoClaims(t *testing.T) { } func TestMetadataConversions(t *testing.T) { + fromBlock := uint64(123567890) toBlock := uint64(123567890) - c := createCertificateMetadata(toBlock) - extractBlock := extractFromCertificateMetadataToBlock(c) - require.Equal(t, toBlock, extractBlock) + createdAt := uint64(0) + meta := aggsendertypes.NewCertificateMetadata(fromBlock, toBlock, createdAt) + c := meta.ToHash() + extractBlock := aggsendertypes.NewCertificateMetadataFromHash(c) + require.Equal(t, fromBlock, extractBlock.FromBlock) + require.Equal(t, toBlock, extractBlock.ToBlock) + require.Equal(t, createdAt, extractBlock.CreatedAt) } func TestExtractFromCertificateMetadataToBlock(t *testing.T) { @@ -1723,22 +1738,25 @@ func TestExtractFromCertificateMetadataToBlock(t *testing.T) { tests := []struct { name string metadata common.Hash - expected uint64 + expected aggsendertypes.CertificateMetadata }{ { name: "Valid metadata", - metadata: common.BigToHash(big.NewInt(123567890)), - expected: 123567890, + metadata: aggsendertypes.NewCertificateMetadata(0, 123567890, 123567890).ToHash(), + expected: aggsendertypes.CertificateMetadata{ + FromBlock: 0, + ToBlock: 123567890, + CreatedAt: 123567890, + }, }, { name: "Zero metadata", - metadata: common.BigToHash(big.NewInt(0)), - expected: 0, - }, - { - name: "Max uint64 metadata", - metadata: common.BigToHash(new(big.Int).SetUint64(^uint64(0))), - expected: ^uint64(0), + metadata: aggsendertypes.NewCertificateMetadata(0, 0, 0).ToHash(), + expected: aggsendertypes.CertificateMetadata{ + FromBlock: 0, + ToBlock: 0, + CreatedAt: 0, + }, }, } @@ -1748,7 +1766,7 @@ func TestExtractFromCertificateMetadataToBlock(t *testing.T) { t.Run(tt.name, func(t *testing.T) { t.Parallel() - result := extractFromCertificateMetadataToBlock(tt.metadata) + result := *aggsendertypes.NewCertificateMetadataFromHash(tt.metadata) require.Equal(t, tt.expected, result) }) } @@ -1931,7 +1949,11 @@ func certInfoToCertHeader(certInfo *aggsendertypes.CertificateInfo, networkID ui CertificateID: certInfo.CertificateID, NewLocalExitRoot: certInfo.NewLocalExitRoot, Status: agglayer.Pending, - Metadata: createCertificateMetadata(certInfo.ToBlock), + Metadata: aggsendertypes.NewCertificateMetadata( + certInfo.FromBlock, + certInfo.ToBlock, + uint64(certInfo.CreatedAt), + ).ToHash(), } } diff --git a/aggsender/types/types.go b/aggsender/types/types.go index 1c0d8353e..73dc720d9 100644 --- a/aggsender/types/types.go +++ b/aggsender/types/types.go @@ -2,6 +2,7 @@ package types import ( "context" + "encoding/binary" "fmt" "math/big" "time" @@ -122,3 +123,47 @@ func (c *CertificateInfo) ElapsedTimeSinceCreation() time.Duration { } return time.Now().UTC().Sub(time.UnixMilli(c.CreatedAt)) } + +type CertificateMetadata struct { + FromBlock uint64 `json:"fromBlock"` + ToBlock uint64 `json:"toBlock"` + CreatedAt uint64 `json:"createdAt"` +} + +// NewCertificateMetadataFromHash returns a new CertificateMetadata from the given hash +func NewCertificateMetadata(fromBlock, toBlock, createdAt uint64) *CertificateMetadata { + return &CertificateMetadata{ + FromBlock: fromBlock, + ToBlock: toBlock, + CreatedAt: createdAt, + } +} + +// NewCertificateMetadataFromHash returns a new CertificateMetadata from the given hash +func NewCertificateMetadataFromHash(hash common.Hash) *CertificateMetadata { + b := hash.Bytes() + + return NewCertificateMetadata( + binary.BigEndian.Uint64(b[0:8]), + binary.BigEndian.Uint64(b[8:16]), + binary.BigEndian.Uint64(b[16:24]), + ) +} + +// ToHash returns the hash of the metadata +func (c *CertificateMetadata) ToHash() common.Hash { + b := make([]byte, common.HashLength) // 32-byte hash + + // Encode fromBlock into first 8 bytes + binary.BigEndian.PutUint64(b[0:8], c.FromBlock) + + // Encode toBlock into next 8 bytes + binary.BigEndian.PutUint64(b[8:16], c.ToBlock) + + // Encode createdAt into next 8 bytes + binary.BigEndian.PutUint64(b[16:24], c.CreatedAt) + + // Last 8 bytes remain as zero padding + + return common.BytesToHash(b) +}