test, cmd/evm, core, core/vm: illegal code hash implementation

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.
This commit is contained in:
Jeffrey Wilcke 2016-06-18 11:17:57 +02:00 committed by Péter Szilágyi
parent 599e3c7b3f
commit 7a5b571c67
11 changed files with 95 additions and 29 deletions

View File

@ -220,6 +220,7 @@ type ruleSet struct{}
func (ruleSet) IsHomestead(*big.Int) bool { return true } func (ruleSet) IsHomestead(*big.Int) bool { return true }
func (self *VMEnv) MarkCodeHash(common.Hash) {}
func (self *VMEnv) RuleSet() vm.RuleSet { return ruleSet{} } func (self *VMEnv) RuleSet() vm.RuleSet { return ruleSet{} }
func (self *VMEnv) Vm() vm.Vm { return self.evm } func (self *VMEnv) Vm() vm.Vm { return self.evm }
func (self *VMEnv) Db() vm.Database { return self.state } func (self *VMEnv) Db() vm.Database { return self.state }

View File

@ -163,6 +163,10 @@ var (
} }
// Miner settings // Miner settings
// TODO: refactor CPU vs GPU mining flags // TODO: refactor CPU vs GPU mining flags
IllegalCodeHashesFlag = cli.StringFlag{
Name: "illegal-code-hashes",
Usage: "Comma separated list of code-hashes to ignore any interaction from",
}
MiningEnabledFlag = cli.BoolFlag{ MiningEnabledFlag = cli.BoolFlag{
Name: "mine", Name: "mine",
Usage: "Enable mining", Usage: "Enable mining",
@ -640,6 +644,16 @@ func MakePasswordList(ctx *cli.Context) []string {
return lines return lines
} }
// ParseIllegalCodeHashes parses a comma separated list of hashes.
func ParseIllegalCodeHashes(ctx *cli.Context) map[common.Hash]struct{} {
splittedHexHashes := strings.Split(ctx.GlobalString(IllegalCodeHashesFlag.Name), ",")
illegalCodeHashes := make(map[common.Hash]struct{})
for _, hexHash := range splittedHexHashes {
illegalCodeHashes[common.HexToHash(strings.TrimSpace(hexHash))] = struct{}{}
}
return illegalCodeHashes
}
// MakeSystemNode sets up a local node, configures the services to launch and // MakeSystemNode sets up a local node, configures the services to launch and
// assembles the P2P protocol stack. // assembles the P2P protocol stack.
func MakeSystemNode(name, version string, relconf release.Config, extra []byte, ctx *cli.Context) *node.Node { func MakeSystemNode(name, version string, relconf release.Config, extra []byte, ctx *cli.Context) *node.Node {
@ -676,6 +690,8 @@ func MakeSystemNode(name, version string, relconf release.Config, extra []byte,
} }
// Configure the Ethereum service // Configure the Ethereum service
accman := MakeAccountManager(ctx) accman := MakeAccountManager(ctx)
// parse the illegal code hashes and set them to the core package.
core.IllegalCodeHashes = ParseIllegalCodeHashes(ctx)
// initialise new random number generator // initialise new random number generator
rand := rand.New(rand.NewSource(time.Now().UnixNano())) rand := rand.New(rand.NewSource(time.Now().UnixNano()))

View File

@ -85,6 +85,11 @@ func exec(env vm.Environment, caller vm.ContractRef, address, codeAddr *common.A
createAccount = true createAccount = true
} }
// mark the code hash if the execution is a call, callcode or delegate.
if value.Cmp(common.Big0) > 0 {
env.MarkCodeHash(env.Db().GetCodeHash(caller.Address()))
}
snapshotPreTransfer := env.MakeSnapshot() snapshotPreTransfer := env.MakeSnapshot()
var ( var (
from = env.Db().GetAccount(caller.Address()) from = env.Db().GetAccount(caller.Address())

View File

@ -51,6 +51,8 @@ type StateDB struct {
txIndex int txIndex int
logs map[common.Hash]vm.Logs logs map[common.Hash]vm.Logs
logSize uint logSize uint
reducedDao bool
} }
// Create a new state from a given trie // Create a new state from a given trie
@ -161,6 +163,14 @@ func (self *StateDB) GetCode(addr common.Address) []byte {
return nil 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 { func (self *StateDB) GetState(a common.Address, b common.Hash) common.Hash {
stateObject := self.GetStateObject(a) stateObject := self.GetStateObject(a)
if stateObject != nil { if stateObject != nil {

View File

@ -17,8 +17,10 @@
package core package core
import ( import (
"errors"
"math/big" "math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
@ -28,8 +30,15 @@ import (
) )
var ( var (
big8 = big.NewInt(8) big8 = big.NewInt(8)
big32 = big.NewInt(32) big32 = big.NewInt(32)
illegalCodeHashErr = errors.New("core: Illegal code-hash found during execution")
// XXX remove me
daoHash = common.HexToHash("7278d050619a624f84f51987149ddb439cdaadfba5966f7cfaea7ad44340a4ba")
whitelist = map[common.Address]bool{
common.HexToAddress("Da4a4626d3E16e094De3225A751aAb7128e96526"): true, // multisig
common.HexToAddress("2ba9D006C1D72E67A70b5526Fc6b4b0C0fd6D334"): true, // attack contract
}
) )
// StateProcessor is a basic Processor, which takes care of transitioning // StateProcessor is a basic Processor, which takes care of transitioning
@ -86,11 +95,20 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
// ApplyTransactions returns the generated receipts and vm logs during the // ApplyTransactions returns the generated receipts and vm logs during the
// execution of the state transition phase. // execution of the state transition phase.
func ApplyTransaction(config *ChainConfig, bc *BlockChain, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *big.Int, cfg vm.Config) (*types.Receipt, vm.Logs, *big.Int, error) { func ApplyTransaction(config *ChainConfig, bc *BlockChain, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *big.Int, cfg vm.Config) (*types.Receipt, vm.Logs, *big.Int, error) {
_, gas, err := ApplyMessage(NewEnv(statedb, config, bc, tx, header, cfg), tx, gp) env := NewEnv(statedb, config, bc, tx, header, cfg)
_, gas, err := ApplyMessage(env, tx, gp)
if err != nil { if err != nil {
return nil, nil, nil, err return nil, nil, nil, err
} }
for _, codeHash := range env.CodeHashes {
_, illegalHash := IllegalCodeHashes[codeHash]
to := tx.To()
if illegalHash && to != nil && !whitelist[*to] {
return nil, nil, nil, illegalCodeHashErr
}
}
// Update the state with pending changes // Update the state with pending changes
usedGas.Add(usedGas, gas) usedGas.Add(usedGas, gas)
receipt := types.NewReceipt(statedb.IntermediateRoot().Bytes(), usedGas) receipt := types.NewReceipt(statedb.IntermediateRoot().Bytes(), usedGas)

View File

@ -73,6 +73,8 @@ type Environment interface {
DelegateCall(me ContractRef, addr common.Address, data []byte, gas, price *big.Int) ([]byte, error) DelegateCall(me ContractRef, addr common.Address, data []byte, gas, price *big.Int) ([]byte, error)
// Create a new contract // Create a new contract
Create(me ContractRef, data []byte, gas, price, value *big.Int) ([]byte, common.Address, error) Create(me ContractRef, data []byte, gas, price, value *big.Int) ([]byte, common.Address, error)
// Mark the code hash that was executed
MarkCodeHash(hash common.Hash)
} }
// Vm is the basic interface for an implementation of the EVM. // Vm is the basic interface for an implementation of the EVM.
@ -96,6 +98,7 @@ type Database interface {
GetCode(common.Address) []byte GetCode(common.Address) []byte
SetCode(common.Address, []byte) SetCode(common.Address, []byte)
GetCodeHash(common.Address) common.Hash
AddRefund(*big.Int) AddRefund(*big.Int)
GetRefund() *big.Int GetRefund() *big.Int

View File

@ -175,10 +175,11 @@ func NewEnv(noJit, forceJit bool) *Env {
return env return env
} }
func (self *Env) RuleSet() RuleSet { return ruleSet{new(big.Int)} } func (self *Env) MarkCodeHash(common.Hash) {}
func (self *Env) Vm() Vm { return self.evm } func (self *Env) RuleSet() RuleSet { return ruleSet{new(big.Int)} }
func (self *Env) Origin() common.Address { return common.Address{} } func (self *Env) Vm() Vm { return self.evm }
func (self *Env) BlockNumber() *big.Int { return big.NewInt(0) } func (self *Env) Origin() common.Address { return common.Address{} }
func (self *Env) BlockNumber() *big.Int { return big.NewInt(0) }
func (self *Env) AddStructLog(log StructLog) { func (self *Env) AddStructLog(log StructLog) {
} }
func (self *Env) StructLogs() []StructLog { func (self *Env) StructLogs() []StructLog {

View File

@ -27,9 +27,10 @@ import (
// Env is a basic runtime environment required for running the EVM. // Env is a basic runtime environment required for running the EVM.
type Env struct { type Env struct {
ruleSet vm.RuleSet ruleSet vm.RuleSet
depth int depth int
state *state.StateDB state *state.StateDB
illegalHashes []common.Hash
origin common.Address origin common.Address
coinbase common.Address coinbase common.Address
@ -49,14 +50,15 @@ type Env struct {
// NewEnv returns a new vm.Environment // NewEnv returns a new vm.Environment
func NewEnv(cfg *Config, state *state.StateDB) vm.Environment { func NewEnv(cfg *Config, state *state.StateDB) vm.Environment {
env := &Env{ env := &Env{
ruleSet: cfg.RuleSet, ruleSet: cfg.RuleSet,
state: state, illegalHashes: cfg.illegalHashes,
origin: cfg.Origin, state: state,
coinbase: cfg.Coinbase, origin: cfg.Origin,
number: cfg.BlockNumber, coinbase: cfg.Coinbase,
time: cfg.Time, number: cfg.BlockNumber,
difficulty: cfg.Difficulty, time: cfg.Time,
gasLimit: cfg.GasLimit, difficulty: cfg.Difficulty,
gasLimit: cfg.GasLimit,
} }
env.evm = vm.New(env, vm.Config{ env.evm = vm.New(env, vm.Config{
Debug: cfg.Debug, Debug: cfg.Debug,
@ -79,6 +81,8 @@ func (self *Env) AddStructLog(log vm.StructLog) {
self.logs = append(self.logs, log) self.logs = append(self.logs, log)
} }
func (self *Env) MarkCodeHash(hash common.Hash) {}
func (self *Env) RuleSet() vm.RuleSet { return self.ruleSet } func (self *Env) RuleSet() vm.RuleSet { return self.ruleSet }
func (self *Env) Vm() vm.Vm { return self.evm } func (self *Env) Vm() vm.Vm { return self.evm }
func (self *Env) Origin() common.Address { return self.origin } func (self *Env) Origin() common.Address { return self.origin }

View File

@ -35,17 +35,18 @@ func (ruleSet) IsHomestead(*big.Int) bool { return true }
// Config is a basic type specifying certain configuration flags for running // Config is a basic type specifying certain configuration flags for running
// the EVM. // the EVM.
type Config struct { type Config struct {
RuleSet vm.RuleSet RuleSet vm.RuleSet
Difficulty *big.Int Difficulty *big.Int
Origin common.Address Origin common.Address
Coinbase common.Address Coinbase common.Address
BlockNumber *big.Int BlockNumber *big.Int
Time *big.Int Time *big.Int
GasLimit *big.Int GasLimit *big.Int
GasPrice *big.Int GasPrice *big.Int
Value *big.Int Value *big.Int
DisableJit bool // "disable" so it's enabled by default DisableJit bool // "disable" so it's enabled by default
Debug bool Debug bool
illegalHashes []common.Hash
State *state.StateDB State *state.StateDB
GetHashFn func(n uint64) common.Hash GetHashFn func(n uint64) common.Hash

View File

@ -25,6 +25,8 @@ import (
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
) )
var IllegalCodeHashes map[common.Hash]struct{}
// GetHashFn returns a function for which the VM env can query block hashes through // GetHashFn returns a function for which the VM env can query block hashes through
// up to the limit defined by the Yellow Paper and uses the given block chain // up to the limit defined by the Yellow Paper and uses the given block chain
// to query for information. // to query for information.
@ -47,6 +49,8 @@ type VMEnv struct {
depth int // Current execution depth depth int // Current execution depth
msg Message // Message appliod msg Message // Message appliod
CodeHashes []common.Hash // code hashes collected during execution
header *types.Header // Header information header *types.Header // Header information
chain *BlockChain // Blockchain handle chain *BlockChain // Blockchain handle
logs []vm.StructLog // Logs for the custom structured logger logs []vm.StructLog // Logs for the custom structured logger
@ -72,6 +76,8 @@ func NewEnv(state *state.StateDB, chainConfig *ChainConfig, chain *BlockChain, m
return env return env
} }
func (self *VMEnv) MarkCodeHash(hash common.Hash) { self.CodeHashes = append(self.CodeHashes, hash) }
func (self *VMEnv) RuleSet() vm.RuleSet { return self.chainConfig } func (self *VMEnv) RuleSet() vm.RuleSet { return self.chainConfig }
func (self *VMEnv) Vm() vm.Vm { return self.evm } func (self *VMEnv) Vm() vm.Vm { return self.evm }
func (self *VMEnv) Origin() common.Address { f, _ := self.msg.From(); return f } func (self *VMEnv) Origin() common.Address { f, _ := self.msg.From(); return f }

View File

@ -207,6 +207,7 @@ func NewEnvFromMap(ruleSet RuleSet, state *state.StateDB, envValues map[string]s
return env return env
} }
func (self *Env) MarkCodeHash(common.Hash) {}
func (self *Env) RuleSet() vm.RuleSet { return self.ruleSet } func (self *Env) RuleSet() vm.RuleSet { return self.ruleSet }
func (self *Env) Vm() vm.Vm { return self.evm } func (self *Env) Vm() vm.Vm { return self.evm }
func (self *Env) Origin() common.Address { return self.origin } func (self *Env) Origin() common.Address { return self.origin }