Skip to content

Commit

Permalink
Cherry-pick 0xPolygonHermez#3551: Check GER and index of synced L1Inf…
Browse files Browse the repository at this point in the history
…oRoot matches with sc values (0xPolygonHermez#3569)

* check GER and index of synced L1InfoRoot matches with sc values (0xPolygonHermez#3551)

* fix etherman.DepositCount method

* fix comments

* fix checkL1InfoTreeUpdate (skip check valid InfoRoot when lastInfoTreeIndex is 0)
  • Loading branch information
agnusmor authored Apr 18, 2024
1 parent 3d08a43 commit c7266e1
Show file tree
Hide file tree
Showing 6 changed files with 184 additions and 35 deletions.
11 changes: 11 additions & 0 deletions etherman/etherman.go
Original file line number Diff line number Diff line change
Expand Up @@ -1852,6 +1852,17 @@ func (etherMan *Client) EstimateGas(ctx context.Context, from common.Address, to
})
}

// DepositCount returns deposits count
func (etherman *Client) DepositCount(ctx context.Context, blockNumber *uint64) (*big.Int, error) {
var opts *bind.CallOpts
if blockNumber != nil {
opts = new(bind.CallOpts)
opts.BlockNumber = new(big.Int).SetUint64(*blockNumber)
}

return etherman.EtrogGlobalExitRootManager.DepositCount(opts)
}

// CheckTxWasMined check if a tx was already mined
func (etherMan *Client) CheckTxWasMined(ctx context.Context, txHash common.Hash) (bool, *types.Receipt, error) {
receipt, err := etherMan.EthClient.TransactionReceipt(ctx, txHash)
Expand Down
122 changes: 93 additions & 29 deletions sequencer/finalizer.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"time"

"github.com/0xPolygonHermez/zkevm-data-streamer/datastreamer"
ethermanTypes "github.com/0xPolygonHermez/zkevm-node/etherman"
"github.com/0xPolygonHermez/zkevm-node/event"
"github.com/0xPolygonHermez/zkevm-node/hex"
"github.com/0xPolygonHermez/zkevm-node/log"
Expand Down Expand Up @@ -38,7 +39,7 @@ type finalizer struct {
workerIntf workerInterface
poolIntf txPool
stateIntf stateInterface
etherman etherman
etherman ethermanInterface
wipBatch *Batch
wipL2Block *L2Block
batchConstraints state.BatchConstraintsCfg
Expand Down Expand Up @@ -87,7 +88,7 @@ func newFinalizer(
workerIntf workerInterface,
poolIntf txPool,
stateIntf stateInterface,
etherman etherman,
etherman ethermanInterface,
sequencerAddr common.Address,
isSynced func(ctx context.Context) bool,
batchConstraints state.BatchConstraintsCfg,
Expand Down Expand Up @@ -220,18 +221,95 @@ func (f *finalizer) updateFlushIDs(newPendingFlushID, newStoredFlushID uint64) {
f.storedFlushIDCond.L.Unlock()
}

func (f *finalizer) checkL1InfoTreeUpdate(ctx context.Context) {
firstL1InfoRootUpdate := true
skipFirstSleep := true
func (f *finalizer) checkValidL1InfoRoot(ctx context.Context, l1InfoRoot state.L1InfoTreeExitRootStorageEntry) (bool, error) {
// Check L1 block hash matches
l1BlockState, err := f.stateIntf.GetBlockByNumber(ctx, l1InfoRoot.BlockNumber, nil)
if err != nil {
return false, fmt.Errorf("error getting L1 block %d from the state, error: %v", l1InfoRoot.BlockNumber, err)
}

l1BlockEth, err := f.etherman.HeaderByNumber(ctx, new(big.Int).SetUint64(l1InfoRoot.BlockNumber))
if err != nil {
return false, fmt.Errorf("error getting L1 block %d from ethereum, error: %v", l1InfoRoot.BlockNumber, err)
}

if l1BlockState.BlockHash != l1BlockEth.Hash() {
warnmsg := fmt.Sprintf("invalid l1InfoRoot %s, index: %d, GER: %s, l1Block: %d. L1 block hash %s doesn't match block hash on ethereum %s (L1 reorg?)",
l1InfoRoot.L1InfoTreeRoot, l1InfoRoot.L1InfoTreeIndex, l1InfoRoot.GlobalExitRoot.GlobalExitRoot, l1InfoRoot.BlockNumber, l1BlockState.BlockHash, l1BlockEth.Hash())
log.Warnf(warnmsg)
f.LogEvent(ctx, event.Level_Critical, event.EventID_InvalidInfoRoot, warnmsg, nil)

return false, nil
}

// Check l1InfoRootIndex and GER matches
// We retrieve first the info of the last l1InfoTree event in the block
log.Debugf("getting l1InfoRoot events for L1 block %d, hash: %s", l1InfoRoot.BlockNumber, l1BlockState.BlockHash)
blocks, eventsOrder, err := f.etherman.GetRollupInfoByBlockRange(ctx, l1InfoRoot.BlockNumber, &l1InfoRoot.BlockNumber)
if err != nil {
return false, err
}

// Since in the case we have several l1InfoTree events in the same block, we retrieve only the GER of last one and skips the others
lastGER := state.ZeroHash
for _, block := range blocks {
blockEventsOrder := eventsOrder[block.BlockHash]
for _, order := range blockEventsOrder {
if order.Name == ethermanTypes.L1InfoTreeOrder {
lastGER = block.L1InfoTree[order.Pos].GlobalExitRoot
log.Debugf("l1InfoTree event, pos: %d, GER: %s", order.Pos, lastGER)
}
}
}

if f.cfg.L1InfoTreeCheckInterval.Duration.Seconds() == 999999 { //nolint:gomnd
// Get the deposit count in the moment when the L1InfoRoot was synced
depositCount, err := f.etherman.DepositCount(ctx, &l1InfoRoot.BlockNumber)
if err != nil {
return false, err
}
// l1InfoTree index starts at 0, therefore we need to subtract 1 to the depositCount to get the last index at that moment
index := uint32(depositCount.Uint64())
if index > 0 { // we check this as protection, but depositCount should be greater that 0 in this context
index--
} else {
warnmsg := fmt.Sprintf("invalid l1InfoRoot %s, index: %d, GER: %s, blockNum: %d. DepositCount value returned by the smartcontrat is 0 and that isn't possible in this context",
l1InfoRoot.L1InfoTreeRoot, l1InfoRoot.L1InfoTreeIndex, l1InfoRoot.GlobalExitRoot.GlobalExitRoot, l1InfoRoot.BlockNumber)
log.Warn(warnmsg)
f.LogEvent(ctx, event.Level_Critical, event.EventID_InvalidInfoRoot, warnmsg, nil)

return false, nil
}

log.Debugf("checking valid l1InfoRoot, index: %d, GER: %s, l1Block: %d, scIndex: %d, scGER: %s",
l1InfoRoot.BlockNumber, l1InfoRoot.L1InfoTreeIndex, l1InfoRoot.GlobalExitRoot.GlobalExitRoot, index, lastGER)

if (l1InfoRoot.GlobalExitRoot.GlobalExitRoot != lastGER) || (l1InfoRoot.L1InfoTreeIndex != index) {
warnmsg := fmt.Sprintf("invalid l1InfoRoot %s, index: %d, GER: %s, blockNum: %d. It doesn't match with smartcontract l1InfoRoot, index: %d, GER: %s",
l1InfoRoot.L1InfoTreeRoot, l1InfoRoot.L1InfoTreeIndex, l1InfoRoot.GlobalExitRoot.GlobalExitRoot, l1InfoRoot.BlockNumber, index, lastGER)
log.Warn(warnmsg)
f.LogEvent(ctx, event.Level_Critical, event.EventID_InvalidInfoRoot, warnmsg, nil)

return false, nil
}

return true, nil
}

func (f *finalizer) checkL1InfoTreeUpdate(ctx context.Context) {
broadcastL1InfoTreeValid := func() {
if !f.lastL1InfoTreeValid {
f.lastL1InfoTreeCond.L.Lock()
f.lastL1InfoTreeValid = true
f.lastL1InfoTreeCond.Broadcast()
f.lastL1InfoTreeCond.L.Unlock()
}
}

firstL1InfoRootUpdate := true
skipFirstSleep := true

if f.cfg.L1InfoTreeCheckInterval.Duration.Seconds() == 0 { //nolint:gomnd
broadcastL1InfoTreeValid()
return
}

Expand All @@ -255,7 +333,7 @@ func (f *finalizer) checkL1InfoTreeUpdate(ctx context.Context) {

l1InfoRoot, err := f.stateIntf.GetLatestL1InfoRoot(ctx, maxBlockNumber)
if err != nil {
log.Errorf("error checking latest L1InfoRoot, error: %v", err)
log.Errorf("error getting latest l1InfoRoot, error: %v", err)
continue
}

Expand All @@ -265,27 +343,18 @@ func (f *finalizer) checkL1InfoTreeUpdate(ctx context.Context) {
}

if firstL1InfoRootUpdate || l1InfoRoot.L1InfoTreeIndex > f.lastL1InfoTree.L1InfoTreeIndex {
log.Infof("received new L1InfoRoot, l1InfoTreeIndex: %d, l1InfoTreeRoot: %s, l1Block: %d",
l1InfoRoot.L1InfoTreeIndex, l1InfoRoot.L1InfoTreeRoot, l1InfoRoot.BlockNumber)
log.Infof("received new l1InfoRoot %s, index: %d, l1Block: %d", l1InfoRoot.L1InfoTreeRoot, l1InfoRoot.L1InfoTreeIndex, l1InfoRoot.BlockNumber)

// Sanity check l1BlockState (l1InfoRoot.BlockNumber) blockhash matches blockhash on ethereum. We skip it if l1InfoRoot.BlockNumber == 0 (empty tree)
if l1InfoRoot.BlockNumber > 0 {
l1BlockState, err := f.stateIntf.GetBlockByNumber(ctx, l1InfoRoot.BlockNumber, nil)
// Check if new l1InfoRoot is valid. We skip it if l1InfoTreeIndex is 0 (it's a special case)
if l1InfoRoot.L1InfoTreeIndex > 0 {
valid, err := f.checkValidL1InfoRoot(ctx, l1InfoRoot)
if err != nil {
log.Errorf("error getting L1 block %d from the state, error: %v", l1InfoRoot.BlockNumber, err)
log.Errorf("error validating new l1InfoRoot, index: %d, error: %v", l1InfoRoot.L1InfoTreeIndex, err)
continue
}

l1BlockEth, err := f.etherman.HeaderByNumber(ctx, new(big.Int).SetUint64(l1InfoRoot.BlockNumber))
if err != nil {
log.Errorf("error getting L1 block %d from ethereum, error: %v", l1InfoRoot.BlockNumber, err)
continue
}
if l1BlockState.BlockHash != l1BlockEth.Hash() {
warnmsg := fmt.Sprintf("invalid l1InfoTreeIndex %d, L1 block %d blockhash %s doesn't match blockhash on ethereum %s (L1 reorg?). Stopping syncing l1IntroTreeIndex",
l1InfoRoot.L1InfoTreeIndex, l1InfoRoot.BlockNumber, l1BlockState.BlockHash, l1BlockEth.Hash())
log.Warn(warnmsg)
f.LogEvent(ctx, event.Level_Critical, event.EventID_InvalidInfoRoot, warnmsg, nil)
if !valid {
log.Warnf("invalid l1InfoRoot %s, index: %d, l1Block: %d. Stopping syncing l1InfoTreeIndex", l1InfoRoot.L1InfoTreeRoot, l1InfoRoot.L1InfoTreeIndex, l1InfoRoot.BlockNumber)
return
}
}
Expand All @@ -296,12 +365,7 @@ func (f *finalizer) checkL1InfoTreeUpdate(ctx context.Context) {
f.lastL1InfoTree = l1InfoRoot
f.lastL1InfoTreeMux.Unlock()

if !f.lastL1InfoTreeValid {
f.lastL1InfoTreeCond.L.Lock()
f.lastL1InfoTreeValid = true
f.lastL1InfoTreeCond.Broadcast()
f.lastL1InfoTreeCond.L.Unlock()
}
broadcastL1InfoTreeValid()
}
}
}
Expand Down
7 changes: 5 additions & 2 deletions sequencer/interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"math/big"
"time"

ethermanTypes "github.com/0xPolygonHermez/zkevm-node/etherman"
"github.com/0xPolygonHermez/zkevm-node/pool"
"github.com/0xPolygonHermez/zkevm-node/state"
"github.com/ethereum/go-ethereum/common"
Expand All @@ -30,12 +31,14 @@ type txPool interface {
GetEarliestProcessedTx(ctx context.Context) (common.Hash, error)
}

// etherman contains the methods required to interact with ethereum.
type etherman interface {
// ethermanInterface contains the methods required to interact with ethereum.
type ethermanInterface interface {
TrustedSequencer() (common.Address, error)
GetLatestBatchNumber() (uint64, error)
GetLatestBlockNumber(ctx context.Context) (uint64, error)
HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error)
GetRollupInfoByBlockRange(ctx context.Context, fromBlock uint64, toBlock *uint64) ([]ethermanTypes.Block, map[common.Hash][]ethermanTypes.Order, error)
DepositCount(ctx context.Context, blockNumber *uint64) (*big.Int, error)
}

// stateInterface gathers the methods required to interact with the state.
Expand Down
73 changes: 72 additions & 1 deletion sequencer/mock_etherman.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions sequencer/sequencer.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ type Sequencer struct {
pool txPool
stateIntf stateInterface
eventLog *event.EventLog
etherman etherman
etherman ethermanInterface
worker *Worker
finalizer *finalizer

Expand All @@ -42,7 +42,7 @@ type Sequencer struct {
}

// New init sequencer
func New(cfg Config, batchCfg state.BatchConfig, poolCfg pool.Config, txPool txPool, stateIntf stateInterface, etherman etherman, eventLog *event.EventLog) (*Sequencer, error) {
func New(cfg Config, batchCfg state.BatchConfig, poolCfg pool.Config, txPool txPool, stateIntf stateInterface, etherman ethermanInterface, eventLog *event.EventLog) (*Sequencer, error) {
addr, err := etherman.TrustedSequencer()
if err != nil {
return nil, fmt.Errorf("failed to get trusted sequencer address, error: %v", err)
Expand Down
2 changes: 1 addition & 1 deletion state/pgstatestorage/l1infotree.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ func (p *PostgresStorage) GetLatestL1InfoRootVx(ctx context.Context, maxBlockNum
const getL1InfoRootSQL = `SELECT block_num, timestamp, mainnet_exit_root, rollup_exit_root, global_exit_root, prev_block_hash, l1_info_root, %s
FROM state.exit_root
WHERE %s IS NOT NULL AND block_num <= $1
ORDER BY %s DESC`
ORDER BY %s DESC LIMIT 1`

sql := fmt.Sprintf(getL1InfoRootSQL, indexFieldName, indexFieldName, indexFieldName)

Expand Down

0 comments on commit c7266e1

Please sign in to comment.