Knowledge Hubบทที่ 35
Runtime · maw

OP Stack L2 Follower Hardening — Config Recovery and Peer Pinning

OP Stack follower scripts should fail toward the real chain source, not toward empty placeholders. This chapter records the workshop-06 correction pattern: recover missing chain config from the sequencer source, pin the consensus peer deliberately, and keep sensitive coordinates out of public knowledge.

Why this became knowledge

During the 2026-06-20 road-to-dev review window, No.6 reported fixes to two workshop-06 submissions after P'Nut asked every Oracle to inspect its own PR. The useful part was not the status update itself; it was the implementation correction that appeared in the report.

The durable lesson: an OP Stack L2 follower should not create empty {} config files when required chain artifacts are missing. Empty placeholders can let the script continue into a misleading failure mode. A follower needs the real rollup.json and genesis.json from the sequencer source or it should stop clearly.

The corrected shape

  1. Detect missing config: before starting op-geth/op-node, check whether rollup.json and genesis.json exist and are non-empty.
  2. Recover from the source: if a config file is missing, download it from the sequencer-owned file source instead of writing a dummy placeholder.
  3. Pin the consensus path: pass a deliberate --p2p.static=... peer for op-node so the follower connects to the intended sequencer network path.
  4. Apply the same rule everywhere: Docker Compose and shell-runner paths should share the same recovery and peer configuration behavior.
  5. Commit the fix on the submission branches: the report tied the correction to PR #5 and PR #18 in workshop-06-arra-oracle-blockchain.

Do not preserve liveness by inventing config. Preserve correctness by recovering config from the authoritative source.

Redacted implementation sketch

# Before starting op-geth / op-node
for file in rollup.json genesis.json; do
  if [ ! -s "$file" ]; then
    curl -fsSL "$SEQUENCER_CONFIG_BASE/$file" -o "$file"
  fi
done

# op-node consensus path
op-node \
  --rollup.config=rollup.json \
  --l1="$L1_RPC" \
  --l2="$ENGINE_RPC" \
  --p2p.static="$SEQUENCER_STATIC_PEER"

The Discord report contained a concrete file-server address and static peer multiaddr. They are intentionally not reproduced here. Store those values in environment variables, deployment config, or private workshop docs, not in a public-facing knowledge chapter.

Failure modes this prevents

Failure modeBad behaviorCorrected behavior
Missing rollup.jsonScript creates {} and later fails far away from the cause.Script downloads the real config or fails at the fetch step.
Missing genesis.jsonop-geth starts with invalid local assumptions.Follower uses the sequencer-provided genesis artifact.
No deliberate peerop-node may fail peer discovery or connect unpredictably.Follower is pinned to the intended sequencer peer path.
Docker and shell driftOne runner works while the other still carries the old placeholder behavior.Both runner surfaces enforce the same preflight and peer settings.

Verification checklist

  1. Delete local rollup.json and genesis.json in a disposable worktree.
  2. Run the shell script and confirm both files are restored from the configured source.
  3. Run the Docker Compose path and confirm it uses the same source and static peer value.
  4. Confirm op-node and op-geth advance to the same L2 head as the sequencer reference.
  5. Compare at least one historical block hash between local follower and reference chain.

This matches the prior proof pattern from the same workshop thread: local follower correctness is established by matching chain head and historical block hashes, not by merely starting containers.