Skip to content

Commit f755f8f

Browse files
joanestebanrARR552
andauthored
fork-etrog: synchronizer: update L1InfoTree (0xPolygonHermez#2818)
* update LxLy changes --------- Co-authored-by: Alonso Rodriguez <[email protected]>
1 parent 511b41f commit f755f8f

31 files changed

+764
-83
lines changed

db/migrations/state/0013.sql

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
-- +migrate Up
2+
ALTER TABLE state.exit_root
3+
ADD COLUMN prev_block_hash BYTEA DEFAULT NULL,
4+
ADD COLUMN l1_info_root BYTEA DEFAULT NULL,
5+
ADD COLUMN l1_info_tree_index BIGINT DEFAULT NULL UNIQUE;
6+
CREATE INDEX IF NOT EXISTS exit_root_l1_info_tree_index ON state.exit_root (l1_info_tree_index);
7+
8+
-- +migrate Down
9+
ALTER TABLE state.exit_root
10+
DROP COLUMN prev_block_hash,
11+
DROP COLUMN l1_info_root,
12+
DROP COLUMN l1_info_tree_index;
13+
DROP INDEX IF EXISTS state.exit_root_l1_info_tree_index;

db/migrations/state/0013_test.go

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
package migrations_test
2+
3+
import (
4+
"database/sql"
5+
"testing"
6+
"time"
7+
8+
"github.com/stretchr/testify/assert"
9+
)
10+
11+
const (
12+
blockHashValue = "0x29e885edaf8e4b51e1d2e05f9da28161d2fb4f6b1d53827d9b80a23cf2d7d9f1"
13+
mainExitRootValue = "0x83fc198de31e1b2b1a8212d2430fbb7766c13d9ad305637dea3759065606475d"
14+
rollupExitRootValue = "0xadb91a6a1fce56eaea561002bc9a993f4e65a7710bd72f4eee3067cbd73a743c"
15+
globalExitRootValue = "0x5bf4af1a651a2a74b36e6eb208481f94c69fc959f756223dfa49608061937585"
16+
previousBlockHashValue = "0xe865e912b504572a4d80ad018e29797e3c11f00bf9ae2549548a25779c9d7e57"
17+
l1InfoRootValue = "0x2b9484b83c6398033241865b015fb9430eb3e159182a6075d00c924845cc393e"
18+
)
19+
20+
// this migration changes length of the token name
21+
type migrationTest0013 struct{}
22+
23+
func (m migrationTest0013) insertBlock(blockNumber uint64, db *sql.DB) error {
24+
const addBlock = "INSERT INTO state.block (block_num, received_at, block_hash) VALUES ($1, $2, $3)"
25+
if _, err := db.Exec(addBlock, blockNumber, time.Now(), blockHashValue); err != nil {
26+
return err
27+
}
28+
return nil
29+
}
30+
31+
func (m migrationTest0013) insertRowInOldTable(db *sql.DB, args []interface{}) error {
32+
insert := `
33+
INSERT INTO state.exit_root (block_num, timestamp, mainnet_exit_root, rollup_exit_root, global_exit_root)
34+
VALUES ($1, $2, $3, $4, $5 );`
35+
36+
_, err := db.Exec(insert, args...)
37+
return err
38+
}
39+
40+
func (m migrationTest0013) InsertData(db *sql.DB) error {
41+
var err error
42+
if err = m.insertBlock(uint64(123), db); err != nil {
43+
return err
44+
}
45+
if err = m.insertBlock(uint64(124), db); err != nil {
46+
return err
47+
}
48+
if err = m.insertRowInOldTable(db, []interface{}{123, time.Now(), mainExitRootValue, rollupExitRootValue, globalExitRootValue}); err != nil {
49+
return err
50+
}
51+
if err = m.insertRowInOldTable(db, []interface{}{124, time.Now(), mainExitRootValue, rollupExitRootValue, globalExitRootValue}); err != nil {
52+
return err
53+
}
54+
55+
return nil
56+
}
57+
func (m migrationTest0013) insertRowInMigratedTable(db *sql.DB, args []interface{}) error {
58+
insert := `
59+
INSERT INTO state.exit_root (block_num, timestamp, mainnet_exit_root, rollup_exit_root, global_exit_root, prev_block_hash, l1_info_root, l1_info_tree_index)
60+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8);`
61+
62+
_, err := db.Exec(insert, args...)
63+
return err
64+
}
65+
66+
func (m migrationTest0013) RunAssertsAfterMigrationUp(t *testing.T, db *sql.DB) {
67+
err := m.insertBlock(uint64(125), db)
68+
assert.NoError(t, err)
69+
err = m.insertBlock(uint64(126), db)
70+
assert.NoError(t, err)
71+
err = m.insertBlock(uint64(127), db)
72+
assert.NoError(t, err)
73+
prevBlockHash := previousBlockHashValue
74+
l1InfoRoot := l1InfoRootValue
75+
err = m.insertRowInMigratedTable(db, []interface{}{125, time.Now(), mainExitRootValue, rollupExitRootValue, globalExitRootValue, prevBlockHash, l1InfoRoot, 1})
76+
assert.NoError(t, err)
77+
// insert duplicated l1_info_root
78+
err = m.insertRowInMigratedTable(db, []interface{}{126, time.Now(), mainExitRootValue, rollupExitRootValue, globalExitRootValue, prevBlockHash, l1InfoRoot, 1})
79+
assert.Error(t, err)
80+
81+
// insert in the old way must work
82+
err = m.insertRowInOldTable(db, []interface{}{127, time.Now(), mainExitRootValue, rollupExitRootValue, globalExitRootValue})
83+
assert.NoError(t, err)
84+
85+
sqlSelect := `SELECT prev_block_hash, l1_info_root FROM state.exit_root WHERE l1_info_tree_index = $1`
86+
currentPrevBlockHash := ""
87+
currentL1InfoRoot := ""
88+
err = db.QueryRow(sqlSelect, 1).Scan(&currentPrevBlockHash, &currentL1InfoRoot)
89+
assert.NoError(t, err)
90+
assert.Equal(t, prevBlockHash, currentPrevBlockHash)
91+
assert.Equal(t, l1InfoRoot, currentL1InfoRoot)
92+
}
93+
94+
func (m migrationTest0013) RunAssertsAfterMigrationDown(t *testing.T, db *sql.DB) {
95+
sqlSelect := `SELECT count(id) FROM state.exit_root`
96+
count := 0
97+
err := db.QueryRow(sqlSelect).Scan(&count)
98+
assert.NoError(t, err)
99+
assert.Equal(t, 4, count)
100+
}
101+
102+
func TestMigration0013(t *testing.T) {
103+
runMigrationTest(t, 13, migrationTest0013{})
104+
}

etherman/etherman.go

Lines changed: 54 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,8 @@ type EventOrder string
117117
const (
118118
// GlobalExitRootsOrder identifies a GlobalExitRoot event
119119
GlobalExitRootsOrder EventOrder = "GlobalExitRoots"
120+
// L1InfoTreeOrder identifies a L1InTree event
121+
L1InfoTreeOrder EventOrder = "L1InfoTreeOrder"
120122
// SequenceBatchesOrder identifies a VerifyBatch event
121123
SequenceBatchesOrder EventOrder = "SequenceBatches"
122124
// ForcedBatchesOrder identifies a ForcedBatches event
@@ -646,6 +648,58 @@ func (etherMan *Client) updateForkId(ctx context.Context, vLog types.Log, blocks
646648
return nil
647649
}
648650

651+
func (etherMan *Client) updateL1InfoTreeEvent(ctx context.Context, vLog types.Log, blocks *[]Block, blocksOrder *map[common.Hash][]Order) error {
652+
log.Debug("updateL1InfoTree event detected")
653+
var err error
654+
//TODO: Check that this way os unpacking parameters are right
655+
MainnetExitRoot := vLog.Topics[1]
656+
RollupExitRoot := vLog.Topics[2]
657+
658+
var gExitRoot L1InfoTree
659+
gExitRoot.MainnetExitRoot = MainnetExitRoot
660+
gExitRoot.RollupExitRoot = RollupExitRoot
661+
gExitRoot.BlockNumber = vLog.BlockNumber
662+
gExitRoot.GlobalExitRoot.GlobalExitRoot = hash(MainnetExitRoot, RollupExitRoot)
663+
var block *Block
664+
if !isheadBlockInArray(blocks, vLog.BlockHash, vLog.BlockNumber) {
665+
// Need to add the block, doesnt mind if inside the blocks because I have to respect the order so insert at end
666+
block, err = etherMan.retrieveFullBlockForEvent(ctx, vLog)
667+
if err != nil {
668+
return err
669+
}
670+
*blocks = append(*blocks, *block)
671+
}
672+
// Get the block in the HEAD of the array that contain the current block
673+
block = &(*blocks)[len(*blocks)-1]
674+
gExitRoot.PreviousBlockHash = block.ParentHash
675+
gExitRoot.MinTimestamp = block.ReceivedAt
676+
// Add the event to the block
677+
block.L1InfoTree = append(block.L1InfoTree, gExitRoot)
678+
order := Order{
679+
Name: L1InfoTreeOrder,
680+
Pos: len(block.L1InfoTree) - 1,
681+
}
682+
(*blocksOrder)[block.BlockHash] = append((*blocksOrder)[block.BlockHash], order)
683+
return nil
684+
}
685+
686+
func (etherMan *Client) retrieveFullBlockForEvent(ctx context.Context, vLog types.Log) (*Block, error) {
687+
fullBlock, err := etherMan.EthClient.BlockByHash(ctx, vLog.BlockHash)
688+
if err != nil {
689+
return nil, fmt.Errorf("error getting hashParent. BlockNumber: %d. Error: %w", vLog.BlockNumber, err)
690+
}
691+
t := time.Unix(int64(fullBlock.Time()), 0)
692+
block := prepareBlock(vLog, t, fullBlock)
693+
return &block, nil
694+
}
695+
696+
// Check if head block in blocks array is the same as blockHash / blockNumber
697+
func isheadBlockInArray(blocks *[]Block, blockHash common.Hash, blockNumber uint64) bool {
698+
// Check last item on array blocks if match Hash and Number
699+
headBlockIsNotExpected := len(*blocks) == 0 || ((*blocks)[len(*blocks)-1].BlockHash != blockHash || (*blocks)[len(*blocks)-1].BlockNumber != blockNumber)
700+
return !headBlockIsNotExpected
701+
}
702+
649703
func (etherMan *Client) updateGlobalExitRootEvent(ctx context.Context, vLog types.Log, blocks *[]Block, blocksOrder *map[common.Hash][]Order) error {
650704
log.Debug("UpdateGlobalExitRoot event detected")
651705
globalExitRoot, err := etherMan.GlobalExitRootManager.ParseUpdateGlobalExitRoot(vLog)
@@ -655,11 +709,6 @@ func (etherMan *Client) updateGlobalExitRootEvent(ctx context.Context, vLog type
655709
return etherMan.processUpdateGlobalExitRootEvent(ctx, globalExitRoot.MainnetExitRoot, globalExitRoot.RollupExitRoot, vLog, blocks, blocksOrder)
656710
}
657711

658-
func (etherMan *Client) updateL1InfoTreeEvent(ctx context.Context, vLog types.Log, blocks *[]Block, blocksOrder *map[common.Hash][]Order) error {
659-
log.Debug("UpdateL1InfoTree event detected")
660-
return etherMan.processUpdateGlobalExitRootEvent(ctx, vLog.Topics[1], vLog.Topics[2], vLog, blocks, blocksOrder)
661-
}
662-
663712
func (etherMan *Client) processUpdateGlobalExitRootEvent(ctx context.Context, mainnetExitRoot, rollupExitRoot common.Hash, vLog types.Log, blocks *[]Block, blocksOrder *map[common.Hash][]Order) error {
664713
var gExitRoot GlobalExitRoot
665714
gExitRoot.MainnetExitRoot = mainnetExitRoot

etherman/etherman_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -82,9 +82,9 @@ func TestGEREvent(t *testing.T) {
8282
blocks, _, err := etherman.GetRollupInfoByBlockRange(ctx, initBlock.NumberU64(), &finalBlockNumber)
8383
require.NoError(t, err)
8484
t.Logf("Blocks: %+v", blocks)
85-
assert.Equal(t, uint64(5), blocks[0].GlobalExitRoots[0].BlockNumber)
86-
assert.NotEqual(t, common.Hash{}, blocks[0].GlobalExitRoots[0].MainnetExitRoot)
87-
assert.Equal(t, common.Hash{}, blocks[0].GlobalExitRoots[0].RollupExitRoot)
85+
assert.Equal(t, uint64(5), blocks[0].L1InfoTree[0].BlockNumber)
86+
assert.NotEqual(t, common.Hash{}, blocks[0].L1InfoTree[0].MainnetExitRoot)
87+
assert.Equal(t, common.Hash{}, blocks[0].L1InfoTree[0].RollupExitRoot)
8888
}
8989

9090
func TestForcedBatchEvent(t *testing.T) {
@@ -237,7 +237,7 @@ func TestVerifyBatchEvent(t *testing.T) {
237237
assert.Equal(t, uint64(1), blocks[1].VerifiedBatches[0].BatchNumber)
238238
assert.NotEqual(t, common.Address{}, blocks[1].VerifiedBatches[0].Aggregator)
239239
assert.NotEqual(t, common.Hash{}, blocks[1].VerifiedBatches[0].TxHash)
240-
assert.Equal(t, GlobalExitRootsOrder, order[blocks[1].BlockHash][1].Name)
240+
assert.Equal(t, L1InfoTreeOrder, order[blocks[1].BlockHash][1].Name)
241241
assert.Equal(t, VerifyBatchOrder, order[blocks[1].BlockHash][0].Name)
242242
assert.Equal(t, 0, order[blocks[1].BlockHash][0].Pos)
243243
assert.Equal(t, 0, order[blocks[1].BlockHash][1].Pos)

etherman/types.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ type Block struct {
1313
BlockHash common.Hash
1414
ParentHash common.Hash
1515
GlobalExitRoots []GlobalExitRoot
16+
L1InfoTree []L1InfoTree
1617
ForcedBatches []ForcedBatch
1718
SequencedBatches [][]SequencedBatch
1819
VerifiedBatches []VerifiedBatch
@@ -30,6 +31,13 @@ type GlobalExitRoot struct {
3031
Timestamp time.Time
3132
}
3233

34+
// L1InfoTree struct (etrog)
35+
type L1InfoTree struct {
36+
GlobalExitRoot
37+
PreviousBlockHash common.Hash
38+
MinTimestamp time.Time
39+
}
40+
3341
// SequencedBatch represents virtual batch
3442
type SequencedBatch struct {
3543
BatchNumber uint64

l1infotree/hash.go

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package l1infotree
33
import (
44
"encoding/binary"
55

6-
"github.com/0xPolygonHermez/zkevm-node/log"
76
"github.com/ethereum/go-ethereum/common"
87
"github.com/iden3/go-iden3-crypto/keccak256"
98
"golang.org/x/crypto/sha3"
@@ -37,7 +36,6 @@ func HashLeafData(ger, prevBlockHash common.Hash, minTimestamp uint64) [32]byte
3736
var res [32]byte
3837
t := make([]byte, 8) //nolint:gomnd
3938
binary.BigEndian.PutUint64(t, minTimestamp)
40-
log.Debug(ger.Bytes(), prevBlockHash.Bytes(), t)
4139
copy(res[:], keccak256.Hash(ger.Bytes(), prevBlockHash.Bytes(), t))
4240
return res
4341
}

l1infotree/tree_test.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1-
package l1infotree
1+
package l1infotree_test
22

33
import (
44
"encoding/hex"
55
"encoding/json"
66
"os"
77
"testing"
88

9+
"github.com/0xPolygonHermez/zkevm-node/l1infotree"
910
"github.com/0xPolygonHermez/zkevm-node/test/vectors"
1011
"github.com/ethereum/go-ethereum/common"
1112
"github.com/stretchr/testify/require"
@@ -19,7 +20,7 @@ func TestComputeTreeRoot(t *testing.T) {
1920
require.NoError(t, err)
2021
for _, testVector := range mtTestVectors {
2122
input := testVector.PreviousLeafValues
22-
mt := NewL1InfoTree(uint8(32))
23+
mt := l1infotree.NewL1InfoTree(uint8(32))
2324
require.NoError(t, err)
2425

2526
var leaves [][32]byte
@@ -41,7 +42,7 @@ func TestComputeTreeRoot(t *testing.T) {
4142
}
4243

4344
func TestComputeSiblings(t *testing.T) {
44-
mt := NewL1InfoTree(uint8(32))
45+
mt := l1infotree.NewL1InfoTree(uint8(32))
4546
leaves := [][32]byte{
4647
common.HexToHash("0x83fc198de31e1b2b1a8212d2430fbb7766c13d9ad305637dea3759065606475d"),
4748
common.HexToHash("0x83fc198de31e1b2b1a8212d2430fbb7766c13d9ad305637dea3759065606475d"),

state/interfaces.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,4 +127,6 @@ type storage interface {
127127
GetDSL2Transactions(ctx context.Context, firstL2Block, lastL2Block uint64, dbTx pgx.Tx) ([]*DSL2Transaction, error)
128128
OpenBatchInStorage(ctx context.Context, batchContext ProcessingContext, dbTx pgx.Tx) error
129129
CloseBatchInStorage(ctx context.Context, receipt ProcessingReceipt, dbTx pgx.Tx) error
130+
AddL1InfoRootToExitRoot(ctx context.Context, exitRoot *L1InfoTreeExitRootStorageEntry, dbTx pgx.Tx) (uint64, error)
131+
GetAllL1InfoRootEntries(ctx context.Context, dbTx pgx.Tx) ([]L1InfoTreeExitRootStorageEntry, error)
130132
}

state/l1infotree.go

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package state
2+
3+
import (
4+
"context"
5+
6+
"github.com/0xPolygonHermez/zkevm-node/l1infotree"
7+
"github.com/0xPolygonHermez/zkevm-node/log"
8+
"github.com/ethereum/go-ethereum/common"
9+
"github.com/jackc/pgx/v4"
10+
)
11+
12+
// L1InfoTreeLeaf leaf of the L1InfoTree
13+
type L1InfoTreeLeaf struct {
14+
GlobalExitRoot
15+
PreviousBlockHash common.Hash
16+
}
17+
18+
// L1InfoTreeExitRootStorageEntry entry of the Database
19+
type L1InfoTreeExitRootStorageEntry struct {
20+
L1InfoTreeLeaf
21+
L1InfoTreeRoot common.Hash
22+
L1InfoTreeIndex uint64
23+
}
24+
25+
var (
26+
// TODO: Put the real hash of Leaf 0, pending of deploying contracts
27+
leaf0Hash = [32]byte{} //nolint:gomnd
28+
)
29+
30+
// Hash returns the hash of the leaf
31+
func (l *L1InfoTreeLeaf) Hash() common.Hash {
32+
timestamp := uint64(l.Timestamp.Unix())
33+
return l1infotree.HashLeafData(l.GlobalExitRoot.GlobalExitRoot, l.PreviousBlockHash, timestamp)
34+
}
35+
36+
// AddL1InfoTreeLeaf adds a new leaf to the L1InfoTree and returns the entry and error
37+
func (s *State) AddL1InfoTreeLeaf(ctx context.Context, L1InfoTreeLeaf *L1InfoTreeLeaf, dbTx pgx.Tx) (*L1InfoTreeExitRootStorageEntry, error) {
38+
allLeaves, err := s.GetAllL1InfoRootEntries(ctx, dbTx)
39+
if err != nil {
40+
log.Error("error getting all leaves. Error: ", err)
41+
return nil, err
42+
}
43+
root, err := buildL1InfoTree(allLeaves)
44+
if err != nil {
45+
log.Error("error building L1InfoTree. Error: ", err)
46+
return nil, err
47+
}
48+
entry := L1InfoTreeExitRootStorageEntry{
49+
L1InfoTreeLeaf: *L1InfoTreeLeaf,
50+
L1InfoTreeRoot: root,
51+
}
52+
index, err := s.AddL1InfoRootToExitRoot(ctx, &entry, dbTx)
53+
if err != nil {
54+
log.Error("error adding L1InfoRoot to ExitRoot. Error: ", err)
55+
return nil, err
56+
}
57+
entry.L1InfoTreeIndex = index
58+
return &entry, nil
59+
}
60+
61+
func buildL1InfoTree(allLeaves []L1InfoTreeExitRootStorageEntry) (common.Hash, error) {
62+
mt := l1infotree.NewL1InfoTree(uint8(32)) //nolint:gomnd
63+
var leaves [][32]byte
64+
// Insert the Leaf0 that is not used but compute for the Merkle Tree
65+
leaves = append(leaves, leaf0Hash)
66+
for _, leaf := range allLeaves {
67+
leaves = append(leaves, leaf.Hash())
68+
}
69+
root, err := mt.BuildL1InfoRoot(leaves)
70+
return root, err
71+
}

0 commit comments

Comments
 (0)