From 68966a89f1a7f7c23ec835f2beced03c39f9095e Mon Sep 17 00:00:00 2001 From: arkpar Date: Fri, 31 Mar 2017 16:06:58 +0200 Subject: [PATCH 1/2] EIP-86 --- ethcore/light/src/client/mod.rs | 7 ++ ethcore/res/ethereum/classic.json | 3 +- ethcore/res/ethereum/eip150_test.json | 3 +- ethcore/res/ethereum/eip161_test.json | 3 +- ethcore/res/ethereum/expanse.json | 3 +- ethcore/res/ethereum/foundation.json | 3 +- ethcore/res/ethereum/frontier_like_test.json | 3 +- ethcore/res/ethereum/frontier_test.json | 3 +- ethcore/res/ethereum/homestead_test.json | 3 +- ethcore/res/ethereum/morden.json | 3 +- ethcore/res/ethereum/olympic.json | 3 +- ethcore/res/ethereum/ropsten.json | 3 +- ethcore/res/ethereum/tests | 2 +- ethcore/res/ethereum/transition_test.json | 5 +- ethcore/res/null.json | 3 +- ethcore/src/client/client.rs | 21 ++-- ethcore/src/client/test_client.rs | 4 +- ethcore/src/client/traits.rs | 3 + ethcore/src/engines/authority_round.rs | 19 +-- ethcore/src/engines/basic_authority.rs | 17 +-- ethcore/src/engines/instant_seal.rs | 7 +- ethcore/src/engines/mod.rs | 17 +-- ethcore/src/engines/null_engine.rs | 4 +- ethcore/src/engines/tendermint/mod.rs | 19 +-- .../engines/validator_set/safe_contract.rs | 7 +- ethcore/src/ethereum/ethash.rs | 53 +++----- ethcore/src/evm/ext.rs | 13 +- ethcore/src/evm/instructions.rs | 3 + ethcore/src/evm/interpreter/gasometer.rs | 2 +- ethcore/src/evm/interpreter/mod.rs | 12 +- ethcore/src/evm/mod.rs | 2 +- ethcore/src/evm/schedule.rs | 16 ++- ethcore/src/evm/tests.rs | 4 +- ethcore/src/executive.rs | 77 ++++++++---- ethcore/src/externalities.rs | 20 +-- ethcore/src/json_tests/executive.rs | 15 ++- ethcore/src/json_tests/state.rs | 19 ++- ethcore/src/json_tests/transaction.rs | 24 ++-- ethcore/src/lib.rs | 1 + ethcore/src/miner/miner.rs | 15 ++- ethcore/src/spec/spec.rs | 5 + ethcore/src/state/mod.rs | 2 +- ethcore/src/tests/client.rs | 2 +- ethcore/src/tests/helpers.rs | 5 +- ethcore/src/types/transaction.rs | 116 +++++++++++++----- evmbin/src/ext.rs | 6 +- json/src/spec/params.rs | 3 + json/src/state/test.rs | 2 +- json/src/state/transaction.rs | 2 +- parity/rpc_apis.rs | 1 + rpc/src/v1/impls/eth.rs | 9 +- rpc/src/v1/impls/light/eth.rs | 7 +- rpc/src/v1/impls/light/parity.rs | 9 +- rpc/src/v1/impls/parity.rs | 11 +- rpc/src/v1/impls/parity_set.rs | 9 +- rpc/src/v1/tests/mocked/parity_set.rs | 2 +- rpc/src/v1/tests/mocked/signing.rs | 2 +- rpc/src/v1/types/transaction.rs | 49 ++++---- sync/src/tests/consensus.rs | 36 +++--- 59 files changed, 433 insertions(+), 289 deletions(-) diff --git a/ethcore/light/src/client/mod.rs b/ethcore/light/src/client/mod.rs index 6d241f7fdb1..7e6213273bb 100644 --- a/ethcore/light/src/client/mod.rs +++ b/ethcore/light/src/client/mod.rs @@ -106,6 +106,9 @@ pub trait LightChainClient: Send + Sync { /// Get the `i`th CHT root. fn cht_root(&self, i: usize) -> Option; + + /// Get the EIP-86 transition block number. + fn eip86_transition(&self) -> u64; } /// Something which can be treated as a `LightChainClient`. @@ -384,4 +387,8 @@ impl LightChainClient for Client { fn cht_root(&self, i: usize) -> Option { Client::cht_root(self, i) } + + fn eip86_transition(&self) -> u64 { + self.engine().params().eip86_transition + } } diff --git a/ethcore/res/ethereum/classic.json b/ethcore/res/ethereum/classic.json index f6d30c4d6ee..f7cb4dacaa0 100644 --- a/ethcore/res/ethereum/classic.json +++ b/ethcore/res/ethereum/classic.json @@ -30,7 +30,8 @@ "chainID": "0x3d", "forkBlock": "0x1d4c00", "forkCanonHash": "0x94365e3a8c0b35089c1d1195081fe7489b528a84b22199c916180db8b28ade7f", - "eip98Transition": "0x7fffffffffffff" + "eip98Transition": "0x7fffffffffffff", + "eip86Transition": "0x7fffffffffffff" }, "genesis": { "seal": { diff --git a/ethcore/res/ethereum/eip150_test.json b/ethcore/res/ethereum/eip150_test.json index 8331c8fc2a7..3091ee27b57 100644 --- a/ethcore/res/ethereum/eip150_test.json +++ b/ethcore/res/ethereum/eip150_test.json @@ -24,7 +24,8 @@ "maximumExtraDataSize": "0x20", "minGasLimit": "0x1388", "networkID" : "0x1", - "eip98Transition": "0x7fffffffffffffff" + "eip98Transition": "0x7fffffffffffffff", + "eip86Transition": "0x7fffffffffffffff" }, "genesis": { "seal": { diff --git a/ethcore/res/ethereum/eip161_test.json b/ethcore/res/ethereum/eip161_test.json index 00885bae18f..e6e8bf3bb03 100644 --- a/ethcore/res/ethereum/eip161_test.json +++ b/ethcore/res/ethereum/eip161_test.json @@ -24,7 +24,8 @@ "maximumExtraDataSize": "0x20", "minGasLimit": "0x1388", "networkID" : "0x1", - "eip98Transition": "0x7fffffffffffffff" + "eip98Transition": "0x7fffffffffffffff", + "eip86Transition": "0x7fffffffffffffff" }, "genesis": { "seal": { diff --git a/ethcore/res/ethereum/expanse.json b/ethcore/res/ethereum/expanse.json index 3ec04052b46..b7d22ac3ed4 100644 --- a/ethcore/res/ethereum/expanse.json +++ b/ethcore/res/ethereum/expanse.json @@ -30,7 +30,8 @@ "networkID": "0x1", "chainID": "0x2", "subprotocolName": "exp", - "eip98Transition": "0x7fffffffffffff" + "eip98Transition": "0x7fffffffffffff", + "eip86Transition": "0x7fffffffffffff" }, "genesis": { "seal": { diff --git a/ethcore/res/ethereum/foundation.json b/ethcore/res/ethereum/foundation.json index 54f10b70c95..8253987f3ed 100644 --- a/ethcore/res/ethereum/foundation.json +++ b/ethcore/res/ethereum/foundation.json @@ -147,7 +147,8 @@ "networkID" : "0x1", "forkBlock": "0x1d4c00", "forkCanonHash": "0x4985f5ca3d2afbec36529aa96f74de3cc10a2a4a6c44f2157a57d2c6059a11bb", - "eip98Transition": "0x7fffffffffffff" + "eip98Transition": "0x7fffffffffffff", + "eip86Transition": "0x7fffffffffffff" }, "genesis": { "seal": { diff --git a/ethcore/res/ethereum/frontier_like_test.json b/ethcore/res/ethereum/frontier_like_test.json index 7b73faa30dd..aab433033e6 100644 --- a/ethcore/res/ethereum/frontier_like_test.json +++ b/ethcore/res/ethereum/frontier_like_test.json @@ -143,7 +143,8 @@ "maximumExtraDataSize": "0x20", "minGasLimit": "0x1388", "networkID" : "0x1", - "eip98Transition": "0x7fffffffffffff" + "eip98Transition": "0x7fffffffffffff", + "eip86Transition": "0x7fffffffffffff" }, "genesis": { "seal": { diff --git a/ethcore/res/ethereum/frontier_test.json b/ethcore/res/ethereum/frontier_test.json index bf79729ba65..6761c79f3a8 100644 --- a/ethcore/res/ethereum/frontier_test.json +++ b/ethcore/res/ethereum/frontier_test.json @@ -23,7 +23,8 @@ "maximumExtraDataSize": "0x20", "minGasLimit": "0x1388", "networkID" : "0x1", - "eip98Transition": "0x7fffffffffffff" + "eip98Transition": "0x7fffffffffffff", + "eip86Transition": "0x7fffffffffffff" }, "genesis": { "seal": { diff --git a/ethcore/res/ethereum/homestead_test.json b/ethcore/res/ethereum/homestead_test.json index 95bd85a940d..25b06185769 100644 --- a/ethcore/res/ethereum/homestead_test.json +++ b/ethcore/res/ethereum/homestead_test.json @@ -23,7 +23,8 @@ "maximumExtraDataSize": "0x20", "minGasLimit": "0x1388", "networkID" : "0x1", - "eip98Transition": "0x7fffffffffffff" + "eip98Transition": "0x7fffffffffffff", + "eip86Transition": "0x7fffffffffffff" }, "genesis": { "seal": { diff --git a/ethcore/res/ethereum/morden.json b/ethcore/res/ethereum/morden.json index 22f253bf85d..495849dafed 100644 --- a/ethcore/res/ethereum/morden.json +++ b/ethcore/res/ethereum/morden.json @@ -30,7 +30,8 @@ "chainID": "0x3e", "forkBlock": "0x1b34d8", "forkCanonHash": "0xf376243aeff1f256d970714c3de9fd78fa4e63cf63e32a51fe1169e375d98145", - "eip98Transition": "0x7fffffffffffff" + "eip98Transition": "0x7fffffffffffff", + "eip86Transition": "0x7fffffffffffff" }, "genesis": { "seal": { diff --git a/ethcore/res/ethereum/olympic.json b/ethcore/res/ethereum/olympic.json index baf1c7d050b..3d2e7baf96a 100644 --- a/ethcore/res/ethereum/olympic.json +++ b/ethcore/res/ethereum/olympic.json @@ -23,7 +23,8 @@ "maximumExtraDataSize": "0x0400", "minGasLimit": "125000", "networkID" : "0x0", - "eip98Transition": "0x7fffffffffffff" + "eip98Transition": "0x7fffffffffffff", + "eip86Transition": "0x7fffffffffffff" }, "genesis": { "seal": { diff --git a/ethcore/res/ethereum/ropsten.json b/ethcore/res/ethereum/ropsten.json index 5391682794c..1e350d636fb 100644 --- a/ethcore/res/ethereum/ropsten.json +++ b/ethcore/res/ethereum/ropsten.json @@ -27,7 +27,8 @@ "networkID" : "0x3", "forkBlock": 641350, "forkCanonHash": "0x8033403e9fe5811a7b6d6b469905915de1c59207ce2172cbcf5d6ff14fa6a2eb", - "eip98Transition": "0x7fffffffffffff" + "eip98Transition": "0x7fffffffffffff", + "eip86Transition": "0x7fffffffffffff" }, "genesis": { "seal": { diff --git a/ethcore/res/ethereum/tests b/ethcore/res/ethereum/tests index d520593078f..ef191fdc61c 160000 --- a/ethcore/res/ethereum/tests +++ b/ethcore/res/ethereum/tests @@ -1 +1 @@ -Subproject commit d520593078fa0849dcd1f907e44ed0a616892e33 +Subproject commit ef191fdc61cf76cdb9cdc147465fb447304b0ed2 diff --git a/ethcore/res/ethereum/transition_test.json b/ethcore/res/ethereum/transition_test.json index 1b502f0876d..41251dc8c31 100644 --- a/ethcore/res/ethereum/transition_test.json +++ b/ethcore/res/ethereum/transition_test.json @@ -139,11 +139,12 @@ } }, "params": { - "eip98Transition": "0x7fffffffffffffff", "accountStartNonce": "0x00", "maximumExtraDataSize": "0x20", "minGasLimit": "0x1388", - "networkID" : "0x1" + "networkID" : "0x1", + "eip98Transition": "0x7fffffffffffff", + "eip86Transition": "0x7fffffffffffff" }, "genesis": { "seal": { diff --git a/ethcore/res/null.json b/ethcore/res/null.json index 3ec7ce75e45..ffb0ea06109 100644 --- a/ethcore/res/null.json +++ b/ethcore/res/null.json @@ -7,7 +7,8 @@ "accountStartNonce": "0x0", "maximumExtraDataSize": "0x20", "minGasLimit": "0x1388", - "networkID" : "0x2" + "networkID" : "0x2", + "eip86Transition": "0x7fffffffffffff" }, "genesis": { "seal": { diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index 2ad4fa8d661..e7755e4d00a 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -960,7 +960,7 @@ impl BlockChainClient for Client { return Err(err.into()) } } - let lower = t.gas_required(&self.engine.schedule(&env_info)).into(); + let lower = t.gas_required(&self.engine.schedule(env_info.number)).into(); if cond(lower)? { trace!(target: "estimate_gas", "estimate_gas succeeded with {}", lower); return Ok(lower) @@ -1259,7 +1259,8 @@ impl BlockChainClient for Client { .collect(); match (transaction, previous_receipts) { (Some(transaction), Some(previous_receipts)) => { - Some(transaction_receipt(transaction, previous_receipts)) + let schedule = self.engine().schedule(block_number); + Some(transaction_receipt(&schedule, transaction, previous_receipts)) }, _ => None, } @@ -1501,11 +1502,15 @@ impl BlockChainClient for Client { }) .and_then(|a| if a.is_zero() { None } else { Some(a) }) } + + fn eip86_transition(&self) -> u64 { + self.engine().params().eip86_transition + } } impl MiningBlockChainClient for Client { fn latest_schedule(&self) -> Schedule { - self.engine.schedule(&self.latest_env_info()) + self.engine.schedule(self.latest_env_info().number) } fn prepare_open_block(&self, author: Address, gas_range_target: (U256, U256), extra_data: Bytes) -> OpenBlock { @@ -1655,7 +1660,7 @@ impl Drop for Client { /// Returns `LocalizedReceipt` given `LocalizedTransaction` /// and a vector of receipts from given block up to transaction index. -fn transaction_receipt(mut tx: LocalizedTransaction, mut receipts: Vec) -> LocalizedReceipt { +fn transaction_receipt(schedule: &Schedule, mut tx: LocalizedTransaction, mut receipts: Vec) -> LocalizedReceipt { assert_eq!(receipts.len(), tx.transaction_index + 1, "All previous receipts are provided."); let sender = tx.sender(); @@ -1674,12 +1679,12 @@ fn transaction_receipt(mut tx: LocalizedTransaction, mut receipts: Vec) transaction_hash: transaction_hash, transaction_index: transaction_index, block_hash: block_hash, - block_number:block_number, + block_number: block_number, cumulative_gas_used: receipt.gas_used, gas_used: receipt.gas_used - prior_gas_used, contract_address: match tx.action { Action::Call(_) => None, - Action::Create => Some(contract_address(&sender, &tx.nonce)) + Action::Create => Some(contract_address(schedule.create_address, &sender, &tx.nonce, &tx.data.sha3())) }, logs: receipt.logs.into_iter().enumerate().map(|(i, log)| LocalizedLogEntry { entry: log, @@ -1734,6 +1739,7 @@ mod tests { #[test] fn should_return_correct_log_index() { use super::transaction_receipt; + use evm::schedule::Schedule; use ethkey::KeyPair; use log_entry::{LogEntry, LocalizedLogEntry}; use receipt::{Receipt, LocalizedReceipt}; @@ -1743,6 +1749,7 @@ mod tests { // given let key = KeyPair::from_secret_slice(&"test".sha3()).unwrap(); let secret = key.secret(); + let schedule = Schedule::new_homestead(); let block_number = 1; let block_hash = 5.into(); @@ -1786,7 +1793,7 @@ mod tests { }]; // when - let receipt = transaction_receipt(transaction, receipts); + let receipt = transaction_receipt(&schedule, transaction, receipts); // then assert_eq!(receipt, LocalizedReceipt { diff --git a/ethcore/src/client/test_client.rs b/ethcore/src/client/test_client.rs index 16f38203f46..feda29107dd 100644 --- a/ethcore/src/client/test_client.rs +++ b/ethcore/src/client/test_client.rs @@ -353,7 +353,7 @@ pub fn get_temp_state_db() -> GuardedTempResult { impl MiningBlockChainClient for TestBlockChainClient { fn latest_schedule(&self) -> Schedule { - Schedule::new_post_eip150(24576, true, true, true) + Schedule::new_post_eip150(24576, true, true, true, true) } fn prepare_open_block(&self, author: Address, gas_range_target: (U256, U256), extra_data: Bytes) -> OpenBlock { @@ -756,6 +756,8 @@ impl BlockChainClient for TestBlockChainClient { fn registrar_address(&self) -> Option
{ None } fn registry_address(&self, _name: String) -> Option
{ None } + + fn eip86_transition(&self) -> u64 { u64::max_value() } } impl ProvingBlockChainClient for TestBlockChainClient { diff --git a/ethcore/src/client/traits.rs b/ethcore/src/client/traits.rs index a612d8a7754..8ccd022e2c9 100644 --- a/ethcore/src/client/traits.rs +++ b/ethcore/src/client/traits.rs @@ -272,6 +272,9 @@ pub trait BlockChainClient : Sync + Send { /// Get the address of a particular blockchain service, if available. fn registry_address(&self, name: String) -> Option
; + + /// Get the EIP-86 transition block number. + fn eip86_transition(&self) -> u64; } impl IpcConfig for BlockChainClient { } diff --git a/ethcore/src/engines/authority_round.rs b/ethcore/src/engines/authority_round.rs index 2e8e3932cd4..85c848d1628 100644 --- a/ethcore/src/engines/authority_round.rs +++ b/ethcore/src/engines/authority_round.rs @@ -26,12 +26,11 @@ use account_provider::AccountProvider; use block::*; use spec::CommonParams; use engines::{Engine, Seal, EngineError}; -use header::Header; +use header::{Header, BlockNumber}; use error::{Error, TransactionError, BlockError}; use evm::Schedule; use ethjson; use io::{IoContext, IoHandler, TimerToken, IoService}; -use env_info::EnvInfo; use builtin::Builtin; use transaction::UnverifiedTransaction; use client::{Client, EngineClient}; @@ -241,8 +240,9 @@ impl Engine for AuthorityRound { ] } - fn schedule(&self, _env_info: &EnvInfo) -> Schedule { - Schedule::new_post_eip150(usize::max_value(), true, true, true) + fn schedule(&self, block_number: BlockNumber) -> Schedule { + let eip86 = block_number >= self.params.eip86_transition; + Schedule::new_post_eip150(usize::max_value(), true, true, true, eip86) } fn populate_from_parent(&self, header: &mut Header, parent: &Header, gas_floor_target: U256, _gas_ceil_target: U256) { @@ -387,7 +387,6 @@ impl Engine for AuthorityRound { #[cfg(test)] mod tests { use util::*; - use env_info::EnvInfo; use header::Header; use error::{Error, BlockError}; use ethkey::Secret; @@ -408,15 +407,7 @@ mod tests { #[test] fn can_return_schedule() { let engine = Spec::new_test_round().engine; - let schedule = engine.schedule(&EnvInfo { - number: 10000000, - author: 0.into(), - timestamp: 0, - difficulty: 0.into(), - last_hashes: Arc::new(vec![]), - gas_used: 0.into(), - gas_limit: 0.into(), - }); + let schedule = engine.schedule(10000000); assert!(schedule.stack_limit > 0); } diff --git a/ethcore/src/engines/basic_authority.rs b/ethcore/src/engines/basic_authority.rs index 19867a64801..43be5fe70d3 100644 --- a/ethcore/src/engines/basic_authority.rs +++ b/ethcore/src/engines/basic_authority.rs @@ -24,11 +24,10 @@ use block::*; use builtin::Builtin; use spec::CommonParams; use engines::{Engine, Seal}; -use env_info::EnvInfo; use error::{BlockError, Error}; use evm::Schedule; use ethjson; -use header::Header; +use header::{Header, BlockNumber}; use client::Client; use super::signer::EngineSigner; use super::validator_set::{ValidatorSet, new_validator_set}; @@ -86,7 +85,7 @@ impl Engine for BasicAuthority { /// Additional engine-specific information for the user/developer concerning `header`. fn extra_info(&self, _header: &Header) -> BTreeMap { map!["signature".to_owned() => "TODO".to_owned()] } - fn schedule(&self, _env_info: &EnvInfo) -> Schedule { + fn schedule(&self, _block_number: BlockNumber) -> Schedule { Schedule::new_homestead() } @@ -181,7 +180,6 @@ impl Engine for BasicAuthority { mod tests { use util::*; use block::*; - use env_info::EnvInfo; use error::{BlockError, Error}; use tests::helpers::*; use account_provider::AccountProvider; @@ -206,16 +204,7 @@ mod tests { #[test] fn can_return_schedule() { let engine = new_test_authority().engine; - let schedule = engine.schedule(&EnvInfo { - number: 10000000, - author: 0.into(), - timestamp: 0, - difficulty: 0.into(), - last_hashes: Arc::new(vec![]), - gas_used: 0.into(), - gas_limit: 0.into(), - }); - + let schedule = engine.schedule(10000000); assert!(schedule.stack_limit > 0); } diff --git a/ethcore/src/engines/instant_seal.rs b/ethcore/src/engines/instant_seal.rs index 88672c6f432..702afde4f4f 100644 --- a/ethcore/src/engines/instant_seal.rs +++ b/ethcore/src/engines/instant_seal.rs @@ -18,10 +18,10 @@ use std::collections::BTreeMap; use util::{Address, HashMap}; use builtin::Builtin; use engines::{Engine, Seal}; -use env_info::EnvInfo; use spec::CommonParams; use evm::Schedule; use block::ExecutedBlock; +use header::BlockNumber; /// An engine which does not provide any consensus mechanism, just seals blocks internally. pub struct InstantSeal { @@ -58,8 +58,9 @@ impl Engine for InstantSeal { &self.builtins } - fn schedule(&self, _env_info: &EnvInfo) -> Schedule { - Schedule::new_post_eip150(usize::max_value(), true, true, true) + fn schedule(&self, block_number: BlockNumber) -> Schedule { + let eip86 = block_number >= self.params.eip86_transition; + Schedule::new_post_eip150(usize::max_value(), true, true, true, eip86) } fn seals_internally(&self) -> Option { Some(true) } diff --git a/ethcore/src/engines/mod.rs b/ethcore/src/engines/mod.rs index 438b9bda066..4de76f079d8 100644 --- a/ethcore/src/engines/mod.rs +++ b/ethcore/src/engines/mod.rs @@ -39,10 +39,10 @@ use account_provider::AccountProvider; use block::ExecutedBlock; use builtin::Builtin; use env_info::EnvInfo; -use error::{Error, TransactionError}; +use error::Error; use spec::CommonParams; use evm::Schedule; -use header::Header; +use header::{Header, BlockNumber}; use transaction::{UnverifiedTransaction, SignedTransaction}; use client::Client; @@ -107,8 +107,8 @@ pub trait Engine : Sync + Send { /// Get the general parameters of the chain. fn params(&self) -> &CommonParams; - /// Get the EVM schedule for the given `env_info`. - fn schedule(&self, env_info: &EnvInfo) -> Schedule; + /// Get the EVM schedule for the given `block_number`. + fn schedule(&self, block_number: BlockNumber) -> Schedule; /// Builtin-contracts we would like to see in the chain. /// (In principle these are just hints for the engine since that has the last word on them.) @@ -156,14 +156,7 @@ pub trait Engine : Sync + Send { // TODO: Add flags for which bits of the transaction to check. // TODO: consider including State in the params. fn verify_transaction_basic(&self, t: &UnverifiedTransaction, _header: &Header) -> Result<(), Error> { - t.check_low_s()?; - - if let Some(n) = t.network_id() { - if n != self.params().chain_id { - return Err(TransactionError::InvalidNetworkId.into()); - } - } - + t.verify_basic(true, Some(self.params().network_id), true)?; Ok(()) } diff --git a/ethcore/src/engines/null_engine.rs b/ethcore/src/engines/null_engine.rs index 0611fc08e97..838ddf809cc 100644 --- a/ethcore/src/engines/null_engine.rs +++ b/ethcore/src/engines/null_engine.rs @@ -20,7 +20,7 @@ use builtin::Builtin; use engines::Engine; use spec::CommonParams; use evm::Schedule; -use env_info::EnvInfo; +use header::BlockNumber; /// An engine which does not provide any consensus mechanism and does not seal blocks. pub struct NullEngine { @@ -57,7 +57,7 @@ impl Engine for NullEngine { &self.builtins } - fn schedule(&self, _env_info: &EnvInfo) -> Schedule { + fn schedule(&self, _block_number: BlockNumber) -> Schedule { Schedule::new_homestead() } } diff --git a/ethcore/src/engines/tendermint/mod.rs b/ethcore/src/engines/tendermint/mod.rs index cb284d3c45e..65e11d19eb4 100644 --- a/ethcore/src/engines/tendermint/mod.rs +++ b/ethcore/src/engines/tendermint/mod.rs @@ -30,9 +30,8 @@ use std::sync::atomic::{AtomicUsize, Ordering as AtomicOrdering}; use util::*; use client::{Client, EngineClient}; use error::{Error, BlockError}; -use header::Header; +use header::{Header, BlockNumber}; use builtin::Builtin; -use env_info::EnvInfo; use rlp::UntrustedRlp; use ethkey::{recover, public_to_address, Signature}; use account_provider::AccountProvider; @@ -405,8 +404,9 @@ impl Engine for Tendermint { ] } - fn schedule(&self, _env_info: &EnvInfo) -> Schedule { - Schedule::new_post_eip150(usize::max_value(), true, true, true) + fn schedule(&self, block_number: BlockNumber) -> Schedule { + let eip86 = block_number >= self.params.eip86_transition; + Schedule::new_post_eip150(usize::max_value(), true, true, true, eip86) } fn populate_from_parent(&self, header: &mut Header, parent: &Header, gas_floor_target: U256, _gas_ceil_target: U256) { @@ -658,7 +658,6 @@ mod tests { use block::*; use error::{Error, BlockError}; use header::Header; - use env_info::EnvInfo; use ethkey::Secret; use client::chain_notify::ChainNotify; use miner::MinerService; @@ -740,15 +739,7 @@ mod tests { #[test] fn can_return_schedule() { let engine = Spec::new_test_tendermint().engine; - let schedule = engine.schedule(&EnvInfo { - number: 10000000, - author: 0.into(), - timestamp: 0, - difficulty: 0.into(), - last_hashes: Arc::new(vec![]), - gas_used: 0.into(), - gas_limit: 0.into(), - }); + let schedule = engine.schedule(10000000); assert!(schedule.stack_limit > 0); } diff --git a/ethcore/src/engines/validator_set/safe_contract.rs b/ethcore/src/engines/validator_set/safe_contract.rs index 0a0eaecfd9e..a9388bbbf8a 100644 --- a/ethcore/src/engines/validator_set/safe_contract.rs +++ b/ethcore/src/engines/validator_set/safe_contract.rs @@ -196,6 +196,7 @@ mod tests { let s0 = Secret::from_slice(&"1".sha3()).unwrap(); let v0 = tap.insert_account(s0.clone(), "").unwrap(); let v1 = tap.insert_account(Secret::from_slice(&"0".sha3()).unwrap(), "").unwrap(); + let network_id = Spec::new_validator_safe_contract().network_id(); let client = generate_dummy_client_with_spec_and_accounts(Spec::new_validator_safe_contract, Some(tap)); client.engine().register_client(Arc::downgrade(&client)); let validator_contract = Address::from_str("0000000000000000000000000000000000000005").unwrap(); @@ -209,7 +210,7 @@ mod tests { action: Action::Call(validator_contract), value: 0.into(), data: "bfc708a000000000000000000000000082a978b3f5962a5b0957d9ee9eef472ee55b42f1".from_hex().unwrap(), - }.sign(&s0, None); + }.sign(&s0, Some(network_id)); client.miner().import_own_transaction(client.as_ref(), tx.into()).unwrap(); client.update_sealing(); assert_eq!(client.chain_info().best_block_number, 1); @@ -221,7 +222,7 @@ mod tests { action: Action::Call(validator_contract), value: 0.into(), data: "4d238c8e00000000000000000000000082a978b3f5962a5b0957d9ee9eef472ee55b42f1".from_hex().unwrap(), - }.sign(&s0, None); + }.sign(&s0, Some(network_id)); client.miner().import_own_transaction(client.as_ref(), tx.into()).unwrap(); client.update_sealing(); // The transaction is not yet included so still unable to seal. @@ -240,7 +241,7 @@ mod tests { action: Action::Call(Address::default()), value: 0.into(), data: Vec::new(), - }.sign(&s0, None); + }.sign(&s0, Some(network_id)); client.miner().import_own_transaction(client.as_ref(), tx.into()).unwrap(); client.update_sealing(); // Able to seal again. diff --git a/ethcore/src/ethereum/ethash.rs b/ethcore/src/ethereum/ethash.rs index 88655fa86e1..ae865da4f68 100644 --- a/ethcore/src/ethereum/ethash.rs +++ b/ethcore/src/ethereum/ethash.rs @@ -19,8 +19,8 @@ use util::*; use block::*; use builtin::Builtin; use env_info::EnvInfo; -use error::{BlockError, TransactionError, Error}; -use header::Header; +use error::{BlockError, Error, TransactionError}; +use header::{Header, BlockNumber}; use state::CleanupMode; use spec::CommonParams; use transaction::UnverifiedTransaction; @@ -167,19 +167,20 @@ impl Engine for Ethash { map!["nonce".to_owned() => format!("0x{}", header.nonce().hex()), "mixHash".to_owned() => format!("0x{}", header.mix_hash().hex())] } - fn schedule(&self, env_info: &EnvInfo) -> Schedule { + fn schedule(&self, block_number: BlockNumber) -> Schedule { trace!(target: "client", "Creating schedule. fCML={}, bGCML={}", self.ethash_params.homestead_transition, self.ethash_params.eip150_transition); - if env_info.number < self.ethash_params.homestead_transition { + if block_number < self.ethash_params.homestead_transition { Schedule::new_frontier() - } else if env_info.number < self.ethash_params.eip150_transition { + } else if block_number < self.ethash_params.eip150_transition { Schedule::new_homestead() } else { Schedule::new_post_eip150( self.ethash_params.max_code_size as usize, - env_info.number >= self.ethash_params.eip160_transition, - env_info.number >= self.ethash_params.eip161abc_transition, - env_info.number >= self.ethash_params.eip161d_transition + block_number >= self.ethash_params.eip160_transition, + block_number >= self.ethash_params.eip161abc_transition, + block_number >= self.ethash_params.eip161d_transition, + block_number >= self.params.eip86_transition ) } } @@ -369,20 +370,13 @@ impl Engine for Ethash { } fn verify_transaction_basic(&self, t: &UnverifiedTransaction, header: &Header) -> result::Result<(), Error> { - if header.number() >= self.ethash_params.homestead_transition { - t.check_low_s()?; - } - - if let Some(n) = t.network_id() { - if header.number() < self.ethash_params.eip155_transition || n != self.params().chain_id { - return Err(TransactionError::InvalidNetworkId.into()) - } - } - if header.number() >= self.ethash_params.min_gas_price_transition && t.gas_price < self.ethash_params.min_gas_price { return Err(TransactionError::InsufficientGasPrice { minimal: self.ethash_params.min_gas_price, got: t.gas_price }.into()); } + let check_low_s = header.number() >= self.ethash_params.homestead_transition; + let network_id = if header.number() >= self.ethash_params.eip155_transition { Some(self.params().chain_id) } else { None }; + t.verify_basic(check_low_s, network_id, false)?; Ok(()) } } @@ -512,7 +506,6 @@ mod tests { use block::*; use tests::helpers::*; use engines::Engine; - use env_info::EnvInfo; use error::{BlockError, Error}; use header::Header; use super::super::{new_morden, new_homestead_test}; @@ -559,28 +552,10 @@ mod tests { #[test] fn can_return_schedule() { let engine = new_morden().engine; - let schedule = engine.schedule(&EnvInfo { - number: 10000000, - author: 0.into(), - timestamp: 0, - difficulty: 0.into(), - last_hashes: Arc::new(vec![]), - gas_used: 0.into(), - gas_limit: 0.into(), - }); - + let schedule = engine.schedule(10000000); assert!(schedule.stack_limit > 0); - let schedule = engine.schedule(&EnvInfo { - number: 100, - author: 0.into(), - timestamp: 0, - difficulty: 0.into(), - last_hashes: Arc::new(vec![]), - gas_used: 0.into(), - gas_limit: 0.into(), - }); - + let schedule = engine.schedule(100); assert!(!schedule.have_delegate_call); } diff --git a/ethcore/src/evm/ext.rs b/ethcore/src/evm/ext.rs index 352ffb7d9b8..e6b6448742c 100644 --- a/ethcore/src/evm/ext.rs +++ b/ethcore/src/evm/ext.rs @@ -41,6 +41,17 @@ pub enum MessageCallResult { Failed } +/// Specifies how an address is calculated for a new contract. +#[derive(Copy, Clone, PartialEq, Eq)] +pub enum CreateContractAddress { + /// Address is calculated from nonce and sender. Pre EIP-86 (Metropolis) + FromSenderAndNonce, + /// Address is calculated from code hash. Default since EIP-86 + FromCodeHash, + /// Address is calculated from code hash and sender. Used by CREATE_P2SH instruction. + FromSenderAndCodeHash, +} + /// Externalities interface for EVMs // TODO: [rob] associated error type instead of `trie::Result`. Not all EVMs are trie powered. pub trait Ext { @@ -68,7 +79,7 @@ pub trait Ext { /// Creates new contract. /// /// Returns gas_left and contract address if contract creation was succesfull. - fn create(&mut self, gas: &U256, value: &U256, code: &[u8]) -> ContractCreateResult; + fn create(&mut self, gas: &U256, value: &U256, code: &[u8], address: CreateContractAddress) -> ContractCreateResult; /// Message call. /// diff --git a/ethcore/src/evm/instructions.rs b/ethcore/src/evm/instructions.rs index d93ddc437b7..41c9e1ea1e3 100644 --- a/ethcore/src/evm/instructions.rs +++ b/ethcore/src/evm/instructions.rs @@ -278,6 +278,7 @@ lazy_static! { arr[RETURN as usize] = InstructionInfo::new("RETURN", 0, 2, 0, true, GasPriceTier::Zero); arr[DELEGATECALL as usize] = InstructionInfo::new("DELEGATECALL", 0, 6, 1, true, GasPriceTier::Special); arr[SUICIDE as usize] = InstructionInfo::new("SUICIDE", 0, 1, 0, true, GasPriceTier::Special); + arr[CREATE_P2SH as usize] = InstructionInfo::new("CREATE_P2SH", 0, 3, 1, true, GasPriceTier::Special); arr }; } @@ -553,6 +554,8 @@ pub const CALLCODE: Instruction = 0xf2; pub const RETURN: Instruction = 0xf3; /// like CALLCODE but keeps caller's value and sender pub const DELEGATECALL: Instruction = 0xf4; +/// create a new account and set creation address to sha3(sender + sha3(init code)) % 2**160 +pub const CREATE_P2SH: Instruction = 0xfb; /// halt execution and register account for later deletion pub const SUICIDE: Instruction = 0xff; diff --git a/ethcore/src/evm/interpreter/gasometer.rs b/ethcore/src/evm/interpreter/gasometer.rs index 9086200fafb..fb0c86d35bb 100644 --- a/ethcore/src/evm/interpreter/gasometer.rs +++ b/ethcore/src/evm/interpreter/gasometer.rs @@ -223,7 +223,7 @@ impl Gasometer { Request::GasMemProvide(gas, mem, Some(requested)) }, - instructions::CREATE => { + instructions::CREATE | instructions::CREATE_P2SH => { let gas = Gas::from(schedule.create_gas); let mem = mem_needed(stack.peek(1), stack.peek(2))?; diff --git a/ethcore/src/evm/interpreter/mod.rs b/ethcore/src/evm/interpreter/mod.rs index 79304793eae..7fbab7ebc6a 100644 --- a/ethcore/src/evm/interpreter/mod.rs +++ b/ethcore/src/evm/interpreter/mod.rs @@ -32,7 +32,7 @@ use std::marker::PhantomData; use action_params::{ActionParams, ActionValue}; use types::executed::CallType; use evm::instructions::{self, Instruction, InstructionInfo}; -use evm::{self, MessageCallResult, ContractCreateResult, GasLeft, CostType}; +use evm::{self, MessageCallResult, ContractCreateResult, GasLeft, CostType, CreateContractAddress}; use bit_set::BitSet; use util::*; @@ -182,7 +182,9 @@ impl Interpreter { fn verify_instruction(&self, ext: &evm::Ext, instruction: Instruction, info: &InstructionInfo, stack: &Stack) -> evm::Result<()> { let schedule = ext.schedule(); - if !schedule.have_delegate_call && instruction == instructions::DELEGATECALL { + if (instruction == instructions::DELEGATECALL && !schedule.have_delegate_call) || + (instruction == instructions::CREATE_P2SH && !schedule.have_create_p2sh) { + return Err(evm::Error::BadInstruction { instruction: instruction }); @@ -266,10 +268,12 @@ impl Interpreter { instructions::JUMPDEST => { // ignore }, - instructions::CREATE => { + instructions::CREATE | instructions::CREATE_P2SH => { let endowment = stack.pop_back(); let init_off = stack.pop_back(); let init_size = stack.pop_back(); + + let address_scheme = if instruction == instructions::CREATE { ext.schedule().create_address } else { CreateContractAddress::FromSenderAndCodeHash }; let create_gas = provided.expect("`provided` comes through Self::exec from `Gasometer::get_gas_cost_mem`; `gas_gas_mem_cost` guarantees `Some` when instruction is `CALL`/`CALLCODE`/`DELEGATECALL`/`CREATE`; this is `CREATE`; qed"); let contract_code = self.mem.read_slice(init_off, init_size); @@ -280,7 +284,7 @@ impl Interpreter { return Ok(InstructionResult::UnusedGas(create_gas)); } - let create_result = ext.create(&create_gas.as_u256(), &endowment, contract_code); + let create_result = ext.create(&create_gas.as_u256(), &endowment, contract_code, address_scheme); return match create_result { ContractCreateResult::Created(address, gas_left) => { stack.push(address_to_u256(address)); diff --git a/ethcore/src/evm/mod.rs b/ethcore/src/evm/mod.rs index cc707d6ef54..7906b81ffff 100644 --- a/ethcore/src/evm/mod.rs +++ b/ethcore/src/evm/mod.rs @@ -32,7 +32,7 @@ mod tests; mod benches; pub use self::evm::{Evm, Error, Finalize, GasLeft, Result, CostType}; -pub use self::ext::{Ext, ContractCreateResult, MessageCallResult}; +pub use self::ext::{Ext, ContractCreateResult, MessageCallResult, CreateContractAddress}; pub use self::factory::{Factory, VMType}; pub use self::schedule::Schedule; pub use types::executed::CallType; diff --git a/ethcore/src/evm/schedule.rs b/ethcore/src/evm/schedule.rs index 70801983dc4..97df4d78473 100644 --- a/ethcore/src/evm/schedule.rs +++ b/ethcore/src/evm/schedule.rs @@ -15,6 +15,7 @@ // along with Parity. If not, see . //! Cost schedule and other parameterisations for the EVM. +use evm::CreateContractAddress; /// Definition of the cost schedule and other parameterisations for the EVM. pub struct Schedule { @@ -22,6 +23,8 @@ pub struct Schedule { pub exceptional_failed_code_deposit: bool, /// Does it have a delegate cal pub have_delegate_call: bool, + /// Does it have a CREATE_P2SH instruction + pub have_create_p2sh: bool, /// VM stack limit pub stack_limit: usize, /// Max number of nested calls/creates @@ -99,6 +102,8 @@ pub struct Schedule { pub no_empty: bool, /// Kill empty accounts if touched. pub kill_empty: bool, + /// Contract address generation scheme + pub create_address: CreateContractAddress, } impl Schedule { @@ -113,10 +118,11 @@ impl Schedule { } /// Schedule for the post-EIP-150-era of the Ethereum main net. - pub fn new_post_eip150(max_code_size: usize, fix_exp: bool, no_empty: bool, kill_empty: bool) -> Schedule { + pub fn new_post_eip150(max_code_size: usize, fix_exp: bool, no_empty: bool, kill_empty: bool, have_create_p2sh: bool) -> Schedule { Schedule { exceptional_failed_code_deposit: true, have_delegate_call: true, + have_create_p2sh: have_create_p2sh, stack_limit: 1024, max_depth: 1024, tier_step_gas: [0, 2, 3, 5, 8, 10, 20, 0], @@ -155,13 +161,20 @@ impl Schedule { sub_gas_cap_divisor: Some(64), no_empty: no_empty, kill_empty: kill_empty, + create_address: if have_create_p2sh { CreateContractAddress::FromCodeHash } else { CreateContractAddress::FromSenderAndNonce }, } } + /// Schedule for the Metropolis of the Ethereum main net. + pub fn new_metropolis() -> Schedule { + Self::new_post_eip150(24576, true, true, true, true) + } + fn new(efcd: bool, hdc: bool, tcg: usize) -> Schedule { Schedule { exceptional_failed_code_deposit: efcd, have_delegate_call: hdc, + have_create_p2sh: false, stack_limit: 1024, max_depth: 1024, tier_step_gas: [0, 2, 3, 5, 8, 10, 20, 0], @@ -200,6 +213,7 @@ impl Schedule { sub_gas_cap_divisor: None, no_empty: false, kill_empty: false, + create_address: CreateContractAddress::FromSenderAndNonce, } } } diff --git a/ethcore/src/evm/tests.rs b/ethcore/src/evm/tests.rs index 3002c170c4f..b5b2341aaeb 100644 --- a/ethcore/src/evm/tests.rs +++ b/ethcore/src/evm/tests.rs @@ -18,7 +18,7 @@ use util::*; use action_params::{ActionParams, ActionValue}; use env_info::EnvInfo; use types::executed::CallType; -use evm::{self, Ext, Schedule, Factory, GasLeft, VMType, ContractCreateResult, MessageCallResult}; +use evm::{self, Ext, Schedule, Factory, GasLeft, VMType, ContractCreateResult, MessageCallResult, CreateContractAddress}; use std::fmt::Debug; pub struct FakeLogEntry { @@ -111,7 +111,7 @@ impl Ext for FakeExt { self.blockhashes.get(number).unwrap_or(&H256::new()).clone() } - fn create(&mut self, gas: &U256, value: &U256, code: &[u8]) -> ContractCreateResult { + fn create(&mut self, gas: &U256, value: &U256, code: &[u8], _address: CreateContractAddress) -> ContractCreateResult { self.calls.insert(FakeCall { call_type: FakeCallType::Create, gas: *gas, diff --git a/ethcore/src/executive.rs b/ethcore/src/executive.rs index 5e0ee5662de..dc9dcec173d 100644 --- a/ethcore/src/executive.rs +++ b/ethcore/src/executive.rs @@ -22,7 +22,7 @@ use engines::Engine; use types::executed::CallType; use env_info::EnvInfo; use error::ExecutionError; -use evm::{self, Ext, Factory, Finalize}; +use evm::{self, Ext, Factory, Finalize, CreateContractAddress}; use externalities::*; use trace::{FlatTrace, Tracer, NoopTracer, ExecutiveTracer, VMTrace, VMTracer, ExecutiveVMTracer, NoopVMTracer}; use transaction::{Action, SignedTransaction}; @@ -34,14 +34,29 @@ pub use types::executed::{Executed, ExecutionResult}; /// Maybe something like here: `https://github.com/ethereum/libethereum/blob/4db169b8504f2b87f7d5a481819cfb959fc65f6c/libethereum/ExtVM.cpp` const STACK_SIZE_PER_DEPTH: usize = 24*1024; -/// Returns new address created from address and given nonce. -pub fn contract_address(address: &Address, nonce: &U256) -> Address { +/// Returns new address created from address, nonce, and code hash +pub fn contract_address(address_scheme: CreateContractAddress, sender: &Address, nonce: &U256, code_hash: &H256) -> Address { use rlp::RlpStream; - let mut stream = RlpStream::new_list(2); - stream.append(address); - stream.append(nonce); - From::from(stream.out().sha3()) + match address_scheme { + CreateContractAddress::FromSenderAndNonce => { + let mut stream = RlpStream::new_list(2); + stream.append(sender); + stream.append(nonce); + From::from(stream.as_raw().sha3()) + }, + CreateContractAddress::FromCodeHash => { + let mut buffer = [0u8; 20 + 32]; + &mut buffer[20..].copy_from_slice(&code_hash[..]); + From::from((&buffer[..]).sha3()) + }, + CreateContractAddress::FromSenderAndCodeHash => { + let mut buffer = [0u8; 20 + 32]; + &mut buffer[..20].copy_from_slice(&sender[..]); + &mut buffer[20..].copy_from_slice(&code_hash[..]); + From::from((&buffer[..]).sha3()) + }, + } } /// Transaction execution options. @@ -125,7 +140,7 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> { let sender = t.sender(); let nonce = self.state.nonce(&sender)?; - let schedule = self.engine.schedule(self.info); + let schedule = self.engine.schedule(self.info.number); let base_gas_required = U256::from(t.gas_required(&schedule)); if t.gas < base_gas_required { @@ -160,17 +175,20 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> { } // NOTE: there can be no invalid transactions from this point. - self.state.inc_nonce(&sender)?; + if !t.is_unsigned() { + self.state.inc_nonce(&sender)?; + } self.state.sub_balance(&sender, &U256::from(gas_cost))?; let mut substate = Substate::new(); let (gas_left, output) = match t.action { Action::Create => { - let new_address = contract_address(&sender, &nonce); + let code_hash = t.data.sha3(); + let new_address = contract_address(schedule.create_address, &sender, &nonce, &code_hash); let params = ActionParams { code_address: new_address.clone(), - code_hash: t.data.sha3(), + code_hash: code_hash, address: new_address, sender: sender.clone(), origin: sender.clone(), @@ -253,7 +271,7 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> { // backup used in case of running out of gas self.state.checkpoint(); - let schedule = self.engine.schedule(self.info); + let schedule = self.engine.schedule(self.info.number); // at first, transfer value to destination if let ActionValue::Transfer(val) = params.value { @@ -365,8 +383,14 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> { params: ActionParams, substate: &mut Substate, tracer: &mut T, - vm_tracer: &mut V + vm_tracer: &mut V, ) -> evm::Result where T: Tracer, V: VMTracer { + + let schedule = self.engine.schedule(self.info.number); + if schedule.create_address != CreateContractAddress::FromSenderAndNonce && self.state.exists(¶ms.address)? { + return Err(evm::Error::OutOfGas); + } + // backup used in case of running out of gas self.state.checkpoint(); @@ -374,7 +398,6 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> { let mut unconfirmed_substate = Substate::new(); // create contract and transfer value to it if necessary - let schedule = self.engine.schedule(self.info); let nonce_offset = if schedule.no_empty {1} else {0}.into(); let prev_bal = self.state.balance(¶ms.address)?; if let ActionValue::Transfer(val) = params.value { @@ -423,7 +446,7 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> { trace: Vec, vm_trace: Option ) -> ExecutionResult { - let schedule = self.engine.schedule(self.info); + let schedule = self.engine.schedule(self.info.number); // refunds from SSTORE nonzero -> zero let sstore_refunds = U256::from(schedule.sstore_refund_gas) * substate.sstore_clears_count; @@ -525,7 +548,7 @@ mod tests { use util::bytes::BytesRef; use action_params::{ActionParams, ActionValue}; use env_info::EnvInfo; - use evm::{Factory, VMType}; + use evm::{Factory, VMType, CreateContractAddress}; use error::ExecutionError; use state::{Substate, CleanupMode}; use tests::helpers::*; @@ -540,14 +563,14 @@ mod tests { fn test_contract_address() { let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); let expected_address = Address::from_str("3f09c73a5ed19289fb9bdc72f1742566df146f56").unwrap(); - assert_eq!(expected_address, contract_address(&address, &U256::from(88))); + assert_eq!(expected_address, contract_address(CreateContractAddress::FromSenderAndNonce, &address, &U256::from(88), &H256::default())); } // TODO: replace params with transactions! evm_test!{test_sender_balance: test_sender_balance_jit, test_sender_balance_int} fn test_sender_balance(factory: Factory) { let sender = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); - let address = contract_address(&sender, &U256::zero()); + let address = contract_address(CreateContractAddress::FromSenderAndNonce, &sender, &U256::zero(), &H256::default()); let mut params = ActionParams::default(); params.address = address.clone(); params.sender = sender.clone(); @@ -602,7 +625,7 @@ mod tests { let code = "7c601080600c6000396000f3006000355415600957005b60203560003555600052601d60036017f0600055".from_hex().unwrap(); let sender = Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap(); - let address = contract_address(&sender, &U256::zero()); + let address = contract_address(CreateContractAddress::FromSenderAndNonce, &sender, &U256::zero(), &H256::default()); // TODO: add tests for 'callcreate' //let next_address = contract_address(&address, &U256::zero()); let mut params = ActionParams::default(); @@ -658,7 +681,7 @@ mod tests { let code = "7c601080600c6000396000f3006000355415600957005b60203560003555600052601d60036017f0600055".from_hex().unwrap(); let sender = Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap(); - let address = contract_address(&sender, &U256::zero()); + let address = contract_address(CreateContractAddress::FromSenderAndNonce, &sender, &U256::zero(), &H256::default()); // TODO: add tests for 'callcreate' //let next_address = contract_address(&address, &U256::zero()); let mut params = ActionParams::default(); @@ -770,7 +793,7 @@ mod tests { let code = "601080600c6000396000f3006000355415600957005b60203560003555".from_hex().unwrap(); let sender = Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap(); - let address = contract_address(&sender, &U256::zero()); + let address = contract_address(CreateContractAddress::FromSenderAndNonce, &sender, &U256::zero(), &H256::default()); // TODO: add tests for 'callcreate' //let next_address = contract_address(&address, &U256::zero()); let mut params = ActionParams::default(); @@ -857,7 +880,7 @@ mod tests { let code = "7c601080600c6000396000f3006000355415600957005b60203560003555600052601d600360e6f0600055".from_hex().unwrap(); let sender = Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap(); - let address = contract_address(&sender, &U256::zero()); + let address = contract_address(CreateContractAddress::FromSenderAndNonce, &sender, &U256::zero(), &H256::default()); // TODO: add tests for 'callcreate' //let next_address = contract_address(&address, &U256::zero()); let mut params = ActionParams::default(); @@ -909,8 +932,8 @@ mod tests { let code = "7c601080600c6000396000f3006000355415600957005b60203560003555600052601d60036017f0".from_hex().unwrap(); let sender = Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap(); - let address = contract_address(&sender, &U256::zero()); - let next_address = contract_address(&address, &U256::zero()); + let address = contract_address(CreateContractAddress::FromSenderAndNonce, &sender, &U256::zero(), &H256::default()); + let next_address = contract_address(CreateContractAddress::FromSenderAndNonce, &address, &U256::zero(), &H256::default()); let mut params = ActionParams::default(); params.address = address.clone(); params.sender = sender.clone(); @@ -1017,7 +1040,7 @@ mod tests { // 55 - sstore let sender = Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap(); let code = "600160005401600055600060006000600060003060e05a03f1600155".from_hex().unwrap(); - let address = contract_address(&sender, &U256::zero()); + let address = contract_address(CreateContractAddress::FromSenderAndNonce, &sender, &U256::zero(), &H256::default()); let mut params = ActionParams::default(); params.address = address.clone(); params.gas = U256::from(100_000); @@ -1052,7 +1075,7 @@ mod tests { nonce: U256::zero() }.sign(keypair.secret(), None); let sender = t.sender(); - let contract = contract_address(&sender, &U256::zero()); + let contract = contract_address(CreateContractAddress::FromSenderAndNonce, &sender, &U256::zero(), &H256::default()); let mut state = get_temp_state(); state.add_balance(&sender, &U256::from(18), CleanupMode::NoEmpty).unwrap(); @@ -1181,7 +1204,7 @@ mod tests { let code = "6064640fffffffff20600055".from_hex().unwrap(); let sender = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); - let address = contract_address(&sender, &U256::zero()); + let address = contract_address(CreateContractAddress::FromSenderAndNonce, &sender, &U256::zero(), &H256::default()); // TODO: add tests for 'callcreate' //let next_address = contract_address(&address, &U256::zero()); let mut params = ActionParams::default(); diff --git a/ethcore/src/externalities.rs b/ethcore/src/externalities.rs index 8591f15fdfd..0b849033e23 100644 --- a/ethcore/src/externalities.rs +++ b/ethcore/src/externalities.rs @@ -21,8 +21,9 @@ use state::{Backend as StateBackend, State, Substate}; use engines::Engine; use env_info::EnvInfo; use executive::*; -use evm::{self, Schedule, Ext, ContractCreateResult, MessageCallResult, Factory}; +use evm::{self, Schedule, Ext, ContractCreateResult, MessageCallResult, Factory, CreateContractAddress}; use types::executed::CallType; +use types::transaction::UNSIGNED_SENDER; use trace::{Tracer, VMTracer}; /// Policy for handling output data on `RETURN` opcode. @@ -97,7 +98,7 @@ impl<'a, T: 'a, V: 'a, B: 'a> Externalities<'a, T, V, B> depth: depth, origin_info: origin_info, substate: substate, - schedule: engine.schedule(env_info), + schedule: engine.schedule(env_info.number), output: output, tracer: tracer, vm_tracer: vm_tracer, @@ -147,10 +148,11 @@ impl<'a, T: 'a, V: 'a, B: 'a> Ext for Externalities<'a, T, V, B> } } - fn create(&mut self, gas: &U256, value: &U256, code: &[u8]) -> ContractCreateResult { + fn create(&mut self, gas: &U256, value: &U256, code: &[u8], address_scheme: CreateContractAddress) -> ContractCreateResult { // create new contract address + let code_hash = code.sha3(); let address = match self.state.nonce(&self.origin_info.address) { - Ok(nonce) => contract_address(&self.origin_info.address, &nonce), + Ok(nonce) => contract_address(address_scheme, &self.origin_info.address, &nonce, &code_hash), Err(e) => { debug!(target: "ext", "Database corruption encountered: {:?}", e); return ContractCreateResult::Failed @@ -167,14 +169,16 @@ impl<'a, T: 'a, V: 'a, B: 'a> Ext for Externalities<'a, T, V, B> gas_price: self.origin_info.gas_price, value: ActionValue::Transfer(*value), code: Some(Arc::new(code.to_vec())), - code_hash: code.sha3(), + code_hash: code_hash, data: None, call_type: CallType::None, }; - if let Err(e) = self.state.inc_nonce(&self.origin_info.address) { - debug!(target: "ext", "Database corruption encountered: {:?}", e); - return ContractCreateResult::Failed + if params.sender != UNSIGNED_SENDER { + if let Err(e) = self.state.inc_nonce(&self.origin_info.address) { + debug!(target: "ext", "Database corruption encountered: {:?}", e); + return ContractCreateResult::Failed + } } let mut ex = Executive::from_parent(self.state, self.env_info, self.engine, self.vm_factory, self.depth); diff --git a/ethcore/src/json_tests/executive.rs b/ethcore/src/json_tests/executive.rs index 9526e5ec222..c34ad69e360 100644 --- a/ethcore/src/json_tests/executive.rs +++ b/ethcore/src/json_tests/executive.rs @@ -21,7 +21,7 @@ use executive::*; use engines::Engine; use env_info::EnvInfo; use evm; -use evm::{Schedule, Ext, Factory, Finalize, VMType, ContractCreateResult, MessageCallResult}; +use evm::{Schedule, Ext, Factory, Finalize, VMType, ContractCreateResult, MessageCallResult, CreateContractAddress}; use externalities::*; use types::executed::CallType; use tests::helpers::*; @@ -56,7 +56,8 @@ struct TestExt<'a, T: 'a, V: 'a, B: 'a> { ext: Externalities<'a, T, V, B>, callcreates: Vec, - contract_address: Address + nonce: U256, + sender: Address, } impl<'a, T: 'a, V: 'a, B: 'a> TestExt<'a, T, V, B> @@ -76,9 +77,10 @@ impl<'a, T: 'a, V: 'a, B: 'a> TestExt<'a, T, V, B> vm_tracer: &'a mut V, ) -> trie::Result { Ok(TestExt { - contract_address: contract_address(&address, &state.nonce(&address)?), + nonce: state.nonce(&address)?, ext: Externalities::new(state, info, engine, vm_factory, depth, origin_info, substate, output, tracer, vm_tracer), - callcreates: vec![] + callcreates: vec![], + sender: address, }) } } @@ -114,14 +116,15 @@ impl<'a, T: 'a, V: 'a, B: 'a> Ext for TestExt<'a, T, V, B> self.ext.blockhash(number) } - fn create(&mut self, gas: &U256, value: &U256, code: &[u8]) -> ContractCreateResult { + fn create(&mut self, gas: &U256, value: &U256, code: &[u8], address: CreateContractAddress) -> ContractCreateResult { self.callcreates.push(CallCreate { data: code.to_vec(), destination: None, gas_limit: *gas, value: *value }); - ContractCreateResult::Created(self.contract_address.clone(), *gas) + let contract_address = contract_address(address, &self.sender, &self.nonce, &code.sha3()); + ContractCreateResult::Created(contract_address, *gas) } fn call(&mut self, diff --git a/ethcore/src/json_tests/state.rs b/ethcore/src/json_tests/state.rs index d0b18d99c25..c15847896c2 100644 --- a/ethcore/src/json_tests/state.rs +++ b/ethcore/src/json_tests/state.rs @@ -21,6 +21,8 @@ use ethereum; use spec::Spec; use ethjson; use ethjson::state::test::ForkSpec; +use types::transaction::SignedTransaction; +use env_info::EnvInfo; lazy_static! { pub static ref FRONTIER: Spec = ethereum::new_frontier_test(); @@ -37,7 +39,7 @@ pub fn json_chain_test(json_data: &[u8]) -> Vec { for (name, test) in tests.into_iter() { { let multitransaction = test.transaction; - let env = test.env.into(); + let env: EnvInfo = test.env.into(); let pre: PodState = test.pre_state.into(); for (spec, states) in test.post_states { @@ -54,12 +56,15 @@ pub fn json_chain_test(json_data: &[u8]) -> Vec { let info = format!(" - {} | {:?} ({}/{}) ...", name, spec, i + 1, total); let post_root: H256 = state.hash.into(); - let transaction = multitransaction.select(&state.indexes).into(); - + let transaction: SignedTransaction = multitransaction.select(&state.indexes).into(); let mut state = get_temp_state(); state.populate_from(pre.clone()); - state.commit().expect(&format!("State test {} failed due to internal error.", name)); - let _res = state.apply(&env, &**engine, &transaction, false); + if transaction.verify_basic(true, None, env.number >= engine.params().eip86_transition).is_ok() { + state.commit().expect(&format!("State test {} failed due to internal error.", name)); + let _res = state.apply(&env, &**engine, &transaction, false); + } else { + let _rest = state.commit(); + } if state.root() != &post_root { println!("{} !!! State mismatch (got: {}, expect: {}", info, state.root(), post_root); flushln!("{} fail", info); @@ -73,7 +78,9 @@ pub fn json_chain_test(json_data: &[u8]) -> Vec { } - println!("!!! {:?} tests from failed.", failed.len()); + if !failed.is_empty() { + println!("!!! {:?} tests failed.", failed.len()); + } failed } diff --git a/ethcore/src/json_tests/transaction.rs b/ethcore/src/json_tests/transaction.rs index f400180ee23..a3c3c889d3a 100644 --- a/ethcore/src/json_tests/transaction.rs +++ b/ethcore/src/json_tests/transaction.rs @@ -18,35 +18,37 @@ use super::test_common::*; use evm; use ethjson; use rlp::UntrustedRlp; -use transaction::{Action, UnverifiedTransaction}; -use ethstore::ethkey::public_to_address; +use transaction::{Action, UnverifiedTransaction, SignedTransaction}; fn do_json_test(json_data: &[u8]) -> Vec { let tests = ethjson::transaction::Test::load(json_data).unwrap(); let mut failed = Vec::new(); - let old_schedule = evm::Schedule::new_frontier(); - let new_schedule = evm::Schedule::new_homestead(); + let frontier_schedule = evm::Schedule::new_frontier(); + let homestead_schedule = evm::Schedule::new_homestead(); + let metropolis_schedule = evm::Schedule::new_metropolis(); for (name, test) in tests.into_iter() { let mut fail_unless = |cond: bool, title: &str| if !cond { failed.push(name.clone()); println!("Transaction failed: {:?}: {:?}", name, title); }; let number: Option = test.block_number.map(Into::into); let schedule = match number { - None => &old_schedule, - Some(x) if x < 1_150_000 => &old_schedule, - Some(_) => &new_schedule + None => &frontier_schedule, + Some(x) if x < 1_150_000 => &frontier_schedule, + Some(x) if x < 3_000_000 => &homestead_schedule, + Some(_) => &metropolis_schedule }; let allow_network_id_of_one = number.map_or(false, |n| n >= 2_675_000); + let allow_unsigned = number.map_or(false, |n| n >= 3_000_000); let rlp: Vec = test.rlp.into(); let res = UntrustedRlp::new(&rlp) .as_val() .map_err(From::from) - .and_then(|t: UnverifiedTransaction| t.validate(schedule, schedule.have_delegate_call, allow_network_id_of_one)); + .and_then(|t: UnverifiedTransaction| t.validate(schedule, schedule.have_delegate_call, allow_network_id_of_one, allow_unsigned)); fail_unless(test.transaction.is_none() == res.is_err(), "Validity different"); if let (Some(tx), Some(sender)) = (test.transaction, test.sender) { let t = res.unwrap(); - fail_unless(public_to_address(&t.recover_public().unwrap()) == sender.into(), "sender mismatch"); + fail_unless(SignedTransaction::new(t.clone()).unwrap().sender() == sender.into(), "sender mismatch"); let is_acceptable_network_id = match t.network_id() { None => true, Some(1) if allow_network_id_of_one => true, @@ -84,3 +86,7 @@ declare_test!{TransactionTests_Homestead_ttTransactionTestEip155VitaliksTests, " declare_test!{TransactionTests_EIP155_ttTransactionTest, "TransactionTests/EIP155/ttTransactionTest"} declare_test!{TransactionTests_EIP155_ttTransactionTestEip155VitaliksTests, "TransactionTests/EIP155/ttTransactionTestEip155VitaliksTests"} declare_test!{TransactionTests_EIP155_ttTransactionTestVRule, "TransactionTests/EIP155/ttTransactionTestVRule"} + +declare_test!{TransactionTests_Metropolis_ttMetropolisTest, "TransactionTests/Metropolis/ttMetropolisTest"} +declare_test!{TransactionTests_Metropolis_ttTransactionTest, "TransactionTests/Metropolis/ttTransactionTest"} +declare_test!{TransactionTests_Metropolis_ttTransactionTestZeroSig, "TransactionTests/Metropolis/ttTransactionTestZeroSig"} diff --git a/ethcore/src/lib.rs b/ethcore/src/lib.rs index 8f9ed2e5fd8..980bdd25cb9 100644 --- a/ethcore/src/lib.rs +++ b/ethcore/src/lib.rs @@ -170,3 +170,4 @@ mod json_tests; pub use types::*; pub use executive::contract_address; +pub use evm::CreateContractAddress; diff --git a/ethcore/src/miner/miner.rs b/ethcore/src/miner/miner.rs index 0347e984f8f..286a89bc17f 100644 --- a/ethcore/src/miner/miner.rs +++ b/ethcore/src/miner/miner.rs @@ -1048,7 +1048,7 @@ impl MinerService for Miner { Action::Call(_) => None, Action::Create => { let sender = tx.sender(); - Some(contract_address(&sender, &tx.nonce)) + Some(contract_address(self.engine.schedule(pending.header().number()).create_address, &sender, &tx.nonce, &tx.data.sha3())) } }, logs: receipt.logs.clone(), @@ -1327,6 +1327,10 @@ mod tests { } fn transaction() -> SignedTransaction { + transaction_with_network_id(2) + } + + fn transaction_with_network_id(id: u64) -> SignedTransaction { let keypair = Random.generate().unwrap(); Transaction { action: Action::Create, @@ -1335,7 +1339,7 @@ mod tests { gas: U256::from(100_000), gas_price: U256::zero(), nonce: U256::zero(), - }.sign(keypair.secret(), None) + }.sign(keypair.secret(), Some(id)) } #[test] @@ -1411,18 +1415,19 @@ mod tests { #[test] fn internal_seals_without_work() { - let miner = Miner::with_spec(&Spec::new_instant()); + let spec = Spec::new_instant(); + let miner = Miner::with_spec(&spec); let client = generate_dummy_client(2); - assert_eq!(miner.import_external_transactions(&*client, vec![transaction().into()]).pop().unwrap().unwrap(), TransactionImportResult::Current); + assert_eq!(miner.import_external_transactions(&*client, vec![transaction_with_network_id(spec.network_id()).into()]).pop().unwrap().unwrap(), TransactionImportResult::Current); miner.update_sealing(&*client); client.flush_queue(); assert!(miner.pending_block().is_none()); assert_eq!(client.chain_info().best_block_number, 3 as BlockNumber); - assert_eq!(miner.import_own_transaction(&*client, PendingTransaction::new(transaction().into(), None)).unwrap(), TransactionImportResult::Current); + assert_eq!(miner.import_own_transaction(&*client, PendingTransaction::new(transaction_with_network_id(spec.network_id()).into(), None)).unwrap(), TransactionImportResult::Current); miner.update_sealing(&*client); client.flush_queue(); diff --git a/ethcore/src/spec/spec.rs b/ethcore/src/spec/spec.rs index 35f1ca983ad..fc322c5c89d 100644 --- a/ethcore/src/spec/spec.rs +++ b/ethcore/src/spec/spec.rs @@ -57,6 +57,8 @@ pub struct CommonParams { pub eip98_transition: BlockNumber, /// Validate block receipts root. pub validate_receipts_transition: u64, + /// Number of first block where EIP-86 (Metropolis) rules begin. + pub eip86_transition: BlockNumber, } impl From for CommonParams { @@ -71,6 +73,7 @@ impl From for CommonParams { fork_block: if let (Some(n), Some(h)) = (p.fork_block, p.fork_hash) { Some((n.into(), h.into())) } else { None }, eip98_transition: p.eip98_transition.map_or(0, Into::into), validate_receipts_transition: p.validate_receipts_transition.map_or(0, Into::into), + eip86_transition: p.eip86_transition.map_or(0, Into::into), } } } @@ -306,6 +309,7 @@ impl Spec { call_type: CallType::None, }; let mut substate = Substate::new(); + state.kill_account(address); { let mut exec = Executive::new(&mut state, &env_info, self.engine.as_ref(), &factories.vm); if let Err(e) = exec.create(params, &mut substate, &mut NoopTracer, &mut NoopVMTracer) { @@ -391,6 +395,7 @@ mod tests { #[test] fn genesis_constructor() { + ::ethcore_logger::init_log(); let spec = Spec::new_test_constructor(); let db = spec.ensure_db_good(get_temp_state_db(), &Default::default()).unwrap(); let state = State::from_existing(db.boxed_clone(), spec.state_root(), spec.engine.account_start_nonce(), Default::default()).unwrap(); diff --git a/ethcore/src/state/mod.rs b/ethcore/src/state/mod.rs index 12d7718cc4b..639fac053db 100644 --- a/ethcore/src/state/mod.rs +++ b/ethcore/src/state/mod.rs @@ -1279,7 +1279,7 @@ mod tests { info.number = 0x789b0; let engine = &*Spec::new_test().engine; - println!("schedule.have_delegate_call: {:?}", engine.schedule(&info).have_delegate_call); + println!("schedule.have_delegate_call: {:?}", engine.schedule(info.number).have_delegate_call); let t = Transaction { nonce: 0.into(), diff --git a/ethcore/src/tests/client.rs b/ethcore/src/tests/client.rs index 6f0d7bc2636..755c9f3ef8c 100644 --- a/ethcore/src/tests/client.rs +++ b/ethcore/src/tests/client.rs @@ -162,7 +162,7 @@ fn returns_logs_with_limit() { to_block: BlockId::Latest, address: None, topics: vec![], - limit: Some(2), + limit: None, }); assert_eq!(logs.len(), 0); } diff --git a/ethcore/src/tests/helpers.rs b/ethcore/src/tests/helpers.rs index 1391c37f4e4..548187e4843 100644 --- a/ethcore/src/tests/helpers.rs +++ b/ethcore/src/tests/helpers.rs @@ -27,7 +27,6 @@ use builtin::Builtin; use state::*; use evm::Schedule; use engines::Engine; -use env_info::EnvInfo; use ethereum; use ethereum::ethash::EthashParams; use miner::Miner; @@ -72,7 +71,7 @@ impl Engine for TestEngine { self.engine.builtins() } - fn schedule(&self, _env_info: &EnvInfo) -> Schedule { + fn schedule(&self, _block_number: u64) -> Schedule { let mut schedule = Schedule::new_frontier(); schedule.max_depth = self.max_depth; schedule @@ -201,7 +200,7 @@ pub fn generate_dummy_client_with_spec_accounts_and_data(get_test_spec: F, ac action: Action::Create, data: vec![], value: U256::zero(), - }.sign(kp.secret(), None), None).unwrap(); + }.sign(kp.secret(), Some(test_spec.network_id())), None).unwrap(); n += 1; } diff --git a/ethcore/src/types/transaction.rs b/ethcore/src/types/transaction.rs index 79e27d97d12..f9e9f2c47fb 100644 --- a/ethcore/src/types/transaction.rs +++ b/ethcore/src/types/transaction.rs @@ -19,13 +19,16 @@ use std::ops::Deref; use rlp::*; use util::sha3::Hashable; -use util::{H256, Address, U256, Bytes, HeapSizeOf}; +use util::{H256, Address, U256, Bytes, HeapSizeOf, Uint}; use ethkey::{Signature, Secret, Public, recover, public_to_address, Error as EthkeyError}; use error::*; use evm::Schedule; use header::BlockNumber; use ethjson; +/// Fake address for unsigned transactions as defined by EIP-86. +pub const UNSIGNED_SENDER: Address = ::util::H160([0xff; 20]); + #[derive(Debug, Clone, PartialEq, Eq)] #[cfg_attr(feature = "ipc", binary)] /// Transaction action type. @@ -110,8 +113,8 @@ impl HeapSizeOf for Transaction { impl From for SignedTransaction { fn from(t: ethjson::state::Transaction) -> Self { let to: Option = t.to.into(); - let secret = Secret::from_slice(&t.secret.0).expect("Valid secret expected."); - Transaction { + let secret = t.secret.map(|s| Secret::from_slice(&s.0).expect("Valid secret expected.")); + let tx = Transaction { nonce: t.nonce.into(), gas_price: t.gas_price.into(), gas: t.gas_limit.into(), @@ -121,7 +124,11 @@ impl From for SignedTransaction { }, value: t.value.into(), data: t.data.into(), - }.sign(&secret, None) + }; + match secret { + Some(s) => tx.sign(&s, None), + None => tx.null_sign(), + } } } @@ -180,8 +187,8 @@ impl Transaction { pub fn invalid_sign(self) -> UnverifiedTransaction { UnverifiedTransaction { unsigned: self, - r: U256::default(), - s: U256::default(), + r: U256::one(), + s: U256::one(), v: 0, hash: 0.into(), }.compute_hash() @@ -192,13 +199,28 @@ impl Transaction { SignedTransaction { transaction: UnverifiedTransaction { unsigned: self, - r: U256::default(), - s: U256::default(), + r: U256::one(), + s: U256::one(), v: 0, hash: 0.into(), }.compute_hash(), sender: from, - public: Public::default(), + public: None, + } + } + + /// Add EIP-86 compatible empty signature. + pub fn null_sign(self) -> SignedTransaction { + SignedTransaction { + transaction: UnverifiedTransaction { + unsigned: self, + r: U256::zero(), + s: U256::zero(), + v: 0, + hash: 0.into(), + }.compute_hash(), + sender: UNSIGNED_SENDER, + public: None, } } @@ -276,6 +298,11 @@ impl UnverifiedTransaction { self } + /// Checks is signature is empty. + pub fn is_unsigned(&self) -> bool { + self.r.is_zero() && self.s.is_zero() + } + /// Append object with a signature into RLP stream fn rlp_append_sealed_transaction(&self, s: &mut RlpStream) { s.begin_list(9); @@ -307,6 +334,7 @@ impl UnverifiedTransaction { /// The network ID, or `None` if this is a global transaction. pub fn network_id(&self) -> Option { match self.v { + v if self.is_unsigned() => Some(v), v if v > 36 => Some((v - 35) / 2), _ => None, } @@ -340,21 +368,33 @@ impl UnverifiedTransaction { // TODO: consider use in block validation. #[cfg(test)] #[cfg(feature = "json-tests")] - pub fn validate(self, schedule: &Schedule, require_low: bool, allow_network_id_of_one: bool) -> Result { - if require_low && !self.signature().is_low_s() { - return Err(EthkeyError::InvalidSignature.into()) + pub fn validate(self, schedule: &Schedule, require_low: bool, allow_network_id_of_one: bool, allow_empty_signature: bool) -> Result { + let chain_id = if allow_network_id_of_one { Some(1) } else { None }; + self.verify_basic(require_low, chain_id, allow_empty_signature)?; + if !allow_empty_signature || !self.is_unsigned() { + self.recover_public()?; } - match self.network_id() { - None => {}, - Some(1) if allow_network_id_of_one => {}, - _ => return Err(TransactionError::InvalidNetworkId.into()), - } - self.recover_public()?; if self.gas < U256::from(self.gas_required(&schedule)) { - Err(TransactionError::InvalidGasLimit(::util::OutOfBounds{min: Some(U256::from(self.gas_required(&schedule))), max: None, found: self.gas}).into()) - } else { - Ok(self) + return Err(TransactionError::InvalidGasLimit(::util::OutOfBounds{min: Some(U256::from(self.gas_required(&schedule))), max: None, found: self.gas}).into()) } + Ok(self) + } + + /// Verify basic signature params. Does not attempt sender recovery. + pub fn verify_basic(&self, check_low_s: bool, chain_id: Option, allow_empty_signature: bool) -> Result<(), Error> { + if check_low_s && !(allow_empty_signature && self.is_unsigned()) { + self.check_low_s()?; + } + // EIP-86: Transactions of this form MUST have gasprice = 0, nonce = 0, value = 0, and do NOT increment the nonce of account 0. + if allow_empty_signature && self.is_unsigned() && !(self.gas_price.is_zero() && self.value.is_zero() && self.nonce.is_zero()) { + return Err(EthkeyError::InvalidSignature.into()) + } + match (self.network_id(), chain_id) { + (None, _) => {}, + (Some(n), Some(m)) if n == m => {}, + _ => return Err(TransactionError::InvalidNetworkId.into()), + }; + Ok(()) } } @@ -363,7 +403,7 @@ impl UnverifiedTransaction { pub struct SignedTransaction { transaction: UnverifiedTransaction, sender: Address, - public: Public, + public: Option, } impl HeapSizeOf for SignedTransaction { @@ -392,13 +432,21 @@ impl From for UnverifiedTransaction { impl SignedTransaction { /// Try to verify transaction and recover sender. pub fn new(transaction: UnverifiedTransaction) -> Result { - let public = transaction.recover_public()?; - let sender = public_to_address(&public); - Ok(SignedTransaction { - transaction: transaction, - sender: sender, - public: public, - }) + if transaction.is_unsigned() { + Ok(SignedTransaction { + transaction: transaction, + sender: UNSIGNED_SENDER, + public: None, + }) + } else { + let public = transaction.recover_public()?; + let sender = public_to_address(&public); + Ok(SignedTransaction { + transaction: transaction, + sender: sender, + public: Some(public), + }) + } } /// Returns transaction sender. @@ -407,9 +455,14 @@ impl SignedTransaction { } /// Returns a public key of the sender. - pub fn public_key(&self) -> Public { + pub fn public_key(&self) -> Option { self.public } + + /// Checks is signature is empty. + pub fn is_unsigned(&self) -> bool { + self.transaction.is_unsigned() + } } /// Signed Transaction that is a part of canon blockchain. @@ -435,6 +488,9 @@ impl LocalizedTransaction { if let Some(sender) = self.cached_sender { return sender; } + if self.is_unsigned() { + return UNSIGNED_SENDER.clone(); + } let sender = public_to_address(&self.recover_public() .expect("LocalizedTransaction is always constructed from transaction from blockchain; Blockchain only stores verified transactions; qed")); self.cached_sender = Some(sender); diff --git a/evmbin/src/ext.rs b/evmbin/src/ext.rs index a293ccc8047..781120c3666 100644 --- a/evmbin/src/ext.rs +++ b/evmbin/src/ext.rs @@ -20,7 +20,7 @@ use std::sync::Arc; use std::collections::HashMap; use util::{U256, H256, Address, Bytes, trie}; use ethcore::client::EnvInfo; -use ethcore::evm::{self, Ext, ContractCreateResult, MessageCallResult, Schedule, CallType}; +use ethcore::evm::{self, Ext, ContractCreateResult, MessageCallResult, Schedule, CallType, CreateContractAddress}; pub struct FakeExt { schedule: Schedule, @@ -31,7 +31,7 @@ pub struct FakeExt { impl Default for FakeExt { fn default() -> Self { FakeExt { - schedule: Schedule::new_post_eip150(usize::max_value(), true, true, true), + schedule: Schedule::new_post_eip150(usize::max_value(), true, true, true, true), store: HashMap::new(), depth: 1, } @@ -68,7 +68,7 @@ impl Ext for FakeExt { unimplemented!(); } - fn create(&mut self, _gas: &U256, _value: &U256, _code: &[u8]) -> ContractCreateResult { + fn create(&mut self, _gas: &U256, _value: &U256, _code: &[u8], _address: CreateContractAddress) -> ContractCreateResult { unimplemented!(); } diff --git a/json/src/spec/params.rs b/json/src/spec/params.rs index aa2c15da4db..31b5cf68af7 100644 --- a/json/src/spec/params.rs +++ b/json/src/spec/params.rs @@ -56,6 +56,9 @@ pub struct Params { /// See `CommonParams` docs. #[serde(rename="validateReceiptsTransition")] pub validate_receipts_transition: Option, + /// See `CommonParams` docs. + #[serde(rename="eip86Transition")] + pub eip86_transition: Option, } #[cfg(test)] diff --git a/json/src/state/test.rs b/json/src/state/test.rs index 5cc2b5f4fd7..ceaccfd1751 100644 --- a/json/src/state/test.rs +++ b/json/src/state/test.rs @@ -75,7 +75,7 @@ pub struct MultiTransaction { pub nonce: Uint, /// Secret key. #[serde(rename="secretKey")] - pub secret: H256, + pub secret: Option, /// To. pub to: MaybeEmpty
, /// Value set. diff --git a/json/src/state/transaction.rs b/json/src/state/transaction.rs index 608be68fe5a..606c40f21f7 100644 --- a/json/src/state/transaction.rs +++ b/json/src/state/transaction.rs @@ -36,7 +36,7 @@ pub struct Transaction { pub nonce: Uint, /// Secret key. #[serde(rename="secretKey")] - pub secret: H256, + pub secret: Option, /// To. pub to: MaybeEmpty
, /// Value. diff --git a/parity/rpc_apis.rs b/parity/rpc_apis.rs index 15fe660b2a8..7643e001033 100644 --- a/parity/rpc_apis.rs +++ b/parity/rpc_apis.rs @@ -395,6 +395,7 @@ impl Dependencies for LightDependencies { false => None, }; handler.extend_with(light::ParityClient::new( + self.client.clone(), Arc::new(dispatcher.clone()), self.secret_store.clone(), self.logger.clone(), diff --git a/rpc/src/v1/impls/eth.rs b/rpc/src/v1/impls/eth.rs index bce460350e3..17835485c04 100644 --- a/rpc/src/v1/impls/eth.rs +++ b/rpc/src/v1/impls/eth.rs @@ -103,6 +103,7 @@ pub struct EthClient where external_miner: Arc, seed_compute: Mutex, options: EthClientOptions, + eip86_transition: u64, } impl EthClient where @@ -131,6 +132,7 @@ impl EthClient where external_miner: em.clone(), seed_compute: Mutex::new(SeedHashCompute::new()), options: options, + eip86_transition: client.eip86_transition(), } } @@ -166,7 +168,7 @@ impl EthClient where seal_fields: view.seal().into_iter().map(Into::into).collect(), uncles: block.uncle_hashes().into_iter().map(Into::into).collect(), transactions: match include_txs { - true => BlockTransactions::Full(block.view().localized_transactions().into_iter().map(Into::into).collect()), + true => BlockTransactions::Full(block.view().localized_transactions().into_iter().map(|t| Transaction::from_localized(t, self.eip86_transition)).collect()), false => BlockTransactions::Hashes(block.transaction_hashes().into_iter().map(Into::into).collect()), }, extra_data: Bytes::new(view.extra_data()), @@ -180,7 +182,7 @@ impl EthClient where fn transaction(&self, id: TransactionId) -> Result, Error> { match take_weak!(self.client).transaction(id) { - Some(t) => Ok(Some(Transaction::from(t))), + Some(t) => Ok(Some(Transaction::from_localized(t, self.eip86_transition))), None => Ok(None), } } @@ -507,7 +509,8 @@ impl Eth for EthClient where let hash: H256 = hash.into(); let miner = take_weak!(self.miner); let client = take_weak!(self.client); - Ok(self.transaction(TransactionId::Hash(hash))?.or_else(|| miner.transaction(client.chain_info().best_block_number, &hash).map(Into::into))) + let block_number = client.chain_info().best_block_number; + Ok(self.transaction(TransactionId::Hash(hash))?.or_else(|| miner.transaction(block_number, &hash).map(|t| Transaction::from_pending(t, block_number, self.eip86_transition)))) } fn transaction_by_block_hash_and_index(&self, hash: RpcH256, index: Index) -> Result, Error> { diff --git a/rpc/src/v1/impls/light/eth.rs b/rpc/src/v1/impls/light/eth.rs index bd55f8c63e0..e45397f7db0 100644 --- a/rpc/src/v1/impls/light/eth.rs +++ b/rpc/src/v1/impls/light/eth.rs @@ -25,7 +25,7 @@ use jsonrpc_core::Error; use jsonrpc_macros::Trailing; use light::cache::Cache as LightDataCache; -use light::client::Client as LightClient; +use light::client::{Client as LightClient, LightChainClient}; use light::{cht, TransactionQueue}; use light::on_demand::{request, OnDemand}; @@ -123,6 +123,7 @@ impl EthClient { fn rich_block(&self, id: BlockId, include_txs: bool) -> BoxFuture, Error> { let (on_demand, sync) = (self.on_demand.clone(), self.sync.clone()); let (client, engine) = (self.client.clone(), self.client.engine().clone()); + let eip86_transition = self.client.eip86_transition(); // helper for filling out a rich block once we've got a block and a score. let fill_rich = move |block: encoded::Block, score: Option| { @@ -149,8 +150,8 @@ impl EthClient { seal_fields: header.seal().into_iter().cloned().map(Into::into).collect(), uncles: block.uncle_hashes().into_iter().map(Into::into).collect(), transactions: match include_txs { - true => BlockTransactions::Full(block.view().localized_transactions().into_iter().map(Into::into).collect()), - false => BlockTransactions::Hashes(block.transaction_hashes().into_iter().map(Into::into).collect()), + true => BlockTransactions::Full(block.view().localized_transactions().into_iter().map(|t| Transaction::from_localized(t, eip86_transition)).collect()), + _ => BlockTransactions::Hashes(block.transaction_hashes().into_iter().map(Into::into).collect()), }, extra_data: Bytes::new(header.extra_data().to_vec()), }, diff --git a/rpc/src/v1/impls/light/parity.rs b/rpc/src/v1/impls/light/parity.rs index 96fdb61dee8..cacf33db5af 100644 --- a/rpc/src/v1/impls/light/parity.rs +++ b/rpc/src/v1/impls/light/parity.rs @@ -28,6 +28,8 @@ use ethstore::random_phrase; use ethsync::LightSyncProvider; use ethcore::account_provider::AccountProvider; +use light::client::LightChainClient; + use jsonrpc_core::Error; use jsonrpc_macros::Trailing; use v1::helpers::{errors, ipfs, SigningQueue, SignerService, NetworkSettings}; @@ -53,11 +55,13 @@ pub struct ParityClient { signer: Option>, dapps_interface: Option, dapps_port: Option, + eip86_transition: u64, } impl ParityClient { /// Creates new `ParityClient`. pub fn new( + client: Arc, light_dispatch: Arc, accounts: Arc, logger: Arc, @@ -74,6 +78,7 @@ impl ParityClient { signer: signer, dapps_interface: dapps_interface, dapps_port: dapps_port, + eip86_transition: client.eip86_transition(), } } @@ -245,7 +250,7 @@ impl Parity for ParityClient { Ok( txq.ready_transactions(chain_info.best_block_number, chain_info.best_block_timestamp) .into_iter() - .map(Into::into) + .map(|tx| Transaction::from_pending(tx, chain_info.best_block_number, self.eip86_transition)) .collect::>() ) } @@ -256,7 +261,7 @@ impl Parity for ParityClient { Ok( txq.future_transactions(chain_info.best_block_number, chain_info.best_block_timestamp) .into_iter() - .map(Into::into) + .map(|tx| Transaction::from_pending(tx, chain_info.best_block_number, self.eip86_transition)) .collect::>() ) } diff --git a/rpc/src/v1/impls/parity.rs b/rpc/src/v1/impls/parity.rs index 78592a91ce5..1b62b7f8197 100644 --- a/rpc/src/v1/impls/parity.rs +++ b/rpc/src/v1/impls/parity.rs @@ -69,6 +69,7 @@ pub struct ParityClient where signer: Option>, dapps_interface: Option, dapps_port: Option, + eip86_transition: u64, } impl ParityClient where @@ -103,6 +104,7 @@ impl ParityClient where signer: signer, dapps_interface: dapps_interface, dapps_port: dapps_port, + eip86_transition: client.eip86_transition(), } } @@ -288,11 +290,13 @@ impl Parity for ParityClient where } fn pending_transactions(&self) -> Result, Error> { - Ok(take_weak!(self.miner).pending_transactions().into_iter().map(Into::into).collect::>()) + let block_number = take_weak!(self.client).chain_info().best_block_number; + Ok(take_weak!(self.miner).pending_transactions().into_iter().map(|t| Transaction::from_pending(t, block_number, self.eip86_transition)).collect::>()) } fn future_transactions(&self) -> Result, Error> { - Ok(take_weak!(self.miner).future_transactions().into_iter().map(Into::into).collect::>()) + let block_number = take_weak!(self.client).chain_info().best_block_number; + Ok(take_weak!(self.miner).future_transactions().into_iter().map(|t| Transaction::from_pending(t, block_number, self.eip86_transition)).collect::>()) } fn pending_transactions_stats(&self) -> Result, Error> { @@ -305,9 +309,10 @@ impl Parity for ParityClient where fn local_transactions(&self) -> Result, Error> { let transactions = take_weak!(self.miner).local_transactions(); + let block_number = take_weak!(self.client).chain_info().best_block_number; Ok(transactions .into_iter() - .map(|(hash, status)| (hash.into(), status.into())) + .map(|(hash, status)| (hash.into(), LocalTransactionStatus::from(status, block_number, self.eip86_transition))) .collect() ) } diff --git a/rpc/src/v1/impls/parity_set.rs b/rpc/src/v1/impls/parity_set.rs index 19483635c58..7bd8bcb9178 100644 --- a/rpc/src/v1/impls/parity_set.rs +++ b/rpc/src/v1/impls/parity_set.rs @@ -39,9 +39,12 @@ pub struct ParitySetClient { updater: Weak, net: Weak, fetch: F, + eip86_transition: u64, } -impl ParitySetClient { +impl ParitySetClient + where C: MiningBlockChainClient + 'static, +{ /// Creates new `ParitySetClient` with given `Fetch`. pub fn new(client: &Arc, miner: &Arc, updater: &Arc, net: &Arc, fetch: F) -> Self { ParitySetClient { @@ -50,6 +53,7 @@ impl ParitySetClient { updater: Arc::downgrade(updater), net: Arc::downgrade(net), fetch: fetch, + eip86_transition: client.eip86_transition(), } } } @@ -175,8 +179,9 @@ impl ParitySet for ParitySetClient where fn remove_transaction(&self, hash: H256) -> Result, Error> { let miner = take_weak!(self.miner); let client = take_weak!(self.client); + let block_number = take_weak!(self.client).chain_info().best_block_number; let hash = hash.into(); - Ok(miner.remove_pending_transaction(&*client, &hash).map(Into::into)) + Ok(miner.remove_pending_transaction(&*client, &hash).map(|t| Transaction::from_pending(t, block_number, self.eip86_transition))) } } diff --git a/rpc/src/v1/tests/mocked/parity_set.rs b/rpc/src/v1/tests/mocked/parity_set.rs index 65d69775a47..4383ee560c4 100644 --- a/rpc/src/v1/tests/mocked/parity_set.rs +++ b/rpc/src/v1/tests/mocked/parity_set.rs @@ -227,7 +227,7 @@ fn rpc_parity_remove_transaction() { let hash = signed.hash(); let request = r#"{"jsonrpc": "2.0", "method": "parity_removeTransaction", "params":[""#.to_owned() + &format!("0x{:?}", hash) + r#""], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":{"blockHash":null,"blockNumber":null,"condition":null,"creates":null,"from":"0x0000000000000000000000000000000000000002","gas":"0x76c0","gasPrice":"0x9184e72a000","hash":"0x0072c69d780cdfbfc02fed5c7d184151f9a166971d045e55e27695aaa5bcb55e","input":"0x","networkId":null,"nonce":"0x1","publicKey":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","r":"0x0","raw":"0xe9018609184e72a0008276c0940000000000000000000000000000000000000005849184e72a80808080","s":"0x0","standardV":"0x4","to":"0x0000000000000000000000000000000000000005","transactionIndex":null,"v":"0x0","value":"0x9184e72a"},"id":1}"#; + let response = r#"{"jsonrpc":"2.0","result":{"blockHash":null,"blockNumber":null,"condition":null,"creates":null,"from":"0x0000000000000000000000000000000000000002","gas":"0x76c0","gasPrice":"0x9184e72a000","hash":"0xa2e0da8a8064e0b9f93e95a53c2db6d01280efb8ac72a708d25487e67dd0f8fc","input":"0x","networkId":null,"nonce":"0x1","publicKey":null,"r":"0x1","raw":"0xe9018609184e72a0008276c0940000000000000000000000000000000000000005849184e72a80800101","s":"0x1","standardV":"0x4","to":"0x0000000000000000000000000000000000000005","transactionIndex":null,"v":"0x0","value":"0x9184e72a"},"id":1}"#; miner.pending_transactions.lock().insert(hash, signed); assert_eq!(io.handle_request_sync(&request), Some(response.to_owned())); diff --git a/rpc/src/v1/tests/mocked/signing.rs b/rpc/src/v1/tests/mocked/signing.rs index 70b16302360..9307d95b697 100644 --- a/rpc/src/v1/tests/mocked/signing.rs +++ b/rpc/src/v1/tests/mocked/signing.rs @@ -304,7 +304,7 @@ fn should_add_sign_transaction_to_the_queue() { r#""input":"0x","# + &format!("\"networkId\":{},", t.network_id().map_or("null".to_owned(), |n| format!("{}", n))) + r#""nonce":"0x1","# + - &format!("\"publicKey\":\"0x{:?}\",", t.public_key()) + + &format!("\"publicKey\":\"0x{:?}\",", t.public_key().unwrap()) + &format!("\"r\":\"0x{}\",", U256::from(signature.r()).to_hex()) + &format!("\"raw\":\"0x{}\",", rlp.to_hex()) + &format!("\"s\":\"0x{}\",", U256::from(signature.s()).to_hex()) + diff --git a/rpc/src/v1/types/transaction.rs b/rpc/src/v1/types/transaction.rs index b40dec3fb0b..e8cd1602f39 100644 --- a/rpc/src/v1/types/transaction.rs +++ b/rpc/src/v1/types/transaction.rs @@ -16,8 +16,9 @@ use serde::{Serialize, Serializer}; use serde::ser::SerializeStruct; +use util::Hashable; use ethcore::miner; -use ethcore::contract_address; +use ethcore::{contract_address, CreateContractAddress}; use ethcore::transaction::{LocalizedTransaction, Action, PendingTransaction, SignedTransaction}; use v1::helpers::errors; use v1::types::{Bytes, H160, H256, U256, H512, TransactionCondition}; @@ -158,9 +159,11 @@ pub struct RichRawTransaction { pub transaction: Transaction } + impl From for RichRawTransaction { fn from(t: SignedTransaction) -> Self { - let tx: Transaction = t.into(); + // TODO: change transition to 0 when EIP-86 is commonly used. + let tx: Transaction = Transaction::from_signed(t, 0, u64::max_value()); RichRawTransaction { raw: tx.raw.clone(), transaction: tx, @@ -168,9 +171,11 @@ impl From for RichRawTransaction { } } -impl From for Transaction { - fn from(mut t: LocalizedTransaction) -> Transaction { +impl Transaction { + /// Convert `LocalizedTransaction` into RPC Transaction. + pub fn from_localized(mut t: LocalizedTransaction, eip86_transition: u64) -> Transaction { let signature = t.signature(); + let scheme = if t.block_number >= eip86_transition { CreateContractAddress::FromCodeHash } else { CreateContractAddress::FromSenderAndNonce }; Transaction { hash: t.hash().into(), nonce: t.nonce.into(), @@ -187,7 +192,7 @@ impl From for Transaction { gas: t.gas.into(), input: Bytes::new(t.data.clone()), creates: match t.action { - Action::Create => Some(contract_address(&t.sender(), &t.nonce).into()), + Action::Create => Some(contract_address(scheme, &t.sender(), &t.nonce, &t.data.sha3()).into()), Action::Call(_) => None, }, raw: ::rlp::encode(&t.signed).to_vec().into(), @@ -200,11 +205,11 @@ impl From for Transaction { condition: None, } } -} -impl From for Transaction { - fn from(t: SignedTransaction) -> Transaction { + /// Convert `SignedTransaction` into RPC Transaction. + pub fn from_signed(t: SignedTransaction, block_number: u64, eip86_transition: u64) -> Transaction { let signature = t.signature(); + let scheme = if block_number >= eip86_transition { CreateContractAddress::FromCodeHash } else { CreateContractAddress::FromSenderAndNonce }; Transaction { hash: t.hash().into(), nonce: t.nonce.into(), @@ -221,11 +226,11 @@ impl From for Transaction { gas: t.gas.into(), input: Bytes::new(t.data.clone()), creates: match t.action { - Action::Create => Some(contract_address(&t.sender(), &t.nonce).into()), + Action::Create => Some(contract_address(scheme, &t.sender(), &t.nonce, &t.data.sha3()).into()), Action::Call(_) => None, }, raw: ::rlp::encode(&t).to_vec().into(), - public_key: Some(t.public_key().into()), + public_key: t.public_key().map(Into::into), network_id: t.network_id(), standard_v: t.standard_v().into(), v: t.original_v().into(), @@ -234,28 +239,28 @@ impl From for Transaction { condition: None, } } -} -impl From for Transaction { - fn from(t: PendingTransaction) -> Transaction { - let mut r = Transaction::from(t.transaction); + /// Convert `PendingTransaction` into RPC Transaction. + pub fn from_pending(t: PendingTransaction, block_number: u64, eip86_transition: u64) -> Transaction { + let mut r = Transaction::from_signed(t.transaction, block_number, eip86_transition); r.condition = t.condition.map(|b| b.into()); r } } -impl From for LocalTransactionStatus { - fn from(s: miner::LocalTransactionStatus) -> Self { +impl LocalTransactionStatus { + /// Convert `LocalTransactionStatus` into RPC `LocalTransactionStatus`. + pub fn from(s: miner::LocalTransactionStatus, block_number: u64, eip86_transition: u64) -> Self { use ethcore::miner::LocalTransactionStatus::*; match s { Pending => LocalTransactionStatus::Pending, Future => LocalTransactionStatus::Future, - Mined(tx) => LocalTransactionStatus::Mined(tx.into()), - Dropped(tx) => LocalTransactionStatus::Dropped(tx.into()), - Rejected(tx, err) => LocalTransactionStatus::Rejected(tx.into(), errors::transaction_message(err)), - Replaced(tx, gas_price, hash) => LocalTransactionStatus::Replaced(tx.into(), gas_price.into(), hash.into()), - Invalid(tx) => LocalTransactionStatus::Invalid(tx.into()), - Canceled(tx) => LocalTransactionStatus::Canceled(tx.into()), + Mined(tx) => LocalTransactionStatus::Mined(Transaction::from_signed(tx, block_number, eip86_transition)), + Dropped(tx) => LocalTransactionStatus::Dropped(Transaction::from_signed(tx, block_number, eip86_transition)), + Rejected(tx, err) => LocalTransactionStatus::Rejected(Transaction::from_signed(tx, block_number, eip86_transition), errors::transaction_message(err)), + Replaced(tx, gas_price, hash) => LocalTransactionStatus::Replaced(Transaction::from_signed(tx, block_number, eip86_transition), gas_price.into(), hash.into()), + Invalid(tx) => LocalTransactionStatus::Invalid(Transaction::from_signed(tx, block_number, eip86_transition)), + Canceled(tx) => LocalTransactionStatus::Canceled(Transaction::from_pending(tx, block_number, eip86_transition)), } } } diff --git a/sync/src/tests/consensus.rs b/sync/src/tests/consensus.rs index 00a7452c499..6b91b11c6f0 100644 --- a/sync/src/tests/consensus.rs +++ b/sync/src/tests/consensus.rs @@ -41,7 +41,7 @@ impl IoHandler for TestIoHandler { } } -fn new_tx(secret: &Secret, nonce: U256) -> PendingTransaction { +fn new_tx(secret: &Secret, nonce: U256, network_id: u64) -> PendingTransaction { let signed = Transaction { nonce: nonce.into(), gas_price: 0.into(), @@ -49,7 +49,7 @@ fn new_tx(secret: &Secret, nonce: U256) -> PendingTransaction { action: Action::Call(Address::default()), value: 0.into(), data: Vec::new(), - }.sign(secret, None); + }.sign(secret, Some(network_id)); PendingTransaction::new(signed, None) } @@ -61,6 +61,7 @@ fn authority_round() { ap.insert_account(s0.secret().clone(), "").unwrap(); ap.insert_account(s1.secret().clone(), "").unwrap(); + let network_id = Spec::new_test_round().network_id(); let mut net = TestNet::with_spec_and_accounts(2, SyncConfig::default(), Spec::new_test_round, Some(ap)); let io_handler0: Arc> = Arc::new(TestIoHandler { client: net.peer(0).chain.clone() }); let io_handler1: Arc> = Arc::new(TestIoHandler { client: net.peer(1).chain.clone() }); @@ -74,15 +75,15 @@ fn authority_round() { // exchange statuses net.sync(); // Trigger block proposal - net.peer(0).chain.miner().import_own_transaction(&*net.peer(0).chain, new_tx(s0.secret(), 0.into())).unwrap(); - net.peer(1).chain.miner().import_own_transaction(&*net.peer(1).chain, new_tx(s1.secret(), 0.into())).unwrap(); + net.peer(0).chain.miner().import_own_transaction(&*net.peer(0).chain, new_tx(s0.secret(), 0.into(), network_id)).unwrap(); + net.peer(1).chain.miner().import_own_transaction(&*net.peer(1).chain, new_tx(s1.secret(), 0.into(), network_id)).unwrap(); // Sync a block net.sync(); assert_eq!(net.peer(0).chain.chain_info().best_block_number, 1); assert_eq!(net.peer(1).chain.chain_info().best_block_number, 1); - net.peer(0).chain.miner().import_own_transaction(&*net.peer(0).chain, new_tx(s0.secret(), 1.into())).unwrap(); - net.peer(1).chain.miner().import_own_transaction(&*net.peer(1).chain, new_tx(s1.secret(), 1.into())).unwrap(); + net.peer(0).chain.miner().import_own_transaction(&*net.peer(0).chain, new_tx(s0.secret(), 1.into(), network_id)).unwrap(); + net.peer(1).chain.miner().import_own_transaction(&*net.peer(1).chain, new_tx(s1.secret(), 1.into(), network_id)).unwrap(); // Move to next proposer step. net.peer(0).chain.engine().step(); net.peer(1).chain.engine().step(); @@ -91,8 +92,8 @@ fn authority_round() { assert_eq!(net.peer(1).chain.chain_info().best_block_number, 2); // Fork the network with equal height. - net.peer(0).chain.miner().import_own_transaction(&*net.peer(0).chain, new_tx(s0.secret(), 2.into())).unwrap(); - net.peer(1).chain.miner().import_own_transaction(&*net.peer(1).chain, new_tx(s1.secret(), 2.into())).unwrap(); + net.peer(0).chain.miner().import_own_transaction(&*net.peer(0).chain, new_tx(s0.secret(), 2.into(), network_id)).unwrap(); + net.peer(1).chain.miner().import_own_transaction(&*net.peer(1).chain, new_tx(s1.secret(), 2.into(), network_id)).unwrap(); // Let both nodes build one block. net.peer(0).chain.engine().step(); let early_hash = net.peer(0).chain.chain_info().best_block_hash; @@ -114,8 +115,8 @@ fn authority_round() { assert_eq!(ci1.best_block_hash, early_hash); // Selfish miner - net.peer(0).chain.miner().import_own_transaction(&*net.peer(0).chain, new_tx(s0.secret(), 3.into())).unwrap(); - net.peer(1).chain.miner().import_own_transaction(&*net.peer(1).chain, new_tx(s1.secret(), 3.into())).unwrap(); + net.peer(0).chain.miner().import_own_transaction(&*net.peer(0).chain, new_tx(s0.secret(), 3.into(), network_id)).unwrap(); + net.peer(1).chain.miner().import_own_transaction(&*net.peer(1).chain, new_tx(s1.secret(), 3.into(), network_id)).unwrap(); // Node 0 is an earlier primary. net.peer(0).chain.engine().step(); assert_eq!(net.peer(0).chain.chain_info().best_block_number, 4); @@ -126,7 +127,7 @@ fn authority_round() { // Node 1 makes 2 blocks, but is a later primary on the first one. net.peer(1).chain.engine().step(); net.peer(1).chain.engine().step(); - net.peer(1).chain.miner().import_own_transaction(&*net.peer(1).chain, new_tx(s1.secret(), 4.into())).unwrap(); + net.peer(1).chain.miner().import_own_transaction(&*net.peer(1).chain, new_tx(s1.secret(), 4.into(), network_id)).unwrap(); net.peer(1).chain.engine().step(); net.peer(1).chain.engine().step(); assert_eq!(net.peer(1).chain.chain_info().best_block_number, 5); @@ -147,6 +148,7 @@ fn tendermint() { ap.insert_account(s0.secret().clone(), "").unwrap(); ap.insert_account(s1.secret().clone(), "").unwrap(); + let network_id = Spec::new_test_tendermint().network_id(); let mut net = TestNet::with_spec_and_accounts(2, SyncConfig::default(), Spec::new_test_tendermint, Some(ap)); let io_handler0: Arc> = Arc::new(TestIoHandler { client: net.peer(0).chain.clone() }); let io_handler1: Arc> = Arc::new(TestIoHandler { client: net.peer(1).chain.clone() }); @@ -162,7 +164,7 @@ fn tendermint() { // Exhange statuses net.sync(); // Propose - net.peer(0).chain.miner().import_own_transaction(&*net.peer(0).chain, new_tx(s0.secret(), 0.into())).unwrap(); + net.peer(0).chain.miner().import_own_transaction(&*net.peer(0).chain, new_tx(s0.secret(), 0.into(), network_id)).unwrap(); net.sync(); // Propose timeout, synchronous for now net.peer(0).chain.engine().step(); @@ -173,7 +175,7 @@ fn tendermint() { assert_eq!(net.peer(0).chain.chain_info().best_block_number, 1); assert_eq!(net.peer(1).chain.chain_info().best_block_number, 1); - net.peer(1).chain.miner().import_own_transaction(&*net.peer(1).chain, new_tx(s1.secret(), 0.into())).unwrap(); + net.peer(1).chain.miner().import_own_transaction(&*net.peer(1).chain, new_tx(s1.secret(), 0.into(), network_id)).unwrap(); // Commit timeout net.peer(0).chain.engine().step(); net.peer(1).chain.engine().step(); @@ -187,8 +189,8 @@ fn tendermint() { assert_eq!(net.peer(0).chain.chain_info().best_block_number, 2); assert_eq!(net.peer(1).chain.chain_info().best_block_number, 2); - net.peer(0).chain.miner().import_own_transaction(&*net.peer(0).chain, new_tx(s0.secret(), 1.into())).unwrap(); - net.peer(1).chain.miner().import_own_transaction(&*net.peer(1).chain, new_tx(s1.secret(), 1.into())).unwrap(); + net.peer(0).chain.miner().import_own_transaction(&*net.peer(0).chain, new_tx(s0.secret(), 1.into(), network_id)).unwrap(); + net.peer(1).chain.miner().import_own_transaction(&*net.peer(1).chain, new_tx(s1.secret(), 1.into(), network_id)).unwrap(); // Peers get disconnected. // Commit net.peer(0).chain.engine().step(); @@ -196,8 +198,8 @@ fn tendermint() { // Propose net.peer(0).chain.engine().step(); net.peer(1).chain.engine().step(); - net.peer(0).chain.miner().import_own_transaction(&*net.peer(0).chain, new_tx(s0.secret(), 2.into())).unwrap(); - net.peer(1).chain.miner().import_own_transaction(&*net.peer(1).chain, new_tx(s1.secret(), 2.into())).unwrap(); + net.peer(0).chain.miner().import_own_transaction(&*net.peer(0).chain, new_tx(s0.secret(), 2.into(), network_id)).unwrap(); + net.peer(1).chain.miner().import_own_transaction(&*net.peer(1).chain, new_tx(s1.secret(), 2.into(), network_id)).unwrap(); // Send different prevotes net.sync(); // Prevote timeout From c2ab130ceb9c361c3d09f5c4a7efa30e2d438717 Mon Sep 17 00:00:00 2001 From: arkpar Date: Wed, 19 Apr 2017 12:55:57 +0200 Subject: [PATCH 2/2] Disable EIP-86 auto activation for now --- ethcore/src/spec/spec.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ethcore/src/spec/spec.rs b/ethcore/src/spec/spec.rs index fc322c5c89d..c258d89cb64 100644 --- a/ethcore/src/spec/spec.rs +++ b/ethcore/src/spec/spec.rs @@ -73,7 +73,7 @@ impl From for CommonParams { fork_block: if let (Some(n), Some(h)) = (p.fork_block, p.fork_hash) { Some((n.into(), h.into())) } else { None }, eip98_transition: p.eip98_transition.map_or(0, Into::into), validate_receipts_transition: p.validate_receipts_transition.map_or(0, Into::into), - eip86_transition: p.eip86_transition.map_or(0, Into::into), + eip86_transition: p.eip86_transition.map_or(BlockNumber::max_value(), Into::into), } } }