ソースを参照

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.
Jeffrey Wilcke 9 年 前
コミット
7a5b571c67

+ 1 - 0
cmd/evm/main.go

@@ -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 }

+ 16 - 0
cmd/utils/flags.go

@@ -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()))

+ 5 - 0
core/execution.go

@@ -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())

+ 10 - 0
core/state/statedb.go

@@ -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 {

+ 21 - 3
core/state_processor.go

@@ -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)

+ 3 - 0
core/vm/environment.go

@@ -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

+ 5 - 4
core/vm/jit_test.go

@@ -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 {

+ 15 - 11
core/vm/runtime/env.go

@@ -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 }

+ 12 - 11
core/vm/runtime/runtime.go

@@ -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

+ 6 - 0
core/vm_env.go

@@ -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 }

+ 1 - 0
tests/util.go

@@ -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 }