feat(cold): add eth_getLogs support with block-level log indexing#24
Merged
feat(cold): add eth_getLogs support with block-level log indexing#24
Conversation
6 tasks
Adds the index of a receipt's first log among all logs in its block, enabling callers to compute per-log logIndex for RPC responses without refetching prior receipts. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…text Add log filtering to cold storage following eth_getLogs semantics, and include block-level log indexing for RPC response construction. - Add `LogFilter` type with block range, address, and topic filters - Add `RichLog` type with full block/tx context and block_log_index - Add `ColdStorage::get_logs` with implementations for in-memory, SQLite, and PostgreSQL backends - Add `first_log_index` to `ReceiptContext` (cherry-picked from #23) - Replace `idx_logs_address` with composite `idx_logs_address_block` - Factor out `row_to_log_row` helper in SQL backend - Wire through task channel plumbing (request, handle, runner) - Comprehensive conformance tests covering all filter combinations Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
bcad481 to
4d640b4
Compare
…ackend iter_k2 had two bugs: (1) next_dual_above positioned the cursor at the first entry and returned it, but the iterator discarded that result and called next_k2() which advanced past it — so the first entry was never yielded. Fix: capture the first entry in the iterator struct and yield it before advancing. (2) The in-memory backend's next_k2 used next_dual_above(current_k1, current_k2) which is "at or above", returning the same entry forever. Fix: use read_next() (strictly above) and verify k1 still matches. Adds iter_k2 regression tests to the conformance suite. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…tches_log Move log-matching logic from a private function in mem.rs to a public LogFilter::matches_log method for cross-backend reuse. Implement get_logs on MdbxColdBackend using per-index exact_dual lookups. Remove unused SQL helper methods left over from a prior refactor. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…aits The default iter() and iter_from() implementations on KvTraverse and DualKeyTraverse discarded the entry returned by the initial positioning call (first()/lower_bound()/next_dual_above()), causing the first entry to be skipped. The typed iter_from() on TableTraverse and DualTableTraverse had a second bug: they positioned the cursor then called iter() which reset it via first(). Fix by capturing the first entry as owned data in the iterator structs (RawKvIter, RawDualKeyIter) and yielding it before calling read_next(). Also override iter()/iter_from() on the MDBX cursor to use native libmdbx iterators which handle first-entry capture natively. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
prestwich
commented
Feb 13, 2026
Replace manual exact_dual index probing with iter_k2 in MdbxColdBackend::get_logs_inner, matching the convention used by all other dual-table iterations in this file. Replace inner for/if/push loops with filter/map/extend in both MDBX and in-memory backends. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…-types These are pure data types with no cold-storage-specific dependencies. Moving them to the shared types crate makes them available to all storage backends without depending on signet-cold. Re-exported from signet_cold for backward compatibility. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… log iterators Replace verbose turbofish trait calls (DualTableTraverse::<T, _>::iter_k2, TableTraverse::<T, _>::exact) with method syntax via tx.traverse() and tx.traverse_dual(). Zip receipt and transaction iterators in get_logs_inner instead of doing per-receipt point lookups for transaction hashes. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Precompute receipt metadata at write time to eliminate read-path recomputation. IndexedReceipt wraps Receipt with tx_hash and first_log_index, removing the need to join with transactions or iterate prior receipts during queries. MDBX now stores SealedHeader (hash alongside header bytes) to eliminate all hash_slow() calls on reads. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… IndexedReceipt Replace custom RichLog/LogFilter types with alloy::rpc::types::Log (aliased as RpcLog) and alloy::rpc::types::Filter. This eliminates redundant types in favor of the standard Ethereum RPC log type. Add gas_used field to IndexedReceipt, precomputed at append time from the cumulative gas sequence. This avoids needing prior receipt lookups at query time, following the same pattern as first_log_index and tx_hash. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace unnecessary `Trait::method(receiver, ...)` calls with `receiver.method(...)` where no competing trait in scope defines the same method name. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…m queries Return SealedHeader from all header queries to preserve the cached block hash and eliminate redundant seal_slow() calls in backends. Replace the three overlapping receipt types (Confirmed<Receipt>, IndexedReceipt, ReceiptContext) with a single ColdReceipt type containing a consensus receipt with RPC-enriched logs and full block/transaction metadata. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Accept `Recovered<TransactionSigned>` in `BlockData` so the sender address is preserved at append time. Transaction queries now return `Confirmed<RecoveredTx>` and `ColdReceipt` includes a `from` field, eliminating the need for consumers to run ecrecover themselves. - Add `sender: Address` to `IndexedReceipt` and `ColdReceipt` - Add `ColdTxSenders` MDBX table for sender storage - Add SQL migration for `from_address` column - Update all backends (mem, MDBX, SQL) and conformance tests - Add `RecoveredTx` type alias and `Recovered` re-export Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move the `from_address` column from a separate migration 002 into the initial schema (001_initial.sql / 001_initial_pg.sql) and remove the idempotent ALTER TABLE logic from the backend constructor. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
ColdStorage::get_logs(Filter)witheth_getLogssemantics — filter by block range, address, and topics (OR within position, AND between positions)RichLog/LogFiltertypes withalloy::rpc::types::Log(aliased asRpcLog) andalloy::rpc::types::Filter— eliminates redundant types in favor of standard Ethereum RPC typesgas_usedtoIndexedReceipt, precomputed at append time from cumulative gas sequence — avoids prior receipt lookups at query timeConfirmed<T>tosignet-storage-types— pure data type with no cold-storage-specific deps, re-exported fromsignet_coldfor backward compatibilitytraverse()/traverse_dual()method syntax; zips receipt and transaction iterators inget_logsinstead of per-receipt point lookupsidx_logs_addresswith compositeidx_logs_address_block(address, block_number)for efficient filtered range scansrow_to_log_rowhelper in the SQL backendblock_log_indexSealedHeaderfrom all header queries, preserving cached block hash and eliminating redundantseal_slow()calls in backendsConfirmed<Receipt>,IndexedReceipt,ReceiptContext) with a singleColdReceipttype containing a consensus receipt with RPC-enriched logs and full block/transaction metadataBlockDatanow acceptsSealedHeaderdirectly, removing redundant re-sealing in both MDBX and in-memory backendsTest plan
cargo t -p signet-cold)cargo t -p signet-cold-sql --features test-utils)./scripts/test-postgres.sh)cargo t -p signet-cold-mdbx --all-features)cargo t -p signet-storage)--all-featuresand--no-default-featuresfor all cold/hot/storage crateslog_indexcorrectness, metadata correctness,gas_usedon IndexedReceiptColdReceiptmetadata verified:block_number,block_hash,transaction_index,tx_hash,gas_used,log_indexCloses #23
🤖 Generated with Claude Code