Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
arnaubennassar committed Jul 31, 2024
1 parent b1a399b commit 3e21f64
Show file tree
Hide file tree
Showing 7 changed files with 207 additions and 27 deletions.
2 changes: 1 addition & 1 deletion aggoracle/e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ func commonSetup(t *testing.T) (
require.NoError(t, err)
// Syncer
dbPathSyncer := t.TempDir()
syncer, err := l1infotreesync.New(ctx, dbPathSyncer, gerL1Addr, 10, etherman.LatestBlock, reorg, l1Client.Client(), time.Millisecond, 0)
syncer, err := l1infotreesync.New(ctx, dbPathSyncer, gerL1Addr, common.Address{}, 10, etherman.LatestBlock, reorg, l1Client.Client(), time.Millisecond, 0)
require.NoError(t, err)
go syncer.Start(ctx)

Expand Down
1 change: 1 addition & 0 deletions cmd/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,7 @@ func newL1InfoTreeSyncer(
ctx,
cfg.L1InfoTreeSync.DBPath,
cfg.L1InfoTreeSync.GlobalExitRootAddr,
cfg.L1InfoTreeSync.RollupManagerAddr,
cfg.L1InfoTreeSync.SyncBlockChunkSize,
etherman.BlockNumberFinality(cfg.L1InfoTreeSync.BlockFinality),
reorgDetector,
Expand Down
37 changes: 30 additions & 7 deletions l1infotreesync/downloader.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package l1infotreesync
import (
"fmt"

"github.com/0xPolygon/cdk-contracts-tooling/contracts/elderberry/polygonrollupmanager"
"github.com/0xPolygon/cdk-contracts-tooling/contracts/elderberry/polygonzkevmglobalexitrootv2"
"github.com/0xPolygon/cdk/sync"
"github.com/ethereum/go-ethereum"
Expand All @@ -13,7 +14,9 @@ import (
)

var (
updateL1InfoTreeSignature = crypto.Keccak256Hash([]byte("UpdateL1InfoTree(bytes32,bytes32)"))
updateL1InfoTreeSignature = crypto.Keccak256Hash([]byte("UpdateL1InfoTree(bytes32,bytes32)"))
verifyBatchesSignature = crypto.Keccak256Hash([]byte("VerifyBatches(uint32,uint64,bytes32,bytes32,address)"))
verifyBatchesTrustedAggregatorSignature = crypto.Keccak256Hash([]byte("VerifyBatchesTrustedAggregator(uint32,uint64,bytes32,bytes32,address)"))
)

type EthClienter interface {
Expand All @@ -23,27 +26,47 @@ type EthClienter interface {
bind.ContractBackend
}

func buildAppender(client EthClienter, globalExitRoot common.Address) (sync.LogAppenderMap, error) {
contract, err := polygonzkevmglobalexitrootv2.NewPolygonzkevmglobalexitrootv2(globalExitRoot, client)
func buildAppender(client EthClienter, globalExitRoot, rollupManager common.Address) (sync.LogAppenderMap, error) {
ger, err := polygonzkevmglobalexitrootv2.NewPolygonzkevmglobalexitrootv2(globalExitRoot, client)
rm, err := polygonrollupmanager.NewPolygonrollupmanager(rollupManager, client)
if err != nil {
return nil, err
}
appender := make(sync.LogAppenderMap)
appender[updateL1InfoTreeSignature] = func(b *sync.EVMBlock, l types.Log) error {
l1InfoTreeUpdate, err := contract.ParseUpdateL1InfoTree(l)
l1InfoTreeUpdate, err := ger.ParseUpdateL1InfoTree(l)
if err != nil {
return fmt.Errorf(
"error parsing log %+v using contract.ParseUpdateL1InfoTree: %v",
"error parsing log %+v using ger.ParseUpdateL1InfoTree: %v",
l, err,
)
}
b.Events = append(b.Events, Event{
b.Events = append(b.Events, Event{UpdateL1InfoTree: &UpdateL1InfoTree{
MainnetExitRoot: l1InfoTreeUpdate.MainnetExitRoot,
RollupExitRoot: l1InfoTreeUpdate.RollupExitRoot,
ParentHash: b.ParentHash,
Timestamp: b.Timestamp,
})
}})
return nil
}
appender[verifyBatchesSignature] = func(b *sync.EVMBlock, l types.Log) error {
verifyBatches, err := rm.ParseVerifyBatches(l)
if err != nil {
return fmt.Errorf(
"error parsing log %+v using rm.ParseVerifyBatches: %v",
l, err,
)
}
fmt.Println(verifyBatches)
b.Events = append(b.Events, Event{VerifyBatches: &VerifyBatches{
RollupID: verifyBatches.RollupID,
NumBatch: verifyBatches.NumBatch,
StateRoot: verifyBatches.StateRoot,
ExitRoot: verifyBatches.ExitRoot,
Aggregator: verifyBatches.Aggregator,
}})
return nil
}

return appender, nil
}
2 changes: 1 addition & 1 deletion l1infotreesync/e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ func TestE2E(t *testing.T) {
rdm.On("AddBlockToTrack", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil)
client, gerAddr, gerSc, err := newSimulatedClient(auth)
require.NoError(t, err)
syncer, err := New(ctx, dbPath, gerAddr, 10, etherman.LatestBlock, rdm, client.Client(), time.Millisecond, 0)
syncer, err := New(ctx, dbPath, gerAddr, common.Address{}, 10, etherman.LatestBlock, rdm, client.Client(), time.Millisecond, 0)
require.NoError(t, err)
go syncer.Start(ctx)

Expand Down
5 changes: 3 additions & 2 deletions l1infotreesync/l1infotreesync.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ var (
type Config struct {
DBPath string `mapstructure:"DBPath"`
GlobalExitRootAddr common.Address `mapstructure:"GlobalExitRootAddr"`
RollupManagerAddr common.Address `mapstructure:"RollupManagerAddr"`
SyncBlockChunkSize uint64 `mapstructure:"SyncBlockChunkSize"`
// TODO: BlockFinality doesnt work as per the jsonschema
BlockFinality string `jsonschema:"enum=latest,enum=safe, enum=pending, enum=finalized" mapstructure:"BlockFinality"`
Expand All @@ -39,7 +40,7 @@ type L1InfoTreeSync struct {
func New(
ctx context.Context,
dbPath string,
globalExitRoot common.Address,
globalExitRoot, rollupManager common.Address,
syncBlockChunkSize uint64,
blockFinalityType etherman.BlockNumberFinality,
rd sync.ReorgDetector,
Expand All @@ -65,7 +66,7 @@ func New(
}
}

appender, err := buildAppender(l1Client, globalExitRoot)
appender, err := buildAppender(l1Client, globalExitRoot, rollupManager)
if err != nil {
return nil, err
}
Expand Down
68 changes: 52 additions & 16 deletions l1infotreesync/processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,17 +53,31 @@ var (
)

type processor struct {
db kv.RwDB
tree *l1infotree.L1InfoTree
db kv.RwDB
l1InfoTree *l1infotree.L1InfoTree
rollupExitTree *rollupExitTree
}

type Event struct {
type UpdateL1InfoTree struct {
MainnetExitRoot ethCommon.Hash
RollupExitRoot ethCommon.Hash
ParentHash ethCommon.Hash
Timestamp uint64
}

type VerifyBatches struct {
RollupID uint32
NumBatch uint64
StateRoot ethCommon.Hash
ExitRoot ethCommon.Hash
Aggregator ethCommon.Address
}

type Event struct {
UpdateL1InfoTree *UpdateL1InfoTree
VerifyBatches *VerifyBatches
}

type L1InfoTreeLeaf struct {
L1InfoTreeRoot ethCommon.Hash
L1InfoTreeIndex uint32
Expand Down Expand Up @@ -130,7 +144,9 @@ func newProcessor(ctx context.Context, dbPath string) (*processor, error) {
if err != nil {
return nil, err
}
p.tree = tree
p.l1InfoTree = tree
rollupExitTree := newRollupExitTree()
p.rollupExitTree = rollupExitTree
return p, nil
}

Expand Down Expand Up @@ -167,7 +183,7 @@ func (p *processor) ComputeMerkleProofByIndex(ctx context.Context, index uint32)
if err != nil {
return nil, ethCommon.Hash{}, err
}
return p.tree.ComputeMerkleProof(index, leaves)
return p.l1InfoTree.ComputeMerkleProof(index, leaves)
}

func (p *processor) getHashedLeaves(tx kv.Tx, untilIndex uint32) ([][32]byte, error) {
Expand Down Expand Up @@ -415,19 +431,39 @@ func (p *processor) ProcessBlock(b sync.Block) error {
} else {
initialIndex = lastIndex + 1
}
var nextExpectedRollupExitTreeRoot *ethCommon.Hash
for i, e := range b.Events {
event := e.(Event)
leafToStore := storeLeaf{
Index: initialIndex + uint32(i),
MainnetExitRoot: event.MainnetExitRoot,
RollupExitRoot: event.RollupExitRoot,
ParentHash: event.ParentHash,
Timestamp: event.Timestamp,
BlockNumber: b.Num,
if event.UpdateL1InfoTree != nil {
leafToStore := storeLeaf{
Index: initialIndex + uint32(i),
MainnetExitRoot: event.UpdateL1InfoTree.MainnetExitRoot,
RollupExitRoot: event.UpdateL1InfoTree.RollupExitRoot,
ParentHash: event.UpdateL1InfoTree.ParentHash,
Timestamp: event.UpdateL1InfoTree.Timestamp,
BlockNumber: b.Num,
}
if err := p.addLeaf(tx, leafToStore); err != nil {
tx.Rollback()
return err
}
nextExpectedRollupExitTreeRoot = &leafToStore.RollupExitRoot
}
if err := p.addLeaf(tx, leafToStore); err != nil {
tx.Rollback()
return err

if event.VerifyBatches != nil {
// before the verify batches event happens, the updateExitRoot event is emitted.
// Since the previous event include the rollup exit root, this can use it to assert
// that the computation of the tree is correct. However, there are some execution paths
// on the contract that don't follow this (verifyBatches + pendingStateTimeout != 0)
if err := p.rollupExitTree.addLeaf(
tx, event.VerifyBatches.RollupID,
event.VerifyBatches.ExitRoot,
nextExpectedRollupExitTreeRoot,
); err != nil {
tx.Rollback()
return err
}
nextExpectedRollupExitTreeRoot = nil
}
}
bwl := blockWithLeafs{
Expand Down Expand Up @@ -481,7 +517,7 @@ func (p *processor) getLastIndex(tx kv.Tx) (uint32, error) {
func (p *processor) addLeaf(tx kv.RwTx, leaf storeLeaf) error {
// Update tree
hash := l1infotree.HashLeafData(leaf.GlobalExitRoot(), leaf.ParentHash, leaf.Timestamp)
root, err := p.tree.AddLeaf(leaf.Index, hash)
root, err := p.l1InfoTree.AddLeaf(leaf.Index, hash)
if err != nil {
return err
}
Expand Down
119 changes: 119 additions & 0 deletions l1infotreesync/rollupexittree.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
package l1infotreesync

import (
"fmt"

"github.com/ethereum/go-ethereum/common"
"github.com/ledgerwatch/erigon-lib/kv"
"golang.org/x/crypto/sha3"
)

type treeNode struct {
left common.Hash
right common.Hash
}

func (n *treeNode) hash() common.Hash {
var hash common.Hash
hasher := sha3.NewLegacyKeccak256()
hasher.Write(n.left[:])
hasher.Write(n.right[:])
copy(hash[:], hasher.Sum(nil))
return hash
}

func (n *treeNode) MarshalBinary() ([]byte, error) {
return append(n.left[:], n.right[:]...), nil
}

func (n *treeNode) UnmarshalBinary(data []byte) error {
if len(data) != 64 {
return fmt.Errorf("expected len %d, actual len %d", 64, len(data))
}
n.left = common.Hash(data[:32])
n.right = common.Hash(data[32:])
return nil
}

type rollupExitTree struct {
height uint8
rhtTable string
lastExitTreeRoot common.Hash
}

func newRollupExitTree() *rollupExitTree {
return &rollupExitTree{}
}

func (t *rollupExitTree) addLeaf(
tx kv.RwTx,
rollupID uint32,
rollupExitRoot common.Hash,
expectedRollupExitRoot *common.Hash,
) error {
siblings, err := t.getProof(tx, rollupID, t.lastExitTreeRoot)

Check failure on line 54 in l1infotreesync/rollupexittree.go

View workflow job for this annotation

GitHub Actions / test-unittest

siblings declared and not used
if err != nil {
return err
}
if expectedRollupExitRoot != nil && *expectedRollupExitRoot != t.lastExitTreeRoot {
return fmt.Errorf(
"expectedRollupExitRoot: %s, actual: %s",
expectedRollupExitRoot.Hex(), t.lastExitTreeRoot.Hex(),
)
}
return nil
}

func (t *rollupExitTree) getSiblings(tx kv.RwTx, rollupID uint32, root common.Hash) (bool, []common.Hash, error) {
siblings := make([]common.Hash, int(t.height))

currentNodeHash := root
// It starts in height-1 because 0 is the level of the leafs
for h := int(t.height - 1); h >= 0; h-- {
currentNode, err := t.getRHTNode(tx, currentNodeHash)
if err != nil {
// handle not found for inserts and shit
return false, nil, fmt.Errorf(
"height: %d, currentNode: %s, error: %v",
h, currentNodeHash.Hex(), err,
)
}
if rollupID&(1<<h) > 0 {
siblings = append(siblings, currentNode.left)
currentNodeHash = currentNode.right
} else {
siblings = append(siblings, currentNode.right)
currentNodeHash = currentNode.left
}
}

// Reverse siblings to go from leafs to root
for i, j := 0, len(siblings)-1; i < j; i, j = i+1, j-1 {
siblings[i], siblings[j] = siblings[j], siblings[i]
}

return false, siblings, nil

}

// getProof returns the merkle proof for a given deposit count and root.
func (t *rollupExitTree) getProof(tx kv.RwTx, rollupID uint32, root common.Hash) ([]common.Hash, error) {
usedEmptyTree, siblings, err := t.getSiblings(tx, rollupID, root)
if usedEmptyTree {
return nil, ErrNotFound
}
return siblings, err
}

func (t *rollupExitTree) getRHTNode(tx kv.Tx, nodeHash common.Hash) (*treeNode, error) {
nodeBytes, err := tx.GetOne(t.rhtTable, nodeHash[:])
if err != nil {
return nil, err
}
if nodeBytes == nil {
return nil, ErrNotFound
}
node := &treeNode{}
err = node.UnmarshalBinary(nodeBytes)
return node, err
}

0 comments on commit 3e21f64

Please sign in to comment.