parent
0180b21dd1
commit
b50fb71dd1
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -30,7 +30,8 @@
|
||||
"chainID": "0x3d",
|
||||
"forkBlock": "0x1d4c00",
|
||||
"forkCanonHash": "0x94365e3a8c0b35089c1d1195081fe7489b528a84b22199c916180db8b28ade7f",
|
||||
"eip98Transition": "0x7fffffffffffff"
|
||||
"eip98Transition": "0x7fffffffffffff",
|
||||
"eip86Transition": "0x7fffffffffffff"
|
||||
},
|
||||
"genesis": {
|
||||
"seal": {
|
||||
|
@ -24,7 +24,8 @@
|
||||
"maximumExtraDataSize": "0x20",
|
||||
"minGasLimit": "0x1388",
|
||||
"networkID" : "0x1",
|
||||
"eip98Transition": "0x7fffffffffffffff"
|
||||
"eip98Transition": "0x7fffffffffffffff",
|
||||
"eip86Transition": "0x7fffffffffffffff"
|
||||
},
|
||||
"genesis": {
|
||||
"seal": {
|
||||
|
@ -24,7 +24,8 @@
|
||||
"maximumExtraDataSize": "0x20",
|
||||
"minGasLimit": "0x1388",
|
||||
"networkID" : "0x1",
|
||||
"eip98Transition": "0x7fffffffffffffff"
|
||||
"eip98Transition": "0x7fffffffffffffff",
|
||||
"eip86Transition": "0x7fffffffffffffff"
|
||||
},
|
||||
"genesis": {
|
||||
"seal": {
|
||||
|
@ -30,7 +30,8 @@
|
||||
"networkID": "0x1",
|
||||
"chainID": "0x2",
|
||||
"subprotocolName": "exp",
|
||||
"eip98Transition": "0x7fffffffffffff"
|
||||
"eip98Transition": "0x7fffffffffffff",
|
||||
"eip86Transition": "0x7fffffffffffff"
|
||||
},
|
||||
"genesis": {
|
||||
"seal": {
|
||||
|
@ -147,7 +147,8 @@
|
||||
"networkID" : "0x1",
|
||||
"forkBlock": "0x1d4c00",
|
||||
"forkCanonHash": "0x4985f5ca3d2afbec36529aa96f74de3cc10a2a4a6c44f2157a57d2c6059a11bb",
|
||||
"eip98Transition": "0x7fffffffffffff"
|
||||
"eip98Transition": "0x7fffffffffffff",
|
||||
"eip86Transition": "0x7fffffffffffff"
|
||||
},
|
||||
"genesis": {
|
||||
"seal": {
|
||||
|
@ -143,7 +143,8 @@
|
||||
"maximumExtraDataSize": "0x20",
|
||||
"minGasLimit": "0x1388",
|
||||
"networkID" : "0x1",
|
||||
"eip98Transition": "0x7fffffffffffff"
|
||||
"eip98Transition": "0x7fffffffffffff",
|
||||
"eip86Transition": "0x7fffffffffffff"
|
||||
},
|
||||
"genesis": {
|
||||
"seal": {
|
||||
|
@ -23,7 +23,8 @@
|
||||
"maximumExtraDataSize": "0x20",
|
||||
"minGasLimit": "0x1388",
|
||||
"networkID" : "0x1",
|
||||
"eip98Transition": "0x7fffffffffffff"
|
||||
"eip98Transition": "0x7fffffffffffff",
|
||||
"eip86Transition": "0x7fffffffffffff"
|
||||
},
|
||||
"genesis": {
|
||||
"seal": {
|
||||
|
@ -23,7 +23,8 @@
|
||||
"maximumExtraDataSize": "0x20",
|
||||
"minGasLimit": "0x1388",
|
||||
"networkID" : "0x1",
|
||||
"eip98Transition": "0x7fffffffffffff"
|
||||
"eip98Transition": "0x7fffffffffffff",
|
||||
"eip86Transition": "0x7fffffffffffff"
|
||||
},
|
||||
"genesis": {
|
||||
"seal": {
|
||||
|
@ -30,7 +30,8 @@
|
||||
"chainID": "0x3e",
|
||||
"forkBlock": "0x1b34d8",
|
||||
"forkCanonHash": "0xf376243aeff1f256d970714c3de9fd78fa4e63cf63e32a51fe1169e375d98145",
|
||||
"eip98Transition": "0x7fffffffffffff"
|
||||
"eip98Transition": "0x7fffffffffffff",
|
||||
"eip86Transition": "0x7fffffffffffff"
|
||||
},
|
||||
"genesis": {
|
||||
"seal": {
|
||||
|
@ -23,7 +23,8 @@
|
||||
"maximumExtraDataSize": "0x0400",
|
||||
"minGasLimit": "125000",
|
||||
"networkID" : "0x0",
|
||||
"eip98Transition": "0x7fffffffffffff"
|
||||
"eip98Transition": "0x7fffffffffffff",
|
||||
"eip86Transition": "0x7fffffffffffff"
|
||||
},
|
||||
"genesis": {
|
||||
"seal": {
|
||||
|
@ -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
|
@ -139,11 +139,12 @@
|
||||
}
|
||||
},
|
||||
"params": {
|
||||
"eip98Transition": "0x7fffffffffffffff",
|
||||
"accountStartNonce": "0x00",
|
||||
"maximumExtraDataSize": "0x20",
|
||||
"minGasLimit": "0x1388",
|
||||
"networkID" : "0x1"
|
||||
"networkID" : "0x1",
|
||||
"eip98Transition": "0x7fffffffffffff",
|
||||
"eip86Transition": "0x7fffffffffffff"
|
||||
},
|
||||
"genesis": {
|
||||
"seal": {
|
||||
|
@ -7,7 +7,8 @@
|
||||
"accountStartNonce": "0x0",
|
||||
"maximumExtraDataSize": "0x20",
|
||||
"minGasLimit": "0x1388",
|
||||
"networkID" : "0x2"
|
||||
"networkID" : "0x2",
|
||||
"eip86Transition": "0x7fffffffffffff"
|
||||
},
|
||||
"genesis": {
|
||||
"seal": {
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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 { }
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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) }
|
||||
|
@ -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(())
|
||||
}
|
||||
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
///
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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))?;
|
||||
|
||||
|
@ -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));
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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(¶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<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();
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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"}
|
||||
|
@ -170,3 +170,4 @@ mod json_tests;
|
||||
|
||||
pub use types::*;
|
||||
pub use executive::contract_address;
|
||||
pub use evm::CreateContractAddress;
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
|
@ -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(),
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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!();
|
||||
}
|
||||
|
||||
|
@ -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)]
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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(),
|
||||
|
@ -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> {
|
||||
|
@ -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()),
|
||||
},
|
||||
|
@ -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<_>>()
|
||||
)
|
||||
}
|
||||
|
@ -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()
|
||||
)
|
||||
}
|
||||
|
@ -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)))
|
||||
}
|
||||
}
|
||||
|
@ -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()));
|
||||
|
@ -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()) +
|
||||
|
@ -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)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user