forked from cerc-io/plugeth
core/state: value diff tracking in StateDB (#27349)
This change makes the StateDB track the state key value diff of a block transition. We already tracked current account and storage values for the purpose of updating the state snapshot. With this PR, we now also track the original (pre-transition) values of accounts and storage slots.
This commit is contained in:
+8
-1
@@ -24,12 +24,16 @@ import (
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/trie/triedb/hashdb"
|
||||
"github.com/ethereum/go-ethereum/trie/trienode"
|
||||
"github.com/ethereum/go-ethereum/trie/triestate"
|
||||
)
|
||||
|
||||
// Config defines all necessary options for database.
|
||||
type Config struct {
|
||||
Cache int // Memory allowance (MB) to use for caching trie nodes in memory
|
||||
Preimages bool // Flag whether the preimage of trie key is recorded
|
||||
|
||||
// Testing hooks
|
||||
OnCommit func(states *triestate.Set) // Hook invoked when commit is performed
|
||||
}
|
||||
|
||||
// backend defines the methods needed to access/update trie nodes in different
|
||||
@@ -114,7 +118,10 @@ func (db *Database) Reader(blockRoot common.Hash) (Reader, error) {
|
||||
// given set in order to update state from the specified parent to the specified
|
||||
// root. The held pre-images accumulated up to this point will be flushed in case
|
||||
// the size exceeds the threshold.
|
||||
func (db *Database) Update(root common.Hash, parent common.Hash, nodes *trienode.MergedNodeSet) error {
|
||||
func (db *Database) Update(root common.Hash, parent common.Hash, nodes *trienode.MergedNodeSet, states *triestate.Set) error {
|
||||
if db.config != nil && db.config.OnCommit != nil {
|
||||
db.config.OnCommit(states)
|
||||
}
|
||||
if db.preimages != nil {
|
||||
db.preimages.commit(false)
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ func TestIterator(t *testing.T) {
|
||||
trie.MustUpdate([]byte(val.k), []byte(val.v))
|
||||
}
|
||||
root, nodes, _ := trie.Commit(false)
|
||||
db.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes))
|
||||
db.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes), nil)
|
||||
|
||||
trie, _ = New(TrieID(root), db)
|
||||
found := make(map[string]string)
|
||||
@@ -255,7 +255,7 @@ func TestDifferenceIterator(t *testing.T) {
|
||||
triea.MustUpdate([]byte(val.k), []byte(val.v))
|
||||
}
|
||||
rootA, nodesA, _ := triea.Commit(false)
|
||||
dba.Update(rootA, types.EmptyRootHash, trienode.NewWithNodeSet(nodesA))
|
||||
dba.Update(rootA, types.EmptyRootHash, trienode.NewWithNodeSet(nodesA), nil)
|
||||
triea, _ = New(TrieID(rootA), dba)
|
||||
|
||||
dbb := NewDatabase(rawdb.NewMemoryDatabase())
|
||||
@@ -264,7 +264,7 @@ func TestDifferenceIterator(t *testing.T) {
|
||||
trieb.MustUpdate([]byte(val.k), []byte(val.v))
|
||||
}
|
||||
rootB, nodesB, _ := trieb.Commit(false)
|
||||
dbb.Update(rootB, types.EmptyRootHash, trienode.NewWithNodeSet(nodesB))
|
||||
dbb.Update(rootB, types.EmptyRootHash, trienode.NewWithNodeSet(nodesB), nil)
|
||||
trieb, _ = New(TrieID(rootB), dbb)
|
||||
|
||||
found := make(map[string]string)
|
||||
@@ -297,7 +297,7 @@ func TestUnionIterator(t *testing.T) {
|
||||
triea.MustUpdate([]byte(val.k), []byte(val.v))
|
||||
}
|
||||
rootA, nodesA, _ := triea.Commit(false)
|
||||
dba.Update(rootA, types.EmptyRootHash, trienode.NewWithNodeSet(nodesA))
|
||||
dba.Update(rootA, types.EmptyRootHash, trienode.NewWithNodeSet(nodesA), nil)
|
||||
triea, _ = New(TrieID(rootA), dba)
|
||||
|
||||
dbb := NewDatabase(rawdb.NewMemoryDatabase())
|
||||
@@ -306,7 +306,7 @@ func TestUnionIterator(t *testing.T) {
|
||||
trieb.MustUpdate([]byte(val.k), []byte(val.v))
|
||||
}
|
||||
rootB, nodesB, _ := trieb.Commit(false)
|
||||
dbb.Update(rootB, types.EmptyRootHash, trienode.NewWithNodeSet(nodesB))
|
||||
dbb.Update(rootB, types.EmptyRootHash, trienode.NewWithNodeSet(nodesB), nil)
|
||||
trieb, _ = New(TrieID(rootB), dbb)
|
||||
|
||||
di, _ := NewUnionIterator([]NodeIterator{triea.MustNodeIterator(nil), trieb.MustNodeIterator(nil)})
|
||||
@@ -368,7 +368,7 @@ func testIteratorContinueAfterError(t *testing.T, memonly bool, scheme string) {
|
||||
tr.MustUpdate([]byte(val.k), []byte(val.v))
|
||||
}
|
||||
root, nodes, _ := tr.Commit(false)
|
||||
tdb.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes))
|
||||
tdb.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes), nil)
|
||||
if !memonly {
|
||||
tdb.Commit(root, false)
|
||||
}
|
||||
@@ -484,7 +484,7 @@ func testIteratorContinueAfterSeekError(t *testing.T, memonly bool, scheme strin
|
||||
break
|
||||
}
|
||||
}
|
||||
triedb.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes))
|
||||
triedb.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes), nil)
|
||||
if !memonly {
|
||||
triedb.Commit(root, false)
|
||||
}
|
||||
@@ -605,7 +605,7 @@ func makeLargeTestTrie() (*Database, *StateTrie, *loggingDb) {
|
||||
trie.MustUpdate(key, val)
|
||||
}
|
||||
root, nodes, _ := trie.Commit(false)
|
||||
triedb.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes))
|
||||
triedb.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes), nil)
|
||||
triedb.Commit(root, false)
|
||||
|
||||
// Return the generated trie
|
||||
@@ -648,7 +648,7 @@ func testIteratorNodeBlob(t *testing.T, scheme string) {
|
||||
trie.MustUpdate([]byte(val.k), []byte(val.v))
|
||||
}
|
||||
root, nodes, _ := trie.Commit(false)
|
||||
triedb.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes))
|
||||
triedb.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes), nil)
|
||||
triedb.Commit(root, false)
|
||||
|
||||
var found = make(map[common.Hash][]byte)
|
||||
|
||||
@@ -61,7 +61,7 @@ func makeTestStateTrie() (*Database, *StateTrie, map[string][]byte) {
|
||||
}
|
||||
}
|
||||
root, nodes, _ := trie.Commit(false)
|
||||
if err := triedb.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes)); err != nil {
|
||||
if err := triedb.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes), nil); err != nil {
|
||||
panic(fmt.Errorf("failed to commit db %v", err))
|
||||
}
|
||||
// Re-create the trie based on the new state
|
||||
|
||||
+3
-3
@@ -57,7 +57,7 @@ func makeTestTrie(scheme string) (ethdb.Database, *Database, *StateTrie, map[str
|
||||
}
|
||||
}
|
||||
root, nodes, _ := trie.Commit(false)
|
||||
if err := triedb.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes)); err != nil {
|
||||
if err := triedb.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes), nil); err != nil {
|
||||
panic(fmt.Errorf("failed to commit db %v", err))
|
||||
}
|
||||
if err := triedb.Commit(root, false); err != nil {
|
||||
@@ -740,7 +740,7 @@ func testSyncMovingTarget(t *testing.T, scheme string) {
|
||||
diff[string(key)] = val
|
||||
}
|
||||
root, nodes, _ := srcTrie.Commit(false)
|
||||
if err := srcDb.Update(root, preRoot, trienode.NewWithNodeSet(nodes)); err != nil {
|
||||
if err := srcDb.Update(root, preRoot, trienode.NewWithNodeSet(nodes), nil); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := srcDb.Commit(root, false); err != nil {
|
||||
@@ -765,7 +765,7 @@ func testSyncMovingTarget(t *testing.T, scheme string) {
|
||||
reverted[k] = val
|
||||
}
|
||||
root, nodes, _ = srcTrie.Commit(false)
|
||||
if err := srcDb.Update(root, preRoot, trienode.NewWithNodeSet(nodes)); err != nil {
|
||||
if err := srcDb.Update(root, preRoot, trienode.NewWithNodeSet(nodes), nil); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := srcDb.Commit(root, false); err != nil {
|
||||
|
||||
+9
-9
@@ -71,7 +71,7 @@ func testTrieTracer(t *testing.T, vals []struct{ k, v string }) {
|
||||
insertSet := copySet(trie.tracer.inserts) // copy before commit
|
||||
deleteSet := copySet(trie.tracer.deletes) // copy before commit
|
||||
root, nodes, _ := trie.Commit(false)
|
||||
db.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes))
|
||||
db.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes), nil)
|
||||
|
||||
seen := setKeys(iterNodes(db, root))
|
||||
if !compareSet(insertSet, seen) {
|
||||
@@ -137,7 +137,7 @@ func testAccessList(t *testing.T, vals []struct{ k, v string }) {
|
||||
trie.MustUpdate([]byte(val.k), []byte(val.v))
|
||||
}
|
||||
root, nodes, _ := trie.Commit(false)
|
||||
db.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes))
|
||||
db.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes), nil)
|
||||
|
||||
trie, _ = New(TrieID(root), db)
|
||||
if err := verifyAccessList(orig, trie, nodes); err != nil {
|
||||
@@ -152,7 +152,7 @@ func testAccessList(t *testing.T, vals []struct{ k, v string }) {
|
||||
trie.MustUpdate([]byte(val.k), randBytes(32))
|
||||
}
|
||||
root, nodes, _ = trie.Commit(false)
|
||||
db.Update(root, parent, trienode.NewWithNodeSet(nodes))
|
||||
db.Update(root, parent, trienode.NewWithNodeSet(nodes), nil)
|
||||
|
||||
trie, _ = New(TrieID(root), db)
|
||||
if err := verifyAccessList(orig, trie, nodes); err != nil {
|
||||
@@ -170,7 +170,7 @@ func testAccessList(t *testing.T, vals []struct{ k, v string }) {
|
||||
trie.MustUpdate(key, randBytes(32))
|
||||
}
|
||||
root, nodes, _ = trie.Commit(false)
|
||||
db.Update(root, parent, trienode.NewWithNodeSet(nodes))
|
||||
db.Update(root, parent, trienode.NewWithNodeSet(nodes), nil)
|
||||
|
||||
trie, _ = New(TrieID(root), db)
|
||||
if err := verifyAccessList(orig, trie, nodes); err != nil {
|
||||
@@ -185,7 +185,7 @@ func testAccessList(t *testing.T, vals []struct{ k, v string }) {
|
||||
trie.MustUpdate([]byte(key), nil)
|
||||
}
|
||||
root, nodes, _ = trie.Commit(false)
|
||||
db.Update(root, parent, trienode.NewWithNodeSet(nodes))
|
||||
db.Update(root, parent, trienode.NewWithNodeSet(nodes), nil)
|
||||
|
||||
trie, _ = New(TrieID(root), db)
|
||||
if err := verifyAccessList(orig, trie, nodes); err != nil {
|
||||
@@ -200,7 +200,7 @@ func testAccessList(t *testing.T, vals []struct{ k, v string }) {
|
||||
trie.MustUpdate([]byte(val.k), nil)
|
||||
}
|
||||
root, nodes, _ = trie.Commit(false)
|
||||
db.Update(root, parent, trienode.NewWithNodeSet(nodes))
|
||||
db.Update(root, parent, trienode.NewWithNodeSet(nodes), nil)
|
||||
|
||||
trie, _ = New(TrieID(root), db)
|
||||
if err := verifyAccessList(orig, trie, nodes); err != nil {
|
||||
@@ -219,7 +219,7 @@ func TestAccessListLeak(t *testing.T) {
|
||||
trie.MustUpdate([]byte(val.k), []byte(val.v))
|
||||
}
|
||||
root, nodes, _ := trie.Commit(false)
|
||||
db.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes))
|
||||
db.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes), nil)
|
||||
|
||||
var cases = []struct {
|
||||
op func(tr *Trie)
|
||||
@@ -269,7 +269,7 @@ func TestTinyTree(t *testing.T) {
|
||||
trie.MustUpdate([]byte(val.k), randBytes(32))
|
||||
}
|
||||
root, set, _ := trie.Commit(false)
|
||||
db.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(set))
|
||||
db.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(set), nil)
|
||||
|
||||
parent := root
|
||||
trie, _ = New(TrieID(root), db)
|
||||
@@ -278,7 +278,7 @@ func TestTinyTree(t *testing.T) {
|
||||
trie.MustUpdate([]byte(val.k), []byte(val.v))
|
||||
}
|
||||
root, set, _ = trie.Commit(false)
|
||||
db.Update(root, parent, trienode.NewWithNodeSet(set))
|
||||
db.Update(root, parent, trienode.NewWithNodeSet(set), nil)
|
||||
|
||||
trie, _ = New(TrieID(root), db)
|
||||
if err := verifyAccessList(orig, trie, set); err != nil {
|
||||
|
||||
+10
-10
@@ -89,7 +89,7 @@ func testMissingNode(t *testing.T, memonly bool, scheme string) {
|
||||
updateString(trie, "120000", "qwerqwerqwerqwerqwerqwerqwerqwer")
|
||||
updateString(trie, "123456", "asdfasdfasdfasdfasdfasdfasdfasdf")
|
||||
root, nodes, _ := trie.Commit(false)
|
||||
triedb.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes))
|
||||
triedb.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes), nil)
|
||||
|
||||
if !memonly {
|
||||
triedb.Commit(root, false)
|
||||
@@ -203,7 +203,7 @@ func TestGet(t *testing.T) {
|
||||
return
|
||||
}
|
||||
root, nodes, _ := trie.Commit(false)
|
||||
db.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes))
|
||||
db.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes), nil)
|
||||
trie, _ = New(TrieID(root), db)
|
||||
}
|
||||
}
|
||||
@@ -275,7 +275,7 @@ func TestReplication(t *testing.T) {
|
||||
updateString(trie, val.k, val.v)
|
||||
}
|
||||
root, nodes, _ := trie.Commit(false)
|
||||
db.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes))
|
||||
db.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes), nil)
|
||||
|
||||
// create a new trie on top of the database and check that lookups work.
|
||||
trie2, err := New(TrieID(root), db)
|
||||
@@ -294,7 +294,7 @@ func TestReplication(t *testing.T) {
|
||||
|
||||
// recreate the trie after commit
|
||||
if nodes != nil {
|
||||
db.Update(hash, types.EmptyRootHash, trienode.NewWithNodeSet(nodes))
|
||||
db.Update(hash, types.EmptyRootHash, trienode.NewWithNodeSet(nodes), nil)
|
||||
}
|
||||
trie2, err = New(TrieID(hash), db)
|
||||
if err != nil {
|
||||
@@ -503,7 +503,7 @@ func runRandTest(rt randTest) bool {
|
||||
case opCommit:
|
||||
root, nodes, _ := tr.Commit(true)
|
||||
if nodes != nil {
|
||||
triedb.Update(root, origin, trienode.NewWithNodeSet(nodes))
|
||||
triedb.Update(root, origin, trienode.NewWithNodeSet(nodes), nil)
|
||||
}
|
||||
newtr, err := New(TrieID(root), triedb)
|
||||
if err != nil {
|
||||
@@ -838,7 +838,7 @@ func TestCommitSequence(t *testing.T) {
|
||||
}
|
||||
// Flush trie -> database
|
||||
root, nodes, _ := trie.Commit(false)
|
||||
db.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes))
|
||||
db.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes), nil)
|
||||
// Flush memdb -> disk (sponge)
|
||||
db.Commit(root, false)
|
||||
if got, exp := s.sponge.Sum(nil), tc.expWriteSeqHash; !bytes.Equal(got, exp) {
|
||||
@@ -879,7 +879,7 @@ func TestCommitSequenceRandomBlobs(t *testing.T) {
|
||||
}
|
||||
// Flush trie -> database
|
||||
root, nodes, _ := trie.Commit(false)
|
||||
db.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes))
|
||||
db.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes), nil)
|
||||
// Flush memdb -> disk (sponge)
|
||||
db.Commit(root, false)
|
||||
if got, exp := s.sponge.Sum(nil), tc.expWriteSeqHash; !bytes.Equal(got, exp) {
|
||||
@@ -919,7 +919,7 @@ func TestCommitSequenceStackTrie(t *testing.T) {
|
||||
// Flush trie -> database
|
||||
root, nodes, _ := trie.Commit(false)
|
||||
// Flush memdb -> disk (sponge)
|
||||
db.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes))
|
||||
db.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes), nil)
|
||||
db.Commit(root, false)
|
||||
// And flush stacktrie -> disk
|
||||
stRoot, err := stTrie.Commit()
|
||||
@@ -967,7 +967,7 @@ func TestCommitSequenceSmallRoot(t *testing.T) {
|
||||
// Flush trie -> database
|
||||
root, nodes, _ := trie.Commit(false)
|
||||
// Flush memdb -> disk (sponge)
|
||||
db.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes))
|
||||
db.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes), nil)
|
||||
db.Commit(root, false)
|
||||
// And flush stacktrie -> disk
|
||||
stRoot, err := stTrie.Commit()
|
||||
@@ -1139,7 +1139,7 @@ func benchmarkDerefRootFixedSize(b *testing.B, addresses [][20]byte, accounts []
|
||||
}
|
||||
h := trie.Hash()
|
||||
root, nodes, _ := trie.Commit(false)
|
||||
triedb.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes))
|
||||
triedb.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes), nil)
|
||||
b.StartTimer()
|
||||
triedb.Dereference(h)
|
||||
b.StopTimer()
|
||||
|
||||
+22
-2
@@ -123,6 +123,26 @@ func (set *NodeSet) AddNode(path []byte, n *WithPrev) {
|
||||
set.Nodes[string(path)] = n
|
||||
}
|
||||
|
||||
// Merge adds a set of nodes into the set.
|
||||
func (set *NodeSet) Merge(owner common.Hash, nodes map[string]*WithPrev) error {
|
||||
if set.Owner != owner {
|
||||
return fmt.Errorf("nodesets belong to different owner are not mergeable %x-%x", set.Owner, owner)
|
||||
}
|
||||
for path, node := range nodes {
|
||||
prev, ok := set.Nodes[path]
|
||||
if ok {
|
||||
// overwrite happens, revoke the counter
|
||||
if prev.IsDeleted() {
|
||||
set.deletes -= 1
|
||||
} else {
|
||||
set.updates -= 1
|
||||
}
|
||||
}
|
||||
set.AddNode([]byte(path), node)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddLeaf adds the provided leaf node into set. TODO(rjl493456442) how can
|
||||
// we get rid of it?
|
||||
func (set *NodeSet) AddLeaf(parent common.Hash, blob []byte) {
|
||||
@@ -190,9 +210,9 @@ func NewWithNodeSet(set *NodeSet) *MergedNodeSet {
|
||||
// Merge merges the provided dirty nodes of a trie into the set. The assumption
|
||||
// is held that no duplicated set belonging to the same trie will be merged twice.
|
||||
func (set *MergedNodeSet) Merge(other *NodeSet) error {
|
||||
_, present := set.Sets[other.Owner]
|
||||
subset, present := set.Sets[other.Owner]
|
||||
if present {
|
||||
return fmt.Errorf("duplicate trie for owner %#x", other.Owner)
|
||||
return subset.Merge(other.Owner, other.Nodes)
|
||||
}
|
||||
set.Sets[other.Owner] = other
|
||||
return nil
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
// 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 triestate
|
||||
|
||||
import "github.com/ethereum/go-ethereum/common"
|
||||
|
||||
// Set represents a collection of mutated states during a state transition.
|
||||
// The value refers to the original content of state before the transition
|
||||
// is made. Nil means that the state was not present previously.
|
||||
type Set struct {
|
||||
Accounts map[common.Hash][]byte // Mutated account set, nil means the account was not present
|
||||
Storages map[common.Hash]map[common.Hash][]byte // Mutated storage set, nil means the slot was not present
|
||||
Incomplete map[common.Hash]struct{} // Indicator whether the storage slot is incomplete due to large deletion
|
||||
}
|
||||
Reference in New Issue
Block a user