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
During test and script execution, it is currently not possible to get an ordered list of account/storage accesses. For account accesses, cheats such as vm.expectCall requires knowing the exact destination and parameters of calls ahead of time. For storage, vm.record and vm.accesses cheats are not enumerated or ordered. It also requires knowing ahead of time which accounts to check for storage accesses.
Feature Proposal
Introduce a new vm.recordStateDiff() cheatcode that records all account and storage accesses in an ordered, chronological (for scripts, parallelized testing may be more tricky).
struct AccountAccess {
address account;
bool isCreate;
bool initialized;
uint64 prevNonce;
uint64 nonce;
uint256 prevValue;
uint256 value;
bytes prevCode;
bytes code;
bytes data;
bool reverted;
StorageAccess[] storageAccesses;
}
struct StorageAccess {
address account;
bytes32 slot;
bool isWrite;
bytes32 previousValue;
bytes32 newValue;
bool reverted;
}
/// Record all account and storage accesses./// CREATE or CALL opcodes produce an AccountAccess./// Storage accesses produce a StorageAccess object.function recordStateDiff() external;
// Returns an ordered array of all account and storage accessesfunction getStateDiff() externalreturns (AccountAccess[] memory);
Implementation
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 previous Vec to preserve absolute ordering.
On each call or create, a single-element Vec<RecordedAccountAccess> is appended to the state with the context of the CALL or CREATE opcode.
On call_end or create_end, the last Vec<RecordedAccountAccess> is pop()'d off and the accesses therein are updated with the failure status of the CREATE or CALL opcode. This Vec<RecordedAccountAccess> is then merged with the previous in the Vec<Vec<...>>, or else re-appended to the empty Vec<Vec<...>>.
Within RecordedAccountAccess, there is a Vec<RecordedStorageAccess> containing a list of storage accesses pertaining to that account. On each SLOAD or SSTORE, a RecordedStorageAccess is appended to the last RecordedAccountAccess.
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.
Thus, context like previousValue, newValue, isCreate, initialized, and reverted are important for understanding and evaluating the (hypothetical) behavior of the EVM.
The text was updated successfully, but these errors were encountered:
Should the structs also include a forkId or chainId? That way if you record, switch between forks, then stop recording, you know which chain these occurred on. Other recording cheats do not currently have this, but it might be valuable to include anyway
Component
Forge
Describe the feature you would like
Background Context
During test and script execution, it is currently not possible to get an ordered list of account/storage accesses. For account accesses, cheats such as
vm.expectCall
requires knowing the exact destination and parameters of calls ahead of time. For storage,vm.record
andvm.accesses
cheats are not enumerated or ordered. It also requires knowing ahead of time which accounts to check for storage accesses.Feature Proposal
Introduce a new
vm.recordStateDiff()
cheatcode that records all account and storage accesses in an ordered, chronological (for scripts, parallelized testing may be more tricky).Implementation
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<...>>
.Within
RecordedAccountAccess
, there is aVec<RecordedStorageAccess>
containing a list of storage accesses pertaining to that account. On eachSLOAD
orSSTORE
, aRecordedStorageAccess
is appended to the lastRecordedAccountAccess
.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.The text was updated successfully, but these errors were encountered: