diff options
author | Jeffrey Wilcke <jeffrey@ethereum.org> | 2016-03-02 06:32:43 +0800 |
---|---|---|
committer | Jeffrey Wilcke <geffobscura@gmail.com> | 2016-04-01 07:01:10 +0800 |
commit | f0cbebb19f3137ee3ba0e66dadd1b5b9dbf98b1c (patch) | |
tree | 02e31a0e31040980e30e3a835ff9eba73e419439 /core | |
parent | 10d3466c934bd425a8c941270749a652a588527d (diff) | |
download | go-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')
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 } |