Skip to content

fix: validate receipt/log membership against block txs#160

Open
jxom wants to merge 1 commit intomainfrom
fix/receipt-membership-validation
Open

fix: validate receipt/log membership against block txs#160
jxom wants to merge 1 commit intomainfrom
fix/receipt-membership-validation

Conversation

@jxom
Copy link
Copy Markdown
Member

@jxom jxom commented Mar 31, 2026

Audit Findings Fixed

Two High severity findings:

1. Unverified eth_getBlockReceipts Data Allows Malicious RPC to Inject Orphan Logs/Receipts

A malicious or buggy RPC could return receipts with transaction_hash values not present in the block's transaction list. These orphan receipts/logs would be written to the database unchecked.

Fix: After fetching both blocks and receipts, validate_receipts() verifies that every receipt's transaction_hash exists in the corresponding block's transaction list.

2. Partial eth_getBlockReceipts Response Bypasses Receipt Backfill

If the RPC returns fewer receipts than transactions in a block, sync would succeed but leave permanent gaps in receipt/log data (gas_used, fee_payer remain NULL).

Fix: validate_receipts() verifies that the receipt count matches the transaction count for each block, erroring on mismatch.

Changes

  • Added validate_receipts() function in src/sync/decoder.rs that checks both invariants
  • Called it from all three sync entry points:
    • SyncEngine::fetch_range (realtime sync)
    • SyncEngine::sync_block (single block sync)
    • sync_range_standalone (gap-fill workers)
  • Added 6 unit tests covering happy path, empty blocks, orphan detection, count mismatches, and batch length mismatches

Testing

cargo test --lib decoder::tests  # 11 tests pass (6 new)
cargo test --lib                 # 179 tests pass
cargo check                      # clean (no new warnings)

Two High severity audit findings fixed:

1. Unverified eth_getBlockReceipts data — a malicious/buggy RPC could
   inject orphan receipts/logs with transaction hashes not present in
   the block. Now validate_receipts() checks every receipt's tx_hash
   exists in the block's transaction list.

2. Partial eth_getBlockReceipts response — if RPC returns fewer receipts
   than transactions, sync would succeed with permanent gaps. Now
   validate_receipts() verifies receipt count matches tx count per block.

Validation applied at all three sync entry points:
- SyncEngine::fetch_range (realtime sync)
- SyncEngine::sync_block (single block sync)
- sync_range_standalone (gap-fill workers)

Added 6 unit tests covering: happy path, empty blocks, orphan receipt
detection, count mismatch (fewer/more), and batch length mismatch.

Amp-Thread-ID: https://ampcode.com/threads/T-019d458d-01b9-76ca-9fb1-b960cd163292
Co-authored-by: Amp <amp@ampcode.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant