Hello @jochem-brouwer
Thank you for this review. It touches a number of very important topics that should be thoroughly discussed, so I will go over all of them and highlight the choices we have at each step.
- Transaction diff assertions vs. batch diff assertions
I agree that being able to define a POST_TX assertion frame per atomic batch is a powerful feature and I would gladly add it. It is not currently in the EIP because I am worried that it would add some complexity to the EIP while it is criticised for being too complex.
Technically, the main question here is which state does the POST_TX assertion should see as the ‘original’ one.
If a POST_TX frame is scoped to an atomic batch, the ‘original’ value it sees must be the state outcome of a previous batch - and not the state the user could observe before signing a transaction.
(For example, pre-tx state is 0, batch A sets it to 1, batch B sets it to 2, a POST_TX frame from batch B sees ‘before’ as 1 and ‘after’ as 2, but the user saw 0 before the transaction.)
I am concerned this might become pretty hard for the wallets to track.
We will also need to be careful with what reverting an atomic batch does with the outcome state - the transaction would probably still need a combined, transaction-scoped assertion frame to make sure the aggregate state of all executed and reverted atomic batches does not lead to an invalid state.
- Explicit diff snapshots
Regarding the “signers who care about specific parts of the transaction”, it is actually pretty easy to express the “X happened, but I don’t care if Y happened” rule with the EVM assertion as long as X & Y are clearly understood and scoped - the assertion can just ignore everything that is not X.
But I am wondering if these cases could benefit from POST_TX frames scoped to explicit snapshots, instead of an atomic batch and an explicit split between X and Y. The verifying contract could specify where the part that interests them begins, and then observe only the relevant diffs with their POST_TX, and we could even have multiple snapshots running simultaneously for different scopes. This all can be achieved with a relatively simple addition to the opcodes, but it would be great to hear from wallet & paymaster developers first.
- Following BAL shape vs. defining a custom assertion-specific structure
The BALs contain the majority of parameters that we might want to expose for Transaction Assertions; however, they were never explicitly designed to serve this use case, and are missing things we might need - “before” values, events, codehashes, gas charges, etc. So by attaching the assertions opcodes API to the shape defined by BALs, we make an implicit decision to abandon everything that cannot be read from the BAL - regardless of how useful it might be for Transaction Assertions.
Alternatively, we could take into account the added cost of tracking and make sure we don’t cause any performance problems for any client. For what it’s worth, the current EVM already requires tracking the ‘original’ value of each modified storage slot before calculating the fair gas cost of the SSTORE according to EIP-2200 etc., so there should not be much overhead in exposing these values back to the EVM. The same likely applies to balances, nonces, and code, as EVM clients are probably already retaining their pre-transaction values via the ordinary call-revert journal, since a transaction/batch is always “revertible” back to the original state. All information we need is out there, even if it lives outside of the BAL shape and is not easily accessible, and if it is useful for assertions - hiding it should be an explicit decision for the Transaction Assertions EIP.
- A special
SENDERframe vs. a newPOST_TXframe
One reason for introducing the POST_TX frame was to allow the revert caused by a violated assertion to take effect across the entire transaction, ignoring the lines between atomic batches. This way we provide users with a bulletproof guarantee - if they see a POST_TX frame in their wallet, it’s rules are enforced.
With a simple SENDER frame, this is only possible for a frame transaction with a single atomic batch, and users must be aware of that - which is a UX issue in my opinion.
Another reason for a POST_TX frame was to ensure there is no mechanism for an assertion script to be used to leak a ‘private’ contract state into the current transaction’s execution - which could be used to violate other contracts’ security assumptions, potentially extract aggressive MEV, create entangled state and so on.
We can certainly achieve a similar set of rules with a SENDER frame and a specific canonical “verify contract”, but if we ever allow an arbitrary contract to access the diff opcodes, it may be challenging to restrict a SENDER frame.
Also, I have been following some of the recent discussions around EIP-8141 and I became convinced that the “guarantor” frames might be the common ground between an explicit POST_TX and an unrestricted TXDIFF opcode.
- Clarity and fixes
Thanks for brigning those up - I will go over the specification again to make sure to address those!
I will be happy to hear you feedback on these topics and would be happy to discuss it in detail. Thanks again for the help!