Unverified Commit 4b06e4f2 authored by rjl493456442's avatar rjl493456442 Committed by GitHub

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.
parent aecf3f95
...@@ -171,7 +171,7 @@ func (s *StateDB) DumpToCollector(c DumpCollector, conf *DumpConfig) (nextKey [] ...@@ -171,7 +171,7 @@ func (s *StateDB) DumpToCollector(c DumpCollector, conf *DumpConfig) (nextKey []
} else { } else {
address = &addr address = &addr
} }
obj := newObject(s, addr, data) obj := newObject(s, addr, &data)
if !conf.SkipCode { if !conf.SkipCode {
account.Code = obj.Code(s.db) account.Code = obj.Code(s.db)
} }
......
...@@ -95,6 +95,10 @@ type ( ...@@ -95,6 +95,10 @@ type (
prevdestruct bool prevdestruct bool
prevAccount []byte prevAccount []byte
prevStorage map[common.Hash][]byte prevStorage map[common.Hash][]byte
prevAccountOriginExist bool
prevAccountOrigin []byte
prevStorageOrigin map[common.Hash][]byte
} }
suicideChange struct { suicideChange struct {
account *common.Address account *common.Address
...@@ -163,10 +167,16 @@ func (ch resetObjectChange) revert(s *StateDB) { ...@@ -163,10 +167,16 @@ func (ch resetObjectChange) revert(s *StateDB) {
delete(s.stateObjectsDestruct, ch.prev.address) delete(s.stateObjectsDestruct, ch.prev.address)
} }
if ch.prevAccount != nil { if ch.prevAccount != nil {
s.snapAccounts[ch.prev.addrHash] = ch.prevAccount s.accounts[ch.prev.addrHash] = ch.prevAccount
} }
if ch.prevStorage != nil { if ch.prevStorage != nil {
s.snapStorage[ch.prev.addrHash] = ch.prevStorage s.storages[ch.prev.addrHash] = ch.prevStorage
}
if ch.prevAccountOriginExist {
s.accountsOrigin[ch.prev.addrHash] = ch.prevAccountOrigin
}
if ch.prevStorageOrigin != nil {
s.storagesOrigin[ch.prev.addrHash] = ch.prevStorageOrigin
} }
} }
......
...@@ -27,4 +27,11 @@ var ( ...@@ -27,4 +27,11 @@ var (
storageTriesUpdatedMeter = metrics.NewRegisteredMeter("state/update/storagenodes", nil) storageTriesUpdatedMeter = metrics.NewRegisteredMeter("state/update/storagenodes", nil)
accountTrieDeletedMeter = metrics.NewRegisteredMeter("state/delete/accountnodes", nil) accountTrieDeletedMeter = metrics.NewRegisteredMeter("state/delete/accountnodes", nil)
storageTriesDeletedMeter = metrics.NewRegisteredMeter("state/delete/storagenodes", nil) storageTriesDeletedMeter = metrics.NewRegisteredMeter("state/delete/storagenodes", nil)
slotDeletionMaxCount = metrics.NewRegisteredGauge("state/delete/storage/max/slot", nil)
slotDeletionMaxSize = metrics.NewRegisteredGauge("state/delete/storage/max/size", nil)
slotDeletionTimer = metrics.NewRegisteredResettingTimer("state/delete/storage/timer", nil)
slotDeletionCount = metrics.NewRegisteredMeter("state/delete/storage/slot", nil)
slotDeletionSize = metrics.NewRegisteredMeter("state/delete/storage/size", nil)
slotDeletionSkip = metrics.NewRegisteredGauge("state/delete/storage/skip", nil)
) )
...@@ -366,7 +366,7 @@ func (dl *diskLayer) generateRange(ctx *generatorContext, trieId *trie.ID, prefi ...@@ -366,7 +366,7 @@ func (dl *diskLayer) generateRange(ctx *generatorContext, trieId *trie.ID, prefi
return false, nil, err return false, nil, err
} }
if nodes != nil { if nodes != nil {
tdb.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes)) tdb.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes), nil)
tdb.Commit(root, false) tdb.Commit(root, false)
} }
resolver = func(owner common.Hash, path []byte, hash common.Hash) []byte { resolver = func(owner common.Hash, path []byte, hash common.Hash) []byte {
......
...@@ -203,7 +203,7 @@ func (t *testHelper) Commit() common.Hash { ...@@ -203,7 +203,7 @@ func (t *testHelper) Commit() common.Hash {
if nodes != nil { if nodes != nil {
t.nodes.Merge(nodes) t.nodes.Merge(nodes)
} }
t.triedb.Update(root, types.EmptyRootHash, t.nodes) t.triedb.Update(root, types.EmptyRootHash, t.nodes, nil)
t.triedb.Commit(root, false) t.triedb.Commit(root, false)
return root return root
} }
......
...@@ -57,29 +57,35 @@ func (s Storage) Copy() Storage { ...@@ -57,29 +57,35 @@ func (s Storage) Copy() Storage {
// stateObject represents an Ethereum account which is being modified. // stateObject represents an Ethereum account which is being modified.
// //
// The usage pattern is as follows: // The usage pattern is as follows:
// First you need to obtain a state object. // - First you need to obtain a state object.
// Account values can be accessed and modified through the object. // - Account values as well as storages can be accessed and modified through the object.
// Finally, call commitTrie to write the modified storage trie into a database. // - Finally, call commit to return the changes of storage trie and update account data.
type stateObject struct { type stateObject struct {
address common.Address
addrHash common.Hash // hash of ethereum address of the account
data types.StateAccount
db *StateDB db *StateDB
address common.Address // address of ethereum account
addrHash common.Hash // hash of ethereum address of the account
origin *types.StateAccount // Account original data without any change applied, nil means it was not existent
data types.StateAccount // Account data with all mutations applied in the scope of block
// Write caches. // Write caches.
trie Trie // storage trie, which becomes non-nil on first access trie Trie // storage trie, which becomes non-nil on first access
code Code // contract bytecode, which gets set when code is loaded code Code // contract bytecode, which gets set when code is loaded
originStorage Storage // Storage cache of original entries to dedup rewrites, reset for every transaction originStorage Storage // Storage cache of original entries to dedup rewrites
pendingStorage Storage // Storage entries that need to be flushed to disk, at the end of an entire block pendingStorage Storage // Storage entries that need to be flushed to disk, at the end of an entire block
dirtyStorage Storage // Storage entries that have been modified in the current transaction execution dirtyStorage Storage // Storage entries that have been modified in the current transaction execution, reset for every transaction
// Cache flags. // Cache flags.
// When an object is marked suicided it will be deleted from the trie
// during the "update" phase of the state transition.
dirtyCode bool // true if the code was updated dirtyCode bool // true if the code was updated
suicided bool
deleted bool // Flag whether the account was marked as suicided. The suicided account
// is still accessible in the scope of same transaction.
suicided bool
// Flag whether the account was marked as deleted. The suicided account
// or the account is considered as empty will be marked as deleted at
// the end of transaction and no longer accessible anymore.
deleted bool
} }
// empty returns whether the account is considered empty. // empty returns whether the account is considered empty.
...@@ -88,21 +94,17 @@ func (s *stateObject) empty() bool { ...@@ -88,21 +94,17 @@ func (s *stateObject) empty() bool {
} }
// newObject creates a state object. // newObject creates a state object.
func newObject(db *StateDB, address common.Address, data types.StateAccount) *stateObject { func newObject(db *StateDB, address common.Address, acct *types.StateAccount) *stateObject {
if data.Balance == nil { origin := acct
data.Balance = new(big.Int) if acct == nil {
} acct = types.NewEmptyStateAccount()
if data.CodeHash == nil {
data.CodeHash = types.EmptyCodeHash.Bytes()
}
if data.Root == (common.Hash{}) {
data.Root = types.EmptyRootHash
} }
return &stateObject{ return &stateObject{
db: db, db: db,
address: address, address: address,
addrHash: crypto.Keccak256Hash(address[:]), addrHash: crypto.Keccak256Hash(address[:]),
data: data, origin: origin,
data: *acct,
originStorage: make(Storage), originStorage: make(Storage),
pendingStorage: make(Storage), pendingStorage: make(Storage),
dirtyStorage: make(Storage), dirtyStorage: make(Storage),
...@@ -135,10 +137,8 @@ func (s *stateObject) touch() { ...@@ -135,10 +137,8 @@ func (s *stateObject) touch() {
func (s *stateObject) getTrie(db Database) (Trie, error) { func (s *stateObject) getTrie(db Database) (Trie, error) {
if s.trie == nil { if s.trie == nil {
// Try fetching from prefetcher first // Try fetching from prefetcher first
// We don't prefetch empty tries
if s.data.Root != types.EmptyRootHash && s.db.prefetcher != nil { if s.data.Root != types.EmptyRootHash && s.db.prefetcher != nil {
// When the miner is creating the pending state, there is no // When the miner is creating the pending state, there is no prefetcher
// prefetcher
s.trie = s.db.prefetcher.trie(s.addrHash, s.data.Root) s.trie = s.db.prefetcher.trie(s.addrHash, s.data.Root)
} }
if s.trie == nil { if s.trie == nil {
...@@ -277,6 +277,7 @@ func (s *stateObject) updateTrie(db Database) (Trie, error) { ...@@ -277,6 +277,7 @@ func (s *stateObject) updateTrie(db Database) (Trie, error) {
// The snapshot storage map for the object // The snapshot storage map for the object
var ( var (
storage map[common.Hash][]byte storage map[common.Hash][]byte
origin map[common.Hash][]byte
hasher = s.db.hasher hasher = s.db.hasher
) )
tr, err := s.getTrie(db) tr, err := s.getTrie(db)
...@@ -291,6 +292,7 @@ func (s *stateObject) updateTrie(db Database) (Trie, error) { ...@@ -291,6 +292,7 @@ func (s *stateObject) updateTrie(db Database) (Trie, error) {
if value == s.originStorage[key] { if value == s.originStorage[key] {
continue continue
} }
prev := s.originStorage[key]
s.originStorage[key] = value s.originStorage[key] = value
// rlp-encoded value to be used by the snapshot // rlp-encoded value to be used by the snapshot
...@@ -311,17 +313,34 @@ func (s *stateObject) updateTrie(db Database) (Trie, error) { ...@@ -311,17 +313,34 @@ func (s *stateObject) updateTrie(db Database) (Trie, error) {
} }
s.db.StorageUpdated += 1 s.db.StorageUpdated += 1
} }
// If state snapshotting is active, cache the data til commit // Cache the mutated storage slots until commit
if s.db.snap != nil { if storage == nil {
if storage == nil { if storage = s.db.storages[s.addrHash]; storage == nil {
// Retrieve the old storage map, if available, create a new one otherwise storage = make(map[common.Hash][]byte)
if storage = s.db.snapStorage[s.addrHash]; storage == nil { s.db.storages[s.addrHash] = storage
storage = make(map[common.Hash][]byte) }
s.db.snapStorage[s.addrHash] = storage }
} khash := crypto.HashData(hasher, key[:])
storage[khash] = snapshotVal // snapshotVal will be nil if it's deleted
// Cache the original value of mutated storage slots
if origin == nil {
if origin = s.db.storagesOrigin[s.addrHash]; origin == nil {
origin = make(map[common.Hash][]byte)
s.db.storagesOrigin[s.addrHash] = origin
}
}
// Track the original value of slot only if it's mutated first time
if _, ok := origin[khash]; !ok {
if prev == (common.Hash{}) {
origin[khash] = nil // nil if it was not present previously
} else {
// Encoding []byte cannot fail, ok to ignore the error.
b, _ := rlp.EncodeToBytes(common.TrimLeftZeroes(prev[:]))
origin[khash] = b
} }
storage[crypto.HashData(hasher, key[:])] = snapshotVal // will be nil if it's deleted
} }
// Cache the items for preloading
usedStorage = append(usedStorage, common.CopyBytes(key[:])) // Copy needed for closure usedStorage = append(usedStorage, common.CopyBytes(key[:])) // Copy needed for closure
} }
if s.db.prefetcher != nil { if s.db.prefetcher != nil {
...@@ -351,15 +370,15 @@ func (s *stateObject) updateRoot(db Database) { ...@@ -351,15 +370,15 @@ func (s *stateObject) updateRoot(db Database) {
s.data.Root = tr.Hash() s.data.Root = tr.Hash()
} }
// commitTrie submits the storage changes into the storage trie and re-computes // commit returns the changes made in storage trie and updates the account data.
// the root. Besides, all trie changes will be collected in a nodeset and returned. func (s *stateObject) commit(db Database) (*trienode.NodeSet, error) {
func (s *stateObject) commitTrie(db Database) (*trienode.NodeSet, error) {
tr, err := s.updateTrie(db) tr, err := s.updateTrie(db)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// If nothing changed, don't bother with committing anything // If nothing changed, don't bother with committing anything
if tr == nil { if tr == nil {
s.origin = s.data.Copy()
return nil, nil return nil, nil
} }
// Track the amount of time wasted on committing the storage trie // Track the amount of time wasted on committing the storage trie
...@@ -371,6 +390,9 @@ func (s *stateObject) commitTrie(db Database) (*trienode.NodeSet, error) { ...@@ -371,6 +390,9 @@ func (s *stateObject) commitTrie(db Database) (*trienode.NodeSet, error) {
return nil, err return nil, err
} }
s.data.Root = root s.data.Root = root
// Update original account data after commit
s.origin = s.data.Copy()
return nodes, nil return nodes, nil
} }
...@@ -410,18 +432,24 @@ func (s *stateObject) setBalance(amount *big.Int) { ...@@ -410,18 +432,24 @@ func (s *stateObject) setBalance(amount *big.Int) {
} }
func (s *stateObject) deepCopy(db *StateDB) *stateObject { func (s *stateObject) deepCopy(db *StateDB) *stateObject {
stateObject := newObject(db, s.address, s.data) obj := &stateObject{
db: db,
address: s.address,
addrHash: s.addrHash,
origin: s.origin,
data: s.data,
}
if s.trie != nil { if s.trie != nil {
stateObject.trie = db.db.CopyTrie(s.trie) obj.trie = db.db.CopyTrie(s.trie)
} }
stateObject.code = s.code obj.code = s.code
stateObject.dirtyStorage = s.dirtyStorage.Copy() obj.dirtyStorage = s.dirtyStorage.Copy()
stateObject.originStorage = s.originStorage.Copy() obj.originStorage = s.originStorage.Copy()
stateObject.pendingStorage = s.pendingStorage.Copy() obj.pendingStorage = s.pendingStorage.Copy()
stateObject.suicided = s.suicided obj.suicided = s.suicided
stateObject.dirtyCode = s.dirtyCode obj.dirtyCode = s.dirtyCode
stateObject.deleted = s.deleted obj.deleted = s.deleted
return stateObject return obj
} }
// //
......
...@@ -30,22 +30,22 @@ import ( ...@@ -30,22 +30,22 @@ import (
"github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/trie"
) )
type stateTest struct { type stateEnv struct {
db ethdb.Database db ethdb.Database
state *StateDB state *StateDB
} }
func newStateTest() *stateTest { func newStateEnv() *stateEnv {
db := rawdb.NewMemoryDatabase() db := rawdb.NewMemoryDatabase()
sdb, _ := New(types.EmptyRootHash, NewDatabase(db), nil) sdb, _ := New(types.EmptyRootHash, NewDatabase(db), nil)
return &stateTest{db: db, state: sdb} return &stateEnv{db: db, state: sdb}
} }
func TestDump(t *testing.T) { func TestDump(t *testing.T) {
db := rawdb.NewMemoryDatabase() db := rawdb.NewMemoryDatabase()
tdb := NewDatabaseWithConfig(db, &trie.Config{Preimages: true}) tdb := NewDatabaseWithConfig(db, &trie.Config{Preimages: true})
sdb, _ := New(types.EmptyRootHash, tdb, nil) sdb, _ := New(types.EmptyRootHash, tdb, nil)
s := &stateTest{db: db, state: sdb} s := &stateEnv{db: db, state: sdb}
// generate a few entries // generate a few entries
obj1 := s.state.GetOrNewStateObject(common.BytesToAddress([]byte{0x01})) obj1 := s.state.GetOrNewStateObject(common.BytesToAddress([]byte{0x01}))
...@@ -99,7 +99,7 @@ func TestIterativeDump(t *testing.T) { ...@@ -99,7 +99,7 @@ func TestIterativeDump(t *testing.T) {
db := rawdb.NewMemoryDatabase() db := rawdb.NewMemoryDatabase()
tdb := NewDatabaseWithConfig(db, &trie.Config{Preimages: true}) tdb := NewDatabaseWithConfig(db, &trie.Config{Preimages: true})
sdb, _ := New(types.EmptyRootHash, tdb, nil) sdb, _ := New(types.EmptyRootHash, tdb, nil)
s := &stateTest{db: db, state: sdb} s := &stateEnv{db: db, state: sdb}
// generate a few entries // generate a few entries
obj1 := s.state.GetOrNewStateObject(common.BytesToAddress([]byte{0x01})) obj1 := s.state.GetOrNewStateObject(common.BytesToAddress([]byte{0x01}))
...@@ -133,7 +133,7 @@ func TestIterativeDump(t *testing.T) { ...@@ -133,7 +133,7 @@ func TestIterativeDump(t *testing.T) {
} }
func TestNull(t *testing.T) { func TestNull(t *testing.T) {
s := newStateTest() s := newStateEnv()
address := common.HexToAddress("0x823140710bf13990e4500136726d8b55") address := common.HexToAddress("0x823140710bf13990e4500136726d8b55")
s.state.CreateAccount(address) s.state.CreateAccount(address)
//value := common.FromHex("0x823140710bf13990e4500136726d8b55") //value := common.FromHex("0x823140710bf13990e4500136726d8b55")
...@@ -155,7 +155,7 @@ func TestSnapshot(t *testing.T) { ...@@ -155,7 +155,7 @@ func TestSnapshot(t *testing.T) {
var storageaddr common.Hash var storageaddr common.Hash
data1 := common.BytesToHash([]byte{42}) data1 := common.BytesToHash([]byte{42})
data2 := common.BytesToHash([]byte{43}) data2 := common.BytesToHash([]byte{43})
s := newStateTest() s := newStateEnv()
// snapshot the genesis state // snapshot the genesis state
genesis := s.state.Snapshot() genesis := s.state.Snapshot()
...@@ -186,7 +186,7 @@ func TestSnapshot(t *testing.T) { ...@@ -186,7 +186,7 @@ func TestSnapshot(t *testing.T) {
} }
func TestSnapshotEmpty(t *testing.T) { func TestSnapshotEmpty(t *testing.T) {
s := newStateTest() s := newStateEnv()
s.state.RevertToSnapshot(s.state.Snapshot()) s.state.RevertToSnapshot(s.state.Snapshot())
} }
......
This diff is collapsed.
This diff is collapsed.
...@@ -485,7 +485,7 @@ func (test *snapshotTest) checkEqual(state, checkstate *StateDB) error { ...@@ -485,7 +485,7 @@ func (test *snapshotTest) checkEqual(state, checkstate *StateDB) error {
} }
func TestTouchDelete(t *testing.T) { func TestTouchDelete(t *testing.T) {
s := newStateTest() s := newStateEnv()
s.state.GetOrNewStateObject(common.Address{}) s.state.GetOrNewStateObject(common.Address{})
root, _ := s.state.Commit(false) root, _ := s.state.Commit(false)
s.state, _ = New(root, s.state.db, s.state.snaps) s.state, _ = New(root, s.state.db, s.state.snaps)
......
...@@ -35,6 +35,29 @@ type StateAccount struct { ...@@ -35,6 +35,29 @@ type StateAccount struct {
CodeHash []byte CodeHash []byte
} }
// NewEmptyStateAccount constructs an empty state account.
func NewEmptyStateAccount() *StateAccount {
return &StateAccount{
Balance: new(big.Int),
Root: EmptyRootHash,
CodeHash: EmptyCodeHash.Bytes(),
}
}
// Copy returns a deep-copied state account object.
func (acct *StateAccount) Copy() *StateAccount {
var balance *big.Int
if acct.Balance != nil {
balance = new(big.Int).Set(acct.Balance)
}
return &StateAccount{
Nonce: acct.Nonce,
Balance: balance,
Root: acct.Root,
CodeHash: common.CopyBytes(acct.CodeHash),
}
}
// SlimAccount is a modified version of an Account, where the root is replaced // SlimAccount is a modified version of an Account, where the root is replaced
// with a byte slice. This format can be used to represent full-consensus format // with a byte slice. This format can be used to represent full-consensus format
// or slim format which replaces the empty root and code hash as nil byte slice. // or slim format which replaces the empty root and code hash as nil byte slice.
......
...@@ -1387,7 +1387,7 @@ func makeAccountTrieNoStorage(n int) (string, *trie.Trie, []*kv) { ...@@ -1387,7 +1387,7 @@ func makeAccountTrieNoStorage(n int) (string, *trie.Trie, []*kv) {
// Commit the state changes into db and re-create the trie // Commit the state changes into db and re-create the trie
// for accessing later. // for accessing later.
root, nodes, _ := accTrie.Commit(false) root, nodes, _ := accTrie.Commit(false)
db.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes)) db.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes), nil)
accTrie, _ = trie.New(trie.StateTrieID(root), db) accTrie, _ = trie.New(trie.StateTrieID(root), db)
return db.Scheme(), accTrie, entries return db.Scheme(), accTrie, entries
...@@ -1449,7 +1449,7 @@ func makeBoundaryAccountTrie(n int) (string, *trie.Trie, []*kv) { ...@@ -1449,7 +1449,7 @@ func makeBoundaryAccountTrie(n int) (string, *trie.Trie, []*kv) {
// Commit the state changes into db and re-create the trie // Commit the state changes into db and re-create the trie
// for accessing later. // for accessing later.
root, nodes, _ := accTrie.Commit(false) root, nodes, _ := accTrie.Commit(false)
db.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes)) db.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes), nil)
accTrie, _ = trie.New(trie.StateTrieID(root), db) accTrie, _ = trie.New(trie.StateTrieID(root), db)
return db.Scheme(), accTrie, entries return db.Scheme(), accTrie, entries
...@@ -1498,7 +1498,7 @@ func makeAccountTrieWithStorageWithUniqueStorage(accounts, slots int, code bool) ...@@ -1498,7 +1498,7 @@ func makeAccountTrieWithStorageWithUniqueStorage(accounts, slots int, code bool)
nodes.Merge(set) nodes.Merge(set)
// Commit gathered dirty nodes into database // Commit gathered dirty nodes into database
db.Update(root, types.EmptyRootHash, nodes) db.Update(root, types.EmptyRootHash, nodes, nil)
// Re-create tries with new root // Re-create tries with new root
accTrie, _ = trie.New(trie.StateTrieID(root), db) accTrie, _ = trie.New(trie.StateTrieID(root), db)
...@@ -1563,7 +1563,7 @@ func makeAccountTrieWithStorage(accounts, slots int, code, boundary bool) (strin ...@@ -1563,7 +1563,7 @@ func makeAccountTrieWithStorage(accounts, slots int, code, boundary bool) (strin
nodes.Merge(set) nodes.Merge(set)
// Commit gathered dirty nodes into database // Commit gathered dirty nodes into database
db.Update(root, types.EmptyRootHash, nodes) db.Update(root, types.EmptyRootHash, nodes, nil)
// Re-create tries with new root // Re-create tries with new root
accTrie, err := trie.New(trie.StateTrieID(root), db) accTrie, err := trie.New(trie.StateTrieID(root), db)
......
...@@ -220,7 +220,7 @@ func (c *ChtIndexerBackend) Commit() error { ...@@ -220,7 +220,7 @@ func (c *ChtIndexerBackend) Commit() error {
} }
// Commit trie changes into trie database in case it's not nil. // Commit trie changes into trie database in case it's not nil.
if nodes != nil { if nodes != nil {
if err := c.triedb.Update(root, c.originRoot, trienode.NewWithNodeSet(nodes)); err != nil { if err := c.triedb.Update(root, c.originRoot, trienode.NewWithNodeSet(nodes), nil); err != nil {
return err return err
} }
if err := c.triedb.Commit(root, false); err != nil { if err := c.triedb.Commit(root, false); err != nil {
...@@ -473,7 +473,7 @@ func (b *BloomTrieIndexerBackend) Commit() error { ...@@ -473,7 +473,7 @@ func (b *BloomTrieIndexerBackend) Commit() error {
} }
// Commit trie changes into trie database in case it's not nil. // Commit trie changes into trie database in case it's not nil.
if nodes != nil { if nodes != nil {
if err := b.triedb.Update(root, b.originRoot, trienode.NewWithNodeSet(nodes)); err != nil { if err := b.triedb.Update(root, b.originRoot, trienode.NewWithNodeSet(nodes), nil); err != nil {
return err return err
} }
if err := b.triedb.Commit(root, false); err != nil { if err := b.triedb.Commit(root, false); err != nil {
......
...@@ -121,19 +121,22 @@ func (t *odrTrie) GetStorage(_ common.Address, key []byte) ([]byte, error) { ...@@ -121,19 +121,22 @@ func (t *odrTrie) GetStorage(_ common.Address, key []byte) ([]byte, error) {
} }
func (t *odrTrie) GetAccount(address common.Address) (*types.StateAccount, error) { func (t *odrTrie) GetAccount(address common.Address) (*types.StateAccount, error) {
var res types.StateAccount var (
key := crypto.Keccak256(address.Bytes()) enc []byte
key = crypto.Keccak256(address.Bytes())
)
err := t.do(key, func() (err error) { err := t.do(key, func() (err error) {
value, err := t.trie.Get(key) enc, err = t.trie.Get(key)
if err != nil { return err
return err
}
if value == nil {
return nil
}
return rlp.DecodeBytes(value, &res)
}) })
return &res, err if err != nil || len(enc) == 0 {
return nil, err
}
acct := new(types.StateAccount)
if err := rlp.DecodeBytes(enc, acct); err != nil {
return nil, err
}
return acct, nil
} }
func (t *odrTrie) UpdateAccount(address common.Address, acc *types.StateAccount) error { func (t *odrTrie) UpdateAccount(address common.Address, acc *types.StateAccount) error {
......
...@@ -176,7 +176,7 @@ func (f *fuzzer) fuzz() int { ...@@ -176,7 +176,7 @@ func (f *fuzzer) fuzz() int {
panic(err) panic(err)
} }
if nodes != nil { if nodes != nil {
dbA.Update(rootA, types.EmptyRootHash, trienode.NewWithNodeSet(nodes)) dbA.Update(rootA, types.EmptyRootHash, trienode.NewWithNodeSet(nodes), nil)
} }
// Flush memdb -> disk (sponge) // Flush memdb -> disk (sponge)
dbA.Commit(rootA, false) dbA.Commit(rootA, false)
......
...@@ -170,7 +170,7 @@ func runRandTest(rt randTest) error { ...@@ -170,7 +170,7 @@ func runRandTest(rt randTest) error {
return err return err
} }
if nodes != nil { if nodes != nil {
if err := triedb.Update(hash, origin, trienode.NewWithNodeSet(nodes)); err != nil { if err := triedb.Update(hash, origin, trienode.NewWithNodeSet(nodes), nil); err != nil {
return err return err
} }
} }
......
...@@ -24,12 +24,16 @@ import ( ...@@ -24,12 +24,16 @@ import (
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/trie/triedb/hashdb" "github.com/ethereum/go-ethereum/trie/triedb/hashdb"
"github.com/ethereum/go-ethereum/trie/trienode" "github.com/ethereum/go-ethereum/trie/trienode"
"github.com/ethereum/go-ethereum/trie/triestate"
) )
// Config defines all necessary options for database. // Config defines all necessary options for database.
type Config struct { type Config struct {
Cache int // Memory allowance (MB) to use for caching trie nodes in memory Cache int // Memory allowance (MB) to use for caching trie nodes in memory
Preimages bool // Flag whether the preimage of trie key is recorded 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 // 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) { ...@@ -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 // 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 // root. The held pre-images accumulated up to this point will be flushed in case
// the size exceeds the threshold. // 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 { if db.preimages != nil {
db.preimages.commit(false) db.preimages.commit(false)
} }
......
...@@ -63,7 +63,7 @@ func TestIterator(t *testing.T) { ...@@ -63,7 +63,7 @@ func TestIterator(t *testing.T) {
trie.MustUpdate([]byte(val.k), []byte(val.v)) trie.MustUpdate([]byte(val.k), []byte(val.v))
} }
root, nodes, _ := trie.Commit(false) 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) trie, _ = New(TrieID(root), db)
found := make(map[string]string) found := make(map[string]string)
...@@ -255,7 +255,7 @@ func TestDifferenceIterator(t *testing.T) { ...@@ -255,7 +255,7 @@ func TestDifferenceIterator(t *testing.T) {
triea.MustUpdate([]byte(val.k), []byte(val.v)) triea.MustUpdate([]byte(val.k), []byte(val.v))
} }
rootA, nodesA, _ := triea.Commit(false) 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) triea, _ = New(TrieID(rootA), dba)
dbb := NewDatabase(rawdb.NewMemoryDatabase()) dbb := NewDatabase(rawdb.NewMemoryDatabase())
...@@ -264,7 +264,7 @@ func TestDifferenceIterator(t *testing.T) { ...@@ -264,7 +264,7 @@ func TestDifferenceIterator(t *testing.T) {
trieb.MustUpdate([]byte(val.k), []byte(val.v)) trieb.MustUpdate([]byte(val.k), []byte(val.v))
} }
rootB, nodesB, _ := trieb.Commit(false) 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) trieb, _ = New(TrieID(rootB), dbb)
found := make(map[string]string) found := make(map[string]string)
...@@ -297,7 +297,7 @@ func TestUnionIterator(t *testing.T) { ...@@ -297,7 +297,7 @@ func TestUnionIterator(t *testing.T) {
triea.MustUpdate([]byte(val.k), []byte(val.v)) triea.MustUpdate([]byte(val.k), []byte(val.v))
} }
rootA, nodesA, _ := triea.Commit(false) 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) triea, _ = New(TrieID(rootA), dba)
dbb := NewDatabase(rawdb.NewMemoryDatabase()) dbb := NewDatabase(rawdb.NewMemoryDatabase())
...@@ -306,7 +306,7 @@ func TestUnionIterator(t *testing.T) { ...@@ -306,7 +306,7 @@ func TestUnionIterator(t *testing.T) {
trieb.MustUpdate([]byte(val.k), []byte(val.v)) trieb.MustUpdate([]byte(val.k), []byte(val.v))
} }
rootB, nodesB, _ := trieb.Commit(false) 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) trieb, _ = New(TrieID(rootB), dbb)
di, _ := NewUnionIterator([]NodeIterator{triea.MustNodeIterator(nil), trieb.MustNodeIterator(nil)}) di, _ := NewUnionIterator([]NodeIterator{triea.MustNodeIterator(nil), trieb.MustNodeIterator(nil)})
...@@ -368,7 +368,7 @@ func testIteratorContinueAfterError(t *testing.T, memonly bool, scheme string) { ...@@ -368,7 +368,7 @@ func testIteratorContinueAfterError(t *testing.T, memonly bool, scheme string) {
tr.MustUpdate([]byte(val.k), []byte(val.v)) tr.MustUpdate([]byte(val.k), []byte(val.v))
} }
root, nodes, _ := tr.Commit(false) root, nodes, _ := tr.Commit(false)
tdb.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes)) tdb.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes), nil)
if !memonly { if !memonly {
tdb.Commit(root, false) tdb.Commit(root, false)
} }
...@@ -484,7 +484,7 @@ func testIteratorContinueAfterSeekError(t *testing.T, memonly bool, scheme strin ...@@ -484,7 +484,7 @@ func testIteratorContinueAfterSeekError(t *testing.T, memonly bool, scheme strin
break break
} }
} }
triedb.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes)) triedb.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes), nil)
if !memonly { if !memonly {
triedb.Commit(root, false) triedb.Commit(root, false)
} }
...@@ -605,7 +605,7 @@ func makeLargeTestTrie() (*Database, *StateTrie, *loggingDb) { ...@@ -605,7 +605,7 @@ func makeLargeTestTrie() (*Database, *StateTrie, *loggingDb) {
trie.MustUpdate(key, val) trie.MustUpdate(key, val)
} }
root, nodes, _ := trie.Commit(false) 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) triedb.Commit(root, false)
// Return the generated trie // Return the generated trie
...@@ -648,7 +648,7 @@ func testIteratorNodeBlob(t *testing.T, scheme string) { ...@@ -648,7 +648,7 @@ func testIteratorNodeBlob(t *testing.T, scheme string) {
trie.MustUpdate([]byte(val.k), []byte(val.v)) trie.MustUpdate([]byte(val.k), []byte(val.v))
} }
root, nodes, _ := trie.Commit(false) 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) triedb.Commit(root, false)
var found = make(map[common.Hash][]byte) var found = make(map[common.Hash][]byte)
......
...@@ -61,7 +61,7 @@ func makeTestStateTrie() (*Database, *StateTrie, map[string][]byte) { ...@@ -61,7 +61,7 @@ func makeTestStateTrie() (*Database, *StateTrie, map[string][]byte) {
} }
} }
root, nodes, _ := trie.Commit(false) 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)) panic(fmt.Errorf("failed to commit db %v", err))
} }
// Re-create the trie based on the new state // Re-create the trie based on the new state
......
...@@ -57,7 +57,7 @@ func makeTestTrie(scheme string) (ethdb.Database, *Database, *StateTrie, map[str ...@@ -57,7 +57,7 @@ func makeTestTrie(scheme string) (ethdb.Database, *Database, *StateTrie, map[str
} }
} }
root, nodes, _ := trie.Commit(false) 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)) panic(fmt.Errorf("failed to commit db %v", err))
} }
if err := triedb.Commit(root, false); err != nil { if err := triedb.Commit(root, false); err != nil {
...@@ -740,7 +740,7 @@ func testSyncMovingTarget(t *testing.T, scheme string) { ...@@ -740,7 +740,7 @@ func testSyncMovingTarget(t *testing.T, scheme string) {
diff[string(key)] = val diff[string(key)] = val
} }
root, nodes, _ := srcTrie.Commit(false) 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) panic(err)
} }
if err := srcDb.Commit(root, false); err != nil { if err := srcDb.Commit(root, false); err != nil {
...@@ -765,7 +765,7 @@ func testSyncMovingTarget(t *testing.T, scheme string) { ...@@ -765,7 +765,7 @@ func testSyncMovingTarget(t *testing.T, scheme string) {
reverted[k] = val reverted[k] = val
} }
root, nodes, _ = srcTrie.Commit(false) 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) panic(err)
} }
if err := srcDb.Commit(root, false); err != nil { if err := srcDb.Commit(root, false); err != nil {
......
...@@ -71,7 +71,7 @@ func testTrieTracer(t *testing.T, vals []struct{ k, v string }) { ...@@ -71,7 +71,7 @@ func testTrieTracer(t *testing.T, vals []struct{ k, v string }) {
insertSet := copySet(trie.tracer.inserts) // copy before commit insertSet := copySet(trie.tracer.inserts) // copy before commit
deleteSet := copySet(trie.tracer.deletes) // copy before commit deleteSet := copySet(trie.tracer.deletes) // copy before commit
root, nodes, _ := trie.Commit(false) 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)) seen := setKeys(iterNodes(db, root))
if !compareSet(insertSet, seen) { if !compareSet(insertSet, seen) {
...@@ -137,7 +137,7 @@ func testAccessList(t *testing.T, vals []struct{ k, v string }) { ...@@ -137,7 +137,7 @@ func testAccessList(t *testing.T, vals []struct{ k, v string }) {
trie.MustUpdate([]byte(val.k), []byte(val.v)) trie.MustUpdate([]byte(val.k), []byte(val.v))
} }
root, nodes, _ := trie.Commit(false) 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) trie, _ = New(TrieID(root), db)
if err := verifyAccessList(orig, trie, nodes); err != nil { if err := verifyAccessList(orig, trie, nodes); err != nil {
...@@ -152,7 +152,7 @@ func testAccessList(t *testing.T, vals []struct{ k, v string }) { ...@@ -152,7 +152,7 @@ func testAccessList(t *testing.T, vals []struct{ k, v string }) {
trie.MustUpdate([]byte(val.k), randBytes(32)) trie.MustUpdate([]byte(val.k), randBytes(32))
} }
root, nodes, _ = trie.Commit(false) 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) trie, _ = New(TrieID(root), db)
if err := verifyAccessList(orig, trie, nodes); err != nil { if err := verifyAccessList(orig, trie, nodes); err != nil {
...@@ -170,7 +170,7 @@ func testAccessList(t *testing.T, vals []struct{ k, v string }) { ...@@ -170,7 +170,7 @@ func testAccessList(t *testing.T, vals []struct{ k, v string }) {
trie.MustUpdate(key, randBytes(32)) trie.MustUpdate(key, randBytes(32))
} }
root, nodes, _ = trie.Commit(false) 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) trie, _ = New(TrieID(root), db)
if err := verifyAccessList(orig, trie, nodes); err != nil { if err := verifyAccessList(orig, trie, nodes); err != nil {
...@@ -185,7 +185,7 @@ func testAccessList(t *testing.T, vals []struct{ k, v string }) { ...@@ -185,7 +185,7 @@ func testAccessList(t *testing.T, vals []struct{ k, v string }) {
trie.MustUpdate([]byte(key), nil) trie.MustUpdate([]byte(key), nil)
} }
root, nodes, _ = trie.Commit(false) 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) trie, _ = New(TrieID(root), db)
if err := verifyAccessList(orig, trie, nodes); err != nil { if err := verifyAccessList(orig, trie, nodes); err != nil {
...@@ -200,7 +200,7 @@ func testAccessList(t *testing.T, vals []struct{ k, v string }) { ...@@ -200,7 +200,7 @@ func testAccessList(t *testing.T, vals []struct{ k, v string }) {
trie.MustUpdate([]byte(val.k), nil) trie.MustUpdate([]byte(val.k), nil)
} }
root, nodes, _ = trie.Commit(false) 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) trie, _ = New(TrieID(root), db)
if err := verifyAccessList(orig, trie, nodes); err != nil { if err := verifyAccessList(orig, trie, nodes); err != nil {
...@@ -219,7 +219,7 @@ func TestAccessListLeak(t *testing.T) { ...@@ -219,7 +219,7 @@ func TestAccessListLeak(t *testing.T) {
trie.MustUpdate([]byte(val.k), []byte(val.v)) trie.MustUpdate([]byte(val.k), []byte(val.v))
} }
root, nodes, _ := trie.Commit(false) 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 { var cases = []struct {
op func(tr *Trie) op func(tr *Trie)
...@@ -269,7 +269,7 @@ func TestTinyTree(t *testing.T) { ...@@ -269,7 +269,7 @@ func TestTinyTree(t *testing.T) {
trie.MustUpdate([]byte(val.k), randBytes(32)) trie.MustUpdate([]byte(val.k), randBytes(32))
} }
root, set, _ := trie.Commit(false) root, set, _ := trie.Commit(false)
db.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(set)) db.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(set), nil)
parent := root parent := root
trie, _ = New(TrieID(root), db) trie, _ = New(TrieID(root), db)
...@@ -278,7 +278,7 @@ func TestTinyTree(t *testing.T) { ...@@ -278,7 +278,7 @@ func TestTinyTree(t *testing.T) {
trie.MustUpdate([]byte(val.k), []byte(val.v)) trie.MustUpdate([]byte(val.k), []byte(val.v))
} }
root, set, _ = trie.Commit(false) 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) trie, _ = New(TrieID(root), db)
if err := verifyAccessList(orig, trie, set); err != nil { if err := verifyAccessList(orig, trie, set); err != nil {
......
...@@ -89,7 +89,7 @@ func testMissingNode(t *testing.T, memonly bool, scheme string) { ...@@ -89,7 +89,7 @@ func testMissingNode(t *testing.T, memonly bool, scheme string) {
updateString(trie, "120000", "qwerqwerqwerqwerqwerqwerqwerqwer") updateString(trie, "120000", "qwerqwerqwerqwerqwerqwerqwerqwer")
updateString(trie, "123456", "asdfasdfasdfasdfasdfasdfasdfasdf") updateString(trie, "123456", "asdfasdfasdfasdfasdfasdfasdfasdf")
root, nodes, _ := trie.Commit(false) root, nodes, _ := trie.Commit(false)
triedb.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes)) triedb.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes), nil)
if !memonly { if !memonly {
triedb.Commit(root, false) triedb.Commit(root, false)
...@@ -203,7 +203,7 @@ func TestGet(t *testing.T) { ...@@ -203,7 +203,7 @@ func TestGet(t *testing.T) {
return return
} }
root, nodes, _ := trie.Commit(false) 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) trie, _ = New(TrieID(root), db)
} }
} }
...@@ -275,7 +275,7 @@ func TestReplication(t *testing.T) { ...@@ -275,7 +275,7 @@ func TestReplication(t *testing.T) {
updateString(trie, val.k, val.v) updateString(trie, val.k, val.v)
} }
root, nodes, _ := trie.Commit(false) 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. // create a new trie on top of the database and check that lookups work.
trie2, err := New(TrieID(root), db) trie2, err := New(TrieID(root), db)
...@@ -294,7 +294,7 @@ func TestReplication(t *testing.T) { ...@@ -294,7 +294,7 @@ func TestReplication(t *testing.T) {
// recreate the trie after commit // recreate the trie after commit
if nodes != nil { 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) trie2, err = New(TrieID(hash), db)
if err != nil { if err != nil {
...@@ -503,7 +503,7 @@ func runRandTest(rt randTest) bool { ...@@ -503,7 +503,7 @@ func runRandTest(rt randTest) bool {
case opCommit: case opCommit:
root, nodes, _ := tr.Commit(true) root, nodes, _ := tr.Commit(true)
if nodes != nil { if nodes != nil {
triedb.Update(root, origin, trienode.NewWithNodeSet(nodes)) triedb.Update(root, origin, trienode.NewWithNodeSet(nodes), nil)
} }
newtr, err := New(TrieID(root), triedb) newtr, err := New(TrieID(root), triedb)
if err != nil { if err != nil {
...@@ -838,7 +838,7 @@ func TestCommitSequence(t *testing.T) { ...@@ -838,7 +838,7 @@ func TestCommitSequence(t *testing.T) {
} }
// Flush trie -> database // Flush trie -> database
root, nodes, _ := trie.Commit(false) 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) // Flush memdb -> disk (sponge)
db.Commit(root, false) db.Commit(root, false)
if got, exp := s.sponge.Sum(nil), tc.expWriteSeqHash; !bytes.Equal(got, exp) { if got, exp := s.sponge.Sum(nil), tc.expWriteSeqHash; !bytes.Equal(got, exp) {
...@@ -879,7 +879,7 @@ func TestCommitSequenceRandomBlobs(t *testing.T) { ...@@ -879,7 +879,7 @@ func TestCommitSequenceRandomBlobs(t *testing.T) {
} }
// Flush trie -> database // Flush trie -> database
root, nodes, _ := trie.Commit(false) 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) // Flush memdb -> disk (sponge)
db.Commit(root, false) db.Commit(root, false)
if got, exp := s.sponge.Sum(nil), tc.expWriteSeqHash; !bytes.Equal(got, exp) { if got, exp := s.sponge.Sum(nil), tc.expWriteSeqHash; !bytes.Equal(got, exp) {
...@@ -919,7 +919,7 @@ func TestCommitSequenceStackTrie(t *testing.T) { ...@@ -919,7 +919,7 @@ func TestCommitSequenceStackTrie(t *testing.T) {
// Flush trie -> database // Flush trie -> database
root, nodes, _ := trie.Commit(false) root, nodes, _ := trie.Commit(false)
// Flush memdb -> disk (sponge) // 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) db.Commit(root, false)
// And flush stacktrie -> disk // And flush stacktrie -> disk
stRoot, err := stTrie.Commit() stRoot, err := stTrie.Commit()
...@@ -967,7 +967,7 @@ func TestCommitSequenceSmallRoot(t *testing.T) { ...@@ -967,7 +967,7 @@ func TestCommitSequenceSmallRoot(t *testing.T) {
// Flush trie -> database // Flush trie -> database
root, nodes, _ := trie.Commit(false) root, nodes, _ := trie.Commit(false)
// Flush memdb -> disk (sponge) // 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) db.Commit(root, false)
// And flush stacktrie -> disk // And flush stacktrie -> disk
stRoot, err := stTrie.Commit() stRoot, err := stTrie.Commit()
...@@ -1139,7 +1139,7 @@ func benchmarkDerefRootFixedSize(b *testing.B, addresses [][20]byte, accounts [] ...@@ -1139,7 +1139,7 @@ func benchmarkDerefRootFixedSize(b *testing.B, addresses [][20]byte, accounts []
} }
h := trie.Hash() h := trie.Hash()
root, nodes, _ := trie.Commit(false) root, nodes, _ := trie.Commit(false)
triedb.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes)) triedb.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes), nil)
b.StartTimer() b.StartTimer()
triedb.Dereference(h) triedb.Dereference(h)
b.StopTimer() b.StopTimer()
......
...@@ -123,6 +123,26 @@ func (set *NodeSet) AddNode(path []byte, n *WithPrev) { ...@@ -123,6 +123,26 @@ func (set *NodeSet) AddNode(path []byte, n *WithPrev) {
set.Nodes[string(path)] = n 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 // AddLeaf adds the provided leaf node into set. TODO(rjl493456442) how can
// we get rid of it? // we get rid of it?
func (set *NodeSet) AddLeaf(parent common.Hash, blob []byte) { func (set *NodeSet) AddLeaf(parent common.Hash, blob []byte) {
...@@ -190,9 +210,9 @@ func NewWithNodeSet(set *NodeSet) *MergedNodeSet { ...@@ -190,9 +210,9 @@ func NewWithNodeSet(set *NodeSet) *MergedNodeSet {
// Merge merges the provided dirty nodes of a trie into the set. The assumption // 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. // is held that no duplicated set belonging to the same trie will be merged twice.
func (set *MergedNodeSet) Merge(other *NodeSet) error { func (set *MergedNodeSet) Merge(other *NodeSet) error {
_, present := set.Sets[other.Owner] subset, present := set.Sets[other.Owner]
if present { if present {
return fmt.Errorf("duplicate trie for owner %#x", other.Owner) return subset.Merge(other.Owner, other.Nodes)
} }
set.Sets[other.Owner] = other set.Sets[other.Owner] = other
return nil return nil
......
// 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
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment