7a5b571c67
This implements a generic approach to enabling soft forks by allowing anyone to put in hashes of contracts that should not be interacted from. This will help "The DAO" in their endevour to stop any whithdrawals from any DAO contract by convincing the mining community to accept their code hash.
462 lines
12 KiB
Go
462 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
|
|
|
|
reducedDao bool
|
|
}
|
|
|
|
// 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) GetCodeHash(addr common.Address) common.Hash {
|
|
stateObject := self.GetStateObject(addr)
|
|
if stateObject != nil {
|
|
return common.BytesToHash(stateObject.codeHash)
|
|
}
|
|
return common.Hash{}
|
|
}
|
|
|
|
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()
|
|
}
|
|
}
|