Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 20 additions & 9 deletions consensus/l2/consensus.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"errors"
"fmt"
"math/big"
"time"

"github.com/morph-l2/go-ethereum/common"
"github.com/morph-l2/go-ethereum/consensus"
Expand All @@ -16,8 +17,8 @@ import (
)

var (
l2Difficulty = common.Big0 // The default block difficulty in the l2 consensus
l2Nonce = types.EncodeNonce(0) // The default block nonce in the l2 consensus
Difficulty = common.Big0 // The default block difficulty in the l2 consensus
Nonce = types.EncodeNonce(0) // The default block nonce in the l2 consensus
)

// Various error messages to mark blocks invalid. These should be private to
Expand Down Expand Up @@ -135,7 +136,7 @@ func (l2 *Consensus) verifyHeader(chain consensus.ChainHeaderReader, header, par
return fmt.Errorf("extra-data must be empty")
}
// Verify the seal parts. Ensure the nonce and uncle hash are the expected value.
if header.Nonce != l2Nonce {
if header.Nonce != Nonce {
return errInvalidNonce
}
if header.UncleHash != types.EmptyUncleHash {
Expand All @@ -145,13 +146,19 @@ func (l2 *Consensus) verifyHeader(chain consensus.ChainHeaderReader, header, par
if l2.config.Morph.FeeVaultEnabled() && header.Coinbase != types.EmptyAddress {
return errInvalidCoinbase
}
// Don't waste time checking blocks from the future
if header.Time > uint64(time.Now().Unix()) {
return consensus.ErrFutureBlock
}
// Verify the timestamp
if header.Time <= parent.Time {
// we allow the block time to be the same as the parent time after the emerald fork
isEmerald := l2.config.IsEmerald(header.Number, header.Time)
if header.Time < parent.Time || (header.Time == parent.Time && !isEmerald) {
return errInvalidTimestamp
}
// Verify the block's difficulty to ensure it's the default constant
if l2Difficulty.Cmp(header.Difficulty) != 0 {
return fmt.Errorf("invalid difficulty: have %v, want %v", header.Difficulty, l2Difficulty)
if Difficulty.Cmp(header.Difficulty) != 0 {
return fmt.Errorf("invalid difficulty: have %v, want %v", header.Difficulty, Difficulty)
}
// Verify that the gas limit is <= 2^63-1
if header.GasLimit > params.MaxGasLimit {
Expand Down Expand Up @@ -183,14 +190,18 @@ func (l2 *Consensus) verifyHeader(chain consensus.ChainHeaderReader, header, par
// Prepare implements consensus.Engine, initializing the difficulty field of a
// header to conform to the beacon protocol. The changes are done inline.
func (l2 *Consensus) Prepare(chain consensus.ChainHeaderReader, header *types.Header) error {
header.Difficulty = l2Difficulty
header.Nonce = l2Nonce
header.Difficulty = Difficulty
header.Nonce = Nonce
header.UncleHash = types.EmptyUncleHash
header.Extra = []byte{} // disable extra field filling with bytes
// set coinbase to empty address, if feeVault is enabled
if l2.config.Morph.FeeVaultEnabled() {
header.Coinbase = types.EmptyAddress
}
// ensure the block is not in the future
if l2.config.IsEmerald(header.Number, header.Time) && header.Time > uint64(time.Now().Unix()) {
header.Time = uint64(time.Now().Unix())
}
Comment on lines +202 to +204
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Avoid clamping header time below the parent’s timestamp

Clamping to time.Now() here breaks block production as soon as the parent block is even slightly ahead of the local wall clock (which is common, since verifyHeader still accepts future timestamps). worker prepares headers with header.Time ≥ parent.Time; this clamp drags it back below the parent, the subsequent verification trips header.Time < parent.Time, and the sequencer stalls until real time catches up. Please drop this clamp or only adjust after comparing with the parent header so we never regress below parent.Time.

-	if l2.config.IsEmerald(header.Number, header.Time) && header.Time > uint64(time.Now().Unix()) {
-		header.Time = uint64(time.Now().Unix())
-	}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if l2.config.IsEmerald(header.Number, header.Time) && header.Time > uint64(time.Now().Unix()) {
header.Time = uint64(time.Now().Unix())
}
🤖 Prompt for AI Agents
In consensus/l2/consensus.go around lines 198-200, the current clamp that forces
header.Time down to time.Now() can regress the header below its parent’s
timestamp and break block production; remove the unconditional clamp or modify
the logic so you only adjust header.Time after checking the parent timestamp —
ensure header.Time is never set lower than parent.Time (e.g., take the maximum
of parent.Time and the desired time/now) so the header never regresses below its
parent.

return nil
}

Expand Down Expand Up @@ -231,7 +242,7 @@ func (l2 *Consensus) SealHash(header *types.Header) common.Hash {
// the difficulty that a new block should have when created at time
// given the parent block's time and difficulty.
func (l2 *Consensus) CalcDifficulty(chain consensus.ChainHeaderReader, time uint64, parent *types.Header) *big.Int {
return l2Difficulty
return Difficulty
}

// APIs implements consensus.Engine, returning the user facing RPC APIs.
Expand Down
17 changes: 15 additions & 2 deletions eth/catalyst/l2_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"time"

"github.com/morph-l2/go-ethereum/common"
"github.com/morph-l2/go-ethereum/consensus/l2"
"github.com/morph-l2/go-ethereum/core/state"
"github.com/morph-l2/go-ethereum/core/types"
"github.com/morph-l2/go-ethereum/eth"
Expand Down Expand Up @@ -277,7 +278,13 @@ func (api *l2ConsensusAPI) safeDataToBlock(params SafeL2Data) (*types.Block, err
BaseFee: params.BaseFee,
BatchHash: batchHash,
}
api.eth.Engine().Prepare(api.eth.BlockChain(), header)
header.Difficulty = l2.Difficulty
header.Nonce = l2.Nonce
header.UncleHash = types.EmptyUncleHash
header.Extra = []byte{}
if api.eth.BlockChain().Config().Morph.FeeVaultEnabled() {
header.Coinbase = types.EmptyAddress
}
txs, err := decodeTransactions(params.Transactions)
if err != nil {
return nil, err
Expand All @@ -302,7 +309,13 @@ func (api *l2ConsensusAPI) executableDataToBlock(params ExecutableL2Data, batchH
NextL1MsgIndex: params.NextL1MessageIndex,
BatchHash: bh,
}
api.eth.Engine().Prepare(api.eth.BlockChain(), header)
header.Difficulty = l2.Difficulty
header.Nonce = l2.Nonce
header.UncleHash = types.EmptyUncleHash
header.Extra = []byte{}
if api.eth.BlockChain().Config().Morph.FeeVaultEnabled() {
header.Coinbase = types.EmptyAddress
}

txs, err := decodeTransactions(params.Transactions)
if err != nil {
Expand Down
Loading