ledger N7: the method map (the shipped app never sends the panic class before synced); 0.3.18 plan row

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
igneum-labs 2026-10-07 06:54:45 +00:00
parent cabe9cd12a
commit 32f02088e6
2 changed files with 3 additions and 1 deletions

View file

@ -2362,4 +2362,4 @@ Open 6 October 2026 22:43Z to the 0.3.17 publish (the fix rides 0.3.17; main's r
### N7. One block-tagged RPC request kills a node whose exec follower has no record yet
Open 7 October 2026 06:4xZ (found by the node lane's Mac headers-proof join gate: the joining node died at 66 percent of the headers stage with "index out of bounds: the len is 0 but the index is 0" at igneum/exec/src/rpc.rs, a local client having polled eth_getBlockByNumber("latest") on 127.0.0.1:26790). Class: `resolve_block` maps "latest" to `tip_number()` = `records.len().saturating_sub(1)` = 0 on an empty vector, and every method that then indexes `state.records[n]` (eth_getBlockByNumber, eth_getBlockTransactionCountByNumber, eth_getBlockReceipts, eth_getTransactionByBlockNumberAndIndex, eth_feeHistory's range, eth_getLogs' range, eth_call / eth_estimateGas / igneum_estimateGas through `simulate`) panics on a tokio worker; the node's panic hook exits the process. Every shipped tree carries it (0.3.17's 5899f603 and the 0.3.18 candidates). Exposure: a fresh or restarting node between the exec RPC binding and the follower's first record; the shipped app calls only eth_blockNumber and igneum_* methods (none index records), so no app-driven node died; a wallet or any third-party client takes the path. The testnet seeds at height 0 hold the genesis record and answered block 0 on 7 October 06:5xZ, so their window is a restart, not steady state. Mitigation on the public testnet RPC (main's order): the whole method class refused at the rpc-filter layer on seed1 until the fixed node is on every seed (BLOCKED_UNTIL_FIXED_NODE; seeds 2 and 3 expose no RPC; no devnet public RPC exists). Fix: every records index bounds-checked and "latest" on an empty state answering null, with a test on an empty state, by the node lane on ca3-v4-0318 (commit to be named), into release-0.3.18-node as its third merge; 0.3.18 carries it. Status: OPEN until 0.3.18 is live and the seeds run it.
Open 7 October 2026 06:4xZ (found by the node lane's Mac headers-proof join gate: the joining node died at 66 percent of the headers stage with "index out of bounds: the len is 0 but the index is 0" at igneum/exec/src/rpc.rs, a local client having polled eth_getBlockByNumber("latest") on 127.0.0.1:26790). Class: `resolve_block` maps "latest" to `tip_number()` = `records.len().saturating_sub(1)` = 0 on an empty vector, and every method that then indexes `state.records[n]` (eth_getBlockByNumber, eth_getBlockTransactionCountByNumber, eth_getBlockReceipts, eth_getTransactionByBlockNumberAndIndex, eth_feeHistory's range, eth_getLogs' range, eth_call / eth_estimateGas / igneum_estimateGas through `simulate`) panics on a tokio worker; the node's panic hook exits the process. Every shipped tree carries it (0.3.17's 5899f603 and the 0.3.18 candidates). Exposure: a fresh or restarting node between the exec RPC binding and the follower's first record; the shipped app calls only eth_blockNumber and igneum_* methods (none index records), so no app-driven node died; a wallet or any third-party client takes the path. The testnet seeds at height 0 hold the genesis record and answered block 0 on 7 October 06:5xZ, so their window is a restart, not steady state. Mitigation on the public testnet RPC (main's order): the whole method class refused at the rpc-filter layer on seed1 until the fixed node is on every seed (BLOCKED_UNTIL_FIXED_NODE; seeds 2 and 3 expose no RPC; no devnet public RPC exists). Fix: every records index bounds-checked and "latest" on an empty state answering null, with a test on an empty state, by the node lane on ca3-v4-0318 (commit to be named), into release-0.3.18-node as its third merge; 0.3.18 carries it. The method map (7 October 07:0xZ, settled against a 0.3.17.1 cut): the dead node's site is eth_getBlockByNumber (rpc.rs:689 in 6e4ace3f). The shipped 0.3.17 Igneum Miner app sends twelve exec RPC methods, all from prover.rs: eth_blockNumber and eleven igneum_* reads and submits; no block-tagged eth_ method. Of those, igneum_getAssignedShards (records[from..=tip], from = max(tip-lookback, 1)) and igneum_getProofRecords (records[n] after resolve_block) panic on an empty vector too, but the prover loop sends them only once the node reads synced, and while unsynced sends only igneum_getExecStatus and igneum_getProvingStatus, which index nothing; on the devnet the exec follower holds records from the packaged restart block (27276) early in the block stage, before isSynced flips. The Igneum Wallet app sends eth_getBlockByNumber("latest") (igneum-wallet/src/evm.rs:58) to its own bundled node (26800). Public filter block extended to the five igneum_* allowlist reads that index records (getProofRecords, getAssignedShards, getSegment, getShardPlan, getTransactionStatus). Ruling (main): 0.3.18 carries the fix; no 0.3.17.1. Status: OPEN until 0.3.18 is live and the seeds run it.

View file

@ -196,3 +196,5 @@ Windows run 37580969266 green (06:27Z): Igneum-Miner-Setup-0.3.18.exe 2fcaf093,
## 15. HOLD on ae17ad00: the exec RPC panic (the node lane, 06:4xZ)
The Mac's headers-proof join gate killed its joining node mid-IBD on the canary-fix binaries: a panic at igneum/exec/src/rpc.rs ("index out of bounds: the len is 0 but the index is 0") on a tokio worker, and the node's panic hook exits the process. The site: eth_getBlockByNumber reading state.records[n] after resolve_block mapped "latest" to tip_number() = 0 on an exec state with no records (the follower still "waiting for consensus to sync"); a local client polled 127.0.0.1:26790 during the IBD and the node died at 66 percent of the headers stage. Every records index in that file is unchecked on both trees (0.3.17's 5899f603: rpc.rs lines 474, 550, 591 to 629; ae17ad00: 575, 651, 692 to 730), so any fresh or restarting node with the exec RPC bound and a client asking eth_getBlockByNumber("latest") before the follower has a record dies. The shipped app itself calls only eth_blockNumber and igneum_* methods (none index records by block number), so the live 0.3.17 risk is a wallet or third-party client on a fresh node mid-IBD; the fix (every index bounds-checked; "latest" on an empty state answers null; a test on an empty state) goes onto ca3-v4-0318 and into release-0.3.18-node as a third merge. The pin stays at ae17ad00 on origin and nothing is staged further until the third tip; the ae17ad00 canary runs on (nothing attached to its eth_ port) as an early read of the other rows.
**The 0.3.17.1 question settled (07:0xZ, main's rule: a hotfix only if the shipped app sends the panic class):** no. The dead harness node's site is eth_getBlockByNumber; the shipped app sends eth_blockNumber and eleven igneum_* methods only (grep of the whole 0.3.17 app tree); its two records-indexing polls (igneum_getAssignedShards, igneum_getProofRecords) run only after the node reads synced, when a devnet follower already holds records from the packaged restart block; the two it sends while unsynced index nothing. The wallet app sends eth_getBlockByNumber to its own node. Ledger N7 carries the method map. The public testnet filter now blocks ten eth_ and six igneum_ records-indexing methods (seed1, verified; tree copy committed).