Implementing EIP-8025 Optional Execution Proofs in Grandine
Devcon 8 · 4 Nov 2026 · 14:33
Ethereum Protocol Fellowship contributors Charles Reese and Keshav Sharma describe adding EIP-8025 optional execution proofs to Grandine, a consensus client written in Rust. A proof is checked against an expected public input built from the accepted payload, and a hashing difference between the consensus spec and the proving stack needed a compatibility layer. They cover an opt-in proof gossip topic, in-process verification, duplicate handling, a private devnet test, and plans for a Sepolia verifier.
Speakers
Topics
From Devcon 8, hosted by Ethereum Foundation.
Transcript
[music] is yours. Please go ahead. Yeah. >> Well, thanks Mario. >> Thank you. >> So, I'm Charles and this is KV. Uh, and we're going to be talking about how we added optional execution proofs to Grandine uh consensus client written in Rust. So today uh nodes repeat the same execution to validate a payload and as the gas limit rises that work gets more expensive. Execution proofs offer uh another way to check payload validity. Instead of repeating the execution a node verifies a proof and that verification cost stays nearly flat as the gas limit rises.
Uh EIP 8025 lets us test that alongside reexecution. It doesn't change consensus or remove the execution client. Uh it lets us build confidence in the proving stack before considering such a move. Uh for Grand Dine, the question is simple. Does the proof that you have actually match the payload you accepted? So the public input is the claim we're checking and uh a pier sends proof bytes and identifies a beacon block. Randon uses that block's accepted payload context to build the expected public input itself.
The proof then has to match that input. Uh when we tried to make this uh work with real proofs, uh we ran into a compatibility issue because the consensus spec and the proving stack hashed the payload request differently. Uh the same bytes gave different routes. Um so we kept the canonical calculation and added a compatibility layer for uh the proving stack uh checked against an independent reference. Here's how a proof moves through Grandine. Uh networking brings in the envelope. The controller coordinates the work.
Uh the store provides the payload and state, tracks proof attempts and stores verified proofs. Proof engine verifies the proof and checks that it's committed public values match the expected input. And then the controller returns the gossip outcome. I'll start with uh networking and verification, the two edges. So we added the execution proof topic to uh to lip P2P which is a library that Grandian uses uh for for P2P. Uh peers opt in through networking setting which is off by default and in Grandian the flag I'll show you later turns it on.
Our transport test checks proof gossip between two peers. Uh when both opt in the receiver gets the same typed message and when the receiver opts out it doesn't see it because it's not subscribed to the topic. The proof engine decodes the proof, verifies it against a trusted verifying key and then compares its committed public values with our expected input. Uh the key identifies the guest program which is the program whose execution is being proved. Uh we tested this with a genuine s ethrax sp1 proof from zk boost. Uh with the correct key and a matching input, the engine returns valid. Uh different guest key fails verification. And if we change the
expected payload route, the proof still passes cryptographic verification, but its public values don't match the input. Um, both of those failures result in invalid. And if a node has no backend for given proof type, it just returns undetermined. So, KV will now show how Grandian handles incoming proofs and decides which ones to store and forward. So, I will show you how the processing part works. uh now everything starts from the payload being accepted. So after that everything works but before that uh we have to wait for the proof context to be captured and that happens with uh u payload check being passed and
uh you submit the payload with the uh notify execution payload and uh u once you capture the proof it's asynchronous so the execution layer validates it and grandine accepts it as as it does today. So there is no change as per consensus. Meanwhile, Grandine captures the uh proof context. U the proof context uh comes asynchronously. Like I said with you, if uh uh the proof works later, uh proofs of this payload are checked against the saved context. If it fails, those proofs are ignored.
The payload is accepted either way. So uh now I will show you the how the uh same proof being declined works. So uh currently there's a possibility that uh two proofs can be declined if they come at the same time because uh technically during the u grand has a multi- workers and single producer. So uh there is a possibility that there the two workers can see the same proof and u so what we do is we have a single admission gate just before the producer and that uh checks the uh if the duplicate uh the
proofs are indeed duplicate. So it it checks via the same proof. If there's the same proof for the same block, it is going to be uh rejected uh ignored. And the same prover uh sending another proof for the same block and the same proof type, it is also going to be ignored. [snorts] So now I'll show you how the verification part works. So the verification part happens once we get the response from the proof engine. So the proof engine either is going to return the uh three options that is valid uh invalid or undetermined and likewise the gossip is going to reply
for the accept reject and ignore like uh respectively and u the the proof uh the only requirement for the exception uh accept part is that the proof context during the whole duration of the uh proof uh verification via the proof engine as it is an external uh thing to the consensus client. Once you get the response of it, the proof context throughout should remain the same or else the it is going to get ignored again.
Uh the store uh keep two uh keeps two things like the proof context and the first valid proof. All the others are ignored and storing leaves the payload untouched like because uh optional execution proofs being uh optional to [snorts] the whole consensus validation part they don't even uh touch the consensus validation like validity the head and the fork choice like I said so now Charles will show you how to turn the functionality of uh uh execution proofs in grand So one flag uh turns verification on. Uh you repeat the same flag once for each proof type you want to configure. Uh for
our devet we used a proof type of one for mock proofs uh and a proof type of two for etherrex SP1. So to use the sp1 back end you also have to build with granding uh with that that backend enabled. Um without the flag subscription and verification just just stay off. So it works as if you know it's not part of the uh the client. The execution client still receives and validates payloads. Either way uh we ran an end toend test on a private curtosis devnet. Uh publisher sends signed proofs to grandine and observer checks in that what grand forwards. Uh the matching mock proof was accepted, stored and forwarded. Uh second proof of the same block and proof type was ignored um as already verified. Um then
we reused the uh genuine SP1 proof from ZK boost and it was made for a different payload. So cryptographic verification passed but but the proof didn't match uh the devet payload. So grand rejected it because the public input didn't match. Um and when we flipped one bit in the proof verification itself failed. So that demonstrated that genuine SP1 verification uh and to end in a running grandandy node. I'll hand back to Kv. So last week Franchesco shared that uh onx that he has started running a sepoleia prover to prove sepoleia blocks. So our ultimate goal is to have a running uh verifier uh in uh via grand
so that we can accept uh incoming proofs on sepoleia and verify them. To get there three things must match. Uh the first is the ZK ZKVM. The currently uh the prover is uh uses the open VM and we are currently verifying SP1. So we have to change that. Uh second is the guest the for uh uh uh the Sepia approver it uses uh two guests. Uh so we have support for Ether and RET. So we have to have support for both proof types as well as the guest pair. so that uh they have unique public input and we can verify them. So we need to have them in
the grand config. Apart from that there another thing which we need is uh the public input must match with uh like I said in order to have a valid verification and uh after we have uh set up the whole sepoleia we will try to uh give a final push to complete our prototype with uh having a having a stabilized curtosis devet runner test runner so we can just end to and test it. We will also try to add prune uh uh support for pruning proofs once the finalization reaches so that uh the uh storage doesn't explode after uh uh like
exponentially. Uh and the third thing we will try do is do is we will try to add support for K ofN. So currently separ has two uh guest programs. So there's a possibility of um one of two or any of thing like that. So we will try to have k valid proofs out of n proof types. So it's going to be one of two. And finally we'll rate limit the proof verification to keep the node responsive so that in case of ds we'll try to test the DOS as well. So if it passes uh uh it will be a great test to have. Uh thank you so much. Uh thank you to Mario. Uh thank you to the ETH act team, applied
cryptography team and um thank you to all the fellows who have been so generous. Look you can Yeah. Thank you so much. >> Thank you. >> Awesome. Thank you so much guys. Uh incredible work. Thank you folks. Do we have any questions? Any thoughts? Um, great project. Yeah, Solio. Um, let's have a mic for him. Uh, there. This guy right here. Yeah. Awesome. >> So, thanks for working on that. Uh, and since you are focusing on main on Sapolia now, right?
>> Yeah. Yeah. >> Yeah. But I think there are some uh uh approvers uh doing proofs uh for the mainet uh right now. I just don't see why you shouldn't focus on the mainet because you know it's it's uh you can simply try to verify those proofs and do this uh uh out of K thing. Uh uh is there any reason you don't do that?
>> The only reason is we have we wanted to be part of the devet. So uh currently there is going to be the first devet test uh going to happen for the uh so lighthouse has implemented it and prism has implemented this thing. So they are test going to test it with the seapoleia provers. So that's the only reason we wanted to be compatible with what they were working in. So because we were following the whole their whole L1Z KVM thread discord thread. So that's the only reason we were inclined before that we were uh uh committed to work on the ETH proofs uh all the all the provers that are for the mainet in the ETH proof. So we first we were first focused
for on that and that's what Franchesco also said that that you can also give that a try but the only reason to go for sepia is to have another client grand to have support. Yeah, I I think maybe you can just uh because I believe that I'm not fully sure but I believe that uh the majority of the provers are there and they have a stable uh provers working and the proofs are there and so on and the test nets usually are a bit um let's say secondary uh focus uh for a lot of projects uh you know so so you you can always later switch to test net.
>> Uh and now we mainly focus on the mainet to to to make your code to make your grand patch working correctly and then whenever you need you know to collaborate with other teams or whatever on the you just quickly change configs and uh and it should work. Yeah. So my my suggestion would be to focus on the mainet and try all these options and features like an out of K proofs or whatever. Yeah. And the great work.
Thanks. >> Yeah. Thank you so much. >> Thank you. >> Thank you indeed. Incredible work. Can we get another round of applause for the game? [music]