Merge branch 'vmtracing' into diffing

This commit is contained in:
Gav Wood
2016-06-02 12:28:09 +02:00
93 changed files with 2826 additions and 1597 deletions

View File

@@ -20,150 +20,24 @@ use std::sync::Arc;
use std::str::FromStr;
use ethcore::client::{BlockChainClient, Client, ClientConfig};
use ethcore::spec::Genesis;
use ethcore::ids::BlockID;
use ethcore::spec::{Genesis, Spec};
use ethcore::block::Block;
use ethcore::views::BlockView;
use ethcore::ethereum;
use ethcore::transaction::{Transaction, Action};
use ethminer::{MinerService, ExternalMiner};
use ethcore::miner::{MinerService, ExternalMiner, Miner};
use devtools::RandomTempPath;
use util::Hashable;
use util::io::IoChannel;
use util::hash::Address;
use util::numbers::{Uint, U256};
use util::hash::{Address, H256};
use util::numbers::U256;
use util::keys::{AccountProvider, TestAccount, TestAccountProvider};
use jsonrpc_core::IoHandler;
use ethjson::blockchain::BlockChain;
use v1::traits::eth::Eth;
use v1::impls::EthClient;
use v1::tests::helpers::{TestSyncProvider, Config, TestMinerService};
struct EthTester {
_client: Arc<BlockChainClient>,
_miner: Arc<MinerService>,
accounts: Arc<TestAccountProvider>,
handler: IoHandler,
}
#[test]
fn harness_works() {
let chain: BlockChain = extract_chain!("BlockchainTests/bcUncleTest");
chain_harness(chain, |_| {});
}
#[test]
fn eth_get_balance() {
let chain = extract_chain!("BlockchainTests/bcWalletTest", "wallet2outOf3txs");
chain_harness(chain, |tester| {
// final account state
let req_latest = r#"{
"jsonrpc": "2.0",
"method": "eth_getBalance",
"params": ["0xaaaf5374fce5edbc8e2a8697c15331677e6ebaaa", "latest"],
"id": 1
}"#;
let res_latest = r#"{"jsonrpc":"2.0","result":"0x09","id":1}"#.to_owned();
assert_eq!(tester.handler.handle_request(req_latest).unwrap(), res_latest);
// non-existant account
let req_new_acc = r#"{
"jsonrpc": "2.0",
"method": "eth_getBalance",
"params": ["0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"],
"id": 3
}"#;
let res_new_acc = r#"{"jsonrpc":"2.0","result":"0x00","id":3}"#.to_owned();
assert_eq!(tester.handler.handle_request(req_new_acc).unwrap(), res_new_acc);
});
}
#[test]
fn eth_block_number() {
let chain = extract_chain!("BlockchainTests/bcRPC_API_Test");
chain_harness(chain, |tester| {
let req_number = r#"{
"jsonrpc": "2.0",
"method": "eth_blockNumber",
"params": [],
"id": 1
}"#;
let res_number = r#"{"jsonrpc":"2.0","result":"0x20","id":1}"#.to_owned();
assert_eq!(tester.handler.handle_request(req_number).unwrap(), res_number);
});
}
#[cfg(test)]
#[test]
fn eth_transaction_count() {
let chain = extract_chain!("BlockchainTests/bcRPC_API_Test");
chain_harness(chain, |tester| {
let address = tester.accounts.new_account("123").unwrap();
let secret = tester.accounts.account_secret(&address).unwrap();
let req_before = r#"{
"jsonrpc": "2.0",
"method": "eth_getTransactionCount",
"params": [""#.to_owned() + format!("0x{:?}", address).as_ref() + r#"", "latest"],
"id": 15
}"#;
let res_before = r#"{"jsonrpc":"2.0","result":"0x00","id":15}"#;
assert_eq!(tester.handler.handle_request(&req_before).unwrap(), res_before);
let t = Transaction {
nonce: U256::zero(),
gas_price: U256::from(0x9184e72a000u64),
gas: U256::from(0x76c0),
action: Action::Call(Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap()),
value: U256::from(0x9184e72au64),
data: vec![]
}.sign(&secret);
let req_send_trans = r#"{
"jsonrpc": "2.0",
"method": "eth_sendTransaction",
"params": [{
"from": ""#.to_owned() + format!("0x{:?}", address).as_ref() + r#"",
"to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567",
"gas": "0x76c0",
"gasPrice": "0x9184e72a000",
"value": "0x9184e72a"
}],
"id": 16
}"#;
let res_send_trans = r#"{"jsonrpc":"2.0","result":""#.to_owned() + format!("0x{:?}", t.hash()).as_ref() + r#"","id":16}"#;
// dispatch the transaction.
assert_eq!(tester.handler.handle_request(&req_send_trans).unwrap(), res_send_trans);
// we have submitted the transaction -- but this shouldn't be reflected in a "latest" query.
let req_after_latest = r#"{
"jsonrpc": "2.0",
"method": "eth_getTransactionCount",
"params": [""#.to_owned() + format!("0x{:?}", address).as_ref() + r#"", "latest"],
"id": 17
}"#;
let res_after_latest = r#"{"jsonrpc":"2.0","result":"0x00","id":17}"#;
assert_eq!(&tester.handler.handle_request(&req_after_latest).unwrap(), res_after_latest);
// the pending transactions should have been updated.
let req_after_pending = r#"{
"jsonrpc": "2.0",
"method": "eth_getTransactionCount",
"params": [""#.to_owned() + format!("0x{:?}", address).as_ref() + r#"", "pending"],
"id": 18
}"#;
let res_after_pending = r#"{"jsonrpc":"2.0","result":"0x01","id":18}"#;
assert_eq!(&tester.handler.handle_request(&req_after_pending).unwrap(), res_after_pending);
});
}
use v1::traits::eth::{Eth, EthSigning};
use v1::impls::{EthClient, EthSigningUnsafeClient};
use v1::tests::helpers::{TestSyncProvider, Config};
fn account_provider() -> Arc<TestAccountProvider> {
let mut accounts = HashMap::new();
@@ -179,51 +53,308 @@ fn sync_provider() -> Arc<TestSyncProvider> {
}))
}
fn miner_service() -> Arc<TestMinerService> {
Arc::new(TestMinerService::default())
fn miner_service(spec: Spec, accounts: Arc<AccountProvider>) -> Arc<Miner> {
Miner::with_accounts(true, spec, accounts)
}
// given a blockchain, this harness will create an EthClient wrapping it
// which tests can pass specially crafted requests to.
fn chain_harness<F, U>(chain: BlockChain, mut cb: F) -> U
where F: FnMut(&EthTester) -> U {
fn make_spec(chain: &BlockChain) -> Spec {
let genesis = Genesis::from(chain.genesis());
let mut spec = ethereum::new_frontier_test();
let state = chain.pre_state.clone().into();
spec.set_genesis_state(state);
spec.overwrite_genesis_params(genesis);
assert!(spec.is_state_root_valid());
spec
}
let dir = RandomTempPath::new();
let client = Client::new(ClientConfig::default(), spec, dir.as_path(), IoChannel::disconnected()).unwrap();
let sync_provider = sync_provider();
let miner_service = miner_service();
let account_provider = account_provider();
let external_miner = Arc::new(ExternalMiner::default());
struct EthTester {
client: Arc<Client>,
_miner: Arc<MinerService>,
accounts: Arc<TestAccountProvider>,
handler: IoHandler,
}
for b in &chain.blocks_rlp() {
if Block::is_good(&b) {
let _ = client.import_block(b.clone());
client.flush_queue();
client.import_verified_blocks(&IoChannel::disconnected());
impl EthTester {
fn from_chain(chain: &BlockChain) -> Self {
let tester = Self::from_spec_provider(|| make_spec(chain));
for b in &chain.blocks_rlp() {
if Block::is_good(&b) {
let _ = tester.client.import_block(b.clone());
tester.client.flush_queue();
tester.client.import_verified_blocks(&IoChannel::disconnected());
}
}
tester.client.flush_queue();
assert!(tester.client.chain_info().best_block_hash == chain.best_block.clone().into());
tester
}
fn from_spec_provider<F>(spec_provider: F) -> Self
where F: Fn() -> Spec {
let dir = RandomTempPath::new();
let account_provider = account_provider();
let miner_service = miner_service(spec_provider(), account_provider.clone());
let client = Client::new(ClientConfig::default(), spec_provider(), dir.as_path(), miner_service.clone(), IoChannel::disconnected()).unwrap();
let sync_provider = sync_provider();
let external_miner = Arc::new(ExternalMiner::default());
let eth_client = EthClient::new(
&client,
&sync_provider,
&account_provider,
&miner_service,
&external_miner
);
let eth_sign = EthSigningUnsafeClient::new(
&client,
&account_provider,
&miner_service
);
let handler = IoHandler::new();
handler.add_delegate(eth_client.to_delegate());
handler.add_delegate(eth_sign.to_delegate());
EthTester {
_miner: miner_service,
client: client,
accounts: account_provider,
handler: handler,
}
}
}
#[test]
fn harness_works() {
let chain: BlockChain = extract_chain!("BlockchainTests/bcUncleTest");
let _ = EthTester::from_chain(&chain);
}
#[test]
fn eth_get_balance() {
let chain = extract_chain!("BlockchainTests/bcWalletTest", "wallet2outOf3txs");
let tester = EthTester::from_chain(&chain);
// final account state
let req_latest = r#"{
"jsonrpc": "2.0",
"method": "eth_getBalance",
"params": ["0xaaaf5374fce5edbc8e2a8697c15331677e6ebaaa", "latest"],
"id": 1
}"#;
let res_latest = r#"{"jsonrpc":"2.0","result":"0x09","id":1}"#.to_owned();
assert_eq!(tester.handler.handle_request(req_latest).unwrap(), res_latest);
// non-existant account
let req_new_acc = r#"{
"jsonrpc": "2.0",
"method": "eth_getBalance",
"params": ["0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"],
"id": 3
}"#;
let res_new_acc = r#"{"jsonrpc":"2.0","result":"0x00","id":3}"#.to_owned();
assert_eq!(tester.handler.handle_request(req_new_acc).unwrap(), res_new_acc);
}
#[test]
fn eth_block_number() {
let chain = extract_chain!("BlockchainTests/bcRPC_API_Test");
let tester = EthTester::from_chain(&chain);
let req_number = r#"{
"jsonrpc": "2.0",
"method": "eth_blockNumber",
"params": [],
"id": 1
}"#;
let res_number = r#"{"jsonrpc":"2.0","result":"0x20","id":1}"#.to_owned();
assert_eq!(tester.handler.handle_request(req_number).unwrap(), res_number);
}
// a frontier-like test with an expanded gas limit and balance on known account.
const TRANSACTION_COUNT_SPEC: &'static [u8] = br#"{
"name": "Frontier (Test)",
"engine": {
"Ethash": {
"params": {
"gasLimitBoundDivisor": "0x0400",
"minimumDifficulty": "0x020000",
"difficultyBoundDivisor": "0x0800",
"durationLimit": "0x0d",
"blockReward": "0x4563918244F40000",
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
"frontierCompatibilityModeLimit": "0xffffffffffffffff"
}
}
},
"params": {
"accountStartNonce": "0x00",
"maximumExtraDataSize": "0x20",
"minGasLimit": "0x50000",
"networkID" : "0x1"
},
"genesis": {
"seal": {
"ethereum": {
"nonce": "0x0000000000000042",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000"
}
},
"difficulty": "0x400000000",
"author": "0x0000000000000000000000000000000000000000",
"timestamp": "0x00",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"extraData": "0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa",
"gasLimit": "0x50000"
},
"accounts": {
"0000000000000000000000000000000000000001": { "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } },
"0000000000000000000000000000000000000002": { "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } },
"0000000000000000000000000000000000000003": { "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } },
"0000000000000000000000000000000000000004": { "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } },
"faa34835af5c2ea724333018a515fbb7d5bc0b33": { "balance": "10000000000000", "nonce": "0" }
}
}
"#;
#[test]
fn eth_transaction_count() {
use util::crypto::Secret;
let address = Address::from_str("faa34835af5c2ea724333018a515fbb7d5bc0b33").unwrap();
let secret = Secret::from_str("8a283037bb19c4fed7b1c569e40c7dcff366165eb869110a1b11532963eb9cb2").unwrap();
let tester = EthTester::from_spec_provider(|| Spec::load(TRANSACTION_COUNT_SPEC));
tester.accounts.accounts.write().unwrap().insert(address, TestAccount {
unlocked: false,
password: "123".into(),
secret: secret
});
let req_before = r#"{
"jsonrpc": "2.0",
"method": "eth_getTransactionCount",
"params": [""#.to_owned() + format!("0x{:?}", address).as_ref() + r#"", "latest"],
"id": 15
}"#;
let res_before = r#"{"jsonrpc":"2.0","result":"0x00","id":15}"#;
assert_eq!(tester.handler.handle_request(&req_before).unwrap(), res_before);
let req_send_trans = r#"{
"jsonrpc": "2.0",
"method": "eth_sendTransaction",
"params": [{
"from": ""#.to_owned() + format!("0x{:?}", address).as_ref() + r#"",
"to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567",
"gas": "0x30000",
"gasPrice": "0x01",
"value": "0x9184e72a"
}],
"id": 16
}"#;
// dispatch the transaction.
tester.handler.handle_request(&req_send_trans).unwrap();
// we have submitted the transaction -- but this shouldn't be reflected in a "latest" query.
let req_after_latest = r#"{
"jsonrpc": "2.0",
"method": "eth_getTransactionCount",
"params": [""#.to_owned() + format!("0x{:?}", address).as_ref() + r#"", "latest"],
"id": 17
}"#;
let res_after_latest = r#"{"jsonrpc":"2.0","result":"0x00","id":17}"#;
assert_eq!(&tester.handler.handle_request(&req_after_latest).unwrap(), res_after_latest);
// the pending transactions should have been updated.
let req_after_pending = r#"{
"jsonrpc": "2.0",
"method": "eth_getTransactionCount",
"params": [""#.to_owned() + format!("0x{:?}", address).as_ref() + r#"", "pending"],
"id": 18
}"#;
let res_after_pending = r#"{"jsonrpc":"2.0","result":"0x01","id":18}"#;
assert_eq!(&tester.handler.handle_request(&req_after_pending).unwrap(), res_after_pending);
}
fn verify_transaction_counts(name: String, chain: BlockChain) {
struct PanicHandler(String);
impl Drop for PanicHandler {
fn drop(&mut self) {
if ::std::thread::panicking() {
println!("Test failed: {}", self.0);
}
}
}
assert!(client.chain_info().best_block_hash == chain.best_block.into());
let _panic = PanicHandler(name);
let eth_client = EthClient::new(&client, &sync_provider, &account_provider,
&miner_service, &external_miner);
fn by_hash(hash: H256, count: usize, id: &mut usize) -> (String, String) {
let req = r#"{
"jsonrpc": "2.0",
"method": "eth_getBlockTransactionCountByHash",
"params": [
""#.to_owned() + format!("0x{:?}", hash).as_ref() + r#""
],
"id": "# + format!("{}", *id).as_ref() + r#"
}"#;
let handler = IoHandler::new();
let delegate = eth_client.to_delegate();
handler.add_delegate(delegate);
let res = r#"{"jsonrpc":"2.0","result":""#.to_owned()
+ format!("0x{:02x}", count).as_ref()
+ r#"","id":"#
+ format!("{}", *id).as_ref() + r#"}"#;
*id += 1;
(req, res)
}
let tester = EthTester {
_miner: miner_service,
_client: client,
accounts: account_provider,
handler: handler,
};
fn by_number(num: u64, count: usize, id: &mut usize) -> (String, String) {
let req = r#"{
"jsonrpc": "2.0",
"method": "eth_getBlockTransactionCountByNumber",
"params": [
"#.to_owned() + &::serde_json::to_string(&U256::from(num)).unwrap() + r#"
],
"id": "# + format!("{}", *id).as_ref() + r#"
}"#;
cb(&tester)
let res = r#"{"jsonrpc":"2.0","result":""#.to_owned()
+ format!("0x{:02x}", count).as_ref()
+ r#"","id":"#
+ format!("{}", *id).as_ref() + r#"}"#;
*id += 1;
(req, res)
}
let tester = EthTester::from_chain(&chain);
let mut id = 1;
for b in chain.blocks_rlp().iter().filter(|b| Block::is_good(b)).map(|b| BlockView::new(b)) {
let count = b.transactions_count();
let hash = b.sha3();
let number = b.header_view().number();
let (req, res) = by_hash(hash, count, &mut id);
assert_eq!(tester.handler.handle_request(&req), Some(res));
// uncles can share block numbers, so skip them.
if tester.client.block_hash(BlockID::Number(number)) == Some(hash) {
let (req, res) = by_number(number, count, &mut id);
assert_eq!(tester.handler.handle_request(&req), Some(res));
}
}
}
register_test!(eth_transaction_count_1, verify_transaction_counts, "BlockchainTests/bcWalletTest");
register_test!(eth_transaction_count_2, verify_transaction_counts, "BlockchainTests/bcTotalDifficultyTest");
register_test!(eth_transaction_count_3, verify_transaction_counts, "BlockchainTests/bcGasPricerTest");

View File

@@ -19,11 +19,11 @@
use util::{Address, H256, Bytes, U256, FixedHash, Uint};
use util::standard::*;
use ethcore::error::{Error, ExecutionError};
use ethcore::client::{BlockChainClient, Executed, CallAnalytics};
use ethcore::client::{MiningBlockChainClient, Executed, CallAnalytics};
use ethcore::block::{ClosedBlock, IsBlock};
use ethcore::transaction::SignedTransaction;
use ethcore::receipt::Receipt;
use ethminer::{MinerService, MinerStatus, AccountDetails, TransactionImportResult};
use ethcore::miner::{MinerService, MinerStatus, AccountDetails, TransactionImportResult};
/// Test miner service.
pub struct TestMinerService {
@@ -132,7 +132,7 @@ impl MinerService for TestMinerService {
}
/// Imports transactions to transaction queue.
fn import_own_transaction<T>(&self, chain: &BlockChainClient, transaction: SignedTransaction, _fetch_account: T) ->
fn import_own_transaction<T>(&self, chain: &MiningBlockChainClient, transaction: SignedTransaction, _fetch_account: T) ->
Result<TransactionImportResult, Error>
where T: Fn(&Address) -> AccountDetails {
@@ -154,21 +154,21 @@ impl MinerService for TestMinerService {
}
/// Removes all transactions from the queue and restart mining operation.
fn clear_and_reset(&self, _chain: &BlockChainClient) {
fn clear_and_reset(&self, _chain: &MiningBlockChainClient) {
unimplemented!();
}
/// Called when blocks are imported to chain, updates transactions queue.
fn chain_new_blocks(&self, _chain: &BlockChainClient, _imported: &[H256], _invalid: &[H256], _enacted: &[H256], _retracted: &[H256]) {
fn chain_new_blocks(&self, _chain: &MiningBlockChainClient, _imported: &[H256], _invalid: &[H256], _enacted: &[H256], _retracted: &[H256]) {
unimplemented!();
}
/// New chain head event. Restart mining operation.
fn update_sealing(&self, _chain: &BlockChainClient) {
fn update_sealing(&self, _chain: &MiningBlockChainClient) {
unimplemented!();
}
fn map_sealing_work<F, T>(&self, _chain: &BlockChainClient, _f: F) -> Option<T> where F: FnOnce(&ClosedBlock) -> T {
fn map_sealing_work<F, T>(&self, _chain: &MiningBlockChainClient, _f: F) -> Option<T> where F: FnOnce(&ClosedBlock) -> T {
unimplemented!();
}
@@ -194,29 +194,29 @@ impl MinerService for TestMinerService {
/// Submit `seal` as a valid solution for the header of `pow_hash`.
/// Will check the seal, but not actually insert the block into the chain.
fn submit_seal(&self, _chain: &BlockChainClient, _pow_hash: H256, _seal: Vec<Bytes>) -> Result<(), Error> {
fn submit_seal(&self, _chain: &MiningBlockChainClient, _pow_hash: H256, _seal: Vec<Bytes>) -> Result<(), Error> {
unimplemented!();
}
fn balance(&self, _chain: &BlockChainClient, address: &Address) -> U256 {
fn balance(&self, _chain: &MiningBlockChainClient, address: &Address) -> U256 {
self.latest_closed_block.lock().unwrap().as_ref().map_or_else(U256::zero, |b| b.block().fields().state.balance(address).clone())
}
fn call(&self, _chain: &BlockChainClient, _t: &SignedTransaction, _analytics: CallAnalytics) -> Result<Executed, ExecutionError> {
fn call(&self, _chain: &MiningBlockChainClient, _t: &SignedTransaction, _analytics: CallAnalytics) -> Result<Executed, ExecutionError> {
unimplemented!();
}
fn storage_at(&self, _chain: &BlockChainClient, address: &Address, position: &H256) -> H256 {
fn storage_at(&self, _chain: &MiningBlockChainClient, address: &Address, position: &H256) -> H256 {
self.latest_closed_block.lock().unwrap().as_ref().map_or_else(H256::default, |b| b.block().fields().state.storage_at(address, position).clone())
}
fn nonce(&self, _chain: &BlockChainClient, address: &Address) -> U256 {
fn nonce(&self, _chain: &MiningBlockChainClient, address: &Address) -> U256 {
// we assume all transactions are in a pending block, ignoring the
// reality of gas limits.
self.last_nonce(address).unwrap_or(U256::zero())
}
fn code(&self, _chain: &BlockChainClient, address: &Address) -> Option<Bytes> {
fn code(&self, _chain: &MiningBlockChainClient, address: &Address) -> Option<Bytes> {
self.latest_closed_block.lock().unwrap().as_ref().map_or(None, |b| b.block().fields().state.code(address).clone())
}

View File

@@ -25,9 +25,9 @@ use ethcore::client::{TestBlockChainClient, EachBlockWith, Executed, Transaction
use ethcore::log_entry::{LocalizedLogEntry, LogEntry};
use ethcore::receipt::LocalizedReceipt;
use ethcore::transaction::{Transaction, Action};
use ethminer::{ExternalMiner, MinerService};
use ethcore::miner::{ExternalMiner, MinerService};
use ethsync::SyncState;
use v1::{Eth, EthClient};
use v1::{Eth, EthClient, EthSigning, EthSigningUnsafeClient};
use v1::tests::helpers::{TestSyncProvider, Config, TestMinerService};
use rustc_serialize::hex::ToHex;
@@ -72,8 +72,11 @@ impl Default for EthTester {
let hashrates = Arc::new(RwLock::new(HashMap::new()));
let external_miner = Arc::new(ExternalMiner::new(hashrates.clone()));
let eth = EthClient::new(&client, &sync, &ap, &miner, &external_miner).to_delegate();
let sign = EthSigningUnsafeClient::new(&client, &ap, &miner).to_delegate();
let io = IoHandler::new();
io.add_delegate(eth);
io.add_delegate(sign);
EthTester {
client: client,
sync: sync,
@@ -428,6 +431,7 @@ fn rpc_eth_call() {
output: vec![0x12, 0x34, 0xff],
trace: None,
vm_trace: None,
diff: None,
});
let request = r#"{
@@ -462,6 +466,7 @@ fn rpc_eth_call_default_block() {
output: vec![0x12, 0x34, 0xff],
trace: None,
vm_trace: None,
diff: None,
});
let request = r#"{
@@ -495,6 +500,7 @@ fn rpc_eth_estimate_gas() {
output: vec![0x12, 0x34, 0xff],
trace: None,
vm_trace: None,
diff: None,
});
let request = r#"{
@@ -529,6 +535,7 @@ fn rpc_eth_estimate_gas_default_block() {
output: vec![0x12, 0x34, 0xff],
trace: None,
vm_trace: None,
diff: None,
});
let request = r#"{

View File

@@ -0,0 +1,75 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use std::sync::Arc;
use jsonrpc_core::IoHandler;
use v1::impls::EthSigningQueueClient;
use v1::traits::EthSigning;
use v1::helpers::{ConfirmationsQueue, SigningQueue};
use util::keys::TestAccount;
struct EthSigningTester {
pub queue: Arc<ConfirmationsQueue>,
pub io: IoHandler,
}
impl Default for EthSigningTester {
fn default() -> Self {
let queue = Arc::new(ConfirmationsQueue::default());
let io = IoHandler::new();
io.add_delegate(EthSigningQueueClient::new(&queue).to_delegate());
EthSigningTester {
queue: queue,
io: io,
}
}
}
fn eth_signing() -> EthSigningTester {
EthSigningTester::default()
}
#[test]
fn should_add_transaction_to_queue() {
// given
let tester = eth_signing();
let account = TestAccount::new("123");
let address = account.address();
assert_eq!(tester.queue.requests().len(), 0);
// when
let request = r#"{
"jsonrpc": "2.0",
"method": "eth_sendTransaction",
"params": [{
"from": ""#.to_owned() + format!("0x{:?}", address).as_ref() + r#"",
"to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567",
"gas": "0x76c0",
"gasPrice": "0x9184e72a000",
"value": "0x9184e72a"
}],
"id": 1
}"#;
let response = r#"{"jsonrpc":"2.0","result":"0x0000000000000000000000000000000000000000000000000000000000000000","id":1}"#;
// then
assert_eq!(tester.io.handle_request(&request), Some(response.to_owned()));
assert_eq!(tester.queue.requests().len(), 1);
}

View File

@@ -18,8 +18,8 @@ use std::sync::Arc;
use std::str::FromStr;
use jsonrpc_core::IoHandler;
use v1::{Ethcore, EthcoreClient};
use ethminer::MinerService;
use ethcore::client::{TestBlockChainClient};
use ethcore::miner::MinerService;
use v1::tests::helpers::TestMinerService;
use util::numbers::*;
use rustc_serialize::hex::FromHex;

View File

@@ -18,8 +18,10 @@
//! method calls properly.
mod eth;
mod eth_signing;
mod net;
mod web3;
mod personal;
mod personal_signer;
mod ethcore;
mod rpc;

View File

@@ -176,4 +176,4 @@ fn sign_and_send_transaction() {
let response = r#"{"jsonrpc":"2.0","result":""#.to_owned() + format!("0x{:?}", t.hash()).as_ref() + r#"","id":1}"#;
assert_eq!(tester.io.handle_request(request.as_ref()), Some(response));
}
}

View File

@@ -0,0 +1,169 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use std::sync::Arc;
use std::str::FromStr;
use std::collections::HashMap;
use jsonrpc_core::IoHandler;
use util::numbers::*;
use util::keys::{TestAccount, TestAccountProvider};
use ethcore::client::TestBlockChainClient;
use ethcore::transaction::{Transaction, Action};
use v1::{SignerClient, PersonalSigner};
use v1::tests::helpers::TestMinerService;
use v1::helpers::{SigningQueue, ConfirmationsQueue};
use v1::types::TransactionRequest;
struct PersonalSignerTester {
queue: Arc<ConfirmationsQueue>,
accounts: Arc<TestAccountProvider>,
io: IoHandler,
miner: Arc<TestMinerService>,
// these unused fields are necessary to keep the data alive
// as the handler has only weak pointers.
_client: Arc<TestBlockChainClient>,
}
fn blockchain_client() -> Arc<TestBlockChainClient> {
let client = TestBlockChainClient::new();
Arc::new(client)
}
fn accounts_provider() -> Arc<TestAccountProvider> {
let accounts = HashMap::new();
let ap = TestAccountProvider::new(accounts);
Arc::new(ap)
}
fn miner_service() -> Arc<TestMinerService> {
Arc::new(TestMinerService::default())
}
fn signer_tester() -> PersonalSignerTester {
let queue = Arc::new(ConfirmationsQueue::default());
let accounts = accounts_provider();
let client = blockchain_client();
let miner = miner_service();
let io = IoHandler::new();
io.add_delegate(SignerClient::new(&accounts, &client, &miner, &queue).to_delegate());
PersonalSignerTester {
queue: queue,
accounts: accounts,
io: io,
miner: miner,
_client: client,
}
}
#[test]
fn should_return_list_of_transactions_in_queue() {
// given
let tester = signer_tester();
tester.queue.add_request(TransactionRequest {
from: Address::from(1),
to: Some(Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap()),
gas_price: Some(U256::from(10_000)),
gas: Some(U256::from(10_000_000)),
value: Some(U256::from(1)),
data: None,
nonce: None,
});
// when
let request = r#"{"jsonrpc":"2.0","method":"personal_transactionsToConfirm","params":[],"id":1}"#;
let response = r#"{"jsonrpc":"2.0","result":[{"id":"0x01","transaction":{"data":null,"from":"0x0000000000000000000000000000000000000001","gas":"0x989680","gasPrice":"0x2710","nonce":null,"to":"0xd46e8dd67c5d32be8058bb8eb970870f07244567","value":"0x01"}}],"id":1}"#;
// then
assert_eq!(tester.io.handle_request(&request), Some(response.to_owned()));
}
#[test]
fn should_reject_transaction_from_queue_without_dispatching() {
// given
let tester = signer_tester();
tester.queue.add_request(TransactionRequest {
from: Address::from(1),
to: Some(Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap()),
gas_price: Some(U256::from(10_000)),
gas: Some(U256::from(10_000_000)),
value: Some(U256::from(1)),
data: None,
nonce: None,
});
assert_eq!(tester.queue.requests().len(), 1);
// when
let request = r#"{"jsonrpc":"2.0","method":"personal_rejectTransaction","params":["0x01"],"id":1}"#;
let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#;
// then
assert_eq!(tester.io.handle_request(&request), Some(response.to_owned()));
assert_eq!(tester.queue.requests().len(), 0);
assert_eq!(tester.miner.imported_transactions.lock().unwrap().len(), 0);
}
#[test]
fn should_confirm_transaction_and_dispatch() {
// given
let tester = signer_tester();
let account = TestAccount::new("test");
let address = account.address();
let secret = account.secret.clone();
let recipient = Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap();
tester.accounts.accounts
.write()
.unwrap()
.insert(address, account);
tester.queue.add_request(TransactionRequest {
from: address,
to: Some(recipient),
gas_price: Some(U256::from(10_000)),
gas: Some(U256::from(10_000_000)),
value: Some(U256::from(1)),
data: None,
nonce: None,
});
let t = Transaction {
nonce: U256::zero(),
gas_price: U256::from(0x1000),
gas: U256::from(10_000_000),
action: Action::Call(recipient),
value: U256::from(0x1),
data: vec![]
}.sign(&secret);
assert_eq!(tester.queue.requests().len(), 1);
// when
let request = r#"{
"jsonrpc":"2.0",
"method":"personal_confirmTransaction",
"params":["0x01", {"gasPrice":"0x1000"}, "test"],
"id":1
}"#;
let response = r#"{"jsonrpc":"2.0","result":""#.to_owned() + format!("0x{:?}", t.hash()).as_ref() + r#"","id":1}"#;
// then
assert_eq!(tester.io.handle_request(&request), Some(response.to_owned()));
assert_eq!(tester.queue.requests().len(), 0);
assert_eq!(tester.miner.imported_transactions.lock().unwrap().len(), 1);
}

View File

@@ -13,11 +13,15 @@ pub mod helpers;
// extract the chain with that name. This will panic if no chain by that name
// is found.
macro_rules! extract_chain {
($file:expr, $name:expr) => {{
(iter $file:expr) => {{
const RAW_DATA: &'static [u8] =
include_bytes!(concat!("../../../../ethcore/res/ethereum/tests/", $file, ".json"));
::ethjson::blockchain::Test::load(RAW_DATA).unwrap().into_iter()
}};
($file:expr, $name:expr) => {{
let mut chain = None;
for (name, c) in ::ethjson::blockchain::Test::load(RAW_DATA).unwrap() {
for (name, c) in extract_chain!(iter $file) {
if name == $name {
chain = Some(c);
break;
@@ -27,14 +31,41 @@ macro_rules! extract_chain {
}};
($file:expr) => {{
const RAW_DATA: &'static [u8] =
include_bytes!(concat!("../../../../ethcore/res/ethereum/tests/", $file, ".json"));
::ethjson::blockchain::Test::load(RAW_DATA)
.unwrap().into_iter().next().unwrap().1
extract_chain!(iter $file).next().unwrap().1
}};
}
macro_rules! register_test {
($name:ident, $cb:expr, $file:expr) => {
#[test]
fn $name() {
for (name, chain) in extract_chain!(iter $file) {
$cb(name, chain);
}
}
};
(heavy $name:ident, $cb:expr, $file:expr) => {
#[test]
#[cfg(feature = "test-heavy")]
fn $name() {
for (name, chain) in extract_chain!(iter $file) {
$cb(name, chain);
}
}
};
(ignore $name:ident, $cb:expr, $file:expr) => {
#[test]
#[ignore]
fn $name() {
for (name, chain) in extract_chain!(iter $file) {
$cb(name, chain);
}
}
};
}
#[cfg(test)]
mod mocked;
#[cfg(test)]