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 (self *VMEnv) MarkCodeHash(common.Hash) {}
func (self *VMEnv) RuleSet() vm.RuleSet { return ruleSet{} }
func (self *VMEnv) Vm() vm.Vm { return self.evm }
func (self *VMEnv) Db() vm.Database { return self.state }

View File

@ -163,6 +163,10 @@ var (
}
// Miner settings
// 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{
Name: "mine",
Usage: "Enable mining",
@ -640,6 +644,16 @@ func MakePasswordList(ctx *cli.Context) []string {
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
// assembles the P2P protocol stack.
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
accman := MakeAccountManager(ctx)
// parse the illegal code hashes and set them to the core package.
core.IllegalCodeHashes = ParseIllegalCodeHashes(ctx)
// initialise new random number generator
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
}
// 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()
var (
from = env.Db().GetAccount(caller.Address())

View File

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

View File

@ -17,8 +17,10 @@
package core
import (
"errors"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
@ -28,8 +30,15 @@ import (
)
var (
big8 = big.NewInt(8)
big32 = big.NewInt(32)
big8 = big.NewInt(8)
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
@ -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
// 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) {
_, 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 {
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
usedGas.Add(usedGas, gas)
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)
// Create a new contract
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.
@ -96,6 +98,7 @@ type Database interface {
GetCode(common.Address) []byte
SetCode(common.Address, []byte)
GetCodeHash(common.Address) common.Hash
AddRefund(*big.Int)
GetRefund() *big.Int

View File

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

View File

@ -27,9 +27,10 @@ import (
// Env is a basic runtime environment required for running the EVM.
type Env struct {
ruleSet vm.RuleSet
depth int
state *state.StateDB
ruleSet vm.RuleSet
depth int
state *state.StateDB
illegalHashes []common.Hash
origin common.Address
coinbase common.Address
@ -49,14 +50,15 @@ type Env struct {
// NewEnv returns a new vm.Environment
func NewEnv(cfg *Config, state *state.StateDB) vm.Environment {
env := &Env{
ruleSet: cfg.RuleSet,
state: state,
origin: cfg.Origin,
coinbase: cfg.Coinbase,
number: cfg.BlockNumber,
time: cfg.Time,
difficulty: cfg.Difficulty,
gasLimit: cfg.GasLimit,
ruleSet: cfg.RuleSet,
illegalHashes: cfg.illegalHashes,
state: state,
origin: cfg.Origin,
coinbase: cfg.Coinbase,
number: cfg.BlockNumber,
time: cfg.Time,
difficulty: cfg.Difficulty,
gasLimit: cfg.GasLimit,
}
env.evm = vm.New(env, vm.Config{
Debug: cfg.Debug,
@ -79,6 +81,8 @@ func (self *Env) AddStructLog(log vm.StructLog) {
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) Vm() vm.Vm { return self.evm }
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
// the EVM.
type Config struct {
RuleSet vm.RuleSet
Difficulty *big.Int
Origin common.Address
Coinbase common.Address
BlockNumber *big.Int
Time *big.Int
GasLimit *big.Int
GasPrice *big.Int
Value *big.Int
DisableJit bool // "disable" so it's enabled by default
Debug bool
RuleSet vm.RuleSet
Difficulty *big.Int
Origin common.Address
Coinbase common.Address
BlockNumber *big.Int
Time *big.Int
GasLimit *big.Int
GasPrice *big.Int
Value *big.Int
DisableJit bool // "disable" so it's enabled by default
Debug bool
illegalHashes []common.Hash
State *state.StateDB
GetHashFn func(n uint64) common.Hash

View File

@ -25,6 +25,8 @@ import (
"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
// up to the limit defined by the Yellow Paper and uses the given block chain
// to query for information.
@ -47,6 +49,8 @@ type VMEnv struct {
depth int // Current execution depth
msg Message // Message appliod
CodeHashes []common.Hash // code hashes collected during execution
header *types.Header // Header information
chain *BlockChain // Blockchain handle
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
}
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) Vm() vm.Vm { return self.evm }
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
}
func (self *Env) MarkCodeHash(common.Hash) {}
func (self *Env) RuleSet() vm.RuleSet { return self.ruleSet }
func (self *Env) Vm() vm.Vm { return self.evm }
func (self *Env) Origin() common.Address { return self.origin }