* EIP-86

* Disable EIP-86 auto activation for now
This commit is contained in:
Arkadiy Paronyan 2017-04-19 14:30:00 +02:00 committed by GitHub
parent 0180b21dd1
commit b50fb71dd1
59 changed files with 433 additions and 289 deletions

View File

@ -106,6 +106,9 @@ pub trait LightChainClient: Send + Sync {
/// Get the `i`th CHT root.
fn cht_root(&self, i: usize) -> Option<H256>;
/// 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<H256> {
Client::cht_root(self, i)
}
fn eip86_transition(&self) -> u64 {
self.engine().params().eip86_transition
}
}

View File

@ -30,7 +30,8 @@
"chainID": "0x3d",
"forkBlock": "0x1d4c00",
"forkCanonHash": "0x94365e3a8c0b35089c1d1195081fe7489b528a84b22199c916180db8b28ade7f",
"eip98Transition": "0x7fffffffffffff"
"eip98Transition": "0x7fffffffffffff",
"eip86Transition": "0x7fffffffffffff"
},
"genesis": {
"seal": {

View File

@ -24,7 +24,8 @@
"maximumExtraDataSize": "0x20",
"minGasLimit": "0x1388",
"networkID" : "0x1",
"eip98Transition": "0x7fffffffffffffff"
"eip98Transition": "0x7fffffffffffffff",
"eip86Transition": "0x7fffffffffffffff"
},
"genesis": {
"seal": {

View File

@ -24,7 +24,8 @@
"maximumExtraDataSize": "0x20",
"minGasLimit": "0x1388",
"networkID" : "0x1",
"eip98Transition": "0x7fffffffffffffff"
"eip98Transition": "0x7fffffffffffffff",
"eip86Transition": "0x7fffffffffffffff"
},
"genesis": {
"seal": {

View File

@ -30,7 +30,8 @@
"networkID": "0x1",
"chainID": "0x2",
"subprotocolName": "exp",
"eip98Transition": "0x7fffffffffffff"
"eip98Transition": "0x7fffffffffffff",
"eip86Transition": "0x7fffffffffffff"
},
"genesis": {
"seal": {

View File

@ -147,7 +147,8 @@
"networkID" : "0x1",
"forkBlock": "0x1d4c00",
"forkCanonHash": "0x4985f5ca3d2afbec36529aa96f74de3cc10a2a4a6c44f2157a57d2c6059a11bb",
"eip98Transition": "0x7fffffffffffff"
"eip98Transition": "0x7fffffffffffff",
"eip86Transition": "0x7fffffffffffff"
},
"genesis": {
"seal": {

View File

@ -143,7 +143,8 @@
"maximumExtraDataSize": "0x20",
"minGasLimit": "0x1388",
"networkID" : "0x1",
"eip98Transition": "0x7fffffffffffff"
"eip98Transition": "0x7fffffffffffff",
"eip86Transition": "0x7fffffffffffff"
},
"genesis": {
"seal": {

View File

@ -23,7 +23,8 @@
"maximumExtraDataSize": "0x20",
"minGasLimit": "0x1388",
"networkID" : "0x1",
"eip98Transition": "0x7fffffffffffff"
"eip98Transition": "0x7fffffffffffff",
"eip86Transition": "0x7fffffffffffff"
},
"genesis": {
"seal": {

View File

@ -23,7 +23,8 @@
"maximumExtraDataSize": "0x20",
"minGasLimit": "0x1388",
"networkID" : "0x1",
"eip98Transition": "0x7fffffffffffff"
"eip98Transition": "0x7fffffffffffff",
"eip86Transition": "0x7fffffffffffff"
},
"genesis": {
"seal": {

View File

@ -30,7 +30,8 @@
"chainID": "0x3e",
"forkBlock": "0x1b34d8",
"forkCanonHash": "0xf376243aeff1f256d970714c3de9fd78fa4e63cf63e32a51fe1169e375d98145",
"eip98Transition": "0x7fffffffffffff"
"eip98Transition": "0x7fffffffffffff",
"eip86Transition": "0x7fffffffffffff"
},
"genesis": {
"seal": {

View File

@ -23,7 +23,8 @@
"maximumExtraDataSize": "0x0400",
"minGasLimit": "125000",
"networkID" : "0x0",
"eip98Transition": "0x7fffffffffffff"
"eip98Transition": "0x7fffffffffffff",
"eip86Transition": "0x7fffffffffffff"
},
"genesis": {
"seal": {

View File

@ -27,7 +27,8 @@
"networkID" : "0x3",
"forkBlock": 641350,
"forkCanonHash": "0x8033403e9fe5811a7b6d6b469905915de1c59207ce2172cbcf5d6ff14fa6a2eb",
"eip98Transition": "0x7fffffffffffff"
"eip98Transition": "0x7fffffffffffff",
"eip86Transition": "0x7fffffffffffff"
},
"genesis": {
"seal": {

@ -1 +1 @@
Subproject commit d520593078fa0849dcd1f907e44ed0a616892e33
Subproject commit ef191fdc61cf76cdb9cdc147465fb447304b0ed2

View File

@ -139,11 +139,12 @@
}
},
"params": {
"eip98Transition": "0x7fffffffffffffff",
"accountStartNonce": "0x00",
"maximumExtraDataSize": "0x20",
"minGasLimit": "0x1388",
"networkID" : "0x1"
"networkID" : "0x1",
"eip98Transition": "0x7fffffffffffff",
"eip86Transition": "0x7fffffffffffff"
},
"genesis": {
"seal": {

View File

@ -7,7 +7,8 @@
"accountStartNonce": "0x0",
"maximumExtraDataSize": "0x20",
"minGasLimit": "0x1388",
"networkID" : "0x2"
"networkID" : "0x2",
"eip86Transition": "0x7fffffffffffff"
},
"genesis": {
"seal": {

View File

@ -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<Receipt>) -> LocalizedReceipt {
fn transaction_receipt(schedule: &Schedule, mut tx: LocalizedTransaction, mut receipts: Vec<Receipt>) -> 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<Receipt>)
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 {

View File

@ -353,7 +353,7 @@ pub fn get_temp_state_db() -> GuardedTempResult<StateDB> {
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<Address> { None }
fn registry_address(&self, _name: String) -> Option<Address> { None }
fn eip86_transition(&self) -> u64 { u64::max_value() }
}
impl ProvingBlockChainClient for TestBlockChainClient {

View File

@ -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<Address>;
/// Get the EIP-86 transition block number.
fn eip86_transition(&self) -> u64;
}
impl IpcConfig for BlockChainClient { }

View File

@ -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);
}

View File

@ -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<String, String> { 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);
}

View File

@ -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<bool> { Some(true) }

View File

@ -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(())
}

View File

@ -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()
}
}

View File

@ -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);
}

View File

@ -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.

View File

@ -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);
}

View File

@ -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.
///

View File

@ -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;

View File

@ -223,7 +223,7 @@ impl<Gas: CostType> Gasometer<Gas> {
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))?;

View File

@ -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<Cost: CostType> Interpreter<Cost> {
fn verify_instruction(&self, ext: &evm::Ext, instruction: Instruction, info: &InstructionInfo, stack: &Stack<U256>) -> 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<Cost: CostType> Interpreter<Cost> {
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<Cost: CostType> Interpreter<Cost> {
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));

View File

@ -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;

View File

@ -15,6 +15,7 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
//! 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,
}
}
}

View File

@ -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,

View File

@ -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<U256> where T: Tracer, V: VMTracer {
let schedule = self.engine.schedule(self.info.number);
if schedule.create_address != CreateContractAddress::FromSenderAndNonce && self.state.exists(&params.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(&params.address)?;
if let ActionValue::Transfer(val) = params.value {
@ -423,7 +446,7 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> {
trace: Vec<FlatTrace>,
vm_trace: Option<VMTrace>
) -> 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();

View File

@ -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);

View File

@ -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<CallCreate>,
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<Self> {
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,

View File

@ -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<String> {
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<String> {
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<String> {
}
println!("!!! {:?} tests from failed.", failed.len());
if !failed.is_empty() {
println!("!!! {:?} tests failed.", failed.len());
}
failed
}

View File

@ -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<String> {
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<u64> = 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<u8> = 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"}

View File

@ -170,3 +170,4 @@ mod json_tests;
pub use types::*;
pub use executive::contract_address;
pub use evm::CreateContractAddress;

View File

@ -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();

View File

@ -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<ethjson::spec::Params> for CommonParams {
@ -71,6 +73,7 @@ impl From<ethjson::spec::Params> 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(BlockNumber::max_value(), 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();

View File

@ -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(),

View File

@ -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);
}

View File

@ -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<F>(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;
}

View File

@ -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<ethjson::state::Transaction> for SignedTransaction {
fn from(t: ethjson::state::Transaction) -> Self {
let to: Option<ethjson::hash::Address> = 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<ethjson::state::Transaction> 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<u64> {
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<UnverifiedTransaction, Error> {
if require_low && !self.signature().is_low_s() {
pub fn validate(self, schedule: &Schedule, require_low: bool, allow_network_id_of_one: bool, allow_empty_signature: bool) -> Result<UnverifiedTransaction, Error> {
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()?;
}
if self.gas < U256::from(self.gas_required(&schedule)) {
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<u64>, 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() {
None => {},
Some(1) if allow_network_id_of_one => {},
match (self.network_id(), chain_id) {
(None, _) => {},
(Some(n), Some(m)) if n == m => {},
_ => 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)
}
};
Ok(())
}
}
@ -363,7 +403,7 @@ impl UnverifiedTransaction {
pub struct SignedTransaction {
transaction: UnverifiedTransaction,
sender: Address,
public: Public,
public: Option<Public>,
}
impl HeapSizeOf for SignedTransaction {
@ -392,13 +432,21 @@ impl From<SignedTransaction> for UnverifiedTransaction {
impl SignedTransaction {
/// Try to verify transaction and recover sender.
pub fn new(transaction: UnverifiedTransaction) -> Result<Self, Error> {
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<Public> {
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);

View File

@ -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!();
}

View File

@ -56,6 +56,9 @@ pub struct Params {
/// See `CommonParams` docs.
#[serde(rename="validateReceiptsTransition")]
pub validate_receipts_transition: Option<Uint>,
/// See `CommonParams` docs.
#[serde(rename="eip86Transition")]
pub eip86_transition: Option<Uint>,
}
#[cfg(test)]

View File

@ -75,7 +75,7 @@ pub struct MultiTransaction {
pub nonce: Uint,
/// Secret key.
#[serde(rename="secretKey")]
pub secret: H256,
pub secret: Option<H256>,
/// To.
pub to: MaybeEmpty<Address>,
/// Value set.

View File

@ -36,7 +36,7 @@ pub struct Transaction {
pub nonce: Uint,
/// Secret key.
#[serde(rename="secretKey")]
pub secret: H256,
pub secret: Option<H256>,
/// To.
pub to: MaybeEmpty<Address>,
/// Value.

View File

@ -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(),

View File

@ -103,6 +103,7 @@ pub struct EthClient<C, SN: ?Sized, S: ?Sized, M, EM> where
external_miner: Arc<EM>,
seed_compute: Mutex<SeedHashCompute>,
options: EthClientOptions,
eip86_transition: u64,
}
impl<C, SN: ?Sized, S: ?Sized, M, EM> EthClient<C, SN, S, M, EM> where
@ -131,6 +132,7 @@ impl<C, SN: ?Sized, S: ?Sized, M, EM> EthClient<C, SN, S, M, EM> where
external_miner: em.clone(),
seed_compute: Mutex::new(SeedHashCompute::new()),
options: options,
eip86_transition: client.eip86_transition(),
}
}
@ -166,7 +168,7 @@ impl<C, SN: ?Sized, S: ?Sized, M, EM> EthClient<C, SN, S, M, EM> 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<C, SN: ?Sized, S: ?Sized, M, EM> EthClient<C, SN, S, M, EM> where
fn transaction(&self, id: TransactionId) -> Result<Option<Transaction>, 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<C, SN: ?Sized, S: ?Sized, M, EM> Eth for EthClient<C, SN, S, M, EM> 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<Option<Transaction>, Error> {

View File

@ -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<Option<RichBlock>, 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<U256>| {
@ -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()),
},

View File

@ -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<Arc<SignerService>>,
dapps_interface: Option<String>,
dapps_port: Option<u16>,
eip86_transition: u64,
}
impl ParityClient {
/// Creates new `ParityClient`.
pub fn new(
client: Arc<LightChainClient>,
light_dispatch: Arc<LightDispatcher>,
accounts: Arc<AccountProvider>,
logger: Arc<RotatingLogger>,
@ -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::<Vec<_>>()
)
}
@ -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::<Vec<_>>()
)
}

View File

@ -69,6 +69,7 @@ pub struct ParityClient<C, M, S: ?Sized, U> where
signer: Option<Arc<SignerService>>,
dapps_interface: Option<String>,
dapps_port: Option<u16>,
eip86_transition: u64,
}
impl<C, M, S: ?Sized, U> ParityClient<C, M, S, U> where
@ -103,6 +104,7 @@ impl<C, M, S: ?Sized, U> ParityClient<C, M, S, U> where
signer: signer,
dapps_interface: dapps_interface,
dapps_port: dapps_port,
eip86_transition: client.eip86_transition(),
}
}
@ -288,11 +290,13 @@ impl<C, M, S: ?Sized, U> Parity for ParityClient<C, M, S, U> where
}
fn pending_transactions(&self) -> Result<Vec<Transaction>, Error> {
Ok(take_weak!(self.miner).pending_transactions().into_iter().map(Into::into).collect::<Vec<_>>())
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::<Vec<_>>())
}
fn future_transactions(&self) -> Result<Vec<Transaction>, Error> {
Ok(take_weak!(self.miner).future_transactions().into_iter().map(Into::into).collect::<Vec<_>>())
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::<Vec<_>>())
}
fn pending_transactions_stats(&self) -> Result<BTreeMap<H256, TransactionStats>, Error> {
@ -305,9 +309,10 @@ impl<C, M, S: ?Sized, U> Parity for ParityClient<C, M, S, U> where
fn local_transactions(&self) -> Result<BTreeMap<H256, LocalTransactionStatus>, 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()
)
}

View File

@ -39,9 +39,12 @@ pub struct ParitySetClient<C, M, U, F = fetch::Client> {
updater: Weak<U>,
net: Weak<ManageNetwork>,
fetch: F,
eip86_transition: u64,
}
impl<C, M, U, F> ParitySetClient<C, M, U, F> {
impl<C, M, U, F> ParitySetClient<C, M, U, F>
where C: MiningBlockChainClient + 'static,
{
/// Creates new `ParitySetClient` with given `Fetch`.
pub fn new(client: &Arc<C>, miner: &Arc<M>, updater: &Arc<U>, net: &Arc<ManageNetwork>, fetch: F) -> Self {
ParitySetClient {
@ -50,6 +53,7 @@ impl<C, M, U, F> ParitySetClient<C, M, U, F> {
updater: Arc::downgrade(updater),
net: Arc::downgrade(net),
fetch: fetch,
eip86_transition: client.eip86_transition(),
}
}
}
@ -175,8 +179,9 @@ impl<C, M, U, F> ParitySet for ParitySetClient<C, M, U, F> where
fn remove_transaction(&self, hash: H256) -> Result<Option<Transaction>, 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)))
}
}

View File

@ -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()));

View File

@ -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()) +

View File

@ -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<SignedTransaction> 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<SignedTransaction> for RichRawTransaction {
}
}
impl From<LocalizedTransaction> 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<LocalizedTransaction> 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<LocalizedTransaction> for Transaction {
condition: None,
}
}
}
impl From<SignedTransaction> 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<SignedTransaction> 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<SignedTransaction> for Transaction {
condition: None,
}
}
}
impl From<PendingTransaction> 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<miner::LocalTransactionStatus> 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)),
}
}
}

View File

@ -41,7 +41,7 @@ impl IoHandler<ClientIoMessage> 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<IoHandler<ClientIoMessage>> = Arc::new(TestIoHandler { client: net.peer(0).chain.clone() });
let io_handler1: Arc<IoHandler<ClientIoMessage>> = 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<IoHandler<ClientIoMessage>> = Arc::new(TestIoHandler { client: net.peer(0).chain.clone() });
let io_handler1: Arc<IoHandler<ClientIoMessage>> = 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