2023-07-11 08:43:23 -05:00
|
|
|
// Copyright 2023 The go-ethereum Authors
|
|
|
|
// This file is part of the go-ethereum library.
|
|
|
|
//
|
|
|
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
|
|
// it under the terms of the GNU Lesser General Public License as published by
|
|
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
|
|
// (at your option) any later version.
|
|
|
|
//
|
|
|
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
// GNU Lesser General Public License for more details.
|
|
|
|
//
|
|
|
|
// You should have received a copy of the GNU Lesser General Public License
|
|
|
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>
|
|
|
|
|
|
|
|
package state
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"encoding/binary"
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
core, triedb: remove destruct flag in state snapshot (#30752)
This pull request removes the destruct flag from the state snapshot to
simplify the code.
Previously, this flag indicated that an account was removed during a
state transition, making all associated storage slots inaccessible.
Because storage deletion can involve a large number of slots, the actual
deletion is deferred until the end of the process, where it is handled
in batches.
With the deprecation of self-destruct in the Cancun fork, storage
deletions are no longer expected. Historically, the largest storage
deletion event in Ethereum was around 15 megabytes—manageable in memory.
In this pull request, the single destruct flag is replaced by a set of
deletion markers for individual storage slots. Each deleted storage slot
will now appear in the Storage set with a nil value.
This change will simplify a lot logics, such as storage accessing,
storage flushing, storage iteration and so on.
2024-11-22 02:55:43 -06:00
|
|
|
"maps"
|
2023-07-11 08:43:23 -05:00
|
|
|
"math"
|
|
|
|
"math/rand"
|
|
|
|
"reflect"
|
|
|
|
"strings"
|
|
|
|
"testing"
|
|
|
|
"testing/quick"
|
|
|
|
|
|
|
|
"github.com/ethereum/go-ethereum/common"
|
|
|
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
2023-08-26 03:13:22 -05:00
|
|
|
"github.com/ethereum/go-ethereum/core/state/snapshot"
|
2024-03-22 12:53:53 -05:00
|
|
|
"github.com/ethereum/go-ethereum/core/tracing"
|
2023-07-11 08:43:23 -05:00
|
|
|
"github.com/ethereum/go-ethereum/core/types"
|
2023-07-31 07:07:51 -05:00
|
|
|
"github.com/ethereum/go-ethereum/crypto"
|
2023-07-11 08:43:23 -05:00
|
|
|
"github.com/ethereum/go-ethereum/rlp"
|
|
|
|
"github.com/ethereum/go-ethereum/trie"
|
2024-02-13 07:49:53 -06:00
|
|
|
"github.com/ethereum/go-ethereum/triedb"
|
|
|
|
"github.com/ethereum/go-ethereum/triedb/pathdb"
|
2024-01-23 07:51:58 -06:00
|
|
|
"github.com/holiman/uint256"
|
2023-07-11 08:43:23 -05:00
|
|
|
)
|
|
|
|
|
|
|
|
// A stateTest checks that the state changes are correctly captured. Instances
|
|
|
|
// of this test with pseudorandom content are created by Generate.
|
|
|
|
//
|
|
|
|
// The test works as follows:
|
|
|
|
//
|
|
|
|
// A list of states are created by applying actions. The state changes between
|
|
|
|
// each state instance are tracked and be verified.
|
|
|
|
type stateTest struct {
|
|
|
|
addrs []common.Address // all account addresses
|
|
|
|
actions [][]testAction // modifications to the state, grouped by block
|
|
|
|
chunk int // The number of actions per chunk
|
|
|
|
err error // failure details are reported through this field
|
|
|
|
}
|
|
|
|
|
|
|
|
// newStateTestAction creates a random action that changes state.
|
|
|
|
func newStateTestAction(addr common.Address, r *rand.Rand, index int) testAction {
|
|
|
|
actions := []testAction{
|
|
|
|
{
|
|
|
|
name: "SetBalance",
|
|
|
|
fn: func(a testAction, s *StateDB) {
|
2024-03-22 12:53:53 -05:00
|
|
|
s.SetBalance(addr, uint256.NewInt(uint64(a.args[0])), tracing.BalanceChangeUnspecified)
|
2023-07-11 08:43:23 -05:00
|
|
|
},
|
|
|
|
args: make([]int64, 1),
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "SetNonce",
|
|
|
|
fn: func(a testAction, s *StateDB) {
|
|
|
|
s.SetNonce(addr, uint64(a.args[0]))
|
|
|
|
},
|
|
|
|
args: make([]int64, 1),
|
|
|
|
},
|
|
|
|
{
|
core/state: semantic journalling (part 1) (#28880)
This is a follow-up to #29520, and a preparatory PR to a more thorough
change in the journalling system.
### API methods instead of `append` operations
This PR hides the journal-implementation details away, so that the
statedb invokes methods like `JournalCreate`, instead of explicitly
appending journal-events in a list. This means that it's up to the
journal whether to implement it as a sequence of events or
aggregate/merge events.
### Snapshot-management inside the journal
This PR also makes it so that management of valid snapshots is moved
inside the journal, exposed via the methods `Snapshot() int` and
`RevertToSnapshot(revid int, s *StateDB)`.
### SetCode
JournalSetCode journals the setting of code: it is implicit that the
previous values were "no code" and emptyCodeHash. Therefore, we can
simplify the setCode journal.
### Selfdestruct
The self-destruct journalling is a bit strange: we allow the
selfdestruct operation to be journalled several times. This makes it so
that we also are forced to store whether the account was already
destructed.
What we can do instead, is to only journal the first destruction, and
after that only journal balance-changes, but not journal the
selfdestruct itself.
This simplifies the journalling, so that internals about state
management does not leak into the journal-API.
### Preimages
Preimages were, for some reason, integrated into the journal management,
despite not being a consensus-critical data structure. This PR undoes
that.
---------
Co-authored-by: Gary Rong <garyrong0905@gmail.com>
2024-08-28 01:18:23 -05:00
|
|
|
name: "SetStorage",
|
2023-07-11 08:43:23 -05:00
|
|
|
fn: func(a testAction, s *StateDB) {
|
|
|
|
var key, val common.Hash
|
|
|
|
binary.BigEndian.PutUint16(key[:], uint16(a.args[0]))
|
|
|
|
binary.BigEndian.PutUint16(val[:], uint16(a.args[1]))
|
|
|
|
s.SetState(addr, key, val)
|
|
|
|
},
|
|
|
|
args: make([]int64, 2),
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "SetCode",
|
|
|
|
fn: func(a testAction, s *StateDB) {
|
|
|
|
code := make([]byte, 16)
|
|
|
|
binary.BigEndian.PutUint64(code, uint64(a.args[0]))
|
|
|
|
binary.BigEndian.PutUint64(code[8:], uint64(a.args[1]))
|
|
|
|
s.SetCode(addr, code)
|
|
|
|
},
|
|
|
|
args: make([]int64, 2),
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "CreateAccount",
|
|
|
|
fn: func(a testAction, s *StateDB) {
|
2024-04-24 04:59:06 -05:00
|
|
|
if !s.Exist(addr) {
|
|
|
|
s.CreateAccount(addr)
|
|
|
|
}
|
2023-07-11 08:43:23 -05:00
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
2023-07-15 09:35:30 -05:00
|
|
|
name: "Selfdestruct",
|
2023-07-11 08:43:23 -05:00
|
|
|
fn: func(a testAction, s *StateDB) {
|
2023-07-15 09:35:30 -05:00
|
|
|
s.SelfDestruct(addr)
|
2023-07-11 08:43:23 -05:00
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
var nonRandom = index != -1
|
|
|
|
if index == -1 {
|
|
|
|
index = r.Intn(len(actions))
|
|
|
|
}
|
|
|
|
action := actions[index]
|
|
|
|
var names []string
|
|
|
|
if !action.noAddr {
|
|
|
|
names = append(names, addr.Hex())
|
|
|
|
}
|
|
|
|
for i := range action.args {
|
|
|
|
if nonRandom {
|
|
|
|
action.args[i] = rand.Int63n(10000) + 1 // set balance to non-zero
|
|
|
|
} else {
|
|
|
|
action.args[i] = rand.Int63n(10000)
|
|
|
|
}
|
|
|
|
names = append(names, fmt.Sprint(action.args[i]))
|
|
|
|
}
|
|
|
|
action.name += " " + strings.Join(names, ", ")
|
|
|
|
return action
|
|
|
|
}
|
|
|
|
|
|
|
|
// Generate returns a new snapshot test of the given size. All randomness is
|
|
|
|
// derived from r.
|
|
|
|
func (*stateTest) Generate(r *rand.Rand, size int) reflect.Value {
|
|
|
|
addrs := make([]common.Address, 5)
|
|
|
|
for i := range addrs {
|
|
|
|
addrs[i][0] = byte(i)
|
|
|
|
}
|
|
|
|
actions := make([][]testAction, rand.Intn(5)+1)
|
|
|
|
|
|
|
|
for i := 0; i < len(actions); i++ {
|
|
|
|
actions[i] = make([]testAction, size)
|
|
|
|
for j := range actions[i] {
|
|
|
|
if j == 0 {
|
|
|
|
// Always include a set balance action to make sure
|
|
|
|
// the state changes are not empty.
|
|
|
|
actions[i][j] = newStateTestAction(common.HexToAddress("0xdeadbeef"), r, 0)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
actions[i][j] = newStateTestAction(addrs[r.Intn(len(addrs))], r, -1)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
chunk := int(math.Sqrt(float64(size)))
|
|
|
|
if size > 0 && chunk == 0 {
|
|
|
|
chunk = 1
|
|
|
|
}
|
|
|
|
return reflect.ValueOf(&stateTest{
|
|
|
|
addrs: addrs,
|
|
|
|
actions: actions,
|
|
|
|
chunk: chunk,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func (test *stateTest) String() string {
|
|
|
|
out := new(bytes.Buffer)
|
|
|
|
for i, actions := range test.actions {
|
|
|
|
fmt.Fprintf(out, "---- block %d ----\n", i)
|
|
|
|
for j, action := range actions {
|
|
|
|
if j%test.chunk == 0 {
|
|
|
|
fmt.Fprintf(out, "---- transaction %d ----\n", j/test.chunk)
|
|
|
|
}
|
|
|
|
fmt.Fprintf(out, "%4d: %s\n", j%test.chunk, action.name)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return out.String()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (test *stateTest) run() bool {
|
|
|
|
var (
|
core, triedb: remove destruct flag in state snapshot (#30752)
This pull request removes the destruct flag from the state snapshot to
simplify the code.
Previously, this flag indicated that an account was removed during a
state transition, making all associated storage slots inaccessible.
Because storage deletion can involve a large number of slots, the actual
deletion is deferred until the end of the process, where it is handled
in batches.
With the deprecation of self-destruct in the Cancun fork, storage
deletions are no longer expected. Historically, the largest storage
deletion event in Ethereum was around 15 megabytes—manageable in memory.
In this pull request, the single destruct flag is replaced by a set of
deletion markers for individual storage slots. Each deleted storage slot
will now appear in the Storage set with a nil value.
This change will simplify a lot logics, such as storage accessing,
storage flushing, storage iteration and so on.
2024-11-22 02:55:43 -06:00
|
|
|
roots []common.Hash
|
|
|
|
accounts []map[common.Hash][]byte
|
|
|
|
accountOrigin []map[common.Address][]byte
|
|
|
|
storages []map[common.Hash]map[common.Hash][]byte
|
|
|
|
storageOrigin []map[common.Address]map[common.Hash][]byte
|
|
|
|
copyUpdate = func(update *stateUpdate) {
|
|
|
|
accounts = append(accounts, maps.Clone(update.accounts))
|
|
|
|
accountOrigin = append(accountOrigin, maps.Clone(update.accountsOrigin))
|
|
|
|
storages = append(storages, maps.Clone(update.storages))
|
|
|
|
storageOrigin = append(storageOrigin, maps.Clone(update.storagesOrigin))
|
2023-07-11 08:43:23 -05:00
|
|
|
}
|
|
|
|
disk = rawdb.NewMemoryDatabase()
|
2024-02-13 07:49:53 -06:00
|
|
|
tdb = triedb.NewDatabase(disk, &triedb.Config{PathDB: pathdb.Defaults})
|
2023-07-11 08:43:23 -05:00
|
|
|
byzantium = rand.Intn(2) == 0
|
|
|
|
)
|
2023-08-26 03:13:22 -05:00
|
|
|
defer disk.Close()
|
|
|
|
defer tdb.Close()
|
|
|
|
|
|
|
|
var snaps *snapshot.Tree
|
|
|
|
if rand.Intn(3) == 0 {
|
|
|
|
snaps, _ = snapshot.New(snapshot.Config{
|
|
|
|
CacheSize: 1,
|
|
|
|
Recovery: false,
|
|
|
|
NoBuild: false,
|
|
|
|
AsyncBuild: false,
|
|
|
|
}, disk, tdb, types.EmptyRootHash)
|
|
|
|
}
|
2023-07-11 08:43:23 -05:00
|
|
|
for i, actions := range test.actions {
|
|
|
|
root := types.EmptyRootHash
|
|
|
|
if i != 0 {
|
|
|
|
root = roots[len(roots)-1]
|
|
|
|
}
|
2024-09-05 05:10:47 -05:00
|
|
|
state, err := New(root, NewDatabase(tdb, snaps))
|
2023-07-11 08:43:23 -05:00
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
for i, action := range actions {
|
|
|
|
if i%test.chunk == 0 && i != 0 {
|
|
|
|
if byzantium {
|
|
|
|
state.Finalise(true) // call finalise at the transaction boundary
|
|
|
|
} else {
|
|
|
|
state.IntermediateRoot(true) // call intermediateRoot at the transaction boundary
|
|
|
|
}
|
|
|
|
}
|
|
|
|
action.fn(action, state)
|
|
|
|
}
|
|
|
|
if byzantium {
|
|
|
|
state.Finalise(true) // call finalise at the transaction boundary
|
|
|
|
} else {
|
|
|
|
state.IntermediateRoot(true) // call intermediateRoot at the transaction boundary
|
|
|
|
}
|
2024-06-03 06:17:12 -05:00
|
|
|
ret, err := state.commitAndFlush(0, true) // call commit at the block boundary
|
2023-07-11 08:43:23 -05:00
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
2024-06-03 06:17:12 -05:00
|
|
|
if ret.empty() {
|
|
|
|
return true
|
2023-07-11 08:43:23 -05:00
|
|
|
}
|
2024-06-03 06:17:12 -05:00
|
|
|
copyUpdate(ret)
|
|
|
|
roots = append(roots, ret.root)
|
2023-07-11 08:43:23 -05:00
|
|
|
}
|
|
|
|
for i := 0; i < len(test.actions); i++ {
|
|
|
|
root := types.EmptyRootHash
|
|
|
|
if i != 0 {
|
|
|
|
root = roots[i-1]
|
|
|
|
}
|
core, triedb: remove destruct flag in state snapshot (#30752)
This pull request removes the destruct flag from the state snapshot to
simplify the code.
Previously, this flag indicated that an account was removed during a
state transition, making all associated storage slots inaccessible.
Because storage deletion can involve a large number of slots, the actual
deletion is deferred until the end of the process, where it is handled
in batches.
With the deprecation of self-destruct in the Cancun fork, storage
deletions are no longer expected. Historically, the largest storage
deletion event in Ethereum was around 15 megabytes—manageable in memory.
In this pull request, the single destruct flag is replaced by a set of
deletion markers for individual storage slots. Each deleted storage slot
will now appear in the Storage set with a nil value.
This change will simplify a lot logics, such as storage accessing,
storage flushing, storage iteration and so on.
2024-11-22 02:55:43 -06:00
|
|
|
test.err = test.verify(root, roots[i], tdb, accounts[i], accountOrigin[i], storages[i], storageOrigin[i])
|
2023-07-11 08:43:23 -05:00
|
|
|
if test.err != nil {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
// verifyAccountCreation this function is called once the state diff says that
|
|
|
|
// specific account was not present. A serial of checks will be performed to
|
|
|
|
// ensure the state diff is correct, includes:
|
|
|
|
//
|
|
|
|
// - the account was indeed not present in trie
|
|
|
|
// - the account is present in new trie, nil->nil is regarded as invalid
|
|
|
|
// - the slots transition is correct
|
core, triedb: remove destruct flag in state snapshot (#30752)
This pull request removes the destruct flag from the state snapshot to
simplify the code.
Previously, this flag indicated that an account was removed during a
state transition, making all associated storage slots inaccessible.
Because storage deletion can involve a large number of slots, the actual
deletion is deferred until the end of the process, where it is handled
in batches.
With the deprecation of self-destruct in the Cancun fork, storage
deletions are no longer expected. Historically, the largest storage
deletion event in Ethereum was around 15 megabytes—manageable in memory.
In this pull request, the single destruct flag is replaced by a set of
deletion markers for individual storage slots. Each deleted storage slot
will now appear in the Storage set with a nil value.
This change will simplify a lot logics, such as storage accessing,
storage flushing, storage iteration and so on.
2024-11-22 02:55:43 -06:00
|
|
|
func (test *stateTest) verifyAccountCreation(next common.Hash, db *triedb.Database, otr, ntr *trie.Trie, addr common.Address, account []byte, storages map[common.Hash][]byte, storagesOrigin map[common.Hash][]byte) error {
|
2023-07-11 08:43:23 -05:00
|
|
|
// Verify account change
|
2023-07-31 07:07:51 -05:00
|
|
|
addrHash := crypto.Keccak256Hash(addr.Bytes())
|
2023-07-11 08:43:23 -05:00
|
|
|
oBlob, err := otr.Get(addrHash.Bytes())
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
nBlob, err := ntr.Get(addrHash.Bytes())
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if len(oBlob) != 0 {
|
|
|
|
return fmt.Errorf("unexpected account in old trie, %x", addrHash)
|
|
|
|
}
|
|
|
|
if len(nBlob) == 0 {
|
|
|
|
return fmt.Errorf("missing account in new trie, %x", addrHash)
|
|
|
|
}
|
core, triedb: remove destruct flag in state snapshot (#30752)
This pull request removes the destruct flag from the state snapshot to
simplify the code.
Previously, this flag indicated that an account was removed during a
state transition, making all associated storage slots inaccessible.
Because storage deletion can involve a large number of slots, the actual
deletion is deferred until the end of the process, where it is handled
in batches.
With the deprecation of self-destruct in the Cancun fork, storage
deletions are no longer expected. Historically, the largest storage
deletion event in Ethereum was around 15 megabytes—manageable in memory.
In this pull request, the single destruct flag is replaced by a set of
deletion markers for individual storage slots. Each deleted storage slot
will now appear in the Storage set with a nil value.
This change will simplify a lot logics, such as storage accessing,
storage flushing, storage iteration and so on.
2024-11-22 02:55:43 -06:00
|
|
|
full, err := types.FullAccountRLP(account)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if !bytes.Equal(nBlob, full) {
|
|
|
|
return fmt.Errorf("unexpected account data, want: %v, got: %v", full, nBlob)
|
|
|
|
}
|
2023-07-11 08:43:23 -05:00
|
|
|
|
|
|
|
// Verify storage changes
|
|
|
|
var nAcct types.StateAccount
|
|
|
|
if err := rlp.DecodeBytes(nBlob, &nAcct); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
// Account has no slot, empty slot set is expected
|
|
|
|
if nAcct.Root == types.EmptyRootHash {
|
core, triedb: remove destruct flag in state snapshot (#30752)
This pull request removes the destruct flag from the state snapshot to
simplify the code.
Previously, this flag indicated that an account was removed during a
state transition, making all associated storage slots inaccessible.
Because storage deletion can involve a large number of slots, the actual
deletion is deferred until the end of the process, where it is handled
in batches.
With the deprecation of self-destruct in the Cancun fork, storage
deletions are no longer expected. Historically, the largest storage
deletion event in Ethereum was around 15 megabytes—manageable in memory.
In this pull request, the single destruct flag is replaced by a set of
deletion markers for individual storage slots. Each deleted storage slot
will now appear in the Storage set with a nil value.
This change will simplify a lot logics, such as storage accessing,
storage flushing, storage iteration and so on.
2024-11-22 02:55:43 -06:00
|
|
|
if len(storagesOrigin) != 0 {
|
|
|
|
return fmt.Errorf("unexpected slot changes %x", addrHash)
|
|
|
|
}
|
|
|
|
if len(storages) != 0 {
|
2023-07-11 08:43:23 -05:00
|
|
|
return fmt.Errorf("unexpected slot changes %x", addrHash)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
// Account has slots, ensure all new slots are contained
|
|
|
|
st, err := trie.New(trie.StorageTrieID(next, addrHash, nAcct.Root), db)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
core, triedb: remove destruct flag in state snapshot (#30752)
This pull request removes the destruct flag from the state snapshot to
simplify the code.
Previously, this flag indicated that an account was removed during a
state transition, making all associated storage slots inaccessible.
Because storage deletion can involve a large number of slots, the actual
deletion is deferred until the end of the process, where it is handled
in batches.
With the deprecation of self-destruct in the Cancun fork, storage
deletions are no longer expected. Historically, the largest storage
deletion event in Ethereum was around 15 megabytes—manageable in memory.
In this pull request, the single destruct flag is replaced by a set of
deletion markers for individual storage slots. Each deleted storage slot
will now appear in the Storage set with a nil value.
This change will simplify a lot logics, such as storage accessing,
storage flushing, storage iteration and so on.
2024-11-22 02:55:43 -06:00
|
|
|
for key, val := range storagesOrigin {
|
|
|
|
if _, exist := storages[key]; !exist {
|
|
|
|
return errors.New("storage data is not found")
|
|
|
|
}
|
|
|
|
got, err := st.Get(key.Bytes())
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if !bytes.Equal(got, storages[key]) {
|
|
|
|
return fmt.Errorf("unexpected storage data, want: %v, got: %v", storages[key], got)
|
|
|
|
}
|
2023-07-11 08:43:23 -05:00
|
|
|
st.Update(key.Bytes(), val)
|
|
|
|
}
|
core, triedb: remove destruct flag in state snapshot (#30752)
This pull request removes the destruct flag from the state snapshot to
simplify the code.
Previously, this flag indicated that an account was removed during a
state transition, making all associated storage slots inaccessible.
Because storage deletion can involve a large number of slots, the actual
deletion is deferred until the end of the process, where it is handled
in batches.
With the deprecation of self-destruct in the Cancun fork, storage
deletions are no longer expected. Historically, the largest storage
deletion event in Ethereum was around 15 megabytes—manageable in memory.
In this pull request, the single destruct flag is replaced by a set of
deletion markers for individual storage slots. Each deleted storage slot
will now appear in the Storage set with a nil value.
This change will simplify a lot logics, such as storage accessing,
storage flushing, storage iteration and so on.
2024-11-22 02:55:43 -06:00
|
|
|
if len(storagesOrigin) != len(storages) {
|
|
|
|
return fmt.Errorf("extra storage found, want: %d, got: %d", len(storagesOrigin), len(storages))
|
|
|
|
}
|
2023-07-11 08:43:23 -05:00
|
|
|
if st.Hash() != types.EmptyRootHash {
|
|
|
|
return errors.New("invalid slot changes")
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// verifyAccountUpdate this function is called once the state diff says that
|
|
|
|
// specific account was present. A serial of checks will be performed to
|
|
|
|
// ensure the state diff is correct, includes:
|
|
|
|
//
|
|
|
|
// - the account was indeed present in trie
|
|
|
|
// - the account in old trie matches the provided value
|
|
|
|
// - the slots transition is correct
|
core, triedb: remove destruct flag in state snapshot (#30752)
This pull request removes the destruct flag from the state snapshot to
simplify the code.
Previously, this flag indicated that an account was removed during a
state transition, making all associated storage slots inaccessible.
Because storage deletion can involve a large number of slots, the actual
deletion is deferred until the end of the process, where it is handled
in batches.
With the deprecation of self-destruct in the Cancun fork, storage
deletions are no longer expected. Historically, the largest storage
deletion event in Ethereum was around 15 megabytes—manageable in memory.
In this pull request, the single destruct flag is replaced by a set of
deletion markers for individual storage slots. Each deleted storage slot
will now appear in the Storage set with a nil value.
This change will simplify a lot logics, such as storage accessing,
storage flushing, storage iteration and so on.
2024-11-22 02:55:43 -06:00
|
|
|
func (test *stateTest) verifyAccountUpdate(next common.Hash, db *triedb.Database, otr, ntr *trie.Trie, addr common.Address, account []byte, accountOrigin []byte, storages map[common.Hash][]byte, storageOrigin map[common.Hash][]byte) error {
|
2023-07-11 08:43:23 -05:00
|
|
|
// Verify account change
|
2023-07-31 07:07:51 -05:00
|
|
|
addrHash := crypto.Keccak256Hash(addr.Bytes())
|
2023-07-11 08:43:23 -05:00
|
|
|
oBlob, err := otr.Get(addrHash.Bytes())
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
nBlob, err := ntr.Get(addrHash.Bytes())
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if len(oBlob) == 0 {
|
|
|
|
return fmt.Errorf("missing account in old trie, %x", addrHash)
|
|
|
|
}
|
core, triedb: remove destruct flag in state snapshot (#30752)
This pull request removes the destruct flag from the state snapshot to
simplify the code.
Previously, this flag indicated that an account was removed during a
state transition, making all associated storage slots inaccessible.
Because storage deletion can involve a large number of slots, the actual
deletion is deferred until the end of the process, where it is handled
in batches.
With the deprecation of self-destruct in the Cancun fork, storage
deletions are no longer expected. Historically, the largest storage
deletion event in Ethereum was around 15 megabytes—manageable in memory.
In this pull request, the single destruct flag is replaced by a set of
deletion markers for individual storage slots. Each deleted storage slot
will now appear in the Storage set with a nil value.
This change will simplify a lot logics, such as storage accessing,
storage flushing, storage iteration and so on.
2024-11-22 02:55:43 -06:00
|
|
|
full, err := types.FullAccountRLP(accountOrigin)
|
2023-07-11 08:43:23 -05:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if !bytes.Equal(full, oBlob) {
|
|
|
|
return fmt.Errorf("account value is not matched, %x", addrHash)
|
|
|
|
}
|
core, triedb: remove destruct flag in state snapshot (#30752)
This pull request removes the destruct flag from the state snapshot to
simplify the code.
Previously, this flag indicated that an account was removed during a
state transition, making all associated storage slots inaccessible.
Because storage deletion can involve a large number of slots, the actual
deletion is deferred until the end of the process, where it is handled
in batches.
With the deprecation of self-destruct in the Cancun fork, storage
deletions are no longer expected. Historically, the largest storage
deletion event in Ethereum was around 15 megabytes—manageable in memory.
In this pull request, the single destruct flag is replaced by a set of
deletion markers for individual storage slots. Each deleted storage slot
will now appear in the Storage set with a nil value.
This change will simplify a lot logics, such as storage accessing,
storage flushing, storage iteration and so on.
2024-11-22 02:55:43 -06:00
|
|
|
if len(nBlob) == 0 {
|
|
|
|
if len(account) != 0 {
|
|
|
|
return errors.New("unexpected account data")
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
full, _ = types.FullAccountRLP(account)
|
|
|
|
if !bytes.Equal(full, nBlob) {
|
|
|
|
return fmt.Errorf("unexpected account data, %x, want %v, got: %v", addrHash, full, nBlob)
|
|
|
|
}
|
|
|
|
}
|
2023-07-11 08:43:23 -05:00
|
|
|
// Decode accounts
|
|
|
|
var (
|
|
|
|
oAcct types.StateAccount
|
|
|
|
nAcct types.StateAccount
|
|
|
|
nRoot common.Hash
|
|
|
|
)
|
|
|
|
if err := rlp.DecodeBytes(oBlob, &oAcct); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if len(nBlob) == 0 {
|
|
|
|
nRoot = types.EmptyRootHash
|
|
|
|
} else {
|
|
|
|
if err := rlp.DecodeBytes(nBlob, &nAcct); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
nRoot = nAcct.Root
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify storage
|
|
|
|
st, err := trie.New(trie.StorageTrieID(next, addrHash, nRoot), db)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
core, triedb: remove destruct flag in state snapshot (#30752)
This pull request removes the destruct flag from the state snapshot to
simplify the code.
Previously, this flag indicated that an account was removed during a
state transition, making all associated storage slots inaccessible.
Because storage deletion can involve a large number of slots, the actual
deletion is deferred until the end of the process, where it is handled
in batches.
With the deprecation of self-destruct in the Cancun fork, storage
deletions are no longer expected. Historically, the largest storage
deletion event in Ethereum was around 15 megabytes—manageable in memory.
In this pull request, the single destruct flag is replaced by a set of
deletion markers for individual storage slots. Each deleted storage slot
will now appear in the Storage set with a nil value.
This change will simplify a lot logics, such as storage accessing,
storage flushing, storage iteration and so on.
2024-11-22 02:55:43 -06:00
|
|
|
for key, val := range storageOrigin {
|
|
|
|
if _, exist := storages[key]; !exist {
|
|
|
|
return errors.New("storage data is not found")
|
|
|
|
}
|
|
|
|
got, err := st.Get(key.Bytes())
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if !bytes.Equal(got, storages[key]) {
|
|
|
|
return fmt.Errorf("unexpected storage data, want: %v, got: %v", storages[key], got)
|
|
|
|
}
|
2023-07-11 08:43:23 -05:00
|
|
|
st.Update(key.Bytes(), val)
|
|
|
|
}
|
core, triedb: remove destruct flag in state snapshot (#30752)
This pull request removes the destruct flag from the state snapshot to
simplify the code.
Previously, this flag indicated that an account was removed during a
state transition, making all associated storage slots inaccessible.
Because storage deletion can involve a large number of slots, the actual
deletion is deferred until the end of the process, where it is handled
in batches.
With the deprecation of self-destruct in the Cancun fork, storage
deletions are no longer expected. Historically, the largest storage
deletion event in Ethereum was around 15 megabytes—manageable in memory.
In this pull request, the single destruct flag is replaced by a set of
deletion markers for individual storage slots. Each deleted storage slot
will now appear in the Storage set with a nil value.
This change will simplify a lot logics, such as storage accessing,
storage flushing, storage iteration and so on.
2024-11-22 02:55:43 -06:00
|
|
|
if len(storageOrigin) != len(storages) {
|
|
|
|
return fmt.Errorf("extra storage found, want: %d, got: %d", len(storageOrigin), len(storages))
|
|
|
|
}
|
2023-07-11 08:43:23 -05:00
|
|
|
if st.Hash() != oAcct.Root {
|
|
|
|
return errors.New("invalid slot changes")
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
core, triedb: remove destruct flag in state snapshot (#30752)
This pull request removes the destruct flag from the state snapshot to
simplify the code.
Previously, this flag indicated that an account was removed during a
state transition, making all associated storage slots inaccessible.
Because storage deletion can involve a large number of slots, the actual
deletion is deferred until the end of the process, where it is handled
in batches.
With the deprecation of self-destruct in the Cancun fork, storage
deletions are no longer expected. Historically, the largest storage
deletion event in Ethereum was around 15 megabytes—manageable in memory.
In this pull request, the single destruct flag is replaced by a set of
deletion markers for individual storage slots. Each deleted storage slot
will now appear in the Storage set with a nil value.
This change will simplify a lot logics, such as storage accessing,
storage flushing, storage iteration and so on.
2024-11-22 02:55:43 -06:00
|
|
|
func (test *stateTest) verify(root common.Hash, next common.Hash, db *triedb.Database, accounts map[common.Hash][]byte, accountsOrigin map[common.Address][]byte, storages map[common.Hash]map[common.Hash][]byte, storagesOrigin map[common.Address]map[common.Hash][]byte) error {
|
2023-07-11 08:43:23 -05:00
|
|
|
otr, err := trie.New(trie.StateTrieID(root), db)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
ntr, err := trie.New(trie.StateTrieID(next), db)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
core, triedb: remove destruct flag in state snapshot (#30752)
This pull request removes the destruct flag from the state snapshot to
simplify the code.
Previously, this flag indicated that an account was removed during a
state transition, making all associated storage slots inaccessible.
Because storage deletion can involve a large number of slots, the actual
deletion is deferred until the end of the process, where it is handled
in batches.
With the deprecation of self-destruct in the Cancun fork, storage
deletions are no longer expected. Historically, the largest storage
deletion event in Ethereum was around 15 megabytes—manageable in memory.
In this pull request, the single destruct flag is replaced by a set of
deletion markers for individual storage slots. Each deleted storage slot
will now appear in the Storage set with a nil value.
This change will simplify a lot logics, such as storage accessing,
storage flushing, storage iteration and so on.
2024-11-22 02:55:43 -06:00
|
|
|
for addr, accountOrigin := range accountsOrigin {
|
|
|
|
var (
|
|
|
|
err error
|
|
|
|
addrHash = crypto.Keccak256Hash(addr.Bytes())
|
|
|
|
)
|
|
|
|
if len(accountOrigin) == 0 {
|
|
|
|
err = test.verifyAccountCreation(next, db, otr, ntr, addr, accounts[addrHash], storages[addrHash], storagesOrigin[addr])
|
2023-07-11 08:43:23 -05:00
|
|
|
} else {
|
core, triedb: remove destruct flag in state snapshot (#30752)
This pull request removes the destruct flag from the state snapshot to
simplify the code.
Previously, this flag indicated that an account was removed during a
state transition, making all associated storage slots inaccessible.
Because storage deletion can involve a large number of slots, the actual
deletion is deferred until the end of the process, where it is handled
in batches.
With the deprecation of self-destruct in the Cancun fork, storage
deletions are no longer expected. Historically, the largest storage
deletion event in Ethereum was around 15 megabytes—manageable in memory.
In this pull request, the single destruct flag is replaced by a set of
deletion markers for individual storage slots. Each deleted storage slot
will now appear in the Storage set with a nil value.
This change will simplify a lot logics, such as storage accessing,
storage flushing, storage iteration and so on.
2024-11-22 02:55:43 -06:00
|
|
|
err = test.verifyAccountUpdate(next, db, otr, ntr, addr, accounts[addrHash], accountsOrigin[addr], storages[addrHash], storagesOrigin[addr])
|
2023-07-11 08:43:23 -05:00
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestStateChanges(t *testing.T) {
|
|
|
|
config := &quick.Config{MaxCount: 1000}
|
|
|
|
err := quick.Check((*stateTest).run, config)
|
|
|
|
if cerr, ok := err.(*quick.CheckError); ok {
|
|
|
|
test := cerr.In[0].(*stateTest)
|
|
|
|
t.Errorf("%v:\n%s", test.err, test)
|
|
|
|
} else if err != nil {
|
|
|
|
t.Error(err)
|
|
|
|
}
|
|
|
|
}
|