Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
G
Geth-Modification
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
张蕾
Geth-Modification
Commits
223b9509
Unverified
Commit
223b9509
authored
Aug 12, 2019
by
Péter Szilágyi
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
core/state: accumulate writes and only update tries when must
parent
96fb8391
Changes
3
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
167 additions
and
84 deletions
+167
-84
state_object.go
core/state/state_object.go
+39
-16
statedb.go
core/state/statedb.go
+93
-68
statedb_test.go
core/state/statedb_test.go
+35
-0
No files found.
core/state/state_object.go
View file @
223b9509
...
@@ -79,9 +79,10 @@ type stateObject struct {
...
@@ -79,9 +79,10 @@ 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
fakeStorage
Storage
// Fake storage which constructed by caller for debugging purpose.
dirtyStorage
Storage
// Storage entries that have been modified in the current transaction execution
fakeStorage
Storage
// Fake storage which constructed by caller for debugging purpose.
// Cache flags.
// Cache flags.
// When an object is marked suicided it will be delete from the trie
// When an object is marked suicided it will be delete from the trie
...
@@ -113,13 +114,17 @@ func newObject(db *StateDB, address common.Address, data Account) *stateObject {
...
@@ -113,13 +114,17 @@ 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
),
dirtyStorage
:
make
(
Storage
),
pendingStorage
:
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 slots in
the trie
//
Insert all the pending updates into
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
}
}
...
...
core/state/statedb.go
View file @
223b9509
This diff is collapsed.
Click to expand it.
core/state/statedb_test.go
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"
)
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment