Skip to content

Feat: Single Sequencer #36

Open
tomatoishealthy wants to merge 9 commits into
mainfrom
feat/sequencer-optimize
Open

Feat: Single Sequencer #36
tomatoishealthy wants to merge 9 commits into
mainfrom
feat/sequencer-optimize

Conversation

@tomatoishealthy
Copy link
Copy Markdown


PR checklist

  • Tests written/updated, or no tests needed
  • CHANGELOG_PENDING.md updated, or no changelog entry needed
  • Updated relevant documentation (docs/) and code comments, or no
    documentation updates needed

allen.wu and others added 7 commits May 8, 2026 14:47
- StateV2: roleCheckRoutine for dynamic role detection, SequencerHA interface
- ApplyBlock: full mutex serialization + idempotent height check
- VerifyBlockSignature: fail-close, all V2 blocks must have valid signatures
- SignatureStore: independent block signature persistence (LevelDB)
- BroadcastReactor: unified signature verification, SyncReq tracking, HasSigner filter
- Interfaces: SequencerVerifier, Signer, SequencerHA

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Remove SequencerAddress, SetSequencerAddress, IsSequencerAddress,
  RecoverBlockV2Signer from types/block_v2.go (unused since SequencerVerifier)
- Remove duplicate BlockV2ToProto/ProtoToBlockV2 from broadcast_reactor.go,
  use types.BlockV2ToProto instead
- Clean up unused imports (fmt, crypto, math/big, seqproto)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
SequencerHA interface (sequencer/interfaces.go):
- Start/Stop/IsLeader/Join/Commit/Subscribe/SetOnBlockApplied
- Abstracts Raft cluster for StateV2 block production

StateV2 changes (sequencer/state_v2.go):
- Dynamic block interval: fastTicker (300ms) polls txpool, slowTimer (3s)
  produces empty blocks as fallback for chain liveness
- Split produceBlock into assembleBlock + commitBlock for clean separation
  of block assembly (read-only) vs signing+committing (side-effecting)
- HA path: signBlock -> ha.Commit (Raft replication, FSM handles apply)
- Non-HA path: signBlock -> ApplyBlock -> broadcastCh
- ApplyBlock: reorg detection + idempotent skip + signature enforcement
- Remove blockInterval constructor parameter, use package-level constants

node/node.go:
- Accept SequencerHA parameter, inject SetOnBlockApplied callback

Test fixes:
- Add Start/Stop/SetOnBlockApplied to mockSequencerHA
- Add Signature field to ApplyBlock test blocks (signature enforcement)
- Fix RollbackTo_All assertion (count should be 0, not 1)
- Remove blockInterval parameter from all NewStateV2 test calls

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ror)

Reorg detection and idempotent checks are now handled in the Executor
layer (morph node), not in StateV2:

- L2Node.ApplyBlockV2 interface returns (applied bool, err error)
  so callers can distinguish real applies from idempotent skips
- StateV2.ApplyBlock only updates latestBlock when applied=true,
  preventing state regression during HA Raft log replay
- Remove reorg detection logic and common.Hash import from StateV2
  (reorg log now emitted by Executor.ApplyBlockV2)
- MockL2Node tracks maxAppliedHeight for idempotent behavior in tests
- Remove mock reorg code (startMockReorgRoutine, triggerMockReorg)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Bind the sequencer signature to the block body by rehashing BlockV2
fields in VerifyBlockSignature and rejecting on mismatch. Companion
changes in go-ethereum and morph/node.

Made-with: Cursor
…zation

- Ban mechanism: in-memory bannedPeers map (10-min TTL) + isBanned check in AddPeer
- Rate limiter: per-peer token bucket (50 req/s, burst 250) protecting L2 geth RPC
- Height validation: reject blocks outside [localHeight-MaxPendingHeightBehind, localHeight+MaxPendingHeightAhead]
- Timeout-ban: removeTimeoutPeers bans peers that declare height but never respond (20s TTL)
- Error classification: ErrInvalidSignature (ban) vs ErrVerifierUnavailable (local problem, no ban)
- Startup gate: routinesStarted atomic.Bool replaces waitSync; Receive() silently drops until routines start
- OnBlockRequest sanity check: reject Height<=0 or Height>localHeight without touching cache/RPC
- Decode failures in broadcast/sync handlers now trigger banPeer

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Audit finding M-02: switchToSequencerMode used time.Sleep(100ms) as a
heuristic to wait for finalizeCommit to return before stopping the
consensus reactor. This is a non-deterministic synchronization that may
fail under high load or GC pauses.

Changes:

1. consensus/state.go: in finalizeCommit's upgrade branch, call cs.Stop()
   BEFORE cs.onUpgrade(). Stop() closes the BaseService quit channel
   synchronously, which establishes the happens-before relationship that
   any goroutine started by onUpgrade can rely on.

2. node/node.go: replace time.Sleep(100ms) with consensusState.Wait().
   Wait() blocks until cs.done is closed (the very last action of
   receiveRoutine), giving a deterministic synchronization point that
   replaces the time-based heuristic.

3. consensus/reactor.go: remove unused StopForUpgrade method. It had no
   callers and bypassed BaseService.Stop's atomic stopped flag, which
   would cause OnStop to be invoked twice during node shutdown.

Verified via Docker integration test on feat/sequencer-optimize:
- PBFT → V2 upgrade flow passes
- HA cluster forms correctly (35/35 HA tests pass)
- P2P security tests pass

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@tomatoishealthy tomatoishealthy requested a review from a team as a code owner May 9, 2026 15:26
@tomatoishealthy tomatoishealthy requested review from Web3Jumb0 and removed request for a team May 9, 2026 15:26
allen.wu and others added 2 commits May 15, 2026 12:32
Two indirect deps were missing from go.mod:
  - github.com/VictoriaMetrics/fastcache v1.12.1
  - github.com/prometheus/tsdb v0.7.1

These are pulled in transitively. `go build` was failing with 'updates to
go.mod needed'. `go mod tidy` fixes it without changing direct dependencies.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Allow operators to whitelist trusted upstream peers (sequencer / sentry /
boot nodes) so that fullnodes do not accidentally ban them on transient
faults. Whitelist is configured via the existing [p2p].persistent_peers
setting — no new config option introduced.

broadcast_reactor.go:
  banPeer now short-circuits for peers where Peer.IsPersistent() is true.
  The peer is still disconnected via Switch.StopPeerForError so connection
  state (sync requests, gossip cache, rate limiter) is fully reset by the
  RemovePeer callback chain. The peer is NOT added to bannedPeers, so the
  Switch's automatic reconnect will pass AddPeer's isBanned gate and the
  link recovers immediately instead of being unreachable for 10 minutes.

  A new log prefix '[WHITELIST_ALARM]' is emitted on each exemption so
  log-aggregation systems can alert when a whitelisted peer is misbehaving.

signature_store.go:
  Add Close() method delegating to the underlying tm-db. Closes a code
  consistency gap where blockStore and stateStore were closed on shutdown
  but sigStore was not.

node.go:
  Invoke sigStore.Close() in OnStop alongside blockStore and stateStore.

Tests:
  TestBanPeer_PersistentPeerSkipsBanList — core invariant
  TestBanPeer_NonPersistentPeerEntersBanList — regression guard
  TestBanPeer_PersistentPeerAddPeerNotRejected — full reconnect cycle

Note: the value of this exemption is verified end-to-end by an integration
test on a separate disconnect-test branch (see ops/docker-sequencer-test
follow-up).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.

1 participant