Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
**/*.rs.bk
*.pdb
.DS_Store
__pycache__/
36 changes: 36 additions & 0 deletions docs/tasks/015-gate-d-real-prestate.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,42 @@ and Hiro approves the (small) spend.
- [ ] Test path uses NO RPC (only the one-time capture did); `probatio-evm` lockfile still has no revm/c-kzg.
- [ ] Provenance recorded so anyone can re-capture the same fixture.

## Capture — DONE (CC, 2026-07-10). Q1/Q2 frozen; fixture committed.

The RPC-gated step is complete. Provenance for re-capture:

- **Q1 — endpoint (FROZEN):** `https://eth.drpc.org` — free public endpoint that serves
`debug_traceTransaction` with **both** `prestateTracer` (pre-mode) and `callTracer`. **No paid
subscription, no spend.** (Probed: publicnode / 1rpc / cloudflare reject `debug_*`; Ankr needs a key;
drpc serves it free. Note: drpc 403s urllib's default User-Agent and transiently 5xxs on its free LB —
`capture_fixture.py` now sends an explicit `User-Agent` and retries 429/5xx with backoff.)
- **Q2 — tx (FROZEN):** `0x4a8f5f07e3f1019db5ddc46b344a6af9ffc3895d5128ba5f1b16a54568542f8f`
(Ethereum mainnet, block **25499896**). A Uniswap **V2** router
(`0x7a25…2488d`) `swapExactTokensForETHSupportingFeeOnTransferTokens` (`0x791ac947`), **type 2,
empty access list, tx.value 0** — satisfies the `block_env` freeze. The router does `WETH.withdraw`
then forwards **native ETH to the subject via a plain value-CALL (zero calldata, no `Transfer` log)** —
exactly ONE clean log-invisible leg. Subject = tx.from = `0xab43…4624` (an EOA, no code).
- **Fixture:** `probatio-evm/tests/fixtures/gate-d-real-swap.json` — 6 prestate accounts (token, pair,
WETH, router, subject, coinbase; only touched slots), full `block_env`,
`expected_subject_eth_wei = 0x4419e3f5a3d0a7` (**19173706984341863 wei ≈ 0.01917 ETH**), from callTracer.
- **Re-capture:** `RPC_URL=https://eth.drpc.org python3 tools/capture_fixture.py <txhash> > tests/fixtures/gate-d-real-swap.json`

### CC smoke (frame-thin, manual — NOT yet a committed test; this is Codex's job to encode)

Driving the built `reexec-server` with the fixture (`capture_inner=true`) **reproduces the leg exactly**:
`recovered == expected == 19173706984341863 wei` (both hops present: WETH→router unwrap, router→subject).
Anti-stub premise confirmed: perturbing the **pair's reserves slot `0x…08`** diverges the leg
(…341863 → …263682); perturbing slots `0x06/0x07/0x0c` breaks the swap path → leg `0` (revert-equivalent).
**Use pair slot `0x08` as the canonical anti-stub perturbation.**

### Remaining (Codex, frame-thick implementation; CC reviews)

1. **Offline reproduction test** in `probatio-evm` (no RPC): load the fixture, drive `reexec-server`
(auto-build via `REEXEC_SERVER_BIN`), assert recovered inbound leg to `subject` == `expected_subject_eth_wei`.
2. **Anti-stub test:** clone the fixture, mutate pair slot `0x08`, assert the recovered leg diverges
(or `execute` reverts). Keep the `revm`/`c-kzg`-free lockfile invariant intact (no new deps in the test path).
3. Flip Gate D to ✅ in `probatio-evm/README.md` + `STAGE1.5_EVM_REEXEC.md` once both tests are green.

## Review focus for Codex (brief-review, before CC implements)
- Is native-leg-on-real-tx the right minimal Gate D, or must it reconstruct a perp `StateSnapshot` on a real venue?
- prestateTracer completeness (Q3) — will revm re-execution diverge from mainnet on any missing prestate account?
Expand Down
1 change: 1 addition & 0 deletions probatio-evm/tests/fixtures/gate-d-real-swap.json

Large diffs are not rendered by default.

22 changes: 18 additions & 4 deletions probatio-evm/tools/capture_fixture.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,28 @@
import json
import os
import sys
import time
import urllib.error
import urllib.request


def rpc(url, method, params):
def rpc(url, method, params, retries=5):
body = json.dumps({"jsonrpc": "2.0", "id": 1, "method": method, "params": params}).encode()
req = urllib.request.Request(url, data=body, headers={"Content-Type": "application/json"})
with urllib.request.urlopen(req) as r:
out = json.load(r)
# Some public trace endpoints (e.g. drpc.org) 403 the default urllib User-Agent; send an explicit one.
req = urllib.request.Request(
url, data=body, headers={"Content-Type": "application/json", "User-Agent": "probatio-gate-d"}
)
# Free-tier trace endpoints load-balance across backends and flake transiently (429/5xx); retry with backoff.
for attempt in range(retries):
try:
with urllib.request.urlopen(req, timeout=60) as r:
out = json.load(r)
break
except urllib.error.HTTPError as e:
if e.code in (429, 500, 502, 503, 504) and attempt < retries - 1:
time.sleep(1.5 * (attempt + 1))
continue
raise
if out.get("error"):
raise SystemExit(f"RPC {method} error: {out['error']}")
return out["result"]
Expand Down