plugeth/light/state_object.go
Jeffrey Wilcke 3fc7c97827 core, core/vm: implemented a generic environment (#3348)
Environment is now a struct (not an interface). This
reduces a lot of tech-debt throughout the codebase where a virtual
machine environment had to be implemented in order to test or run it.

The new environment is suitable to be used en the json tests, core
consensus and light client.
2016-12-06 02:16:03 +01:00

290 lines
8.1 KiB
Go

// 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"
"fmt"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/rlp"
"golang.org/x/net/context"
)
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
if glog.V(logger.Core) {
glog.Infof("%x: #%d %v X\n", self.Address(), self.nonce, self.balance)
}
}
// 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))
if glog.V(logger.Core) {
glog.Infof("%x: #%d %v (+ %v)\n", c.Address(), c.nonce, 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))
if glog.V(logger.Core) {
glog.Infof("%x: #%d %v (- %v)\n", c.Address(), c.nonce, 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.BitLen() == 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
}