You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
In #2010, we addressed a bug (raised in #2009) in the EVM where instantiating the VM and then calling vm.runTx with no opts.caller and opts.to specified to undefined (i.e. a transaction that creates a contract) resulted in an error. The heart of the issue is that in the EVM, when runCall is called with no caller specified in the runCallOpts, the caller defaults to Address.zero which is an unused address in the VM state. When you couple this with a CREATE message, the error was occuring when the new contract address was generated in evm._generateAddress since an input to the new address is the pre-execution nonce of the calling account (which the EVM assumes to be caller.nonce - 1 since historically the nonce was always incremented prior to running the transaction in vm.runTx based on the logic that a mined/executed transaction always increases the account's nonce, even if the transaction itself reverts). We made various attempts to bring the step of incrementing the nonce from the vm to the evm and have it occur inside runCall since the logic in generateAddress of subtracting 1 from the account nonce seems slightly odd but this proved difficult and generated a variety of new bugs that required increasingly complex logic.
Instead, #2010 opted to fix this by just setting a "more sensible default" if no caller is provided in the opts provided to evm.runCall. The approach is that when we are defaulting the caller to Address.zero, we also increment the nonce to 1 so that any future subsidiary calls to generateAddress result in a correct nonce input (i.e. nonce of 0 for Address.zero).
This leaves open the potential issue that a user could pass a caller address in the runCallOpts that is an unused account in a CREATE message and the same error still occurrs. Therefore, the question is, should we expect the state of a calling account in the EVM to represent the "pre-execution" or "post-execution" state of the account? Today, we are subtracting the balance and incrementing the nonce in the VM prior to running anything in the EVM. Our intent in having the EVM be a standalone package was for it to be a pure execution environmnet which implies to me that we should expect the developer to provide the correct Ethereum state for whatever message call they are seeking to have the EVM execute. Since account nonce and balance adjustments for baseFee are handled outside of message execution (i.e. should occur regardless of any execution result), it seems to me that we should leave it to the developer to correctly condition the account prior to a message call being passed to the EVM.
An alternative proposal is to add a transactionContext flag to the runCallOpts and move nonce incrementing into the EVM and then deal with the various potential issues that arise around ensuring the incremented nonce is preserved in the state and not reverted if a message call reverts inside of runCall.
So, we should decide if we like the current approach or else prefer the alternative (or some other option) and if we like the current approach
clearly document the expectations for managing account state outside of the EVM and perhaps improve the error messaging if the calling account is unused in the VM state
or else
code a better solution
The text was updated successfully, but these errors were encountered:
I think that the state in EVM should be pre-execution. This means that the complete state of the EVM is pre-execution: before execution we subtract the balance from the calling account, so this is already right. However, due to this "nonce - 1" bug in EVM we want to update the nonce of the calling account after calling into EVM into runTx. This ensures that for all accounts we call from, this "nonce - 1" bug does not happen. #2010 only fixes the bug if the calling account is undefined, if it is set and the user does not update the account nonce to at least 1 prior to executing the call (the CREATE in this case, since to is undefined) then it will always fail. This thus introduces an edge case (which I do not like).
If we update the nonce after runCall, this ensures that the balance and also the nonce of the account are right, since the execution and thus all state changes have failed except that we updated the calling account balance and the nonce.
It would be nice if we can remove this nonce - 1 thing altogether in EVM (since it looks weird), but that means we should also update the nonce in interpreter.ts after doing runCall: this is not possible, since if you recursively try to CREATE from the same contract the nonce always stays the same and thus you try to create a contract at the same address each time (which is not right).
If we do update the nonce after the runCall in runTx, as in #2054, this does introduce a tiny edge case to the EVM though, since if you insert an opts.message which has depth nonzero, then we have to ensure the user updates the account to whatever nonce they want, plus 1.
I think if we document this clearly, it is fine. Most people will want to execute code or create at depth 0. Only if you want to simulate contracts calling other contracts, you want to set the depth, and then also if the current contract at that states tries to CREATE something, then thus the nonce should be updated to a nonce which is the nonce we want plus 1. (If the nonce is zero, it thus always fails). But, this is a big edge case and if you want to insert these kind of messages this would need an end-user which is well aware of EVM, so if we document this clearly it should be fine.
In #2010, we addressed a bug (raised in #2009) in the EVM where instantiating the VM and then calling
vm.runTx
with noopts.caller
andopts.to
specified toundefined
(i.e. a transaction that creates a contract) resulted in an error. The heart of the issue is that in the EVM, whenrunCall
is called with nocaller
specified in therunCallOpts
, the caller defaults toAddress.zero
which is an unused address in the VM state. When you couple this with aCREATE
message, the error was occuring when the new contract address was generated inevm._generateAddress
since an input to the new address is the pre-execution nonce of the calling account (which the EVM assumes to be caller.nonce - 1 since historically the nonce was always incremented prior to running the transaction invm.runTx
based on the logic that a mined/executed transaction always increases the account's nonce, even if the transaction itself reverts). We made various attempts to bring the step of incrementing the nonce from thevm
to theevm
and have it occur insiderunCall
since the logic ingenerateAddress
of subtracting 1 from the account nonce seems slightly odd but this proved difficult and generated a variety of new bugs that required increasingly complex logic.Instead, #2010 opted to fix this by just setting a "more sensible default" if no
caller
is provided in the opts provided toevm.runCall
. The approach is that when we are defaulting the caller toAddress.zero
, we also increment the nonce to 1 so that any future subsidiary calls togenerateAddress
result in a correct nonce input (i.e. nonce of 0 forAddress.zero
).This leaves open the potential issue that a user could pass a
caller
address in therunCallOpts
that is an unused account in a CREATE message and the same error still occurrs. Therefore, the question is, should we expect the state of a calling account in the EVM to represent the "pre-execution" or "post-execution" state of the account? Today, we are subtracting the balance and incrementing the nonce in the VM prior to running anything in the EVM. Our intent in having the EVM be a standalone package was for it to be a pure execution environmnet which implies to me that we should expect the developer to provide the correct Ethereum state for whatever message call they are seeking to have the EVM execute. Since account nonce and balance adjustments for baseFee are handled outside of message execution (i.e. should occur regardless of any execution result), it seems to me that we should leave it to the developer to correctly condition the account prior to a message call being passed to the EVM.An alternative proposal is to add a
transactionContext
flag to therunCallOpts
and move nonce incrementing into the EVM and then deal with the various potential issues that arise around ensuring the incremented nonce is preserved in the state and not reverted if a message call reverts inside ofrunCall
.So, we should decide if we like the current approach or else prefer the alternative (or some other option) and if we like the current approach
or else
The text was updated successfully, but these errors were encountered: