core, core/state, core/vm: removal of exported account getters

Removed exported statedb object accessors, reducing the chance for nasty
bugs to creep in. It's also ugly and unnecessary to have these methods.
This commit is contained in:
Jeffrey Wilcke 2017-01-27 11:33:47 +01:00
parent 94334c233e
commit 9f1bdae638
22 changed files with 168 additions and 200 deletions

View file

@ -29,7 +29,6 @@ import (
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/core/vm/runtime"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/logger/glog"
"gopkg.in/urfave/cli.v1"
@ -115,9 +114,13 @@ func run(ctx *cli.Context) error {
glog.SetToStderr(true)
glog.SetV(ctx.GlobalInt(VerbosityFlag.Name))
db, _ := ethdb.NewMemDatabase()
statedb, _ := state.New(common.Hash{}, db)
sender := statedb.CreateAccount(common.StringToAddress("sender"))
var (
db, _ = ethdb.NewMemDatabase()
statedb, _ = state.New(common.Hash{}, db)
address = common.StringToAddress("sender")
sender = vm.Reference(address)
)
statedb.CreateAccount(common.StringToAddress("sender"))
logger := vm.NewStructLogger(nil)
@ -166,10 +169,11 @@ func run(ctx *cli.Context) error {
},
})
} else {
receiver := statedb.CreateAccount(common.StringToAddress("receiver"))
receiver.SetCode(crypto.Keccak256Hash(code), code)
receiverAddress := common.StringToAddress("receiver")
statedb.CreateAccount(receiverAddress)
statedb.SetCode(receiverAddress, code)
ret, err = runtime.Call(receiver.Address(), common.Hex2Bytes(ctx.GlobalString(InputFlag.Name)), &runtime.Config{
ret, err = runtime.Call(receiverAddress, common.Hex2Bytes(ctx.GlobalString(InputFlag.Name)), &runtime.Config{
Origin: sender.Address(),
State: statedb,
GasLimit: common.Big(ctx.GlobalString(GasFlag.Name)).Uint64(),

View file

@ -214,7 +214,8 @@ func (self *BlockChain) loadLastState() error {
return err
}
self.stateCache = statedb
self.stateCache.GetAccount(common.Address{})
// XXX Reviewers: what's this for? I need to know if I need to backport this somehow
//self.stateCache.GetAccount(common.Address{})
// Issue a status log for the user
headerTd := self.GetTd(currentHeader.Hash(), currentHeader.Number.Uint64())

View file

@ -62,13 +62,13 @@ func ValidateDAOHeaderExtraData(config *params.ChainConfig, header *types.Header
// contract.
func ApplyDAOHardFork(statedb *state.StateDB) {
// Retrieve the contract to refund balances into
refund := statedb.GetOrNewStateObject(params.DAORefundContract)
if !statedb.Exist(params.DAORefundContract) {
statedb.CreateAccount(params.DAORefundContract)
}
// Move every DAO account and extra-balance account funds into the refund contract
for _, addr := range params.DAODrainList {
if account := statedb.GetStateObject(addr); account != nil {
refund.AddBalance(account.Balance())
account.SetBalance(new(big.Int))
}
statedb.AddBalance(params.DAORefundContract, statedb.GetBalance(addr))
statedb.SetBalance(addr, new(big.Int))
}
}

View file

@ -34,7 +34,7 @@ type (
account *common.Address
}
resetObjectChange struct {
prev *StateObject
prev *stateObject
}
suicideChange struct {
account *common.Address
@ -86,7 +86,7 @@ func (ch resetObjectChange) undo(s *StateDB) {
}
func (ch suicideChange) undo(s *StateDB) {
obj := s.GetStateObject(*ch.account)
obj := s.getStateObject(*ch.account)
if obj != nil {
obj.suicided = ch.prev
obj.setBalance(ch.prevbalance)
@ -103,19 +103,19 @@ func (ch touchChange) undo(s *StateDB) {
}
func (ch balanceChange) undo(s *StateDB) {
s.GetStateObject(*ch.account).setBalance(ch.prev)
s.getStateObject(*ch.account).setBalance(ch.prev)
}
func (ch nonceChange) undo(s *StateDB) {
s.GetStateObject(*ch.account).setNonce(ch.prev)
s.getStateObject(*ch.account).setNonce(ch.prev)
}
func (ch codeChange) undo(s *StateDB) {
s.GetStateObject(*ch.account).setCode(common.BytesToHash(ch.prevhash), ch.prevcode)
s.getStateObject(*ch.account).setCode(common.BytesToHash(ch.prevhash), ch.prevcode)
}
func (ch storageChange) undo(s *StateDB) {
s.GetStateObject(*ch.account).setState(ch.key, ch.prevalue)
s.getStateObject(*ch.account).setState(ch.key, ch.prevalue)
}
func (ch refundChange) undo(s *StateDB) {

View file

@ -23,7 +23,7 @@ import (
)
type account struct {
stateObject *StateObject
stateObject *stateObject
nstart uint64
nonces []bool
}
@ -128,7 +128,7 @@ func (ms *ManagedState) getAccount(addr common.Address) *account {
} else {
// Always make sure the state account nonce isn't actually higher
// 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() {
ms.accounts[addr] = newAccount(so)
}
@ -138,6 +138,6 @@ func (ms *ManagedState) getAccount(addr common.Address) *account {
return ms.accounts[addr]
}
func newAccount(so *StateObject) *account {
func newAccount(so *stateObject) *account {
return &account{so, so.Nonce(), nil}
}

View file

@ -30,7 +30,7 @@ func create() (*ManagedState, *account) {
statedb, _ := New(common.Hash{}, db)
ms := ManageState(statedb)
ms.StateDB.SetNonce(addr, 100)
ms.accounts[addr] = newAccount(ms.StateDB.GetStateObject(addr))
ms.accounts[addr] = newAccount(ms.StateDB.getStateObject(addr))
return ms, ms.accounts[addr]
}

View file

@ -57,13 +57,13 @@ func (self Storage) Copy() Storage {
return cpy
}
// StateObject represents an Ethereum account which is being modified.
// 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 {
address common.Address // Ethereum address of this account
data Account
db *StateDB
@ -93,7 +93,7 @@ type StateObject struct {
}
// empty returns whether the account is considered empty.
func (s *StateObject) empty() bool {
func (s *stateObject) empty() bool {
return s.data.Nonce == 0 && s.data.Balance.BitLen() == 0 && bytes.Equal(s.data.CodeHash, emptyCodeHash)
}
@ -107,29 +107,29 @@ type Account struct {
}
// newObject creates a state object.
func newObject(db *StateDB, address common.Address, data Account, onDirty func(addr common.Address)) *StateObject {
func newObject(db *StateDB, address common.Address, data Account, onDirty func(addr common.Address)) *stateObject {
if data.Balance == nil {
data.Balance = new(big.Int)
}
if data.CodeHash == nil {
data.CodeHash = emptyCodeHash
}
return &StateObject{db: db, address: address, data: data, cachedStorage: make(Storage), dirtyStorage: make(Storage), onDirty: onDirty}
return &stateObject{db: db, address: address, data: data, cachedStorage: make(Storage), dirtyStorage: make(Storage), onDirty: onDirty}
}
// EncodeRLP implements rlp.Encoder.
func (c *StateObject) EncodeRLP(w io.Writer) error {
func (c *stateObject) EncodeRLP(w io.Writer) error {
return rlp.Encode(w, c.data)
}
// setError remembers the first non-nil error it is called with.
func (self *StateObject) setError(err error) {
func (self *stateObject) setError(err error) {
if self.dbErr == nil {
self.dbErr = err
}
}
func (self *StateObject) markSuicided() {
func (self *stateObject) markSuicided() {
self.suicided = true
if self.onDirty != nil {
self.onDirty(self.Address())
@ -140,7 +140,7 @@ func (self *StateObject) markSuicided() {
}
}
func (c *StateObject) touch() {
func (c *stateObject) touch() {
c.db.journal = append(c.db.journal, touchChange{
account: &c.address,
prev: c.touched,
@ -152,7 +152,7 @@ func (c *StateObject) touch() {
c.touched = true
}
func (c *StateObject) getTrie(db trie.Database) *trie.SecureTrie {
func (c *stateObject) getTrie(db trie.Database) *trie.SecureTrie {
if c.trie == nil {
var err error
c.trie, err = trie.NewSecure(c.data.Root, db, 0)
@ -165,7 +165,7 @@ func (c *StateObject) getTrie(db trie.Database) *trie.SecureTrie {
}
// GetState returns a value in account storage.
func (self *StateObject) GetState(db trie.Database, key common.Hash) common.Hash {
func (self *stateObject) GetState(db trie.Database, key common.Hash) common.Hash {
value, exists := self.cachedStorage[key]
if exists {
return value
@ -185,7 +185,7 @@ func (self *StateObject) GetState(db trie.Database, key common.Hash) common.Hash
}
// SetState updates a value in account storage.
func (self *StateObject) SetState(db trie.Database, key, value common.Hash) {
func (self *stateObject) SetState(db trie.Database, key, value common.Hash) {
self.db.journal = append(self.db.journal, storageChange{
account: &self.address,
key: key,
@ -194,7 +194,7 @@ func (self *StateObject) SetState(db trie.Database, key, value common.Hash) {
self.setState(key, value)
}
func (self *StateObject) setState(key, value common.Hash) {
func (self *stateObject) setState(key, value common.Hash) {
self.cachedStorage[key] = value
self.dirtyStorage[key] = value
@ -205,7 +205,7 @@ func (self *StateObject) setState(key, value common.Hash) {
}
// updateTrie writes cached storage modifications into the object's storage trie.
func (self *StateObject) updateTrie(db trie.Database) {
func (self *stateObject) updateTrie(db trie.Database) {
tr := self.getTrie(db)
for key, value := range self.dirtyStorage {
delete(self.dirtyStorage, key)
@ -220,14 +220,14 @@ func (self *StateObject) updateTrie(db trie.Database) {
}
// UpdateRoot sets the trie root to the current root hash of
func (self *StateObject) updateRoot(db trie.Database) {
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 {
func (self *stateObject) CommitTrie(db trie.Database, dbw trie.DatabaseWriter) error {
self.updateTrie(db)
if self.dbErr != nil {
return self.dbErr
@ -241,7 +241,7 @@ func (self *StateObject) CommitTrie(db trie.Database, dbw trie.DatabaseWriter) e
// AddBalance removes amount from c's balance.
// It is used to add funds to the destination account of a transfer.
func (c *StateObject) AddBalance(amount *big.Int) {
func (c *stateObject) AddBalance(amount *big.Int) {
// EIP158: We must check emptiness for the objects such that the account
// clearing (0,0,0 objects) can take effect.
if amount.Cmp(common.Big0) == 0 {
@ -260,7 +260,7 @@ func (c *StateObject) AddBalance(amount *big.Int) {
// SubBalance removes amount from c's balance.
// It is used to remove funds from the origin account of a transfer.
func (c *StateObject) SubBalance(amount *big.Int) {
func (c *stateObject) SubBalance(amount *big.Int) {
if amount.Cmp(common.Big0) == 0 {
return
}
@ -271,7 +271,7 @@ func (c *StateObject) SubBalance(amount *big.Int) {
}
}
func (self *StateObject) SetBalance(amount *big.Int) {
func (self *stateObject) SetBalance(amount *big.Int) {
self.db.journal = append(self.db.journal, balanceChange{
account: &self.address,
prev: new(big.Int).Set(self.data.Balance),
@ -279,7 +279,7 @@ func (self *StateObject) SetBalance(amount *big.Int) {
self.setBalance(amount)
}
func (self *StateObject) setBalance(amount *big.Int) {
func (self *stateObject) setBalance(amount *big.Int) {
self.data.Balance = amount
if self.onDirty != nil {
self.onDirty(self.Address())
@ -288,9 +288,9 @@ func (self *StateObject) setBalance(amount *big.Int) {
}
// Return the gas back to the origin. Used by the Virtual machine or Closures
func (c *StateObject) ReturnGas(gas *big.Int) {}
func (c *stateObject) ReturnGas(gas *big.Int) {}
func (self *StateObject) deepCopy(db *StateDB, onDirty func(addr common.Address)) *StateObject {
func (self *stateObject) deepCopy(db *StateDB, onDirty func(addr common.Address)) *stateObject {
stateObject := newObject(db, self.address, self.data, onDirty)
stateObject.trie = self.trie
stateObject.code = self.code
@ -307,12 +307,12 @@ func (self *StateObject) deepCopy(db *StateDB, onDirty func(addr common.Address)
//
// Returns the address of the contract/account
func (c *StateObject) Address() common.Address {
func (c *stateObject) Address() common.Address {
return c.address
}
// Code returns the contract code associated with this object, if any.
func (self *StateObject) Code(db trie.Database) []byte {
func (self *stateObject) Code(db trie.Database) []byte {
if self.code != nil {
return self.code
}
@ -327,7 +327,7 @@ func (self *StateObject) Code(db trie.Database) []byte {
return code
}
func (self *StateObject) SetCode(codeHash common.Hash, code []byte) {
func (self *stateObject) SetCode(codeHash common.Hash, code []byte) {
prevcode := self.Code(self.db.db)
self.db.journal = append(self.db.journal, codeChange{
account: &self.address,
@ -337,7 +337,7 @@ func (self *StateObject) SetCode(codeHash common.Hash, code []byte) {
self.setCode(codeHash, code)
}
func (self *StateObject) setCode(codeHash common.Hash, code []byte) {
func (self *stateObject) setCode(codeHash common.Hash, code []byte) {
self.code = code
self.data.CodeHash = codeHash[:]
self.dirtyCode = true
@ -347,7 +347,7 @@ func (self *StateObject) setCode(codeHash common.Hash, code []byte) {
}
}
func (self *StateObject) SetNonce(nonce uint64) {
func (self *stateObject) SetNonce(nonce uint64) {
self.db.journal = append(self.db.journal, nonceChange{
account: &self.address,
prev: self.data.Nonce,
@ -355,7 +355,7 @@ func (self *StateObject) SetNonce(nonce uint64) {
self.setNonce(nonce)
}
func (self *StateObject) setNonce(nonce uint64) {
func (self *stateObject) setNonce(nonce uint64) {
self.data.Nonce = nonce
if self.onDirty != nil {
self.onDirty(self.Address())
@ -363,26 +363,26 @@ func (self *StateObject) setNonce(nonce uint64) {
}
}
func (self *StateObject) CodeHash() []byte {
func (self *stateObject) CodeHash() []byte {
return self.data.CodeHash
}
func (self *StateObject) Balance() *big.Int {
func (self *stateObject) Balance() *big.Int {
return self.data.Balance
}
func (self *StateObject) Nonce() uint64 {
func (self *stateObject) Nonce() uint64 {
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
// 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")
func (self *stateObject) Value() *big.Int {
panic("Value on stateObject should never be called")
}
func (self *StateObject) ForEachStorage(cb func(key, value common.Hash) bool) {
func (self *stateObject) ForEachStorage(cb func(key, value common.Hash) bool) {
// When iterating over the storage check the cache first
for h, value := range self.cachedStorage {
cb(h, value)

View file

@ -152,7 +152,7 @@ func TestSnapshot2(t *testing.T) {
state.SetState(stateobjaddr1, storageaddr, data1)
// db, trie are already non-empty values
so0 := state.GetStateObject(stateobjaddr0)
so0 := state.getStateObject(stateobjaddr0)
so0.SetBalance(big.NewInt(42))
so0.SetNonce(43)
so0.SetCode(crypto.Keccak256Hash([]byte{'c', 'a', 'f', 'e'}), []byte{'c', 'a', 'f', 'e'})
@ -164,7 +164,7 @@ func TestSnapshot2(t *testing.T) {
state.Reset(root)
// and one with deleted == true
so1 := state.GetStateObject(stateobjaddr1)
so1 := state.getStateObject(stateobjaddr1)
so1.SetBalance(big.NewInt(52))
so1.SetNonce(53)
so1.SetCode(crypto.Keccak256Hash([]byte{'c', 'a', 'f', 'e', '2'}), []byte{'c', 'a', 'f', 'e', '2'})
@ -172,7 +172,7 @@ func TestSnapshot2(t *testing.T) {
so1.deleted = true
state.setStateObject(so1)
so1 = state.GetStateObject(stateobjaddr1)
so1 = state.getStateObject(stateobjaddr1)
if so1 != nil {
t.Fatalf("deleted object not nil when getting")
}
@ -180,7 +180,7 @@ func TestSnapshot2(t *testing.T) {
snapshot := state.Snapshot()
state.RevertToSnapshot(snapshot)
so0Restored := state.GetStateObject(stateobjaddr0)
so0Restored := state.getStateObject(stateobjaddr0)
// Update lazily-loaded values before comparing.
so0Restored.GetState(db, storageaddr)
so0Restored.Code(db)
@ -188,13 +188,13 @@ func TestSnapshot2(t *testing.T) {
compareStateObjects(so0Restored, so0, t)
// deleted should be nil, both before and after restore of state copy
so1Restored := state.GetStateObject(stateobjaddr1)
so1Restored := state.getStateObject(stateobjaddr1)
if so1Restored != nil {
t.Fatalf("deleted object not nil after restoring snapshot: %+v", so1Restored)
}
}
func compareStateObjects(so0, so1 *StateObject, t *testing.T) {
func compareStateObjects(so0, so1 *stateObject, t *testing.T) {
if so0.Address() != so1.Address() {
t.Fatalf("Address mismatch: have %v, want %v", so0.address, so1.address)
}

View file

@ -25,7 +25,6 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/logger"
@ -64,7 +63,7 @@ type StateDB struct {
codeSizeCache *lru.Cache
// This map holds 'live' objects, which will get modified while processing a state transition.
stateObjects map[common.Address]*StateObject
stateObjects map[common.Address]*stateObject
stateObjectsDirty map[common.Address]struct{}
// The refund counter, also used by state transitioning.
@ -97,7 +96,7 @@ func New(root common.Hash, db ethdb.Database) (*StateDB, error) {
db: db,
trie: tr,
codeSizeCache: csc,
stateObjects: make(map[common.Address]*StateObject),
stateObjects: make(map[common.Address]*stateObject),
stateObjectsDirty: make(map[common.Address]struct{}),
refund: new(big.Int),
logs: make(map[common.Hash][]*types.Log),
@ -119,7 +118,7 @@ func (self *StateDB) New(root common.Hash) (*StateDB, error) {
db: self.db,
trie: tr,
codeSizeCache: self.codeSizeCache,
stateObjects: make(map[common.Address]*StateObject),
stateObjects: make(map[common.Address]*stateObject),
stateObjectsDirty: make(map[common.Address]struct{}),
refund: new(big.Int),
logs: make(map[common.Hash][]*types.Log),
@ -138,7 +137,7 @@ func (self *StateDB) Reset(root common.Hash) error {
return err
}
self.trie = tr
self.stateObjects = make(map[common.Address]*StateObject)
self.stateObjects = make(map[common.Address]*stateObject)
self.stateObjectsDirty = make(map[common.Address]struct{})
self.thash = common.Hash{}
self.bhash = common.Hash{}
@ -227,23 +226,19 @@ func (self *StateDB) AddRefund(gas *big.Int) {
// Exist reports whether the given account address exists in the state.
// Notably this also returns true for suicided accounts.
func (self *StateDB) Exist(addr common.Address) bool {
return self.GetStateObject(addr) != nil
return self.getStateObject(addr) != nil
}
// Empty returns whether the state object is either non-existent
// or empty according to the EIP161 specification (balance = nonce = code = 0)
func (self *StateDB) Empty(addr common.Address) bool {
so := self.GetStateObject(addr)
so := self.getStateObject(addr)
return so == nil || so.empty()
}
func (self *StateDB) GetAccount(addr common.Address) vm.Account {
return self.GetStateObject(addr)
}
// Retrieve the balance from the given address or 0 if object not found
func (self *StateDB) GetBalance(addr common.Address) *big.Int {
stateObject := self.GetStateObject(addr)
stateObject := self.getStateObject(addr)
if stateObject != nil {
return stateObject.Balance()
}
@ -251,7 +246,7 @@ func (self *StateDB) GetBalance(addr common.Address) *big.Int {
}
func (self *StateDB) GetNonce(addr common.Address) uint64 {
stateObject := self.GetStateObject(addr)
stateObject := self.getStateObject(addr)
if stateObject != nil {
return stateObject.Nonce()
}
@ -260,7 +255,7 @@ func (self *StateDB) GetNonce(addr common.Address) uint64 {
}
func (self *StateDB) GetCode(addr common.Address) []byte {
stateObject := self.GetStateObject(addr)
stateObject := self.getStateObject(addr)
if stateObject != nil {
code := stateObject.Code(self.db)
key := common.BytesToHash(stateObject.CodeHash())
@ -271,7 +266,7 @@ func (self *StateDB) GetCode(addr common.Address) []byte {
}
func (self *StateDB) GetCodeSize(addr common.Address) int {
stateObject := self.GetStateObject(addr)
stateObject := self.getStateObject(addr)
if stateObject == nil {
return 0
}
@ -287,7 +282,7 @@ func (self *StateDB) GetCodeSize(addr common.Address) int {
}
func (self *StateDB) GetCodeHash(addr common.Address) common.Hash {
stateObject := self.GetStateObject(addr)
stateObject := self.getStateObject(addr)
if stateObject == nil {
return common.Hash{}
}
@ -295,7 +290,7 @@ func (self *StateDB) GetCodeHash(addr common.Address) 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 {
return stateObject.GetState(self.db, b)
}
@ -303,7 +298,7 @@ func (self *StateDB) GetState(a common.Address, b common.Hash) common.Hash {
}
func (self *StateDB) HasSuicided(addr common.Address) bool {
stateObject := self.GetStateObject(addr)
stateObject := self.getStateObject(addr)
if stateObject != nil {
return stateObject.suicided
}
@ -362,9 +357,9 @@ func (self *StateDB) SetState(addr common.Address, key common.Hash, value common
// This clears the account balance.
//
// The account's state object is still available until the state is committed,
// GetStateObject will return a non-nil account after Suicide.
// getStateObject will return a non-nil account after Suicide.
func (self *StateDB) Suicide(addr common.Address) bool {
stateObject := self.GetStateObject(addr)
stateObject := self.getStateObject(addr)
if stateObject == nil {
return false
}
@ -383,7 +378,7 @@ func (self *StateDB) Suicide(addr common.Address) bool {
//
// updateStateObject writes the given object to the trie.
func (self *StateDB) updateStateObject(stateObject *StateObject) {
func (self *StateDB) updateStateObject(stateObject *stateObject) {
addr := stateObject.Address()
data, err := rlp.EncodeToBytes(stateObject)
if err != nil {
@ -393,14 +388,14 @@ func (self *StateDB) updateStateObject(stateObject *StateObject) {
}
// deleteStateObject removes the given object from the state trie.
func (self *StateDB) deleteStateObject(stateObject *StateObject) {
func (self *StateDB) deleteStateObject(stateObject *stateObject) {
stateObject.deleted = true
addr := stateObject.Address()
self.trie.Delete(addr[:])
}
// 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) {
// Prefer 'live' objects.
if obj := self.stateObjects[addr]; obj != nil {
if obj.deleted {
@ -425,13 +420,13 @@ func (self *StateDB) GetStateObject(addr common.Address) (stateObject *StateObje
return obj
}
func (self *StateDB) setStateObject(object *StateObject) {
func (self *StateDB) setStateObject(object *stateObject) {
self.stateObjects[object.Address()] = object
}
// Retrieve a state object or create a new state object if nil
func (self *StateDB) GetOrNewStateObject(addr common.Address) *StateObject {
stateObject := self.GetStateObject(addr)
func (self *StateDB) GetOrNewStateObject(addr common.Address) *stateObject {
stateObject := self.getStateObject(addr)
if stateObject == nil || stateObject.deleted {
stateObject, _ = self.createObject(addr)
}
@ -446,8 +441,8 @@ func (self *StateDB) MarkStateObjectDirty(addr common.Address) {
// createObject creates a new state object. If there is an existing account with
// the given address, it is overwritten and returned as the second return value.
func (self *StateDB) createObject(addr common.Address) (newobj, prev *StateObject) {
prev = self.GetStateObject(addr)
func (self *StateDB) createObject(addr common.Address) (newobj, prev *stateObject) {
prev = self.getStateObject(addr)
newobj = newObject(self, addr, Account{}, self.MarkStateObjectDirty)
newobj.setNonce(0) // sets the object to dirty
if prev == nil {
@ -472,12 +467,11 @@ func (self *StateDB) createObject(addr common.Address) (newobj, prev *StateObjec
// 2. tx_create(sha(account ++ nonce)) (note that this gets the address of 1)
//
// Carrying over the balance ensures that Ether doesn't disappear.
func (self *StateDB) CreateAccount(addr common.Address) vm.Account {
func (self *StateDB) CreateAccount(addr common.Address) {
new, prev := self.createObject(addr)
if prev != nil {
new.setBalance(prev.data.Balance)
}
return new
}
// Copy creates a deep, independent copy of the state.
@ -492,7 +486,7 @@ func (self *StateDB) Copy() *StateDB {
trie: self.trie,
pastTries: self.pastTries,
codeSizeCache: self.codeSizeCache,
stateObjects: make(map[common.Address]*StateObject, len(self.stateObjectsDirty)),
stateObjects: make(map[common.Address]*stateObject, len(self.stateObjectsDirty)),
stateObjectsDirty: make(map[common.Address]struct{}, len(self.stateObjectsDirty)),
refund: new(big.Int).Set(self.refund),
logs: make(map[common.Hash][]*types.Log, len(self.logs)),

View file

@ -331,11 +331,11 @@ func (test *snapshotTest) checkEqual(state, checkstate *StateDB) error {
checkeq("GetCodeHash", state.GetCodeHash(addr), checkstate.GetCodeHash(addr))
checkeq("GetCodeSize", state.GetCodeSize(addr), checkstate.GetCodeSize(addr))
// Check storage.
if obj := state.GetStateObject(addr); obj != nil {
if obj := state.getStateObject(addr); obj != nil {
obj.ForEachStorage(func(key, val common.Hash) bool {
return checkeq("GetState("+key.Hex()+")", val, checkstate.GetState(addr, key))
})
checkobj := checkstate.GetStateObject(addr)
checkobj := checkstate.getStateObject(addr)
checkobj.ForEachStorage(func(key, checkval common.Hash) bool {
return checkeq("GetState("+key.Hex()+")", state.GetState(addr, key), checkval)
})

View file

@ -17,7 +17,7 @@
package core
import (
"fmt"
"errors"
"math/big"
"github.com/ethereum/go-ethereum/common"
@ -29,6 +29,7 @@ import (
var (
Big0 = big.NewInt(0)
errInsufficientBalanceForGas = errors.New("insufficient balance to pay for gas")
)
/*
@ -136,27 +137,28 @@ func ApplyMessage(evm *vm.EVM, msg Message, gp *GasPool) ([]byte, *big.Int, erro
return ret, gasUsed, err
}
func (self *StateTransition) from() vm.Account {
func (self *StateTransition) from() vm.AccountRef {
f := self.msg.From()
if !self.state.Exist(f) {
return self.state.CreateAccount(f)
self.state.CreateAccount(f)
}
return self.state.GetAccount(f)
return vm.Reference(f)
}
func (self *StateTransition) to() vm.Account {
func (self *StateTransition) to() vm.AccountRef {
if self.msg == nil {
return nil
return vm.AccountRef{}
}
to := self.msg.To()
if to == nil {
return nil // contract creation
return vm.AccountRef{} // contract creation
}
reference := vm.Reference(*to)
if !self.state.Exist(*to) {
return self.state.CreateAccount(*to)
self.state.CreateAccount(*to)
}
return self.state.GetAccount(*to)
return reference
}
func (self *StateTransition) useGas(amount uint64) error {
@ -176,9 +178,12 @@ func (self *StateTransition) buyGas() error {
mgval := new(big.Int).Mul(mgas, self.gasPrice)
sender := self.from()
if sender.Balance().Cmp(mgval) < 0 {
return fmt.Errorf("insufficient ETH for gas (%x). Req %v, has %v", sender.Address().Bytes()[:4], mgval, sender.Balance())
var (
state = self.state
sender = self.from()
)
if state.GetBalance(sender.Address()).Cmp(mgval) < 0 {
return errInsufficientBalanceForGas
}
if err := self.gp.SubGas(mgas); err != nil {
return err
@ -186,7 +191,7 @@ func (self *StateTransition) buyGas() error {
self.gas += mgas.Uint64()
self.initialGas.Set(mgas)
sender.SubBalance(mgval)
state.SubBalance(sender.Address(), mgval)
return nil
}
@ -272,7 +277,7 @@ func (self *StateTransition) refundGas() {
// exchanged at the original rate.
sender := self.from() // err already checked
remaining := new(big.Int).Mul(new(big.Int).SetUint64(self.gas), self.gasPrice)
sender.AddBalance(remaining)
self.state.AddBalance(sender.Address(), remaining)
// Apply refund counter, capped to half of the used gas.
uhalf := remaining.Div(self.gasUsed(), common.Big2)

View file

@ -25,11 +25,13 @@ import (
// ContractRef is a reference to the contract's backing object
type ContractRef interface {
Address() common.Address
Value() *big.Int
SetCode(common.Hash, []byte)
ForEachStorage(callback func(key, value common.Hash) bool)
}
type AccountRef common.Address
func (ar AccountRef) Address() common.Address { return (common.Address)(ar) }
func Reference(addr common.Address) AccountRef { return (AccountRef)(addr) }
// Contract represents an ethereum contract in the state database. It contains
// the the contract code, calling arguments. Contract implements ContractRef
type Contract struct {
@ -69,7 +71,8 @@ func NewContract(caller ContractRef, object ContractRef, value *big.Int, gas uin
// Gas should be a pointer so it can safely be reduced through the run
// This pointer will be off the state transition
c.Gas = gas
c.value = new(big.Int).Set(value)
// ensures a value is set
c.value = value
return c
}
@ -80,7 +83,10 @@ func (c *Contract) AsDelegate() *Contract {
c.DelegateCall = true
// NOTE: caller must, at all times be a contract. It should never happen
// that caller is something other than a Contract.
c.CallerAddress = c.caller.(*Contract).CallerAddress
parent := c.caller.(*Contract)
c.CallerAddress = parent.CallerAddress
c.value = parent.value
return c
}
@ -138,9 +144,3 @@ func (self *Contract) SetCallCode(addr *common.Address, hash common.Hash, code [
self.CodeHash = hash
self.CodeAddr = addr
}
// EachStorage iterates the contract's storage and calls a method for every key
// value pair.
func (self *Contract) ForEachStorage(cb func(key, value common.Hash) bool) {
self.caller.ForEachStorage(cb)
}

View file

@ -116,7 +116,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
}
var (
to Account
to = Reference(addr)
snapshot = evm.StateDB.Snapshot()
)
if !evm.StateDB.Exist(addr) {
@ -124,9 +124,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
return nil, gas, nil
}
to = evm.StateDB.CreateAccount(addr)
} else {
to = evm.StateDB.GetAccount(addr)
evm.StateDB.CreateAccount(addr)
}
evm.Transfer(evm.StateDB, caller.Address(), to.Address(), value)
@ -169,7 +167,7 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte,
var (
snapshot = evm.StateDB.Snapshot()
to = evm.StateDB.GetAccount(caller.Address())
to = Reference(caller.Address())
)
// initialise a new contract and set the code that is to be used by the
// E The contract is a scoped evmironment for this execution context
@ -205,11 +203,11 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by
var (
snapshot = evm.StateDB.Snapshot()
to = evm.StateDB.GetAccount(caller.Address())
to = Reference(caller.Address())
)
// Iinitialise a new contract and make initialise the delegate values
contract := NewContract(caller, to, caller.Value(), gas).AsDelegate()
contract := NewContract(caller, to, nil, gas).AsDelegate()
contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr))
ret, err = evm.interpreter.Run(contract, input)
@ -243,16 +241,16 @@ func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.I
snapshot := evm.StateDB.Snapshot()
contractAddr = crypto.CreateAddress(caller.Address(), nonce)
to := evm.StateDB.CreateAccount(contractAddr)
evm.StateDB.CreateAccount(contractAddr)
if evm.ChainConfig().IsEIP158(evm.BlockNumber) {
evm.StateDB.SetNonce(contractAddr, 1)
}
evm.Transfer(evm.StateDB, caller.Address(), to.Address(), value)
evm.Transfer(evm.StateDB, caller.Address(), contractAddr, value)
// initialise a new contract and set the code that is to be used by the
// E The contract is a scoped evmironment for this execution context
// only.
contract := NewContract(caller, to, value, gas)
contract := NewContract(caller, Reference(contractAddr), value, gas)
contract.SetCallCode(&contractAddr, crypto.Keccak256Hash(code), code)
ret, err = evm.interpreter.Run(contract, nil)

View file

@ -25,8 +25,7 @@ import (
// StateDB is an EVM database for full state querying.
type StateDB interface {
GetAccount(common.Address) Account
CreateAccount(common.Address) Account
CreateAccount(common.Address)
SubBalance(common.Address, *big.Int)
AddBalance(common.Address, *big.Int)
@ -63,20 +62,6 @@ type StateDB interface {
AddPreimage(common.Hash, []byte)
}
// Account represents a contract or basic ethereum account.
type Account interface {
SubBalance(amount *big.Int)
AddBalance(amount *big.Int)
SetBalance(*big.Int)
SetNonce(uint64)
Balance() *big.Int
Address() common.Address
ReturnGas(*big.Int)
SetCode(common.Hash, []byte)
ForEachStorage(cb func(key, value common.Hash) bool)
Value() *big.Int
}
// CallContext provides a basic interface for the EVM calling conventions. The EVM EVM
// depends on this context being implemented for doing subcalls and initialising new EVM contracts.
type CallContext interface {

View file

@ -144,11 +144,14 @@ func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost *b
storage = make(Storage)
// Get the contract account and loop over each storage entry. This may involve looping over
// the trie and is a very expensive process.
/* TODO
env.StateDB.GetAccount(contract.Address()).ForEachStorage(func(key, value common.Hash) bool {
storage[key] = value
// Return true, indicating we'd like to continue.
return true
})
*/
} else {
// copy a snapshot of the current storage to a new container.
storage = l.changedValues[contract.Address()].Copy()

View file

@ -46,10 +46,6 @@ type dummyStateDB struct {
ref *dummyContractRef
}
func (d dummyStateDB) GetAccount(common.Address) Account {
return d.ref
}
func TestStoreCapture(t *testing.T) {
var (
env = NewEVM(Context{}, nil, params.TestChainConfig, Config{EnableJit: false, ForceJit: false})

View file

@ -45,8 +45,7 @@ func (NoopEVMCallContext) DelegateCall(me ContractRef, addr common.Address, data
type NoopStateDB struct{}
func (NoopStateDB) GetAccount(common.Address) Account { return nil }
func (NoopStateDB) CreateAccount(common.Address) Account { return nil }
func (NoopStateDB) CreateAccount(common.Address) {}
func (NoopStateDB) SubBalance(common.Address, *big.Int) {}
func (NoopStateDB) AddBalance(common.Address, *big.Int) {}
func (NoopStateDB) GetBalance(common.Address) *big.Int { return nil }

View file

@ -105,17 +105,17 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) {
cfg.State, _ = state.New(common.Hash{}, db)
}
var (
address = common.StringToAddress("contract")
vmenv = NewEnv(cfg, cfg.State)
sender = cfg.State.CreateAccount(cfg.Origin)
receiver = cfg.State.CreateAccount(common.StringToAddress("contract"))
sender = vm.Reference(cfg.Origin)
)
cfg.State.CreateAccount(address)
// set the receiver's (the executing contract) code for execution.
receiver.SetCode(crypto.Keccak256Hash(code), code)
cfg.State.SetCode(address, code)
// Call the code with the given configuration.
ret, _, err := vmenv.Call(
sender,
receiver.Address(),
common.StringToAddress("contract"),
input,
cfg.GasLimit,
cfg.Value,
@ -137,7 +137,7 @@ func Create(input []byte, cfg *Config) ([]byte, common.Address, error) {
}
var (
vmenv = NewEnv(cfg, cfg.State)
sender = cfg.State.CreateAccount(cfg.Origin)
sender = vm.Reference(cfg.Origin)
)
// Call the code with the given configuration.

View file

@ -21,7 +21,6 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
"golang.org/x/net/context"
)
@ -64,27 +63,10 @@ func (self *VMState) RevertToSnapshot(idx int) {
self.snapshots = self.snapshots[:idx]
}
// GetAccount returns the account object of the given account or nil if the
// account does not exist
func (s *VMState) GetAccount(addr common.Address) vm.Account {
so, err := s.state.GetStateObject(s.ctx, addr)
s.errHandler(err)
if err != nil {
// return a dummy state object to avoid panics
so = s.state.newStateObject(addr)
}
return so
}
// CreateAccount creates creates a new account object and takes ownership.
func (s *VMState) CreateAccount(addr common.Address) vm.Account {
so, err := s.state.CreateStateObject(s.ctx, addr)
func (s *VMState) CreateAccount(addr common.Address) {
_, err := s.state.CreateStateObject(s.ctx, addr)
s.errHandler(err)
if err != nil {
// return a dummy state object to avoid panics
so = s.state.newStateObject(addr)
}
return so
}
// AddBalance adds the given amount to the balance of the specified account

View file

@ -32,7 +32,6 @@ import (
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/logger/glog"
@ -222,10 +221,12 @@ func (t *BlockTest) InsertPreState(db ethdb.Database) (*state.StateDB, error) {
if err != nil {
return nil, err
}
obj := statedb.CreateAccount(common.HexToAddress(addrString))
obj.SetCode(crypto.Keccak256Hash(code), code)
obj.SetBalance(balance)
obj.SetNonce(nonce)
addr := common.HexToAddress(addrString)
statedb.CreateAccount(addr)
statedb.SetCode(addr, code)
statedb.SetBalance(addr, balance)
statedb.SetNonce(addr, nonce)
for k, v := range acct.Storage {
statedb.SetState(common.HexToAddress(addrString), common.HexToHash(k), common.HexToHash(v))
}

View file

@ -165,25 +165,25 @@ func runStateTest(chainConfig *params.ChainConfig, test VmTest) error {
// check post state
for addr, account := range test.Post {
obj := statedb.GetStateObject(common.HexToAddress(addr))
if obj == nil {
address := common.HexToAddress(addr)
if !statedb.Exist(address) {
return fmt.Errorf("did not find expected post-state account: %s", addr)
}
if obj.Balance().Cmp(common.Big(account.Balance)) != 0 {
return fmt.Errorf("(%x) balance failed. Expected: %v have: %v\n", obj.Address().Bytes()[:4], common.String2Big(account.Balance), obj.Balance())
if balance := statedb.GetBalance(address); balance.Cmp(common.Big(account.Balance)) != 0 {
return fmt.Errorf("(%x) balance failed. Expected: %v have: %v\n", address[:4], common.String2Big(account.Balance), balance)
}
if obj.Nonce() != common.String2Big(account.Nonce).Uint64() {
return fmt.Errorf("(%x) nonce failed. Expected: %v have: %v\n", obj.Address().Bytes()[:4], account.Nonce, obj.Nonce())
if nonce := statedb.GetNonce(address); nonce != common.String2Big(account.Nonce).Uint64() {
return fmt.Errorf("(%x) nonce failed. Expected: %v have: %v\n", address[:4], account.Nonce, nonce)
}
for addr, value := range account.Storage {
v := statedb.GetState(obj.Address(), common.HexToHash(addr))
v := statedb.GetState(address, common.HexToHash(addr))
vexp := common.HexToHash(value)
if v != vexp {
return fmt.Errorf("storage failed:\n%x: %s:\nexpected: %x\nhave: %x\n(%v %v)\n", obj.Address().Bytes(), addr, vexp, v, vexp.Big(), v.Big())
return fmt.Errorf("storage failed:\n%x: %s:\nexpected: %x\nhave: %x\n(%v %v)\n", address[:4], addr, vexp, v, vexp.Big(), v.Big())
}
}
}

View file

@ -187,16 +187,16 @@ func runVmTest(test VmTest) error {
}
// check post state
for addr, account := range test.Post {
obj := statedb.GetStateObject(common.HexToAddress(addr))
if obj == nil {
for address, account := range test.Post {
accountAddr := common.HexToAddress(address)
if !statedb.Exist(accountAddr) {
continue
}
for addr, value := range account.Storage {
v := statedb.GetState(obj.Address(), common.HexToHash(addr))
v := statedb.GetState(accountAddr, common.HexToHash(addr))
vexp := common.HexToHash(value)
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", addr[:4], addr, vexp, v, vexp.Big(), v.Big())
}
}
}