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"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
)
|
||||
|
||||
type DumpAccount struct {
|
||||
|
|
@ -46,20 +47,22 @@ func (self *StateDB) RawDump() Dump {
|
|||
it := self.trie.Iterator()
|
||||
for it.Next() {
|
||||
addr := self.trie.GetKey(it.Key)
|
||||
stateObject, err := DecodeObject(common.BytesToAddress(addr), self.db, it.Value)
|
||||
if err != nil {
|
||||
var data Account
|
||||
if err := rlp.DecodeBytes(it.Value, &data); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
obj := NewObject(common.BytesToAddress(addr), data)
|
||||
account := DumpAccount{
|
||||
Balance: stateObject.Balance().String(),
|
||||
Nonce: stateObject.Nonce(),
|
||||
Root: common.Bytes2Hex(stateObject.data.Root[:]),
|
||||
CodeHash: common.Bytes2Hex(stateObject.CodeHash()),
|
||||
Code: common.Bytes2Hex(stateObject.Code(self.db)),
|
||||
Balance: data.Balance.String(),
|
||||
Nonce: data.Nonce,
|
||||
Root: common.Bytes2Hex(data.Root[:]),
|
||||
CodeHash: common.Bytes2Hex(data.CodeHash),
|
||||
Code: common.Bytes2Hex(obj.Code(self.db)),
|
||||
Storage: make(map[string]string),
|
||||
}
|
||||
stateObject.initTrie(self.db)
|
||||
storageIt := stateObject.trie.Iterator()
|
||||
obj.initTrie(self.db)
|
||||
storageIt := obj.trie.Iterator()
|
||||
for storageIt.Next() {
|
||||
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.
|
||||
type StateObject struct {
|
||||
address common.Address // Ethereum address of this account
|
||||
data accountData
|
||||
data Account
|
||||
|
||||
// DB error.
|
||||
// State objects are used by the consensus core and VM which are
|
||||
|
|
@ -87,23 +87,29 @@ type StateObject struct {
|
|||
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.
|
||||
type accountData struct {
|
||||
type Account struct {
|
||||
Nonce uint64
|
||||
Balance *big.Int
|
||||
Root common.Hash // merkle root of the storage trie
|
||||
CodeHash []byte
|
||||
}
|
||||
|
||||
func NewStateObject(address common.Address, db trie.Database) *StateObject {
|
||||
object := &StateObject{
|
||||
address: address,
|
||||
data: accountData{Balance: new(big.Int), CodeHash: emptyCodeHash},
|
||||
storage: make(Storage),
|
||||
dirty: true,
|
||||
// NewObject creates a state object.
|
||||
func NewObject(address common.Address, data Account) *StateObject {
|
||||
if data.Balance == nil {
|
||||
data.Balance = new(big.Int)
|
||||
}
|
||||
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.
|
||||
|
|
@ -224,8 +230,7 @@ func (c *StateObject) SetBalance(amount *big.Int) {
|
|||
func (c *StateObject) ReturnGas(gas, price *big.Int) {}
|
||||
|
||||
func (self *StateObject) Copy(db trie.Database) *StateObject {
|
||||
stateObject := NewStateObject(self.address, db)
|
||||
stateObject.data = self.data
|
||||
stateObject := NewObject(self.address, self.data)
|
||||
stateObject.data.Balance.Set(self.data.Balance)
|
||||
stateObject.trie = self.trie
|
||||
stateObject.code = self.code
|
||||
|
|
@ -233,7 +238,6 @@ func (self *StateObject) Copy(db trie.Database) *StateObject {
|
|||
stateObject.remove = self.remove
|
||||
stateObject.dirty = self.dirty
|
||||
stateObject.deleted = self.deleted
|
||||
|
||||
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
|
||||
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
|
||||
|
||||
// The refund counter, also used by state transitioning.
|
||||
refund *big.Int
|
||||
|
||||
thash, bhash common.Hash
|
||||
|
|
@ -62,6 +68,7 @@ func New(root common.Hash, db ethdb.Database) (*StateDB, error) {
|
|||
return &StateDB{
|
||||
db: db,
|
||||
trie: tr,
|
||||
all: make(map[common.Address]Account),
|
||||
stateObjects: make(map[common.Address]*StateObject),
|
||||
refund: new(big.Int),
|
||||
logs: make(map[common.Hash]vm.Logs),
|
||||
|
|
@ -74,15 +81,19 @@ func (self *StateDB) Reset(root common.Hash) error {
|
|||
var (
|
||||
err error
|
||||
tr = self.trie
|
||||
all = self.all
|
||||
)
|
||||
if self.trie.Hash() != root {
|
||||
// The root has changed, invalidate canon state.
|
||||
if tr, err = trie.NewSecure(root, self.db); err != nil {
|
||||
return err
|
||||
}
|
||||
all = make(map[common.Address]Account)
|
||||
}
|
||||
*self = StateDB{
|
||||
db: self.db,
|
||||
trie: tr,
|
||||
all: all,
|
||||
stateObjects: make(map[common.Address]*StateObject),
|
||||
refund: new(big.Int),
|
||||
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.
|
||||
func (self *StateDB) GetStateObject(addr common.Address) (stateObject *StateObject) {
|
||||
stateObject = self.stateObjects[addr]
|
||||
if stateObject != nil {
|
||||
if stateObject.deleted {
|
||||
stateObject = nil
|
||||
}
|
||||
|
||||
return stateObject
|
||||
}
|
||||
|
||||
data := self.trie.Get(addr[:])
|
||||
if len(data) == 0 {
|
||||
// Prefer 'live' objects.
|
||||
if obj := self.stateObjects[addr]; obj != nil {
|
||||
if obj.deleted {
|
||||
return nil
|
||||
}
|
||||
stateObject, err := DecodeObject(addr, self.db, data)
|
||||
if err != nil {
|
||||
return obj
|
||||
}
|
||||
|
||||
// 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)
|
||||
return nil
|
||||
}
|
||||
self.SetStateObject(stateObject)
|
||||
return stateObject
|
||||
// Update the all cache. Content in DB always corresponds
|
||||
// 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) {
|
||||
|
|
@ -285,15 +307,14 @@ func (self *StateDB) newStateObject(addr common.Address) *StateObject {
|
|||
if glog.V(logger.Core) {
|
||||
glog.Infof("(+) %x\n", addr)
|
||||
}
|
||||
|
||||
stateObject := NewStateObject(addr, self.db)
|
||||
stateObject.SetNonce(StartingNonce)
|
||||
self.stateObjects[addr] = stateObject
|
||||
|
||||
return stateObject
|
||||
obj := NewObject(addr, Account{})
|
||||
obj.dirty = true
|
||||
obj.SetNonce(StartingNonce)
|
||||
self.stateObjects[addr] = obj
|
||||
return obj
|
||||
}
|
||||
|
||||
// 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 {
|
||||
// Get previous (if any)
|
||||
so := self.GetStateObject(addr)
|
||||
|
|
@ -320,6 +341,7 @@ func (self *StateDB) Copy() *StateDB {
|
|||
// ignore error - we assume state-to-be-copied always exists
|
||||
state, _ := New(common.Hash{}, self.db)
|
||||
state.trie = self.trie
|
||||
state.all = self.all
|
||||
for addr, stateObject := range self.stateObjects {
|
||||
if stateObject.dirty {
|
||||
state.stateObjects[addr] = stateObject.Copy(self.db)
|
||||
|
|
@ -340,6 +362,7 @@ func (self *StateDB) Copy() *StateDB {
|
|||
func (self *StateDB) Set(state *StateDB) {
|
||||
self.trie = state.trie
|
||||
self.stateObjects = state.stateObjects
|
||||
self.all = state.all
|
||||
|
||||
self.refund = state.refund
|
||||
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) {
|
||||
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 the object has been removed, don't bother syncing it
|
||||
// and just mark it for deletion in the trie.
|
||||
s.DeleteStateObject(stateObject)
|
||||
delete(s.all, addr)
|
||||
} else {
|
||||
// Write any contract code associated with the state object
|
||||
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.
|
||||
s.UpdateStateObject(stateObject)
|
||||
s.all[addr] = stateObject.data
|
||||
}
|
||||
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 {
|
||||
|
|
|
|||
|
|
@ -104,15 +104,16 @@ func (self Log) Topics() [][]byte {
|
|||
}
|
||||
|
||||
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) {
|
||||
account.Code = account.Code[2:]
|
||||
}
|
||||
obj.SetCode(common.Hex2Bytes(account.Code))
|
||||
obj.SetNonce(common.Big(account.Nonce).Uint64())
|
||||
|
||||
code := common.Hex2Bytes(account.Code)
|
||||
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
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue