trie errors propagated through state

This commit is contained in:
zsfelfoldi 2015-10-16 03:19:06 +02:00
parent f466243417
commit b87e55e722
7 changed files with 156 additions and 90 deletions

View file

@ -45,7 +45,7 @@ func (self *StateDB) RawDump() World {
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 := NewStateObjectFromBytes(common.BytesToAddress(addr), it.Value, self.db) stateObject, _ := NewStateObjectFromBytes(common.BytesToAddress(addr), it.Value, self.db)
account := Account{Balance: stateObject.balance.String(), Nonce: stateObject.nonce, Root: common.Bytes2Hex(stateObject.Root()), CodeHash: common.Bytes2Hex(stateObject.codeHash)} account := Account{Balance: stateObject.balance.String(), Nonce: stateObject.nonce, Root: common.Bytes2Hex(stateObject.Root()), CodeHash: common.Bytes2Hex(stateObject.codeHash)}
account.Storage = make(map[string]string) account.Storage = make(map[string]string)

View file

@ -91,7 +91,8 @@ func (ms *ManagedState) GetNonce(addr common.Address) uint64 {
account := ms.getAccount(addr) account := ms.getAccount(addr)
return uint64(len(account.nonces)) + account.nstart return uint64(len(account.nonces)) + account.nstart
} else { } else {
return ms.StateDB.GetNonce(addr) nonce, _ := ms.StateDB.GetNonce(addr)
return nonce
} }
} }
@ -100,7 +101,7 @@ func (ms *ManagedState) SetNonce(addr common.Address, nonce uint64) {
ms.mu.Lock() ms.mu.Lock()
defer ms.mu.Unlock() defer ms.mu.Unlock()
so := ms.GetOrNewStateObject(addr) so, _ := ms.GetOrNewStateObject(addr)
so.SetNonce(nonce) so.SetNonce(nonce)
ms.accounts[addr.Str()] = newAccount(so) ms.accounts[addr.Str()] = newAccount(so)
@ -122,12 +123,12 @@ func (ms *ManagedState) hasAccount(addr common.Address) bool {
func (ms *ManagedState) getAccount(addr common.Address) *account { func (ms *ManagedState) getAccount(addr common.Address) *account {
straddr := addr.Str() straddr := addr.Str()
if account, ok := ms.accounts[straddr]; !ok { if account, ok := ms.accounts[straddr]; !ok {
so := ms.GetOrNewStateObject(addr) so, _ := ms.GetOrNewStateObject(addr)
ms.accounts[straddr] = newAccount(so) ms.accounts[straddr] = newAccount(so)
} else { } else {
// 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[straddr] = newAccount(so) ms.accounts[straddr] = newAccount(so)
} }

View file

@ -96,7 +96,7 @@ func NewStateObject(address common.Address, db ethdb.Database) *StateObject {
return object return object
} }
func NewStateObjectFromBytes(address common.Address, data []byte, db ethdb.Database) *StateObject { func NewStateObjectFromBytes(address common.Address, data []byte, db ethdb.Database) (*StateObject, error) {
var extobject struct { var extobject struct {
Nonce uint64 Nonce uint64
Balance *big.Int Balance *big.Int
@ -106,13 +106,13 @@ func NewStateObjectFromBytes(address common.Address, data []byte, db ethdb.Datab
err := rlp.Decode(bytes.NewReader(data), &extobject) err := rlp.Decode(bytes.NewReader(data), &extobject)
if err != nil { if err != nil {
glog.Errorf("can't decode state object %x: %v", address, err) glog.Errorf("can't decode state object %x: %v", address, err)
return nil return nil, err
} }
trie, err := trie.NewSecure(extobject.Root, db) trie, err := trie.NewSecure(extobject.Root, db)
if err != nil { if err != nil {
// TODO: bubble this up or panic // TODO: bubble this up or panic
glog.Errorf("can't create account trie with root %x: %v", extobject.Root[:], err) glog.Errorf("can't create account trie with root %x: %v", extobject.Root[:], err)
return nil return nil, err
} }
object := &StateObject{address: address, db: db} object := &StateObject{address: address, db: db}
@ -122,8 +122,12 @@ func NewStateObjectFromBytes(address common.Address, data []byte, db ethdb.Datab
object.trie = trie object.trie = trie
object.storage = make(map[string]common.Hash) object.storage = make(map[string]common.Hash)
object.gasPool = new(big.Int) object.gasPool = new(big.Int)
object.code, _ = db.Get(extobject.CodeHash) object.code, err = db.Get(extobject.CodeHash)
return object if err != nil {
glog.Errorf("can't retrieve contract code %x: %v", extobject.CodeHash, err)
return nil, err
}
return object, nil
} }
func (self *StateObject) MarkForDeletion() { func (self *StateObject) MarkForDeletion() {
@ -135,10 +139,14 @@ func (self *StateObject) MarkForDeletion() {
} }
} }
func (c *StateObject) getAddr(addr common.Hash) common.Hash { func (c *StateObject) getAddr(addr common.Hash) (common.Hash, error) {
var ret []byte var ret []byte
rlp.DecodeBytes(c.trie.Get(addr[:]), &ret) value, err := c.trie.Get(addr[:])
return common.BytesToHash(ret) if err != nil {
return common.Hash{}, err
}
rlp.DecodeBytes(value, &ret)
return common.BytesToHash(ret), nil
} }
func (c *StateObject) setAddr(addr []byte, value common.Hash) { func (c *StateObject) setAddr(addr []byte, value common.Hash) {
@ -154,17 +162,21 @@ func (self *StateObject) Storage() Storage {
return self.storage return self.storage
} }
func (self *StateObject) GetState(key common.Hash) common.Hash { func (self *StateObject) GetState(key common.Hash) (common.Hash, error) {
strkey := key.Str() strkey := key.Str()
value, exists := self.storage[strkey] value, exists := self.storage[strkey]
if !exists { if !exists {
value = self.getAddr(key) var err error
value, err = self.getAddr(key)
if err != nil {
return common.Hash{}, err
}
if (value != common.Hash{}) { if (value != common.Hash{}) {
self.storage[strkey] = value self.storage[strkey] = value
} }
} }
return value return value, nil
} }
func (self *StateObject) SetState(k, value common.Hash) { func (self *StateObject) SetState(k, value common.Hash) {

View file

@ -99,105 +99,152 @@ func (self *StateDB) AddRefund(gas *big.Int) {
self.refund.Add(self.refund, gas) self.refund.Add(self.refund, gas)
} }
func (self *StateDB) HasAccount(addr common.Address) bool { func (self *StateDB) HasAccount(addr common.Address) (bool, error) {
return self.GetStateObject(addr) != nil acc, err := self.GetStateObject(addr)
if err != nil {
return false, err
}
return acc != nil, nil
} }
func (self *StateDB) Exist(addr common.Address) bool { func (self *StateDB) Exist(addr common.Address) (bool, error) {
return self.GetStateObject(addr) != nil acc, err := self.GetStateObject(addr)
if err != nil {
return false, err
}
return acc != nil, nil
} }
func (self *StateDB) GetAccount(addr common.Address) vm.Account { func (self *StateDB) GetAccount(addr common.Address) (vm.Account, error) {
return self.GetStateObject(addr) acc, err := self.GetStateObject(addr)
if err != nil {
return nil, err
}
return acc, nil
} }
// Retrieve the balance from the given address or 0 if object not found // Retrieve the balance from the given address or 0 if object not found
func (self *StateDB) GetBalance(addr common.Address) *big.Int { func (self *StateDB) GetBalance(addr common.Address) (*big.Int, error) {
stateObject := self.GetStateObject(addr) stateObject, err := self.GetStateObject(addr)
if err != nil {
return nil, err
}
if stateObject != nil { if stateObject != nil {
return stateObject.balance return stateObject.balance, nil
} }
return common.Big0 return common.Big0, nil
}
func (self *StateDB) GetNonce(addr common.Address) (uint64, error) {
stateObject, err := self.GetStateObject(addr)
if err != nil {
return 0, err
} }
func (self *StateDB) GetNonce(addr common.Address) uint64 {
stateObject := self.GetStateObject(addr)
if stateObject != nil { if stateObject != nil {
return stateObject.nonce return stateObject.nonce, nil
} }
return 0 return 0, nil
} }
func (self *StateDB) GetCode(addr common.Address) []byte { func (self *StateDB) GetCode(addr common.Address) ([]byte, error) {
stateObject := self.GetStateObject(addr) stateObject, err := self.GetStateObject(addr)
if err != nil {
return nil, err
}
if stateObject != nil { if stateObject != nil {
return stateObject.code return stateObject.code, nil
}
return nil, nil
} }
return nil func (self *StateDB) GetState(a common.Address, b common.Hash) (common.Hash, error) {
stateObject, err := self.GetStateObject(a)
if err != nil {
return common.Hash{}, err
} }
func (self *StateDB) GetState(a common.Address, b common.Hash) common.Hash {
stateObject := self.GetStateObject(a)
if stateObject != nil { if stateObject != nil {
return stateObject.GetState(b) return stateObject.GetState(b)
} }
return common.Hash{} return common.Hash{}, nil
} }
func (self *StateDB) IsDeleted(addr common.Address) bool { func (self *StateDB) IsDeleted(addr common.Address) (bool, error) {
stateObject := self.GetStateObject(addr) stateObject, err := self.GetStateObject(addr)
if stateObject != nil { if err != nil {
return stateObject.remove return false, err
} }
return false if stateObject != nil {
return stateObject.remove, nil
}
return false, nil
} }
/* /*
* SETTERS * SETTERS
*/ */
func (self *StateDB) AddBalance(addr common.Address, amount *big.Int) { func (self *StateDB) AddBalance(addr common.Address, amount *big.Int) error {
stateObject := self.GetOrNewStateObject(addr) stateObject, err := self.GetOrNewStateObject(addr)
if err != nil {
return err
}
if stateObject != nil { if stateObject != nil {
stateObject.AddBalance(amount) stateObject.AddBalance(amount)
} }
return nil
} }
func (self *StateDB) SetNonce(addr common.Address, nonce uint64) { func (self *StateDB) SetNonce(addr common.Address, nonce uint64) error {
stateObject := self.GetOrNewStateObject(addr) stateObject, err := self.GetOrNewStateObject(addr)
if err != nil {
return err
}
if stateObject != nil { if stateObject != nil {
stateObject.SetNonce(nonce) stateObject.SetNonce(nonce)
} }
return nil
} }
func (self *StateDB) SetCode(addr common.Address, code []byte) { func (self *StateDB) SetCode(addr common.Address, code []byte) error {
stateObject := self.GetOrNewStateObject(addr) stateObject, err := self.GetOrNewStateObject(addr)
if err != nil {
return err
}
if stateObject != nil { if stateObject != nil {
stateObject.SetCode(code) stateObject.SetCode(code)
} }
return nil
} }
func (self *StateDB) SetState(addr common.Address, key common.Hash, value common.Hash) { func (self *StateDB) SetState(addr common.Address, key common.Hash, value common.Hash) error {
stateObject := self.GetOrNewStateObject(addr) stateObject, err := self.GetOrNewStateObject(addr)
if err != nil {
return err
}
if stateObject != nil { if stateObject != nil {
stateObject.SetState(key, value) stateObject.SetState(key, value)
} }
return nil
} }
func (self *StateDB) Delete(addr common.Address) bool { func (self *StateDB) Delete(addr common.Address) (bool, error) {
stateObject := self.GetStateObject(addr) stateObject, err := self.GetStateObject(addr)
if err != nil {
return false, err
}
if stateObject != nil { if stateObject != nil {
stateObject.MarkForDeletion() stateObject.MarkForDeletion()
stateObject.balance = new(big.Int) stateObject.balance = new(big.Int)
return true return true, nil
} }
return false return false, nil
} }
// //
@ -225,25 +272,28 @@ func (self *StateDB) DeleteStateObject(stateObject *StateObject) {
} }
// Retrieve a state object given my the address. Nil if not found // Retrieve a state object given my the address. Nil if not found
func (self *StateDB) GetStateObject(addr common.Address) (stateObject *StateObject) { func (self *StateDB) GetStateObject(addr common.Address) (stateObject *StateObject, err error) {
stateObject = self.stateObjects[addr.Str()] stateObject = self.stateObjects[addr.Str()]
if stateObject != nil { if stateObject != nil {
if stateObject.deleted { if stateObject.deleted {
stateObject = nil stateObject = nil
} }
return stateObject return stateObject, nil
} }
data := self.trie.Get(addr[:]) data, err := self.trie.Get(addr[:])
if len(data) == 0 { if err != nil {
return nil return nil, err
} }
stateObject = NewStateObjectFromBytes(addr, []byte(data), self.db) stateObject, err = NewStateObjectFromBytes(addr, []byte(data), self.db)
if err != nil {
return nil, err
}
self.SetStateObject(stateObject) self.SetStateObject(stateObject)
return stateObject return stateObject, nil
} }
func (self *StateDB) SetStateObject(object *StateObject) { func (self *StateDB) SetStateObject(object *StateObject) {
@ -251,13 +301,16 @@ func (self *StateDB) SetStateObject(object *StateObject) {
} }
// Retrieve a state object or create a new state object if nil // Retrieve a state object or create a new state object if nil
func (self *StateDB) GetOrNewStateObject(addr common.Address) *StateObject { func (self *StateDB) GetOrNewStateObject(addr common.Address) (*StateObject, error) {
stateObject := self.GetStateObject(addr) stateObject, err := self.GetStateObject(addr)
if err != nil {
return nil, err
}
if stateObject == nil || stateObject.deleted { if stateObject == nil || stateObject.deleted {
stateObject = self.CreateStateObject(addr) stateObject, err = self.CreateStateObject(addr)
} }
return stateObject return stateObject, err
} }
// NewStateObject create a state object whether it exist in the trie or not // NewStateObject create a state object whether it exist in the trie or not
@ -274,9 +327,12 @@ func (self *StateDB) newStateObject(addr common.Address) *StateObject {
} }
// Creates creates a new state object and takes ownership. This is different from "NewStateObject" // Creates creates a new state object and takes ownership. This is different from "NewStateObject"
func (self *StateDB) CreateStateObject(addr common.Address) *StateObject { func (self *StateDB) CreateStateObject(addr common.Address) (*StateObject, error) {
// Get previous (if any) // Get previous (if any)
so := self.GetStateObject(addr) so, err := self.GetStateObject(addr)
if err != nil {
return nil, err
}
// Create a new one // Create a new one
newSo := self.newStateObject(addr) newSo := self.newStateObject(addr)
@ -285,10 +341,10 @@ func (self *StateDB) CreateStateObject(addr common.Address) *StateObject {
newSo.balance = so.balance newSo.balance = so.balance
} }
return newSo return newSo, nil
} }
func (self *StateDB) CreateAccount(addr common.Address) vm.Account { func (self *StateDB) CreateAccount(addr common.Address) (vm.Account, error) {
return self.CreateStateObject(addr) return self.CreateStateObject(addr)
} }

View file

@ -62,7 +62,7 @@ func NewSecure(root common.Hash, db Database) (*SecureTrie, error) {
// Get returns the value for key stored in the trie. // Get returns the value for key stored in the trie.
// The value bytes must not be modified by the caller. // The value bytes must not be modified by the caller.
func (t *SecureTrie) Get(key []byte) []byte { func (t *SecureTrie) Get(key []byte) ([]byte, error) {
return t.Trie.Get(t.hashKey(key)) return t.Trie.Get(t.hashKey(key))
} }

View file

@ -25,8 +25,6 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto/sha3" "github.com/ethereum/go-ethereum/crypto/sha3"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
) )
@ -40,6 +38,8 @@ var (
) )
var ErrMissingRoot = errors.New("missing root node") var ErrMissingRoot = errors.New("missing root node")
var ErrMissingNode = errors.New("missing trie node")
var ErrInvalidNode = errors.New("invalid trie node")
// Database must be implemented by backing stores for the trie. // Database must be implemented by backing stores for the trie.
type Database interface { type Database interface {
@ -94,29 +94,30 @@ func (t *Trie) Iterator() *Iterator {
// Get returns the value for key stored in the trie. // Get returns the value for key stored in the trie.
// The value bytes must not be modified by the caller. // The value bytes must not be modified by the caller.
func (t *Trie) Get(key []byte) []byte { func (t *Trie) Get(key []byte) ([]byte, error) {
key = compactHexDecode(key) key = compactHexDecode(key)
tn := t.root tn := t.root
for len(key) > 0 { for len(key) > 0 {
switch n := tn.(type) { switch n := tn.(type) {
case shortNode: case shortNode:
if len(key) < len(n.Key) || !bytes.Equal(n.Key, key[:len(n.Key)]) { if len(key) < len(n.Key) || !bytes.Equal(n.Key, key[:len(n.Key)]) {
return nil return nil, ErrInvalidNode
} }
tn = n.Val tn = n.Val
key = key[len(n.Key):] key = key[len(n.Key):]
case fullNode: case fullNode:
tn = n[key[0]] tn = n[key[0]]
key = key[1:] key = key[1:]
case nil:
return nil
case hashNode: case hashNode:
tn = t.resolveHash(n) tn = t.resolveHash(n)
if tn == nil {
return nil, ErrMissingNode
}
default: default:
panic(fmt.Sprintf("%T: invalid node: %v", tn, tn)) panic(fmt.Sprintf("%T: invalid node: %v", tn, tn))
} }
} }
return tn.(valueNode) return tn.(valueNode), nil
} }
// Update associates key with value in the trie. Subsequent calls to // Update associates key with value in the trie. Subsequent calls to
@ -295,14 +296,6 @@ func (t *Trie) resolveHash(n hashNode) node {
} }
enc, err := t.db.Get(n) enc, err := t.db.Get(n)
if err != nil || enc == nil { if err != nil || enc == nil {
// TODO: This needs to be improved to properly distinguish errors.
// Disk I/O errors shouldn't produce nil (and cause a
// consensus failure or weird crash), but it is unclear how
// they could be handled because the entire stack above the trie isn't
// prepared to cope with missing state nodes.
if glog.V(logger.Error) {
glog.Errorf("Dangling hash node ref %x: %v", n, err)
}
return nil return nil
} }
dec := mustDecodeNode(n, enc) dec := mustDecodeNode(n, enc)

View file

@ -490,8 +490,12 @@ func (self *XEth) StorageAt(addr, storageAddr string) string {
return self.State().state.GetState(common.HexToAddress(addr), common.HexToHash(storageAddr)).Hex() return self.State().state.GetState(common.HexToAddress(addr), common.HexToHash(storageAddr)).Hex()
} }
func (self *XEth) BalanceAt(addr string) string { func (self *XEth) BalanceAt(addr string) (string, error) {
return common.ToHex(self.State().state.GetBalance(common.HexToAddress(addr)).Bytes()) val, err := self.State().state.GetBalance(common.HexToAddress(addr))
if err != nil {
return nil, err
}
return common.ToHex(val.Bytes())
} }
func (self *XEth) TxCountAt(address string) int { func (self *XEth) TxCountAt(address string) int {