forked from mirror/go-ethereum
core/state: access trie through Database interface, track errors (#14589)
With this commit, core/state's access to the underlying key/value database is mediated through an interface. Database errors are tracked in StateDB and returned by CommitTo or the new Error method. Motivation for this change: We can remove the light client's duplicated copy of core/state. The light client now supports node iteration, so tracing and storage enumeration can work with the light client (not implemented in this commit).release/1.6
parent
bb366271fe
commit
9e5f03b6c4
@ -0,0 +1,154 @@ |
|||||||
|
// Copyright 2017 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package state |
||||||
|
|
||||||
|
import ( |
||||||
|
"fmt" |
||||||
|
"sync" |
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common" |
||||||
|
"github.com/ethereum/go-ethereum/ethdb" |
||||||
|
"github.com/ethereum/go-ethereum/trie" |
||||||
|
lru "github.com/hashicorp/golang-lru" |
||||||
|
) |
||||||
|
|
||||||
|
// Trie cache generation limit after which to evic trie nodes from memory.
|
||||||
|
var MaxTrieCacheGen = uint16(120) |
||||||
|
|
||||||
|
const ( |
||||||
|
// Number of past tries to keep. This value is chosen such that
|
||||||
|
// reasonable chain reorg depths will hit an existing trie.
|
||||||
|
maxPastTries = 12 |
||||||
|
|
||||||
|
// Number of codehash->size associations to keep.
|
||||||
|
codeSizeCacheSize = 100000 |
||||||
|
) |
||||||
|
|
||||||
|
// Database wraps access to tries and contract code.
|
||||||
|
type Database interface { |
||||||
|
// Accessing tries:
|
||||||
|
// OpenTrie opens the main account trie.
|
||||||
|
// OpenStorageTrie opens the storage trie of an account.
|
||||||
|
OpenTrie(root common.Hash) (Trie, error) |
||||||
|
OpenStorageTrie(addrHash, root common.Hash) (Trie, error) |
||||||
|
// Accessing contract code:
|
||||||
|
ContractCode(addrHash, codeHash common.Hash) ([]byte, error) |
||||||
|
ContractCodeSize(addrHash, codeHash common.Hash) (int, error) |
||||||
|
// CopyTrie returns an independent copy of the given trie.
|
||||||
|
CopyTrie(Trie) Trie |
||||||
|
} |
||||||
|
|
||||||
|
// Trie is a Ethereum Merkle Trie.
|
||||||
|
type Trie interface { |
||||||
|
TryGet(key []byte) ([]byte, error) |
||||||
|
TryUpdate(key, value []byte) error |
||||||
|
TryDelete(key []byte) error |
||||||
|
CommitTo(trie.DatabaseWriter) (common.Hash, error) |
||||||
|
Hash() common.Hash |
||||||
|
NodeIterator(startKey []byte) trie.NodeIterator |
||||||
|
GetKey([]byte) []byte // TODO(fjl): remove this when SecureTrie is removed
|
||||||
|
} |
||||||
|
|
||||||
|
// NewDatabase creates a backing store for state. The returned database is safe for
|
||||||
|
// concurrent use and retains cached trie nodes in memory.
|
||||||
|
func NewDatabase(db ethdb.Database) Database { |
||||||
|
csc, _ := lru.New(codeSizeCacheSize) |
||||||
|
return &cachingDB{db: db, codeSizeCache: csc} |
||||||
|
} |
||||||
|
|
||||||
|
type cachingDB struct { |
||||||
|
db ethdb.Database |
||||||
|
mu sync.Mutex |
||||||
|
pastTries []*trie.SecureTrie |
||||||
|
codeSizeCache *lru.Cache |
||||||
|
} |
||||||
|
|
||||||
|
func (db *cachingDB) OpenTrie(root common.Hash) (Trie, error) { |
||||||
|
db.mu.Lock() |
||||||
|
defer db.mu.Unlock() |
||||||
|
|
||||||
|
for i := len(db.pastTries) - 1; i >= 0; i-- { |
||||||
|
if db.pastTries[i].Hash() == root { |
||||||
|
return cachedTrie{db.pastTries[i].Copy(), db}, nil |
||||||
|
} |
||||||
|
} |
||||||
|
tr, err := trie.NewSecure(root, db.db, MaxTrieCacheGen) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
return cachedTrie{tr, db}, nil |
||||||
|
} |
||||||
|
|
||||||
|
func (db *cachingDB) pushTrie(t *trie.SecureTrie) { |
||||||
|
db.mu.Lock() |
||||||
|
defer db.mu.Unlock() |
||||||
|
|
||||||
|
if len(db.pastTries) >= maxPastTries { |
||||||
|
copy(db.pastTries, db.pastTries[1:]) |
||||||
|
db.pastTries[len(db.pastTries)-1] = t |
||||||
|
} else { |
||||||
|
db.pastTries = append(db.pastTries, t) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func (db *cachingDB) OpenStorageTrie(addrHash, root common.Hash) (Trie, error) { |
||||||
|
return trie.NewSecure(root, db.db, 0) |
||||||
|
} |
||||||
|
|
||||||
|
func (db *cachingDB) CopyTrie(t Trie) Trie { |
||||||
|
switch t := t.(type) { |
||||||
|
case cachedTrie: |
||||||
|
return cachedTrie{t.SecureTrie.Copy(), db} |
||||||
|
case *trie.SecureTrie: |
||||||
|
return t.Copy() |
||||||
|
default: |
||||||
|
panic(fmt.Errorf("unknown trie type %T", t)) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func (db *cachingDB) ContractCode(addrHash, codeHash common.Hash) ([]byte, error) { |
||||||
|
code, err := db.db.Get(codeHash[:]) |
||||||
|
if err == nil { |
||||||
|
db.codeSizeCache.Add(codeHash, len(code)) |
||||||
|
} |
||||||
|
return code, err |
||||||
|
} |
||||||
|
|
||||||
|
func (db *cachingDB) ContractCodeSize(addrHash, codeHash common.Hash) (int, error) { |
||||||
|
if cached, ok := db.codeSizeCache.Get(codeHash); ok { |
||||||
|
return cached.(int), nil |
||||||
|
} |
||||||
|
code, err := db.ContractCode(addrHash, codeHash) |
||||||
|
if err == nil { |
||||||
|
db.codeSizeCache.Add(codeHash, len(code)) |
||||||
|
} |
||||||
|
return len(code), err |
||||||
|
} |
||||||
|
|
||||||
|
// cachedTrie inserts its trie into a cachingDB on commit.
|
||||||
|
type cachedTrie struct { |
||||||
|
*trie.SecureTrie |
||||||
|
db *cachingDB |
||||||
|
} |
||||||
|
|
||||||
|
func (m cachedTrie) CommitTo(dbw trie.DatabaseWriter) (common.Hash, error) { |
||||||
|
root, err := m.SecureTrie.CommitTo(dbw) |
||||||
|
if err == nil { |
||||||
|
m.db.pushTrie(m.SecureTrie) |
||||||
|
} |
||||||
|
return root, err |
||||||
|
} |
@ -1,316 +0,0 @@ |
|||||||
// Copyright 2015 The go-ethereum Authors
|
|
||||||
// This file is part of the go-ethereum library.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package light |
|
||||||
|
|
||||||
import ( |
|
||||||
"context" |
|
||||||
"math/big" |
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common" |
|
||||||
"github.com/ethereum/go-ethereum/crypto" |
|
||||||
) |
|
||||||
|
|
||||||
// LightState is a memory representation of a state.
|
|
||||||
// This version is ODR capable, caching only the already accessed part of the
|
|
||||||
// state, retrieving unknown parts on-demand from the ODR backend. Changes are
|
|
||||||
// never stored in the local database, only in the memory objects.
|
|
||||||
type LightState struct { |
|
||||||
odr OdrBackend |
|
||||||
trie *LightTrie |
|
||||||
id *TrieID |
|
||||||
stateObjects map[string]*StateObject |
|
||||||
refund *big.Int |
|
||||||
} |
|
||||||
|
|
||||||
// NewLightState creates a new LightState with the specified root.
|
|
||||||
// Note that the creation of a light state is always successful, even if the
|
|
||||||
// root is non-existent. In that case, ODR retrieval will always be unsuccessful
|
|
||||||
// and every operation will return with an error or wait for the context to be
|
|
||||||
// cancelled.
|
|
||||||
func NewLightState(id *TrieID, odr OdrBackend) *LightState { |
|
||||||
var tr *LightTrie |
|
||||||
if id != nil { |
|
||||||
tr = NewLightTrie(id, odr, true) |
|
||||||
} |
|
||||||
return &LightState{ |
|
||||||
odr: odr, |
|
||||||
trie: tr, |
|
||||||
id: id, |
|
||||||
stateObjects: make(map[string]*StateObject), |
|
||||||
refund: new(big.Int), |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// AddRefund adds an amount to the refund value collected during a vm execution
|
|
||||||
func (self *LightState) AddRefund(gas *big.Int) { |
|
||||||
self.refund.Add(self.refund, gas) |
|
||||||
} |
|
||||||
|
|
||||||
// HasAccount returns true if an account exists at the given address
|
|
||||||
func (self *LightState) HasAccount(ctx context.Context, addr common.Address) (bool, error) { |
|
||||||
so, err := self.GetStateObject(ctx, addr) |
|
||||||
return so != nil, err |
|
||||||
} |
|
||||||
|
|
||||||
// GetBalance retrieves the balance from the given address or 0 if the account does
|
|
||||||
// not exist
|
|
||||||
func (self *LightState) GetBalance(ctx context.Context, addr common.Address) (*big.Int, error) { |
|
||||||
stateObject, err := self.GetStateObject(ctx, addr) |
|
||||||
if err != nil { |
|
||||||
return common.Big0, err |
|
||||||
} |
|
||||||
if stateObject != nil { |
|
||||||
return stateObject.balance, nil |
|
||||||
} |
|
||||||
|
|
||||||
return common.Big0, nil |
|
||||||
} |
|
||||||
|
|
||||||
// GetNonce returns the nonce at the given address or 0 if the account does
|
|
||||||
// not exist
|
|
||||||
func (self *LightState) GetNonce(ctx context.Context, addr common.Address) (uint64, error) { |
|
||||||
stateObject, err := self.GetStateObject(ctx, addr) |
|
||||||
if err != nil { |
|
||||||
return 0, err |
|
||||||
} |
|
||||||
if stateObject != nil { |
|
||||||
return stateObject.nonce, nil |
|
||||||
} |
|
||||||
return 0, nil |
|
||||||
} |
|
||||||
|
|
||||||
// GetCode returns the contract code at the given address or nil if the account
|
|
||||||
// does not exist
|
|
||||||
func (self *LightState) GetCode(ctx context.Context, addr common.Address) ([]byte, error) { |
|
||||||
stateObject, err := self.GetStateObject(ctx, addr) |
|
||||||
if err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
if stateObject != nil { |
|
||||||
return stateObject.code, nil |
|
||||||
} |
|
||||||
return nil, nil |
|
||||||
} |
|
||||||
|
|
||||||
// GetState returns the contract storage value at storage address b from the
|
|
||||||
// contract address a or common.Hash{} if the account does not exist
|
|
||||||
func (self *LightState) GetState(ctx context.Context, a common.Address, b common.Hash) (common.Hash, error) { |
|
||||||
stateObject, err := self.GetStateObject(ctx, a) |
|
||||||
if err == nil && stateObject != nil { |
|
||||||
return stateObject.GetState(ctx, b) |
|
||||||
} |
|
||||||
return common.Hash{}, err |
|
||||||
} |
|
||||||
|
|
||||||
// HasSuicided returns true if the given account has been marked for deletion
|
|
||||||
// or false if the account does not exist
|
|
||||||
func (self *LightState) HasSuicided(ctx context.Context, addr common.Address) (bool, error) { |
|
||||||
stateObject, err := self.GetStateObject(ctx, addr) |
|
||||||
if err == nil && stateObject != nil { |
|
||||||
return stateObject.remove, nil |
|
||||||
} |
|
||||||
return false, err |
|
||||||
} |
|
||||||
|
|
||||||
/* |
|
||||||
* SETTERS |
|
||||||
*/ |
|
||||||
|
|
||||||
// AddBalance adds the given amount to the balance of the specified account
|
|
||||||
func (self *LightState) AddBalance(ctx context.Context, addr common.Address, amount *big.Int) error { |
|
||||||
stateObject, err := self.GetOrNewStateObject(ctx, addr) |
|
||||||
if err == nil && stateObject != nil { |
|
||||||
stateObject.AddBalance(amount) |
|
||||||
} |
|
||||||
return err |
|
||||||
} |
|
||||||
|
|
||||||
// SubBalance adds the given amount to the balance of the specified account
|
|
||||||
func (self *LightState) SubBalance(ctx context.Context, addr common.Address, amount *big.Int) error { |
|
||||||
stateObject, err := self.GetOrNewStateObject(ctx, addr) |
|
||||||
if err == nil && stateObject != nil { |
|
||||||
stateObject.SubBalance(amount) |
|
||||||
} |
|
||||||
return err |
|
||||||
} |
|
||||||
|
|
||||||
// SetNonce sets the nonce of the specified account
|
|
||||||
func (self *LightState) SetNonce(ctx context.Context, addr common.Address, nonce uint64) error { |
|
||||||
stateObject, err := self.GetOrNewStateObject(ctx, addr) |
|
||||||
if err == nil && stateObject != nil { |
|
||||||
stateObject.SetNonce(nonce) |
|
||||||
} |
|
||||||
return err |
|
||||||
} |
|
||||||
|
|
||||||
// SetCode sets the contract code at the specified account
|
|
||||||
func (self *LightState) SetCode(ctx context.Context, addr common.Address, code []byte) error { |
|
||||||
stateObject, err := self.GetOrNewStateObject(ctx, addr) |
|
||||||
if err == nil && stateObject != nil { |
|
||||||
stateObject.SetCode(crypto.Keccak256Hash(code), code) |
|
||||||
} |
|
||||||
return err |
|
||||||
} |
|
||||||
|
|
||||||
// SetState sets the storage value at storage address key of the account addr
|
|
||||||
func (self *LightState) SetState(ctx context.Context, addr common.Address, key common.Hash, value common.Hash) error { |
|
||||||
stateObject, err := self.GetOrNewStateObject(ctx, addr) |
|
||||||
if err == nil && stateObject != nil { |
|
||||||
stateObject.SetState(key, value) |
|
||||||
} |
|
||||||
return err |
|
||||||
} |
|
||||||
|
|
||||||
// Delete marks an account to be removed and clears its balance
|
|
||||||
func (self *LightState) Suicide(ctx context.Context, addr common.Address) (bool, error) { |
|
||||||
stateObject, err := self.GetOrNewStateObject(ctx, addr) |
|
||||||
if err == nil && stateObject != nil { |
|
||||||
stateObject.MarkForDeletion() |
|
||||||
stateObject.balance = new(big.Int) |
|
||||||
|
|
||||||
return true, nil |
|
||||||
} |
|
||||||
|
|
||||||
return false, err |
|
||||||
} |
|
||||||
|
|
||||||
//
|
|
||||||
// Get, set, new state object methods
|
|
||||||
//
|
|
||||||
|
|
||||||
// GetStateObject returns the state object of the given account or nil if the
|
|
||||||
// account does not exist
|
|
||||||
func (self *LightState) GetStateObject(ctx context.Context, addr common.Address) (stateObject *StateObject, err error) { |
|
||||||
stateObject = self.stateObjects[addr.Str()] |
|
||||||
if stateObject != nil { |
|
||||||
if stateObject.deleted { |
|
||||||
stateObject = nil |
|
||||||
} |
|
||||||
return stateObject, nil |
|
||||||
} |
|
||||||
data, err := self.trie.Get(ctx, addr[:]) |
|
||||||
if err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
if len(data) == 0 { |
|
||||||
return nil, nil |
|
||||||
} |
|
||||||
|
|
||||||
stateObject, err = DecodeObject(ctx, self.id, addr, self.odr, []byte(data)) |
|
||||||
if err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
|
|
||||||
self.SetStateObject(stateObject) |
|
||||||
|
|
||||||
return stateObject, nil |
|
||||||
} |
|
||||||
|
|
||||||
// SetStateObject sets the state object of the given account
|
|
||||||
func (self *LightState) SetStateObject(object *StateObject) { |
|
||||||
self.stateObjects[object.Address().Str()] = object |
|
||||||
} |
|
||||||
|
|
||||||
// GetOrNewStateObject returns the state object of the given account or creates a
|
|
||||||
// new one if the account does not exist
|
|
||||||
func (self *LightState) GetOrNewStateObject(ctx context.Context, addr common.Address) (*StateObject, error) { |
|
||||||
stateObject, err := self.GetStateObject(ctx, addr) |
|
||||||
if err == nil && (stateObject == nil || stateObject.deleted) { |
|
||||||
stateObject, err = self.CreateStateObject(ctx, addr) |
|
||||||
} |
|
||||||
return stateObject, err |
|
||||||
} |
|
||||||
|
|
||||||
// newStateObject creates a state object whether it exists in the state or not
|
|
||||||
func (self *LightState) newStateObject(addr common.Address) *StateObject { |
|
||||||
stateObject := NewStateObject(addr, self.odr) |
|
||||||
self.stateObjects[addr.Str()] = stateObject |
|
||||||
|
|
||||||
return stateObject |
|
||||||
} |
|
||||||
|
|
||||||
// CreateStateObject creates creates a new state object and takes ownership.
|
|
||||||
// This is different from "NewStateObject"
|
|
||||||
func (self *LightState) CreateStateObject(ctx context.Context, addr common.Address) (*StateObject, error) { |
|
||||||
// Get previous (if any)
|
|
||||||
so, err := self.GetStateObject(ctx, addr) |
|
||||||
if err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
// Create a new one
|
|
||||||
newSo := self.newStateObject(addr) |
|
||||||
|
|
||||||
// If it existed set the balance to the new account
|
|
||||||
if so != nil { |
|
||||||
newSo.balance = so.balance |
|
||||||
} |
|
||||||
|
|
||||||
return newSo, nil |
|
||||||
} |
|
||||||
|
|
||||||
// ForEachStorage calls a callback function for every key/value pair found
|
|
||||||
// in the local storage cache. Note that unlike core/state.StateObject,
|
|
||||||
// light.StateObject only returns cached values and doesn't download the
|
|
||||||
// entire storage tree.
|
|
||||||
func (self *LightState) ForEachStorage(ctx context.Context, addr common.Address, cb func(key, value common.Hash) bool) error { |
|
||||||
so, err := self.GetStateObject(ctx, addr) |
|
||||||
if err != nil { |
|
||||||
return err |
|
||||||
} |
|
||||||
|
|
||||||
if so == nil { |
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
for h, v := range so.storage { |
|
||||||
cb(h, v) |
|
||||||
} |
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
//
|
|
||||||
// Setting, copying of the state methods
|
|
||||||
//
|
|
||||||
|
|
||||||
// Copy creates a copy of the state
|
|
||||||
func (self *LightState) Copy() *LightState { |
|
||||||
// ignore error - we assume state-to-be-copied always exists
|
|
||||||
state := NewLightState(nil, self.odr) |
|
||||||
state.trie = self.trie |
|
||||||
state.id = self.id |
|
||||||
for k, stateObject := range self.stateObjects { |
|
||||||
if stateObject.dirty { |
|
||||||
state.stateObjects[k] = stateObject.Copy() |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
state.refund.Set(self.refund) |
|
||||||
return state |
|
||||||
} |
|
||||||
|
|
||||||
// Set copies the contents of the given state onto this state, overwriting
|
|
||||||
// its contents
|
|
||||||
func (self *LightState) Set(state *LightState) { |
|
||||||
self.trie = state.trie |
|
||||||
self.stateObjects = state.stateObjects |
|
||||||
self.refund = state.refund |
|
||||||
} |
|
||||||
|
|
||||||
// GetRefund returns the refund value collected during a vm execution
|
|
||||||
func (self *LightState) GetRefund() *big.Int { |
|
||||||
return self.refund |
|
||||||
} |
|
@ -1,275 +0,0 @@ |
|||||||
// Copyright 2015 The go-ethereum Authors
|
|
||||||
// This file is part of the go-ethereum library.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package light |
|
||||||
|
|
||||||
import ( |
|
||||||
"bytes" |
|
||||||
"context" |
|
||||||
"fmt" |
|
||||||
"math/big" |
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common" |
|
||||||
"github.com/ethereum/go-ethereum/crypto" |
|
||||||
"github.com/ethereum/go-ethereum/rlp" |
|
||||||
) |
|
||||||
|
|
||||||
var emptyCodeHash = crypto.Keccak256(nil) |
|
||||||
|
|
||||||
// Code represents a contract code in binary form
|
|
||||||
type Code []byte |
|
||||||
|
|
||||||
// String returns a string representation of the code
|
|
||||||
func (self Code) String() string { |
|
||||||
return string(self) //strings.Join(Disassemble(self), " ")
|
|
||||||
} |
|
||||||
|
|
||||||
// Storage is a memory map cache of a contract storage
|
|
||||||
type Storage map[common.Hash]common.Hash |
|
||||||
|
|
||||||
// String returns a string representation of the storage cache
|
|
||||||
func (self Storage) String() (str string) { |
|
||||||
for key, value := range self { |
|
||||||
str += fmt.Sprintf("%X : %X\n", key, value) |
|
||||||
} |
|
||||||
|
|
||||||
return |
|
||||||
} |
|
||||||
|
|
||||||
// Copy copies the contents of a storage cache
|
|
||||||
func (self Storage) Copy() Storage { |
|
||||||
cpy := make(Storage) |
|
||||||
for key, value := range self { |
|
||||||
cpy[key] = value |
|
||||||
} |
|
||||||
|
|
||||||
return cpy |
|
||||||
} |
|
||||||
|
|
||||||
// StateObject is a memory representation of an account or contract and its storage.
|
|
||||||
// This version is ODR capable, caching only the already accessed part of the
|
|
||||||
// storage, retrieving unknown parts on-demand from the ODR backend. Changes are
|
|
||||||
// never stored in the local database, only in the memory objects.
|
|
||||||
type StateObject struct { |
|
||||||
odr OdrBackend |
|
||||||
trie *LightTrie |
|
||||||
|
|
||||||
// Address belonging to this account
|
|
||||||
address common.Address |
|
||||||
// The balance of the account
|
|
||||||
balance *big.Int |
|
||||||
// The nonce of the account
|
|
||||||
nonce uint64 |
|
||||||
// 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
|
|
||||||
// When an object is marked for deletion it will be delete from the trie
|
|
||||||
// during the "update" phase of the state transition
|
|
||||||
remove bool |
|
||||||
deleted bool |
|
||||||
dirty bool |
|
||||||
} |
|
||||||
|
|
||||||
// NewStateObject creates a new StateObject of the specified account address
|
|
||||||
func NewStateObject(address common.Address, odr OdrBackend) *StateObject { |
|
||||||
object := &StateObject{ |
|
||||||
odr: odr, |
|
||||||
address: address, |
|
||||||
balance: new(big.Int), |
|
||||||
dirty: true, |
|
||||||
codeHash: emptyCodeHash, |
|
||||||
storage: make(Storage), |
|
||||||
} |
|
||||||
object.trie = NewLightTrie(&TrieID{}, odr, true) |
|
||||||
return object |
|
||||||
} |
|
||||||
|
|
||||||
// MarkForDeletion marks an account to be removed
|
|
||||||
func (self *StateObject) MarkForDeletion() { |
|
||||||
self.remove = true |
|
||||||
self.dirty = true |
|
||||||
} |
|
||||||
|
|
||||||
// getAddr gets the storage value at the given address from the trie
|
|
||||||
func (c *StateObject) getAddr(ctx context.Context, addr common.Hash) (common.Hash, error) { |
|
||||||
var ret []byte |
|
||||||
val, err := c.trie.Get(ctx, addr[:]) |
|
||||||
if err != nil { |
|
||||||
return common.Hash{}, err |
|
||||||
} |
|
||||||
rlp.DecodeBytes(val, &ret) |
|
||||||
return common.BytesToHash(ret), nil |
|
||||||
} |
|
||||||
|
|
||||||
// Storage returns the storage cache object of the account
|
|
||||||
func (self *StateObject) Storage() Storage { |
|
||||||
return self.storage |
|
||||||
} |
|
||||||
|
|
||||||
// GetState returns the storage value at the given address from either the cache
|
|
||||||
// or the trie
|
|
||||||
func (self *StateObject) GetState(ctx context.Context, key common.Hash) (common.Hash, error) { |
|
||||||
value, exists := self.storage[key] |
|
||||||
if !exists { |
|
||||||
var err error |
|
||||||
value, err = self.getAddr(ctx, key) |
|
||||||
if err != nil { |
|
||||||
return common.Hash{}, err |
|
||||||
} |
|
||||||
if (value != common.Hash{}) { |
|
||||||
self.storage[key] = value |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
return value, nil |
|
||||||
} |
|
||||||
|
|
||||||
// SetState sets the storage value at the given address
|
|
||||||
func (self *StateObject) SetState(k, value common.Hash) { |
|
||||||
self.storage[k] = value |
|
||||||
self.dirty = true |
|
||||||
} |
|
||||||
|
|
||||||
// AddBalance adds the given amount to the account balance
|
|
||||||
func (c *StateObject) AddBalance(amount *big.Int) { |
|
||||||
c.SetBalance(new(big.Int).Add(c.balance, amount)) |
|
||||||
} |
|
||||||
|
|
||||||
// SubBalance subtracts the given amount from the account balance
|
|
||||||
func (c *StateObject) SubBalance(amount *big.Int) { |
|
||||||
c.SetBalance(new(big.Int).Sub(c.balance, amount)) |
|
||||||
} |
|
||||||
|
|
||||||
// SetBalance sets the account balance to the given amount
|
|
||||||
func (c *StateObject) SetBalance(amount *big.Int) { |
|
||||||
c.balance = amount |
|
||||||
c.dirty = true |
|
||||||
} |
|
||||||
|
|
||||||
// ReturnGas returns the gas back to the origin. Used by the Virtual machine or Closures
|
|
||||||
func (c *StateObject) ReturnGas(gas *big.Int) {} |
|
||||||
|
|
||||||
// Copy creates a copy of the state object
|
|
||||||
func (self *StateObject) Copy() *StateObject { |
|
||||||
stateObject := NewStateObject(self.Address(), self.odr) |
|
||||||
stateObject.balance.Set(self.balance) |
|
||||||
stateObject.codeHash = common.CopyBytes(self.codeHash) |
|
||||||
stateObject.nonce = self.nonce |
|
||||||
stateObject.trie = self.trie |
|
||||||
stateObject.code = self.code |
|
||||||
stateObject.storage = self.storage.Copy() |
|
||||||
stateObject.remove = self.remove |
|
||||||
stateObject.dirty = self.dirty |
|
||||||
stateObject.deleted = self.deleted |
|
||||||
|
|
||||||
return stateObject |
|
||||||
} |
|
||||||
|
|
||||||
//
|
|
||||||
// Attribute accessors
|
|
||||||
//
|
|
||||||
|
|
||||||
// empty returns whether the account is considered empty.
|
|
||||||
func (self *StateObject) empty() bool { |
|
||||||
return self.nonce == 0 && self.balance.Sign() == 0 && bytes.Equal(self.codeHash, emptyCodeHash) |
|
||||||
} |
|
||||||
|
|
||||||
// Balance returns the account balance
|
|
||||||
func (self *StateObject) Balance() *big.Int { |
|
||||||
return self.balance |
|
||||||
} |
|
||||||
|
|
||||||
// Address returns the address of the contract/account
|
|
||||||
func (self *StateObject) Address() common.Address { |
|
||||||
return self.address |
|
||||||
} |
|
||||||
|
|
||||||
// Code returns the contract code
|
|
||||||
func (self *StateObject) Code() []byte { |
|
||||||
return self.code |
|
||||||
} |
|
||||||
|
|
||||||
// SetCode sets the contract code
|
|
||||||
func (self *StateObject) SetCode(hash common.Hash, code []byte) { |
|
||||||
self.code = code |
|
||||||
self.codeHash = hash[:] |
|
||||||
self.dirty = true |
|
||||||
} |
|
||||||
|
|
||||||
// SetNonce sets the account nonce
|
|
||||||
func (self *StateObject) SetNonce(nonce uint64) { |
|
||||||
self.nonce = nonce |
|
||||||
self.dirty = true |
|
||||||
} |
|
||||||
|
|
||||||
// Nonce returns the account nonce
|
|
||||||
func (self *StateObject) Nonce() uint64 { |
|
||||||
return self.nonce |
|
||||||
} |
|
||||||
|
|
||||||
// ForEachStorage calls a callback function for every key/value pair found
|
|
||||||
// in the local storage cache. Note that unlike core/state.StateObject,
|
|
||||||
// light.StateObject only returns cached values and doesn't download the
|
|
||||||
// entire storage tree.
|
|
||||||
func (self *StateObject) ForEachStorage(cb func(key, value common.Hash) bool) { |
|
||||||
for h, v := range self.storage { |
|
||||||
cb(h, v) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// Never called, but must be present to allow StateObject to be used
|
|
||||||
// as a vm.Account interface that also satisfies the vm.ContractRef
|
|
||||||
// interface. Interfaces are awesome.
|
|
||||||
func (self *StateObject) Value() *big.Int { |
|
||||||
panic("Value on StateObject should never be called") |
|
||||||
} |
|
||||||
|
|
||||||
// Encoding
|
|
||||||
|
|
||||||
type extStateObject struct { |
|
||||||
Nonce uint64 |
|
||||||
Balance *big.Int |
|
||||||
Root common.Hash |
|
||||||
CodeHash []byte |
|
||||||
} |
|
||||||
|
|
||||||
// DecodeObject decodes an RLP-encoded state object.
|
|
||||||
func DecodeObject(ctx context.Context, stateID *TrieID, address common.Address, odr OdrBackend, data []byte) (*StateObject, error) { |
|
||||||
var ( |
|
||||||
obj = &StateObject{address: address, odr: odr, storage: make(Storage)} |
|
||||||
ext extStateObject |
|
||||||
err error |
|
||||||
) |
|
||||||
if err = rlp.DecodeBytes(data, &ext); err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
trieID := StorageTrieID(stateID, address, ext.Root) |
|
||||||
obj.trie = NewLightTrie(trieID, odr, true) |
|
||||||
if !bytes.Equal(ext.CodeHash, emptyCodeHash) { |
|
||||||
if obj.code, err = retrieveContractCode(ctx, obj.odr, trieID, common.BytesToHash(ext.CodeHash)); err != nil { |
|
||||||
return nil, fmt.Errorf("can't find code for hash %x: %v", ext.CodeHash, err) |
|
||||||
} |
|
||||||
} |
|
||||||
obj.nonce = ext.Nonce |
|
||||||
obj.balance = ext.Balance |
|
||||||
obj.codeHash = ext.CodeHash |
|
||||||
return obj, nil |
|
||||||
} |
|
@ -1,248 +0,0 @@ |
|||||||
// Copyright 2015 The go-ethereum Authors
|
|
||||||
// This file is part of the go-ethereum library.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package light |
|
||||||
|
|
||||||
import ( |
|
||||||
"bytes" |
|
||||||
"context" |
|
||||||
"math/big" |
|
||||||
"testing" |
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common" |
|
||||||
"github.com/ethereum/go-ethereum/core" |
|
||||||
"github.com/ethereum/go-ethereum/core/state" |
|
||||||
"github.com/ethereum/go-ethereum/core/types" |
|
||||||
"github.com/ethereum/go-ethereum/ethdb" |
|
||||||
) |
|
||||||
|
|
||||||
func makeTestState() (common.Hash, ethdb.Database) { |
|
||||||
sdb, _ := ethdb.NewMemDatabase() |
|
||||||
st, _ := state.New(common.Hash{}, sdb) |
|
||||||
for i := byte(0); i < 100; i++ { |
|
||||||
addr := common.Address{i} |
|
||||||
for j := byte(0); j < 100; j++ { |
|
||||||
st.SetState(addr, common.Hash{j}, common.Hash{i, j}) |
|
||||||
} |
|
||||||
st.SetNonce(addr, 100) |
|
||||||
st.AddBalance(addr, big.NewInt(int64(i))) |
|
||||||
st.SetCode(addr, []byte{i, i, i}) |
|
||||||
} |
|
||||||
root, _ := st.Commit(false) |
|
||||||
return root, sdb |
|
||||||
} |
|
||||||
|
|
||||||
func TestLightStateOdr(t *testing.T) { |
|
||||||
root, sdb := makeTestState() |
|
||||||
header := &types.Header{Root: root, Number: big.NewInt(0)} |
|
||||||
core.WriteHeader(sdb, header) |
|
||||||
ldb, _ := ethdb.NewMemDatabase() |
|
||||||
odr := &testOdr{sdb: sdb, ldb: ldb} |
|
||||||
ls := NewLightState(StateTrieID(header), odr) |
|
||||||
ctx := context.Background() |
|
||||||
|
|
||||||
for i := byte(0); i < 100; i++ { |
|
||||||
addr := common.Address{i} |
|
||||||
err := ls.AddBalance(ctx, addr, big.NewInt(1000)) |
|
||||||
if err != nil { |
|
||||||
t.Fatalf("Error adding balance to acc[%d]: %v", i, err) |
|
||||||
} |
|
||||||
err = ls.SetState(ctx, addr, common.Hash{100}, common.Hash{i, 100}) |
|
||||||
if err != nil { |
|
||||||
t.Fatalf("Error setting storage of acc[%d]: %v", i, err) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
addr := common.Address{100} |
|
||||||
_, err := ls.CreateStateObject(ctx, addr) |
|
||||||
if err != nil { |
|
||||||
t.Fatalf("Error creating state object: %v", err) |
|
||||||
} |
|
||||||
err = ls.SetCode(ctx, addr, []byte{100, 100, 100}) |
|
||||||
if err != nil { |
|
||||||
t.Fatalf("Error setting code: %v", err) |
|
||||||
} |
|
||||||
err = ls.AddBalance(ctx, addr, big.NewInt(1100)) |
|
||||||
if err != nil { |
|
||||||
t.Fatalf("Error adding balance to acc[100]: %v", err) |
|
||||||
} |
|
||||||
for j := byte(0); j < 101; j++ { |
|
||||||
err = ls.SetState(ctx, addr, common.Hash{j}, common.Hash{100, j}) |
|
||||||
if err != nil { |
|
||||||
t.Fatalf("Error setting storage of acc[100]: %v", err) |
|
||||||
} |
|
||||||
} |
|
||||||
err = ls.SetNonce(ctx, addr, 100) |
|
||||||
if err != nil { |
|
||||||
t.Fatalf("Error setting nonce for acc[100]: %v", err) |
|
||||||
} |
|
||||||
|
|
||||||
for i := byte(0); i < 101; i++ { |
|
||||||
addr := common.Address{i} |
|
||||||
|
|
||||||
bal, err := ls.GetBalance(ctx, addr) |
|
||||||
if err != nil { |
|
||||||
t.Fatalf("Error getting balance of acc[%d]: %v", i, err) |
|
||||||
} |
|
||||||
if bal.Int64() != int64(i)+1000 { |
|
||||||
t.Fatalf("Incorrect balance at acc[%d]: expected %v, got %v", i, int64(i)+1000, bal.Int64()) |
|
||||||
} |
|
||||||
|
|
||||||
nonce, err := ls.GetNonce(ctx, addr) |
|
||||||
if err != nil { |
|
||||||
t.Fatalf("Error getting nonce of acc[%d]: %v", i, err) |
|
||||||
} |
|
||||||
if nonce != 100 { |
|
||||||
t.Fatalf("Incorrect nonce at acc[%d]: expected %v, got %v", i, 100, nonce) |
|
||||||
} |
|
||||||
|
|
||||||
code, err := ls.GetCode(ctx, addr) |
|
||||||
exp := []byte{i, i, i} |
|
||||||
if err != nil { |
|
||||||
t.Fatalf("Error getting code of acc[%d]: %v", i, err) |
|
||||||
} |
|
||||||
if !bytes.Equal(code, exp) { |
|
||||||
t.Fatalf("Incorrect code at acc[%d]: expected %v, got %v", i, exp, code) |
|
||||||
} |
|
||||||
|
|
||||||
for j := byte(0); j < 101; j++ { |
|
||||||
exp := common.Hash{i, j} |
|
||||||
val, err := ls.GetState(ctx, addr, common.Hash{j}) |
|
||||||
if err != nil { |
|
||||||
t.Fatalf("Error retrieving acc[%d].storage[%d]: %v", i, j, err) |
|
||||||
} |
|
||||||
if val != exp { |
|
||||||
t.Fatalf("Retrieved wrong value from acc[%d].storage[%d]: expected %04x, got %04x", i, j, exp, val) |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
func TestLightStateSetCopy(t *testing.T) { |
|
||||||
root, sdb := makeTestState() |
|
||||||
header := &types.Header{Root: root, Number: big.NewInt(0)} |
|
||||||
core.WriteHeader(sdb, header) |
|
||||||
ldb, _ := ethdb.NewMemDatabase() |
|
||||||
odr := &testOdr{sdb: sdb, ldb: ldb} |
|
||||||
ls := NewLightState(StateTrieID(header), odr) |
|
||||||
ctx := context.Background() |
|
||||||
|
|
||||||
for i := byte(0); i < 100; i++ { |
|
||||||
addr := common.Address{i} |
|
||||||
err := ls.AddBalance(ctx, addr, big.NewInt(1000)) |
|
||||||
if err != nil { |
|
||||||
t.Fatalf("Error adding balance to acc[%d]: %v", i, err) |
|
||||||
} |
|
||||||
err = ls.SetState(ctx, addr, common.Hash{100}, common.Hash{i, 100}) |
|
||||||
if err != nil { |
|
||||||
t.Fatalf("Error setting storage of acc[%d]: %v", i, err) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
ls2 := ls.Copy() |
|
||||||
|
|
||||||
for i := byte(0); i < 100; i++ { |
|
||||||
addr := common.Address{i} |
|
||||||
err := ls2.AddBalance(ctx, addr, big.NewInt(1000)) |
|
||||||
if err != nil { |
|
||||||
t.Fatalf("Error adding balance to acc[%d]: %v", i, err) |
|
||||||
} |
|
||||||
err = ls2.SetState(ctx, addr, common.Hash{100}, common.Hash{i, 200}) |
|
||||||
if err != nil { |
|
||||||
t.Fatalf("Error setting storage of acc[%d]: %v", i, err) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
lsx := ls.Copy() |
|
||||||
ls.Set(ls2) |
|
||||||
ls2.Set(lsx) |
|
||||||
|
|
||||||
for i := byte(0); i < 100; i++ { |
|
||||||
addr := common.Address{i} |
|
||||||
// check balance in ls
|
|
||||||
bal, err := ls.GetBalance(ctx, addr) |
|
||||||
if err != nil { |
|
||||||
t.Fatalf("Error getting balance to acc[%d]: %v", i, err) |
|
||||||
} |
|
||||||
if bal.Int64() != int64(i)+2000 { |
|
||||||
t.Fatalf("Incorrect balance at ls.acc[%d]: expected %v, got %v", i, int64(i)+1000, bal.Int64()) |
|
||||||
} |
|
||||||
// check balance in ls2
|
|
||||||
bal, err = ls2.GetBalance(ctx, addr) |
|
||||||
if err != nil { |
|
||||||
t.Fatalf("Error getting balance to acc[%d]: %v", i, err) |
|
||||||
} |
|
||||||
if bal.Int64() != int64(i)+1000 { |
|
||||||
t.Fatalf("Incorrect balance at ls.acc[%d]: expected %v, got %v", i, int64(i)+1000, bal.Int64()) |
|
||||||
} |
|
||||||
// check storage in ls
|
|
||||||
exp := common.Hash{i, 200} |
|
||||||
val, err := ls.GetState(ctx, addr, common.Hash{100}) |
|
||||||
if err != nil { |
|
||||||
t.Fatalf("Error retrieving acc[%d].storage[100]: %v", i, err) |
|
||||||
} |
|
||||||
if val != exp { |
|
||||||
t.Fatalf("Retrieved wrong value from acc[%d].storage[100]: expected %04x, got %04x", i, exp, val) |
|
||||||
} |
|
||||||
// check storage in ls2
|
|
||||||
exp = common.Hash{i, 100} |
|
||||||
val, err = ls2.GetState(ctx, addr, common.Hash{100}) |
|
||||||
if err != nil { |
|
||||||
t.Fatalf("Error retrieving acc[%d].storage[100]: %v", i, err) |
|
||||||
} |
|
||||||
if val != exp { |
|
||||||
t.Fatalf("Retrieved wrong value from acc[%d].storage[100]: expected %04x, got %04x", i, exp, val) |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
func TestLightStateDelete(t *testing.T) { |
|
||||||
root, sdb := makeTestState() |
|
||||||
header := &types.Header{Root: root, Number: big.NewInt(0)} |
|
||||||
core.WriteHeader(sdb, header) |
|
||||||
ldb, _ := ethdb.NewMemDatabase() |
|
||||||
odr := &testOdr{sdb: sdb, ldb: ldb} |
|
||||||
ls := NewLightState(StateTrieID(header), odr) |
|
||||||
ctx := context.Background() |
|
||||||
|
|
||||||
addr := common.Address{42} |
|
||||||
|
|
||||||
b, err := ls.HasAccount(ctx, addr) |
|
||||||
if err != nil { |
|
||||||
t.Fatalf("HasAccount error: %v", err) |
|
||||||
} |
|
||||||
if !b { |
|
||||||
t.Fatalf("HasAccount returned false, expected true") |
|
||||||
} |
|
||||||
|
|
||||||
b, err = ls.HasSuicided(ctx, addr) |
|
||||||
if err != nil { |
|
||||||
t.Fatalf("HasSuicided error: %v", err) |
|
||||||
} |
|
||||||
if b { |
|
||||||
t.Fatalf("HasSuicided returned true, expected false") |
|
||||||
} |
|
||||||
|
|
||||||
ls.Suicide(ctx, addr) |
|
||||||
|
|
||||||
b, err = ls.HasSuicided(ctx, addr) |
|
||||||
if err != nil { |
|
||||||
t.Fatalf("HasSuicided error: %v", err) |
|
||||||
} |
|
||||||
if !b { |
|
||||||
t.Fatalf("HasSuicided returned false, expected true") |
|
||||||
} |
|
||||||
} |
|
@ -0,0 +1,83 @@ |
|||||||
|
// Copyright 2017 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package light |
||||||
|
|
||||||
|
import ( |
||||||
|
"bytes" |
||||||
|
"context" |
||||||
|
"fmt" |
||||||
|
"testing" |
||||||
|
|
||||||
|
"github.com/davecgh/go-spew/spew" |
||||||
|
"github.com/ethereum/go-ethereum/consensus/ethash" |
||||||
|
"github.com/ethereum/go-ethereum/core" |
||||||
|
"github.com/ethereum/go-ethereum/core/state" |
||||||
|
"github.com/ethereum/go-ethereum/core/vm" |
||||||
|
"github.com/ethereum/go-ethereum/ethdb" |
||||||
|
"github.com/ethereum/go-ethereum/event" |
||||||
|
"github.com/ethereum/go-ethereum/params" |
||||||
|
"github.com/ethereum/go-ethereum/trie" |
||||||
|
) |
||||||
|
|
||||||
|
func TestNodeIterator(t *testing.T) { |
||||||
|
var ( |
||||||
|
fulldb, _ = ethdb.NewMemDatabase() |
||||||
|
lightdb, _ = ethdb.NewMemDatabase() |
||||||
|
gspec = core.Genesis{Alloc: core.GenesisAlloc{testBankAddress: {Balance: testBankFunds}}} |
||||||
|
genesis = gspec.MustCommit(fulldb) |
||||||
|
) |
||||||
|
gspec.MustCommit(lightdb) |
||||||
|
blockchain, _ := core.NewBlockChain(fulldb, params.TestChainConfig, ethash.NewFullFaker(), new(event.TypeMux), vm.Config{}) |
||||||
|
gchain, _ := core.GenerateChain(params.TestChainConfig, genesis, fulldb, 4, testChainGen) |
||||||
|
if _, err := blockchain.InsertChain(gchain); err != nil { |
||||||
|
panic(err) |
||||||
|
} |
||||||
|
|
||||||
|
ctx := context.Background() |
||||||
|
odr := &testOdr{sdb: fulldb, ldb: lightdb} |
||||||
|
head := blockchain.CurrentHeader() |
||||||
|
lightTrie, _ := NewStateDatabase(ctx, head, odr).OpenTrie(head.Root) |
||||||
|
fullTrie, _ := state.NewDatabase(fulldb).OpenTrie(head.Root) |
||||||
|
if err := diffTries(fullTrie, lightTrie); err != nil { |
||||||
|
t.Fatal(err) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func diffTries(t1, t2 state.Trie) error { |
||||||
|
i1 := trie.NewIterator(t1.NodeIterator(nil)) |
||||||
|
i2 := trie.NewIterator(t2.NodeIterator(nil)) |
||||||
|
for i1.Next() && i2.Next() { |
||||||
|
if !bytes.Equal(i1.Key, i2.Key) { |
||||||
|
spew.Dump(i2) |
||||||
|
return fmt.Errorf("tries have different keys %x, %x", i1.Key, i2.Key) |
||||||
|
} |
||||||
|
if !bytes.Equal(i2.Value, i2.Value) { |
||||||
|
return fmt.Errorf("tries differ at key %x", i1.Key) |
||||||
|
} |
||||||
|
} |
||||||
|
switch { |
||||||
|
case i1.Err != nil: |
||||||
|
return fmt.Errorf("full trie iterator error: %v", i1.Err) |
||||||
|
case i2.Err != nil: |
||||||
|
return fmt.Errorf("light trie iterator error: %v", i1.Err) |
||||||
|
case i1.Next(): |
||||||
|
return fmt.Errorf("full trie iterator has more k/v pairs") |
||||||
|
case i2.Next(): |
||||||
|
return fmt.Errorf("light trie iterator has more k/v pairs") |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
@ -1,194 +0,0 @@ |
|||||||
// Copyright 2016 The go-ethereum Authors
|
|
||||||
// This file is part of the go-ethereum library.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package light |
|
||||||
|
|
||||||
import ( |
|
||||||
"context" |
|
||||||
"math/big" |
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common" |
|
||||||
"github.com/ethereum/go-ethereum/core/types" |
|
||||||
"github.com/ethereum/go-ethereum/crypto" |
|
||||||
) |
|
||||||
|
|
||||||
// VMState is a wrapper for the light state that holds the actual context and
|
|
||||||
// passes it to any state operation that requires it.
|
|
||||||
type VMState struct { |
|
||||||
ctx context.Context |
|
||||||
state *LightState |
|
||||||
snapshots []*LightState |
|
||||||
err error |
|
||||||
} |
|
||||||
|
|
||||||
func NewVMState(ctx context.Context, state *LightState) *VMState { |
|
||||||
return &VMState{ctx: ctx, state: state} |
|
||||||
} |
|
||||||
|
|
||||||
func (s *VMState) Error() error { |
|
||||||
return s.err |
|
||||||
} |
|
||||||
|
|
||||||
func (s *VMState) AddLog(log *types.Log) {} |
|
||||||
|
|
||||||
func (s *VMState) AddPreimage(hash common.Hash, preimage []byte) {} |
|
||||||
|
|
||||||
// errHandler handles and stores any state error that happens during execution.
|
|
||||||
func (s *VMState) errHandler(err error) { |
|
||||||
if err != nil && s.err == nil { |
|
||||||
s.err = err |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
func (self *VMState) Snapshot() int { |
|
||||||
self.snapshots = append(self.snapshots, self.state.Copy()) |
|
||||||
return len(self.snapshots) - 1 |
|
||||||
} |
|
||||||
|
|
||||||
func (self *VMState) RevertToSnapshot(idx int) { |
|
||||||
self.state.Set(self.snapshots[idx]) |
|
||||||
self.snapshots = self.snapshots[:idx] |
|
||||||
} |
|
||||||
|
|
||||||
// CreateAccount creates creates a new account object and takes ownership.
|
|
||||||
func (s *VMState) CreateAccount(addr common.Address) { |
|
||||||
_, err := s.state.CreateStateObject(s.ctx, addr) |
|
||||||
s.errHandler(err) |
|
||||||
} |
|
||||||
|
|
||||||
// AddBalance adds the given amount to the balance of the specified account
|
|
||||||
func (s *VMState) AddBalance(addr common.Address, amount *big.Int) { |
|
||||||
err := s.state.AddBalance(s.ctx, addr, amount) |
|
||||||
s.errHandler(err) |
|
||||||
} |
|
||||||
|
|
||||||
// SubBalance adds the given amount to the balance of the specified account
|
|
||||||
func (s *VMState) SubBalance(addr common.Address, amount *big.Int) { |
|
||||||
err := s.state.SubBalance(s.ctx, addr, amount) |
|
||||||
s.errHandler(err) |
|
||||||
} |
|
||||||
|
|
||||||
// ForEachStorage calls a callback function for every key/value pair found
|
|
||||||
// in the local storage cache. Note that unlike core/state.StateObject,
|
|
||||||
// light.StateObject only returns cached values and doesn't download the
|
|
||||||
// entire storage tree.
|
|
||||||
func (s *VMState) ForEachStorage(addr common.Address, cb func(key, value common.Hash) bool) { |
|
||||||
err := s.state.ForEachStorage(s.ctx, addr, cb) |
|
||||||
s.errHandler(err) |
|
||||||
} |
|
||||||
|
|
||||||
// GetBalance retrieves the balance from the given address or 0 if the account does
|
|
||||||
// not exist
|
|
||||||
func (s *VMState) GetBalance(addr common.Address) *big.Int { |
|
||||||
res, err := s.state.GetBalance(s.ctx, addr) |
|
||||||
s.errHandler(err) |
|
||||||
return res |
|
||||||
} |
|
||||||
|
|
||||||
// GetNonce returns the nonce at the given address or 0 if the account does
|
|
||||||
// not exist
|
|
||||||
func (s *VMState) GetNonce(addr common.Address) uint64 { |
|
||||||
res, err := s.state.GetNonce(s.ctx, addr) |
|
||||||
s.errHandler(err) |
|
||||||
return res |
|
||||||
} |
|
||||||
|
|
||||||
// SetNonce sets the nonce of the specified account
|
|
||||||
func (s *VMState) SetNonce(addr common.Address, nonce uint64) { |
|
||||||
err := s.state.SetNonce(s.ctx, addr, nonce) |
|
||||||
s.errHandler(err) |
|
||||||
} |
|
||||||
|
|
||||||
// GetCode returns the contract code at the given address or nil if the account
|
|
||||||
// does not exist
|
|
||||||
func (s *VMState) GetCode(addr common.Address) []byte { |
|
||||||
res, err := s.state.GetCode(s.ctx, addr) |
|
||||||
s.errHandler(err) |
|
||||||
return res |
|
||||||
} |
|
||||||
|
|
||||||
// GetCodeHash returns the contract code hash at the given address
|
|
||||||
func (s *VMState) GetCodeHash(addr common.Address) common.Hash { |
|
||||||
res, err := s.state.GetCode(s.ctx, addr) |
|
||||||
s.errHandler(err) |
|
||||||
return crypto.Keccak256Hash(res) |
|
||||||
} |
|
||||||
|
|
||||||
// GetCodeSize returns the contract code size at the given address
|
|
||||||
func (s *VMState) GetCodeSize(addr common.Address) int { |
|
||||||
res, err := s.state.GetCode(s.ctx, addr) |
|
||||||
s.errHandler(err) |
|
||||||
return len(res) |
|
||||||
} |
|
||||||
|
|
||||||
// SetCode sets the contract code at the specified account
|
|
||||||
func (s *VMState) SetCode(addr common.Address, code []byte) { |
|
||||||
err := s.state.SetCode(s.ctx, addr, code) |
|
||||||
s.errHandler(err) |
|
||||||
} |
|
||||||
|
|
||||||
// AddRefund adds an amount to the refund value collected during a vm execution
|
|
||||||
func (s *VMState) AddRefund(gas *big.Int) { |
|
||||||
s.state.AddRefund(gas) |
|
||||||
} |
|
||||||
|
|
||||||
// GetRefund returns the refund value collected during a vm execution
|
|
||||||
func (s *VMState) GetRefund() *big.Int { |
|
||||||
return s.state.GetRefund() |
|
||||||
} |
|
||||||
|
|
||||||
// GetState returns the contract storage value at storage address b from the
|
|
||||||
// contract address a or common.Hash{} if the account does not exist
|
|
||||||
func (s *VMState) GetState(a common.Address, b common.Hash) common.Hash { |
|
||||||
res, err := s.state.GetState(s.ctx, a, b) |
|
||||||
s.errHandler(err) |
|
||||||
return res |
|
||||||
} |
|
||||||
|
|
||||||
// SetState sets the storage value at storage address key of the account addr
|
|
||||||
func (s *VMState) SetState(addr common.Address, key common.Hash, value common.Hash) { |
|
||||||
err := s.state.SetState(s.ctx, addr, key, value) |
|
||||||
s.errHandler(err) |
|
||||||
} |
|
||||||
|
|
||||||
// Suicide marks an account to be removed and clears its balance
|
|
||||||
func (s *VMState) Suicide(addr common.Address) bool { |
|
||||||
res, err := s.state.Suicide(s.ctx, addr) |
|
||||||
s.errHandler(err) |
|
||||||
return res |
|
||||||
} |
|
||||||
|
|
||||||
// Exist returns true if an account exists at the given address
|
|
||||||
func (s *VMState) Exist(addr common.Address) bool { |
|
||||||
res, err := s.state.HasAccount(s.ctx, addr) |
|
||||||
s.errHandler(err) |
|
||||||
return res |
|
||||||
} |
|
||||||
|
|
||||||
// Empty returns true if the account at the given address is considered empty
|
|
||||||
func (s *VMState) Empty(addr common.Address) bool { |
|
||||||
so, err := s.state.GetStateObject(s.ctx, addr) |
|
||||||
s.errHandler(err) |
|
||||||
return so == nil || so.empty() |
|
||||||
} |
|
||||||
|
|
||||||
// HasSuicided returns true if the given account has been marked for deletion
|
|
||||||
// or false if the account does not exist
|
|
||||||
func (s *VMState) HasSuicided(addr common.Address) bool { |
|
||||||
res, err := s.state.HasSuicided(s.ctx, addr) |
|
||||||
s.errHandler(err) |
|
||||||
return res |
|
||||||
} |
|
Loading…
Reference in new issue