Skip to content

consensus/l2: allow the block has the same time with the parent block #224

Merged
FletcherMan merged 3 commits intomainfrom
morph_header_time
Nov 14, 2025
Merged

consensus/l2: allow the block has the same time with the parent block #224
FletcherMan merged 3 commits intomainfrom
morph_header_time

Conversation

@FletcherMan
Copy link
Copy Markdown
Collaborator

@FletcherMan FletcherMan commented Nov 3, 2025

1. Purpose or design rationale of this PR

Considering that the block time will be less than 1 second in the future, and the timestamp has a precision of seconds. Therefore, this PR supports the possibility that adjacent blocks can have the same time.

2. PR title

Your PR title must follow conventional commits (as we are doing squash merge for each PR), so it must start with one of the following types:

  • build: Changes that affect the build system or external dependencies (example scopes: yarn, eslint, typescript)
  • ci: Changes to our CI configuration files and scripts (example scopes: vercel, github, cypress)
  • docs: Documentation-only changes
  • feat: A new feature
  • fix: A bug fix
  • perf: A code change that improves performance
  • refactor: A code change that doesn't fix a bug, or add a feature, or improves performance
  • style: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)
  • test: Adding missing tests or correcting existing tests

3. Deployment tag versioning

Has the version in params/version.go been updated?

  • This PR doesn't involve a new deployment, git tag, docker image tag, and it doesn't affect traces
  • Yes

4. Breaking change label

Does this PR have the breaking-change label?

  • This PR is not a breaking change
  • Yes

Summary by CodeRabbit

  • New Features

    • Enhanced timestamp validation with Emerald fork support — allows equal timestamps post-fork and rejects future-dated blocks.
  • Refactor

    • Consensus parameters (difficulty, nonce) made public for clearer configuration and consistent validation.
    • Block header initialization standardized with explicit defaults for difficulty, nonce, uncle hash, extra data, and conditional coinbase handling when fee-vault is enabled.

@FletcherMan FletcherMan requested a review from a team as a code owner November 3, 2025 12:12
@FletcherMan FletcherMan requested review from Web3Jumb0 and removed request for a team November 3, 2025 12:12
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Nov 3, 2025

Walkthrough

The L2 consensus code exports Difficulty and Nonce, adds Emerald-aware timestamp validation and a future-block guard, and Catalyst now directly initializes header fields (Difficulty, Nonce, UncleHash, Extra, conditional Coinbase) instead of calling Engine().Prepare.

Changes

Cohort / File(s) Summary
L2 Consensus Constants & Validation
consensus/l2/consensus.go
Renames l2DifficultyDifficulty and l2NonceNonce (exported). Updates verifyHeader to validate header.Difficulty/header.Nonce against these exports. Adds future-block guard and Emerald-aware timestamp validation (allows equal timestamps after Emerald fork). Prepare now sets header.Difficulty and header.Nonce and adjusts future-timestamp handling. CalcDifficulty returns Difficulty. Adds time import.
Catalyst API Header Initialization
eth/catalyst/l2_api.go
Imports consensus/l2. Replaces Engine().Prepare calls in safeDataToBlock and executableDataToBlock by explicitly setting header.Difficulty = l2.Difficulty, header.Nonce = l2.Nonce, header.UncleHash = types.EmptyUncleHash, header.Extra = []byte{}, and conditionally header.Coinbase when Fee Vault is enabled. Preserves other header fields.

Sequence Diagram(s)

sequenceDiagram
    actor Client
    participant Catalyst as eth/catalyst
    participant L2 as consensus/l2

    note over Client,Catalyst: Previous flow (delegated Prepare)
    Client->>Catalyst: create block data
    Catalyst->>L2: Engine().Prepare(header, ...)
    L2->>L2: set Difficulty, Nonce, timestamp guards
    L2-->>Catalyst: prepared header
    Catalyst-->>Client: block

    note over Client,Catalyst: New flow (direct init)
    Client->>Catalyst: create block data
    Catalyst->>Catalyst: header.Difficulty = l2.Difficulty
    Catalyst->>Catalyst: header.Nonce = l2.Nonce
    Catalyst->>Catalyst: header.UncleHash = EmptyUncleHash
    Catalyst->>Catalyst: header.Extra = []byte{}
    Catalyst->>Catalyst: if FeeVault: header.Coinbase = empty
    Catalyst-->>Client: block
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • Inspect consensus/l2/consensus.go for correct export semantics, timestamp/future-time logic, and that Prepare still enforces intended invariants.
  • Verify eth/catalyst/l2_api.go now sets all header fields previously handled by Engine().Prepare (no missing side-effects).
  • Search repo for other uses of the old private names or expectations of Engine().Prepare side-effects.

Poem

I’m a rabbit in the code so spry,
Difficulty and Nonce now wave hi,
Timestamps bend with Emerald light,
Headers hop into place just right,
Hop—new blocks bound by moonlit sky 🐰✨

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly addresses the main change: allowing blocks with the same timestamp as parent blocks in the L2 consensus logic.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch morph_header_time

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f5d8b8a and 44b447f.

📒 Files selected for processing (1)
  • consensus/l2/consensus.go (6 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
consensus/l2/consensus.go (2)
core/types/block.go (3)
  • EncodeNonce (46-50)
  • EmptyUncleHash (36-36)
  • EmptyAddress (37-37)
consensus/errors.go (1)
  • ErrFutureBlock (32-32)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (go)
🔇 Additional comments (3)
consensus/l2/consensus.go (3)

7-7: LGTM: Import and exported constants support the new validation logic.

The time import is necessary for the future-block checks, and exporting Difficulty and Nonce aligns with their usage in eth/catalyst/l2_api.go where they directly initialize header fields.

Also applies to: 20-21


149-152: Future-block guard is sound, but verify clock-skew tolerance.

Rejecting blocks with timestamps ahead of the local clock is correct. However, zero drift tolerance may reject valid blocks from peers whose clocks are slightly ahead. For L2 consensus with a centralized sequencer, this is likely acceptable, but confirm whether a small drift window (e.g., 5-15 seconds) is needed for network resilience.


154-157: LGTM: Timestamp validation correctly implements the Emerald fork behavior.

The logic properly enforces strict monotonic increase before Emerald (header.Time > parent.Time) and allows non-decreasing timestamps after Emerald (header.Time >= parent.Time). This aligns with the PR objective to support sub-second block production intervals.

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 golangci-lint (2.5.0)

Error: can't load config: unsupported version of the configuration: "" See https://golangci-lint.run/docs/product/migration-guide for migration instructions
The command is terminated due to an error: can't load config: unsupported version of the configuration: "" See https://golangci-lint.run/docs/product/migration-guide for migration instructions


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 62fcaab and fcfbb14.

📒 Files selected for processing (2)
  • consensus/l2/consensus.go (6 hunks)
  • eth/catalyst/l2_api.go (3 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
eth/catalyst/l2_api.go (2)
consensus/l2/consensus.go (2)
  • Difficulty (20-20)
  • Nonce (21-21)
core/types/block.go (2)
  • EmptyUncleHash (36-36)
  • EmptyAddress (37-37)
consensus/l2/consensus.go (1)
core/types/block.go (1)
  • EncodeNonce (46-50)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (go)

Comment thread consensus/l2/consensus.go
Comment on lines +198 to +200
if l2.config.IsEmerald(header.Number, header.Time) && header.Time > uint64(time.Now().Unix()) {
header.Time = uint64(time.Now().Unix())
}
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.

@FletcherMan FletcherMan merged commit aa40ed0 into main Nov 14, 2025
8 checks passed
@FletcherMan FletcherMan deleted the morph_header_time branch November 14, 2025 07:31
@coderabbitai coderabbitai Bot mentioned this pull request Dec 11, 2025
13 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants