Feat: Sequencer Final PR #966
Conversation
- L1Sequencer.sol: sequencerHistory[], updateSequencer, getSequencerAt, initializeHistory - Bindings: updated ABI for new contract interface - SequencerVerifier: L1 history cache with interval cursor optimization - Signer: simplified interface (removed IsActiveSequencer) - 022-SequencerInit.ts: fixed initialize call (1 param instead of 2) - Docker: added L1_SEQUENCER_CONTRACT env for all nodes Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- run-test.sh: added build-malicious and p2p-test commands - docker-compose.override.yml: malicious-geth-0 and malicious-node-0 services - Tests: T-01~T-05 (active attacks) + T-06 (BlockSync pollution) + T-07 (resilience) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Fix grep -c multiline: use || true instead of || echo "0" - Fix env var loss: malicious override must include full env list - Swap approach: reuse synced sentry instead of fresh malicious container - Uncomment CONSENSUS_SWITCH_HEIGHT for V2 mode activation - Add SEQUENCER_PRIVATE_KEY to node-0 override Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Use staking key (0xd998...) as SEQUENCER_PRIVATE_KEY for node-0 - Add initializeHistory() call in setup to register sequencer on L1 - Fixes "no sequencer record" error in V2 mode Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- T-06: use blocksync-forge (blocksync/reactor.go) instead of sync-forge (broadcast_reactor.go) - targets the actual V1 vulnerability path - T-06: stop node-3 to create gap, restart to trigger BlockSync - Phase 0: explicit checks for V2 mode, signer, and switch height - T-04: use futureHeight (currentHeight+10000) for deterministic unsolicited test - Separate log files per phase to prevent cross-contamination Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add L1Sequencer.t.sol: 27 Foundry tests covering initialize, initializeHistory, updateSequencer, getSequencerAt binary search edge cases, and access control - Regenerate l1sequencer.go with abigen (bytecode now matches current contract with sequencerHistory[], binary search, etc.) - Update verifier.go: L1SequencerHistoryRecord -> L1SequencerSequencerRecord - Add exponential backoff retry (10s -> 20s -> ... -> 5min) when initial history load fails, instead of waiting full 5 minutes Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Avoids stuttering in abigen output (L1SequencerSequencerRecord -> L1SequencerHistoryRecord). No ABI/storage layout change. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…om L1 contract Unify the upgrade height source: instead of a CLI flag / env var, the verifier now sets upgrade.UpgradeBlockHeight from the first history record fetched from the L1Sequencer contract. - node/l1sequencer/verifier.go: call SetUpgradeBlockHeight on first successful history load (prev==0) - node/cmd/node/main.go: remove ConsensusSwitchHeight flag read; require L1 Sequencer contract address - node/flags/flags.go: delete ConsensusSwitchHeight flag definition - docker-compose.override.yml: remove 5× MORPH_NODE_CONSENSUS_SWITCH_HEIGHT - run-test.sh: remove set_upgrade_height function, add wait_for_l1_finalized to ensure L1 contract data is finalized before L2 nodes start Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
These env var overrides (DEPOSIT_CONTRACT_ADDRESS, SYNC_START_HEIGHT) and the malicious_geth_data volume should be managed via overlay/override files, not by modifying the base compose file directly. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ncer Add hakeeper module implementing a 3-node Raft cluster for sequencer HA. The HA cluster provides automatic leader election, block replication, and failover without changing the on-chain sequencer identity. node/hakeeper/: - HAService: wraps hashicorp/raft, implements SequencerHA interface - Config: layered loading (defaults -> TOML file -> CLI flags -> resolve -> validate) supports auto-detection of server_id (hostname) and advertised_addr (local IP) - BlockFSM: Raft FSM for block replication; onApplied callback drives geth apply - leaderMonitor: gates block production behind Barrier to ensure log catch-up - rpc/: JSON-RPC admin API (ha_leader, ha_clusterMembership, ha_addServerAsVoter, ha_removeServer, ha_transferLeader, ha_transferLeaderToServer) with HTTP middleware token auth on write operations node/flags/flags.go: - New flags: --ha.enabled, --ha.config, --ha.bootstrap, --ha.join, --ha.server-id, --ha.advertised-addr, --ha.rpc-token node/cmd/node/main.go: - initHAService(): init HA from flags/config when --ha.enabled is set - Fix typed-nil interface bug: pass untyped nil when HA is disabled node/sequencer/tm_node.go: - Pass HA service to tendermint node setup node/go.mod: - Add hashicorp/raft v1.7.1, raft-boltdb/v2 ops/docker-sequencer-test/: - docker-compose.ha-override.yml: 3-node Raft cluster config for devnet - run-ha-test.sh: 29-case integration test suite (config, cluster, block production, failover, admin API, lifecycle) - run-perf-test.sh: performance test harness Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Wire up the new engine_newL2BlockV2 API for reorg support: - Executor.ApplyBlockV2 now returns (applied bool, err error) matching the updated L2Node interface; detects idempotent skips and reorgs using BlockNumber + BlockByNumber checks before calling NewL2BlockV2 - RetryableClient.NewL2BlockV2 wraps the new authclient method with exponential backoff retry; excludes WrongBlockNumberError and ParentNotFoundError from retry (permanent errors) - RetryableClient.AssembleL2BlockV2 added for parent-hash-based block assembly Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add BlockHashMismatchError and InvalidNextL1MsgIndexError to the retryableError() exclusion list so the executor stops re-sending invalid payloads back to geth. Made-with: Cursor
… not in PBFT validator set - Add Syncer()/SetSyncer() accessors to Executor for explicit syncer wiring - Start L1 syncer eagerly in main.go for separated-deployment / HA sequencers that are not PBFT validators (lazy-init path never fires for them) - Guard Syncer.Start() with atomic flag to prevent duplicate goroutines Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Increase blockCh buffer from 200 to 1000 to reduce drops under load. - Panic on nil onApplied callback in BlockFSM.Apply: this can only happen due to a programmer error (forgot to wire SetOnBlockApplied) and would otherwise silently succeed on the leader while followers diverge. - gofmt: realign one-line method bodies. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- go-ethereum: v0.0.0-20260508105911-56deb7072ae4 - tendermint: v0.0.0-20260508065906-9e56b04da3c8 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add explicit replace directives in every go.mod to override MVS, because token-price-oracle indirectly required v1.10.14-..., which caused all workspace modules to resolve to the older version and miss new APIs (NewL2BlockV2, AssembleL2BlockV2, SetBlockTags, MorphTxType, updated AssembleL2Block/NewL2Block signatures). Drop the local-path replace block from go.work so the pinned pseudo-version is actually used. Follow-up: investigate the indirect dep that requires v1.10.14 and bump it so these per-module replaces can be removed. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Pull in the persistent-peer ban exemption + sigStore.Close fixes from morph-l2/tendermint feat/sequencer-optimize (commit c6f7e21e4). Updated via 'make update' after bumping TENDERMINT_TARGET_VERSION in the Makefile. All sub-modules tidied. morphnode + tendermint binaries build cleanly. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Implements SPEC-005 derivation verification: - verify_local.go: Path B local-rebuild blob verification (rebuild blockContext + L2 tx blob from local chain, compare against on-chain Rollup batch) - verify.go: extract verifyBatchRoots, gate stateException on real divergence verdicts (not transient errors) - finalizer.go + reorg.go: derivation-driven finalizer + L1 reorg detection (SPEC-005 §4.7.6), rewind-and-reset for canonicality - tag_advance.go + metrics.go: derivation-driven L2 tag management, structured failure diagnostics for Path B Common: export common/batch.BuildBlockContext for derivation reuse. go-ethereum: bump submodule to 045be0fdc7ca (NewL2BlockV2 self-heal). Ops: add second sentry node for derivation validation (4-nodes compose, node5 key, devnet setup). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaced by derivation-driven tag management (see prior commit): - node/validator/*: removed dedicated validator role - node/blocktag/*: removed standalone block-tag advancer service - node/cmd/node/main.go: drop validator/blocktag wiring - node/flags/flags.go: drop validator-specific CLI flags - ops-morph/docker-compose-validator.yml: drop validator compose file Tags are now advanced inline by the derivation loop, eliminating the extra service and the role-based branching in main. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- sync/syncer.go: make Syncer.Start idempotent via sync.Once so re-entry on retry no longer leaks goroutines or races on state - types/retryable_client.go (+test): treat ethereum.NotFound as a permanent failure rather than retrying forever - db/keys.go + db/store.go: derivation-related key helpers used by the new finalizer / tag-advance paths Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Shared L1 client: main.go does a single ethclient.Dial and threads
it into syncer, derivation, l1sequencer Tracker/Verifier/Signer, and
the rollup binding. Reads l1.rpc directly from CLI flags instead of
going through derivation.Config first.
* Derivation reorg: blob-hash mismatch now invokes the tendermint
Node.StopReactorsBeforeReorg → fetch full batch → deriveForce →
StartReactorsAfterReorg(post-reorg height) flow. HA-mode adds a
hard-stop guard (cluster invariant violation; logs full context and
returns instead of self-healing). Mock mode (d.node==nil) skips the
reactor cycle.
* deriveForce uses the new NewL2BlockV2 (*Header, error) return; the
redundant HeaderByNumber readback is gone, parent chains via the
returned header. lastHeader is initialised from the batch's parent
so it tracks the chain head end-to-end.
* Executor.ApplyBlockV2 + RetryableClient.NewL2BlockV2 updated for the
new signature. Executor.updateSequencerSet no longer stops the
syncer when this node ceases to be sequencer — derivation needs it
running on every node.
* deps: bump tendermint to 6393e1eaad71 (derivation reorg API,
StopReactorsBeforeReorg / StartReactorsAfterReorg) and go-ethereum
to 5c5b433f18f2 (NewL2BlockV2 returns header, NextL1MsgIndex
backfill on isSafe path so writeBlockStateWithoutHead's gate passes
when callers don't know the per-block index). Replace directives
for both grouped at the top of every go.mod for review locality.
* docker-sequencer-test: Dockerfile copies common/go.mod alongside
the others (workspace replace requires it). HA override adds
L1_ETH_BEACON_RPC env for ha-node-{0,1,2} (derivation refactor
validates it at startup). run-ha-test.sh service names corrected
from morph-geth-* / sentry-geth-0 to morph-el-* / sentry-el-0.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Claude Code Review
This repository is configured for manual code reviews. Comment @claude review to trigger a review and subscribe this PR to future pushes, or @claude review once for a one-time review.
Tip: disable this comment in your organization's Code Review settings.
|
Warning Review limit reached
More reviews will be available in 17 minutes and 31 seconds. Learn how PR review limits work. Your organization has run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: ⛔ Files ignored due to path filters (10)
📒 Files selected for processing (81)
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
Moves the L1-derived block insertion path off of NewL2BlockV2(isSafe=true) onto NewSafeL2Block, which now accepts SafeL2Data.ParentHash for non-head parents and lets SetCanonical reorg the chain automatically. NewL2BlockV2 becomes sequencer-signed-only (caller supplies pre-computed execution results, gate validates). The previous isSafe=true path on NewL2BlockV2 wrote blocks with caller- supplied StateRoot — for derivation.deriveForce that was the zero hash, because L1 batch metadata only carries the batch-final PostStateRoot, not per-block roots. The resulting blocks had header.Root=0 even though their state was correctly executed and committed, breaking verifyBatchRoots forever and blocking derivation cursor advance. NewSafeL2Block executes internally and fills header.Root from stateDB.IntermediateRoot, so the header on disk is consistent with the state. * node/derivation/derivation.go: deriveForce builds SafeL2Data with ParentHash = lastHeader.Hash() and calls NewSafeL2Block instead of NewL2BlockV2(isSafe=true); safeL2DataToExecutable helper deleted. * node/types/retryable_client.go + node/core/executor.go: drop isSafe arg from NewL2BlockV2. * node/derivation/verify_local.go: outline path got a v0-parent compat shim — only reachable on test/devnet where genesis batch is v0 and V1 is day-1 enabled, so the only v0 batch in the chain is genesis; on prod (V1 day-1, V2 layered on V1) the branch is dead. Reorg semantics only exist post-V2 anyway, so processing pre-V2 via outline is not a load-bearing path. Comment explains the assumption. * deps: bump tendermint to b1b3a3a1d806 (drop dead reorg-restart test harness from Node) and go-ethereum to eb5fbf8f9748 (NewSafeL2Block ParentHash, drop NewL2BlockV2 isSafe). * ops/docker-sequencer-test/Dockerfile.tx-submitter-test: new polyrepo- context Dockerfile so tx-submitter builds against local ../tendermint and ../go-ethereum siblings — the original ops/docker/Dockerfile.submitter only sees the morph repo and can't resolve workspace replace directives. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
No description provided.