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
39f4c801
Unverified
Commit
39f4c801
authored
Apr 10, 2018
by
Péter Szilágyi
Committed by
GitHub
Apr 10, 2018
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #15225 from holiman/test_removefrom_dirtyset
Change handling of dirty objects in state
parents
1100e8ba
8c31d289
Changes
7
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
277 additions
and
97 deletions
+277
-97
blockchain_test.go
core/blockchain_test.go
+111
-0
dump.go
core/state/dump.go
+1
-1
journal.go
core/state/journal.go
+113
-19
state_object.go
core/state/state_object.go
+13
-38
statedb.go
core/state/statedb.go
+36
-31
statedb_test.go
core/state/statedb_test.go
+3
-2
state_test.go
tests/state_test.go
+0
-6
No files found.
core/blockchain_test.go
View file @
39f4c801
...
...
@@ -1338,3 +1338,114 @@ func TestLargeReorgTrieGC(t *testing.T) {
}
}
}
// Benchmarks large blocks with value transfers to non-existing accounts
func
benchmarkLargeNumberOfValueToNonexisting
(
b
*
testing
.
B
,
numTxs
,
numBlocks
int
,
recipientFn
func
(
uint64
)
common
.
Address
,
dataFn
func
(
uint64
)
[]
byte
)
{
var
(
signer
=
types
.
HomesteadSigner
{}
testBankKey
,
_
=
crypto
.
HexToECDSA
(
"b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291"
)
testBankAddress
=
crypto
.
PubkeyToAddress
(
testBankKey
.
PublicKey
)
bankFunds
=
big
.
NewInt
(
100000000000000000
)
gspec
=
Genesis
{
Config
:
params
.
TestChainConfig
,
Alloc
:
GenesisAlloc
{
testBankAddress
:
{
Balance
:
bankFunds
},
common
.
HexToAddress
(
"0xc0de"
)
:
{
Code
:
[]
byte
{
0x60
,
0x01
,
0x50
},
Balance
:
big
.
NewInt
(
0
),
},
// push 1, pop
},
GasLimit
:
100e6
,
// 100 M
}
)
// Generate the original common chain segment and the two competing forks
engine
:=
ethash
.
NewFaker
()
db
,
_
:=
ethdb
.
NewMemDatabase
()
genesis
:=
gspec
.
MustCommit
(
db
)
blockGenerator
:=
func
(
i
int
,
block
*
BlockGen
)
{
block
.
SetCoinbase
(
common
.
Address
{
1
})
for
txi
:=
0
;
txi
<
numTxs
;
txi
++
{
uniq
:=
uint64
(
i
*
numTxs
+
txi
)
recipient
:=
recipientFn
(
uniq
)
//recipient := common.BigToAddress(big.NewInt(0).SetUint64(1337 + uniq))
tx
,
err
:=
types
.
SignTx
(
types
.
NewTransaction
(
uniq
,
recipient
,
big
.
NewInt
(
1
),
params
.
TxGas
,
big
.
NewInt
(
1
),
nil
),
signer
,
testBankKey
)
if
err
!=
nil
{
b
.
Error
(
err
)
}
block
.
AddTx
(
tx
)
}
}
shared
,
_
:=
GenerateChain
(
params
.
TestChainConfig
,
genesis
,
engine
,
db
,
numBlocks
,
blockGenerator
)
b
.
StopTimer
()
b
.
ResetTimer
()
for
i
:=
0
;
i
<
b
.
N
;
i
++
{
// Import the shared chain and the original canonical one
diskdb
,
_
:=
ethdb
.
NewMemDatabase
()
gspec
.
MustCommit
(
diskdb
)
chain
,
err
:=
NewBlockChain
(
diskdb
,
nil
,
params
.
TestChainConfig
,
engine
,
vm
.
Config
{})
if
err
!=
nil
{
b
.
Fatalf
(
"failed to create tester chain: %v"
,
err
)
}
b
.
StartTimer
()
if
_
,
err
:=
chain
.
InsertChain
(
shared
);
err
!=
nil
{
b
.
Fatalf
(
"failed to insert shared chain: %v"
,
err
)
}
b
.
StopTimer
()
if
got
:=
chain
.
CurrentBlock
()
.
Transactions
()
.
Len
();
got
!=
numTxs
*
numBlocks
{
b
.
Fatalf
(
"Transactions were not included, expected %d, got %d"
,
(
numTxs
*
numBlocks
),
got
)
}
}
}
func
BenchmarkBlockChain_1x1000ValueTransferToNonexisting
(
b
*
testing
.
B
)
{
var
(
numTxs
=
1000
numBlocks
=
1
)
recipientFn
:=
func
(
nonce
uint64
)
common
.
Address
{
return
common
.
BigToAddress
(
big
.
NewInt
(
0
)
.
SetUint64
(
1337
+
nonce
))
}
dataFn
:=
func
(
nonce
uint64
)
[]
byte
{
return
nil
}
benchmarkLargeNumberOfValueToNonexisting
(
b
,
numTxs
,
numBlocks
,
recipientFn
,
dataFn
)
}
func
BenchmarkBlockChain_1x1000ValueTransferToExisting
(
b
*
testing
.
B
)
{
var
(
numTxs
=
1000
numBlocks
=
1
)
b
.
StopTimer
()
b
.
ResetTimer
()
recipientFn
:=
func
(
nonce
uint64
)
common
.
Address
{
return
common
.
BigToAddress
(
big
.
NewInt
(
0
)
.
SetUint64
(
1337
))
}
dataFn
:=
func
(
nonce
uint64
)
[]
byte
{
return
nil
}
benchmarkLargeNumberOfValueToNonexisting
(
b
,
numTxs
,
numBlocks
,
recipientFn
,
dataFn
)
}
func
BenchmarkBlockChain_1x1000Executions
(
b
*
testing
.
B
)
{
var
(
numTxs
=
1000
numBlocks
=
1
)
b
.
StopTimer
()
b
.
ResetTimer
()
recipientFn
:=
func
(
nonce
uint64
)
common
.
Address
{
return
common
.
BigToAddress
(
big
.
NewInt
(
0
)
.
SetUint64
(
0xc0de
))
}
dataFn
:=
func
(
nonce
uint64
)
[]
byte
{
return
nil
}
benchmarkLargeNumberOfValueToNonexisting
(
b
,
numTxs
,
numBlocks
,
recipientFn
,
dataFn
)
}
core/state/dump.go
View file @
39f4c801
...
...
@@ -53,7 +53,7 @@ func (self *StateDB) RawDump() Dump {
panic
(
err
)
}
obj
:=
newObject
(
nil
,
common
.
BytesToAddress
(
addr
),
data
,
nil
)
obj
:=
newObject
(
nil
,
common
.
BytesToAddress
(
addr
),
data
)
account
:=
DumpAccount
{
Balance
:
data
.
Balance
.
String
(),
Nonce
:
data
.
Nonce
,
...
...
core/state/journal.go
View file @
39f4c801
...
...
@@ -22,11 +22,67 @@ import (
"github.com/ethereum/go-ethereum/common"
)
// journalEntry is a modification entry in the state change journal that can be
// reverted on demand.
type
journalEntry
interface
{
undo
(
*
StateDB
)
// revert undoes the changes introduced by this journal entry.
revert
(
*
StateDB
)
// dirtied returns the Ethereum address modified by this journal entry.
dirtied
()
*
common
.
Address
}
// journal contains the list of state modifications applied since the last state
// commit. These are tracked to be able to be reverted in case of an execution
// exception or revertal request.
type
journal
struct
{
entries
[]
journalEntry
// Current changes tracked by the journal
dirties
map
[
common
.
Address
]
int
// Dirty accounts and the number of changes
}
// newJournal create a new initialized journal.
func
newJournal
()
*
journal
{
return
&
journal
{
dirties
:
make
(
map
[
common
.
Address
]
int
),
}
}
// append inserts a new modification entry to the end of the change journal.
func
(
j
*
journal
)
append
(
entry
journalEntry
)
{
j
.
entries
=
append
(
j
.
entries
,
entry
)
if
addr
:=
entry
.
dirtied
();
addr
!=
nil
{
j
.
dirties
[
*
addr
]
++
}
}
// revert undoes a batch of journalled modifications along with any reverted
// dirty handling too.
func
(
j
*
journal
)
revert
(
statedb
*
StateDB
,
snapshot
int
)
{
for
i
:=
len
(
j
.
entries
)
-
1
;
i
>=
snapshot
;
i
--
{
// Undo the changes made by the operation
j
.
entries
[
i
]
.
revert
(
statedb
)
// Drop any dirty tracking induced by the change
if
addr
:=
j
.
entries
[
i
]
.
dirtied
();
addr
!=
nil
{
if
j
.
dirties
[
*
addr
]
--
;
j
.
dirties
[
*
addr
]
==
0
{
delete
(
j
.
dirties
,
*
addr
)
}
}
}
j
.
entries
=
j
.
entries
[
:
snapshot
]
}
// dirty explicitly sets an address to dirty, even if the change entries would
// otherwise suggest it as clean. This method is an ugly hack to handle the RIPEMD
// precompile consensus exception.
func
(
j
*
journal
)
dirty
(
addr
common
.
Address
)
{
j
.
dirties
[
addr
]
++
}
type
journal
[]
journalEntry
// length returns the current number of entries in the journal.
func
(
j
*
journal
)
length
()
int
{
return
len
(
j
.
entries
)
}
type
(
// Changes to the account trie.
...
...
@@ -77,16 +133,24 @@ type (
}
)
func
(
ch
createObjectChange
)
undo
(
s
*
StateDB
)
{
func
(
ch
createObjectChange
)
revert
(
s
*
StateDB
)
{
delete
(
s
.
stateObjects
,
*
ch
.
account
)
delete
(
s
.
stateObjectsDirty
,
*
ch
.
account
)
}
func
(
ch
resetObjectChange
)
undo
(
s
*
StateDB
)
{
func
(
ch
createObjectChange
)
dirtied
()
*
common
.
Address
{
return
ch
.
account
}
func
(
ch
resetObjectChange
)
revert
(
s
*
StateDB
)
{
s
.
setStateObject
(
ch
.
prev
)
}
func
(
ch
suicideChange
)
undo
(
s
*
StateDB
)
{
func
(
ch
resetObjectChange
)
dirtied
()
*
common
.
Address
{
return
nil
}
func
(
ch
suicideChange
)
revert
(
s
*
StateDB
)
{
obj
:=
s
.
getStateObject
(
*
ch
.
account
)
if
obj
!=
nil
{
obj
.
suicided
=
ch
.
prev
...
...
@@ -94,38 +158,60 @@ func (ch suicideChange) undo(s *StateDB) {
}
}
func
(
ch
suicideChange
)
dirtied
()
*
common
.
Address
{
return
ch
.
account
}
var
ripemd
=
common
.
HexToAddress
(
"0000000000000000000000000000000000000003"
)
func
(
ch
touchChange
)
undo
(
s
*
StateDB
)
{
if
!
ch
.
prev
&&
*
ch
.
account
!=
ripemd
{
s
.
getStateObject
(
*
ch
.
account
)
.
touched
=
ch
.
prev
if
!
ch
.
prevDirty
{
delete
(
s
.
stateObjectsDirty
,
*
ch
.
account
)
}
}
func
(
ch
touchChange
)
revert
(
s
*
StateDB
)
{
}
func
(
ch
touchChange
)
dirtied
()
*
common
.
Address
{
return
ch
.
account
}
func
(
ch
balanceChange
)
undo
(
s
*
StateDB
)
{
func
(
ch
balanceChange
)
revert
(
s
*
StateDB
)
{
s
.
getStateObject
(
*
ch
.
account
)
.
setBalance
(
ch
.
prev
)
}
func
(
ch
nonceChange
)
undo
(
s
*
StateDB
)
{
func
(
ch
balanceChange
)
dirtied
()
*
common
.
Address
{
return
ch
.
account
}
func
(
ch
nonceChange
)
revert
(
s
*
StateDB
)
{
s
.
getStateObject
(
*
ch
.
account
)
.
setNonce
(
ch
.
prev
)
}
func
(
ch
codeChange
)
undo
(
s
*
StateDB
)
{
func
(
ch
nonceChange
)
dirtied
()
*
common
.
Address
{
return
ch
.
account
}
func
(
ch
codeChange
)
revert
(
s
*
StateDB
)
{
s
.
getStateObject
(
*
ch
.
account
)
.
setCode
(
common
.
BytesToHash
(
ch
.
prevhash
),
ch
.
prevcode
)
}
func
(
ch
storageChange
)
undo
(
s
*
StateDB
)
{
func
(
ch
codeChange
)
dirtied
()
*
common
.
Address
{
return
ch
.
account
}
func
(
ch
storageChange
)
revert
(
s
*
StateDB
)
{
s
.
getStateObject
(
*
ch
.
account
)
.
setState
(
ch
.
key
,
ch
.
prevalue
)
}
func
(
ch
refundChange
)
undo
(
s
*
StateDB
)
{
func
(
ch
storageChange
)
dirtied
()
*
common
.
Address
{
return
ch
.
account
}
func
(
ch
refundChange
)
revert
(
s
*
StateDB
)
{
s
.
refund
=
ch
.
prev
}
func
(
ch
addLogChange
)
undo
(
s
*
StateDB
)
{
func
(
ch
refundChange
)
dirtied
()
*
common
.
Address
{
return
nil
}
func
(
ch
addLogChange
)
revert
(
s
*
StateDB
)
{
logs
:=
s
.
logs
[
ch
.
txhash
]
if
len
(
logs
)
==
1
{
delete
(
s
.
logs
,
ch
.
txhash
)
...
...
@@ -135,6 +221,14 @@ func (ch addLogChange) undo(s *StateDB) {
s
.
logSize
--
}
func
(
ch
addPreimageChange
)
undo
(
s
*
StateDB
)
{
func
(
ch
addLogChange
)
dirtied
()
*
common
.
Address
{
return
nil
}
func
(
ch
addPreimageChange
)
revert
(
s
*
StateDB
)
{
delete
(
s
.
preimages
,
ch
.
hash
)
}
func
(
ch
addPreimageChange
)
dirtied
()
*
common
.
Address
{
return
nil
}
core/state/state_object.go
View file @
39f4c801
...
...
@@ -85,9 +85,7 @@ type stateObject struct {
// during the "update" phase of the state transition.
dirtyCode
bool
// true if the code was updated
suicided
bool
touched
bool
deleted
bool
onDirty
func
(
addr
common
.
Address
)
// Callback method to mark a state object newly dirty
}
// empty returns whether the account is considered empty.
...
...
@@ -105,7 +103,7 @@ type Account struct {
}
// newObject creates a state object.
func
newObject
(
db
*
StateDB
,
address
common
.
Address
,
data
Account
,
onDirty
func
(
addr
common
.
Address
)
)
*
stateObject
{
func
newObject
(
db
*
StateDB
,
address
common
.
Address
,
data
Account
)
*
stateObject
{
if
data
.
Balance
==
nil
{
data
.
Balance
=
new
(
big
.
Int
)
}
...
...
@@ -119,7 +117,6 @@ func newObject(db *StateDB, address common.Address, data Account, onDirty func(a
data
:
data
,
cachedStorage
:
make
(
Storage
),
dirtyStorage
:
make
(
Storage
),
onDirty
:
onDirty
,
}
}
...
...
@@ -137,23 +134,17 @@ func (self *stateObject) setError(err error) {
func
(
self
*
stateObject
)
markSuicided
()
{
self
.
suicided
=
true
if
self
.
onDirty
!=
nil
{
self
.
onDirty
(
self
.
Address
())
self
.
onDirty
=
nil
}
}
func
(
c
*
stateObject
)
touch
()
{
c
.
db
.
journal
=
append
(
c
.
db
.
journal
,
touchChange
{
account
:
&
c
.
address
,
prev
:
c
.
touched
,
prevDirty
:
c
.
onDirty
==
nil
,
c
.
db
.
journal
.
append
(
touchChange
{
account
:
&
c
.
address
,
})
if
c
.
onDirty
!=
nil
{
c
.
onDirty
(
c
.
Address
())
c
.
onDirty
=
nil
if
c
.
address
==
ripemd
{
// Explicitly put it in the dirty-cache, which is otherwise generated from
// flattened journals.
c
.
db
.
journal
.
dirty
(
c
.
address
)
}
c
.
touched
=
true
}
func
(
c
*
stateObject
)
getTrie
(
db
Database
)
Trie
{
...
...
@@ -195,7 +186,7 @@ func (self *stateObject) GetState(db Database, key common.Hash) common.Hash {
// SetState updates a value in account storage.
func
(
self
*
stateObject
)
SetState
(
db
Database
,
key
,
value
common
.
Hash
)
{
self
.
db
.
journal
=
append
(
self
.
db
.
journal
,
storageChange
{
self
.
db
.
journal
.
append
(
storageChange
{
account
:
&
self
.
address
,
key
:
key
,
prevalue
:
self
.
GetState
(
db
,
key
),
...
...
@@ -207,10 +198,6 @@ func (self *stateObject) setState(key, value common.Hash) {
self
.
cachedStorage
[
key
]
=
value
self
.
dirtyStorage
[
key
]
=
value
if
self
.
onDirty
!=
nil
{
self
.
onDirty
(
self
.
Address
())
self
.
onDirty
=
nil
}
}
// updateTrie writes cached storage modifications into the object's storage trie.
...
...
@@ -274,7 +261,7 @@ func (c *stateObject) SubBalance(amount *big.Int) {
}
func
(
self
*
stateObject
)
SetBalance
(
amount
*
big
.
Int
)
{
self
.
db
.
journal
=
append
(
self
.
db
.
journal
,
balanceChange
{
self
.
db
.
journal
.
append
(
balanceChange
{
account
:
&
self
.
address
,
prev
:
new
(
big
.
Int
)
.
Set
(
self
.
data
.
Balance
),
})
...
...
@@ -283,17 +270,13 @@ func (self *stateObject) SetBalance(amount *big.Int) {
func
(
self
*
stateObject
)
setBalance
(
amount
*
big
.
Int
)
{
self
.
data
.
Balance
=
amount
if
self
.
onDirty
!=
nil
{
self
.
onDirty
(
self
.
Address
())
self
.
onDirty
=
nil
}
}
// Return the gas back to the origin. Used by the Virtual machine or Closures
func
(
c
*
stateObject
)
ReturnGas
(
gas
*
big
.
Int
)
{}
func
(
self
*
stateObject
)
deepCopy
(
db
*
StateDB
,
onDirty
func
(
addr
common
.
Address
)
)
*
stateObject
{
stateObject
:=
newObject
(
db
,
self
.
address
,
self
.
data
,
onDirty
)
func
(
self
*
stateObject
)
deepCopy
(
db
*
StateDB
)
*
stateObject
{
stateObject
:=
newObject
(
db
,
self
.
address
,
self
.
data
)
if
self
.
trie
!=
nil
{
stateObject
.
trie
=
db
.
db
.
CopyTrie
(
self
.
trie
)
}
...
...
@@ -333,7 +316,7 @@ func (self *stateObject) Code(db Database) []byte {
func
(
self
*
stateObject
)
SetCode
(
codeHash
common
.
Hash
,
code
[]
byte
)
{
prevcode
:=
self
.
Code
(
self
.
db
.
db
)
self
.
db
.
journal
=
append
(
self
.
db
.
journal
,
codeChange
{
self
.
db
.
journal
.
append
(
codeChange
{
account
:
&
self
.
address
,
prevhash
:
self
.
CodeHash
(),
prevcode
:
prevcode
,
...
...
@@ -345,14 +328,10 @@ func (self *stateObject) setCode(codeHash common.Hash, code []byte) {
self
.
code
=
code
self
.
data
.
CodeHash
=
codeHash
[
:
]
self
.
dirtyCode
=
true
if
self
.
onDirty
!=
nil
{
self
.
onDirty
(
self
.
Address
())
self
.
onDirty
=
nil
}
}
func
(
self
*
stateObject
)
SetNonce
(
nonce
uint64
)
{
self
.
db
.
journal
=
append
(
self
.
db
.
journal
,
nonceChange
{
self
.
db
.
journal
.
append
(
nonceChange
{
account
:
&
self
.
address
,
prev
:
self
.
data
.
Nonce
,
})
...
...
@@ -361,10 +340,6 @@ func (self *stateObject) SetNonce(nonce uint64) {
func
(
self
*
stateObject
)
setNonce
(
nonce
uint64
)
{
self
.
data
.
Nonce
=
nonce
if
self
.
onDirty
!=
nil
{
self
.
onDirty
(
self
.
Address
())
self
.
onDirty
=
nil
}
}
func
(
self
*
stateObject
)
CodeHash
()
[]
byte
{
...
...
core/state/statedb.go
View file @
39f4c801
...
...
@@ -76,7 +76,7 @@ type StateDB struct {
// Journal of state modifications. This is the backbone of
// Snapshot and RevertToSnapshot.
journal
journal
journal
*
journal
validRevisions
[]
revision
nextRevisionId
int
...
...
@@ -96,6 +96,7 @@ func New(root common.Hash, db Database) (*StateDB, error) {
stateObjectsDirty
:
make
(
map
[
common
.
Address
]
struct
{}),
logs
:
make
(
map
[
common
.
Hash
][]
*
types
.
Log
),
preimages
:
make
(
map
[
common
.
Hash
][]
byte
),
journal
:
newJournal
(),
},
nil
}
...
...
@@ -131,7 +132,7 @@ func (self *StateDB) Reset(root common.Hash) error {
}
func
(
self
*
StateDB
)
AddLog
(
log
*
types
.
Log
)
{
self
.
journal
=
append
(
self
.
journal
,
addLogChange
{
txhash
:
self
.
thash
})
self
.
journal
.
append
(
addLogChange
{
txhash
:
self
.
thash
})
log
.
TxHash
=
self
.
thash
log
.
BlockHash
=
self
.
bhash
...
...
@@ -156,7 +157,7 @@ func (self *StateDB) Logs() []*types.Log {
// AddPreimage records a SHA3 preimage seen by the VM.
func
(
self
*
StateDB
)
AddPreimage
(
hash
common
.
Hash
,
preimage
[]
byte
)
{
if
_
,
ok
:=
self
.
preimages
[
hash
];
!
ok
{
self
.
journal
=
append
(
self
.
journal
,
addPreimageChange
{
hash
:
hash
})
self
.
journal
.
append
(
addPreimageChange
{
hash
:
hash
})
pi
:=
make
([]
byte
,
len
(
preimage
))
copy
(
pi
,
preimage
)
self
.
preimages
[
hash
]
=
pi
...
...
@@ -169,7 +170,7 @@ func (self *StateDB) Preimages() map[common.Hash][]byte {
}
func
(
self
*
StateDB
)
AddRefund
(
gas
uint64
)
{
self
.
journal
=
append
(
self
.
journal
,
refundChange
{
prev
:
self
.
refund
})
self
.
journal
.
append
(
refundChange
{
prev
:
self
.
refund
})
self
.
refund
+=
gas
}
...
...
@@ -255,7 +256,7 @@ func (self *StateDB) StorageTrie(addr common.Address) Trie {
if
stateObject
==
nil
{
return
nil
}
cpy
:=
stateObject
.
deepCopy
(
self
,
nil
)
cpy
:=
stateObject
.
deepCopy
(
self
)
return
cpy
.
updateTrie
(
self
.
db
)
}
...
...
@@ -325,7 +326,7 @@ func (self *StateDB) Suicide(addr common.Address) bool {
if
stateObject
==
nil
{
return
false
}
self
.
journal
=
append
(
self
.
journal
,
suicideChange
{
self
.
journal
.
append
(
suicideChange
{
account
:
&
addr
,
prev
:
stateObject
.
suicided
,
prevbalance
:
new
(
big
.
Int
)
.
Set
(
stateObject
.
Balance
()),
...
...
@@ -379,7 +380,7 @@ func (self *StateDB) getStateObject(addr common.Address) (stateObject *stateObje
return
nil
}
// Insert into the live set.
obj
:=
newObject
(
self
,
addr
,
data
,
self
.
MarkStateObjectDirty
)
obj
:=
newObject
(
self
,
addr
,
data
)
self
.
setStateObject
(
obj
)
return
obj
}
...
...
@@ -397,22 +398,16 @@ func (self *StateDB) GetOrNewStateObject(addr common.Address) *stateObject {
return
stateObject
}
// MarkStateObjectDirty adds the specified object to the dirty map to avoid costly
// state object cache iteration to find a handful of modified ones.
func
(
self
*
StateDB
)
MarkStateObjectDirty
(
addr
common
.
Address
)
{
self
.
stateObjectsDirty
[
addr
]
=
struct
{}{}
}
// 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.
func
(
self
*
StateDB
)
createObject
(
addr
common
.
Address
)
(
newobj
,
prev
*
stateObject
)
{
prev
=
self
.
getStateObject
(
addr
)
newobj
=
newObject
(
self
,
addr
,
Account
{}
,
self
.
MarkStateObjectDirty
)
newobj
=
newObject
(
self
,
addr
,
Account
{})
newobj
.
setNonce
(
0
)
// sets the object to dirty
if
prev
==
nil
{
self
.
journal
=
append
(
self
.
journal
,
createObjectChange
{
account
:
&
addr
})
self
.
journal
.
append
(
createObjectChange
{
account
:
&
addr
})
}
else
{
self
.
journal
=
append
(
self
.
journal
,
resetObjectChange
{
prev
:
prev
})
self
.
journal
.
append
(
resetObjectChange
{
prev
:
prev
})
}
self
.
setStateObject
(
newobj
)
return
newobj
,
prev
...
...
@@ -466,16 +461,17 @@ func (self *StateDB) Copy() *StateDB {
state
:=
&
StateDB
{
db
:
self
.
db
,
trie
:
self
.
db
.
CopyTrie
(
self
.
trie
),
stateObjects
:
make
(
map
[
common
.
Address
]
*
stateObject
,
len
(
self
.
stateObjectsDirty
)),
stateObjectsDirty
:
make
(
map
[
common
.
Address
]
struct
{},
len
(
self
.
stateObjectsDirty
)),
stateObjects
:
make
(
map
[
common
.
Address
]
*
stateObject
,
len
(
self
.
journal
.
dirties
)),
stateObjectsDirty
:
make
(
map
[
common
.
Address
]
struct
{},
len
(
self
.
journal
.
dirties
)),
refund
:
self
.
refund
,
logs
:
make
(
map
[
common
.
Hash
][]
*
types
.
Log
,
len
(
self
.
logs
)),
logSize
:
self
.
logSize
,
preimages
:
make
(
map
[
common
.
Hash
][]
byte
),
journal
:
newJournal
(),
}
// Copy the dirty states, logs, and preimages
for
addr
:=
range
self
.
stateObjectsDirty
{
state
.
stateObjects
[
addr
]
=
self
.
stateObjects
[
addr
]
.
deepCopy
(
state
,
state
.
MarkStateObjectDirty
)
for
addr
:=
range
self
.
journal
.
dirties
{
state
.
stateObjects
[
addr
]
=
self
.
stateObjects
[
addr
]
.
deepCopy
(
state
)
state
.
stateObjectsDirty
[
addr
]
=
struct
{}{}
}
for
hash
,
logs
:=
range
self
.
logs
{
...
...
@@ -492,7 +488,7 @@ func (self *StateDB) Copy() *StateDB {
func
(
self
*
StateDB
)
Snapshot
()
int
{
id
:=
self
.
nextRevisionId
self
.
nextRevisionId
++
self
.
validRevisions
=
append
(
self
.
validRevisions
,
revision
{
id
,
len
(
self
.
journal
)})
self
.
validRevisions
=
append
(
self
.
validRevisions
,
revision
{
id
,
self
.
journal
.
length
(
)})
return
id
}
...
...
@@ -507,13 +503,8 @@ func (self *StateDB) RevertToSnapshot(revid int) {
}
snapshot
:=
self
.
validRevisions
[
idx
]
.
journalIndex
// Replay the journal to undo changes.
for
i
:=
len
(
self
.
journal
)
-
1
;
i
>=
snapshot
;
i
--
{
self
.
journal
[
i
]
.
undo
(
self
)
}
self
.
journal
=
self
.
journal
[
:
snapshot
]
// Remove invalidated snapshots from the stack.
// Replay the journal to undo changes and remove invalidated snapshots
self
.
journal
.
revert
(
self
,
snapshot
)
self
.
validRevisions
=
self
.
validRevisions
[
:
idx
]
}
...
...
@@ -525,14 +516,25 @@ func (self *StateDB) GetRefund() uint64 {
// Finalise finalises the state by removing the self destructed objects
// and clears the journal as well as the refunds.
func
(
s
*
StateDB
)
Finalise
(
deleteEmptyObjects
bool
)
{
for
addr
:=
range
s
.
stateObjectsDirty
{
stateObject
:=
s
.
stateObjects
[
addr
]
for
addr
:=
range
s
.
journal
.
dirties
{
stateObject
,
exist
:=
s
.
stateObjects
[
addr
]
if
!
exist
{
// 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
// touch-event will still be recorded in the journal. Since ripeMD is a special snowflake,
// it will persist in the journal even though the journal is reverted. In this special circumstance,
// it may exist in `s.journal.dirties` but not in `s.stateObjects`.
// Thus, we can safely ignore it here
continue
}
if
stateObject
.
suicided
||
(
deleteEmptyObjects
&&
stateObject
.
empty
())
{
s
.
deleteStateObject
(
stateObject
)
}
else
{
stateObject
.
updateRoot
(
s
.
db
)
s
.
updateStateObject
(
stateObject
)
}
s
.
stateObjectsDirty
[
addr
]
=
struct
{}{}
}
// Invalidate journal because reverting across transactions is not allowed.
s
.
clearJournalAndRefund
()
...
...
@@ -576,7 +578,7 @@ func (s *StateDB) DeleteSuicides() {
}
func
(
s
*
StateDB
)
clearJournalAndRefund
()
{
s
.
journal
=
n
il
s
.
journal
=
n
ewJournal
()
s
.
validRevisions
=
s
.
validRevisions
[
:
0
]
s
.
refund
=
0
}
...
...
@@ -585,6 +587,9 @@ func (s *StateDB) clearJournalAndRefund() {
func
(
s
*
StateDB
)
Commit
(
deleteEmptyObjects
bool
)
(
root
common
.
Hash
,
err
error
)
{
defer
s
.
clearJournalAndRefund
()
for
addr
:=
range
s
.
journal
.
dirties
{
s
.
stateObjectsDirty
[
addr
]
=
struct
{}{}
}
// Commit objects to the trie.
for
addr
,
stateObject
:=
range
s
.
stateObjects
{
_
,
isDirty
:=
s
.
stateObjectsDirty
[
addr
]
...
...
core/state/statedb_test.go
View file @
39f4c801
...
...
@@ -413,11 +413,12 @@ func (s *StateSuite) TestTouchDelete(c *check.C) {
snapshot
:=
s
.
state
.
Snapshot
()
s
.
state
.
AddBalance
(
common
.
Address
{},
new
(
big
.
Int
))
if
len
(
s
.
state
.
stateObjectsDirty
)
!=
1
{
if
len
(
s
.
state
.
journal
.
dirties
)
!=
1
{
c
.
Fatal
(
"expected one dirty state object"
)
}
s
.
state
.
RevertToSnapshot
(
snapshot
)
if
len
(
s
.
state
.
stateObjectsDirty
)
!=
0
{
if
len
(
s
.
state
.
journal
.
dirties
)
!=
0
{
c
.
Fatal
(
"expected no dirty state object"
)
}
}
tests/state_test.go
View file @
39f4c801
...
...
@@ -36,14 +36,8 @@ func TestState(t *testing.T) {
st
.
skipLoad
(
`^stTransactionTest/zeroSigTransa[^/]*\.json`
)
// EIP-86 is not supported yet
// Expected failures:
st
.
fails
(
`^stRevertTest/RevertPrecompiledTouch\.json/EIP158`
,
"bug in test"
)
st
.
fails
(
`^stRevertTest/RevertPrefoundEmptyOOG\.json/EIP158`
,
"bug in test"
)
st
.
fails
(
`^stRevertTest/RevertPrecompiledTouch\.json/Byzantium`
,
"bug in test"
)
st
.
fails
(
`^stRevertTest/RevertPrefoundEmptyOOG\.json/Byzantium`
,
"bug in test"
)
st
.
fails
(
`^stRandom2/randomStatetest64[45]\.json/(EIP150|Frontier|Homestead)/.*`
,
"known bug #15119"
)
st
.
fails
(
`^stCreateTest/TransactionCollisionToEmpty\.json/EIP158/2`
,
"known bug "
)
st
.
fails
(
`^stCreateTest/TransactionCollisionToEmpty\.json/EIP158/3`
,
"known bug "
)
st
.
fails
(
`^stCreateTest/TransactionCollisionToEmpty\.json/Byzantium/2`
,
"known bug "
)
st
.
fails
(
`^stCreateTest/TransactionCollisionToEmpty\.json/Byzantium/3`
,
"known bug "
)
st
.
walk
(
t
,
stateTestDir
,
func
(
t
*
testing
.
T
,
name
string
,
test
*
StateTest
)
{
for
_
,
subtest
:=
range
test
.
Subtests
()
{
...
...
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