diff options
author | Jeffrey Wilcke <jeffrey@ethereum.org> | 2017-02-23 06:29:59 +0800 |
---|---|---|
committer | Felix Lange <fjl@users.noreply.github.com> | 2017-02-23 06:29:59 +0800 |
commit | 024d41d0c2660d8f1dfbeb14921c7109e30493a2 (patch) | |
tree | a2b4ed630b84084c7f439d1539ed0551ec729cbd | |
parent | 46ec4357e73dd0c43951d11638d9aed94f8ffd29 (diff) | |
download | dexon-024d41d0c2660d8f1dfbeb14921c7109e30493a2.tar dexon-024d41d0c2660d8f1dfbeb14921c7109e30493a2.tar.gz dexon-024d41d0c2660d8f1dfbeb14921c7109e30493a2.tar.bz2 dexon-024d41d0c2660d8f1dfbeb14921c7109e30493a2.tar.lz dexon-024d41d0c2660d8f1dfbeb14921c7109e30493a2.tar.xz dexon-024d41d0c2660d8f1dfbeb14921c7109e30493a2.tar.zst dexon-024d41d0c2660d8f1dfbeb14921c7109e30493a2.zip |
core, core/state, core/vm: remove exported account getters (#3618)
Removed exported statedb object accessors, reducing the chance for nasty
bugs to creep in. It's also ugly and unnecessary to have these methods.
-rw-r--r-- | cmd/evm/main.go | 18 | ||||
-rw-r--r-- | core/blockchain.go | 1 | ||||
-rw-r--r-- | core/dao.go | 10 | ||||
-rw-r--r-- | core/state/journal.go | 12 | ||||
-rw-r--r-- | core/state/managed_state.go | 6 | ||||
-rw-r--r-- | core/state/managed_state_test.go | 2 | ||||
-rw-r--r-- | core/state/state_object.go | 84 | ||||
-rw-r--r-- | core/state/state_test.go | 12 | ||||
-rw-r--r-- | core/state/statedb.go | 77 | ||||
-rw-r--r-- | core/state/statedb_test.go | 7 | ||||
-rw-r--r-- | core/state_transition.go | 35 | ||||
-rw-r--r-- | core/vm/contract.go | 29 | ||||
-rw-r--r-- | core/vm/evm.go | 18 | ||||
-rw-r--r-- | core/vm/interface.go | 17 | ||||
-rw-r--r-- | core/vm/logger.go | 3 | ||||
-rw-r--r-- | core/vm/logger_test.go | 4 | ||||
-rw-r--r-- | core/vm/noop.go | 46 | ||||
-rw-r--r-- | core/vm/runtime/runtime.go | 14 | ||||
-rw-r--r-- | light/state.go | 20 | ||||
-rw-r--r-- | light/vm_env.go | 31 | ||||
-rw-r--r-- | tests/block_test_util.go | 11 | ||||
-rw-r--r-- | tests/state_test_util.go | 16 | ||||
-rw-r--r-- | tests/vm_test_util.go | 10 |
23 files changed, 245 insertions, 238 deletions
diff --git a/cmd/evm/main.go b/cmd/evm/main.go index 8af0cebbb..0693d7cd3 100644 --- a/cmd/evm/main.go +++ b/cmd/evm/main.go @@ -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.AccountRef(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(), diff --git a/core/blockchain.go b/core/blockchain.go index cc0ddf1ed..b57eb48e3 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -214,7 +214,6 @@ func (self *BlockChain) loadLastState() error { return err } self.stateCache = statedb - self.stateCache.GetAccount(common.Address{}) // Issue a status log for the user headerTd := self.GetTd(currentHeader.Hash(), currentHeader.Number.Uint64()) diff --git a/core/dao.go b/core/dao.go index a7f544c3d..7e376e68a 100644 --- a/core/dao.go +++ b/core/dao.go @@ -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)) } } diff --git a/core/state/journal.go b/core/state/journal.go index 68d07fa03..5cd41477d 100644 --- a/core/state/journal.go +++ b/core/state/journal.go @@ -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) { diff --git a/core/state/managed_state.go b/core/state/managed_state.go index 0d8f9dd28..1390ef4a0 100644 --- a/core/state/managed_state.go +++ b/core/state/managed_state.go @@ -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} } diff --git a/core/state/managed_state_test.go b/core/state/managed_state_test.go index 0a3be9f5a..ea5737a08 100644 --- a/core/state/managed_state_test.go +++ b/core/state/managed_state_test.go @@ -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] } diff --git a/core/state/state_object.go b/core/state/state_object.go index da1914c9b..4fb69b646 100644 --- a/core/state/state_object.go +++ b/core/state/state_object.go @@ -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,37 +363,21 @@ 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) 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) - } - - it := self.getTrie(self.db.db).Iterator() - for it.Next() { - // ignore cached values - key := common.BytesToHash(self.trie.GetKey(it.Key)) - if _, ok := self.cachedStorage[key]; !ok { - cb(key, common.BytesToHash(it.Value)) - } - } +func (self *stateObject) Value() *big.Int { + panic("Value on stateObject should never be called") } diff --git a/core/state/state_test.go b/core/state/state_test.go index 435d1d829..3bc63c148 100644 --- a/core/state/state_test.go +++ b/core/state/state_test.go @@ -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) } diff --git a/core/state/statedb.go b/core/state/statedb.go index 68009deec..cae2dc4b2 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -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,32 @@ 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 +} + +func (db *StateDB) ForEachStorage(addr common.Address, cb func(key, value common.Hash) bool) { + so := db.getStateObject(addr) + if so == nil { + return + } + + // When iterating over the storage check the cache first + for h, value := range so.cachedStorage { + cb(h, value) + } + + it := so.getTrie(db.db).Iterator() + for it.Next() { + // ignore cached values + key := common.BytesToHash(db.trie.GetKey(it.Key)) + if _, ok := so.cachedStorage[key]; !ok { + cb(key, common.BytesToHash(it.Value)) + } + } } // Copy creates a deep, independent copy of the state. @@ -492,7 +507,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)), diff --git a/core/state/statedb_test.go b/core/state/statedb_test.go index 874317300..597de3be5 100644 --- a/core/state/statedb_test.go +++ b/core/state/statedb_test.go @@ -331,12 +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 { - obj.ForEachStorage(func(key, val common.Hash) bool { + if obj := state.getStateObject(addr); obj != nil { + state.ForEachStorage(addr, func(key, val common.Hash) bool { return checkeq("GetState("+key.Hex()+")", val, checkstate.GetState(addr, key)) }) - checkobj := checkstate.GetStateObject(addr) - checkobj.ForEachStorage(func(key, checkval common.Hash) bool { + checkstate.ForEachStorage(addr, func(key, checkval common.Hash) bool { return checkeq("GetState("+key.Hex()+")", state.GetState(addr, key), checkval) }) } diff --git a/core/state_transition.go b/core/state_transition.go index 09c6c8bf3..6acc78479 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -17,7 +17,7 @@ package core import ( - "fmt" + "errors" "math/big" "github.com/ethereum/go-ethereum/common" @@ -28,7 +28,8 @@ import ( ) var ( - Big0 = big.NewInt(0) + 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.AccountRef(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.AccountRef(*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) diff --git a/core/vm/contract.go b/core/vm/contract.go index 091106d84..66748e821 100644 --- a/core/vm/contract.go +++ b/core/vm/contract.go @@ -25,11 +25,20 @@ 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) } +// AccountRef implements ContractRef. +// +// Account references are used during EVM initialisation and +// it's primary use is to fetch addresses. Removing this object +// proves difficult because of the cached jump destinations which +// are fetched from the parent contract (i.e. the caller), which +// is a ContractRef. +type AccountRef common.Address + +// Address casts AccountRef to a Address +func (ar AccountRef) Address() common.Address { return (common.Address)(ar) } + // 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 +78,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 +90,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 +151,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) -} diff --git a/core/vm/evm.go b/core/vm/evm.go index 0c5d998c2..d1fac6c10 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -116,7 +116,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas } var ( - to Account + to = AccountRef(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 = AccountRef(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 = AccountRef(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, AccountRef(contractAddr), value, gas) contract.SetCallCode(&contractAddr, crypto.Keccak256Hash(code), code) ret, err = evm.interpreter.Run(contract, nil) diff --git a/core/vm/interface.go b/core/vm/interface.go index 6f15112ee..4d8ece41c 100644 --- a/core/vm/interface.go +++ b/core/vm/interface.go @@ -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) @@ -61,20 +60,8 @@ type StateDB interface { AddLog(*types.Log) 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 + ForEachStorage(common.Address, func(common.Hash, common.Hash) bool) } // CallContext provides a basic interface for the EVM calling conventions. The EVM EVM diff --git a/core/vm/logger.go b/core/vm/logger.go index db8c20e07..3845b1073 100644 --- a/core/vm/logger.go +++ b/core/vm/logger.go @@ -144,7 +144,8 @@ 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. - env.StateDB.GetAccount(contract.Address()).ForEachStorage(func(key, value common.Hash) bool { + + env.StateDB.ForEachStorage(contract.Address(), func(key, value common.Hash) bool { storage[key] = value // Return true, indicating we'd like to continue. return true diff --git a/core/vm/logger_test.go b/core/vm/logger_test.go index ca60cba43..e755a18e2 100644 --- a/core/vm/logger_test.go +++ b/core/vm/logger_test.go @@ -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}) diff --git a/core/vm/noop.go b/core/vm/noop.go index 7835eeaf3..2a04a9565 100644 --- a/core/vm/noop.go +++ b/core/vm/noop.go @@ -45,26 +45,26 @@ 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) SubBalance(common.Address, *big.Int) {} -func (NoopStateDB) AddBalance(common.Address, *big.Int) {} -func (NoopStateDB) GetBalance(common.Address) *big.Int { return nil } -func (NoopStateDB) GetNonce(common.Address) uint64 { return 0 } -func (NoopStateDB) SetNonce(common.Address, uint64) {} -func (NoopStateDB) GetCodeHash(common.Address) common.Hash { return common.Hash{} } -func (NoopStateDB) GetCode(common.Address) []byte { return nil } -func (NoopStateDB) SetCode(common.Address, []byte) {} -func (NoopStateDB) GetCodeSize(common.Address) int { return 0 } -func (NoopStateDB) AddRefund(*big.Int) {} -func (NoopStateDB) GetRefund() *big.Int { return nil } -func (NoopStateDB) GetState(common.Address, common.Hash) common.Hash { return common.Hash{} } -func (NoopStateDB) SetState(common.Address, common.Hash, common.Hash) {} -func (NoopStateDB) Suicide(common.Address) bool { return false } -func (NoopStateDB) HasSuicided(common.Address) bool { return false } -func (NoopStateDB) Exist(common.Address) bool { return false } -func (NoopStateDB) Empty(common.Address) bool { return false } -func (NoopStateDB) RevertToSnapshot(int) {} -func (NoopStateDB) Snapshot() int { return 0 } -func (NoopStateDB) AddLog(*types.Log) {} -func (NoopStateDB) AddPreimage(common.Hash, []byte) {} +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 } +func (NoopStateDB) GetNonce(common.Address) uint64 { return 0 } +func (NoopStateDB) SetNonce(common.Address, uint64) {} +func (NoopStateDB) GetCodeHash(common.Address) common.Hash { return common.Hash{} } +func (NoopStateDB) GetCode(common.Address) []byte { return nil } +func (NoopStateDB) SetCode(common.Address, []byte) {} +func (NoopStateDB) GetCodeSize(common.Address) int { return 0 } +func (NoopStateDB) AddRefund(*big.Int) {} +func (NoopStateDB) GetRefund() *big.Int { return nil } +func (NoopStateDB) GetState(common.Address, common.Hash) common.Hash { return common.Hash{} } +func (NoopStateDB) SetState(common.Address, common.Hash, common.Hash) {} +func (NoopStateDB) Suicide(common.Address) bool { return false } +func (NoopStateDB) HasSuicided(common.Address) bool { return false } +func (NoopStateDB) Exist(common.Address) bool { return false } +func (NoopStateDB) Empty(common.Address) bool { return false } +func (NoopStateDB) RevertToSnapshot(int) {} +func (NoopStateDB) Snapshot() int { return 0 } +func (NoopStateDB) AddLog(*types.Log) {} +func (NoopStateDB) AddPreimage(common.Hash, []byte) {} +func (NoopStateDB) ForEachStorage(common.Address, func(common.Hash, common.Hash) bool) {} diff --git a/core/vm/runtime/runtime.go b/core/vm/runtime/runtime.go index cf46603db..94265626f 100644 --- a/core/vm/runtime/runtime.go +++ b/core/vm/runtime/runtime.go @@ -105,17 +105,17 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) { cfg.State, _ = state.New(common.Hash{}, db) } var ( - vmenv = NewEnv(cfg, cfg.State) - sender = cfg.State.CreateAccount(cfg.Origin) - receiver = cfg.State.CreateAccount(common.StringToAddress("contract")) + address = common.StringToAddress("contract") + vmenv = NewEnv(cfg, cfg.State) + sender = vm.AccountRef(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.AccountRef(cfg.Origin) ) // Call the code with the given configuration. diff --git a/light/state.go b/light/state.go index b6cefc9b9..f19748e89 100644 --- a/light/state.go +++ b/light/state.go @@ -268,6 +268,26 @@ func (self *LightState) CreateStateObject(ctx context.Context, addr common.Addre 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 // diff --git a/light/vm_env.go b/light/vm_env.go index d2cc7e960..ebd229de8 100644 --- a/light/vm_env.go +++ b/light/vm_env.go @@ -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 @@ -99,6 +81,15 @@ func (s *VMState) SubBalance(addr common.Address, amount *big.Int) { 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 { diff --git a/tests/block_test_util.go b/tests/block_test_util.go index 695b47e0b..9199be774 100644 --- a/tests/block_test_util.go +++ b/tests/block_test_util.go @@ -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)) } diff --git a/tests/state_test_util.go b/tests/state_test_util.go index e8ab29d14..a2a048205 100644 --- a/tests/state_test_util.go +++ b/tests/state_test_util.go @@ -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()) } } } diff --git a/tests/vm_test_util.go b/tests/vm_test_util.go index 1edf0e425..3b7ba9b31 100644 --- a/tests/vm_test_util.go +++ b/tests/vm_test_util.go @@ -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()) } } } |