core/state: lazy-load account storage tries and code

With this change, StateObject can work without loading the storage trie
and contract code. This is required because we're about to cache state
objects and want to reduce the amount of memory referenced by idle
objects.
This commit is contained in:
Felix Lange 2016-09-23 00:10:00 +02:00
parent 9df74cb584
commit e6ed80e899
10 changed files with 186 additions and 174 deletions

View file

@ -135,11 +135,8 @@ func (b *SimulatedBackend) StorageAt(ctx context.Context, contract common.Addres
return nil, errBlockNumberUnsupported return nil, errBlockNumberUnsupported
} }
statedb, _ := b.blockchain.State() statedb, _ := b.blockchain.State()
if obj := statedb.GetStateObject(contract); obj != nil { val := statedb.GetState(contract, key)
val := obj.GetState(key) return val[:], nil
return val[:], nil
}
return nil, nil
} }
// TransactionReceipt returns the receipt of a transaction. // TransactionReceipt returns the receipt of a transaction.

View file

@ -50,15 +50,15 @@ func (self *StateDB) RawDump() World {
if err != nil { if err != nil {
panic(err) panic(err)
} }
account := Account{ account := Account{
Balance: stateObject.balance.String(), Balance: stateObject.Balance().String(),
Nonce: stateObject.nonce, Nonce: stateObject.Nonce(),
Root: common.Bytes2Hex(stateObject.Root()), Root: common.Bytes2Hex(stateObject.data.Root[:]),
CodeHash: common.Bytes2Hex(stateObject.codeHash), CodeHash: common.Bytes2Hex(stateObject.CodeHash()),
Code: common.Bytes2Hex(stateObject.Code()), Code: common.Bytes2Hex(stateObject.Code(self.db)),
Storage: make(map[string]string), Storage: make(map[string]string),
} }
stateObject.initTrie(self.db)
storageIt := stateObject.trie.Iterator() storageIt := stateObject.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)
@ -76,12 +76,3 @@ func (self *StateDB) Dump() []byte {
return json return json
} }
// Debug stuff
func (self *StateObject) CreateOutputForDiff() {
fmt.Printf("%x %x %x %x\n", self.Address(), self.Root(), self.balance.Bytes(), self.nonce)
it := self.trie.Iterator()
for it.Next() {
fmt.Printf("%x %x\n", it.Key, it.Value)
}
}

View file

@ -127,7 +127,7 @@ func (ms *ManagedState) getAccount(addr common.Address) *account {
// Always make sure the state account nonce isn't actually higher // Always make sure the state account nonce isn't actually higher
// than the tracked one. // than the tracked one.
so := ms.StateDB.GetStateObject(addr) so := ms.StateDB.GetStateObject(addr)
if so != nil && uint64(len(account.nonces))+account.nstart < so.nonce { if so != nil && uint64(len(account.nonces))+account.nstart < so.Nonce() {
ms.accounts[addr] = newAccount(so) ms.accounts[addr] = newAccount(so)
} }
@ -137,5 +137,5 @@ func (ms *ManagedState) getAccount(addr common.Address) *account {
} }
func newAccount(so *StateObject) *account { func newAccount(so *StateObject) *account {
return &account{so, so.nonce, nil} return &account{so, so.Nonce(), nil}
} }

View file

@ -29,7 +29,8 @@ func create() (*ManagedState, *account) {
db, _ := ethdb.NewMemDatabase() db, _ := ethdb.NewMemDatabase()
statedb, _ := New(common.Hash{}, db) statedb, _ := New(common.Hash{}, db)
ms := ManageState(statedb) ms := ManageState(statedb)
so := &StateObject{address: addr, nonce: 100} so := &StateObject{address: addr}
so.SetNonce(100)
ms.StateDB.stateObjects[addr] = so ms.StateDB.stateObjects[addr] = so
ms.accounts[addr] = newAccount(so) ms.accounts[addr] = newAccount(so)
@ -92,7 +93,7 @@ func TestRemoteNonceChange(t *testing.T) {
account.nonces = append(account.nonces, nn...) account.nonces = append(account.nonces, nn...)
nonce := ms.NewNonce(addr) nonce := ms.NewNonce(addr)
ms.StateDB.stateObjects[addr].nonce = 200 ms.StateDB.stateObjects[addr].data.Nonce = 200
nonce = ms.NewNonce(addr) nonce = ms.NewNonce(addr)
if nonce != 200 { if nonce != 200 {
t.Error("expected nonce after remote update to be", 201, "got", nonce) t.Error("expected nonce after remote update to be", 201, "got", nonce)
@ -100,7 +101,7 @@ func TestRemoteNonceChange(t *testing.T) {
ms.NewNonce(addr) ms.NewNonce(addr)
ms.NewNonce(addr) ms.NewNonce(addr)
ms.NewNonce(addr) ms.NewNonce(addr)
ms.StateDB.stateObjects[addr].nonce = 200 ms.StateDB.stateObjects[addr].data.Nonce = 200
nonce = ms.NewNonce(addr) nonce = ms.NewNonce(addr)
if nonce != 204 { if nonce != 204 {
t.Error("expected nonce after remote update to be", 201, "got", nonce) t.Error("expected nonce after remote update to be", 201, "got", nonce)

View file

@ -57,112 +57,160 @@ func (self Storage) Copy() Storage {
return cpy return cpy
} }
// StateObject represents an Ethereum account which is being modified.
//
// The usage pattern is as follows:
// First you need to obtain a state object.
// Account values can be accessed and modified through the object.
// Finally, call CommitTrie to write the modified storage trie into a database.
type StateObject struct { type StateObject struct {
trie *trie.SecureTrie address common.Address // Ethereum address of this account
data accountData
// Address belonging to this account // DB error.
address common.Address // State objects are used by the consensus core and VM which are
// The balance of the account // unable to deal with database-level errors. Any error that occurs
balance *big.Int // during a database read is memoized here and will eventually be returned
// The nonce of the account // by StateDB.Commit.
nonce uint64 dbErr error
// The code hash if code is present (i.e. a contract)
codeHash []byte
// The code for this account
code Code
// Cached storage (flushed when updated)
storage Storage
// Mark for deletion // Write caches.
trie *trie.SecureTrie // storage trie, which becomes non-nil on first access
code Code // contract bytecode, which gets set when code is loaded
storage Storage // Cached storage (flushed when updated)
// Cache flags.
// When an object is marked for deletion it will be delete from the trie // When an object is marked for deletion it will be delete from the trie
// during the "update" phase of the state transition // during the "update" phase of the state transition
dirty bool // true if anything has changed
remove bool remove bool
deleted bool deleted bool
dirty bool }
// accountData is the Ethereum consensus representation of accounts.
// These objects are stored in the main account trie.
type accountData 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 { func NewStateObject(address common.Address, db trie.Database) *StateObject {
object := &StateObject{ object := &StateObject{
address: address, address: address,
balance: new(big.Int), data: accountData{Balance: new(big.Int), CodeHash: emptyCodeHash},
dirty: true, storage: make(Storage),
codeHash: emptyCodeHash, dirty: true,
storage: make(Storage),
} }
object.trie, _ = trie.NewSecure(common.Hash{}, db)
return object return object
} }
// setError remembers the first non-nil error it is called with.
func (self *StateObject) setError(err error) {
if self.dbErr == nil {
self.dbErr = err
}
}
func (self *StateObject) MarkForDeletion() { func (self *StateObject) MarkForDeletion() {
self.remove = true self.remove = true
self.dirty = true self.dirty = true
if glog.V(logger.Core) { if glog.V(logger.Core) {
glog.Infof("%x: #%d %v X\n", self.Address(), self.nonce, self.balance) glog.Infof("%x: #%d %v X\n", self.Address(), self.Nonce(), self.Balance())
} }
} }
func (c *StateObject) getAddr(addr common.Hash) common.Hash { func (c *StateObject) initTrie(db trie.Database) {
var ret []byte if c.trie == nil {
rlp.DecodeBytes(c.trie.Get(addr[:]), &ret) var err error
return common.BytesToHash(ret) c.trie, err = trie.NewSecure(c.data.Root, db)
} if err != nil {
c.trie, _ = trie.NewSecure(common.Hash{}, db)
func (c *StateObject) setAddr(addr, value common.Hash) { c.setError(fmt.Errorf("can't create storage trie: %v", err))
v, err := rlp.EncodeToBytes(bytes.TrimLeft(value[:], "\x00"))
if err != nil {
// if RLPing failed we better panic and not fail silently. This would be considered a consensus issue
panic(err)
}
c.trie.Update(addr[:], v)
}
func (self *StateObject) GetState(key common.Hash) common.Hash {
value, exists := self.storage[key]
if !exists {
value = self.getAddr(key)
if (value != common.Hash{}) {
self.storage[key] = value
} }
} }
}
// GetState returns a value in account storage.
func (self *StateObject) GetState(db trie.Database, key common.Hash) common.Hash {
value, exists := self.storage[key]
if exists {
return value
}
// Load from DB in case it is missing.
self.initTrie(db)
var ret []byte
rlp.DecodeBytes(self.trie.Get(key[:]), &ret)
value = common.BytesToHash(ret)
if (value != common.Hash{}) {
self.storage[key] = value
}
return value return value
} }
// SetState updates a value in account storage.
func (self *StateObject) SetState(key, value common.Hash) { func (self *StateObject) SetState(key, value common.Hash) {
self.storage[key] = value self.storage[key] = value
self.dirty = true self.dirty = true
} }
// Update updates the current cached storage to the trie // updateTrie writes cached storage modifications into the object's storage trie.
func (self *StateObject) Update() { func (self *StateObject) updateTrie(db trie.Database) {
self.initTrie(db)
for key, value := range self.storage { for key, value := range self.storage {
if (value == common.Hash{}) { if (value == common.Hash{}) {
self.trie.Delete(key[:]) self.trie.Delete(key[:])
continue continue
} }
self.setAddr(key, value) // Encoding []byte cannot fail, ok to ignore the error.
v, _ := rlp.EncodeToBytes(bytes.TrimLeft(value[:], "\x00"))
self.trie.Update(key[:], v)
} }
} }
// UpdateRoot sets the trie root to the current root hash of
func (self *StateObject) UpdateRoot(db trie.Database) {
self.updateTrie(db)
self.data.Root = self.trie.Hash()
}
// CommitTrie the storage trie of the object to dwb.
// This updates the trie root.
func (self *StateObject) CommitTrie(db trie.Database, dbw trie.DatabaseWriter) error {
self.updateTrie(db)
if self.dbErr != nil {
fmt.Println("dbErr:", self.dbErr)
return self.dbErr
}
self.dbErr = nil
root, err := self.trie.CommitTo(dbw)
if err == nil {
self.data.Root = root
}
return err
}
func (c *StateObject) AddBalance(amount *big.Int) { func (c *StateObject) AddBalance(amount *big.Int) {
c.SetBalance(new(big.Int).Add(c.balance, amount)) c.SetBalance(new(big.Int).Add(c.Balance(), amount))
if glog.V(logger.Core) { if glog.V(logger.Core) {
glog.Infof("%x: #%d %v (+ %v)\n", c.Address(), c.nonce, c.balance, amount) glog.Infof("%x: #%d %v (+ %v)\n", c.Address(), c.Nonce(), c.Balance(), amount)
} }
} }
func (c *StateObject) SubBalance(amount *big.Int) { func (c *StateObject) SubBalance(amount *big.Int) {
c.SetBalance(new(big.Int).Sub(c.balance, amount)) c.SetBalance(new(big.Int).Sub(c.Balance(), amount))
if glog.V(logger.Core) { if glog.V(logger.Core) {
glog.Infof("%x: #%d %v (- %v)\n", c.Address(), c.nonce, c.balance, amount) glog.Infof("%x: #%d %v (- %v)\n", c.Address(), c.Nonce(), c.Balance(), amount)
} }
} }
func (c *StateObject) SetBalance(amount *big.Int) { func (c *StateObject) SetBalance(amount *big.Int) {
c.balance = amount c.data.Balance = amount
c.dirty = true c.dirty = true
} }
@ -171,9 +219,8 @@ 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 := NewStateObject(self.address, db)
stateObject.balance.Set(self.balance) stateObject.data = self.data
stateObject.codeHash = common.CopyBytes(self.codeHash) stateObject.data.Balance.Set(self.data.Balance)
stateObject.nonce = self.nonce
stateObject.trie = self.trie stateObject.trie = self.trie
stateObject.code = self.code stateObject.code = self.code
stateObject.storage = self.storage.Copy() stateObject.storage = self.storage.Copy()
@ -188,40 +235,48 @@ func (self *StateObject) Copy(db trie.Database) *StateObject {
// Attribute accessors // Attribute accessors
// //
func (self *StateObject) Balance() *big.Int {
return self.balance
}
// Returns the address of the contract/account // Returns the address of the contract/account
func (c *StateObject) Address() common.Address { func (c *StateObject) Address() common.Address {
return c.address return c.address
} }
func (self *StateObject) Trie() *trie.SecureTrie { // LoadCode returns the contract code associated with this object, if any.
return self.trie func (self *StateObject) Code(db trie.Database) []byte {
} if self.code != nil {
return self.code
func (self *StateObject) Root() []byte { }
return self.trie.Root() if bytes.Equal(self.CodeHash(), emptyCodeHash) {
} return nil
}
func (self *StateObject) Code() []byte { code, err := db.Get(self.CodeHash())
return self.code if err != nil {
self.setError(fmt.Errorf("can't load code hash %x: %v", self.CodeHash(), err))
}
self.code = code
return code
} }
func (self *StateObject) SetCode(code []byte) { func (self *StateObject) SetCode(code []byte) {
self.code = code self.code = code
self.codeHash = crypto.Keccak256(code) self.data.CodeHash = crypto.Keccak256(code)
self.dirty = true self.dirty = true
} }
func (self *StateObject) SetNonce(nonce uint64) { func (self *StateObject) SetNonce(nonce uint64) {
self.nonce = nonce self.data.Nonce = nonce
self.dirty = true self.dirty = true
} }
func (self *StateObject) CodeHash() []byte {
return self.data.CodeHash
}
func (self *StateObject) Balance() *big.Int {
return self.data.Balance
}
func (self *StateObject) Nonce() uint64 { func (self *StateObject) Nonce() uint64 {
return self.nonce return self.data.Nonce
} }
// Never called, but must be present to allow StateObject to be used // Never called, but must be present to allow StateObject to be used
@ -247,38 +302,14 @@ func (self *StateObject) ForEachStorage(cb func(key, value common.Hash) bool) {
} }
} }
type extStateObject struct {
Nonce uint64
Balance *big.Int
Root common.Hash
CodeHash []byte
}
// EncodeRLP implements rlp.Encoder. // EncodeRLP implements rlp.Encoder.
func (c *StateObject) EncodeRLP(w io.Writer) error { func (c *StateObject) EncodeRLP(w io.Writer) error {
return rlp.Encode(w, []interface{}{c.nonce, c.balance, c.Root(), c.codeHash}) return rlp.Encode(w, c.data)
} }
// DecodeObject decodes an RLP-encoded state object. // DecodeObject decodes an RLP-encoded state object.
func DecodeObject(address common.Address, db trie.Database, data []byte) (*StateObject, error) { func DecodeObject(address common.Address, db trie.Database, data []byte) (*StateObject, error) {
var ( obj := StateObject{address: address, storage: make(Storage)}
obj = &StateObject{address: address, storage: make(Storage)} err := rlp.DecodeBytes(data, &obj.data)
ext extStateObject return &obj, err
err error
)
if err = rlp.DecodeBytes(data, &ext); err != nil {
return nil, err
}
if obj.trie, err = trie.NewSecure(ext.Root, db); err != nil {
return nil, err
}
if !bytes.Equal(ext.CodeHash, emptyCodeHash) {
if obj.code, err = db.Get(ext.CodeHash); err != nil {
return nil, fmt.Errorf("can't get code for hash %x: %v", ext.CodeHash, err)
}
}
obj.nonce = ext.Nonce
obj.balance = ext.Balance
obj.codeHash = ext.CodeHash
return obj, nil
} }

View file

@ -146,8 +146,8 @@ func TestSnapshot2(t *testing.T) {
// db, trie are already non-empty values // db, trie are already non-empty values
so0 := state.GetStateObject(stateobjaddr0) so0 := state.GetStateObject(stateobjaddr0)
so0.balance = big.NewInt(42) so0.SetBalance(big.NewInt(42))
so0.nonce = 43 so0.SetNonce(43)
so0.SetCode([]byte{'c', 'a', 'f', 'e'}) so0.SetCode([]byte{'c', 'a', 'f', 'e'})
so0.remove = false so0.remove = false
so0.deleted = false so0.deleted = false
@ -157,8 +157,8 @@ func TestSnapshot2(t *testing.T) {
// and one with deleted == true // and one with deleted == true
so1 := state.GetStateObject(stateobjaddr1) so1 := state.GetStateObject(stateobjaddr1)
so1.balance = big.NewInt(52) so1.SetBalance(big.NewInt(52))
so1.nonce = 53 so1.SetNonce(53)
so1.SetCode([]byte{'c', 'a', 'f', 'e', '2'}) so1.SetCode([]byte{'c', 'a', 'f', 'e', '2'})
so1.remove = true so1.remove = true
so1.deleted = true so1.deleted = true
@ -174,41 +174,50 @@ func TestSnapshot2(t *testing.T) {
state.Set(snapshot) state.Set(snapshot)
so0Restored := state.GetStateObject(stateobjaddr0) so0Restored := state.GetStateObject(stateobjaddr0)
so0Restored.GetState(storageaddr) // Update lazily-loaded values before comparing.
so1Restored := state.GetStateObject(stateobjaddr1) so0Restored.GetState(db, storageaddr)
so0Restored.Code(db)
// non-deleted is equal (restored) // non-deleted is equal (restored)
compareStateObjects(so0Restored, so0, t) compareStateObjects(so0Restored, so0, t)
// deleted should be nil, both before and after restore of state copy // deleted should be nil, both before and after restore of state copy
so1Restored := state.GetStateObject(stateobjaddr1)
if so1Restored != nil { if so1Restored != nil {
t.Fatalf("deleted object not nil after restoring snapshot") t.Fatalf("deleted object not nil after restoring snapshot")
} }
} }
func compareStateObjects(so0, so1 *StateObject, t *testing.T) { func compareStateObjects(so0, so1 *StateObject, t *testing.T) {
if so0.address != so1.address { if so0.Address() != so1.Address() {
t.Fatalf("Address mismatch: have %v, want %v", so0.address, so1.address) t.Fatalf("Address mismatch: have %v, want %v", so0.address, so1.address)
} }
if so0.balance.Cmp(so1.balance) != 0 { if so0.Balance().Cmp(so1.Balance()) != 0 {
t.Fatalf("Balance mismatch: have %v, want %v", so0.balance, so1.balance) t.Fatalf("Balance mismatch: have %v, want %v", so0.Balance(), so1.Balance())
} }
if so0.nonce != so1.nonce { if so0.Nonce() != so1.Nonce() {
t.Fatalf("Nonce mismatch: have %v, want %v", so0.nonce, so1.nonce) t.Fatalf("Nonce mismatch: have %v, want %v", so0.Nonce(), so1.Nonce())
} }
if !bytes.Equal(so0.codeHash, so1.codeHash) { if so0.data.Root != so1.data.Root {
t.Fatalf("CodeHash mismatch: have %v, want %v", so0.codeHash, so1.codeHash) t.Errorf("Root mismatch: have %x, want %x", so0.data.Root[:], so1.data.Root[:])
}
if !bytes.Equal(so0.CodeHash(), so1.CodeHash()) {
t.Fatalf("CodeHash mismatch: have %v, want %v", so0.CodeHash(), so1.CodeHash())
} }
if !bytes.Equal(so0.code, so1.code) { if !bytes.Equal(so0.code, so1.code) {
t.Fatalf("Code mismatch: have %v, want %v", so0.code, so1.code) t.Fatalf("Code mismatch: have %v, want %v", so0.code, so1.code)
} }
if len(so1.storage) != len(so0.storage) {
t.Errorf("Storage size mismatch: have %d, want %d", len(so1.storage), len(so0.storage))
}
for k, v := range so1.storage { for k, v := range so1.storage {
if so0.storage[k] != v { if so0.storage[k] != v {
t.Fatalf("Storage key %s mismatch: have %v, want %v", k, so0.storage[k], v) t.Errorf("Storage key %x mismatch: have %v, want %v", k, so0.storage[k], v)
} }
} }
for k, v := range so0.storage { for k, v := range so0.storage {
if so1.storage[k] != v { if so1.storage[k] != v {
t.Fatalf("Storage key %s mismatch: have %v, want none.", k, v) t.Errorf("Storage key %x mismatch: have %v, want none.", k, v)
} }
} }

View file

@ -137,7 +137,7 @@ func (self *StateDB) GetAccount(addr common.Address) vm.Account {
func (self *StateDB) GetBalance(addr common.Address) *big.Int { func (self *StateDB) GetBalance(addr common.Address) *big.Int {
stateObject := self.GetStateObject(addr) stateObject := self.GetStateObject(addr)
if stateObject != nil { if stateObject != nil {
return stateObject.balance return stateObject.Balance()
} }
return common.Big0 return common.Big0
@ -146,7 +146,7 @@ func (self *StateDB) GetBalance(addr common.Address) *big.Int {
func (self *StateDB) GetNonce(addr common.Address) uint64 { func (self *StateDB) GetNonce(addr common.Address) uint64 {
stateObject := self.GetStateObject(addr) stateObject := self.GetStateObject(addr)
if stateObject != nil { if stateObject != nil {
return stateObject.nonce return stateObject.Nonce()
} }
return StartingNonce return StartingNonce
@ -155,18 +155,16 @@ func (self *StateDB) GetNonce(addr common.Address) uint64 {
func (self *StateDB) GetCode(addr common.Address) []byte { func (self *StateDB) GetCode(addr common.Address) []byte {
stateObject := self.GetStateObject(addr) stateObject := self.GetStateObject(addr)
if stateObject != nil { if stateObject != nil {
return stateObject.code return stateObject.Code(self.db)
} }
return nil return nil
} }
func (self *StateDB) GetState(a common.Address, b common.Hash) common.Hash { func (self *StateDB) GetState(a common.Address, b common.Hash) common.Hash {
stateObject := self.GetStateObject(a) stateObject := self.GetStateObject(a)
if stateObject != nil { if stateObject != nil {
return stateObject.GetState(b) return stateObject.GetState(self.db, b)
} }
return common.Hash{} return common.Hash{}
} }
@ -214,8 +212,7 @@ func (self *StateDB) Delete(addr common.Address) bool {
stateObject := self.GetStateObject(addr) stateObject := self.GetStateObject(addr)
if stateObject != nil { if stateObject != nil {
stateObject.MarkForDeletion() stateObject.MarkForDeletion()
stateObject.balance = new(big.Int) stateObject.data.Balance = new(big.Int)
return true return true
} }
@ -245,7 +242,7 @@ func (self *StateDB) DeleteStateObject(stateObject *StateObject) {
//delete(self.stateObjects, addr) //delete(self.stateObjects, addr)
} }
// Retrieve a state object given my the address. 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] stateObject = self.stateObjects[addr]
if stateObject != nil { if stateObject != nil {
@ -305,7 +302,7 @@ func (self *StateDB) CreateStateObject(addr common.Address) *StateObject {
// If it existed set the balance to the new account // If it existed set the balance to the new account
if so != nil { if so != nil {
newSo.balance = so.balance newSo.data.Balance = so.data.Balance
} }
return newSo return newSo
@ -363,7 +360,7 @@ func (s *StateDB) IntermediateRoot() common.Hash {
if stateObject.remove { if stateObject.remove {
s.DeleteStateObject(stateObject) s.DeleteStateObject(stateObject)
} else { } else {
stateObject.Update() stateObject.UpdateRoot(s.db)
s.UpdateStateObject(stateObject) s.UpdateStateObject(stateObject)
} }
} }
@ -407,7 +404,7 @@ func (s *StateDB) CommitBatch() (root common.Hash, batch ethdb.Batch) {
return root, batch return root, batch
} }
func (s *StateDB) commit(db 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 { for _, stateObject := range s.stateObjects {
@ -417,36 +414,24 @@ func (s *StateDB) commit(db trie.DatabaseWriter) (common.Hash, error) {
s.DeleteStateObject(stateObject) s.DeleteStateObject(stateObject)
} else { } else {
// Write any contract code associated with the state object // Write any contract code associated with the state object
if len(stateObject.code) > 0 { if stateObject.code != nil {
if err := db.Put(stateObject.codeHash, stateObject.code); err != nil { if err := dbw.Put(stateObject.CodeHash(), stateObject.code); err != nil {
return common.Hash{}, err return common.Hash{}, err
} }
} }
// Write any storage changes in the state object to its trie.
stateObject.Update()
// Commit the trie of the object to the batch. // Write any storage changes in the state object to its storage trie.
// This updates the trie root internally, so if err := stateObject.CommitTrie(s.db, dbw); err != nil {
// getting the root hash of the storage trie
// through UpdateStateObject is fast.
if _, err := stateObject.trie.CommitTo(db); err != nil {
return common.Hash{}, err return common.Hash{}, err
} }
// Update the object in the account trie. // Update the object in the main account trie.
s.UpdateStateObject(stateObject) s.UpdateStateObject(stateObject)
} }
stateObject.dirty = false stateObject.dirty = false
} }
return s.trie.CommitTo(db) return s.trie.CommitTo(dbw)
} }
func (self *StateDB) Refunds() *big.Int { func (self *StateDB) Refunds() *big.Int {
return self.refund return self.refund
} }
// Debug stuff
func (self *StateDB) CreateOutputForDiff() {
for _, stateObject := range self.stateObjects {
stateObject.CreateOutputForDiff()
}
}

View file

@ -62,7 +62,7 @@ func makeTestState() (common.Hash, ethdb.Database) {
} }
so.AddBalance(big.NewInt(int64(i))) so.AddBalance(big.NewInt(int64(i)))
so.SetCode([]byte{i, i, i}) so.SetCode([]byte{i, i, i})
so.Update() so.UpdateRoot(sdb)
st.UpdateStateObject(so) st.UpdateStateObject(so)
} }
root, _ := st.Commit() root, _ := st.Commit()

View file

@ -187,7 +187,7 @@ func runStateTest(ruleSet RuleSet, test VmTest) error {
} }
for addr, value := range account.Storage { for addr, value := range account.Storage {
v := obj.GetState(common.HexToHash(addr)) v := statedb.GetState(obj.Address(), common.HexToHash(addr))
vexp := common.HexToHash(value) vexp := common.HexToHash(value)
if v != vexp { if v != vexp {

View file

@ -205,11 +205,9 @@ func runVmTest(test VmTest) error {
if obj == nil { if obj == nil {
continue continue
} }
for addr, value := range account.Storage { for addr, value := range account.Storage {
v := obj.GetState(common.HexToHash(addr)) v := statedb.GetState(obj.Address(), common.HexToHash(addr))
vexp := common.HexToHash(value) vexp := common.HexToHash(value)
if v != vexp { if v != vexp {
return fmt.Errorf("(%x: %s) storage failed. Expected %x, got %x (%v %v)\n", obj.Address().Bytes()[0:4], addr, vexp, v, vexp.Big(), v.Big()) return fmt.Errorf("(%x: %s) storage failed. Expected %x, got %x (%v %v)\n", obj.Address().Bytes()[0:4], addr, vexp, v, vexp.Big(), v.Big())
} }