Skip to content

Commit a59a93f

Browse files
committed
core/state: track all accounts in canon state
This change introduces a global, per-state cache that keeps account data in the canon state. Thanks to @karalabe for lots of fixes.
1 parent e859f36 commit a59a93f

17 files changed

+414
-336
lines changed

accounts/abi/bind/backends/simulated.go

+2-5
Original file line numberDiff line numberDiff line change
@@ -135,11 +135,8 @@ func (b *SimulatedBackend) StorageAt(ctx context.Context, contract common.Addres
135135
return nil, errBlockNumberUnsupported
136136
}
137137
statedb, _ := b.blockchain.State()
138-
if obj := statedb.GetStateObject(contract); obj != nil {
139-
val := obj.GetState(key)
140-
return val[:], nil
141-
}
142-
return nil, nil
138+
val := statedb.GetState(contract, key)
139+
return val[:], nil
143140
}
144141

145142
// TransactionReceipt returns the receipt of a transaction.

core/blockchain.go

+22-13
Original file line numberDiff line numberDiff line change
@@ -93,10 +93,11 @@ type BlockChain struct {
9393
currentBlock *types.Block // Current head of the block chain
9494
currentFastBlock *types.Block // Current head of the fast-sync chain (may be above the block chain!)
9595

96-
bodyCache *lru.Cache // Cache for the most recent block bodies
97-
bodyRLPCache *lru.Cache // Cache for the most recent block bodies in RLP encoded format
98-
blockCache *lru.Cache // Cache for the most recent entire blocks
99-
futureBlocks *lru.Cache // future blocks are blocks added for later processing
96+
stateCache *state.StateDB // State database to reuse between imports (contains state cache)
97+
bodyCache *lru.Cache // Cache for the most recent block bodies
98+
bodyRLPCache *lru.Cache // Cache for the most recent block bodies in RLP encoded format
99+
blockCache *lru.Cache // Cache for the most recent entire blocks
100+
futureBlocks *lru.Cache // future blocks are blocks added for later processing
100101

101102
quit chan struct{} // blockchain quit channel
102103
running int32 // running must be called atomically
@@ -196,7 +197,15 @@ func (self *BlockChain) loadLastState() error {
196197
self.currentFastBlock = block
197198
}
198199
}
199-
// Issue a status log and return
200+
// Initialize a statedb cache to ensure singleton account bloom filter generation
201+
statedb, err := state.New(self.currentBlock.Root(), self.chainDb)
202+
if err != nil {
203+
return err
204+
}
205+
self.stateCache = statedb
206+
self.stateCache.GetAccount(common.Address{})
207+
208+
// Issue a status log for the user
200209
headerTd := self.GetTd(currentHeader.Hash(), currentHeader.Number.Uint64())
201210
blockTd := self.GetTd(self.currentBlock.Hash(), self.currentBlock.NumberU64())
202211
fastTd := self.GetTd(self.currentFastBlock.Hash(), self.currentFastBlock.NumberU64())
@@ -826,7 +835,6 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) {
826835
tstart = time.Now()
827836

828837
nonceChecked = make([]bool, len(chain))
829-
statedb *state.StateDB
830838
)
831839

832840
// Start the parallel nonce verifier.
@@ -893,29 +901,30 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) {
893901

894902
// Create a new statedb using the parent block and report an
895903
// error if it fails.
896-
if statedb == nil {
897-
statedb, err = state.New(self.GetBlock(block.ParentHash(), block.NumberU64()-1).Root(), self.chainDb)
898-
} else {
899-
err = statedb.Reset(chain[i-1].Root())
904+
switch {
905+
case i == 0:
906+
err = self.stateCache.Reset(self.GetBlock(block.ParentHash(), block.NumberU64()-1).Root())
907+
default:
908+
err = self.stateCache.Reset(chain[i-1].Root())
900909
}
901910
if err != nil {
902911
reportBlock(block, err)
903912
return i, err
904913
}
905914
// Process block using the parent state as reference point.
906-
receipts, logs, usedGas, err := self.processor.Process(block, statedb, self.config.VmConfig)
915+
receipts, logs, usedGas, err := self.processor.Process(block, self.stateCache, self.config.VmConfig)
907916
if err != nil {
908917
reportBlock(block, err)
909918
return i, err
910919
}
911920
// Validate the state using the default validator
912-
err = self.Validator().ValidateState(block, self.GetBlock(block.ParentHash(), block.NumberU64()-1), statedb, receipts, usedGas)
921+
err = self.Validator().ValidateState(block, self.GetBlock(block.ParentHash(), block.NumberU64()-1), self.stateCache, receipts, usedGas)
913922
if err != nil {
914923
reportBlock(block, err)
915924
return i, err
916925
}
917926
// Write state changes to database
918-
_, err = statedb.Commit()
927+
_, err = self.stateCache.Commit()
919928
if err != nil {
920929
return i, err
921930
}

core/chain_makers_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ func ExampleGenerateChain() {
7979
evmux := &event.TypeMux{}
8080
blockchain, _ := NewBlockChain(db, MakeChainConfig(), FakePow{}, evmux)
8181
if i, err := blockchain.InsertChain(chain); err != nil {
82-
fmt.Printf("insert error (block %d): %v\n", i, err)
82+
fmt.Printf("insert error (block %d): %v\n", chain[i].NumberU64(), err)
8383
return
8484
}
8585

core/state/dump.go

+20-27
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,10 @@ import (
2121
"fmt"
2222

2323
"github.com/ethereum/go-ethereum/common"
24+
"github.com/ethereum/go-ethereum/rlp"
2425
)
2526

26-
type Account struct {
27+
type DumpAccount struct {
2728
Balance string `json:"balance"`
2829
Nonce uint64 `json:"nonce"`
2930
Root string `json:"root"`
@@ -32,40 +33,41 @@ type Account struct {
3233
Storage map[string]string `json:"storage"`
3334
}
3435

35-
type World struct {
36-
Root string `json:"root"`
37-
Accounts map[string]Account `json:"accounts"`
36+
type Dump struct {
37+
Root string `json:"root"`
38+
Accounts map[string]DumpAccount `json:"accounts"`
3839
}
3940

40-
func (self *StateDB) RawDump() World {
41-
world := World{
41+
func (self *StateDB) RawDump() Dump {
42+
dump := Dump{
4243
Root: common.Bytes2Hex(self.trie.Root()),
43-
Accounts: make(map[string]Account),
44+
Accounts: make(map[string]DumpAccount),
4445
}
4546

4647
it := self.trie.Iterator()
4748
for it.Next() {
4849
addr := self.trie.GetKey(it.Key)
49-
stateObject, err := DecodeObject(common.BytesToAddress(addr), self.db, it.Value)
50-
if err != nil {
50+
var data Account
51+
if err := rlp.DecodeBytes(it.Value, &data); err != nil {
5152
panic(err)
5253
}
5354

54-
account := Account{
55-
Balance: stateObject.balance.String(),
56-
Nonce: stateObject.nonce,
57-
Root: common.Bytes2Hex(stateObject.Root()),
58-
CodeHash: common.Bytes2Hex(stateObject.codeHash),
59-
Code: common.Bytes2Hex(stateObject.Code()),
55+
obj := NewObject(common.BytesToAddress(addr), data, nil)
56+
account := DumpAccount{
57+
Balance: data.Balance.String(),
58+
Nonce: data.Nonce,
59+
Root: common.Bytes2Hex(data.Root[:]),
60+
CodeHash: common.Bytes2Hex(data.CodeHash),
61+
Code: common.Bytes2Hex(obj.Code(self.db)),
6062
Storage: make(map[string]string),
6163
}
62-
storageIt := stateObject.trie.Iterator()
64+
storageIt := obj.getTrie(self.db).Iterator()
6365
for storageIt.Next() {
6466
account.Storage[common.Bytes2Hex(self.trie.GetKey(storageIt.Key))] = common.Bytes2Hex(storageIt.Value)
6567
}
66-
world.Accounts[common.Bytes2Hex(addr)] = account
68+
dump.Accounts[common.Bytes2Hex(addr)] = account
6769
}
68-
return world
70+
return dump
6971
}
7072

7173
func (self *StateDB) Dump() []byte {
@@ -76,12 +78,3 @@ func (self *StateDB) Dump() []byte {
7678

7779
return json
7880
}
79-
80-
// Debug stuff
81-
func (self *StateObject) CreateOutputForDiff() {
82-
fmt.Printf("%x %x %x %x\n", self.Address(), self.Root(), self.balance.Bytes(), self.nonce)
83-
it := self.trie.Iterator()
84-
for it.Next() {
85-
fmt.Printf("%x %x\n", it.Key, it.Value)
86-
}
87-
}

core/state/managed_state.go

+10-11
Original file line numberDiff line numberDiff line change
@@ -33,14 +33,14 @@ type ManagedState struct {
3333

3434
mu sync.RWMutex
3535

36-
accounts map[string]*account
36+
accounts map[common.Address]*account
3737
}
3838

3939
// ManagedState returns a new managed state with the statedb as it's backing layer
4040
func ManageState(statedb *StateDB) *ManagedState {
4141
return &ManagedState{
4242
StateDB: statedb.Copy(),
43-
accounts: make(map[string]*account),
43+
accounts: make(map[common.Address]*account),
4444
}
4545
}
4646

@@ -103,7 +103,7 @@ func (ms *ManagedState) SetNonce(addr common.Address, nonce uint64) {
103103
so := ms.GetOrNewStateObject(addr)
104104
so.SetNonce(nonce)
105105

106-
ms.accounts[addr.Str()] = newAccount(so)
106+
ms.accounts[addr] = newAccount(so)
107107
}
108108

109109
// HasAccount returns whether the given address is managed or not
@@ -114,29 +114,28 @@ func (ms *ManagedState) HasAccount(addr common.Address) bool {
114114
}
115115

116116
func (ms *ManagedState) hasAccount(addr common.Address) bool {
117-
_, ok := ms.accounts[addr.Str()]
117+
_, ok := ms.accounts[addr]
118118
return ok
119119
}
120120

121121
// populate the managed state
122122
func (ms *ManagedState) getAccount(addr common.Address) *account {
123-
straddr := addr.Str()
124-
if account, ok := ms.accounts[straddr]; !ok {
123+
if account, ok := ms.accounts[addr]; !ok {
125124
so := ms.GetOrNewStateObject(addr)
126-
ms.accounts[straddr] = newAccount(so)
125+
ms.accounts[addr] = newAccount(so)
127126
} else {
128127
// Always make sure the state account nonce isn't actually higher
129128
// than the tracked one.
130129
so := ms.StateDB.GetStateObject(addr)
131-
if so != nil && uint64(len(account.nonces))+account.nstart < so.nonce {
132-
ms.accounts[straddr] = newAccount(so)
130+
if so != nil && uint64(len(account.nonces))+account.nstart < so.Nonce() {
131+
ms.accounts[addr] = newAccount(so)
133132
}
134133

135134
}
136135

137-
return ms.accounts[straddr]
136+
return ms.accounts[addr]
138137
}
139138

140139
func newAccount(so *StateObject) *account {
141-
return &account{so, so.nonce, nil}
140+
return &account{so, so.Nonce(), nil}
142141
}

core/state/managed_state_test.go

+7-6
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,12 @@ func create() (*ManagedState, *account) {
2929
db, _ := ethdb.NewMemDatabase()
3030
statedb, _ := New(common.Hash{}, db)
3131
ms := ManageState(statedb)
32-
so := &StateObject{address: addr, nonce: 100}
33-
ms.StateDB.stateObjects[addr.Str()] = so
34-
ms.accounts[addr.Str()] = newAccount(so)
32+
so := &StateObject{address: addr}
33+
so.SetNonce(100)
34+
ms.StateDB.stateObjects[addr] = so
35+
ms.accounts[addr] = newAccount(so)
3536

36-
return ms, ms.accounts[addr.Str()]
37+
return ms, ms.accounts[addr]
3738
}
3839

3940
func TestNewNonce(t *testing.T) {
@@ -92,15 +93,15 @@ func TestRemoteNonceChange(t *testing.T) {
9293
account.nonces = append(account.nonces, nn...)
9394
nonce := ms.NewNonce(addr)
9495

95-
ms.StateDB.stateObjects[addr.Str()].nonce = 200
96+
ms.StateDB.stateObjects[addr].data.Nonce = 200
9697
nonce = ms.NewNonce(addr)
9798
if nonce != 200 {
9899
t.Error("expected nonce after remote update to be", 201, "got", nonce)
99100
}
100101
ms.NewNonce(addr)
101102
ms.NewNonce(addr)
102103
ms.NewNonce(addr)
103-
ms.StateDB.stateObjects[addr.Str()].nonce = 200
104+
ms.StateDB.stateObjects[addr].data.Nonce = 200
104105
nonce = ms.NewNonce(addr)
105106
if nonce != 204 {
106107
t.Error("expected nonce after remote update to be", 201, "got", nonce)

0 commit comments

Comments
 (0)