mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-25 14:16:44 +00:00
core/state: track all accounts in canon state
This change introduces a global, per-state cache that keeps account data in the canon state.
This commit is contained in:
parent
aa2a4f8659
commit
8ecd29b4de
4 changed files with 90 additions and 61 deletions
|
|
@ -21,6 +21,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
)
|
)
|
||||||
|
|
||||||
type DumpAccount struct {
|
type DumpAccount struct {
|
||||||
|
|
@ -46,20 +47,22 @@ func (self *StateDB) RawDump() Dump {
|
||||||
it := self.trie.Iterator()
|
it := self.trie.Iterator()
|
||||||
for it.Next() {
|
for it.Next() {
|
||||||
addr := self.trie.GetKey(it.Key)
|
addr := self.trie.GetKey(it.Key)
|
||||||
stateObject, err := DecodeObject(common.BytesToAddress(addr), self.db, it.Value)
|
var data Account
|
||||||
if err != nil {
|
if err := rlp.DecodeBytes(it.Value, &data); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
obj := NewObject(common.BytesToAddress(addr), data)
|
||||||
account := DumpAccount{
|
account := DumpAccount{
|
||||||
Balance: stateObject.Balance().String(),
|
Balance: data.Balance.String(),
|
||||||
Nonce: stateObject.Nonce(),
|
Nonce: data.Nonce,
|
||||||
Root: common.Bytes2Hex(stateObject.data.Root[:]),
|
Root: common.Bytes2Hex(data.Root[:]),
|
||||||
CodeHash: common.Bytes2Hex(stateObject.CodeHash()),
|
CodeHash: common.Bytes2Hex(data.CodeHash),
|
||||||
Code: common.Bytes2Hex(stateObject.Code(self.db)),
|
Code: common.Bytes2Hex(obj.Code(self.db)),
|
||||||
Storage: make(map[string]string),
|
Storage: make(map[string]string),
|
||||||
}
|
}
|
||||||
stateObject.initTrie(self.db)
|
obj.initTrie(self.db)
|
||||||
storageIt := stateObject.trie.Iterator()
|
storageIt := obj.trie.Iterator()
|
||||||
for storageIt.Next() {
|
for storageIt.Next() {
|
||||||
account.Storage[common.Bytes2Hex(self.trie.GetKey(storageIt.Key))] = common.Bytes2Hex(storageIt.Value)
|
account.Storage[common.Bytes2Hex(self.trie.GetKey(storageIt.Key))] = common.Bytes2Hex(storageIt.Value)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,7 @@ func (self Storage) Copy() Storage {
|
||||||
// Finally, call CommitTrie to write the modified storage trie into a database.
|
// Finally, call CommitTrie to write the modified storage trie into a database.
|
||||||
type StateObject struct {
|
type StateObject struct {
|
||||||
address common.Address // Ethereum address of this account
|
address common.Address // Ethereum address of this account
|
||||||
data accountData
|
data Account
|
||||||
|
|
||||||
// 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
|
||||||
|
|
@ -87,23 +87,29 @@ type StateObject struct {
|
||||||
deleted bool
|
deleted bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// accountData is the Ethereum consensus representation of accounts.
|
// Account is the Ethereum consensus representation of accounts.
|
||||||
// These objects are stored in the main account trie.
|
// These objects are stored in the main account trie.
|
||||||
type accountData struct {
|
type Account struct {
|
||||||
Nonce uint64
|
Nonce uint64
|
||||||
Balance *big.Int
|
Balance *big.Int
|
||||||
Root common.Hash // merkle root of the storage trie
|
Root common.Hash // merkle root of the storage trie
|
||||||
CodeHash []byte
|
CodeHash []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewStateObject(address common.Address, db trie.Database) *StateObject {
|
// NewObject creates a state object.
|
||||||
object := &StateObject{
|
func NewObject(address common.Address, data Account) *StateObject {
|
||||||
address: address,
|
if data.Balance == nil {
|
||||||
data: accountData{Balance: new(big.Int), CodeHash: emptyCodeHash},
|
data.Balance = new(big.Int)
|
||||||
storage: make(Storage),
|
|
||||||
dirty: true,
|
|
||||||
}
|
}
|
||||||
return object
|
if data.CodeHash == nil {
|
||||||
|
data.CodeHash = emptyCodeHash
|
||||||
|
}
|
||||||
|
return &StateObject{address: address, data: data, storage: make(Storage)}
|
||||||
|
}
|
||||||
|
|
||||||
|
// EncodeRLP implements rlp.Encoder.
|
||||||
|
func (c *StateObject) EncodeRLP(w io.Writer) error {
|
||||||
|
return rlp.Encode(w, c.data)
|
||||||
}
|
}
|
||||||
|
|
||||||
// setError remembers the first non-nil error it is called with.
|
// setError remembers the first non-nil error it is called with.
|
||||||
|
|
@ -224,8 +230,7 @@ func (c *StateObject) SetBalance(amount *big.Int) {
|
||||||
func (c *StateObject) ReturnGas(gas, price *big.Int) {}
|
func (c *StateObject) ReturnGas(gas, price *big.Int) {}
|
||||||
|
|
||||||
func (self *StateObject) Copy(db trie.Database) *StateObject {
|
func (self *StateObject) Copy(db trie.Database) *StateObject {
|
||||||
stateObject := NewStateObject(self.address, db)
|
stateObject := NewObject(self.address, self.data)
|
||||||
stateObject.data = self.data
|
|
||||||
stateObject.data.Balance.Set(self.data.Balance)
|
stateObject.data.Balance.Set(self.data.Balance)
|
||||||
stateObject.trie = self.trie
|
stateObject.trie = self.trie
|
||||||
stateObject.code = self.code
|
stateObject.code = self.code
|
||||||
|
|
@ -233,7 +238,6 @@ func (self *StateObject) Copy(db trie.Database) *StateObject {
|
||||||
stateObject.remove = self.remove
|
stateObject.remove = self.remove
|
||||||
stateObject.dirty = self.dirty
|
stateObject.dirty = self.dirty
|
||||||
stateObject.deleted = self.deleted
|
stateObject.deleted = self.deleted
|
||||||
|
|
||||||
return stateObject
|
return stateObject
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -307,15 +311,3 @@ func (self *StateObject) ForEachStorage(cb func(key, value common.Hash) bool) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// EncodeRLP implements rlp.Encoder.
|
|
||||||
func (c *StateObject) EncodeRLP(w io.Writer) error {
|
|
||||||
return rlp.Encode(w, c.data)
|
|
||||||
}
|
|
||||||
|
|
||||||
// DecodeObject decodes an RLP-encoded state object.
|
|
||||||
func DecodeObject(address common.Address, db trie.Database, data []byte) (*StateObject, error) {
|
|
||||||
obj := StateObject{address: address, storage: make(Storage)}
|
|
||||||
err := rlp.DecodeBytes(data, &obj.data)
|
|
||||||
return &obj, err
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -43,8 +43,14 @@ type StateDB struct {
|
||||||
db ethdb.Database
|
db ethdb.Database
|
||||||
trie *trie.SecureTrie
|
trie *trie.SecureTrie
|
||||||
|
|
||||||
|
// This map caches canon state accounts.
|
||||||
|
all map[common.Address]Account
|
||||||
|
|
||||||
|
// This map holds 'live' objects, which will get modified
|
||||||
|
// while processing a state transition.
|
||||||
stateObjects map[common.Address]*StateObject
|
stateObjects map[common.Address]*StateObject
|
||||||
|
|
||||||
|
// The refund counter, also used by state transitioning.
|
||||||
refund *big.Int
|
refund *big.Int
|
||||||
|
|
||||||
thash, bhash common.Hash
|
thash, bhash common.Hash
|
||||||
|
|
@ -62,6 +68,7 @@ func New(root common.Hash, db ethdb.Database) (*StateDB, error) {
|
||||||
return &StateDB{
|
return &StateDB{
|
||||||
db: db,
|
db: db,
|
||||||
trie: tr,
|
trie: tr,
|
||||||
|
all: make(map[common.Address]Account),
|
||||||
stateObjects: make(map[common.Address]*StateObject),
|
stateObjects: make(map[common.Address]*StateObject),
|
||||||
refund: new(big.Int),
|
refund: new(big.Int),
|
||||||
logs: make(map[common.Hash]vm.Logs),
|
logs: make(map[common.Hash]vm.Logs),
|
||||||
|
|
@ -74,15 +81,19 @@ func (self *StateDB) Reset(root common.Hash) error {
|
||||||
var (
|
var (
|
||||||
err error
|
err error
|
||||||
tr = self.trie
|
tr = self.trie
|
||||||
|
all = self.all
|
||||||
)
|
)
|
||||||
if self.trie.Hash() != root {
|
if self.trie.Hash() != root {
|
||||||
|
// The root has changed, invalidate canon state.
|
||||||
if tr, err = trie.NewSecure(root, self.db); err != nil {
|
if tr, err = trie.NewSecure(root, self.db); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
all = make(map[common.Address]Account)
|
||||||
}
|
}
|
||||||
*self = StateDB{
|
*self = StateDB{
|
||||||
db: self.db,
|
db: self.db,
|
||||||
trie: tr,
|
trie: tr,
|
||||||
|
all: all,
|
||||||
stateObjects: make(map[common.Address]*StateObject),
|
stateObjects: make(map[common.Address]*StateObject),
|
||||||
refund: new(big.Int),
|
refund: new(big.Int),
|
||||||
logs: make(map[common.Hash]vm.Logs),
|
logs: make(map[common.Hash]vm.Logs),
|
||||||
|
|
@ -244,26 +255,37 @@ func (self *StateDB) DeleteStateObject(stateObject *StateObject) {
|
||||||
|
|
||||||
// Retrieve a state object given my the address. Returns nil if not found.
|
// Retrieve a state object given my the address. Returns nil if not found.
|
||||||
func (self *StateDB) GetStateObject(addr common.Address) (stateObject *StateObject) {
|
func (self *StateDB) GetStateObject(addr common.Address) (stateObject *StateObject) {
|
||||||
stateObject = self.stateObjects[addr]
|
// Prefer 'live' objects.
|
||||||
if stateObject != nil {
|
if obj := self.stateObjects[addr]; obj != nil {
|
||||||
if stateObject.deleted {
|
if obj.deleted {
|
||||||
stateObject = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return stateObject
|
|
||||||
}
|
|
||||||
|
|
||||||
data := self.trie.Get(addr[:])
|
|
||||||
if len(data) == 0 {
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
stateObject, err := DecodeObject(addr, self.db, data)
|
return obj
|
||||||
if err != nil {
|
}
|
||||||
|
|
||||||
|
// Use cached account data from the canon state if possible.
|
||||||
|
if data, ok := self.all[addr]; ok {
|
||||||
|
return NewObject(addr, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load the object from the database.
|
||||||
|
enc := self.trie.Get(addr[:])
|
||||||
|
if len(enc) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
var data Account
|
||||||
|
if err := rlp.DecodeBytes(enc, &data); err != nil {
|
||||||
glog.Errorf("can't decode object at %x: %v", addr[:], err)
|
glog.Errorf("can't decode object at %x: %v", addr[:], err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
self.SetStateObject(stateObject)
|
// Update the all cache. Content in DB always corresponds
|
||||||
return stateObject
|
// to the current head state so this is ok to do here.
|
||||||
|
// The object we just loaded has no storage trie and code yet.
|
||||||
|
self.all[addr] = data
|
||||||
|
// Insert into the live set.
|
||||||
|
obj := NewObject(addr, data)
|
||||||
|
self.SetStateObject(obj)
|
||||||
|
return obj
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *StateDB) SetStateObject(object *StateObject) {
|
func (self *StateDB) SetStateObject(object *StateObject) {
|
||||||
|
|
@ -285,15 +307,14 @@ func (self *StateDB) newStateObject(addr common.Address) *StateObject {
|
||||||
if glog.V(logger.Core) {
|
if glog.V(logger.Core) {
|
||||||
glog.Infof("(+) %x\n", addr)
|
glog.Infof("(+) %x\n", addr)
|
||||||
}
|
}
|
||||||
|
obj := NewObject(addr, Account{})
|
||||||
stateObject := NewStateObject(addr, self.db)
|
obj.dirty = true
|
||||||
stateObject.SetNonce(StartingNonce)
|
obj.SetNonce(StartingNonce)
|
||||||
self.stateObjects[addr] = stateObject
|
self.stateObjects[addr] = obj
|
||||||
|
return obj
|
||||||
return stateObject
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Creates creates a new state object and takes ownership. This is different from "NewStateObject"
|
// Creates creates a new state object and takes ownership.
|
||||||
func (self *StateDB) CreateStateObject(addr common.Address) *StateObject {
|
func (self *StateDB) CreateStateObject(addr common.Address) *StateObject {
|
||||||
// Get previous (if any)
|
// Get previous (if any)
|
||||||
so := self.GetStateObject(addr)
|
so := self.GetStateObject(addr)
|
||||||
|
|
@ -320,6 +341,7 @@ func (self *StateDB) Copy() *StateDB {
|
||||||
// ignore error - we assume state-to-be-copied always exists
|
// ignore error - we assume state-to-be-copied always exists
|
||||||
state, _ := New(common.Hash{}, self.db)
|
state, _ := New(common.Hash{}, self.db)
|
||||||
state.trie = self.trie
|
state.trie = self.trie
|
||||||
|
state.all = self.all
|
||||||
for addr, stateObject := range self.stateObjects {
|
for addr, stateObject := range self.stateObjects {
|
||||||
if stateObject.dirty {
|
if stateObject.dirty {
|
||||||
state.stateObjects[addr] = stateObject.Copy(self.db)
|
state.stateObjects[addr] = stateObject.Copy(self.db)
|
||||||
|
|
@ -340,6 +362,7 @@ func (self *StateDB) Copy() *StateDB {
|
||||||
func (self *StateDB) Set(state *StateDB) {
|
func (self *StateDB) Set(state *StateDB) {
|
||||||
self.trie = state.trie
|
self.trie = state.trie
|
||||||
self.stateObjects = state.stateObjects
|
self.stateObjects = state.stateObjects
|
||||||
|
self.all = state.all
|
||||||
|
|
||||||
self.refund = state.refund
|
self.refund = state.refund
|
||||||
self.logs = state.logs
|
self.logs = state.logs
|
||||||
|
|
@ -407,11 +430,13 @@ func (s *StateDB) CommitBatch() (root common.Hash, batch ethdb.Batch) {
|
||||||
func (s *StateDB) commit(dbw trie.DatabaseWriter) (common.Hash, error) {
|
func (s *StateDB) commit(dbw trie.DatabaseWriter) (common.Hash, error) {
|
||||||
s.refund = new(big.Int)
|
s.refund = new(big.Int)
|
||||||
|
|
||||||
for _, stateObject := range s.stateObjects {
|
// Commit objects to the trie.
|
||||||
|
for addr, stateObject := range s.stateObjects {
|
||||||
if stateObject.remove {
|
if stateObject.remove {
|
||||||
// If the object has been removed, don't bother syncing it
|
// If the object has been removed, don't bother syncing it
|
||||||
// and just mark it for deletion in the trie.
|
// and just mark it for deletion in the trie.
|
||||||
s.DeleteStateObject(stateObject)
|
s.DeleteStateObject(stateObject)
|
||||||
|
delete(s.all, addr)
|
||||||
} else {
|
} else {
|
||||||
// Write any contract code associated with the state object
|
// Write any contract code associated with the state object
|
||||||
if stateObject.code != nil {
|
if stateObject.code != nil {
|
||||||
|
|
@ -426,10 +451,18 @@ func (s *StateDB) commit(dbw trie.DatabaseWriter) (common.Hash, error) {
|
||||||
}
|
}
|
||||||
// Update the object in the main account trie.
|
// Update the object in the main account trie.
|
||||||
s.UpdateStateObject(stateObject)
|
s.UpdateStateObject(stateObject)
|
||||||
|
s.all[addr] = stateObject.data
|
||||||
}
|
}
|
||||||
stateObject.dirty = false
|
stateObject.dirty = false
|
||||||
}
|
}
|
||||||
return s.trie.CommitTo(dbw)
|
|
||||||
|
// Write trie changes.
|
||||||
|
root, err := s.trie.CommitTo(dbw)
|
||||||
|
if err != nil {
|
||||||
|
// Committing failed, any updates to the canon state are invalid.
|
||||||
|
s.all = make(map[common.Address]Account)
|
||||||
|
}
|
||||||
|
return root, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *StateDB) Refunds() *big.Int {
|
func (self *StateDB) Refunds() *big.Int {
|
||||||
|
|
|
||||||
|
|
@ -104,15 +104,16 @@ func (self Log) Topics() [][]byte {
|
||||||
}
|
}
|
||||||
|
|
||||||
func StateObjectFromAccount(db ethdb.Database, addr string, account Account) *state.StateObject {
|
func StateObjectFromAccount(db ethdb.Database, addr string, account Account) *state.StateObject {
|
||||||
obj := state.NewStateObject(common.HexToAddress(addr), db)
|
|
||||||
obj.SetBalance(common.Big(account.Balance))
|
|
||||||
|
|
||||||
if common.IsHex(account.Code) {
|
if common.IsHex(account.Code) {
|
||||||
account.Code = account.Code[2:]
|
account.Code = account.Code[2:]
|
||||||
}
|
}
|
||||||
obj.SetCode(common.Hex2Bytes(account.Code))
|
code := common.Hex2Bytes(account.Code)
|
||||||
obj.SetNonce(common.Big(account.Nonce).Uint64())
|
obj := state.NewObject(common.HexToAddress(addr), state.Account{
|
||||||
|
Balance: common.Big(account.Balance),
|
||||||
|
CodeHash: crypto.Keccak256(code),
|
||||||
|
Nonce: common.Big(account.Nonce).Uint64(),
|
||||||
|
})
|
||||||
|
obj.SetCode(code)
|
||||||
return obj
|
return obj
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue