REORG LAB / SAVED LOCAL EXECUTION

Same height. Different history.

Run rl-1e4e5f8aae75 · 2026-09-13T17:00:22.645Z
Hardhat 3.16.0 / EDR · v24.16.0

The experiment

Branch A head: 3; branch B head: 3. Same height: true. Different hashes: true. Index unchanged after node-only revert: true.

01 / Anchor — Deploy TaskBoard at block 1; snapshot the node before any task event.

02 / Branch A — Complete tasks #42 and #99. Persist both indexers at A’s head, including row identities and block checkpoints.

03 / Replace history — Restore only the node. Complete #7 and #9 on branch B; keep A’s persisted index untouched. Both heads are block 3.

04 / Reconcile — Locate the common ancestor at block 1. Remove 2 A rows and replay 2 B rows.

05 / Challenge the conclusion — Run replay, stale-disk restart, shallow-history and interrupted-RPC controls. Exercise a separate completion-count projector on a subsequent block.

Projection comparison

AdapterOutcomeState agreesOrphan / missing / duplicate rows
Height-only cursordivergedfalse2 / 2 / 0
Rollback + replaymatchedtrue0 / 0 / 0
Clean rebuildmatchedtrue0 / 0 / 0

Controls

Normal indexing: PASS (expected pass; expectation met)
Before the branch switch, the projection agrees with contract storage and canonical event identities.

Branch replacement exercised: PASS (expected pass; expectation met)
Node restore returned true; A/B reach the same height with distinct block hashes. The on-disk index stayed byte-for-byte unchanged when only the node reverted.

Expose the flawed projection: PASS (expected pass; expectation met)
Height-only indexing retains two orphaned rows and misses two canonical rows, despite the same completed-task total.

Recover state and exact rows: PASS (expected pass; expectation met)
Recovered task state and row identities match both pinned contract truth and a clean rebuild.

Replay is idempotent: PASS (expected pass; expectation met)
A second sync at the same canonical head preserves the complete persisted snapshot and adds zero rows.

Restart from the stale disk index: PASS (expected pass; expectation met)
A fresh adapter instance loads A from JSON on disk and recovers to the same B state and rows; no in-memory recovery state is carried over.

Insufficient rollback depth: INCONCLUSIVE (expected inconclusive; expectation met)
No common ancestor within 1 block(s); full reconstruction required. The old index must remain unchanged; no claim of recovery.

Interrupted RPC read: INCONCLUSIVE (expected inconclusive; expectation met)
Injected RPC failure while reading canonical logs. The candidate snapshot is discarded; the persisted index stays unchanged.

Second projector example: PASS (expected pass; expectation met)
The exported Projector interface runs an independent transition counter: after reopening #7, the result is 1 and agrees with contract storage.

Canonical rows

Block 2 · task #7 → complete
0x1d81b1e60bcc244e3f76baa473fbf43a3035a30c2774f50d3363fced42682830:0x8a1b7d6ac79ac3e17964d4388e10a2d220823dbec7c78527716a657eec5a7f68:0

Block 3 · task #9 → complete
0x15da9b43db2853b3f010650c8ece038474fc3f92967fe62cad078f01f84a0ccf:0x262330430bc829d772af7dd23f93ec5584d9a0008262a7ccbd3db3db45e3bc29:0

Pinned truth

TaskBoard storage via eth_call with EIP-1898 blockHash + requireCanonical; eth_getLogs pinned to each canonical blockHash. Target hash checked before and after.

Head 0x15da9b43db2853b3f010650c8ece038474fc3f92967fe62cad078f01f84a0ccf

{
  "7": true,
  "9": true,
  "42": false,
  "99": false
}

Second adapter

A separate delta-based projector consumes the canonical stream and then a new #7 → open transition at block 4. The contract counter is read at that exact block hash. Result 1; contract 1.

Limits

Reproduce

npm ci
npm run lab

The runner creates a new local EVM and overwrites the latest JSON, standalone HTML and local index snapshots. No wallet or funds are required.

Engine source digest: 16f7135f19cf96ed4a3a9330f93d1e950fa16c93f287ea372047baf7c817e098
Generated by Reorg Lab · Jhonatan del rio mejia, with Codex assistance · MIT