You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
docs(shadow-testing): verifier wrapper lessons, anvil gas fix, and l2_block repair script
- AGENTS.md: Add verifier wrapper deployment pitfalls
- anvil_setCode does NOT reset immutables (wrong digests)
- Do not 'fix' production Solidity without evidence
- finalizeBundlePostEuclidV2 access control notes
- estimategas.go: Fix Anvil eth_estimateGas failure
- Strip GasFeeCap/GasTipCap from CallMsg before EstimateGas
- Set high Gas limit to prevent Gas=0 rejection
- shadow-testing docs: Update Sepolia traps and lessons learned
- L1MessageQueueV2 slot 104 verification
- Anvil gas estimation and sender balance traps
- psql timeout vs TCP connectivity diagnosis
- Add fix_l2_block_transactions.py script for repairing missing
transaction data in shadow DB from L2 RPC.
|**`L1MessageQueueV2`**| Reset `nextUnfinalizedQueueIndex = 0` sufficient | Set to `MIN(total_l1_messages_popped_before)` of first target batch; **slot 104** (verify with `forge inspect`) | Wrong slot/value → `ErrorFinalizedIndexTooLarge(0x16465978)`|
55
+
|**Anvil gas estimation**| Same as mainnet |`eth_estimateGas` fails with fee caps present (`Gas=0`) | Patch `estimategas.go` or use `--min-codec-version` workaround |
56
+
|**Sender balance**| Persisted across restarts |**Resets to 0** after Anvil restart | Must re-fund EOAs before each relayer start |
57
+
|**Relayer flags**| Standard | Requires `--config <path>` AND `--min-codec-version 10`| Missing flags = wrong config or immediate exit |
55
58
|**DB scope**| Imported limited range | Full production snapshot (batches 128080+) | Relayer batch committer floods logs with commit retries |
56
59
|**Blob version**| Usually V0 | Anvil 1.0.0 cannot decode BlobSidecar V1 | Set `fusaka_timestamp: 2000000000` in relayer config |
57
60
|**Proofs in DB**| May already be v0.8.0 | Old proofs are v0.7.3 | Must reset `proving_status = 1` to regenerate with v0.8.0 |
@@ -100,6 +103,28 @@ make coordinator_setup
100
103
|`zkvm-prover/`| Build scripts and runtime config for the prover binary |
101
104
|`build/dockerfiles/`| Dockerfiles for production images |
102
105
106
+
## Troubleshooting: Verifier Wrapper Deployment on Shadow Forks
107
+
108
+
### `anvil_setCode` Does NOT Reset Immutables
109
+
-**Problem**: Copying a mainnet verifier wrapper (e.g., `ZkEvmVerifierPostFeynman`) to Anvil via `anvil_setCode` preserves the **original immutables** (`verifierDigest1`, `verifierDigest2`, `protocolVersion`). These digests are bound to the mainnet plonk verifier VK and will **never** match locally-generated proofs.
110
+
-**Symptom**: `VerificationFailed` (selector `0x439cc0cd`) even when the plonk verifier binary, public input hash, and proof are all individually correct.
111
+
-**Root cause**: The wrapper assembles its own `instances` array from immutables + `keccak256(protocolVersion || publicInput)`. Wrong immutables = wrong instances = plonk verifier rejects the proof.
112
+
-**Solution**: **Always recompile and redeploy** the wrapper with immutables extracted from the *local* proof's `instances` array (bytes 384–416 and 416–448 for digest1/digest2).
113
+
114
+
### Do Not "Fix" Production Solidity Without Evidence
115
+
-**Problem**: When `VerificationFailed` appears, it's tempting to blame the assembly loop in the wrapper (`sub(0x5a0, i)` vs `add(0x1c0, i)`).
116
+
-**Reality**: The production wrapper (`ZkEvmVerifierPostFeynman.sol`) has used `sub(0x5a0, i)` since deployment and has finalized thousands of bundles on mainnet. The loop direction maps hash bytes in **reverse order** to instance words, which matches the verifier circuit's expectation.
117
+
-**Symptom of wrong patch**: Changing the loop to `add(0x1c0, i)` inverts the hash-word layout, producing a different set of instances that also fail verification.
118
+
-**Correct diagnosis flow**:
119
+
1. Verify the plonk verifier binary matches the deployed contract runtime code.
120
+
2. Verify `keccak256(abi.encodePacked(protocolVersion, publicInput))` matches the proof metadata `bundle_pi_hash`.
121
+
3. Verify the wrapper's immutables match the local proof's digest words.
122
+
4. Only after (1–3) pass should you look at Solidity logic — and even then, production code is almost certainly correct.
123
+
124
+
### Access Control on `finalizeBundlePostEuclidV2`
125
+
-`ScrollChain.finalizeBundlePostEuclidV2` has `OnlyProver` modifier.
126
+
- On shadow fork, impersonate the registered prover EOA before sending the transaction: `cast rpc anvil_impersonateAccount <prover_address>`.
127
+
103
128
## Troubleshooting Common E2E Test Issues
104
129
105
130
### Port Conflicts (Shared Servers)
@@ -177,4 +202,6 @@ make coordinator_setup
177
202
|[`docs/testing/openvm-upgrade-testing-guide.md`](docs/testing/openvm-upgrade-testing-guide.md)| Step-by-step testing checklist after OpenVM / zkvm-prover upgrades |
|[`tests/shadow-testing/docs/README.md`](tests/shadow-testing/docs/README.md)| Shadow coordinator + local prover setup for production task replay |
205
+
|[`tests/shadow-testing/docs/LESSONS_LEARNED.md`](tests/shadow-testing/docs/LESSONS_LEARNED.md)| Hard-won debugging knowledge from past shadow tests (read before experimenting) |
206
+
|[`tests/shadow-testing/docs/QUICKSTART.md`](tests/shadow-testing/docs/QUICKSTART.md)| Quick reference for common shadow testing commands |
180
207
|[`docs/testing_reports/openvm-v1.6.0-guest-v0.8.0-May19.md`](docs/testing_reports/openvm-v1.6.0-guest-v0.8.0-May19.md)| Test report for PR #1783 (OpenVM 1.6.0, guest v0.8.0) |
| Verifier | Copy from mainnet (`anvil_setCode`) |Check MVRV first; may already match|`cast call <MVRV> "getVerifier(uint256,uint256)" 10 <batchIndex>`|
30
30
31
31
## Critical Traps (Do Not Skip)
32
32
@@ -58,9 +58,12 @@ Before executing a single command:
58
58
-**Rule**: The contract checks `committedBatches[batchIndex]` where `batchIndex` is the **end batch** of the bundle. Verify this is non-zero before finalizing.
59
59
60
60
### Trap 5: `L1MessageQueueV2` Index Mismatch (Sepolia)
-**Cause**: `nextUnfinalizedQueueIndex < totalL1MessagesPoppedOverall` because `nextCrossDomainMessageIndex` was not synced.
63
-
-**Rule**: On Sepolia, `nextCrossDomainMessageIndex` and `nextUnfinalizedQueueIndex` must be **equal** before finalizing.
61
+
-**Symptom**: `ErrorFinalizedIndexTooLarge(0x16465978)` or `ErrorFinalizedIndexTooSmall`.
62
+
-**Cause**: `nextUnfinalizedQueueIndex` does not match the pre-finalization state expected by the first target batch. The fork block is AFTER real finalization, so the real state has post-finalization values.
63
+
-**Rule**:
64
+
1. Set `nextUnfinalizedQueueIndex` to `MIN(total_l1_messages_popped_before)` of the first target batch's chunks (from DB).
65
+
2.**Use `forge inspect L1MessageQueueV2 storage-layout`** to find the exact storage slot (it's slot 104, NOT slot 0 or 4, due to OpenZeppelin `__gap`).
66
+
3. Never guess storage slots from source code.
64
67
65
68
### Trap 6: Parent Batch Missing
66
69
-**Symptom**: Relayer logs `Batch.GetBatchByIndex error: record not found, index: <parent>`.
@@ -81,6 +84,27 @@ Before executing a single command:
81
84
-**Cause**: Import script copied `proof` columns from production RDS, overwriting locally-valid shadow proofs.
82
85
-**Rule**: **Never import `proof` columns**. Import only metadata, then reset `proving_status = 1` and re-prove locally.
-**Symptom**: `failed to get fee data, err: Out of gas: gas required exceeds allowance: 0`.
89
+
-**Cause**: Anvil's `eth_estimateGas` fails when `CallMsg` has `GasFeeCap`/`GasTipCap` set but `Gas` is 0 (Go Ethereum client's default).
90
+
-**Rule**: If testing relayer against Anvil and gas estimation fails, patch `estimategas.go` to strip fee caps from the `EstimateGas` call (see `LESSONS_LEARNED.md` for exact patch).
91
+
92
+
### Trap 10: Sender Balance Lost After Anvil Restart
93
+
-**Symptom**: `failed to send transaction, err: Insufficient funds for gas * price + value` even after successful gas estimation.
94
+
-**Cause**: `anvil_setBalance` funds do not persist across Anvil restarts.
95
+
-**Rule**: After every Anvil restart, verify and re-fund sender EOAs before starting the relayer:
0 commit comments