Skip to content

Commit

Permalink
feat: use metadata field on certificate
Browse files Browse the repository at this point in the history
  • Loading branch information
goran-ethernal committed Oct 31, 2024
1 parent 2a76deb commit 4c85ece
Show file tree
Hide file tree
Showing 5 changed files with 116 additions and 5 deletions.
21 changes: 20 additions & 1 deletion agglayer/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ type Certificate struct {
NewLocalExitRoot [32]byte `json:"new_local_exit_root"`
BridgeExits []*BridgeExit `json:"bridge_exits"`
ImportedBridgeExits []*ImportedBridgeExit `json:"imported_bridge_exits"`
Metadata common.Hash `json:"metadata"`
}

// Hash returns a hash that uniquely identifies the certificate
Expand Down Expand Up @@ -110,6 +111,20 @@ func (c *Certificate) Hash() common.Hash {
)
}

// HashToSign is the actual hash that needs to be signed by the aggsender
// as expected by the agglayer
func (c *Certificate) HashToSign() common.Hash {
globalIndexHashes := make([][]byte, len(c.ImportedBridgeExits))
for i, importedBridgeExit := range c.ImportedBridgeExits {
globalIndexHashes[i] = importedBridgeExit.GlobalIndex.Hash().Bytes()
}

return crypto.Keccak256Hash(
c.NewLocalExitRoot[:],
crypto.Keccak256Hash(globalIndexHashes...).Bytes(),
)
}

// SignedCertificate is the struct that contains the certificate and the signature of the signer
type SignedCertificate struct {
*Certificate
Expand Down Expand Up @@ -138,7 +153,10 @@ type GlobalIndex struct {

func (g *GlobalIndex) Hash() common.Hash {
return crypto.Keccak256Hash(
bridgesync.GenerateGlobalIndex(g.MainnetFlag, g.RollupIndex, g.LeafIndex).Bytes())
cdkcommon.AsLittleEndianSlice(
bridgesync.GenerateGlobalIndex(g.MainnetFlag, g.RollupIndex, g.LeafIndex),
),
)
}

// BridgeExit represents a token bridge exit
Expand Down Expand Up @@ -379,6 +397,7 @@ type CertificateHeader struct {
CertificateID common.Hash `json:"certificate_id"`
NewLocalExitRoot common.Hash `json:"new_local_exit_root"`
Status CertificateStatus `json:"status"`
Metadata common.Hash `json:"metadata"`
}

func (c CertificateHeader) String() string {
Expand Down
20 changes: 17 additions & 3 deletions aggsender/aggsender.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"encoding/json"
"errors"
"fmt"
"math/big"
"os"
"time"

Expand Down Expand Up @@ -153,7 +154,7 @@ func (a *AggSender) sendCertificate(ctx context.Context) error {

a.log.Infof("building certificate for block: %d to block: %d", fromBlock, toBlock)

certificate, err := a.buildCertificate(ctx, bridges, claims, lastSentCertificateInfo)
certificate, err := a.buildCertificate(ctx, bridges, claims, lastSentCertificateInfo, toBlock)
if err != nil {
return fmt.Errorf("error building certificate: %w", err)
}
Expand Down Expand Up @@ -209,7 +210,8 @@ func (a *AggSender) saveCertificateToFile(signedCertificate *agglayer.SignedCert
func (a *AggSender) buildCertificate(ctx context.Context,
bridges []bridgesync.Bridge,
claims []bridgesync.Claim,
lastSentCertificateInfo aggsendertypes.CertificateInfo) (*agglayer.Certificate, error) {
lastSentCertificateInfo aggsendertypes.CertificateInfo,
toBlock uint64) (*agglayer.Certificate, error) {
if len(bridges) == 0 && len(claims) == 0 {
return nil, errNoBridgesAndClaims
}
Expand Down Expand Up @@ -245,6 +247,7 @@ func (a *AggSender) buildCertificate(ctx context.Context,
BridgeExits: bridgeExits,
ImportedBridgeExits: importedBridgeExits,
Height: height,
Metadata: createCertificateMetadata(toBlock),
}, nil
}

Expand Down Expand Up @@ -412,13 +415,19 @@ func (a *AggSender) getImportedBridgeExits(

// signCertificate signs a certificate with the sequencer key
func (a *AggSender) signCertificate(certificate *agglayer.Certificate) (*agglayer.SignedCertificate, error) {
hashToSign := certificate.Hash()
hashToSign := certificate.HashToSign()

sig, err := crypto.Sign(hashToSign.Bytes(), a.sequencerKey)
if err != nil {
return nil, err
}

a.log.Infof("Signed certificate. sequencer address: %s. New local exit root: %s Hash signed: %s",
crypto.PubkeyToAddress(a.sequencerKey.PublicKey).String(),
common.BytesToHash(certificate.NewLocalExitRoot[:]).String(),
hashToSign.String(),
)

r, s, isOddParity, err := extractSignatureData(sig)
if err != nil {
return nil, err
Expand Down Expand Up @@ -500,3 +509,8 @@ 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))
}
5 changes: 4 additions & 1 deletion aggsender/aggsender_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -493,6 +493,7 @@ func TestBuildCertificate(t *testing.T) {
bridges []bridgesync.Bridge
claims []bridgesync.Claim
lastSentCertificateInfo aggsendertypes.CertificateInfo
toBlock uint64
mockFn func()
expectedCert *agglayer.Certificate
expectedError bool
Expand Down Expand Up @@ -532,10 +533,12 @@ func TestBuildCertificate(t *testing.T) {
NewLocalExitRoot: common.HexToHash("0x123"),
Height: 1,
},
toBlock: 10,
expectedCert: &agglayer.Certificate{
NetworkID: 1,
PrevLocalExitRoot: common.HexToHash("0x123"),
NewLocalExitRoot: common.HexToHash("0x789"),
Metadata: createCertificateMetadata(10),
BridgeExits: []*agglayer.BridgeExit{
{
LeafType: agglayer.LeafTypeAsset,
Expand Down Expand Up @@ -686,7 +689,7 @@ func TestBuildCertificate(t *testing.T) {
l1infoTreeSyncer: mockL1InfoTreeSyncer,
log: log.WithFields("test", "unittest"),
}
cert, err := aggSender.buildCertificate(context.Background(), tt.bridges, tt.claims, tt.lastSentCertificateInfo)
cert, err := aggSender.buildCertificate(context.Background(), tt.bridges, tt.claims, tt.lastSentCertificateInfo, tt.toBlock)

if tt.expectedError {
require.Error(t, err)
Expand Down
16 changes: 16 additions & 0 deletions common/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,3 +109,19 @@ func NewKeyFromKeystore(cfg types.KeystoreFileConfig) (*ecdsa.PrivateKey, error)
}
return key.PrivateKey, nil
}

// AsLittleEndianSlice converts a big.Int to a 32-byte little-endian representation.
func AsLittleEndianSlice(n *big.Int) []byte {
// Get the absolute value in big-endian byte slice
beBytes := n.Bytes()

// Initialize a 32-byte array for the result
leBytes := make([]byte, 32)

Check failure on line 119 in common/common.go

View workflow job for this annotation

GitHub Actions / lint

Magic number: 32, in <argument> detected (mnd)

// Fill the array in reverse order to convert to little-endian
for i := 0; i < len(beBytes) && i < 32; i++ {
leBytes[i] = beBytes[len(beBytes)-1-i]
}

return leBytes
}
59 changes: 59 additions & 0 deletions common/common_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package common

import (
"math/big"
"testing"
)

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

tests := []struct {
name string
input *big.Int
expected []byte
}{
{
name: "Zero value",
input: big.NewInt(0),
expected: make([]byte, 32),
},
{
name: "Positive value",
input: big.NewInt(123456789),
expected: append([]byte{21, 205, 91, 7}, make([]byte, 28)...),
},
{
name: "Negative value",
input: big.NewInt(-123456789),
expected: append([]byte{21, 205, 91, 7}, make([]byte, 28)...),
},
{
name: "Large positive value",
input: new(big.Int).SetBytes([]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}),
expected: []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
},
}

for _, tt := range tests {
tt := tt

t.Run(tt.name, func(t *testing.T) {
t.Parallel()

result := AsLittleEndianSlice(tt.input)
if len(result) != 32 {
t.Errorf("expected length 32, got %d", len(result))
}
for i := range result {
if result[i] != tt.expected[i] {
t.Errorf("expected byte at index %d to be %x, got %x", i, tt.expected[i], result[i])
}
}
})
}
}

0 comments on commit 4c85ece

Please sign in to comment.