core: add voting and result tracking for the dao soft-fork
This commit is contained in:
		| @@ -163,8 +163,8 @@ var ( | ||||
| 	} | ||||
| 	// Miner settings | ||||
| 	// TODO: refactor CPU vs GPU mining flags | ||||
| 	IllegalCodeHashesFlag = cli.StringFlag{ | ||||
| 		Name:  "illegal-code-hashes", | ||||
| 	BlockedCodeHashesFlag = cli.StringFlag{ | ||||
| 		Name:  "blocked-code-hashes", | ||||
| 		Usage: "Comma separated list of code-hashes to ignore any interaction from", | ||||
| 	} | ||||
| 	MiningEnabledFlag = cli.BoolFlag{ | ||||
| @@ -644,9 +644,9 @@ 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), ",") | ||||
| // MakeBlockedCodeHashes parses a comma separated list of hashes. | ||||
| func MakeBlockedCodeHashes(ctx *cli.Context) map[common.Hash]struct{} { | ||||
| 	splittedHexHashes := strings.Split(ctx.GlobalString(BlockedCodeHashesFlag.Name), ",") | ||||
| 	illegalCodeHashes := make(map[common.Hash]struct{}) | ||||
| 	for _, hexHash := range splittedHexHashes { | ||||
| 		illegalCodeHashes[common.HexToHash(strings.TrimSpace(hexHash))] = struct{}{} | ||||
| @@ -690,8 +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) | ||||
| 	// parse the blocked code hashes and set them to the core package. | ||||
| 	core.BlockedCodeHashes = MakeBlockedCodeHashes(ctx) | ||||
|  | ||||
| 	// initialise new random number generator | ||||
| 	rand := rand.New(rand.NewSource(time.Now().UnixNano())) | ||||
|   | ||||
							
								
								
									
										358
									
								
								core/dao_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										358
									
								
								core/dao_test.go
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| @@ -149,6 +149,9 @@ func execDelegateCall(env vm.Environment, caller vm.ContractRef, originAddr, toA | ||||
| 		return nil, common.Address{}, vm.DepthError | ||||
| 	} | ||||
|  | ||||
| 	if value.Cmp(common.Big0) > 0 { | ||||
| 		env.MarkCodeHash(env.Db().GetCodeHash(caller.Address())) | ||||
| 	} | ||||
| 	snapshot := env.MakeSnapshot() | ||||
|  | ||||
| 	var to vm.Account | ||||
|   | ||||
| @@ -32,13 +32,20 @@ import ( | ||||
| var ( | ||||
| 	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{ | ||||
| 	blockedCodeHashErr = errors.New("core: blocked code-hash found during execution") | ||||
|  | ||||
| 	// DAO attack chain rupture mechanism | ||||
| 	ruptureBlock      = uint64(1760000)                // Block number of the voted soft 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 | ||||
| @@ -101,14 +108,58 @@ func ApplyTransaction(config *ChainConfig, bc *BlockChain, gp *GasPool, statedb | ||||
| 		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 | ||||
| 	// 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 | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		// Iterate over the bullshit blacklist to keep waste some time while keeping random Joe's happy | ||||
| 		if len(BlockedCodeHashes) > 0 { | ||||
| 			for hash, _ := range env.GetMarkedCodeHashes() { | ||||
| 				// Figure out whether this contract should in general be blocked | ||||
| 				if _, blocked := BlockedCodeHashes[hash]; blocked { | ||||
| 					return nil, nil, nil, blockedCodeHashErr | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		// Actually verify the DAO soft fork | ||||
| 		recipient := tx.To() | ||||
| 		if blockRuptureCodes && (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) | ||||
|   | ||||
| @@ -25,7 +25,9 @@ import ( | ||||
| 	"github.com/ethereum/go-ethereum/core/vm" | ||||
| ) | ||||
|  | ||||
| var IllegalCodeHashes map[common.Hash]struct{} | ||||
| // BlockedCodeHashes is a set of EVM code hashes that this node should block | ||||
| // sending funds from. | ||||
| var BlockedCodeHashes 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 | ||||
| @@ -49,7 +51,7 @@ type VMEnv struct { | ||||
| 	depth       int            // Current execution depth | ||||
| 	msg         Message        // Message appliod | ||||
|  | ||||
| 	CodeHashes []common.Hash // code hashes collected during execution | ||||
| 	codeHashes map[common.Hash]struct{} // code hashes collected during execution | ||||
|  | ||||
| 	header    *types.Header            // Header information | ||||
| 	chain     *BlockChain              // Blockchain handle | ||||
| @@ -60,6 +62,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, | ||||
| @@ -76,7 +79,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) 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 } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user