From dc2e34ddf37cc46e32959a50f03e1d7fe1445630 Mon Sep 17 00:00:00 2001 From: Jeffrey Wilcke Date: Thu, 20 Oct 2016 13:36:29 +0200 Subject: core, core/state, trie: Hardfork EIP155, EIP161, EIP170 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 --- core/bench_test.go | 4 +- core/block_validator.go | 24 ++- core/block_validator_test.go | 12 +- core/blockchain.go | 11 +- core/blockchain_test.go | 123 ++++++++++- core/chain_makers.go | 22 +- core/chain_makers_test.go | 7 +- core/chain_pow_test.go | 7 +- core/config.go | 61 ------ core/dao.go | 2 +- core/dao_test.go | 6 +- core/database_util.go | 11 +- core/database_util_test.go | 5 +- core/execution.go | 22 +- core/genesis.go | 6 +- core/headerchain.go | 9 +- core/state/state_object.go | 13 +- core/state/state_test.go | 6 +- core/state/statedb.go | 26 ++- core/state/statedb_test.go | 8 +- core/state/sync_test.go | 2 +- core/state_processor.go | 20 +- core/state_transition.go | 4 +- core/tx_pool.go | 9 +- core/types/transaction.go | 113 +++++----- core/types/transaction_signing.go | 376 +++++++++++++++++++++++++++++++++ core/types/transaction_signing_test.go | 33 +++ core/vm/environment.go | 12 +- core/vm/instructions.go | 4 +- core/vm/jit.go | 2 +- core/vm/jit_test.go | 5 +- core/vm/jump_table.go | 8 +- core/vm/jump_table_test.go | 6 +- core/vm/runtime/env.go | 43 ++-- core/vm/runtime/runtime.go | 7 +- core/vm/vm.go | 35 ++- core/vm_env.go | 37 ++-- 37 files changed, 814 insertions(+), 287 deletions(-) delete mode 100644 core/config.go create mode 100644 core/types/transaction_signing.go create mode 100644 core/types/transaction_signing_test.go (limited to 'core') diff --git a/core/bench_test.go b/core/bench_test.go index 00395efa4..ccf666e9a 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 a7c7f76dd..520877c65 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)) } @@ -248,13 +248,21 @@ func ValidateHeader(config *ChainConfig, pow pow.PoW, header *types.Header, pare } } // If all checks passed, validate the extra-data field for hard forks - return ValidateDAOHeaderExtraData(config, header) + if err := ValidateDAOHeaderExtraData(config, header); err != nil { + return err + } + 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 } // 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 4f11439bc..03dd8bd77 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" @@ -80,7 +81,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 @@ -115,7 +116,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) @@ -904,7 +905,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 @@ -916,7 +917,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 } @@ -1259,4 +1260,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 18fe30f18..0154fc0d6 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(), new(big.Int).Add(block.Difficulty(), blockchain.GetTd(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) { @@ -1128,3 +1128,106 @@ func TestCanonicalBlockRetrieval(t *testing.T) { blockchain.InsertChain(types.Blocks{chain[i]}) } } + +func TestEIP155Transition(t *testing.T) { + // Configure and generate a sample block chain + var ( + db, _ = ethdb.NewMemDatabase() + key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + address = crypto.PubkeyToAddress(key.PublicKey) + funds = big.NewInt(1000000000) + genesis = WriteGenesisBlockForTesting(db, GenesisAccount{address, funds}) + config = ¶ms.ChainConfig{ChainId: big.NewInt(1), EIP155Block: big.NewInt(2), HomesteadBlock: new(big.Int)} + mux event.TypeMux + ) + + blockchain, _ := NewBlockChain(db, config, FakePow{}, &mux) + blocks, _ := GenerateChain(config, genesis, db, 4, func(i int, block *BlockGen) { + var ( + tx *types.Transaction + err error + basicTx = func(signer types.Signer) (*types.Transaction, error) { + tx := types.NewTransaction(block.TxNonce(address), common.Address{}, new(big.Int), big.NewInt(21000), new(big.Int), nil) + tx.SetSigner(signer) + return tx.SignECDSA(key) + } + ) + switch i { + case 0: + tx, err = basicTx(types.HomesteadSigner{}) + if err != nil { + t.Fatal(err) + } + block.AddTx(tx) + case 2: + tx, err = basicTx(types.HomesteadSigner{}) + if err != nil { + t.Fatal(err) + } + block.AddTx(tx) + + tx, err = basicTx(types.NewEIP155Signer(config.ChainId)) + if err != nil { + t.Fatal(err) + } + block.AddTx(tx) + case 3: + tx, err = basicTx(types.HomesteadSigner{}) + if err != nil { + t.Fatal(err) + } + block.AddTx(tx) + + tx, err = basicTx(types.NewEIP155Signer(config.ChainId)) + if err != nil { + t.Fatal(err) + } + block.AddTx(tx) + } + }) + + if _, err := blockchain.InsertChain(blocks); err != nil { + t.Fatal(err) + } + block := blockchain.GetBlockByNumber(1) + if block.Transactions()[0].Protected() { + t.Error("Expected block[0].txs[0] to not be replay protected") + } + + block = blockchain.GetBlockByNumber(3) + if block.Transactions()[0].Protected() { + t.Error("Expected block[3].txs[0] to not be replay protected") + } + if !block.Transactions()[1].Protected() { + t.Error("Expected block[3].txs[1] to be replay protected") + } + if _, err := blockchain.InsertChain(blocks[4:]); err != nil { + t.Fatal(err) + } + + // generate an invalid chain id transaction + config = ¶ms.ChainConfig{ChainId: big.NewInt(2), EIP155Block: big.NewInt(2), HomesteadBlock: new(big.Int)} + blocks, _ = GenerateChain(config, blocks[len(blocks)-1], db, 4, func(i int, block *BlockGen) { + var ( + tx *types.Transaction + err error + basicTx = func(signer types.Signer) (*types.Transaction, error) { + tx := types.NewTransaction(block.TxNonce(address), common.Address{}, new(big.Int), big.NewInt(21000), new(big.Int), nil) + tx.SetSigner(signer) + return tx.SignECDSA(key) + } + ) + switch i { + case 0: + tx, err = basicTx(types.NewEIP155Signer(big.NewInt(2))) + if err != nil { + t.Fatal(err) + } + block.AddTx(tx) + } + }) + _, err := blockchain.InsertChain(blocks) + if err != types.ErrInvalidChainId { + t.Error("expected error:", types.ErrInvalidChainId) + } +} 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/config.go b/core/config.go deleted file mode 100644 index 3ab04e520..000000000 --- a/core/config.go +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package core - -import ( - "errors" - "math/big" - - "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 -// that any network, identified by its genesis block, can have its own -// set of configuration options. -type ChainConfig struct { - HomesteadBlock *big.Int `json:"homesteadBlock"` // Homestead switch block (nil = no fork, 0 = already homestead) - 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) - - VmConfig vm.Config `json:"-"` -} - -// IsHomestead returns whether num is either equal to the homestead block or greater. -func (c *ChainConfig) IsHomestead(num *big.Int) bool { - if c.HomesteadBlock == nil || num == nil { - return false - } - return num.Cmp(c.HomesteadBlock) >= 0 -} - -// 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 - } - - return params.GasTableHomesteadGasRepriceFork -} 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 3a110f7d0..62521cba6 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" ) @@ -52,7 +54,8 @@ var ( blockHashPrefix = []byte("block-hash-") // [deprecated by the header/block split, remove eventually] - configPrefix = []byte("ethereum-config-") // config prefix for the db + configPrefix = []byte("ethereum-config-") // config prefix for the db + ChainConfigNotFoundErr = errors.New("ChainConfig not found") // general config not found error ) // GetCanonicalHash retrieves a hash assigned to a canonical block number. @@ -536,7 +539,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 { @@ -552,13 +555,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 3c3837163..0ff4046dc 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..b049e556b 100644 --- a/core/execution.go +++ b/core/execution.go @@ -27,14 +27,14 @@ 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) + ret, _, err = exec(true, env, caller, &addr, &addr, env.Db().GetCodeHash(addr), input, env.Db().GetCode(addr), gas, gasPrice, value) 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) + ret, _, err = exec(false, env, caller, &callerAddr, &addr, env.Db().GetCodeHash(addr), input, env.Db().GetCode(addr), gas, gasPrice, value) return ret, err } @@ -49,7 +49,7 @@ func DelegateCall(env vm.Environment, caller vm.ContractRef, addr common.Address // 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) + ret, address, err = exec(true, 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 @@ -59,7 +59,7 @@ func Create(env vm.Environment, caller vm.ContractRef, code []byte, gas, gasPric return ret, address, 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) { +func exec(transfers bool, 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() // Depth check execution. Fail if we're trying to execute above the // limit. @@ -92,14 +92,24 @@ func exec(env vm.Environment, caller vm.ContractRef, address, codeAddr *common.A ) if createAccount { to = env.Db().CreateAccount(*address) + if env.ChainConfig().IsEIP158(env.BlockNumber()) { + env.Db().SetNonce(*address, 1) + } } else { if !env.Db().Exist(*address) { + if vm.Precompiled[address.Str()] == nil && env.ChainConfig().IsEIP158(env.BlockNumber()) && value.BitLen() == 0 { + caller.ReturnGas(gas, gasPrice) + return nil, common.Address{}, nil + } + to = env.Db().CreateAccount(*address) } else { to = env.Db().GetAccount(*address) } } - env.Transfer(from, to, value) + if transfers { + 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 @@ -126,7 +136,7 @@ func exec(env vm.Environment, caller vm.ContractRef, address, codeAddr *common.A // When an error was returned by the EVM or when setting the creation code // above we revert to the snapshot and consume any gas remaining. Additionally // when we're in homestead this also counts for code storage gas errors. - if err != nil && (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) diff --git a/core/genesis.go b/core/genesis.go index 40d799621..52c53d2ce 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 3503a1c33..b91b1cf71 100644 --- a/core/headerchain.go +++ b/core/headerchain.go @@ -31,6 +31,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" ) @@ -41,7 +42,7 @@ import ( // 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 @@ -65,7 +66,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) @@ -443,13 +444,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 dcb897628..31957a6fe 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -213,6 +213,11 @@ func (self *StateDB) Exist(addr common.Address) bool { return self.GetStateObject(addr) != nil } +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 +521,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 +558,18 @@ 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) + return root, batch } @@ -573,16 +579,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 949df7301..c40bbabaa 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..84c461665 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,18 @@ 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) { + var signer types.Signer + switch { + case config.IsEIP155(header.Number): + signer = types.NewEIP155Signer(config.ChainId) + case config.IsHomestead(header.Number): + signer = types.HomesteadSigner{} + default: + signer = types.FrontierSigner{} + } + tx.SetSigner(signer) + _, gas, err := ApplyMessage(NewEnv(statedb, config, bc, tx, header, cfg), tx, gp) if err != nil { return nil, nil, nil, err @@ -97,7 +109,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 c8160424b..f3e752048 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -138,7 +138,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() @@ -231,7 +231,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 2c8a5c396..9e97ad8f7 100644 --- a/core/tx_pool.go +++ b/core/tx_pool.go @@ -30,6 +30,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" "gopkg.in/karalabe/cookiejar.v2/collections/prque" ) @@ -65,7 +66,9 @@ 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 + signer types.Signer + 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 @@ -86,9 +89,10 @@ 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, + signer: types.NewEIP155Signer(config.ChainId), pending: make(map[common.Address]*txList), queue: make(map[common.Address]*txList), all: make(map[common.Hash]*types.Transaction), @@ -253,6 +257,7 @@ func (pool *TxPool) validateTx(tx *types.Transaction) error { return err } + tx.SetSigner(pool.signer) from, err := tx.From() if err != nil { return ErrInvalidSender diff --git a/core/types/transaction.go b/core/types/transaction.go index b826a8900..084c42ab7 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -27,14 +27,14 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/logger" - "github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/rlp" ) var ErrInvalidSig = errors.New("invalid v, r, s values") type Transaction struct { + signer Signer + data txdata // caches hash atomic.Value @@ -48,24 +48,27 @@ type txdata struct { Recipient *common.Address `rlp:"nil"` // nil means contract creation Amount *big.Int Payload []byte - V byte // signature - R, S *big.Int // signature + V, R, S *big.Int // signature } func NewContractCreation(nonce uint64, amount, gasLimit, gasPrice *big.Int, data []byte) *Transaction { if len(data) > 0 { data = common.CopyBytes(data) } - return &Transaction{data: txdata{ - AccountNonce: nonce, - Recipient: nil, - Amount: new(big.Int).Set(amount), - GasLimit: new(big.Int).Set(gasLimit), - Price: new(big.Int).Set(gasPrice), - Payload: data, - R: new(big.Int), - S: new(big.Int), - }} + return &Transaction{ + signer: HomesteadSigner{}, + data: txdata{ + AccountNonce: nonce, + Recipient: nil, + Amount: new(big.Int).Set(amount), + GasLimit: new(big.Int).Set(gasLimit), + Price: new(big.Int).Set(gasPrice), + Payload: data, + V: new(big.Int), + R: new(big.Int), + S: new(big.Int), + }, + } } func NewTransaction(nonce uint64, to common.Address, amount, gasLimit, gasPrice *big.Int, data []byte) *Transaction { @@ -79,6 +82,7 @@ func NewTransaction(nonce uint64, to common.Address, amount, gasLimit, gasPrice Amount: new(big.Int), GasLimit: new(big.Int), Price: new(big.Int), + V: new(big.Int), R: new(big.Int), S: new(big.Int), } @@ -91,7 +95,24 @@ func NewTransaction(nonce uint64, to common.Address, amount, gasLimit, gasPrice if gasPrice != nil { d.Price.Set(gasPrice) } - return &Transaction{data: d} + return &Transaction{signer: HomesteadSigner{}, data: d} +} + +func (tx *Transaction) SetSigner(s Signer) { + // reset the cached value incase another value was cached + tx.from.Store((*common.Address)(nil)) + + tx.signer = s +} + +// ChainId returns which chain id this transaction was signed for (if at all) +func (tx *Transaction) ChainId() *big.Int { + return deriveChainId(tx.data.V) +} + +// Protected returns whether the transaction is pretected from replay protection +func (tx *Transaction) Protected() bool { + return isProtectedV(tx.data.V) } func (tx *Transaction) EncodeRLP(w io.Writer) error { @@ -104,6 +125,7 @@ func (tx *Transaction) DecodeRLP(s *rlp.Stream) error { if err == nil { tx.size.Store(common.StorageSize(rlp.ListSize(size))) } + tx.signer = HomesteadSigner{} return err } @@ -136,14 +158,7 @@ func (tx *Transaction) Hash() common.Hash { // SigHash returns the hash to be signed by the sender. // It does not uniquely identify the transaction. func (tx *Transaction) SigHash() common.Hash { - return rlpHash([]interface{}{ - tx.data.AccountNonce, - tx.data.Price, - tx.data.GasLimit, - tx.data.Recipient, - tx.data.Amount, - tx.data.Payload, - }) + return tx.signer.Hash(tx) } func (tx *Transaction) Size() common.StorageSize { @@ -194,15 +209,17 @@ func (tx *Transaction) FromFrontier() (common.Address, error) { func doFrom(tx *Transaction, homestead bool) (common.Address, error) { if from := tx.from.Load(); from != nil { - return from.(common.Address), nil + if faddr := from.(*common.Address); faddr != nil { + return *faddr, nil + } } - pubkey, err := tx.publicKey(homestead) + pubkey, err := tx.signer.PublicKey(tx) if err != nil { return common.Address{}, err } var addr common.Address copy(addr[:], crypto.Keccak256(pubkey[1:])[12:]) - tx.from.Store(addr) + tx.from.Store(&addr) return addr, nil } @@ -214,52 +231,20 @@ func (tx *Transaction) Cost() *big.Int { } func (tx *Transaction) SignatureValues() (v byte, r *big.Int, s *big.Int) { - return tx.data.V, new(big.Int).Set(tx.data.R), new(big.Int).Set(tx.data.S) + return SignatureValues(tx.signer, tx) } -func (tx *Transaction) publicKey(homestead bool) ([]byte, error) { - if !crypto.ValidateSignatureValues(tx.data.V, tx.data.R, tx.data.S, homestead) { - return nil, ErrInvalidSig - } - - // encode the signature in uncompressed format - r, s := tx.data.R.Bytes(), tx.data.S.Bytes() - sig := make([]byte, 65) - copy(sig[32-len(r):32], r) - copy(sig[64-len(s):64], s) - sig[64] = tx.data.V - 27 - - // recover the public key from the signature - hash := tx.SigHash() - pub, err := crypto.Ecrecover(hash[:], sig) - if err != nil { - glog.V(logger.Error).Infof("Could not get pubkey from signature: ", err) - return nil, err - } - if len(pub) == 0 || pub[0] != 4 { - return nil, errors.New("invalid public key") - } - return pub, nil +func (tx *Transaction) RawSignatureValues() (v *big.Int, r *big.Int, s *big.Int) { + return tx.data.V, tx.data.R, tx.data.S } func (tx *Transaction) WithSignature(sig []byte) (*Transaction, error) { - if len(sig) != 65 { - panic(fmt.Sprintf("wrong size for signature: got %d, want 65", len(sig))) - } - cpy := &Transaction{data: tx.data} - cpy.data.R = new(big.Int).SetBytes(sig[:32]) - cpy.data.S = new(big.Int).SetBytes(sig[32:64]) - cpy.data.V = sig[64] + 27 - return cpy, nil + return tx.signer.WithSignature(tx, sig) } func (tx *Transaction) SignECDSA(prv *ecdsa.PrivateKey) (*Transaction, error) { - h := tx.SigHash() - sig, err := crypto.Sign(h[:], prv) - if err != nil { - return nil, err - } - return tx.WithSignature(sig) + tx, err := tx.signer.SignECDSA(tx, prv) + return tx, err } func (tx *Transaction) String() string { diff --git a/core/types/transaction_signing.go b/core/types/transaction_signing.go new file mode 100644 index 000000000..e63c25060 --- /dev/null +++ b/core/types/transaction_signing.go @@ -0,0 +1,376 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package types + +import ( + "crypto/ecdsa" + "errors" + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/params" +) + +var ErrInvalidChainId = errors.New("invalid chain id for signer") + +// deriveSigner makes a *best* guess about which signer to use. +func deriveSigner(V *big.Int) Signer { + if V.BitLen() > 0 && isProtectedV(V) { + return EIP155Signer{chainId: deriveChainId(V)} + } else { + return HomesteadSigner{} + } +} + +func pickSigner(rules params.Rules) Signer { + var signer Signer + switch { + case rules.IsEIP155: + signer = NewEIP155Signer(rules.ChainId) + case rules.IsHomestead: + signer = HomesteadSigner{} + default: + signer = FrontierSigner{} + } + return signer +} + +func isProtectedV(V *big.Int) bool { + if V.BitLen() <= 8 { + v := V.Uint64() + return v != 27 && v != 28 + } + // anything not 27 or 28 are considered unprotected + return true +} + +// MakeSigner returns a Signer based on the given chain config and block number. +func MakeSigner(config *params.ChainConfig, blockNumber *big.Int) Signer { + var signer Signer + switch { + case config.IsEIP155(blockNumber): + signer = NewEIP155Signer(config.ChainId) + case config.IsHomestead(blockNumber): + signer = HomesteadSigner{} + default: + signer = FrontierSigner{} + } + return signer +} + +// SignECDSA signs the transaction using the given signer and private key +func SignECDSA(s Signer, tx *Transaction, prv *ecdsa.PrivateKey) (*Transaction, error) { + h := s.Hash(tx) + sig, err := crypto.Sign(h[:], prv) + if err != nil { + return nil, err + } + return s.WithSignature(tx, sig) +} + +// From derives the sender from the tx using the signer derivation +// functions. + +// From returns the address derived from the signature (V, R, S) using secp256k1 +// elliptic curve and an error if it failed deriving or upon an incorrect +// signature. +// +// From may cache the address, allowing it to be used regardless of +// signing method. +func From(signer Signer, tx *Transaction, cache bool) (common.Address, error) { + if from := tx.from.Load(); from != nil { + return from.(common.Address), nil + } + + pubkey, err := signer.PublicKey(tx) + if err != nil { + return common.Address{}, err + } + var addr common.Address + copy(addr[:], crypto.Keccak256(pubkey[1:])[12:]) + if cache { + tx.from.Store(addr) + } + return addr, nil +} + +// SignatureValues returns the ECDSA signature values contained in the transaction. +func SignatureValues(signer Signer, tx *Transaction) (v byte, r *big.Int, s *big.Int) { + return normaliseV(signer, tx.data.V), new(big.Int).Set(tx.data.R), new(big.Int).Set(tx.data.S) +} + +type Signer interface { + // Hash returns the rlp encoded hash for signatures + Hash(tx *Transaction) common.Hash + // PubilcKey returns the public key derived from the signature + PublicKey(tx *Transaction) ([]byte, error) + // SignECDSA signs the transaction with the given and returns a copy of the tx + SignECDSA(tx *Transaction, prv *ecdsa.PrivateKey) (*Transaction, error) + // WithSignature returns a copy of the transaction with the given signature + WithSignature(tx *Transaction, sig []byte) (*Transaction, error) +} + +// EIP155Transaction implements TransactionInterface using the +// EIP155 rules +type EIP155Signer struct { + HomesteadSigner + + chainId, chainIdMul *big.Int +} + +func NewEIP155Signer(chainId *big.Int) EIP155Signer { + return EIP155Signer{ + chainId: chainId, + chainIdMul: new(big.Int).Mul(chainId, big.NewInt(2)), + } +} + +func (s EIP155Signer) SignECDSA(tx *Transaction, prv *ecdsa.PrivateKey) (*Transaction, error) { + return SignECDSA(s, tx, prv) +} + +func (s EIP155Signer) PublicKey(tx *Transaction) ([]byte, error) { + // if the transaction is not protected fall back to homestead signer + if !tx.Protected() { + return (HomesteadSigner{}).PublicKey(tx) + } + if tx.ChainId().Cmp(s.chainId) != 0 { + return nil, ErrInvalidChainId + } + + V := normaliseV(s, tx.data.V) + if !crypto.ValidateSignatureValues(V, tx.data.R, tx.data.S, true) { + return nil, ErrInvalidSig + } + + // encode the signature in uncompressed format + R, S := tx.data.R.Bytes(), tx.data.S.Bytes() + sig := make([]byte, 65) + copy(sig[32-len(R):32], R) + copy(sig[64-len(S):64], S) + sig[64] = V - 27 + + // recover the public key from the signature + hash := s.Hash(tx) + pub, err := crypto.Ecrecover(hash[:], sig) + if err != nil { + return nil, err + } + if len(pub) == 0 || pub[0] != 4 { + return nil, errors.New("invalid public key") + } + return pub, nil +} + +// WithSignature returns a new transaction with the given signature. +// This signature needs to be formatted as described in the yellow paper (v+27). +func (s EIP155Signer) WithSignature(tx *Transaction, sig []byte) (*Transaction, error) { + if len(sig) != 65 { + panic(fmt.Sprintf("wrong size for snature: got %d, want 65", len(sig))) + } + + cpy := &Transaction{signer: tx.signer, data: tx.data} + cpy.data.R = new(big.Int).SetBytes(sig[:32]) + cpy.data.S = new(big.Int).SetBytes(sig[32:64]) + cpy.data.V = new(big.Int).SetBytes([]byte{sig[64]}) + if s.chainId.BitLen() > 0 { + cpy.data.V = big.NewInt(int64(sig[64] + 35)) + cpy.data.V.Add(cpy.data.V, s.chainIdMul) + } + return cpy, nil +} + +// Hash returns the hash to be signed by the sender. +// It does not uniquely identify the transaction. +func (s EIP155Signer) Hash(tx *Transaction) common.Hash { + return rlpHash([]interface{}{ + tx.data.AccountNonce, + tx.data.Price, + tx.data.GasLimit, + tx.data.Recipient, + tx.data.Amount, + tx.data.Payload, + s.chainId, uint(0), uint(0), + }) +} + +func (s EIP155Signer) SigECDSA(tx *Transaction, prv *ecdsa.PrivateKey) (*Transaction, error) { + h := s.Hash(tx) + sig, err := crypto.Sign(h[:], prv) + if err != nil { + return nil, err + } + return s.WithSignature(tx, sig) +} + +// HomesteadTransaction implements TransactionInterface using the +// homestead rules. +type HomesteadSigner struct{} + +// WithSignature returns a new transaction with the given snature. +// This snature needs to be formatted as described in the yellow paper (v+27). +func (hs HomesteadSigner) WithSignature(tx *Transaction, sig []byte) (*Transaction, error) { + if len(sig) != 65 { + panic(fmt.Sprintf("wrong size for snature: got %d, want 65", len(sig))) + } + cpy := &Transaction{signer: tx.signer, data: tx.data} + cpy.data.R = new(big.Int).SetBytes(sig[:32]) + cpy.data.S = new(big.Int).SetBytes(sig[32:64]) + cpy.data.V = new(big.Int).SetBytes([]byte{sig[64] + 27}) + return cpy, nil +} + +func (hs HomesteadSigner) SignECDSA(tx *Transaction, prv *ecdsa.PrivateKey) (*Transaction, error) { + h := hs.Hash(tx) + sig, err := crypto.Sign(h[:], prv) + if err != nil { + return nil, err + } + return hs.WithSignature(tx, sig) +} + +func (hs HomesteadSigner) PublicKey(tx *Transaction) ([]byte, error) { + if tx.data.V.BitLen() > 8 { + return nil, ErrInvalidSig + } + V := byte(tx.data.V.Uint64()) + if !crypto.ValidateSignatureValues(V, tx.data.R, tx.data.S, true) { + return nil, ErrInvalidSig + } + // encode the snature in uncompressed format + r, s := tx.data.R.Bytes(), tx.data.S.Bytes() + sig := make([]byte, 65) + copy(sig[32-len(r):32], r) + copy(sig[64-len(s):64], s) + sig[64] = V - 27 + + // recover the public key from the snature + hash := hs.Hash(tx) + pub, err := crypto.Ecrecover(hash[:], sig) + if err != nil { + return nil, err + } + if len(pub) == 0 || pub[0] != 4 { + return nil, errors.New("invalid public key") + } + return pub, nil +} + +// Hash returns the hash to be sned by the sender. +// It does not uniquely identify the transaction. +func (hs HomesteadSigner) Hash(tx *Transaction) common.Hash { + return rlpHash([]interface{}{ + tx.data.AccountNonce, + tx.data.Price, + tx.data.GasLimit, + tx.data.Recipient, + tx.data.Amount, + tx.data.Payload, + }) +} + +type FrontierSigner struct{} + +// WithSignature returns a new transaction with the given snature. +// This snature needs to be formatted as described in the yellow paper (v+27). +func (fs FrontierSigner) WithSignature(tx *Transaction, sig []byte) (*Transaction, error) { + if len(sig) != 65 { + panic(fmt.Sprintf("wrong size for snature: got %d, want 65", len(sig))) + } + cpy := &Transaction{signer: tx.signer, data: tx.data} + cpy.data.R = new(big.Int).SetBytes(sig[:32]) + cpy.data.S = new(big.Int).SetBytes(sig[32:64]) + cpy.data.V = new(big.Int).SetBytes([]byte{sig[64] + 27}) + return cpy, nil +} + +func (fs FrontierSigner) SignECDSA(tx *Transaction, prv *ecdsa.PrivateKey) (*Transaction, error) { + h := fs.Hash(tx) + sig, err := crypto.Sign(h[:], prv) + if err != nil { + return nil, err + } + return fs.WithSignature(tx, sig) +} + +// Hash returns the hash to be sned by the sender. +// It does not uniquely identify the transaction. +func (fs FrontierSigner) Hash(tx *Transaction) common.Hash { + return rlpHash([]interface{}{ + tx.data.AccountNonce, + tx.data.Price, + tx.data.GasLimit, + tx.data.Recipient, + tx.data.Amount, + tx.data.Payload, + }) +} + +func (fs FrontierSigner) PublicKey(tx *Transaction) ([]byte, error) { + if tx.data.V.BitLen() > 8 { + return nil, ErrInvalidSig + } + + V := byte(tx.data.V.Uint64()) + if !crypto.ValidateSignatureValues(V, tx.data.R, tx.data.S, false) { + return nil, ErrInvalidSig + } + // encode the snature in uncompressed format + r, s := tx.data.R.Bytes(), tx.data.S.Bytes() + sig := make([]byte, 65) + copy(sig[32-len(r):32], r) + copy(sig[64-len(s):64], s) + sig[64] = V - 27 + + // recover the public key from the snature + hash := fs.Hash(tx) + pub, err := crypto.Ecrecover(hash[:], sig) + if err != nil { + return nil, err + } + if len(pub) == 0 || pub[0] != 4 { + return nil, errors.New("invalid public key") + } + return pub, nil +} + +// normaliseV returns the Ethereum version of the V parameter +func normaliseV(s Signer, v *big.Int) byte { + if s, ok := s.(EIP155Signer); ok { + stdV := v.BitLen() <= 8 && (v.Uint64() == 27 || v.Uint64() == 28) + if s.chainId.BitLen() > 0 && !stdV { + nv := byte((new(big.Int).Sub(v, s.chainIdMul).Uint64()) - 35 + 27) + return nv + } + } + return byte(v.Uint64()) +} + +// deriveChainId derives the chain id from the given v parameter +func deriveChainId(v *big.Int) *big.Int { + if v.BitLen() <= 64 { + v := v.Uint64() + if v == 27 || v == 28 { + return new(big.Int) + } + return new(big.Int).SetUint64((v - 35) / 2) + } + v = new(big.Int).Sub(v, big.NewInt(35)) + return v.Div(v, big.NewInt(2)) +} diff --git a/core/types/transaction_signing_test.go b/core/types/transaction_signing_test.go new file mode 100644 index 000000000..ab4eb2708 --- /dev/null +++ b/core/types/transaction_signing_test.go @@ -0,0 +1,33 @@ +package types + +import ( + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/common" +) + +func TestChainId(t *testing.T) { + key, _ := defaultTestKey() + + tx := NewTransaction(0, common.Address{}, new(big.Int), new(big.Int), new(big.Int), nil) + tx.SetSigner(NewEIP155Signer(big.NewInt(1))) + + var err error + tx, err = tx.SignECDSA(key) + if err != nil { + t.Fatal(err) + } + + tx.SetSigner(NewEIP155Signer(big.NewInt(2))) + _, err = tx.From() + if err != ErrInvalidChainId { + t.Error("expected error:", ErrInvalidChainId) + } + + tx.SetSigner(NewEIP155Signer(big.NewInt(1))) + _, err = tx.From() + if err != nil { + t.Error("expected no error") + } +} diff --git a/core/vm/environment.go b/core/vm/environment.go index f8996e648..803d51f35 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,7 @@ 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(common.Address) bool } // Account represents a contract or basic ethereum account. diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 5682d2a1b..861593b36 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -514,7 +514,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) } @@ -525,7 +525,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 7e16301bf..3fdd22904 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 502a221a9..0c74f7481 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 @@ -175,7 +176,9 @@ func NewEnv(noJit, forceJit bool) *Env { return env } -func (self *Env) RuleSet() RuleSet { return ruleSet{new(big.Int)} } +func (self *Env) ChainConfig() *params.ChainConfig { + return params.TestChainConfig +} 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 7722cbe4d..ef583a8c6 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 @@ -49,14 +50,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, @@ -79,16 +80,16 @@ func (self *Env) AddStructLog(log vm.StructLog) { self.logs = append(self.logs, log) } -func (self *Env) RuleSet() vm.RuleSet { return self.ruleSet } -func (self *Env) Vm() vm.Vm { return self.evm } -func (self *Env) Origin() common.Address { return self.origin } -func (self *Env) BlockNumber() *big.Int { return self.number } -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..aa944ffdc 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 = params.TestChainConfig } if cfg.Difficulty == nil { diff --git a/core/vm/vm.go b/core/vm/vm.go index a73dfc811..1c2229be5 100644 --- a/core/vm/vm.go +++ b/core/vm/vm.go @@ -57,10 +57,10 @@ 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, logger: logger, - gasTable: env.RuleSet().GasTable(env.BlockNumber()), + gasTable: env.ChainConfig().GasTable(env.BlockNumber()), } } @@ -177,6 +177,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 { @@ -256,10 +257,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) } } @@ -304,7 +315,8 @@ func calculateGasAndSize(gasTable params.GasTable, env Environment, contract *Co quadMemGas(mem, newMemSize, gas) case EXP: - gas.Add(gas, new(big.Int).Mul(big.NewInt(int64(len(stack.data[stack.len()-2].Bytes()))), params.ExpByteGas)) + expByteLen := int64((stack.data[stack.len()-2].BitLen() + 7) / 8) + gas.Add(gas, new(big.Int).Mul(big.NewInt(expByteLen), gasTable.ExpByte)) case SSTORE: err := stack.require(2) if err != nil { @@ -380,12 +392,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 c8b77138e..010d6ef22 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,11 +42,11 @@ 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 @@ -53,7 +54,7 @@ type VMEnv struct { 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, @@ -72,18 +73,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) } -- cgit v1.2.3