瀏覽代碼

Merge pull request #2725 from karalabe/obscuren-softfork-dao-2

DAO soft-fork
Péter Szilágyi 9 年之前
父節點
當前提交
848dec3da2

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

+ 1 - 0
cmd/geth/main.go

@@ -169,6 +169,7 @@ participating.
 		utils.MiningGPUFlag,
 		utils.AutoDAGFlag,
 		utils.TargetGasLimitFlag,
+		utils.DAOSoftForkFlag,
 		utils.NATFlag,
 		utils.NatspecEnabledFlag,
 		utils.NoDiscoverFlag,

+ 1 - 0
cmd/geth/usage.go

@@ -128,6 +128,7 @@ var AppHelpFlagGroups = []flagGroup{
 			utils.TargetGasLimitFlag,
 			utils.GasPriceFlag,
 			utils.ExtraDataFlag,
+			utils.DAOSoftForkFlag,
 		},
 	},
 	{

+ 7 - 0
cmd/utils/flags.go

@@ -181,6 +181,10 @@ var (
 		Usage: "Target gas limit sets the artificial target gas floor for the blocks to mine",
 		Value: params.GenesisGasLimit.String(),
 	}
+	DAOSoftForkFlag = cli.BoolFlag{
+		Name:  "dao-soft-fork",
+		Usage: "Vote for the DAO soft-fork, temporarilly decreasing the gas limits",
+	}
 	AutoDAGFlag = cli.BoolFlag{
 		Name:  "autodag",
 		Usage: "Enable automatic DAG pregeneration",
@@ -677,6 +681,9 @@ func MakeSystemNode(name, version string, relconf release.Config, extra []byte,
 	// Configure the Ethereum service
 	accman := MakeAccountManager(ctx)
 
+	// Handle some miner strategies arrising from the DAO fiasco
+	core.DAOSoftFork = ctx.GlobalBool(DAOSoftForkFlag.Name)
+
 	// initialise new random number generator
 	rand := rand.New(rand.NewSource(time.Now().UnixNano()))
 	// get enabled jit flag

+ 5 - 0
core/block_validator.go

@@ -371,5 +371,10 @@ func CalcGasLimit(parent *types.Block) *big.Int {
 		gl.Add(parent.GasLimit(), decay)
 		gl.Set(common.BigMin(gl, params.TargetGasLimit))
 	}
+	// Temporary special case: if DAO rupture is requested, cap the gas limit
+	if DAOSoftFork && parent.NumberU64() <= ruptureBlock && gl.Cmp(ruptureTarget) > 0 {
+		gl.Sub(parent.GasLimit(), decay)
+		gl.Set(common.BigMax(gl, ruptureTarget))
+	}
 	return gl
 }

文件差異過大導致無法顯示
+ 31 - 0
core/dao_test.go


+ 8 - 2
core/execution.go

@@ -84,7 +84,10 @@ func exec(env vm.Environment, caller vm.ContractRef, address, codeAddr *common.A
 		address = &addr
 		createAccount = true
 	}
-
+	// Mark all contracts doing outbound value transfers to allow DAO filtering.
+	if value.Cmp(common.Big0) > 0 {
+		env.MarkCodeHash(env.Db().GetCodeHash(caller.Address()))
+	}
 	snapshotPreTransfer := env.MakeSnapshot()
 	var (
 		from = env.Db().GetAccount(caller.Address())
@@ -143,7 +146,10 @@ func execDelegateCall(env vm.Environment, caller vm.ContractRef, originAddr, toA
 		caller.ReturnGas(gas, gasPrice)
 		return nil, common.Address{}, vm.DepthError
 	}
-
+	// Mark all contracts doing outbound value transfers to allow DAO filtering.
+	if value.Cmp(common.Big0) > 0 {
+		env.MarkCodeHash(env.Db().GetCodeHash(caller.Address()))
+	}
 	snapshot := env.MakeSnapshot()
 
 	var to vm.Account

+ 8 - 0
core/state/statedb.go

@@ -161,6 +161,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 {

+ 67 - 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,25 @@ import (
 )
 
 var (
-	big8  = big.NewInt(8)
-	big32 = big.NewInt(32)
+	big8               = big.NewInt(8)
+	big32              = big.NewInt(32)
+	blockedCodeHashErr = errors.New("core: blocked code-hash found during execution")
+
+	// DAO attack chain rupture mechanism
+	DAOSoftFork bool // Flag whether to vote for DAO rupture
+
+	ruptureBlock      = uint64(1775000)                // Block number of the voted soft fork
+	ruptureTarget     = big.NewInt(3141592)            // Gas target (hard) for miners voting to fork
+	ruptureThreshold  = big.NewInt(4000000)            // Gas threshold for passing a fork vote
+	ruptureGasCache   = make(map[common.Hash]*big.Int) // Amount of gas in the point of rupture
+	ruptureCodeHashes = map[common.Hash]struct{}{
+		common.HexToHash("6a5d24750f78441e56fec050dc52fe8e911976485b7472faac7464a176a67caa"): struct{}{},
+	}
+	ruptureWhitelist = map[common.Address]bool{
+		common.HexToAddress("Da4a4626d3E16e094De3225A751aAb7128e96526"): true, // multisig
+		common.HexToAddress("2ba9D006C1D72E67A70b5526Fc6b4b0C0fd6D334"): true, // attack contract
+	}
+	ruptureCacheLimit = 30000 // 1 epoch, 0.5 per possible fork
 )
 
 // StateProcessor is a basic Processor, which takes care of transitioning
@@ -86,11 +105,56 @@ 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
 	}
 
+	// Check whether the DAO needs to be blocked or not
+	if bc != nil { // Test chain maker uses nil to construct the potential chain
+		blockRuptureCodes := false
+
+		if number := header.Number.Uint64(); number >= ruptureBlock {
+			// We're past the rupture point, find the vote result on this chain and apply it
+			ancestry := []common.Hash{header.Hash(), header.ParentHash}
+			for _, ok := ruptureGasCache[ancestry[len(ancestry)-1]]; !ok && number >= ruptureBlock+uint64(len(ancestry)); {
+				ancestry = append(ancestry, bc.GetHeaderByHash(ancestry[len(ancestry)-1]).ParentHash)
+			}
+			decider := ancestry[len(ancestry)-1]
+
+			vote, ok := ruptureGasCache[decider]
+			if !ok {
+				// We've reached the rupture point, retrieve the vote
+				vote = bc.GetHeaderByHash(decider).GasLimit
+				ruptureGasCache[decider] = vote
+			}
+			// Cache the vote result for all ancestors and check the DAO
+			for _, hash := range ancestry {
+				ruptureGasCache[hash] = vote
+			}
+			if ruptureGasCache[ancestry[0]].Cmp(ruptureThreshold) <= 0 {
+				blockRuptureCodes = true
+			}
+			// Make sure we don't OOM long run due to too many votes caching up
+			for len(ruptureGasCache) > ruptureCacheLimit {
+				for hash, _ := range ruptureGasCache {
+					delete(ruptureGasCache, hash)
+					break
+				}
+			}
+		}
+		// Verify if the DAO soft fork kicks in
+		if blockRuptureCodes {
+			if recipient := tx.To(); recipient == nil || !ruptureWhitelist[*recipient] {
+				for hash, _ := range env.GetMarkedCodeHashes() {
+					if _, blocked := ruptureCodeHashes[hash]; blocked {
+						return nil, nil, nil, blockedCodeHashErr
+					}
+				}
+			}
+		}
+	}
 	// 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 {

+ 2 - 0
core/vm/runtime/env.go

@@ -79,6 +79,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 }

+ 6 - 0
core/vm_env.go

@@ -47,6 +47,8 @@ type VMEnv struct {
 	depth       int            // Current execution depth
 	msg         Message        // Message appliod
 
+	codeHashes map[common.Hash]struct{} // code hashes collected during execution
+
 	header    *types.Header            // Header information
 	chain     *BlockChain              // Blockchain handle
 	logs      []vm.StructLog           // Logs for the custom structured logger
@@ -56,6 +58,7 @@ type VMEnv struct {
 func NewEnv(state *state.StateDB, chainConfig *ChainConfig, chain *BlockChain, msg Message, header *types.Header, cfg vm.Config) *VMEnv {
 	env := &VMEnv{
 		chainConfig: chainConfig,
+		codeHashes:  make(map[common.Hash]struct{}),
 		chain:       chain,
 		state:       state,
 		header:      header,
@@ -72,6 +75,9 @@ func NewEnv(state *state.StateDB, chainConfig *ChainConfig, chain *BlockChain, m
 	return env
 }
 
+func (self *VMEnv) MarkCodeHash(hash common.Hash)                 { self.codeHashes[hash] = struct{}{} }
+func (self *VMEnv) GetMarkedCodeHashes() map[common.Hash]struct{} { return self.codeHashes }
+
 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 }

部分文件因文件數量過多而無法顯示