diff options
author | Jeffrey Wilcke <jeffrey@ethereum.org> | 2016-04-01 19:42:19 +0800 |
---|---|---|
committer | Jeffrey Wilcke <jeffrey@ethereum.org> | 2016-04-01 19:42:19 +0800 |
commit | c58079461bafe508bea9233e2b81852df5188f57 (patch) | |
tree | 33d4c6a96eed3bf1ce9f69bbef3cbc1561f2b114 | |
parent | 10d3466c934bd425a8c941270749a652a588527d (diff) | |
parent | 1f3596c25af077a3303c554ee6b49404b20f7117 (diff) | |
download | dexon-c58079461bafe508bea9233e2b81852df5188f57.tar dexon-c58079461bafe508bea9233e2b81852df5188f57.tar.gz dexon-c58079461bafe508bea9233e2b81852df5188f57.tar.bz2 dexon-c58079461bafe508bea9233e2b81852df5188f57.tar.lz dexon-c58079461bafe508bea9233e2b81852df5188f57.tar.xz dexon-c58079461bafe508bea9233e2b81852df5188f57.tar.zst dexon-c58079461bafe508bea9233e2b81852df5188f57.zip |
Merge pull request #2281 from obscuren/configurable-genesis
core: homestead chain configuration & artificial gas floor target mining flag
55 files changed, 751 insertions, 447 deletions
diff --git a/accounts/abi/bind/backends/simulated.go b/accounts/abi/bind/backends/simulated.go index 18e8481c5..213a4f1a7 100644 --- a/accounts/abi/bind/backends/simulated.go +++ b/accounts/abi/bind/backends/simulated.go @@ -24,10 +24,14 @@ 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" ) +// Default chain configuration which sets homestead phase at block 0 (i.e. no frontier) +var chainConfig = &core.ChainConfig{HomesteadBlock: big.NewInt(0)} + // This nil assignment ensures compile time that SimulatedBackend implements bind.ContractBackend. var _ bind.ContractBackend = (*SimulatedBackend)(nil) @@ -46,7 +50,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 +106,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 +149,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", @@ -248,6 +249,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", Usage: `Geth Console: interactive JavaScript environment`, @@ -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..c6daf9e7f 100644 --- a/core/block_validator_test.go +++ b/core/block_validator_test.go @@ -30,12 +30,16 @@ import ( "github.com/ethereum/go-ethereum/pow/ezp" ) +func testChainConfig() *ChainConfig { + return &ChainConfig{HomesteadBlock: big.NewInt(0)} +} + 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 +53,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..876dd2ba1 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,25 +1023,23 @@ 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 { - 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 7ae3c98b0..ef0ac66d1 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -23,11 +23,21 @@ 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/pow" ) +/* + * TODO: move this to another package. + */ + +// MakeChainConfig returns a new ChainConfig with the ethereum default chain settings. +func MakeChainConfig() *ChainConfig { + return &ChainConfig{HomesteadBlock: big.NewInt(0)} +} + // FakePow is a non-validating proof of work implementation. // It returns true from Verify for any block. type FakePow struct{} @@ -91,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(nil, b.gasPool, b.statedb, b.header, tx, b.header.GasUsed, nil) + receipt, _, _, err := ApplyTransaction(MakeChainConfig(), nil, b.gasPool, b.statedb, b.header, tx, b.header.GasUsed, vm.Config{}) if err != nil { panic(err) } @@ -148,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(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 @@ -203,7 +213,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(), 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 +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, 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 b9c1d89b7..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, 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/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 <http://www.gnu.org/licenses/>. + +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..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) { @@ -124,7 +138,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() @@ -209,18 +223,19 @@ 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 } 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 { - 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/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..af03c096d 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,14 +671,14 @@ 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) + 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. @@ -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..13de18670 100644 --- a/eth/helper_test.go +++ b/eth/helper_test.go @@ -34,13 +34,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: 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) 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 |