EIP-8304: Trustless log and transaction index in Geth
Devcon 8 · 4 Nov 2026 · 11:12
Vansh Sahay presents EIP-8304 and its first full implementation in Geth, a protocol-level index that makes Ethereum logs and transactions provable. A system contract commits entries into chained SSZ-merkleized tables each block, giving light clients trustless queries with inclusion, exclusion and completeness proofs instead of relying on honest nodes and indexers.
Speakers
Topics
From Devcon 8, hosted by Ethereum Foundation.
Transcript
[music] date coded. I appreciate that. And the stage is yours. Go for it. All right. Uh hello everyone. Uh I'm W. uh and yeah I was a fellow this time working on uh get and uh I've been implementing EIP8304 uh which is the trust log and transaction index uh and yeah I'll go through the design uh I'll tell you what I spent time on what I broke uh and basically how the experience was uh okay so uh the problem that we have
right now is that uh eth_get logs does not have a proof. So let's say if you want to talk to a node that you don't run, uh you can't tell if it dropped a log, invented a new one, uh you just don't know what's happening. So uh A304 puts this uh in an index table uh with roots uh with a system contract. So it can be checked with a proof. So whenever you ask for a proof, whenever you ask for logs, you get a proof with it which you can verify uh on your client. Yeah. So, uh we Yeah. So, we do this through creating
logs in different tables. So, we create one for a single block, we create one for four blocks, uh 16 blocks, 64 blocks, 256 blocks. Uh and each of these tables have uh 1,024 roots inside them. So roughly we go back uh and we can verify logs for up to like uh 36 days approximately and if you want to verify logs from before that you can just create a ZK proof for it uh and verify it on your client. Uh the part that's actually really hard uh is not just showing that a log exists. Uh you can just do that with receipts right now. Uh but it's it's showing that nothing was missing. So if you query for a set of logs uh that's what you want to know
that you uh that the client or uh the node did not leave out any block. So how it works is essentially that uh each block uh adds entries like I talked about. So a block entry, a transaction entry uh one for each log address uh and each topic. uh the entries are then uh sorted lexographically uh and that is done for each of the tables that I talked about. So 1 46 64 and 20 256 uh and all of this is hashedged into a route uh and this route is something that the uh system call writes into the uh contract. The contract then keeps a ring of the 124 uh roots every level. So
like I said it goes back to like around 36 days and uh for a query everything that matches a value uh is basically one continuous run uh in the sorted table. So uh if I show you that run uh plus the entry before it and the entry after it there's nothing hiding at either end. So you can verify that the logs that you got back were complete. Yeah. So uh the proof that you get uh is basically you get multiproofs for the runs uh you get receipts of the matching blocks and you also get uh a state proof uh for the uh for the contract slots.
Uh yeah so uh most of my work was divided into these two parts. Uh the first uh was the node side of things. So uh basically you know getting the roots uh from the chain uh and the local index is basically only for serving uh it follows reogs. It also follows uh all the back fill uh gaps and it keeps a bounded window uh by having its own uh fork uh it its own fork gate. So this was uh so EIP804 was uh DF5 for Hegota. Uh but yeah so I just ended up uh creating my own 4 gate uh and testing through that. Uh the second uh part uh that I worked on was the client side of
things. So basically the verifier uh so it's just uh one function which takes the trusted header uh the request and some options uh plus uh you know it has RLP proofs uh it has cursor paging and also historic mode for uh zk proofs. Okay so uh this was something that was uh really interesting that I came across while I was testing this. The first version that I built out uh had the system call take the table roots from the uh node's own index uh and that's just wrong uh which I like came across while reviewing my own code. Uh a node that has no index or just you know snaps
synced or just reorged would compute different routes uh and would reject a valid block. Uh and yeah med was red. So that's that's how I found out about it. Uh so now every root is a pure function of the chain. I derive it from the blocks uh ancestry. Uh and the uh index only serves queries. I tested that uh by importing the same chain with indexing on and off. So uh I tried it tried it out with uh the indexing on and off. I tried it out uh with and without uh parallel processing uh and then across a reorg and a due table and uh I was running it on a devet with uh one uh validator with the log index enabled and
one and one for which it was disabled. Uh the cost was like around like 319 blocks. So the 256 block table uh is due 64 blocks after it ends. So at that block a node needs the receipts of the uh previous 319 blocks. uh if they are pruned the node refuses to build or import that block uh and it just tells you which uh receipts are missing. So for snaps sync and history pruning that uh that is like a real requirement that you need. Yeah. So uh how does the proof actually hold and what the checks are? Uh the f uh it's basically three really important checks. The first one is basically uh
that the header is the one you trust uh that you get from uh that you get from the block. Uh yeah and the contract is the one that you're expecting uh by address and by the uh by the code hash. Uh the second one is that the proof answers the request that you actually made. Uh and the table roots match the contract state uh at that single header. Uh and the third check is that the uh runs are bounded and that there's a receipt for every single candidate. So uh yeah, nothing is actually like hiding or being left out. Uh to test it, I wrote like uh a differential test of like 200 uh 200 around like 200 queries.
Uh so it was like uh against brute forcing, it was against a fuzzer. Uh a few attack modes uh that I uh investigated. Uh and uh there was also like a really cool test that I tried out which uh basically changes every bite of every string in a proof one at a time just to you know see what could break or what could not. Uh and yeah none of them verified uh they all failed. Uh and you know the only the correct ones were accepted. Okay. So uh what's the problem with what we have right now? Uh the doom doom filter is what you have today. uh and you can scan every uh header in the range uh download the receipts of every block where it says maybe and uh drop
the false positives but uh at the present stage uh most wallets or uh most indexers don't use bloom filters anymore. Uh it just always says yes and you have to end up you know checking the receipts in the end. uh 8304 is uh exact uh so the proof size follows all the matches uh not the amount of blocks that you searched and the uh cost is a bit on the consensus side so you have new state in every single block uh and uh you know every node is deriving roots from the last 319 blocks of receipts so uh I don't think that's free but it's something that I've yet to benchmark yeah so this is just you know a metric
that uh I came across when I was uh testing it on my own devet. So it was as I said it was like a two client devet with uh one which which I had log index on and uh one which didn't. So I asked for around like these many numbers of logs. Uh they came back in a lot of pages and I verified every single page through the custom uh verifier that I wrote. Uh and that verifier doesn't talk to the node at all. uh so identical to like e get logs and uh yeah the node without the uh log index stayed on the same chain the entire time.
So yeah, that's uh EIP8304. Uh so far uh there's been a lot of recent work going on in this uh so after it was JFI for Igota uh there's been a new version uh I think it was being discussed last or last last week uh during the RPC breakouts uh where a new version for get logs uh v2 is being proposed and uh they're considering adding uh trust log index inside it. So I'm excited to see if uh I can get this work uh you know useful there. Uh and yeah, thank you. If uh you have any questions, I would love to.
>> Oh, thank you. Uh yeah, not a question, just a comment that uh so now that uh this EIP has unfortunately been uh rejected for for Hegota and now with the priorities I don't have very high hopes that uh an EIP with such a complexity could get into the protocol. But uh I mean the data structure I think it's great and it's great that you're so you're working on it and and I would really like to realize it. So uh now I believe it's a great time to just move step take a step forward and uh do this uh whole index with ZK proofs. So yeah that's just just wanted to mention that
this is my plan for the future now and yeah so I think this is also a path where basically we can just have the same data structure just not in the protocol. >> [music]