From 6db72b404965199f75e6380f315458e2890ad32d Mon Sep 17 00:00:00 2001 From: Goran Rojovic Date: Tue, 17 Sep 2024 15:58:08 +0200 Subject: [PATCH] feat: new changes --- agglayer/types.go | 31 ++++++++++--- aggsender/aggsender.go | 31 ++++++++++--- bridgesync/processor.go | 53 +++++++++++++++++++++- bridgesync/processor_test.go | 85 ++++++++++++++++++++++++++++++++++++ common/common.go | 7 --- 5 files changed, 186 insertions(+), 21 deletions(-) diff --git a/agglayer/types.go b/agglayer/types.go index 1349d931e..8b9150687 100644 --- a/agglayer/types.go +++ b/agglayer/types.go @@ -9,6 +9,17 @@ import ( "github.com/ethereum/go-ethereum/crypto" ) +type LeafType uint8 + +func (l LeafType) Uint8() uint8 { + return uint8(l) +} + +const ( + LeafTypeAsset LeafType = 0 + LeafTypeMessage LeafType = 1 +) + // Certificate is the data structure that will be sent to the aggLayer type Certificate struct { NetworkID uint32 `json:"network_id"` @@ -35,8 +46,7 @@ func (c *Certificate) Hash() common.Hash { // SignedCertificate is the struct that contains the certificate and the signature of the signer type SignedCertificate struct { *Certificate - Signature []byte `json:"signature"` - Signer common.Address `json:"signer"` + Signature []byte `json:"signature"` } // TokenInfo encapsulates the information to uniquely identify a token on the origin network. @@ -45,9 +55,16 @@ type TokenInfo struct { OriginTokenAddress common.Address `json:"origin_token_address"` } +// GlobalIndex represents the global index of an imported bridge exit +type GlobalIndex struct { + MainnetFlag bool `json:"mainnet_flag"` + RollupIndex uint32 `json:"rollup_index"` + LeafIndex uint32 `json:"leaf_index"` +} + // BridgeExit represents a token bridge exit type BridgeExit struct { - LeafType uint8 `json:"leaf_type"` + LeafType LeafType `json:"leaf_type"` TokenInfo *TokenInfo `json:"token_info"` DestinationNetwork uint32 `json:"destination_network"` DestinationAddress common.Address `json:"destination_address"` @@ -57,8 +74,12 @@ type BridgeExit struct { // Hash returns a hash that uniquely identifies the bridge exit func (c *BridgeExit) Hash() common.Hash { + if c.Amount == nil { + c.Amount = big.NewInt(0) + } + return crypto.Keccak256Hash( - cdkcommon.Uint8ToBytes(c.LeafType), + []byte{c.LeafType.Uint8()}, cdkcommon.Uint32ToBytes(c.TokenInfo.OriginNetwork), c.TokenInfo.OriginTokenAddress.Bytes(), cdkcommon.Uint32ToBytes(c.DestinationNetwork), @@ -74,7 +95,7 @@ type ImportedBridgeExit struct { ImportedLocalExitRoot common.Hash `json:"imported_local_exit_root"` InclusionProof [types.DefaultHeight]common.Hash `json:"inclusion_proof"` InclusionProofRER [types.DefaultHeight]common.Hash `json:"inclusion_proof_rer"` - GlobalIndex *big.Int `json:"global_index"` + GlobalIndex *GlobalIndex `json:"global_index"` } // Hash returns a hash that uniquely identifies the imported bridge exit diff --git a/aggsender/aggsender.go b/aggsender/aggsender.go index dd251bf1a..46a784b84 100644 --- a/aggsender/aggsender.go +++ b/aggsender/aggsender.go @@ -110,7 +110,11 @@ func (a *AggSender) buildCertificate(ctx context.Context, } for _, claim := range claims { - importedBridgeExit := convertClaimToImportedBridgeExit(claim) + importedBridgeExit, err := convertClaimToImportedBridgeExit(claim) + if err != nil { + return nil, fmt.Errorf("error converting claim to imported bridge exit: %w", err) + } + importedBridgeExits = append(importedBridgeExits, importedBridgeExit) } @@ -136,7 +140,7 @@ func (a *AggSender) buildCertificate(ctx context.Context, func convertBridgeToBridgeExit(bridge bridgesync.Bridge) *agglayer.BridgeExit { return &agglayer.BridgeExit{ - LeafType: bridge.LeafType, + LeafType: agglayer.LeafType(bridge.LeafType), TokenInfo: &agglayer.TokenInfo{ OriginNetwork: bridge.OriginNetwork, OriginTokenAddress: bridge.OriginAddress, @@ -148,9 +152,14 @@ func convertBridgeToBridgeExit(bridge bridgesync.Bridge) *agglayer.BridgeExit { } } -func convertClaimToImportedBridgeExit(claim bridgesync.Claim) *agglayer.ImportedBridgeExit { +func convertClaimToImportedBridgeExit(claim bridgesync.Claim) (*agglayer.ImportedBridgeExit, error) { + leafType := agglayer.LeafTypeAsset + if claim.IsMessage { + leafType = agglayer.LeafTypeMessage + } + bridgeExit := &agglayer.BridgeExit{ - // LeafType: claim.LeafType, TODO + LeafType: leafType, TokenInfo: &agglayer.TokenInfo{ OriginNetwork: claim.OriginNetwork, OriginTokenAddress: claim.OriginAddress, @@ -161,13 +170,22 @@ func convertClaimToImportedBridgeExit(claim bridgesync.Claim) *agglayer.Imported Metadata: claim.Metadata, } + mainnetFlag, rollupIndex, leafIndex, err := bridgesync.DecodeGlobalIndex(claim.GlobalIndex) + if err != nil { + return nil, fmt.Errorf("error decoding global index: %w", err) + } + return &agglayer.ImportedBridgeExit{ BridgeExit: bridgeExit, ImportedLocalExitRoot: tree.CalculateRoot(bridgeExit.Hash(), claim.ProofLocalExitRoot, uint32(claim.GlobalIndex.Uint64())), InclusionProof: claim.ProofLocalExitRoot, InclusionProofRER: claim.ProofRollupExitRoot, - GlobalIndex: claim.GlobalIndex, - } + GlobalIndex: &agglayer.GlobalIndex{ + MainnetFlag: mainnetFlag, + RollupIndex: rollupIndex, + LeafIndex: leafIndex, + }, + }, nil } // sendCertificatesForNetwork sends certificates for a network @@ -286,6 +304,5 @@ func (a *AggSender) signCertificate(certificate *agglayer.Certificate) (*agglaye return &agglayer.SignedCertificate{ Certificate: certificate, Signature: sig, - Signer: crypto.PubkeyToAddress(a.sequencerKey.PublicKey), }, nil } diff --git a/bridgesync/processor.go b/bridgesync/processor.go index 47b26595d..b954a8218 100644 --- a/bridgesync/processor.go +++ b/bridgesync/processor.go @@ -20,6 +20,8 @@ import ( _ "modernc.org/sqlite" ) +const globalIndexSize = 4 + var ( // ErrBlockNotProcessed indicates that the given block(s) have not been processed yet. ErrBlockNotProcessed = errors.New("given block(s) have not been processed yet") @@ -301,7 +303,7 @@ func (p *processor) ProcessBlock(ctx context.Context, block sync.Block) error { func GenerateGlobalIndex(mainnetFlag bool, rollupIndex uint32, localExitRootIndex uint32) *big.Int { var ( globalIndexBytes []byte - buf [4]byte + buf [globalIndexSize]byte ) if mainnetFlag { globalIndexBytes = append(globalIndexBytes, big.NewInt(1).Bytes()...) @@ -314,5 +316,52 @@ func GenerateGlobalIndex(mainnetFlag bool, rollupIndex uint32, localExitRootInde leri := big.NewInt(0).SetUint64(uint64(localExitRootIndex)).FillBytes(buf[:]) globalIndexBytes = append(globalIndexBytes, leri...) - return big.NewInt(0).SetBytes(globalIndexBytes) + result := big.NewInt(0).SetBytes(globalIndexBytes) + + return result +} + +// Decodes global index to its three parts: +// 1. mainnetFlag - first byte +// 2. rollupIndex - next 4 bytes +// 3. localExitRootIndex - last 4 bytes +// NOTE - mainnet flag is not in the global index bytes if it is false +// NOTE - rollup index is 0 if mainnet flag is true +// NOTE - rollup index is not in the global index bytes if mainnet flag is false and rollup index is 0 +func DecodeGlobalIndex(globalIndex *big.Int) (mainnetFlag bool, rollupIndex uint32, localExitRootIndex uint32, err error) { + globalIndexBytes := globalIndex.Bytes() + l := len(globalIndexBytes) + + if l > 9 { + return false, 0, 0, errors.New("invalid global index length") + } + + if l == 0 { + // false, 0, 0 + return + } + + if l == 9 { + // true, rollupIndex, localExitRootIndex + mainnetFlag = true + } + + localExitRootFromIdx := l - globalIndexSize + if localExitRootFromIdx < 0 { + localExitRootFromIdx = 0 + } + + rollupIndexFromIdx := localExitRootFromIdx - globalIndexSize + if rollupIndexFromIdx < 0 { + rollupIndexFromIdx = 0 + } + + rollupIndex = convertBytesToUint32(globalIndexBytes[rollupIndexFromIdx:localExitRootFromIdx]) + localExitRootIndex = convertBytesToUint32(globalIndexBytes[localExitRootFromIdx:]) + + return +} + +func convertBytesToUint32(bytes []byte) uint32 { + return uint32(big.NewInt(0).SetBytes(bytes).Uint64()) } diff --git a/bridgesync/processor_test.go b/bridgesync/processor_test.go index 2ff03c76b..1e82071d5 100644 --- a/bridgesync/processor_test.go +++ b/bridgesync/processor_test.go @@ -3,6 +3,7 @@ package bridgesync import ( "context" "encoding/json" + "errors" "fmt" "math/big" "os" @@ -582,3 +583,87 @@ func TestHashBridge(t *testing.T) { }) } } + +func TestDecodeGlobalIndex(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + globalIndex *big.Int + expectedMainnetFlag bool + expectedRollupIndex uint32 + expectedLocalIndex uint32 + expectedErr error + }{ + { + name: "Mainnet flag true, rollup index 0", + globalIndex: GenerateGlobalIndex(true, 0, 2), + expectedMainnetFlag: true, + expectedRollupIndex: 0, + expectedLocalIndex: 2, + expectedErr: nil, + }, + { + name: "Mainnet flag true, indexes 0", + globalIndex: GenerateGlobalIndex(true, 0, 0), + expectedMainnetFlag: true, + expectedRollupIndex: 0, + expectedLocalIndex: 0, + expectedErr: nil, + }, + { + name: "Mainnet flag false, rollup index 0", + globalIndex: GenerateGlobalIndex(false, 0, 2), + expectedMainnetFlag: false, + expectedRollupIndex: 0, + expectedLocalIndex: 2, + expectedErr: nil, + }, + { + name: "Mainnet flag false, rollup index non-zero", + globalIndex: GenerateGlobalIndex(false, 11, 0), + expectedMainnetFlag: false, + expectedRollupIndex: 11, + expectedLocalIndex: 0, + expectedErr: nil, + }, + { + name: "Mainnet flag false, indexes 0", + globalIndex: GenerateGlobalIndex(false, 0, 0), + expectedMainnetFlag: false, + expectedRollupIndex: 0, + expectedLocalIndex: 0, + expectedErr: nil, + }, + { + name: "Mainnet flag false, indexes non zero", + globalIndex: GenerateGlobalIndex(false, 1231, 111234), + expectedMainnetFlag: false, + expectedRollupIndex: 1231, + expectedLocalIndex: 111234, + expectedErr: nil, + }, + { + name: "Invalid global index length", + globalIndex: big.NewInt(0).SetBytes([]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}), + expectedMainnetFlag: false, + expectedRollupIndex: 0, + expectedLocalIndex: 0, + expectedErr: errors.New("invalid global index length"), + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + mainnetFlag, rollupIndex, localExitRootIndex, err := DecodeGlobalIndex(tt.globalIndex) + if tt.expectedErr != nil { + require.EqualError(t, err, tt.expectedErr.Error()) + } else { + require.NoError(t, err) + } + require.Equal(t, tt.expectedMainnetFlag, mainnetFlag) + require.Equal(t, tt.expectedRollupIndex, rollupIndex) + require.Equal(t, tt.expectedLocalIndex, localExitRootIndex) + }) + } +} diff --git a/common/common.go b/common/common.go index f3f395336..c74f56e4a 100644 --- a/common/common.go +++ b/common/common.go @@ -39,13 +39,6 @@ func Uint32ToBytes(num uint32) []byte { return key } -// Uint8ToBytes converts a uint8 to a byte slice -func Uint8ToBytes(num uint8) []byte { - key := make([]byte, 1) - key[0] = num - return key -} - // BytesToUint32 converts a byte slice to a uint32 func BytesToUint32(bytes []byte) uint32 { return binary.BigEndian.Uint32(bytes)