From f0cbebb19f3137ee3ba0e66dadd1b5b9dbf98b1c Mon Sep 17 00:00:00 2001 From: Jeffrey Wilcke Date: Tue, 1 Mar 2016 23:32:43 +0100 Subject: core: added basic chain configuration Added chain configuration options and write out during genesis database insertion. If no "config" was found, nothing is written to the database. Configurations are written on a per genesis base. This means that any chain (which is identified by it's genesis hash) can have their own chain settings. --- accounts/abi/bind/backends/simulated.go | 11 +- cmd/ethtest/main.go | 5 +- cmd/evm/main.go | 19 +-- cmd/geth/js_test.go | 1 + cmd/geth/main.go | 42 +++++- cmd/geth/usage.go | 1 + cmd/utils/flags.go | 72 +++++++--- common/debug.go | 16 +++ common/registrar/ethreg/api.go | 20 ++- core/bench_test.go | 2 +- core/block_validator.go | 32 ++--- core/block_validator_test.go | 12 +- core/blockchain.go | 25 ++-- core/blockchain_test.go | 35 ++--- core/chain_makers.go | 27 +++- core/chain_makers_test.go | 2 +- core/config.go | 46 +++++++ core/database_util.go | 34 +++++ core/execution.go | 2 +- core/genesis.go | 23 ++-- core/headerchain.go | 19 ++- core/state_processor.go | 23 +++- core/state_transition.go | 4 +- core/tx_pool.go | 7 +- core/tx_pool_test.go | 2 +- core/types.go | 2 +- core/vm/environment.go | 12 +- core/vm/instructions.go | 2 +- core/vm/jit.go | 23 ++-- core/vm/jit_test.go | 15 ++- core/vm/jump_table.go | 10 +- core/vm/jump_table_test.go | 8 +- core/vm/logger.go | 2 +- core/vm/logger_test.go | 10 +- core/vm/runtime/env.go | 9 +- core/vm/runtime/runtime.go | 11 ++ core/vm/util_test.go | 9 ++ core/vm/vm.go | 15 +-- core/vm/vm_jit_fake.go | 2 +- core/vm_env.go | 29 ++-- eth/api.go | 24 ++-- eth/backend.go | 42 ++++-- eth/handler.go | 4 +- eth/helper_test.go | 7 +- miner/miner.go | 4 +- miner/worker.go | 13 +- params/protocol_params.go | 5 +- params/util.go | 13 +- tests/block_test.go | 80 ++++------- tests/block_test_util.go | 15 ++- tests/state_test.go | 227 +++++++++++++++++++------------- tests/state_test_util.go | 36 ++--- tests/transaction_test_util.go | 7 +- tests/util.go | 34 ++++- tests/vm_test_util.go | 11 +- 55 files changed, 732 insertions(+), 431 deletions(-) create mode 100644 core/config.go create mode 100644 core/vm/util_test.go diff --git a/accounts/abi/bind/backends/simulated.go b/accounts/abi/bind/backends/simulated.go index 18e8481c5..43d490988 100644 --- a/accounts/abi/bind/backends/simulated.go +++ b/accounts/abi/bind/backends/simulated.go @@ -24,10 +24,15 @@ import ( "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/params" ) +// Default chain configuration which sets homestead phase at block 0 (i.e. no frontier) +var chainConfig = &core.ChainConfig{HomesteadBlock: params.MainNetHomesteadBlock} + // This nil assignment ensures compile time that SimulatedBackend implements bind.ContractBackend. var _ bind.ContractBackend = (*SimulatedBackend)(nil) @@ -46,7 +51,7 @@ type SimulatedBackend struct { func NewSimulatedBackend(accounts ...core.GenesisAccount) *SimulatedBackend { database, _ := ethdb.NewMemDatabase() core.WriteGenesisBlockForTesting(database, accounts...) - blockchain, _ := core.NewBlockChain(database, new(core.FakePow), new(event.TypeMux)) + blockchain, _ := core.NewBlockChain(database, chainConfig, new(core.FakePow), new(event.TypeMux)) backend := &SimulatedBackend{ database: database, @@ -102,7 +107,7 @@ func (b *SimulatedBackend) ContractCall(contract common.Address, data []byte, pe data: data, } // Execute the call and return - vmenv := core.NewEnv(statedb, b.blockchain, msg, block.Header(), nil) + vmenv := core.NewEnv(statedb, chainConfig, b.blockchain, msg, block.Header(), vm.Config{}) gaspool := new(core.GasPool).AddGas(common.MaxBig) out, _, err := core.ApplyMessage(vmenv, msg, gaspool) @@ -145,7 +150,7 @@ func (b *SimulatedBackend) EstimateGasLimit(sender common.Address, contract *com data: data, } // Execute the call and return - vmenv := core.NewEnv(statedb, b.blockchain, msg, block.Header(), nil) + vmenv := core.NewEnv(statedb, chainConfig, b.blockchain, msg, block.Header(), vm.Config{}) gaspool := new(core.GasPool).AddGas(common.MaxBig) _, gas, err := core.ApplyMessage(vmenv, msg, gaspool) diff --git a/cmd/ethtest/main.go b/cmd/ethtest/main.go index e19dca86b..3916e297f 100644 --- a/cmd/ethtest/main.go +++ b/cmd/ethtest/main.go @@ -27,6 +27,7 @@ import ( "github.com/codegangsta/cli" "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/tests" ) @@ -73,9 +74,9 @@ func runTestWithReader(test string, r io.Reader) error { var err error switch strings.ToLower(test) { case "bk", "block", "blocktest", "blockchaintest", "blocktests", "blockchaintests": - err = tests.RunBlockTestWithReader(r, skipTests) + err = tests.RunBlockTestWithReader(params.MainNetHomesteadBlock, r, skipTests) case "st", "state", "statetest", "statetests": - err = tests.RunStateTestWithReader(r, skipTests) + err = tests.RunStateTestWithReader(tests.RuleSet{params.MainNetHomesteadBlock}, r, skipTests) case "tx", "transactiontest", "transactiontests": err = tests.RunTransactionTestsWithReader(r, skipTests) case "vm", "vmtest", "vmtests": diff --git a/cmd/evm/main.go b/cmd/evm/main.go index 2cc70d81b..7d9b3a6c3 100644 --- a/cmd/evm/main.go +++ b/cmd/evm/main.go @@ -33,7 +33,6 @@ import ( "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/logger/glog" - "github.com/ethereum/go-ethereum/params" ) var ( @@ -106,9 +105,6 @@ func init() { } func run(ctx *cli.Context) { - vm.ForceJit = ctx.GlobalBool(ForceJitFlag.Name) - vm.EnableJit = !ctx.GlobalBool(DisableJitFlag.Name) - glog.SetToStderr(true) glog.SetV(ctx.GlobalInt(VerbosityFlag.Name)) @@ -118,8 +114,10 @@ func run(ctx *cli.Context) { receiver := statedb.CreateAccount(common.StringToAddress("receiver")) receiver.SetCode(common.Hex2Bytes(ctx.GlobalString(CodeFlag.Name))) - vmenv := NewEnv(statedb, common.StringToAddress("evmuser"), common.Big(ctx.GlobalString(ValueFlag.Name)), &vm.Config{ - Debug: ctx.GlobalBool(DebugFlag.Name), + vmenv := NewEnv(statedb, common.StringToAddress("evmuser"), common.Big(ctx.GlobalString(ValueFlag.Name)), vm.Config{ + Debug: ctx.GlobalBool(DebugFlag.Name), + ForceJit: ctx.GlobalBool(ForceJitFlag.Name), + EnableJit: !ctx.GlobalBool(DisableJitFlag.Name), }) tstart := time.Now() @@ -180,8 +178,7 @@ type VMEnv struct { evm *vm.EVM } -func NewEnv(state *state.StateDB, transactor common.Address, value *big.Int, cfg *vm.Config) *VMEnv { - params.HomesteadBlock = new(big.Int) +func NewEnv(state *state.StateDB, transactor common.Address, value *big.Int, cfg vm.Config) *VMEnv { env := &VMEnv{ state: state, transactor: &transactor, @@ -194,6 +191,12 @@ func NewEnv(state *state.StateDB, transactor common.Address, value *big.Int, cfg return env } +// ruleSet implements vm.RuleSet and will always default to the homestead rule set. +type ruleSet struct{} + +func (ruleSet) IsHomestead(*big.Int) bool { return true } + +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 } func (self *VMEnv) MakeSnapshot() vm.Database { return self.state.Copy() } diff --git a/cmd/geth/js_test.go b/cmd/geth/js_test.go index af435e68c..e0c4dacbc 100644 --- a/cmd/geth/js_test.go +++ b/cmd/geth/js_test.go @@ -106,6 +106,7 @@ func testREPL(t *testing.T, config func(*eth.Config)) (string, *testjethre, *nod core.WriteGenesisBlockForTesting(db, core.GenesisAccount{common.HexToAddress(testAddress), common.String2Big(testBalance)}) ethConf := ð.Config{ + ChainConfig: &core.ChainConfig{HomesteadBlock: new(big.Int)}, TestGenesisState: db, AccountManager: accman, DocRoot: "/", diff --git a/cmd/geth/main.go b/cmd/geth/main.go index a21fe71b5..5d5ab4559 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -32,7 +32,9 @@ import ( "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/eth" + "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/internal/debug" "github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger/glog" @@ -108,7 +110,6 @@ Runs quick benchmark on first GPU found. The output of this command is supposed to be machine-readable. `, }, - { Name: "wallet", Usage: "ethereum presale wallet", @@ -247,6 +248,16 @@ nodes. }, }, }, + { + Action: initGenesis, + Name: "init", + Usage: "bootstraps and initialises a new genesis block (JSON)", + Description: ` +The init command initialises a new genesis block and definition for the network. +This is a destructive action and changes the network in which you will be +participating. +`, + }, { Action: console, Name: "console", @@ -255,7 +266,8 @@ nodes. The Geth console is an interactive shell for the JavaScript runtime environment which exposes a node admin interface as well as the Ðapp JavaScript API. See https://github.com/ethereum/go-ethereum/wiki/Javascipt-Console -`}, +`, + }, { Action: attach, Name: "attach", @@ -347,7 +359,6 @@ JavaScript API. See https://github.com/ethereum/go-ethereum/wiki/Javascipt-Conso go metrics.CollectProcessMetrics(3 * time.Second) utils.SetupNetwork(ctx) - utils.SetupVM(ctx) return nil } @@ -417,6 +428,31 @@ func attach(ctx *cli.Context) { } } +// initGenesis will initialise the given JSON format genesis file and writes it as +// the zero'd block (i.e. genesis) or will fail hard if it can't succeed. +func initGenesis(ctx *cli.Context) { + genesisPath := ctx.Args().First() + if len(genesisPath) == 0 { + utils.Fatalf("must supply path to genesis JSON file") + } + + chainDb, err := ethdb.NewLDBDatabase(filepath.Join(utils.MustMakeDataDir(ctx), "chaindata"), 0, 0) + if err != nil { + utils.Fatalf("could not open database: %v", err) + } + + genesisFile, err := os.Open(genesisPath) + if err != nil { + utils.Fatalf("failed to read genesis file: %v", err) + } + + block, err := core.WriteGenesisBlock(chainDb, genesisFile) + if err != nil { + utils.Fatalf("failed to write genesis block: %v", err) + } + glog.V(logger.Info).Infof("successfully wrote genesis block and/or chain rule set: %x", block.Hash()) +} + // console starts a new geth node, attaching a JavaScript console to it at the // same time. func console(ctx *cli.Context) { diff --git a/cmd/geth/usage.go b/cmd/geth/usage.go index 55daa63d7..a31532bea 100644 --- a/cmd/geth/usage.go +++ b/cmd/geth/usage.go @@ -121,6 +121,7 @@ var AppHelpFlagGroups = []flagGroup{ Flags: []cli.Flag{ utils.MiningEnabledFlag, utils.MinerThreadsFlag, + utils.TargetGasLimitFlag, utils.MiningGPUFlag, utils.AutoDAGFlag, utils.EtherbaseFlag, diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index a00466d0a..3f54b40ca 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -34,7 +34,6 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/state" - "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/ethdb" @@ -173,6 +172,11 @@ var ( Name: "minergpus", Usage: "List of GPUs to use for mining (e.g. '0,1' will use the first two GPUs found)", } + TargetGasLimitFlag = cli.StringFlag{ + Name: "targetgaslimit", + Usage: "Target gas limit sets the artificial target gas floor for the blocks to mine", + Value: params.GenesisGasLimit.String(), + } AutoDAGFlag = cli.BoolFlag{ Name: "autodag", Usage: "Enable automatic DAG pregeneration", @@ -656,6 +660,7 @@ func MakeSystemNode(name, version string, extra []byte, ctx *cli.Context) *node. accman := MakeAccountManager(ctx) ethConf := ð.Config{ + ChainConfig: MustMakeChainConfig(ctx), Genesis: MakeGenesisBlock(ctx), FastSync: ctx.GlobalBool(FastSyncFlag.Name), BlockChainVersion: ctx.GlobalInt(BlockchainVersionFlag.Name), @@ -701,8 +706,6 @@ func MakeSystemNode(name, version string, extra []byte, ctx *cli.Context) *node. ethConf.Genesis = core.TestNetGenesisBlock() } state.StartingNonce = 1048576 // (2**20) - // overwrite homestead block - params.HomesteadBlock = params.TestNetHomesteadBlock case ctx.GlobalBool(DevModeFlag.Name): // Override the base network stack configs @@ -758,25 +761,56 @@ func SetupNetwork(ctx *cli.Context) { core.BlockReward = big.NewInt(1.5e+18) core.ExpDiffPeriod = big.NewInt(math.MaxInt64) } + params.TargetGasLimit = common.String2Big(ctx.GlobalString(TargetGasLimitFlag.Name)) } -// SetupVM configured the VM package's global settings -func SetupVM(ctx *cli.Context) { - vm.EnableJit = ctx.GlobalBool(VMEnableJitFlag.Name) - vm.ForceJit = ctx.GlobalBool(VMForceJitFlag.Name) - vm.SetJITCacheSize(ctx.GlobalInt(VMJitCacheFlag.Name)) +// MustMakeChainConfig reads the chain configuration from the given database. +func MustMakeChainConfig(ctx *cli.Context) *core.ChainConfig { + var ( + db = MakeChainDatabase(ctx) + genesis = core.GetBlock(db, core.GetCanonicalHash(db, 0)) + ) + defer db.Close() + + chainConfig, err := core.GetChainConfig(db, genesis.Hash()) + if err != nil { + if err != core.ChainConfigNotFoundErr { + Fatalf("Could not make chain configuration: %v", err) + } + var homesteadBlockNo *big.Int + if ctx.GlobalBool(TestNetFlag.Name) { + homesteadBlockNo = params.TestNetHomesteadBlock + } else { + homesteadBlockNo = params.MainNetHomesteadBlock + } + + chainConfig = &core.ChainConfig{ + HomesteadBlock: homesteadBlockNo, + } + } + return chainConfig } -// MakeChain creates a chain manager from set command line flags. -func MakeChain(ctx *cli.Context) (chain *core.BlockChain, chainDb ethdb.Database) { - datadir := MustMakeDataDir(ctx) - cache := ctx.GlobalInt(CacheFlag.Name) - handles := MakeDatabaseHandles() +// MakeChainDatabase open an LevelDB using the flags passed to the client and will hard crash if it fails. +func MakeChainDatabase(ctx *cli.Context) ethdb.Database { + var ( + datadir = MustMakeDataDir(ctx) + cache = ctx.GlobalInt(CacheFlag.Name) + handles = MakeDatabaseHandles() + ) - var err error - if chainDb, err = ethdb.NewLDBDatabase(filepath.Join(datadir, "chaindata"), cache, handles); err != nil { + chainDb, err := ethdb.NewLDBDatabase(filepath.Join(datadir, "chaindata"), cache, handles) + if err != nil { Fatalf("Could not open database: %v", err) } + return chainDb +} + +// MakeChain creates a chain manager from set command line flags. +func MakeChain(ctx *cli.Context) (chain *core.BlockChain, chainDb ethdb.Database) { + var err error + chainDb = MakeChainDatabase(ctx) + if ctx.GlobalBool(OlympicFlag.Name) { _, err := core.WriteTestNetGenesisBlock(chainDb) if err != nil { @@ -784,10 +818,10 @@ func MakeChain(ctx *cli.Context) (chain *core.BlockChain, chainDb ethdb.Database } } - eventMux := new(event.TypeMux) - pow := ethash.New() - //genesis := core.GenesisBlock(uint64(ctx.GlobalInt(GenesisNonceFlag.Name)), blockDB) - chain, err = core.NewBlockChain(chainDb, pow, eventMux) + chainConfig := MustMakeChainConfig(ctx) + + var eventMux event.TypeMux + chain, err = core.NewBlockChain(chainDb, chainConfig, ethash.New(), &eventMux) if err != nil { Fatalf("Could not start chainmanager: %v", err) } diff --git a/common/debug.go b/common/debug.go index fa93d7bda..61acd8ce7 100644 --- a/common/debug.go +++ b/common/debug.go @@ -21,8 +21,10 @@ import ( "os" "runtime" "runtime/debug" + "strings" ) +// Report gives off a warning requesting the user to submit an issue to the github tracker. func Report(extra ...interface{}) { fmt.Fprintln(os.Stderr, "You've encountered a sought after, hard to reproduce bug. Please report this to the developers <3 https://github.com/ethereum/go-ethereum/issues") fmt.Fprintln(os.Stderr, extra...) @@ -34,3 +36,17 @@ func Report(extra ...interface{}) { fmt.Fprintln(os.Stderr, "#### BUG! PLEASE REPORT ####") } + +// PrintDepricationWarning prinst the given string in a box using fmt.Println. +func PrintDepricationWarning(str string) { + line := strings.Repeat("#", len(str)+4) + emptyLine := strings.Repeat(" ", len(str)) + fmt.Printf(` +%s +# %s # +# %s # +# %s # +%s + +`, line, emptyLine, str, emptyLine, line) +} diff --git a/common/registrar/ethreg/api.go b/common/registrar/ethreg/api.go index 60a97f4ce..d035616f2 100644 --- a/common/registrar/ethreg/api.go +++ b/common/registrar/ethreg/api.go @@ -27,6 +27,7 @@ import ( "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/logger" @@ -35,6 +36,7 @@ import ( // registryAPIBackend is a backend for an Ethereum Registry. type registryAPIBackend struct { + config *core.ChainConfig bc *core.BlockChain chainDb ethdb.Database txPool *core.TxPool @@ -43,12 +45,22 @@ type registryAPIBackend struct { // PrivateRegistarAPI offers various functions to access the Ethereum registry. type PrivateRegistarAPI struct { - be *registryAPIBackend + config *core.ChainConfig + be *registryAPIBackend } // NewPrivateRegistarAPI creates a new PrivateRegistarAPI instance. -func NewPrivateRegistarAPI(bc *core.BlockChain, chainDb ethdb.Database, txPool *core.TxPool, am *accounts.Manager) *PrivateRegistarAPI { - return &PrivateRegistarAPI{®istryAPIBackend{bc, chainDb, txPool, am}} +func NewPrivateRegistarAPI(config *core.ChainConfig, bc *core.BlockChain, chainDb ethdb.Database, txPool *core.TxPool, am *accounts.Manager) *PrivateRegistarAPI { + return &PrivateRegistarAPI{ + config: config, + be: ®istryAPIBackend{ + config: config, + bc: bc, + chainDb: chainDb, + txPool: txPool, + am: am, + }, + } } // SetGlobalRegistrar allows clients to set the global registry for the node. @@ -179,7 +191,7 @@ func (be *registryAPIBackend) Call(fromStr, toStr, valueStr, gasStr, gasPriceStr } header := be.bc.CurrentBlock().Header() - vmenv := core.NewEnv(statedb, be.bc, msg, header, nil) + vmenv := core.NewEnv(statedb, be.config, be.bc, msg, header, vm.Config{}) gp := new(core.GasPool).AddGas(common.MaxBig) res, gas, err := core.ApplyMessage(vmenv, msg, gp) diff --git a/core/bench_test.go b/core/bench_test.go index 0ff847ed5..ac5b57bc8 100644 --- a/core/bench_test.go +++ b/core/bench_test.go @@ -168,7 +168,7 @@ func benchInsertChain(b *testing.B, disk bool, gen func(int, *BlockGen)) { // Time the insertion of the new chain. // State and blocks are stored in the same DB. evmux := new(event.TypeMux) - chainman, _ := NewBlockChain(db, FakePow{}, evmux) + chainman, _ := NewBlockChain(db, &ChainConfig{HomesteadBlock: new(big.Int)}, FakePow{}, evmux) defer chainman.Stop() b.ReportAllocs() b.ResetTimer() diff --git a/core/block_validator.go b/core/block_validator.go index 4d710ae3f..747e61ccc 100644 --- a/core/block_validator.go +++ b/core/block_validator.go @@ -41,15 +41,17 @@ var ( // // BlockValidator implements Validator. type BlockValidator struct { - bc *BlockChain // Canonical block chain - Pow pow.PoW // Proof of work used for validating + config *ChainConfig // Chain configuration options + bc *BlockChain // Canonical block chain + Pow pow.PoW // Proof of work used for validating } // NewBlockValidator returns a new block validator which is safe for re-use -func NewBlockValidator(blockchain *BlockChain, pow pow.PoW) *BlockValidator { +func NewBlockValidator(config *ChainConfig, blockchain *BlockChain, pow pow.PoW) *BlockValidator { validator := &BlockValidator{ - Pow: pow, - bc: blockchain, + config: config, + Pow: pow, + bc: blockchain, } return validator } @@ -80,7 +82,7 @@ func (v *BlockValidator) ValidateBlock(block *types.Block) error { header := block.Header() // validate the block header - if err := ValidateHeader(v.Pow, header, parent.Header(), false, false); err != nil { + if err := ValidateHeader(v.config, v.Pow, header, parent.Header(), false, false); err != nil { return err } // verify the uncles are correctly rewarded @@ -175,7 +177,7 @@ func (v *BlockValidator) VerifyUncles(block, parent *types.Block) error { return UncleError("uncle[%d](%x)'s parent is not ancestor (%x)", i, hash[:4], uncle.ParentHash[0:4]) } - if err := ValidateHeader(v.Pow, uncle, ancestors[uncle.ParentHash].Header(), true, true); err != nil { + if err := ValidateHeader(v.config, v.Pow, uncle, ancestors[uncle.ParentHash].Header(), true, true); err != nil { return ValidationError(fmt.Sprintf("uncle[%d](%x) header invalid: %v", i, hash[:4], err)) } } @@ -195,13 +197,13 @@ func (v *BlockValidator) ValidateHeader(header, parent *types.Header, checkPow b if v.bc.HasHeader(header.Hash()) { return nil } - return ValidateHeader(v.Pow, header, parent, checkPow, false) + return ValidateHeader(v.config, v.Pow, header, parent, checkPow, false) } // Validates a header. Returns an error if the header is invalid. // // See YP section 4.3.4. "Block Header Validity" -func ValidateHeader(pow pow.PoW, header *types.Header, parent *types.Header, checkPow, uncle bool) error { +func ValidateHeader(config *ChainConfig, pow pow.PoW, header *types.Header, parent *types.Header, checkPow, uncle bool) error { if big.NewInt(int64(len(header.Extra))).Cmp(params.MaximumExtraDataSize) == 1 { return fmt.Errorf("Header extra data too long (%d)", len(header.Extra)) } @@ -219,7 +221,7 @@ func ValidateHeader(pow pow.PoW, header *types.Header, parent *types.Header, che return BlockEqualTSErr } - expd := CalcDifficulty(header.Time.Uint64(), parent.Time.Uint64(), parent.Number, parent.Difficulty) + expd := CalcDifficulty(config, header.Time.Uint64(), parent.Time.Uint64(), parent.Number, parent.Difficulty) if expd.Cmp(header.Difficulty) != 0 { return fmt.Errorf("Difficulty check failed for header %v, %v", header.Difficulty, expd) } @@ -251,8 +253,8 @@ func ValidateHeader(pow pow.PoW, header *types.Header, parent *types.Header, che // CalcDifficulty is the difficulty adjustment algorithm. It returns // the difficulty that a new block should have when created at time // given the parent block's time and difficulty. -func CalcDifficulty(time, parentTime uint64, parentNumber, parentDiff *big.Int) *big.Int { - if params.IsHomestead(new(big.Int).Add(parentNumber, common.Big1)) { +func CalcDifficulty(config *ChainConfig, time, parentTime uint64, parentNumber, parentDiff *big.Int) *big.Int { + if config.IsHomestead(new(big.Int).Add(parentNumber, common.Big1)) { return calcDifficultyHomestead(time, parentTime, parentNumber, parentDiff) } else { return calcDifficultyFrontier(time, parentTime, parentNumber, parentDiff) @@ -363,11 +365,11 @@ func CalcGasLimit(parent *types.Block) *big.Int { gl = gl.Add(gl, contrib) gl.Set(common.BigMax(gl, params.MinGasLimit)) - // however, if we're now below the target (GenesisGasLimit) we increase the + // however, if we're now below the target (TargetGasLimit) we increase the // limit as much as we can (parentGasLimit / 1024 -1) - if gl.Cmp(params.GenesisGasLimit) < 0 { + if gl.Cmp(params.TargetGasLimit) < 0 { gl.Add(parent.GasLimit(), decay) - gl.Set(common.BigMin(gl, params.GenesisGasLimit)) + gl.Set(common.BigMin(gl, params.TargetGasLimit)) } return gl } diff --git a/core/block_validator_test.go b/core/block_validator_test.go index 2c4a97b45..aa29717b1 100644 --- a/core/block_validator_test.go +++ b/core/block_validator_test.go @@ -27,15 +27,20 @@ import ( "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/pow/ezp" ) +func testChainConfig() *ChainConfig { + return &ChainConfig{HomesteadBlock: params.MainNetHomesteadBlock} +} + func proc() (Validator, *BlockChain) { db, _ := ethdb.NewMemDatabase() var mux event.TypeMux WriteTestNetGenesisBlock(db) - blockchain, err := NewBlockChain(db, thePow(), &mux) + blockchain, err := NewBlockChain(db, testChainConfig(), thePow(), &mux) if err != nil { fmt.Println(err) } @@ -49,13 +54,14 @@ func TestNumber(t *testing.T) { statedb, _ := state.New(chain.Genesis().Root(), chain.chainDb) header := makeHeader(chain.Genesis(), statedb) header.Number = big.NewInt(3) - err := ValidateHeader(pow, header, chain.Genesis().Header(), false, false) + cfg := testChainConfig() + err := ValidateHeader(cfg, pow, header, chain.Genesis().Header(), false, false) if err != BlockNumberErr { t.Errorf("expected block number error, got %q", err) } header = makeHeader(chain.Genesis(), statedb) - err = ValidateHeader(pow, header, chain.Genesis().Header(), false, false) + err = ValidateHeader(cfg, pow, header, chain.Genesis().Header(), false, false) if err == BlockNumberErr { t.Errorf("didn't expect block number error") } diff --git a/core/blockchain.go b/core/blockchain.go index 2c3c2bb5c..177a3bbce 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -80,11 +80,12 @@ const ( // included in the canonical one where as GetBlockByNumber always represents the // canonical chain. type BlockChain struct { + config *ChainConfig // chain & network configuration + hc *HeaderChain chainDb ethdb.Database eventMux *event.TypeMux genesisBlock *types.Block - vmConfig *vm.Config mu sync.RWMutex // global mutex for locking chain operations chainmu sync.RWMutex // blockchain insertion lock @@ -113,13 +114,14 @@ type BlockChain struct { // NewBlockChain returns a fully initialised block chain using information // available in the database. It initialiser the default Ethereum Validator and // Processor. -func NewBlockChain(chainDb ethdb.Database, pow pow.PoW, mux *event.TypeMux) (*BlockChain, error) { +func NewBlockChain(chainDb ethdb.Database, config *ChainConfig, pow pow.PoW, mux *event.TypeMux) (*BlockChain, error) { bodyCache, _ := lru.New(bodyCacheLimit) bodyRLPCache, _ := lru.New(bodyCacheLimit) blockCache, _ := lru.New(blockCacheLimit) futureBlocks, _ := lru.New(maxFutureBlocks) bc := &BlockChain{ + config: config, chainDb: chainDb, eventMux: mux, quit: make(chan struct{}), @@ -129,24 +131,21 @@ func NewBlockChain(chainDb ethdb.Database, pow pow.PoW, mux *event.TypeMux) (*Bl futureBlocks: futureBlocks, pow: pow, } - bc.SetValidator(NewBlockValidator(bc, pow)) - bc.SetProcessor(NewStateProcessor(bc)) + bc.SetValidator(NewBlockValidator(config, bc, pow)) + bc.SetProcessor(NewStateProcessor(config, bc)) gv := func() HeaderValidator { return bc.Validator() } var err error - bc.hc, err = NewHeaderChain(chainDb, gv, bc.getProcInterrupt) + bc.hc, err = NewHeaderChain(chainDb, config, gv, bc.getProcInterrupt) if err != nil { return nil, err } bc.genesisBlock = bc.GetBlockByNumber(0) if bc.genesisBlock == nil { - bc.genesisBlock, err = WriteDefaultGenesisBlock(chainDb) - if err != nil { - return nil, err - } - glog.V(logger.Info).Infoln("WARNING: Wrote default ethereum genesis block") + return nil, ErrNoGenesis } + if err := bc.loadLastState(); err != nil { return nil, err } @@ -163,10 +162,6 @@ func NewBlockChain(chainDb ethdb.Database, pow pow.PoW, mux *event.TypeMux) (*Bl return bc, nil } -func (self *BlockChain) SetConfig(vmConfig *vm.Config) { - self.vmConfig = vmConfig -} - func (self *BlockChain) getProcInterrupt() bool { return atomic.LoadInt32(&self.procInterrupt) == 1 } @@ -896,7 +891,7 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) { return i, err } // Process block using the parent state as reference point. - receipts, logs, usedGas, err := self.processor.Process(block, statedb, self.vmConfig) + receipts, logs, usedGas, err := self.processor.Process(block, statedb, self.config.VmConfig) if err != nil { reportBlock(block, err) return i, err diff --git a/core/blockchain_test.go b/core/blockchain_test.go index d7cd24fa8..9e59b2ff6 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -53,7 +53,7 @@ func thePow() pow.PoW { func theBlockChain(db ethdb.Database, t *testing.T) *BlockChain { var eventMux event.TypeMux WriteTestNetGenesisBlock(db) - blockchain, err := NewBlockChain(db, thePow(), &eventMux) + blockchain, err := NewBlockChain(db, testChainConfig(), thePow(), &eventMux) if err != nil { t.Error("failed creating blockchain:", err) t.FailNow() @@ -141,7 +141,7 @@ func testBlockChainImport(chain types.Blocks, blockchain *BlockChain) error { if err != nil { return err } - receipts, _, usedGas, err := blockchain.Processor().Process(block, statedb, nil) + receipts, _, usedGas, err := blockchain.Processor().Process(block, statedb, vm.Config{}) if err != nil { reportBlock(block, err) return err @@ -435,7 +435,7 @@ func (bproc) ValidateHeader(*types.Header, *types.Header, bool) error { return n func (bproc) ValidateState(block, parent *types.Block, state *state.StateDB, receipts types.Receipts, usedGas *big.Int) error { return nil } -func (bproc) Process(block *types.Block, statedb *state.StateDB, cfg *vm.Config) (types.Receipts, vm.Logs, *big.Int, error) { +func (bproc) Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (types.Receipts, vm.Logs, *big.Int, error) { return nil, nil, nil, nil } @@ -473,13 +473,14 @@ func makeBlockChainWithDiff(genesis *types.Block, d []int, seed byte) []*types.B func chm(genesis *types.Block, db ethdb.Database) *BlockChain { var eventMux event.TypeMux bc := &BlockChain{ - chainDb: db, + chainDb: db, genesisBlock: genesis, - eventMux: &eventMux, - pow: FakePow{}, + eventMux: &eventMux, + pow: FakePow{}, + config: testChainConfig(), } valFn := func() HeaderValidator { return bc.Validator() } - bc.hc, _ = NewHeaderChain(db, valFn, bc.getProcInterrupt) + bc.hc, _ = NewHeaderChain(db, testChainConfig(), valFn, bc.getProcInterrupt) bc.bodyCache, _ = lru.New(100) bc.bodyRLPCache, _ = lru.New(100) bc.blockCache, _ = lru.New(100) @@ -613,7 +614,7 @@ func testReorgBadHashes(t *testing.T, full bool) { defer func() { delete(BadHashes, headers[3].Hash()) }() } // Create a new chain manager and check it rolled back the state - ncm, err := NewBlockChain(db, FakePow{}, new(event.TypeMux)) + ncm, err := NewBlockChain(db, testChainConfig(), FakePow{}, new(event.TypeMux)) if err != nil { t.Fatalf("failed to create new chain manager: %v", err) } @@ -667,7 +668,7 @@ func testInsertNonceError(t *testing.T, full bool) { failHash = headers[failAt].Hash() blockchain.pow = failPow{failNum} - blockchain.validator = NewBlockValidator(blockchain, failPow{failNum}) + blockchain.validator = NewBlockValidator(testChainConfig(), blockchain, failPow{failNum}) failRes, err = blockchain.InsertHeaderChain(headers, 1) } @@ -733,7 +734,7 @@ func TestFastVsFullChains(t *testing.T) { archiveDb, _ := ethdb.NewMemDatabase() WriteGenesisBlockForTesting(archiveDb, GenesisAccount{address, funds}) - archive, _ := NewBlockChain(archiveDb, FakePow{}, new(event.TypeMux)) + archive, _ := NewBlockChain(archiveDb, testChainConfig(), FakePow{}, new(event.TypeMux)) if n, err := archive.InsertChain(blocks); err != nil { t.Fatalf("failed to process block %d: %v", n, err) @@ -741,7 +742,7 @@ func TestFastVsFullChains(t *testing.T) { // Fast import the chain as a non-archive node to test fastDb, _ := ethdb.NewMemDatabase() WriteGenesisBlockForTesting(fastDb, GenesisAccount{address, funds}) - fast, _ := NewBlockChain(fastDb, FakePow{}, new(event.TypeMux)) + fast, _ := NewBlockChain(fastDb, testChainConfig(), FakePow{}, new(event.TypeMux)) headers := make([]*types.Header, len(blocks)) for i, block := range blocks { @@ -817,7 +818,7 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) { archiveDb, _ := ethdb.NewMemDatabase() WriteGenesisBlockForTesting(archiveDb, GenesisAccount{address, funds}) - archive, _ := NewBlockChain(archiveDb, FakePow{}, new(event.TypeMux)) + archive, _ := NewBlockChain(archiveDb, testChainConfig(), FakePow{}, new(event.TypeMux)) if n, err := archive.InsertChain(blocks); err != nil { t.Fatalf("failed to process block %d: %v", n, err) @@ -829,7 +830,7 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) { // Import the chain as a non-archive node and ensure all pointers are updated fastDb, _ := ethdb.NewMemDatabase() WriteGenesisBlockForTesting(fastDb, GenesisAccount{address, funds}) - fast, _ := NewBlockChain(fastDb, FakePow{}, new(event.TypeMux)) + fast, _ := NewBlockChain(fastDb, testChainConfig(), FakePow{}, new(event.TypeMux)) headers := make([]*types.Header, len(blocks)) for i, block := range blocks { @@ -848,7 +849,7 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) { // Import the chain as a light node and ensure all pointers are updated lightDb, _ := ethdb.NewMemDatabase() WriteGenesisBlockForTesting(lightDb, GenesisAccount{address, funds}) - light, _ := NewBlockChain(lightDb, FakePow{}, new(event.TypeMux)) + light, _ := NewBlockChain(lightDb, testChainConfig(), FakePow{}, new(event.TypeMux)) if n, err := light.InsertHeaderChain(headers, 1); err != nil { t.Fatalf("failed to insert header %d: %v", n, err) @@ -913,7 +914,7 @@ func TestChainTxReorgs(t *testing.T) { }) // Import the chain. This runs all block validation rules. evmux := &event.TypeMux{} - blockchain, _ := NewBlockChain(db, FakePow{}, evmux) + blockchain, _ := NewBlockChain(db, testChainConfig(), FakePow{}, evmux) if i, err := blockchain.InsertChain(chain); err != nil { t.Fatalf("failed to insert original chain[%d]: %v", i, err) } @@ -986,7 +987,7 @@ func TestLogReorgs(t *testing.T) { ) evmux := &event.TypeMux{} - blockchain, _ := NewBlockChain(db, FakePow{}, evmux) + blockchain, _ := NewBlockChain(db, testChainConfig(), FakePow{}, evmux) subs := evmux.Subscribe(RemovedLogsEvent{}) chain, _ := GenerateChain(genesis, db, 2, func(i int, gen *BlockGen) { @@ -1022,7 +1023,7 @@ func TestReorgSideEvent(t *testing.T) { ) evmux := &event.TypeMux{} - blockchain, _ := NewBlockChain(db, FakePow{}, evmux) + blockchain, _ := NewBlockChain(db, testChainConfig(), FakePow{}, evmux) chain, _ := GenerateChain(genesis, db, 3, func(i int, gen *BlockGen) { if i == 2 { diff --git a/core/chain_makers.go b/core/chain_makers.go index 7ae3c98b0..c740f9c37 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -23,11 +23,30 @@ import ( "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" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/pow" ) +/* + * TODO: move this to another package. + */ + +// MakeChainConfig returns a new ChainConfig with the ethereum default chain settings. +func MakeChainConfig(testnet bool) *ChainConfig { + homesteadBlock := params.MainNetHomesteadBlock + // set a different default homestead block for the testnet + if testnet { + homesteadBlock = params.TestNetHomesteadBlock + } + + return &ChainConfig{ + HomesteadBlock: homesteadBlock, + } +} + // FakePow is a non-validating proof of work implementation. // It returns true from Verify for any block. type FakePow struct{} @@ -91,7 +110,7 @@ func (b *BlockGen) AddTx(tx *types.Transaction) { b.SetCoinbase(common.Address{}) } b.statedb.StartRecord(tx.Hash(), common.Hash{}, len(b.txs)) - receipt, _, _, err := ApplyTransaction(nil, b.gasPool, b.statedb, b.header, tx, b.header.GasUsed, nil) + receipt, _, _, err := ApplyTransaction(MakeChainConfig(true), nil, b.gasPool, b.statedb, b.header, tx, b.header.GasUsed, vm.Config{}) if err != nil { panic(err) } @@ -148,7 +167,7 @@ func (b *BlockGen) OffsetTime(seconds int64) { if b.header.Time.Cmp(b.parent.Header().Time) <= 0 { panic("block time out of range") } - b.header.Difficulty = CalcDifficulty(b.header.Time.Uint64(), b.parent.Time().Uint64(), b.parent.Number(), b.parent.Difficulty()) + b.header.Difficulty = CalcDifficulty(MakeChainConfig(true), b.header.Time.Uint64(), b.parent.Time().Uint64(), b.parent.Number(), b.parent.Difficulty()) } // GenerateChain creates a chain of n blocks. The first block's @@ -203,7 +222,7 @@ func makeHeader(parent *types.Block, state *state.StateDB) *types.Header { Root: state.IntermediateRoot(), ParentHash: parent.Hash(), Coinbase: parent.Coinbase(), - Difficulty: CalcDifficulty(time.Uint64(), new(big.Int).Sub(time, big.NewInt(10)).Uint64(), parent.Number(), parent.Difficulty()), + Difficulty: CalcDifficulty(MakeChainConfig(true), time.Uint64(), new(big.Int).Sub(time, big.NewInt(10)).Uint64(), parent.Number(), parent.Difficulty()), GasLimit: CalcGasLimit(parent), GasUsed: new(big.Int), Number: new(big.Int).Add(parent.Number(), common.Big1), @@ -222,7 +241,7 @@ func newCanonical(n int, full bool) (ethdb.Database, *BlockChain, error) { // Initialize a fresh chain with only a genesis block genesis, _ := WriteTestNetGenesisBlock(db) - blockchain, _ := NewBlockChain(db, FakePow{}, evmux) + blockchain, _ := NewBlockChain(db, MakeChainConfig(false), FakePow{}, evmux) // Create and inject the requested chain if n == 0 { return db, blockchain, nil diff --git a/core/chain_makers_test.go b/core/chain_makers_test.go index b9c1d89b7..86949246b 100644 --- a/core/chain_makers_test.go +++ b/core/chain_makers_test.go @@ -77,7 +77,7 @@ func ExampleGenerateChain() { // Import the chain. This runs all block validation rules. evmux := &event.TypeMux{} - blockchain, _ := NewBlockChain(db, FakePow{}, evmux) + blockchain, _ := NewBlockChain(db, MakeChainConfig(true), FakePow{}, evmux) if i, err := blockchain.InsertChain(chain); err != nil { fmt.Printf("insert error (block %d): %v\n", i, err) return diff --git a/core/config.go b/core/config.go new file mode 100644 index 000000000..81ca76aa3 --- /dev/null +++ b/core/config.go @@ -0,0 +1,46 @@ +// 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 . + +package core + +import ( + "errors" + "math/big" + + "github.com/ethereum/go-ethereum/core/vm" +) + +var ChainConfigNotFoundErr = errors.New("ChainConfig not found") // general config not found error + +// ChainConfig is the core config which determines the blockchain settings. +// +// ChainConfig is stored in the database on a per block basis. This means +// that any network, identified by its genesis block, can have its own +// set of configuration options. +type ChainConfig struct { + HomesteadBlock *big.Int // homestead switch block + + VmConfig vm.Config `json:"-"` +} + +// IsHomestead returns whether num is either equal to the homestead block or greater. +func (c *ChainConfig) IsHomestead(num *big.Int) bool { + if num == nil { + return false + } + + return num.Cmp(c.HomesteadBlock) >= 0 +} diff --git a/core/database_util.go b/core/database_util.go index fd2b4c312..e1e8136d1 100644 --- a/core/database_util.go +++ b/core/database_util.go @@ -19,6 +19,7 @@ package core import ( "bytes" "encoding/binary" + "encoding/json" "fmt" "math/big" @@ -50,6 +51,8 @@ var ( MIPMapLevels = []uint64{1000000, 500000, 100000, 50000, 1000} blockHashPrefix = []byte("block-hash-") // [deprecated by the header/block split, remove eventually] + + configPrefix = []byte("ethereum-config-") // config prefix for the db ) // GetCanonicalHash retrieves a hash assigned to a canonical block number. @@ -527,3 +530,34 @@ func WriteBlockChainVersion(db ethdb.Database, vsn int) { enc, _ := rlp.EncodeToBytes(uint(vsn)) db.Put([]byte("BlockchainVersion"), enc) } + +// WriteChainConfig writes the chain config settings to the database. +func WriteChainConfig(db ethdb.Database, hash common.Hash, cfg *ChainConfig) error { + // short circuit and ignore if nil config. GetChainConfig + // will return a default. + if cfg == nil { + return nil + } + + jsonChainConfig, err := json.Marshal(cfg) + if err != nil { + return err + } + + return db.Put(append(configPrefix, hash[:]...), jsonChainConfig) +} + +// GetChainConfig will fetch the network settings based on the given hash. +func GetChainConfig(db ethdb.Database, hash common.Hash) (*ChainConfig, error) { + jsonChainConfig, _ := db.Get(append(configPrefix, hash[:]...)) + if len(jsonChainConfig) == 0 { + return nil, ChainConfigNotFoundErr + } + + var config ChainConfig + if err := json.Unmarshal(jsonChainConfig, &config); err != nil { + return nil, err + } + + return &config, nil +} diff --git a/core/execution.go b/core/execution.go index d90dceafc..82143443c 100644 --- a/core/execution.go +++ b/core/execution.go @@ -126,7 +126,7 @@ func exec(env vm.Environment, caller vm.ContractRef, address, codeAddr *common.A // When an error was returned by the EVM or when setting the creation code // above we revert to the snapshot and consume any gas remaining. Additionally // when we're in homestead this also counts for code storage gas errors. - if err != nil && (params.IsHomestead(env.BlockNumber()) || err != vm.CodeStoreOutOfGasError) { + if err != nil && (env.RuleSet().IsHomestead(env.BlockNumber()) || err != vm.CodeStoreOutOfGasError) { contract.UseGas(contract.Gas) env.SetSnapshot(snapshotPreTransfer) diff --git a/core/genesis.go b/core/genesis.go index d8c6e9cea..5c69b216c 100644 --- a/core/genesis.go +++ b/core/genesis.go @@ -43,15 +43,16 @@ func WriteGenesisBlock(chainDb ethdb.Database, reader io.Reader) (*types.Block, } var genesis struct { - Nonce string - Timestamp string - ParentHash string - ExtraData string - GasLimit string - Difficulty string - Mixhash string - Coinbase string - Alloc map[string]struct { + ChainConfig *ChainConfig + Nonce string + Timestamp string + ParentHash string + ExtraData string + GasLimit string + Difficulty string + Mixhash string + Coinbase string + Alloc map[string]struct { Code string Storage map[string]string Balance string @@ -114,6 +115,10 @@ func WriteGenesisBlock(chainDb ethdb.Database, reader io.Reader) (*types.Block, if err := WriteHeadBlockHash(chainDb, block.Hash()); err != nil { return nil, err } + if err := WriteChainConfig(chainDb, block.Hash(), genesis.ChainConfig); err != nil { + return nil, err + } + return block, nil } diff --git a/core/headerchain.go b/core/headerchain.go index 255139dde..21fc6e80a 100644 --- a/core/headerchain.go +++ b/core/headerchain.go @@ -40,6 +40,8 @@ import ( // It is not thread safe either, the encapsulating chain structures should do // the necessary mutex locking/unlocking. type HeaderChain struct { + config *ChainConfig + chainDb ethdb.Database genesisHeader *types.Header @@ -62,7 +64,7 @@ type getHeaderValidatorFn func() HeaderValidator // getValidator should return the parent's validator // procInterrupt points to the parent's interrupt semaphore // wg points to the parent's shutdown wait group -func NewHeaderChain(chainDb ethdb.Database, getValidator getHeaderValidatorFn, procInterrupt func() bool) (*HeaderChain, error) { +func NewHeaderChain(chainDb ethdb.Database, config *ChainConfig, getValidator getHeaderValidatorFn, procInterrupt func() bool) (*HeaderChain, error) { headerCache, _ := lru.New(headerCacheLimit) tdCache, _ := lru.New(tdCacheLimit) @@ -73,6 +75,7 @@ func NewHeaderChain(chainDb ethdb.Database, getValidator getHeaderValidatorFn, p } hc := &HeaderChain{ + config: config, chainDb: chainDb, headerCache: headerCache, tdCache: tdCache, @@ -436,15 +439,17 @@ func (hc *HeaderChain) SetGenesis(head *types.Header) { // // headerValidator implements HeaderValidator. type headerValidator struct { - hc *HeaderChain // Canonical header chain - Pow pow.PoW // Proof of work used for validating + config *ChainConfig + hc *HeaderChain // Canonical header chain + Pow pow.PoW // Proof of work used for validating } // NewBlockValidator returns a new block validator which is safe for re-use -func NewHeaderValidator(chain *HeaderChain, pow pow.PoW) HeaderValidator { +func NewHeaderValidator(config *ChainConfig, chain *HeaderChain, pow pow.PoW) HeaderValidator { return &headerValidator{ - Pow: pow, - hc: chain, + config: config, + Pow: pow, + hc: chain, } } @@ -460,5 +465,5 @@ func (v *headerValidator) ValidateHeader(header, parent *types.Header, checkPow if v.hc.HasHeader(header.Hash()) { return nil } - return ValidateHeader(v.Pow, header, parent, checkPow, false) + return ValidateHeader(v.config, v.Pow, header, parent, checkPow, false) } diff --git a/core/state_processor.go b/core/state_processor.go index 38cd0e675..441f6a7a8 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -16,12 +16,21 @@ var ( big32 = big.NewInt(32) ) +// StateProcessor is a basic Processor, which takes care of transitioning +// state from one point to another. +// +// StateProcessor implements Processor. type StateProcessor struct { - bc *BlockChain + config *ChainConfig + bc *BlockChain } -func NewStateProcessor(bc *BlockChain) *StateProcessor { - return &StateProcessor{bc} +// NewStateProcessor initialises a new StateProcessor. +func NewStateProcessor(config *ChainConfig, bc *BlockChain) *StateProcessor { + return &StateProcessor{ + config: config, + bc: bc, + } } // Process processes the state changes according to the Ethereum rules by running @@ -31,7 +40,7 @@ func NewStateProcessor(bc *BlockChain) *StateProcessor { // Process returns the receipts and logs accumulated during the process and // returns the amount of gas that was used in the process. If any of the // transactions failed to execute due to insufficient gas it will return an error. -func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg *vm.Config) (types.Receipts, vm.Logs, *big.Int, error) { +func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (types.Receipts, vm.Logs, *big.Int, error) { var ( receipts types.Receipts totalUsedGas = big.NewInt(0) @@ -43,7 +52,7 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg for i, tx := range block.Transactions() { statedb.StartRecord(tx.Hash(), block.Hash(), i) - receipt, logs, _, err := ApplyTransaction(p.bc, gp, statedb, header, tx, totalUsedGas, cfg) + receipt, logs, _, err := ApplyTransaction(p.config, p.bc, gp, statedb, header, tx, totalUsedGas, cfg) if err != nil { return nil, nil, totalUsedGas, err } @@ -60,8 +69,8 @@ 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(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, bc, tx, header, cfg), tx, gp) +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) if err != nil { return nil, nil, nil, err } diff --git a/core/state_transition.go b/core/state_transition.go index cc357aaca..60ac7d8cb 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -124,7 +124,7 @@ func (self *StateTransition) from() (vm.Account, error) { f common.Address err error ) - if params.IsHomestead(self.env.BlockNumber()) { + if self.env.RuleSet().IsHomestead(self.env.BlockNumber()) { f, err = self.msg.From() } else { f, err = self.msg.FromFrontier() @@ -216,7 +216,7 @@ func (self *StateTransition) transitionDb() (ret []byte, usedGas *big.Int, err e msg := self.msg sender, _ := self.from() // err checked in preCheck - homestead := params.IsHomestead(self.env.BlockNumber()) + homestead := self.env.RuleSet().IsHomestead(self.env.BlockNumber()) contractCreation := MessageCreatesContract(msg) // Pay intrinsic gas if err = self.useGas(IntrinsicGas(self.data, contractCreation, homestead)); err != nil { diff --git a/core/tx_pool.go b/core/tx_pool.go index f4e964bf7..e997e8cd0 100644 --- a/core/tx_pool.go +++ b/core/tx_pool.go @@ -30,7 +30,6 @@ import ( "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger/glog" - "github.com/ethereum/go-ethereum/params" ) var ( @@ -60,6 +59,7 @@ type stateFn func() (*state.StateDB, error) // current state) and future transactions. Transactions move between those // two states over time as they are received and processed. type TxPool struct { + config *ChainConfig quit chan bool // Quitting channel currentState stateFn // The state function which will allow us to do some pre checks pendingState *state.ManagedState @@ -75,8 +75,9 @@ type TxPool struct { homestead bool } -func NewTxPool(eventMux *event.TypeMux, currentStateFn stateFn, gasLimitFn func() *big.Int) *TxPool { +func NewTxPool(config *ChainConfig, eventMux *event.TypeMux, currentStateFn stateFn, gasLimitFn func() *big.Int) *TxPool { pool := &TxPool{ + config: config, pending: make(map[common.Hash]*types.Transaction), queue: make(map[common.Address]map[common.Hash]*types.Transaction), quit: make(chan bool), @@ -102,7 +103,7 @@ func (pool *TxPool) eventLoop() { switch ev := ev.Data.(type) { case ChainHeadEvent: pool.mu.Lock() - if ev.Block != nil && params.IsHomestead(ev.Block.Number()) { + if ev.Block != nil && pool.config.IsHomestead(ev.Block.Number()) { pool.homestead = true } diff --git a/core/tx_pool_test.go b/core/tx_pool_test.go index fa1a740dc..ed9bea75f 100644 --- a/core/tx_pool_test.go +++ b/core/tx_pool_test.go @@ -40,7 +40,7 @@ func setupTxPool() (*TxPool, *ecdsa.PrivateKey) { var m event.TypeMux key, _ := crypto.GenerateKey() - newPool := NewTxPool(&m, func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) }) + newPool := NewTxPool(testChainConfig(), &m, func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) }) newPool.resetState() return newPool, key } diff --git a/core/types.go b/core/types.go index af9bc567b..e2b31643a 100644 --- a/core/types.go +++ b/core/types.go @@ -61,7 +61,7 @@ type HeaderValidator interface { // of gas used in the process and return an error if any of the internal rules // failed. type Processor interface { - Process(block *types.Block, statedb *state.StateDB, cfg *vm.Config) (types.Receipts, vm.Logs, *big.Int, error) + Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (types.Receipts, vm.Logs, *big.Int, error) } // Backend is an interface defining the basic functionality for an operable node diff --git a/core/vm/environment.go b/core/vm/environment.go index 568218edd..747627565 100644 --- a/core/vm/environment.go +++ b/core/vm/environment.go @@ -22,9 +22,17 @@ import ( "github.com/ethereum/go-ethereum/common" ) +// RuleSet is an interface that defines the current rule set during the +// execution of the EVM instructions (e.g. whether it's homestead) +type RuleSet interface { + IsHomestead(*big.Int) bool +} + // Environment is an EVM requirement and helper which allows access to outside // information such as states. type Environment interface { + // The current ruleset + RuleSet() RuleSet // The state database Db() Database // Creates a restorable snapshot @@ -53,10 +61,10 @@ type Environment interface { AddLog(*Log) // Type of the VM Vm() Vm - // Current calling depth + // Get the curret calling depth Depth() int + // Set the current calling depth SetDepth(i int) - // Call another contract Call(me ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) // Take another's contract code and execute within our own context diff --git a/core/vm/instructions.go b/core/vm/instructions.go index c4b4339a2..942fafde7 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -520,7 +520,7 @@ func opCreate(instr instruction, pc *uint64, env Environment, contract *Contract // homestead we must check for CodeStoreOutOfGasError (homestead only // rule) and treat as an error, if the ruleset is frontier we must // ignore this error and pretend the operation was successful. - if params.IsHomestead(env.BlockNumber()) && suberr == CodeStoreOutOfGasError { + if env.RuleSet().IsHomestead(env.BlockNumber()) && suberr == CodeStoreOutOfGasError { stack.push(new(big.Int)) } else if suberr != nil && suberr != CodeStoreOutOfGasError { stack.push(new(big.Int)) diff --git a/core/vm/jit.go b/core/vm/jit.go index 71ffcf0f6..ac2083f54 100644 --- a/core/vm/jit.go +++ b/core/vm/jit.go @@ -30,27 +30,24 @@ import ( "github.com/hashicorp/golang-lru" ) +// progStatus is the type for the JIT program status. type progStatus int32 const ( - progUnknown progStatus = iota - progCompile - progReady - progError + progUnknown progStatus = iota // unknown status + progCompile // compile status + progReady // ready for use status + progError // error status (usually caused during compilation) - defaultJitMaxCache int = 64 + defaultJitMaxCache int = 64 // maximum amount of jit cached programs ) -var ( - EnableJit bool // Enables the JIT VM - ForceJit bool // Force the JIT, skip byte VM - MaxProgSize int // Max cache size for JIT Programs -) +var MaxProgSize int // Max cache size for JIT programs -var programs *lru.Cache +var programs *lru.Cache // lru cache for the JIT programs. func init() { - programs, _ = lru.New(defaultJitMaxCache) + SetJITCacheSize(defaultJitMaxCache) } // SetJITCacheSize recreates the program cache with the max given size. Setting @@ -322,7 +319,7 @@ func runProgram(program *Program, pcstart uint64, mem *Memory, stack *stack, env }() } - homestead := params.IsHomestead(env.BlockNumber()) + homestead := env.RuleSet().IsHomestead(env.BlockNumber()) for pc < uint64(len(program.instructions)) { instrCount++ diff --git a/core/vm/jit_test.go b/core/vm/jit_test.go index 43b1dee2a..c503a3a81 100644 --- a/core/vm/jit_test.go +++ b/core/vm/jit_test.go @@ -84,7 +84,7 @@ func TestCompiling(t *testing.T) { func TestResetInput(t *testing.T) { var sender account - env := NewEnv() + env := NewEnv(false, true) contract := NewContract(sender, sender, big.NewInt(100), big.NewInt(10000), big.NewInt(0)) contract.CodeAddr = &common.Address{} @@ -143,10 +143,7 @@ func runVmBench(test vmBench, b *testing.B) { if test.precompile && !test.forcejit { NewProgram(test.code) } - env := NewEnv() - - EnableJit = !test.nojit - ForceJit = test.forcejit + env := NewEnv(test.nojit, test.forcejit) b.ResetTimer() @@ -168,12 +165,16 @@ type Env struct { evm *EVM } -func NewEnv() *Env { +func NewEnv(noJit, forceJit bool) *Env { env := &Env{gasLimit: big.NewInt(10000), depth: 0} - env.evm = New(env, nil) + env.evm = New(env, Config{ + EnableJit: !noJit, + ForceJit: forceJit, + }) 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) } diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go index 8297d3e1d..2c3796679 100644 --- a/core/vm/jump_table.go +++ b/core/vm/jump_table.go @@ -1,10 +1,6 @@ package vm -import ( - "math/big" - - "github.com/ethereum/go-ethereum/params" -) +import "math/big" type jumpPtr struct { fn instrFn @@ -13,12 +9,12 @@ type jumpPtr struct { type vmJumpTable [256]jumpPtr -func newJumpTable(blockNumber *big.Int) vmJumpTable { +func newJumpTable(ruleset RuleSet, blockNumber *big.Int) vmJumpTable { var jumpTable vmJumpTable // when initialising a new VM execution we must first check the homestead // changes. - if params.IsHomestead(blockNumber) { + if ruleset.IsHomestead(blockNumber) { jumpTable[DELEGATECALL] = jumpPtr{opDelegateCall, true} } diff --git a/core/vm/jump_table_test.go b/core/vm/jump_table_test.go index 2ed1b26fc..2386a7525 100644 --- a/core/vm/jump_table_test.go +++ b/core/vm/jump_table_test.go @@ -3,20 +3,16 @@ package vm import ( "math/big" "testing" - - "github.com/ethereum/go-ethereum/params" ) func TestInit(t *testing.T) { - params.HomesteadBlock = big.NewInt(1) - - jumpTable := newJumpTable(big.NewInt(0)) + jumpTable := newJumpTable(ruleSet{big.NewInt(1)}, big.NewInt(0)) if jumpTable[DELEGATECALL].valid { t.Error("Expected DELEGATECALL not to be present") } for _, n := range []int64{1, 2, 100} { - jumpTable := newJumpTable(big.NewInt(n)) + jumpTable := newJumpTable(ruleSet{big.NewInt(1)}, big.NewInt(n)) if !jumpTable[DELEGATECALL].valid { t.Error("Expected DELEGATECALL to be present for block", n) } diff --git a/core/vm/logger.go b/core/vm/logger.go index 8d333dfd2..cbdc8a744 100644 --- a/core/vm/logger.go +++ b/core/vm/logger.go @@ -89,7 +89,7 @@ func newLogger(cfg LogConfig, env Environment) *Logger { // captureState logs a new structured log message and pushes it out to the environment // // captureState also tracks SSTORE ops to track dirty values. -func (l *Logger) captureState(pc uint64, op OpCode, gas, cost *big.Int, memory *Memory, stack *stack, contract *Contract, err error) { +func (l *Logger) captureState(pc uint64, op OpCode, gas, cost *big.Int, memory *Memory, stack *stack, contract *Contract, depth int, err error) { // short circuit if no log collector is present if l.cfg.Collector == nil { return diff --git a/core/vm/logger_test.go b/core/vm/logger_test.go index 77fee2c64..144569865 100644 --- a/core/vm/logger_test.go +++ b/core/vm/logger_test.go @@ -47,7 +47,7 @@ type dummyEnv struct { func newDummyEnv(ref *dummyContractRef) *dummyEnv { return &dummyEnv{ - Env: NewEnv(), + Env: NewEnv(true, false), ref: ref, } } @@ -58,7 +58,7 @@ func (d dummyEnv) AddStructLog(StructLog) {} func TestStoreCapture(t *testing.T) { var ( - env = NewEnv() + env = NewEnv(true, false) logger = newLogger(LogConfig{Collector: env}, env) mem = NewMemory() stack = newstack() @@ -69,7 +69,7 @@ func TestStoreCapture(t *testing.T) { var index common.Hash - logger.captureState(0, SSTORE, new(big.Int), new(big.Int), mem, stack, contract, nil) + logger.captureState(0, SSTORE, new(big.Int), new(big.Int), mem, stack, contract, 0, nil) if len(logger.changedValues[contract.Address()]) == 0 { t.Fatalf("expected exactly 1 changed value on address %x, got %d", contract.Address(), len(logger.changedValues[contract.Address()])) } @@ -91,13 +91,13 @@ func TestStorageCapture(t *testing.T) { stack = newstack() ) - logger.captureState(0, STOP, new(big.Int), new(big.Int), mem, stack, contract, nil) + logger.captureState(0, STOP, new(big.Int), new(big.Int), mem, stack, contract, 0, nil) if ref.calledForEach { t.Error("didn't expect for each to be called") } logger = newLogger(LogConfig{Collector: env, FullStorage: true}, env) - logger.captureState(0, STOP, new(big.Int), new(big.Int), mem, stack, contract, nil) + logger.captureState(0, STOP, new(big.Int), new(big.Int), mem, stack, contract, 0, nil) if !ref.calledForEach { t.Error("expected for each to be called") } diff --git a/core/vm/runtime/env.go b/core/vm/runtime/env.go index ce64d7117..1e943940b 100644 --- a/core/vm/runtime/env.go +++ b/core/vm/runtime/env.go @@ -27,8 +27,9 @@ import ( // Env is a basic runtime environment required for running the EVM. type Env struct { - depth int - state *state.StateDB + ruleSet vm.RuleSet + depth int + state *state.StateDB origin common.Address coinbase common.Address @@ -48,6 +49,7 @@ 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, @@ -56,7 +58,7 @@ func NewEnv(cfg *Config, state *state.StateDB) vm.Environment { difficulty: cfg.Difficulty, gasLimit: cfg.GasLimit, } - env.evm = vm.New(env, &vm.Config{ + env.evm = vm.New(env, vm.Config{ Debug: cfg.Debug, EnableJit: !cfg.DisableJit, ForceJit: !cfg.DisableJit, @@ -77,6 +79,7 @@ func (self *Env) AddStructLog(log vm.StructLog) { self.logs = append(self.logs, log) } +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 } func (self *Env) BlockNumber() *big.Int { return self.number } diff --git a/core/vm/runtime/runtime.go b/core/vm/runtime/runtime.go index f88a20170..553864a83 100644 --- a/core/vm/runtime/runtime.go +++ b/core/vm/runtime/runtime.go @@ -22,13 +22,20 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb" ) +// The default, always homestead, rule set for the vm env +type ruleSet struct{} + +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 @@ -46,6 +53,10 @@ type Config struct { // sets defaults on the config func setDefaults(cfg *Config) { + if cfg.RuleSet == nil { + cfg.RuleSet = ruleSet{} + } + if cfg.Difficulty == nil { cfg.Difficulty = new(big.Int) } diff --git a/core/vm/util_test.go b/core/vm/util_test.go new file mode 100644 index 000000000..3da742bfa --- /dev/null +++ b/core/vm/util_test.go @@ -0,0 +1,9 @@ +package vm + +import "math/big" + +type ruleSet struct { + hs *big.Int +} + +func (r ruleSet) IsHomestead(n *big.Int) bool { return n.Cmp(r.hs) >= 0 } diff --git a/core/vm/vm.go b/core/vm/vm.go index f72c853a2..0f93715d6 100644 --- a/core/vm/vm.go +++ b/core/vm/vm.go @@ -43,18 +43,13 @@ type Config struct { type EVM struct { env Environment jumpTable vmJumpTable - cfg *Config + cfg Config logger *Logger } // New returns a new instance of the EVM. -func New(env Environment, cfg *Config) *EVM { - // initialise a default config if none is present - if cfg == nil { - cfg = new(Config) - } - +func New(env Environment, cfg Config) *EVM { var logger *Logger if cfg.Debug { logger = newLogger(cfg.Logger, env) @@ -62,7 +57,7 @@ func New(env Environment, cfg *Config) *EVM { return &EVM{ env: env, - jumpTable: newJumpTable(env.BlockNumber()), + jumpTable: newJumpTable(env.RuleSet(), env.BlockNumber()), cfg: cfg, logger: logger, } @@ -154,7 +149,7 @@ func (evm *EVM) Run(contract *Contract, input []byte) (ret []byte, err error) { // User defer pattern to check for an error and, based on the error being nil or not, use all gas and return. defer func() { if err != nil && evm.cfg.Debug { - evm.logger.captureState(pc, op, contract.Gas, cost, mem, stack, contract, err) + evm.logger.captureState(pc, op, contract.Gas, cost, mem, stack, contract, evm.env.Depth(), err) } }() @@ -196,7 +191,7 @@ func (evm *EVM) Run(contract *Contract, input []byte) (ret []byte, err error) { mem.Resize(newMemSize.Uint64()) // Add a log message if evm.cfg.Debug { - evm.logger.captureState(pc, op, contract.Gas, cost, mem, stack, contract, nil) + evm.logger.captureState(pc, op, contract.Gas, cost, mem, stack, contract, evm.env.Depth(), nil) } if opPtr := evm.jumpTable[op]; opPtr.valid { diff --git a/core/vm/vm_jit_fake.go b/core/vm/vm_jit_fake.go index b26cf1ad0..4fa98ccd9 100644 --- a/core/vm/vm_jit_fake.go +++ b/core/vm/vm_jit_fake.go @@ -22,5 +22,5 @@ import "fmt" func NewJitVm(env Environment) VirtualMachine { fmt.Printf("Warning! EVM JIT not enabled.\n") - return New(env, nil) + return New(env, Config{}) } diff --git a/core/vm_env.go b/core/vm_env.go index 880baa7b0..f50140c68 100644 --- a/core/vm_env.go +++ b/core/vm_env.go @@ -41,30 +41,26 @@ func GetHashFn(ref common.Hash, chain *BlockChain) func(n uint64) common.Hash { } type VMEnv struct { - state *state.StateDB // State to use for executing - evm *vm.EVM // The Ethereum Virtual Machine - depth int // Current execution depth - msg Message // Message appliod + chainConfig *ChainConfig // Chain configuration + state *state.StateDB // State to use for executing + evm *vm.EVM // The Ethereum Virtual Machine + depth int // Current execution depth + msg Message // Message appliod header *types.Header // Header information chain *BlockChain // Blockchain handle logs []vm.StructLog // Logs for the custom structured logger getHashFn func(uint64) common.Hash // getHashFn callback is used to retrieve block hashes - } -func NewEnv(state *state.StateDB, 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{ - chain: chain, - state: state, - header: header, - msg: msg, - getHashFn: GetHashFn(header.ParentHash, chain), - } - - // initialise a default config if none present - if cfg == nil { - cfg = new(vm.Config) + chainConfig: chainConfig, + chain: chain, + state: state, + header: header, + msg: msg, + getHashFn: GetHashFn(header.ParentHash, chain), } // if no log collector is present set self as the collector @@ -76,6 +72,7 @@ func NewEnv(state *state.StateDB, chain *BlockChain, msg Message, header *types. return env } +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 } func (self *VMEnv) BlockNumber() *big.Int { return self.header.Number } diff --git a/eth/api.go b/eth/api.go index 4a03a0940..06c10daa9 100644 --- a/eth/api.go +++ b/eth/api.go @@ -456,6 +456,7 @@ func (s *PrivateAccountAPI) LockAccount(addr common.Address) bool { // PublicBlockChainAPI provides an API to access the Ethereum blockchain. // It offers only methods that operate on public data that is freely available to anyone. type PublicBlockChainAPI struct { + config *core.ChainConfig bc *core.BlockChain chainDb ethdb.Database eventMux *event.TypeMux @@ -464,8 +465,8 @@ type PublicBlockChainAPI struct { } // NewPublicBlockChainAPI creates a new Etheruem blockchain API. -func NewPublicBlockChainAPI(bc *core.BlockChain, m *miner.Miner, chainDb ethdb.Database, eventMux *event.TypeMux, am *accounts.Manager) *PublicBlockChainAPI { - return &PublicBlockChainAPI{bc: bc, miner: m, chainDb: chainDb, eventMux: eventMux, am: am} +func NewPublicBlockChainAPI(config *core.ChainConfig, bc *core.BlockChain, m *miner.Miner, chainDb ethdb.Database, eventMux *event.TypeMux, am *accounts.Manager) *PublicBlockChainAPI { + return &PublicBlockChainAPI{config: config, bc: bc, miner: m, chainDb: chainDb, eventMux: eventMux, am: am} } // BlockNumber returns the block number of the chain head. @@ -670,7 +671,7 @@ func (s *PublicBlockChainAPI) doCall(args CallArgs, blockNr rpc.BlockNumber) (st } // Execute the call and return - vmenv := core.NewEnv(stateDb, s.bc, msg, block.Header(), nil) + vmenv := core.NewEnv(stateDb, s.config, s.bc, msg, block.Header(), s.config.VmConfig) gp := new(core.GasPool).AddGas(common.MaxBig) res, gas, err := core.ApplyMessage(vmenv, msg, gp) @@ -1501,13 +1502,14 @@ func (api *PublicDebugAPI) SeedHash(number uint64) (string, error) { // PrivateDebugAPI is the collection of Etheruem APIs exposed over the private // debugging endpoint. type PrivateDebugAPI struct { - eth *Ethereum + config *core.ChainConfig + eth *Ethereum } // NewPrivateDebugAPI creates a new API definition for the private debug methods // of the Ethereum service. -func NewPrivateDebugAPI(eth *Ethereum) *PrivateDebugAPI { - return &PrivateDebugAPI{eth: eth} +func NewPrivateDebugAPI(config *core.ChainConfig, eth *Ethereum) *PrivateDebugAPI { + return &PrivateDebugAPI{config: config, eth: eth} } // BlockTraceResults is the returned value when replaying a block to check for @@ -1601,7 +1603,7 @@ func (api *PrivateDebugAPI) traceBlock(block *types.Block, config vm.Config) (bo config.Debug = true // make sure debug is set. config.Logger.Collector = collector - if err := core.ValidateHeader(blockchain.AuxValidator(), block.Header(), blockchain.GetHeader(block.ParentHash()), true, false); err != nil { + if err := core.ValidateHeader(api.config, blockchain.AuxValidator(), block.Header(), blockchain.GetHeader(block.ParentHash()), true, false); err != nil { return false, collector.traces, err } statedb, err := state.New(blockchain.GetBlock(block.ParentHash()).Root(), api.eth.ChainDb()) @@ -1609,7 +1611,7 @@ func (api *PrivateDebugAPI) traceBlock(block *types.Block, config vm.Config) (bo return false, collector.traces, err } - receipts, _, usedGas, err := processor.Process(block, statedb, &config) + receipts, _, usedGas, err := processor.Process(block, statedb, config) if err != nil { return false, collector.traces, err } @@ -1731,7 +1733,7 @@ func (s *PrivateDebugAPI) TraceTransaction(txHash common.Hash, logger vm.LogConf data: tx.Data(), } - vmenv := core.NewEnv(stateDb, s.eth.BlockChain(), msg, block.Header(), &vm.Config{ + vmenv := core.NewEnv(stateDb, s.config, s.eth.BlockChain(), msg, block.Header(), vm.Config{ Debug: true, Logger: logger, }) @@ -1788,7 +1790,9 @@ func (s *PublicBlockChainAPI) TraceCall(args CallArgs, blockNr rpc.BlockNumber) } // Execute the call and return - vmenv := core.NewEnv(stateDb, s.bc, msg, block.Header(), nil) + vmenv := core.NewEnv(stateDb, s.config, s.bc, msg, block.Header(), vm.Config{ + Debug: true, + }) gp := new(core.GasPool).AddGas(common.MaxBig) ret, gas, err := core.ApplyMessage(vmenv, msg, gp) diff --git a/eth/backend.go b/eth/backend.go index 4f3e11a50..3c3440a53 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -63,6 +63,8 @@ var ( ) type Config struct { + ChainConfig *core.ChainConfig // chain configuration + NetworkId int // Network ID to use for selecting peers to connect to Genesis string // Genesis JSON to seed the chain database with FastSync bool // Enables the state download based fast synchronisation algorithm @@ -100,6 +102,7 @@ type Config struct { } type Ethereum struct { + chainConfig *core.ChainConfig // Channel for shutting down the ethereum shutdownChan chan bool @@ -166,12 +169,17 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) { // Load up any custom genesis block if requested if len(config.Genesis) > 0 { + // Using println instead of glog to make sure it **always** displays regardless of + // verbosity settings. + common.PrintDepricationWarning("--genesis is deprecated. Switch to use 'geth init /path/to/file'") + block, err := core.WriteGenesisBlock(chainDb, strings.NewReader(config.Genesis)) if err != nil { return nil, err } glog.V(logger.Info).Infof("Successfully wrote custom genesis block: %x", block.Hash()) } + // Load up a test setup if directly injected if config.TestGenesisState != nil { chainDb = config.TestGenesisState @@ -227,26 +235,38 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) { default: eth.pow = ethash.New() } - //genesis := core.GenesisBlock(uint64(config.GenesisNonce), stateDb) - eth.blockchain, err = core.NewBlockChain(chainDb, eth.pow, eth.EventMux()) - eth.blockchain.SetConfig(&vm.Config{ + + // load the genesis block or write a new one if no genesis + // block is prenent in the database. + genesis := core.GetBlock(chainDb, core.GetCanonicalHash(chainDb, 0)) + if genesis == nil { + genesis, err = core.WriteDefaultGenesisBlock(chainDb) + if err != nil { + return nil, err + } + glog.V(logger.Info).Infoln("WARNING: Wrote default ethereum genesis block") + } + + eth.chainConfig = config.ChainConfig + eth.chainConfig.VmConfig = vm.Config{ EnableJit: config.EnableJit, ForceJit: config.ForceJit, - }) + } + eth.blockchain, err = core.NewBlockChain(chainDb, eth.chainConfig, eth.pow, eth.EventMux()) if err != nil { if err == core.ErrNoGenesis { - return nil, fmt.Errorf(`Genesis block not found. Please supply a genesis block with the "--genesis /path/to/file" argument`) + return nil, fmt.Errorf(`No chain found. Please initialise a new chain using the "init" subcommand.`) } return nil, err } - newPool := core.NewTxPool(eth.EventMux(), eth.blockchain.State, eth.blockchain.GasLimit) + newPool := core.NewTxPool(eth.chainConfig, eth.EventMux(), eth.blockchain.State, eth.blockchain.GasLimit) eth.txPool = newPool - if eth.protocolManager, err = NewProtocolManager(config.FastSync, config.NetworkId, eth.eventMux, eth.txPool, eth.pow, eth.blockchain, chainDb); err != nil { + if eth.protocolManager, err = NewProtocolManager(eth.chainConfig, config.FastSync, config.NetworkId, eth.eventMux, eth.txPool, eth.pow, eth.blockchain, chainDb); err != nil { return nil, err } - eth.miner = miner.New(eth, eth.EventMux(), eth.pow) + eth.miner = miner.New(eth, eth.chainConfig, eth.EventMux(), eth.pow) eth.miner.SetGasPrice(config.GasPrice) eth.miner.SetExtra(config.ExtraData) @@ -275,7 +295,7 @@ func (s *Ethereum) APIs() []rpc.API { }, { Namespace: "eth", Version: "1.0", - Service: NewPublicBlockChainAPI(s.BlockChain(), s.Miner(), s.ChainDb(), s.EventMux(), s.AccountManager()), + Service: NewPublicBlockChainAPI(s.chainConfig, s.BlockChain(), s.Miner(), s.ChainDb(), s.EventMux(), s.AccountManager()), Public: true, }, { Namespace: "eth", @@ -319,7 +339,7 @@ func (s *Ethereum) APIs() []rpc.API { }, { Namespace: "debug", Version: "1.0", - Service: NewPrivateDebugAPI(s), + Service: NewPrivateDebugAPI(s.chainConfig, s), }, { Namespace: "net", Version: "1.0", @@ -328,7 +348,7 @@ func (s *Ethereum) APIs() []rpc.API { }, { Namespace: "admin", Version: "1.0", - Service: ethreg.NewPrivateRegistarAPI(s.BlockChain(), s.ChainDb(), s.TxPool(), s.AccountManager()), + Service: ethreg.NewPrivateRegistarAPI(s.chainConfig, s.BlockChain(), s.ChainDb(), s.TxPool(), s.AccountManager()), }, } } diff --git a/eth/handler.go b/eth/handler.go index 2c5cae479..135de3749 100644 --- a/eth/handler.go +++ b/eth/handler.go @@ -86,7 +86,7 @@ type ProtocolManager struct { // NewProtocolManager returns a new ethereum sub protocol manager. The Ethereum sub protocol manages peers capable // with the ethereum network. -func NewProtocolManager(fastSync bool, networkId int, mux *event.TypeMux, txpool txPool, pow pow.PoW, blockchain *core.BlockChain, chaindb ethdb.Database) (*ProtocolManager, error) { +func NewProtocolManager(config *core.ChainConfig, fastSync bool, networkId int, mux *event.TypeMux, txpool txPool, pow pow.PoW, blockchain *core.BlockChain, chaindb ethdb.Database) (*ProtocolManager, error) { // Figure out whether to allow fast sync or not if fastSync && blockchain.CurrentBlock().NumberU64() > 0 { glog.V(logger.Info).Infof("blockchain not empty, fast sync disabled") @@ -144,7 +144,7 @@ func NewProtocolManager(fastSync bool, networkId int, mux *event.TypeMux, txpool manager.removePeer) validator := func(block *types.Block, parent *types.Block) error { - return core.ValidateHeader(pow, block.Header(), parent.Header(), true, false) + return core.ValidateHeader(config, pow, block.Header(), parent.Header(), true, false) } heighter := func() uint64 { return blockchain.CurrentBlock().NumberU64() diff --git a/eth/helper_test.go b/eth/helper_test.go index bbd1fb818..5d141ce56 100644 --- a/eth/helper_test.go +++ b/eth/helper_test.go @@ -17,6 +17,7 @@ import ( "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p/discover" + "github.com/ethereum/go-ethereum/params" ) var ( @@ -34,13 +35,15 @@ func newTestProtocolManager(fastSync bool, blocks int, generator func(int, *core pow = new(core.FakePow) db, _ = ethdb.NewMemDatabase() genesis = core.WriteGenesisBlockForTesting(db, core.GenesisAccount{testBankAddress, testBankFunds}) - blockchain, _ = core.NewBlockChain(db, pow, evmux) + chainConfig = &core.ChainConfig{HomesteadBlock: params.MainNetHomesteadBlock} + blockchain, _ = core.NewBlockChain(db, chainConfig, pow, evmux) ) chain, _ := core.GenerateChain(genesis, db, blocks, generator) if _, err := blockchain.InsertChain(chain); err != nil { panic(err) } - pm, err := NewProtocolManager(fastSync, NetworkId, evmux, &testTxPool{added: newtx}, pow, blockchain, db) + + pm, err := NewProtocolManager(chainConfig, fastSync, NetworkId, evmux, &testTxPool{added: newtx}, pow, blockchain, db) if err != nil { return nil, err } diff --git a/miner/miner.go b/miner/miner.go index e52cefaab..7cc25cdf7 100644 --- a/miner/miner.go +++ b/miner/miner.go @@ -51,8 +51,8 @@ type Miner struct { shouldStart int32 // should start indicates whether we should start after sync } -func New(eth core.Backend, mux *event.TypeMux, pow pow.PoW) *Miner { - miner := &Miner{eth: eth, mux: mux, pow: pow, worker: newWorker(common.Address{}, eth), canStart: 1} +func New(eth core.Backend, config *core.ChainConfig, mux *event.TypeMux, pow pow.PoW) *Miner { + miner := &Miner{eth: eth, mux: mux, pow: pow, worker: newWorker(config, common.Address{}, eth), canStart: 1} go miner.update() return miner diff --git a/miner/worker.go b/miner/worker.go index b3ddf9707..c45945b87 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -61,6 +61,7 @@ type uint64RingBuffer struct { // environment is the workers current environment and holds // all of the current state information type Work struct { + config *core.ChainConfig state *state.StateDB // apply state changes here ancestors *set.Set // ancestor set (used for checking uncle parent validity) family *set.Set // family set (used for checking uncle invalidity) @@ -89,6 +90,8 @@ type Result struct { // worker is the main object which takes care of applying messages to the new state type worker struct { + config *core.ChainConfig + mu sync.Mutex agents map[Agent]struct{} @@ -122,8 +125,9 @@ type worker struct { fullValidation bool } -func newWorker(coinbase common.Address, eth core.Backend) *worker { +func newWorker(config *core.ChainConfig, coinbase common.Address, eth core.Backend) *worker { worker := &worker{ + config: config, eth: eth, mux: eth.EventMux(), chainDb: eth.ChainDb(), @@ -285,7 +289,7 @@ func (self *worker) wait() { } auxValidator := self.eth.BlockChain().AuxValidator() - if err := core.ValidateHeader(auxValidator, block.Header(), parent.Header(), true, false); err != nil && err != core.BlockFutureErr { + if err := core.ValidateHeader(self.config, auxValidator, block.Header(), parent.Header(), true, false); err != nil && err != core.BlockFutureErr { glog.V(logger.Error).Infoln("Invalid header on mined block:", err) continue } @@ -371,6 +375,7 @@ func (self *worker) makeCurrent(parent *types.Block, header *types.Header) error return err } work := &Work{ + config: self.config, state: state, ancestors: set.New(), family: set.New(), @@ -470,7 +475,7 @@ func (self *worker) commitNewWork() { header := &types.Header{ ParentHash: parent.Hash(), Number: num.Add(num, common.Big1), - Difficulty: core.CalcDifficulty(uint64(tstamp), parent.Time().Uint64(), parent.Number(), parent.Difficulty()), + Difficulty: core.CalcDifficulty(self.config, uint64(tstamp), parent.Time().Uint64(), parent.Number(), parent.Difficulty()), GasLimit: core.CalcGasLimit(parent), GasUsed: new(big.Int), Coinbase: self.coinbase, @@ -657,7 +662,7 @@ func (env *Work) commitTransactions(mux *event.TypeMux, transactions types.Trans func (env *Work) commitTransaction(tx *types.Transaction, bc *core.BlockChain, gp *core.GasPool) (error, vm.Logs) { snap := env.state.Copy() - receipt, logs, _, err := core.ApplyTransaction(bc, gp, env.state, env.header, tx, env.header.GasUsed, nil) + receipt, logs, _, err := core.ApplyTransaction(env.config, bc, gp, env.state, env.header, tx, env.header.GasUsed, env.config.VmConfig) if err != nil { env.state.Set(snap) return err, nil diff --git a/params/protocol_params.go b/params/protocol_params.go index 45a9a2549..2dfc251b6 100644 --- a/params/protocol_params.go +++ b/params/protocol_params.go @@ -40,8 +40,9 @@ var ( EcrecoverGas = big.NewInt(3000) // Sha256WordGas = big.NewInt(12) // - MinGasLimit = big.NewInt(5000) // Minimum the gas limit may ever be. - GenesisGasLimit = big.NewInt(4712388) // Gas limit of the Genesis block. + MinGasLimit = big.NewInt(5000) // Minimum the gas limit may ever be. + GenesisGasLimit = big.NewInt(4712388) // Gas limit of the Genesis block. + TargetGasLimit = new(big.Int).Set(GenesisGasLimit) // The artificial target Sha3Gas = big.NewInt(30) // Once per SHA3 operation. Sha256Gas = big.NewInt(60) // diff --git a/params/util.go b/params/util.go index 6a49a2013..b37bc79b2 100644 --- a/params/util.go +++ b/params/util.go @@ -19,15 +19,6 @@ package params import "math/big" var ( - TestNetHomesteadBlock = big.NewInt(494000) // testnet homestead block - MainNetHomesteadBlock = big.NewInt(1150000) // mainnet homestead block - HomesteadBlock = MainNetHomesteadBlock // homestead block used to check against + TestNetHomesteadBlock = big.NewInt(494000) // testnet homestead block + MainNetHomesteadBlock = big.NewInt(1150000) // mainnet homestead block ) - -func IsHomestead(blockNumber *big.Int) bool { - // for unit tests TODO: flip to true after homestead is live - if blockNumber == nil { - return false - } - return blockNumber.Cmp(HomesteadBlock) >= 0 -} diff --git a/tests/block_test.go b/tests/block_test.go index 71dcc4615..c258268db 100644 --- a/tests/block_test.go +++ b/tests/block_test.go @@ -20,77 +20,66 @@ import ( "math/big" "path/filepath" "testing" - - "github.com/ethereum/go-ethereum/params" ) func TestBcValidBlockTests(t *testing.T) { - params.HomesteadBlock = big.NewInt(1000000) - err := RunBlockTest(filepath.Join(blockTestDir, "bcValidBlockTest.json"), BlockSkipTests) + err := RunBlockTest(big.NewInt(1000000), filepath.Join(blockTestDir, "bcValidBlockTest.json"), BlockSkipTests) if err != nil { t.Fatal(err) } } func TestBcUncleHeaderValidityTests(t *testing.T) { - params.HomesteadBlock = big.NewInt(1000000) - err := RunBlockTest(filepath.Join(blockTestDir, "bcUncleHeaderValiditiy.json"), BlockSkipTests) + err := RunBlockTest(big.NewInt(1000000), filepath.Join(blockTestDir, "bcUncleHeaderValiditiy.json"), BlockSkipTests) if err != nil { t.Fatal(err) } } func TestBcUncleTests(t *testing.T) { - params.HomesteadBlock = big.NewInt(1000000) - err := RunBlockTest(filepath.Join(blockTestDir, "bcUncleTest.json"), BlockSkipTests) + err := RunBlockTest(big.NewInt(1000000), filepath.Join(blockTestDir, "bcUncleTest.json"), BlockSkipTests) if err != nil { t.Fatal(err) } } func TestBcForkUncleTests(t *testing.T) { - params.HomesteadBlock = big.NewInt(1000000) - err := RunBlockTest(filepath.Join(blockTestDir, "bcForkUncle.json"), BlockSkipTests) + err := RunBlockTest(big.NewInt(1000000), filepath.Join(blockTestDir, "bcForkUncle.json"), BlockSkipTests) if err != nil { t.Fatal(err) } } func TestBcInvalidHeaderTests(t *testing.T) { - params.HomesteadBlock = big.NewInt(1000000) - err := RunBlockTest(filepath.Join(blockTestDir, "bcInvalidHeaderTest.json"), BlockSkipTests) + err := RunBlockTest(big.NewInt(1000000), filepath.Join(blockTestDir, "bcInvalidHeaderTest.json"), BlockSkipTests) if err != nil { t.Fatal(err) } } func TestBcInvalidRLPTests(t *testing.T) { - params.HomesteadBlock = big.NewInt(1000000) - err := RunBlockTest(filepath.Join(blockTestDir, "bcInvalidRLPTest.json"), BlockSkipTests) + err := RunBlockTest(big.NewInt(1000000), filepath.Join(blockTestDir, "bcInvalidRLPTest.json"), BlockSkipTests) if err != nil { t.Fatal(err) } } func TestBcRPCAPITests(t *testing.T) { - params.HomesteadBlock = big.NewInt(1000000) - err := RunBlockTest(filepath.Join(blockTestDir, "bcRPC_API_Test.json"), BlockSkipTests) + err := RunBlockTest(big.NewInt(1000000), filepath.Join(blockTestDir, "bcRPC_API_Test.json"), BlockSkipTests) if err != nil { t.Fatal(err) } } func TestBcForkBlockTests(t *testing.T) { - params.HomesteadBlock = big.NewInt(1000000) - err := RunBlockTest(filepath.Join(blockTestDir, "bcForkBlockTest.json"), BlockSkipTests) + err := RunBlockTest(big.NewInt(1000000), filepath.Join(blockTestDir, "bcForkBlockTest.json"), BlockSkipTests) if err != nil { t.Fatal(err) } } func TestBcForkStress(t *testing.T) { - params.HomesteadBlock = big.NewInt(1000000) - err := RunBlockTest(filepath.Join(blockTestDir, "bcForkStressTest.json"), BlockSkipTests) + err := RunBlockTest(big.NewInt(1000000), filepath.Join(blockTestDir, "bcForkStressTest.json"), BlockSkipTests) if err != nil { t.Fatal(err) } @@ -100,24 +89,21 @@ func TestBcTotalDifficulty(t *testing.T) { // skip because these will fail due to selfish mining fix t.Skip() - params.HomesteadBlock = big.NewInt(1000000) - err := RunBlockTest(filepath.Join(blockTestDir, "bcTotalDifficultyTest.json"), BlockSkipTests) + err := RunBlockTest(big.NewInt(1000000), filepath.Join(blockTestDir, "bcTotalDifficultyTest.json"), BlockSkipTests) if err != nil { t.Fatal(err) } } func TestBcWallet(t *testing.T) { - params.HomesteadBlock = big.NewInt(1000000) - err := RunBlockTest(filepath.Join(blockTestDir, "bcWalletTest.json"), BlockSkipTests) + err := RunBlockTest(big.NewInt(1000000), filepath.Join(blockTestDir, "bcWalletTest.json"), BlockSkipTests) if err != nil { t.Fatal(err) } } func TestBcGasPricer(t *testing.T) { - params.HomesteadBlock = big.NewInt(1000000) - err := RunBlockTest(filepath.Join(blockTestDir, "bcGasPricerTest.json"), BlockSkipTests) + err := RunBlockTest(big.NewInt(1000000), filepath.Join(blockTestDir, "bcGasPricerTest.json"), BlockSkipTests) if err != nil { t.Fatal(err) } @@ -125,8 +111,7 @@ func TestBcGasPricer(t *testing.T) { // TODO: iterate over files once we got more than a few func TestBcRandom(t *testing.T) { - params.HomesteadBlock = big.NewInt(1000000) - err := RunBlockTest(filepath.Join(blockTestDir, "RandomTests/bl201507071825GO.json"), BlockSkipTests) + err := RunBlockTest(big.NewInt(1000000), filepath.Join(blockTestDir, "RandomTests/bl201507071825GO.json"), BlockSkipTests) if err != nil { t.Fatal(err) } @@ -136,16 +121,14 @@ func TestBcMultiChain(t *testing.T) { // skip due to selfish mining t.Skip() - params.HomesteadBlock = big.NewInt(1000000) - err := RunBlockTest(filepath.Join(blockTestDir, "bcMultiChainTest.json"), BlockSkipTests) + err := RunBlockTest(big.NewInt(1000000), filepath.Join(blockTestDir, "bcMultiChainTest.json"), BlockSkipTests) if err != nil { t.Fatal(err) } } func TestBcState(t *testing.T) { - params.HomesteadBlock = big.NewInt(1000000) - err := RunBlockTest(filepath.Join(blockTestDir, "bcStateTest.json"), BlockSkipTests) + err := RunBlockTest(big.NewInt(1000000), filepath.Join(blockTestDir, "bcStateTest.json"), BlockSkipTests) if err != nil { t.Fatal(err) } @@ -153,88 +136,77 @@ func TestBcState(t *testing.T) { // Homestead tests func TestHomesteadBcValidBlockTests(t *testing.T) { - params.HomesteadBlock = big.NewInt(0) - err := RunBlockTest(filepath.Join(blockTestDir, "Homestead", "bcValidBlockTest.json"), BlockSkipTests) + err := RunBlockTest(big.NewInt(0), filepath.Join(blockTestDir, "Homestead", "bcValidBlockTest.json"), BlockSkipTests) if err != nil { t.Fatal(err) } } func TestHomesteadBcUncleHeaderValidityTests(t *testing.T) { - params.HomesteadBlock = big.NewInt(0) - err := RunBlockTest(filepath.Join(blockTestDir, "Homestead", "bcUncleHeaderValiditiy.json"), BlockSkipTests) + err := RunBlockTest(big.NewInt(0), filepath.Join(blockTestDir, "Homestead", "bcUncleHeaderValiditiy.json"), BlockSkipTests) if err != nil { t.Fatal(err) } } func TestHomesteadBcUncleTests(t *testing.T) { - params.HomesteadBlock = big.NewInt(0) - err := RunBlockTest(filepath.Join(blockTestDir, "Homestead", "bcUncleTest.json"), BlockSkipTests) + err := RunBlockTest(big.NewInt(0), filepath.Join(blockTestDir, "Homestead", "bcUncleTest.json"), BlockSkipTests) if err != nil { t.Fatal(err) } } func TestHomesteadBcInvalidHeaderTests(t *testing.T) { - params.HomesteadBlock = big.NewInt(0) - err := RunBlockTest(filepath.Join(blockTestDir, "Homestead", "bcInvalidHeaderTest.json"), BlockSkipTests) + err := RunBlockTest(big.NewInt(0), filepath.Join(blockTestDir, "Homestead", "bcInvalidHeaderTest.json"), BlockSkipTests) if err != nil { t.Fatal(err) } } func TestHomesteadBcRPCAPITests(t *testing.T) { - params.HomesteadBlock = big.NewInt(0) - err := RunBlockTest(filepath.Join(blockTestDir, "Homestead", "bcRPC_API_Test.json"), BlockSkipTests) + err := RunBlockTest(big.NewInt(0), filepath.Join(blockTestDir, "Homestead", "bcRPC_API_Test.json"), BlockSkipTests) if err != nil { t.Fatal(err) } } func TestHomesteadBcForkStress(t *testing.T) { - params.HomesteadBlock = big.NewInt(0) - err := RunBlockTest(filepath.Join(blockTestDir, "Homestead", "bcForkStressTest.json"), BlockSkipTests) + err := RunBlockTest(big.NewInt(0), filepath.Join(blockTestDir, "Homestead", "bcForkStressTest.json"), BlockSkipTests) if err != nil { t.Fatal(err) } } func TestHomesteadBcTotalDifficulty(t *testing.T) { - params.HomesteadBlock = big.NewInt(0) - err := RunBlockTest(filepath.Join(blockTestDir, "Homestead", "bcTotalDifficultyTest.json"), BlockSkipTests) + err := RunBlockTest(big.NewInt(0), filepath.Join(blockTestDir, "Homestead", "bcTotalDifficultyTest.json"), BlockSkipTests) if err != nil { t.Fatal(err) } } func TestHomesteadBcWallet(t *testing.T) { - params.HomesteadBlock = big.NewInt(0) - err := RunBlockTest(filepath.Join(blockTestDir, "Homestead", "bcWalletTest.json"), BlockSkipTests) + err := RunBlockTest(big.NewInt(0), filepath.Join(blockTestDir, "Homestead", "bcWalletTest.json"), BlockSkipTests) if err != nil { t.Fatal(err) } } func TestHomesteadBcGasPricer(t *testing.T) { - params.HomesteadBlock = big.NewInt(0) - err := RunBlockTest(filepath.Join(blockTestDir, "Homestead", "bcGasPricerTest.json"), BlockSkipTests) + err := RunBlockTest(big.NewInt(0), filepath.Join(blockTestDir, "Homestead", "bcGasPricerTest.json"), BlockSkipTests) if err != nil { t.Fatal(err) } } func TestHomesteadBcMultiChain(t *testing.T) { - params.HomesteadBlock = big.NewInt(0) - err := RunBlockTest(filepath.Join(blockTestDir, "Homestead", "bcMultiChainTest.json"), BlockSkipTests) + err := RunBlockTest(big.NewInt(0), filepath.Join(blockTestDir, "Homestead", "bcMultiChainTest.json"), BlockSkipTests) if err != nil { t.Fatal(err) } } func TestHomesteadBcState(t *testing.T) { - params.HomesteadBlock = big.NewInt(0) - err := RunBlockTest(filepath.Join(blockTestDir, "Homestead", "bcStateTest.json"), BlockSkipTests) + err := RunBlockTest(big.NewInt(0), filepath.Join(blockTestDir, "Homestead", "bcStateTest.json"), BlockSkipTests) if err != nil { t.Fatal(err) } diff --git a/tests/block_test_util.go b/tests/block_test_util.go index f517eddd1..482a9ae0a 100644 --- a/tests/block_test_util.go +++ b/tests/block_test_util.go @@ -108,7 +108,7 @@ type btTransaction struct { Value string } -func RunBlockTestWithReader(r io.Reader, skipTests []string) error { +func RunBlockTestWithReader(homesteadBlock *big.Int, r io.Reader, skipTests []string) error { btjs := make(map[string]*btJSON) if err := readJson(r, &btjs); err != nil { return err @@ -119,13 +119,13 @@ func RunBlockTestWithReader(r io.Reader, skipTests []string) error { return err } - if err := runBlockTests(bt, skipTests); err != nil { + if err := runBlockTests(homesteadBlock, bt, skipTests); err != nil { return err } return nil } -func RunBlockTest(file string, skipTests []string) error { +func RunBlockTest(homesteadBlock *big.Int, file string, skipTests []string) error { btjs := make(map[string]*btJSON) if err := readJsonFile(file, &btjs); err != nil { return err @@ -135,13 +135,13 @@ func RunBlockTest(file string, skipTests []string) error { if err != nil { return err } - if err := runBlockTests(bt, skipTests); err != nil { + if err := runBlockTests(homesteadBlock, bt, skipTests); err != nil { return err } return nil } -func runBlockTests(bt map[string]*BlockTest, skipTests []string) error { +func runBlockTests(homesteadBlock *big.Int, bt map[string]*BlockTest, skipTests []string) error { skipTest := make(map[string]bool, len(skipTests)) for _, name := range skipTests { skipTest[name] = true @@ -153,7 +153,7 @@ func runBlockTests(bt map[string]*BlockTest, skipTests []string) error { continue } // test the block - if err := runBlockTest(test); err != nil { + if err := runBlockTest(homesteadBlock, test); err != nil { return fmt.Errorf("%s: %v", name, err) } glog.Infoln("Block test passed: ", name) @@ -162,7 +162,7 @@ func runBlockTests(bt map[string]*BlockTest, skipTests []string) error { return nil } -func runBlockTest(test *BlockTest) error { +func runBlockTest(homesteadBlock *big.Int, test *BlockTest) error { ks := crypto.NewKeyStorePassphrase(filepath.Join(common.DefaultDataDir(), "keystore"), crypto.StandardScryptN, crypto.StandardScryptP) am := accounts.NewManager(ks) db, _ := ethdb.NewMemDatabase() @@ -174,6 +174,7 @@ func runBlockTest(test *BlockTest) error { } cfg := ð.Config{ + ChainConfig: &core.ChainConfig{HomesteadBlock: homesteadBlock}, TestGenesisState: db, TestGenesisBlock: test.Genesis, Etherbase: common.Address{}, diff --git a/tests/state_test.go b/tests/state_test.go index 4c1820944..59646fd7e 100644 --- a/tests/state_test.go +++ b/tests/state_test.go @@ -21,18 +21,8 @@ import ( "os" "path/filepath" "testing" - - "github.com/ethereum/go-ethereum/core/vm" - "github.com/ethereum/go-ethereum/params" ) -func init() { - if os.Getenv("JITVM") == "true" { - vm.ForceJit = true - vm.EnableJit = true - } -} - func BenchmarkStateCall1024(b *testing.B) { fn := filepath.Join(stateTestDir, "stCallCreateCallCodeTest.json") if err := BenchVmTest(fn, bconf{"Call1024BalanceTooLow", true, os.Getenv("JITVM") == "true"}, b); err != nil { @@ -41,188 +31,228 @@ func BenchmarkStateCall1024(b *testing.B) { } func TestStateSystemOperations(t *testing.T) { - params.HomesteadBlock = big.NewInt(1000000) + ruleSet := RuleSet{ + HomesteadBlock: big.NewInt(1000000), + } fn := filepath.Join(stateTestDir, "stSystemOperationsTest.json") - if err := RunStateTest(fn, StateSkipTests); err != nil { + if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil { t.Error(err) } } func TestStateExample(t *testing.T) { - params.HomesteadBlock = big.NewInt(1000000) + ruleSet := RuleSet{ + HomesteadBlock: big.NewInt(1000000), + } fn := filepath.Join(stateTestDir, "stExample.json") - if err := RunStateTest(fn, StateSkipTests); err != nil { + if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil { t.Error(err) } } func TestStatePreCompiledContracts(t *testing.T) { - params.HomesteadBlock = big.NewInt(1000000) + ruleSet := RuleSet{ + HomesteadBlock: big.NewInt(1000000), + } fn := filepath.Join(stateTestDir, "stPreCompiledContracts.json") - if err := RunStateTest(fn, StateSkipTests); err != nil { + if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil { t.Error(err) } } func TestStateRecursiveCreate(t *testing.T) { - params.HomesteadBlock = big.NewInt(1000000) + ruleSet := RuleSet{ + HomesteadBlock: big.NewInt(1000000), + } fn := filepath.Join(stateTestDir, "stRecursiveCreate.json") - if err := RunStateTest(fn, StateSkipTests); err != nil { + if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil { t.Error(err) } } func TestStateSpecial(t *testing.T) { - params.HomesteadBlock = big.NewInt(1000000) + ruleSet := RuleSet{ + HomesteadBlock: big.NewInt(1000000), + } fn := filepath.Join(stateTestDir, "stSpecialTest.json") - if err := RunStateTest(fn, StateSkipTests); err != nil { + if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil { t.Error(err) } } func TestStateRefund(t *testing.T) { - params.HomesteadBlock = big.NewInt(1000000) + ruleSet := RuleSet{ + HomesteadBlock: big.NewInt(1000000), + } fn := filepath.Join(stateTestDir, "stRefundTest.json") - if err := RunStateTest(fn, StateSkipTests); err != nil { + if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil { t.Error(err) } } func TestStateBlockHash(t *testing.T) { - params.HomesteadBlock = big.NewInt(1000000) + ruleSet := RuleSet{ + HomesteadBlock: big.NewInt(1000000), + } fn := filepath.Join(stateTestDir, "stBlockHashTest.json") - if err := RunStateTest(fn, StateSkipTests); err != nil { + if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil { t.Error(err) } } func TestStateInitCode(t *testing.T) { - params.HomesteadBlock = big.NewInt(1000000) + ruleSet := RuleSet{ + HomesteadBlock: big.NewInt(1000000), + } fn := filepath.Join(stateTestDir, "stInitCodeTest.json") - if err := RunStateTest(fn, StateSkipTests); err != nil { + if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil { t.Error(err) } } func TestStateLog(t *testing.T) { - params.HomesteadBlock = big.NewInt(1000000) + ruleSet := RuleSet{ + HomesteadBlock: big.NewInt(1000000), + } fn := filepath.Join(stateTestDir, "stLogTests.json") - if err := RunStateTest(fn, StateSkipTests); err != nil { + if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil { t.Error(err) } } func TestStateTransaction(t *testing.T) { - params.HomesteadBlock = big.NewInt(1000000) + ruleSet := RuleSet{ + HomesteadBlock: big.NewInt(1000000), + } fn := filepath.Join(stateTestDir, "stTransactionTest.json") - if err := RunStateTest(fn, StateSkipTests); err != nil { + if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil { t.Error(err) } } func TestStateTransition(t *testing.T) { - params.HomesteadBlock = big.NewInt(1000000) + ruleSet := RuleSet{ + HomesteadBlock: big.NewInt(1000000), + } fn := filepath.Join(stateTestDir, "stTransitionTest.json") - if err := RunStateTest(fn, StateSkipTests); err != nil { + if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil { t.Error(err) } } func TestCallCreateCallCode(t *testing.T) { - params.HomesteadBlock = big.NewInt(1000000) + ruleSet := RuleSet{ + HomesteadBlock: big.NewInt(1000000), + } fn := filepath.Join(stateTestDir, "stCallCreateCallCodeTest.json") - if err := RunStateTest(fn, StateSkipTests); err != nil { + if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil { t.Error(err) } } func TestCallCodes(t *testing.T) { - params.HomesteadBlock = big.NewInt(1000000) + ruleSet := RuleSet{ + HomesteadBlock: big.NewInt(1000000), + } fn := filepath.Join(stateTestDir, "stCallCodes.json") - if err := RunStateTest(fn, StateSkipTests); err != nil { + if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil { t.Error(err) } } func TestDelegateCall(t *testing.T) { - params.HomesteadBlock = big.NewInt(1000000) + ruleSet := RuleSet{ + HomesteadBlock: big.NewInt(1000000), + } fn := filepath.Join(stateTestDir, "stDelegatecallTest.json") - if err := RunStateTest(fn, StateSkipTests); err != nil { + if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil { t.Error(err) } } func TestMemory(t *testing.T) { - params.HomesteadBlock = big.NewInt(1000000) + ruleSet := RuleSet{ + HomesteadBlock: big.NewInt(1000000), + } fn := filepath.Join(stateTestDir, "stMemoryTest.json") - if err := RunStateTest(fn, StateSkipTests); err != nil { + if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil { t.Error(err) } } func TestMemoryStress(t *testing.T) { - params.HomesteadBlock = big.NewInt(1000000) + ruleSet := RuleSet{ + HomesteadBlock: big.NewInt(1000000), + } if os.Getenv("TEST_VM_COMPLEX") == "" { t.Skip() } fn := filepath.Join(stateTestDir, "stMemoryStressTest.json") - if err := RunStateTest(fn, StateSkipTests); err != nil { + if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil { t.Error(err) } } func TestQuadraticComplexity(t *testing.T) { - params.HomesteadBlock = big.NewInt(1000000) + ruleSet := RuleSet{ + HomesteadBlock: big.NewInt(1000000), + } if os.Getenv("TEST_VM_COMPLEX") == "" { t.Skip() } fn := filepath.Join(stateTestDir, "stQuadraticComplexityTest.json") - if err := RunStateTest(fn, StateSkipTests); err != nil { + if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil { t.Error(err) } } func TestSolidity(t *testing.T) { - params.HomesteadBlock = big.NewInt(1000000) + ruleSet := RuleSet{ + HomesteadBlock: big.NewInt(1000000), + } fn := filepath.Join(stateTestDir, "stSolidityTest.json") - if err := RunStateTest(fn, StateSkipTests); err != nil { + if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil { t.Error(err) } } func TestWallet(t *testing.T) { - params.HomesteadBlock = big.NewInt(1000000) + ruleSet := RuleSet{ + HomesteadBlock: big.NewInt(1000000), + } fn := filepath.Join(stateTestDir, "stWalletTest.json") - if err := RunStateTest(fn, StateSkipTests); err != nil { + if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil { t.Error(err) } } func TestStateTestsRandom(t *testing.T) { - params.HomesteadBlock = big.NewInt(1000000) + ruleSet := RuleSet{ + HomesteadBlock: big.NewInt(1000000), + } fns, _ := filepath.Glob("./files/StateTests/RandomTests/*") for _, fn := range fns { - if err := RunStateTest(fn, StateSkipTests); err != nil { + if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil { t.Error(err) } } @@ -230,151 +260,172 @@ func TestStateTestsRandom(t *testing.T) { // homestead tests func TestHomesteadStateSystemOperations(t *testing.T) { - params.HomesteadBlock = big.NewInt(0) + ruleSet := RuleSet{ + HomesteadBlock: new(big.Int), + } fn := filepath.Join(stateTestDir, "Homestead", "stSystemOperationsTest.json") - if err := RunStateTest(fn, StateSkipTests); err != nil { + if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil { t.Error(err) } } func TestHomesteadStatePreCompiledContracts(t *testing.T) { - params.HomesteadBlock = big.NewInt(0) + ruleSet := RuleSet{ + HomesteadBlock: new(big.Int), + } fn := filepath.Join(stateTestDir, "Homestead", "stPreCompiledContracts.json") - if err := RunStateTest(fn, StateSkipTests); err != nil { + if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil { t.Error(err) } } func TestHomesteadStateRecursiveCreate(t *testing.T) { - params.HomesteadBlock = big.NewInt(0) - - fn := filepath.Join(stateTestDir, "Homestead", "stRecursiveCreate.json") - if err := RunStateTest(fn, StateSkipTests); err != nil { - t.Error(err) + ruleSet := RuleSet{ + HomesteadBlock: new(big.Int), } -} - -func TestHomesteadStateSpecial(t *testing.T) { - params.HomesteadBlock = big.NewInt(0) fn := filepath.Join(stateTestDir, "Homestead", "stSpecialTest.json") - if err := RunStateTest(fn, StateSkipTests); err != nil { + if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil { t.Error(err) } } func TestHomesteadStateRefund(t *testing.T) { - params.HomesteadBlock = big.NewInt(0) + ruleSet := RuleSet{ + HomesteadBlock: new(big.Int), + } fn := filepath.Join(stateTestDir, "Homestead", "stRefundTest.json") - if err := RunStateTest(fn, StateSkipTests); err != nil { + if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil { t.Error(err) } } func TestHomesteadStateInitCode(t *testing.T) { - params.HomesteadBlock = big.NewInt(0) + ruleSet := RuleSet{ + HomesteadBlock: new(big.Int), + } fn := filepath.Join(stateTestDir, "Homestead", "stInitCodeTest.json") - if err := RunStateTest(fn, StateSkipTests); err != nil { + if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil { t.Error(err) } } func TestHomesteadStateLog(t *testing.T) { - params.HomesteadBlock = big.NewInt(0) + ruleSet := RuleSet{ + HomesteadBlock: new(big.Int), + } fn := filepath.Join(stateTestDir, "Homestead", "stLogTests.json") - if err := RunStateTest(fn, StateSkipTests); err != nil { + if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil { t.Error(err) } } func TestHomesteadStateTransaction(t *testing.T) { - params.HomesteadBlock = big.NewInt(0) + ruleSet := RuleSet{ + HomesteadBlock: new(big.Int), + } fn := filepath.Join(stateTestDir, "Homestead", "stTransactionTest.json") - if err := RunStateTest(fn, StateSkipTests); err != nil { + if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil { t.Error(err) } } func TestHomesteadCallCreateCallCode(t *testing.T) { - params.HomesteadBlock = big.NewInt(0) + ruleSet := RuleSet{ + HomesteadBlock: new(big.Int), + } fn := filepath.Join(stateTestDir, "Homestead", "stCallCreateCallCodeTest.json") - if err := RunStateTest(fn, StateSkipTests); err != nil { + if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil { t.Error(err) } } func TestHomesteadCallCodes(t *testing.T) { - params.HomesteadBlock = big.NewInt(0) + ruleSet := RuleSet{ + HomesteadBlock: new(big.Int), + } fn := filepath.Join(stateTestDir, "Homestead", "stCallCodes.json") - if err := RunStateTest(fn, StateSkipTests); err != nil { + if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil { t.Error(err) } } func TestHomesteadMemory(t *testing.T) { - params.HomesteadBlock = big.NewInt(0) + ruleSet := RuleSet{ + HomesteadBlock: new(big.Int), + } fn := filepath.Join(stateTestDir, "Homestead", "stMemoryTest.json") - if err := RunStateTest(fn, StateSkipTests); err != nil { + if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil { t.Error(err) } } func TestHomesteadMemoryStress(t *testing.T) { - params.HomesteadBlock = big.NewInt(0) + ruleSet := RuleSet{ + HomesteadBlock: new(big.Int), + } if os.Getenv("TEST_VM_COMPLEX") == "" { t.Skip() } fn := filepath.Join(stateTestDir, "Homestead", "stMemoryStressTest.json") - if err := RunStateTest(fn, StateSkipTests); err != nil { + if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil { t.Error(err) } } func TestHomesteadQuadraticComplexity(t *testing.T) { - params.HomesteadBlock = big.NewInt(0) + ruleSet := RuleSet{ + HomesteadBlock: new(big.Int), + } if os.Getenv("TEST_VM_COMPLEX") == "" { t.Skip() } fn := filepath.Join(stateTestDir, "Homestead", "stQuadraticComplexityTest.json") - if err := RunStateTest(fn, StateSkipTests); err != nil { + if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil { t.Error(err) } } func TestHomesteadWallet(t *testing.T) { - params.HomesteadBlock = big.NewInt(0) + ruleSet := RuleSet{ + HomesteadBlock: new(big.Int), + } fn := filepath.Join(stateTestDir, "Homestead", "stWalletTest.json") - if err := RunStateTest(fn, StateSkipTests); err != nil { + if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil { t.Error(err) } } func TestHomesteadDelegateCodes(t *testing.T) { - params.HomesteadBlock = big.NewInt(0) + ruleSet := RuleSet{ + HomesteadBlock: new(big.Int), + } fn := filepath.Join(stateTestDir, "Homestead", "stCallDelegateCodes.json") - if err := RunStateTest(fn, StateSkipTests); err != nil { + if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil { t.Error(err) } } func TestHomesteadDelegateCodesCallCode(t *testing.T) { - params.HomesteadBlock = big.NewInt(0) + ruleSet := RuleSet{ + HomesteadBlock: new(big.Int), + } fn := filepath.Join(stateTestDir, "Homestead", "stCallDelegateCodesCallCode.json") - if err := RunStateTest(fn, StateSkipTests); err != nil { + if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil { t.Error(err) } } diff --git a/tests/state_test_util.go b/tests/state_test_util.go index 50be3a1ac..9d6069ac7 100644 --- a/tests/state_test_util.go +++ b/tests/state_test_util.go @@ -34,26 +34,26 @@ import ( "github.com/ethereum/go-ethereum/logger/glog" ) -func RunStateTestWithReader(r io.Reader, skipTests []string) error { +func RunStateTestWithReader(ruleSet RuleSet, r io.Reader, skipTests []string) error { tests := make(map[string]VmTest) if err := readJson(r, &tests); err != nil { return err } - if err := runStateTests(tests, skipTests); err != nil { + if err := runStateTests(ruleSet, tests, skipTests); err != nil { return err } return nil } -func RunStateTest(p string, skipTests []string) error { +func RunStateTest(ruleSet RuleSet, p string, skipTests []string) error { tests := make(map[string]VmTest) if err := readJsonFile(p, &tests); err != nil { return err } - if err := runStateTests(tests, skipTests); err != nil { + if err := runStateTests(ruleSet, tests, skipTests); err != nil { return err } @@ -61,7 +61,7 @@ func RunStateTest(p string, skipTests []string) error { } -func BenchStateTest(p string, conf bconf, b *testing.B) error { +func BenchStateTest(ruleSet RuleSet, p string, conf bconf, b *testing.B) error { tests := make(map[string]VmTest) if err := readJsonFile(p, &tests); err != nil { return err @@ -71,11 +71,6 @@ func BenchStateTest(p string, conf bconf, b *testing.B) error { return fmt.Errorf("test not found: %s", conf.name) } - pJit := vm.EnableJit - vm.EnableJit = conf.jit - pForceJit := vm.ForceJit - vm.ForceJit = conf.precomp - // XXX Yeah, yeah... env := make(map[string]string) env["currentCoinbase"] = test.Env.CurrentCoinbase @@ -91,16 +86,13 @@ func BenchStateTest(p string, conf bconf, b *testing.B) error { b.ResetTimer() for i := 0; i < b.N; i++ { - benchStateTest(test, env, b) + benchStateTest(ruleSet, test, env, b) } - vm.EnableJit = pJit - vm.ForceJit = pForceJit - return nil } -func benchStateTest(test VmTest, env map[string]string, b *testing.B) { +func benchStateTest(ruleSet RuleSet, test VmTest, env map[string]string, b *testing.B) { b.StopTimer() db, _ := ethdb.NewMemDatabase() statedb, _ := state.New(common.Hash{}, db) @@ -113,10 +105,10 @@ func benchStateTest(test VmTest, env map[string]string, b *testing.B) { } b.StartTimer() - RunState(statedb, env, test.Exec) + RunState(ruleSet, statedb, env, test.Exec) } -func runStateTests(tests map[string]VmTest, skipTests []string) error { +func runStateTests(ruleSet RuleSet, tests map[string]VmTest, skipTests []string) error { skipTest := make(map[string]bool, len(skipTests)) for _, name := range skipTests { skipTest[name] = true @@ -129,7 +121,7 @@ func runStateTests(tests map[string]VmTest, skipTests []string) error { } //fmt.Println("StateTest:", name) - if err := runStateTest(test); err != nil { + if err := runStateTest(ruleSet, test); err != nil { return fmt.Errorf("%s: %s\n", name, err.Error()) } @@ -140,7 +132,7 @@ func runStateTests(tests map[string]VmTest, skipTests []string) error { } -func runStateTest(test VmTest) error { +func runStateTest(ruleSet RuleSet, test VmTest) error { db, _ := ethdb.NewMemDatabase() statedb, _ := state.New(common.Hash{}, db) for addr, account := range test.Pre { @@ -171,7 +163,7 @@ func runStateTest(test VmTest) error { logs vm.Logs ) - ret, logs, _, _ = RunState(statedb, env, test.Transaction) + ret, logs, _, _ = RunState(ruleSet, statedb, env, test.Transaction) // Compare expected and actual return rexp := common.FromHex(test.Out) @@ -219,7 +211,7 @@ func runStateTest(test VmTest) error { return nil } -func RunState(statedb *state.StateDB, env, tx map[string]string) ([]byte, vm.Logs, *big.Int, error) { +func RunState(ruleSet RuleSet, statedb *state.StateDB, env, tx map[string]string) ([]byte, vm.Logs, *big.Int, error) { var ( data = common.FromHex(tx["data"]) gas = common.Big(tx["gasLimit"]) @@ -241,7 +233,7 @@ func RunState(statedb *state.StateDB, env, tx map[string]string) ([]byte, vm.Log key, _ := hex.DecodeString(tx["secretKey"]) addr := crypto.PubkeyToAddress(crypto.ToECDSA(key).PublicKey) message := NewMessage(addr, to, data, value, gas, price, nonce) - vmenv := NewEnvFromMap(statedb, env, tx) + vmenv := NewEnvFromMap(ruleSet, statedb, env, tx) vmenv.origin = addr ret, _, err := core.ApplyMessage(vmenv, message, gaspool) if core.IsNonceErr(err) || core.IsInvalidTxErr(err) || core.IsGasLimitErr(err) { diff --git a/tests/transaction_test_util.go b/tests/transaction_test_util.go index 6302b9d5b..178f90284 100644 --- a/tests/transaction_test_util.go +++ b/tests/transaction_test_util.go @@ -21,10 +21,10 @@ import ( "errors" "fmt" "io" - "math/big" "runtime" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/params" @@ -91,8 +91,6 @@ func RunTransactionTests(file string, skipTests []string) error { } func runTransactionTests(tests map[string]TransactionTest, skipTests []string) error { - params.HomesteadBlock = big.NewInt(900000) - skipTest := make(map[string]bool, len(skipTests)) for _, name := range skipTests { skipTest[name] = true @@ -166,7 +164,8 @@ func verifyTxFields(txTest TransactionTest, decodedTx *types.Transaction) (err e decodedSender common.Address ) - if params.IsHomestead(common.String2Big(txTest.Blocknumber)) { + chainConfig := &core.ChainConfig{HomesteadBlock: params.MainNetHomesteadBlock} + if chainConfig.IsHomestead(common.String2Big(txTest.Blocknumber)) { decodedSender, err = decodedTx.From() } else { decodedSender, err = decodedTx.FromFrontier() diff --git a/tests/util.go b/tests/util.go index a0eb8158e..9cb43ccb1 100644 --- a/tests/util.go +++ b/tests/util.go @@ -20,6 +20,7 @@ import ( "bytes" "fmt" "math/big" + "os" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core" @@ -31,8 +32,17 @@ import ( "github.com/ethereum/go-ethereum/logger/glog" ) +var ( + ForceJit bool + EnableJit bool +) + func init() { glog.SetV(0) + if os.Getenv("JITVM") == "true" { + ForceJit = true + EnableJit = true + } } func checkLogs(tlog []Log, logs vm.Logs) error { @@ -129,7 +139,16 @@ type VmTest struct { PostStateRoot string } +type RuleSet struct { + HomesteadBlock *big.Int +} + +func (r RuleSet) IsHomestead(n *big.Int) bool { + return n.Cmp(r.HomesteadBlock) >= 0 +} + type Env struct { + ruleSet RuleSet depth int state *state.StateDB skipTransfer bool @@ -152,9 +171,10 @@ type Env struct { evm *vm.EVM } -func NewEnv(state *state.StateDB) *Env { +func NewEnv(ruleSet RuleSet, state *state.StateDB) *Env { env := &Env{ - state: state, + ruleSet: ruleSet, + state: state, } return env } @@ -167,8 +187,8 @@ func (self *Env) AddStructLog(log vm.StructLog) { self.logs = append(self.logs, log) } -func NewEnvFromMap(state *state.StateDB, envValues map[string]string, exeValues map[string]string) *Env { - env := NewEnv(state) +func NewEnvFromMap(ruleSet RuleSet, state *state.StateDB, envValues map[string]string, exeValues map[string]string) *Env { + env := NewEnv(ruleSet, state) env.origin = common.HexToAddress(exeValues["caller"]) env.parent = common.HexToHash(envValues["previousHash"]) @@ -179,11 +199,15 @@ func NewEnvFromMap(state *state.StateDB, envValues map[string]string, exeValues env.gasLimit = common.Big(envValues["currentGasLimit"]) env.Gas = new(big.Int) - env.evm = vm.New(env, nil) + env.evm = vm.New(env, vm.Config{ + EnableJit: EnableJit, + ForceJit: ForceJit, + }) return env } +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 } func (self *Env) BlockNumber() *big.Int { return self.number } diff --git a/tests/vm_test_util.go b/tests/vm_test_util.go index ddd14b1a3..d17c59905 100644 --- a/tests/vm_test_util.go +++ b/tests/vm_test_util.go @@ -29,6 +29,7 @@ import ( "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/params" ) func RunVmTestWithReader(r io.Reader, skipTests []string) error { @@ -67,11 +68,6 @@ func BenchVmTest(p string, conf bconf, b *testing.B) error { return fmt.Errorf("test not found: %s", conf.name) } - pJit := vm.EnableJit - vm.EnableJit = conf.jit - pForceJit := vm.ForceJit - vm.ForceJit = conf.precomp - env := make(map[string]string) env["currentCoinbase"] = test.Env.CurrentCoinbase env["currentDifficulty"] = test.Env.CurrentDifficulty @@ -99,9 +95,6 @@ func BenchVmTest(p string, conf bconf, b *testing.B) error { benchVmTest(test, env, b) } - vm.EnableJit = pJit - vm.ForceJit = pForceJit - return nil } @@ -248,7 +241,7 @@ func RunVm(state *state.StateDB, env, exec map[string]string) ([]byte, vm.Logs, caller := state.GetOrNewStateObject(from) - vmenv := NewEnvFromMap(state, env, exec) + vmenv := NewEnvFromMap(RuleSet{params.MainNetHomesteadBlock}, state, env, exec) vmenv.vmTest = true vmenv.skipTransfer = true vmenv.initial = true -- cgit v1.2.3 From 9055c16efad80d0c69992e7992083f967733aa9c Mon Sep 17 00:00:00 2001 From: Jeffrey Wilcke Date: Thu, 31 Mar 2016 17:43:41 +0200 Subject: accounts/a/b/backends, core: chain maker homestead block set to 0 The chain maker and the simulated backend now run with a homestead phase beginning at block 0 (i.e. there's no frontier). This commit also fixes up #2388 --- accounts/abi/bind/backends/simulated.go | 3 +-- core/block_validator_test.go | 3 +-- core/blockchain_test.go | 10 ++++------ core/chain_makers.go | 21 ++++++--------------- core/chain_makers_test.go | 2 +- eth/helper_test.go | 3 +-- 6 files changed, 14 insertions(+), 28 deletions(-) diff --git a/accounts/abi/bind/backends/simulated.go b/accounts/abi/bind/backends/simulated.go index 43d490988..213a4f1a7 100644 --- a/accounts/abi/bind/backends/simulated.go +++ b/accounts/abi/bind/backends/simulated.go @@ -27,11 +27,10 @@ import ( "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" - "github.com/ethereum/go-ethereum/params" ) // Default chain configuration which sets homestead phase at block 0 (i.e. no frontier) -var chainConfig = &core.ChainConfig{HomesteadBlock: params.MainNetHomesteadBlock} +var chainConfig = &core.ChainConfig{HomesteadBlock: big.NewInt(0)} // This nil assignment ensures compile time that SimulatedBackend implements bind.ContractBackend. var _ bind.ContractBackend = (*SimulatedBackend)(nil) diff --git a/core/block_validator_test.go b/core/block_validator_test.go index aa29717b1..c6daf9e7f 100644 --- a/core/block_validator_test.go +++ b/core/block_validator_test.go @@ -27,12 +27,11 @@ import ( "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" - "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/pow/ezp" ) func testChainConfig() *ChainConfig { - return &ChainConfig{HomesteadBlock: params.MainNetHomesteadBlock} + return &ChainConfig{HomesteadBlock: big.NewInt(0)} } func proc() (Validator, *BlockChain) { diff --git a/core/blockchain_test.go b/core/blockchain_test.go index 9e59b2ff6..876dd2ba1 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -1025,23 +1025,21 @@ func TestReorgSideEvent(t *testing.T) { evmux := &event.TypeMux{} blockchain, _ := NewBlockChain(db, testChainConfig(), FakePow{}, evmux) - chain, _ := GenerateChain(genesis, db, 3, func(i int, gen *BlockGen) { - if i == 2 { - gen.OffsetTime(9) - } - }) + chain, _ := GenerateChain(genesis, db, 3, func(i int, gen *BlockGen) {}) if _, err := blockchain.InsertChain(chain); err != nil { t.Fatalf("failed to insert chain: %v", err) } replacementBlocks, _ := GenerateChain(genesis, db, 4, func(i int, gen *BlockGen) { tx, err := types.NewContractCreation(gen.TxNonce(addr1), new(big.Int), big.NewInt(1000000), new(big.Int), nil).SignECDSA(key1) + if i == 2 { + gen.OffsetTime(-1) + } if err != nil { t.Fatalf("failed to create tx: %v", err) } gen.AddTx(tx) }) - subs := evmux.Subscribe(ChainSideEvent{}) if _, err := blockchain.InsertChain(replacementBlocks); err != nil { t.Fatalf("failed to insert chain: %v", err) diff --git a/core/chain_makers.go b/core/chain_makers.go index c740f9c37..ef0ac66d1 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -26,7 +26,6 @@ import ( "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" - "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/pow" ) @@ -35,16 +34,8 @@ import ( */ // MakeChainConfig returns a new ChainConfig with the ethereum default chain settings. -func MakeChainConfig(testnet bool) *ChainConfig { - homesteadBlock := params.MainNetHomesteadBlock - // set a different default homestead block for the testnet - if testnet { - homesteadBlock = params.TestNetHomesteadBlock - } - - return &ChainConfig{ - HomesteadBlock: homesteadBlock, - } +func MakeChainConfig() *ChainConfig { + return &ChainConfig{HomesteadBlock: big.NewInt(0)} } // FakePow is a non-validating proof of work implementation. @@ -110,7 +101,7 @@ func (b *BlockGen) AddTx(tx *types.Transaction) { b.SetCoinbase(common.Address{}) } b.statedb.StartRecord(tx.Hash(), common.Hash{}, len(b.txs)) - receipt, _, _, err := ApplyTransaction(MakeChainConfig(true), nil, b.gasPool, b.statedb, b.header, tx, b.header.GasUsed, vm.Config{}) + receipt, _, _, err := ApplyTransaction(MakeChainConfig(), nil, b.gasPool, b.statedb, b.header, tx, b.header.GasUsed, vm.Config{}) if err != nil { panic(err) } @@ -167,7 +158,7 @@ func (b *BlockGen) OffsetTime(seconds int64) { if b.header.Time.Cmp(b.parent.Header().Time) <= 0 { panic("block time out of range") } - b.header.Difficulty = CalcDifficulty(MakeChainConfig(true), b.header.Time.Uint64(), b.parent.Time().Uint64(), b.parent.Number(), b.parent.Difficulty()) + b.header.Difficulty = CalcDifficulty(MakeChainConfig(), b.header.Time.Uint64(), b.parent.Time().Uint64(), b.parent.Number(), b.parent.Difficulty()) } // GenerateChain creates a chain of n blocks. The first block's @@ -222,7 +213,7 @@ func makeHeader(parent *types.Block, state *state.StateDB) *types.Header { Root: state.IntermediateRoot(), ParentHash: parent.Hash(), Coinbase: parent.Coinbase(), - Difficulty: CalcDifficulty(MakeChainConfig(true), time.Uint64(), new(big.Int).Sub(time, big.NewInt(10)).Uint64(), parent.Number(), parent.Difficulty()), + Difficulty: CalcDifficulty(MakeChainConfig(), time.Uint64(), new(big.Int).Sub(time, big.NewInt(10)).Uint64(), parent.Number(), parent.Difficulty()), GasLimit: CalcGasLimit(parent), GasUsed: new(big.Int), Number: new(big.Int).Add(parent.Number(), common.Big1), @@ -241,7 +232,7 @@ func newCanonical(n int, full bool) (ethdb.Database, *BlockChain, error) { // Initialize a fresh chain with only a genesis block genesis, _ := WriteTestNetGenesisBlock(db) - blockchain, _ := NewBlockChain(db, MakeChainConfig(false), FakePow{}, evmux) + blockchain, _ := NewBlockChain(db, MakeChainConfig(), FakePow{}, evmux) // Create and inject the requested chain if n == 0 { return db, blockchain, nil diff --git a/core/chain_makers_test.go b/core/chain_makers_test.go index 86949246b..32c3efe8d 100644 --- a/core/chain_makers_test.go +++ b/core/chain_makers_test.go @@ -77,7 +77,7 @@ func ExampleGenerateChain() { // Import the chain. This runs all block validation rules. evmux := &event.TypeMux{} - blockchain, _ := NewBlockChain(db, MakeChainConfig(true), FakePow{}, evmux) + blockchain, _ := NewBlockChain(db, MakeChainConfig(), FakePow{}, evmux) if i, err := blockchain.InsertChain(chain); err != nil { fmt.Printf("insert error (block %d): %v\n", i, err) return diff --git a/eth/helper_test.go b/eth/helper_test.go index 5d141ce56..13de18670 100644 --- a/eth/helper_test.go +++ b/eth/helper_test.go @@ -17,7 +17,6 @@ import ( "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p/discover" - "github.com/ethereum/go-ethereum/params" ) var ( @@ -35,7 +34,7 @@ func newTestProtocolManager(fastSync bool, blocks int, generator func(int, *core pow = new(core.FakePow) db, _ = ethdb.NewMemDatabase() genesis = core.WriteGenesisBlockForTesting(db, core.GenesisAccount{testBankAddress, testBankFunds}) - chainConfig = &core.ChainConfig{HomesteadBlock: params.MainNetHomesteadBlock} + chainConfig = &core.ChainConfig{HomesteadBlock: big.NewInt(0)} // homestead set to 0 because of chain maker blockchain, _ = core.NewBlockChain(db, chainConfig, pow, evmux) ) chain, _ := core.GenerateChain(genesis, db, blocks, generator) -- cgit v1.2.3 From 1f3596c25af077a3303c554ee6b49404b20f7117 Mon Sep 17 00:00:00 2001 From: Jeffrey Wilcke Date: Fri, 1 Apr 2016 00:30:21 +0200 Subject: core: transition db now also returns the required gas amount Exposes some core methods to transition and compute new state information and adds an additional return value to the transition db method to fetch required gas for that particular message (excluding gas refunds from any SSTORE[X] = 0 and SUICIDE. Fixes #2395 --- core/state_transition.go | 31 ++++++++++++++++++++++++------- eth/api.go | 6 +++--- 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/core/state_transition.go b/core/state_transition.go index 60ac7d8cb..c8160424b 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -104,8 +104,9 @@ func IntrinsicGas(data []byte, contractCreation, homestead bool) *big.Int { return igas } -func ApplyMessage(env vm.Environment, msg Message, gp *GasPool) ([]byte, *big.Int, error) { - var st = StateTransition{ +// NewStateTransition initialises and returns a new state transition object. +func NewStateTransition(env vm.Environment, msg Message, gp *GasPool) *StateTransition { + return &StateTransition{ gp: gp, env: env, msg: msg, @@ -116,7 +117,20 @@ func ApplyMessage(env vm.Environment, msg Message, gp *GasPool) ([]byte, *big.In data: msg.Data(), state: env.Db(), } - return st.transitionDb() +} + +// ApplyMessage computes the new state by applying the given message +// against the old state within the environment. +// +// ApplyMessage returns the bytes returned by any EVM execution (if it took place), +// the gas used (which includes gas refunds) and an error if it failed. An error always +// indicates a core error meaning that the message would always fail for that particular +// state and would never be accepted within a block. +func ApplyMessage(env vm.Environment, msg Message, gp *GasPool) ([]byte, *big.Int, error) { + st := NewStateTransition(env, msg, gp) + + ret, _, gasUsed, err := st.TransitionDb() + return ret, gasUsed, err } func (self *StateTransition) from() (vm.Account, error) { @@ -209,7 +223,8 @@ func (self *StateTransition) preCheck() (err error) { return nil } -func (self *StateTransition) transitionDb() (ret []byte, usedGas *big.Int, err error) { +// TransitionDb will move the state by applying the message against the given environment. +func (self *StateTransition) TransitionDb() (ret []byte, requiredGas, usedGas *big.Int, err error) { if err = self.preCheck(); err != nil { return } @@ -220,7 +235,7 @@ func (self *StateTransition) transitionDb() (ret []byte, usedGas *big.Int, err e contractCreation := MessageCreatesContract(msg) // Pay intrinsic gas if err = self.useGas(IntrinsicGas(self.data, contractCreation, homestead)); err != nil { - return nil, nil, InvalidTxError(err) + return nil, nil, nil, InvalidTxError(err) } vmenv := self.env @@ -245,7 +260,7 @@ func (self *StateTransition) transitionDb() (ret []byte, usedGas *big.Int, err e } if err != nil && IsValueTransferErr(err) { - return nil, nil, InvalidTxError(err) + return nil, nil, nil, InvalidTxError(err) } // We aren't interested in errors here. Errors returned by the VM are non-consensus errors and therefor shouldn't bubble up @@ -253,10 +268,12 @@ func (self *StateTransition) transitionDb() (ret []byte, usedGas *big.Int, err e err = nil } + requiredGas = new(big.Int).Set(self.gasUsed()) + self.refundGas() self.state.AddBalance(self.env.Coinbase(), new(big.Int).Mul(self.gasUsed(), self.gasPrice)) - return ret, self.gasUsed(), err + return ret, requiredGas, self.gasUsed(), err } func (self *StateTransition) refundGas() { diff --git a/eth/api.go b/eth/api.go index 06c10daa9..af03c096d 100644 --- a/eth/api.go +++ b/eth/api.go @@ -674,11 +674,11 @@ func (s *PublicBlockChainAPI) doCall(args CallArgs, blockNr rpc.BlockNumber) (st vmenv := core.NewEnv(stateDb, s.config, s.bc, msg, block.Header(), s.config.VmConfig) gp := new(core.GasPool).AddGas(common.MaxBig) - res, gas, err := core.ApplyMessage(vmenv, msg, gp) + res, requiredGas, _, err := core.NewStateTransition(vmenv, msg, gp).TransitionDb() if len(res) == 0 { // backwards compatibility - return "0x", gas, err + return "0x", requiredGas, err } - return common.ToHex(res), gas, err + return common.ToHex(res), requiredGas, err } // Call executes the given transaction on the state for the given block number. -- cgit v1.2.3