Merge pull request #2281 from obscuren/configurable-genesis

core: homestead chain configuration & artificial gas floor target mining flag
This commit is contained in:
Jeffrey Wilcke 2016-04-01 13:42:19 +02:00
commit c58079461b
55 changed files with 751 additions and 447 deletions

View File

@ -24,10 +24,14 @@ import (
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types" "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/ethdb"
"github.com/ethereum/go-ethereum/event" "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. // This nil assignment ensures compile time that SimulatedBackend implements bind.ContractBackend.
var _ bind.ContractBackend = (*SimulatedBackend)(nil) var _ bind.ContractBackend = (*SimulatedBackend)(nil)
@ -46,7 +50,7 @@ type SimulatedBackend struct {
func NewSimulatedBackend(accounts ...core.GenesisAccount) *SimulatedBackend { func NewSimulatedBackend(accounts ...core.GenesisAccount) *SimulatedBackend {
database, _ := ethdb.NewMemDatabase() database, _ := ethdb.NewMemDatabase()
core.WriteGenesisBlockForTesting(database, accounts...) 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{ backend := &SimulatedBackend{
database: database, database: database,
@ -102,7 +106,7 @@ func (b *SimulatedBackend) ContractCall(contract common.Address, data []byte, pe
data: data, data: data,
} }
// Execute the call and return // 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) gaspool := new(core.GasPool).AddGas(common.MaxBig)
out, _, err := core.ApplyMessage(vmenv, msg, gaspool) out, _, err := core.ApplyMessage(vmenv, msg, gaspool)
@ -145,7 +149,7 @@ func (b *SimulatedBackend) EstimateGasLimit(sender common.Address, contract *com
data: data, data: data,
} }
// Execute the call and return // 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) gaspool := new(core.GasPool).AddGas(common.MaxBig)
_, gas, err := core.ApplyMessage(vmenv, msg, gaspool) _, gas, err := core.ApplyMessage(vmenv, msg, gaspool)

View File

@ -27,6 +27,7 @@ import (
"github.com/codegangsta/cli" "github.com/codegangsta/cli"
"github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/tests" "github.com/ethereum/go-ethereum/tests"
) )
@ -73,9 +74,9 @@ func runTestWithReader(test string, r io.Reader) error {
var err error var err error
switch strings.ToLower(test) { switch strings.ToLower(test) {
case "bk", "block", "blocktest", "blockchaintest", "blocktests", "blockchaintests": case "bk", "block", "blocktest", "blockchaintest", "blocktests", "blockchaintests":
err = tests.RunBlockTestWithReader(r, skipTests) err = tests.RunBlockTestWithReader(params.MainNetHomesteadBlock, r, skipTests)
case "st", "state", "statetest", "statetests": case "st", "state", "statetest", "statetests":
err = tests.RunStateTestWithReader(r, skipTests) err = tests.RunStateTestWithReader(tests.RuleSet{params.MainNetHomesteadBlock}, r, skipTests)
case "tx", "transactiontest", "transactiontests": case "tx", "transactiontest", "transactiontests":
err = tests.RunTransactionTestsWithReader(r, skipTests) err = tests.RunTransactionTestsWithReader(r, skipTests)
case "vm", "vmtest", "vmtests": case "vm", "vmtest", "vmtests":

View File

@ -33,7 +33,6 @@ import (
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/params"
) )
var ( var (
@ -106,9 +105,6 @@ func init() {
} }
func run(ctx *cli.Context) { func run(ctx *cli.Context) {
vm.ForceJit = ctx.GlobalBool(ForceJitFlag.Name)
vm.EnableJit = !ctx.GlobalBool(DisableJitFlag.Name)
glog.SetToStderr(true) glog.SetToStderr(true)
glog.SetV(ctx.GlobalInt(VerbosityFlag.Name)) glog.SetV(ctx.GlobalInt(VerbosityFlag.Name))
@ -118,8 +114,10 @@ func run(ctx *cli.Context) {
receiver := statedb.CreateAccount(common.StringToAddress("receiver")) receiver := statedb.CreateAccount(common.StringToAddress("receiver"))
receiver.SetCode(common.Hex2Bytes(ctx.GlobalString(CodeFlag.Name))) receiver.SetCode(common.Hex2Bytes(ctx.GlobalString(CodeFlag.Name)))
vmenv := NewEnv(statedb, common.StringToAddress("evmuser"), common.Big(ctx.GlobalString(ValueFlag.Name)), &vm.Config{ vmenv := NewEnv(statedb, common.StringToAddress("evmuser"), common.Big(ctx.GlobalString(ValueFlag.Name)), vm.Config{
Debug: ctx.GlobalBool(DebugFlag.Name), Debug: ctx.GlobalBool(DebugFlag.Name),
ForceJit: ctx.GlobalBool(ForceJitFlag.Name),
EnableJit: !ctx.GlobalBool(DisableJitFlag.Name),
}) })
tstart := time.Now() tstart := time.Now()
@ -180,8 +178,7 @@ type VMEnv struct {
evm *vm.EVM evm *vm.EVM
} }
func NewEnv(state *state.StateDB, transactor common.Address, value *big.Int, cfg *vm.Config) *VMEnv { func NewEnv(state *state.StateDB, transactor common.Address, value *big.Int, cfg vm.Config) *VMEnv {
params.HomesteadBlock = new(big.Int)
env := &VMEnv{ env := &VMEnv{
state: state, state: state,
transactor: &transactor, transactor: &transactor,
@ -194,6 +191,12 @@ func NewEnv(state *state.StateDB, transactor common.Address, value *big.Int, cfg
return env 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) Vm() vm.Vm { return self.evm }
func (self *VMEnv) Db() vm.Database { return self.state } func (self *VMEnv) Db() vm.Database { return self.state }
func (self *VMEnv) MakeSnapshot() vm.Database { return self.state.Copy() } func (self *VMEnv) MakeSnapshot() vm.Database { return self.state.Copy() }

View File

@ -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)}) core.WriteGenesisBlockForTesting(db, core.GenesisAccount{common.HexToAddress(testAddress), common.String2Big(testBalance)})
ethConf := &eth.Config{ ethConf := &eth.Config{
ChainConfig: &core.ChainConfig{HomesteadBlock: new(big.Int)},
TestGenesisState: db, TestGenesisState: db,
AccountManager: accman, AccountManager: accman,
DocRoot: "/", DocRoot: "/",

View File

@ -32,7 +32,9 @@ import (
"github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/internal/debug" "github.com/ethereum/go-ethereum/internal/debug"
"github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog" "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. The output of this command is supposed to be machine-readable.
`, `,
}, },
{ {
Name: "wallet", Name: "wallet",
Usage: "ethereum presale wallet", Usage: "ethereum presale wallet",
@ -247,6 +248,16 @@ nodes.
}, },
}, },
}, },
{
Action: initGenesis,
Name: "init",
Usage: "bootstraps and initialises a new genesis block (JSON)",
Description: `
The init command initialises a new genesis block and definition for the network.
This is a destructive action and changes the network in which you will be
participating.
`,
},
{ {
Action: console, Action: console,
Name: "console", Name: "console",
@ -255,7 +266,8 @@ nodes.
The Geth console is an interactive shell for the JavaScript runtime environment 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. which exposes a node admin interface as well as the Ðapp JavaScript API.
See https://github.com/ethereum/go-ethereum/wiki/Javascipt-Console See https://github.com/ethereum/go-ethereum/wiki/Javascipt-Console
`}, `,
},
{ {
Action: attach, Action: attach,
Name: "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) go metrics.CollectProcessMetrics(3 * time.Second)
utils.SetupNetwork(ctx) utils.SetupNetwork(ctx)
utils.SetupVM(ctx)
return nil 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 // console starts a new geth node, attaching a JavaScript console to it at the
// same time. // same time.
func console(ctx *cli.Context) { func console(ctx *cli.Context) {

View File

@ -121,6 +121,7 @@ var AppHelpFlagGroups = []flagGroup{
Flags: []cli.Flag{ Flags: []cli.Flag{
utils.MiningEnabledFlag, utils.MiningEnabledFlag,
utils.MinerThreadsFlag, utils.MinerThreadsFlag,
utils.TargetGasLimitFlag,
utils.MiningGPUFlag, utils.MiningGPUFlag,
utils.AutoDAGFlag, utils.AutoDAGFlag,
utils.EtherbaseFlag, utils.EtherbaseFlag,

View File

@ -34,7 +34,6 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/state" "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/crypto"
"github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
@ -173,6 +172,11 @@ var (
Name: "minergpus", Name: "minergpus",
Usage: "List of GPUs to use for mining (e.g. '0,1' will use the first two GPUs found)", 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{ AutoDAGFlag = cli.BoolFlag{
Name: "autodag", Name: "autodag",
Usage: "Enable automatic DAG pregeneration", Usage: "Enable automatic DAG pregeneration",
@ -656,6 +660,7 @@ func MakeSystemNode(name, version string, extra []byte, ctx *cli.Context) *node.
accman := MakeAccountManager(ctx) accman := MakeAccountManager(ctx)
ethConf := &eth.Config{ ethConf := &eth.Config{
ChainConfig: MustMakeChainConfig(ctx),
Genesis: MakeGenesisBlock(ctx), Genesis: MakeGenesisBlock(ctx),
FastSync: ctx.GlobalBool(FastSyncFlag.Name), FastSync: ctx.GlobalBool(FastSyncFlag.Name),
BlockChainVersion: ctx.GlobalInt(BlockchainVersionFlag.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() ethConf.Genesis = core.TestNetGenesisBlock()
} }
state.StartingNonce = 1048576 // (2**20) state.StartingNonce = 1048576 // (2**20)
// overwrite homestead block
params.HomesteadBlock = params.TestNetHomesteadBlock
case ctx.GlobalBool(DevModeFlag.Name): case ctx.GlobalBool(DevModeFlag.Name):
// Override the base network stack configs // Override the base network stack configs
@ -758,25 +761,56 @@ func SetupNetwork(ctx *cli.Context) {
core.BlockReward = big.NewInt(1.5e+18) core.BlockReward = big.NewInt(1.5e+18)
core.ExpDiffPeriod = big.NewInt(math.MaxInt64) core.ExpDiffPeriod = big.NewInt(math.MaxInt64)
} }
params.TargetGasLimit = common.String2Big(ctx.GlobalString(TargetGasLimitFlag.Name))
} }
// SetupVM configured the VM package's global settings // MustMakeChainConfig reads the chain configuration from the given database.
func SetupVM(ctx *cli.Context) { func MustMakeChainConfig(ctx *cli.Context) *core.ChainConfig {
vm.EnableJit = ctx.GlobalBool(VMEnableJitFlag.Name) var (
vm.ForceJit = ctx.GlobalBool(VMForceJitFlag.Name) db = MakeChainDatabase(ctx)
vm.SetJITCacheSize(ctx.GlobalInt(VMJitCacheFlag.Name)) 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
}
// 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()
)
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. // MakeChain creates a chain manager from set command line flags.
func MakeChain(ctx *cli.Context) (chain *core.BlockChain, chainDb ethdb.Database) { func MakeChain(ctx *cli.Context) (chain *core.BlockChain, chainDb ethdb.Database) {
datadir := MustMakeDataDir(ctx)
cache := ctx.GlobalInt(CacheFlag.Name)
handles := MakeDatabaseHandles()
var err error var err error
if chainDb, err = ethdb.NewLDBDatabase(filepath.Join(datadir, "chaindata"), cache, handles); err != nil { chainDb = MakeChainDatabase(ctx)
Fatalf("Could not open database: %v", err)
}
if ctx.GlobalBool(OlympicFlag.Name) { if ctx.GlobalBool(OlympicFlag.Name) {
_, err := core.WriteTestNetGenesisBlock(chainDb) _, err := core.WriteTestNetGenesisBlock(chainDb)
if err != nil { if err != nil {
@ -784,10 +818,10 @@ func MakeChain(ctx *cli.Context) (chain *core.BlockChain, chainDb ethdb.Database
} }
} }
eventMux := new(event.TypeMux) chainConfig := MustMakeChainConfig(ctx)
pow := ethash.New()
//genesis := core.GenesisBlock(uint64(ctx.GlobalInt(GenesisNonceFlag.Name)), blockDB) var eventMux event.TypeMux
chain, err = core.NewBlockChain(chainDb, pow, eventMux) chain, err = core.NewBlockChain(chainDb, chainConfig, ethash.New(), &eventMux)
if err != nil { if err != nil {
Fatalf("Could not start chainmanager: %v", err) Fatalf("Could not start chainmanager: %v", err)
} }

View File

@ -21,8 +21,10 @@ import (
"os" "os"
"runtime" "runtime"
"runtime/debug" "runtime/debug"
"strings"
) )
// Report gives off a warning requesting the user to submit an issue to the github tracker.
func Report(extra ...interface{}) { 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, "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...) fmt.Fprintln(os.Stderr, extra...)
@ -34,3 +36,17 @@ func Report(extra ...interface{}) {
fmt.Fprintln(os.Stderr, "#### BUG! PLEASE REPORT ####") 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)
}

View File

@ -27,6 +27,7 @@ import (
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types" "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/crypto"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger"
@ -35,6 +36,7 @@ import (
// registryAPIBackend is a backend for an Ethereum Registry. // registryAPIBackend is a backend for an Ethereum Registry.
type registryAPIBackend struct { type registryAPIBackend struct {
config *core.ChainConfig
bc *core.BlockChain bc *core.BlockChain
chainDb ethdb.Database chainDb ethdb.Database
txPool *core.TxPool txPool *core.TxPool
@ -43,12 +45,22 @@ type registryAPIBackend struct {
// PrivateRegistarAPI offers various functions to access the Ethereum registry. // PrivateRegistarAPI offers various functions to access the Ethereum registry.
type PrivateRegistarAPI struct { type PrivateRegistarAPI struct {
be *registryAPIBackend config *core.ChainConfig
be *registryAPIBackend
} }
// NewPrivateRegistarAPI creates a new PrivateRegistarAPI instance. // NewPrivateRegistarAPI creates a new PrivateRegistarAPI instance.
func NewPrivateRegistarAPI(bc *core.BlockChain, chainDb ethdb.Database, txPool *core.TxPool, am *accounts.Manager) *PrivateRegistarAPI { func NewPrivateRegistarAPI(config *core.ChainConfig, bc *core.BlockChain, chainDb ethdb.Database, txPool *core.TxPool, am *accounts.Manager) *PrivateRegistarAPI {
return &PrivateRegistarAPI{&registryAPIBackend{bc, chainDb, txPool, am}} return &PrivateRegistarAPI{
config: config,
be: &registryAPIBackend{
config: config,
bc: bc,
chainDb: chainDb,
txPool: txPool,
am: am,
},
}
} }
// SetGlobalRegistrar allows clients to set the global registry for the node. // 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() 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) gp := new(core.GasPool).AddGas(common.MaxBig)
res, gas, err := core.ApplyMessage(vmenv, msg, gp) res, gas, err := core.ApplyMessage(vmenv, msg, gp)

View File

@ -168,7 +168,7 @@ func benchInsertChain(b *testing.B, disk bool, gen func(int, *BlockGen)) {
// Time the insertion of the new chain. // Time the insertion of the new chain.
// State and blocks are stored in the same DB. // State and blocks are stored in the same DB.
evmux := new(event.TypeMux) evmux := new(event.TypeMux)
chainman, _ := NewBlockChain(db, FakePow{}, evmux) chainman, _ := NewBlockChain(db, &ChainConfig{HomesteadBlock: new(big.Int)}, FakePow{}, evmux)
defer chainman.Stop() defer chainman.Stop()
b.ReportAllocs() b.ReportAllocs()
b.ResetTimer() b.ResetTimer()

View File

@ -41,15 +41,17 @@ var (
// //
// BlockValidator implements Validator. // BlockValidator implements Validator.
type BlockValidator struct { type BlockValidator struct {
bc *BlockChain // Canonical block chain config *ChainConfig // Chain configuration options
Pow pow.PoW // Proof of work used for validating 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 // 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{ validator := &BlockValidator{
Pow: pow, config: config,
bc: blockchain, Pow: pow,
bc: blockchain,
} }
return validator return validator
} }
@ -80,7 +82,7 @@ func (v *BlockValidator) ValidateBlock(block *types.Block) error {
header := block.Header() header := block.Header()
// validate the 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 return err
} }
// verify the uncles are correctly rewarded // 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]) 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)) 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()) { if v.bc.HasHeader(header.Hash()) {
return nil 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. // Validates a header. Returns an error if the header is invalid.
// //
// See YP section 4.3.4. "Block Header Validity" // 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 { if big.NewInt(int64(len(header.Extra))).Cmp(params.MaximumExtraDataSize) == 1 {
return fmt.Errorf("Header extra data too long (%d)", len(header.Extra)) 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 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 { if expd.Cmp(header.Difficulty) != 0 {
return fmt.Errorf("Difficulty check failed for header %v, %v", header.Difficulty, expd) 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 // CalcDifficulty is the difficulty adjustment algorithm. It returns
// the difficulty that a new block should have when created at time // the difficulty that a new block should have when created at time
// given the parent block's time and difficulty. // given the parent block's time and difficulty.
func CalcDifficulty(time, parentTime uint64, parentNumber, parentDiff *big.Int) *big.Int { func CalcDifficulty(config *ChainConfig, time, parentTime uint64, parentNumber, parentDiff *big.Int) *big.Int {
if params.IsHomestead(new(big.Int).Add(parentNumber, common.Big1)) { if config.IsHomestead(new(big.Int).Add(parentNumber, common.Big1)) {
return calcDifficultyHomestead(time, parentTime, parentNumber, parentDiff) return calcDifficultyHomestead(time, parentTime, parentNumber, parentDiff)
} else { } else {
return calcDifficultyFrontier(time, parentTime, parentNumber, parentDiff) return calcDifficultyFrontier(time, parentTime, parentNumber, parentDiff)
@ -363,11 +365,11 @@ func CalcGasLimit(parent *types.Block) *big.Int {
gl = gl.Add(gl, contrib) gl = gl.Add(gl, contrib)
gl.Set(common.BigMax(gl, params.MinGasLimit)) 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) // 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.Add(parent.GasLimit(), decay)
gl.Set(common.BigMin(gl, params.GenesisGasLimit)) gl.Set(common.BigMin(gl, params.TargetGasLimit))
} }
return gl return gl
} }

View File

@ -30,12 +30,16 @@ import (
"github.com/ethereum/go-ethereum/pow/ezp" "github.com/ethereum/go-ethereum/pow/ezp"
) )
func testChainConfig() *ChainConfig {
return &ChainConfig{HomesteadBlock: big.NewInt(0)}
}
func proc() (Validator, *BlockChain) { func proc() (Validator, *BlockChain) {
db, _ := ethdb.NewMemDatabase() db, _ := ethdb.NewMemDatabase()
var mux event.TypeMux var mux event.TypeMux
WriteTestNetGenesisBlock(db) WriteTestNetGenesisBlock(db)
blockchain, err := NewBlockChain(db, thePow(), &mux) blockchain, err := NewBlockChain(db, testChainConfig(), thePow(), &mux)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
} }
@ -49,13 +53,14 @@ func TestNumber(t *testing.T) {
statedb, _ := state.New(chain.Genesis().Root(), chain.chainDb) statedb, _ := state.New(chain.Genesis().Root(), chain.chainDb)
header := makeHeader(chain.Genesis(), statedb) header := makeHeader(chain.Genesis(), statedb)
header.Number = big.NewInt(3) 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 { if err != BlockNumberErr {
t.Errorf("expected block number error, got %q", err) t.Errorf("expected block number error, got %q", err)
} }
header = makeHeader(chain.Genesis(), statedb) 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 { if err == BlockNumberErr {
t.Errorf("didn't expect block number error") t.Errorf("didn't expect block number error")
} }

View File

@ -80,11 +80,12 @@ const (
// included in the canonical one where as GetBlockByNumber always represents the // included in the canonical one where as GetBlockByNumber always represents the
// canonical chain. // canonical chain.
type BlockChain struct { type BlockChain struct {
config *ChainConfig // chain & network configuration
hc *HeaderChain hc *HeaderChain
chainDb ethdb.Database chainDb ethdb.Database
eventMux *event.TypeMux eventMux *event.TypeMux
genesisBlock *types.Block genesisBlock *types.Block
vmConfig *vm.Config
mu sync.RWMutex // global mutex for locking chain operations mu sync.RWMutex // global mutex for locking chain operations
chainmu sync.RWMutex // blockchain insertion lock chainmu sync.RWMutex // blockchain insertion lock
@ -113,13 +114,14 @@ type BlockChain struct {
// NewBlockChain returns a fully initialised block chain using information // NewBlockChain returns a fully initialised block chain using information
// available in the database. It initialiser the default Ethereum Validator and // available in the database. It initialiser the default Ethereum Validator and
// Processor. // 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) bodyCache, _ := lru.New(bodyCacheLimit)
bodyRLPCache, _ := lru.New(bodyCacheLimit) bodyRLPCache, _ := lru.New(bodyCacheLimit)
blockCache, _ := lru.New(blockCacheLimit) blockCache, _ := lru.New(blockCacheLimit)
futureBlocks, _ := lru.New(maxFutureBlocks) futureBlocks, _ := lru.New(maxFutureBlocks)
bc := &BlockChain{ bc := &BlockChain{
config: config,
chainDb: chainDb, chainDb: chainDb,
eventMux: mux, eventMux: mux,
quit: make(chan struct{}), quit: make(chan struct{}),
@ -129,24 +131,21 @@ func NewBlockChain(chainDb ethdb.Database, pow pow.PoW, mux *event.TypeMux) (*Bl
futureBlocks: futureBlocks, futureBlocks: futureBlocks,
pow: pow, pow: pow,
} }
bc.SetValidator(NewBlockValidator(bc, pow)) bc.SetValidator(NewBlockValidator(config, bc, pow))
bc.SetProcessor(NewStateProcessor(bc)) bc.SetProcessor(NewStateProcessor(config, bc))
gv := func() HeaderValidator { return bc.Validator() } gv := func() HeaderValidator { return bc.Validator() }
var err error var err error
bc.hc, err = NewHeaderChain(chainDb, gv, bc.getProcInterrupt) bc.hc, err = NewHeaderChain(chainDb, config, gv, bc.getProcInterrupt)
if err != nil { if err != nil {
return nil, err return nil, err
} }
bc.genesisBlock = bc.GetBlockByNumber(0) bc.genesisBlock = bc.GetBlockByNumber(0)
if bc.genesisBlock == nil { if bc.genesisBlock == nil {
bc.genesisBlock, err = WriteDefaultGenesisBlock(chainDb) return nil, ErrNoGenesis
if err != nil {
return nil, err
}
glog.V(logger.Info).Infoln("WARNING: Wrote default ethereum genesis block")
} }
if err := bc.loadLastState(); err != nil { if err := bc.loadLastState(); err != nil {
return nil, err return nil, err
} }
@ -163,10 +162,6 @@ func NewBlockChain(chainDb ethdb.Database, pow pow.PoW, mux *event.TypeMux) (*Bl
return bc, nil return bc, nil
} }
func (self *BlockChain) SetConfig(vmConfig *vm.Config) {
self.vmConfig = vmConfig
}
func (self *BlockChain) getProcInterrupt() bool { func (self *BlockChain) getProcInterrupt() bool {
return atomic.LoadInt32(&self.procInterrupt) == 1 return atomic.LoadInt32(&self.procInterrupt) == 1
} }
@ -896,7 +891,7 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) {
return i, err return i, err
} }
// Process block using the parent state as reference point. // 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 { if err != nil {
reportBlock(block, err) reportBlock(block, err)
return i, err return i, err

View File

@ -53,7 +53,7 @@ func thePow() pow.PoW {
func theBlockChain(db ethdb.Database, t *testing.T) *BlockChain { func theBlockChain(db ethdb.Database, t *testing.T) *BlockChain {
var eventMux event.TypeMux var eventMux event.TypeMux
WriteTestNetGenesisBlock(db) WriteTestNetGenesisBlock(db)
blockchain, err := NewBlockChain(db, thePow(), &eventMux) blockchain, err := NewBlockChain(db, testChainConfig(), thePow(), &eventMux)
if err != nil { if err != nil {
t.Error("failed creating blockchain:", err) t.Error("failed creating blockchain:", err)
t.FailNow() t.FailNow()
@ -141,7 +141,7 @@ func testBlockChainImport(chain types.Blocks, blockchain *BlockChain) error {
if err != nil { if err != nil {
return err return err
} }
receipts, _, usedGas, err := blockchain.Processor().Process(block, statedb, nil) receipts, _, usedGas, err := blockchain.Processor().Process(block, statedb, vm.Config{})
if err != nil { if err != nil {
reportBlock(block, err) reportBlock(block, err)
return 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 { func (bproc) ValidateState(block, parent *types.Block, state *state.StateDB, receipts types.Receipts, usedGas *big.Int) error {
return nil 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 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 { func chm(genesis *types.Block, db ethdb.Database) *BlockChain {
var eventMux event.TypeMux var eventMux event.TypeMux
bc := &BlockChain{ bc := &BlockChain{
chainDb: db, chainDb: db,
genesisBlock: genesis, genesisBlock: genesis,
eventMux: &eventMux, eventMux: &eventMux,
pow: FakePow{}, pow: FakePow{},
config: testChainConfig(),
} }
valFn := func() HeaderValidator { return bc.Validator() } 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.bodyCache, _ = lru.New(100)
bc.bodyRLPCache, _ = lru.New(100) bc.bodyRLPCache, _ = lru.New(100)
bc.blockCache, _ = 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()) }() defer func() { delete(BadHashes, headers[3].Hash()) }()
} }
// Create a new chain manager and check it rolled back the state // 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 { if err != nil {
t.Fatalf("failed to create new chain manager: %v", err) 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() failHash = headers[failAt].Hash()
blockchain.pow = failPow{failNum} blockchain.pow = failPow{failNum}
blockchain.validator = NewBlockValidator(blockchain, failPow{failNum}) blockchain.validator = NewBlockValidator(testChainConfig(), blockchain, failPow{failNum})
failRes, err = blockchain.InsertHeaderChain(headers, 1) failRes, err = blockchain.InsertHeaderChain(headers, 1)
} }
@ -733,7 +734,7 @@ func TestFastVsFullChains(t *testing.T) {
archiveDb, _ := ethdb.NewMemDatabase() archiveDb, _ := ethdb.NewMemDatabase()
WriteGenesisBlockForTesting(archiveDb, GenesisAccount{address, funds}) 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 { if n, err := archive.InsertChain(blocks); err != nil {
t.Fatalf("failed to process block %d: %v", n, err) 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 // Fast import the chain as a non-archive node to test
fastDb, _ := ethdb.NewMemDatabase() fastDb, _ := ethdb.NewMemDatabase()
WriteGenesisBlockForTesting(fastDb, GenesisAccount{address, funds}) 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)) headers := make([]*types.Header, len(blocks))
for i, block := range blocks { for i, block := range blocks {
@ -817,7 +818,7 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) {
archiveDb, _ := ethdb.NewMemDatabase() archiveDb, _ := ethdb.NewMemDatabase()
WriteGenesisBlockForTesting(archiveDb, GenesisAccount{address, funds}) 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 { if n, err := archive.InsertChain(blocks); err != nil {
t.Fatalf("failed to process block %d: %v", n, err) 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 // Import the chain as a non-archive node and ensure all pointers are updated
fastDb, _ := ethdb.NewMemDatabase() fastDb, _ := ethdb.NewMemDatabase()
WriteGenesisBlockForTesting(fastDb, GenesisAccount{address, funds}) 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)) headers := make([]*types.Header, len(blocks))
for i, block := range 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 // Import the chain as a light node and ensure all pointers are updated
lightDb, _ := ethdb.NewMemDatabase() lightDb, _ := ethdb.NewMemDatabase()
WriteGenesisBlockForTesting(lightDb, GenesisAccount{address, funds}) 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 { if n, err := light.InsertHeaderChain(headers, 1); err != nil {
t.Fatalf("failed to insert header %d: %v", n, err) 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. // Import the chain. This runs all block validation rules.
evmux := &event.TypeMux{} evmux := &event.TypeMux{}
blockchain, _ := NewBlockChain(db, FakePow{}, evmux) blockchain, _ := NewBlockChain(db, testChainConfig(), FakePow{}, evmux)
if i, err := blockchain.InsertChain(chain); err != nil { if i, err := blockchain.InsertChain(chain); err != nil {
t.Fatalf("failed to insert original chain[%d]: %v", i, err) t.Fatalf("failed to insert original chain[%d]: %v", i, err)
} }
@ -986,7 +987,7 @@ func TestLogReorgs(t *testing.T) {
) )
evmux := &event.TypeMux{} evmux := &event.TypeMux{}
blockchain, _ := NewBlockChain(db, FakePow{}, evmux) blockchain, _ := NewBlockChain(db, testChainConfig(), FakePow{}, evmux)
subs := evmux.Subscribe(RemovedLogsEvent{}) subs := evmux.Subscribe(RemovedLogsEvent{})
chain, _ := GenerateChain(genesis, db, 2, func(i int, gen *BlockGen) { chain, _ := GenerateChain(genesis, db, 2, func(i int, gen *BlockGen) {
@ -1022,25 +1023,23 @@ func TestReorgSideEvent(t *testing.T) {
) )
evmux := &event.TypeMux{} evmux := &event.TypeMux{}
blockchain, _ := NewBlockChain(db, FakePow{}, evmux) blockchain, _ := NewBlockChain(db, testChainConfig(), FakePow{}, evmux)
chain, _ := GenerateChain(genesis, db, 3, func(i int, gen *BlockGen) { chain, _ := GenerateChain(genesis, db, 3, func(i int, gen *BlockGen) {})
if i == 2 {
gen.OffsetTime(9)
}
})
if _, err := blockchain.InsertChain(chain); err != nil { if _, err := blockchain.InsertChain(chain); err != nil {
t.Fatalf("failed to insert chain: %v", err) t.Fatalf("failed to insert chain: %v", err)
} }
replacementBlocks, _ := GenerateChain(genesis, db, 4, func(i int, gen *BlockGen) { 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) 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 { if err != nil {
t.Fatalf("failed to create tx: %v", err) t.Fatalf("failed to create tx: %v", err)
} }
gen.AddTx(tx) gen.AddTx(tx)
}) })
subs := evmux.Subscribe(ChainSideEvent{}) subs := evmux.Subscribe(ChainSideEvent{})
if _, err := blockchain.InsertChain(replacementBlocks); err != nil { if _, err := blockchain.InsertChain(replacementBlocks); err != nil {
t.Fatalf("failed to insert chain: %v", err) t.Fatalf("failed to insert chain: %v", err)

View File

@ -23,11 +23,21 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types" "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/ethdb"
"github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/pow" "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. // FakePow is a non-validating proof of work implementation.
// It returns true from Verify for any block. // It returns true from Verify for any block.
type FakePow struct{} type FakePow struct{}
@ -91,7 +101,7 @@ func (b *BlockGen) AddTx(tx *types.Transaction) {
b.SetCoinbase(common.Address{}) b.SetCoinbase(common.Address{})
} }
b.statedb.StartRecord(tx.Hash(), common.Hash{}, len(b.txs)) 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 { if err != nil {
panic(err) panic(err)
} }
@ -148,7 +158,7 @@ func (b *BlockGen) OffsetTime(seconds int64) {
if b.header.Time.Cmp(b.parent.Header().Time) <= 0 { if b.header.Time.Cmp(b.parent.Header().Time) <= 0 {
panic("block time out of range") 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 // 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(), Root: state.IntermediateRoot(),
ParentHash: parent.Hash(), ParentHash: parent.Hash(),
Coinbase: parent.Coinbase(), 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), GasLimit: CalcGasLimit(parent),
GasUsed: new(big.Int), GasUsed: new(big.Int),
Number: new(big.Int).Add(parent.Number(), common.Big1), 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 // Initialize a fresh chain with only a genesis block
genesis, _ := WriteTestNetGenesisBlock(db) genesis, _ := WriteTestNetGenesisBlock(db)
blockchain, _ := NewBlockChain(db, FakePow{}, evmux) blockchain, _ := NewBlockChain(db, MakeChainConfig(), FakePow{}, evmux)
// Create and inject the requested chain // Create and inject the requested chain
if n == 0 { if n == 0 {
return db, blockchain, nil return db, blockchain, nil

View File

@ -77,7 +77,7 @@ func ExampleGenerateChain() {
// Import the chain. This runs all block validation rules. // Import the chain. This runs all block validation rules.
evmux := &event.TypeMux{} evmux := &event.TypeMux{}
blockchain, _ := NewBlockChain(db, FakePow{}, evmux) blockchain, _ := NewBlockChain(db, MakeChainConfig(), FakePow{}, evmux)
if i, err := blockchain.InsertChain(chain); err != nil { if i, err := blockchain.InsertChain(chain); err != nil {
fmt.Printf("insert error (block %d): %v\n", i, err) fmt.Printf("insert error (block %d): %v\n", i, err)
return return

46
core/config.go Normal file
View File

@ -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
}

View File

@ -19,6 +19,7 @@ package core
import ( import (
"bytes" "bytes"
"encoding/binary" "encoding/binary"
"encoding/json"
"fmt" "fmt"
"math/big" "math/big"
@ -50,6 +51,8 @@ var (
MIPMapLevels = []uint64{1000000, 500000, 100000, 50000, 1000} MIPMapLevels = []uint64{1000000, 500000, 100000, 50000, 1000}
blockHashPrefix = []byte("block-hash-") // [deprecated by the header/block split, remove eventually] 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. // 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)) enc, _ := rlp.EncodeToBytes(uint(vsn))
db.Put([]byte("BlockchainVersion"), enc) 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
}

View File

@ -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 // 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 // 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. // 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) contract.UseGas(contract.Gas)
env.SetSnapshot(snapshotPreTransfer) env.SetSnapshot(snapshotPreTransfer)

View File

@ -43,15 +43,16 @@ func WriteGenesisBlock(chainDb ethdb.Database, reader io.Reader) (*types.Block,
} }
var genesis struct { var genesis struct {
Nonce string ChainConfig *ChainConfig
Timestamp string Nonce string
ParentHash string Timestamp string
ExtraData string ParentHash string
GasLimit string ExtraData string
Difficulty string GasLimit string
Mixhash string Difficulty string
Coinbase string Mixhash string
Alloc map[string]struct { Coinbase string
Alloc map[string]struct {
Code string Code string
Storage map[string]string Storage map[string]string
Balance 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 { if err := WriteHeadBlockHash(chainDb, block.Hash()); err != nil {
return nil, err return nil, err
} }
if err := WriteChainConfig(chainDb, block.Hash(), genesis.ChainConfig); err != nil {
return nil, err
}
return block, nil return block, nil
} }

View File

@ -40,6 +40,8 @@ import (
// It is not thread safe either, the encapsulating chain structures should do // It is not thread safe either, the encapsulating chain structures should do
// the necessary mutex locking/unlocking. // the necessary mutex locking/unlocking.
type HeaderChain struct { type HeaderChain struct {
config *ChainConfig
chainDb ethdb.Database chainDb ethdb.Database
genesisHeader *types.Header genesisHeader *types.Header
@ -62,7 +64,7 @@ type getHeaderValidatorFn func() HeaderValidator
// getValidator should return the parent's validator // getValidator should return the parent's validator
// procInterrupt points to the parent's interrupt semaphore // procInterrupt points to the parent's interrupt semaphore
// wg points to the parent's shutdown wait group // 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) headerCache, _ := lru.New(headerCacheLimit)
tdCache, _ := lru.New(tdCacheLimit) tdCache, _ := lru.New(tdCacheLimit)
@ -73,6 +75,7 @@ func NewHeaderChain(chainDb ethdb.Database, getValidator getHeaderValidatorFn, p
} }
hc := &HeaderChain{ hc := &HeaderChain{
config: config,
chainDb: chainDb, chainDb: chainDb,
headerCache: headerCache, headerCache: headerCache,
tdCache: tdCache, tdCache: tdCache,
@ -436,15 +439,17 @@ func (hc *HeaderChain) SetGenesis(head *types.Header) {
// //
// headerValidator implements HeaderValidator. // headerValidator implements HeaderValidator.
type headerValidator struct { type headerValidator struct {
hc *HeaderChain // Canonical header chain config *ChainConfig
Pow pow.PoW // Proof of work used for validating 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 // 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{ return &headerValidator{
Pow: pow, config: config,
hc: chain, Pow: pow,
hc: chain,
} }
} }
@ -460,5 +465,5 @@ func (v *headerValidator) ValidateHeader(header, parent *types.Header, checkPow
if v.hc.HasHeader(header.Hash()) { if v.hc.HasHeader(header.Hash()) {
return nil return nil
} }
return ValidateHeader(v.Pow, header, parent, checkPow, false) return ValidateHeader(v.config, v.Pow, header, parent, checkPow, false)
} }

View File

@ -16,12 +16,21 @@ var (
big32 = big.NewInt(32) 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 { type StateProcessor struct {
bc *BlockChain config *ChainConfig
bc *BlockChain
} }
func NewStateProcessor(bc *BlockChain) *StateProcessor { // NewStateProcessor initialises a new StateProcessor.
return &StateProcessor{bc} 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 // 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 // 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 // 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. // 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 ( var (
receipts types.Receipts receipts types.Receipts
totalUsedGas = big.NewInt(0) 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() { for i, tx := range block.Transactions() {
statedb.StartRecord(tx.Hash(), block.Hash(), i) 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 { if err != nil {
return nil, nil, totalUsedGas, err 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 // ApplyTransactions returns the generated receipts and vm logs during the
// execution of the state transition phase. // 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) { 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, bc, tx, header, cfg), tx, gp) _, gas, err := ApplyMessage(NewEnv(statedb, config, bc, tx, header, cfg), tx, gp)
if err != nil { if err != nil {
return nil, nil, nil, err return nil, nil, nil, err
} }

View File

@ -104,8 +104,9 @@ func IntrinsicGas(data []byte, contractCreation, homestead bool) *big.Int {
return igas return igas
} }
func ApplyMessage(env vm.Environment, msg Message, gp *GasPool) ([]byte, *big.Int, error) { // NewStateTransition initialises and returns a new state transition object.
var st = StateTransition{ func NewStateTransition(env vm.Environment, msg Message, gp *GasPool) *StateTransition {
return &StateTransition{
gp: gp, gp: gp,
env: env, env: env,
msg: msg, msg: msg,
@ -116,7 +117,20 @@ func ApplyMessage(env vm.Environment, msg Message, gp *GasPool) ([]byte, *big.In
data: msg.Data(), data: msg.Data(),
state: env.Db(), 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) { func (self *StateTransition) from() (vm.Account, error) {
@ -124,7 +138,7 @@ func (self *StateTransition) from() (vm.Account, error) {
f common.Address f common.Address
err error err error
) )
if params.IsHomestead(self.env.BlockNumber()) { if self.env.RuleSet().IsHomestead(self.env.BlockNumber()) {
f, err = self.msg.From() f, err = self.msg.From()
} else { } else {
f, err = self.msg.FromFrontier() f, err = self.msg.FromFrontier()
@ -209,18 +223,19 @@ func (self *StateTransition) preCheck() (err error) {
return nil 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 { if err = self.preCheck(); err != nil {
return return
} }
msg := self.msg msg := self.msg
sender, _ := self.from() // err checked in preCheck sender, _ := self.from() // err checked in preCheck
homestead := params.IsHomestead(self.env.BlockNumber()) homestead := self.env.RuleSet().IsHomestead(self.env.BlockNumber())
contractCreation := MessageCreatesContract(msg) contractCreation := MessageCreatesContract(msg)
// Pay intrinsic gas // Pay intrinsic gas
if err = self.useGas(IntrinsicGas(self.data, contractCreation, homestead)); err != nil { 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 vmenv := self.env
@ -245,7 +260,7 @@ func (self *StateTransition) transitionDb() (ret []byte, usedGas *big.Int, err e
} }
if err != nil && IsValueTransferErr(err) { 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 // 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 err = nil
} }
requiredGas = new(big.Int).Set(self.gasUsed())
self.refundGas() self.refundGas()
self.state.AddBalance(self.env.Coinbase(), new(big.Int).Mul(self.gasUsed(), self.gasPrice)) 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() { func (self *StateTransition) refundGas() {

View File

@ -30,7 +30,6 @@ import (
"github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/params"
) )
var ( var (
@ -60,6 +59,7 @@ type stateFn func() (*state.StateDB, error)
// current state) and future transactions. Transactions move between those // current state) and future transactions. Transactions move between those
// two states over time as they are received and processed. // two states over time as they are received and processed.
type TxPool struct { type TxPool struct {
config *ChainConfig
quit chan bool // Quitting channel quit chan bool // Quitting channel
currentState stateFn // The state function which will allow us to do some pre checks currentState stateFn // The state function which will allow us to do some pre checks
pendingState *state.ManagedState pendingState *state.ManagedState
@ -75,8 +75,9 @@ type TxPool struct {
homestead bool 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{ pool := &TxPool{
config: config,
pending: make(map[common.Hash]*types.Transaction), pending: make(map[common.Hash]*types.Transaction),
queue: make(map[common.Address]map[common.Hash]*types.Transaction), queue: make(map[common.Address]map[common.Hash]*types.Transaction),
quit: make(chan bool), quit: make(chan bool),
@ -102,7 +103,7 @@ func (pool *TxPool) eventLoop() {
switch ev := ev.Data.(type) { switch ev := ev.Data.(type) {
case ChainHeadEvent: case ChainHeadEvent:
pool.mu.Lock() 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 pool.homestead = true
} }

View File

@ -40,7 +40,7 @@ func setupTxPool() (*TxPool, *ecdsa.PrivateKey) {
var m event.TypeMux var m event.TypeMux
key, _ := crypto.GenerateKey() 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() newPool.resetState()
return newPool, key return newPool, key
} }

View File

@ -61,7 +61,7 @@ type HeaderValidator interface {
// of gas used in the process and return an error if any of the internal rules // of gas used in the process and return an error if any of the internal rules
// failed. // failed.
type Processor interface { 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 // Backend is an interface defining the basic functionality for an operable node

View File

@ -22,9 +22,17 @@ import (
"github.com/ethereum/go-ethereum/common" "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 // Environment is an EVM requirement and helper which allows access to outside
// information such as states. // information such as states.
type Environment interface { type Environment interface {
// The current ruleset
RuleSet() RuleSet
// The state database // The state database
Db() Database Db() Database
// Creates a restorable snapshot // Creates a restorable snapshot
@ -53,10 +61,10 @@ type Environment interface {
AddLog(*Log) AddLog(*Log)
// Type of the VM // Type of the VM
Vm() Vm Vm() Vm
// Current calling depth // Get the curret calling depth
Depth() int Depth() int
// Set the current calling depth
SetDepth(i int) SetDepth(i int)
// Call another contract // Call another contract
Call(me ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) 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 // Take another's contract code and execute within our own context

View File

@ -520,7 +520,7 @@ func opCreate(instr instruction, pc *uint64, env Environment, contract *Contract
// homestead we must check for CodeStoreOutOfGasError (homestead only // homestead we must check for CodeStoreOutOfGasError (homestead only
// rule) and treat as an error, if the ruleset is frontier we must // rule) and treat as an error, if the ruleset is frontier we must
// ignore this error and pretend the operation was successful. // 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)) stack.push(new(big.Int))
} else if suberr != nil && suberr != CodeStoreOutOfGasError { } else if suberr != nil && suberr != CodeStoreOutOfGasError {
stack.push(new(big.Int)) stack.push(new(big.Int))

View File

@ -30,27 +30,24 @@ import (
"github.com/hashicorp/golang-lru" "github.com/hashicorp/golang-lru"
) )
// progStatus is the type for the JIT program status.
type progStatus int32 type progStatus int32
const ( const (
progUnknown progStatus = iota progUnknown progStatus = iota // unknown status
progCompile progCompile // compile status
progReady progReady // ready for use status
progError progError // error status (usually caused during compilation)
defaultJitMaxCache int = 64 defaultJitMaxCache int = 64 // maximum amount of jit cached programs
) )
var ( var MaxProgSize int // Max cache size for JIT programs
EnableJit bool // Enables the JIT VM
ForceJit bool // Force the JIT, skip byte VM
MaxProgSize int // Max cache size for JIT Programs
)
var programs *lru.Cache var programs *lru.Cache // lru cache for the JIT programs.
func init() { func init() {
programs, _ = lru.New(defaultJitMaxCache) SetJITCacheSize(defaultJitMaxCache)
} }
// SetJITCacheSize recreates the program cache with the max given size. Setting // 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)) { for pc < uint64(len(program.instructions)) {
instrCount++ instrCount++

View File

@ -84,7 +84,7 @@ func TestCompiling(t *testing.T) {
func TestResetInput(t *testing.T) { func TestResetInput(t *testing.T) {
var sender account var sender account
env := NewEnv() env := NewEnv(false, true)
contract := NewContract(sender, sender, big.NewInt(100), big.NewInt(10000), big.NewInt(0)) contract := NewContract(sender, sender, big.NewInt(100), big.NewInt(10000), big.NewInt(0))
contract.CodeAddr = &common.Address{} contract.CodeAddr = &common.Address{}
@ -143,10 +143,7 @@ func runVmBench(test vmBench, b *testing.B) {
if test.precompile && !test.forcejit { if test.precompile && !test.forcejit {
NewProgram(test.code) NewProgram(test.code)
} }
env := NewEnv() env := NewEnv(test.nojit, test.forcejit)
EnableJit = !test.nojit
ForceJit = test.forcejit
b.ResetTimer() b.ResetTimer()
@ -168,12 +165,16 @@ type Env struct {
evm *EVM evm *EVM
} }
func NewEnv() *Env { func NewEnv(noJit, forceJit bool) *Env {
env := &Env{gasLimit: big.NewInt(10000), depth: 0} env := &Env{gasLimit: big.NewInt(10000), depth: 0}
env.evm = New(env, nil) env.evm = New(env, Config{
EnableJit: !noJit,
ForceJit: forceJit,
})
return env return env
} }
func (self *Env) RuleSet() RuleSet { return ruleSet{new(big.Int)} }
func (self *Env) Vm() Vm { return self.evm } func (self *Env) Vm() Vm { return self.evm }
func (self *Env) Origin() common.Address { return common.Address{} } func (self *Env) Origin() common.Address { return common.Address{} }
func (self *Env) BlockNumber() *big.Int { return big.NewInt(0) } func (self *Env) BlockNumber() *big.Int { return big.NewInt(0) }

View File

@ -1,10 +1,6 @@
package vm package vm
import ( import "math/big"
"math/big"
"github.com/ethereum/go-ethereum/params"
)
type jumpPtr struct { type jumpPtr struct {
fn instrFn fn instrFn
@ -13,12 +9,12 @@ type jumpPtr struct {
type vmJumpTable [256]jumpPtr type vmJumpTable [256]jumpPtr
func newJumpTable(blockNumber *big.Int) vmJumpTable { func newJumpTable(ruleset RuleSet, blockNumber *big.Int) vmJumpTable {
var jumpTable vmJumpTable var jumpTable vmJumpTable
// when initialising a new VM execution we must first check the homestead // when initialising a new VM execution we must first check the homestead
// changes. // changes.
if params.IsHomestead(blockNumber) { if ruleset.IsHomestead(blockNumber) {
jumpTable[DELEGATECALL] = jumpPtr{opDelegateCall, true} jumpTable[DELEGATECALL] = jumpPtr{opDelegateCall, true}
} }

View File

@ -3,20 +3,16 @@ package vm
import ( import (
"math/big" "math/big"
"testing" "testing"
"github.com/ethereum/go-ethereum/params"
) )
func TestInit(t *testing.T) { func TestInit(t *testing.T) {
params.HomesteadBlock = big.NewInt(1) jumpTable := newJumpTable(ruleSet{big.NewInt(1)}, big.NewInt(0))
jumpTable := newJumpTable(big.NewInt(0))
if jumpTable[DELEGATECALL].valid { if jumpTable[DELEGATECALL].valid {
t.Error("Expected DELEGATECALL not to be present") t.Error("Expected DELEGATECALL not to be present")
} }
for _, n := range []int64{1, 2, 100} { 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 { if !jumpTable[DELEGATECALL].valid {
t.Error("Expected DELEGATECALL to be present for block", n) t.Error("Expected DELEGATECALL to be present for block", n)
} }

View File

@ -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 logs a new structured log message and pushes it out to the environment
// //
// captureState also tracks SSTORE ops to track dirty values. // 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 // short circuit if no log collector is present
if l.cfg.Collector == nil { if l.cfg.Collector == nil {
return return

View File

@ -47,7 +47,7 @@ type dummyEnv struct {
func newDummyEnv(ref *dummyContractRef) *dummyEnv { func newDummyEnv(ref *dummyContractRef) *dummyEnv {
return &dummyEnv{ return &dummyEnv{
Env: NewEnv(), Env: NewEnv(true, false),
ref: ref, ref: ref,
} }
} }
@ -58,7 +58,7 @@ func (d dummyEnv) AddStructLog(StructLog) {}
func TestStoreCapture(t *testing.T) { func TestStoreCapture(t *testing.T) {
var ( var (
env = NewEnv() env = NewEnv(true, false)
logger = newLogger(LogConfig{Collector: env}, env) logger = newLogger(LogConfig{Collector: env}, env)
mem = NewMemory() mem = NewMemory()
stack = newstack() stack = newstack()
@ -69,7 +69,7 @@ func TestStoreCapture(t *testing.T) {
var index common.Hash 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 { 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()])) 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() 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 { if ref.calledForEach {
t.Error("didn't expect for each to be called") t.Error("didn't expect for each to be called")
} }
logger = newLogger(LogConfig{Collector: env, FullStorage: true}, env) 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 { if !ref.calledForEach {
t.Error("expected for each to be called") t.Error("expected for each to be called")
} }

View File

@ -27,8 +27,9 @@ import (
// Env is a basic runtime environment required for running the EVM. // Env is a basic runtime environment required for running the EVM.
type Env struct { type Env struct {
depth int ruleSet vm.RuleSet
state *state.StateDB depth int
state *state.StateDB
origin common.Address origin common.Address
coinbase common.Address coinbase common.Address
@ -48,6 +49,7 @@ type Env struct {
// NewEnv returns a new vm.Environment // NewEnv returns a new vm.Environment
func NewEnv(cfg *Config, state *state.StateDB) vm.Environment { func NewEnv(cfg *Config, state *state.StateDB) vm.Environment {
env := &Env{ env := &Env{
ruleSet: cfg.RuleSet,
state: state, state: state,
origin: cfg.Origin, origin: cfg.Origin,
coinbase: cfg.Coinbase, coinbase: cfg.Coinbase,
@ -56,7 +58,7 @@ func NewEnv(cfg *Config, state *state.StateDB) vm.Environment {
difficulty: cfg.Difficulty, difficulty: cfg.Difficulty,
gasLimit: cfg.GasLimit, gasLimit: cfg.GasLimit,
} }
env.evm = vm.New(env, &vm.Config{ env.evm = vm.New(env, vm.Config{
Debug: cfg.Debug, Debug: cfg.Debug,
EnableJit: !cfg.DisableJit, EnableJit: !cfg.DisableJit,
ForceJit: !cfg.DisableJit, ForceJit: !cfg.DisableJit,
@ -77,6 +79,7 @@ func (self *Env) AddStructLog(log vm.StructLog) {
self.logs = append(self.logs, log) 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) Vm() vm.Vm { return self.evm }
func (self *Env) Origin() common.Address { return self.origin } func (self *Env) Origin() common.Address { return self.origin }
func (self *Env) BlockNumber() *big.Int { return self.number } func (self *Env) BlockNumber() *big.Int { return self.number }

View File

@ -22,13 +22,20 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/state" "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/crypto"
"github.com/ethereum/go-ethereum/ethdb" "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 // Config is a basic type specifying certain configuration flags for running
// the EVM. // the EVM.
type Config struct { type Config struct {
RuleSet vm.RuleSet
Difficulty *big.Int Difficulty *big.Int
Origin common.Address Origin common.Address
Coinbase common.Address Coinbase common.Address
@ -46,6 +53,10 @@ type Config struct {
// sets defaults on the config // sets defaults on the config
func setDefaults(cfg *Config) { func setDefaults(cfg *Config) {
if cfg.RuleSet == nil {
cfg.RuleSet = ruleSet{}
}
if cfg.Difficulty == nil { if cfg.Difficulty == nil {
cfg.Difficulty = new(big.Int) cfg.Difficulty = new(big.Int)
} }

9
core/vm/util_test.go Normal file
View File

@ -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 }

View File

@ -43,18 +43,13 @@ type Config struct {
type EVM struct { type EVM struct {
env Environment env Environment
jumpTable vmJumpTable jumpTable vmJumpTable
cfg *Config cfg Config
logger *Logger logger *Logger
} }
// New returns a new instance of the EVM. // New returns a new instance of the EVM.
func New(env Environment, cfg *Config) *EVM { func New(env Environment, cfg Config) *EVM {
// initialise a default config if none is present
if cfg == nil {
cfg = new(Config)
}
var logger *Logger var logger *Logger
if cfg.Debug { if cfg.Debug {
logger = newLogger(cfg.Logger, env) logger = newLogger(cfg.Logger, env)
@ -62,7 +57,7 @@ func New(env Environment, cfg *Config) *EVM {
return &EVM{ return &EVM{
env: env, env: env,
jumpTable: newJumpTable(env.BlockNumber()), jumpTable: newJumpTable(env.RuleSet(), env.BlockNumber()),
cfg: cfg, cfg: cfg,
logger: logger, 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. // User defer pattern to check for an error and, based on the error being nil or not, use all gas and return.
defer func() { defer func() {
if err != nil && evm.cfg.Debug { 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()) mem.Resize(newMemSize.Uint64())
// Add a log message // Add a log message
if evm.cfg.Debug { 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 { if opPtr := evm.jumpTable[op]; opPtr.valid {

View File

@ -22,5 +22,5 @@ import "fmt"
func NewJitVm(env Environment) VirtualMachine { func NewJitVm(env Environment) VirtualMachine {
fmt.Printf("Warning! EVM JIT not enabled.\n") fmt.Printf("Warning! EVM JIT not enabled.\n")
return New(env, nil) return New(env, Config{})
} }

View File

@ -41,30 +41,26 @@ func GetHashFn(ref common.Hash, chain *BlockChain) func(n uint64) common.Hash {
} }
type VMEnv struct { type VMEnv struct {
state *state.StateDB // State to use for executing chainConfig *ChainConfig // Chain configuration
evm *vm.EVM // The Ethereum Virtual Machine state *state.StateDB // State to use for executing
depth int // Current execution depth evm *vm.EVM // The Ethereum Virtual Machine
msg Message // Message appliod depth int // Current execution depth
msg Message // Message appliod
header *types.Header // Header information header *types.Header // Header information
chain *BlockChain // Blockchain handle chain *BlockChain // Blockchain handle
logs []vm.StructLog // Logs for the custom structured logger logs []vm.StructLog // Logs for the custom structured logger
getHashFn func(uint64) common.Hash // getHashFn callback is used to retrieve block hashes 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{ env := &VMEnv{
chain: chain, chainConfig: chainConfig,
state: state, chain: chain,
header: header, state: state,
msg: msg, header: header,
getHashFn: GetHashFn(header.ParentHash, chain), msg: msg,
} getHashFn: GetHashFn(header.ParentHash, chain),
// initialise a default config if none present
if cfg == nil {
cfg = new(vm.Config)
} }
// if no log collector is present set self as the collector // 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 return env
} }
func (self *VMEnv) RuleSet() vm.RuleSet { return self.chainConfig }
func (self *VMEnv) Vm() vm.Vm { return self.evm } func (self *VMEnv) Vm() vm.Vm { return self.evm }
func (self *VMEnv) Origin() common.Address { f, _ := self.msg.From(); return f } func (self *VMEnv) Origin() common.Address { f, _ := self.msg.From(); return f }
func (self *VMEnv) BlockNumber() *big.Int { return self.header.Number } func (self *VMEnv) BlockNumber() *big.Int { return self.header.Number }

View File

@ -456,6 +456,7 @@ func (s *PrivateAccountAPI) LockAccount(addr common.Address) bool {
// PublicBlockChainAPI provides an API to access the Ethereum blockchain. // PublicBlockChainAPI provides an API to access the Ethereum blockchain.
// It offers only methods that operate on public data that is freely available to anyone. // It offers only methods that operate on public data that is freely available to anyone.
type PublicBlockChainAPI struct { type PublicBlockChainAPI struct {
config *core.ChainConfig
bc *core.BlockChain bc *core.BlockChain
chainDb ethdb.Database chainDb ethdb.Database
eventMux *event.TypeMux eventMux *event.TypeMux
@ -464,8 +465,8 @@ type PublicBlockChainAPI struct {
} }
// NewPublicBlockChainAPI creates a new Etheruem blockchain API. // 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 { func NewPublicBlockChainAPI(config *core.ChainConfig, 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} return &PublicBlockChainAPI{config: config, bc: bc, miner: m, chainDb: chainDb, eventMux: eventMux, am: am}
} }
// BlockNumber returns the block number of the chain head. // 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 // 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) 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 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. // 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 // PrivateDebugAPI is the collection of Etheruem APIs exposed over the private
// debugging endpoint. // debugging endpoint.
type PrivateDebugAPI struct { type PrivateDebugAPI struct {
eth *Ethereum config *core.ChainConfig
eth *Ethereum
} }
// NewPrivateDebugAPI creates a new API definition for the private debug methods // NewPrivateDebugAPI creates a new API definition for the private debug methods
// of the Ethereum service. // of the Ethereum service.
func NewPrivateDebugAPI(eth *Ethereum) *PrivateDebugAPI { func NewPrivateDebugAPI(config *core.ChainConfig, eth *Ethereum) *PrivateDebugAPI {
return &PrivateDebugAPI{eth: eth} return &PrivateDebugAPI{config: config, eth: eth}
} }
// BlockTraceResults is the returned value when replaying a block to check for // 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.Debug = true // make sure debug is set.
config.Logger.Collector = collector 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 return false, collector.traces, err
} }
statedb, err := state.New(blockchain.GetBlock(block.ParentHash()).Root(), api.eth.ChainDb()) 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 return false, collector.traces, err
} }
receipts, _, usedGas, err := processor.Process(block, statedb, &config) receipts, _, usedGas, err := processor.Process(block, statedb, config)
if err != nil { if err != nil {
return false, collector.traces, err return false, collector.traces, err
} }
@ -1731,7 +1733,7 @@ func (s *PrivateDebugAPI) TraceTransaction(txHash common.Hash, logger vm.LogConf
data: tx.Data(), 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, Debug: true,
Logger: logger, Logger: logger,
}) })
@ -1788,7 +1790,9 @@ func (s *PublicBlockChainAPI) TraceCall(args CallArgs, blockNr rpc.BlockNumber)
} }
// Execute the call and return // 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) gp := new(core.GasPool).AddGas(common.MaxBig)
ret, gas, err := core.ApplyMessage(vmenv, msg, gp) ret, gas, err := core.ApplyMessage(vmenv, msg, gp)

View File

@ -63,6 +63,8 @@ var (
) )
type Config struct { type Config struct {
ChainConfig *core.ChainConfig // chain configuration
NetworkId int // Network ID to use for selecting peers to connect to NetworkId int // Network ID to use for selecting peers to connect to
Genesis string // Genesis JSON to seed the chain database with Genesis string // Genesis JSON to seed the chain database with
FastSync bool // Enables the state download based fast synchronisation algorithm FastSync bool // Enables the state download based fast synchronisation algorithm
@ -100,6 +102,7 @@ type Config struct {
} }
type Ethereum struct { type Ethereum struct {
chainConfig *core.ChainConfig
// Channel for shutting down the ethereum // Channel for shutting down the ethereum
shutdownChan chan bool shutdownChan chan bool
@ -166,12 +169,17 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
// Load up any custom genesis block if requested // Load up any custom genesis block if requested
if len(config.Genesis) > 0 { 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)) block, err := core.WriteGenesisBlock(chainDb, strings.NewReader(config.Genesis))
if err != nil { if err != nil {
return nil, err return nil, err
} }
glog.V(logger.Info).Infof("Successfully wrote custom genesis block: %x", block.Hash()) glog.V(logger.Info).Infof("Successfully wrote custom genesis block: %x", block.Hash())
} }
// Load up a test setup if directly injected // Load up a test setup if directly injected
if config.TestGenesisState != nil { if config.TestGenesisState != nil {
chainDb = config.TestGenesisState chainDb = config.TestGenesisState
@ -227,26 +235,38 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
default: default:
eth.pow = ethash.New() eth.pow = ethash.New()
} }
//genesis := core.GenesisBlock(uint64(config.GenesisNonce), stateDb)
eth.blockchain, err = core.NewBlockChain(chainDb, eth.pow, eth.EventMux()) // load the genesis block or write a new one if no genesis
eth.blockchain.SetConfig(&vm.Config{ // 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, EnableJit: config.EnableJit,
ForceJit: config.ForceJit, ForceJit: config.ForceJit,
}) }
eth.blockchain, err = core.NewBlockChain(chainDb, eth.chainConfig, eth.pow, eth.EventMux())
if err != nil { if err != nil {
if err == core.ErrNoGenesis { 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 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 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 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.SetGasPrice(config.GasPrice)
eth.miner.SetExtra(config.ExtraData) eth.miner.SetExtra(config.ExtraData)
@ -275,7 +295,7 @@ func (s *Ethereum) APIs() []rpc.API {
}, { }, {
Namespace: "eth", Namespace: "eth",
Version: "1.0", 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, Public: true,
}, { }, {
Namespace: "eth", Namespace: "eth",
@ -319,7 +339,7 @@ func (s *Ethereum) APIs() []rpc.API {
}, { }, {
Namespace: "debug", Namespace: "debug",
Version: "1.0", Version: "1.0",
Service: NewPrivateDebugAPI(s), Service: NewPrivateDebugAPI(s.chainConfig, s),
}, { }, {
Namespace: "net", Namespace: "net",
Version: "1.0", Version: "1.0",
@ -328,7 +348,7 @@ func (s *Ethereum) APIs() []rpc.API {
}, { }, {
Namespace: "admin", Namespace: "admin",
Version: "1.0", 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()),
}, },
} }
} }

View File

@ -86,7 +86,7 @@ type ProtocolManager struct {
// NewProtocolManager returns a new ethereum sub protocol manager. The Ethereum sub protocol manages peers capable // NewProtocolManager returns a new ethereum sub protocol manager. The Ethereum sub protocol manages peers capable
// with the ethereum network. // 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 // Figure out whether to allow fast sync or not
if fastSync && blockchain.CurrentBlock().NumberU64() > 0 { if fastSync && blockchain.CurrentBlock().NumberU64() > 0 {
glog.V(logger.Info).Infof("blockchain not empty, fast sync disabled") 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) manager.removePeer)
validator := func(block *types.Block, parent *types.Block) error { 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 { heighter := func() uint64 {
return blockchain.CurrentBlock().NumberU64() return blockchain.CurrentBlock().NumberU64()

View File

@ -34,13 +34,15 @@ func newTestProtocolManager(fastSync bool, blocks int, generator func(int, *core
pow = new(core.FakePow) pow = new(core.FakePow)
db, _ = ethdb.NewMemDatabase() db, _ = ethdb.NewMemDatabase()
genesis = core.WriteGenesisBlockForTesting(db, core.GenesisAccount{testBankAddress, testBankFunds}) 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) chain, _ := core.GenerateChain(genesis, db, blocks, generator)
if _, err := blockchain.InsertChain(chain); err != nil { if _, err := blockchain.InsertChain(chain); err != nil {
panic(err) 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 { if err != nil {
return nil, err return nil, err
} }

View File

@ -51,8 +51,8 @@ type Miner struct {
shouldStart int32 // should start indicates whether we should start after sync shouldStart int32 // should start indicates whether we should start after sync
} }
func New(eth core.Backend, mux *event.TypeMux, pow pow.PoW) *Miner { 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(common.Address{}, eth), canStart: 1} miner := &Miner{eth: eth, mux: mux, pow: pow, worker: newWorker(config, common.Address{}, eth), canStart: 1}
go miner.update() go miner.update()
return miner return miner

View File

@ -61,6 +61,7 @@ type uint64RingBuffer struct {
// environment is the workers current environment and holds // environment is the workers current environment and holds
// all of the current state information // all of the current state information
type Work struct { type Work struct {
config *core.ChainConfig
state *state.StateDB // apply state changes here state *state.StateDB // apply state changes here
ancestors *set.Set // ancestor set (used for checking uncle parent validity) ancestors *set.Set // ancestor set (used for checking uncle parent validity)
family *set.Set // family set (used for checking uncle invalidity) 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 // worker is the main object which takes care of applying messages to the new state
type worker struct { type worker struct {
config *core.ChainConfig
mu sync.Mutex mu sync.Mutex
agents map[Agent]struct{} agents map[Agent]struct{}
@ -122,8 +125,9 @@ type worker struct {
fullValidation bool 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{ worker := &worker{
config: config,
eth: eth, eth: eth,
mux: eth.EventMux(), mux: eth.EventMux(),
chainDb: eth.ChainDb(), chainDb: eth.ChainDb(),
@ -285,7 +289,7 @@ func (self *worker) wait() {
} }
auxValidator := self.eth.BlockChain().AuxValidator() 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) glog.V(logger.Error).Infoln("Invalid header on mined block:", err)
continue continue
} }
@ -371,6 +375,7 @@ func (self *worker) makeCurrent(parent *types.Block, header *types.Header) error
return err return err
} }
work := &Work{ work := &Work{
config: self.config,
state: state, state: state,
ancestors: set.New(), ancestors: set.New(),
family: set.New(), family: set.New(),
@ -470,7 +475,7 @@ func (self *worker) commitNewWork() {
header := &types.Header{ header := &types.Header{
ParentHash: parent.Hash(), ParentHash: parent.Hash(),
Number: num.Add(num, common.Big1), 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), GasLimit: core.CalcGasLimit(parent),
GasUsed: new(big.Int), GasUsed: new(big.Int),
Coinbase: self.coinbase, 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) { func (env *Work) commitTransaction(tx *types.Transaction, bc *core.BlockChain, gp *core.GasPool) (error, vm.Logs) {
snap := env.state.Copy() 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 { if err != nil {
env.state.Set(snap) env.state.Set(snap)
return err, nil return err, nil

View File

@ -40,8 +40,9 @@ var (
EcrecoverGas = big.NewInt(3000) // EcrecoverGas = big.NewInt(3000) //
Sha256WordGas = big.NewInt(12) // Sha256WordGas = big.NewInt(12) //
MinGasLimit = big.NewInt(5000) // Minimum the gas limit may ever be. MinGasLimit = big.NewInt(5000) // Minimum the gas limit may ever be.
GenesisGasLimit = big.NewInt(4712388) // Gas limit of the Genesis block. 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. Sha3Gas = big.NewInt(30) // Once per SHA3 operation.
Sha256Gas = big.NewInt(60) // Sha256Gas = big.NewInt(60) //

View File

@ -19,15 +19,6 @@ package params
import "math/big" import "math/big"
var ( var (
TestNetHomesteadBlock = big.NewInt(494000) // testnet homestead block TestNetHomesteadBlock = big.NewInt(494000) // testnet homestead block
MainNetHomesteadBlock = big.NewInt(1150000) // mainnet homestead block MainNetHomesteadBlock = big.NewInt(1150000) // mainnet homestead block
HomesteadBlock = MainNetHomesteadBlock // homestead block used to check against
) )
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
}

View File

@ -20,77 +20,66 @@ import (
"math/big" "math/big"
"path/filepath" "path/filepath"
"testing" "testing"
"github.com/ethereum/go-ethereum/params"
) )
func TestBcValidBlockTests(t *testing.T) { func TestBcValidBlockTests(t *testing.T) {
params.HomesteadBlock = big.NewInt(1000000) err := RunBlockTest(big.NewInt(1000000), filepath.Join(blockTestDir, "bcValidBlockTest.json"), BlockSkipTests)
err := RunBlockTest(filepath.Join(blockTestDir, "bcValidBlockTest.json"), BlockSkipTests)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
} }
func TestBcUncleHeaderValidityTests(t *testing.T) { func TestBcUncleHeaderValidityTests(t *testing.T) {
params.HomesteadBlock = big.NewInt(1000000) err := RunBlockTest(big.NewInt(1000000), filepath.Join(blockTestDir, "bcUncleHeaderValiditiy.json"), BlockSkipTests)
err := RunBlockTest(filepath.Join(blockTestDir, "bcUncleHeaderValiditiy.json"), BlockSkipTests)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
} }
func TestBcUncleTests(t *testing.T) { func TestBcUncleTests(t *testing.T) {
params.HomesteadBlock = big.NewInt(1000000) err := RunBlockTest(big.NewInt(1000000), filepath.Join(blockTestDir, "bcUncleTest.json"), BlockSkipTests)
err := RunBlockTest(filepath.Join(blockTestDir, "bcUncleTest.json"), BlockSkipTests)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
} }
func TestBcForkUncleTests(t *testing.T) { func TestBcForkUncleTests(t *testing.T) {
params.HomesteadBlock = big.NewInt(1000000) err := RunBlockTest(big.NewInt(1000000), filepath.Join(blockTestDir, "bcForkUncle.json"), BlockSkipTests)
err := RunBlockTest(filepath.Join(blockTestDir, "bcForkUncle.json"), BlockSkipTests)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
} }
func TestBcInvalidHeaderTests(t *testing.T) { func TestBcInvalidHeaderTests(t *testing.T) {
params.HomesteadBlock = big.NewInt(1000000) err := RunBlockTest(big.NewInt(1000000), filepath.Join(blockTestDir, "bcInvalidHeaderTest.json"), BlockSkipTests)
err := RunBlockTest(filepath.Join(blockTestDir, "bcInvalidHeaderTest.json"), BlockSkipTests)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
} }
func TestBcInvalidRLPTests(t *testing.T) { func TestBcInvalidRLPTests(t *testing.T) {
params.HomesteadBlock = big.NewInt(1000000) err := RunBlockTest(big.NewInt(1000000), filepath.Join(blockTestDir, "bcInvalidRLPTest.json"), BlockSkipTests)
err := RunBlockTest(filepath.Join(blockTestDir, "bcInvalidRLPTest.json"), BlockSkipTests)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
} }
func TestBcRPCAPITests(t *testing.T) { func TestBcRPCAPITests(t *testing.T) {
params.HomesteadBlock = big.NewInt(1000000) err := RunBlockTest(big.NewInt(1000000), filepath.Join(blockTestDir, "bcRPC_API_Test.json"), BlockSkipTests)
err := RunBlockTest(filepath.Join(blockTestDir, "bcRPC_API_Test.json"), BlockSkipTests)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
} }
func TestBcForkBlockTests(t *testing.T) { func TestBcForkBlockTests(t *testing.T) {
params.HomesteadBlock = big.NewInt(1000000) err := RunBlockTest(big.NewInt(1000000), filepath.Join(blockTestDir, "bcForkBlockTest.json"), BlockSkipTests)
err := RunBlockTest(filepath.Join(blockTestDir, "bcForkBlockTest.json"), BlockSkipTests)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
} }
func TestBcForkStress(t *testing.T) { func TestBcForkStress(t *testing.T) {
params.HomesteadBlock = big.NewInt(1000000) err := RunBlockTest(big.NewInt(1000000), filepath.Join(blockTestDir, "bcForkStressTest.json"), BlockSkipTests)
err := RunBlockTest(filepath.Join(blockTestDir, "bcForkStressTest.json"), BlockSkipTests)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -100,24 +89,21 @@ func TestBcTotalDifficulty(t *testing.T) {
// skip because these will fail due to selfish mining fix // skip because these will fail due to selfish mining fix
t.Skip() t.Skip()
params.HomesteadBlock = big.NewInt(1000000) err := RunBlockTest(big.NewInt(1000000), filepath.Join(blockTestDir, "bcTotalDifficultyTest.json"), BlockSkipTests)
err := RunBlockTest(filepath.Join(blockTestDir, "bcTotalDifficultyTest.json"), BlockSkipTests)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
} }
func TestBcWallet(t *testing.T) { func TestBcWallet(t *testing.T) {
params.HomesteadBlock = big.NewInt(1000000) err := RunBlockTest(big.NewInt(1000000), filepath.Join(blockTestDir, "bcWalletTest.json"), BlockSkipTests)
err := RunBlockTest(filepath.Join(blockTestDir, "bcWalletTest.json"), BlockSkipTests)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
} }
func TestBcGasPricer(t *testing.T) { func TestBcGasPricer(t *testing.T) {
params.HomesteadBlock = big.NewInt(1000000) err := RunBlockTest(big.NewInt(1000000), filepath.Join(blockTestDir, "bcGasPricerTest.json"), BlockSkipTests)
err := RunBlockTest(filepath.Join(blockTestDir, "bcGasPricerTest.json"), BlockSkipTests)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -125,8 +111,7 @@ func TestBcGasPricer(t *testing.T) {
// TODO: iterate over files once we got more than a few // TODO: iterate over files once we got more than a few
func TestBcRandom(t *testing.T) { func TestBcRandom(t *testing.T) {
params.HomesteadBlock = big.NewInt(1000000) err := RunBlockTest(big.NewInt(1000000), filepath.Join(blockTestDir, "RandomTests/bl201507071825GO.json"), BlockSkipTests)
err := RunBlockTest(filepath.Join(blockTestDir, "RandomTests/bl201507071825GO.json"), BlockSkipTests)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -136,16 +121,14 @@ func TestBcMultiChain(t *testing.T) {
// skip due to selfish mining // skip due to selfish mining
t.Skip() t.Skip()
params.HomesteadBlock = big.NewInt(1000000) err := RunBlockTest(big.NewInt(1000000), filepath.Join(blockTestDir, "bcMultiChainTest.json"), BlockSkipTests)
err := RunBlockTest(filepath.Join(blockTestDir, "bcMultiChainTest.json"), BlockSkipTests)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
} }
func TestBcState(t *testing.T) { func TestBcState(t *testing.T) {
params.HomesteadBlock = big.NewInt(1000000) err := RunBlockTest(big.NewInt(1000000), filepath.Join(blockTestDir, "bcStateTest.json"), BlockSkipTests)
err := RunBlockTest(filepath.Join(blockTestDir, "bcStateTest.json"), BlockSkipTests)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -153,88 +136,77 @@ func TestBcState(t *testing.T) {
// Homestead tests // Homestead tests
func TestHomesteadBcValidBlockTests(t *testing.T) { func TestHomesteadBcValidBlockTests(t *testing.T) {
params.HomesteadBlock = big.NewInt(0) err := RunBlockTest(big.NewInt(0), filepath.Join(blockTestDir, "Homestead", "bcValidBlockTest.json"), BlockSkipTests)
err := RunBlockTest(filepath.Join(blockTestDir, "Homestead", "bcValidBlockTest.json"), BlockSkipTests)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
} }
func TestHomesteadBcUncleHeaderValidityTests(t *testing.T) { func TestHomesteadBcUncleHeaderValidityTests(t *testing.T) {
params.HomesteadBlock = big.NewInt(0) err := RunBlockTest(big.NewInt(0), filepath.Join(blockTestDir, "Homestead", "bcUncleHeaderValiditiy.json"), BlockSkipTests)
err := RunBlockTest(filepath.Join(blockTestDir, "Homestead", "bcUncleHeaderValiditiy.json"), BlockSkipTests)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
} }
func TestHomesteadBcUncleTests(t *testing.T) { func TestHomesteadBcUncleTests(t *testing.T) {
params.HomesteadBlock = big.NewInt(0) err := RunBlockTest(big.NewInt(0), filepath.Join(blockTestDir, "Homestead", "bcUncleTest.json"), BlockSkipTests)
err := RunBlockTest(filepath.Join(blockTestDir, "Homestead", "bcUncleTest.json"), BlockSkipTests)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
} }
func TestHomesteadBcInvalidHeaderTests(t *testing.T) { func TestHomesteadBcInvalidHeaderTests(t *testing.T) {
params.HomesteadBlock = big.NewInt(0) err := RunBlockTest(big.NewInt(0), filepath.Join(blockTestDir, "Homestead", "bcInvalidHeaderTest.json"), BlockSkipTests)
err := RunBlockTest(filepath.Join(blockTestDir, "Homestead", "bcInvalidHeaderTest.json"), BlockSkipTests)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
} }
func TestHomesteadBcRPCAPITests(t *testing.T) { func TestHomesteadBcRPCAPITests(t *testing.T) {
params.HomesteadBlock = big.NewInt(0) err := RunBlockTest(big.NewInt(0), filepath.Join(blockTestDir, "Homestead", "bcRPC_API_Test.json"), BlockSkipTests)
err := RunBlockTest(filepath.Join(blockTestDir, "Homestead", "bcRPC_API_Test.json"), BlockSkipTests)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
} }
func TestHomesteadBcForkStress(t *testing.T) { func TestHomesteadBcForkStress(t *testing.T) {
params.HomesteadBlock = big.NewInt(0) err := RunBlockTest(big.NewInt(0), filepath.Join(blockTestDir, "Homestead", "bcForkStressTest.json"), BlockSkipTests)
err := RunBlockTest(filepath.Join(blockTestDir, "Homestead", "bcForkStressTest.json"), BlockSkipTests)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
} }
func TestHomesteadBcTotalDifficulty(t *testing.T) { func TestHomesteadBcTotalDifficulty(t *testing.T) {
params.HomesteadBlock = big.NewInt(0) err := RunBlockTest(big.NewInt(0), filepath.Join(blockTestDir, "Homestead", "bcTotalDifficultyTest.json"), BlockSkipTests)
err := RunBlockTest(filepath.Join(blockTestDir, "Homestead", "bcTotalDifficultyTest.json"), BlockSkipTests)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
} }
func TestHomesteadBcWallet(t *testing.T) { func TestHomesteadBcWallet(t *testing.T) {
params.HomesteadBlock = big.NewInt(0) err := RunBlockTest(big.NewInt(0), filepath.Join(blockTestDir, "Homestead", "bcWalletTest.json"), BlockSkipTests)
err := RunBlockTest(filepath.Join(blockTestDir, "Homestead", "bcWalletTest.json"), BlockSkipTests)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
} }
func TestHomesteadBcGasPricer(t *testing.T) { func TestHomesteadBcGasPricer(t *testing.T) {
params.HomesteadBlock = big.NewInt(0) err := RunBlockTest(big.NewInt(0), filepath.Join(blockTestDir, "Homestead", "bcGasPricerTest.json"), BlockSkipTests)
err := RunBlockTest(filepath.Join(blockTestDir, "Homestead", "bcGasPricerTest.json"), BlockSkipTests)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
} }
func TestHomesteadBcMultiChain(t *testing.T) { func TestHomesteadBcMultiChain(t *testing.T) {
params.HomesteadBlock = big.NewInt(0) err := RunBlockTest(big.NewInt(0), filepath.Join(blockTestDir, "Homestead", "bcMultiChainTest.json"), BlockSkipTests)
err := RunBlockTest(filepath.Join(blockTestDir, "Homestead", "bcMultiChainTest.json"), BlockSkipTests)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
} }
func TestHomesteadBcState(t *testing.T) { func TestHomesteadBcState(t *testing.T) {
params.HomesteadBlock = big.NewInt(0) err := RunBlockTest(big.NewInt(0), filepath.Join(blockTestDir, "Homestead", "bcStateTest.json"), BlockSkipTests)
err := RunBlockTest(filepath.Join(blockTestDir, "Homestead", "bcStateTest.json"), BlockSkipTests)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

View File

@ -108,7 +108,7 @@ type btTransaction struct {
Value string 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) btjs := make(map[string]*btJSON)
if err := readJson(r, &btjs); err != nil { if err := readJson(r, &btjs); err != nil {
return err return err
@ -119,13 +119,13 @@ func RunBlockTestWithReader(r io.Reader, skipTests []string) error {
return err return err
} }
if err := runBlockTests(bt, skipTests); err != nil { if err := runBlockTests(homesteadBlock, bt, skipTests); err != nil {
return err return err
} }
return nil return nil
} }
func RunBlockTest(file string, skipTests []string) error { func RunBlockTest(homesteadBlock *big.Int, file string, skipTests []string) error {
btjs := make(map[string]*btJSON) btjs := make(map[string]*btJSON)
if err := readJsonFile(file, &btjs); err != nil { if err := readJsonFile(file, &btjs); err != nil {
return err return err
@ -135,13 +135,13 @@ func RunBlockTest(file string, skipTests []string) error {
if err != nil { if err != nil {
return err return err
} }
if err := runBlockTests(bt, skipTests); err != nil { if err := runBlockTests(homesteadBlock, bt, skipTests); err != nil {
return err return err
} }
return nil 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)) skipTest := make(map[string]bool, len(skipTests))
for _, name := range skipTests { for _, name := range skipTests {
skipTest[name] = true skipTest[name] = true
@ -153,7 +153,7 @@ func runBlockTests(bt map[string]*BlockTest, skipTests []string) error {
continue continue
} }
// test the block // test the block
if err := runBlockTest(test); err != nil { if err := runBlockTest(homesteadBlock, test); err != nil {
return fmt.Errorf("%s: %v", name, err) return fmt.Errorf("%s: %v", name, err)
} }
glog.Infoln("Block test passed: ", name) glog.Infoln("Block test passed: ", name)
@ -162,7 +162,7 @@ func runBlockTests(bt map[string]*BlockTest, skipTests []string) error {
return nil 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) ks := crypto.NewKeyStorePassphrase(filepath.Join(common.DefaultDataDir(), "keystore"), crypto.StandardScryptN, crypto.StandardScryptP)
am := accounts.NewManager(ks) am := accounts.NewManager(ks)
db, _ := ethdb.NewMemDatabase() db, _ := ethdb.NewMemDatabase()
@ -174,6 +174,7 @@ func runBlockTest(test *BlockTest) error {
} }
cfg := &eth.Config{ cfg := &eth.Config{
ChainConfig: &core.ChainConfig{HomesteadBlock: homesteadBlock},
TestGenesisState: db, TestGenesisState: db,
TestGenesisBlock: test.Genesis, TestGenesisBlock: test.Genesis,
Etherbase: common.Address{}, Etherbase: common.Address{},

View File

@ -21,18 +21,8 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"testing" "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) { func BenchmarkStateCall1024(b *testing.B) {
fn := filepath.Join(stateTestDir, "stCallCreateCallCodeTest.json") fn := filepath.Join(stateTestDir, "stCallCreateCallCodeTest.json")
if err := BenchVmTest(fn, bconf{"Call1024BalanceTooLow", true, os.Getenv("JITVM") == "true"}, b); err != nil { 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) { func TestStateSystemOperations(t *testing.T) {
params.HomesteadBlock = big.NewInt(1000000) ruleSet := RuleSet{
HomesteadBlock: big.NewInt(1000000),
}
fn := filepath.Join(stateTestDir, "stSystemOperationsTest.json") fn := filepath.Join(stateTestDir, "stSystemOperationsTest.json")
if err := RunStateTest(fn, StateSkipTests); err != nil { if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil {
t.Error(err) t.Error(err)
} }
} }
func TestStateExample(t *testing.T) { func TestStateExample(t *testing.T) {
params.HomesteadBlock = big.NewInt(1000000) ruleSet := RuleSet{
HomesteadBlock: big.NewInt(1000000),
}
fn := filepath.Join(stateTestDir, "stExample.json") fn := filepath.Join(stateTestDir, "stExample.json")
if err := RunStateTest(fn, StateSkipTests); err != nil { if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil {
t.Error(err) t.Error(err)
} }
} }
func TestStatePreCompiledContracts(t *testing.T) { func TestStatePreCompiledContracts(t *testing.T) {
params.HomesteadBlock = big.NewInt(1000000) ruleSet := RuleSet{
HomesteadBlock: big.NewInt(1000000),
}
fn := filepath.Join(stateTestDir, "stPreCompiledContracts.json") fn := filepath.Join(stateTestDir, "stPreCompiledContracts.json")
if err := RunStateTest(fn, StateSkipTests); err != nil { if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil {
t.Error(err) t.Error(err)
} }
} }
func TestStateRecursiveCreate(t *testing.T) { func TestStateRecursiveCreate(t *testing.T) {
params.HomesteadBlock = big.NewInt(1000000) ruleSet := RuleSet{
HomesteadBlock: big.NewInt(1000000),
}
fn := filepath.Join(stateTestDir, "stRecursiveCreate.json") fn := filepath.Join(stateTestDir, "stRecursiveCreate.json")
if err := RunStateTest(fn, StateSkipTests); err != nil { if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil {
t.Error(err) t.Error(err)
} }
} }
func TestStateSpecial(t *testing.T) { func TestStateSpecial(t *testing.T) {
params.HomesteadBlock = big.NewInt(1000000) ruleSet := RuleSet{
HomesteadBlock: big.NewInt(1000000),
}
fn := filepath.Join(stateTestDir, "stSpecialTest.json") fn := filepath.Join(stateTestDir, "stSpecialTest.json")
if err := RunStateTest(fn, StateSkipTests); err != nil { if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil {
t.Error(err) t.Error(err)
} }
} }
func TestStateRefund(t *testing.T) { func TestStateRefund(t *testing.T) {
params.HomesteadBlock = big.NewInt(1000000) ruleSet := RuleSet{
HomesteadBlock: big.NewInt(1000000),
}
fn := filepath.Join(stateTestDir, "stRefundTest.json") fn := filepath.Join(stateTestDir, "stRefundTest.json")
if err := RunStateTest(fn, StateSkipTests); err != nil { if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil {
t.Error(err) t.Error(err)
} }
} }
func TestStateBlockHash(t *testing.T) { func TestStateBlockHash(t *testing.T) {
params.HomesteadBlock = big.NewInt(1000000) ruleSet := RuleSet{
HomesteadBlock: big.NewInt(1000000),
}
fn := filepath.Join(stateTestDir, "stBlockHashTest.json") fn := filepath.Join(stateTestDir, "stBlockHashTest.json")
if err := RunStateTest(fn, StateSkipTests); err != nil { if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil {
t.Error(err) t.Error(err)
} }
} }
func TestStateInitCode(t *testing.T) { func TestStateInitCode(t *testing.T) {
params.HomesteadBlock = big.NewInt(1000000) ruleSet := RuleSet{
HomesteadBlock: big.NewInt(1000000),
}
fn := filepath.Join(stateTestDir, "stInitCodeTest.json") fn := filepath.Join(stateTestDir, "stInitCodeTest.json")
if err := RunStateTest(fn, StateSkipTests); err != nil { if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil {
t.Error(err) t.Error(err)
} }
} }
func TestStateLog(t *testing.T) { func TestStateLog(t *testing.T) {
params.HomesteadBlock = big.NewInt(1000000) ruleSet := RuleSet{
HomesteadBlock: big.NewInt(1000000),
}
fn := filepath.Join(stateTestDir, "stLogTests.json") fn := filepath.Join(stateTestDir, "stLogTests.json")
if err := RunStateTest(fn, StateSkipTests); err != nil { if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil {
t.Error(err) t.Error(err)
} }
} }
func TestStateTransaction(t *testing.T) { func TestStateTransaction(t *testing.T) {
params.HomesteadBlock = big.NewInt(1000000) ruleSet := RuleSet{
HomesteadBlock: big.NewInt(1000000),
}
fn := filepath.Join(stateTestDir, "stTransactionTest.json") fn := filepath.Join(stateTestDir, "stTransactionTest.json")
if err := RunStateTest(fn, StateSkipTests); err != nil { if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil {
t.Error(err) t.Error(err)
} }
} }
func TestStateTransition(t *testing.T) { func TestStateTransition(t *testing.T) {
params.HomesteadBlock = big.NewInt(1000000) ruleSet := RuleSet{
HomesteadBlock: big.NewInt(1000000),
}
fn := filepath.Join(stateTestDir, "stTransitionTest.json") fn := filepath.Join(stateTestDir, "stTransitionTest.json")
if err := RunStateTest(fn, StateSkipTests); err != nil { if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil {
t.Error(err) t.Error(err)
} }
} }
func TestCallCreateCallCode(t *testing.T) { func TestCallCreateCallCode(t *testing.T) {
params.HomesteadBlock = big.NewInt(1000000) ruleSet := RuleSet{
HomesteadBlock: big.NewInt(1000000),
}
fn := filepath.Join(stateTestDir, "stCallCreateCallCodeTest.json") fn := filepath.Join(stateTestDir, "stCallCreateCallCodeTest.json")
if err := RunStateTest(fn, StateSkipTests); err != nil { if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil {
t.Error(err) t.Error(err)
} }
} }
func TestCallCodes(t *testing.T) { func TestCallCodes(t *testing.T) {
params.HomesteadBlock = big.NewInt(1000000) ruleSet := RuleSet{
HomesteadBlock: big.NewInt(1000000),
}
fn := filepath.Join(stateTestDir, "stCallCodes.json") fn := filepath.Join(stateTestDir, "stCallCodes.json")
if err := RunStateTest(fn, StateSkipTests); err != nil { if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil {
t.Error(err) t.Error(err)
} }
} }
func TestDelegateCall(t *testing.T) { func TestDelegateCall(t *testing.T) {
params.HomesteadBlock = big.NewInt(1000000) ruleSet := RuleSet{
HomesteadBlock: big.NewInt(1000000),
}
fn := filepath.Join(stateTestDir, "stDelegatecallTest.json") fn := filepath.Join(stateTestDir, "stDelegatecallTest.json")
if err := RunStateTest(fn, StateSkipTests); err != nil { if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil {
t.Error(err) t.Error(err)
} }
} }
func TestMemory(t *testing.T) { func TestMemory(t *testing.T) {
params.HomesteadBlock = big.NewInt(1000000) ruleSet := RuleSet{
HomesteadBlock: big.NewInt(1000000),
}
fn := filepath.Join(stateTestDir, "stMemoryTest.json") fn := filepath.Join(stateTestDir, "stMemoryTest.json")
if err := RunStateTest(fn, StateSkipTests); err != nil { if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil {
t.Error(err) t.Error(err)
} }
} }
func TestMemoryStress(t *testing.T) { func TestMemoryStress(t *testing.T) {
params.HomesteadBlock = big.NewInt(1000000) ruleSet := RuleSet{
HomesteadBlock: big.NewInt(1000000),
}
if os.Getenv("TEST_VM_COMPLEX") == "" { if os.Getenv("TEST_VM_COMPLEX") == "" {
t.Skip() t.Skip()
} }
fn := filepath.Join(stateTestDir, "stMemoryStressTest.json") fn := filepath.Join(stateTestDir, "stMemoryStressTest.json")
if err := RunStateTest(fn, StateSkipTests); err != nil { if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil {
t.Error(err) t.Error(err)
} }
} }
func TestQuadraticComplexity(t *testing.T) { func TestQuadraticComplexity(t *testing.T) {
params.HomesteadBlock = big.NewInt(1000000) ruleSet := RuleSet{
HomesteadBlock: big.NewInt(1000000),
}
if os.Getenv("TEST_VM_COMPLEX") == "" { if os.Getenv("TEST_VM_COMPLEX") == "" {
t.Skip() t.Skip()
} }
fn := filepath.Join(stateTestDir, "stQuadraticComplexityTest.json") fn := filepath.Join(stateTestDir, "stQuadraticComplexityTest.json")
if err := RunStateTest(fn, StateSkipTests); err != nil { if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil {
t.Error(err) t.Error(err)
} }
} }
func TestSolidity(t *testing.T) { func TestSolidity(t *testing.T) {
params.HomesteadBlock = big.NewInt(1000000) ruleSet := RuleSet{
HomesteadBlock: big.NewInt(1000000),
}
fn := filepath.Join(stateTestDir, "stSolidityTest.json") fn := filepath.Join(stateTestDir, "stSolidityTest.json")
if err := RunStateTest(fn, StateSkipTests); err != nil { if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil {
t.Error(err) t.Error(err)
} }
} }
func TestWallet(t *testing.T) { func TestWallet(t *testing.T) {
params.HomesteadBlock = big.NewInt(1000000) ruleSet := RuleSet{
HomesteadBlock: big.NewInt(1000000),
}
fn := filepath.Join(stateTestDir, "stWalletTest.json") fn := filepath.Join(stateTestDir, "stWalletTest.json")
if err := RunStateTest(fn, StateSkipTests); err != nil { if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil {
t.Error(err) t.Error(err)
} }
} }
func TestStateTestsRandom(t *testing.T) { func TestStateTestsRandom(t *testing.T) {
params.HomesteadBlock = big.NewInt(1000000) ruleSet := RuleSet{
HomesteadBlock: big.NewInt(1000000),
}
fns, _ := filepath.Glob("./files/StateTests/RandomTests/*") fns, _ := filepath.Glob("./files/StateTests/RandomTests/*")
for _, fn := range fns { for _, fn := range fns {
if err := RunStateTest(fn, StateSkipTests); err != nil { if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil {
t.Error(err) t.Error(err)
} }
} }
@ -230,151 +260,172 @@ func TestStateTestsRandom(t *testing.T) {
// homestead tests // homestead tests
func TestHomesteadStateSystemOperations(t *testing.T) { func TestHomesteadStateSystemOperations(t *testing.T) {
params.HomesteadBlock = big.NewInt(0) ruleSet := RuleSet{
HomesteadBlock: new(big.Int),
}
fn := filepath.Join(stateTestDir, "Homestead", "stSystemOperationsTest.json") 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) t.Error(err)
} }
} }
func TestHomesteadStatePreCompiledContracts(t *testing.T) { func TestHomesteadStatePreCompiledContracts(t *testing.T) {
params.HomesteadBlock = big.NewInt(0) ruleSet := RuleSet{
HomesteadBlock: new(big.Int),
}
fn := filepath.Join(stateTestDir, "Homestead", "stPreCompiledContracts.json") 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) t.Error(err)
} }
} }
func TestHomesteadStateRecursiveCreate(t *testing.T) { func TestHomesteadStateRecursiveCreate(t *testing.T) {
params.HomesteadBlock = big.NewInt(0) ruleSet := RuleSet{
HomesteadBlock: new(big.Int),
fn := filepath.Join(stateTestDir, "Homestead", "stRecursiveCreate.json")
if err := RunStateTest(fn, StateSkipTests); err != nil {
t.Error(err)
} }
}
func TestHomesteadStateSpecial(t *testing.T) {
params.HomesteadBlock = big.NewInt(0)
fn := filepath.Join(stateTestDir, "Homestead", "stSpecialTest.json") 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) t.Error(err)
} }
} }
func TestHomesteadStateRefund(t *testing.T) { func TestHomesteadStateRefund(t *testing.T) {
params.HomesteadBlock = big.NewInt(0) ruleSet := RuleSet{
HomesteadBlock: new(big.Int),
}
fn := filepath.Join(stateTestDir, "Homestead", "stRefundTest.json") 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) t.Error(err)
} }
} }
func TestHomesteadStateInitCode(t *testing.T) { func TestHomesteadStateInitCode(t *testing.T) {
params.HomesteadBlock = big.NewInt(0) ruleSet := RuleSet{
HomesteadBlock: new(big.Int),
}
fn := filepath.Join(stateTestDir, "Homestead", "stInitCodeTest.json") 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) t.Error(err)
} }
} }
func TestHomesteadStateLog(t *testing.T) { func TestHomesteadStateLog(t *testing.T) {
params.HomesteadBlock = big.NewInt(0) ruleSet := RuleSet{
HomesteadBlock: new(big.Int),
}
fn := filepath.Join(stateTestDir, "Homestead", "stLogTests.json") 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) t.Error(err)
} }
} }
func TestHomesteadStateTransaction(t *testing.T) { func TestHomesteadStateTransaction(t *testing.T) {
params.HomesteadBlock = big.NewInt(0) ruleSet := RuleSet{
HomesteadBlock: new(big.Int),
}
fn := filepath.Join(stateTestDir, "Homestead", "stTransactionTest.json") 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) t.Error(err)
} }
} }
func TestHomesteadCallCreateCallCode(t *testing.T) { func TestHomesteadCallCreateCallCode(t *testing.T) {
params.HomesteadBlock = big.NewInt(0) ruleSet := RuleSet{
HomesteadBlock: new(big.Int),
}
fn := filepath.Join(stateTestDir, "Homestead", "stCallCreateCallCodeTest.json") 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) t.Error(err)
} }
} }
func TestHomesteadCallCodes(t *testing.T) { func TestHomesteadCallCodes(t *testing.T) {
params.HomesteadBlock = big.NewInt(0) ruleSet := RuleSet{
HomesteadBlock: new(big.Int),
}
fn := filepath.Join(stateTestDir, "Homestead", "stCallCodes.json") 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) t.Error(err)
} }
} }
func TestHomesteadMemory(t *testing.T) { func TestHomesteadMemory(t *testing.T) {
params.HomesteadBlock = big.NewInt(0) ruleSet := RuleSet{
HomesteadBlock: new(big.Int),
}
fn := filepath.Join(stateTestDir, "Homestead", "stMemoryTest.json") 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) t.Error(err)
} }
} }
func TestHomesteadMemoryStress(t *testing.T) { func TestHomesteadMemoryStress(t *testing.T) {
params.HomesteadBlock = big.NewInt(0) ruleSet := RuleSet{
HomesteadBlock: new(big.Int),
}
if os.Getenv("TEST_VM_COMPLEX") == "" { if os.Getenv("TEST_VM_COMPLEX") == "" {
t.Skip() t.Skip()
} }
fn := filepath.Join(stateTestDir, "Homestead", "stMemoryStressTest.json") 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) t.Error(err)
} }
} }
func TestHomesteadQuadraticComplexity(t *testing.T) { func TestHomesteadQuadraticComplexity(t *testing.T) {
params.HomesteadBlock = big.NewInt(0) ruleSet := RuleSet{
HomesteadBlock: new(big.Int),
}
if os.Getenv("TEST_VM_COMPLEX") == "" { if os.Getenv("TEST_VM_COMPLEX") == "" {
t.Skip() t.Skip()
} }
fn := filepath.Join(stateTestDir, "Homestead", "stQuadraticComplexityTest.json") 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) t.Error(err)
} }
} }
func TestHomesteadWallet(t *testing.T) { func TestHomesteadWallet(t *testing.T) {
params.HomesteadBlock = big.NewInt(0) ruleSet := RuleSet{
HomesteadBlock: new(big.Int),
}
fn := filepath.Join(stateTestDir, "Homestead", "stWalletTest.json") 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) t.Error(err)
} }
} }
func TestHomesteadDelegateCodes(t *testing.T) { func TestHomesteadDelegateCodes(t *testing.T) {
params.HomesteadBlock = big.NewInt(0) ruleSet := RuleSet{
HomesteadBlock: new(big.Int),
}
fn := filepath.Join(stateTestDir, "Homestead", "stCallDelegateCodes.json") 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) t.Error(err)
} }
} }
func TestHomesteadDelegateCodesCallCode(t *testing.T) { func TestHomesteadDelegateCodesCallCode(t *testing.T) {
params.HomesteadBlock = big.NewInt(0) ruleSet := RuleSet{
HomesteadBlock: new(big.Int),
}
fn := filepath.Join(stateTestDir, "Homestead", "stCallDelegateCodesCallCode.json") 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) t.Error(err)
} }
} }

View File

@ -34,26 +34,26 @@ import (
"github.com/ethereum/go-ethereum/logger/glog" "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) tests := make(map[string]VmTest)
if err := readJson(r, &tests); err != nil { if err := readJson(r, &tests); err != nil {
return err return err
} }
if err := runStateTests(tests, skipTests); err != nil { if err := runStateTests(ruleSet, tests, skipTests); err != nil {
return err return err
} }
return nil return nil
} }
func RunStateTest(p string, skipTests []string) error { func RunStateTest(ruleSet RuleSet, p string, skipTests []string) error {
tests := make(map[string]VmTest) tests := make(map[string]VmTest)
if err := readJsonFile(p, &tests); err != nil { if err := readJsonFile(p, &tests); err != nil {
return err return err
} }
if err := runStateTests(tests, skipTests); err != nil { if err := runStateTests(ruleSet, tests, skipTests); err != nil {
return err 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) tests := make(map[string]VmTest)
if err := readJsonFile(p, &tests); err != nil { if err := readJsonFile(p, &tests); err != nil {
return err 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) 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... // XXX Yeah, yeah...
env := make(map[string]string) env := make(map[string]string)
env["currentCoinbase"] = test.Env.CurrentCoinbase env["currentCoinbase"] = test.Env.CurrentCoinbase
@ -91,16 +86,13 @@ func BenchStateTest(p string, conf bconf, b *testing.B) error {
b.ResetTimer() b.ResetTimer()
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
benchStateTest(test, env, b) benchStateTest(ruleSet, test, env, b)
} }
vm.EnableJit = pJit
vm.ForceJit = pForceJit
return nil 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() b.StopTimer()
db, _ := ethdb.NewMemDatabase() db, _ := ethdb.NewMemDatabase()
statedb, _ := state.New(common.Hash{}, db) statedb, _ := state.New(common.Hash{}, db)
@ -113,10 +105,10 @@ func benchStateTest(test VmTest, env map[string]string, b *testing.B) {
} }
b.StartTimer() 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)) skipTest := make(map[string]bool, len(skipTests))
for _, name := range skipTests { for _, name := range skipTests {
skipTest[name] = true skipTest[name] = true
@ -129,7 +121,7 @@ func runStateTests(tests map[string]VmTest, skipTests []string) error {
} }
//fmt.Println("StateTest:", name) //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()) 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() db, _ := ethdb.NewMemDatabase()
statedb, _ := state.New(common.Hash{}, db) statedb, _ := state.New(common.Hash{}, db)
for addr, account := range test.Pre { for addr, account := range test.Pre {
@ -171,7 +163,7 @@ func runStateTest(test VmTest) error {
logs vm.Logs logs vm.Logs
) )
ret, logs, _, _ = RunState(statedb, env, test.Transaction) ret, logs, _, _ = RunState(ruleSet, statedb, env, test.Transaction)
// Compare expected and actual return // Compare expected and actual return
rexp := common.FromHex(test.Out) rexp := common.FromHex(test.Out)
@ -219,7 +211,7 @@ func runStateTest(test VmTest) error {
return nil 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 ( var (
data = common.FromHex(tx["data"]) data = common.FromHex(tx["data"])
gas = common.Big(tx["gasLimit"]) 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"]) key, _ := hex.DecodeString(tx["secretKey"])
addr := crypto.PubkeyToAddress(crypto.ToECDSA(key).PublicKey) addr := crypto.PubkeyToAddress(crypto.ToECDSA(key).PublicKey)
message := NewMessage(addr, to, data, value, gas, price, nonce) message := NewMessage(addr, to, data, value, gas, price, nonce)
vmenv := NewEnvFromMap(statedb, env, tx) vmenv := NewEnvFromMap(ruleSet, statedb, env, tx)
vmenv.origin = addr vmenv.origin = addr
ret, _, err := core.ApplyMessage(vmenv, message, gaspool) ret, _, err := core.ApplyMessage(vmenv, message, gaspool)
if core.IsNonceErr(err) || core.IsInvalidTxErr(err) || core.IsGasLimitErr(err) { if core.IsNonceErr(err) || core.IsInvalidTxErr(err) || core.IsGasLimitErr(err) {

View File

@ -21,10 +21,10 @@ import (
"errors" "errors"
"fmt" "fmt"
"io" "io"
"math/big"
"runtime" "runtime"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/params" "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 { func runTransactionTests(tests map[string]TransactionTest, skipTests []string) error {
params.HomesteadBlock = big.NewInt(900000)
skipTest := make(map[string]bool, len(skipTests)) skipTest := make(map[string]bool, len(skipTests))
for _, name := range skipTests { for _, name := range skipTests {
skipTest[name] = true skipTest[name] = true
@ -166,7 +164,8 @@ func verifyTxFields(txTest TransactionTest, decodedTx *types.Transaction) (err e
decodedSender common.Address 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() decodedSender, err = decodedTx.From()
} else { } else {
decodedSender, err = decodedTx.FromFrontier() decodedSender, err = decodedTx.FromFrontier()

View File

@ -20,6 +20,7 @@ import (
"bytes" "bytes"
"fmt" "fmt"
"math/big" "math/big"
"os"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
@ -31,8 +32,17 @@ import (
"github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/logger/glog"
) )
var (
ForceJit bool
EnableJit bool
)
func init() { func init() {
glog.SetV(0) glog.SetV(0)
if os.Getenv("JITVM") == "true" {
ForceJit = true
EnableJit = true
}
} }
func checkLogs(tlog []Log, logs vm.Logs) error { func checkLogs(tlog []Log, logs vm.Logs) error {
@ -129,7 +139,16 @@ type VmTest struct {
PostStateRoot string 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 { type Env struct {
ruleSet RuleSet
depth int depth int
state *state.StateDB state *state.StateDB
skipTransfer bool skipTransfer bool
@ -152,9 +171,10 @@ type Env struct {
evm *vm.EVM evm *vm.EVM
} }
func NewEnv(state *state.StateDB) *Env { func NewEnv(ruleSet RuleSet, state *state.StateDB) *Env {
env := &Env{ env := &Env{
state: state, ruleSet: ruleSet,
state: state,
} }
return env return env
} }
@ -167,8 +187,8 @@ func (self *Env) AddStructLog(log vm.StructLog) {
self.logs = append(self.logs, log) self.logs = append(self.logs, log)
} }
func NewEnvFromMap(state *state.StateDB, envValues map[string]string, exeValues map[string]string) *Env { func NewEnvFromMap(ruleSet RuleSet, state *state.StateDB, envValues map[string]string, exeValues map[string]string) *Env {
env := NewEnv(state) env := NewEnv(ruleSet, state)
env.origin = common.HexToAddress(exeValues["caller"]) env.origin = common.HexToAddress(exeValues["caller"])
env.parent = common.HexToHash(envValues["previousHash"]) 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.gasLimit = common.Big(envValues["currentGasLimit"])
env.Gas = new(big.Int) env.Gas = new(big.Int)
env.evm = vm.New(env, nil) env.evm = vm.New(env, vm.Config{
EnableJit: EnableJit,
ForceJit: ForceJit,
})
return env return env
} }
func (self *Env) RuleSet() vm.RuleSet { return self.ruleSet }
func (self *Env) Vm() vm.Vm { return self.evm } func (self *Env) Vm() vm.Vm { return self.evm }
func (self *Env) Origin() common.Address { return self.origin } func (self *Env) Origin() common.Address { return self.origin }
func (self *Env) BlockNumber() *big.Int { return self.number } func (self *Env) BlockNumber() *big.Int { return self.number }

View File

@ -29,6 +29,7 @@ import (
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/params"
) )
func RunVmTestWithReader(r io.Reader, skipTests []string) error { 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) 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 := make(map[string]string)
env["currentCoinbase"] = test.Env.CurrentCoinbase env["currentCoinbase"] = test.Env.CurrentCoinbase
env["currentDifficulty"] = test.Env.CurrentDifficulty env["currentDifficulty"] = test.Env.CurrentDifficulty
@ -99,9 +95,6 @@ func BenchVmTest(p string, conf bconf, b *testing.B) error {
benchVmTest(test, env, b) benchVmTest(test, env, b)
} }
vm.EnableJit = pJit
vm.ForceJit = pForceJit
return nil return nil
} }
@ -248,7 +241,7 @@ func RunVm(state *state.StateDB, env, exec map[string]string) ([]byte, vm.Logs,
caller := state.GetOrNewStateObject(from) caller := state.GetOrNewStateObject(from)
vmenv := NewEnvFromMap(state, env, exec) vmenv := NewEnvFromMap(RuleSet{params.MainNetHomesteadBlock}, state, env, exec)
vmenv.vmTest = true vmenv.vmTest = true
vmenv.skipTransfer = true vmenv.skipTransfer = true
vmenv.initial = true vmenv.initial = true