core: add voting and result tracking for the dao soft-fork

This commit is contained in:
Péter Szilágyi 2016-06-22 11:59:28 +03:00
parent 7a5b571c67
commit c4de28938f
5 changed files with 436 additions and 20 deletions

View File

@ -163,8 +163,8 @@ var (
} }
// Miner settings // Miner settings
// TODO: refactor CPU vs GPU mining flags // TODO: refactor CPU vs GPU mining flags
IllegalCodeHashesFlag = cli.StringFlag{ BlockedCodeHashesFlag = cli.StringFlag{
Name: "illegal-code-hashes", Name: "blocked-code-hashes",
Usage: "Comma separated list of code-hashes to ignore any interaction from", Usage: "Comma separated list of code-hashes to ignore any interaction from",
} }
MiningEnabledFlag = cli.BoolFlag{ MiningEnabledFlag = cli.BoolFlag{
@ -644,9 +644,9 @@ func MakePasswordList(ctx *cli.Context) []string {
return lines return lines
} }
// ParseIllegalCodeHashes parses a comma separated list of hashes. // MakeBlockedCodeHashes parses a comma separated list of hashes.
func ParseIllegalCodeHashes(ctx *cli.Context) map[common.Hash]struct{} { func MakeBlockedCodeHashes(ctx *cli.Context) map[common.Hash]struct{} {
splittedHexHashes := strings.Split(ctx.GlobalString(IllegalCodeHashesFlag.Name), ",") splittedHexHashes := strings.Split(ctx.GlobalString(BlockedCodeHashesFlag.Name), ",")
illegalCodeHashes := make(map[common.Hash]struct{}) illegalCodeHashes := make(map[common.Hash]struct{})
for _, hexHash := range splittedHexHashes { for _, hexHash := range splittedHexHashes {
illegalCodeHashes[common.HexToHash(strings.TrimSpace(hexHash))] = struct{}{} 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 // Configure the Ethereum service
accman := MakeAccountManager(ctx) accman := MakeAccountManager(ctx)
// parse the illegal code hashes and set them to the core package. // parse the blocked code hashes and set them to the core package.
core.IllegalCodeHashes = ParseIllegalCodeHashes(ctx) core.BlockedCodeHashes = MakeBlockedCodeHashes(ctx)
// initialise new random number generator // initialise new random number generator
rand := rand.New(rand.NewSource(time.Now().UnixNano())) rand := rand.New(rand.NewSource(time.Now().UnixNano()))

358
core/dao_test.go Normal file

File diff suppressed because one or more lines are too long

View File

@ -149,6 +149,9 @@ func execDelegateCall(env vm.Environment, caller vm.ContractRef, originAddr, toA
return nil, common.Address{}, vm.DepthError return nil, common.Address{}, vm.DepthError
} }
if value.Cmp(common.Big0) > 0 {
env.MarkCodeHash(env.Db().GetCodeHash(caller.Address()))
}
snapshot := env.MakeSnapshot() snapshot := env.MakeSnapshot()
var to vm.Account var to vm.Account

View File

@ -32,13 +32,20 @@ import (
var ( var (
big8 = big.NewInt(8) big8 = big.NewInt(8)
big32 = big.NewInt(32) big32 = big.NewInt(32)
illegalCodeHashErr = errors.New("core: Illegal code-hash found during execution") blockedCodeHashErr = errors.New("core: blocked code-hash found during execution")
// XXX remove me
daoHash = common.HexToHash("7278d050619a624f84f51987149ddb439cdaadfba5966f7cfaea7ad44340a4ba") // DAO attack chain rupture mechanism
whitelist = map[common.Address]bool{ 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("Da4a4626d3E16e094De3225A751aAb7128e96526"): true, // multisig
common.HexToAddress("2ba9D006C1D72E67A70b5526Fc6b4b0C0fd6D334"): true, // attack contract 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 // 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 return nil, nil, nil, err
} }
for _, codeHash := range env.CodeHashes { // Check whether the DAO needs to be blocked or not
_, illegalHash := IllegalCodeHashes[codeHash] if bc != nil { // Test chain maker uses nil to construct the potential chain
to := tx.To() blockRuptureCodes := false
if illegalHash && to != nil && !whitelist[*to] {
return nil, nil, nil, illegalCodeHashErr 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 // Update the state with pending changes
usedGas.Add(usedGas, gas) usedGas.Add(usedGas, gas)
receipt := types.NewReceipt(statedb.IntermediateRoot().Bytes(), usedGas) receipt := types.NewReceipt(statedb.IntermediateRoot().Bytes(), usedGas)

View File

@ -25,7 +25,9 @@ import (
"github.com/ethereum/go-ethereum/core/vm" "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 // 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 // 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 depth int // Current execution depth
msg Message // Message appliod 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 header *types.Header // Header information
chain *BlockChain // Blockchain handle 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 { func NewEnv(state *state.StateDB, chainConfig *ChainConfig, chain *BlockChain, msg Message, header *types.Header, cfg vm.Config) *VMEnv {
env := &VMEnv{ env := &VMEnv{
chainConfig: chainConfig, chainConfig: chainConfig,
codeHashes: make(map[common.Hash]struct{}),
chain: chain, chain: chain,
state: state, state: state,
header: header, header: header,
@ -76,7 +79,8 @@ func NewEnv(state *state.StateDB, chainConfig *ChainConfig, chain *BlockChain, m
return env 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) RuleSet() vm.RuleSet { return self.chainConfig }
func (self *VMEnv) Vm() vm.Vm { return self.evm } func (self *VMEnv) Vm() vm.Vm { return self.evm }