Skip to content

Commit cc10283

Browse files
committed
all: track state changes in state db 27349
1 parent 3af6290 commit cc10283

File tree

13 files changed

+1013
-123
lines changed

13 files changed

+1013
-123
lines changed

core/state/database.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,16 @@ func NewDatabaseWithConfig(db ethdb.Database, config *trie.Config) Database {
137137
}
138138
}
139139

140+
// NewDatabaseWithNodeDB creates a state database with an already initialized node database.
141+
func NewDatabaseWithNodeDB(db ethdb.Database, triedb *trie.Database) Database {
142+
return &cachingDB{
143+
db: triedb,
144+
disk: db,
145+
codeCache: lru.NewSizeConstrainedCache[common.Hash, []byte](codeCacheSize),
146+
codeSizeCache: lru.NewCache[common.Hash, int](codeSizeCacheSize),
147+
}
148+
}
149+
140150
type cachingDB struct {
141151
db *trie.Database
142152
disk ethdb.KeyValueStore

core/state/dump.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ func (s *StateDB) DumpToCollector(c DumpCollector, conf *DumpConfig) (nextKey []
166166
} else {
167167
address = &addr
168168
}
169-
obj := newObject(s, addr, data)
169+
obj := newObject(s, addr, &data)
170170
if !conf.SkipCode {
171171
account.Code = obj.Code(s.db)
172172
}

core/state/journal.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,12 @@ type (
9393
account *common.Address
9494
prev *stateObject
9595
prevdestruct bool
96+
prevAccount []byte
97+
prevStorage map[common.Hash][]byte
98+
99+
prevAccountOriginExist bool
100+
prevAccountOrigin []byte
101+
prevStorageOrigin map[common.Hash][]byte
96102
}
97103
selfDestructChange struct {
98104
account *common.Address
@@ -160,6 +166,18 @@ func (ch resetObjectChange) revert(s *StateDB) {
160166
if !ch.prevdestruct {
161167
delete(s.stateObjectsDestruct, ch.prev.address)
162168
}
169+
if ch.prevAccount != nil {
170+
s.accounts[ch.prev.addrHash] = ch.prevAccount
171+
}
172+
if ch.prevStorage != nil {
173+
s.storages[ch.prev.addrHash] = ch.prevStorage
174+
}
175+
if ch.prevAccountOriginExist {
176+
s.accountsOrigin[ch.prev.addrHash] = ch.prevAccountOrigin
177+
}
178+
if ch.prevStorageOrigin != nil {
179+
s.storagesOrigin[ch.prev.addrHash] = ch.prevStorageOrigin
180+
}
163181
}
164182

165183
func (ch resetObjectChange) dirtied() *common.Address {

core/state/metrics.go

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,19 @@ package state
1919
import "github.com/XinFinOrg/XDPoSChain/metrics"
2020

2121
var (
22-
accountUpdatedMeter = metrics.NewRegisteredMeter("state/update/account", nil)
23-
storageUpdatedMeter = metrics.NewRegisteredMeter("state/update/storage", nil)
24-
accountDeletedMeter = metrics.NewRegisteredMeter("state/delete/account", nil)
25-
storageDeletedMeter = metrics.NewRegisteredMeter("state/delete/storage", nil)
26-
accountTrieCommittedMeter = metrics.NewRegisteredMeter("state/commit/accountnodes", nil)
27-
storageTriesCommittedMeter = metrics.NewRegisteredMeter("state/commit/storagenodes", nil)
22+
accountUpdatedMeter = metrics.NewRegisteredMeter("state/update/account", nil)
23+
storageUpdatedMeter = metrics.NewRegisteredMeter("state/update/storage", nil)
24+
accountDeletedMeter = metrics.NewRegisteredMeter("state/delete/account", nil)
25+
storageDeletedMeter = metrics.NewRegisteredMeter("state/delete/storage", nil)
26+
accountTrieUpdatedMeter = metrics.NewRegisteredMeter("state/update/accountnodes", nil)
27+
storageTriesUpdatedMeter = metrics.NewRegisteredMeter("state/update/storagenodes", nil)
28+
accountTrieDeletedMeter = metrics.NewRegisteredMeter("state/delete/accountnodes", nil)
29+
storageTriesDeletedMeter = metrics.NewRegisteredMeter("state/delete/storagenodes", nil)
30+
31+
slotDeletionMaxCount = metrics.NewRegisteredGauge("state/delete/storage/max/slot", nil)
32+
slotDeletionMaxSize = metrics.NewRegisteredGauge("state/delete/storage/max/size", nil)
33+
slotDeletionTimer = metrics.NewRegisteredResettingTimer("state/delete/storage/timer", nil)
34+
slotDeletionCount = metrics.NewRegisteredMeter("state/delete/storage/slot", nil)
35+
slotDeletionSize = metrics.NewRegisteredMeter("state/delete/storage/size", nil)
36+
slotDeletionSkip = metrics.NewRegisteredGauge("state/delete/storage/skip", nil)
2837
)

core/state/state_object.go

Lines changed: 81 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -59,14 +59,15 @@ func (s Storage) Copy() Storage {
5959
// stateObject represents an Ethereum account which is being modified.
6060
//
6161
// The usage pattern is as follows:
62-
// First you need to obtain a state object.
63-
// Account values can be accessed and modified through the object.
64-
// Finally, call commitTrie to write the modified storage trie into a database.
62+
// - First you need to obtain a state object.
63+
// - Account values as well as storages can be accessed and modified through the object.
64+
// - Finally, call commit to return the changes of storage trie and update account data.
6565
type stateObject struct {
6666
db *StateDB
67-
address common.Address // address of ethereum account
68-
addrHash common.Hash // hash of ethereum address of the account
69-
data types.StateAccount // Account data with all mutations applied in the scope of block
67+
address common.Address // address of ethereum account
68+
addrHash common.Hash // hash of ethereum address of the account
69+
origin *types.StateAccount // Account original data without any change applied, nil means it was not existent
70+
data types.StateAccount // Account data with all mutations applied in the scope of block
7071

7172
// DB error.
7273
// State objects are used by the consensus core and VM which are
@@ -79,9 +80,9 @@ type stateObject struct {
7980
trie Trie // storage trie, which becomes non-nil on first access
8081
code Code // contract bytecode, which gets set when code is loaded
8182

82-
originStorage Storage // Storage cache of original entries to dedup rewrites, reset for every transaction
83+
originStorage Storage // Storage cache of original entries to dedup rewrites
8384
pendingStorage Storage // Storage entries that need to be flushed to disk, at the end of an entire block
84-
dirtyStorage Storage // Storage entries that need to be flushed to disk
85+
dirtyStorage Storage // Storage entries that need to be flushed to disk, reset for every transaction
8586

8687
// Cache flags.
8788
dirtyCode bool // true if the code was updated
@@ -105,21 +106,17 @@ func (s *stateObject) empty() bool {
105106
}
106107

107108
// newObject creates a state object.
108-
func newObject(db *StateDB, address common.Address, data types.StateAccount) *stateObject {
109-
if data.Balance == nil {
110-
data.Balance = new(big.Int)
111-
}
112-
if data.CodeHash == nil {
113-
data.CodeHash = types.EmptyCodeHash.Bytes()
114-
}
115-
if data.Root == (common.Hash{}) {
116-
data.Root = types.EmptyRootHash
109+
func newObject(db *StateDB, address common.Address, acct *types.StateAccount) *stateObject {
110+
origin := acct
111+
if acct == nil {
112+
acct = types.NewEmptyStateAccount()
117113
}
118114
return &stateObject{
119115
db: db,
120116
address: address,
121117
addrHash: crypto.Keccak256Hash(address[:]),
122-
data: data,
118+
origin: origin,
119+
data: *acct,
123120
originStorage: make(Storage),
124121
pendingStorage: make(Storage),
125122
dirtyStorage: make(Storage),
@@ -266,34 +263,74 @@ func (s *stateObject) updateTrie(db Database) (Trie, error) {
266263
}
267264
// Track the amount of time wasted on updating the storage trie
268265
defer func(start time.Time) { s.db.StorageUpdates += time.Since(start) }(time.Now())
266+
// The snapshot storage map for the object
267+
var (
268+
storage map[common.Hash][]byte
269+
origin map[common.Hash][]byte
270+
hasher = s.db.hasher
271+
)
269272
tr, err := s.getTrie(db)
270273
if err != nil {
271274
s.setError(err)
272275
return nil, err
273276
}
274277
// Insert all the pending updates into the trie
278+
usedStorage := make([][]byte, 0, len(s.pendingStorage))
275279
for key, value := range s.pendingStorage {
276280
// Skip noop changes, persist actual changes
277281
if value == s.originStorage[key] {
278282
continue
279283
}
284+
prev := s.originStorage[key]
280285
s.originStorage[key] = value
281286

282-
if (value == common.Hash{}) {
287+
// rlp-encoded value to be used by the snapshot
288+
var snapshotVal []byte
289+
if value == (common.Hash{}) {
283290
if err := tr.TryDelete(key[:]); err != nil {
284291
s.setError(err)
285292
return nil, err
286293
}
287294
s.db.StorageDeleted += 1
288295
} else {
296+
trimmedVal := common.TrimLeftZeroes(value[:])
289297
// Encoding []byte cannot fail, ok to ignore the error.
290-
v, _ := rlp.EncodeToBytes(common.TrimLeftZeroes(value[:]))
291-
if err := tr.TryUpdate(key[:], v); err != nil {
298+
snapshotVal, _ := rlp.EncodeToBytes(trimmedVal)
299+
if err := tr.TryUpdate(key[:], snapshotVal); err != nil {
292300
s.setError(err)
293301
return nil, err
294302
}
295303
s.db.StorageUpdated += 1
296304
}
305+
// Cache the mutated storage slots until commit
306+
if storage == nil {
307+
if storage = s.db.storages[s.addrHash]; storage == nil {
308+
storage = make(map[common.Hash][]byte)
309+
s.db.storages[s.addrHash] = storage
310+
}
311+
}
312+
khash := crypto.HashData(hasher, key[:])
313+
storage[khash] = snapshotVal // snapshotVal will be nil if it's deleted
314+
315+
// Cache the original value of mutated storage slots
316+
if origin == nil {
317+
if origin = s.db.storagesOrigin[s.addrHash]; origin == nil {
318+
origin = make(map[common.Hash][]byte)
319+
s.db.storagesOrigin[s.addrHash] = origin
320+
}
321+
}
322+
// Track the original value of slot only if it's mutated first time
323+
if _, ok := origin[khash]; !ok {
324+
if prev == (common.Hash{}) {
325+
origin[khash] = nil // nil if it was not present previously
326+
} else {
327+
// Encoding []byte cannot fail, ok to ignore the error.
328+
b, _ := rlp.EncodeToBytes(common.TrimLeftZeroes(prev[:]))
329+
origin[khash] = b
330+
}
331+
}
332+
// Cache the items for preloading
333+
usedStorage = append(usedStorage, common.CopyBytes(key[:])) // Copy needed for closure
297334
}
298335
if len(s.pendingStorage) > 0 {
299336
s.pendingStorage = make(Storage)
@@ -318,9 +355,8 @@ func (s *stateObject) updateRoot(db Database) {
318355
s.data.Root = tr.Hash()
319356
}
320357

321-
// CommitTrie the storage trie of the object to dwb.
322-
// This updates the trie root.
323-
func (s *stateObject) commitTrie(db Database) (*trie.NodeSet, error) {
358+
// commit returns the changes made in storage trie and updates the account data.
359+
func (s *stateObject) commit(db Database) (*trie.NodeSet, error) {
324360
// If nothing changed, don't bother with hashing anything
325361
tr, err := s.updateTrie(db)
326362
if err != nil {
@@ -331,6 +367,7 @@ func (s *stateObject) commitTrie(db Database) (*trie.NodeSet, error) {
331367
}
332368
// If nothing changed, don't bother with hashing anything
333369
if tr == nil {
370+
s.origin = s.data.Copy()
334371
return nil, nil
335372
}
336373
// Track the amount of time wasted on committing the storage trie
@@ -339,6 +376,9 @@ func (s *stateObject) commitTrie(db Database) (*trie.NodeSet, error) {
339376
if err == nil {
340377
s.data.Root = root
341378
}
379+
380+
// Update original account data after commit
381+
s.origin = s.data.Copy()
342382
return nodes, err
343383
}
344384

@@ -381,18 +421,24 @@ func (s *stateObject) setBalance(amount *big.Int) {
381421
}
382422

383423
func (s *stateObject) deepCopy(db *StateDB) *stateObject {
384-
stateObject := newObject(db, s.address, s.data)
424+
obj := &stateObject{
425+
db: db,
426+
address: s.address,
427+
addrHash: s.addrHash,
428+
origin: s.origin,
429+
data: s.data,
430+
}
385431
if s.trie != nil {
386-
stateObject.trie = db.db.CopyTrie(s.trie)
387-
}
388-
stateObject.code = s.code
389-
stateObject.dirtyStorage = s.dirtyStorage.Copy()
390-
stateObject.originStorage = s.originStorage.Copy()
391-
stateObject.pendingStorage = s.pendingStorage.Copy()
392-
stateObject.selfDestructed = s.selfDestructed
393-
stateObject.dirtyCode = s.dirtyCode
394-
stateObject.deleted = s.deleted
395-
return stateObject
432+
obj.trie = db.db.CopyTrie(s.trie)
433+
}
434+
obj.code = s.code
435+
obj.dirtyStorage = s.dirtyStorage.Copy()
436+
obj.originStorage = s.originStorage.Copy()
437+
obj.pendingStorage = s.pendingStorage.Copy()
438+
obj.selfDestructed = s.selfDestructed
439+
obj.dirtyCode = s.dirtyCode
440+
obj.deleted = s.deleted
441+
return obj
396442
}
397443

398444
//

core/state/state_test.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -31,21 +31,21 @@ import (
3131
"github.com/XinFinOrg/XDPoSChain/trie"
3232
)
3333

34-
type stateTest struct {
34+
type stateEnv struct {
3535
db ethdb.Database
3636
state *StateDB
3737
}
3838

39-
func newStateTest() *stateTest {
39+
func newStateEnv() *stateEnv {
4040
db := rawdb.NewMemoryDatabase()
4141
sdb, _ := New(common.Hash{}, NewDatabase(db))
42-
return &stateTest{db: db, state: sdb}
42+
return &stateEnv{db: db, state: sdb}
4343
}
4444

4545
func TestDump(t *testing.T) {
4646
db := rawdb.NewMemoryDatabase()
4747
sdb, _ := New(common.Hash{}, NewDatabaseWithConfig(db, &trie.Config{Preimages: true}))
48-
s := &stateTest{db: db, state: sdb}
48+
s := &stateEnv{db: db, state: sdb}
4949

5050
// generate a few entries
5151
obj1 := s.state.GetOrNewStateObject(common.BytesToAddress([]byte{0x01}))
@@ -97,7 +97,7 @@ func TestDump(t *testing.T) {
9797
func TestIterativeDump(t *testing.T) {
9898
db := rawdb.NewMemoryDatabase()
9999
sdb, _ := New(types.EmptyRootHash, NewDatabaseWithConfig(db, &trie.Config{Preimages: true}))
100-
s := &stateTest{db: db, state: sdb}
100+
s := &stateEnv{db: db, state: sdb}
101101

102102
// generate a few entries
103103
obj1 := s.state.GetOrNewStateObject(common.BytesToAddress([]byte{0x01}))
@@ -130,7 +130,7 @@ func TestIterativeDump(t *testing.T) {
130130
}
131131

132132
func TestNull(t *testing.T) {
133-
s := newStateTest()
133+
s := newStateEnv()
134134
address := common.HexToAddress("0x823140710bf13990e4500136726d8b55")
135135
s.state.CreateAccount(address)
136136
//value := common.FromHex("0x823140710bf13990e4500136726d8b55")
@@ -152,7 +152,7 @@ func TestSnapshot(t *testing.T) {
152152
var storageaddr common.Hash
153153
data1 := common.BytesToHash([]byte{42})
154154
data2 := common.BytesToHash([]byte{43})
155-
s := newStateTest()
155+
s := newStateEnv()
156156

157157
// snapshot the genesis state
158158
genesis := s.state.Snapshot()
@@ -183,7 +183,7 @@ func TestSnapshot(t *testing.T) {
183183
}
184184

185185
func TestSnapshotEmpty(t *testing.T) {
186-
s := newStateTest()
186+
s := newStateEnv()
187187
s.state.RevertToSnapshot(s.state.Snapshot())
188188
}
189189

0 commit comments

Comments
 (0)