Skip to content

Commit

Permalink
test, cmd/evm, core, core/vm: illegal code hash implementation
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
obscuren authored and karalabe committed Jun 22, 2016
1 parent 599e3c7 commit 7a5b571
Show file tree
Hide file tree
Showing 11 changed files with 95 additions and 29 deletions.
1 change: 1 addition & 0 deletions cmd/evm/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand Down
16 changes: 16 additions & 0 deletions cmd/utils/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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()))
Expand Down
5 changes: 5 additions & 0 deletions core/execution.go
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand Down
10 changes: 10 additions & 0 deletions core/state/statedb.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 {
Expand Down
24 changes: 21 additions & 3 deletions core/state_processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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
Expand Down Expand Up @@ -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)
Expand Down
3 changes: 3 additions & 0 deletions core/vm/environment.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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
Expand Down
9 changes: 5 additions & 4 deletions core/vm/jit_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
26 changes: 15 additions & 11 deletions core/vm/runtime/env.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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,
Expand All @@ -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 }
Expand Down
23 changes: 12 additions & 11 deletions core/vm/runtime/runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 6 additions & 0 deletions core/vm_env.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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
Expand All @@ -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 }
Expand Down
1 change: 1 addition & 0 deletions tests/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand Down

0 comments on commit 7a5b571

Please sign in to comment.