Skip to content

Commit

Permalink
refactor: encode metadata to be backwards compatible (#216)
Browse files Browse the repository at this point in the history
* refactor: encode metadata to be backwards compatible

* tests: fix UT

* apply feedback

* test: fix

* fix merge issues

* refactor: metadata cert versioning

* test: fix ut

* fix: typo

* use the CertificateBuildParams

* test: fix ut

* Apply feedback

* fix: lint
  • Loading branch information
vcastellm authored Dec 6, 2024
1 parent 07d36a9 commit 0873bb5
Show file tree
Hide file tree
Showing 6 changed files with 154 additions and 50 deletions.
45 changes: 25 additions & 20 deletions aggsender/aggsender.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"encoding/json"
"errors"
"fmt"
"math/big"
"os"
"time"

Expand Down Expand Up @@ -196,6 +195,7 @@ func (a *AggSender) sendCertificate(ctx context.Context) (*agglayer.SignedCertif
ToBlock: toBlock,
Bridges: bridges,
Claims: claims,
CreatedAt: uint32(time.Now().UTC().Unix()),
}

certificateParams, err = a.limitCertSize(certificateParams)
Expand Down Expand Up @@ -229,7 +229,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,
Expand All @@ -239,8 +238,8 @@ func (a *AggSender) sendCertificate(ctx context.Context) (*agglayer.SignedCertif
PreviousLocalExitRoot: &prevLER,
FromBlock: fromBlock,
ToBlock: toBlock,
CreatedAt: createdTime,
UpdatedAt: createdTime,
CreatedAt: certificateParams.CreatedAt,
UpdatedAt: certificateParams.CreatedAt,
SignedCertificate: string(raw),
}
// TODO: Improve this case, if a cert is not save in the storage, we are going to settle a unknown certificate
Expand Down Expand Up @@ -382,7 +381,7 @@ func (a *AggSender) buildCertificate(ctx context.Context,
return nil, fmt.Errorf("error getting imported bridge exits: %w", err)
}

depositCount := certParams.MaxDepoitCount()
depositCount := certParams.MaxDepositCount()

exitRoot, err := a.l2Syncer.GetExitRootByIndex(ctx, depositCount)
if err != nil {
Expand All @@ -394,14 +393,20 @@ func (a *AggSender) buildCertificate(ctx context.Context,
return nil, fmt.Errorf("error getting next height and previous LER: %w", err)
}

meta := types.NewCertificateMetadata(
certParams.FromBlock,
uint32(certParams.ToBlock-certParams.FromBlock),
certParams.CreatedAt,
)

return &agglayer.Certificate{
NetworkID: a.l2Syncer.OriginNetwork(),
PrevLocalExitRoot: previousLER,
NewLocalExitRoot: exitRoot.Hash,
BridgeExits: bridgeExits,
ImportedBridgeExits: importedBridgeExits,
Height: height,
Metadata: createCertificateMetadata(certParams.ToBlock),
Metadata: meta.ToHash(),
}, nil
}

Expand Down Expand Up @@ -677,7 +682,7 @@ func (a *AggSender) updateCertificateStatus(ctx context.Context,
}

localCert.Status = agglayerCert.Status
localCert.UpdatedAt = time.Now().UTC().UnixMilli()
localCert.UpdatedAt = uint32(time.Now().UTC().Unix())
if err := a.storage.UpdateCertificate(ctx, *localCert); err != nil {
a.log.Errorf("error updating certificate %s status in storage: %w", agglayerCert.ID(), err)
return fmt.Errorf("error updating certificate. Err: %w", err)
Expand Down Expand Up @@ -788,28 +793,28 @@ 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
}
now := time.Now().UTC().UnixMilli()
now := uint32(time.Now().UTC().Unix())
meta := types.NewCertificateMetadataFromHash(c.Metadata)
toBlock := meta.FromBlock + uint64(meta.Offset)
createdAt := meta.CreatedAt

if meta.Version < 1 {
toBlock = meta.ToBlock
createdAt = now
}

res := &types.CertificateInfo{
Height: c.Height,
CertificateID: c.CertificateID,
NewLocalExitRoot: c.NewLocalExitRoot,
FromBlock: 0,
ToBlock: extractFromCertificateMetadataToBlock(c.Metadata),
FromBlock: meta.FromBlock,
ToBlock: toBlock,
Status: c.Status,
CreatedAt: now,
CreatedAt: createdAt,
UpdatedAt: now,
SignedCertificate: "na/agglayer header",
}
Expand Down
42 changes: 22 additions & 20 deletions aggsender/aggsender_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -631,7 +631,7 @@ func TestBuildCertificate(t *testing.T) {
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,
Expand Down Expand Up @@ -1703,35 +1703,33 @@ func TestSendCertificate_NoClaims(t *testing.T) {
mockL1InfoTreeSyncer.AssertExpectations(t)
}

func TestMetadataConversions(t *testing.T) {
toBlock := uint64(123567890)
c := createCertificateMetadata(toBlock)
extractBlock := extractFromCertificateMetadataToBlock(c)
require.Equal(t, toBlock, extractBlock)
}

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

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, 1000, 123567890).ToHash(),
expected: aggsendertypes.CertificateMetadata{
Version: 1,
FromBlock: 0,
Offset: 1000,
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{
Version: 1,
FromBlock: 0,
Offset: 0,
CreatedAt: 0,
},
},
}

Expand All @@ -1741,7 +1739,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)
})
}
Expand Down Expand Up @@ -2021,7 +2019,11 @@ func certInfoToCertHeader(t *testing.T, certInfo *aggsendertypes.CertificateInfo
CertificateID: certInfo.CertificateID,
NewLocalExitRoot: certInfo.NewLocalExitRoot,
Status: agglayer.Pending,
Metadata: createCertificateMetadata(certInfo.ToBlock),
Metadata: aggsendertypes.NewCertificateMetadata(
certInfo.FromBlock,
uint32(certInfo.FromBlock-certInfo.ToBlock),
certInfo.CreatedAt,
).ToHash(),
}
}

Expand Down
4 changes: 2 additions & 2 deletions aggsender/db/aggsender_db_storage_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ func Test_Storage(t *testing.T) {
storage, err := NewAggSenderSQLStorage(log.WithFields("aggsender-db"), cfg)
require.NoError(t, err)

updateTime := time.Now().UTC().UnixMilli()
updateTime := uint32(time.Now().UTC().UnixMilli())

t.Run("SaveLastSentCertificate", func(t *testing.T) {
certificate := types.CertificateInfo{
Expand Down Expand Up @@ -242,7 +242,7 @@ func Test_SaveLastSentCertificate(t *testing.T) {
storage, err := NewAggSenderSQLStorage(log.WithFields("aggsender-db"), cfg)
require.NoError(t, err)

updateTime := time.Now().UTC().UnixMilli()
updateTime := uint32(time.Now().UTC().UnixMilli())

t.Run("SaveNewCertificate", func(t *testing.T) {
certificate := types.CertificateInfo{
Expand Down
7 changes: 4 additions & 3 deletions aggsender/types/certificate_build_params.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,12 @@ type CertificateBuildParams struct {
ToBlock uint64
Bridges []bridgesync.Bridge
Claims []bridgesync.Claim
CreatedAt uint32
}

func (c *CertificateBuildParams) String() string {
return fmt.Sprintf("FromBlock: %d, ToBlock: %d, numBridges: %d, numClaims: %d",
c.FromBlock, c.ToBlock, c.NumberOfBridges(), c.NumberOfClaims())
return fmt.Sprintf("FromBlock: %d, ToBlock: %d, numBridges: %d, numClaims: %d, createdAt: %d",
c.FromBlock, c.ToBlock, c.NumberOfBridges(), c.NumberOfClaims(), c.CreatedAt)
}

// Range create a new CertificateBuildParams with the given range
Expand Down Expand Up @@ -103,7 +104,7 @@ func (c *CertificateBuildParams) IsEmpty() bool {
}

// MaxDepoitCount returns the maximum deposit count in the certificate
func (c *CertificateBuildParams) MaxDepoitCount() uint32 {
func (c *CertificateBuildParams) MaxDepositCount() uint32 {
if c == nil || c.NumberOfBridges() == 0 {
return 0
}
Expand Down
78 changes: 73 additions & 5 deletions aggsender/types/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package types

import (
"context"
"encoding/binary"
"fmt"
"math/big"
"time"
Expand Down Expand Up @@ -64,8 +65,8 @@ type CertificateInfo struct {
FromBlock uint64 `meddler:"from_block"`
ToBlock uint64 `meddler:"to_block"`
Status agglayer.CertificateStatus `meddler:"status"`
CreatedAt int64 `meddler:"created_at"`
UpdatedAt int64 `meddler:"updated_at"`
CreatedAt uint32 `meddler:"created_at"`
UpdatedAt uint32 `meddler:"updated_at"`
SignedCertificate string `meddler:"signed_certificate"`
}

Expand Down Expand Up @@ -97,8 +98,8 @@ func (c *CertificateInfo) String() string {
c.Status.String(),
c.FromBlock,
c.ToBlock,
time.UnixMilli(c.CreatedAt),
time.UnixMilli(c.UpdatedAt),
time.Unix(int64(c.CreatedAt), 0),
time.Unix(int64(c.UpdatedAt), 0),
)
}

Expand All @@ -123,5 +124,72 @@ func (c *CertificateInfo) ElapsedTimeSinceCreation() time.Duration {
if c == nil {
return 0
}
return time.Now().UTC().Sub(time.UnixMilli(c.CreatedAt))
return time.Now().UTC().Sub(time.Unix(int64(c.CreatedAt), 0))
}

type CertificateMetadata struct {
// ToBlock contains the pre v1 value stored in the metadata certificate field
// is not stored in the hash post v1
ToBlock uint64

// FromBlock is the block number from which the certificate contains data
FromBlock uint64

// Offset is the number of blocks from the FromBlock that the certificate contains
Offset uint32

// CreatedAt is the timestamp when the certificate was created
CreatedAt uint32

// Version is the version of the metadata
Version uint8
}

// NewCertificateMetadataFromHash returns a new CertificateMetadata from the given hash
func NewCertificateMetadata(fromBlock uint64, offset uint32, createdAt uint32) *CertificateMetadata {
return &CertificateMetadata{
FromBlock: fromBlock,
Offset: offset,
CreatedAt: createdAt,
Version: 1,
}
}

// NewCertificateMetadataFromHash returns a new CertificateMetadata from the given hash
func NewCertificateMetadataFromHash(hash common.Hash) *CertificateMetadata {
b := hash.Bytes()

if b[0] < 1 {
return &CertificateMetadata{
ToBlock: hash.Big().Uint64(),
}
}

return &CertificateMetadata{
Version: b[0],
FromBlock: binary.BigEndian.Uint64(b[1:9]),
Offset: binary.BigEndian.Uint32(b[9:13]),
CreatedAt: binary.BigEndian.Uint32(b[13:17]),
}
}

// ToHash returns the hash of the metadata
func (c *CertificateMetadata) ToHash() common.Hash {
b := make([]byte, common.HashLength) // 32-byte hash

// Encode version
b[0] = c.Version

// Encode fromBlock
binary.BigEndian.PutUint64(b[1:9], c.FromBlock)

// Encode offset
binary.BigEndian.PutUint32(b[9:13], c.Offset)

// Encode createdAt
binary.BigEndian.PutUint32(b[13:17], c.CreatedAt)

// Last 8 bytes remain as zero padding

return common.BytesToHash(b)
}
28 changes: 28 additions & 0 deletions aggsender/types/types_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package types

import (
"math/big"
"testing"

"github.com/ethereum/go-ethereum/common"
"github.com/stretchr/testify/require"
)

func TestMetadataConversions_toBlock_Only(t *testing.T) {
toBlock := uint64(123567890)
hash := common.BigToHash(new(big.Int).SetUint64(toBlock))
meta := NewCertificateMetadataFromHash(hash)
require.Equal(t, toBlock, meta.ToBlock)
}

func TestMetadataConversions(t *testing.T) {
fromBlock := uint64(123567890)
offset := uint32(1000)
createdAt := uint32(0)
meta := NewCertificateMetadata(fromBlock, offset, createdAt)
c := meta.ToHash()
extractBlock := NewCertificateMetadataFromHash(c)
require.Equal(t, fromBlock, extractBlock.FromBlock)
require.Equal(t, offset, extractBlock.Offset)
require.Equal(t, createdAt, extractBlock.CreatedAt)
}

0 comments on commit 0873bb5

Please sign in to comment.