Akash Kshirsagar: Hey everyone, welcome to ACDC 176, which I believe is the right number. We had had a meeting scheduled on March 5th, which the agenda was a little lighter that week. So we decided to go for an async version of things on the Discord. And in any case, yeah, so we have today's meeting and I think we just moved the... meeting number to this date, so number 176. Issue 1941 on the PM repo, I put the link here in the chat. It's the same issue that we had from when we would have had the last call. And there are a few things that I think we made some progress on that we're scheduled for the 5th, but we'll touch on them today just ⁓ to tidy them up. And otherwise, yeah, I think since there have been a number of other threads that have become relevant, so let's touch on those today. And yeah, so we have some things for GlamsterDAM. I think the main topic for today will be EPBS, a few other things, and then we'll touch on Agota towards the end of the call. So let's go ahead get started with GlamsterDAM. First off, would anyone like to give ⁓ any observations or updates from DevNet Zero? So we did launch EPVS to DevNet Zero. ⁓ very exciting. I think the start was a little rocky. ⁓ I was even looking yesterday and you know, it looks pretty good. Participation is all right. ⁓ there's also a new thing I saw in Dora for proposals and that one also looks all right. ⁓ I think every so often there's some rocky epochs. But yeah, otherwise, as far as I've seen, yeah, client teams are working on just working through any bugs there and getting to a stable DevNo zero. Anything else we should, yeah, acknowledge with DevNo zero right now? Barnabas? Yeah, so currently we have Prism, Lighthouse, and Lowstar pulling the chain forward. We have onboarded now every single CL client. Nimbus is having peering issues. Daku is having unknown issues. for Grandin, we're just waiting for a new branch. But they confirmed that they were able to participate as well in a local purchases test. I have tested the exits yesterday. It was working on a local environment. Lighthouse is still unable to process exits. And they just did a bunch of deposits on ⁓ as well as ⁓ Nido is also doing some testing. So as long as nobody is triggering exit, the chain should survive. If we're see an exit and yeah, Northstar in prison is gonna be able to process it, but Lighthouse is going to have a hard time. Also the epochs that you mentioned Alex before. ⁓ We seem to be missing a few blocks. ⁓ Prisms seem to be missing a few blocks randomly. Maybe they can give us an update. I'm not sure if they have investigated it. I'll have a PRL to fix that. We can re-merge that hopefully in the next two days. yeah, we have figured out the issues for that. Was this with the parent block that we were discussing on Discord? No, this is because we were essentially to send for a choice update to Gath, we were using the wrong safe block hash, the finalized block hash. We were using the reorg payload as the block hash. Because of that, Gath doesn't recognize it, and we were not able to read with this block. OK, gotcha. Sounds like a straightforward fix, then. At least I hope. ⁓ Cool. Wait, there's something Barnabas said that I had a question about. ⁓ When you said that when you're talking about exits, are these validator exits or builder exits? It doesn't matter. ⁓ Exit is an exit from the point ⁓ of view. So the endpoint for the validator, sorry, the builder exits is now supported by Prism and Loadstar. And ⁓ yeah, Lighthouse just unable to process the whole exit. I'm not sure if anyone from Lighthouse is in the call. I do see Mark. And here, just not sure I can comment on this. Okay. Well, in any case, yeah, there are a number of things that, yeah, people are finding as we go through, yeah, just operating DevNet Zero and see what happens. Let's see. So, yeah, unless there's anything else to add concretely for DevNet Zero right now, again, there are a number of issues, I think, across most, if not all the clients, but... people are generally aware. ⁓ I was, yeah, okay. So actually POTUS wanted to say some things in chat, which is also something I wanted to touch on just in terms of, yeah, the status here. ⁓ Getting to where we are today to the current spec. And I think that might be what you're talking about POTUS unless you mean something else. A couple of things are on the specs and other things are really just on implementation. OK. Yeah, you want to give us an overview? Sure. I just want to be quick because mostly I just want to call for people that are involved to weigh in. And I just wanted to be this on the minutes of this meeting. So there are a few things that we need to make a decision. One of them is already in the agenda for today, which is this issue of the BTC. This is an issue that was found and raised by Nico from Loadstar. The issues, in a words, is that when we call to get the PTC with a head state at slot 31, we might get a different PTC than at slot 32, even with the same state that just advanced to 32. And this may cause a split when we back a block, or that slot 32 might become invalid. There are a couple of PRs that are open on the spec to fix this issue. This is an issue that needs to be fixed. ⁓ or we just don't include at all PTC decisions on blocks. There are a few options. This option should be waiting and I'm hoping that for Monday, client-debs will show up already with at least an opinion so that we can just choose. ⁓ That's one thing. The other thing that was raised is with the issue of ⁓ proving things on the CL side. Today, we have a block route. that is exposed with a slot ⁓ of delay on the EVM. And this block route might be actually, ⁓ would just change semantics when there are several block routes that actually are happening on the CL side, but they come without a payload. This is a new feature of EPVS that you can have blocks on the CL that don't come with a payload. And then the problem is that the next payload that actually builds on top of a few of empty blocks, it will include the parent's block route and the contract, the system contract that keeps the block routes will have a gap of blocks that are missing. So this might affect mostly pools that are staking pools that are proving, for example, withdrawals or proving about slashings or proving anything. on blocks directly on the CL side. There are a few options. I'll write down a full, what I think is the right fix, which is not do anything here and have pools proving using the parent block hash. I think it's just cheap. There's another option, which is proving using the beacon state, but that ⁓ PK from PandaOps showed that this was just too expensive. So again, if pools come up and say, this affects us this way, we want this fixed, or if you just want to sit down with us and discuss what are the options, that's going to be great. ⁓ I see that Dima is already mentioning this ⁓ for Lidov. So OK, so it's good that Dima that Lidov is already looking at this. There's another option, which is the Engine API. ⁓ EPBS will have, and this was already mentioned in ACDT, I'm just repeating it now for all the LDEVs, EPBS will force reorgs to a previous head. So not only a reorg, but just a rollback of your head. And if that happens, the engine should allow us, not should, must allow us to build on top of previous blocks. So if we send FCU with a previous head, this should not return never on the EL. This is something to be aware and this is something that needs to be specified. Finally, the thing that Terrence mentioned, ⁓ this is not really what was causing failed blocks on Per Prism, but here's an issue. ⁓ When we track finalization and justification or safe head or anything, this is also going to ⁓ affect the fast confirmation rule. We track just the block route. This means that the block route is ⁓ what people are voting on for target or whatever it is. It's true that we have enough information to track whether or not we're voting also for the full or empty target, but that's not what is in the spec today. So when we finalize a particular block route, we are not finalizing nor justifying the payload that was committed in that block. That means that the safe head, the justified head, and the finalized head should always be sent to the engine as the parent hash of the consensus block that had the right block route. So this is a change that I suspect clients haven't had yet. OK, so that's the last one that I had to have on the minute. So it's mostly EL devs and ⁓ pools that should show up and talk about these issues. Okay, yeah, thanks. This last one, is this what Terrence was supposed to hear in the chat with this PR? No, that last one was... So Prism was doing something even worse. Prism was taking the block route and just putting whatever payload that block route had, even though that payload might not even be on chain, which is even worse than just putting it... So we were putting that payload if it was on chain or if it wasn't on chain. So the engine is not complaining if we put that payload when it was on chain, ⁓ but that's also wrong. And we were also putting it when it wasn't on chain, which is even worse. Right. What I put in the chat is actually what Potez talked about on the third point, where that the EL needs to support reward on top of empty, not only food. And then we actually saw that issue, Prism saw that issue we get. So yeah, we need this review and merch soon. Okay, yeah, no thanks for bringing it up. And yeah, that was a good summary. ⁓ Marius, you've your hand. Let's do that next. Yes, regarding the EL stuff. ⁓ So when I implemented it, ⁓ I implemented it a bit naively. And what happened is that the guest nodes kept crashing. And the problem is that during sync if we reorg to a block in basically our sync mechanism and our block advancing mechanism are racing. so if you give us a fortress updated during sync ⁓ and we change the canonical statue of the chronic canonicalness of a block. then that will crash the sync. So what I've done right now for only for the definite is to say that yes, are accepting, ⁓ we are accepting reorgs to the parent, but only if we are not syncing. ⁓ The syncing state is not super clearly defined in our code base. ⁓ And so it might be also bit different between clients. ⁓ But I think that can be these issues where we are currently sinking or we are not, we just started a new sync cycle. And then we get this this reorg. And then we reject the block with I think we rejected with the sinking error or like the sinking flag, just so that consensus players are aware that reorgs to a parent might not succeed if your EL node is in a state where it's not fully synced yet. But in that case, we shouldn't even send a paid attributes, right? So it doesn't matter. ⁓ Yeah, but at least in our implementation that is very different. Like we are checking the canonicalness of the node in a different path than we attend the payload attributes. Like the payload attributes are happening way later. But what you're saying is basically we should only accept this if you're also sending payload attributes. Right, so if, no, we might. We will send you an FCU with a previous block ⁓ because that's what honestly can happen. ⁓ I suspect that if you respond with a network here, it's kind of irrelevant because the worst that can happen is that it's going to put us optimistic. ⁓ But that was already the case because you were already syncing. So the next proposer ⁓ will only be able to propose if it wasn't optimistic already, so it's fine. In which case that guy didn't have an error. And every node that was optimistic is just going to import nicely the next block. So I think it's no problem. Yeah, I'm just saying that something that CLs should be mindful of, should know that we might pull you into optimistic mode if we are getting this reversal of the head. And for ELs to also be mindful that we might have these reorgs that are kind of changing the canonicalness of blocks that are also included in this thing. Can't that already happen though? Not like this. Okay. Like if we have marked a block as canonical, it is, yeah, it's a bit different to uncanonicalize it. This is like a new way of doing that. So yeah. Yeah. Okay. So what would help here? Like, is it a refinement to the spec? Should we just make sure we have testing in place for this, something else? Yeah, I think both refinement to the spec and making sure that we have the testing for maybe the engine API test. OK. Is there an issue here or something written down that will help me check this? Yeah, Fotis? I actually think that we might be able to help. ⁓ The way we work now is that we actually send an FCU to propose on top of such a reorg. So I think we might be able to coordinate in that we are only going to send this type of reorgs and this type of FCUs only when we are sending actual payload attributes. Because I think for a node that is just syncing, doing that such an FCU that rolls back is kind of useless ⁓ if you're not proposing, because you are going to, the proposer will send such a thing and will just actually cause a reorg on the EL. So an attester that is just syncing can just send the next FCU with an actual honest reorg on the EL. Sure. But there is also another use case as the RPC provider, right? If you're a RPC provider, you're using the head on the RPC employee on the execution of client, you may get the wrong information if they reorg back on top of MT. So I don't think that attribute. is enough. We probably need to do this regularly for FCU as well. No, that RPC provider is going to be providing information over its last head, which was that in the EL. And then the next block that is produced is going to actually reorg on the EL and is going to reorg the RPC provider as well. I see. Yeah, I need to think more about this. So I suspect that the EL only needs to deal with this when it's dealing with a proposer that is sending a payload attributes to actually trigger block production on top of a previous block. Is that a similar thing that we're already happening when we do late block reorg and the fork choice filtering? when we roll, so that happens at least in Tango, we roll back our head to the previous block and then we produce the next block on top of the ancestor and we don't send FCU in that case rolling back the head. So it should be kind of similar thing. Well, no, that's, that's what I wanted to avoid actually. so For the CL reorg, we're doing this juggling and this thing that is just artificial because the EL doesn't know how to deal with this situation. So we actually avoid sending an FCU when we suspect that we're going to be reorging. That was the reason why we have to this send of this Should override FCU and whatever this happens. Yeah, the should override. Just dirtying the spec. But now, not even that's going to work because you don't really know ⁓ if you need to reorder back. That's going to depend on other attestations. So I think at least we should allow these FCUs when we are proposing. And if we're not proposing, it's fine. Well, not really fine. It's like, yes, it's fine from a technical perspective, from the UX, it's pretty bad because the CL knows that the block is, that the EL is giving out over the RPC is bad and it waits until the next block. Yeah. In that case, can we just have the CL update the EL when it knows? Well, that's what we're doing now. And this is what's causing the errors from GET. I'm happy if we keep it that way. We send FCU and then the EL replies. And maybe I didn't follow, but then Marius, you're saying there's an issue with this current behavior? Well, no. The current behavior is fine as long as we are very clear that if the sync is still happening, that we can just return syncing to the We should make it very clear that for people that are listening, We should be very clear that it's not that we're going to be rolling back ahead. This is happening on DevNet just because clients are voting incorrectly for empty blocks. So clients are signaling, you should roll back. But in normal behavior, normal situation with the chain, with or without blocks, we're not going to be rolling back ahead. This requires a majority of clients voting incorrectly to saying that a payload did not exist. But what does it incorrectly mean? Like, the proposer has the correct view, but somehow a majority has a different view? No. what's happening on the DevNet is that, well, I think this has changed lately, but what's happening in the DevNet is that Lighthouse and Loadstar did not include the committee index being one when there was a missing ⁓ block. So they were voting for the previous consensus block with committee index zero and this means that they were voting for the empty consensus block. So that forced Prism to reorg the payload. And that's what caused all of these previous reorgs. But as soon as Lighthouse and Lodes are fixed, I think this stops happening. All right. it seems... It's theoretically possible anyway. I didn't hear Enrico. Sorry, was on the Just saying that it is still... possible that this thing happens. is possible that it happens, but it will require a very... It's not going to be as common as in the DevNet. It's going to be something very, very unusual as reorgs are unusual today. Yeah, but I don't even want CLs to start being optimistically in sync where they start... But what Mario said is that it's not that you're going to become optimistic. What Mario said is you are already optimistic and it's going to return you thinking. Are you sure? Perhaps Marius can repeat, but I understood that this would affect only nodes that were already syncing. Yes, it only affects nodes that are in the syncing mode. The problem is the syncing mode is a bit iffy defined. So if you are sending us on a bigger side chain, then we might go into syncing mode. ⁓ Or if there is, I don't know, a bigger reorg, like a normal reorg, then that also might put us into syncing mode. So it's not just the initial sync. It can happen that ⁓ after the initial sync, you will also be kicked into the syncing mode. Like if the CL tells us, we want to go to this block and we have no idea about that block, that might put us into syncing mode. Yeah. And as I understand, even if we think it will be rare, say on main net, this could happen. And then if we want to change the behavior, we should definitely make sure it happens. Like this isn't just like a transient dev net thing. It'll work itself out. Right. Okay, in any case, I will track this somewhere and yeah, we will do what we need to do to harden the specs here and our implementations. ⁓ Yeah, just to make this work how we want. OK, since we've gotten into it, so yeah, there are a number of things. Plotus touched on, I think, most of them already. Let's see. Let's circle back to this 4788 issue. So yeah, I think Plotus gave a pretty good summary. Essentially, now that we can have empty execution payloads under EPPS, 4788, doesn't quite work like we expect. And the question now is what we do about it. So yeah, there are a couple options. One of them is to do nothing, like Ploetus said, which is what I kind of lean towards now. And the reason why is because given the way we have different history accumulators on the beacon chain, you can still get to the data you need. And maybe the proof looks a little bit different, but you can still do so. From there, yeah, PK brought this up that it is possible that if you need to go through this different path, the proofs might become bigger than we would like. At that point, I would like to quantify the impact. Does anyone else have any thoughts on this problem right now or preferences on how we handle it? I think there would also be a middle way. ⁓ when we basically put in the parent roots of full beacon blocks only. So we only have to gap for the empty ones. So we at least have all the beacon roots for blocks that have with 12s and everything included. be a counter and middle way of both. Right. So yeah, this kind of gets very deep in the weeds, but how it happens is that you would have a beacon block execution block. There would be a number of beacon blocks, no execution blocks. And then you would get to the next full slot, let's say. And then, yeah, like one option here is, I think this is what you're just saying, just not PK, like you pass all of the intermediate routes. I think you could get away with just passing the previous, so the previous beacon block that was not empty, which again, this is the semantics are like, pretty intricate, but at that point then I think what happens is it looks transparent to ELs today. And like part of my point here is I don't think people really need the intermediate beacon box with empty payloads. And if somehow they do, they can go through this more indirect route. So yeah, like I think either do nothing or yeah, we try to pass these quote missing roots or let's say differently. We passed the route that we would otherwise miss today. POTUS. So the intermediate blocks will include a lot of information that might be useful for pools. ⁓ Has this validator been slashed? ⁓ Has this validator ⁓ exited? And a bunch of things that can actually happen on the CL. The problem is that these things will not have any impact at all on money until the payload actually arrives. So if the question is, has this validator been slashed? Do we really need to know that it was slashed in this block? or it's just enough to know that it is slashed now. Has this validated with thrown? Do we really need to know that it withdrew in this particular CL block? Or do we need to know that it was fulfilled in this particular EL block? And with throwouts is very special because there will be no withdrawals in those empty blocks anyways. There's only withdrawals in the first one and then the chain of empty blocks will just not process any withdrawals anyways. I think this can be exploited just using the parent block and that's it. Right, yeah, like there is no execution for these empty payloads. So in terms of the EVM, which is where you would consume 4788, there's just no way to change anything. Timo? Yeah, first of all, yeah, I do agree that most of the information that is on the consensus layer is not required to be delivered. Like the proof is not required to be delivered precisely against the exact block. So for example, slashing, can just prove that the validator is slashed and doesn't matter if it's this block or three blocks away or something like that. That's true. However, there are at least two cases that we have already identified in LIDAR that do require us to point to a specific consensus layer block. The first case is when the consolidation happens. So ⁓ we really need to have two adjacent consensus layer blocks, the one where consolidation was still pending and the other one where the consolidation was applied, so that we can do a correct accounting of balance being moved from one validator to another. And if this validators belong to say different entities or different staking modules or different structures of different owners within a protocol, That is crucial. The other thing that is way bigger and way more problematic and not that obvious, honestly, is to have the ability for the ZK Oracle. So we use Oracles in LIDO. Right now, we are still using ⁓ trusted Oracles, unfortunately, to argue about ⁓ the TVL of the protocol. So what's the total balance of the validators as of a very particular slot? And again, even though there are no changes on the execution layer regarding to that, the protocol needs to have a very precise information about what was the balance of all of the validators within the protocol at a specific slot. And since we are gradually introducing ZK Oracle to later on probably replace the trusted Oracle, we indeed need to point to a specific consensus layer block which or slot. that corresponds to a particular slot so that we can make all of this ZK proofs against this particular one. And again, even though as I mentioned in the comments, it's not like the end of the world and everything Alex you are mentioning and what Pottus is mentioning is correct that this information is still reachable. This information now is reachable in a different way, hence, ⁓ development maintenance and so on is required. So in general, I am not a big fan of changing stuff just because it's easier to change this stuff and then later on require protocols ⁓ that do rely on a previous behavior to now adapt to new behavior unless the option to keep the old behavior is prohibitively expensive for the Ethereum developers. So from my point and from Lida point of view, I would say that it will be way better and way easier for us to have this missing roots back propagated in this or that way. And if you refer to the document linked, it's either option three or option four. It's again, it's not the end of the world, but if we will go with the missing blocks, it means that by the time of Glamstertum, we would have to redo a significant portion of the protocol that relies on this trustless proofs. And over the last years, we did a great job at ⁓ adding this permissionless proofs to as many places as we can so that the reliance on oracles is lower and lower. So to summarize it up, it will be way better if we will keep the original behavior of 4788, and it would save us a tremendous amount of effort that can be reallocated to other places like stuff that we are currently working on, like consolidations of the whole curated set and in the future, transforming our community set into OXO2 as well. But this all requires time and effort. And whenever we introduce such a change that requires an intermediate attention from our side, we are getting further away from ⁓ achieving long-term goals, like again, increasing the number of OXO2 validators in the net. Yeah, that makes sense. Yeah, thanks for the context. Okay. In terms of moving forward, yeah, POTUS has this idea to think about the parent hash, because I think that just makes the proofs less cumbersome. POTUS, would you, are you thinking we do, let's call it option one from PKS doc? Just do nothing? I haven't seen what Pig Hey wrote. Last time I spoke with him, we were thinking about using the fact that the block accesses the state and then using the state to proof against. And that is quite expensive. This adds like 22 hashes. But the thing is that we always have the parent hash, which points to the parent block or the parent root for a block, which points to the parent block. ⁓ have a parent route, then you can just provide the parent route and it's only one hash to access your block route. So since we always have the block that we're interested in on chain or it's child on chain, then it just adds a single hash to prove against the block that you're interested in in the worst scenario. So I think it's really cheap to prove against blocks and not the state. It requires changing contracts. So I empathize with this. It might be that it's just the same cost, but then just changing the contract and auditing the contracts and whatever it is and deploying these new contracts might be just already prohibitively expensive for pools. So I empathize with their points. I have no opinion. I'm just saying that if we prove with block routes, it should be just essentially just as cheap as it is today. Yeah, the other small thing here, and I'm super sorry that no one from RocketPool is here because their architecture is slightly different from LIDAR. And in LIDAR, we can basically do upgrades via governance vote, which is ⁓ complex and ⁓ costly, but not impossible. For RocketPool, they have a different architecture where it might be even more problematic for them not to ⁓ develop and deploy the code, but to make their mini and now mega pools to upgrade to this thing. I'm not that well into rocket pool architecture, but I think we should definitely reach out to them directly and ask how much of a problem it would create for them because their architecture is slightly different from LIDAR. Yeah, I can make sure we talked to them. I also believe I can layer users 4788. So yeah, we can do it sort of an impact analysis here. What I think the highest cost is would be changing the system contract. So yeah, strongly prefer to not do that. ⁓ Then from there, the question is, yeah, how nice can we make it? I think some combination of option one, which is where we add this rule that you would send the most recent non-empty beacon block as the parent. I think that handles most of the things. There are possibly some transitions only in the beacon blocks that would be missing. And then you'd have to go through this more indirect route. Maybe then what POTUS is suggesting is a good mitigation there. In any case, yeah, we'll need to do a little more analysis here. But yeah. we should definitely figure this out in like the next couple of weeks in terms of how we want to actually move forward. Okay. Any other comments on this one? Otherwise we'll move forward. Okay. Okay, the next thing I had here, this was also something POTUS mentioned with this PTC issue. So I linked what I think as of yesterday was the latest ⁓ ActivePR to handle this. There was another one from Inflag that looks like we should also look at. So yeah. These are the straightforward solutions given today. I looked into this some earlier this week and I started to go down this thread where I'm not sure we actually even need to put the payload attestations on chain. And then at that point, we have more flexibility, I think in the specs and we can just say, hey, you're expected to have these committees cashed and then you just use the appropriate committee for the right slot. And then there's no issue with the state changing on you. That being said, there is some argument that having them in the blocks and process like we do today makes things a bit more stable. I started looking more deeply into the fork choice to try to wrap my head around this. I don't have a clear answer there yet. I believe POTUS just from talking on Discord, it seems like you'd prefer to keep it as is. Is that still correct? For disclaimer, I believe Francesco is fine with removing them. And I would just take Francesco's voice on Fortress over mine. But I also believe that it's clear that it, well, I am wary about ex-anti reorgs that can be produced by removing this. If you can convince somehow on a split decision, or if you can convince the next proposer that he should reor the payload. ⁓ and then it turns out that the next proposer is being re-orged because he tried to re-org a payload. I think having the PTC in the block ensures that the view of the proposer is the right one. Yeah, that intuition makes sense. The part that I haven't done myself yet is just look deeply enough into the fork twice to see if this actually holds. Because I think it's possible today that this works more like a hint to the next proposer. No, no, no, no. the proposer, so it is a hint to the next, the proposer is never forced to reorder payload. the purchase today is such that. The proposer always has a decision to build on full if he's seen a payload and if he's seen a payload, it doesn't matter what the PTC done, he's verified the payload, he can always build on top of full. The question is what happens if a proposer decides to build on top of empty for whatever reason? And then in that situation, we might want to allow that proposer to actually build on empty. if there was a split view, if there were equivocations, if there were a bunch of situations. And in those situations, this PTC on the blog ensures that the proposal view is respected by the testers. OK, because then that same proposal includes the payload attestations that everyone else then sees? Yeah, because imagine, for example, the PTC is actually equivocating. It's sending attestations to testers, and it's sending a different attestation to the proposal. So these kind of things might be resolved by the proposer asserting, these are the ones that I'm considering. There is no slashing for PTC, for example. This is one of the few things that gets. It's not that it's very important. And I'm also envisioning a not canonical behavior by the proposer. Because if the proposer had processed the payload, he should propose on top of full. He shouldn't be proposing on top of empty. On the other hand, not including this and not allowing proposers to reorg payloads, I think, allows timing games with builders and off-protocol arrangements of the next proposers with the builders so that the payload can actually come later and a bunch of other things. Yeah. Okay. So in any case, like as is we do need to say, merge one of these PRs. And then at that point I'd prefer like the simplest thing that gets the job done. And it looks like this latest PR 50 20 does that. So in any case, I think today, or at least for now, I would ask people just to look at these and Yeah, ideally we can make a decision in like the next week. Like possibly even by ACDT on Monday. ⁓ do you want to more about that, POTUS? Because yeah, that's what I meant is it just has the least impact on the state. Because otherwise, you know, like in general, we just keep adding things to the state to patch these issues and then the state becomes gross. Okay. So, okay. Yeah. Take a look. I think, yeah, to fully grok this, it's actually pretty subtle. But yeah, take a look and then let's just keep. chatting about this. Perhaps by next ACDT, we'll feel more strongly about one route versus the other. What do mean we're shipping this in DevNet 1, like one of these PRs? Yeah, so this is the one thing that will require hard work. So this is the only feature that will force a new DevNet. think we are, all of us don't have complete implementations of EPBS. That's why we're seeing a lot of bugs and not even bugs, things that we haven't implemented and we keep implementing and testing on DevNet 0. So DevNet 0 is very useful for us. The one thing that would be a hard fork would be this. So this would force a new devnet. Right. Assuming we don't go down this other route of even just taking them out and handling this a different way. But yeah. OK. Take them in the mouth is also hard work. But no, just like, ⁓ think, yeah, okay. No, just for my own understanding of the timelines here. But yes, it would be for sure. Okay. So yeah, I think that's all we're going to do on that one for now today. So yeah, please take a look if you have a minute. ⁓ Review these PRs, slash if you prefer one over the other, please add a comment on the PR as well. That would be helpful. Okay, let's see. So, the beginning of this call, you touched on these two things that we just discussed. There was this engine API PR that I think is smaller in scope that I think we just need to merge. And this is 770 to be clear. There was also this issue that we were discussing for a bit around the syncing semantics and reorgs there. I don't... see something written down right now to track that one, but I will do something, unless I can find something that exists already. One more to discuss. So yeah, I was trying to keep a list of like kind of open things in terms of getting towards GlamsterDAMmDevNet 0, where we would merge us all together and then move forward, you know, probably towards shipping GlamsterDAMm. One more thing that I think we still need to discuss a bit more was this concept of the variable PTC deadline. ⁓ Certainly, you know, the flow should be to iron out the current DevNet and yeah, just all the mechanics there. But then I think there's at least some interest in exploring this variable PTC deadline and a future DevNet. So from there, I think I just wanted to bring it up just so everyone's aware of the status there. Although Tony had document. And yeah, if you'd like to say a few words, Tony, this would be a good time. Yes, I will quickly post it into the chat, including the PR. This is one of those open points that are still discussed with EPBS. Like, should we do a variable payload deadline that is independent from the PDC deadline? And I thought it might make sense to ⁓ also look into the call data pricing, because on the EO, we have a call data pricing that is very much focused on capping the worst case while staying as much backwards compatible as possible. But this also means that the worst case block size can also include quite a lot of execution. So there is EAP 7976 that is CFI for Amsterdam. And with that ⁓ EAP, the worst case block size is kept at 60 million gas, you would be at one megabyte. But you could still use like over 90 % of the gas for execution. There has been a discussion among clients to enthrine snappy compression, but what I read was clients are very much against that because this could lead to other failures, consensus issues or something. So if we want to anchor the payload, the variable payload deadline based on the uncompressed block size, then I don't think it's a good idea because you could create a one megabyte big uncompressed block and one block compresses 200 kilobytes, the other compresses not at all. So I have some charts in the blog post. Yeah. I don't have a strong opinion on it and would just say, ⁓ yeah, let the clients take the shot. But I definitely think, yeah, we should find a solution there. Right. Yeah. Mark. Yeah. Yeah. I spent a while coming through the Zatua data to try to figure out what we were looking at in terms of how Basically, the end-to-end dissemination time varies with the payload size. And I mean, it seems like if we don't do a variable deadline, we're forced to basically set the block deadline back in order to accommodate the larger payloads that are slower to propagate. But on the average case, payloads are significantly smaller than those more extreme cases. ⁓ And so, you know, we've artificially lowered the gas basically. And I think the headline from the results that I saw is that we should gain somewhere between 300 to 500 milliseconds for execution with a variable deadline, which is not nothing. ⁓ But I agree with. Certainly with Tony's conclusion that EIP 7999 seems like the real way to solve this problem. it That was just my attempt to quantify this in terms of like how much, how much can actually be gained. And yeah, like I said, 300 to 500 milliseconds for execution is what I had found. Okay, yeah, thanks a lot, Mark. Maybe just to add one more thing. If we want to do it, then we should also rethink if we actually want to just linearly interpolate between 3.6 seconds and 9 seconds, because today there is a lot of room towards the maximum block size. So the average block size is significantly smaller than the worst case. And if we would just naively interpolate, then most of the blocks would have a deadline at second four. And this seems unrealistic, so we cannot demand builders to publish blocks very early, or I guess this would very much defeat the purpose of EPBS helping to scale. So if we want to do it, we should rethink the formula. also the PR currently has a worst-case block size hard-coded at 10 megabytes, which is, I guess I get where it's from, but I think it should be. Yeah, significantly lower. And it should be calculated based on the gas limit because we know gas limit divided by the call data cost is the worst case block, at least as of Amsterdam. And then the formula already improves, but we're still like, we would still end up with a very early payload deadline, eventually giving the CL like six or seven seconds to execute. I don't think we can ever scale the CL to make use of that. are the EL to ever make use of six or seven seconds of worst case execution. Okay, any other comments on this for now? I don't think we're going to make a decision today. And yeah, this is just kind of a gray area around how we want to do this. At least for now. So I suggest, yeah, let's just think about it some more. And I think we'll have more clarity in the future. Okay. Let me find the agenda. OK, yeah, so that was super helpful. Thanks, everyone. ⁓ Yeah, we need to get through all these things at some point. They're very much top of mind, especially for as we think about DevNet 1 and potentially DevNet 2. That being said, I think we have plenty to do for DevNet 0 right now. Zooming out just a bit from there, I did want to think about GlamsterDAM DevNet Zero and like our path there. And again, we'll have to see how this goes, but it would be nice to get to something end of April in terms of GlamsterDAM DevNet Zero. And that would mean merging the two different CL and EL work streams, both BALs and also BVs. But then from there, that would be nice in terms of us now merging the streams and being able to work together on the actual hard fork. Does anyone have any thoughts on that timeline? ⁓ yeah, okay. I mean, this is what I meant end of April, sorry, but yeah, mid April. ⁓ Essentially before interop is what I'm getting at. I think we can just make a call on the PTC Monday and I think that's going to be fine. OK, it's at least from the lack of responses. This doesn't seem. ⁓ an inappropriate suggestion. So let's just go ahead and pencil that in. ⁓ Let's all try to work towards GlamsterDAM definite zero mid-April, which is roughly a month from now. And cool. Okay. I think that'd be good. From there, there are a few things ⁓ that are GlamsterDAM related, but not as specific to the seal. And a few things that were on the agenda from what have what would have been last ACDC's time slot. Let's run through these. believe it was, okay, Spencer wanted to SFI the ball down to EIPs and to Amsterdam. Okay, he also says he won't make the call, so I guess he's not here. But yeah. anyone opposed to this? I think my first question would be is DevNet 2 stable enough? I feel like that's a pretty reasonable blocker on making something CFI to SFI. From my perspective, DevNet 2 has been pretty stable so far. Does anyone have a reason not to SFI these? One thing I can point out is this is a CL call and not an EL call. And I'm not trying to delay another week, but yeah, it's the EL side on Scar. So I'm not trying to delay another week, but you know, it is slightly more appropriate to handle next week. For example, I don't know if you know the right amount of EL devs are even here today. Okay. So then let's handle it this way at least. Yeah. It doesn't seem like there are any. strong opinions either way on the sales side, but it does seem more appropriate to be an EL call, a decision to make on the EL call. So let's just push this next week. We make SVI decisions on ACDT. And again, I think if you have the right people, the right place, the right time, then it doesn't matter as much. Do we think we can make that happen for Monday? Okay, how about we, oh yeah, I was very saying he wants to discuss next week on ACDE. Okay, then sure. ⁓ Barnabas, you, well, okay. I'll just pull on this. Let's discuss this a week from now on ACDE. And yeah, we can get into that conversation there. Okay. What I going to say on the ground then is Barnabas could optimistically try on Monday. But again, I think it needs to be, ⁓ like it can't be just like a few EL teams being like, yeah, let's do this. Like it should be, ⁓ should be the full set, but I'm the option to weigh in. So in any case, let's fall back to doing this next week, I would suggest. And OK, it sounds like you guys can discuss further. OK. OK, next thing, the SSZ Engine API. So this was from Julio. He had some PRs. I think I also saw a very similar PR from Barnabas. And my understanding is that what we want to do here is support SSZ as like a transport encoding for Engine API, especially for pushing blobs back and forth. This should help quite a bit. And I think now there have been some ⁓ benchmarks to prove this point. So there was a thread on Discord that got quite a bit into ⁓ this. And yeah, I don't know if anyone would like to. Okay, there are 122 messages. ⁓ There's quite a bit here, but ultimately, I mean, again, I think, you know, I would say this makes sense to do. And then from there, we can discuss when. ⁓ do we want to go ahead and try to do this in Amsterdam? So I basically proposed this in order for us to be able to do a performance upgrade without needing to even have an EIP for it. ⁓ I proposed a engine API change only, which could be an optional thing. So we could basically have all the CRs supported basically as early as tomorrow. And all the ERs could opt in. And when they end up supporting them, ⁓ the negotiation would basically going to SSD instead of JSON. Yeah, I mean, I like that approach. Essentially be opt-in for whoever supports it and then you get the performance benefit. Right. The major question is basically finalizing the spec and see if the spec is fine by everyone. Yeah. Marius and then Julio. ⁓ Yeah, so I think, I personally think we should take this opportunity ⁓ to also rethink the engine API. Makes no sense to me to just do the same thing that we have right now with the engine API and move it to SSD. Yes, it's a bit of a performance improvement, but ⁓ it just adds a maintenance burden on all of the ELs. ⁓ I think the way I would like to do it would be to take this as an opportunity to rethink ⁓ how we are doing the Engine API, how we are doing versioning in the Engine API, how we are defining structs in the Engine API, and so on. Because, that is gotten kind of out of hand. ⁓ The Engine API implementations in the clients are pretty ugly. Now that we have done like four or five folks on top of the original engine API. And so, I think, yeah. Yeah. So I mean, yeah, that sounds like a much bigger set of work than just supporting SSC as an optional transport. So how do you think about that? I guess you're saying you think we should just go ahead and do it now. Julia. Yeah. So actually, um, I think I, mean, so first of all, I agree with Marius mostly directionally. Um, I think that kind of, can also do this in kind of incremental steps too. Like you don't need to do every, like, you know, if you can introduce a way to optionally support the multiple ways of. implement engine API, you can first do SSZ and then do a better engine API on top of it without blocking anything. Like you can, you can have a, you can, you can have a free thing, work on free things at the same time. It's not, I mean, you just need a way to, you know, do you need to give away the CL to expose it? So I think, I think you can pretty much do both if you really want. Like you can, First, you can do the SSZ thing, and then you can also, in the meantime, try to find a way or create a spec to improve the engine API. It's not entirely clear to me. I I think it can be improved, but it's not entirely clear to me how it can be improved. So yeah, maybe there needs to be some ideas on that. But I overall agree directionally. But we can do this in incremental steps still. people don't need to implement the SSZ that it's optional. So if, know, if Gaff doesn't want to implement it, they just don't implement it. And if NetherMate wants to implement it, it's their business whether they're fine with the complexity. Yeah. That makes sense. ⁓ Sure. Baros? Yeah, I would be just curious to know like ⁓ what kind of exact changes Marius was thinking about making. He says that the versioning is a bit ugly, but like. I'm not sure what the exact solution is. Yeah, kind of what Raoul is saying, what Perry is saying, the versioning system of the engine API method, it's just really dumb. And the way we are constructing structs ⁓ means that at least with the SSC implementations that we have, we need to create a new struct for every fork. I think the ⁓ CL devs have the same problem. ⁓ But instead of fixing that problem, are kind of now importing the same problem to the EL. And yeah, it's a. Then the And the way we specify some parameters, the way we specify functionality and behavior is kind of very annoying, like the way we specify errors in the Engine API. And I think all of this needs to be. Yeah, role. Yeah, so to add to that, maybe the networking perspective here is ⁓ besides kind of like the semantics, the versioning and ⁓ the error handling and so on. One principle that I would like to see on a revamped engine API is ⁓ something that allows EL and CL to coordinate a lot better about where they are in the pipeline and specifically on the CL. So the reason for this is that our resource constraints ⁓ or requirements are shared, specifically when it comes to bandwidth. So it really helps if the EL and the CL can be telling each other continuously ⁓ where they are and kind of like the pipeline. So for example, when the CL is done ⁓ receiving a, ⁓ like receiving blobs, for example, then it can tell the EL ⁓ that it's done so, and the EL can then throttle traffic on the mempool accordingly or throttle ⁓ sync traffic, which is kind of like not on the critical path accordingly. And ideally, if they have kind of like a shared view of bandwidth, they have available bandwidth, which like we have several ways of achieving, then they can basically implement some traffic. We can implement traffic shaping that allows both layers to actually act as one and not kind of like be conflicting with one another. ⁓ and kind of overshared resources. But this would require maybe like a different set of interaction patterns. So right now we have this RPC approach, which is synchronous. And we would probably need more streaming-oriented patterns. And maybe there's some web sockets kind of like approaches that we can add here. So multiplexing, there's a bunch of notions from the networking side of things as well that we can bring into this API design. Yeah, guess, I guess, I guess what I think maybe should be done is that we could take Barnaba spec, just kind of rename this in like, sorry, there is a bit of noise because I'm outside. ⁓ is a, so what we can do is that we can take Barnaba spec, rename it as kind of to be, you know, a next gen API instead, or whatever name we want to give it, honestly. Uh, first iteration might be only SSD, right? And then maybe we can just, you know, create some kind of DevNet and then iterate over it and just do all of our, do a bunch of changes to it that we like. I'm not sure actually what you can do about the versioning honestly. Yeah. In terms of, yeah, in terms of change management, what I would do is, uh, I think what Bironibus was, was proposing is basically like a content type. header sort of thing. think it's pretty much a direct translation of the types into SSC, to SSC. I think that should be something we do regardless. ⁓ And then on top, then like the next next gen API would be another effort, but it doesn't necessarily like you could technically conceive a world in which you still do Jason, right? For this new design, which we want to, but hopefully. But yeah, I think it's decoupled efforts. would go with the SSC1 seems like a pretty quick win to be honest, especially as we scale the number of blobs. Yeah, actually, okay. Well, we can do it incrementally. It's not like we need to do everything in one go. yeah, mean, honestly, yeah, maybe SSC is just probably, yeah. Yeah, I don't know. ⁓ I don't have really a strong opinion, but my answer would be that doing it incrementally is better. So do SSZ first and then maybe switch data. For blobs, ⁓ what's important is that whatever serialization format we pick is zero copy and that client's implemented in a zero copy way. Because right now the JSON base64 encoding decoding is what's killing us. And it's just absurd. You cannot do it zero copy regardless because you need to... I mean, unless... SSZ you can definitely do zero copy on the reader side. I mean, you do less copy. mean, it's a bit more complicated than that. You can do less copies. Yes. But it's a bit more complicated than that. I wouldn't call it zero copy. Anyways. All right. I have to check. Last time I checked, was pretty sure that I got a zero copy version working, at least in BrassPad. No, Well, okay. This is going to do in the details, but it's not zero copy unless you send over the C struct over TCP, right? Like that's zero copy. But no, no, but I don't want to argue on that. Yeah. Yeah. Yeah. Yeah. Yeah. Yeah. Yeah. Okay. understand what you mean. Sorry. It just, I was being little bit fadantic. But anyways, what I wanted to say was, yeah, maybe we should just do SCZ. Also SCZ is super simple. mean, I basically, took me like a weekend to open PRs in like eight clients. It's not like, you know, and it doesn't take that much time to refine them into being decent. It's just, you know, it's pretty simple. mean, you just, to be honest, I want to be completely clear here. I basically, it was just an, this was just for me, just an AI experiment. And what ended up coming up was honestly pretty decent. can, it will take me probably a week to just refine all of these PRs into something that works and that is decent to look at. I already have made Kurtaz's dev nets for all of these clients. And I already, bombard them with blobs. also wrote an article in research on this. So this is pretty easy. This is pretty quick. ⁓ Yeah. And to make this code better, you just, I just need to spend probably like a week working on this. It's not like that much time for me to do. Like it's pretty, it's one, it's a one person job to make this happen quickly. OK, yeah, let's zoom out a little bit. So yeah, sounds like there's certainly interest in making some change here. It does sound like a good first step would be thinking about as a Z for especially the blobs. There's a, yeah, it sounds like a deeper conversation around general changes and improvements to the Engine API. So yeah, would Barnabas's PR be a good place to start? And yeah, if. you care, we can move forward the conversation there. Thank you, Barnabas. Yeah, so take a look at this PR 7.64. And yeah, I think the question is just fitting in, actually getting something together. But yeah, sure, Julia, if you want to bring it up there, that would make sense. But yeah, I think if anything, takeaway for today is, yeah, we want to do something here. And then we just need to figure out exactly what that is. Okay, ⁓ there was one more thing that's not quite a gooda, so let's just go ahead and knock it out. And this was from Bowman Ops. I'm not sure how you see the handle, but essentially it was a standardization question for the JSON web token, I believe is what this stands for, secrets. This is going, yeah, quite back into history. ⁓ but there was an ask for the client behavior here just so we could tighten the spec. ⁓ I think this is saying it might be a should and the question is if we can make it a must. But in any case, does anyone have any thoughts on this? Let me grab, I think this is development pure. yeah. I would imagine given how this is clients have some way to handle this and it works. Okay, yeah, if you want to dig into this more, take a look at the PR. But otherwise, let's go ahead and move on to the next topic, which is Hegotah. So yeah, two things here. One of them is just around timelines for scoping. So we have picked the CL headliner for Hegotah with fossil. They are still working on the EL side to pick a headliner there. I believe the plan is to make a decision next week on ACD for the headliner. And then from there, ⁓ I will go double check the ETH research post where we're kind of tracking this formally. But in any case, the suggestion would be that we open up not a headliner. Let me say this differently. We would open up the not a headliner proposal window. as of that call, so a week from today. And then I think roughly there'd be about a month for proposals and then we would continue the scoping process with the non-headliner EIPs there. That was all I really had to update there, more of an FYI. I think again, the next step here will be finalizing the EL headliner for Hagota, but something to have on the radar. And last thing on the agenda, let me just take a look. I think we've covered the other stuff. OK. Yeah, OK, there was a comment, but it's related to us. Let's see. I don't know ⁓ if this is Greg or G. Kumaut. But they had a comment here just to talk about EIP 8141. So I think the idea is they would want to propose this as a not headliner EIP for Agota. given what I just said, we're maybe a call or two early, but we do have some time. So if someone here would like to say a few words, I think this is a good moment. And then we can wrap up today after that. Cool. So let me grab like five minutes of your time guys to give a short introduction of the EIP we are proposing to. as a non-headliner for Hegotab. This is EIP 8148. And the core idea of this EIP is to add a so-called custom sweep threshold for validators with compounding withdrawal credentials. ⁓ This threshold basically defines the value of effective balance above which the validator rewards will be swept to execution layer in the same way it currently works with max effective balance. And the main motivation to even introduce this feature is to allow validators not to choose between like, do we want to get our rewards after we are at 32 ETH for traditional validators or if we want to get our rewards after 2048 ETH for OXO2 validators, but rather select any value in the middle while still utilizing OXO2, which means that We will now be able to offer this convenience of automatic sweep for any validator that is in between 32EVE and to 2048EVE. And as mentioned in the last comment to our today's issue, a lot of ecosystem participants report that the main reason why they are not consolidating their validators to OXO2 is the liquidity problem because the step of 2K ETH is a pretty significant one. And even if you have more than 2K ETH, ⁓ but it's like equally precisely divisible by 2K, your amount of ETH is not precisely divisible by 2K, it would mean that for some of your validators, or if you have a lot of them, you will have to do manual partial withdrawals. if, for example, your business model does not ⁓ correspond to a system where you simply accumulate rewards and that's it, and when you need this extra liquidity on the execution layer. So this applies both to mid-sized ⁓ home and solo stakers. This applies to a professional operators who might have clients that are willing to have their EVE exclusively at a and only their validators and their amount of deposit that they have delegated to a professional operator is not a multiple of 2K. ⁓ So that's the motivation. And we believe that by introducing this very simple and small change ⁓ to Ethereum network, we would be able to significantly increase ⁓ the rate of consolidations or migration to OXO2 validators by offering such a a small but very useful thing. Now to a bit of a technical details on how this functionality actually works and why we believe that ⁓ it would not introduce any additional risks or concerns to the protocol. So first of all, the requests to set custom sweep threshold for the validators are made from validator with the draw credentials. following the same process as we already have for partial withdrawals and triggerable exits and consolidations. So no major changes here, the same architecture of the contract, the same fee inhibitor, and so on and so forth. Next, custom sweep threshold is set with a step of one ETH to match the effective balance increment so that you can only set your custom sweep threshold as a multiple of one ETH so that it would match ⁓ would potentially match the effective balance. Next thing, custom sweep threshold can only be set above the current validator balance to prevent usage of this feature for bypassing regular withdrawals. I'm sorry. ⁓ Which means that ⁓ you would not be able to set this threshold below your balance and then within the next sweep cycle get some of your ETH swept. You would only be able to set it above so that you will need to first accumulate enough balance and only after that you will start participating in the automatic sweep. Next thing, custom sweep threshold can be changed at any moment in time and any number of times for a given validator, ⁓ assuming that the new value that is provided for the custom sweep threshold is below the current validator balance. Custom sweep thresholds are proposed to be stored as a separate list in the beacon state and not in the validator structure in the validator object so that we do not surpass this limit of eight fields in the validator structure and so that it would not affect the way how we merkleized the state, particularly ⁓ validator objects. And the last thing is that custom sweep threshold can be set for any value starting from 33 ETH, which is ⁓ min activation balance plus 1 ETH, to allow for the maximum flexibility and encourage migration of any sized stakers starting from those only having, I don't know, like something like ⁓ 36 ETH, for example, and those who are not using this for ETH in staking at all. moving towards people who have, for example, two solo validators and might want to have this sweep threshold at 64 ETH and all the way up to a pretty large validators or homes takers who might have, say, 1.5K worth of ETH, but they still want to limit the amount of ETH that will be accumulated on their validator and get the rest swept. So that was a short description. Again, to wrap it up, ⁓ the main idea of this EAP is to make 0x02 ⁓ and potentially 0x03. But right now, I'm not sure, because when I was writing this EAP with ⁓ my co-contributors, 0x03 validators were still validators. Right now, they no longer are. So let's assume it applies to 0x02 validators. So the idea is to make 0x02 validators way more user-friendly. so that people can select at which threshold they want to automatically get their rewards. And we believe from our talks with ecosystem participants, with home stakers, with professional operators, that introducing this feature would significantly increase the rate of migration from ⁓ old OX01 validators to a new OX02 validators and would basically make OX02 validators suitable for everyone. who is running validators on Ethereum. Thank you. Cool. Thanks, yeah, that was a nice overview. ⁓ There was some discussion in the chat and yeah, I guess I'll say, yeah, we did originally consider something like this with the consolidations design in Petra. Petra got a little too big for its own sake and so it was not included, but yeah, in general. This is a nice idea ⁓ and yeah, something to consider. Yeah, and a quick comment on that. Yes, this idea indeed was considered back in the day when ⁓ Pectra was about to go live. And as far as I understood from reading all of the discussions, all of the memos and stuff like that, the major reason for not doing it in Pectra was the concerns regarding timelines, the amount of work required. and no significant support from participants like protocols and maybe other staking participants. However, right now it is apparent that this feature is needed. And if we will have time and space in Hegotah to implement this again relatively simple feature, it will be great to have it now because right now it is apparent that Without this feature, the rates of migration to OXO2 is pretty slow. Right now we are at below 20 % of stake being on OXO2 validators one year after Pectra. And in my opinion, this is unacceptable and we should do our best to increase the rates of migration. So even if with this limit, even if a solo staker with just two validators would transform... his two validators into one, it would already be a two times decrease in the number of validators. And as we can see from the data, most of the solo home stakers and again, professional operators are controlling way more than just 64 E, which means that we would definitely see a major reduction in the number of validators. And as far as I concerned, this is exactly the goal we are trying to achieve so that we can enable fast finality. and many more other features. Yeah, thanks for the intro. And again, we will get deeper into the Noted Liner EIP set ⁓ in the future. But yeah, this is a good one to have on the radar. We are time. So I think we go ahead and wrap up for today. Thank you, everyone. I'll see you next time.