This pull request delivers the new version of the state history, where
the raw storage key is used instead of the hash.
Before the cancun fork, it's supported by protocol to destruct a
specific account and therefore, all the storage slot owned by it should
be wiped in the same transition.
Technically, storage wiping should be performed through storage
iteration, and only the storage key hash will be available for traversal
if the state snapshot is not available. Therefore, the storage key hash
is chosen as the identifier in the old version state history.
Fortunately, account self-destruction has been deprecated by the
protocol since the Cancun fork, and there are no empty accounts eligible
for deletion under EIP-158. Therefore, we can conclude that no storage
wiping should occur after the Cancun fork. In this case, it makes no
sense to keep using hash.
Besides, another big reason for making this change is the current format
state history is unusable if verkle is activated. Verkle tree has a
different key derivation scheme (merkle uses keccak256), the preimage of
key hash must be provided in order to make verkle rollback functional.
This pull request is a prerequisite for landing verkle.
Additionally, the raw storage key is more human-friendly for those who
want to manually check the history, even though Solidity already
performs some hashing to derive the storage location.
---
This pull request doesn't bump the database version, as I believe the
database should still be compatible if users degrade from the new geth
version to old one, the only side effect is the persistent new version
state history will be unusable.
---------
Co-authored-by: Zsolt Felfoldi <zsfelfoldi@gmail.com>
// accountDelete represents an operation for deleting an Ethereum account.
// accountDelete represents an operation for deleting an Ethereum account.
typeaccountDeletestruct{
typeaccountDeletestruct{
addresscommon.Address// address is the unique account identifier
addresscommon.Address// address is the unique account identifier
origin[]byte// origin is the original value of account data in slim-RLP encoding.
origin[]byte// origin is the original value of account data in slim-RLP encoding.
storagesmap[common.Hash][]byte// storages stores mutated slots, the value should be nil.
storagesOriginmap[common.Hash][]byte// storagesOrigin stores the original values of mutated slots in prefix-zero-trimmed RLP format.
// storages stores mutated slots, the value should be nil.
storagesmap[common.Hash][]byte
// storagesOrigin stores the original values of mutated slots in
// prefix-zero-trimmed RLP format. The map key refers to the **HASH**
// of the raw storage slot key.
storagesOriginmap[common.Hash][]byte
}
}
// accountUpdate represents an operation for updating an Ethereum account.
// accountUpdate represents an operation for updating an Ethereum account.
typeaccountUpdatestruct{
typeaccountUpdatestruct{
addresscommon.Address// address is the unique account identifier
addresscommon.Address// address is the unique account identifier
data[]byte// data is the slim-RLP encoded account data.
data[]byte// data is the slim-RLP encoded account data.
origin[]byte// origin is the original value of account data in slim-RLP encoding.
origin[]byte// origin is the original value of account data in slim-RLP encoding.
code*contractCode// code represents mutated contract code; nil means it's not modified.
code*contractCode// code represents mutated contract code; nil means it's not modified.
storagesmap[common.Hash][]byte// storages stores mutated slots in prefix-zero-trimmed RLP format.
storagesmap[common.Hash][]byte// storages stores mutated slots in prefix-zero-trimmed RLP format.
storagesOriginmap[common.Hash][]byte// storagesOrigin stores the original values of mutated slots in prefix-zero-trimmed RLP format.
// storagesOriginByKey and storagesOriginByHash both store the original values
// of mutated slots in prefix-zero-trimmed RLP format. The difference is that
// storagesOriginByKey uses the **raw** storage slot key as the map ID, while
// storagesOriginByHash uses the **hash** of the storage slot key instead.
storagesOriginByKeymap[common.Hash][]byte
storagesOriginByHashmap[common.Hash][]byte
}
}
// stateUpdate represents the difference between two states resulting from state
// stateUpdate represents the difference between two states resulting from state
// execution. It contains information about mutated contract codes, accounts,
// execution. It contains information about mutated contract codes, accounts,
// and storage slots, along with their original values.
// and storage slots, along with their original values.
typestateUpdatestruct{
typestateUpdatestruct{
originRootcommon.Hash// hash of the state before applying mutation
originRootcommon.Hash// hash of the state before applying mutation
rootcommon.Hash// hash of the state after applying mutation
rootcommon.Hash// hash of the state after applying mutation
accountsmap[common.Hash][]byte// accounts stores mutated accounts in 'slim RLP' encoding
accountsmap[common.Hash][]byte// accounts stores mutated accounts in 'slim RLP' encoding
accountsOriginmap[common.Address][]byte// accountsOrigin stores the original values of mutated accounts in 'slim RLP' encoding
accountsOriginmap[common.Address][]byte// accountsOrigin stores the original values of mutated accounts in 'slim RLP' encoding
storagesmap[common.Hash]map[common.Hash][]byte// storages stores mutated slots in 'prefix-zero-trimmed' RLP format
storagesOriginmap[common.Address]map[common.Hash][]byte// storagesOrigin stores the original values of mutated slots in 'prefix-zero-trimmed' RLP format
// storages stores mutated slots in 'prefix-zero-trimmed' RLP format.
codesmap[common.Address]contractCode// codes contains the set of dirty codes
// The value is keyed by account hash and **storage slot key hash**.
nodes*trienode.MergedNodeSet// Aggregated dirty nodes caused by state changes
storagesmap[common.Hash]map[common.Hash][]byte
// storagesOrigin stores the original values of mutated slots in
// 'prefix-zero-trimmed' RLP format.
// (a) the value is keyed by account hash and **storage slot key** if rawStorageKey is true;
// (b) the value is keyed by account hash and **storage slot key hash** if rawStorageKey is false;
accountListSorted[]common.Hash// List of account for iteration. If it exists, it's sorted, otherwise it's nil
accountListSorted[]common.Hash// List of account for iteration. If it exists, it's sorted, otherwise it's nil
storageListSortedmap[common.Hash][]common.Hash// List of storage slots for iterated retrievals, one per account. Any existing lists are sorted if non-nil
storageListSortedmap[common.Hash][]common.Hash// List of storage slots for iterated retrievals, one per account. Any existing lists are sorted if non-nil
rawStorageKeybool// indicates whether the storage set uses the raw slot key or the hash
// Lock for guarding the two lists above. These lists might be accessed
// Lock for guarding the two lists above. These lists might be accessed
// concurrently and lock protection is essential to avoid concurrent
// concurrently and lock protection is essential to avoid concurrent
// slice or map read/write.
// slice or map read/write.
@ -72,7 +74,7 @@ type stateSet struct {
}
}
// newStates constructs the state set with the provided account and storage data.
// newStates constructs the state set with the provided account and storage data.