Skip to content

Commit

Permalink
fork-etrog: synchronizer: update L1InfoTree (0xPolygonHermez#2818)
Browse files Browse the repository at this point in the history
* update LxLy changes


---------

Co-authored-by: Alonso Rodriguez <[email protected]>
  • Loading branch information
joanestebanr and ARR552 authored Nov 24, 2023
1 parent 511b41f commit f755f8f
Show file tree
Hide file tree
Showing 31 changed files with 764 additions and 83 deletions.
13 changes: 13 additions & 0 deletions db/migrations/state/0013.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
-- +migrate Up
ALTER TABLE state.exit_root
ADD COLUMN prev_block_hash BYTEA DEFAULT NULL,
ADD COLUMN l1_info_root BYTEA DEFAULT NULL,
ADD COLUMN l1_info_tree_index BIGINT DEFAULT NULL UNIQUE;
CREATE INDEX IF NOT EXISTS exit_root_l1_info_tree_index ON state.exit_root (l1_info_tree_index);

-- +migrate Down
ALTER TABLE state.exit_root
DROP COLUMN prev_block_hash,
DROP COLUMN l1_info_root,
DROP COLUMN l1_info_tree_index;
DROP INDEX IF EXISTS state.exit_root_l1_info_tree_index;
104 changes: 104 additions & 0 deletions db/migrations/state/0013_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package migrations_test

import (
"database/sql"
"testing"
"time"

"github.com/stretchr/testify/assert"
)

const (
blockHashValue = "0x29e885edaf8e4b51e1d2e05f9da28161d2fb4f6b1d53827d9b80a23cf2d7d9f1"
mainExitRootValue = "0x83fc198de31e1b2b1a8212d2430fbb7766c13d9ad305637dea3759065606475d"
rollupExitRootValue = "0xadb91a6a1fce56eaea561002bc9a993f4e65a7710bd72f4eee3067cbd73a743c"
globalExitRootValue = "0x5bf4af1a651a2a74b36e6eb208481f94c69fc959f756223dfa49608061937585"
previousBlockHashValue = "0xe865e912b504572a4d80ad018e29797e3c11f00bf9ae2549548a25779c9d7e57"
l1InfoRootValue = "0x2b9484b83c6398033241865b015fb9430eb3e159182a6075d00c924845cc393e"
)

// this migration changes length of the token name
type migrationTest0013 struct{}

func (m migrationTest0013) insertBlock(blockNumber uint64, db *sql.DB) error {
const addBlock = "INSERT INTO state.block (block_num, received_at, block_hash) VALUES ($1, $2, $3)"
if _, err := db.Exec(addBlock, blockNumber, time.Now(), blockHashValue); err != nil {
return err
}
return nil
}

func (m migrationTest0013) insertRowInOldTable(db *sql.DB, args []interface{}) error {
insert := `
INSERT INTO state.exit_root (block_num, timestamp, mainnet_exit_root, rollup_exit_root, global_exit_root)
VALUES ($1, $2, $3, $4, $5 );`

_, err := db.Exec(insert, args...)
return err
}

func (m migrationTest0013) InsertData(db *sql.DB) error {
var err error
if err = m.insertBlock(uint64(123), db); err != nil {
return err
}
if err = m.insertBlock(uint64(124), db); err != nil {
return err
}
if err = m.insertRowInOldTable(db, []interface{}{123, time.Now(), mainExitRootValue, rollupExitRootValue, globalExitRootValue}); err != nil {
return err
}
if err = m.insertRowInOldTable(db, []interface{}{124, time.Now(), mainExitRootValue, rollupExitRootValue, globalExitRootValue}); err != nil {
return err
}

return nil
}
func (m migrationTest0013) insertRowInMigratedTable(db *sql.DB, args []interface{}) error {
insert := `
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)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8);`

_, err := db.Exec(insert, args...)
return err
}

func (m migrationTest0013) RunAssertsAfterMigrationUp(t *testing.T, db *sql.DB) {
err := m.insertBlock(uint64(125), db)
assert.NoError(t, err)
err = m.insertBlock(uint64(126), db)
assert.NoError(t, err)
err = m.insertBlock(uint64(127), db)
assert.NoError(t, err)
prevBlockHash := previousBlockHashValue
l1InfoRoot := l1InfoRootValue
err = m.insertRowInMigratedTable(db, []interface{}{125, time.Now(), mainExitRootValue, rollupExitRootValue, globalExitRootValue, prevBlockHash, l1InfoRoot, 1})
assert.NoError(t, err)
// insert duplicated l1_info_root
err = m.insertRowInMigratedTable(db, []interface{}{126, time.Now(), mainExitRootValue, rollupExitRootValue, globalExitRootValue, prevBlockHash, l1InfoRoot, 1})
assert.Error(t, err)

// insert in the old way must work
err = m.insertRowInOldTable(db, []interface{}{127, time.Now(), mainExitRootValue, rollupExitRootValue, globalExitRootValue})
assert.NoError(t, err)

sqlSelect := `SELECT prev_block_hash, l1_info_root FROM state.exit_root WHERE l1_info_tree_index = $1`
currentPrevBlockHash := ""
currentL1InfoRoot := ""
err = db.QueryRow(sqlSelect, 1).Scan(&currentPrevBlockHash, &currentL1InfoRoot)
assert.NoError(t, err)
assert.Equal(t, prevBlockHash, currentPrevBlockHash)
assert.Equal(t, l1InfoRoot, currentL1InfoRoot)
}

func (m migrationTest0013) RunAssertsAfterMigrationDown(t *testing.T, db *sql.DB) {
sqlSelect := `SELECT count(id) FROM state.exit_root`
count := 0
err := db.QueryRow(sqlSelect).Scan(&count)
assert.NoError(t, err)
assert.Equal(t, 4, count)
}

func TestMigration0013(t *testing.T) {
runMigrationTest(t, 13, migrationTest0013{})
}
59 changes: 54 additions & 5 deletions etherman/etherman.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,8 @@ type EventOrder string
const (
// GlobalExitRootsOrder identifies a GlobalExitRoot event
GlobalExitRootsOrder EventOrder = "GlobalExitRoots"
// L1InfoTreeOrder identifies a L1InTree event
L1InfoTreeOrder EventOrder = "L1InfoTreeOrder"
// SequenceBatchesOrder identifies a VerifyBatch event
SequenceBatchesOrder EventOrder = "SequenceBatches"
// ForcedBatchesOrder identifies a ForcedBatches event
Expand Down Expand Up @@ -646,6 +648,58 @@ func (etherMan *Client) updateForkId(ctx context.Context, vLog types.Log, blocks
return nil
}

func (etherMan *Client) updateL1InfoTreeEvent(ctx context.Context, vLog types.Log, blocks *[]Block, blocksOrder *map[common.Hash][]Order) error {
log.Debug("updateL1InfoTree event detected")
var err error
//TODO: Check that this way os unpacking parameters are right
MainnetExitRoot := vLog.Topics[1]
RollupExitRoot := vLog.Topics[2]

var gExitRoot L1InfoTree
gExitRoot.MainnetExitRoot = MainnetExitRoot
gExitRoot.RollupExitRoot = RollupExitRoot
gExitRoot.BlockNumber = vLog.BlockNumber
gExitRoot.GlobalExitRoot.GlobalExitRoot = hash(MainnetExitRoot, RollupExitRoot)
var block *Block
if !isheadBlockInArray(blocks, vLog.BlockHash, vLog.BlockNumber) {
// Need to add the block, doesnt mind if inside the blocks because I have to respect the order so insert at end
block, err = etherMan.retrieveFullBlockForEvent(ctx, vLog)
if err != nil {
return err
}
*blocks = append(*blocks, *block)
}
// Get the block in the HEAD of the array that contain the current block
block = &(*blocks)[len(*blocks)-1]
gExitRoot.PreviousBlockHash = block.ParentHash
gExitRoot.MinTimestamp = block.ReceivedAt
// Add the event to the block
block.L1InfoTree = append(block.L1InfoTree, gExitRoot)
order := Order{
Name: L1InfoTreeOrder,
Pos: len(block.L1InfoTree) - 1,
}
(*blocksOrder)[block.BlockHash] = append((*blocksOrder)[block.BlockHash], order)
return nil
}

func (etherMan *Client) retrieveFullBlockForEvent(ctx context.Context, vLog types.Log) (*Block, error) {
fullBlock, err := etherMan.EthClient.BlockByHash(ctx, vLog.BlockHash)
if err != nil {
return nil, fmt.Errorf("error getting hashParent. BlockNumber: %d. Error: %w", vLog.BlockNumber, err)
}
t := time.Unix(int64(fullBlock.Time()), 0)
block := prepareBlock(vLog, t, fullBlock)
return &block, nil
}

// Check if head block in blocks array is the same as blockHash / blockNumber
func isheadBlockInArray(blocks *[]Block, blockHash common.Hash, blockNumber uint64) bool {
// Check last item on array blocks if match Hash and Number
headBlockIsNotExpected := len(*blocks) == 0 || ((*blocks)[len(*blocks)-1].BlockHash != blockHash || (*blocks)[len(*blocks)-1].BlockNumber != blockNumber)
return !headBlockIsNotExpected
}

func (etherMan *Client) updateGlobalExitRootEvent(ctx context.Context, vLog types.Log, blocks *[]Block, blocksOrder *map[common.Hash][]Order) error {
log.Debug("UpdateGlobalExitRoot event detected")
globalExitRoot, err := etherMan.GlobalExitRootManager.ParseUpdateGlobalExitRoot(vLog)
Expand All @@ -655,11 +709,6 @@ func (etherMan *Client) updateGlobalExitRootEvent(ctx context.Context, vLog type
return etherMan.processUpdateGlobalExitRootEvent(ctx, globalExitRoot.MainnetExitRoot, globalExitRoot.RollupExitRoot, vLog, blocks, blocksOrder)
}

func (etherMan *Client) updateL1InfoTreeEvent(ctx context.Context, vLog types.Log, blocks *[]Block, blocksOrder *map[common.Hash][]Order) error {
log.Debug("UpdateL1InfoTree event detected")
return etherMan.processUpdateGlobalExitRootEvent(ctx, vLog.Topics[1], vLog.Topics[2], vLog, blocks, blocksOrder)
}

func (etherMan *Client) processUpdateGlobalExitRootEvent(ctx context.Context, mainnetExitRoot, rollupExitRoot common.Hash, vLog types.Log, blocks *[]Block, blocksOrder *map[common.Hash][]Order) error {
var gExitRoot GlobalExitRoot
gExitRoot.MainnetExitRoot = mainnetExitRoot
Expand Down
8 changes: 4 additions & 4 deletions etherman/etherman_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,9 @@ func TestGEREvent(t *testing.T) {
blocks, _, err := etherman.GetRollupInfoByBlockRange(ctx, initBlock.NumberU64(), &finalBlockNumber)
require.NoError(t, err)
t.Logf("Blocks: %+v", blocks)
assert.Equal(t, uint64(5), blocks[0].GlobalExitRoots[0].BlockNumber)
assert.NotEqual(t, common.Hash{}, blocks[0].GlobalExitRoots[0].MainnetExitRoot)
assert.Equal(t, common.Hash{}, blocks[0].GlobalExitRoots[0].RollupExitRoot)
assert.Equal(t, uint64(5), blocks[0].L1InfoTree[0].BlockNumber)
assert.NotEqual(t, common.Hash{}, blocks[0].L1InfoTree[0].MainnetExitRoot)
assert.Equal(t, common.Hash{}, blocks[0].L1InfoTree[0].RollupExitRoot)
}

func TestForcedBatchEvent(t *testing.T) {
Expand Down Expand Up @@ -237,7 +237,7 @@ func TestVerifyBatchEvent(t *testing.T) {
assert.Equal(t, uint64(1), blocks[1].VerifiedBatches[0].BatchNumber)
assert.NotEqual(t, common.Address{}, blocks[1].VerifiedBatches[0].Aggregator)
assert.NotEqual(t, common.Hash{}, blocks[1].VerifiedBatches[0].TxHash)
assert.Equal(t, GlobalExitRootsOrder, order[blocks[1].BlockHash][1].Name)
assert.Equal(t, L1InfoTreeOrder, order[blocks[1].BlockHash][1].Name)
assert.Equal(t, VerifyBatchOrder, order[blocks[1].BlockHash][0].Name)
assert.Equal(t, 0, order[blocks[1].BlockHash][0].Pos)
assert.Equal(t, 0, order[blocks[1].BlockHash][1].Pos)
Expand Down
8 changes: 8 additions & 0 deletions etherman/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ type Block struct {
BlockHash common.Hash
ParentHash common.Hash
GlobalExitRoots []GlobalExitRoot
L1InfoTree []L1InfoTree
ForcedBatches []ForcedBatch
SequencedBatches [][]SequencedBatch
VerifiedBatches []VerifiedBatch
Expand All @@ -30,6 +31,13 @@ type GlobalExitRoot struct {
Timestamp time.Time
}

// L1InfoTree struct (etrog)
type L1InfoTree struct {
GlobalExitRoot
PreviousBlockHash common.Hash
MinTimestamp time.Time
}

// SequencedBatch represents virtual batch
type SequencedBatch struct {
BatchNumber uint64
Expand Down
2 changes: 0 additions & 2 deletions l1infotree/hash.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package l1infotree
import (
"encoding/binary"

"github.com/0xPolygonHermez/zkevm-node/log"
"github.com/ethereum/go-ethereum/common"
"github.com/iden3/go-iden3-crypto/keccak256"
"golang.org/x/crypto/sha3"
Expand Down Expand Up @@ -37,7 +36,6 @@ func HashLeafData(ger, prevBlockHash common.Hash, minTimestamp uint64) [32]byte
var res [32]byte
t := make([]byte, 8) //nolint:gomnd
binary.BigEndian.PutUint64(t, minTimestamp)
log.Debug(ger.Bytes(), prevBlockHash.Bytes(), t)
copy(res[:], keccak256.Hash(ger.Bytes(), prevBlockHash.Bytes(), t))
return res
}
7 changes: 4 additions & 3 deletions l1infotree/tree_test.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
package l1infotree
package l1infotree_test

import (
"encoding/hex"
"encoding/json"
"os"
"testing"

"github.com/0xPolygonHermez/zkevm-node/l1infotree"
"github.com/0xPolygonHermez/zkevm-node/test/vectors"
"github.com/ethereum/go-ethereum/common"
"github.com/stretchr/testify/require"
Expand All @@ -19,7 +20,7 @@ func TestComputeTreeRoot(t *testing.T) {
require.NoError(t, err)
for _, testVector := range mtTestVectors {
input := testVector.PreviousLeafValues
mt := NewL1InfoTree(uint8(32))
mt := l1infotree.NewL1InfoTree(uint8(32))
require.NoError(t, err)

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

func TestComputeSiblings(t *testing.T) {
mt := NewL1InfoTree(uint8(32))
mt := l1infotree.NewL1InfoTree(uint8(32))
leaves := [][32]byte{
common.HexToHash("0x83fc198de31e1b2b1a8212d2430fbb7766c13d9ad305637dea3759065606475d"),
common.HexToHash("0x83fc198de31e1b2b1a8212d2430fbb7766c13d9ad305637dea3759065606475d"),
Expand Down
2 changes: 2 additions & 0 deletions state/interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,4 +127,6 @@ type storage interface {
GetDSL2Transactions(ctx context.Context, firstL2Block, lastL2Block uint64, dbTx pgx.Tx) ([]*DSL2Transaction, error)
OpenBatchInStorage(ctx context.Context, batchContext ProcessingContext, dbTx pgx.Tx) error
CloseBatchInStorage(ctx context.Context, receipt ProcessingReceipt, dbTx pgx.Tx) error
AddL1InfoRootToExitRoot(ctx context.Context, exitRoot *L1InfoTreeExitRootStorageEntry, dbTx pgx.Tx) (uint64, error)
GetAllL1InfoRootEntries(ctx context.Context, dbTx pgx.Tx) ([]L1InfoTreeExitRootStorageEntry, error)
}
71 changes: 71 additions & 0 deletions state/l1infotree.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package state

import (
"context"

"github.com/0xPolygonHermez/zkevm-node/l1infotree"
"github.com/0xPolygonHermez/zkevm-node/log"
"github.com/ethereum/go-ethereum/common"
"github.com/jackc/pgx/v4"
)

// L1InfoTreeLeaf leaf of the L1InfoTree
type L1InfoTreeLeaf struct {
GlobalExitRoot
PreviousBlockHash common.Hash
}

// L1InfoTreeExitRootStorageEntry entry of the Database
type L1InfoTreeExitRootStorageEntry struct {
L1InfoTreeLeaf
L1InfoTreeRoot common.Hash
L1InfoTreeIndex uint64
}

var (
// TODO: Put the real hash of Leaf 0, pending of deploying contracts
leaf0Hash = [32]byte{} //nolint:gomnd
)

// Hash returns the hash of the leaf
func (l *L1InfoTreeLeaf) Hash() common.Hash {
timestamp := uint64(l.Timestamp.Unix())
return l1infotree.HashLeafData(l.GlobalExitRoot.GlobalExitRoot, l.PreviousBlockHash, timestamp)
}

// AddL1InfoTreeLeaf adds a new leaf to the L1InfoTree and returns the entry and error
func (s *State) AddL1InfoTreeLeaf(ctx context.Context, L1InfoTreeLeaf *L1InfoTreeLeaf, dbTx pgx.Tx) (*L1InfoTreeExitRootStorageEntry, error) {
allLeaves, err := s.GetAllL1InfoRootEntries(ctx, dbTx)
if err != nil {
log.Error("error getting all leaves. Error: ", err)
return nil, err
}
root, err := buildL1InfoTree(allLeaves)
if err != nil {
log.Error("error building L1InfoTree. Error: ", err)
return nil, err
}
entry := L1InfoTreeExitRootStorageEntry{
L1InfoTreeLeaf: *L1InfoTreeLeaf,
L1InfoTreeRoot: root,
}
index, err := s.AddL1InfoRootToExitRoot(ctx, &entry, dbTx)
if err != nil {
log.Error("error adding L1InfoRoot to ExitRoot. Error: ", err)
return nil, err
}
entry.L1InfoTreeIndex = index
return &entry, nil
}

func buildL1InfoTree(allLeaves []L1InfoTreeExitRootStorageEntry) (common.Hash, error) {
mt := l1infotree.NewL1InfoTree(uint8(32)) //nolint:gomnd
var leaves [][32]byte
// Insert the Leaf0 that is not used but compute for the Merkle Tree
leaves = append(leaves, leaf0Hash)
for _, leaf := range allLeaves {
leaves = append(leaves, leaf.Hash())
}
root, err := mt.BuildL1InfoRoot(leaves)
return root, err
}
Loading

0 comments on commit f755f8f

Please sign in to comment.