Skip to content

fix(l1): decode stored EIP-8141 frame receipts without MalformedBoolean - #7047

Draft
AnkushinDaniil wants to merge 1 commit into
lambdaclass:frames-devnet-0from
AnkushinDaniil:daniil/eip8141-receipt-rpc-decode
Draft

fix(l1): decode stored EIP-8141 frame receipts without MalformedBoolean#7047
AnkushinDaniil wants to merge 1 commit into
lambdaclass:frames-devnet-0from
AnkushinDaniil:daniil/eip8141-receipt-rpc-decode

Conversation

@AnkushinDaniil

@AnkushinDaniil AnkushinDaniil commented Jul 28, 2026

Copy link
Copy Markdown

Problem

EIP-8141 gives frame transactions their own receipt shape (ReceiptPayload = [cumulative_gas_used, payer, [frame_receipt, ...]], no top-level succeeded), so ethrex stores frame receipts with a dedicated storage encoding: every receipt write path uses Receipt::encode_storage() and every read path uses Receipt::decode_storage().

The deferred-persistence flush path (flush_block_data) was the one exception: it wrote receipts to RECEIPTS_V2 with encode_to_vec(), the consensus encoding. For legacy receipt types the two encodings happen to coincide, so this went unnoticed. For a frame receipt they differ, and reading the flushed receipt back (e.g. eth_getTransactionReceipt after the block is flushed from the deferred buffer) fails to decode with RLPDecodeError::MalformedBoolean.

Change

  • Use receipt.encode_storage() in flush_block_data, matching the direct write paths.
  • Add a testing-gated helper to buffer a block with receipts, and a regression test that round-trips a frame receipt through the deferred buffer and a flush, asserting it decodes back unchanged.

Evidence

Commands run on this branch:

cargo check -p ethrex-storage                               # pass
cargo test -p ethrex-test --test ethrex_tests storage::deferred_persistence_tests
# 16 passed; 0 failed; 0 ignored; 811 filtered out
cargo fmt --all -- --check                                  # pass
git diff --check                                            # pass

Stack

Targets frames-devnet-0. Part of a series of independent EIP-8141 fixes, one PR per logical change, each standing alone against the same base. Siblings: #7040 (canonical low-s), #7043 (recovery id at ecrecover boundary), #7044 (intrinsic gas over payload bytes), #7045 (arbitrary-scheme verification gas), #7046 (frame gas refunds), #7048 (skipped-frame status 2), #7049 (EIP-7623 calldata floor).

Consensus risk / limits

Not consensus-affecting: this is a local storage-encoding bug; block execution and receipt roots are unchanged. It affects RPC availability of frame receipts once a block leaves the deferred buffer. Covered by an in-memory store round-trip test; not exercised against a RocksDB-backed store here.

@AnkushinDaniil
AnkushinDaniil force-pushed the daniil/eip8141-receipt-rpc-decode branch from 84497d5 to ebfd1c1 Compare July 29, 2026 06:12
@AnkushinDaniil AnkushinDaniil changed the title fix(rpc): EIP-8141 frame receipt RPC MalformedBoolean decoding fix(l1): decode stored EIP-8141 frame receipts without MalformedBoolean Jul 29, 2026
@edg-l edg-l added the hegota label Jul 29, 2026
@edg-l

edg-l commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Thanks, this looks like a real gap: main's store.rs has no frame-receipt-specific decode handling, so nothing has addressed it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants