Block-Level Access Lists Breakout #11
Block-Level Access Lists Breakout · 28 Jan 2026 · 1:09:36
Block-Level Access Lists Breakout #11, held on 28 January 2026. Block-level access lists, defined in EIP-7928, record which accounts and storage slots a block touches, which can help clients process blocks in parallel. DevNet1 was successful, with most clients syncing and minimal forks found by the EVM fuzzer, and DevNet2 focuses on testing optimizations without blocking on the EIP-7928 spec.
Speakers
Topics
From Block-Level Access Lists Breakout, hosted by Ethereum Foundation.
Transcript
Toni Wahrstätter: Hello, everyone.
Perfect. Hello, everyone. Let's wait one more minute before we get started. Great. I think we're ready to go. I think we can start the stream.
Pooja Ranjan: We are live.
Toni Wahrstätter: Hello, everyone, and welcome to today's EIP-7928 breakout call. It's the 11th breakout call, and it's 20… January 28 today. So, looking at the agenda, let's start at the beginning, let me quickly post the agenda into the chat. Such that everyone can have a look. So the first two items were basically already discussed, at ACDT, beginning of this week, but I think it would make sense to get an update from Stefan. where we are at, there were some delays, as far as I know. It would be great to have an update there.
Stefan Starflinger: Yeah, sure. So, from the last ACDT, we decided to include a change that would remove the gas spent, I think, from the receipts. And, agreed that we would add it and delay the DevNet 2 until next week at the latest. Hopefully, we can get it running a little bit earlier. Yeah, it got changed. Exactly, but otherwise, also, maybe to summarize, maybe not everyone was in ACDT. from DevNet 1, I think it went pretty well. We got most clients to be in agreement. We didn't have too many forks anymore, even with the EVM fuzzer running, so that's… very great, and we also got most of the clients syncing. There were still a few hiccups, but in general, it was working pretty well. And now, I hope that with DevNet 2, we can look into the optimizations more.
But, let's talk more about the spec clarification, because I think it would be great that we could tie everything of that up, and get everything in definite, too, of the open issues that we're gonna discuss next, and I think I've added everything to the spec sheet. From what we're gonna discuss next.
Toni Wahrstätter: Perfect, thanks for the update. So, just to… just to confirm, it looks like 7928 is not blocking anything regarding DevNet 2, and we are ready to go, basically, but, it's more about other smaller EIPs, like 7778, right?
Stefan Starflinger: Yep, that's pretty much it.
Toni Wahrstätter: Okay, here. to echo what Jared is saying in the chat. Regarding 7778, there was, recently a change that reverted it back to keeping the receipt as it is, so it should be… done pretty soon, or pretty easy, I would say. But let's see. Great, so what is then… what was the latest, then? When will DevNet 2 launch? Do we have a date for that yet?
Stefan Starflinger: So, I'm aiming for next week, Wednesday, as the latest date, that I want to launch DevNet 2. And we'll see. I will be testing the clients regularly to see how we get on, but that would really, it would be great if we could launch earlier. I don't know how the development is going on, it would be great to get some client updates. I think the last update, from everyone that I got was last Wednesday.
Toni Wahrstätter: Awesome. Yeah, and this plays into the next topic, the client optimizations. We already saw in the Discord that Geth and Besu are both ready Having the flags ready… to… enable and disable the optimizations, for example, parallel execution, parallel… batch I.O. and parallel state route computation. Is there someone from the other clients that could provide an update on how far they are with regards to optimizations, and if they're Will be flags to turn them on and off.
Marc Harvey-Hill: Yeah, I can give an update for Nethermind. So working still on, parallel transaction execution, and state recommutation. I haven't started with the batch read yet. But yeah, just been focused on, preparing for the DevNet for now. Yeah.
Toni Wahrstätter: Awesome, thank you. Maybe someone from Reth, or from… Erigon? Otherwise, it's also not a problem. If we don't get updates, that soon, because, importantly. We need at least a few clients to do the benchmarks on top of a BAL. DevNet. So, having Geth and Besu is already a good start, and I think we should then… Get the benchmarking going as soon as possible, and for that. Yeah, I mean, I will leave that to Jochem anyway, because he's the expert there, so I'm not sure how to best tackle that, but I would assume we will just have a mainnet shadow fork and do the benchmarking there. Maybe Jochem, if you're on the call. You wanna say a few words to that?
Jochem Brouwer: Yeah, it depends, I think, on what exactly we want to test. I think we want to benchmark the, the, the, the impact, what a block access list could have. I also think this is… Also, like, about, Because you could implement block access lists to just be consensus compliant. And you could also implement the block accesses with the optimizations in there, so, like, parallel execution. I think this is, at least something what we should do. And what we would then do to benchmark this is we have to benchmark test. But we will then benchmark the, the same client, which does not have these optimizations enabled. Against the client which… sorry, the same client, against a client which has these optimizations enabled, to see what the impact of these, of the block access list is.
Toni Wahrstätter: Perfect, yeah, sounds good.
Jochem Brouwer: And, oh yeah, so, and to mention, like, this should be done on, like, indeed a shadow fork of mainnet, because if we do this on empty state… oh, Karim is already asking this. So, what network is being used for this? We need a network with state, and that… the simplest way is to just do a shadow fork of mainnet, because if you would, run these things on, like, empty state, or, like, consensus test-sized states, so, like, a very small pre-state, then you will see almost no influence at all, because the state reads are in there super fast, so you would not have any performance increase there. So we need, like, a big network, and we can also see what we could also do is also release on top of BloatNet, so this is, like, a shadow fog off-mainnet.
But increased in size, to see also what the impact is if mainnet size, well, it will, of course, increase over the years. But the impact is then, on even bigger size, sites. And when do we expect to have the shadow fork ready? Well, I'm not sure if someone from… if PandaOps is here, Stefan is here, but I think this, the shadow fork can be done, instantly. So we can just run it on top of a shadow fork. Well, in an.
Stefan Starflinger: Yeah, I think as soon as we have DevNet 2 running, we can focus on the optimizations, and when the optimizations are in, and they're working as expected, I think we can start the shadow work.
Jochem Brouwer: And maybe then a question, this DevNet 2, do we… is this planned to be a shadow focus of mainnet, or, like, an empty network?
Stefan Starflinger: DevNet 2 would be an empty network, but we would increase the gas limit to, I think, 150 million, so that we can also have a reasonable state after some time there, too.
Jochem Brouwer: Okay, that's it, that's fine. Yeah, like, the benchmarks are, of course, also somewhat different than just testing the networking aspects.
Toni Wahrstätter: So we can also run these locally on, like, a shadow form to test the impact of state. Yeah, I'm not 100% sure about this, because even if we… put the gas limit to a billion, we will not be able to generate, like, reasonable state within a short period of time. So I think we should focus those tests purely on on Shadow Fork, or on BloatNet, especially BloatNet might be very interesting, because It gives us already a glimpse into the future, basically. So, and then we basically focused, DevNet 2 more on everything consensus-related and testing, all the new changes that come with DevNet 2, for example, the BAL payload, or the bulb EL block separation, the new engine API methods, and so on.
If that makes sense.
Stefan Starflinger: Yeah, I agree with that also.
Jochem Brouwer: Yeah, yeah, I think that what you then do is, like, DevNet tools, because it also makes sense to not immediately stress test the DevNet, and then do the stress testing on, like, a very specific benchmark network, so, like, a shadow fork, or BloatNet, or mainnet, or whatever, and then use that for the benchmarks. Yeah, I think that makes a lot of sense.
Toni Wahrstätter: Yeah, and just to be on the safe side, maybe we should first, clarify on ACDE or ACDT with clients, if they are actually ready for higher guest limits Because I don't know if we can just bump it up to 150, and then suddenly we'll run into weird RLP size limits, or whatever. So, just to double check. I assume clients will be fine with that, so every client might be fine with just bumping it up, but just to double check there. We don't need to trigger, like, running into the 10MB, uncompressed size limit or anything. Perfect. Anything else regarding… this discussion point. Just to summarize, I think we will now also start a shadow fork, and probably a fork of BloatNet, and then also
basically unblock the benchmarking efforts by having two clients already ready, Besu and Geth, that can be used to test parallel execution and batch I.O. At the same time already. Anything to add?
Jochem Brouwer: Yeah, maybe two. Clarify a bit, or maybe to ask that we are all in, like, the same, line of thought. So the parallel execution is using the block access list to, well, run multiple transactions in multiple threads. And the batch I.O. is actually used to read from the bal to a parallel fetch from the disk, right?
Toni Wahrstätter: Yeah, exactly.
Jochem Brouwer: Okay, so those are, those are, like, two different optimizations, and I think it is nice also for the benchmarking. For the benchmarks to be able to test these, individually. So now, not individually, but maybe, like, with only parallel execution, and with parallel execution, and the batch I.O, to see what the impact is, so we can also measure the impact of these of adding the REITs. To the BAL, what the impact of this is.
Toni Wahrstätter: Exactly, and this will be the very interesting comparison. that we are… yeah, we've postponed this until we have DevNets, and now we're… it's time, I would say. Basically. finally determining, do we need the state locations in the block level access list or not? Are they actually worth it? And the best way to do so would be exactly what you said, comparing it with the batch I.O. enabled, and Then, with Batch.io disabled, and then seeing what are the numbers. And if we do that on a… Realistic state size definite, plus on a higher gas limit, we should get numbers. That's… yeah. We should get confidence for Glamsterdam. Great, anything else?
Jochem Brouwer: Yeah, just asking what, for Corinne, the clarification, like, the background… Are those stations?
Toni Wahrstätter: Yeah, I think we can ignore it. I think we can ignore it at this point, because this is like an optimization that is solely dependent on the post-transaction state divs being in the block. So, this doesn't actually tell us anything about Should we keep the state locations or not, so… We can use them, we can also turn them off, depending on what the client supports.
Jochem Brouwer: Yeah, I think this is, like, an optimization which, it's not really influenced Like, if you just think about, like, the reads, if it would remove that or not, like, this will not influence the, oh, right, the state would also have to get the visa. Yeah, okay. Interesting. Yeah, something to keep in mind, mate.
Toni Wahrstätter: Yep, exactly. Great. The second agenda item that we have on today's agenda would be spec and Spec updates and clarifications, so there were… three updates? Nothing new. So the first one was the engine API methods. And there was some discussions in the PR regarding, do we need the engine API methods and for what they're used for? Also, Mikhail had some comments there. Mikhail, are you on the call? Do you wanna briefly… tell us why the engine API methods are needed, and Your… your thoughts around it?
Mikhail Kalinin: Yeah, okay, so, hey, everyone. I'll try to summarize. So, if, I believe that BAL is, will be part of of the, CL block, of the beacon block. Right? Is that correct?
Toni Wahrstätter: Right, yeah, that's correct. It's in the execution period.
Mikhail Kalinin: Yeah, it's a part of an execution payload. It's quite similar to transactions, so there is no possibility for the execution layer client to process a block without a BAL, right? And, yeah, and if, we make it as a requirement, for… to pass, BAL to, to, to EL, Alongside to… to the payload. Then we… We have this dependency. kind of like, that the CL block has to… has to… keep BAL on its side, in, in its storage. Like, it would have to keep transactions, but, we have these deduplication methods, like. For, that allows CL to basically request, Work payload bodies, which are…
Consisting of, transactions and withdrawals today, from EL's side. So if we make a requirement that the value is required to pass to a new payload call. And, EL will… will get it from CL. Via… in the regular case. Then we have to… Then we likely have to also introduce, this piece of data into these, payload bodies, methods. So, some CLs really use these, Rely on those methods to deduplicate, Data, and, reduce the… disk storage, impact. So, yeah. In this case, yeah, if it's required, then we have to kind of provide this additional be, additional data that can be pulled off from EL.
So if it's… the other option, basically, yeah, make PAL as a non… as an optional field of a new payload call. And in this case, EL will have to either execute transactions without it, or look into… its own network, via DevP2P. Extract BAL, get BAL from there, and basic execute the block. So… And, yeah. Optional means that sometimes CL will just have it. When the block is just… is gossiped, it will come with a BAL included, and then CL can pass it to EL. So there's kind of, like, two options here. That I see. I don't know if it's clear or not, so… yeah. Let's probably discuss it.
Toni Wahrstätter: Yeah, thanks a lot. To me, it makes a lot of sense. what I don't get is why the optional BAL field is, like, even on the table. To me, it feels like treating the block-level access list the same as we treat the transactions, and thereby forcing CL clients to either keep the buzz around, or blind them. Which is, kind of what CL clients do in order to deduplicate, transactions. So basically, instead of storing the whole BAL, CL clients would only need to store the bul root. and have those two engine API methods available in order to retrieve the full BAL if needed. To me, this feels, like, very clean. with the optional BAL field in the execution playlist, I'm not… I'm not so sure, because essentially, the block of access list will be
necessarily needed on the EL side, because you cannot validate the full block without having the block web access list. So… Yeah, I'm having difficulties wrapping my hand around optional. About the fact that it's optional. I know that this is more like a technical thing here, to make sure that CL clients can just get rid of it without blinding it, is that correct?
Mikhail Kalinin: Yeah. Yes, it is. So, like, for a normal case, BAL will be passed from CL, because it gets it from gossip. But in the case when, CL is, like, syncing, is in a sync mode, Then it will just pass the payload without BAL. And then the EL side will have to fetch it from the network. I don't know. Does it make sense or not? So…
Toni Wahrstätter: It does… it does make sense, so it's very much… we're… we're talking about syncing here, too. So essentially, the CL could provide other CL peers. With beacon blocks, even though those beacon blocks would not contain a full execution payload, because the BAL is missing. and the other CL that is syncing would then pass the beacon block execution payload to its EL without the bar, and the EL would then have to figure out where to get the BAL from, and the EL could then use DevP2P for that.
Mikhail Kalinin: And then…
Toni Wahrstätter: What?
Mikhail Kalinin: will happen… yeah, sorry.
Toni Wahrstätter: Go ahead, go ahead, please.
Mikhail Kalinin: Yeah, and then what will happen, in the case when, There is nothing, nothing to fetch, so there is, like, imagine there is no syncing case, EL has all the data to process a block, but CL does not give a, does not pass a BAL, because it's, it's optional. It's an optional field. then, EL… Will not be able to synchronously execute the synchronously process block and get back with a payload status. As it happens today, it will have to go to the network. And it sounds like a synchronous stuff, so it will have to return syncing. Instead of… The payload status. Something like that. It makes the thing a bit more complicated than…
Toni Wahrstätter: Right, yeah, and we could have done the same with transactions, right? So there's… there's nothing… speaking against doing the same for transactions. So we would suddenly actually introduce this new path that might be better. But even though it's… it's, like, different from how… from existing code paths.
Mikhail Kalinin: Right, but what… I see transactions are really necessary. Actually, it's not… there is no possibility to process a block without transactions, but you could process a block without Is your book-level access to this. Couldn't you?
Toni Wahrstätter: You can process it, their transactions at least, but you would not be able to tell if the block is valid or not, because in the end, you would still need to… comparator. So, you would at least need the BAL hash, right? Yeah, so the CL could also provide the BAL hash. then the engine API call to the EL would also need to provide the BAL hash, and then it would work.
Mikhail Kalinin: Right.
Toni Wahrstätter: Maybe a question to the EL clients. Is there already… has some EL team already implemented BALs into DevP2P for SnapSync, for example?
Jared Wasinger: I'll just say on the Geth side, we've slated this to actually use these for the SnapSync. That has been pushed back to something we plan to do in Q2. I mean, we can, of course… Yeah, I'll just leave it at that.
Toni Wahrstätter: Okay. Yeah, maybe we will just have to revisit that topic, Sometime later on. I would say, correct me if I'm wrong, Mikael, but I would say, right now, it seems like the simplest approach would just be to keep it as it is, and implement those two engine API methods that are defined in the… in this PR that I will now post into the chat. Let me quickly through that. So I think this was the right one, where we have defined those two new API methods. And those would essentially be needed by the CL in order to help other CL nodes to sync, because CL will be able to prune the block. But then, when it comes to helping other nodes sync, they need the BAL again. And because the EL will store the BAL, there needs to be this engine API method to retrieve it.
Mikhail Kalinin: Yep, that's… that's correct. And I think, yeah, it's easier to… To leave it as is, and change… and probably change it later on. So… I don't know, it's also a question to CL client developers. What do they think about it?
Toni Wahrstätter: Right, yeah, we don't have many serial developers here in this call, unfortunately. I think most of them are busy with EPBS anyway. But yeah, this will probably come up in the future again, and we should, We should think how, like, what is the cleanest solution for such things. And if we should just keep on doing the same that we do with transactions, or if there would be a smaller path. Awesome. Is there anything else on that topic, on the engine API method? Otherwise, we can go on to the next item, which is further clarifying what access means. We have had discussions on that, like, when do we count an account as being accessed versus not yet accessed?
Having to do with the different phases. And this PR here, I will also post it into the agenda, just clarified that further. Unfortunately, I saw it get merged automatically before, I could respond to Jocham's comment here. I will… I will still do that and incorporate that, but I think right now we're in a good state because Felipe and Rahul have created tests for all those edge cases, and clients have been passing those tests, so I think we are quite aligned on that topic. But just to make sure, is there anything on the excess topic? I know that Jared and Gary brought it up in Discord recently, is there anything we should discuss?
Jared Wasinger: Yeah, I guess I'll just say… so, the proposal we brought up, Basically, it was about, changing the… Sort of changing the, The logic for whether or not we include a, like, when making a call and executing the gas handler, depending on how much gas there is, and it goes out of gas at some point in the handler. We will or will not include the, the target as an axis. And… the reason we made this proposal was that it greatly simplified the change there that need to be made on the side of Geth. But I think, yeah, Joakim brought up, A pretty good point, regarding,
basically, we… I think the current spec is… is good, because we need to… we can't make it… we can't create a situation where somebody can make a call with one gas, and then the target gets included in the access list. It is sort of an attack vector. So, I… personally, I think that the current spec is… Is okay.
Toni Wahrstätter: Perfect, thank you. Awesome. I think we have, alignment on that point in this case. And then the last agenda item For the specs was to chase in RPC endpoints, I think clients are already… For DEFNET2, having the JSON RPC endpoints implemented. Is that the case for all clients, or is there a client that won't have the JSTAR PC already implemented? Until next week. Great. Then we have another agenda item by Stefan. Stefan, you wanted to talk a bit about standardized ALT traces?
Stefan Starflinger: Yeah, I just, thought about, generally, how could we approach, kind of. In a standardized way, testing the performance gains of bowels. And one of the most standard ways to capture these insights is with open telemetry traces. And I think there are two clients that have that implemented, Geth and Reth, but I think Geth is in the process of adding that. I can share the document in the chat. It's a little bit of a long shot. But I wanted to maybe see if we can just use it as a way to brainstorm exactly What kind of metrics block-level access will add that we want to capture, that we want to follow in a more structured fashion.
And it would be good that we can get, kind of, the BAL rolling and communicate, how best we can extract these metrics. I mean, this document is a suggestion to use OpenTelemetry. But of course, that's not a hard requirement, if other clients might do it differently, but in general, it would be good if we align on exactly when we capture the timings. For example, the prefetching is one of the important, like, elements of the bowel, so how long does it take to prefetch everything? Have you implemented the prefetching the same way as other clients? Does it make sense that we capture it all in a standardized way? And then, how well does the parallel execution work? Like, which… how many transactions are running in parallel? That would be really nice to see.
And to visualize. I would really like to get something like that running for all of the clients in Kurtosis, So yeah, that's why I created the document, and I'm very happy to take any comments or feedback on it.
Toni Wahrstätter: Awesome, thank you. I see Jocham, you have your hand up.
Jochem Brouwer: Yeah, I know that, Stefano, I've already talked to Carlos, but Carlos is also… I'm not sure if this works in parallel, but he has posted this research, post about, adding some more general metrics, and I think it will be helpful to coordinate this, like, maybe… Getting, like, these general metrics standardized, and then also, on top of that, the BAL metrics. Because then, otherwise, I will do two things, Not in parallel, but, yeah. I think you get the point. So, the question is, yeah, maybe work together with Carlos on these metrics.
Stefan Starflinger: Yeah, for sure. His metrics are a little bit different, than mine, because I, like, built on top of, kind of, what he was working on. And just focused on, block-level access lists. So this is just generally to identify, I think, execution metrics, but this does not, consider BALs, yet. And that's kind of, the spec that I wrote, to add on top of that.
Jochem Brouwer: Okay, so it's, yeah, it's already written on top of, of this, thing.
Stefan Starflinger: Yeah, that's the idea. And in general, it's not a… just to reiterate, it's not a requirement for DevNet 2, but what I would like to have at some point is to be able to have some visualization of these metrics in Grafana or something like that, to be able to export them. And to have that in kurtosis would be the first step. That'll be very nice.
Toni Wahrstätter: Awesome, thank you very much. Yeah, please. Have a look. Let's make sure that we get those metrics implemented such that we can then actually benchmark and test if optimizations make sense, and especially get some certainty that clients are doing the same and getting the same out of the optimizations. This is… this is great. Thanks a lot, Stefan. Perfect. Anything else on that topic? Otherwise, this would bring me to the last topic, namely… One thing that we should discuss is how we would deal with malicious block lab access lists, basically with Block web accesses that are wrong. But of course, for such block of access lists, we must make sure that we are able to invalidate the block
That contains such a wrong block access list as early as possible in the process. And let me actually quickly share my screen. Let me actually try to do so. Can you see my screen already?
Jochem Brouwer: I see a black screen and a mouse.
Toni Wahrstätter: Okay, that's correct. Okay, interesting. Interesting, though. Because it already showed me in the preview that it will be a black screen, but… Let me quickly try something here. Okay, seems like the sharing is not… Working, as expected. Then I will just post, or maybe can someone else share? Jochem does the sharing work for you? Could you share your screen?
Jochem Brouwer: I can? Yes, I can.
Toni Wahrstätter: Okay, could you then open a link for me?
Jochem Brouwer: Yes. Let me first share my…
Toni Wahrstätter: Perfect.
Jochem Brouwer: This shows the local time. Right? Perfect. Okay, great. And what's your title?
Toni Wahrstätter: And I will not just post the short presentation I have here into the chat so everyone can even follow it live, but you should be able to open it.
Jochem Brouwer: Yes. Just, tell me, what to do.
Toni Wahrstätter: Perfect, yeah, just click on the… on slideshow for the first slide. Or for the second one already. Okay, so just to quickly introduce you into the topic, we can go one slide back here often, please. just as a quick intro, so this will… this is how I think of block execution today. So today, you don't have a block of access list, so what happens is you first do some I.O. in the block, then there might be some execution in the mean… After that, there might be again an S load, and you have to go to disk again, and so on. And at the end, right after. our virtual execution deadline. There is also the state route calculation. This is basically the last thing we do before we say a block is valid or not.
Now, going to the next slide. the picture changes a bit with block lists. First of all, we can ignore the gas limit here, this is just… a random number showing us, okay, we have now scaled from 60 to 30 million, so blocks are actually bigger already, and the best thing we get from block lab access lists is that we can now parallelize between batch I.O, Then there is still some I.O, for example, I.O. that happens in the very first transactions, when the state is not yet cached through the batch I.O, you might still need to go to disk, and then there is execution and the state recalculation happening in parallel. And this is essentially a block, how it might look with block web access lists.
Let's go to the next slide. This summarizes it again. So, essentially, the state locations in the BAL are very useful for batch.io. Most of the state that we need during execution will already be pre-cached. And the bulk gas limit can quite significantly be increased. So this is, like, the happy case. Let's go to the next slide, then. So now, think of a malicious proposer, or a malicious builder, and what this builder does is the following attack scenario. So you would build… so let's start at the very beginning. What… what do we try to exploit here? So a malicious BAL would exploit the fact That in our block level access list, we don't map state locations, so the reads, how we tend to say, we don't map them to transaction indices.
So, from the perspective of a single transaction, you cannot really know are the state locations in the BAL correct or not? Because they are not mapped to any transaction index. So, what a malicious bul would now do is, you would try to declare as many garbage storage slots as possible. So essentially, a malicious proposer could fill the BAL with garbage storage slots. So far, so good. So if you fill it with completely garbage, and you fill it full with garbage slots, then this might be quite easy to detect for any client, because we can enforce a maximum number of storage slots in the BAL, and this is quite easily derivable from the block gas limit, so we should probably do that anyway. Like, have a cap of how many storage slots can there be in the block level access list, just by derived from gas limit divided by the cost of accessing a storage slot.
But now, what we could do is. what I have on this first iteration point here, creating a malicious bottle that doesn't use, like, naively the full gas limit for That transaction, but… the full gas limit minus one max size transaction. So, essentially, Assuming, like, 150, 200 million gas, you would use all of it except 16 million, 16.78 million. 4… declaring storage slots in the BAL. So this is, like, the number of storage slots you would put into the BAL. And then, in the same block, the transactions would actually only do heavy computation. So this is like the attack. You would have a BAL with garbage storage slots, many of them, and you would have a block with transactions that do heavy computation.
And now the question is, when is the first time that clients could figure out that this block is actually invalid? Right, and this is something we have to think about, because, of course, now, assuming we parallelize all transactions on a separate core, right? Assuming you have an infinite number of core, or you have 8 cores and there are 8 transactions, doesn't matter. From the perspective of each core. you can validate that the state diff is correct, right? You have a definite validation there. But with regards to storage keys, because they are not mapped to transaction indices. You cannot really tell if this malicious bar that only contains garbage is actually valid or not. on each core. So what you would need to do is you would have to actually execute all transactions.
And as said, they only do heavy computation, and only after executing all transactions, you would basically be able to sync your course And compare which storage slots each core saw. And only then you could tell, okay, this button must be invalid, because the transactions never accessed what is in the block web access list. And in this malicious scenario, what you would get is basically a proposer would sacrifice his block, because you would end up with an invalid block. But you would cause the prefetch process to be completely useless, right? The batch I.O. process would basically fetch completely useless storage keys from the disk and put them into the cache. And at the same time, we would engage in quite expensive computation, because we would still execute those transactions, and while executing each transaction in parallel.
We would think, okay, those storage slots are not accessed in this transaction, so they must be accessed in another transaction on a different core. And then the question is, okay, how… Fast can clients fail such a block? Let's go to the next slide. Or even to the next one. Actually, go to the next one. Sorry, go 3 slides back, I thought I had another slide there. Two slides, sorry. Right, so this is how the malicious block would look like. It would come with a lot of batch I.O, and it would still require… actually, it would not come with any I.O, this is actually wrong, it should only have execution, but there would be a long tail of batch I.O,
for garbage slots, and a lot of execution happening. And if we go to the next slide, this would essentially mean that the state locations in the BAL give us nothing anymore, because the worst case having the state locations would be the same as the worst case not having the state locations. And this worst case would be an invalid block. And this would essentially mean we cannot scale as much as we want, which is, of course, Sad. Let's go to the next slide. And there's 3 possible ways forward. So the first one, the simplest one, is we do nothing about this problem. It means we can… we can get quite far, I think, with having, like, caps on the bar itself. For example, in the block lab access list, we can already tell
That if we see a certain number of storage slots, that each storage slot must come at least with 2,000 GAS, right? Because that's the least you need to access all of those storage slots. And if there is a bul that already contains too many storage slots for what is realistic within the block size, within the block gas limit, then we can already fail those balls earlier. And then, also, a sub-point under the do-nothing is we can keep track during execution. Let's say you have, 4 cores, and there are 8 transactions, then if you keep track during execution of which storage piece, do I already… have I already accessed? And how much gas was already spent?
then you can fail the block early on, because you would realize, okay, the transactions I just executed, they didn't touch any of the storage keys, so all the storage keys in the BAL must still be touched, but there's not enough gas available at this point in time, so I can Failed the block early on, without executing all transactions in that block. So this is one option, and probably the simplest. I'm just not sure If the worst case would actually be The same as the worst case without even having the storage locations at all. The second least complex option would be we remove the state locations, so this is basically something that is anyway, to be decided, if we keep the state locations in the BAL, and this depends on the benchmarks we get.
But one of the benchmarks we should definitely do is test such a malicious block, essentially having a malicious proposer that submits a malicious block-level access list that causes clients to Waste a lot of time, and then figuring out that the block is invalid very late into the process. And the last option, which would probably be the most elegant solution to this specific problem, would be for all state locations in the BAL that are currently, of course, not mapped to any transaction index. We actually map them to the transaction index when they are first accessed within the block. This would mean we don't have any duplication, because if a storage slot is kind of, like, changed, it would still not appear twice as a read again, so this would not change compared to today. But what would change is that each account would come with a first access index, and each
storage read would come with a first access index. And I already did some Experiments, and it looks like with today's gas limit, this would increase the average bottle size. By approximately 4%. So we are only talking about a 1, 2, 3 byte number here, so it's quite insignificant from a size perspective, but of course, add some logic again to it. Also, it touches the worst case file size, which would increase from 0.2MB to 0.97MB. This is, like, negligible, we can totally ignore that, because as long as the worst case BAL size still stays below the call data block, and we are still, like, half a megabyte smaller than the call data block, I think we are fine, because we're not creating, like, a new dust vector.
Then, the next point, it allows clients to prefetch Storage slots in the correct order, which might be quite nice, because today, with the storage keys, or with the state locations not being ordered. There is another worst-case scenario where The keys you need first are actually the lexicographically last in the vowel, so you would read them last while you need them earlier in the block, so this would be prevented. And one nice thing is that we would still not have any duplication of addresses and storage keys, so the power would Basically, from a size perspective, it would stay quite compact. But yeah, it would change something, and suddenly we're not talking about removing the state locations anymore, but actually adding indices to it.
So yeah, this is, of course, a lot. that I presented right now, I guess, We won't find an answer about this today, but I'm curious if clients have already thought about this, like, especially how do we want to invalidate a block? as early on in the process without risking that we do a lot of batch I.O, and we also do a lot of execution, and then only figure out That the block is invalid very late into the process. And… Yeah. getting nothing out of the batch I.O. Yeah, any thoughts on this? Yeah, jochem, go ahead.
Jochem Brouwer: Yeah, like, maybe, maybe one comment, which I just realized, because now we are talking about, like, we have to realize the current way where we have to, where we invalidate invalid blocks. That means that we have to first sequentially execute the entire block, and then, well, the simplest example on how to construct, like, an invalid block, is to just construct a valid block. But just change the state root at the end. So you would only realize at the end that the state root is incorrect. And… What we are talking about here is, like, we already have the parallel execution. So, if we would compare this worst case, or, like, this situation, to the current situation where we don't have the block access lists, then it likely still is, like, a very big improvement. I just wanted to raise that point for the perspective of this.
Toni Wahrstätter: Yeah, this is very true. So it's… it's… it's not, like, setting us back that far, but it's more like… Comparing the block-level access list with state locations, with the block-level access list without state locations. This malicious block would essentially get us into the same spot. Or, yeah, basically the same spot, where we waste almost all of the batch I.O, And… still have to do, like, full execution. And of course, we can put numbers to it, right? Let's say we have 3 seconds for execution, and we can support the gas limit of 500 million, I don't know, doesn't matter that much. But if we kind of scale the gas limit to a point where leveraging the worst-case batch I.O. and leveraging the worst-case execution would exactly get us to that boundary.
Right? Then, suddenly, we find out that there is a worst-case block where you get nothing from batch.io. So this would definitely mean we have to sacrifice some scaling there. Which would, of course, be bad, because at this point, we should then decide for removing the state locations from the BAL, directly? without, yeah, doing nothing. Yeah, Jared, go ahead.
Jared Wasinger: Yeah, I… so, I guess I'm having… I'm not quite understanding how adding first access, to the BAL prevents the case where we have to execute the entire… A set of transactions, because with parallel execution, there's not really… Any guarantee about what order transactions will be executed in, so to validate this first access, we still have to… I mean, because at any given point, if we access a storage slot, it might… not be the first access of that slot in the block, and so we still need to execute the entire set of transactions to validate the first access, and once we… I mean, if we have to execute the entire transaction set, then we've already read all of the state locations.
Toni Wahrstätter: Right, yeah, I mean, this is true, but then you… you have at least a guarantee, with the first access index, you have the guarantee that after executing your first batch of transactions, so you only need to execute one single transaction, then you could already invalidate the block. Because imagine you have you execute… you have a transaction, and you execute all of them in parallel. Then, from the perspective of each individual transaction, you don't know if the whole block is invalid, because any of the other transactions could have read what is in the BAL, right? So, from the perspective of your transaction that only does Execution, you could not tell. maybe the others do execute, some storage reads. But now, having at least one storage… having at least the storage keys mapped to a transaction index, you can fail after executing the first transaction, basically. Because you would then already, within the first transaction, realize
That this transaction, for example, does only compute And as soon as you executed the first transaction, you could fail the whole block. And if you now think, okay, we have 8 transactions and 4 cores. then this would mean you don't execute all 8 transactions until realizing you… You can invalidate the block already, but you would only execute 4 transactions, because then after those 4 transactions, the bul would have already been invalidated.
Jared Wasinger: Mmm, okay.
Toni Wahrstätter: So basically, basically what helps a lot is… Today, you give a transaction to a thread, and the thread can very clearly enforce that the state diff is correct, because there is nothing other transactions can interfere with. from the storage read location, it's different. So they need to be brought together at the end of transactions. Kinda collected during execution, and then compared with the BAL. So for… until we don't map storage keys to indices, we might not be able to get this isolation of being able to validate that transaction in full isolation.
Jared Wasinger: Yeah, I think I… I think I was actually just mis… misunderstanding it. I think I… I… I think it's starting to make more sense to me now.
Toni Wahrstätter: Yeah, we don't need to take a decision on this today. I would suggest that we definitely think about such situation when setting up the test framework, such that we are actually able to test such cases. the attack vector would basically be causing a reorg for the next proposal. So the next proposer would… Potentially face a block that takes too long to execute, and would then be in a very uncertain… situation where you don't really know, should I reorg the previous block because the execution took so long, or should I try to finish execution and build on top of it? So this is essentially the attack here. We could trigger, I think we could trigger reorg's,
On a definite, by doing so. And also, of course, like, scaling the gas limit very aggressively to points where We don't have much buffer left. But yeah, I think we… clients should just look into this problem and think how they would actually practically Tech lit? Such that clients are not tricked into doing a lot of batch I.O, while at the same time Facing transactions that only do execution, and in the end, figuring out that everything was, invalid. But figuring out That it was invalid too late into the process. Yeah, is there any other comment regarding this?
Yeah, seems like it's not the case, and we don't need to find solutions today. Just wanted to bring it up such that we are aware of this situation. To me, it feels like the cleanest thing right now would be… To do nothing, and just, I mean, for DevNet 2, we will do nothing anyway, because it's, it's not worth it to change something for DevNet 2. But then for DevNet 3, we should probably… It should be the goal that we have this situation, resolved. and Ida. Remove the state locations from the bundle entirely, or we agree on adding, like. first access indices, something like this, to the BAL. I think it would be, from a size perspective, it would be not a big deal. It's more like additional complexity,
Though I would… Want to avoid, personally. But yeah. It's good that we have flagged that problem, and we can now… Collectively start figuring out solutions. Awesome. This is quite good timing, we're already at the end of the call. Anything regarding this topic, how to… invalidate. Invalid blocks as fast as possible, or anything that we forgot to… to do… Today, on today's call. Yeah, Cherry, go ahead.
Jared Wasinger: Yeah, I wanted to, bring up something that's a completely different subject, and it should be pretty quick. So… and maybe this is something that, should've… well, I wasn't actually aware of this by the time ACDT happened this week, so… basically, and I brought it up before, but it's just kind of… somewhat of a blocker. In fact, it is a blocker for Geth being able to… to make progress towards DevNet 2, is that the test fixtures currently don't have receipts in them, and we're getting, a lot of receipt root mismatches, and this is related to, I think it's 7778? the ETH transfer emit log EIP, so…
Toni Wahrstätter: We have different ones, so 7778 is the one that excludes the refunds.
Jared Wasinger: Okay, maybe, maybe it's 7708, I, I, I'm probably getting the number wrong. But, it would be great if we could get, like a… A release that has the receipts themselves and the fixtures, because otherwise it's basically impossible to debug failing tests, because right now there's just… Cool, okay. Yeah. Yeah, okay, cool.
Toni Wahrstätter: Yeah, seems like this. Was resolved quite quickly. Anything else we should add? For today, anything else people want to discuss?
Felipe Selmo: Real quick… Oh, yep.
Jochem Brouwer: Yeah, Philippe, please go ahead, because I think I want to ask exactly what you are about to be spoken.
Felipe Selmo: Yeah, real quick, I just want to get consensus on the updates that were made to both self-destruct and calls and transactions to self. I think we're all in agreement that Any transfer to self should not emit a log unless there's a self-destruct ETH burn case. But then also, we need to talk about… We need to… we need agreement for DevNet 2 on whether or not it's only a self-destruct log if it's created and destroyed in the same transaction. And so, I just… I think we're all on the same page, but these are changes that have just come up recently. And we just need to make sure that we're all in agreement. For DevNet 2.
Jared Wasinger: Yeah, on the guest side, we're… We're in agreement.
Jochem Brouwer: Yeah, I wanted to ask Philippe about the receipts, like, is adding… because I don't think the fixture lease ever have, like, the receipts in them. Is it, like, easy to add this to the fixtures, or not?
Felipe Selmo: Off the top of my head, I… I don't think it will be too difficult, famous last words, right? But, I think we can… yeah, I think we can take care of this, and I think the request is very sensible. I think we'll… either way, we should figure out a way to do this, for some… for some of these EAPs. To help with testing and debugging. In fact, I'm a little late to the STEL meeting right now, and I'll bring this up, right now, and we'll find a way around it.
Toni Wahrstätter: Perfect. Awesome, yeah, then let's continue that conversation async, and we're at the end of the call. If there's nothing else, then I would say thanks to everyone. And see ya! In two weeks at the next critical call.
Jochem Brouwer: Bye-bye. Thanks, everyone.
Felipe Selmo: Hi, everyone.
Toni Wahrstätter: Thank you, bye-bye.
Transcript adapted from ethereum/pm, CC BY-SA 3.0.