An Ethereum log bloom is a compact filter that can rule out some candidate blocks or receipts. A positive match only means the requested address or topic might be present. A receipt inclusion proof instead links exact receipt bytes, including their logs, to a block's receipts root. That proof depends on an independently trusted header and does not by itself establish finality or the meaning of an event.
The distinction matters when an event search returns a promising block. The bloom helps decide where to look next. Verifying a particular event requires inspecting its raw log and, when independent authentication is needed, checking the receipt against the block's commitment.
A bloom match keeps a candidate in the search
Ethereum's traditional logsBloom format has 2,048 bits, or 256 bytes. Each emitting address and each topic contributes three positions derived from Keccak-256. Setting those positions preserves a compact hint about membership; it discards the original values. The named BPO2 execution specification documents this bloom construction.
A receipt bloom covers its own logs. The block bloom combines the information across receipts. Geth v1.16.5's release-pinned implementation inserts addresses and topics, merges receipt blooms with bitwise OR, and checks whether all positions required by a query value are set.
For a correctly constructed bloom, a missing required bit excludes that value from the covered logs. An inserted value would have set that bit, so the construction has no false negatives for inserted values. This conclusion depends on using the right bytes, the right bloom and an authenticated source for it.
The reverse does not follow. Different values can set overlapping positions, producing a false positive. The filter also loses relationships: it cannot tell whether a particular address and topic occurred together in one log or whether a topic appeared in the requested position.
Log data is outside this bloom construction. A non-indexed amount in the data field cannot be confirmed or excluded by testing its number against logsBloom. Exact log filtering still has to respect the address, ordered topics and data being investigated.
Two false leads in a 16-bit filter
Consider an explicitly hypothetical filter with 16 bits and two positions per value. These are invented mappings, not shortened Ethereum hashes. Bit zero is the least significant bit. The example contains exactly two logs:
| Toy receipt | Emitter and topic | Positions inserted | Data payload |
|---|---|---|---|
| 0 | Address A, topic P | A: 1, 6; P: 4, 9 | 40 |
| 1 | Address B, topic Q | B: 2, 7; Q: 5, 12 | 70 |
The first receipt sets bits 1, 4, 6 and 9, giving the hexadecimal mask 0x0252. The second sets bits 2, 5, 7 and 12, giving 0x10a4. Their combined mask is 0x12f6, or 4,854 in decimal. Its eight set positions are 1, 2, 4, 5, 6, 7, 9 and 12.
Add two absent addresses to the lookup table. Address C maps to positions 1 and 9; address D maps to positions 3 and 14. Neither address emitted a log.
Now ask for an emitter and topic together:
| Query | Required positions | Bloom result | Actual matching log |
|---|---|---|---|
| A with P | 1, 4, 6, 9 | All set | Yes, in receipt 0 |
| A with Q | 1, 5, 6, 12 | All set | None |
| C with P | 1, 4, 9 | All set | None |
| D with P | 3, 4, 9, 14 | Bits 3 and 14 missing | None; excluded |
The A-with-Q query passes even though no such log exists. A appears in the first receipt and Q in the second. Both components really occur, but their requested relationship does not.
The C-with-P query shows a different failure. C never appears anywhere. Its two positions happen to be set by values that do appear, so the bloom cannot distinguish C from that combination.
D-with-P can be excluded because D needs two bits that remain clear. The absent bits give a definite answer within this example's scope; the positive results require further inspection.
The payloads 40 and 70 never enter the bit calculations. Changing a payload while keeping the same emitter and topics would leave this toy bloom unchanged. Ethereum's address-and-topic construction has the same limitation.
You can reproduce the combined mask by summing 2 raised to each listed set position: the result is 4,854. Three of the four queries pass, but only one has a matching log. This small set illustrates the mechanism; it does not estimate Ethereum's false-positive rate.
Connect the receipt to a trusted header
An exact scan of candidate logs can eliminate those false leads. Independent authentication then needs an anchor: the intended block's accepted header. A root copied from an unverified provider alongside its proof only shows that the two agree. Establish the header's chain membership through your verification process.
Ethereum commits receipts through a Merkle Patricia trie. A receipt proof supplies the nodes needed to check a particular receipt value at its key against a known receipts root. The Ethereum trie documentation describes this link between a path, its value and the root commitment.
Read receiptsRoot from that header. The state root commits different information; an account or storage proof cannot replace a receipt proof. The comparison of Ethereum state roots and receipts roots explains which commitment belongs to each question.
The receipt trie key uses Recursive Length Prefix (RLP) encoding of transactionIndex, the transaction's zero-based position in that block. It is not the transaction hash or the event's log index. For index 1, the key is the single byte 0x01 under the RLP encoding rules.
The value must also use the correct consensus encoding. EIP-2718 distinguishes legacy receipts from typed receipts, whose type byte precedes their type-specific payload. Omitting the required type byte or using the whole RPC JSON response as the leaf changes the value being proved.
Once the receipt bytes verify against the root, decode their retained logs and inspect the exact emitter, topic sequence and data. Those are the fields in the execution specification's raw log structure. An ordinary receipt proof authenticates the receipt containing the log; it does not prove a separate event leaf in an event-specific Merkle tree.
Changing an authenticated log's data while retaining the old proof should fail verification against the same root, subject to the cryptographic assumptions. A bloom could remain identical after that change because it never included the data. This is the practical difference between preserving a search hint and committing exact bytes.
Keep JSON context and event meaning separate
An RPC receipt contains useful contextual fields beyond the serialized receipt value. Do not assume the receipt branch authenticates every displayed field.
For example, Geth v1.16.5 derives transaction hashes, block context and global log indices from receipts plus their surrounding block and transaction information. A receipt proof at index 1 authenticates the receipt at that position. To independently bind it to a claimed transaction hash, also verify the corresponding transaction against transactionsRoot at the same index, or obtain it from a fully verified block.
Within an authenticated receipt, a log's position in its log list is clear. The block-wide logIndex additionally depends on how many logs precede that receipt. The transaction hash versus log index guide explains those different scopes. Keep the receipt position and receipt-local log position in your evidence instead of treating an unexplained RPC index as part of the leaf.
Authentication also leaves interpretation to the application. A familiar topic signature does not make an arbitrary emitter the intended token or protocol. Check the network, emitting address and relevant implementation before decoding business meaning. The distinction between contract addresses and code hashes helps frame that identity check.
An authenticated log establishes which bytes were retained in that block. It does not automatically establish a recipient's balance change, reconstruct every internal call or explain reverted branches. Those are the subjects of event logs versus internal transactions.
Choose evidence for the claim you need
Use this worksheet before accepting an event record or declaring that none exists.
| Check | Evidence to retain | Decision |
|---|---|---|
| Define the event | Network, emitter bytes, ordered topic conditions and data test | Know exactly what counts as a match |
| Fix the block | Header, block hash and source of header trust | Scope every later result to that block |
| Test the bloom | Correctly encoded values and required bit positions | Exclude on a justified missing-bit result; otherwise inspect |
| Match the raw log | Receipt position, local log position, address, topics and data | Reject candidates that fail an exact condition |
| Verify inclusion | Receipt encoding, trie key, proof nodes and trusted receipts root | Accept only if the proof verifies |
| Interpret the result | Contract identity, decoding rules and required settlement status | State only the conclusion those checks support |
For real bloom inputs, use the raw 20-byte emitter address or 32-byte topic. Hashing printed hexadecimal text, a decoded event name or the wrong ABI representation changes the query. It can create an apparent absence caused by the implementation rather than the underlying logs.
Address alternatives also need care. If a query accepts either A or B, excluding A alone cannot exclude the query. Every allowed address must fail before that address condition rules out the block. Respect topic alternatives and wildcards in the same way; the JSON-RPC filter schema defines their grouping and order.
Do not treat an RPC response as a proof package merely because it contains a receipt. The ordinary receipt and log methods return records. EIP-1186's eth_getProof specification covers account and storage proofs against state, rather than receipt inclusion. Check the actual client's or provider's receipt-proof facilities and historical data availability. An unsupported method, missing history or timeout leaves evidence unavailable.
Finally, distinguish inclusion from completeness. One valid receipt proof establishes that receipt's contents; it cannot establish that no other receipt in the block contains a match. If the block bloom passes but exact inspection finds nothing, an independently verified absence claim needs broader evidence. One route is to retrieve the complete receipt set, reproduce its trie root and compare it with the trusted header before checking every log. A search over a range similarly needs coverage of every relevant block.
A valid proof can also refer to a block that your application no longer accepts. Apply the required canonical-chain and finality checks to the header anchoring the evidence.
Close a positive record with the block hash, verified receipt position and matching raw log. Close a negative record with the authenticated scope and the exclusion or complete-search evidence. A passing bloom test belongs in the search record; a verified receipt and trusted header belong behind the inclusion claim.
