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

This commit is contained in:
Péter Szilágyi committed 2016-06-23 16:43:35 +03:00
1 parent 7a5b571c67
commit c4de28938f
5 files changed
+436 -20

No files matched your search

+7 -7
View File
@@ -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
View File
@@ -0,0 +1,358 @@
// Copyright 2016 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package core
import (
"math/big"
"testing"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/params"
)
// Some weird dependency for deploying a DAO instance.
var creatorDeployCode = common.FromHex("606060405261339c806100126000396000f3606060405260e060020a6000350463e2faf0448114601a575b005b6097600435602435604435606435600084308585853360606132e8806100b48339018087600160a060020a0316815260200186600160a060020a0316815260200185815260200184815260200183815260200182600160a060020a031681526020019650505050505050604051809103906000f095945050505050565b60408051600160a060020a03929092168252519081900360200190f3606060405260405160c0806132e88339610120604052905160805160a051925160e0516101005193949293828282600f829055601083905560118054610100830261010060a860020a031990911617905560405130906001906101bc806103378339600160a060020a039390931692019182526020820152604080519182900301906000f060128054600160a060020a031916919091179055505060038054600160a060020a03199081168917909155600e80549091168717905550600c84905560405130906000906101bc806104f38339018083600160a060020a0316815260200182815260200192505050604051809103906000f060405160078054600160a060020a0319169290921790915530906000906101bc806106af8339018083600160a060020a0316815260200182815260200192505050604051809103906000f060088054600160a060020a031916919091179055600754600160a060020a0316600014156101c357610002565b50505030600160a060020a03908116600090815260046020526040808220805460ff19908116600190811790925560035490941683529120805490921617905550505050505050612a7d8061086b6000396000f35b600854600160a060020a0316600014156101dc57610002565b4260025560056001908155600080548282558290801582901161016e57600e0281600e02836000526020600020918201910161016e919061023e565b5050600060098201819055600a820155600d81018054600160a060020a03191690556001015b80821115610333578054600160a060020a03191681556000600182810182905560028381018054848255909281161561010002600019011604601f81901061030557505b506000600383018190556004838101805461ffff19169055600584018290556006840182905560078401805460ff191690556008840180548382559083526020909220610218929091028101905b808211156103335760008082556001820181815560028301919091556003919091018054600160a060020a03191690556102d0565b601f01602090049060005260206000209081019061028291905b80821115610333576000815560010161031f565b50905660606040818152806101bc833960a090525160805160008054600160a060020a03191690921760a060020a60ff0219167401000000000000000000000000000000000000000090910217815561016290819061005a90396000f3606060405236156100405760e060020a60003504630221038a811461004d57806318bdc79a146100aa5780638da5cb5b146100be578063d2cc718f146100d0575b6100d96001805434019055565b6100db6004356024356000805433600160a060020a0390811691161415806100755750600034115b806100a05750805460a060020a900460ff1680156100a057508054600160a060020a03848116911614155b156100f757610002565b6100db60005460ff60a060020a9091041681565b6100ed600054600160a060020a031681565b6100db60015481565b005b60408051918252519081900360200190f35b6060908152602090f35b600160a060020a0383168260608381818185876185025a03f1925050501561015c57604080518381529051600160a060020a038516917f9735b0cb909f3d21d5c16bbcccd272d85fa11446f6d679f6ecb170d2dabfecfc919081900360200190a25060015b929150505660606040818152806101bc833960a090525160805160008054600160a060020a03191690921760a060020a60ff0219167401000000000000000000000000000000000000000090910217815561016290819061005a90396000f3606060405236156100405760e060020a60003504630221038a811461004d57806318bdc79a146100aa5780638da5cb5b146100be578063d2cc718f146100d0575b6100d96001805434019055565b6100db6004356024356000805433600160a060020a0390811691161415806100755750600034115b806100a05750805460a060020a900460ff1680156100a057508054600160a060020a03848116911614155b156100f757610002565b6100db60005460ff60a060020a9091041681565b6100ed600054600160a060020a031681565b6100db60015481565b005b60408051918252519081900360200190f35b6060908152602090f35b600160a060020a0383168260608381818185876185025a03f1925050501561015c57604080518381529051600160a060020a038516917f9735b0cb909f3d21d5c16bbcccd272d85fa11446f6d679f6ecb170d2dabfecfc919081900360200190a25060015b929150505660606040818152806101bc833960a090525160805160008054600160a060020a03191690921760a060020a60ff0219167401000000000000000000000000000000000000000090910217815561016290819061005a90396000f3606060405236156100405760e060020a60003504630221038a811461004d57806318bdc79a146100aa5780638da5cb5b146100be578063d2cc718f146100d0575b6100d96001805434019055565b6100db6004356024356000805433600160a060020a0390811691161415806100755750600034115b806100a05750805460a060020a900460ff1680156100a057508054600160a060020a03848116911614155b156100f757610002565b6100db60005460ff60a060020a9091041681565b6100ed600054600160a060020a031681565b6100db60015481565b005b60408051918252519081900360200190f35b6060908152602090f35b600160a060020a0383168260608381818185876185025a03f1925050501561015c57604080518381529051600160a060020a038516917f9735b0cb909f3d21d5c16bbcccd272d85fa11446f6d679f6ecb170d2dabfecfc919081900360200190a25060015b92915050566060604052361561020e5760e060020a6000350463013cf08b811461022c578063095ea7b3146103ea5780630c3b7b961461045e5780630e70820314610467578063149acf9a1461047957806318160ddd1461048b5780631f2dc5ef1461049457806321b5b8dd146104b4578063234693d3146104c6578063237e9492146Line truncated
// EVM deploy bytecode of the DAO to simulate blocking.
var daoDeployCode = common.FromHex("606060405260405160c0806132c88339610120604052905160805160a051925160e0516101005193949293828282600f829055601083905560118054610100830261010060a860020a031990911617905560405130906001906101bc8061033e8339600160a060020a03909316908301526101408201526040519081900361016001906000f060128054600160a060020a031916919091179055505060038054600160a060020a03199081168917909155600e80549091168717905550600c84905560405130906000906101bc806104fa8339018083600160a060020a0316815260200182815260200192505050604051809103906000f0600760006101000a815481600160a060020a03021916908302179055503060006040516101bc806106b68339018083600160a060020a0316815260200182815260200192505050604051809103906000f060088054600160a060020a031916919091179055600754600160a060020a03166000141561017557610002565b600854600160a060020a03166000141561018e57610002565b426002556005600190815560008054828255829080158290116101ca57600e0281600e0283600052602060002091820191016101ca9190610245565b50505030600160a060020a03908116600090815260046020526040808220805460ff19908116600190811790925560035490941683529120805490921617905550505050505050612a56806108726000396000f35b5050600060098201819055600a820155600d81018054600160a060020a03191690556001015b8082111561033a578054600160a060020a03191681556000600182810182905560028381018054848255909281161561010002600019011604601f81901061030c57505b506000600383018190556004838101805461ffff19169055600584018290556006840182905560078401805460ff19169055600884018054838255908352602090922061021f929091028101905b8082111561033a5760008082556001820181815560028301919091556003919091018054600160a060020a03191690556102d7565b601f01602090049060005260206000209081019061028991905b8082111561033a5760008155600101610326565b50905660606040818152806101bc833960a090525160805160008054600160a060020a03191690921760a060020a60ff0219167401000000000000000000000000000000000000000090910217815561016290819061005a90396000f3606060405236156100405760e060020a60003504630221038a811461004d57806318bdc79a146100aa5780638da5cb5b146100be578063d2cc718f146100d0575b6100d96001805434019055565b6100db6004356024356000805433600160a060020a0390811691161415806100755750600034115b806100a05750805460a060020a900460ff1680156100a057508054600160a060020a03848116911614155b156100f757610002565b6100db60005460ff60a060020a9091041681565b6100ed600054600160a060020a031681565b6100db60015481565b005b60408051918252519081900360200190f35b6060908152602090f35b600160a060020a0383168260608381818185876185025a03f1925050501561015c57604080518381529051600160a060020a038516917f9735b0cb909f3d21d5c16bbcccd272d85fa11446f6d679f6ecb170d2dabfecfc919081900360200190a25060015b929150505660606040818152806101bc833960a090525160805160008054600160a060020a03191690921760a060020a60ff0219167401000000000000000000000000000000000000000090910217815561016290819061005a90396000f3606060405236156100405760e060020a60003504630221038a811461004d57806318bdc79a146100aa5780638da5cb5b146100be578063d2cc718f146100d0575b6100d96001805434019055565b6100db6004356024356000805433600160a060020a0390811691161415806100755750600034115b806100a05750805460a060020a900460ff1680156100a057508054600160a060020a03848116911614155b156100f757610002565b6100db60005460ff60a060020a9091041681565b6100ed600054600160a060020a031681565b6100db60015481565b005b60408051918252519081900360200190f35b6060908152602090f35b600160a060020a0383168260608381818185876185025a03f1925050501561015c57604080518381529051600160a060020a038516917f9735b0cb909f3d21d5c16bbcccd272d85fa11446f6d679f6ecb170d2dabfecfc919081900360200190a25060015b929150505660606040818152806101bc833960a090525160805160008054600160a060020a03191690921760a060020a60ff0219167401000000000000000000000000000000000000000090910217815561016290819061005a90396000f3606060405236156100405760e060020a60003504630221038a811461004d57806318bdc79a146100aa5780638da5cb5b146100be578063d2cc718f146100d0575b6100d96001805434019055565b6100db6004356024356000805433600160a060020a0390811691161415806100755750600034115b806100a05750805460a060020a900460ff1680156100a057508054600160a060020a03848116911614155b156100f757610002565b6100db60005460ff60a060020a9091041681565b6100ed600054600160a060020a031681565b6100db60015481565b005b60408051918252519081900360200190f35b6060908152602090f35b600160a060020a0383168260608381818185876185025a03f1925050501561015c57604080518381529051600160a060020a038516917f9735b0cb909f3d21d5c16bbcccd272d85fa11446f6d679f6ecb170d2dabfecfc919081900360200190a25060015b92915050566060604052361561020e5760e060020a6000350463013cf08b8114610247578063095ea7b3146102d05780630c3b7b96146103455780630e7082031461034e578063149acf9a1461036057806318160ddd146103725780631f2dc5ef1461037b57806321b5b8dd1461039b578063237e9492146103ad57806323b872dd1461040e5780632632bf2014610441578063341458081461047257806339d1f9081461047b5780634b6753bc146104935780634df6d6cc1461049c5780634e10c3ee146104b7578063590e1ae3146104ca578063612e45a3146104db578063643f7cdd1461057a578063674ed066146105925780636837ff1e1461059b57806370a08231146105e5578063749f98891461060b57806378524b2e1461062457806381f03fcb1461067e57806382661dc41461069657806382bf6464146106b75780638Line truncated
// Proxy contract to whitelist interaction with:
//
// contract Proxy {
// address _dao;
//
// function Proxy(address dao) {
// _dao = dao;
// }
// function() {
// if (msg.value > 0) {
// _dao.call.value(msg.value)();
// } else {
// _dao.call(bytes4(sha3("refund()")));
// }
// }
// }
var proxyDeployCode = common.FromHex("606060405260405160208060dd83395060806040525160008054600160a060020a031916821790555060a98060346000396000f360606040523615600a575b60486000341115604a576000805473ffffffffffffffffffffffffffffffffffffffff1690349060609081818185876185025a03f1925050505060a7565b005b600080547f590e1ae300000000000000000000000000000000000000000000000000000000606090815273ffffffffffffffffffffffffffffffffffffffff9091169163590e1ae3916064919060048183876161da5a03f1505050505b56")
// Tests that unannimous votes are handled correctly (i.e. the simple case).
func TestDAOForkUnanimousFail(t *testing.T) { testDAOUnanimousFork(t, false) }
func TestDAOForkUnanimousPass(t *testing.T) { testDAOUnanimousFork(t, true) }
func testDAOUnanimousFork(t *testing.T, pass bool) {
// Reduce the test size to avoid wasting too much time
defer func(old uint64) { ruptureBlock = old }(ruptureBlock)
ruptureBlock = 128
defer func(old int) { ruptureCacheLimit = old }(ruptureCacheLimit)
ruptureCacheLimit = 256
// Depending on the tested outcome, set a high or a low gas limit
defer func(old *big.Int) { params.GenesisGasLimit = old }(params.GenesisGasLimit)
defer func(old *big.Int) { params.TargetGasLimit = old }(params.TargetGasLimit)
if pass {
params.GenesisGasLimit = new(big.Int).Add(ruptureThreshold, big.NewInt(-10))
params.TargetGasLimit = new(big.Int).Add(ruptureThreshold, big.NewInt(-10))
} else {
params.GenesisGasLimit = new(big.Int).Add(ruptureThreshold, big.NewInt(10))
params.TargetGasLimit = new(big.Int).Add(ruptureThreshold, big.NewInt(10))
}
// Create a test chain with a DAO instance deployed and attempt to transact with it
var (
slockit, _ = crypto.HexToECDSA("49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee")
black, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
white, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a")
slockitAddr = crypto.PubkeyToAddress(slockit.PublicKey)
blackAddr = crypto.PubkeyToAddress(black.PublicKey)
whiteAddr = crypto.PubkeyToAddress(white.PublicKey)
creatorAddr common.Address
daoAddr common.Address
blackProxy common.Address
whiteProxy common.Address
db, _ = ethdb.NewMemDatabase()
)
genesis := WriteGenesisBlockForTesting(db,
GenesisAccount{slockitAddr, big.NewInt(1000000)},
GenesisAccount{blackAddr, big.NewInt(1000000)},
GenesisAccount{whiteAddr, big.NewInt(1000000)},
)
length := int(ruptureBlock) + 2*ruptureCacheLimit
chain, _ := GenerateChain(genesis, db, length, func(i int, gen *BlockGen) {
switch i {
case 0:
// Deploy the DAO creator in the first block
tx, _ := types.NewContractCreation(gen.TxNonce(slockitAddr), big.NewInt(1), params.GenesisGasLimit, new(big.Int), creatorDeployCode).SignECDSA(slockit)
gen.AddTx(tx)
creatorAddr = crypto.CreateAddress(slockitAddr, tx.Nonce())
case 1:
// Deploy the DAO in the second block
var args []byte
args = append(args, common.LeftPadBytes(slockitAddr.Bytes(), 32)...) // curator
args = append(args, common.LeftPadBytes(creatorAddr.Bytes(), 32)...) // daoCreator
args = append(args, common.LeftPadBytes([]byte{0}, 32)...) // proposalDeposit
args = append(args, common.LeftPadBytes([]byte{100}, 32)...) // minTokensToCreate
args = append(args, common.LeftPadBytes([]byte{100}, 32)...) // closingTime
args = append(args, common.LeftPadBytes([]byte{0}, 32)...) // privateCreation
tx, _ := types.NewContractCreation(gen.TxNonce(slockitAddr), big.NewInt(0), params.GenesisGasLimit, new(big.Int), append(daoDeployCode, args...)).SignECDSA(slockit)
gen.AddTx(tx)
daoAddr = crypto.CreateAddress(slockitAddr, tx.Nonce())
case 2:
// Deploy the black and white hat proxy contracts
tx, _ := types.NewContractCreation(gen.TxNonce(blackAddr), big.NewInt(1), big.NewInt(1000000), new(big.Int), append(proxyDeployCode, common.LeftPadBytes(daoAddr.Bytes(), 32)...)).SignECDSA(black)
gen.AddTx(tx)
blackProxy = crypto.CreateAddress(blackAddr, tx.Nonce())
tx, _ = types.NewContractCreation(gen.TxNonce(whiteAddr), big.NewInt(1), big.NewInt(1000000), new(big.Int), append(proxyDeployCode, common.LeftPadBytes(daoAddr.Bytes(), 32)...)).SignECDSA(white)
gen.AddTx(tx)
whiteProxy = crypto.CreateAddress(whiteAddr, tx.Nonce())
ruptureWhitelist[whiteProxy] = true
case 3:
// Fund the DAO with some funds, but not enough for successful creation
tx, _ := types.NewTransaction(gen.TxNonce(whiteAddr), whiteProxy, big.NewInt(2), big.NewInt(1000000), nil, nil).SignECDSA(white)
gen.AddTx(tx)
tx, _ = types.NewTransaction(gen.TxNonce(blackAddr), blackProxy, big.NewInt(2), big.NewInt(1000000), nil, nil).SignECDSA(black)
gen.AddTx(tx)
case length - 2:
// DAO creation failed by this time and forks are in effect; try retrieving whitelisted funds
tx, _ := types.NewTransaction(gen.TxNonce(whiteAddr), whiteProxy, big.NewInt(0), big.NewInt(1000000), nil, nil).SignECDSA(white)
gen.AddTx(tx)
case length - 1:
// DAO creation failed by this time and forks are in effect; try retrieving blacklisted funds
tx, _ := types.NewTransaction(gen.TxNonce(blackAddr), blackProxy, big.NewInt(0), big.NewInt(1000000), nil, nil).SignECDSA(black)
gen.AddTx(tx)
}
})
defer delete(ruptureWhitelist, whiteProxy) // This can't be done in the aboge generator sadly
// Import the chain. This runs all block validation rules.
bc, _ := NewBlockChain(db, MakeChainConfig(), FakePow{}, new(event.TypeMux))
n, err := bc.InsertChain(chain)
// The chain should either succeed (no votes), or fail on the black address
if !pass {
if err != nil {
t.Errorf("block %d rejected on non forked chain: %v", n, err)
}
} else {
if n != length-1 || err != blockedCodeHashErr {
t.Errorf("failure mismatch: have %d:%v, want %d:%v", n, err, length-1, blockedCodeHashErr)
}
}
// Verify account balances to make sure transactions executd properly, not just the test is bugged
state, _ := bc.State()
if state.GetBalance(whiteProxy).Cmp(big.NewInt(3)) != 0 { // 1 init + 2 refund
t.Errorf("white proxy balance mismatch: have %v, want 3", state.GetBalance(whiteProxy))
}
if !pass {
if state.GetBalance(blackProxy).Cmp(big.NewInt(3)) != 0 { // 1 init + 2 refund (fork refused)
t.Errorf("black proxy balance mismatch: have %v, want 3", state.GetBalance(blackProxy))
}
if state.GetBalance(daoAddr).Cmp(big.NewInt(0)) != 0 { // all refunded (fork refused)
t.Errorf("dao balance mismatch: have %v, want 0", state.GetBalance(daoAddr))
}
} else {
if state.GetBalance(blackProxy).Cmp(big.NewInt(1)) != 0 { // 1 init (refund refused due to fork)
t.Errorf("black proxy balance mismatch: have %v, want 1", state.GetBalance(blackProxy))
}
if state.GetBalance(daoAddr).Cmp(big.NewInt(2)) != 0 { // 2 from black (refund refused due to fork)
t.Errorf("dao balance mismatch: have %v, want 2", state.GetBalance(daoAddr))
}
}
}
// Tests the unique corner case where miners don't cleanly decide on the outcome
// of the vote, rather
func TestDAOForkRaceyResolution(t *testing.T) {
// Reduce the test size to avoid wasting too much time
defer func(old uint64) { ruptureBlock = old }(ruptureBlock)
ruptureBlock = 128
defer func(old int) { ruptureCacheLimit = old }(ruptureCacheLimit)
ruptureCacheLimit = 256
// Set up the parameters of the test chain
var (
slockit, _ = crypto.HexToECDSA("49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee")
black, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
white, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a")
slockitAddr = crypto.PubkeyToAddress(slockit.PublicKey)
blackAddr = crypto.PubkeyToAddress(black.PublicKey)
whiteAddr = crypto.PubkeyToAddress(white.PublicKey)
creatorAddr common.Address
daoAddr common.Address
blackProxy common.Address
whiteProxy common.Address
db, _ = ethdb.NewMemDatabase()
)
defer func(old *big.Int) { params.GenesisGasLimit = old }(params.GenesisGasLimit)
params.GenesisGasLimit = ruptureThreshold
genesis := WriteGenesisBlockForTesting(db,
GenesisAccount{slockitAddr, big.NewInt(1000000)},
GenesisAccount{blackAddr, big.NewInt(1000000)},
GenesisAccount{whiteAddr, big.NewInt(1000000)},
)
// Generate a chain where the outcome for the fork proposal is pass
defer func(old *big.Int) { params.TargetGasLimit = old }(params.TargetGasLimit)
params.TargetGasLimit = new(big.Int).Add(ruptureThreshold, big.NewInt(-10))
length := int(ruptureBlock) + 2*ruptureCacheLimit
chainPass, _ := GenerateChain(genesis, db, length, func(i int, gen *BlockGen) {
switch i {
case 0:
// Deploy the DAO creator in the first block
tx, _ := types.NewContractCreation(gen.TxNonce(slockitAddr), big.NewInt(1), big.NewInt(3999980), new(big.Int), creatorDeployCode).SignECDSA(slockit)
gen.AddTx(tx)
creatorAddr = crypto.CreateAddress(slockitAddr, tx.Nonce())
case 1:
// Deploy the DAO in the second block
var args []byte
args = append(args, common.LeftPadBytes(slockitAddr.Bytes(), 32)...) // curator
args = append(args, common.LeftPadBytes(creatorAddr.Bytes(), 32)...) // daoCreator
args = append(args, common.LeftPadBytes([]byte{0}, 32)...) // proposalDeposit
args = append(args, common.LeftPadBytes([]byte{100}, 32)...) // minTokensToCreate
args = append(args, common.LeftPadBytes([]byte{100}, 32)...) // closingTime
args = append(args, common.LeftPadBytes([]byte{0}, 32)...) // privateCreation
tx, _ := types.NewContractCreation(gen.TxNonce(slockitAddr), big.NewInt(0), big.NewInt(3999980), new(big.Int), append(daoDeployCode, args...)).SignECDSA(slockit)
gen.AddTx(tx)
daoAddr = crypto.CreateAddress(slockitAddr, tx.Nonce())
case 2:
// Deploy the black and white hat proxy contracts
tx, _ := types.NewContractCreation(gen.TxNonce(blackAddr), big.NewInt(1), big.NewInt(1000000), new(big.Int), append(proxyDeployCode, common.LeftPadBytes(daoAddr.Bytes(), 32)...)).SignECDSA(black)
gen.AddTx(tx)
blackProxy = crypto.CreateAddress(blackAddr, tx.Nonce())
tx, _ = types.NewContractCreation(gen.TxNonce(whiteAddr), big.NewInt(1), big.NewInt(1000000), new(big.Int), append(proxyDeployCode, common.LeftPadBytes(daoAddr.Bytes(), 32)...)).SignECDSA(white)
gen.AddTx(tx)
whiteProxy = crypto.CreateAddress(whiteAddr, tx.Nonce())
ruptureWhitelist[whiteProxy] = true
case 3:
// Fund the DAO with some funds, but not enough for successful creation
tx, _ := types.NewTransaction(gen.TxNonce(whiteAddr), whiteProxy, big.NewInt(2), big.NewInt(1000000), nil, nil).SignECDSA(white)
gen.AddTx(tx)
tx, _ = types.NewTransaction(gen.TxNonce(blackAddr), blackProxy, big.NewInt(2), big.NewInt(1000000), nil, nil).SignECDSA(black)
gen.AddTx(tx)
case length - 2:
// DAO creation failed by this time and forks are in effect; try retrieving whitelisted funds
tx, _ := types.NewTransaction(gen.TxNonce(whiteAddr), whiteProxy, big.NewInt(0), big.NewInt(1000000), nil, nil).SignECDSA(white)
gen.AddTx(tx)
case length - 1:
// DAO creation failed by this time and forks are in effect; try retrieving blacklisted funds
tx, _ := types.NewTransaction(gen.TxNonce(blackAddr), blackProxy, big.NewInt(0), big.NewInt(1000000), nil, nil).SignECDSA(black)
gen.AddTx(tx)
}
})
// Generate a chain to work around the last bad block above
chainPassCont, _ := GenerateChain(chainPass[len(chainPass)-2], db, 3, func(i int, gen *BlockGen) {})
// Generate a fork of the chain where the outcome of the fork proposal is fail
params.TargetGasLimit = new(big.Int).Add(ruptureThreshold, big.NewInt(10))
chainFail, _ := GenerateChain(chainPass[4], db, length-4, func(i int, gen *BlockGen) { // Start fro block #4 to reuse previous creation code
switch i {
case length - 4 - 2:
// DAO creation failed by this time and forks are in effect; try retrieving whitelisted funds
tx, _ := types.NewTransaction(gen.TxNonce(whiteAddr), whiteProxy, big.NewInt(0), big.NewInt(1000000), nil, nil).SignECDSA(white)
gen.AddTx(tx)
case length - 4 - 1:
// DAO creation failed by this time and forks are in effect; try retrieving blacklisted funds
tx, _ := types.NewTransaction(gen.TxNonce(blackAddr), blackProxy, big.NewInt(0), big.NewInt(1000000), nil, nil).SignECDSA(black)
gen.AddTx(tx)
}
})
defer delete(ruptureWhitelist, whiteProxy) // This can't be done in the aboge generator sadly
// Import the chain where the vote passed
bc, _ := NewBlockChain(db, MakeChainConfig(), FakePow{}, new(event.TypeMux))
if n, err := bc.InsertChain(chainPass); n != length-1 || err != blockedCodeHashErr {
t.Fatalf("forked chain error mismatch: have %v/%v, want %v/%v", n, err, length-1, blockedCodeHashErr)
}
state, _ := bc.State()
if state.GetBalance(whiteProxy).Cmp(big.NewInt(3)) != 0 { // 1 init + 2 refund
t.Errorf("white proxy balance mismatch: have %v, want 3", state.GetBalance(whiteProxy))
}
if state.GetBalance(blackProxy).Cmp(big.NewInt(1)) != 0 { // 1 init (no refund on the passing chain)
t.Errorf("black proxy balance mismatch: have %v, want 1", state.GetBalance(blackProxy))
}
if state.GetBalance(daoAddr).Cmp(big.NewInt(2)) != 0 { // 2 from black (refund refused due to fork)
t.Errorf("dao balance mismatch: have %v, want 2", state.GetBalance(daoAddr))
}
// Import the chain where the vote failed (longer than the passing with 1 block (dropped from the passing))
if _, err := bc.InsertChain(chainFail); err != nil {
t.Fatalf("failed to import non-forked chain: %v", err)
}
state, _ = bc.State()
if state.GetBalance(whiteProxy).Cmp(big.NewInt(3)) != 0 { // 1 init + 2 refund
t.Errorf("white proxy balance mismatch: have %v, want 3", state.GetBalance(whiteProxy))
}
if state.GetBalance(blackProxy).Cmp(big.NewInt(3)) != 0 { // 1 init + 2 refund (fork refused)
t.Errorf("black proxy balance mismatch: have %v, want 3", state.GetBalance(blackProxy))
}
if state.GetBalance(daoAddr).Cmp(big.NewInt(0)) != 0 { // all refunded (fork refused)
t.Errorf("dao balance mismatch: have %v, want 0", state.GetBalance(daoAddr))
}
// Try to import a bad block on theforked chain to make sure no cache screwes up
if _, err := bc.InsertChain(chainPass[len(chainPass)-2:]); err != blockedCodeHashErr {
t.Fatalf("forked chain error mismatch: have %v, want %v", err, blockedCodeHashErr)
}
// The balances must still be that of the failed chain
state, _ = bc.State()
if state.GetBalance(whiteProxy).Cmp(big.NewInt(3)) != 0 { // 1 init + 2 refund
t.Errorf("white proxy balance mismatch: have %v, want 3", state.GetBalance(whiteProxy))
}
if state.GetBalance(blackProxy).Cmp(big.NewInt(3)) != 0 { // 1 init + 2 refund (fork refused)
t.Errorf("black proxy balance mismatch: have %v, want 3", state.GetBalance(blackProxy))
}
if state.GetBalance(daoAddr).Cmp(big.NewInt(0)) != 0 { // all refunded (fork refused)
t.Errorf("dao balance mismatch: have %v, want 0", state.GetBalance(daoAddr))
}
// Overrule the passing chain with fresh blocks
if _, err := bc.InsertChain(chainPassCont); err != nil {
t.Fatalf("failed to import forked chain continuation: %v", err)
}
// Balances must be switched over to the forked version
state, _ = bc.State()
if state.GetBalance(whiteProxy).Cmp(big.NewInt(3)) != 0 { // 1 init + 2 refund
t.Errorf("white proxy balance mismatch: have %v, want 3", state.GetBalance(whiteProxy))
}
if state.GetBalance(blackProxy).Cmp(big.NewInt(1)) != 0 { // 1 init (no refund on the passing chain)
t.Errorf("black proxy balance mismatch: have %v, want 1", state.GetBalance(blackProxy))
}
if state.GetBalance(daoAddr).Cmp(big.NewInt(2)) != 0 { // 2 from black (refund refused due to fork)
t.Errorf("dao balance mismatch: have %v, want 2", state.GetBalance(daoAddr))
}
}
+3
View File
@@ -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
+61 -10
View File
@@ -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)
+7 -3
View File
@@ -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 }