good morning!!!!
Skip to content
GitLab
Explore
Sign in
Register
Primary navigation
Search or go to…
Project
B
bor
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Iterations
Wiki
Requirements
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Locked files
Build
Pipelines
Jobs
Pipeline schedules
Test cases
Artifacts
Deploy
Releases
Package registry
Container registry
Harbor Registry
Model registry
Operate
Environments
Terraform modules
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Code review analytics
Issue analytics
Insights
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
GitLab community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
open
bor
Commits
223b9509
Unverified
Commit
223b9509
authored
Aug 12, 2019
by
Péter Szilágyi
Browse files
Options
Downloads
Patches
Plain Diff
core/state: accumulate writes and only update tries when must
parent
96fb8391
Branches
Branches containing commit
Tags
Tags containing commit
No related merge requests found
Changes
3
Show whitespace changes
Inline
Side-by-side
Showing
3 changed files
core/state/state_object.go
+39
-16
39 additions, 16 deletions
core/state/state_object.go
core/state/statedb.go
+93
-68
93 additions, 68 deletions
core/state/statedb.go
core/state/statedb_test.go
+35
-0
35 additions, 0 deletions
core/state/statedb_test.go
with
167 additions
and
84 deletions
core/state/state_object.go
+
39
−
16
View file @
223b9509
...
@@ -79,8 +79,9 @@ type stateObject struct {
...
@@ -79,8 +79,9 @@ type stateObject struct {
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
originStorage
Storage
// Storage cache of original entries to dedup rewrites, reset for every transaction
dirtyStorage
Storage
// Storage entries that need to be flushed to disk
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
fakeStorage
Storage
// Fake storage which constructed by caller for debugging purpose.
fakeStorage
Storage
// Fake storage which constructed by caller for debugging purpose.
// Cache flags.
// Cache flags.
...
@@ -113,12 +114,16 @@ func newObject(db *StateDB, address common.Address, data Account) *stateObject {
...
@@ -113,12 +114,16 @@ func newObject(db *StateDB, address common.Address, data Account) *stateObject {
if
data
.
CodeHash
==
nil
{
if
data
.
CodeHash
==
nil
{
data
.
CodeHash
=
emptyCodeHash
data
.
CodeHash
=
emptyCodeHash
}
}
if
data
.
Root
==
(
common
.
Hash
{})
{
data
.
Root
=
emptyRoot
}
return
&
stateObject
{
return
&
stateObject
{
db
:
db
,
db
:
db
,
address
:
address
,
address
:
address
,
addrHash
:
crypto
.
Keccak256Hash
(
address
[
:
]),
addrHash
:
crypto
.
Keccak256Hash
(
address
[
:
]),
data
:
data
,
data
:
data
,
originStorage
:
make
(
Storage
),
originStorage
:
make
(
Storage
),
pendingStorage
:
make
(
Storage
),
dirtyStorage
:
make
(
Storage
),
dirtyStorage
:
make
(
Storage
),
}
}
}
}
...
@@ -183,9 +188,11 @@ func (s *stateObject) GetCommittedState(db Database, key common.Hash) common.Has
...
@@ -183,9 +188,11 @@ func (s *stateObject) GetCommittedState(db Database, key common.Hash) common.Has
if
s
.
fakeStorage
!=
nil
{
if
s
.
fakeStorage
!=
nil
{
return
s
.
fakeStorage
[
key
]
return
s
.
fakeStorage
[
key
]
}
}
// If we have the original value cached, return that
// If we have a pending write or clean cached, return that
value
,
cached
:=
s
.
originStorage
[
key
]
if
value
,
pending
:=
s
.
pendingStorage
[
key
];
pending
{
if
cached
{
return
value
}
if
value
,
cached
:=
s
.
originStorage
[
key
];
cached
{
return
value
return
value
}
}
// Track the amount of time wasted on reading the storage trie
// Track the amount of time wasted on reading the storage trie
...
@@ -198,6 +205,7 @@ func (s *stateObject) GetCommittedState(db Database, key common.Hash) common.Has
...
@@ -198,6 +205,7 @@ func (s *stateObject) GetCommittedState(db Database, key common.Hash) common.Has
s
.
setError
(
err
)
s
.
setError
(
err
)
return
common
.
Hash
{}
return
common
.
Hash
{}
}
}
var
value
common
.
Hash
if
len
(
enc
)
>
0
{
if
len
(
enc
)
>
0
{
_
,
content
,
_
,
err
:=
rlp
.
Split
(
enc
)
_
,
content
,
_
,
err
:=
rlp
.
Split
(
enc
)
if
err
!=
nil
{
if
err
!=
nil
{
...
@@ -252,17 +260,29 @@ func (s *stateObject) setState(key, value common.Hash) {
...
@@ -252,17 +260,29 @@ func (s *stateObject) setState(key, value common.Hash) {
s
.
dirtyStorage
[
key
]
=
value
s
.
dirtyStorage
[
key
]
=
value
}
}
// finalise moves all dirty storage slots into the pending area to be hashed or
// committed later. It is invoked at the end of every transaction.
func
(
s
*
stateObject
)
finalise
()
{
for
key
,
value
:=
range
s
.
dirtyStorage
{
s
.
pendingStorage
[
key
]
=
value
}
if
len
(
s
.
dirtyStorage
)
>
0
{
s
.
dirtyStorage
=
make
(
Storage
)
}
}
// updateTrie writes cached storage modifications into the object's storage trie.
// updateTrie writes cached storage modifications into the object's storage trie.
func
(
s
*
stateObject
)
updateTrie
(
db
Database
)
Trie
{
func
(
s
*
stateObject
)
updateTrie
(
db
Database
)
Trie
{
// Make sure all dirty slots are finalized into the pending storage area
s
.
finalise
()
// Track the amount of time wasted on updating the storge trie
// Track the amount of time wasted on updating the storge trie
if
metrics
.
EnabledExpensive
{
if
metrics
.
EnabledExpensive
{
defer
func
(
start
time
.
Time
)
{
s
.
db
.
StorageUpdates
+=
time
.
Since
(
start
)
}(
time
.
Now
())
defer
func
(
start
time
.
Time
)
{
s
.
db
.
StorageUpdates
+=
time
.
Since
(
start
)
}(
time
.
Now
())
}
}
//
Update
all the
dirty slot
s in the trie
//
Insert
all the
pending update
s in
to
the trie
tr
:=
s
.
getTrie
(
db
)
tr
:=
s
.
getTrie
(
db
)
for
key
,
value
:=
range
s
.
dirtyStorage
{
for
key
,
value
:=
range
s
.
pendingStorage
{
delete
(
s
.
dirtyStorage
,
key
)
// Skip noop changes, persist actual changes
// Skip noop changes, persist actual changes
if
value
==
s
.
originStorage
[
key
]
{
if
value
==
s
.
originStorage
[
key
]
{
continue
continue
...
@@ -277,6 +297,9 @@ func (s *stateObject) updateTrie(db Database) Trie {
...
@@ -277,6 +297,9 @@ func (s *stateObject) updateTrie(db Database) Trie {
v
,
_
:=
rlp
.
EncodeToBytes
(
common
.
TrimLeftZeroes
(
value
[
:
]))
v
,
_
:=
rlp
.
EncodeToBytes
(
common
.
TrimLeftZeroes
(
value
[
:
]))
s
.
setError
(
tr
.
TryUpdate
(
key
[
:
],
v
))
s
.
setError
(
tr
.
TryUpdate
(
key
[
:
],
v
))
}
}
if
len
(
s
.
pendingStorage
)
>
0
{
s
.
pendingStorage
=
make
(
Storage
)
}
return
tr
return
tr
}
}
...
...
This diff is collapsed.
Click to expand it.
core/state/statedb.go
+
93
−
68
View file @
223b9509
...
@@ -68,7 +68,8 @@ type StateDB struct {
...
@@ -68,7 +68,8 @@ type StateDB struct {
// This map holds 'live' objects, which will get modified while processing a state transition.
// This map holds 'live' objects, which will get modified while processing a state transition.
stateObjects
map
[
common
.
Address
]
*
stateObject
stateObjects
map
[
common
.
Address
]
*
stateObject
stateObjectsDirty
map
[
common
.
Address
]
struct
{}
stateObjectsPending
map
[
common
.
Address
]
struct
{}
// State objects finalized but not yet written to the trie
stateObjectsDirty
map
[
common
.
Address
]
struct
{}
// State objects modified in the current execution
// DB error.
// DB error.
// State objects are used by the consensus core and VM which are
// State objects are used by the consensus core and VM which are
...
@@ -114,6 +115,7 @@ func New(root common.Hash, db Database) (*StateDB, error) {
...
@@ -114,6 +115,7 @@ func New(root common.Hash, db Database) (*StateDB, error) {
db
:
db
,
db
:
db
,
trie
:
tr
,
trie
:
tr
,
stateObjects
:
make
(
map
[
common
.
Address
]
*
stateObject
),
stateObjects
:
make
(
map
[
common
.
Address
]
*
stateObject
),
stateObjectsPending
:
make
(
map
[
common
.
Address
]
struct
{}),
stateObjectsDirty
:
make
(
map
[
common
.
Address
]
struct
{}),
stateObjectsDirty
:
make
(
map
[
common
.
Address
]
struct
{}),
logs
:
make
(
map
[
common
.
Hash
][]
*
types
.
Log
),
logs
:
make
(
map
[
common
.
Hash
][]
*
types
.
Log
),
preimages
:
make
(
map
[
common
.
Hash
][]
byte
),
preimages
:
make
(
map
[
common
.
Hash
][]
byte
),
...
@@ -141,6 +143,7 @@ func (self *StateDB) Reset(root common.Hash) error {
...
@@ -141,6 +143,7 @@ func (self *StateDB) Reset(root common.Hash) error {
}
}
self
.
trie
=
tr
self
.
trie
=
tr
self
.
stateObjects
=
make
(
map
[
common
.
Address
]
*
stateObject
)
self
.
stateObjects
=
make
(
map
[
common
.
Address
]
*
stateObject
)
self
.
stateObjectsPending
=
make
(
map
[
common
.
Address
]
struct
{})
self
.
stateObjectsDirty
=
make
(
map
[
common
.
Address
]
struct
{})
self
.
stateObjectsDirty
=
make
(
map
[
common
.
Address
]
struct
{})
self
.
thash
=
common
.
Hash
{}
self
.
thash
=
common
.
Hash
{}
self
.
bhash
=
common
.
Hash
{}
self
.
bhash
=
common
.
Hash
{}
...
@@ -421,15 +424,15 @@ func (self *StateDB) Suicide(addr common.Address) bool {
...
@@ -421,15 +424,15 @@ func (self *StateDB) Suicide(addr common.Address) bool {
//
//
// updateStateObject writes the given object to the trie.
// updateStateObject writes the given object to the trie.
func
(
s
*
StateDB
)
updateStateObject
(
stateObject
*
stateObject
)
{
func
(
s
*
StateDB
)
updateStateObject
(
obj
*
stateObject
)
{
// Track the amount of time wasted on updating the account from the trie
// Track the amount of time wasted on updating the account from the trie
if
metrics
.
EnabledExpensive
{
if
metrics
.
EnabledExpensive
{
defer
func
(
start
time
.
Time
)
{
s
.
AccountUpdates
+=
time
.
Since
(
start
)
}(
time
.
Now
())
defer
func
(
start
time
.
Time
)
{
s
.
AccountUpdates
+=
time
.
Since
(
start
)
}(
time
.
Now
())
}
}
// Encode the account and update the account trie
// Encode the account and update the account trie
addr
:=
stateObject
.
Address
()
addr
:=
obj
.
Address
()
data
,
err
:=
rlp
.
EncodeToBytes
(
stateObject
)
data
,
err
:=
rlp
.
EncodeToBytes
(
obj
)
if
err
!=
nil
{
if
err
!=
nil
{
panic
(
fmt
.
Errorf
(
"can't encode object at %x: %v"
,
addr
[
:
],
err
))
panic
(
fmt
.
Errorf
(
"can't encode object at %x: %v"
,
addr
[
:
],
err
))
}
}
...
@@ -437,25 +440,33 @@ func (s *StateDB) updateStateObject(stateObject *stateObject) {
...
@@ -437,25 +440,33 @@ func (s *StateDB) updateStateObject(stateObject *stateObject) {
}
}
// deleteStateObject removes the given object from the state trie.
// deleteStateObject removes the given object from the state trie.
func
(
s
*
StateDB
)
deleteStateObject
(
stateObject
*
stateObject
)
{
func
(
s
*
StateDB
)
deleteStateObject
(
obj
*
stateObject
)
{
// Track the amount of time wasted on deleting the account from the trie
// Track the amount of time wasted on deleting the account from the trie
if
metrics
.
EnabledExpensive
{
if
metrics
.
EnabledExpensive
{
defer
func
(
start
time
.
Time
)
{
s
.
AccountUpdates
+=
time
.
Since
(
start
)
}(
time
.
Now
())
defer
func
(
start
time
.
Time
)
{
s
.
AccountUpdates
+=
time
.
Since
(
start
)
}(
time
.
Now
())
}
}
// Delete the account from the trie
// Delete the account from the trie
stateObject
.
deleted
=
true
addr
:=
obj
.
Address
()
addr
:=
stateObject
.
Address
()
s
.
setError
(
s
.
trie
.
TryDelete
(
addr
[
:
]))
s
.
setError
(
s
.
trie
.
TryDelete
(
addr
[
:
]))
}
}
// Retrieve a state object given by the address. Returns nil if not found.
// getStateObject retrieves a state object given by the address, returning nil if
func
(
s
*
StateDB
)
getStateObject
(
addr
common
.
Address
)
(
stateObject
*
stateObject
)
{
// the object is not found or was deleted in this execution context. If you need
// Prefer live objects
// to differentiate between non-existent/just-deleted, use getDeletedStateObject.
if
obj
:=
s
.
stateObjects
[
addr
];
obj
!=
nil
{
func
(
s
*
StateDB
)
getStateObject
(
addr
common
.
Address
)
*
stateObject
{
if
obj
.
deleted
{
if
obj
:=
s
.
getDeletedStateObject
(
addr
);
obj
!=
nil
&&
!
obj
.
deleted
{
return
obj
}
return
nil
return
nil
}
}
// getDeletedStateObject is similar to getStateObject, but instead of returning
// nil for a deleted state object, it returns the actual object with the deleted
// flag set. This is needed by the state journal to revert to the correct self-
// destructed object instead of wiping all knowledge about the state object.
func
(
s
*
StateDB
)
getDeletedStateObject
(
addr
common
.
Address
)
*
stateObject
{
// Prefer live objects if any is available
if
obj
:=
s
.
stateObjects
[
addr
];
obj
!=
nil
{
return
obj
return
obj
}
}
// Track the amount of time wasted on loading the object from the database
// Track the amount of time wasted on loading the object from the database
...
@@ -486,7 +497,7 @@ func (self *StateDB) setStateObject(object *stateObject) {
...
@@ -486,7 +497,7 @@ func (self *StateDB) setStateObject(object *stateObject) {
// Retrieve a state object or create a new state object if nil.
// Retrieve a state object or create a new state object if nil.
func
(
self
*
StateDB
)
GetOrNewStateObject
(
addr
common
.
Address
)
*
stateObject
{
func
(
self
*
StateDB
)
GetOrNewStateObject
(
addr
common
.
Address
)
*
stateObject
{
stateObject
:=
self
.
getStateObject
(
addr
)
stateObject
:=
self
.
getStateObject
(
addr
)
if
stateObject
==
nil
||
stateObject
.
deleted
{
if
stateObject
==
nil
{
stateObject
,
_
=
self
.
createObject
(
addr
)
stateObject
,
_
=
self
.
createObject
(
addr
)
}
}
return
stateObject
return
stateObject
...
@@ -495,7 +506,8 @@ func (self *StateDB) GetOrNewStateObject(addr common.Address) *stateObject {
...
@@ -495,7 +506,8 @@ func (self *StateDB) GetOrNewStateObject(addr common.Address) *stateObject {
// createObject creates a new state object. If there is an existing account with
// createObject creates a new state object. If there is an existing account with
// the given address, it is overwritten and returned as the second return value.
// the given address, it is overwritten and returned as the second return value.
func
(
self
*
StateDB
)
createObject
(
addr
common
.
Address
)
(
newobj
,
prev
*
stateObject
)
{
func
(
self
*
StateDB
)
createObject
(
addr
common
.
Address
)
(
newobj
,
prev
*
stateObject
)
{
prev
=
self
.
getStateObject
(
addr
)
prev
=
self
.
getDeletedStateObject
(
addr
)
// Note, prev might have been deleted, we need that!
newobj
=
newObject
(
self
,
addr
,
Account
{})
newobj
=
newObject
(
self
,
addr
,
Account
{})
newobj
.
setNonce
(
0
)
// sets the object to dirty
newobj
.
setNonce
(
0
)
// sets the object to dirty
if
prev
==
nil
{
if
prev
==
nil
{
...
@@ -561,6 +573,7 @@ func (self *StateDB) Copy() *StateDB {
...
@@ -561,6 +573,7 @@ func (self *StateDB) Copy() *StateDB {
db
:
self
.
db
,
db
:
self
.
db
,
trie
:
self
.
db
.
CopyTrie
(
self
.
trie
),
trie
:
self
.
db
.
CopyTrie
(
self
.
trie
),
stateObjects
:
make
(
map
[
common
.
Address
]
*
stateObject
,
len
(
self
.
journal
.
dirties
)),
stateObjects
:
make
(
map
[
common
.
Address
]
*
stateObject
,
len
(
self
.
journal
.
dirties
)),
stateObjectsPending
:
make
(
map
[
common
.
Address
]
struct
{},
len
(
self
.
stateObjectsPending
)),
stateObjectsDirty
:
make
(
map
[
common
.
Address
]
struct
{},
len
(
self
.
journal
.
dirties
)),
stateObjectsDirty
:
make
(
map
[
common
.
Address
]
struct
{},
len
(
self
.
journal
.
dirties
)),
refund
:
self
.
refund
,
refund
:
self
.
refund
,
logs
:
make
(
map
[
common
.
Hash
][]
*
types
.
Log
,
len
(
self
.
logs
)),
logs
:
make
(
map
[
common
.
Hash
][]
*
types
.
Log
,
len
(
self
.
logs
)),
...
@@ -582,11 +595,17 @@ func (self *StateDB) Copy() *StateDB {
...
@@ -582,11 +595,17 @@ func (self *StateDB) Copy() *StateDB {
// Above, we don't copy the actual journal. This means that if the copy is copied, the
// Above, we don't copy the actual journal. This means that if the copy is copied, the
// loop above will be a no-op, since the copy's journal is empty.
// loop above will be a no-op, since the copy's journal is empty.
// Thus, here we iterate over stateObjects, to enable copies of copies
// Thus, here we iterate over stateObjects, to enable copies of copies
for
addr
:=
range
self
.
stateObjectsPending
{
if
_
,
exist
:=
state
.
stateObjects
[
addr
];
!
exist
{
state
.
stateObjects
[
addr
]
=
self
.
stateObjects
[
addr
]
.
deepCopy
(
state
)
}
state
.
stateObjectsPending
[
addr
]
=
struct
{}{}
}
for
addr
:=
range
self
.
stateObjectsDirty
{
for
addr
:=
range
self
.
stateObjectsDirty
{
if
_
,
exist
:=
state
.
stateObjects
[
addr
];
!
exist
{
if
_
,
exist
:=
state
.
stateObjects
[
addr
];
!
exist
{
state
.
stateObjects
[
addr
]
=
self
.
stateObjects
[
addr
]
.
deepCopy
(
state
)
state
.
stateObjects
[
addr
]
=
self
.
stateObjects
[
addr
]
.
deepCopy
(
state
)
state
.
stateObjectsDirty
[
addr
]
=
struct
{}{}
}
}
state
.
stateObjectsDirty
[
addr
]
=
struct
{}{}
}
}
for
hash
,
logs
:=
range
self
.
logs
{
for
hash
,
logs
:=
range
self
.
logs
{
cpy
:=
make
([]
*
types
.
Log
,
len
(
logs
))
cpy
:=
make
([]
*
types
.
Log
,
len
(
logs
))
...
@@ -631,11 +650,12 @@ func (self *StateDB) GetRefund() uint64 {
...
@@ -631,11 +650,12 @@ func (self *StateDB) GetRefund() uint64 {
return
self
.
refund
return
self
.
refund
}
}
// Finalise finalises the state by removing the self destructed objects
// Finalise finalises the state by removing the self destructed objects and clears
// and clears the journal as well as the refunds.
// the journal as well as the refunds. Finalise, however, will not push any updates
// into the tries just yet. Only IntermediateRoot or Commit will do that.
func
(
s
*
StateDB
)
Finalise
(
deleteEmptyObjects
bool
)
{
func
(
s
*
StateDB
)
Finalise
(
deleteEmptyObjects
bool
)
{
for
addr
:=
range
s
.
journal
.
dirties
{
for
addr
:=
range
s
.
journal
.
dirties
{
stateObject
,
exist
:=
s
.
stateObjects
[
addr
]
obj
,
exist
:=
s
.
stateObjects
[
addr
]
if
!
exist
{
if
!
exist
{
// ripeMD is 'touched' at block 1714175, in tx 0x1237f737031e40bcde4a8b7e717b2d15e3ecadfe49bb1bbc71ee9deb09c6fcf2
// ripeMD is 'touched' at block 1714175, in tx 0x1237f737031e40bcde4a8b7e717b2d15e3ecadfe49bb1bbc71ee9deb09c6fcf2
// That tx goes out of gas, and although the notion of 'touched' does not exist there, the
// That tx goes out of gas, and although the notion of 'touched' does not exist there, the
...
@@ -645,13 +665,12 @@ func (s *StateDB) Finalise(deleteEmptyObjects bool) {
...
@@ -645,13 +665,12 @@ func (s *StateDB) Finalise(deleteEmptyObjects bool) {
// Thus, we can safely ignore it here
// Thus, we can safely ignore it here
continue
continue
}
}
if
obj
.
suicided
||
(
deleteEmptyObjects
&&
obj
.
empty
())
{
if
stateObject
.
suicided
||
(
deleteEmptyObjects
&&
stateObject
.
empty
())
{
obj
.
deleted
=
true
s
.
deleteStateObject
(
stateObject
)
}
else
{
}
else
{
stateObject
.
updateRoot
(
s
.
db
)
obj
.
finalise
()
s
.
updateStateObject
(
stateObject
)
}
}
s
.
stateObjectsPending
[
addr
]
=
struct
{}{}
s
.
stateObjectsDirty
[
addr
]
=
struct
{}{}
s
.
stateObjectsDirty
[
addr
]
=
struct
{}{}
}
}
// Invalidate journal because reverting across transactions is not allowed.
// Invalidate journal because reverting across transactions is not allowed.
...
@@ -662,8 +681,21 @@ func (s *StateDB) Finalise(deleteEmptyObjects bool) {
...
@@ -662,8 +681,21 @@ func (s *StateDB) Finalise(deleteEmptyObjects bool) {
// It is called in between transactions to get the root hash that
// It is called in between transactions to get the root hash that
// goes into transaction receipts.
// goes into transaction receipts.
func
(
s
*
StateDB
)
IntermediateRoot
(
deleteEmptyObjects
bool
)
common
.
Hash
{
func
(
s
*
StateDB
)
IntermediateRoot
(
deleteEmptyObjects
bool
)
common
.
Hash
{
// Finalise all the dirty storage states and write them into the tries
s
.
Finalise
(
deleteEmptyObjects
)
s
.
Finalise
(
deleteEmptyObjects
)
for
addr
:=
range
s
.
stateObjectsPending
{
obj
:=
s
.
stateObjects
[
addr
]
if
obj
.
deleted
{
s
.
deleteStateObject
(
obj
)
}
else
{
obj
.
updateRoot
(
s
.
db
)
s
.
updateStateObject
(
obj
)
}
}
if
len
(
s
.
stateObjectsPending
)
>
0
{
s
.
stateObjectsPending
=
make
(
map
[
common
.
Address
]
struct
{})
}
// Track the amount of time wasted on hashing the account trie
// Track the amount of time wasted on hashing the account trie
if
metrics
.
EnabledExpensive
{
if
metrics
.
EnabledExpensive
{
defer
func
(
start
time
.
Time
)
{
s
.
AccountHashes
+=
time
.
Since
(
start
)
}(
time
.
Now
())
defer
func
(
start
time
.
Time
)
{
s
.
AccountHashes
+=
time
.
Since
(
start
)
}(
time
.
Now
())
...
@@ -680,46 +712,40 @@ func (self *StateDB) Prepare(thash, bhash common.Hash, ti int) {
...
@@ -680,46 +712,40 @@ func (self *StateDB) Prepare(thash, bhash common.Hash, ti int) {
}
}
func
(
s
*
StateDB
)
clearJournalAndRefund
()
{
func
(
s
*
StateDB
)
clearJournalAndRefund
()
{
if
len
(
s
.
journal
.
entries
)
>
0
{
s
.
journal
=
newJournal
()
s
.
journal
=
newJournal
()
s
.
validRevisions
=
s
.
validRevisions
[
:
0
]
s
.
refund
=
0
s
.
refund
=
0
}
}
s
.
validRevisions
=
s
.
validRevisions
[
:
0
]
// Snapshots can be created without journal entires
}
// Commit writes the state to the underlying in-memory trie database.
// Commit writes the state to the underlying in-memory trie database.
func
(
s
*
StateDB
)
Commit
(
deleteEmptyObjects
bool
)
(
root
common
.
Hash
,
err
error
)
{
func
(
s
*
StateDB
)
Commit
(
deleteEmptyObjects
bool
)
(
common
.
Hash
,
error
)
{
defer
s
.
clearJournalAndRefund
()
// Finalize any pending changes and merge everything into the tries
s
.
IntermediateRoot
(
deleteEmptyObjects
)
for
addr
:=
range
s
.
journal
.
dirties
{
s
.
stateObjectsDirty
[
addr
]
=
struct
{}{}
}
// Commit objects to the trie, measuring the elapsed time
// Commit objects to the trie, measuring the elapsed time
for
addr
,
stateObject
:=
range
s
.
stateObjects
{
for
addr
:=
range
s
.
stateObjectsDirty
{
_
,
isDirty
:=
s
.
stateObjectsDirty
[
addr
]
if
obj
:=
s
.
stateObjects
[
addr
];
!
obj
.
deleted
{
switch
{
case
stateObject
.
suicided
||
(
isDirty
&&
deleteEmptyObjects
&&
stateObject
.
empty
())
:
// If the object has been removed, don't bother syncing it
// and just mark it for deletion in the trie.
s
.
deleteStateObject
(
stateObject
)
case
isDirty
:
// Write any contract code associated with the state object
// Write any contract code associated with the state object
if
stateObject
.
code
!=
nil
&&
stateObject
.
dirtyCode
{
if
obj
.
code
!=
nil
&&
obj
.
dirtyCode
{
s
.
db
.
TrieDB
()
.
InsertBlob
(
common
.
BytesToHash
(
stateObject
.
CodeHash
()),
stateObject
.
code
)
s
.
db
.
TrieDB
()
.
InsertBlob
(
common
.
BytesToHash
(
obj
.
CodeHash
()),
obj
.
code
)
stateObject
.
dirtyCode
=
false
obj
.
dirtyCode
=
false
}
}
// Write any storage changes in the state object to its storage trie
.
// Write any storage changes in the state object to its storage trie
if
err
:=
stateObject
.
CommitTrie
(
s
.
db
);
err
!=
nil
{
if
err
:=
obj
.
CommitTrie
(
s
.
db
);
err
!=
nil
{
return
common
.
Hash
{},
err
return
common
.
Hash
{},
err
}
}
// Update the object in the main account trie.
s
.
updateStateObject
(
stateObject
)
}
}
delete
(
s
.
stateObjectsDirty
,
addr
)
}
if
len
(
s
.
stateObjectsDirty
)
>
0
{
s
.
stateObjectsDirty
=
make
(
map
[
common
.
Address
]
struct
{})
}
}
// Write the account trie changes, measuing the amount of wasted time
// Write the account trie changes, measuing the amount of wasted time
if
metrics
.
EnabledExpensive
{
if
metrics
.
EnabledExpensive
{
defer
func
(
start
time
.
Time
)
{
s
.
AccountCommits
+=
time
.
Since
(
start
)
}(
time
.
Now
())
defer
func
(
start
time
.
Time
)
{
s
.
AccountCommits
+=
time
.
Since
(
start
)
}(
time
.
Now
())
}
}
r
oot
,
err
=
s
.
trie
.
Commit
(
func
(
leaf
[]
byte
,
parent
common
.
Hash
)
error
{
r
eturn
s
.
trie
.
Commit
(
func
(
leaf
[]
byte
,
parent
common
.
Hash
)
error
{
var
account
Account
var
account
Account
if
err
:=
rlp
.
DecodeBytes
(
leaf
,
&
account
);
err
!=
nil
{
if
err
:=
rlp
.
DecodeBytes
(
leaf
,
&
account
);
err
!=
nil
{
return
nil
return
nil
...
@@ -733,5 +759,4 @@ func (s *StateDB) Commit(deleteEmptyObjects bool) (root common.Hash, err error)
...
@@ -733,5 +759,4 @@ func (s *StateDB) Commit(deleteEmptyObjects bool) (root common.Hash, err error)
}
}
return
nil
return
nil
})
})
return
root
,
err
}
}
This diff is collapsed.
Click to expand it.
core/state/statedb_test.go
+
35
−
0
View file @
223b9509
...
@@ -449,3 +449,38 @@ func TestCopyOfCopy(t *testing.T) {
...
@@ -449,3 +449,38 @@ func TestCopyOfCopy(t *testing.T) {
t
.
Fatalf
(
"2nd copy fail, expected 42, got %v"
,
got
)
t
.
Fatalf
(
"2nd copy fail, expected 42, got %v"
,
got
)
}
}
}
}
// TestDeleteCreateRevert tests a weird state transition corner case that we hit
// while changing the internals of statedb. The workflow is that a contract is
// self destructed, then in a followup transaction (but same block) it's created
// again and the transaction reverted.
//
// The original statedb implementation flushed dirty objects to the tries after
// each transaction, so this works ok. The rework accumulated writes in memory
// first, but the journal wiped the entire state object on create-revert.
func
TestDeleteCreateRevert
(
t
*
testing
.
T
)
{
// Create an initial state with a single contract
state
,
_
:=
New
(
common
.
Hash
{},
NewDatabase
(
rawdb
.
NewMemoryDatabase
()))
addr
:=
toAddr
([]
byte
(
"so"
))
state
.
SetBalance
(
addr
,
big
.
NewInt
(
1
))
root
,
_
:=
state
.
Commit
(
false
)
state
.
Reset
(
root
)
// Simulate self-destructing in one transaction, then create-reverting in another
state
.
Suicide
(
addr
)
state
.
Finalise
(
true
)
id
:=
state
.
Snapshot
()
state
.
SetBalance
(
addr
,
big
.
NewInt
(
2
))
state
.
RevertToSnapshot
(
id
)
// Commit the entire state and make sure we don't crash and have the correct state
root
,
_
=
state
.
Commit
(
true
)
state
.
Reset
(
root
)
if
state
.
getStateObject
(
addr
)
!=
nil
{
t
.
Fatalf
(
"self-destructed contract came alive"
)
}
}
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment