aboutsummaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
authorJeffrey Wilcke <jeffrey@ethereum.org>2016-03-02 06:32:43 +0800
committerJeffrey Wilcke <geffobscura@gmail.com>2016-04-01 07:01:10 +0800
commitf0cbebb19f3137ee3ba0e66dadd1b5b9dbf98b1c (patch)
tree02e31a0e31040980e30e3a835ff9eba73e419439 /core
parent10d3466c934bd425a8c941270749a652a588527d (diff)
downloadgo-tangerine-f0cbebb19f3137ee3ba0e66dadd1b5b9dbf98b1c.tar.gz
go-tangerine-f0cbebb19f3137ee3ba0e66dadd1b5b9dbf98b1c.tar.zst
go-tangerine-f0cbebb19f3137ee3ba0e66dadd1b5b9dbf98b1c.zip
core: added basic chain configuration
Added chain configuration options and write out during genesis database insertion. If no "config" was found, nothing is written to the database. Configurations are written on a per genesis base. This means that any chain (which is identified by it's genesis hash) can have their own chain settings.
Diffstat (limited to 'core')
-rw-r--r--core/bench_test.go2
-rw-r--r--core/block_validator.go32
-rw-r--r--core/block_validator_test.go12
-rw-r--r--core/blockchain.go25
-rw-r--r--core/blockchain_test.go35
-rw-r--r--core/chain_makers.go27
-rw-r--r--core/chain_makers_test.go2
-rw-r--r--core/config.go46
-rw-r--r--core/database_util.go34
-rw-r--r--core/execution.go2
-rw-r--r--core/genesis.go23
-rw-r--r--core/headerchain.go19
-rw-r--r--core/state_processor.go23
-rw-r--r--core/state_transition.go4
-rw-r--r--core/tx_pool.go7
-rw-r--r--core/tx_pool_test.go2
-rw-r--r--core/types.go2
-rw-r--r--core/vm/environment.go12
-rw-r--r--core/vm/instructions.go2
-rw-r--r--core/vm/jit.go23
-rw-r--r--core/vm/jit_test.go15
-rw-r--r--core/vm/jump_table.go10
-rw-r--r--core/vm/jump_table_test.go8
-rw-r--r--core/vm/logger.go2
-rw-r--r--core/vm/logger_test.go10
-rw-r--r--core/vm/runtime/env.go9
-rw-r--r--core/vm/runtime/runtime.go11
-rw-r--r--core/vm/util_test.go9
-rw-r--r--core/vm/vm.go15
-rw-r--r--core/vm/vm_jit_fake.go2
-rw-r--r--core/vm_env.go29
31 files changed, 295 insertions, 159 deletions
diff --git a/core/bench_test.go b/core/bench_test.go
index 0ff847ed5..ac5b57bc8 100644
--- a/core/bench_test.go
+++ b/core/bench_test.go
@@ -168,7 +168,7 @@ func benchInsertChain(b *testing.B, disk bool, gen func(int, *BlockGen)) {
// Time the insertion of the new chain.
// State and blocks are stored in the same DB.
evmux := new(event.TypeMux)
- chainman, _ := NewBlockChain(db, FakePow{}, evmux)
+ chainman, _ := NewBlockChain(db, &ChainConfig{HomesteadBlock: new(big.Int)}, FakePow{}, evmux)
defer chainman.Stop()
b.ReportAllocs()
b.ResetTimer()
diff --git a/core/block_validator.go b/core/block_validator.go
index 4d710ae3f..747e61ccc 100644
--- a/core/block_validator.go
+++ b/core/block_validator.go
@@ -41,15 +41,17 @@ var (
//
// BlockValidator implements Validator.
type BlockValidator struct {
- bc *BlockChain // Canonical block chain
- Pow pow.PoW // Proof of work used for validating
+ config *ChainConfig // Chain configuration options
+ bc *BlockChain // Canonical block chain
+ Pow pow.PoW // Proof of work used for validating
}
// NewBlockValidator returns a new block validator which is safe for re-use
-func NewBlockValidator(blockchain *BlockChain, pow pow.PoW) *BlockValidator {
+func NewBlockValidator(config *ChainConfig, blockchain *BlockChain, pow pow.PoW) *BlockValidator {
validator := &BlockValidator{
- Pow: pow,
- bc: blockchain,
+ config: config,
+ Pow: pow,
+ bc: blockchain,
}
return validator
}
@@ -80,7 +82,7 @@ func (v *BlockValidator) ValidateBlock(block *types.Block) error {
header := block.Header()
// validate the block header
- if err := ValidateHeader(v.Pow, header, parent.Header(), false, false); err != nil {
+ if err := ValidateHeader(v.config, v.Pow, header, parent.Header(), false, false); err != nil {
return err
}
// verify the uncles are correctly rewarded
@@ -175,7 +177,7 @@ func (v *BlockValidator) VerifyUncles(block, parent *types.Block) error {
return UncleError("uncle[%d](%x)'s parent is not ancestor (%x)", i, hash[:4], uncle.ParentHash[0:4])
}
- if err := ValidateHeader(v.Pow, uncle, ancestors[uncle.ParentHash].Header(), true, true); err != nil {
+ if err := ValidateHeader(v.config, v.Pow, uncle, ancestors[uncle.ParentHash].Header(), true, true); err != nil {
return ValidationError(fmt.Sprintf("uncle[%d](%x) header invalid: %v", i, hash[:4], err))
}
}
@@ -195,13 +197,13 @@ func (v *BlockValidator) ValidateHeader(header, parent *types.Header, checkPow b
if v.bc.HasHeader(header.Hash()) {
return nil
}
- return ValidateHeader(v.Pow, header, parent, checkPow, false)
+ return ValidateHeader(v.config, v.Pow, header, parent, checkPow, false)
}
// Validates a header. Returns an error if the header is invalid.
//
// See YP section 4.3.4. "Block Header Validity"
-func ValidateHeader(pow pow.PoW, header *types.Header, parent *types.Header, checkPow, uncle bool) error {
+func ValidateHeader(config *ChainConfig, pow pow.PoW, header *types.Header, parent *types.Header, checkPow, uncle bool) error {
if big.NewInt(int64(len(header.Extra))).Cmp(params.MaximumExtraDataSize) == 1 {
return fmt.Errorf("Header extra data too long (%d)", len(header.Extra))
}
@@ -219,7 +221,7 @@ func ValidateHeader(pow pow.PoW, header *types.Header, parent *types.Header, che
return BlockEqualTSErr
}
- expd := CalcDifficulty(header.Time.Uint64(), parent.Time.Uint64(), parent.Number, parent.Difficulty)
+ expd := CalcDifficulty(config, header.Time.Uint64(), parent.Time.Uint64(), parent.Number, parent.Difficulty)
if expd.Cmp(header.Difficulty) != 0 {
return fmt.Errorf("Difficulty check failed for header %v, %v", header.Difficulty, expd)
}
@@ -251,8 +253,8 @@ func ValidateHeader(pow pow.PoW, header *types.Header, parent *types.Header, che
// CalcDifficulty is the difficulty adjustment algorithm. It returns
// the difficulty that a new block should have when created at time
// given the parent block's time and difficulty.
-func CalcDifficulty(time, parentTime uint64, parentNumber, parentDiff *big.Int) *big.Int {
- if params.IsHomestead(new(big.Int).Add(parentNumber, common.Big1)) {
+func CalcDifficulty(config *ChainConfig, time, parentTime uint64, parentNumber, parentDiff *big.Int) *big.Int {
+ if config.IsHomestead(new(big.Int).Add(parentNumber, common.Big1)) {
return calcDifficultyHomestead(time, parentTime, parentNumber, parentDiff)
} else {
return calcDifficultyFrontier(time, parentTime, parentNumber, parentDiff)
@@ -363,11 +365,11 @@ func CalcGasLimit(parent *types.Block) *big.Int {
gl = gl.Add(gl, contrib)
gl.Set(common.BigMax(gl, params.MinGasLimit))
- // however, if we're now below the target (GenesisGasLimit) we increase the
+ // however, if we're now below the target (TargetGasLimit) we increase the
// limit as much as we can (parentGasLimit / 1024 -1)
- if gl.Cmp(params.GenesisGasLimit) < 0 {
+ if gl.Cmp(params.TargetGasLimit) < 0 {
gl.Add(parent.GasLimit(), decay)
- gl.Set(common.BigMin(gl, params.GenesisGasLimit))
+ gl.Set(common.BigMin(gl, params.TargetGasLimit))
}
return gl
}
diff --git a/core/block_validator_test.go b/core/block_validator_test.go
index 2c4a97b45..aa29717b1 100644
--- a/core/block_validator_test.go
+++ b/core/block_validator_test.go
@@ -27,15 +27,20 @@ import (
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event"
+ "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/pow/ezp"
)
+func testChainConfig() *ChainConfig {
+ return &ChainConfig{HomesteadBlock: params.MainNetHomesteadBlock}
+}
+
func proc() (Validator, *BlockChain) {
db, _ := ethdb.NewMemDatabase()
var mux event.TypeMux
WriteTestNetGenesisBlock(db)
- blockchain, err := NewBlockChain(db, thePow(), &mux)
+ blockchain, err := NewBlockChain(db, testChainConfig(), thePow(), &mux)
if err != nil {
fmt.Println(err)
}
@@ -49,13 +54,14 @@ func TestNumber(t *testing.T) {
statedb, _ := state.New(chain.Genesis().Root(), chain.chainDb)
header := makeHeader(chain.Genesis(), statedb)
header.Number = big.NewInt(3)
- err := ValidateHeader(pow, header, chain.Genesis().Header(), false, false)
+ cfg := testChainConfig()
+ err := ValidateHeader(cfg, pow, header, chain.Genesis().Header(), false, false)
if err != BlockNumberErr {
t.Errorf("expected block number error, got %q", err)
}
header = makeHeader(chain.Genesis(), statedb)
- err = ValidateHeader(pow, header, chain.Genesis().Header(), false, false)
+ err = ValidateHeader(cfg, pow, header, chain.Genesis().Header(), false, false)
if err == BlockNumberErr {
t.Errorf("didn't expect block number error")
}
diff --git a/core/blockchain.go b/core/blockchain.go
index 2c3c2bb5c..177a3bbce 100644
--- a/core/blockchain.go
+++ b/core/blockchain.go
@@ -80,11 +80,12 @@ const (
// included in the canonical one where as GetBlockByNumber always represents the
// canonical chain.
type BlockChain struct {
+ config *ChainConfig // chain & network configuration
+
hc *HeaderChain
chainDb ethdb.Database
eventMux *event.TypeMux
genesisBlock *types.Block
- vmConfig *vm.Config
mu sync.RWMutex // global mutex for locking chain operations
chainmu sync.RWMutex // blockchain insertion lock
@@ -113,13 +114,14 @@ type BlockChain struct {
// NewBlockChain returns a fully initialised block chain using information
// available in the database. It initialiser the default Ethereum Validator and
// Processor.
-func NewBlockChain(chainDb ethdb.Database, pow pow.PoW, mux *event.TypeMux) (*BlockChain, error) {
+func NewBlockChain(chainDb ethdb.Database, config *ChainConfig, pow pow.PoW, mux *event.TypeMux) (*BlockChain, error) {
bodyCache, _ := lru.New(bodyCacheLimit)
bodyRLPCache, _ := lru.New(bodyCacheLimit)
blockCache, _ := lru.New(blockCacheLimit)
futureBlocks, _ := lru.New(maxFutureBlocks)
bc := &BlockChain{
+ config: config,
chainDb: chainDb,
eventMux: mux,
quit: make(chan struct{}),
@@ -129,24 +131,21 @@ func NewBlockChain(chainDb ethdb.Database, pow pow.PoW, mux *event.TypeMux) (*Bl
futureBlocks: futureBlocks,
pow: pow,
}
- bc.SetValidator(NewBlockValidator(bc, pow))
- bc.SetProcessor(NewStateProcessor(bc))
+ bc.SetValidator(NewBlockValidator(config, bc, pow))
+ bc.SetProcessor(NewStateProcessor(config, bc))
gv := func() HeaderValidator { return bc.Validator() }
var err error
- bc.hc, err = NewHeaderChain(chainDb, gv, bc.getProcInterrupt)
+ bc.hc, err = NewHeaderChain(chainDb, config, gv, bc.getProcInterrupt)
if err != nil {
return nil, err
}
bc.genesisBlock = bc.GetBlockByNumber(0)
if bc.genesisBlock == nil {
- bc.genesisBlock, err = WriteDefaultGenesisBlock(chainDb)
- if err != nil {
- return nil, err
- }
- glog.V(logger.Info).Infoln("WARNING: Wrote default ethereum genesis block")
+ return nil, ErrNoGenesis
}
+
if err := bc.loadLastState(); err != nil {
return nil, err
}
@@ -163,10 +162,6 @@ func NewBlockChain(chainDb ethdb.Database, pow pow.PoW, mux *event.TypeMux) (*Bl
return bc, nil
}
-func (self *BlockChain) SetConfig(vmConfig *vm.Config) {
- self.vmConfig = vmConfig
-}
-
func (self *BlockChain) getProcInterrupt() bool {
return atomic.LoadInt32(&self.procInterrupt) == 1
}
@@ -896,7 +891,7 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) {
return i, err
}
// Process block using the parent state as reference point.
- receipts, logs, usedGas, err := self.processor.Process(block, statedb, self.vmConfig)
+ receipts, logs, usedGas, err := self.processor.Process(block, statedb, self.config.VmConfig)
if err != nil {
reportBlock(block, err)
return i, err
diff --git a/core/blockchain_test.go b/core/blockchain_test.go
index d7cd24fa8..9e59b2ff6 100644
--- a/core/blockchain_test.go
+++ b/core/blockchain_test.go
@@ -53,7 +53,7 @@ func thePow() pow.PoW {
func theBlockChain(db ethdb.Database, t *testing.T) *BlockChain {
var eventMux event.TypeMux
WriteTestNetGenesisBlock(db)
- blockchain, err := NewBlockChain(db, thePow(), &eventMux)
+ blockchain, err := NewBlockChain(db, testChainConfig(), thePow(), &eventMux)
if err != nil {
t.Error("failed creating blockchain:", err)
t.FailNow()
@@ -141,7 +141,7 @@ func testBlockChainImport(chain types.Blocks, blockchain *BlockChain) error {
if err != nil {
return err
}
- receipts, _, usedGas, err := blockchain.Processor().Process(block, statedb, nil)
+ receipts, _, usedGas, err := blockchain.Processor().Process(block, statedb, vm.Config{})
if err != nil {
reportBlock(block, err)
return err
@@ -435,7 +435,7 @@ func (bproc) ValidateHeader(*types.Header, *types.Header, bool) error { return n
func (bproc) ValidateState(block, parent *types.Block, state *state.StateDB, receipts types.Receipts, usedGas *big.Int) error {
return nil
}
-func (bproc) Process(block *types.Block, statedb *state.StateDB, cfg *vm.Config) (types.Receipts, vm.Logs, *big.Int, error) {
+func (bproc) Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (types.Receipts, vm.Logs, *big.Int, error) {
return nil, nil, nil, nil
}
@@ -473,13 +473,14 @@ func makeBlockChainWithDiff(genesis *types.Block, d []int, seed byte) []*types.B
func chm(genesis *types.Block, db ethdb.Database) *BlockChain {
var eventMux event.TypeMux
bc := &BlockChain{
- chainDb: db,
+ chainDb: db,
genesisBlock: genesis,
- eventMux: &eventMux,
- pow: FakePow{},
+ eventMux: &eventMux,
+ pow: FakePow{},
+ config: testChainConfig(),
}
valFn := func() HeaderValidator { return bc.Validator() }
- bc.hc, _ = NewHeaderChain(db, valFn, bc.getProcInterrupt)
+ bc.hc, _ = NewHeaderChain(db, testChainConfig(), valFn, bc.getProcInterrupt)
bc.bodyCache, _ = lru.New(100)
bc.bodyRLPCache, _ = lru.New(100)
bc.blockCache, _ = lru.New(100)
@@ -613,7 +614,7 @@ func testReorgBadHashes(t *testing.T, full bool) {
defer func() { delete(BadHashes, headers[3].Hash()) }()
}
// Create a new chain manager and check it rolled back the state
- ncm, err := NewBlockChain(db, FakePow{}, new(event.TypeMux))
+ ncm, err := NewBlockChain(db, testChainConfig(), FakePow{}, new(event.TypeMux))
if err != nil {
t.Fatalf("failed to create new chain manager: %v", err)
}
@@ -667,7 +668,7 @@ func testInsertNonceError(t *testing.T, full bool) {
failHash = headers[failAt].Hash()
blockchain.pow = failPow{failNum}
- blockchain.validator = NewBlockValidator(blockchain, failPow{failNum})
+ blockchain.validator = NewBlockValidator(testChainConfig(), blockchain, failPow{failNum})
failRes, err = blockchain.InsertHeaderChain(headers, 1)
}
@@ -733,7 +734,7 @@ func TestFastVsFullChains(t *testing.T) {
archiveDb, _ := ethdb.NewMemDatabase()
WriteGenesisBlockForTesting(archiveDb, GenesisAccount{address, funds})
- archive, _ := NewBlockChain(archiveDb, FakePow{}, new(event.TypeMux))
+ archive, _ := NewBlockChain(archiveDb, testChainConfig(), FakePow{}, new(event.TypeMux))
if n, err := archive.InsertChain(blocks); err != nil {
t.Fatalf("failed to process block %d: %v", n, err)
@@ -741,7 +742,7 @@ func TestFastVsFullChains(t *testing.T) {
// Fast import the chain as a non-archive node to test
fastDb, _ := ethdb.NewMemDatabase()
WriteGenesisBlockForTesting(fastDb, GenesisAccount{address, funds})
- fast, _ := NewBlockChain(fastDb, FakePow{}, new(event.TypeMux))
+ fast, _ := NewBlockChain(fastDb, testChainConfig(), FakePow{}, new(event.TypeMux))
headers := make([]*types.Header, len(blocks))
for i, block := range blocks {
@@ -817,7 +818,7 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) {
archiveDb, _ := ethdb.NewMemDatabase()
WriteGenesisBlockForTesting(archiveDb, GenesisAccount{address, funds})
- archive, _ := NewBlockChain(archiveDb, FakePow{}, new(event.TypeMux))
+ archive, _ := NewBlockChain(archiveDb, testChainConfig(), FakePow{}, new(event.TypeMux))
if n, err := archive.InsertChain(blocks); err != nil {
t.Fatalf("failed to process block %d: %v", n, err)
@@ -829,7 +830,7 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) {
// Import the chain as a non-archive node and ensure all pointers are updated
fastDb, _ := ethdb.NewMemDatabase()
WriteGenesisBlockForTesting(fastDb, GenesisAccount{address, funds})
- fast, _ := NewBlockChain(fastDb, FakePow{}, new(event.TypeMux))
+ fast, _ := NewBlockChain(fastDb, testChainConfig(), FakePow{}, new(event.TypeMux))
headers := make([]*types.Header, len(blocks))
for i, block := range blocks {
@@ -848,7 +849,7 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) {
// Import the chain as a light node and ensure all pointers are updated
lightDb, _ := ethdb.NewMemDatabase()
WriteGenesisBlockForTesting(lightDb, GenesisAccount{address, funds})
- light, _ := NewBlockChain(lightDb, FakePow{}, new(event.TypeMux))
+ light, _ := NewBlockChain(lightDb, testChainConfig(), FakePow{}, new(event.TypeMux))
if n, err := light.InsertHeaderChain(headers, 1); err != nil {
t.Fatalf("failed to insert header %d: %v", n, err)
@@ -913,7 +914,7 @@ func TestChainTxReorgs(t *testing.T) {
})
// Import the chain. This runs all block validation rules.
evmux := &event.TypeMux{}
- blockchain, _ := NewBlockChain(db, FakePow{}, evmux)
+ blockchain, _ := NewBlockChain(db, testChainConfig(), FakePow{}, evmux)
if i, err := blockchain.InsertChain(chain); err != nil {
t.Fatalf("failed to insert original chain[%d]: %v", i, err)
}
@@ -986,7 +987,7 @@ func TestLogReorgs(t *testing.T) {
)
evmux := &event.TypeMux{}
- blockchain, _ := NewBlockChain(db, FakePow{}, evmux)
+ blockchain, _ := NewBlockChain(db, testChainConfig(), FakePow{}, evmux)
subs := evmux.Subscribe(RemovedLogsEvent{})
chain, _ := GenerateChain(genesis, db, 2, func(i int, gen *BlockGen) {
@@ -1022,7 +1023,7 @@ func TestReorgSideEvent(t *testing.T) {
)
evmux := &event.TypeMux{}
- blockchain, _ := NewBlockChain(db, FakePow{}, evmux)
+ blockchain, _ := NewBlockChain(db, testChainConfig(), FakePow{}, evmux)
chain, _ := GenerateChain(genesis, db, 3, func(i int, gen *BlockGen) {
if i == 2 {
diff --git a/core/chain_makers.go b/core/chain_makers.go
index 7ae3c98b0..c740f9c37 100644
--- a/core/chain_makers.go
+++ b/core/chain_makers.go
@@ -23,11 +23,30 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event"
+ "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/pow"
)
+/*
+ * TODO: move this to another package.
+ */
+
+// MakeChainConfig returns a new ChainConfig with the ethereum default chain settings.
+func MakeChainConfig(testnet bool) *ChainConfig {
+ homesteadBlock := params.MainNetHomesteadBlock
+ // set a different default homestead block for the testnet
+ if testnet {
+ homesteadBlock = params.TestNetHomesteadBlock
+ }
+
+ return &ChainConfig{
+ HomesteadBlock: homesteadBlock,
+ }
+}
+
// FakePow is a non-validating proof of work implementation.
// It returns true from Verify for any block.
type FakePow struct{}
@@ -91,7 +110,7 @@ func (b *BlockGen) AddTx(tx *types.Transaction) {
b.SetCoinbase(common.Address{})
}
b.statedb.StartRecord(tx.Hash(), common.Hash{}, len(b.txs))
- receipt, _, _, err := ApplyTransaction(nil, b.gasPool, b.statedb, b.header, tx, b.header.GasUsed, nil)
+ receipt, _, _, err := ApplyTransaction(MakeChainConfig(true), nil, b.gasPool, b.statedb, b.header, tx, b.header.GasUsed, vm.Config{})
if err != nil {
panic(err)
}
@@ -148,7 +167,7 @@ func (b *BlockGen) OffsetTime(seconds int64) {
if b.header.Time.Cmp(b.parent.Header().Time) <= 0 {
panic("block time out of range")
}
- b.header.Difficulty = CalcDifficulty(b.header.Time.Uint64(), b.parent.Time().Uint64(), b.parent.Number(), b.parent.Difficulty())
+ b.header.Difficulty = CalcDifficulty(MakeChainConfig(true), b.header.Time.Uint64(), b.parent.Time().Uint64(), b.parent.Number(), b.parent.Difficulty())
}
// GenerateChain creates a chain of n blocks. The first block's
@@ -203,7 +222,7 @@ func makeHeader(parent *types.Block, state *state.StateDB) *types.Header {
Root: state.IntermediateRoot(),
ParentHash: parent.Hash(),
Coinbase: parent.Coinbase(),
- Difficulty: CalcDifficulty(time.Uint64(), new(big.Int).Sub(time, big.NewInt(10)).Uint64(), parent.Number(), parent.Difficulty()),
+ Difficulty: CalcDifficulty(MakeChainConfig(true), time.Uint64(), new(big.Int).Sub(time, big.NewInt(10)).Uint64(), parent.Number(), parent.Difficulty()),
GasLimit: CalcGasLimit(parent),
GasUsed: new(big.Int),
Number: new(big.Int).Add(parent.Number(), common.Big1),
@@ -222,7 +241,7 @@ func newCanonical(n int, full bool) (ethdb.Database, *BlockChain, error) {
// Initialize a fresh chain with only a genesis block
genesis, _ := WriteTestNetGenesisBlock(db)
- blockchain, _ := NewBlockChain(db, FakePow{}, evmux)
+ blockchain, _ := NewBlockChain(db, MakeChainConfig(false), FakePow{}, evmux)
// Create and inject the requested chain
if n == 0 {
return db, blockchain, nil
diff --git a/core/chain_makers_test.go b/core/chain_makers_test.go
index b9c1d89b7..86949246b 100644
--- a/core/chain_makers_test.go
+++ b/core/chain_makers_test.go
@@ -77,7 +77,7 @@ func ExampleGenerateChain() {
// Import the chain. This runs all block validation rules.
evmux := &event.TypeMux{}
- blockchain, _ := NewBlockChain(db, FakePow{}, evmux)
+ blockchain, _ := NewBlockChain(db, MakeChainConfig(true), FakePow{}, evmux)
if i, err := blockchain.InsertChain(chain); err != nil {
fmt.Printf("insert error (block %d): %v\n", i, err)
return
diff --git a/core/config.go b/core/config.go
new file mode 100644
index 000000000..81ca76aa3
--- /dev/null
+++ b/core/config.go
@@ -0,0 +1,46 @@
+// Copyright 2016 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+package core
+
+import (
+ "errors"
+ "math/big"
+
+ "github.com/ethereum/go-ethereum/core/vm"
+)
+
+var ChainConfigNotFoundErr = errors.New("ChainConfig not found") // general config not found error
+
+// ChainConfig is the core config which determines the blockchain settings.
+//
+// ChainConfig is stored in the database on a per block basis. This means
+// that any network, identified by its genesis block, can have its own
+// set of configuration options.
+type ChainConfig struct {
+ HomesteadBlock *big.Int // homestead switch block
+
+ VmConfig vm.Config `json:"-"`
+}
+
+// IsHomestead returns whether num is either equal to the homestead block or greater.
+func (c *ChainConfig) IsHomestead(num *big.Int) bool {
+ if num == nil {
+ return false
+ }
+
+ return num.Cmp(c.HomesteadBlock) >= 0
+}
diff --git a/core/database_util.go b/core/database_util.go
index fd2b4c312..e1e8136d1 100644
--- a/core/database_util.go
+++ b/core/database_util.go
@@ -19,6 +19,7 @@ package core
import (
"bytes"
"encoding/binary"
+ "encoding/json"
"fmt"
"math/big"
@@ -50,6 +51,8 @@ var (
MIPMapLevels = []uint64{1000000, 500000, 100000, 50000, 1000}
blockHashPrefix = []byte("block-hash-") // [deprecated by the header/block split, remove eventually]
+
+ configPrefix = []byte("ethereum-config-") // config prefix for the db
)
// GetCanonicalHash retrieves a hash assigned to a canonical block number.
@@ -527,3 +530,34 @@ func WriteBlockChainVersion(db ethdb.Database, vsn int) {
enc, _ := rlp.EncodeToBytes(uint(vsn))
db.Put([]byte("BlockchainVersion"), enc)
}
+
+// WriteChainConfig writes the chain config settings to the database.
+func WriteChainConfig(db ethdb.Database, hash common.Hash, cfg *ChainConfig) error {
+ // short circuit and ignore if nil config. GetChainConfig
+ // will return a default.
+ if cfg == nil {
+ return nil
+ }
+
+ jsonChainConfig, err := json.Marshal(cfg)
+ if err != nil {
+ return err
+ }
+
+ return db.Put(append(configPrefix, hash[:]...), jsonChainConfig)
+}
+
+// GetChainConfig will fetch the network settings based on the given hash.
+func GetChainConfig(db ethdb.Database, hash common.Hash) (*ChainConfig, error) {
+ jsonChainConfig, _ := db.Get(append(configPrefix, hash[:]...))
+ if len(jsonChainConfig) == 0 {
+ return nil, ChainConfigNotFoundErr
+ }
+
+ var config ChainConfig
+ if err := json.Unmarshal(jsonChainConfig, &config); err != nil {
+ return nil, err
+ }
+
+ return &config, nil
+}
diff --git a/core/execution.go b/core/execution.go
index d90dceafc..82143443c 100644
--- a/core/execution.go
+++ b/core/execution.go
@@ -126,7 +126,7 @@ func exec(env vm.Environment, caller vm.ContractRef, address, codeAddr *common.A
// When an error was returned by the EVM or when setting the creation code
// above we revert to the snapshot and consume any gas remaining. Additionally
// when we're in homestead this also counts for code storage gas errors.
- if err != nil && (params.IsHomestead(env.BlockNumber()) || err != vm.CodeStoreOutOfGasError) {
+ if err != nil && (env.RuleSet().IsHomestead(env.BlockNumber()) || err != vm.CodeStoreOutOfGasError) {
contract.UseGas(contract.Gas)
env.SetSnapshot(snapshotPreTransfer)
diff --git a/core/genesis.go b/core/genesis.go
index d8c6e9cea..5c69b216c 100644
--- a/core/genesis.go
+++ b/core/genesis.go
@@ -43,15 +43,16 @@ func WriteGenesisBlock(chainDb ethdb.Database, reader io.Reader) (*types.Block,
}
var genesis struct {
- Nonce string
- Timestamp string
- ParentHash string
- ExtraData string
- GasLimit string
- Difficulty string
- Mixhash string
- Coinbase string
- Alloc map[string]struct {
+ ChainConfig *ChainConfig
+ Nonce string
+ Timestamp string
+ ParentHash string
+ ExtraData string
+ GasLimit string
+ Difficulty string
+ Mixhash string
+ Coinbase string
+ Alloc map[string]struct {
Code string
Storage map[string]string
Balance string
@@ -114,6 +115,10 @@ func WriteGenesisBlock(chainDb ethdb.Database, reader io.Reader) (*types.Block,
if err := WriteHeadBlockHash(chainDb, block.Hash()); err != nil {
return nil, err
}
+ if err := WriteChainConfig(chainDb, block.Hash(), genesis.ChainConfig); err != nil {
+ return nil, err
+ }
+
return block, nil
}
diff --git a/core/headerchain.go b/core/headerchain.go
index 255139dde..21fc6e80a 100644
--- a/core/headerchain.go
+++ b/core/headerchain.go
@@ -40,6 +40,8 @@ import (
// It is not thread safe either, the encapsulating chain structures should do
// the necessary mutex locking/unlocking.
type HeaderChain struct {
+ config *ChainConfig
+
chainDb ethdb.Database
genesisHeader *types.Header
@@ -62,7 +64,7 @@ type getHeaderValidatorFn func() HeaderValidator
// getValidator should return the parent's validator
// procInterrupt points to the parent's interrupt semaphore
// wg points to the parent's shutdown wait group
-func NewHeaderChain(chainDb ethdb.Database, getValidator getHeaderValidatorFn, procInterrupt func() bool) (*HeaderChain, error) {
+func NewHeaderChain(chainDb ethdb.Database, config *ChainConfig, getValidator getHeaderValidatorFn, procInterrupt func() bool) (*HeaderChain, error) {
headerCache, _ := lru.New(headerCacheLimit)
tdCache, _ := lru.New(tdCacheLimit)
@@ -73,6 +75,7 @@ func NewHeaderChain(chainDb ethdb.Database, getValidator getHeaderValidatorFn, p
}
hc := &HeaderChain{
+ config: config,
chainDb: chainDb,
headerCache: headerCache,
tdCache: tdCache,
@@ -436,15 +439,17 @@ func (hc *HeaderChain) SetGenesis(head *types.Header) {
//
// headerValidator implements HeaderValidator.
type headerValidator struct {
- hc *HeaderChain // Canonical header chain
- Pow pow.PoW // Proof of work used for validating
+ config *ChainConfig
+ hc *HeaderChain // Canonical header chain
+ Pow pow.PoW // Proof of work used for validating
}
// NewBlockValidator returns a new block validator which is safe for re-use
-func NewHeaderValidator(chain *HeaderChain, pow pow.PoW) HeaderValidator {
+func NewHeaderValidator(config *ChainConfig, chain *HeaderChain, pow pow.PoW) HeaderValidator {
return &headerValidator{
- Pow: pow,
- hc: chain,
+ config: config,
+ Pow: pow,
+ hc: chain,
}
}
@@ -460,5 +465,5 @@ func (v *headerValidator) ValidateHeader(header, parent *types.Header, checkPow
if v.hc.HasHeader(header.Hash()) {
return nil
}
- return ValidateHeader(v.Pow, header, parent, checkPow, false)
+ return ValidateHeader(v.config, v.Pow, header, parent, checkPow, false)
}
diff --git a/core/state_processor.go b/core/state_processor.go
index 38cd0e675..441f6a7a8 100644
--- a/core/state_processor.go
+++ b/core/state_processor.go
@@ -16,12 +16,21 @@ var (
big32 = big.NewInt(32)
)
+// StateProcessor is a basic Processor, which takes care of transitioning
+// state from one point to another.
+//
+// StateProcessor implements Processor.
type StateProcessor struct {
- bc *BlockChain
+ config *ChainConfig
+ bc *BlockChain
}
-func NewStateProcessor(bc *BlockChain) *StateProcessor {
- return &StateProcessor{bc}
+// NewStateProcessor initialises a new StateProcessor.
+func NewStateProcessor(config *ChainConfig, bc *BlockChain) *StateProcessor {
+ return &StateProcessor{
+ config: config,
+ bc: bc,
+ }
}
// Process processes the state changes according to the Ethereum rules by running
@@ -31,7 +40,7 @@ func NewStateProcessor(bc *BlockChain) *StateProcessor {
// Process returns the receipts and logs accumulated during the process and
// returns the amount of gas that was used in the process. If any of the
// transactions failed to execute due to insufficient gas it will return an error.
-func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg *vm.Config) (types.Receipts, vm.Logs, *big.Int, error) {
+func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (types.Receipts, vm.Logs, *big.Int, error) {
var (
receipts types.Receipts
totalUsedGas = big.NewInt(0)
@@ -43,7 +52,7 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
for i, tx := range block.Transactions() {
statedb.StartRecord(tx.Hash(), block.Hash(), i)
- receipt, logs, _, err := ApplyTransaction(p.bc, gp, statedb, header, tx, totalUsedGas, cfg)
+ receipt, logs, _, err := ApplyTransaction(p.config, p.bc, gp, statedb, header, tx, totalUsedGas, cfg)
if err != nil {
return nil, nil, totalUsedGas, err
}
@@ -60,8 +69,8 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
//
// ApplyTransactions returns the generated receipts and vm logs during the
// execution of the state transition phase.
-func ApplyTransaction(bc *BlockChain, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *big.Int, cfg *vm.Config) (*types.Receipt, vm.Logs, *big.Int, error) {
- _, gas, err := ApplyMessage(NewEnv(statedb, bc, tx, header, cfg), tx, gp)
+func ApplyTransaction(config *ChainConfig, bc *BlockChain, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *big.Int, cfg vm.Config) (*types.Receipt, vm.Logs, *big.Int, error) {
+ _, gas, err := ApplyMessage(NewEnv(statedb, config, bc, tx, header, cfg), tx, gp)
if err != nil {
return nil, nil, nil, err
}
diff --git a/core/state_transition.go b/core/state_transition.go
index cc357aaca..60ac7d8cb 100644
--- a/core/state_transition.go
+++ b/core/state_transition.go
@@ -124,7 +124,7 @@ func (self *StateTransition) from() (vm.Account, error) {
f common.Address
err error
)
- if params.IsHomestead(self.env.BlockNumber()) {
+ if self.env.RuleSet().IsHomestead(self.env.BlockNumber()) {
f, err = self.msg.From()
} else {
f, err = self.msg.FromFrontier()
@@ -216,7 +216,7 @@ func (self *StateTransition) transitionDb() (ret []byte, usedGas *big.Int, err e
msg := self.msg
sender, _ := self.from() // err checked in preCheck
- homestead := params.IsHomestead(self.env.BlockNumber())
+ homestead := self.env.RuleSet().IsHomestead(self.env.BlockNumber())
contractCreation := MessageCreatesContract(msg)
// Pay intrinsic gas
if err = self.useGas(IntrinsicGas(self.data, contractCreation, homestead)); err != nil {
diff --git a/core/tx_pool.go b/core/tx_pool.go
index f4e964bf7..e997e8cd0 100644
--- a/core/tx_pool.go
+++ b/core/tx_pool.go
@@ -30,7 +30,6 @@ import (
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
- "github.com/ethereum/go-ethereum/params"
)
var (
@@ -60,6 +59,7 @@ type stateFn func() (*state.StateDB, error)
// current state) and future transactions. Transactions move between those
// two states over time as they are received and processed.
type TxPool struct {
+ config *ChainConfig
quit chan bool // Quitting channel
currentState stateFn // The state function which will allow us to do some pre checks
pendingState *state.ManagedState
@@ -75,8 +75,9 @@ type TxPool struct {
homestead bool
}
-func NewTxPool(eventMux *event.TypeMux, currentStateFn stateFn, gasLimitFn func() *big.Int) *TxPool {
+func NewTxPool(config *ChainConfig, eventMux *event.TypeMux, currentStateFn stateFn, gasLimitFn func() *big.Int) *TxPool {
pool := &TxPool{
+ config: config,
pending: make(map[common.Hash]*types.Transaction),
queue: make(map[common.Address]map[common.Hash]*types.Transaction),
quit: make(chan bool),
@@ -102,7 +103,7 @@ func (pool *TxPool) eventLoop() {
switch ev := ev.Data.(type) {
case ChainHeadEvent:
pool.mu.Lock()
- if ev.Block != nil && params.IsHomestead(ev.Block.Number()) {
+ if ev.Block != nil && pool.config.IsHomestead(ev.Block.Number()) {
pool.homestead = true
}
diff --git a/core/tx_pool_test.go b/core/tx_pool_test.go
index fa1a740dc..ed9bea75f 100644
--- a/core/tx_pool_test.go
+++ b/core/tx_pool_test.go
@@ -40,7 +40,7 @@ func setupTxPool() (*TxPool, *ecdsa.PrivateKey) {
var m event.TypeMux
key, _ := crypto.GenerateKey()
- newPool := NewTxPool(&m, func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) })
+ newPool := NewTxPool(testChainConfig(), &m, func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) })
newPool.resetState()
return newPool, key
}
diff --git a/core/types.go b/core/types.go
index af9bc567b..e2b31643a 100644
--- a/core/types.go
+++ b/core/types.go
@@ -61,7 +61,7 @@ type HeaderValidator interface {
// of gas used in the process and return an error if any of the internal rules
// failed.
type Processor interface {
- Process(block *types.Block, statedb *state.StateDB, cfg *vm.Config) (types.Receipts, vm.Logs, *big.Int, error)
+ Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (types.Receipts, vm.Logs, *big.Int, error)
}
// Backend is an interface defining the basic functionality for an operable node
diff --git a/core/vm/environment.go b/core/vm/environment.go
index 568218edd..747627565 100644
--- a/core/vm/environment.go
+++ b/core/vm/environment.go
@@ -22,9 +22,17 @@ import (
"github.com/ethereum/go-ethereum/common"
)
+// RuleSet is an interface that defines the current rule set during the
+// execution of the EVM instructions (e.g. whether it's homestead)
+type RuleSet interface {
+ IsHomestead(*big.Int) bool
+}
+
// Environment is an EVM requirement and helper which allows access to outside
// information such as states.
type Environment interface {
+ // The current ruleset
+ RuleSet() RuleSet
// The state database
Db() Database
// Creates a restorable snapshot
@@ -53,10 +61,10 @@ type Environment interface {
AddLog(*Log)
// Type of the VM
Vm() Vm
- // Current calling depth
+ // Get the curret calling depth
Depth() int
+ // Set the current calling depth
SetDepth(i int)
-
// Call another contract
Call(me ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error)
// Take another's contract code and execute within our own context
diff --git a/core/vm/instructions.go b/core/vm/instructions.go
index c4b4339a2..942fafde7 100644
--- a/core/vm/instructions.go
+++ b/core/vm/instructions.go
@@ -520,7 +520,7 @@ func opCreate(instr instruction, pc *uint64, env Environment, contract *Contract
// homestead we must check for CodeStoreOutOfGasError (homestead only
// rule) and treat as an error, if the ruleset is frontier we must
// ignore this error and pretend the operation was successful.
- if params.IsHomestead(env.BlockNumber()) && suberr == CodeStoreOutOfGasError {
+ if env.RuleSet().IsHomestead(env.BlockNumber()) && suberr == CodeStoreOutOfGasError {
stack.push(new(big.Int))
} else if suberr != nil && suberr != CodeStoreOutOfGasError {
stack.push(new(big.Int))
diff --git a/core/vm/jit.go b/core/vm/jit.go
index 71ffcf0f6..ac2083f54 100644
--- a/core/vm/jit.go
+++ b/core/vm/jit.go
@@ -30,27 +30,24 @@ import (
"github.com/hashicorp/golang-lru"
)
+// progStatus is the type for the JIT program status.
type progStatus int32
const (
- progUnknown progStatus = iota
- progCompile
- progReady
- progError
+ progUnknown progStatus = iota // unknown status
+ progCompile // compile status
+ progReady // ready for use status
+ progError // error status (usually caused during compilation)
- defaultJitMaxCache int = 64
+ defaultJitMaxCache int = 64 // maximum amount of jit cached programs
)
-var (
- EnableJit bool // Enables the JIT VM
- ForceJit bool // Force the JIT, skip byte VM
- MaxProgSize int // Max cache size for JIT Programs
-)
+var MaxProgSize int // Max cache size for JIT programs
-var programs *lru.Cache
+var programs *lru.Cache // lru cache for the JIT programs.
func init() {
- programs, _ = lru.New(defaultJitMaxCache)
+ SetJITCacheSize(defaultJitMaxCache)
}
// SetJITCacheSize recreates the program cache with the max given size. Setting
@@ -322,7 +319,7 @@ func runProgram(program *Program, pcstart uint64, mem *Memory, stack *stack, env
}()
}
- homestead := params.IsHomestead(env.BlockNumber())
+ homestead := env.RuleSet().IsHomestead(env.BlockNumber())
for pc < uint64(len(program.instructions)) {
instrCount++
diff --git a/core/vm/jit_test.go b/core/vm/jit_test.go
index 43b1dee2a..c503a3a81 100644
--- a/core/vm/jit_test.go
+++ b/core/vm/jit_test.go
@@ -84,7 +84,7 @@ func TestCompiling(t *testing.T) {
func TestResetInput(t *testing.T) {
var sender account
- env := NewEnv()
+ env := NewEnv(false, true)
contract := NewContract(sender, sender, big.NewInt(100), big.NewInt(10000), big.NewInt(0))
contract.CodeAddr = &common.Address{}
@@ -143,10 +143,7 @@ func runVmBench(test vmBench, b *testing.B) {
if test.precompile && !test.forcejit {
NewProgram(test.code)
}
- env := NewEnv()
-
- EnableJit = !test.nojit
- ForceJit = test.forcejit
+ env := NewEnv(test.nojit, test.forcejit)
b.ResetTimer()
@@ -168,12 +165,16 @@ type Env struct {
evm *EVM
}
-func NewEnv() *Env {
+func NewEnv(noJit, forceJit bool) *Env {
env := &Env{gasLimit: big.NewInt(10000), depth: 0}
- env.evm = New(env, nil)
+ env.evm = New(env, Config{
+ EnableJit: !noJit,
+ ForceJit: forceJit,
+ })
return env
}
+func (self *Env) RuleSet() RuleSet { return ruleSet{new(big.Int)} }
func (self *Env) Vm() Vm { return self.evm }
func (self *Env) Origin() common.Address { return common.Address{} }
func (self *Env) BlockNumber() *big.Int { return big.NewInt(0) }
diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go
index 8297d3e1d..2c3796679 100644
--- a/core/vm/jump_table.go
+++ b/core/vm/jump_table.go
@@ -1,10 +1,6 @@
package vm
-import (
- "math/big"
-
- "github.com/ethereum/go-ethereum/params"
-)
+import "math/big"
type jumpPtr struct {
fn instrFn
@@ -13,12 +9,12 @@ type jumpPtr struct {
type vmJumpTable [256]jumpPtr
-func newJumpTable(blockNumber *big.Int) vmJumpTable {
+func newJumpTable(ruleset RuleSet, blockNumber *big.Int) vmJumpTable {
var jumpTable vmJumpTable
// when initialising a new VM execution we must first check the homestead
// changes.
- if params.IsHomestead(blockNumber) {
+ if ruleset.IsHomestead(blockNumber) {
jumpTable[DELEGATECALL] = jumpPtr{opDelegateCall, true}
}
diff --git a/core/vm/jump_table_test.go b/core/vm/jump_table_test.go
index 2ed1b26fc..2386a7525 100644
--- a/core/vm/jump_table_test.go
+++ b/core/vm/jump_table_test.go
@@ -3,20 +3,16 @@ package vm
import (
"math/big"
"testing"
-
- "github.com/ethereum/go-ethereum/params"
)
func TestInit(t *testing.T) {
- params.HomesteadBlock = big.NewInt(1)
-
- jumpTable := newJumpTable(big.NewInt(0))
+ jumpTable := newJumpTable(ruleSet{big.NewInt(1)}, big.NewInt(0))
if jumpTable[DELEGATECALL].valid {
t.Error("Expected DELEGATECALL not to be present")
}
for _, n := range []int64{1, 2, 100} {
- jumpTable := newJumpTable(big.NewInt(n))
+ jumpTable := newJumpTable(ruleSet{big.NewInt(1)}, big.NewInt(n))
if !jumpTable[DELEGATECALL].valid {
t.Error("Expected DELEGATECALL to be present for block", n)
}
diff --git a/core/vm/logger.go b/core/vm/logger.go
index 8d333dfd2..cbdc8a744 100644
--- a/core/vm/logger.go
+++ b/core/vm/logger.go
@@ -89,7 +89,7 @@ func newLogger(cfg LogConfig, env Environment) *Logger {
// captureState logs a new structured log message and pushes it out to the environment
//
// captureState also tracks SSTORE ops to track dirty values.
-func (l *Logger) captureState(pc uint64, op OpCode, gas, cost *big.Int, memory *Memory, stack *stack, contract *Contract, err error) {
+func (l *Logger) captureState(pc uint64, op OpCode, gas, cost *big.Int, memory *Memory, stack *stack, contract *Contract, depth int, err error) {
// short circuit if no log collector is present
if l.cfg.Collector == nil {
return
diff --git a/core/vm/logger_test.go b/core/vm/logger_test.go
index 77fee2c64..144569865 100644
--- a/core/vm/logger_test.go
+++ b/core/vm/logger_test.go
@@ -47,7 +47,7 @@ type dummyEnv struct {
func newDummyEnv(ref *dummyContractRef) *dummyEnv {
return &dummyEnv{
- Env: NewEnv(),
+ Env: NewEnv(true, false),
ref: ref,
}
}
@@ -58,7 +58,7 @@ func (d dummyEnv) AddStructLog(StructLog) {}
func TestStoreCapture(t *testing.T) {
var (
- env = NewEnv()
+ env = NewEnv(true, false)
logger = newLogger(LogConfig{Collector: env}, env)
mem = NewMemory()
stack = newstack()
@@ -69,7 +69,7 @@ func TestStoreCapture(t *testing.T) {
var index common.Hash
- logger.captureState(0, SSTORE, new(big.Int), new(big.Int), mem, stack, contract, nil)
+ logger.captureState(0, SSTORE, new(big.Int), new(big.Int), mem, stack, contract, 0, nil)
if len(logger.changedValues[contract.Address()]) == 0 {
t.Fatalf("expected exactly 1 changed value on address %x, got %d", contract.Address(), len(logger.changedValues[contract.Address()]))
}
@@ -91,13 +91,13 @@ func TestStorageCapture(t *testing.T) {
stack = newstack()
)
- logger.captureState(0, STOP, new(big.Int), new(big.Int), mem, stack, contract, nil)
+ logger.captureState(0, STOP, new(big.Int), new(big.Int), mem, stack, contract, 0, nil)
if ref.calledForEach {
t.Error("didn't expect for each to be called")
}
logger = newLogger(LogConfig{Collector: env, FullStorage: true}, env)
- logger.captureState(0, STOP, new(big.Int), new(big.Int), mem, stack, contract, nil)
+ logger.captureState(0, STOP, new(big.Int), new(big.Int), mem, stack, contract, 0, nil)
if !ref.calledForEach {
t.Error("expected for each to be called")
}
diff --git a/core/vm/runtime/env.go b/core/vm/runtime/env.go
index ce64d7117..1e943940b 100644
--- a/core/vm/runtime/env.go
+++ b/core/vm/runtime/env.go
@@ -27,8 +27,9 @@ import (
// Env is a basic runtime environment required for running the EVM.
type Env struct {
- depth int
- state *state.StateDB
+ ruleSet vm.RuleSet
+ depth int
+ state *state.StateDB
origin common.Address
coinbase common.Address
@@ -48,6 +49,7 @@ type Env struct {
// NewEnv returns a new vm.Environment
func NewEnv(cfg *Config, state *state.StateDB) vm.Environment {
env := &Env{
+ ruleSet: cfg.RuleSet,
state: state,
origin: cfg.Origin,
coinbase: cfg.Coinbase,
@@ -56,7 +58,7 @@ func NewEnv(cfg *Config, state *state.StateDB) vm.Environment {
difficulty: cfg.Difficulty,
gasLimit: cfg.GasLimit,
}
- env.evm = vm.New(env, &vm.Config{
+ env.evm = vm.New(env, vm.Config{
Debug: cfg.Debug,
EnableJit: !cfg.DisableJit,
ForceJit: !cfg.DisableJit,
@@ -77,6 +79,7 @@ func (self *Env) AddStructLog(log vm.StructLog) {
self.logs = append(self.logs, log)
}
+func (self *Env) RuleSet() vm.RuleSet { return self.ruleSet }
func (self *Env) Vm() vm.Vm { return self.evm }
func (self *Env) Origin() common.Address { return self.origin }
func (self *Env) BlockNumber() *big.Int { return self.number }
diff --git a/core/vm/runtime/runtime.go b/core/vm/runtime/runtime.go
index f88a20170..553864a83 100644
--- a/core/vm/runtime/runtime.go
+++ b/core/vm/runtime/runtime.go
@@ -22,13 +22,20 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/state"
+ "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
)
+// The default, always homestead, rule set for the vm env
+type ruleSet struct{}
+
+func (ruleSet) IsHomestead(*big.Int) bool { return true }
+
// Config is a basic type specifying certain configuration flags for running
// the EVM.
type Config struct {
+ RuleSet vm.RuleSet
Difficulty *big.Int
Origin common.Address
Coinbase common.Address
@@ -46,6 +53,10 @@ type Config struct {
// sets defaults on the config
func setDefaults(cfg *Config) {
+ if cfg.RuleSet == nil {
+ cfg.RuleSet = ruleSet{}
+ }
+
if cfg.Difficulty == nil {
cfg.Difficulty = new(big.Int)
}
diff --git a/core/vm/util_test.go b/core/vm/util_test.go
new file mode 100644
index 000000000..3da742bfa
--- /dev/null
+++ b/core/vm/util_test.go
@@ -0,0 +1,9 @@
+package vm
+
+import "math/big"
+
+type ruleSet struct {
+ hs *big.Int
+}
+
+func (r ruleSet) IsHomestead(n *big.Int) bool { return n.Cmp(r.hs) >= 0 }
diff --git a/core/vm/vm.go b/core/vm/vm.go
index f72c853a2..0f93715d6 100644
--- a/core/vm/vm.go
+++ b/core/vm/vm.go
@@ -43,18 +43,13 @@ type Config struct {
type EVM struct {
env Environment
jumpTable vmJumpTable
- cfg *Config
+ cfg Config
logger *Logger
}
// New returns a new instance of the EVM.
-func New(env Environment, cfg *Config) *EVM {
- // initialise a default config if none is present
- if cfg == nil {
- cfg = new(Config)
- }
-
+func New(env Environment, cfg Config) *EVM {
var logger *Logger
if cfg.Debug {
logger = newLogger(cfg.Logger, env)
@@ -62,7 +57,7 @@ func New(env Environment, cfg *Config) *EVM {
return &EVM{
env: env,
- jumpTable: newJumpTable(env.BlockNumber()),
+ jumpTable: newJumpTable(env.RuleSet(), env.BlockNumber()),
cfg: cfg,
logger: logger,
}
@@ -154,7 +149,7 @@ func (evm *EVM) Run(contract *Contract, input []byte) (ret []byte, err error) {
// User defer pattern to check for an error and, based on the error being nil or not, use all gas and return.
defer func() {
if err != nil && evm.cfg.Debug {
- evm.logger.captureState(pc, op, contract.Gas, cost, mem, stack, contract, err)
+ evm.logger.captureState(pc, op, contract.Gas, cost, mem, stack, contract, evm.env.Depth(), err)
}
}()
@@ -196,7 +191,7 @@ func (evm *EVM) Run(contract *Contract, input []byte) (ret []byte, err error) {
mem.Resize(newMemSize.Uint64())
// Add a log message
if evm.cfg.Debug {
- evm.logger.captureState(pc, op, contract.Gas, cost, mem, stack, contract, nil)
+ evm.logger.captureState(pc, op, contract.Gas, cost, mem, stack, contract, evm.env.Depth(), nil)
}
if opPtr := evm.jumpTable[op]; opPtr.valid {
diff --git a/core/vm/vm_jit_fake.go b/core/vm/vm_jit_fake.go
index b26cf1ad0..4fa98ccd9 100644
--- a/core/vm/vm_jit_fake.go
+++ b/core/vm/vm_jit_fake.go
@@ -22,5 +22,5 @@ import "fmt"
func NewJitVm(env Environment) VirtualMachine {
fmt.Printf("Warning! EVM JIT not enabled.\n")
- return New(env, nil)
+ return New(env, Config{})
}
diff --git a/core/vm_env.go b/core/vm_env.go
index 880baa7b0..f50140c68 100644
--- a/core/vm_env.go
+++ b/core/vm_env.go
@@ -41,30 +41,26 @@ func GetHashFn(ref common.Hash, chain *BlockChain) func(n uint64) common.Hash {
}
type VMEnv struct {
- state *state.StateDB // State to use for executing
- evm *vm.EVM // The Ethereum Virtual Machine
- depth int // Current execution depth
- msg Message // Message appliod
+ chainConfig *ChainConfig // Chain configuration
+ state *state.StateDB // State to use for executing
+ evm *vm.EVM // The Ethereum Virtual Machine
+ depth int // Current execution depth
+ msg Message // Message appliod
header *types.Header // Header information
chain *BlockChain // Blockchain handle
logs []vm.StructLog // Logs for the custom structured logger
getHashFn func(uint64) common.Hash // getHashFn callback is used to retrieve block hashes
-
}
-func NewEnv(state *state.StateDB, chain *BlockChain, msg Message, header *types.Header, cfg *vm.Config) *VMEnv {
+func NewEnv(state *state.StateDB, chainConfig *ChainConfig, chain *BlockChain, msg Message, header *types.Header, cfg vm.Config) *VMEnv {
env := &VMEnv{
- chain: chain,
- state: state,
- header: header,
- msg: msg,
- getHashFn: GetHashFn(header.ParentHash, chain),
- }
-
- // initialise a default config if none present
- if cfg == nil {
- cfg = new(vm.Config)
+ chainConfig: chainConfig,
+ chain: chain,
+ state: state,
+ header: header,
+ msg: msg,
+ getHashFn: GetHashFn(header.ParentHash, chain),
}
// if no log collector is present set self as the collector
@@ -76,6 +72,7 @@ func NewEnv(state *state.StateDB, chain *BlockChain, msg Message, header *types.
return env
}
+func (self *VMEnv) RuleSet() vm.RuleSet { return self.chainConfig }
func (self *VMEnv) Vm() vm.Vm { return self.evm }
func (self *VMEnv) Origin() common.Address { f, _ := self.msg.From(); return f }
func (self *VMEnv) BlockNumber() *big.Int { return self.header.Number }