diff options
author | Jeffrey Wilcke <jeffrey@ethereum.org> | 2016-10-20 19:36:29 +0800 |
---|---|---|
committer | Jeffrey Wilcke <jeffrey@ethereum.org> | 2016-11-13 17:44:04 +0800 |
commit | 445feaeef58bd89a113743dccf6fd5df55cde6fa (patch) | |
tree | 6c692a0989800f005a94bde2d372fcbe1f7630a1 | |
parent | 932d973e36ff0d41a6005b93d2d4ff1b4430fb04 (diff) | |
download | go-tangerine-445feaeef58bd89a113743dccf6fd5df55cde6fa.tar.gz go-tangerine-445feaeef58bd89a113743dccf6fd5df55cde6fa.tar.zst go-tangerine-445feaeef58bd89a113743dccf6fd5df55cde6fa.zip |
core, core/state, trie: EIP158, reprice & skip empty account write
This commit implements EIP158 part 1, 2, 3 & 4
1. If an account is empty it's no longer written to the trie. An empty
account is defined as (balance=0, nonce=0, storage=0, code=0).
2. Delete an empty account if it's touched
3. An empty account is redefined as either non-existent or empty.
4. Zero value calls and zero value suicides no longer consume the 25k
reation costs.
params: moved core/config to params
Signed-off-by: Jeffrey Wilcke <jeffrey@ethereum.org>
74 files changed, 717 insertions, 561 deletions
diff --git a/accounts/abi/bind/backends/simulated.go b/accounts/abi/bind/backends/simulated.go index bdc2a98bc..f750a1fbd 100644 --- a/accounts/abi/bind/backends/simulated.go +++ b/accounts/abi/bind/backends/simulated.go @@ -31,11 +31,12 @@ 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" "golang.org/x/net/context" ) // Default chain configuration which sets homestead phase at block 0 (i.e. no frontier) -var chainConfig = &core.ChainConfig{HomesteadBlock: big.NewInt(0)} +var chainConfig = ¶ms.ChainConfig{HomesteadBlock: big.NewInt(0), EIP150Block: new(big.Int), EIP158Block: new(big.Int)} // This nil assignment ensures compile time that SimulatedBackend implements bind.ContractBackend. var _ bind.ContractBackend = (*SimulatedBackend)(nil) @@ -51,6 +52,8 @@ type SimulatedBackend struct { mu sync.Mutex pendingBlock *types.Block // Currently pending block that will be imported on request pendingState *state.StateDB // Currently pending state that will be the active on on request + + config *params.ChainConfig } // NewSimulatedBackend creates a new binding backend using a simulated blockchain @@ -85,7 +88,7 @@ func (b *SimulatedBackend) Rollback() { } func (b *SimulatedBackend) rollback() { - blocks, _ := core.GenerateChain(nil, b.blockchain.CurrentBlock(), b.database, 1, func(int, *core.BlockGen) {}) + blocks, _ := core.GenerateChain(chainConfig, b.blockchain.CurrentBlock(), b.database, 1, func(int, *core.BlockGen) {}) b.pendingBlock = blocks[0] b.pendingState, _ = state.New(b.pendingBlock.Root(), b.database) } @@ -243,7 +246,7 @@ func (b *SimulatedBackend) SendTransaction(ctx context.Context, tx *types.Transa panic(fmt.Errorf("invalid transaction nonce: got %d, want %d", tx.Nonce(), nonce)) } - blocks, _ := core.GenerateChain(nil, b.blockchain.CurrentBlock(), b.database, 1, func(number int, block *core.BlockGen) { + blocks, _ := core.GenerateChain(chainConfig, b.blockchain.CurrentBlock(), b.database, 1, func(number int, block *core.BlockGen) { for _, tx := range b.pendingBlock.Transactions() { block.AddTx(tx) } diff --git a/cmd/ethtest/main.go b/cmd/ethtest/main.go index 5663c2623..a58935609 100644 --- a/cmd/ethtest/main.go +++ b/cmd/ethtest/main.go @@ -76,7 +76,7 @@ func runTestWithReader(test string, r io.Reader) error { case "bk", "block", "blocktest", "blockchaintest", "blocktests", "blockchaintests": err = tests.RunBlockTestWithReader(params.MainNetHomesteadBlock, params.MainNetDAOForkBlock, params.MainNetHomesteadGasRepriceBlock, r, skipTests) case "st", "state", "statetest", "statetests": - rs := tests.RuleSet{HomesteadBlock: params.MainNetHomesteadBlock, DAOForkBlock: params.MainNetDAOForkBlock, DAOForkSupport: true} + rs := ¶ms.ChainConfig{HomesteadBlock: params.MainNetHomesteadBlock, DAOForkBlock: params.MainNetDAOForkBlock, DAOForkSupport: true, EIP150Block: params.MainNetHomesteadGasRepriceBlock} err = tests.RunStateTestWithReader(rs, r, skipTests) case "tx", "transactiontest", "transactiontests": err = tests.RunTransactionTestsWithReader(r, skipTests) diff --git a/cmd/evm/main.go b/cmd/evm/main.go index 704469432..2c4329fa5 100644 --- a/cmd/evm/main.go +++ b/cmd/evm/main.go @@ -191,7 +191,7 @@ func run(ctx *cli.Context) error { vmdone := time.Since(tstart) if ctx.GlobalBool(DumpFlag.Name) { - statedb.Commit() + statedb.Commit(true) fmt.Println(string(statedb.Dump())) } vm.StdErrFormat(logger.StructLogs()) @@ -251,7 +251,7 @@ func NewEnv(state *state.StateDB, transactor common.Address, value *big.Int, cfg return env } -// ruleSet implements vm.RuleSet and will always default to the homestead rule set. +// ruleSet implements vm.ChainConfig and will always default to the homestead rule set. type ruleSet struct{} func (ruleSet) IsHomestead(*big.Int) bool { return true } @@ -259,22 +259,22 @@ func (ruleSet) GasTable(*big.Int) params.GasTable { return params.GasTableHomesteadGasRepriceFork } -func (self *VMEnv) RuleSet() vm.RuleSet { return ruleSet{} } -func (self *VMEnv) Vm() vm.Vm { return self.evm } -func (self *VMEnv) Db() vm.Database { return self.state } -func (self *VMEnv) SnapshotDatabase() int { return self.state.Snapshot() } -func (self *VMEnv) RevertToSnapshot(snap int) { self.state.RevertToSnapshot(snap) } -func (self *VMEnv) Origin() common.Address { return *self.transactor } -func (self *VMEnv) BlockNumber() *big.Int { return common.Big0 } -func (self *VMEnv) Coinbase() common.Address { return *self.transactor } -func (self *VMEnv) Time() *big.Int { return self.time } -func (self *VMEnv) Difficulty() *big.Int { return common.Big1 } -func (self *VMEnv) BlockHash() []byte { return make([]byte, 32) } -func (self *VMEnv) Value() *big.Int { return self.value } -func (self *VMEnv) GasLimit() *big.Int { return big.NewInt(1000000000) } -func (self *VMEnv) VmType() vm.Type { return vm.StdVmTy } -func (self *VMEnv) Depth() int { return 0 } -func (self *VMEnv) SetDepth(i int) { self.depth = i } +func (self *VMEnv) ChainConfig() *params.ChainConfig { return params.TestChainConfig } +func (self *VMEnv) Vm() vm.Vm { return self.evm } +func (self *VMEnv) Db() vm.Database { return self.state } +func (self *VMEnv) SnapshotDatabase() int { return self.state.Snapshot() } +func (self *VMEnv) RevertToSnapshot(snap int) { self.state.RevertToSnapshot(snap) } +func (self *VMEnv) Origin() common.Address { return *self.transactor } +func (self *VMEnv) BlockNumber() *big.Int { return common.Big0 } +func (self *VMEnv) Coinbase() common.Address { return *self.transactor } +func (self *VMEnv) Time() *big.Int { return self.time } +func (self *VMEnv) Difficulty() *big.Int { return common.Big1 } +func (self *VMEnv) BlockHash() []byte { return make([]byte, 32) } +func (self *VMEnv) Value() *big.Int { return self.value } +func (self *VMEnv) GasLimit() *big.Int { return big.NewInt(1000000000) } +func (self *VMEnv) VmType() vm.Type { return vm.StdVmTy } +func (self *VMEnv) Depth() int { return 0 } +func (self *VMEnv) SetDepth(i int) { self.depth = i } func (self *VMEnv) GetHash(n uint64) common.Hash { if self.block.Number().Cmp(big.NewInt(int64(n))) == 0 { return self.block.Hash() diff --git a/cmd/gethrpctest/main.go b/cmd/gethrpctest/main.go index 343aedc6f..850bf8eb2 100644 --- a/cmd/gethrpctest/main.go +++ b/cmd/gethrpctest/main.go @@ -23,7 +23,6 @@ import ( "os" "os/signal" - "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/ethdb" @@ -122,7 +121,7 @@ func MakeSystemNode(privkey string, test *tests.BlockTest) (*node.Node, error) { ethConf := ð.Config{ TestGenesisState: db, TestGenesisBlock: test.Genesis, - ChainConfig: &core.ChainConfig{HomesteadBlock: params.MainNetHomesteadBlock}, + ChainConfig: ¶ms.ChainConfig{HomesteadBlock: params.MainNetHomesteadBlock}, } if err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) { return eth.New(ctx, ethConf) }); err != nil { return nil, err diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 626c2615d..641ff6d40 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -801,7 +801,7 @@ func SetupNetwork(ctx *cli.Context) { } // MakeChainConfig reads the chain configuration from the database in ctx.Datadir. -func MakeChainConfig(ctx *cli.Context, stack *node.Node) *core.ChainConfig { +func MakeChainConfig(ctx *cli.Context, stack *node.Node) *params.ChainConfig { db := MakeChainDatabase(ctx, stack) defer db.Close() @@ -809,9 +809,9 @@ func MakeChainConfig(ctx *cli.Context, stack *node.Node) *core.ChainConfig { } // MakeChainConfigFromDb reads the chain configuration from the given database. -func MakeChainConfigFromDb(ctx *cli.Context, db ethdb.Database) *core.ChainConfig { +func MakeChainConfigFromDb(ctx *cli.Context, db ethdb.Database) *params.ChainConfig { // If the chain is already initialized, use any existing chain configs - config := new(core.ChainConfig) + config := new(params.ChainConfig) genesis := core.GetBlock(db, core.GetCanonicalHash(db, 0), 0) if genesis != nil { @@ -849,19 +849,20 @@ func MakeChainConfigFromDb(ctx *cli.Context, db ethdb.Database) *core.ChainConfi } config.DAOForkSupport = true } - if config.HomesteadGasRepriceBlock == nil { - if ctx.GlobalBool(TestNetFlag.Name) { - config.HomesteadGasRepriceBlock = params.TestNetHomesteadGasRepriceBlock - } else { - config.HomesteadGasRepriceBlock = params.MainNetHomesteadGasRepriceBlock - } + config.DAOForkSupport = true + } + if config.EIP150Block == nil { + if ctx.GlobalBool(TestNetFlag.Name) { + config.EIP150Block = params.TestNetHomesteadGasRepriceBlock + } else { + config.EIP150Block = params.MainNetHomesteadGasRepriceBlock } - if config.HomesteadGasRepriceHash == (common.Hash{}) { - if ctx.GlobalBool(TestNetFlag.Name) { - config.HomesteadGasRepriceHash = params.TestNetHomesteadGasRepriceHash - } else { - config.HomesteadGasRepriceHash = params.MainNetHomesteadGasRepriceHash - } + } + if config.EIP150Hash == (common.Hash{}) { + if ctx.GlobalBool(TestNetFlag.Name) { + config.EIP150Hash = params.TestNetHomesteadGasRepriceHash + } else { + config.EIP150Hash = params.MainNetHomesteadGasRepriceHash } } // Force override any existing configs if explicitly requested diff --git a/common/registrar/ethreg/api.go b/common/registrar/ethreg/api.go index 10050a545..bbc34f6ac 100644 --- a/common/registrar/ethreg/api.go +++ b/common/registrar/ethreg/api.go @@ -32,11 +32,12 @@ import ( "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/params" ) // registryAPIBackend is a backend for an Ethereum Registry. type registryAPIBackend struct { - config *core.ChainConfig + config *params.ChainConfig bc *core.BlockChain chainDb ethdb.Database txPool *core.TxPool @@ -45,12 +46,12 @@ type registryAPIBackend struct { // PrivateRegistarAPI offers various functions to access the Ethereum registry. type PrivateRegistarAPI struct { - config *core.ChainConfig + config *params.ChainConfig be *registryAPIBackend } // NewPrivateRegistarAPI creates a new PrivateRegistarAPI instance. -func NewPrivateRegistarAPI(config *core.ChainConfig, bc *core.BlockChain, chainDb ethdb.Database, txPool *core.TxPool, am *accounts.Manager) *PrivateRegistarAPI { +func NewPrivateRegistarAPI(config *params.ChainConfig, bc *core.BlockChain, chainDb ethdb.Database, txPool *core.TxPool, am *accounts.Manager) *PrivateRegistarAPI { return &PrivateRegistarAPI{ config: config, be: ®istryAPIBackend{ diff --git a/console/console_test.go b/console/console_test.go index fd3459139..77dcc198c 100644 --- a/console/console_test.go +++ b/console/console_test.go @@ -28,10 +28,10 @@ import ( "time" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/internal/jsre" "github.com/ethereum/go-ethereum/node" + "github.com/ethereum/go-ethereum/params" ) const ( @@ -97,7 +97,7 @@ func newTester(t *testing.T, confOverride func(*eth.Config)) *tester { t.Fatalf("failed to create node: %v", err) } ethConf := ð.Config{ - ChainConfig: &core.ChainConfig{HomesteadBlock: new(big.Int)}, + ChainConfig: ¶ms.ChainConfig{HomesteadBlock: new(big.Int)}, Etherbase: common.HexToAddress(testAddress), PowTest: true, } diff --git a/core/bench_test.go b/core/bench_test.go index 344e7e3c5..8fe4d41de 100644 --- a/core/bench_test.go +++ b/core/bench_test.go @@ -163,12 +163,12 @@ func benchInsertChain(b *testing.B, disk bool, gen func(int, *BlockGen)) { // Generate a chain of b.N blocks using the supplied block // generator function. genesis := WriteGenesisBlockForTesting(db, GenesisAccount{benchRootAddr, benchRootFunds}) - chain, _ := GenerateChain(nil, genesis, db, b.N, gen) + chain, _ := GenerateChain(params.TestChainConfig, genesis, db, b.N, gen) // Time the insertion of the new chain. // State and blocks are stored in the same DB. evmux := new(event.TypeMux) - chainman, _ := NewBlockChain(db, &ChainConfig{HomesteadBlock: new(big.Int)}, FakePow{}, evmux) + chainman, _ := NewBlockChain(db, ¶ms.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 1bb1a9713..388fb7b3c 100644 --- a/core/block_validator.go +++ b/core/block_validator.go @@ -41,13 +41,13 @@ var ( // // BlockValidator implements Validator. type BlockValidator struct { - config *ChainConfig // Chain configuration options - bc *BlockChain // Canonical block chain - Pow pow.PoW // Proof of work used for validating + config *params.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(config *ChainConfig, blockchain *BlockChain, pow pow.PoW) *BlockValidator { +func NewBlockValidator(config *params.ChainConfig, blockchain *BlockChain, pow pow.PoW) *BlockValidator { validator := &BlockValidator{ config: config, Pow: pow, @@ -128,7 +128,7 @@ func (v *BlockValidator) ValidateState(block, parent *types.Block, statedb *stat } // Validate the state root against the received state root and throw // an error if they don't match. - if root := statedb.IntermediateRoot(); header.Root != root { + if root := statedb.IntermediateRoot(v.config.IsEIP158(header.Number)); header.Root != root { return fmt.Errorf("invalid merkle root: header=%x computed=%x", header.Root, root) } return nil @@ -203,7 +203,7 @@ func (v *BlockValidator) ValidateHeader(header, parent *types.Header, checkPow b // Validates a header. Returns an error if the header is invalid. // // See YP section 4.3.4. "Block Header Validity" -func ValidateHeader(config *ChainConfig, pow pow.PoW, header *types.Header, parent *types.Header, checkPow, uncle bool) error { +func ValidateHeader(config *params.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)) } @@ -251,9 +251,9 @@ func ValidateHeader(config *ChainConfig, pow pow.PoW, header *types.Header, pare if err := ValidateDAOHeaderExtraData(config, header); err != nil { return err } - if config.HomesteadGasRepriceBlock != nil && config.HomesteadGasRepriceBlock.Cmp(header.Number) == 0 { - if config.HomesteadGasRepriceHash != (common.Hash{}) && config.HomesteadGasRepriceHash != header.Hash() { - return ValidationError("Homestead gas reprice fork hash mismatch: have 0x%x, want 0x%x", header.Hash(), config.HomesteadGasRepriceHash) + if config.EIP150Block != nil && config.EIP150Block.Cmp(header.Number) == 0 { + if config.EIP150Hash != (common.Hash{}) && config.EIP150Hash != header.Hash() { + return ValidationError("Homestead gas reprice fork hash mismatch: have 0x%x, want 0x%x", header.Hash(), config.EIP150Hash) } } return nil @@ -262,7 +262,7 @@ func ValidateHeader(config *ChainConfig, pow pow.PoW, header *types.Header, pare // 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(config *ChainConfig, time, parentTime uint64, parentNumber, parentDiff *big.Int) *big.Int { +func CalcDifficulty(config *params.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 { diff --git a/core/block_validator_test.go b/core/block_validator_test.go index c6daf9e7f..6fcab1e5f 100644 --- a/core/block_validator_test.go +++ b/core/block_validator_test.go @@ -27,11 +27,13 @@ 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: big.NewInt(0)} +func testChainConfig() *params.ChainConfig { + return params.TestChainConfig + //return ¶ms.ChainConfig{HomesteadBlock: big.NewInt(0)} } func proc() (Validator, *BlockChain) { @@ -51,15 +53,15 @@ func TestNumber(t *testing.T) { _, chain := proc() statedb, _ := state.New(chain.Genesis().Root(), chain.chainDb) - header := makeHeader(chain.Genesis(), statedb) - header.Number = big.NewInt(3) cfg := testChainConfig() + header := makeHeader(cfg, chain.Genesis(), statedb) + header.Number = big.NewInt(3) 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) + header = makeHeader(cfg, chain.Genesis(), statedb) 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 791a8b91d..8b93da60b 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -38,6 +38,7 @@ import ( "github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/metrics" + "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/pow" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/trie" @@ -78,7 +79,7 @@ const ( // included in the canonical one where as GetBlockByNumber always represents the // canonical chain. type BlockChain struct { - config *ChainConfig // chain & network configuration + config *params.ChainConfig // chain & network configuration hc *HeaderChain chainDb ethdb.Database @@ -113,7 +114,7 @@ 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, config *ChainConfig, pow pow.PoW, mux *event.TypeMux) (*BlockChain, error) { +func NewBlockChain(chainDb ethdb.Database, config *params.ChainConfig, pow pow.PoW, mux *event.TypeMux) (*BlockChain, error) { bodyCache, _ := lru.New(bodyCacheLimit) bodyRLPCache, _ := lru.New(bodyCacheLimit) blockCache, _ := lru.New(blockCacheLimit) @@ -924,7 +925,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, self.stateCache, self.config.VmConfig) + receipts, logs, usedGas, err := self.processor.Process(block, self.stateCache, vm.Config{}) if err != nil { reportBlock(block, err) return i, err @@ -936,7 +937,7 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) { return i, err } // Write state changes to database - _, err = self.stateCache.Commit() + _, err = self.stateCache.Commit(self.config.IsEIP158(block.Number())) if err != nil { return i, err } @@ -1309,4 +1310,4 @@ func (self *BlockChain) GetHeaderByNumber(number uint64) *types.Header { } // Config retrieves the blockchain's chain configuration. -func (self *BlockChain) Config() *ChainConfig { return self.config } +func (self *BlockChain) Config() *params.ChainConfig { return self.config } diff --git a/core/blockchain_test.go b/core/blockchain_test.go index 3c47b2fd9..968cb25ac 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -154,7 +154,7 @@ func testBlockChainImport(chain types.Blocks, blockchain *BlockChain) error { blockchain.mu.Lock() WriteTd(blockchain.chainDb, block.Hash(), block.NumberU64(), new(big.Int).Add(block.Difficulty(), blockchain.GetTdByHash(block.ParentHash()))) WriteBlock(blockchain.chainDb, block) - statedb.Commit() + statedb.Commit(false) blockchain.mu.Unlock() } return nil @@ -712,7 +712,7 @@ func TestFastVsFullChains(t *testing.T) { funds = big.NewInt(1000000000) genesis = GenesisBlockForTesting(gendb, address, funds) ) - blocks, receipts := GenerateChain(nil, genesis, gendb, 1024, func(i int, block *BlockGen) { + blocks, receipts := GenerateChain(params.TestChainConfig, genesis, gendb, 1024, func(i int, block *BlockGen) { block.SetCoinbase(common.Address{0x00}) // If the block number is multiple of 3, send a few bonus transactions to the miner @@ -795,7 +795,7 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) { genesis = GenesisBlockForTesting(gendb, address, funds) ) height := uint64(1024) - blocks, receipts := GenerateChain(nil, genesis, gendb, int(height), nil) + blocks, receipts := GenerateChain(params.TestChainConfig, genesis, gendb, int(height), nil) // Configure a subchain to roll back remove := []common.Hash{} @@ -895,7 +895,7 @@ func TestChainTxReorgs(t *testing.T) { // - futureAdd: transaction added after the reorg has already finished var pastAdd, freshAdd, futureAdd *types.Transaction - chain, _ := GenerateChain(nil, genesis, db, 3, func(i int, gen *BlockGen) { + chain, _ := GenerateChain(params.TestChainConfig, genesis, db, 3, func(i int, gen *BlockGen) { switch i { case 0: pastDrop, _ = types.NewTransaction(gen.TxNonce(addr2), addr2, big.NewInt(1000), params.TxGas, nil, nil).SignECDSA(key2) @@ -920,7 +920,7 @@ func TestChainTxReorgs(t *testing.T) { } // overwrite the old chain - chain, _ = GenerateChain(nil, genesis, db, 5, func(i int, gen *BlockGen) { + chain, _ = GenerateChain(params.TestChainConfig, genesis, db, 5, func(i int, gen *BlockGen) { switch i { case 0: pastAdd, _ = types.NewTransaction(gen.TxNonce(addr3), addr3, big.NewInt(1000), params.TxGas, nil, nil).SignECDSA(key3) @@ -990,7 +990,7 @@ func TestLogReorgs(t *testing.T) { blockchain, _ := NewBlockChain(db, testChainConfig(), FakePow{}, evmux) subs := evmux.Subscribe(RemovedLogsEvent{}) - chain, _ := GenerateChain(nil, genesis, db, 2, func(i int, gen *BlockGen) { + chain, _ := GenerateChain(params.TestChainConfig, genesis, db, 2, func(i int, gen *BlockGen) { if i == 1 { tx, err := types.NewContractCreation(gen.TxNonce(addr1), new(big.Int), big.NewInt(1000000), new(big.Int), code).SignECDSA(key1) if err != nil { @@ -1003,7 +1003,7 @@ func TestLogReorgs(t *testing.T) { t.Fatalf("failed to insert chain: %v", err) } - chain, _ = GenerateChain(nil, genesis, db, 3, func(i int, gen *BlockGen) {}) + chain, _ = GenerateChain(params.TestChainConfig, genesis, db, 3, func(i int, gen *BlockGen) {}) if _, err := blockchain.InsertChain(chain); err != nil { t.Fatalf("failed to insert forked chain: %v", err) } @@ -1025,12 +1025,12 @@ func TestReorgSideEvent(t *testing.T) { evmux := &event.TypeMux{} blockchain, _ := NewBlockChain(db, testChainConfig(), FakePow{}, evmux) - chain, _ := GenerateChain(nil, genesis, db, 3, func(i int, gen *BlockGen) {}) + chain, _ := GenerateChain(params.TestChainConfig, genesis, db, 3, func(i int, gen *BlockGen) {}) if _, err := blockchain.InsertChain(chain); err != nil { t.Fatalf("failed to insert chain: %v", err) } - replacementBlocks, _ := GenerateChain(nil, genesis, db, 4, func(i int, gen *BlockGen) { + replacementBlocks, _ := GenerateChain(params.TestChainConfig, genesis, db, 4, func(i int, gen *BlockGen) { tx, err := types.NewContractCreation(gen.TxNonce(addr1), new(big.Int), big.NewInt(1000000), new(big.Int), nil).SignECDSA(key1) if i == 2 { gen.OffsetTime(-1) @@ -1101,7 +1101,7 @@ func TestCanonicalBlockRetrieval(t *testing.T) { evmux := &event.TypeMux{} blockchain, _ := NewBlockChain(db, testChainConfig(), FakePow{}, evmux) - chain, _ := GenerateChain(nil, genesis, db, 10, func(i int, gen *BlockGen) {}) + chain, _ := GenerateChain(params.TestChainConfig, genesis, db, 10, func(i int, gen *BlockGen) {}) for i, _ := range chain { go func(block *types.Block) { diff --git a/core/chain_makers.go b/core/chain_makers.go index e3ad9cda0..e1dafb32d 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -35,8 +35,8 @@ import ( */ // MakeChainConfig returns a new ChainConfig with the ethereum default chain settings. -func MakeChainConfig() *ChainConfig { - return &ChainConfig{ +func MakeChainConfig() *params.ChainConfig { + return ¶ms.ChainConfig{ HomesteadBlock: big.NewInt(0), DAOForkBlock: nil, DAOForkSupport: true, @@ -73,6 +73,8 @@ type BlockGen struct { txs []*types.Transaction receipts []*types.Receipt uncles []*types.Header + + config *params.ChainConfig } // SetCoinbase sets the coinbase of the generated block. @@ -106,7 +108,7 @@ func (b *BlockGen) AddTx(tx *types.Transaction) { b.SetCoinbase(common.Address{}) } b.statedb.StartRecord(tx.Hash(), common.Hash{}, len(b.txs)) - receipt, _, _, err := ApplyTransaction(MakeChainConfig(), nil, b.gasPool, b.statedb, b.header, tx, b.header.GasUsed, vm.Config{}) + receipt, _, _, err := ApplyTransaction(b.config, nil, b.gasPool, b.statedb, b.header, tx, b.header.GasUsed, vm.Config{}) if err != nil { panic(err) } @@ -178,10 +180,10 @@ func (b *BlockGen) OffsetTime(seconds int64) { // Blocks created by GenerateChain do not contain valid proof of work // values. Inserting them into BlockChain requires use of FakePow or // a similar non-validating proof of work implementation. -func GenerateChain(config *ChainConfig, parent *types.Block, db ethdb.Database, n int, gen func(int, *BlockGen)) ([]*types.Block, []types.Receipts) { +func GenerateChain(config *params.ChainConfig, parent *types.Block, db ethdb.Database, n int, gen func(int, *BlockGen)) ([]*types.Block, []types.Receipts) { blocks, receipts := make(types.Blocks, n), make([]types.Receipts, n) genblock := func(i int, h *types.Header, statedb *state.StateDB) (*types.Block, types.Receipts) { - b := &BlockGen{parent: parent, i: i, chain: blocks, header: h, statedb: statedb} + b := &BlockGen{parent: parent, i: i, chain: blocks, header: h, statedb: statedb, config: config} // Mutate the state and block according to any hard-fork specs if config == nil { @@ -203,7 +205,7 @@ func GenerateChain(config *ChainConfig, parent *types.Block, db ethdb.Database, gen(i, b) } AccumulateRewards(statedb, h, b.uncles) - root, err := statedb.Commit() + root, err := statedb.Commit(config.IsEIP158(h.Number)) if err != nil { panic(fmt.Sprintf("state write error: %v", err)) } @@ -215,7 +217,7 @@ func GenerateChain(config *ChainConfig, parent *types.Block, db ethdb.Database, if err != nil { panic(err) } - header := makeHeader(parent, statedb) + header := makeHeader(config, parent, statedb) block, receipt := genblock(i, header, statedb) blocks[i] = block receipts[i] = receipt @@ -224,7 +226,7 @@ func GenerateChain(config *ChainConfig, parent *types.Block, db ethdb.Database, return blocks, receipts } -func makeHeader(parent *types.Block, state *state.StateDB) *types.Header { +func makeHeader(config *params.ChainConfig, parent *types.Block, state *state.StateDB) *types.Header { var time *big.Int if parent.Time() == nil { time = big.NewInt(10) @@ -232,7 +234,7 @@ func makeHeader(parent *types.Block, state *state.StateDB) *types.Header { time = new(big.Int).Add(parent.Time(), big.NewInt(10)) // block time is fixed at 10 seconds } return &types.Header{ - Root: state.IntermediateRoot(), + Root: state.IntermediateRoot(config.IsEIP158(parent.Number())), ParentHash: parent.Hash(), Coinbase: parent.Coinbase(), Difficulty: CalcDifficulty(MakeChainConfig(), time.Uint64(), new(big.Int).Sub(time, big.NewInt(10)).Uint64(), parent.Number(), parent.Difficulty()), @@ -283,7 +285,7 @@ func makeHeaderChain(parent *types.Header, n int, db ethdb.Database, seed int) [ // makeBlockChain creates a deterministic chain of blocks rooted at parent. func makeBlockChain(parent *types.Block, n int, db ethdb.Database, seed int) []*types.Block { - blocks, _ := GenerateChain(nil, parent, db, n, func(i int, b *BlockGen) { + blocks, _ := GenerateChain(params.TestChainConfig, parent, db, n, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{0: byte(seed), 19: byte(i)}) }) return blocks diff --git a/core/chain_makers_test.go b/core/chain_makers_test.go index 5fc255c71..b297e671e 100644 --- a/core/chain_makers_test.go +++ b/core/chain_makers_test.go @@ -41,13 +41,16 @@ func ExampleGenerateChain() { db, _ = ethdb.NewMemDatabase() ) + chainConfig := ¶ms.ChainConfig{ + HomesteadBlock: new(big.Int), + } // Ensure that key1 has some funds in the genesis block. genesis := WriteGenesisBlockForTesting(db, GenesisAccount{addr1, big.NewInt(1000000)}) // This call generates a chain of 5 blocks. The function runs for // each block and adds different features to gen based on the // block index. - chain, _ := GenerateChain(nil, genesis, db, 5, func(i int, gen *BlockGen) { + chain, _ := GenerateChain(chainConfig, genesis, db, 5, func(i int, gen *BlockGen) { switch i { case 0: // In block 1, addr1 sends addr2 some ether. @@ -77,7 +80,7 @@ func ExampleGenerateChain() { // Import the chain. This runs all block validation rules. evmux := &event.TypeMux{} - blockchain, _ := NewBlockChain(db, MakeChainConfig(), FakePow{}, evmux) + blockchain, _ := NewBlockChain(db, chainConfig, FakePow{}, evmux) if i, err := blockchain.InsertChain(chain); err != nil { fmt.Printf("insert error (block %d): %v\n", chain[i].NumberU64(), err) return diff --git a/core/chain_pow_test.go b/core/chain_pow_test.go index 2e26c8211..1400b166f 100644 --- a/core/chain_pow_test.go +++ b/core/chain_pow_test.go @@ -25,6 +25,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/pow" ) @@ -60,7 +61,7 @@ func TestPowVerification(t *testing.T) { var ( testdb, _ = ethdb.NewMemDatabase() genesis = GenesisBlockForTesting(testdb, common.Address{}, new(big.Int)) - blocks, _ = GenerateChain(nil, genesis, testdb, 8, nil) + blocks, _ = GenerateChain(params.TestChainConfig, genesis, testdb, 8, nil) ) headers := make([]*types.Header, len(blocks)) for i, block := range blocks { @@ -115,7 +116,7 @@ func testPowConcurrentVerification(t *testing.T, threads int) { var ( testdb, _ = ethdb.NewMemDatabase() genesis = GenesisBlockForTesting(testdb, common.Address{}, new(big.Int)) - blocks, _ = GenerateChain(nil, genesis, testdb, 8, nil) + blocks, _ = GenerateChain(params.TestChainConfig, genesis, testdb, 8, nil) ) headers := make([]*types.Header, len(blocks)) for i, block := range blocks { @@ -186,7 +187,7 @@ func testPowConcurrentAbortion(t *testing.T, threads int) { var ( testdb, _ = ethdb.NewMemDatabase() genesis = GenesisBlockForTesting(testdb, common.Address{}, new(big.Int)) - blocks, _ = GenerateChain(nil, genesis, testdb, 1024, nil) + blocks, _ = GenerateChain(params.TestChainConfig, genesis, testdb, 1024, nil) ) headers := make([]*types.Header, len(blocks)) for i, block := range blocks { diff --git a/core/dao.go b/core/dao.go index e315c9884..1260c310a 100644 --- a/core/dao.go +++ b/core/dao.go @@ -33,7 +33,7 @@ import ( // with the fork specific extra-data set // b) if the node is pro-fork, require blocks in the specific range to have the // unique extra-data set. -func ValidateDAOHeaderExtraData(config *ChainConfig, header *types.Header) error { +func ValidateDAOHeaderExtraData(config *params.ChainConfig, header *types.Header) error { // Short circuit validation if the node doesn't care about the DAO fork if config.DAOForkBlock == nil { return nil diff --git a/core/dao_test.go b/core/dao_test.go index 0830b1231..f461131f4 100644 --- a/core/dao_test.go +++ b/core/dao_test.go @@ -33,17 +33,17 @@ func TestDAOForkRangeExtradata(t *testing.T) { // Generate a common prefix for both pro-forkers and non-forkers db, _ := ethdb.NewMemDatabase() genesis := WriteGenesisBlockForTesting(db) - prefix, _ := GenerateChain(nil, genesis, db, int(forkBlock.Int64()-1), func(i int, gen *BlockGen) {}) + prefix, _ := GenerateChain(params.TestChainConfig, genesis, db, int(forkBlock.Int64()-1), func(i int, gen *BlockGen) {}) // Create the concurrent, conflicting two nodes proDb, _ := ethdb.NewMemDatabase() WriteGenesisBlockForTesting(proDb) - proConf := &ChainConfig{HomesteadBlock: big.NewInt(0), DAOForkBlock: forkBlock, DAOForkSupport: true} + proConf := ¶ms.ChainConfig{HomesteadBlock: big.NewInt(0), DAOForkBlock: forkBlock, DAOForkSupport: true} proBc, _ := NewBlockChain(proDb, proConf, new(FakePow), new(event.TypeMux)) conDb, _ := ethdb.NewMemDatabase() WriteGenesisBlockForTesting(conDb) - conConf := &ChainConfig{HomesteadBlock: big.NewInt(0), DAOForkBlock: forkBlock, DAOForkSupport: false} + conConf := ¶ms.ChainConfig{HomesteadBlock: big.NewInt(0), DAOForkBlock: forkBlock, DAOForkSupport: false} conBc, _ := NewBlockChain(conDb, conConf, new(FakePow), new(event.TypeMux)) if _, err := proBc.InsertChain(prefix); err != nil { diff --git a/core/database_util.go b/core/database_util.go index 0fb593554..84669de35 100644 --- a/core/database_util.go +++ b/core/database_util.go @@ -20,6 +20,7 @@ import ( "bytes" "encoding/binary" "encoding/json" + "errors" "fmt" "math/big" @@ -28,6 +29,7 @@ import ( "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" ) @@ -59,6 +61,8 @@ var ( oldBlockNumPrefix = []byte("block-num-") oldBlockReceiptsPrefix = []byte("receipts-block-") oldBlockHashPrefix = []byte("block-hash-") // [deprecated by the header/block split, remove eventually] + + ChainConfigNotFoundErr = errors.New("ChainConfig not found") // general config not found error ) // encodeBlockNumber encodes a block number as big endian uint64 @@ -600,7 +604,7 @@ func WriteBlockChainVersion(db ethdb.Database, vsn int) { } // WriteChainConfig writes the chain config settings to the database. -func WriteChainConfig(db ethdb.Database, hash common.Hash, cfg *ChainConfig) error { +func WriteChainConfig(db ethdb.Database, hash common.Hash, cfg *params.ChainConfig) error { // short circuit and ignore if nil config. GetChainConfig // will return a default. if cfg == nil { @@ -616,13 +620,13 @@ func WriteChainConfig(db ethdb.Database, hash common.Hash, cfg *ChainConfig) err } // GetChainConfig will fetch the network settings based on the given hash. -func GetChainConfig(db ethdb.Database, hash common.Hash) (*ChainConfig, error) { +func GetChainConfig(db ethdb.Database, hash common.Hash) (*params.ChainConfig, error) { jsonChainConfig, _ := db.Get(append(configPrefix, hash[:]...)) if len(jsonChainConfig) == 0 { return nil, ChainConfigNotFoundErr } - var config ChainConfig + var config params.ChainConfig if err := json.Unmarshal(jsonChainConfig, &config); err != nil { return nil, err } diff --git a/core/database_util_test.go b/core/database_util_test.go index ab14ae156..83750aa60 100644 --- a/core/database_util_test.go +++ b/core/database_util_test.go @@ -30,6 +30,7 @@ import ( "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto/sha3" "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" ) @@ -75,7 +76,7 @@ func TestCalcDifficulty(t *testing.T) { t.Fatal(err) } - config := &ChainConfig{HomesteadBlock: big.NewInt(1150000)} + config := ¶ms.ChainConfig{HomesteadBlock: big.NewInt(1150000)} for name, test := range tests { number := new(big.Int).Sub(test.CurrentBlocknumber, big.NewInt(1)) diff := CalcDifficulty(config, test.CurrentTimestamp, test.ParentTimestamp, number, test.ParentDifficulty) @@ -562,7 +563,7 @@ func TestMipmapChain(t *testing.T) { defer db.Close() genesis := WriteGenesisBlockForTesting(db, GenesisAccount{addr, big.NewInt(1000000)}) - chain, receipts := GenerateChain(nil, genesis, db, 1010, func(i int, gen *BlockGen) { + chain, receipts := GenerateChain(params.TestChainConfig, genesis, db, 1010, func(i int, gen *BlockGen) { var receipts types.Receipts switch i { case 1: diff --git a/core/execution.go b/core/execution.go index 1cb507ee7..576e05851 100644 --- a/core/execution.go +++ b/core/execution.go @@ -27,40 +27,93 @@ import ( // Call executes within the given contract func Call(env vm.Environment, caller vm.ContractRef, addr common.Address, input []byte, gas, gasPrice, value *big.Int) (ret []byte, err error) { - ret, _, err = exec(env, caller, &addr, &addr, env.Db().GetCodeHash(addr), input, env.Db().GetCode(addr), gas, gasPrice, value) + // Depth check execution. Fail if we're trying to execute above the + // limit. + if env.Depth() > int(params.CallCreateDepth.Int64()) { + caller.ReturnGas(gas, gasPrice) + + return nil, vm.DepthError + } + if !env.CanTransfer(caller.Address(), value) { + caller.ReturnGas(gas, gasPrice) + + return nil, ValueTransferErr("insufficient funds to transfer value. Req %v, has %v", value, env.Db().GetBalance(caller.Address())) + } + + snapshotPreTransfer := env.SnapshotDatabase() + var ( + from = env.Db().GetAccount(caller.Address()) + to vm.Account + ) + if !env.Db().Exist(addr) { + if vm.Precompiled[addr.Str()] == nil && env.ChainConfig().IsEIP158(env.BlockNumber()) && value.BitLen() == 0 { + caller.ReturnGas(gas, gasPrice) + return nil, nil + } + + to = env.Db().CreateAccount(addr) + } else { + to = env.Db().GetAccount(addr) + } + env.Transfer(from, to, value) + + // initialise a new contract and set the code that is to be used by the + // EVM. The contract is a scoped environment for this execution context + // only. + contract := vm.NewContract(caller, to, value, gas, gasPrice) + contract.SetCallCode(&addr, env.Db().GetCodeHash(addr), env.Db().GetCode(addr)) + defer contract.Finalise() + + ret, err = env.Vm().Run(contract, input) + // 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 { + contract.UseGas(contract.Gas) + + env.RevertToSnapshot(snapshotPreTransfer) + } return ret, err } // CallCode executes the given address' code as the given contract address func CallCode(env vm.Environment, caller vm.ContractRef, addr common.Address, input []byte, gas, gasPrice, value *big.Int) (ret []byte, err error) { - callerAddr := caller.Address() - ret, _, err = exec(env, caller, &callerAddr, &addr, env.Db().GetCodeHash(addr), input, env.Db().GetCode(addr), gas, gasPrice, value) - return ret, err -} + // Depth check execution. Fail if we're trying to execute above the + // limit. + if env.Depth() > int(params.CallCreateDepth.Int64()) { + caller.ReturnGas(gas, gasPrice) -// DelegateCall is equivalent to CallCode except that sender and value propagates from parent scope to child scope -func DelegateCall(env vm.Environment, caller vm.ContractRef, addr common.Address, input []byte, gas, gasPrice *big.Int) (ret []byte, err error) { - callerAddr := caller.Address() - originAddr := env.Origin() - callerValue := caller.Value() - ret, _, err = execDelegateCall(env, caller, &originAddr, &callerAddr, &addr, env.Db().GetCodeHash(addr), input, env.Db().GetCode(addr), gas, gasPrice, callerValue) - return ret, err -} + return nil, vm.DepthError + } + if !env.CanTransfer(caller.Address(), value) { + caller.ReturnGas(gas, gasPrice) -// Create creates a new contract with the given code -func Create(env vm.Environment, caller vm.ContractRef, code []byte, gas, gasPrice, value *big.Int) (ret []byte, address common.Address, err error) { - ret, address, err = exec(env, caller, nil, nil, crypto.Keccak256Hash(code), nil, code, gas, gasPrice, value) - // Here we get an error if we run into maximum stack depth, - // See: https://github.com/ethereum/yellowpaper/pull/131 - // and YP definitions for CREATE instruction + return nil, ValueTransferErr("insufficient funds to transfer value. Req %v, has %v", value, env.Db().GetBalance(caller.Address())) + } + + var ( + snapshotPreTransfer = env.SnapshotDatabase() + to = env.Db().GetAccount(caller.Address()) + ) + // initialise a new contract and set the code that is to be used by the + // EVM. The contract is a scoped environment for this execution context + // only. + contract := vm.NewContract(caller, to, value, gas, gasPrice) + contract.SetCallCode(&addr, env.Db().GetCodeHash(addr), env.Db().GetCode(addr)) + defer contract.Finalise() + + ret, err = env.Vm().Run(contract, input) if err != nil { - return nil, address, err + contract.UseGas(contract.Gas) + + env.RevertToSnapshot(snapshotPreTransfer) } - return ret, address, err + + return ret, err } -func exec(env vm.Environment, caller vm.ContractRef, address, codeAddr *common.Address, codeHash common.Hash, input, code []byte, gas, gasPrice, value *big.Int) (ret []byte, addr common.Address, err error) { - evm := env.Vm() +// Create creates a new contract with the given code +func Create(env vm.Environment, caller vm.ContractRef, code []byte, gas, gasPrice, value *big.Int) (ret []byte, address common.Address, err error) { // Depth check execution. Fail if we're trying to execute above the // limit. if env.Depth() > int(params.CallCreateDepth.Int64()) { @@ -68,36 +121,24 @@ func exec(env vm.Environment, caller vm.ContractRef, address, codeAddr *common.A return nil, common.Address{}, vm.DepthError } - if !env.CanTransfer(caller.Address(), value) { caller.ReturnGas(gas, gasPrice) return nil, common.Address{}, ValueTransferErr("insufficient funds to transfer value. Req %v, has %v", value, env.Db().GetBalance(caller.Address())) } - var createAccount bool - if address == nil { - // Create a new account on the state - nonce := env.Db().GetNonce(caller.Address()) - env.Db().SetNonce(caller.Address(), nonce+1) - addr = crypto.CreateAddress(caller.Address(), nonce) - address = &addr - createAccount = true - } + // Create a new account on the state + nonce := env.Db().GetNonce(caller.Address()) + env.Db().SetNonce(caller.Address(), nonce+1) snapshotPreTransfer := env.SnapshotDatabase() var ( + addr = crypto.CreateAddress(caller.Address(), nonce) from = env.Db().GetAccount(caller.Address()) - to vm.Account + to = env.Db().CreateAccount(addr) ) - if createAccount { - to = env.Db().CreateAccount(*address) - } else { - if !env.Db().Exist(*address) { - to = env.Db().CreateAccount(*address) - } else { - to = env.Db().GetAccount(*address) - } + if env.ChainConfig().IsEIP158(env.BlockNumber()) { + env.Db().SetNonce(addr, 1) } env.Transfer(from, to, value) @@ -105,19 +146,19 @@ func exec(env vm.Environment, caller vm.ContractRef, address, codeAddr *common.A // EVM. The contract is a scoped environment for this execution context // only. contract := vm.NewContract(caller, to, value, gas, gasPrice) - contract.SetCallCode(codeAddr, codeHash, code) + contract.SetCallCode(&addr, crypto.Keccak256Hash(code), code) defer contract.Finalise() - ret, err = evm.Run(contract, input) + ret, err = env.Vm().Run(contract, nil) // if the contract creation ran successfully and no errors were returned // calculate the gas required to store the code. If the code could not // be stored due to not enough gas set an error and let it be handled // by the error checking condition below. - if err == nil && createAccount { + if err == nil { dataGas := big.NewInt(int64(len(ret))) dataGas.Mul(dataGas, params.CreateDataGas) if contract.UseGas(dataGas) { - env.Db().SetCode(*address, ret) + env.Db().SetCode(addr, ret) } else { err = vm.CodeStoreOutOfGasError } @@ -126,46 +167,45 @@ 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 && (env.RuleSet().IsHomestead(env.BlockNumber()) || err != vm.CodeStoreOutOfGasError) { + if err != nil && (env.ChainConfig().IsHomestead(env.BlockNumber()) || err != vm.CodeStoreOutOfGasError) { contract.UseGas(contract.Gas) env.RevertToSnapshot(snapshotPreTransfer) + + // Nothing should be returned when an error is thrown. + return nil, addr, err } return ret, addr, err } -func execDelegateCall(env vm.Environment, caller vm.ContractRef, originAddr, toAddr, codeAddr *common.Address, codeHash common.Hash, input, code []byte, gas, gasPrice, value *big.Int) (ret []byte, addr common.Address, err error) { - evm := env.Vm() +// DelegateCall is equivalent to CallCode except that sender and value propagates from parent scope to child scope +func DelegateCall(env vm.Environment, caller vm.ContractRef, addr common.Address, input []byte, gas, gasPrice *big.Int) (ret []byte, err error) { // Depth check execution. Fail if we're trying to execute above the // limit. if env.Depth() > int(params.CallCreateDepth.Int64()) { caller.ReturnGas(gas, gasPrice) - return nil, common.Address{}, vm.DepthError + return nil, vm.DepthError } - snapshot := env.SnapshotDatabase() - - var to vm.Account - if !env.Db().Exist(*toAddr) { - to = env.Db().CreateAccount(*toAddr) - } else { - to = env.Db().GetAccount(*toAddr) - } + var ( + snapshot = env.SnapshotDatabase() + to = env.Db().GetAccount(caller.Address()) + ) // Iinitialise a new contract and make initialise the delegate values - contract := vm.NewContract(caller, to, value, gas, gasPrice).AsDelegate() - contract.SetCallCode(codeAddr, codeHash, code) + contract := vm.NewContract(caller, to, caller.Value(), gas, gasPrice).AsDelegate() + contract.SetCallCode(&addr, env.Db().GetCodeHash(addr), env.Db().GetCode(addr)) defer contract.Finalise() - ret, err = evm.Run(contract, input) + ret, err = env.Vm().Run(contract, input) if err != nil { contract.UseGas(contract.Gas) env.RevertToSnapshot(snapshot) } - return ret, addr, err + return ret, err } // generic transfer method diff --git a/core/genesis.go b/core/genesis.go index 637b63320..8509f664d 100644 --- a/core/genesis.go +++ b/core/genesis.go @@ -43,7 +43,7 @@ func WriteGenesisBlock(chainDb ethdb.Database, reader io.Reader) (*types.Block, } var genesis struct { - ChainConfig *ChainConfig `json:"config"` + ChainConfig *params.ChainConfig `json:"config"` Nonce string Timestamp string ParentHash string @@ -73,7 +73,7 @@ func WriteGenesisBlock(chainDb ethdb.Database, reader io.Reader) (*types.Block, statedb.SetState(address, common.HexToHash(key), common.HexToHash(value)) } } - root, stateBatch := statedb.CommitBatch() + root, stateBatch := statedb.CommitBatch(false) difficulty := common.String2Big(genesis.Difficulty) block := types.NewBlock(&types.Header{ @@ -128,7 +128,7 @@ func GenesisBlockForTesting(db ethdb.Database, addr common.Address, balance *big statedb, _ := state.New(common.Hash{}, db) obj := statedb.GetOrNewStateObject(addr) obj.SetBalance(balance) - root, err := statedb.Commit() + root, err := statedb.Commit(false) if err != nil { panic(fmt.Sprintf("cannot write state: %v", err)) } diff --git a/core/headerchain.go b/core/headerchain.go index 8ca06d9b4..c53694571 100644 --- a/core/headerchain.go +++ b/core/headerchain.go @@ -32,6 +32,7 @@ import ( "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/pow" "github.com/hashicorp/golang-lru" ) @@ -48,7 +49,7 @@ const ( // It is not thread safe either, the encapsulating chain structures should do // the necessary mutex locking/unlocking. type HeaderChain struct { - config *ChainConfig + config *params.ChainConfig chainDb ethdb.Database genesisHeader *types.Header @@ -73,7 +74,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, config *ChainConfig, getValidator getHeaderValidatorFn, procInterrupt func() bool) (*HeaderChain, error) { +func NewHeaderChain(chainDb ethdb.Database, config *params.ChainConfig, getValidator getHeaderValidatorFn, procInterrupt func() bool) (*HeaderChain, error) { headerCache, _ := lru.New(headerCacheLimit) tdCache, _ := lru.New(tdCacheLimit) numberCache, _ := lru.New(numberCacheLimit) @@ -490,13 +491,13 @@ func (hc *HeaderChain) SetGenesis(head *types.Header) { // // headerValidator implements HeaderValidator. type headerValidator struct { - config *ChainConfig + config *params.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(config *ChainConfig, chain *HeaderChain, pow pow.PoW) HeaderValidator { +func NewHeaderValidator(config *params.ChainConfig, chain *HeaderChain, pow pow.PoW) HeaderValidator { return &headerValidator{ config: config, Pow: pow, diff --git a/core/state/state_object.go b/core/state/state_object.go index edb073173..2b5dfea7d 100644 --- a/core/state/state_object.go +++ b/core/state/state_object.go @@ -91,6 +91,11 @@ type StateObject struct { onDirty func(addr common.Address) // Callback method to mark a state object newly dirty } +// empty returns whether the account is considered empty. +func (s *StateObject) empty() bool { + return s.data.Nonce == 0 && s.data.Balance.BitLen() == 0 && bytes.Equal(s.data.CodeHash, emptyCodeHash) +} + // Account is the Ethereum consensus representation of accounts. // These objects are stored in the main account trie. type Account struct { @@ -221,8 +226,12 @@ func (self *StateObject) CommitTrie(db trie.Database, dbw trie.DatabaseWriter) e return err } +// AddBalance removes amount from c's balance. +// It is used to add funds to the destination account of a transfer. func (c *StateObject) AddBalance(amount *big.Int) { - if amount.Cmp(common.Big0) == 0 { + // EIP158: We must check emptiness for the objects such that the account + // clearing (0,0,0 objects) can take effect. + if amount.Cmp(common.Big0) == 0 && !c.empty() { return } c.SetBalance(new(big.Int).Add(c.Balance(), amount)) @@ -232,6 +241,8 @@ func (c *StateObject) AddBalance(amount *big.Int) { } } +// SubBalance removes amount from c's balance. +// It is used to remove funds from the origin account of a transfer. func (c *StateObject) SubBalance(amount *big.Int) { if amount.Cmp(common.Big0) == 0 { return diff --git a/core/state/state_test.go b/core/state/state_test.go index f188bc271..435d1d829 100644 --- a/core/state/state_test.go +++ b/core/state/state_test.go @@ -48,7 +48,7 @@ func (s *StateSuite) TestDump(c *checker.C) { // write some of them to the trie s.state.updateStateObject(obj1) s.state.updateStateObject(obj2) - s.state.Commit() + s.state.Commit(false) // check that dump contains the state objects that are in trie got := string(s.state.Dump()) @@ -100,7 +100,7 @@ func TestNull(t *testing.T) { //value := common.FromHex("0x823140710bf13990e4500136726d8b55") var value common.Hash state.SetState(address, common.Hash{}, value) - state.Commit() + state.Commit(false) value = state.GetState(address, common.Hash{}) if !common.EmptyHash(value) { t.Errorf("expected empty hash. got %x", value) @@ -160,7 +160,7 @@ func TestSnapshot2(t *testing.T) { so0.deleted = false state.setStateObject(so0) - root, _ := state.Commit() + root, _ := state.Commit(false) state.Reset(root) // and one with deleted == true diff --git a/core/state/statedb.go b/core/state/statedb.go index ae106e03b..1c4af0295 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -213,6 +213,13 @@ func (self *StateDB) Exist(addr common.Address) bool { return self.GetStateObject(addr) != nil } +// Empty returns whether the state object is either non-existant +// or empty according to the EIP161 specification (balance = nonce = code = 0) +func (self *StateDB) Empty(addr common.Address) bool { + so := self.GetStateObject(addr) + return so == nil || so.empty() +} + func (self *StateDB) GetAccount(addr common.Address) vm.Account { return self.GetStateObject(addr) } @@ -516,10 +523,10 @@ func (self *StateDB) GetRefund() *big.Int { // IntermediateRoot computes the current root hash of the state trie. // It is called in between transactions to get the root hash that // goes into transaction receipts. -func (s *StateDB) IntermediateRoot() common.Hash { +func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash { for addr, _ := range s.stateObjectsDirty { stateObject := s.stateObjects[addr] - if stateObject.suicided { + if stateObject.suicided || (deleteEmptyObjects && stateObject.empty()) { s.deleteStateObject(stateObject) } else { stateObject.updateRoot(s.db) @@ -553,17 +560,17 @@ func (s *StateDB) DeleteSuicides() { } // Commit commits all state changes to the database. -func (s *StateDB) Commit() (root common.Hash, err error) { - root, batch := s.CommitBatch() +func (s *StateDB) Commit(deleteEmptyObjects bool) (root common.Hash, err error) { + root, batch := s.CommitBatch(deleteEmptyObjects) return root, batch.Write() } // CommitBatch commits all state changes to a write batch but does not // execute the batch. It is used to validate state changes against // the root hash stored in a block. -func (s *StateDB) CommitBatch() (root common.Hash, batch ethdb.Batch) { +func (s *StateDB) CommitBatch(deleteEmptyObjects bool) (root common.Hash, batch ethdb.Batch) { batch = s.db.NewBatch() - root, _ = s.commit(batch) + root, _ = s.commit(batch, deleteEmptyObjects) glog.V(logger.Debug).Infof("Trie cache stats: %d misses, %d unloads", trie.CacheMisses(), trie.CacheUnloads()) return root, batch @@ -575,16 +582,18 @@ func (s *StateDB) clearJournalAndRefund() { s.refund = new(big.Int) } -func (s *StateDB) commit(dbw trie.DatabaseWriter) (root common.Hash, err error) { +func (s *StateDB) commit(dbw trie.DatabaseWriter, deleteEmptyObjects bool) (root common.Hash, err error) { defer s.clearJournalAndRefund() // Commit objects to the trie. for addr, stateObject := range s.stateObjects { - if stateObject.suicided { + _, isDirty := s.stateObjectsDirty[addr] + switch { + case stateObject.suicided || (isDirty && deleteEmptyObjects && stateObject.empty()): // If the object has been removed, don't bother syncing it // and just mark it for deletion in the trie. s.deleteStateObject(stateObject) - } else if _, ok := s.stateObjectsDirty[addr]; ok { + case isDirty: // Write any contract code associated with the state object if stateObject.code != nil && stateObject.dirtyCode { if err := dbw.Put(stateObject.CodeHash(), stateObject.code); err != nil { diff --git a/core/state/statedb_test.go b/core/state/statedb_test.go index 5d041c740..a44818b7c 100644 --- a/core/state/statedb_test.go +++ b/core/state/statedb_test.go @@ -51,7 +51,7 @@ func TestUpdateLeaks(t *testing.T) { if i%3 == 0 { state.SetCode(addr, []byte{i, i, i, i, i}) } - state.IntermediateRoot() + state.IntermediateRoot(false) } // Ensure that no data was leaked into the database for _, key := range db.Keys() { @@ -86,7 +86,7 @@ func TestIntermediateLeaks(t *testing.T) { modify(transState, common.Address{byte(i)}, i, 0) } // Write modifications to trie. - transState.IntermediateRoot() + transState.IntermediateRoot(false) // Overwrite all the data with new values in the transient database. for i := byte(0); i < 255; i++ { @@ -95,10 +95,10 @@ func TestIntermediateLeaks(t *testing.T) { } // Commit and cross check the databases. - if _, err := transState.Commit(); err != nil { + if _, err := transState.Commit(false); err != nil { t.Fatalf("failed to commit transition state: %v", err) } - if _, err := finalState.Commit(); err != nil { + if _, err := finalState.Commit(false); err != nil { t.Fatalf("failed to commit final state: %v", err) } for _, key := range finalDb.Keys() { diff --git a/core/state/sync_test.go b/core/state/sync_test.go index f5390d80f..8111320e6 100644 --- a/core/state/sync_test.go +++ b/core/state/sync_test.go @@ -60,7 +60,7 @@ func makeTestState() (ethdb.Database, common.Hash, []*testAccount) { state.updateStateObject(obj) accounts = append(accounts, acc) } - root, _ := state.Commit() + root, _ := state.Commit(false) // Return the generated state return db, root, accounts diff --git a/core/state_processor.go b/core/state_processor.go index fd8e9762e..9d7840062 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -25,6 +25,7 @@ import ( "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/params" ) var ( @@ -37,12 +38,12 @@ var ( // // StateProcessor implements Processor. type StateProcessor struct { - config *ChainConfig + config *params.ChainConfig bc *BlockChain } // NewStateProcessor initialises a new StateProcessor. -func NewStateProcessor(config *ChainConfig, bc *BlockChain) *StateProcessor { +func NewStateProcessor(config *params.ChainConfig, bc *BlockChain) *StateProcessor { return &StateProcessor{ config: config, bc: bc, @@ -89,7 +90,7 @@ 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(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) { +func ApplyTransaction(config *params.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 @@ -97,7 +98,7 @@ func ApplyTransaction(config *ChainConfig, bc *BlockChain, gp *GasPool, statedb // Update the state with pending changes usedGas.Add(usedGas, gas) - receipt := types.NewReceipt(statedb.IntermediateRoot().Bytes(), usedGas) + receipt := types.NewReceipt(statedb.IntermediateRoot(config.IsEIP158(header.Number)).Bytes(), usedGas) receipt.TxHash = tx.Hash() receipt.GasUsed = new(big.Int).Set(gas) if MessageCreatesContract(tx) { diff --git a/core/state_transition.go b/core/state_transition.go index 9e6b2f567..2b9d1c6d1 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -139,7 +139,7 @@ func (self *StateTransition) from() (vm.Account, error) { f common.Address err error ) - if self.env.RuleSet().IsHomestead(self.env.BlockNumber()) { + if self.env.ChainConfig().IsHomestead(self.env.BlockNumber()) { f, err = self.msg.From() } else { f, err = self.msg.FromFrontier() @@ -234,7 +234,7 @@ func (self *StateTransition) TransitionDb() (ret []byte, requiredGas, usedGas *b msg := self.msg sender, _ := self.from() // err checked in preCheck - homestead := self.env.RuleSet().IsHomestead(self.env.BlockNumber()) + homestead := self.env.ChainConfig().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 419d9945e..f1e5f0bdd 100644 --- a/core/tx_pool.go +++ b/core/tx_pool.go @@ -31,6 +31,7 @@ import ( "github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/metrics" + "github.com/ethereum/go-ethereum/params" "gopkg.in/karalabe/cookiejar.v2/collections/prque" ) @@ -83,7 +84,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 + config *params.ChainConfig currentState stateFn // The state function which will allow us to do some pre checks pendingState *state.ManagedState gasLimit func() *big.Int // The current gas limit function callback @@ -104,7 +105,7 @@ type TxPool struct { homestead bool } -func NewTxPool(config *ChainConfig, eventMux *event.TypeMux, currentStateFn stateFn, gasLimitFn func() *big.Int) *TxPool { +func NewTxPool(config *params.ChainConfig, eventMux *event.TypeMux, currentStateFn stateFn, gasLimitFn func() *big.Int) *TxPool { pool := &TxPool{ config: config, pending: make(map[common.Address]*txList), diff --git a/core/vm/environment.go b/core/vm/environment.go index f8996e648..e97c1e58c 100644 --- a/core/vm/environment.go +++ b/core/vm/environment.go @@ -23,20 +23,11 @@ import ( "github.com/ethereum/go-ethereum/params" ) -// 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 - // GasTable returns the gas prices for this phase, which is based on - // block number passed in. - GasTable(*big.Int) params.GasTable -} - // Environment is an EVM requirement and helper which allows access to outside // information such as states. type Environment interface { // The current ruleset - RuleSet() RuleSet + ChainConfig() *params.ChainConfig // The state database Db() Database // Creates a restorable snapshot @@ -115,6 +106,9 @@ type Database interface { // Exist reports whether the given account exists in state. // Notably this should also return true for suicided accounts. Exist(common.Address) bool + // Empty returns whether the given account is empty. Empty + // is defined according to EIP161 (balance = nonce = code = 0). + Empty(common.Address) bool } // Account represents a contract or basic ethereum account. diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 94a8c6348..4f98953b5 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -515,7 +515,7 @@ func opCreate(instr instruction, pc *uint64, env Environment, contract *Contract input = memory.Get(offset.Int64(), size.Int64()) gas = new(big.Int).Set(contract.Gas) ) - if env.RuleSet().GasTable(env.BlockNumber()).CreateBySuicide != nil { + if env.ChainConfig().IsEIP150(env.BlockNumber()) { gas.Div(gas, n64) gas = gas.Sub(contract.Gas, gas) } @@ -526,7 +526,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 env.RuleSet().IsHomestead(env.BlockNumber()) && suberr == CodeStoreOutOfGasError { + if env.ChainConfig().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 55d2e0477..b75558d39 100644 --- a/core/vm/jit.go +++ b/core/vm/jit.go @@ -319,7 +319,7 @@ func runProgram(program *Program, pcstart uint64, mem *Memory, stack *Stack, env }() } - homestead := env.RuleSet().IsHomestead(env.BlockNumber()) + homestead := env.ChainConfig().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 a6de710e1..f9c0e8fa2 100644 --- a/core/vm/jit_test.go +++ b/core/vm/jit_test.go @@ -23,6 +23,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/params" ) const maxRun = 1000 @@ -172,7 +173,9 @@ func NewEnv(config *Config) *Env { return env } -func (self *Env) RuleSet() RuleSet { return ruleSet{new(big.Int)} } +func (self *Env) ChainConfig() *params.ChainConfig { + return ¶ms.ChainConfig{new(big.Int), new(big.Int), true, new(big.Int), common.Hash{}, 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 255c4f189..4e997f636 100644 --- a/core/vm/jump_table.go +++ b/core/vm/jump_table.go @@ -16,7 +16,11 @@ package vm -import "math/big" +import ( + "math/big" + + "github.com/ethereum/go-ethereum/params" +) type jumpPtr struct { fn instrFn @@ -25,7 +29,7 @@ type jumpPtr struct { type vmJumpTable [256]jumpPtr -func newJumpTable(ruleset RuleSet, blockNumber *big.Int) vmJumpTable { +func newJumpTable(ruleset *params.ChainConfig, blockNumber *big.Int) vmJumpTable { var jumpTable vmJumpTable // when initialising a new VM execution we must first check the homestead diff --git a/core/vm/jump_table_test.go b/core/vm/jump_table_test.go index 4fcb4a220..6a6bb5cf4 100644 --- a/core/vm/jump_table_test.go +++ b/core/vm/jump_table_test.go @@ -19,16 +19,18 @@ package vm import ( "math/big" "testing" + + "github.com/ethereum/go-ethereum/params" ) func TestInit(t *testing.T) { - jumpTable := newJumpTable(ruleSet{big.NewInt(1)}, big.NewInt(0)) + jumpTable := newJumpTable(¶ms.ChainConfig{HomesteadBlock: 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(ruleSet{big.NewInt(1)}, big.NewInt(n)) + jumpTable := newJumpTable(¶ms.ChainConfig{HomesteadBlock: 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/runtime/env.go b/core/vm/runtime/env.go index 59fbaa792..f1a2b60d3 100644 --- a/core/vm/runtime/env.go +++ b/core/vm/runtime/env.go @@ -23,13 +23,14 @@ import ( "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/params" ) // Env is a basic runtime environment required for running the EVM. type Env struct { - ruleSet vm.RuleSet - depth int - state *state.StateDB + chainConfig *params.ChainConfig + depth int + state *state.StateDB origin common.Address coinbase common.Address @@ -47,14 +48,14 @@ 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, - number: cfg.BlockNumber, - time: cfg.Time, - difficulty: cfg.Difficulty, - gasLimit: cfg.GasLimit, + chainConfig: cfg.ChainConfig, + state: state, + origin: cfg.Origin, + coinbase: cfg.Coinbase, + number: cfg.BlockNumber, + time: cfg.Time, + difficulty: cfg.Difficulty, + gasLimit: cfg.GasLimit, } env.evm = vm.New(env, vm.Config{ Debug: cfg.Debug, @@ -65,16 +66,16 @@ func NewEnv(cfg *Config, state *state.StateDB) vm.Environment { return env } -func (self *Env) RuleSet() vm.RuleSet { return self.ruleSet } -func (self *Env) Vm() vm.Vm { return self.evm } -func (self *Env) Origin() common.Address { return self.origin } -func (self *Env) BlockNumber() *big.Int { return self.number } -func (self *Env) Coinbase() common.Address { return self.coinbase } -func (self *Env) Time() *big.Int { return self.time } -func (self *Env) Difficulty() *big.Int { return self.difficulty } -func (self *Env) Db() vm.Database { return self.state } -func (self *Env) GasLimit() *big.Int { return self.gasLimit } -func (self *Env) VmType() vm.Type { return vm.StdVmTy } +func (self *Env) ChainConfig() *params.ChainConfig { return self.chainConfig } +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 } +func (self *Env) Coinbase() common.Address { return self.coinbase } +func (self *Env) Time() *big.Int { return self.time } +func (self *Env) Difficulty() *big.Int { return self.difficulty } +func (self *Env) Db() vm.Database { return self.state } +func (self *Env) GasLimit() *big.Int { return self.gasLimit } +func (self *Env) VmType() vm.Type { return vm.StdVmTy } func (self *Env) GetHash(n uint64) common.Hash { return self.getHashFn(n) } diff --git a/core/vm/runtime/runtime.go b/core/vm/runtime/runtime.go index 343aee514..f2d86a324 100644 --- a/core/vm/runtime/runtime.go +++ b/core/vm/runtime/runtime.go @@ -22,7 +22,6 @@ 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" "github.com/ethereum/go-ethereum/params" @@ -39,7 +38,7 @@ func (ruleSet) GasTable(*big.Int) params.GasTable { // Config is a basic type specifying certain configuration flags for running // the EVM. type Config struct { - RuleSet vm.RuleSet + ChainConfig *params.ChainConfig Difficulty *big.Int Origin common.Address Coinbase common.Address @@ -57,8 +56,8 @@ type Config struct { // sets defaults on the config func setDefaults(cfg *Config) { - if cfg.RuleSet == nil { - cfg.RuleSet = ruleSet{} + if cfg.ChainConfig == nil { + cfg.ChainConfig = ¶ms.ChainConfig{new(big.Int), new(big.Int), false, new(big.Int), common.Hash{}, new(big.Int)} } if cfg.Difficulty == nil { diff --git a/core/vm/vm.go b/core/vm/vm.go index 09cddc2f8..6e316acda 100644 --- a/core/vm/vm.go +++ b/core/vm/vm.go @@ -51,9 +51,9 @@ type EVM struct { func New(env Environment, cfg Config) *EVM { return &EVM{ env: env, - jumpTable: newJumpTable(env.RuleSet(), env.BlockNumber()), + jumpTable: newJumpTable(env.ChainConfig(), env.BlockNumber()), cfg: cfg, - gasTable: env.RuleSet().GasTable(env.BlockNumber()), + gasTable: env.ChainConfig().GasTable(env.BlockNumber()), } } @@ -172,6 +172,7 @@ func (evm *EVM) Run(contract *Contract, input []byte) (ret []byte, err error) { // Get the memory location of pc op = contract.GetOp(pc) + //fmt.Printf("OP %d %v\n", op, op) // calculate the new memory size and gas price for the current executing opcode newMemSize, cost, err = calculateGasAndSize(evm.gasTable, evm.env, contract, caller, op, statedb, mem, stack) if err != nil { @@ -254,10 +255,20 @@ func calculateGasAndSize(gasTable params.GasTable, env Environment, contract *Co // stack Check, memory resize & gas phase switch op { case SUICIDE: - // if suicide is not nil: homestead gas fork + // EIP150 homestead gas reprice fork: if gasTable.CreateBySuicide != nil { gas.Set(gasTable.Suicide) - if !env.Db().Exist(common.BigToAddress(stack.data[len(stack.data)-1])) { + var ( + address = common.BigToAddress(stack.data[len(stack.data)-1]) + eip158 = env.ChainConfig().IsEIP158(env.BlockNumber()) + ) + + if eip158 { + // if empty and transfers value + if env.Db().Empty(address) && statedb.GetBalance(contract.Address()).BitLen() > 0 { + gas.Add(gas, gasTable.CreateBySuicide) + } + } else if !env.Db().Exist(address) { gas.Add(gas, gasTable.CreateBySuicide) } } @@ -378,12 +389,21 @@ func calculateGasAndSize(gasTable params.GasTable, env Environment, contract *Co case CALL, CALLCODE: gas.Set(gasTable.Calls) + transfersValue := stack.data[len(stack.data)-3].BitLen() > 0 if op == CALL { - if !env.Db().Exist(common.BigToAddress(stack.data[stack.len()-2])) { + var ( + address = common.BigToAddress(stack.data[len(stack.data)-2]) + eip158 = env.ChainConfig().IsEIP158(env.BlockNumber()) + ) + if eip158 { + if env.Db().Empty(address) && transfersValue { + gas.Add(gas, params.CallNewAccountGas) + } + } else if !env.Db().Exist(address) { gas.Add(gas, params.CallNewAccountGas) } } - if len(stack.data[stack.len()-3].Bytes()) > 0 { + if transfersValue { gas.Add(gas, params.CallValueTransferGas) } x := calcMemSize(stack.data[stack.len()-6], stack.data[stack.len()-7]) diff --git a/core/vm_env.go b/core/vm_env.go index d62eebbd9..43e4d2fd6 100644 --- a/core/vm_env.go +++ b/core/vm_env.go @@ -23,6 +23,7 @@ import ( "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/params" ) // GetHashFn returns a function for which the VM env can query block hashes through @@ -41,18 +42,18 @@ func GetHashFn(ref common.Hash, chain *BlockChain) func(n uint64) common.Hash { } type VMEnv struct { - 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 + chainConfig *params.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 getHashFn func(uint64) common.Hash // getHashFn callback is used to retrieve block hashes } -func NewEnv(state *state.StateDB, chainConfig *ChainConfig, chain *BlockChain, msg Message, header *types.Header, cfg vm.Config) *VMEnv { +func NewEnv(state *state.StateDB, chainConfig *params.ChainConfig, chain *BlockChain, msg Message, header *types.Header, cfg vm.Config) *VMEnv { env := &VMEnv{ chainConfig: chainConfig, chain: chain, @@ -66,18 +67,18 @@ func NewEnv(state *state.StateDB, chainConfig *ChainConfig, chain *BlockChain, m 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 } -func (self *VMEnv) Coinbase() common.Address { return self.header.Coinbase } -func (self *VMEnv) Time() *big.Int { return self.header.Time } -func (self *VMEnv) Difficulty() *big.Int { return self.header.Difficulty } -func (self *VMEnv) GasLimit() *big.Int { return self.header.GasLimit } -func (self *VMEnv) Value() *big.Int { return self.msg.Value() } -func (self *VMEnv) Db() vm.Database { return self.state } -func (self *VMEnv) Depth() int { return self.depth } -func (self *VMEnv) SetDepth(i int) { self.depth = i } +func (self *VMEnv) ChainConfig() *params.ChainConfig { 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 } +func (self *VMEnv) Coinbase() common.Address { return self.header.Coinbase } +func (self *VMEnv) Time() *big.Int { return self.header.Time } +func (self *VMEnv) Difficulty() *big.Int { return self.header.Difficulty } +func (self *VMEnv) GasLimit() *big.Int { return self.header.GasLimit } +func (self *VMEnv) Value() *big.Int { return self.msg.Value() } +func (self *VMEnv) Db() vm.Database { return self.state } +func (self *VMEnv) Depth() int { return self.depth } +func (self *VMEnv) SetDepth(i int) { self.depth = i } func (self *VMEnv) GetHash(n uint64) common.Hash { return self.getHashFn(n) } diff --git a/eth/api.go b/eth/api.go index 48f512b1b..7932bbcb6 100644 --- a/eth/api.go +++ b/eth/api.go @@ -37,6 +37,7 @@ import ( "github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/miner" + "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rpc" "golang.org/x/net/context" @@ -303,13 +304,13 @@ func (api *PublicDebugAPI) DumpBlock(number uint64) (state.Dump, error) { // PrivateDebugAPI is the collection of Etheruem full node APIs exposed over // the private debugging endpoint. type PrivateDebugAPI struct { - config *core.ChainConfig + config *params.ChainConfig eth *Ethereum } // NewPrivateDebugAPI creates a new API definition for the full node-related // private debug methods of the Ethereum service. -func NewPrivateDebugAPI(config *core.ChainConfig, eth *Ethereum) *PrivateDebugAPI { +func NewPrivateDebugAPI(config *params.ChainConfig, eth *Ethereum) *PrivateDebugAPI { return &PrivateDebugAPI{config: config, eth: eth} } diff --git a/eth/api_backend.go b/eth/api_backend.go index efe9a7a01..f17ad6a15 100644 --- a/eth/api_backend.go +++ b/eth/api_backend.go @@ -103,7 +103,7 @@ func (b *EthApiBackend) GetVMEnv(ctx context.Context, msg core.Message, state et from := statedb.GetOrNewStateObject(addr) from.SetBalance(common.MaxBig) vmError := func() error { return nil } - return core.NewEnv(statedb, b.eth.chainConfig, b.eth.blockchain, msg, header, b.eth.chainConfig.VmConfig), vmError, nil + return core.NewEnv(statedb, b.eth.chainConfig, b.eth.blockchain, msg, header, vm.Config{}), vmError, nil } func (b *EthApiBackend) SendTx(ctx context.Context, signedTx *types.Transaction) error { diff --git a/eth/backend.go b/eth/backend.go index ec501043a..67598bdbb 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -35,7 +35,6 @@ import ( "github.com/ethereum/go-ethereum/common/registrar/ethreg" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/eth/downloader" "github.com/ethereum/go-ethereum/eth/filters" "github.com/ethereum/go-ethereum/eth/gasprice" @@ -47,6 +46,7 @@ import ( "github.com/ethereum/go-ethereum/miner" "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rpc" ) @@ -64,7 +64,7 @@ var ( ) type Config struct { - ChainConfig *core.ChainConfig // chain configuration + ChainConfig *params.ChainConfig // chain configuration NetworkId int // Network ID to use for selecting peers to connect to Genesis string // Genesis JSON to seed the chain database with @@ -112,7 +112,7 @@ type LesServer interface { // Ethereum implements the Ethereum full node service. type Ethereum struct { - chainConfig *core.ChainConfig + chainConfig *params.ChainConfig // Channel for shutting down the service shutdownChan chan bool // Channel for shutting down the ethereum stopDbUpgrade func() // stop chain db sequential key upgrade @@ -217,10 +217,6 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) { core.WriteChainConfig(chainDb, genesis.Hash(), config.ChainConfig) eth.chainConfig = config.ChainConfig - eth.chainConfig.VmConfig = vm.Config{ - EnableJit: config.EnableJit, - ForceJit: config.ForceJit, - } eth.blockchain, err = core.NewBlockChain(chainDb, eth.chainConfig, eth.pow, eth.EventMux()) if err != nil { diff --git a/eth/backend_test.go b/eth/backend_test.go index 105d71080..212c4c4d3 100644 --- a/eth/backend_test.go +++ b/eth/backend_test.go @@ -25,6 +25,7 @@ import ( "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/params" ) func TestMipmapUpgrade(t *testing.T) { @@ -32,7 +33,7 @@ func TestMipmapUpgrade(t *testing.T) { addr := common.BytesToAddress([]byte("jeff")) genesis := core.WriteGenesisBlockForTesting(db) - chain, receipts := core.GenerateChain(nil, genesis, db, 10, func(i int, gen *core.BlockGen) { + chain, receipts := core.GenerateChain(params.TestChainConfig, genesis, db, 10, func(i int, gen *core.BlockGen) { var receipts types.Receipts switch i { case 1: diff --git a/eth/downloader/downloader_test.go b/eth/downloader/downloader_test.go index ff8cd1044..f5f2437fa 100644 --- a/eth/downloader/downloader_test.go +++ b/eth/downloader/downloader_test.go @@ -109,7 +109,7 @@ func newTester() *downloadTester { // reassembly. func (dl *downloadTester) makeChain(n int, seed byte, parent *types.Block, parentReceipts types.Receipts, heavy bool) ([]common.Hash, map[common.Hash]*types.Header, map[common.Hash]*types.Block, map[common.Hash]types.Receipts) { // Generate the block chain - blocks, receipts := core.GenerateChain(nil, parent, dl.peerDb, n, func(i int, block *core.BlockGen) { + blocks, receipts := core.GenerateChain(params.TestChainConfig, parent, dl.peerDb, n, func(i int, block *core.BlockGen) { block.SetCoinbase(common.Address{seed}) // If a heavy chain is requested, delay blocks to raise difficulty diff --git a/eth/fetcher/fetcher_test.go b/eth/fetcher/fetcher_test.go index ad955a577..5d46c62fd 100644 --- a/eth/fetcher/fetcher_test.go +++ b/eth/fetcher/fetcher_test.go @@ -45,7 +45,7 @@ var ( // contains a transaction and every 5th an uncle to allow testing correct block // reassembly. func makeChain(n int, seed byte, parent *types.Block) ([]common.Hash, map[common.Hash]*types.Block) { - blocks, _ := core.GenerateChain(nil, parent, testdb, n, func(i int, block *core.BlockGen) { + blocks, _ := core.GenerateChain(params.TestChainConfig, parent, testdb, n, func(i int, block *core.BlockGen) { block.SetCoinbase(common.Address{seed}) // If the block number is multiple of 3, send a bonus transaction to the miner diff --git a/eth/filters/filter_system_test.go b/eth/filters/filter_system_test.go index 48d6811c0..d6d4199cc 100644 --- a/eth/filters/filter_system_test.go +++ b/eth/filters/filter_system_test.go @@ -30,6 +30,7 @@ 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/rpc" ) @@ -81,7 +82,7 @@ func TestBlockSubscription(t *testing.T) { var ( genesis = core.WriteGenesisBlockForTesting(db) - chain, _ = core.GenerateChain(nil, genesis, db, 10, func(i int, gen *core.BlockGen) {}) + chain, _ = core.GenerateChain(params.TestChainConfig, genesis, db, 10, func(i int, gen *core.BlockGen) {}) chainEvents = []core.ChainEvent{} ) diff --git a/eth/filters/filter_test.go b/eth/filters/filter_test.go index e0b24046c..a8c767ead 100644 --- a/eth/filters/filter_test.go +++ b/eth/filters/filter_test.go @@ -30,6 +30,7 @@ import ( "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/params" ) func makeReceipt(addr common.Address) *types.Receipt { @@ -60,7 +61,7 @@ func BenchmarkMipmaps(b *testing.B) { defer db.Close() genesis := core.WriteGenesisBlockForTesting(db, core.GenesisAccount{Address: addr1, Balance: big.NewInt(1000000)}) - chain, receipts := core.GenerateChain(nil, genesis, db, 100010, func(i int, gen *core.BlockGen) { + chain, receipts := core.GenerateChain(params.TestChainConfig, genesis, db, 100010, func(i int, gen *core.BlockGen) { var receipts types.Receipts switch i { case 2403: @@ -137,7 +138,7 @@ func TestFilters(t *testing.T) { defer db.Close() genesis := core.WriteGenesisBlockForTesting(db, core.GenesisAccount{Address: addr, Balance: big.NewInt(1000000)}) - chain, receipts := core.GenerateChain(nil, genesis, db, 1000, func(i int, gen *core.BlockGen) { + chain, receipts := core.GenerateChain(params.TestChainConfig, genesis, db, 1000, func(i int, gen *core.BlockGen) { var receipts types.Receipts switch i { case 1: diff --git a/eth/handler.go b/eth/handler.go index 9d6b1ced2..8f05cc5b1 100644 --- a/eth/handler.go +++ b/eth/handler.go @@ -37,6 +37,7 @@ import ( "github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p/discover" + "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/pow" "github.com/ethereum/go-ethereum/rlp" ) @@ -67,7 +68,7 @@ type ProtocolManager struct { txpool txPool blockchain *core.BlockChain chaindb ethdb.Database - chainconfig *core.ChainConfig + chainconfig *params.ChainConfig maxPeers int downloader *downloader.Downloader @@ -95,7 +96,7 @@ type ProtocolManager struct { // NewProtocolManager returns a new ethereum sub protocol manager. The Ethereum sub protocol manages peers capable // with the ethereum network. -func NewProtocolManager(config *core.ChainConfig, fastSync bool, networkId int, maxPeers int, mux *event.TypeMux, txpool txPool, pow pow.PoW, blockchain *core.BlockChain, chaindb ethdb.Database) (*ProtocolManager, error) { +func NewProtocolManager(config *params.ChainConfig, fastSync bool, networkId int, maxPeers int, mux *event.TypeMux, txpool txPool, pow pow.PoW, blockchain *core.BlockChain, chaindb ethdb.Database) (*ProtocolManager, error) { // Create the protocol manager with the base fields manager := &ProtocolManager{ networkId: networkId, diff --git a/eth/handler_test.go b/eth/handler_test.go index 64449afda..045bad0d4 100644 --- a/eth/handler_test.go +++ b/eth/handler_test.go @@ -466,7 +466,7 @@ func testDAOChallenge(t *testing.T, localForked, remoteForked bool, timeout bool pow = new(core.FakePow) db, _ = ethdb.NewMemDatabase() genesis = core.WriteGenesisBlockForTesting(db) - config = &core.ChainConfig{DAOForkBlock: big.NewInt(1), DAOForkSupport: localForked} + config = ¶ms.ChainConfig{DAOForkBlock: big.NewInt(1), DAOForkSupport: localForked} blockchain, _ = core.NewBlockChain(db, config, pow, evmux) ) pm, err := NewProtocolManager(config, false, NetworkId, 1000, evmux, new(testTxPool), pow, blockchain, db) @@ -491,7 +491,7 @@ func testDAOChallenge(t *testing.T, localForked, remoteForked bool, timeout bool } // Create a block to reply to the challenge if no timeout is simualted if !timeout { - blocks, _ := core.GenerateChain(nil, genesis, db, 1, func(i int, block *core.BlockGen) { + blocks, _ := core.GenerateChain(¶ms.ChainConfig{}, genesis, db, 1, func(i int, block *core.BlockGen) { if remoteForked { block.SetExtra(params.DAOForkBlockExtra) } diff --git a/eth/helper_test.go b/eth/helper_test.go index d5295b398..73af04fcf 100644 --- a/eth/helper_test.go +++ b/eth/helper_test.go @@ -35,6 +35,7 @@ import ( "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p/discover" + "github.com/ethereum/go-ethereum/params" ) var ( @@ -54,10 +55,10 @@ func newTestProtocolManager(fastSync bool, blocks int, generator func(int, *core pow = new(core.FakePow) db, _ = ethdb.NewMemDatabase() genesis = core.WriteGenesisBlockForTesting(db, testBank) - chainConfig = &core.ChainConfig{HomesteadBlock: big.NewInt(0)} // homestead set to 0 because of chain maker + chainConfig = ¶ms.ChainConfig{HomesteadBlock: big.NewInt(0)} // homestead set to 0 because of chain maker blockchain, _ = core.NewBlockChain(db, chainConfig, pow, evmux) ) - chain, _ := core.GenerateChain(nil, genesis, db, blocks, generator) + chain, _ := core.GenerateChain(chainConfig, genesis, db, blocks, generator) if _, err := blockchain.InsertChain(chain); err != nil { panic(err) } diff --git a/internal/ethapi/tracer_test.go b/internal/ethapi/tracer_test.go index 577b426f1..b88178a6d 100644 --- a/internal/ethapi/tracer_test.go +++ b/internal/ethapi/tracer_test.go @@ -29,11 +29,6 @@ import ( "github.com/ethereum/go-ethereum/params" ) -type ruleSet struct{} - -func (self *ruleSet) IsHomestead(*big.Int) bool { return true } -func (*ruleSet) GasTable(*big.Int) params.GasTable { return params.GasTableHomesteadGasRepriceFork } - type Env struct { gasLimit *big.Int depth int @@ -46,7 +41,9 @@ func NewEnv(config *vm.Config) *Env { return env } -func (self *Env) RuleSet() vm.RuleSet { return &ruleSet{} } +func (self *Env) ChainConfig() *params.ChainConfig { + return params.TestChainConfig +} func (self *Env) Vm() 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/les/api_backend.go b/les/api_backend.go index 04120c669..42d6cc473 100644 --- a/les/api_backend.go +++ b/les/api_backend.go @@ -87,7 +87,7 @@ func (b *LesApiBackend) GetVMEnv(ctx context.Context, msg core.Message, state et return nil, nil, err } from.SetBalance(common.MaxBig) - env := light.NewEnv(ctx, stateDb, b.eth.chainConfig, b.eth.blockchain, msg, header, b.eth.chainConfig.VmConfig) + env := light.NewEnv(ctx, stateDb, b.eth.chainConfig, b.eth.blockchain, msg, header, vm.Config{}) return env, env.Error, nil } diff --git a/les/backend.go b/les/backend.go index 5eb53d728..1264acfd1 100644 --- a/les/backend.go +++ b/les/backend.go @@ -29,7 +29,6 @@ import ( "github.com/ethereum/go-ethereum/common/httpclient" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/eth/downloader" "github.com/ethereum/go-ethereum/eth/filters" @@ -42,13 +41,14 @@ import ( "github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/params" rpc "github.com/ethereum/go-ethereum/rpc" ) type LightEthereum struct { odr *LesOdr relay *LesTxRelay - chainConfig *core.ChainConfig + chainConfig *params.ChainConfig // Channel for shutting down the service shutdownChan chan bool // Handlers @@ -107,10 +107,6 @@ func New(ctx *node.ServiceContext, config *eth.Config) (*LightEthereum, error) { return nil, errors.New("missing chain config") } eth.chainConfig = config.ChainConfig - eth.chainConfig.VmConfig = vm.Config{ - EnableJit: config.EnableJit, - ForceJit: config.ForceJit, - } eth.blockchain, err = light.NewLightChain(odr, eth.chainConfig, eth.pow, eth.eventMux) if err != nil { if err == core.ErrNoGenesis { diff --git a/les/handler.go b/les/handler.go index cf698d79c..8b8ca75da 100644 --- a/les/handler.go +++ b/les/handler.go @@ -38,6 +38,7 @@ import ( "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p/discover" "github.com/ethereum/go-ethereum/p2p/discv5" + "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/pow" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/trie" @@ -95,7 +96,7 @@ type ProtocolManager struct { txpool txPool txrelay *LesTxRelay networkId int - chainConfig *core.ChainConfig + chainConfig *params.ChainConfig blockchain BlockChain chainDb ethdb.Database odr *LesOdr @@ -129,7 +130,7 @@ type ProtocolManager struct { // NewProtocolManager returns a new ethereum sub protocol manager. The Ethereum sub protocol manages peers capable // with the ethereum network. -func NewProtocolManager(chainConfig *core.ChainConfig, lightSync bool, networkId int, mux *event.TypeMux, pow pow.PoW, blockchain BlockChain, txpool txPool, chainDb ethdb.Database, odr *LesOdr, txrelay *LesTxRelay) (*ProtocolManager, error) { +func NewProtocolManager(chainConfig *params.ChainConfig, lightSync bool, networkId int, mux *event.TypeMux, pow pow.PoW, blockchain BlockChain, txpool txPool, chainDb ethdb.Database, odr *LesOdr, txrelay *LesTxRelay) (*ProtocolManager, error) { // Create the protocol manager with the base fields manager := &ProtocolManager{ lightSync: lightSync, diff --git a/les/helper_test.go b/les/helper_test.go index 1b3c29e48..9569f2f75 100644 --- a/les/helper_test.go +++ b/les/helper_test.go @@ -131,7 +131,7 @@ func newTestProtocolManager(lightSync bool, blocks int, generator func(int, *cor pow = new(core.FakePow) db, _ = ethdb.NewMemDatabase() genesis = core.WriteGenesisBlockForTesting(db, core.GenesisAccount{Address: testBankAddress, Balance: testBankFunds}) - chainConfig = &core.ChainConfig{HomesteadBlock: big.NewInt(0)} // homestead set to 0 because of chain maker + chainConfig = ¶ms.ChainConfig{HomesteadBlock: big.NewInt(0)} // homestead set to 0 because of chain maker odr *LesOdr chain BlockChain ) @@ -141,7 +141,7 @@ func newTestProtocolManager(lightSync bool, blocks int, generator func(int, *cor chain, _ = light.NewLightChain(odr, chainConfig, pow, evmux) } else { blockchain, _ := core.NewBlockChain(db, chainConfig, pow, evmux) - gchain, _ := core.GenerateChain(nil, genesis, db, blocks, generator) + gchain, _ := core.GenerateChain(chainConfig, genesis, db, blocks, generator) if _, err := blockchain.InsertChain(gchain); err != nil { panic(err) } diff --git a/les/odr_test.go b/les/odr_test.go index 3c8ea8c3f..e7084bc20 100644 --- a/les/odr_test.go +++ b/les/odr_test.go @@ -26,17 +26,19 @@ import ( "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/light" + "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" "golang.org/x/net/context" ) -type odrTestFn func(ctx context.Context, db ethdb.Database, config *core.ChainConfig, bc *core.BlockChain, lc *light.LightChain, bhash common.Hash) []byte +type odrTestFn func(ctx context.Context, db ethdb.Database, config *params.ChainConfig, bc *core.BlockChain, lc *light.LightChain, bhash common.Hash) []byte func TestOdrGetBlockLes1(t *testing.T) { testOdr(t, 1, 1, odrGetBlock) } -func odrGetBlock(ctx context.Context, db ethdb.Database, config *core.ChainConfig, bc *core.BlockChain, lc *light.LightChain, bhash common.Hash) []byte { +func odrGetBlock(ctx context.Context, db ethdb.Database, config *params.ChainConfig, bc *core.BlockChain, lc *light.LightChain, bhash common.Hash) []byte { var block *types.Block if bc != nil { block = bc.GetBlockByHash(bhash) @@ -52,7 +54,7 @@ func odrGetBlock(ctx context.Context, db ethdb.Database, config *core.ChainConfi func TestOdrGetReceiptsLes1(t *testing.T) { testOdr(t, 1, 1, odrGetReceipts) } -func odrGetReceipts(ctx context.Context, db ethdb.Database, config *core.ChainConfig, bc *core.BlockChain, lc *light.LightChain, bhash common.Hash) []byte { +func odrGetReceipts(ctx context.Context, db ethdb.Database, config *params.ChainConfig, bc *core.BlockChain, lc *light.LightChain, bhash common.Hash) []byte { var receipts types.Receipts if bc != nil { receipts = core.GetBlockReceipts(db, bhash, core.GetBlockNumber(db, bhash)) @@ -68,7 +70,7 @@ func odrGetReceipts(ctx context.Context, db ethdb.Database, config *core.ChainCo func TestOdrAccountsLes1(t *testing.T) { testOdr(t, 1, 1, odrAccounts) } -func odrAccounts(ctx context.Context, db ethdb.Database, config *core.ChainConfig, bc *core.BlockChain, lc *light.LightChain, bhash common.Hash) []byte { +func odrAccounts(ctx context.Context, db ethdb.Database, config *params.ChainConfig, bc *core.BlockChain, lc *light.LightChain, bhash common.Hash) []byte { dummyAddr := common.HexToAddress("1234567812345678123456781234567812345678") acc := []common.Address{testBankAddress, acc1Addr, acc2Addr, dummyAddr} @@ -138,7 +140,7 @@ func (m lightcallmsg) Gas() *big.Int { return m.gas } func (m lightcallmsg) Value() *big.Int { return m.value } func (m lightcallmsg) Data() []byte { return m.data } -func odrContractCall(ctx context.Context, db ethdb.Database, config *core.ChainConfig, bc *core.BlockChain, lc *light.LightChain, bhash common.Hash) []byte { +func odrContractCall(ctx context.Context, db ethdb.Database, config *params.ChainConfig, bc *core.BlockChain, lc *light.LightChain, bhash common.Hash) []byte { data := common.Hex2Bytes("60CD26850000000000000000000000000000000000000000000000000000000000000000") var res []byte @@ -160,7 +162,7 @@ func odrContractCall(ctx context.Context, db ethdb.Database, config *core.ChainC to: &testContractAddr, } - vmenv := core.NewEnv(statedb, config, bc, msg, header, config.VmConfig) + vmenv := core.NewEnv(statedb, config, bc, msg, header, vm.Config{}) gp := new(core.GasPool).AddGas(common.MaxBig) ret, _, _ := core.ApplyMessage(vmenv, msg, gp) res = append(res, ret...) @@ -181,7 +183,7 @@ func odrContractCall(ctx context.Context, db ethdb.Database, config *core.ChainC to: &testContractAddr, } - vmenv := light.NewEnv(ctx, state, config, lc, msg, header, config.VmConfig) + vmenv := light.NewEnv(ctx, state, config, lc, msg, header, vm.Config{}) gp := new(core.GasPool).AddGas(common.MaxBig) ret, _, _ := core.ApplyMessage(vmenv, msg, gp) if vmenv.Error() == nil { diff --git a/light/lightchain.go b/light/lightchain.go index 461030369..4e895db61 100644 --- a/light/lightchain.go +++ b/light/lightchain.go @@ -29,6 +29,7 @@ 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" "github.com/ethereum/go-ethereum/pow" "github.com/ethereum/go-ethereum/rlp" "github.com/hashicorp/golang-lru" @@ -71,7 +72,7 @@ type LightChain struct { // NewLightChain returns a fully initialised light chain using information // available in the database. It initialises the default Ethereum header // validator. -func NewLightChain(odr OdrBackend, config *core.ChainConfig, pow pow.PoW, mux *event.TypeMux) (*LightChain, error) { +func NewLightChain(odr OdrBackend, config *params.ChainConfig, pow pow.PoW, mux *event.TypeMux) (*LightChain, error) { bodyCache, _ := lru.New(bodyCacheLimit) bodyRLPCache, _ := lru.New(bodyCacheLimit) blockCache, _ := lru.New(blockCacheLimit) diff --git a/light/lightchain_test.go b/light/lightchain_test.go index e42feb026..0ba925887 100644 --- a/light/lightchain_test.go +++ b/light/lightchain_test.go @@ -28,6 +28,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/pow" "github.com/hashicorp/golang-lru" "golang.org/x/net/context" @@ -41,7 +42,7 @@ var ( // makeHeaderChain creates a deterministic chain of headers rooted at parent. func makeHeaderChain(parent *types.Header, n int, db ethdb.Database, seed int) []*types.Header { - blocks, _ := core.GenerateChain(nil, types.NewBlockWithHeader(parent), db, n, func(i int, b *core.BlockGen) { + blocks, _ := core.GenerateChain(params.TestChainConfig, types.NewBlockWithHeader(parent), db, n, func(i int, b *core.BlockGen) { b.SetCoinbase(common.Address{0: byte(seed), 19: byte(i)}) }) headers := make([]*types.Header, len(blocks)) @@ -51,8 +52,8 @@ func makeHeaderChain(parent *types.Header, n int, db ethdb.Database, seed int) [ return headers } -func testChainConfig() *core.ChainConfig { - return &core.ChainConfig{HomesteadBlock: big.NewInt(0)} +func testChainConfig() *params.ChainConfig { + return ¶ms.ChainConfig{HomesteadBlock: big.NewInt(0)} } // newCanonical creates a chain database, and injects a deterministic canonical diff --git a/light/odr_test.go b/light/odr_test.go index 9c83e91c6..1ae600e28 100644 --- a/light/odr_test.go +++ b/light/odr_test.go @@ -294,7 +294,8 @@ func testChainOdr(t *testing.T, protocol int, expFail uint64, fn odrTestFn) { core.WriteGenesisBlockForTesting(ldb, core.GenesisAccount{Address: testBankAddress, Balance: testBankFunds}) // Assemble the test environment blockchain, _ := core.NewBlockChain(sdb, testChainConfig(), pow, evmux) - gchain, _ := core.GenerateChain(nil, genesis, sdb, 4, testChainGen) + chainConfig := ¶ms.ChainConfig{HomesteadBlock: new(big.Int)} + gchain, _ := core.GenerateChain(chainConfig, genesis, sdb, 4, testChainGen) if _, err := blockchain.InsertChain(gchain); err != nil { panic(err) } diff --git a/light/state_test.go b/light/state_test.go index 89a64483d..d594ab9ff 100644 --- a/light/state_test.go +++ b/light/state_test.go @@ -41,7 +41,7 @@ func makeTestState() (common.Hash, ethdb.Database) { st.AddBalance(addr, big.NewInt(int64(i))) st.SetCode(addr, []byte{i, i, i}) } - root, _ := st.Commit() + root, _ := st.Commit(false) return root, sdb } diff --git a/light/txpool.go b/light/txpool.go index 825a0f909..c046cac25 100644 --- a/light/txpool.go +++ b/light/txpool.go @@ -28,6 +28,7 @@ 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" "github.com/ethereum/go-ethereum/rlp" "golang.org/x/net/context" ) @@ -42,7 +43,7 @@ var txPermanent = uint64(500) // always receive all locally signed transactions in the same order as they are // created. type TxPool struct { - config *core.ChainConfig + config *params.ChainConfig quit chan bool eventMux *event.TypeMux events event.Subscription @@ -76,7 +77,7 @@ type TxRelayBackend interface { } // NewTxPool creates a new light transaction pool -func NewTxPool(config *core.ChainConfig, eventMux *event.TypeMux, chain *LightChain, relay TxRelayBackend) *TxPool { +func NewTxPool(config *params.ChainConfig, eventMux *event.TypeMux, chain *LightChain, relay TxRelayBackend) *TxPool { pool := &TxPool{ config: config, nonce: make(map[common.Address]uint64), diff --git a/light/txpool_test.go b/light/txpool_test.go index 4ff1006e5..0f797a9e8 100644 --- a/light/txpool_test.go +++ b/light/txpool_test.go @@ -87,7 +87,8 @@ func TestTxPool(t *testing.T) { core.WriteGenesisBlockForTesting(ldb, core.GenesisAccount{Address: testBankAddress, Balance: testBankFunds}) // Assemble the test environment blockchain, _ := core.NewBlockChain(sdb, testChainConfig(), pow, evmux) - gchain, _ := core.GenerateChain(nil, genesis, sdb, poolTestBlocks, txPoolTestChainGen) + chainConfig := ¶ms.ChainConfig{HomesteadBlock: new(big.Int)} + gchain, _ := core.GenerateChain(chainConfig, genesis, sdb, poolTestBlocks, txPoolTestChainGen) if _, err := blockchain.InsertChain(gchain); err != nil { panic(err) } diff --git a/light/vm_env.go b/light/vm_env.go index 0978755cf..82008a929 100644 --- a/light/vm_env.go +++ b/light/vm_env.go @@ -24,6 +24,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/params" "golang.org/x/net/context" ) @@ -34,7 +35,7 @@ import ( type VMEnv struct { vm.Environment ctx context.Context - chainConfig *core.ChainConfig + chainConfig *params.ChainConfig evm *vm.EVM state *VMState header *types.Header @@ -45,7 +46,7 @@ type VMEnv struct { } // NewEnv creates a new execution environment based on an ODR capable light state -func NewEnv(ctx context.Context, state *LightState, chainConfig *core.ChainConfig, chain *LightChain, msg core.Message, header *types.Header, cfg vm.Config) *VMEnv { +func NewEnv(ctx context.Context, state *LightState, chainConfig *params.ChainConfig, chain *LightChain, msg core.Message, header *types.Header, cfg vm.Config) *VMEnv { env := &VMEnv{ chainConfig: chainConfig, chain: chain, @@ -58,17 +59,17 @@ func NewEnv(ctx context.Context, state *LightState, chainConfig *core.ChainConfi 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 } -func (self *VMEnv) Coinbase() common.Address { return self.header.Coinbase } -func (self *VMEnv) Time() *big.Int { return self.header.Time } -func (self *VMEnv) Difficulty() *big.Int { return self.header.Difficulty } -func (self *VMEnv) GasLimit() *big.Int { return self.header.GasLimit } -func (self *VMEnv) Db() vm.Database { return self.state } -func (self *VMEnv) Depth() int { return self.depth } -func (self *VMEnv) SetDepth(i int) { self.depth = i } +func (self *VMEnv) ChainConfig() *params.ChainConfig { 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 } +func (self *VMEnv) Coinbase() common.Address { return self.header.Coinbase } +func (self *VMEnv) Time() *big.Int { return self.header.Time } +func (self *VMEnv) Difficulty() *big.Int { return self.header.Difficulty } +func (self *VMEnv) GasLimit() *big.Int { return self.header.GasLimit } +func (self *VMEnv) Db() vm.Database { return self.state } +func (self *VMEnv) Depth() int { return self.depth } +func (self *VMEnv) SetDepth(i int) { self.depth = i } func (self *VMEnv) GetHash(n uint64) common.Hash { for header := self.chain.GetHeader(self.header.ParentHash, self.header.Number.Uint64()-1); header != nil; header = self.chain.GetHeader(header.ParentHash, header.Number.Uint64()-1) { if header.Number.Uint64() == n { diff --git a/miner/miner.go b/miner/miner.go index c16cbe6ae..76dbb8380 100644 --- a/miner/miner.go +++ b/miner/miner.go @@ -62,7 +62,7 @@ type Miner struct { shouldStart int32 // should start indicates whether we should start after sync } -func New(eth Backend, config *core.ChainConfig, mux *event.TypeMux, pow pow.PoW) *Miner { +func New(eth Backend, config *params.ChainConfig, mux *event.TypeMux, pow pow.PoW) *Miner { miner := &Miner{ eth: eth, mux: mux, diff --git a/miner/worker.go b/miner/worker.go index 89064c3b9..f98145e8f 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -63,7 +63,7 @@ type uint64RingBuffer struct { // Work is the workers current environment and holds // all of the current state information type Work struct { - config *core.ChainConfig + config *params.ChainConfig state *state.StateDB // apply state changes here ancestors *set.Set // ancestor set (used for checking uncle parent validity) family *set.Set // family set (used for checking uncle invalidity) @@ -90,7 +90,7 @@ type Result struct { // worker is the main object which takes care of applying messages to the new state type worker struct { - config *core.ChainConfig + config *params.ChainConfig mu sync.Mutex @@ -128,7 +128,7 @@ type worker struct { fullValidation bool } -func newWorker(config *core.ChainConfig, coinbase common.Address, eth Backend, mux *event.TypeMux) *worker { +func newWorker(config *params.ChainConfig, coinbase common.Address, eth Backend, mux *event.TypeMux) *worker { worker := &worker{ config: config, eth: eth, @@ -276,7 +276,7 @@ func (self *worker) wait() { } go self.mux.Post(core.NewMinedBlockEvent{Block: block}) } else { - work.state.Commit() + work.state.Commit(self.config.IsEIP158(block.Number())) parent := self.chain.GetBlock(block.ParentHash(), block.NumberU64()-1) if parent == nil { glog.V(logger.Error).Infoln("Invalid block found during mining") @@ -528,7 +528,7 @@ func (self *worker) commitNewWork() { if atomic.LoadInt32(&self.mining) == 1 { // commit state root after all state transitions. core.AccumulateRewards(work.state, header, uncles) - header.Root = work.state.IntermediateRoot() + header.Root = work.state.IntermediateRoot(self.config.IsEIP158(header.Number)) } // create the new block whose nonce will be mined. @@ -620,14 +620,7 @@ func (env *Work) commitTransactions(mux *event.TypeMux, txs *types.TransactionsB func (env *Work) commitTransaction(tx *types.Transaction, bc *core.BlockChain, gp *core.GasPool) (error, vm.Logs) { snap := env.state.Snapshot() - // this is a bit of a hack to force jit for the miners - config := env.config.VmConfig - if !(config.EnableJit && config.ForceJit) { - config.EnableJit = false - } - config.ForceJit = false // disable forcing jit - - receipt, logs, _, err := core.ApplyTransaction(env.config, bc, gp, env.state, env.header, tx, env.header.GasUsed, config) + receipt, logs, _, err := core.ApplyTransaction(env.config, bc, gp, env.state, env.header, tx, env.header.GasUsed, vm.Config{}) if err != nil { env.state.RevertToSnapshot(snap) return err, nil diff --git a/core/config.go b/params/config.go index 96e39ea3c..684e4dc42 100644 --- a/core/config.go +++ b/params/config.go @@ -14,19 +14,14 @@ // 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 +package params import ( - "errors" "math/big" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/vm" - "github.com/ethereum/go-ethereum/params" ) -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 @@ -37,12 +32,15 @@ type ChainConfig struct { DAOForkBlock *big.Int `json:"daoForkBlock"` // TheDAO hard-fork switch block (nil = no fork) DAOForkSupport bool `json:"daoForkSupport"` // Whether the nodes supports or opposes the DAO hard-fork - HomesteadGasRepriceBlock *big.Int `json:"homesteadGasRepriceBlock"` // Homestead gas reprice switch block (nil = no fork) - HomesteadGasRepriceHash common.Hash `json:"homesteadGasRepriceHash"` // Homestead gas reprice switch block hash (fast sync aid) + // EIP150 implements the Gas price changes (https://github.com/ethereum/EIPs/issues/150) + EIP150Block *big.Int `json:"EIP150Block"` // EIP150 HF block (nil = no fork) + EIP150Hash common.Hash `json:"EIP150Hash"` // EIP150 HF hash (fast sync aid) - VmConfig vm.Config `json:"-"` + EIP158Block *big.Int `json:"EIP158Block"` // EIP158 HF block } +var TestChainConfig = &ChainConfig{new(big.Int), new(big.Int), true, new(big.Int), common.Hash{}, new(big.Int)} + // IsHomestead returns whether num is either equal to the homestead block or greater. func (c *ChainConfig) IsHomestead(num *big.Int) bool { if c.HomesteadBlock == nil || num == nil { @@ -54,10 +52,33 @@ func (c *ChainConfig) IsHomestead(num *big.Int) bool { // GasTable returns the gas table corresponding to the current phase (homestead or homestead reprice). // // The returned GasTable's fields shouldn't, under any circumstances, be changed. -func (c *ChainConfig) GasTable(num *big.Int) params.GasTable { - if c.HomesteadGasRepriceBlock == nil || num == nil || num.Cmp(c.HomesteadGasRepriceBlock) < 0 { - return params.GasTableHomestead +func (c *ChainConfig) GasTable(num *big.Int) GasTable { + if num == nil { + return GasTableHomestead + } + + switch { + case c.EIP158Block != nil && num.Cmp(c.EIP158Block) >= 0: + return GasTableEIP158 + case c.EIP150Block != nil && num.Cmp(c.EIP150Block) >= 0: + return GasTableHomesteadGasRepriceFork + default: + return GasTableHomestead + } +} + +func (c *ChainConfig) IsEIP150(num *big.Int) bool { + if c.EIP150Block == nil || num == nil { + return false + } + return num.Cmp(c.EIP150Block) >= 0 + +} + +func (c *ChainConfig) IsEIP158(num *big.Int) bool { + if c.EIP158Block == nil || num == nil { + return false } + return num.Cmp(c.EIP158Block) >= 0 - return params.GasTableHomesteadGasRepriceFork } diff --git a/params/gas_table.go b/params/gas_table.go index 98f00a7ee..3b27cd522 100644 --- a/params/gas_table.go +++ b/params/gas_table.go @@ -26,6 +26,10 @@ type GasTable struct { Calls *big.Int Suicide *big.Int + Exp *big.Int + ExpOneByte *big.Int + Exp256 *big.Int + // CreateBySuicide occurs when the // refunded account is one that does // not exist. This logic is similar @@ -44,6 +48,7 @@ var ( SLoad: big.NewInt(50), Calls: big.NewInt(40), Suicide: big.NewInt(0), + Exp: big.NewInt(20), // explicitly set to nil to indicate // this rule does not apply to homestead. @@ -52,6 +57,8 @@ var ( // GasTableHomestead contain the gas re-prices for // the homestead phase. + // + // TODO rename to GasTableEIP150 GasTableHomesteadGasRepriceFork = GasTable{ ExtcodeSize: big.NewInt(700), ExtcodeCopy: big.NewInt(700), @@ -59,6 +66,21 @@ var ( SLoad: big.NewInt(200), Calls: big.NewInt(700), Suicide: big.NewInt(5000), + Exp: big.NewInt(20), + + CreateBySuicide: big.NewInt(25000), + } + + GasTableEIP158 = GasTable{ + ExtcodeSize: big.NewInt(700), + ExtcodeCopy: big.NewInt(700), + Balance: big.NewInt(400), + SLoad: big.NewInt(200), + Calls: big.NewInt(700), + Suicide: big.NewInt(5000), + Exp: big.NewInt(80), + ExpOneByte: big.NewInt(160), + Exp256: big.NewInt(2640), CreateBySuicide: big.NewInt(25000), } diff --git a/tests/block_test_util.go b/tests/block_test_util.go index 010c8d4c0..f04329546 100644 --- a/tests/block_test_util.go +++ b/tests/block_test_util.go @@ -35,6 +35,7 @@ import ( "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" ) @@ -170,7 +171,7 @@ func runBlockTest(homesteadBlock, daoForkBlock, gasPriceFork *big.Int, test *Blo core.WriteCanonicalHash(db, test.Genesis.Hash(), test.Genesis.NumberU64()) core.WriteHeadBlockHash(db, test.Genesis.Hash()) evmux := new(event.TypeMux) - config := &core.ChainConfig{HomesteadBlock: homesteadBlock, DAOForkBlock: daoForkBlock, DAOForkSupport: true, HomesteadGasRepriceBlock: gasPriceFork} + config := ¶ms.ChainConfig{HomesteadBlock: homesteadBlock, DAOForkBlock: daoForkBlock, DAOForkSupport: true, EIP150Block: gasPriceFork} chain, err := core.NewBlockChain(db, config, ethash.NewShared(), evmux) if err != nil { return err @@ -228,7 +229,7 @@ func (t *BlockTest) InsertPreState(db ethdb.Database) (*state.StateDB, error) { } } - root, err := statedb.Commit() + root, err := statedb.Commit(false) if err != nil { return nil, fmt.Errorf("error writing state: %v", err) } diff --git a/tests/state_test.go b/tests/state_test.go index 1e848ab4e..b266c2215 100644 --- a/tests/state_test.go +++ b/tests/state_test.go @@ -21,6 +21,8 @@ import ( "os" "path/filepath" "testing" + + "github.com/ethereum/go-ethereum/params" ) func BenchmarkStateCall1024(b *testing.B) { @@ -31,172 +33,172 @@ func BenchmarkStateCall1024(b *testing.B) { } func TestStateSystemOperations(t *testing.T) { - ruleSet := RuleSet{ + chainConfig := ¶ms.ChainConfig{ HomesteadBlock: big.NewInt(1150000), } fn := filepath.Join(stateTestDir, "stSystemOperationsTest.json") - if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil { + if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { t.Error(err) } } func TestStateExample(t *testing.T) { - ruleSet := RuleSet{ + chainConfig := ¶ms.ChainConfig{ HomesteadBlock: big.NewInt(1150000), } fn := filepath.Join(stateTestDir, "stExample.json") - if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil { + if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { t.Error(err) } } func TestStatePreCompiledContracts(t *testing.T) { - ruleSet := RuleSet{ + chainConfig := ¶ms.ChainConfig{ HomesteadBlock: big.NewInt(1150000), } fn := filepath.Join(stateTestDir, "stPreCompiledContracts.json") - if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil { + if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { t.Error(err) } } func TestStateRecursiveCreate(t *testing.T) { - ruleSet := RuleSet{ + chainConfig := ¶ms.ChainConfig{ HomesteadBlock: big.NewInt(1150000), } fn := filepath.Join(stateTestDir, "stRecursiveCreate.json") - if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil { + if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { t.Error(err) } } func TestStateSpecial(t *testing.T) { - ruleSet := RuleSet{ + chainConfig := ¶ms.ChainConfig{ HomesteadBlock: big.NewInt(1150000), } fn := filepath.Join(stateTestDir, "stSpecialTest.json") - if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil { + if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { t.Error(err) } } func TestStateRefund(t *testing.T) { - ruleSet := RuleSet{ + chainConfig := ¶ms.ChainConfig{ HomesteadBlock: big.NewInt(1150000), } fn := filepath.Join(stateTestDir, "stRefundTest.json") - if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil { + if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { t.Error(err) } } func TestStateBlockHash(t *testing.T) { - ruleSet := RuleSet{ + chainConfig := ¶ms.ChainConfig{ HomesteadBlock: big.NewInt(1150000), } fn := filepath.Join(stateTestDir, "stBlockHashTest.json") - if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil { + if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { t.Error(err) } } func TestStateInitCode(t *testing.T) { - ruleSet := RuleSet{ + chainConfig := ¶ms.ChainConfig{ HomesteadBlock: big.NewInt(1150000), } fn := filepath.Join(stateTestDir, "stInitCodeTest.json") - if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil { + if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { t.Error(err) } } func TestStateLog(t *testing.T) { - ruleSet := RuleSet{ + chainConfig := ¶ms.ChainConfig{ HomesteadBlock: big.NewInt(1150000), } fn := filepath.Join(stateTestDir, "stLogTests.json") - if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil { + if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { t.Error(err) } } func TestStateTransaction(t *testing.T) { - ruleSet := RuleSet{ + chainConfig := ¶ms.ChainConfig{ HomesteadBlock: big.NewInt(1150000), } fn := filepath.Join(stateTestDir, "stTransactionTest.json") - if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil { + if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { t.Error(err) } } func TestStateTransition(t *testing.T) { - ruleSet := RuleSet{ + chainConfig := ¶ms.ChainConfig{ HomesteadBlock: big.NewInt(1150000), } fn := filepath.Join(stateTestDir, "stTransitionTest.json") - if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil { + if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { t.Error(err) } } func TestCallCreateCallCode(t *testing.T) { - ruleSet := RuleSet{ + chainConfig := ¶ms.ChainConfig{ HomesteadBlock: big.NewInt(1150000), } fn := filepath.Join(stateTestDir, "stCallCreateCallCodeTest.json") - if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil { + if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { t.Error(err) } } func TestCallCodes(t *testing.T) { - ruleSet := RuleSet{ + chainConfig := ¶ms.ChainConfig{ HomesteadBlock: big.NewInt(1150000), } fn := filepath.Join(stateTestDir, "stCallCodes.json") - if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil { + if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { t.Error(err) } } func TestDelegateCall(t *testing.T) { - ruleSet := RuleSet{ + chainConfig := ¶ms.ChainConfig{ HomesteadBlock: big.NewInt(1150000), } fn := filepath.Join(stateTestDir, "stDelegatecallTest.json") - if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil { + if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { t.Error(err) } } func TestMemory(t *testing.T) { - ruleSet := RuleSet{ + chainConfig := ¶ms.ChainConfig{ HomesteadBlock: big.NewInt(1150000), } fn := filepath.Join(stateTestDir, "stMemoryTest.json") - if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil { + if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { t.Error(err) } } func TestMemoryStress(t *testing.T) { - ruleSet := RuleSet{ + chainConfig := ¶ms.ChainConfig{ HomesteadBlock: big.NewInt(1150000), } @@ -204,13 +206,13 @@ func TestMemoryStress(t *testing.T) { t.Skip() } fn := filepath.Join(stateTestDir, "stMemoryStressTest.json") - if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil { + if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { t.Error(err) } } func TestQuadraticComplexity(t *testing.T) { - ruleSet := RuleSet{ + chainConfig := ¶ms.ChainConfig{ HomesteadBlock: big.NewInt(1150000), } @@ -218,41 +220,41 @@ func TestQuadraticComplexity(t *testing.T) { t.Skip() } fn := filepath.Join(stateTestDir, "stQuadraticComplexityTest.json") - if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil { + if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { t.Error(err) } } func TestSolidity(t *testing.T) { - ruleSet := RuleSet{ + chainConfig := ¶ms.ChainConfig{ HomesteadBlock: big.NewInt(1150000), } fn := filepath.Join(stateTestDir, "stSolidityTest.json") - if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil { + if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { t.Error(err) } } func TestWallet(t *testing.T) { - ruleSet := RuleSet{ + chainConfig := ¶ms.ChainConfig{ HomesteadBlock: big.NewInt(1150000), } fn := filepath.Join(stateTestDir, "stWalletTest.json") - if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil { + if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { t.Error(err) } } func TestStateTestsRandom(t *testing.T) { - ruleSet := RuleSet{ + chainConfig := ¶ms.ChainConfig{ HomesteadBlock: big.NewInt(1150000), } fns, _ := filepath.Glob("./files/StateTests/RandomTests/*") for _, fn := range fns { - if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil { + if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { t.Error(fn, err) } } @@ -260,117 +262,117 @@ func TestStateTestsRandom(t *testing.T) { // homestead tests func TestHomesteadStateSystemOperations(t *testing.T) { - ruleSet := RuleSet{ + chainConfig := ¶ms.ChainConfig{ HomesteadBlock: new(big.Int), } fn := filepath.Join(stateTestDir, "Homestead", "stSystemOperationsTest.json") - if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil { + if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { t.Error(err) } } func TestHomesteadStatePreCompiledContracts(t *testing.T) { - ruleSet := RuleSet{ + chainConfig := ¶ms.ChainConfig{ HomesteadBlock: new(big.Int), } fn := filepath.Join(stateTestDir, "Homestead", "stPreCompiledContracts.json") - if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil { + if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { t.Error(err) } } func TestHomesteadStateRecursiveCreate(t *testing.T) { - ruleSet := RuleSet{ + chainConfig := ¶ms.ChainConfig{ HomesteadBlock: new(big.Int), } fn := filepath.Join(stateTestDir, "Homestead", "stSpecialTest.json") - if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil { + if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { t.Error(err) } } func TestHomesteadStateRefund(t *testing.T) { - ruleSet := RuleSet{ + chainConfig := ¶ms.ChainConfig{ HomesteadBlock: new(big.Int), } fn := filepath.Join(stateTestDir, "Homestead", "stRefundTest.json") - if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil { + if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { t.Error(err) } } func TestHomesteadStateInitCode(t *testing.T) { - ruleSet := RuleSet{ + chainConfig := ¶ms.ChainConfig{ HomesteadBlock: new(big.Int), } fn := filepath.Join(stateTestDir, "Homestead", "stInitCodeTest.json") - if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil { + if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { t.Error(err) } } func TestHomesteadStateLog(t *testing.T) { - ruleSet := RuleSet{ + chainConfig := ¶ms.ChainConfig{ HomesteadBlock: new(big.Int), } fn := filepath.Join(stateTestDir, "Homestead", "stLogTests.json") - if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil { + if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { t.Error(err) } } func TestHomesteadStateTransaction(t *testing.T) { - ruleSet := RuleSet{ + chainConfig := ¶ms.ChainConfig{ HomesteadBlock: new(big.Int), } fn := filepath.Join(stateTestDir, "Homestead", "stTransactionTest.json") - if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil { + if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { t.Error(err) } } func TestHomesteadCallCreateCallCode(t *testing.T) { - ruleSet := RuleSet{ + chainConfig := ¶ms.ChainConfig{ HomesteadBlock: new(big.Int), } fn := filepath.Join(stateTestDir, "Homestead", "stCallCreateCallCodeTest.json") - if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil { + if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { t.Error(err) } } func TestHomesteadCallCodes(t *testing.T) { - ruleSet := RuleSet{ + chainConfig := ¶ms.ChainConfig{ HomesteadBlock: new(big.Int), } fn := filepath.Join(stateTestDir, "Homestead", "stCallCodes.json") - if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil { + if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { t.Error(err) } } func TestHomesteadMemory(t *testing.T) { - ruleSet := RuleSet{ + chainConfig := ¶ms.ChainConfig{ HomesteadBlock: new(big.Int), } fn := filepath.Join(stateTestDir, "Homestead", "stMemoryTest.json") - if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil { + if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { t.Error(err) } } func TestHomesteadMemoryStress(t *testing.T) { - ruleSet := RuleSet{ + chainConfig := ¶ms.ChainConfig{ HomesteadBlock: new(big.Int), } @@ -378,13 +380,13 @@ func TestHomesteadMemoryStress(t *testing.T) { t.Skip() } fn := filepath.Join(stateTestDir, "Homestead", "stMemoryStressTest.json") - if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil { + if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { t.Error(err) } } func TestHomesteadQuadraticComplexity(t *testing.T) { - ruleSet := RuleSet{ + chainConfig := ¶ms.ChainConfig{ HomesteadBlock: new(big.Int), } @@ -392,286 +394,313 @@ func TestHomesteadQuadraticComplexity(t *testing.T) { t.Skip() } fn := filepath.Join(stateTestDir, "Homestead", "stQuadraticComplexityTest.json") - if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil { + if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { t.Error(err) } } func TestHomesteadWallet(t *testing.T) { - ruleSet := RuleSet{ + chainConfig := ¶ms.ChainConfig{ HomesteadBlock: new(big.Int), } fn := filepath.Join(stateTestDir, "Homestead", "stWalletTest.json") - if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil { + if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { t.Error(err) } } func TestHomesteadDelegateCodes(t *testing.T) { - ruleSet := RuleSet{ + chainConfig := ¶ms.ChainConfig{ HomesteadBlock: new(big.Int), } fn := filepath.Join(stateTestDir, "Homestead", "stCallDelegateCodes.json") - if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil { + if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { t.Error(err) } } func TestHomesteadDelegateCodesCallCode(t *testing.T) { - ruleSet := RuleSet{ + chainConfig := ¶ms.ChainConfig{ HomesteadBlock: new(big.Int), } fn := filepath.Join(stateTestDir, "Homestead", "stCallDelegateCodesCallCode.json") - if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil { + if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { t.Error(err) } } func TestHomesteadBounds(t *testing.T) { - ruleSet := RuleSet{ + chainConfig := ¶ms.ChainConfig{ HomesteadBlock: new(big.Int), } fn := filepath.Join(stateTestDir, "Homestead", "stBoundsTest.json") - if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil { + if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { t.Error(err) } } // EIP150 tests func TestEIP150Specific(t *testing.T) { - ruleSet := RuleSet{ - HomesteadBlock: new(big.Int), - HomesteadGasRepriceBlock: big.NewInt(2457000), + chainConfig := ¶ms.ChainConfig{ + HomesteadBlock: new(big.Int), + EIP150Block: big.NewInt(2457000), } fn := filepath.Join(stateTestDir, "EIP150", "stEIPSpecificTest.json") - if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil { + if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { t.Error(err) } } func TestEIP150SingleCodeGasPrice(t *testing.T) { - ruleSet := RuleSet{ - HomesteadBlock: new(big.Int), - HomesteadGasRepriceBlock: big.NewInt(2457000), + chainConfig := ¶ms.ChainConfig{ + HomesteadBlock: new(big.Int), + EIP150Block: big.NewInt(2457000), } fn := filepath.Join(stateTestDir, "EIP150", "stEIPSingleCodeGasPrices.json") - if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil { + if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { t.Error(err) } } func TestEIP150MemExpandingCalls(t *testing.T) { - ruleSet := RuleSet{ - HomesteadBlock: new(big.Int), - HomesteadGasRepriceBlock: big.NewInt(2457000), + chainConfig := ¶ms.ChainConfig{ + HomesteadBlock: new(big.Int), + EIP150Block: big.NewInt(2457000), } fn := filepath.Join(stateTestDir, "EIP150", "stMemExpandingEIPCalls.json") - if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil { + if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { t.Error(err) } } func TestEIP150HomesteadStateSystemOperations(t *testing.T) { - ruleSet := RuleSet{ - HomesteadBlock: new(big.Int), - HomesteadGasRepriceBlock: big.NewInt(2457000), + chainConfig := ¶ms.ChainConfig{ + HomesteadBlock: new(big.Int), + EIP150Block: big.NewInt(2457000), } fn := filepath.Join(stateTestDir, "EIP150", "Homestead", "stSystemOperationsTest.json") - if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil { + if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { t.Error(err) } } func TestEIP150HomesteadStatePreCompiledContracts(t *testing.T) { - ruleSet := RuleSet{ - HomesteadBlock: new(big.Int), - HomesteadGasRepriceBlock: big.NewInt(2457000), + chainConfig := ¶ms.ChainConfig{ + HomesteadBlock: new(big.Int), + EIP150Block: big.NewInt(2457000), } fn := filepath.Join(stateTestDir, "EIP150", "Homestead", "stPreCompiledContracts.json") - if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil { + if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { t.Error(err) } } func TestEIP150HomesteadStateRecursiveCreate(t *testing.T) { - ruleSet := RuleSet{ - HomesteadBlock: new(big.Int), - HomesteadGasRepriceBlock: big.NewInt(2457000), + chainConfig := ¶ms.ChainConfig{ + HomesteadBlock: new(big.Int), + EIP150Block: big.NewInt(2457000), } fn := filepath.Join(stateTestDir, "EIP150", "Homestead", "stSpecialTest.json") - if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil { + if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { t.Error(err) } } func TestEIP150HomesteadStateRefund(t *testing.T) { - ruleSet := RuleSet{ - HomesteadBlock: new(big.Int), - HomesteadGasRepriceBlock: big.NewInt(2457000), + chainConfig := ¶ms.ChainConfig{ + HomesteadBlock: new(big.Int), + EIP150Block: big.NewInt(2457000), } fn := filepath.Join(stateTestDir, "EIP150", "Homestead", "stRefundTest.json") - if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil { + if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { t.Error(err) } } func TestEIP150HomesteadStateInitCode(t *testing.T) { - ruleSet := RuleSet{ - HomesteadBlock: new(big.Int), - HomesteadGasRepriceBlock: big.NewInt(2457000), + chainConfig := ¶ms.ChainConfig{ + HomesteadBlock: new(big.Int), + EIP150Block: big.NewInt(2457000), } fn := filepath.Join(stateTestDir, "EIP150", "Homestead", "stInitCodeTest.json") - if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil { + if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { t.Error(err) } } func TestEIP150HomesteadStateLog(t *testing.T) { - ruleSet := RuleSet{ - HomesteadBlock: new(big.Int), - HomesteadGasRepriceBlock: big.NewInt(2457000), + chainConfig := ¶ms.ChainConfig{ + HomesteadBlock: new(big.Int), + EIP150Block: big.NewInt(2457000), } fn := filepath.Join(stateTestDir, "EIP150", "Homestead", "stLogTests.json") - if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil { + if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { t.Error(err) } } func TestEIP150HomesteadStateTransaction(t *testing.T) { - ruleSet := RuleSet{ - HomesteadBlock: new(big.Int), - HomesteadGasRepriceBlock: big.NewInt(2457000), + chainConfig := ¶ms.ChainConfig{ + HomesteadBlock: new(big.Int), + EIP150Block: big.NewInt(2457000), } fn := filepath.Join(stateTestDir, "EIP150", "Homestead", "stTransactionTest.json") - if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil { + if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { t.Error(err) } } func TestEIP150HomesteadCallCreateCallCode(t *testing.T) { - ruleSet := RuleSet{ - HomesteadBlock: new(big.Int), - HomesteadGasRepriceBlock: big.NewInt(2457000), + chainConfig := ¶ms.ChainConfig{ + HomesteadBlock: new(big.Int), + EIP150Block: big.NewInt(2457000), } fn := filepath.Join(stateTestDir, "EIP150", "Homestead", "stCallCreateCallCodeTest.json") - if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil { + if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { t.Error(err) } } func TestEIP150HomesteadCallCodes(t *testing.T) { - ruleSet := RuleSet{ - HomesteadBlock: new(big.Int), - HomesteadGasRepriceBlock: big.NewInt(2457000), + chainConfig := ¶ms.ChainConfig{ + HomesteadBlock: new(big.Int), + EIP150Block: big.NewInt(2457000), } fn := filepath.Join(stateTestDir, "EIP150", "Homestead", "stCallCodes.json") - if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil { + if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { t.Error(err) } } func TestEIP150HomesteadMemory(t *testing.T) { - ruleSet := RuleSet{ - HomesteadBlock: new(big.Int), - HomesteadGasRepriceBlock: big.NewInt(2457000), + chainConfig := ¶ms.ChainConfig{ + HomesteadBlock: new(big.Int), + EIP150Block: big.NewInt(2457000), } fn := filepath.Join(stateTestDir, "EIP150", "Homestead", "stMemoryTest.json") - if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil { + if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { t.Error(err) } } func TestEIP150HomesteadMemoryStress(t *testing.T) { - ruleSet := RuleSet{ - HomesteadBlock: new(big.Int), - HomesteadGasRepriceBlock: big.NewInt(2457000), + chainConfig := ¶ms.ChainConfig{ + HomesteadBlock: new(big.Int), + EIP150Block: big.NewInt(2457000), } if os.Getenv("TEST_VM_COMPLEX") == "" { t.Skip() } fn := filepath.Join(stateTestDir, "EIP150", "Homestead", "stMemoryStressTest.json") - if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil { + if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { t.Error(err) } } func TestEIP150HomesteadQuadraticComplexity(t *testing.T) { - ruleSet := RuleSet{ - HomesteadBlock: new(big.Int), - HomesteadGasRepriceBlock: big.NewInt(2457000), + chainConfig := ¶ms.ChainConfig{ + HomesteadBlock: new(big.Int), + EIP150Block: big.NewInt(2457000), } if os.Getenv("TEST_VM_COMPLEX") == "" { t.Skip() } fn := filepath.Join(stateTestDir, "EIP150", "Homestead", "stQuadraticComplexityTest.json") - if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil { + if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { t.Error(err) } } func TestEIP150HomesteadWallet(t *testing.T) { - ruleSet := RuleSet{ - HomesteadBlock: new(big.Int), - HomesteadGasRepriceBlock: big.NewInt(2457000), + chainConfig := ¶ms.ChainConfig{ + HomesteadBlock: new(big.Int), + EIP150Block: big.NewInt(2457000), } fn := filepath.Join(stateTestDir, "EIP150", "Homestead", "stWalletTest.json") - if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil { + if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { t.Error(err) } } func TestEIP150HomesteadDelegateCodes(t *testing.T) { - ruleSet := RuleSet{ - HomesteadBlock: new(big.Int), - HomesteadGasRepriceBlock: big.NewInt(2457000), + chainConfig := ¶ms.ChainConfig{ + HomesteadBlock: new(big.Int), + EIP150Block: big.NewInt(2457000), } fn := filepath.Join(stateTestDir, "EIP150", "Homestead", "stCallDelegateCodes.json") - if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil { + if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { t.Error(err) } } func TestEIP150HomesteadDelegateCodesCallCode(t *testing.T) { - ruleSet := RuleSet{ - HomesteadBlock: new(big.Int), - HomesteadGasRepriceBlock: big.NewInt(2457000), + chainConfig := ¶ms.ChainConfig{ + HomesteadBlock: new(big.Int), + EIP150Block: big.NewInt(2457000), } fn := filepath.Join(stateTestDir, "EIP150", "Homestead", "stCallDelegateCodesCallCode.json") - if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil { + if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { t.Error(err) } } func TestEIP150HomesteadBounds(t *testing.T) { - ruleSet := RuleSet{ - HomesteadBlock: new(big.Int), - HomesteadGasRepriceBlock: big.NewInt(2457000), + chainConfig := ¶ms.ChainConfig{ + HomesteadBlock: new(big.Int), + EIP150Block: big.NewInt(2457000), } fn := filepath.Join(stateTestDir, "EIP150", "Homestead", "stBoundsTest.json") - if err := RunStateTest(ruleSet, fn, StateSkipTests); err != nil { + if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { + t.Error(err) + } +} + +// EIP158 tests +func TestEIP158Create(t *testing.T) { + chainConfig := ¶ms.ChainConfig{ + HomesteadBlock: new(big.Int), + EIP150Block: big.NewInt(2457000), + EIP158Block: big.NewInt(3500000), + } + + fn := filepath.Join(stateTestDir, "EIP158", "stCreateTest.json") + if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { + t.Error(err) + } +} + +func TestEIP158Specific(t *testing.T) { + chainConfig := ¶ms.ChainConfig{ + HomesteadBlock: new(big.Int), + EIP150Block: big.NewInt(2457000), + EIP158Block: big.NewInt(3500000), + } + + fn := filepath.Join(stateTestDir, "EIP158", "stEIP158SpecificTest.json") + if err := RunStateTest(chainConfig, fn, StateSkipTests); err != nil { t.Error(err) } } diff --git a/tests/state_test_util.go b/tests/state_test_util.go index 985271500..6427537b4 100644 --- a/tests/state_test_util.go +++ b/tests/state_test_util.go @@ -33,28 +33,29 @@ import ( "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/params" ) -func RunStateTestWithReader(ruleSet RuleSet, r io.Reader, skipTests []string) error { +func RunStateTestWithReader(chainConfig *params.ChainConfig, r io.Reader, skipTests []string) error { tests := make(map[string]VmTest) if err := readJson(r, &tests); err != nil { return err } - if err := runStateTests(ruleSet, tests, skipTests); err != nil { + if err := runStateTests(chainConfig, tests, skipTests); err != nil { return err } return nil } -func RunStateTest(ruleSet RuleSet, p string, skipTests []string) error { +func RunStateTest(chainConfig *params.ChainConfig, p string, skipTests []string) error { tests := make(map[string]VmTest) if err := readJsonFile(p, &tests); err != nil { return err } - if err := runStateTests(ruleSet, tests, skipTests); err != nil { + if err := runStateTests(chainConfig, tests, skipTests); err != nil { return err } @@ -62,7 +63,7 @@ func RunStateTest(ruleSet RuleSet, p string, skipTests []string) error { } -func BenchStateTest(ruleSet RuleSet, p string, conf bconf, b *testing.B) error { +func BenchStateTest(chainConfig *params.ChainConfig, p string, conf bconf, b *testing.B) error { tests := make(map[string]VmTest) if err := readJsonFile(p, &tests); err != nil { return err @@ -87,22 +88,22 @@ func BenchStateTest(ruleSet RuleSet, p string, conf bconf, b *testing.B) error { b.ResetTimer() for i := 0; i < b.N; i++ { - benchStateTest(ruleSet, test, env, b) + benchStateTest(chainConfig, test, env, b) } return nil } -func benchStateTest(ruleSet RuleSet, test VmTest, env map[string]string, b *testing.B) { +func benchStateTest(chainConfig *params.ChainConfig, test VmTest, env map[string]string, b *testing.B) { b.StopTimer() db, _ := ethdb.NewMemDatabase() statedb := makePreState(db, test.Pre) b.StartTimer() - RunState(ruleSet, statedb, env, test.Exec) + RunState(chainConfig, statedb, env, test.Exec) } -func runStateTests(ruleSet RuleSet, tests map[string]VmTest, skipTests []string) error { +func runStateTests(chainConfig *params.ChainConfig, tests map[string]VmTest, skipTests []string) error { skipTest := make(map[string]bool, len(skipTests)) for _, name := range skipTests { skipTest[name] = true @@ -115,7 +116,7 @@ func runStateTests(ruleSet RuleSet, tests map[string]VmTest, skipTests []string) } //fmt.Println("StateTest:", name) - if err := runStateTest(ruleSet, test); err != nil { + if err := runStateTest(chainConfig, test); err != nil { return fmt.Errorf("%s: %s\n", name, err.Error()) } @@ -126,7 +127,7 @@ func runStateTests(ruleSet RuleSet, tests map[string]VmTest, skipTests []string) } -func runStateTest(ruleSet RuleSet, test VmTest) error { +func runStateTest(chainConfig *params.ChainConfig, test VmTest) error { db, _ := ethdb.NewMemDatabase() statedb := makePreState(db, test.Pre) @@ -150,7 +151,7 @@ func runStateTest(ruleSet RuleSet, test VmTest) error { logs vm.Logs ) - ret, logs, _, _ = RunState(ruleSet, statedb, env, test.Transaction) + ret, logs, _, _ = RunState(chainConfig, statedb, env, test.Transaction) // Compare expected and actual return var rexp []byte @@ -189,7 +190,7 @@ func runStateTest(ruleSet RuleSet, test VmTest) error { } } - root, _ := statedb.Commit() + root, _ := statedb.Commit(false) if common.HexToHash(test.PostStateRoot) != root { return fmt.Errorf("Post state root error. Expected: %s have: %x", test.PostStateRoot, root) } @@ -204,7 +205,7 @@ func runStateTest(ruleSet RuleSet, test VmTest) error { return nil } -func RunState(ruleSet RuleSet, statedb *state.StateDB, env, tx map[string]string) ([]byte, vm.Logs, *big.Int, error) { +func RunState(chainConfig *params.ChainConfig, statedb *state.StateDB, env, tx map[string]string) ([]byte, vm.Logs, *big.Int, error) { var ( data = common.FromHex(tx["data"]) gas = common.Big(tx["gasLimit"]) @@ -226,13 +227,13 @@ func RunState(ruleSet RuleSet, statedb *state.StateDB, env, tx map[string]string key, _ := hex.DecodeString(tx["secretKey"]) addr := crypto.PubkeyToAddress(crypto.ToECDSA(key).PublicKey) message := NewMessage(addr, to, data, value, gas, price, nonce) - vmenv := NewEnvFromMap(ruleSet, statedb, env, tx) + vmenv := NewEnvFromMap(chainConfig, statedb, env, tx) vmenv.origin = addr ret, _, err := core.ApplyMessage(vmenv, message, gaspool) if core.IsNonceErr(err) || core.IsInvalidTxErr(err) || core.IsGasLimitErr(err) { statedb.RevertToSnapshot(snapshot) } - statedb.Commit() + statedb.Commit(chainConfig.IsEIP158(vmenv.BlockNumber())) return ret, vmenv.state.Logs(), vmenv.Gas, err } diff --git a/tests/transaction_test_util.go b/tests/transaction_test_util.go index 178f90284..be3514737 100644 --- a/tests/transaction_test_util.go +++ b/tests/transaction_test_util.go @@ -24,7 +24,6 @@ import ( "runtime" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/params" @@ -164,7 +163,7 @@ func verifyTxFields(txTest TransactionTest, decodedTx *types.Transaction) (err e decodedSender common.Address ) - chainConfig := &core.ChainConfig{HomesteadBlock: params.MainNetHomesteadBlock} + chainConfig := ¶ms.ChainConfig{HomesteadBlock: params.MainNetHomesteadBlock} if chainConfig.IsHomestead(common.String2Big(txTest.Blocknumber)) { decodedSender, err = decodedTx.From() } else { diff --git a/tests/util.go b/tests/util.go index 8359e46fd..5296bb54b 100644 --- a/tests/util.go +++ b/tests/util.go @@ -148,27 +148,8 @@ type VmTest struct { PostStateRoot string } -type RuleSet struct { - HomesteadBlock *big.Int - DAOForkBlock *big.Int - DAOForkSupport bool - HomesteadGasRepriceBlock *big.Int -} - -func (r RuleSet) IsHomestead(n *big.Int) bool { - return n.Cmp(r.HomesteadBlock) >= 0 -} - -func (r RuleSet) GasTable(num *big.Int) params.GasTable { - if r.HomesteadGasRepriceBlock == nil || num == nil || num.Cmp(r.HomesteadGasRepriceBlock) < 0 { - return params.GasTableHomestead - } - - return params.GasTableHomesteadGasRepriceFork -} - type Env struct { - ruleSet RuleSet + chainConfig *params.ChainConfig depth int state *state.StateDB skipTransfer bool @@ -189,16 +170,16 @@ type Env struct { evm *vm.EVM } -func NewEnv(ruleSet RuleSet, state *state.StateDB) *Env { +func NewEnv(chainConfig *params.ChainConfig, state *state.StateDB) *Env { env := &Env{ - ruleSet: ruleSet, - state: state, + chainConfig: chainConfig, + state: state, } return env } -func NewEnvFromMap(ruleSet RuleSet, state *state.StateDB, envValues map[string]string, exeValues map[string]string) *Env { - env := NewEnv(ruleSet, state) +func NewEnvFromMap(chainConfig *params.ChainConfig, state *state.StateDB, envValues map[string]string, exeValues map[string]string) *Env { + env := NewEnv(chainConfig, state) env.origin = common.HexToAddress(exeValues["caller"]) env.parent = common.HexToHash(envValues["previousHash"]) @@ -217,16 +198,16 @@ func NewEnvFromMap(ruleSet RuleSet, state *state.StateDB, envValues map[string]s return env } -func (self *Env) RuleSet() vm.RuleSet { return self.ruleSet } -func (self *Env) Vm() vm.Vm { return self.evm } -func (self *Env) Origin() common.Address { return self.origin } -func (self *Env) BlockNumber() *big.Int { return self.number } -func (self *Env) Coinbase() common.Address { return self.coinbase } -func (self *Env) Time() *big.Int { return self.time } -func (self *Env) Difficulty() *big.Int { return self.difficulty } -func (self *Env) Db() vm.Database { return self.state } -func (self *Env) GasLimit() *big.Int { return self.gasLimit } -func (self *Env) VmType() vm.Type { return vm.StdVmTy } +func (self *Env) ChainConfig() *params.ChainConfig { return self.chainConfig } +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 } +func (self *Env) Coinbase() common.Address { return self.coinbase } +func (self *Env) Time() *big.Int { return self.time } +func (self *Env) Difficulty() *big.Int { return self.difficulty } +func (self *Env) Db() vm.Database { return self.state } +func (self *Env) GasLimit() *big.Int { return self.gasLimit } +func (self *Env) VmType() vm.Type { return vm.StdVmTy } func (self *Env) GetHash(n uint64) common.Hash { return common.BytesToHash(crypto.Keccak256([]byte(big.NewInt(int64(n)).String()))) } diff --git a/tests/vm_test_util.go b/tests/vm_test_util.go index ea683a8d3..f00af87b9 100644 --- a/tests/vm_test_util.go +++ b/tests/vm_test_util.go @@ -225,7 +225,7 @@ func RunVm(state *state.StateDB, env, exec map[string]string) ([]byte, vm.Logs, caller := state.GetOrNewStateObject(from) - vmenv := NewEnvFromMap(RuleSet{params.MainNetHomesteadBlock, params.MainNetDAOForkBlock, true, nil}, state, env, exec) + vmenv := NewEnvFromMap(¶ms.ChainConfig{params.MainNetHomesteadBlock, params.MainNetDAOForkBlock, true, nil, common.Hash{}, nil}, state, env, exec) vmenv.vmTest = true vmenv.skipTransfer = true vmenv.initial = true |