aboutsummaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
authorFelix Lange <fjl@twurst.com>2016-11-14 17:52:02 +0800
committerGitHub <noreply@github.com>2016-11-14 17:52:02 +0800
commitca73dea3b9bcdf3b5424b5c48c70817439e2e304 (patch)
tree670e2833878e72555644fbbd81db6c5a1b44493f /core
parent21701190ac0a838e347f31b7a918bb0a60c1e8c1 (diff)
parent648bd22427000b6e20a5e1a9c397005aa1ad4f9b (diff)
downloadgo-tangerine-ca73dea3b9bcdf3b5424b5c48c70817439e2e304.tar.gz
go-tangerine-ca73dea3b9bcdf3b5424b5c48c70817439e2e304.tar.zst
go-tangerine-ca73dea3b9bcdf3b5424b5c48c70817439e2e304.zip
Merge pull request #3179 from obscuren/eip-158
EIP158 & 160 Hardfork
Diffstat (limited to 'core')
-rw-r--r--core/bench_test.go8
-rw-r--r--core/block_validator.go20
-rw-r--r--core/block_validator_test.go12
-rw-r--r--core/blockchain.go24
-rw-r--r--core/blockchain_test.go148
-rw-r--r--core/chain_makers.go22
-rw-r--r--core/chain_makers_test.go14
-rw-r--r--core/chain_pow_test.go7
-rw-r--r--core/config.go63
-rw-r--r--core/dao.go2
-rw-r--r--core/dao_test.go6
-rw-r--r--core/database_util.go10
-rw-r--r--core/database_util_test.go5
-rw-r--r--core/execution.go168
-rw-r--r--core/genesis.go6
-rw-r--r--core/headerchain.go9
-rw-r--r--core/state/state_object.go13
-rw-r--r--core/state/state_test.go6
-rw-r--r--core/state/statedb.go27
-rw-r--r--core/state/statedb_test.go8
-rw-r--r--core/state/sync_test.go2
-rw-r--r--core/state_processor.go28
-rw-r--r--core/state_transition.go41
-rw-r--r--core/tx_pool.go23
-rw-r--r--core/tx_pool_test.go51
-rw-r--r--core/types/block_test.go7
-rw-r--r--core/types/json_test.go5
-rw-r--r--core/types/transaction.go260
-rw-r--r--core/types/transaction_signing.go340
-rw-r--r--core/types/transaction_signing_test.go116
-rw-r--r--core/types/transaction_test.go28
-rw-r--r--core/vm/environment.go14
-rw-r--r--core/vm/instructions.go4
-rw-r--r--core/vm/jit.go2
-rw-r--r--core/vm/jit_test.go5
-rw-r--r--core/vm/jump_table.go8
-rw-r--r--core/vm/jump_table_test.go6
-rw-r--r--core/vm/runtime/env.go43
-rw-r--r--core/vm/runtime/runtime.go15
-rw-r--r--core/vm/vm.go35
-rw-r--r--core/vm_env.go37
41 files changed, 1167 insertions, 481 deletions
diff --git a/core/bench_test.go b/core/bench_test.go
index 344e7e3c5..a208ea250 100644
--- a/core/bench_test.go
+++ b/core/bench_test.go
@@ -83,7 +83,7 @@ func genValueTx(nbytes int) func(int, *BlockGen) {
toaddr := common.Address{}
data := make([]byte, nbytes)
gas := IntrinsicGas(data, false, false)
- tx, _ := types.NewTransaction(gen.TxNonce(benchRootAddr), toaddr, big.NewInt(1), gas, nil, data).SignECDSA(benchRootKey)
+ tx, _ := types.NewTransaction(gen.TxNonce(benchRootAddr), toaddr, big.NewInt(1), gas, nil, data).SignECDSA(types.HomesteadSigner{}, benchRootKey)
gen.AddTx(tx)
}
}
@@ -123,7 +123,7 @@ func genTxRing(naccounts int) func(int, *BlockGen) {
nil,
nil,
)
- tx, _ = tx.SignECDSA(ringKeys[from])
+ tx, _ = tx.SignECDSA(types.HomesteadSigner{}, ringKeys[from])
gen.AddTx(tx)
from = to
}
@@ -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, &params.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 &params.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..bbcff3b92 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)
@@ -633,17 +634,19 @@ func (self *BlockChain) Rollback(chain []common.Hash) {
}
// SetReceiptsData computes all the non-consensus fields of the receipts
-func SetReceiptsData(block *types.Block, receipts types.Receipts) {
+func SetReceiptsData(config *params.ChainConfig, block *types.Block, receipts types.Receipts) {
+ signer := types.MakeSigner(config, block.Number())
+
transactions, logIndex := block.Transactions(), uint(0)
for j := 0; j < len(receipts); j++ {
// The transaction hash can be retrieved from the transaction itself
receipts[j].TxHash = transactions[j].Hash()
+ tx, _ := transactions[j].AsMessage(signer)
// The contract address can be derived from the transaction itself
- if MessageCreatesContract(transactions[j]) {
- from, _ := transactions[j].From()
- receipts[j].ContractAddress = crypto.CreateAddress(from, transactions[j].Nonce())
+ if MessageCreatesContract(tx) {
+ receipts[j].ContractAddress = crypto.CreateAddress(tx.From(), tx.Nonce())
}
// The used gas can be calculated based on previous receipts
if j == 0 {
@@ -665,6 +668,7 @@ func SetReceiptsData(block *types.Block, receipts types.Receipts) {
// InsertReceiptChain attempts to complete an already existing header chain with
// transaction and receipt data.
+// XXX should this be moved to the test?
func (self *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain []types.Receipts) (int, error) {
self.wg.Add(1)
defer self.wg.Done()
@@ -704,7 +708,7 @@ func (self *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain
continue
}
// Compute all the non-consensus fields of the receipts
- SetReceiptsData(block, receipts)
+ SetReceiptsData(self.config, block, receipts)
// Write all the data out into the database
if err := WriteBody(self.chainDb, block.Hash(), block.NumberU64(), block.Body()); err != nil {
errs[index] = fmt.Errorf("failed to write block body: %v", err)
@@ -924,7 +928,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 +940,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 +1313,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..dae857f01 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
@@ -711,14 +711,15 @@ func TestFastVsFullChains(t *testing.T) {
address = crypto.PubkeyToAddress(key.PublicKey)
funds = big.NewInt(1000000000)
genesis = GenesisBlockForTesting(gendb, address, funds)
+ signer = types.NewEIP155Signer(big.NewInt(1))
)
- 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
if i%3 == 2 {
for j := 0; j < i%4+1; j++ {
- tx, err := types.NewTransaction(block.TxNonce(address), common.Address{0x00}, big.NewInt(1000), params.TxGas, nil, nil).SignECDSA(key)
+ tx, err := types.NewTransaction(block.TxNonce(address), common.Address{0x00}, big.NewInt(1000), params.TxGas, nil, nil).SignECDSA(signer, key)
if err != nil {
panic(err)
}
@@ -795,7 +796,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{}
@@ -872,6 +873,7 @@ func TestChainTxReorgs(t *testing.T) {
addr2 = crypto.PubkeyToAddress(key2.PublicKey)
addr3 = crypto.PubkeyToAddress(key3.PublicKey)
db, _ = ethdb.NewMemDatabase()
+ signer = types.NewEIP155Signer(big.NewInt(1))
)
genesis := WriteGenesisBlockForTesting(db,
GenesisAccount{addr1, big.NewInt(1000000)},
@@ -881,8 +883,8 @@ func TestChainTxReorgs(t *testing.T) {
// Create two transactions shared between the chains:
// - postponed: transaction included at a later block in the forked chain
// - swapped: transaction included at the same block number in the forked chain
- postponed, _ := types.NewTransaction(0, addr1, big.NewInt(1000), params.TxGas, nil, nil).SignECDSA(key1)
- swapped, _ := types.NewTransaction(1, addr1, big.NewInt(1000), params.TxGas, nil, nil).SignECDSA(key1)
+ postponed, _ := types.NewTransaction(0, addr1, big.NewInt(1000), params.TxGas, nil, nil).SignECDSA(signer, key1)
+ swapped, _ := types.NewTransaction(1, addr1, big.NewInt(1000), params.TxGas, nil, nil).SignECDSA(signer, key1)
// Create two transactions that will be dropped by the forked chain:
// - pastDrop: transaction dropped retroactively from a past block
@@ -895,16 +897,16 @@ 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)
+ pastDrop, _ = types.NewTransaction(gen.TxNonce(addr2), addr2, big.NewInt(1000), params.TxGas, nil, nil).SignECDSA(signer, key2)
gen.AddTx(pastDrop) // This transaction will be dropped in the fork from below the split point
gen.AddTx(postponed) // This transaction will be postponed till block #3 in the fork
case 2:
- freshDrop, _ = types.NewTransaction(gen.TxNonce(addr2), addr2, big.NewInt(1000), params.TxGas, nil, nil).SignECDSA(key2)
+ freshDrop, _ = types.NewTransaction(gen.TxNonce(addr2), addr2, big.NewInt(1000), params.TxGas, nil, nil).SignECDSA(signer, key2)
gen.AddTx(freshDrop) // This transaction will be dropped in the fork from exactly at the split point
gen.AddTx(swapped) // This transaction will be swapped out at the exact height
@@ -920,21 +922,21 @@ 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)
+ pastAdd, _ = types.NewTransaction(gen.TxNonce(addr3), addr3, big.NewInt(1000), params.TxGas, nil, nil).SignECDSA(signer, key3)
gen.AddTx(pastAdd) // This transaction needs to be injected during reorg
case 2:
gen.AddTx(postponed) // This transaction was postponed from block #1 in the original chain
gen.AddTx(swapped) // This transaction was swapped from the exact current spot in the original chain
- freshAdd, _ = types.NewTransaction(gen.TxNonce(addr3), addr3, big.NewInt(1000), params.TxGas, nil, nil).SignECDSA(key3)
+ freshAdd, _ = types.NewTransaction(gen.TxNonce(addr3), addr3, big.NewInt(1000), params.TxGas, nil, nil).SignECDSA(signer, key3)
gen.AddTx(freshAdd) // This transaction will be added exactly at reorg time
case 3:
- futureAdd, _ = types.NewTransaction(gen.TxNonce(addr3), addr3, big.NewInt(1000), params.TxGas, nil, nil).SignECDSA(key3)
+ futureAdd, _ = types.NewTransaction(gen.TxNonce(addr3), addr3, big.NewInt(1000), params.TxGas, nil, nil).SignECDSA(signer, key3)
gen.AddTx(futureAdd) // This transaction will be added after a full reorg
}
})
@@ -980,7 +982,8 @@ func TestLogReorgs(t *testing.T) {
addr1 = crypto.PubkeyToAddress(key1.PublicKey)
db, _ = ethdb.NewMemDatabase()
// this code generates a log
- code = common.Hex2Bytes("60606040525b7f24ec1d3ff24c2f6ff210738839dbc339cd45a5294d85c79361016243157aae7b60405180905060405180910390a15b600a8060416000396000f360606040526008565b00")
+ code = common.Hex2Bytes("60606040525b7f24ec1d3ff24c2f6ff210738839dbc339cd45a5294d85c79361016243157aae7b60405180905060405180910390a15b600a8060416000396000f360606040526008565b00")
+ signer = types.NewEIP155Signer(big.NewInt(1))
)
genesis := WriteGenesisBlockForTesting(db,
GenesisAccount{addr1, big.NewInt(10000000000000)},
@@ -990,9 +993,9 @@ 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)
+ tx, err := types.NewContractCreation(gen.TxNonce(addr1), new(big.Int), big.NewInt(1000000), new(big.Int), code).SignECDSA(signer, key1)
if err != nil {
t.Fatalf("failed to create tx: %v", err)
}
@@ -1003,7 +1006,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)
}
@@ -1020,18 +1023,19 @@ func TestReorgSideEvent(t *testing.T) {
key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
addr1 = crypto.PubkeyToAddress(key1.PublicKey)
genesis = WriteGenesisBlockForTesting(db, GenesisAccount{addr1, big.NewInt(10000000000000)})
+ signer = types.NewEIP155Signer(big.NewInt(1))
)
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) {
- tx, err := types.NewContractCreation(gen.TxNonce(addr1), new(big.Int), big.NewInt(1000000), new(big.Int), nil).SignECDSA(key1)
+ 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(signer, key1)
if i == 2 {
gen.OffsetTime(-1)
}
@@ -1101,7 +1105,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 +1132,105 @@ 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 = &params.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) {
+ return types.NewTransaction(block.TxNonce(address), common.Address{}, new(big.Int), big.NewInt(21000), new(big.Int), nil).SignECDSA(signer, 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 = &params.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) {
+ return types.NewTransaction(block.TxNonce(address), common.Address{}, new(big.Int), big.NewInt(21000), new(big.Int), nil).SignECDSA(signer, key)
+ }
+ )
+ switch i {
+ case 0:
+ tx, err = basicTx(types.NewEIP155Signer(big.NewInt(2)))
+ if err != nil {
+ t.Fatal(err)
+ }
+ block.AddTx(tx)
+ }
+ })
+ errExp := "Invalid transaction chain id. Current chain id: 1 tx chain id: 2"
+ _, err := blockchain.InsertChain(blocks)
+ if err == nil {
+ t.Error("expected transaction chain id error")
+ } else if err.Error() != errExp {
+ t.Error("expected:", errExp, "got:", err)
+ }
+}
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 &params.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..487cd6e18 100644
--- a/core/chain_makers_test.go
+++ b/core/chain_makers_test.go
@@ -39,25 +39,29 @@ func ExampleGenerateChain() {
addr2 = crypto.PubkeyToAddress(key2.PublicKey)
addr3 = crypto.PubkeyToAddress(key3.PublicKey)
db, _ = ethdb.NewMemDatabase()
+ signer = types.HomesteadSigner{}
)
+ chainConfig := &params.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.
- tx, _ := types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(10000), params.TxGas, nil, nil).SignECDSA(key1)
+ tx, _ := types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(10000), params.TxGas, nil, nil).SignECDSA(signer, key1)
gen.AddTx(tx)
case 1:
// In block 2, addr1 sends some more ether to addr2.
// addr2 passes it on to addr3.
- tx1, _ := types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(1000), params.TxGas, nil, nil).SignECDSA(key1)
- tx2, _ := types.NewTransaction(gen.TxNonce(addr2), addr3, big.NewInt(1000), params.TxGas, nil, nil).SignECDSA(key2)
+ tx1, _ := types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(1000), params.TxGas, nil, nil).SignECDSA(signer, key1)
+ tx2, _ := types.NewTransaction(gen.TxNonce(addr2), addr3, big.NewInt(1000), params.TxGas, nil, nil).SignECDSA(signer, key2)
gen.AddTx(tx1)
gen.AddTx(tx2)
case 2:
@@ -77,7 +81,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 96e39ea3c..000000000
--- a/core/config.go
+++ /dev/null
@@ -1,63 +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 <http://www.gnu.org/licenses/>.
-
-package core
-
-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
-// 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)
- HomesteadGasRepriceHash common.Hash `json:"homesteadGasRepriceHash"` // Homestead gas reprice switch block hash (fast sync aid)
-
- 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 := &params.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 := &params.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 := &params.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..e3ea1006c 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,21 @@ 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)
+ // check whether the max code size has been exceeded
+ maxCodeSizeExceeded := len(ret) > params.MaxCodeSize
// 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 && !maxCodeSizeExceeded {
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 +169,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 maxCodeSizeExceeded ||
+ (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..46c5db0ee 100644
--- a/core/state_processor.go
+++ b/core/state_processor.go
@@ -17,6 +17,7 @@
package core
import (
+ "fmt"
"math/big"
"github.com/ethereum/go-ethereum/core/state"
@@ -25,6 +26,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 +39,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,
@@ -71,10 +73,14 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
}
// Iterate over and process the individual transactions
for i, tx := range block.Transactions() {
+ if tx.Protected() && tx.ChainId().Cmp(p.config.ChainId) != 0 {
+ return nil, nil, nil, fmt.Errorf("Invalid transaction chain id. Current chain id: %v tx chain id: %v", p.config.ChainId, tx.ChainId())
+ }
+
statedb.StartRecord(tx.Hash(), block.Hash(), i)
receipt, logs, _, err := ApplyTransaction(p.config, p.bc, gp, statedb, header, tx, totalUsedGas, cfg)
if err != nil {
- return nil, nil, totalUsedGas, err
+ return nil, nil, nil, err
}
receipts = append(receipts, receipt)
allLogs = append(allLogs, logs...)
@@ -89,20 +95,24 @@ 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) {
- _, gas, err := ApplyMessage(NewEnv(statedb, config, bc, tx, header, cfg), tx, gp)
+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) {
+ msg, err := tx.AsMessage(types.MakeSigner(config, header.Number))
+ if err != nil {
+ return nil, nil, nil, err
+ }
+
+ _, gas, err := ApplyMessage(NewEnv(statedb, config, bc, msg, header, cfg), msg, gp)
if err != nil {
return nil, nil, nil, err
}
// 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) {
- from, _ := tx.From()
- receipt.ContractAddress = crypto.CreateAddress(from, tx.Nonce())
+ if MessageCreatesContract(msg) {
+ receipt.ContractAddress = crypto.CreateAddress(msg.From(), tx.Nonce())
}
logs := statedb.GetLogs(tx.Hash())
diff --git a/core/state_transition.go b/core/state_transition.go
index 9e6b2f567..8abe17b0a 100644
--- a/core/state_transition.go
+++ b/core/state_transition.go
@@ -62,8 +62,8 @@ type StateTransition struct {
// Message represents a message sent to a contract.
type Message interface {
- From() (common.Address, error)
- FromFrontier() (common.Address, error)
+ From() common.Address
+ //FromFrontier() (common.Address, error)
To() *common.Address
GasPrice() *big.Int
@@ -134,23 +134,12 @@ func ApplyMessage(env vm.Environment, msg Message, gp *GasPool) ([]byte, *big.In
return ret, gasUsed, err
}
-func (self *StateTransition) from() (vm.Account, error) {
- var (
- f common.Address
- err error
- )
- if self.env.RuleSet().IsHomestead(self.env.BlockNumber()) {
- f, err = self.msg.From()
- } else {
- f, err = self.msg.FromFrontier()
- }
- if err != nil {
- return nil, err
- }
+func (self *StateTransition) from() vm.Account {
+ f := self.msg.From()
if !self.state.Exist(f) {
- return self.state.CreateAccount(f), nil
+ return self.state.CreateAccount(f)
}
- return self.state.GetAccount(f), nil
+ return self.state.GetAccount(f)
}
func (self *StateTransition) to() vm.Account {
@@ -185,14 +174,11 @@ func (self *StateTransition) buyGas() error {
mgas := self.msg.Gas()
mgval := new(big.Int).Mul(mgas, self.gasPrice)
- sender, err := self.from()
- if err != nil {
- return err
- }
+ sender := self.from()
if sender.Balance().Cmp(mgval) < 0 {
return fmt.Errorf("insufficient ETH for gas (%x). Req %v, has %v", sender.Address().Bytes()[:4], mgval, sender.Balance())
}
- if err = self.gp.SubGas(mgas); err != nil {
+ if err := self.gp.SubGas(mgas); err != nil {
return err
}
self.addGas(mgas)
@@ -203,10 +189,7 @@ func (self *StateTransition) buyGas() error {
func (self *StateTransition) preCheck() (err error) {
msg := self.msg
- sender, err := self.from()
- if err != nil {
- return err
- }
+ sender := self.from()
// Make sure this transaction's nonce is correct
if msg.CheckNonce() {
@@ -232,9 +215,9 @@ func (self *StateTransition) TransitionDb() (ret []byte, requiredGas, usedGas *b
return
}
msg := self.msg
- sender, _ := self.from() // err checked in preCheck
+ 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 {
@@ -282,7 +265,7 @@ func (self *StateTransition) TransitionDb() (ret []byte, requiredGas, usedGas *b
func (self *StateTransition) refundGas() {
// Return eth for remaining gas to the sender account,
// exchanged at the original rate.
- sender, _ := self.from() // err already checked
+ sender := self.from() // err already checked
remaining := new(big.Int).Mul(self.gas, self.gasPrice)
sender.AddBalance(remaining)
diff --git a/core/tx_pool.go b/core/tx_pool.go
index 419d9945e..edcbc21eb 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
@@ -91,6 +92,7 @@ type TxPool struct {
eventMux *event.TypeMux
events event.Subscription
localTx *txSet
+ signer types.Signer
mu sync.RWMutex
pending map[common.Address]*txList // All currently processable transactions
@@ -104,9 +106,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),
@@ -138,8 +141,10 @@ func (pool *TxPool) eventLoop() {
switch ev := ev.Data.(type) {
case ChainHeadEvent:
pool.mu.Lock()
- if ev.Block != nil && pool.config.IsHomestead(ev.Block.Number()) {
- pool.homestead = true
+ if ev.Block != nil {
+ if pool.config.IsHomestead(ev.Block.Number()) {
+ pool.homestead = true
+ }
}
pool.resetState()
@@ -271,7 +276,7 @@ func (pool *TxPool) validateTx(tx *types.Transaction) error {
return err
}
- from, err := tx.From()
+ from, err := types.Sender(pool.signer, tx)
if err != nil {
return ErrInvalidSender
}
@@ -306,7 +311,7 @@ func (pool *TxPool) validateTx(tx *types.Transaction) error {
return ErrInsufficientFunds
}
- intrGas := IntrinsicGas(tx.Data(), MessageCreatesContract(tx), pool.homestead)
+ intrGas := IntrinsicGas(tx.Data(), tx.To() == nil, pool.homestead)
if tx.Gas().Cmp(intrGas) < 0 {
return ErrIntrinsicGas
}
@@ -335,7 +340,7 @@ func (pool *TxPool) add(tx *types.Transaction) error {
if to := tx.To(); to != nil {
rcpt = common.Bytes2Hex(to[:4])
}
- from, _ := tx.From() // from already verified during tx validation
+ from, _ := types.Sender(pool.signer, tx) // from already verified during tx validation
glog.Infof("(t) 0x%x => %s (%v) %x\n", from[:4], rcpt, tx.Value, hash)
}
return nil
@@ -346,7 +351,7 @@ func (pool *TxPool) add(tx *types.Transaction) error {
// Note, this method assumes the pool lock is held!
func (pool *TxPool) enqueueTx(hash common.Hash, tx *types.Transaction) {
// Try to insert the transaction into the future queue
- from, _ := tx.From() // already validated
+ from, _ := types.Sender(pool.signer, tx) // already validated
if pool.queue[from] == nil {
pool.queue[from] = newTxList(false)
}
@@ -458,7 +463,7 @@ func (pool *TxPool) removeTx(hash common.Hash) {
if !ok {
return
}
- addr, _ := tx.From() // already validated during insertion
+ addr, _ := types.Sender(pool.signer, tx) // already validated during insertion
// Remove it from the list of known transactions
delete(pool.all, hash)
diff --git a/core/tx_pool_test.go b/core/tx_pool_test.go
index dbe6fa635..009d19886 100644
--- a/core/tx_pool_test.go
+++ b/core/tx_pool_test.go
@@ -32,7 +32,7 @@ import (
)
func transaction(nonce uint64, gaslimit *big.Int, key *ecdsa.PrivateKey) *types.Transaction {
- tx, _ := types.NewTransaction(nonce, common.Address{}, big.NewInt(100), gaslimit, big.NewInt(1), nil).SignECDSA(key)
+ tx, _ := types.NewTransaction(nonce, common.Address{}, big.NewInt(100), gaslimit, big.NewInt(1), nil).SignECDSA(types.HomesteadSigner{}, key)
return tx
}
@@ -47,6 +47,10 @@ func setupTxPool() (*TxPool, *ecdsa.PrivateKey) {
return newPool, key
}
+func deriveSender(tx *types.Transaction) (common.Address, error) {
+ return types.Sender(types.HomesteadSigner{}, tx)
+}
+
func TestInvalidTransactions(t *testing.T) {
pool, key := setupTxPool()
@@ -55,7 +59,7 @@ func TestInvalidTransactions(t *testing.T) {
t.Error("expected", ErrNonExistentAccount)
}
- from, _ := tx.From()
+ from, _ := deriveSender(tx)
currentState, _ := pool.currentState()
currentState.AddBalance(from, big.NewInt(1))
if err := pool.Add(tx); err != ErrInsufficientFunds {
@@ -90,7 +94,7 @@ func TestInvalidTransactions(t *testing.T) {
func TestTransactionQueue(t *testing.T) {
pool, key := setupTxPool()
tx := transaction(0, big.NewInt(100), key)
- from, _ := tx.From()
+ from, _ := deriveSender(tx)
currentState, _ := pool.currentState()
currentState.AddBalance(from, big.NewInt(1000))
pool.enqueueTx(tx.Hash(), tx)
@@ -101,7 +105,7 @@ func TestTransactionQueue(t *testing.T) {
}
tx = transaction(1, big.NewInt(100), key)
- from, _ = tx.From()
+ from, _ = deriveSender(tx)
currentState.SetNonce(from, 2)
pool.enqueueTx(tx.Hash(), tx)
pool.promoteExecutables()
@@ -117,7 +121,7 @@ func TestTransactionQueue(t *testing.T) {
tx1 := transaction(0, big.NewInt(100), key)
tx2 := transaction(10, big.NewInt(100), key)
tx3 := transaction(11, big.NewInt(100), key)
- from, _ = tx1.From()
+ from, _ = deriveSender(tx1)
currentState, _ = pool.currentState()
currentState.AddBalance(from, big.NewInt(1000))
pool.enqueueTx(tx1.Hash(), tx1)
@@ -137,7 +141,7 @@ func TestTransactionQueue(t *testing.T) {
func TestRemoveTx(t *testing.T) {
pool, key := setupTxPool()
tx := transaction(0, big.NewInt(100), key)
- from, _ := tx.From()
+ from, _ := deriveSender(tx)
currentState, _ := pool.currentState()
currentState.AddBalance(from, big.NewInt(1))
@@ -161,8 +165,8 @@ func TestRemoveTx(t *testing.T) {
func TestNegativeValue(t *testing.T) {
pool, key := setupTxPool()
- tx, _ := types.NewTransaction(0, common.Address{}, big.NewInt(-1), big.NewInt(100), big.NewInt(1), nil).SignECDSA(key)
- from, _ := tx.From()
+ tx, _ := types.NewTransaction(0, common.Address{}, big.NewInt(-1), big.NewInt(100), big.NewInt(1), nil).SignECDSA(types.HomesteadSigner{}, key)
+ from, _ := deriveSender(tx)
currentState, _ := pool.currentState()
currentState.AddBalance(from, big.NewInt(1))
if err := pool.Add(tx); err != ErrNegativeValue {
@@ -209,9 +213,10 @@ func TestTransactionDoubleNonce(t *testing.T) {
}
resetState()
- tx1, _ := types.NewTransaction(0, common.Address{}, big.NewInt(100), big.NewInt(100000), big.NewInt(1), nil).SignECDSA(key)
- tx2, _ := types.NewTransaction(0, common.Address{}, big.NewInt(100), big.NewInt(1000000), big.NewInt(2), nil).SignECDSA(key)
- tx3, _ := types.NewTransaction(0, common.Address{}, big.NewInt(100), big.NewInt(1000000), big.NewInt(1), nil).SignECDSA(key)
+ signer := types.HomesteadSigner{}
+ tx1, _ := types.NewTransaction(0, common.Address{}, big.NewInt(100), big.NewInt(100000), big.NewInt(1), nil).SignECDSA(signer, key)
+ tx2, _ := types.NewTransaction(0, common.Address{}, big.NewInt(100), big.NewInt(1000000), big.NewInt(2), nil).SignECDSA(signer, key)
+ tx3, _ := types.NewTransaction(0, common.Address{}, big.NewInt(100), big.NewInt(1000000), big.NewInt(1), nil).SignECDSA(signer, key)
// Add the first two transaction, ensure higher priced stays only
if err := pool.add(tx1); err != nil {
@@ -287,7 +292,7 @@ func TestNonceRecovery(t *testing.T) {
func TestRemovedTxEvent(t *testing.T) {
pool, key := setupTxPool()
tx := transaction(0, big.NewInt(1000000), key)
- from, _ := tx.From()
+ from, _ := deriveSender(tx)
currentState, _ := pool.currentState()
currentState.AddBalance(from, big.NewInt(1000000000000))
pool.eventMux.Post(RemovedTransactionEvent{types.Transactions{tx}})
@@ -305,7 +310,7 @@ func TestRemovedTxEvent(t *testing.T) {
func TestTransactionDropping(t *testing.T) {
// Create a test account and fund it
pool, key := setupTxPool()
- account, _ := transaction(0, big.NewInt(0), key).From()
+ account, _ := deriveSender(transaction(0, big.NewInt(0), key))
state, _ := pool.currentState()
state.AddBalance(account, big.NewInt(1000))
@@ -369,7 +374,7 @@ func TestTransactionDropping(t *testing.T) {
func TestTransactionPostponing(t *testing.T) {
// Create a test account and fund it
pool, key := setupTxPool()
- account, _ := transaction(0, big.NewInt(0), key).From()
+ account, _ := deriveSender(transaction(0, big.NewInt(0), key))
state, _ := pool.currentState()
state.AddBalance(account, big.NewInt(1000))
@@ -443,7 +448,7 @@ func TestTransactionPostponing(t *testing.T) {
func TestTransactionQueueAccountLimiting(t *testing.T) {
// Create a test account and fund it
pool, key := setupTxPool()
- account, _ := transaction(0, big.NewInt(0), key).From()
+ account, _ := deriveSender(transaction(0, big.NewInt(0), key))
state, _ := pool.currentState()
state.AddBalance(account, big.NewInt(1000000))
@@ -531,7 +536,7 @@ func TestTransactionQueueTimeLimiting(t *testing.T) {
// Create a test account and fund it
pool, key := setupTxPool()
- account, _ := transaction(0, big.NewInt(0), key).From()
+ account, _ := deriveSender(transaction(0, big.NewInt(0), key))
state, _ := pool.currentState()
state.AddBalance(account, big.NewInt(1000000))
@@ -555,7 +560,7 @@ func TestTransactionQueueTimeLimiting(t *testing.T) {
func TestTransactionPendingLimiting(t *testing.T) {
// Create a test account and fund it
pool, key := setupTxPool()
- account, _ := transaction(0, big.NewInt(0), key).From()
+ account, _ := deriveSender(transaction(0, big.NewInt(0), key))
state, _ := pool.currentState()
state.AddBalance(account, big.NewInt(1000000))
@@ -585,7 +590,7 @@ func TestTransactionPendingLimitingEquivalency(t *testing.T) { testTransactionLi
func testTransactionLimitingEquivalency(t *testing.T, origin uint64) {
// Add a batch of transactions to a pool one by one
pool1, key1 := setupTxPool()
- account1, _ := transaction(0, big.NewInt(0), key1).From()
+ account1, _ := deriveSender(transaction(0, big.NewInt(0), key1))
state1, _ := pool1.currentState()
state1.AddBalance(account1, big.NewInt(1000000))
@@ -596,7 +601,7 @@ func testTransactionLimitingEquivalency(t *testing.T, origin uint64) {
}
// Add a batch of transactions to a pool in one big batch
pool2, key2 := setupTxPool()
- account2, _ := transaction(0, big.NewInt(0), key2).From()
+ account2, _ := deriveSender(transaction(0, big.NewInt(0), key2))
state2, _ := pool2.currentState()
state2.AddBalance(account2, big.NewInt(1000000))
@@ -717,7 +722,7 @@ func BenchmarkPendingDemotion10000(b *testing.B) { benchmarkPendingDemotion(b, 1
func benchmarkPendingDemotion(b *testing.B, size int) {
// Add a batch of transactions to a pool one by one
pool, key := setupTxPool()
- account, _ := transaction(0, big.NewInt(0), key).From()
+ account, _ := deriveSender(transaction(0, big.NewInt(0), key))
state, _ := pool.currentState()
state.AddBalance(account, big.NewInt(1000000))
@@ -741,7 +746,7 @@ func BenchmarkFuturePromotion10000(b *testing.B) { benchmarkFuturePromotion(b, 1
func benchmarkFuturePromotion(b *testing.B, size int) {
// Add a batch of transactions to a pool one by one
pool, key := setupTxPool()
- account, _ := transaction(0, big.NewInt(0), key).From()
+ account, _ := deriveSender(transaction(0, big.NewInt(0), key))
state, _ := pool.currentState()
state.AddBalance(account, big.NewInt(1000000))
@@ -760,7 +765,7 @@ func benchmarkFuturePromotion(b *testing.B, size int) {
func BenchmarkPoolInsert(b *testing.B) {
// Generate a batch of transactions to enqueue into the pool
pool, key := setupTxPool()
- account, _ := transaction(0, big.NewInt(0), key).From()
+ account, _ := deriveSender(transaction(0, big.NewInt(0), key))
state, _ := pool.currentState()
state.AddBalance(account, big.NewInt(1000000))
@@ -783,7 +788,7 @@ func BenchmarkPoolBatchInsert10000(b *testing.B) { benchmarkPoolBatchInsert(b, 1
func benchmarkPoolBatchInsert(b *testing.B, size int) {
// Generate a batch of transactions to enqueue into the pool
pool, key := setupTxPool()
- account, _ := transaction(0, big.NewInt(0), key).From()
+ account, _ := deriveSender(transaction(0, big.NewInt(0), key))
state, _ := pool.currentState()
state.AddBalance(account, big.NewInt(1000000))
diff --git a/core/types/block_test.go b/core/types/block_test.go
index ac7f17c0d..b95bddcfc 100644
--- a/core/types/block_test.go
+++ b/core/types/block_test.go
@@ -18,6 +18,7 @@ package types
import (
"bytes"
+ "fmt"
"math/big"
"reflect"
"testing"
@@ -51,7 +52,11 @@ func TestBlockEncoding(t *testing.T) {
check("Size", block.Size(), common.StorageSize(len(blockEnc)))
tx1 := NewTransaction(0, common.HexToAddress("095e7baea6a6c7c4c2dfeb977efac326af552d87"), big.NewInt(10), big.NewInt(50000), big.NewInt(10), nil)
- tx1, _ = tx1.WithSignature(common.Hex2Bytes("9bea4c4daac7c7c52e093e6a4c35dbbcf8856f1af7b059ba20253e70848d094f8a8fae537ce25ed8cb5af9adac3f141af69bd515bd2ba031522df09b97dd72b11b"))
+
+ tx1, _ = tx1.WithSignature(HomesteadSigner{}, common.Hex2Bytes("9bea4c4daac7c7c52e093e6a4c35dbbcf8856f1af7b059ba20253e70848d094f8a8fae537ce25ed8cb5af9adac3f141af69bd515bd2ba031522df09b97dd72b11b"))
+ fmt.Println(block.Transactions()[0].Hash())
+ fmt.Println(tx1.data)
+ fmt.Println(tx1.Hash())
check("len(Transactions)", len(block.Transactions()), 1)
check("Transactions[0].Hash", block.Transactions()[0].Hash(), tx1.Hash())
diff --git a/core/types/json_test.go b/core/types/json_test.go
index a028b5d08..d80cda68b 100644
--- a/core/types/json_test.go
+++ b/core/types/json_test.go
@@ -97,10 +97,12 @@ var unmarshalTransactionTests = map[string]struct {
wantHash: common.HexToHash("0xd91c08f1e27c5ce7e1f57d78d7c56a9ee446be07b9635d84d0475660ea8905e9"),
wantFrom: common.HexToAddress("0xf36c3f6c4a2ce8d353fb92d5cd10d19ce69ae689"),
},
+ /* TODO skipping this test as this type can not be tested with the current signing approach
"bad signature fields": {
input: `{"blockHash":"0x0188a05dcc825bd1a05dab91bea0c03622542683446e56302eabb46097d4ae11","blockNumber":"0x1e478d","from":"0xf36c3f6c4a2ce8d353fb92d5cd10d19ce69ae689","gas":"0x15f90","gasPrice":"0x4a817c800","hash":"0xd91c08f1e27c5ce7e1f57d78d7c56a9ee446be07b9635d84d0475660ea8905e9","input":"0x","nonce":"0x58d","to":"0x88f252f674ac755feff877abf957d4aa05adce86","transactionIndex":"0x1","value":"0x19f0ec3ed71ec00","v":"0x58","r":"0x53829f206c99b866672f987909d556cd1c2eb60e990a3425f65083977c14187b","s":"0x5cc52383e41c923ec7d63749c1f13a7236b540527ee5b9a78b3fb869a66f60e"}`,
wantError: ErrInvalidSig,
},
+ */
"missing signature v": {
input: `{"blockHash":"0x0188a05dcc825bd1a05dab91bea0c03622542683446e56302eabb46097d4ae11","blockNumber":"0x1e478d","from":"0xf36c3f6c4a2ce8d353fb92d5cd10d19ce69ae689","gas":"0x15f90","gasPrice":"0x4a817c800","hash":"0xd91c08f1e27c5ce7e1f57d78d7c56a9ee446be07b9635d84d0475660ea8905e9","input":"0x","nonce":"0x58d","to":"0x88f252f674ac755feff877abf957d4aa05adce86","transactionIndex":"0x1","value":"0x19f0ec3ed71ec00","r":"0x53829f206c99b866672f987909d556cd1c2eb60e990a3425f65083977c14187b","s":"0x5cc52383e41c923ec7d63749c1f13a7236b540527ee5b9a78b3fb869a66f60e"}`,
wantError: errMissingTxSignatureFields,
@@ -122,11 +124,12 @@ func TestUnmarshalTransaction(t *testing.T) {
if !checkError(t, name, err, test.wantError) {
continue
}
+
if tx.Hash() != test.wantHash {
t.Errorf("test %q: got hash %x, want %x", name, tx.Hash(), test.wantHash)
continue
}
- from, err := tx.From()
+ from, err := Sender(HomesteadSigner{}, tx)
if err != nil {
t.Errorf("test %q: From error %v", name, err)
}
diff --git a/core/types/transaction.go b/core/types/transaction.go
index ceea4f959..972a36706 100644
--- a/core/types/transaction.go
+++ b/core/types/transaction.go
@@ -28,6 +28,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
+ "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp"
)
@@ -36,8 +37,18 @@ var ErrInvalidSig = errors.New("invalid transaction v, r, s values")
var (
errMissingTxSignatureFields = errors.New("missing required JSON transaction signature fields")
errMissingTxFields = errors.New("missing required JSON transaction fields")
+ errNoSigner = errors.New("missing signing methods")
)
+// 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{}
+ }
+}
+
type Transaction struct {
data txdata
// caches
@@ -52,7 +63,7 @@ type txdata struct {
Recipient *common.Address `rlp:"nil"` // nil means contract creation
Amount *big.Int
Payload []byte
- V byte // signature
+ V *big.Int // signature
R, S *big.Int // signature
}
@@ -64,40 +75,31 @@ type jsonTransaction struct {
Recipient *common.Address `json:"to"`
Amount *hexBig `json:"value"`
Payload *hexBytes `json:"input"`
- V *hexUint64 `json:"v"`
+ V *hexBig `json:"v"`
R *hexBig `json:"r"`
S *hexBig `json:"s"`
}
-// NewContractCreation creates a new transaction with no recipient.
+func NewTransaction(nonce uint64, to common.Address, amount, gasLimit, gasPrice *big.Int, data []byte) *Transaction {
+ return newTransaction(nonce, &to, amount, gasLimit, gasPrice, data)
+}
+
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 newTransaction(nonce, nil, amount, gasLimit, gasPrice, data)
}
-// NewTransaction creates a new transaction with the given fields.
-func NewTransaction(nonce uint64, to common.Address, amount, gasLimit, gasPrice *big.Int, data []byte) *Transaction {
+func newTransaction(nonce uint64, to *common.Address, amount, gasLimit, gasPrice *big.Int, data []byte) *Transaction {
if len(data) > 0 {
data = common.CopyBytes(data)
}
d := txdata{
AccountNonce: nonce,
- Recipient: &to,
+ Recipient: to,
Payload: data,
Amount: new(big.Int),
GasLimit: new(big.Int),
Price: new(big.Int),
+ V: new(big.Int),
R: new(big.Int),
S: new(big.Int),
}
@@ -110,9 +112,42 @@ func NewTransaction(nonce uint64, to common.Address, amount, gasLimit, gasPrice
if gasPrice != nil {
d.Price.Set(gasPrice)
}
+
return &Transaction{data: d}
}
+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
+}
+
+// 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 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
+}
+
// DecodeRLP implements rlp.Encoder
func (tx *Transaction) EncodeRLP(w io.Writer) error {
return rlp.Encode(w, &tx.data)
@@ -125,12 +160,13 @@ func (tx *Transaction) DecodeRLP(s *rlp.Stream) error {
if err == nil {
tx.size.Store(common.StorageSize(rlp.ListSize(size)))
}
+
return err
}
// MarshalJSON encodes transactions into the web3 RPC response block format.
func (tx *Transaction) MarshalJSON() ([]byte, error) {
- hash, v := tx.Hash(), uint64(tx.data.V)
+ hash := tx.Hash()
return json.Marshal(&jsonTransaction{
Hash: &hash,
@@ -140,7 +176,7 @@ func (tx *Transaction) MarshalJSON() ([]byte, error) {
Recipient: tx.data.Recipient,
Amount: (*hexBig)(tx.data.Amount),
Payload: (*hexBytes)(&tx.data.Payload),
- V: (*hexUint64)(&v),
+ V: (*hexBig)(tx.data.V),
R: (*hexBig)(tx.data.R),
S: (*hexBig)(tx.data.S),
})
@@ -159,9 +195,17 @@ func (tx *Transaction) UnmarshalJSON(input []byte) error {
if dec.V == nil || dec.R == nil || dec.S == nil {
return errMissingTxSignatureFields
}
- if !crypto.ValidateSignatureValues(byte(*dec.V), (*big.Int)(dec.R), (*big.Int)(dec.S), false) {
+
+ var V byte
+ if isProtectedV((*big.Int)(dec.V)) {
+ V = normaliseV(NewEIP155Signer(deriveChainId((*big.Int)(dec.V))), (*big.Int)(dec.V))
+ } else {
+ V = byte(((*big.Int)(dec.V)).Uint64())
+ }
+ if !crypto.ValidateSignatureValues(V, (*big.Int)(dec.R), (*big.Int)(dec.S), false) {
return ErrInvalidSig
}
+
if dec.AccountNonce == nil || dec.Price == nil || dec.GasLimit == nil || dec.Amount == nil || dec.Payload == nil {
return errMissingTxFields
}
@@ -175,7 +219,7 @@ func (tx *Transaction) UnmarshalJSON(input []byte) error {
GasLimit: (*big.Int)(dec.GasLimit),
Price: (*big.Int)(dec.Price),
Payload: *dec.Payload,
- V: byte(*dec.V),
+ V: (*big.Int)(dec.V),
R: (*big.Int)(dec.R),
S: (*big.Int)(dec.S),
}
@@ -211,15 +255,8 @@ 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,
- })
+func (tx *Transaction) SigHash(signer Signer) common.Hash {
+ return signer.Hash(tx)
}
func (tx *Transaction) Size() common.StorageSize {
@@ -232,6 +269,7 @@ func (tx *Transaction) Size() common.StorageSize {
return common.StorageSize(c)
}
+/*
// 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.
@@ -247,32 +285,15 @@ func (tx *Transaction) Size() common.StorageSize {
// both txs before and after the first homestead block. Signatures
// valid in homestead are a subset of valid ones in Frontier)
func (tx *Transaction) From() (common.Address, error) {
- return doFrom(tx, true)
-}
-
-// FromFrontier 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.
-//
-// FromFrantier uses the frontier consensus rules to determine whether the
-// signature is valid.
-//
-// FromFrontier caches the address, allowing it to be used regardless of
-// Frontier / Homestead. however, the first time called it runs
-// signature validations, so we need two versions. This makes it
-// easier to ensure backwards compatibility of things like package rpc
-// where eth_getblockbynumber uses tx.From() and needs to work for
-// both txs before and after the first homestead block. Signatures
-// valid in homestead are a subset of valid ones in Frontier)
-func (tx *Transaction) FromFrontier() (common.Address, error) {
- return doFrom(tx, false)
-}
+ if tx.signer == nil {
+ return common.Address{}, errNoSigner
+ }
-func doFrom(tx *Transaction, homestead bool) (common.Address, error) {
if from := tx.from.Load(); from != nil {
return from.(common.Address), nil
}
- pubkey, err := tx.publicKey(homestead)
+
+ pubkey, err := tx.signer.PublicKey(tx)
if err != nil {
return common.Address{}, err
}
@@ -282,68 +303,70 @@ func doFrom(tx *Transaction, homestead bool) (common.Address, error) {
return addr, nil
}
-// Cost returns amount + gasprice * gaslimit.
-func (tx *Transaction) Cost() *big.Int {
- total := new(big.Int).Mul(tx.data.Price, tx.data.GasLimit)
- total.Add(total, tx.data.Amount)
- return total
-}
-
// SignatureValues returns the ECDSA signature values contained in the transaction.
-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)
+func (tx *Transaction) SignatureValues() (v byte, r *big.Int, s *big.Int, err error) {
+ if tx.signer == nil {
+ return 0, nil, nil,errNoSigner
+ }
+
+ return normaliseV(tx.signer, tx.data.V), new(big.Int).Set(tx.data.R),new(big.Int).Set(tx.data.S), nil
}
-func (tx *Transaction) publicKey(homestead bool) ([]byte, error) {
- if !crypto.ValidateSignatureValues(tx.data.V, tx.data.R, tx.data.S, homestead) {
- return nil, ErrInvalidSig
+*/
+
+// AsMessage returns the transaction as a core.Message.
+//
+// AsMessage requires a signer to derive the sender.
+//
+// XXX Rename message to something less arbitrary?
+func (tx *Transaction) AsMessage(s Signer) (Message, error) {
+ msg := Message{
+ nonce: tx.data.AccountNonce,
+ price: new(big.Int).Set(tx.data.Price),
+ gasLimit: new(big.Int).Set(tx.data.GasLimit),
+ to: tx.data.Recipient,
+ amount: tx.data.Amount,
+ data: tx.data.Payload,
}
- // 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
+ var err error
+ msg.from, err = Sender(s, tx)
+ return msg, err
+}
- // recover the public key from the signature
- hash := tx.SigHash()
- 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
+// SignECDSA signs the transaction using the given signer and private key
+//
+// XXX This only makes for a nice API: NewTx(...).SignECDSA(signer, prv). Should
+// we keep this?
+func (tx *Transaction) SignECDSA(signer Signer, prv *ecdsa.PrivateKey) (*Transaction, error) {
+ return signer.SignECDSA(tx, prv)
}
// WithSignature returns a new transaction with the given signature.
// This signature needs to be formatted as described in the yellow paper (v+27).
-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]
- return cpy, nil
+func (tx *Transaction) WithSignature(signer Signer, sig []byte) (*Transaction, error) {
+ return signer.WithSignature(tx, sig)
}
-func (tx *Transaction) SignECDSA(prv *ecdsa.PrivateKey) (*Transaction, error) {
- h := tx.SigHash()
- sig, err := crypto.SignEthereum(h[:], prv)
- if err != nil {
- return nil, err
- }
- return tx.WithSignature(sig)
+// Cost returns amount + gasprice * gaslimit.
+func (tx *Transaction) Cost() *big.Int {
+ total := new(big.Int).Mul(tx.data.Price, tx.data.GasLimit)
+ total.Add(total, tx.data.Amount)
+ return total
+}
+
+func (tx *Transaction) RawSignatureValues() (*big.Int, *big.Int, *big.Int) {
+ return tx.data.V, tx.data.R, tx.data.S
}
func (tx *Transaction) String() string {
+ // make a best guess about the signer and use that to derive
+ // the sender.
+ signer := deriveSigner(tx.data.V)
+
var from, to string
- if f, err := tx.From(); err != nil {
- from = "[invalid sender]"
+ if f, err := Sender(signer, tx); err != nil { // derive but don't cache
+ from = "[invalid sender: invalid sig]"
} else {
from = fmt.Sprintf("%x", f[:])
}
@@ -485,8 +508,9 @@ func (t *TransactionsByPriceAndNonce) Peek() *Transaction {
// Shift replaces the current best head with the next one from the same account.
func (t *TransactionsByPriceAndNonce) Shift() {
- acc, _ := t.heads[0].From() // we only sort valid txs so this cannot fail
-
+ signer := deriveSigner(t.heads[0].data.V)
+ // derive signer but don't cache.
+ acc, _ := Sender(signer, t.heads[0]) // we only sort valid txs so this cannot fail
if txs, ok := t.txs[acc]; ok && len(txs) > 0 {
t.heads[0], t.txs[acc] = txs[0], txs[1:]
heap.Fix(&t.heads, 0)
@@ -501,3 +525,35 @@ func (t *TransactionsByPriceAndNonce) Shift() {
func (t *TransactionsByPriceAndNonce) Pop() {
heap.Pop(&t.heads)
}
+
+// Message is a fully derived transaction and implements core.Message
+//
+// NOTE: In a future PR this will be removed.
+type Message struct {
+ to *common.Address
+ from common.Address
+ nonce uint64
+ amount, price, gasLimit *big.Int
+ data []byte
+}
+
+func NewMessage(from common.Address, to *common.Address, nonce uint64, amount, gasLimit, price *big.Int, data []byte) Message {
+ return Message{
+ from: from,
+ to: to,
+ nonce: nonce,
+ amount: amount,
+ price: price,
+ gasLimit: gasLimit,
+ data: data,
+ }
+}
+
+func (m Message) From() common.Address { return m.from }
+func (m Message) To() *common.Address { return m.to }
+func (m Message) GasPrice() *big.Int { return m.price }
+func (m Message) Value() *big.Int { return m.amount }
+func (m Message) Gas() *big.Int { return m.gasLimit }
+func (m Message) Nonce() uint64 { return m.nonce }
+func (m Message) Data() []byte { return m.data }
+func (m Message) CheckNonce() bool { return true }
diff --git a/core/types/transaction_signing.go b/core/types/transaction_signing.go
new file mode 100644
index 000000000..48209e2d8
--- /dev/null
+++ b/core/types/transaction_signing.go
@@ -0,0 +1,340 @@
+// Copyright 2016 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+package types
+
+import (
+ "crypto/ecdsa"
+ "errors"
+ "fmt"
+ "math/big"
+ "reflect"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/crypto"
+ "github.com/ethereum/go-ethereum/params"
+)
+
+// sigCache is used to cache the derived sender and contains
+// the signer used to derive it.
+type sigCache struct {
+ signer Signer
+ from common.Address
+}
+
+// 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.SignEthereum(h[:], prv)
+ if err != nil {
+ return nil, err
+ }
+ return s.WithSignature(tx, sig)
+}
+
+// Sender derives the sender from the tx using the signer derivation
+// functions.
+
+// Sender 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.
+//
+// Sender may cache the address, allowing it to be used regardless of
+// signing method. The cache is invalidated if the cached signer does
+// not match the signer used in the current call.
+func Sender(signer Signer, tx *Transaction) (common.Address, error) {
+ if sc := tx.from.Load(); sc != nil {
+ sigCache := sc.(sigCache)
+ // If the signer used to derive from in a previous
+ // call is not the same as used current, invalidate
+ // the cache.
+ if reflect.TypeOf(sigCache.signer) == reflect.TypeOf(signer) {
+ return sigCache.from, nil
+ }
+ }
+
+ pubkey, err := signer.PublicKey(tx)
+ if err != nil {
+ return common.Address{}, err
+ }
+ var addr common.Address
+ copy(addr[:], crypto.Keccak256(pubkey[1:])[12:])
+ tx.from.Store(sigCache{signer: signer, from: 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)
+ }
+
+ 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{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] - 27 + 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.SignEthereum(h[:], prv)
+ if err != nil {
+ return nil, err
+ }
+ return s.WithSignature(tx, sig)
+}
+
+// HomesteadTransaction implements TransactionInterface using the
+// homestead rules.
+type HomesteadSigner struct{ FrontierSigner }
+
+// 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{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]})
+ return cpy, nil
+}
+
+func (hs HomesteadSigner) SignECDSA(tx *Transaction, prv *ecdsa.PrivateKey) (*Transaction, error) {
+ h := hs.Hash(tx)
+ sig, err := crypto.SignEthereum(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
+}
+
+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{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]})
+ return cpy, nil
+}
+
+func (fs FrontierSigner) SignECDSA(tx *Transaction, prv *ecdsa.PrivateKey) (*Transaction, error) {
+ h := fs.Hash(tx)
+ sig, err := crypto.SignEthereum(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..89c590262
--- /dev/null
+++ b/core/types/transaction_signing_test.go
@@ -0,0 +1,116 @@
+// Copyright 2016 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+package types
+
+import (
+ "math/big"
+ "testing"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/crypto"
+ "github.com/ethereum/go-ethereum/rlp"
+)
+
+func TestEIP155Signing(t *testing.T) {
+ key, _ := crypto.GenerateKey()
+ addr := crypto.PubkeyToAddress(key.PublicKey)
+
+ signer := NewEIP155Signer(big.NewInt(18))
+ tx, err := NewTransaction(0, addr, new(big.Int), new(big.Int), new(big.Int), nil).SignECDSA(signer, key)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ from, err := Sender(signer, tx)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if from != addr {
+ t.Errorf("exected from and address to be equal. Got %x want %x", from, addr)
+ }
+}
+
+func TestEIP155ChainId(t *testing.T) {
+ key, _ := crypto.GenerateKey()
+ addr := crypto.PubkeyToAddress(key.PublicKey)
+
+ signer := NewEIP155Signer(big.NewInt(18))
+ tx, err := NewTransaction(0, addr, new(big.Int), new(big.Int), new(big.Int), nil).SignECDSA(signer, key)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !tx.Protected() {
+ t.Fatal("expected tx to be protected")
+ }
+
+ if tx.ChainId().Cmp(signer.chainId) != 0 {
+ t.Error("expected chainId to be", signer.chainId, "got", tx.ChainId())
+ }
+
+ tx = NewTransaction(0, addr, new(big.Int), new(big.Int), new(big.Int), nil)
+ tx, err = tx.SignECDSA(HomesteadSigner{}, key)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if tx.Protected() {
+ t.Error("didn't expect tx to be protected")
+ }
+
+ if tx.ChainId().BitLen() > 0 {
+ t.Error("expected chain id to be 0 got", tx.ChainId())
+ }
+}
+
+func TestEIP155SigningVitalik(t *testing.T) {
+ // Test vectors come from http://vitalik.ca/files/eip155_testvec.txt
+ for i, test := range []struct {
+ txRlp, addr string
+ }{
+ {"f864808504a817c800825208943535353535353535353535353535353535353535808025a0044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116da0044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d", "0xf0f6f18bca1b28cd68e4357452947e021241e9ce"},
+ {"f864018504a817c80182a410943535353535353535353535353535353535353535018025a0489efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bcaa0489efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6", "0x23ef145a395ea3fa3deb533b8a9e1b4c6c25d112"},
+ {"f864028504a817c80282f618943535353535353535353535353535353535353535088025a02d7c5bef027816a800da1736444fb58a807ef4c9603b7848673f7e3a68eb14a5a02d7c5bef027816a800da1736444fb58a807ef4c9603b7848673f7e3a68eb14a5", "0x2e485e0c23b4c3c542628a5f672eeab0ad4888be"},
+ {"f865038504a817c803830148209435353535353535353535353535353535353535351b8025a02a80e1ef1d7842f27f2e6be0972bb708b9a135c38860dbe73c27c3486c34f4e0a02a80e1ef1d7842f27f2e6be0972bb708b9a135c38860dbe73c27c3486c34f4de", "0x82a88539669a3fd524d669e858935de5e5410cf0"},
+ {"f865048504a817c80483019a28943535353535353535353535353535353535353535408025a013600b294191fc92924bb3ce4b969c1e7e2bab8f4c93c3fc6d0a51733df3c063a013600b294191fc92924bb3ce4b969c1e7e2bab8f4c93c3fc6d0a51733df3c060", "0xf9358f2538fd5ccfeb848b64a96b743fcc930554"},
+ {"f865058504a817c8058301ec309435353535353535353535353535353535353535357d8025a04eebf77a833b30520287ddd9478ff51abbdffa30aa90a8d655dba0e8a79ce0c1a04eebf77a833b30520287ddd9478ff51abbdffa30aa90a8d655dba0e8a79ce0c1", "0xa8f7aba377317440bc5b26198a363ad22af1f3a4"},
+ {"f866068504a817c80683023e3894353535353535353535353535353535353535353581d88025a06455bf8ea6e7463a1046a0b52804526e119b4bf5136279614e0b1e8e296a4e2fa06455bf8ea6e7463a1046a0b52804526e119b4bf5136279614e0b1e8e296a4e2d", "0xf1f571dc362a0e5b2696b8e775f8491d3e50de35"},
+ {"f867078504a817c807830290409435353535353535353535353535353535353535358201578025a052f1a9b320cab38e5da8a8f97989383aab0a49165fc91c737310e4f7e9821021a052f1a9b320cab38e5da8a8f97989383aab0a49165fc91c737310e4f7e9821021", "0xd37922162ab7cea97c97a87551ed02c9a38b7332"},
+ {"f867088504a817c8088302e2489435353535353535353535353535353535353535358202008025a064b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c12a064b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c10", "0x9bddad43f934d313c2b79ca28a432dd2b7281029"},
+ {"f867098504a817c809830334509435353535353535353535353535353535353535358202d98025a052f8f61201b2b11a78d6e866abc9c3db2ae8631fa656bfe5cb53668255367afba052f8f61201b2b11a78d6e866abc9c3db2ae8631fa656bfe5cb53668255367afb", "0x3c24d7329e92f84f08556ceb6df1cdb0104ca49f"},
+ } {
+ signer := NewEIP155Signer(big.NewInt(1))
+
+ var tx *Transaction
+ err := rlp.DecodeBytes(common.Hex2Bytes(test.txRlp), &tx)
+ if err != nil {
+ t.Errorf("%d: %v", i, err)
+ continue
+ }
+
+ from, err := Sender(signer, tx)
+ if err != nil {
+ t.Errorf("%d: %v", i, err)
+ continue
+ }
+
+ addr := common.HexToAddress(test.addr)
+ if from != addr {
+ t.Errorf("%d: expected %x got %x", i, addr, from)
+ }
+
+ }
+}
diff --git a/core/types/transaction_test.go b/core/types/transaction_test.go
index 98a78d221..ca105566a 100644
--- a/core/types/transaction_test.go
+++ b/core/types/transaction_test.go
@@ -46,15 +46,16 @@ var (
big.NewInt(1),
common.FromHex("5544"),
).WithSignature(
+ HomesteadSigner{},
common.Hex2Bytes("98ff921201554726367d2be8c804a7ff89ccf285ebc57dff8ae4c44b9c19ac4a8887321be575c8095f789dd4c743dfe42c1820f9231f98a962b210e3ac2452a31c"),
)
)
func TestTransactionSigHash(t *testing.T) {
- if emptyTx.SigHash() != common.HexToHash("c775b99e7ad12f50d819fcd602390467e28141316969f4b57f0626f74fe3b386") {
+ if emptyTx.SigHash(HomesteadSigner{}) != common.HexToHash("c775b99e7ad12f50d819fcd602390467e28141316969f4b57f0626f74fe3b386") {
t.Errorf("empty transaction hash mismatch, got %x", emptyTx.Hash())
}
- if rightvrsTx.SigHash() != common.HexToHash("fe7a79529ed5f7c3375d06b26b186a8644e0e16c373d7a12be41c62d6042b77a") {
+ if rightvrsTx.SigHash(HomesteadSigner{}) != common.HexToHash("fe7a79529ed5f7c3375d06b26b186a8644e0e16c373d7a12be41c62d6042b77a") {
t.Errorf("RightVRS transaction hash mismatch, got %x", rightvrsTx.Hash())
}
}
@@ -72,7 +73,9 @@ func TestTransactionEncode(t *testing.T) {
func decodeTx(data []byte) (*Transaction, error) {
var tx Transaction
- return &tx, rlp.Decode(bytes.NewReader(data), &tx)
+ t, err := &tx, rlp.Decode(bytes.NewReader(data), &tx)
+
+ return t, err
}
func defaultTestKey() (*ecdsa.PrivateKey, common.Address) {
@@ -88,7 +91,8 @@ func TestRecipientEmpty(t *testing.T) {
t.Error(err)
t.FailNow()
}
- from, err := tx.From()
+
+ from, err := Sender(HomesteadSigner{}, tx)
if err != nil {
t.Error(err)
t.FailNow()
@@ -107,7 +111,7 @@ func TestRecipientNormal(t *testing.T) {
t.FailNow()
}
- from, err := tx.From()
+ from, err := Sender(HomesteadSigner{}, tx)
if err != nil {
t.Error(err)
t.FailNow()
@@ -127,12 +131,14 @@ func TestTransactionPriceNonceSort(t *testing.T) {
for i := 0; i < len(keys); i++ {
keys[i], _ = crypto.GenerateKey()
}
+
+ signer := HomesteadSigner{}
// Generate a batch of transactions with overlapping values, but shifted nonces
groups := map[common.Address]Transactions{}
for start, key := range keys {
addr := crypto.PubkeyToAddress(key.PublicKey)
for i := 0; i < 25; i++ {
- tx, _ := NewTransaction(uint64(start+i), common.Address{}, big.NewInt(100), big.NewInt(100), big.NewInt(int64(start+i)), nil).SignECDSA(key)
+ tx, _ := NewTransaction(uint64(start+i), common.Address{}, big.NewInt(100), big.NewInt(100), big.NewInt(int64(start+i)), nil).SignECDSA(signer, key)
groups[addr] = append(groups[addr], tx)
}
}
@@ -148,11 +154,11 @@ func TestTransactionPriceNonceSort(t *testing.T) {
break
}
for i, txi := range txs {
- fromi, _ := txi.From()
+ fromi, _ := Sender(signer, txi)
// Make sure the nonce order is valid
for j, txj := range txs[i+1:] {
- fromj, _ := txj.From()
+ fromj, _ := Sender(signer, txj)
if fromi == fromj && txi.Nonce() > txj.Nonce() {
t.Errorf("invalid nonce ordering: tx #%d (A=%x N=%v) < tx #%d (A=%x N=%v)", i, fromi[:4], txi.Nonce(), i+j, fromj[:4], txj.Nonce())
@@ -161,20 +167,20 @@ func TestTransactionPriceNonceSort(t *testing.T) {
// Find the previous and next nonce of this account
prev, next := i-1, i+1
for j := i - 1; j >= 0; j-- {
- if fromj, _ := txs[j].From(); fromi == fromj {
+ if fromj, _ := Sender(signer, txs[j]); fromi == fromj {
prev = j
break
}
}
for j := i + 1; j < len(txs); j++ {
- if fromj, _ := txs[j].From(); fromi == fromj {
+ if fromj, _ := Sender(signer, txs[j]); fromi == fromj {
next = j
break
}
}
// Make sure that in between the neighbor nonces, the transaction is correctly positioned price wise
for j := prev + 1; j < next; j++ {
- fromj, _ := txs[j].From()
+ fromj, _ := Sender(signer, txs[j])
if j < i && txs[j].GasPrice().Cmp(txi.GasPrice()) < 0 {
t.Errorf("invalid gasprice ordering: tx #%d (A=%x P=%v) < tx #%d (A=%x P=%v)", j, fromj[:4], txs[j].GasPrice(), i, fromi[:4], txi.GasPrice())
}
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..6f7ba9250 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 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(&params.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(&params.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..d51b435f8 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,16 @@ 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.ChainConfig{
+ ChainId: big.NewInt(1),
+ HomesteadBlock: new(big.Int),
+ DAOForkBlock: new(big.Int),
+ DAOForkSupport: false,
+ EIP150Block: new(big.Int),
+ EIP155Block: new(big.Int),
+ EIP158Block: new(big.Int),
+ }
}
if cfg.Difficulty == nil {
diff --git a/core/vm/vm.go b/core/vm/vm.go
index 09cddc2f8..56aca6912 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)
}
}
@@ -302,7 +313,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 {
@@ -378,12 +390,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..43637bd13 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 { return self.msg.From() }
+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)
}