EIP-8304 Trustless log and transaction index

Hegota Declined for Inclusion · Scaling & data · EIP status Draft · created 2026-06-17

Builds a log and transaction index table for each block and stores its root hash in a system contract. Anyone can verify with a proof that log and transaction lookup results are correct.

Why it is needed

On Ethereum, a log is an event record that a smart contract emits, and it can be written at a much lower gas cost than writing to state. However, the protocol does not provide an efficient way to look these logs up. The bloom filter in each block was reasonably useful early on, but it is now saturated and practically useless. As a result, most users depend on remote providers (RPC services), which cannot prove that their query results are correct. If a contract sits on a trustless distributed ledger but its output has to be taken on trust, the original purpose is defeated. As Ethereum's throughput grows, this information asymmetry gets worse. This EIP proposes an index structure that can be updated far more cheaply than the state trie while still allowing lookup results to be verified with moderately sized proofs. The goal is to increase the usefulness of logs while keeping their low gas cost.

What changes

Today

After EIP-8304

Who is affected

In depth

Five table levels and a 1024-slot ring buffer

The index tables are maintained at five levels. The table at each level holds TABLE_SIZES = [1, 4, 16, 64, 256] blocks, first_block is always a multiple of table_size, and TABLE_SIZES[i] is a multiple of TABLE_SIZES[i-1]. So one higher-level table can be built by merging the 4 tables (a fixed number) of the level directly below. The level 0 single-block table is recorded immediately after all transactions in that block are processed, and higher-level tables are recorded after a delay of TABLE_SIZES[i] // 4 blocks. This delay lets merging proceed asynchronously from block processing, so block processing latency does not increase. The root hash of each level is stored in a ring buffer of length TABLES_PER_LEVEL = 1024. A common ratio of 4 is a compromise. A smaller ratio increases the number of levels and so adds tree hashing, and a larger one increases the number of small tables needed for a proof, which makes the proof bigger.

Seven entry types and the sorting and hashing rules

There are seven entry types: block (0, block hash), transaction (1, transaction hash), log address (2), and log topics 0 to 3 (3 to 6). Each entry consists of a type ID, the value to search for, and position information (block number, transaction index, log index), and the encoded length is 38 to 50 bytes depending on the type. A transaction entry contains the cumulative log count accumulated in the block before that transaction, and the log index of address and topic entries is relative to the start of the transaction. All numbers are encoded in big-endian so that lexicographic ordering of the binary representation is the correct order. The block entry is added with a one-block delay. When block N is processed, the hash of the parent block N-1 is inserted, because the current block's hash is not yet determined within that block. There is no block entry for the genesis block. The table root is computed as the root of an SSZ List[Hash32, entry_count] holding the SHA2-256 hashes of the entries.

The index contract's set and get

The table root is recorded by SYSTEM_ADDRESS (0xfffffffffffffffffffffffffffffffffffffffe) calling INDEX_CONTRACT_ADDRESS. The input is first_block, table_size, and table_root at 32 bytes each, with a gas limit of 30_000_000 and a value of 0. It uses the same system call convention as EIP-4788, so the call must run to completion, it is not counted against the block gas limit, and it does not follow EIP-1559 burn semantics. If there is no code at that address, it fails silently. If the caller is SYSTEM_ADDRESS, set runs, otherwise get runs. The storage location is slot table_size * TABLES_PER_LEVEL + (first_block // table_size) % TABLES_PER_LEVEL. get reverts if the calldata is not 64 bytes, if first_block is not a multiple of table_size, if the requested root has not been recorded yet or has already been overwritten in the ring buffer, or if the stored value is 0. This function can be used for on-chain verification inside the EVM, or as part of an off-chain lookup proof together with an execution witness.

Post-sync initialization and stateless execution paths

This EIP does not extend the sync protocol, because every table it needs can be rebuilt from recent blocks. Even in the worst case, only TABLE_SIZES[len(TABLE_SIZES)-1] * 5 // 4 - 1 recent blocks, which is 319 blocks with the current parameters, are enough to build the top-level table root. In a fully stateless protocol, merging can be implemented using witnesses of partially merged tables. If evenly spaced entry_index checkpoints are chosen based on the expected entry_count of the merged table, and the witness set includes a Merkle boundary proof of the partially merged table at each checkpoint, the block producer commits the next merge piece and validators verify it using witnesses of the lower-level table pieces. The same approach can be applied to generating execution ZKPs, and there is also the option of proving merges asynchronously.

The protocol handles up to 256 blocks, long history goes to ZKPs

The design assumes the protocol builds only tables of up to 256 blocks. Large tables spanning millions of blocks are built by infrastructure operators, proven with a ZKP, and posted to a separate history index contract that exposes the same get interface. Building large tables inside the protocol would put a heavy burden on every node and would require every node to hold millions of blocks of history, which is not future-compatible under current plans. Conversely, if all indexing is left to external provers, the last block is never indexed and the recent range that people usually care about most is empty, so a meaningful proof must include the receipts of every block since the last update. Thanks to the ring buffer, roughly the most recent 2**18 blocks are covered by 256-block tables, and if the most recent proven history table is no older than 36 days, the entire chain is searchable. A typical lookup proof is expected to consist of 30 to 50 individual table proofs.

Gas costs, security, and room for extension

Because single-block tables are cheap to build and large tables are merged asynchronously between blocks, there is no need to raise the gas cost of LOG operations. On the security side, one discussed attack sprinkles a small amount of ETH on contracts whose storage paths (branches) are updated frequently, in order to pollute tree branches. The authors judged that meaningfully delaying state root updates this way would make the attack cost rise sharply. Room for extension is also built into the design. Because entries start with a type ID, a new entry type carrying a new kind of event can be added without trouble, and everything after the block number field in the position information can change or grow its encoding without re-indexing existing blocks. When EIP-8141 is activated, a 2-byte frame index is added between the transaction index and the log index. Furthermore, the Motivation section describes an idea that, once the logs of the last few blocks become queryable from the EVM, logs could also be written as an alternative form of state that is very cheap to write, easy to expire, and friendly to parallel execution.

Draft status, excluded from the Hegotá fork

This EIP is in Draft status, so the specification can still change. In fact, INDEX_CONTRACT_ADDRESS and the signature values of the deployment transaction are still TBD in the source text. In core developer calls, it was discussed without a decision at 07.30 ACDE #242 and 08.27 ACDE #244. At 09.10 ACDE #245, the decision was postponed by two weeks, Besu and Reth agreed to join the discussion asynchronously, and it was summarized that DFI is the default if there is no progress. Then at 09.24 ACDE #246, it was excluded from Hegota. This means it was finally dropped from the candidates for this fork.

Original EIP · ← EIPs