bb3651abc8
Consensus rules dictate that objects can only be removed during the finalisation of the transaction (i.e. after all calls have finished). Thus calling a suicided contract twice from the same transaction: A->B(S)->ret(A)->B(S) results in 2 suicides. Calling the suicided object twice from two transactions: A->B(S), A->B, results in only one suicide and a call to an empty object. Our current debug tracing functionality replays all transaction that were executed prior to the targetted transaction in order to provide the user with an accurate trace. As a side effect to calling StateDB.IntermediateRoot it also deletes any suicides objects. Our tracing code never calls this function because it isn't interested in the intermediate root. Becasue of this it caused a bug in the tracing code where transactions that were send to priviously deleted objects resulted in two suicides rather than one suicide and a call to an empty object. Fixes #2542
452 lines
12 KiB
Go
452 lines
12 KiB
Go
// Copyright 2014 The go-ethereum Authors
|
|
// This file is part of the go-ethereum library.
|
|
//
|
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU Lesser General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU Lesser General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Lesser General Public License
|
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
// Package state provides a caching layer atop the Ethereum state trie.
|
|
package state
|
|
|
|
import (
|
|
"fmt"
|
|
"math/big"
|
|
|
|
"github.com/ethereum/go-ethereum/common"
|
|
"github.com/ethereum/go-ethereum/core/vm"
|
|
"github.com/ethereum/go-ethereum/ethdb"
|
|
"github.com/ethereum/go-ethereum/logger"
|
|
"github.com/ethereum/go-ethereum/logger/glog"
|
|
"github.com/ethereum/go-ethereum/rlp"
|
|
"github.com/ethereum/go-ethereum/trie"
|
|
)
|
|
|
|
// The starting nonce determines the default nonce when new accounts are being
|
|
// created.
|
|
var StartingNonce uint64
|
|
|
|
// StateDBs within the ethereum protocol are used to store anything
|
|
// within the merkle trie. StateDBs take care of caching and storing
|
|
// nested states. It's the general query interface to retrieve:
|
|
// * Contracts
|
|
// * Accounts
|
|
type StateDB struct {
|
|
db ethdb.Database
|
|
trie *trie.SecureTrie
|
|
|
|
stateObjects map[string]*StateObject
|
|
|
|
refund *big.Int
|
|
|
|
thash, bhash common.Hash
|
|
txIndex int
|
|
logs map[common.Hash]vm.Logs
|
|
logSize uint
|
|
}
|
|
|
|
// Create a new state from a given trie
|
|
func New(root common.Hash, db ethdb.Database) (*StateDB, error) {
|
|
tr, err := trie.NewSecure(root, db)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &StateDB{
|
|
db: db,
|
|
trie: tr,
|
|
stateObjects: make(map[string]*StateObject),
|
|
refund: new(big.Int),
|
|
logs: make(map[common.Hash]vm.Logs),
|
|
}, nil
|
|
}
|
|
|
|
// Reset clears out all emphemeral state objects from the state db, but keeps
|
|
// the underlying state trie to avoid reloading data for the next operations.
|
|
func (self *StateDB) Reset(root common.Hash) error {
|
|
var (
|
|
err error
|
|
tr = self.trie
|
|
)
|
|
if self.trie.Hash() != root {
|
|
if tr, err = trie.NewSecure(root, self.db); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
*self = StateDB{
|
|
db: self.db,
|
|
trie: tr,
|
|
stateObjects: make(map[string]*StateObject),
|
|
refund: new(big.Int),
|
|
logs: make(map[common.Hash]vm.Logs),
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (self *StateDB) StartRecord(thash, bhash common.Hash, ti int) {
|
|
self.thash = thash
|
|
self.bhash = bhash
|
|
self.txIndex = ti
|
|
}
|
|
|
|
func (self *StateDB) AddLog(log *vm.Log) {
|
|
log.TxHash = self.thash
|
|
log.BlockHash = self.bhash
|
|
log.TxIndex = uint(self.txIndex)
|
|
log.Index = self.logSize
|
|
self.logs[self.thash] = append(self.logs[self.thash], log)
|
|
self.logSize++
|
|
}
|
|
|
|
func (self *StateDB) GetLogs(hash common.Hash) vm.Logs {
|
|
return self.logs[hash]
|
|
}
|
|
|
|
func (self *StateDB) Logs() vm.Logs {
|
|
var logs vm.Logs
|
|
for _, lgs := range self.logs {
|
|
logs = append(logs, lgs...)
|
|
}
|
|
return logs
|
|
}
|
|
|
|
func (self *StateDB) AddRefund(gas *big.Int) {
|
|
self.refund.Add(self.refund, gas)
|
|
}
|
|
|
|
func (self *StateDB) HasAccount(addr common.Address) bool {
|
|
return self.GetStateObject(addr) != nil
|
|
}
|
|
|
|
func (self *StateDB) Exist(addr common.Address) bool {
|
|
return self.GetStateObject(addr) != nil
|
|
}
|
|
|
|
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)
|
|
if stateObject != nil {
|
|
return stateObject.balance
|
|
}
|
|
|
|
return common.Big0
|
|
}
|
|
|
|
func (self *StateDB) GetNonce(addr common.Address) uint64 {
|
|
stateObject := self.GetStateObject(addr)
|
|
if stateObject != nil {
|
|
return stateObject.nonce
|
|
}
|
|
|
|
return StartingNonce
|
|
}
|
|
|
|
func (self *StateDB) GetCode(addr common.Address) []byte {
|
|
stateObject := self.GetStateObject(addr)
|
|
if stateObject != nil {
|
|
return stateObject.code
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (self *StateDB) GetState(a common.Address, b common.Hash) common.Hash {
|
|
stateObject := self.GetStateObject(a)
|
|
if stateObject != nil {
|
|
return stateObject.GetState(b)
|
|
}
|
|
|
|
return common.Hash{}
|
|
}
|
|
|
|
func (self *StateDB) IsDeleted(addr common.Address) bool {
|
|
stateObject := self.GetStateObject(addr)
|
|
if stateObject != nil {
|
|
return stateObject.remove
|
|
}
|
|
return false
|
|
}
|
|
|
|
/*
|
|
* SETTERS
|
|
*/
|
|
|
|
func (self *StateDB) AddBalance(addr common.Address, amount *big.Int) {
|
|
stateObject := self.GetOrNewStateObject(addr)
|
|
if stateObject != nil {
|
|
stateObject.AddBalance(amount)
|
|
}
|
|
}
|
|
|
|
func (self *StateDB) SetNonce(addr common.Address, nonce uint64) {
|
|
stateObject := self.GetOrNewStateObject(addr)
|
|
if stateObject != nil {
|
|
stateObject.SetNonce(nonce)
|
|
}
|
|
}
|
|
|
|
func (self *StateDB) SetCode(addr common.Address, code []byte) {
|
|
stateObject := self.GetOrNewStateObject(addr)
|
|
if stateObject != nil {
|
|
stateObject.SetCode(code)
|
|
}
|
|
}
|
|
|
|
func (self *StateDB) SetState(addr common.Address, key common.Hash, value common.Hash) {
|
|
stateObject := self.GetOrNewStateObject(addr)
|
|
if stateObject != nil {
|
|
stateObject.SetState(key, value)
|
|
}
|
|
}
|
|
|
|
func (self *StateDB) Delete(addr common.Address) bool {
|
|
stateObject := self.GetStateObject(addr)
|
|
if stateObject != nil {
|
|
stateObject.MarkForDeletion()
|
|
stateObject.balance = new(big.Int)
|
|
|
|
return true
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
//
|
|
// Setting, updating & deleting state object methods
|
|
//
|
|
|
|
// Update the given state object and apply it to state trie
|
|
func (self *StateDB) UpdateStateObject(stateObject *StateObject) {
|
|
addr := stateObject.Address()
|
|
data, err := rlp.EncodeToBytes(stateObject)
|
|
if err != nil {
|
|
panic(fmt.Errorf("can't encode object at %x: %v", addr[:], err))
|
|
}
|
|
self.trie.Update(addr[:], data)
|
|
}
|
|
|
|
// Delete the given state object and delete it from the state trie
|
|
func (self *StateDB) DeleteStateObject(stateObject *StateObject) {
|
|
stateObject.deleted = true
|
|
|
|
addr := stateObject.Address()
|
|
self.trie.Delete(addr[:])
|
|
//delete(self.stateObjects, addr.Str())
|
|
}
|
|
|
|
// Retrieve a state object given my the address. Nil if not found
|
|
func (self *StateDB) GetStateObject(addr common.Address) (stateObject *StateObject) {
|
|
stateObject = self.stateObjects[addr.Str()]
|
|
if stateObject != nil {
|
|
if stateObject.deleted {
|
|
stateObject = nil
|
|
}
|
|
|
|
return stateObject
|
|
}
|
|
|
|
data := self.trie.Get(addr[:])
|
|
if len(data) == 0 {
|
|
return nil
|
|
}
|
|
stateObject, err := DecodeObject(addr, self.db, data)
|
|
if err != nil {
|
|
glog.Errorf("can't decode object at %x: %v", addr[:], err)
|
|
return nil
|
|
}
|
|
self.SetStateObject(stateObject)
|
|
return stateObject
|
|
}
|
|
|
|
func (self *StateDB) SetStateObject(object *StateObject) {
|
|
self.stateObjects[object.Address().Str()] = 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)
|
|
if stateObject == nil || stateObject.deleted {
|
|
stateObject = self.CreateStateObject(addr)
|
|
}
|
|
|
|
return stateObject
|
|
}
|
|
|
|
// NewStateObject create a state object whether it exist in the trie or not
|
|
func (self *StateDB) newStateObject(addr common.Address) *StateObject {
|
|
if glog.V(logger.Core) {
|
|
glog.Infof("(+) %x\n", addr)
|
|
}
|
|
|
|
stateObject := NewStateObject(addr, self.db)
|
|
stateObject.SetNonce(StartingNonce)
|
|
self.stateObjects[addr.Str()] = stateObject
|
|
|
|
return stateObject
|
|
}
|
|
|
|
// Creates creates a new state object and takes ownership. This is different from "NewStateObject"
|
|
func (self *StateDB) CreateStateObject(addr common.Address) *StateObject {
|
|
// Get previous (if any)
|
|
so := self.GetStateObject(addr)
|
|
// Create a new one
|
|
newSo := self.newStateObject(addr)
|
|
|
|
// If it existed set the balance to the new account
|
|
if so != nil {
|
|
newSo.balance = so.balance
|
|
}
|
|
|
|
return newSo
|
|
}
|
|
|
|
func (self *StateDB) CreateAccount(addr common.Address) vm.Account {
|
|
return self.CreateStateObject(addr)
|
|
}
|
|
|
|
//
|
|
// Setting, copying of the state methods
|
|
//
|
|
|
|
func (self *StateDB) Copy() *StateDB {
|
|
// ignore error - we assume state-to-be-copied always exists
|
|
state, _ := New(common.Hash{}, self.db)
|
|
state.trie = self.trie
|
|
for k, stateObject := range self.stateObjects {
|
|
state.stateObjects[k] = stateObject.Copy()
|
|
}
|
|
|
|
state.refund.Set(self.refund)
|
|
|
|
for hash, logs := range self.logs {
|
|
state.logs[hash] = make(vm.Logs, len(logs))
|
|
copy(state.logs[hash], logs)
|
|
}
|
|
state.logSize = self.logSize
|
|
|
|
return state
|
|
}
|
|
|
|
func (self *StateDB) Set(state *StateDB) {
|
|
self.trie = state.trie
|
|
self.stateObjects = state.stateObjects
|
|
|
|
self.refund = state.refund
|
|
self.logs = state.logs
|
|
self.logSize = state.logSize
|
|
}
|
|
|
|
func (self *StateDB) GetRefund() *big.Int {
|
|
return self.refund
|
|
}
|
|
|
|
// IntermediateRoot computes the current root hash of the state trie.
|
|
// It is called in between transactions to get the root hash that
|
|
// goes into transaction receipts.
|
|
func (s *StateDB) IntermediateRoot() common.Hash {
|
|
s.refund = new(big.Int)
|
|
for _, stateObject := range s.stateObjects {
|
|
if stateObject.dirty {
|
|
if stateObject.remove {
|
|
s.DeleteStateObject(stateObject)
|
|
} else {
|
|
stateObject.Update()
|
|
s.UpdateStateObject(stateObject)
|
|
}
|
|
stateObject.dirty = false
|
|
}
|
|
}
|
|
return s.trie.Hash()
|
|
}
|
|
|
|
// DeleteSuicides flags the suicided objects for deletion so that it
|
|
// won't be referenced again when called / queried up on.
|
|
//
|
|
// DeleteSuicides should not be used for consensus related updates
|
|
// under any circumstances.
|
|
func (s *StateDB) DeleteSuicides() {
|
|
// Reset refund so that any used-gas calculations can use
|
|
// this method.
|
|
s.refund = new(big.Int)
|
|
for _, stateObject := range s.stateObjects {
|
|
if stateObject.dirty {
|
|
// If the object has been removed by a suicide
|
|
// flag the object as deleted.
|
|
if stateObject.remove {
|
|
stateObject.deleted = true
|
|
}
|
|
stateObject.dirty = false
|
|
}
|
|
}
|
|
}
|
|
|
|
// Commit commits all state changes to the database.
|
|
func (s *StateDB) Commit() (root common.Hash, err error) {
|
|
root, batch := s.CommitBatch()
|
|
return root, batch.Write()
|
|
}
|
|
|
|
// CommitBatch commits all state changes to a write batch but does not
|
|
// execute the batch. It is used to validate state changes against
|
|
// the root hash stored in a block.
|
|
func (s *StateDB) CommitBatch() (root common.Hash, batch ethdb.Batch) {
|
|
batch = s.db.NewBatch()
|
|
root, _ = s.commit(batch)
|
|
return root, batch
|
|
}
|
|
|
|
func (s *StateDB) commit(db trie.DatabaseWriter) (common.Hash, error) {
|
|
s.refund = new(big.Int)
|
|
|
|
for _, stateObject := range s.stateObjects {
|
|
if stateObject.remove {
|
|
// If the object has been removed, don't bother syncing it
|
|
// and just mark it for deletion in the trie.
|
|
s.DeleteStateObject(stateObject)
|
|
} else {
|
|
// Write any contract code associated with the state object
|
|
if len(stateObject.code) > 0 {
|
|
if err := db.Put(stateObject.codeHash, stateObject.code); err != nil {
|
|
return common.Hash{}, err
|
|
}
|
|
}
|
|
// Write any storage changes in the state object to its trie.
|
|
stateObject.Update()
|
|
|
|
// Commit the trie of the object to the batch.
|
|
// This updates the trie root internally, so
|
|
// getting the root hash of the storage trie
|
|
// through UpdateStateObject is fast.
|
|
if _, err := stateObject.trie.CommitTo(db); err != nil {
|
|
return common.Hash{}, err
|
|
}
|
|
// Update the object in the account trie.
|
|
s.UpdateStateObject(stateObject)
|
|
}
|
|
stateObject.dirty = false
|
|
}
|
|
return s.trie.CommitTo(db)
|
|
}
|
|
|
|
func (self *StateDB) Refunds() *big.Int {
|
|
return self.refund
|
|
}
|
|
|
|
// Debug stuff
|
|
func (self *StateDB) CreateOutputForDiff() {
|
|
for _, stateObject := range self.stateObjects {
|
|
stateObject.CreateOutputForDiff()
|
|
}
|
|
}
|