feat(cheatcodes
): recordAccountAccesses
and recordStorageAccesses
cheatcodes
#6087
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Motivation
-- Original Context Below --
Currently, it is not possible to get an ordered list of account or storage accesses made by the EVM during test execution.
For account accesses, there are cheats such as
vm.expectCall
, but this requires knowing the exact destination and parameters of calls ahead of time.For storage accesses, there are the
vm.record
/vm.accesses
cheats, but these are not enumerated or ordered overall, and since it is a mapping, it require knowing which accounts exactly you'd like to check storage accesses for (this is made easier by the new mapping cheatcodes).Further, it is useful to know the context of account and storage accesses, in particular:
For accounts
For storage
This would allow for things like manual gas accounting due to accounts and storage slots marked as "warm" during test setup during execution.
Solution
This PR adds the following cheatcodes and structs:
For accounts
Internally, Forge maintains a
Vec<Vec<RecordedAccountAccess>>
. The vector is 2-dimensional to allow for grouping account accesses by relative call depth, so they can be updated with the eventual failure status of that callframe before being merged with the previousVec
to preserve absolute ordering.On each call or create, a single-element
Vec<RecordedAccountAccess>
is appended to the state with the context of theCALL
orCREATE
opcode.On
call_end
orcreate_end
, the lastVec<RecordedAccountAccess>
ispop()
'd off and the accesses therein are updated with the failure status of theCREATE
orCALL
opcode. ThisVec<RecordedAccountAccess>
is then merged with the previous in theVec<Vec<...>>
, or else re-appended to the emptyVec<Vec<...>>
.For storage
Internally, Forge maintains a
Vec<Vec<RecordedStorageAccess>>
. The vector is 2-dimensional to allow for grouping storage accesses by relative call depth, so they can be updated with the eventual failure status of that callframe before being merged with the previousVec
to preserve absolute ordering.On each
SLOAD
orSSTORE
, aRecordedStorageAccess
is appended to the lastVec<RecordedStorageAccess>
, or else appended as a single-elementVec
.On
call_end
orcreate_end
, the lastVec<RecordedStorageAccess>
ispop()
'd off and the accesses therein are updated with the failure status of theCREATE
orCALL
opcode. ThisVec<RecordedStorageAccess>
is then merged with the previous in theVec<Vec<...>>
, or else re-appended to the emptyVec<Vec<...>>
Reasoning
Ordering is important when manually calculating account or storage-slot "warm-ness," as is the eventual reverted status of a callframe in which an account or storage access happened.
Opcodes have different costs depending on whether or not a storage slot is "warm," "cold," or going from zero-to-non-zero value, or when sending value to an empty/uninitialized account. On an even lower-level, the EVM does not mark "cold" accounts or slots as "warm" if the context from which they were accessed reverts (with the exception of the counterfactual create address).
Thus, context like
previousValue
,newValue
,isCreate
,initialized
, andreverted
are important for understanding and evaluating the (hypothetical) behavior of the EVM.