From f85e409ff7f68b3d8061206afc8f7f914c778f7e Mon Sep 17 00:00:00 2001 From: arkpar Date: Fri, 27 May 2016 16:35:52 +0200 Subject: [PATCH 01/30] Make sure downloaded blocks are unmarked on send error --- sync/src/chain.rs | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/sync/src/chain.rs b/sync/src/chain.rs index e66fce0d5..6a54c648f 100644 --- a/sync/src/chain.rs +++ b/sync/src/chain.rs @@ -862,16 +862,12 @@ impl ChainSync { warn!(target:"sync", "Asking {:?} while requesting {:?}", peer.asking, asking); } } - match sync.send(peer_id, packet_id, packet) { - Err(e) => { - debug!(target:"sync", "Error sending request: {:?}", e); - sync.disable_peer(peer_id); - } - Ok(_) => { - let mut peer = self.peers.get_mut(&peer_id).unwrap(); - peer.asking = asking; - peer.ask_time = time::precise_time_s(); - } + let mut peer = self.peers.get_mut(&peer_id).unwrap(); + peer.asking = asking; + peer.ask_time = time::precise_time_s(); + if let Err(e) = sync.send(peer_id, packet_id, packet) { + debug!(target:"sync", "Error sending request: {:?}", e); + sync.disable_peer(peer_id); } } From d1fc5a561180576cc865707982f0b305620f3335 Mon Sep 17 00:00:00 2001 From: arkpar Date: Fri, 27 May 2016 16:49:29 +0200 Subject: [PATCH 02/30] Tweaked some constansts for slower machines --- ethcore/src/client/client.rs | 2 +- sync/src/chain.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index 13d678c14..ab1c21af1 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -254,7 +254,7 @@ impl Client where V: Verifier { /// This is triggered by a message coming from a block queue when the block is ready for insertion pub fn import_verified_blocks(&self, io: &IoChannel) -> usize { - let max_blocks_to_import = 128; + let max_blocks_to_import = 64; let mut imported_blocks = Vec::with_capacity(max_blocks_to_import); let mut invalid_blocks = HashSet::new(); diff --git a/sync/src/chain.rs b/sync/src/chain.rs index 6a54c648f..dde287a57 100644 --- a/sync/src/chain.rs +++ b/sync/src/chain.rs @@ -133,7 +133,7 @@ const NODE_DATA_PACKET: u8 = 0x0e; const GET_RECEIPTS_PACKET: u8 = 0x0f; const RECEIPTS_PACKET: u8 = 0x10; -const CONNECTION_TIMEOUT_SEC: f64 = 10f64; +const CONNECTION_TIMEOUT_SEC: f64 = 15f64; #[derive(Copy, Clone, Eq, PartialEq, Debug)] /// Sync state From 0e905a06d937a745435a6fe0a50d1ba873874c5d Mon Sep 17 00:00:00 2001 From: arkpar Date: Fri, 27 May 2016 17:26:50 +0200 Subject: [PATCH 03/30] Tweaked propagation order --- sync/src/chain.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sync/src/chain.rs b/sync/src/chain.rs index dde287a57..63ab8bcc8 100644 --- a/sync/src/chain.rs +++ b/sync/src/chain.rs @@ -1255,15 +1255,15 @@ impl ChainSync { } fn propagate_latest_blocks(&mut self, io: &mut SyncIo) { - self.propagate_new_transactions(io); let chain_info = io.chain().chain_info(); if (((chain_info.best_block_number as i64) - (self.last_sent_block_number as i64)).abs() as BlockNumber) < MAX_PEER_LAG_PROPAGATION { - let blocks = self.propagate_blocks(&chain_info, io); let hashes = self.propagate_new_hashes(&chain_info, io); + let blocks = self.propagate_blocks(&chain_info, io); if blocks != 0 || hashes != 0 { trace!(target: "sync", "Sent latest {} blocks and {} hashes to peers.", blocks, hashes); } } + self.propagate_new_transactions(io); self.last_sent_block_number = chain_info.best_block_number; } From 1e8bf8c89df7b9db69726441a92d020f49d45cb7 Mon Sep 17 00:00:00 2001 From: arkpar Date: Fri, 27 May 2016 18:57:12 +0200 Subject: [PATCH 04/30] More tweaks --- ethcore/src/tests/client.rs | 4 ++-- sync/src/chain.rs | 25 +++++++++++++++---------- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/ethcore/src/tests/client.rs b/ethcore/src/tests/client.rs index ee2cb9c3c..d734b0b47 100644 --- a/ethcore/src/tests/client.rs +++ b/ethcore/src/tests/client.rs @@ -115,7 +115,7 @@ fn can_collect_garbage() { fn can_handle_long_fork() { let client_result = generate_dummy_client(1200); let client = client_result.reference(); - for _ in 0..10 { + for _ in 0..20 { client.import_verified_blocks(&IoChannel::disconnected()); } assert_eq!(1200, client.chain_info().best_block_number); @@ -124,7 +124,7 @@ fn can_handle_long_fork() { push_blocks_to_client(client, 49, 1201, 800); push_blocks_to_client(client, 53, 1201, 600); - for _ in 0..20 { + for _ in 0..40 { client.import_verified_blocks(&IoChannel::disconnected()); } assert_eq!(2000, client.chain_info().best_block_number); diff --git a/sync/src/chain.rs b/sync/src/chain.rs index 63ab8bcc8..63f699c9e 100644 --- a/sync/src/chain.rs +++ b/sync/src/chain.rs @@ -113,11 +113,12 @@ const MAX_HEADERS_TO_SEND: usize = 512; const MAX_NODE_DATA_TO_SEND: usize = 1024; const MAX_RECEIPTS_TO_SEND: usize = 1024; const MAX_RECEIPTS_HEADERS_TO_SEND: usize = 256; -const MAX_HEADERS_TO_REQUEST: usize = 256; +const MAX_HEADERS_TO_REQUEST: usize = 128; const MAX_BODIES_TO_REQUEST: usize = 64; const MIN_PEERS_PROPAGATION: usize = 4; const MAX_PEERS_PROPAGATION: usize = 128; const MAX_PEER_LAG_PROPAGATION: BlockNumber = 20; +const SUBCHAIN_SIZE: usize = 64; const STATUS_PACKET: u8 = 0x00; const NEW_BLOCK_HASHES_PACKET: u8 = 0x01; @@ -639,7 +640,7 @@ impl ChainSync { self.sync_peer(io, p, false); } } - if !self.peers.values().any(|p| p.asking != PeerAsking::Nothing) { + if self.state != SyncState::Waiting && !self.peers.values().any(|p| p.asking != PeerAsking::Nothing) { self.complete_sync(); } } @@ -665,7 +666,7 @@ impl ChainSync { return; } if self.state == SyncState::Waiting { - trace!(target: "sync", "Waiting for block queue"); + trace!(target: "sync", "Waiting for the block queue"); return; } (peer.latest_hash.clone(), peer.difficulty.clone()) @@ -689,7 +690,7 @@ impl ChainSync { // Request subchain headers trace!(target: "sync", "Starting sync with better chain"); let last = self.last_imported_hash.clone(); - self.request_headers_by_hash(io, peer_id, &last, 128, 255, false, PeerAsking::Heads); + self.request_headers_by_hash(io, peer_id, &last, SUBCHAIN_SIZE, MAX_HEADERS_TO_REQUEST - 1, false, PeerAsking::Heads); }, SyncState::Blocks | SyncState::NewBlocks => { if io.chain().block_status(BlockID::Hash(peer_latest)) == BlockStatus::Unknown { @@ -704,6 +705,8 @@ impl ChainSync { fn start_sync_round(&mut self, io: &mut SyncIo) { self.state = SyncState::ChainHead; trace!(target: "sync", "Starting round (last imported count = {:?}, block = {:?}", self.imported_this_round, self.last_imported_block); + // Check if need to retract to find the common block. The problem is that the peers still return headers by hash even + // from the non-canonical part of the tree. So we also retract if nothing has been imported last round. if self.imported_this_round.is_some() && self.imported_this_round.unwrap() == 0 && self.last_imported_block > 0 { match io.chain().block_hash(BlockID::Number(self.last_imported_block - 1)) { Some(h) => { @@ -781,9 +784,13 @@ impl ChainSync { match io.chain().import_block(block) { Err(Error::Import(ImportError::AlreadyInChain)) => { + self.last_imported_block = number; + self.last_imported_hash = h.clone(); trace!(target: "sync", "Block already in chain {:?}", h); }, Err(Error::Import(ImportError::AlreadyQueued)) => { + self.last_imported_block = number; + self.last_imported_hash = h.clone(); trace!(target: "sync", "Block already queued {:?}", h); }, Ok(_) => { @@ -856,13 +863,10 @@ impl ChainSync { /// Generic request sender fn send_request(&mut self, sync: &mut SyncIo, peer_id: PeerId, asking: PeerAsking, packet_id: PacketId, packet: Bytes) { - { - let peer = self.peers.get_mut(&peer_id).unwrap(); - if peer.asking != PeerAsking::Nothing { - warn!(target:"sync", "Asking {:?} while requesting {:?}", peer.asking, asking); - } + let peer = self.peers.get_mut(&peer_id).unwrap(); + if peer.asking != PeerAsking::Nothing { + warn!(target:"sync", "Asking {:?} while requesting {:?}", peer.asking, asking); } - let mut peer = self.peers.get_mut(&peer_id).unwrap(); peer.asking = asking; peer.ask_time = time::precise_time_s(); if let Err(e) = sync.send(peer_id, packet_id, packet) { @@ -1095,6 +1099,7 @@ impl ChainSync { let tick = time::precise_time_s(); for (peer_id, peer) in &self.peers { if peer.asking != PeerAsking::Nothing && (tick - peer.ask_time) > CONNECTION_TIMEOUT_SEC { + trace!(target:"sync", "Timeouted {}", peer_id); io.disconnect_peer(*peer_id); } } From 7f3ba85a3f83a53f949d96ac0f9dedafafa39d9d Mon Sep 17 00:00:00 2001 From: arkpar Date: Sun, 29 May 2016 00:38:10 +0200 Subject: [PATCH 05/30] Fixed block/hashes propagation --- sync/src/chain.rs | 35 +++++++++++++++++------------------ sync/src/tests/chain.rs | 5 +++-- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/sync/src/chain.rs b/sync/src/chain.rs index 63f699c9e..2f31f1d47 100644 --- a/sync/src/chain.rs +++ b/sync/src/chain.rs @@ -1165,24 +1165,23 @@ impl ChainSync { .collect::>() } + + fn select_lagging_peers(&mut self, chain_info: &BlockChainInfo, io: &mut SyncIo) -> Vec<(PeerId, BlockNumber)> { + use rand::Rng; + let mut lagging_peers = self.get_lagging_peers(chain_info, io); + // take sqrt(x) peers + let mut count = (self.peers.len() as f64).powf(0.5).round() as usize; + count = min(count, MAX_PEERS_PROPAGATION); + count = max(count, MIN_PEERS_PROPAGATION); + ::rand::thread_rng().shuffle(&mut lagging_peers); + lagging_peers.into_iter().take(count).collect::>() + } + /// propagates latest block to lagging peers fn propagate_blocks(&mut self, chain_info: &BlockChainInfo, io: &mut SyncIo) -> usize { - let updated_peers = { - let lagging_peers = self.get_lagging_peers(chain_info, io); - - // sqrt(x)/x scaled to max u32 - let fraction = (self.peers.len() as f64).powf(-0.5).mul(u32::max_value() as f64).round() as u32; - let lucky_peers = match lagging_peers.len() { - 0 ... MIN_PEERS_PROPAGATION => lagging_peers, - _ => lagging_peers.into_iter().filter(|_| ::rand::random::() < fraction).collect::>() - }; - - // taking at max of MAX_PEERS_PROPAGATION - lucky_peers.iter().map(|&(id, _)| id.clone()).take(min(lucky_peers.len(), MAX_PEERS_PROPAGATION)).collect::>() - }; - + let lucky_peers = self.select_lagging_peers(chain_info, io); let mut sent = 0; - for peer_id in updated_peers { + for (peer_id, _) in lucky_peers { let rlp = ChainSync::create_latest_block_rlp(io.chain()); self.send_packet(io, peer_id, NEW_BLOCK_PACKET, rlp); self.peers.get_mut(&peer_id).unwrap().latest_hash = chain_info.best_block_hash.clone(); @@ -1194,12 +1193,12 @@ impl ChainSync { /// propagates new known hashes to all peers fn propagate_new_hashes(&mut self, chain_info: &BlockChainInfo, io: &mut SyncIo) -> usize { - let updated_peers = self.get_lagging_peers(chain_info, io); + let lucky_peers = self.select_lagging_peers(chain_info, io); let mut sent = 0; let last_parent = HeaderView::new(&io.chain().block_header(BlockID::Hash(chain_info.best_block_hash.clone())).unwrap()).parent_hash(); - for (peer_id, peer_number) in updated_peers { + for (peer_id, peer_number) in lucky_peers { let mut peer_best = self.peers.get(&peer_id).unwrap().latest_hash.clone(); - if chain_info.best_block_number - peer_number > MAX_PEERS_PROPAGATION as BlockNumber { + if chain_info.best_block_number - peer_number > MAX_PEER_LAG_PROPAGATION as BlockNumber { // If we think peer is too far behind just send one latest hash peer_best = last_parent.clone(); } diff --git a/sync/src/tests/chain.rs b/sync/src/tests/chain.rs index 2e3ec1f4c..09e83e358 100644 --- a/sync/src/tests/chain.rs +++ b/sync/src/tests/chain.rs @@ -159,7 +159,7 @@ fn propagate_hashes() { #[test] fn propagate_blocks() { - let mut net = TestNet::new(2); + let mut net = TestNet::new(20); net.peer_mut(1).chain.add_blocks(10, EachBlockWith::Uncle); net.sync(); @@ -169,7 +169,8 @@ fn propagate_blocks() { assert!(!net.peer(0).queue.is_empty()); // NEW_BLOCK_PACKET - assert_eq!(0x07, net.peer(0).queue[0].packet_id); + let blocks = net.peer(0).queue.iter().filter(|p| p.packet_id == 0x7).count(); + assert!(blocks > 0); } #[test] From b036f1de98bd82648033d4fc8be5f56aadd95054 Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Tue, 31 May 2016 10:31:36 +0200 Subject: [PATCH 06/30] stop eth_syncing from returning true forever (#1181) --- rpc/src/v1/impls/eth.rs | 6 ++++-- rpc/src/v1/tests/mocked/eth.rs | 11 +++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/rpc/src/v1/impls/eth.rs b/rpc/src/v1/impls/eth.rs index 100770ae1..a57fc333c 100644 --- a/rpc/src/v1/impls/eth.rs +++ b/rpc/src/v1/impls/eth.rs @@ -244,12 +244,14 @@ impl Eth for EthClient where let res = match status.state { SyncState::Idle => SyncStatus::None, SyncState::Waiting | SyncState::Blocks | SyncState::NewBlocks | SyncState::ChainHead => { + let current_block = U256::from(take_weak!(self.client).chain_info().best_block_number); + let info = SyncInfo { starting_block: U256::from(status.start_block_number), - current_block: U256::from(take_weak!(self.client).chain_info().best_block_number), + current_block: current_block, highest_block: U256::from(status.highest_block_number.unwrap_or(status.start_block_number)) }; - match info.highest_block > info.starting_block + U256::from(6) { + match info.highest_block > info.current_block + U256::from(6) { true => SyncStatus::Info(info), false => SyncStatus::None, } diff --git a/rpc/src/v1/tests/mocked/eth.rs b/rpc/src/v1/tests/mocked/eth.rs index aa0955751..c51d6d7da 100644 --- a/rpc/src/v1/tests/mocked/eth.rs +++ b/rpc/src/v1/tests/mocked/eth.rs @@ -107,6 +107,7 @@ fn rpc_eth_syncing() { status.state = SyncState::Blocks; status.highest_block_number = Some(2500); + // "sync" to 1000 blocks. // causes TestBlockChainClient to return 1000 for its best block number. let mut blocks = tester.client.blocks.write().unwrap(); for i in 0..1000 { @@ -116,6 +117,16 @@ fn rpc_eth_syncing() { let true_res = r#"{"jsonrpc":"2.0","result":{"currentBlock":"0x03e8","highestBlock":"0x09c4","startingBlock":"0x00"},"id":1}"#; assert_eq!(tester.io.handle_request(request), Some(true_res.to_owned())); + + { + // finish "syncing" + let mut blocks = tester.client.blocks.write().unwrap(); + for i in 0..1500 { + blocks.insert(H256::from(i + 1000), Vec::new()); + } + } + + assert_eq!(tester.io.handle_request(request), Some(false_res.to_owned())); } #[test] From d0b32f8d426398297bc5d07d859a27b6e7c5d80b Mon Sep 17 00:00:00 2001 From: Nikolay Volf Date: Tue, 31 May 2016 13:02:53 +0200 Subject: [PATCH 07/30] trait import from branch --- db/src/traits.rs | 95 +++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 77 insertions(+), 18 deletions(-) diff --git a/db/src/traits.rs b/db/src/traits.rs index 1e5b2acf4..dd5743fe5 100644 --- a/db/src/traits.rs +++ b/db/src/traits.rs @@ -1,21 +1,38 @@ +// 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 . + //! Ethcore database trait -use ipc::BinaryConvertable; use std::mem; use ipc::binary::BinaryConvertError; use std::collections::VecDeque; +use std::cell::RefCell; -pub type TransactionHandle = u32; pub type IteratorHandle = u32; +pub const DEFAULT_CACHE_LEN: usize = 12288; + #[derive(Binary)] pub struct KeyValue { pub key: Vec, pub value: Vec, } -#[derive(Debug, Binary)] -pub enum Error { + #[derive(Debug, Binary)] + pub enum Error { AlreadyOpen, IsClosed, RocksDb(String), @@ -28,13 +45,36 @@ pub enum Error { #[derive(Binary)] pub struct DatabaseConfig { /// Optional prefix size in bytes. Allows lookup by partial key. - pub prefix_size: Option + pub prefix_size: Option, + /// write cache length + pub cache: usize, } -pub trait DatabaseService { +impl Default for DatabaseConfig { + fn default() -> DatabaseConfig { + DatabaseConfig { + prefix_size: None, + cache: DEFAULT_CACHE_LEN, + } + } +} + +impl DatabaseConfig { + fn with_prefix(prefix: usize) -> DatabaseConfig { + DatabaseConfig { + prefix_size: Some(prefix), + cache: DEFAULT_CACHE_LEN, + } + } +} + + pub trait DatabaseService : Sized { /// Opens database in the specified path fn open(&self, config: DatabaseConfig, path: String) -> Result<(), Error>; + /// Opens database in the specified path with the default config + fn open_default(&self, path: String) -> Result<(), Error>; + /// Closes database fn close(&self) -> Result<(), Error>; @@ -44,18 +84,6 @@ pub trait DatabaseService { /// Delete value by key. fn delete(&self, key: &[u8]) -> Result<(), Error>; - /// Insert a key-value pair in the transaction. Any existing value value will be overwritten. - fn transaction_put(&self, transaction: TransactionHandle, key: &[u8], value: &[u8]) -> Result<(), Error>; - - /// Delete value by key using transaction - fn transaction_delete(&self, transaction: TransactionHandle, key: &[u8]) -> Result<(), Error>; - - /// Commit transaction to database. - fn write(&self, tr: TransactionHandle) -> Result<(), Error>; - - /// Initiate new transaction on database - fn new_transaction(&self) -> TransactionHandle; - /// Get value by key. fn get(&self, key: &[u8]) -> Result>, Error>; @@ -70,4 +98,35 @@ pub trait DatabaseService { /// Next key-value for the the given iterator fn iter_next(&self, iterator: IteratorHandle) -> Option; + + /// Dispose iteration that is no longer needed + fn dispose_iter(&self, handle: IteratorHandle) -> Result<(), Error>; + + /// Write client transaction + fn write(&self, transaction: DBTransaction) -> Result<(), Error>; +} + +#[derive(Binary)] +pub struct DBTransaction { + pub writes: RefCell>, + pub removes: RefCell>>, +} + +impl DBTransaction { + pub fn new() -> DBTransaction { + DBTransaction { + writes: RefCell::new(Vec::new()), + removes: RefCell::new(Vec::new()), + } + } + + pub fn put(&self, key: &[u8], value: &[u8]) { + let mut brw = self.writes.borrow_mut(); + brw.push(KeyValue { key: key.to_vec(), value: value.to_vec() }); + } + + pub fn delete(&self, key: &[u8]) { + let mut brw = self.removes.borrow_mut(); + brw.push(key.to_vec()); + } } From 134f48cdfb211c971fc9db8edf7d150bb8e79ded Mon Sep 17 00:00:00 2001 From: Nikolay Volf Date: Tue, 31 May 2016 13:05:43 +0200 Subject: [PATCH 08/30] lib import --- db/src/lib.rs.in | 64 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/db/src/lib.rs.in b/db/src/lib.rs.in index 694b4e3e1..4fa43b977 100644 --- a/db/src/lib.rs.in +++ b/db/src/lib.rs.in @@ -19,7 +19,71 @@ extern crate rocksdb; extern crate ethcore_devtools as devtools; extern crate semver; extern crate ethcore_ipc_nano as nanoipc; +extern crate nanomsg; +extern crate crossbeam; extern crate ethcore_util as util; pub mod database; pub mod traits; + +pub use traits::{DatabaseService, DBTransaction, Error}; +pub use database::{Database, DatabaseClient, DatabaseIterator}; + +use std::sync::Arc; +use std::sync::atomic::*; +use std::path::PathBuf; + +pub type DatabaseNanoClient = DatabaseClient<::nanomsg::Socket>; +pub type DatabaseConnection = nanoipc::GuardedSocket; + +#[derive(Debug)] +pub enum ServiceError { + Io(std::io::Error), + Socket(nanoipc::SocketError), +} + +impl std::convert::From for ServiceError { + fn from(io_error: std::io::Error) -> ServiceError { ServiceError::Io(io_error) } +} + +impl std::convert::From for ServiceError { + fn from(socket_error: nanoipc::SocketError) -> ServiceError { ServiceError::Socket(socket_error) } +} + +pub fn blocks_service_url(db_path: &str) -> Result { + let mut path = PathBuf::from(db_path); + try!(::std::fs::create_dir_all(db_path)); + path.push("blocks.ipc"); + Ok(format!("ipc://{}", path.to_str().unwrap())) +} + +pub fn extras_service_url(db_path: &str) -> Result { + let mut path = PathBuf::from(db_path); + try!(::std::fs::create_dir_all(db_path)); + path.push("extras.ipc"); + Ok(format!("ipc://{}", path.to_str().unwrap())) +} + +pub fn blocks_client(db_path: &str) -> Result { + let url = try!(blocks_service_url(db_path)); + let client = try!(nanoipc::init_client::>(&url)); + Ok(client) +} + +pub fn extras_client(db_path: &str) -> Result { + let url = try!(extras_service_url(db_path)); + let client = try!(nanoipc::init_client::>(&url)); + Ok(client) +} + +// for tests +pub fn run_worker(scope: &crossbeam::Scope, stop: Arc, socket_path: &str) { + let socket_path = socket_path.to_owned(); + scope.spawn(move || { + let mut worker = nanoipc::Worker::new(&Arc::new(Database::new())); + worker.add_reqrep(&socket_path).unwrap(); + while !stop.load(Ordering::Relaxed) { + worker.poll(); + } + }); +} From 1d5f407a298bf2cdd27730a8558bc9e45d28f3ff Mon Sep 17 00:00:00 2001 From: Nikolay Volf Date: Tue, 31 May 2016 13:08:15 +0200 Subject: [PATCH 09/30] database & write que import --- db/src/database.rs | 399 ++++++++++++++++++++++++++++++++++----------- db/src/service.rs | 0 2 files changed, 301 insertions(+), 98 deletions(-) delete mode 100644 db/src/service.rs diff --git a/db/src/database.rs b/db/src/database.rs index 4abc98467..d535f1f56 100644 --- a/db/src/database.rs +++ b/db/src/database.rs @@ -18,15 +18,13 @@ use traits::*; use rocksdb::{DB, Writable, WriteBatch, IteratorMode, DBIterator, - IndexType, Options, DBCompactionStyle, BlockBasedOptions, Direction}; -use std::collections::BTreeMap; -use std::sync::{RwLock}; +IndexType, Options, DBCompactionStyle, BlockBasedOptions, Direction}; +use std::sync::{RwLock, Arc}; use std::convert::From; use ipc::IpcConfig; -use std::ops::*; use std::mem; use ipc::binary::BinaryConvertError; -use std::collections::VecDeque; +use std::collections::{VecDeque, HashMap, BTreeMap}; impl From for Error { fn from(s: String) -> Error { @@ -34,20 +32,136 @@ impl From for Error { } } +enum WriteCacheEntry { + Remove, + Write(Vec), +} + +pub struct WriteCache { + entries: HashMap, WriteCacheEntry>, + preferred_len: usize, +} + +const FLUSH_BATCH_SIZE: usize = 4096; + +impl WriteCache { + fn new(cache_len: usize) -> WriteCache { + WriteCache { + entries: HashMap::new(), + preferred_len: cache_len, + } + } + + fn write(&mut self, key: Vec, val: Vec) { + self.entries.insert(key, WriteCacheEntry::Write(val)); + } + + fn remove(&mut self, key: Vec) { + self.entries.insert(key, WriteCacheEntry::Remove); + } + + fn get(&self, key: &Vec) -> Option> { + self.entries.get(key).and_then( + |vec_ref| match vec_ref { + &WriteCacheEntry::Write(ref val) => Some(val.clone()), + &WriteCacheEntry::Remove => None + }) + } + + /// WriteCache should be locked for this + fn flush(&mut self, db: &DB, amount: usize) -> Result<(), Error> { + let batch = WriteBatch::new(); + let mut removed_so_far = 0; + while removed_so_far < amount { + if self.entries.len() == 0 { break; } + let removed_key = { + let (key, cache_entry) = self.entries.iter().nth(0) + .expect("if entries.len == 0, we should have break in the loop, still we got here somehow"); + + match *cache_entry { + WriteCacheEntry::Write(ref val) => { + try!(batch.put(&key, val)); + }, + WriteCacheEntry::Remove => { + try!(batch.delete(&key)); + }, + } + key.clone() + }; + + self.entries.remove(&removed_key); + + removed_so_far = removed_so_far + 1; + } + if removed_so_far > 0 { + try!(db.write(batch)); + } + Ok(()) + } + + /// flushes until cache is empty + fn flush_all(&mut self, db: &DB) -> Result<(), Error> { + while !self.is_empty() { try!(self.flush(db, FLUSH_BATCH_SIZE)); } + Ok(()) + } + + fn is_empty(&self) -> bool { + self.entries.is_empty() + } + + fn try_shrink(&mut self, db: &DB) -> Result<(), Error> { + if self.entries.len() > self.preferred_len { + try!(self.flush(db, FLUSH_BATCH_SIZE)); + } + Ok(()) + } +} + pub struct Database { db: RwLock>, - transactions: RwLock>, + /// Iterators - dont't use between threads! iterators: RwLock>, + write_cache: RwLock, } +unsafe impl Send for Database {} +unsafe impl Sync for Database {} + impl Database { pub fn new() -> Database { Database { db: RwLock::new(None), - transactions: RwLock::new(BTreeMap::new()), iterators: RwLock::new(BTreeMap::new()), + write_cache: RwLock::new(WriteCache::new(DEFAULT_CACHE_LEN)), } } + + pub fn flush(&self) -> Result<(), Error> { + let mut cache_lock = self.write_cache.write().unwrap(); + let db_lock = self.db.read().unwrap(); + if db_lock.is_none() { return Ok(()); } + let db = db_lock.as_ref().unwrap(); + + try!(cache_lock.try_shrink(&db)); + Ok(()) + } + + pub fn flush_all(&self) -> Result<(), Error> { + let mut cache_lock = self.write_cache.write().unwrap(); + let db_lock = self.db.read().unwrap(); + if db_lock.is_none() { return Ok(()); } + let db = db_lock.as_ref().expect("we should have exited with Ok(()) on the previous step"); + + try!(cache_lock.flush_all(&db)); + Ok(()) + + } +} + +impl Drop for Database { + fn drop(&mut self) { + self.flush().unwrap(); + } } #[derive(Ipc)] @@ -72,51 +186,64 @@ impl DatabaseService for Database { Ok(()) } + /// Opens database in the specified path with the default config + fn open_default(&self, path: String) -> Result<(), Error> { + self.open(DatabaseConfig::default(), path) + } + fn close(&self) -> Result<(), Error> { + try!(self.flush_all()); + let mut db = self.db.write().unwrap(); if db.is_none() { return Err(Error::IsClosed); } - // TODO: wait for transactions to expire/close here? - if self.transactions.read().unwrap().len() > 0 { return Err(Error::UncommitedTransactions); } - *db = None; Ok(()) } fn put(&self, key: &[u8], value: &[u8]) -> Result<(), Error> { - let db_lock = self.db.read().unwrap(); - let db = try!(db_lock.as_ref().ok_or(Error::IsClosed)); - - try!(db.put(key, value)); + let mut cache_lock = self.write_cache.write().unwrap(); + cache_lock.write(key.to_vec(), value.to_vec()); Ok(()) } fn delete(&self, key: &[u8]) -> Result<(), Error> { - let db_lock = self.db.read().unwrap(); - let db = try!(db_lock.as_ref().ok_or(Error::IsClosed)); - - try!(db.delete(key)); + let mut cache_lock = self.write_cache.write().unwrap(); + cache_lock.remove(key.to_vec()); Ok(()) } - fn write(&self, handle: TransactionHandle) -> Result<(), Error> { - let db_lock = self.db.read().unwrap(); - let db = try!(db_lock.as_ref().ok_or(Error::IsClosed)); + fn write(&self, transaction: DBTransaction) -> Result<(), Error> { + let mut cache_lock = self.write_cache.write().unwrap(); - let mut transactions = self.transactions.write().unwrap(); - let batch = try!( - transactions.remove(&handle).ok_or(Error::TransactionUnknown) - ); - try!(db.write(batch)); + let mut writes = transaction.writes.borrow_mut(); + for kv in writes.drain(..) { + cache_lock.write(kv.key, kv.value); + } + + let mut removes = transaction.removes.borrow_mut(); + for k in removes.drain(..) { + cache_lock.remove(k); + } Ok(()) } fn get(&self, key: &[u8]) -> Result>, Error> { + { + let key_vec = key.to_vec(); + let cache_hit = self.write_cache.read().unwrap().get(&key_vec); + + if cache_hit.is_some() { + return Ok(Some(cache_hit.expect("cache_hit.is_some() = true, still there is none somehow here"))) + } + } let db_lock = self.db.read().unwrap(); let db = try!(db_lock.as_ref().ok_or(Error::IsClosed)); match try!(db.get(key)) { - Some(db_vec) => Ok(Some(db_vec.to_vec())), + Some(db_vec) => { + Ok(Some(db_vec.to_vec())) + }, None => Ok(None), } } @@ -166,37 +293,35 @@ impl DatabaseService for Database { }) } - fn transaction_put(&self, transaction: TransactionHandle, key: &[u8], value: &[u8]) -> Result<(), Error> - { - let mut transactions = self.transactions.write().unwrap(); - let batch = try!( - transactions.get_mut(&transaction).ok_or(Error::TransactionUnknown) - ); - try!(batch.put(&key, &value)); + fn dispose_iter(&self, handle: IteratorHandle) -> Result<(), Error> { + let mut iterators = self.iterators.write().unwrap(); + iterators.remove(&handle); Ok(()) } - - fn transaction_delete(&self, transaction: TransactionHandle, key: &[u8]) -> Result<(), Error> { - let mut transactions = self.transactions.write().unwrap(); - let batch = try!( - transactions.get_mut(&transaction).ok_or(Error::TransactionUnknown) - ); - try!(batch.delete(&key)); - Ok(()) - } - - fn new_transaction(&self) -> TransactionHandle { - let mut transactions = self.transactions.write().unwrap(); - let next_transaction = transactions.keys().last().unwrap_or(&0) + 1; - transactions.insert(next_transaction, WriteBatch::new()); - - next_transaction - } } // TODO : put proper at compile-time impl IpcConfig for Database {} +/// Database iterator +pub struct DatabaseIterator { + client: Arc>, + handle: IteratorHandle, +} + +impl Iterator for DatabaseIterator { + type Item = (Vec, Vec); + + fn next(&mut self) -> Option { + self.client.iter_next(self.handle).and_then(|kv| Some((kv.key, kv.value))) + } +} + +impl Drop for DatabaseIterator { + fn drop(&mut self) { + self.client.dispose_iter(self.handle).unwrap(); + } +} #[cfg(test)] mod test { @@ -215,7 +340,7 @@ mod test { fn can_be_open_empty() { let db = Database::new(); let path = RandomTempPath::create_dir(); - db.open(DatabaseConfig { prefix_size: Some(8) }, path.as_str().to_owned()).unwrap(); + db.open_default(path.as_str().to_owned()).unwrap(); assert!(db.is_empty().is_ok()); } @@ -224,9 +349,10 @@ mod test { fn can_store_key() { let db = Database::new(); let path = RandomTempPath::create_dir(); - db.open(DatabaseConfig { prefix_size: None }, path.as_str().to_owned()).unwrap(); + db.open_default(path.as_str().to_owned()).unwrap(); db.put("xxx".as_bytes(), "1".as_bytes()).unwrap(); + db.flush_all().unwrap(); assert!(!db.is_empty().unwrap()); } @@ -234,15 +360,37 @@ mod test { fn can_retrieve() { let db = Database::new(); let path = RandomTempPath::create_dir(); - db.open(DatabaseConfig { prefix_size: None }, path.as_str().to_owned()).unwrap(); + db.open_default(path.as_str().to_owned()).unwrap(); db.put("xxx".as_bytes(), "1".as_bytes()).unwrap(); db.close().unwrap(); - db.open(DatabaseConfig { prefix_size: None }, path.as_str().to_owned()).unwrap(); + db.open_default(path.as_str().to_owned()).unwrap(); assert_eq!(db.get("xxx".as_bytes()).unwrap().unwrap(), "1".as_bytes().to_vec()); } } +#[cfg(test)] +mod write_cache_tests { + use super::Database; + use traits::*; + use devtools::*; + + #[test] + fn cache_write_flush() { + let db = Database::new(); + let path = RandomTempPath::create_dir(); + + db.open_default(path.as_str().to_owned()).unwrap(); + db.put("100500".as_bytes(), "1".as_bytes()).unwrap(); + db.delete("100500".as_bytes()).unwrap(); + db.flush_all().unwrap(); + + let val = db.get("100500".as_bytes()).unwrap(); + assert!(val.is_none()); + } + +} + #[cfg(test)] mod client_tests { use super::{DatabaseClient, Database}; @@ -251,6 +399,8 @@ mod client_tests { use nanoipc; use std::sync::Arc; use std::sync::atomic::{Ordering, AtomicBool}; + use crossbeam; + use run_worker; fn init_worker(addr: &str) -> nanoipc::Worker { let mut worker = nanoipc::Worker::::new(&Arc::new(Database::new())); @@ -268,7 +418,7 @@ mod client_tests { ::std::thread::spawn(move || { let mut worker = init_worker(url); - while !c_worker_should_exit.load(Ordering::Relaxed) { + while !c_worker_should_exit.load(Ordering::Relaxed) { worker.poll(); c_worker_is_ready.store(true, Ordering::Relaxed); } @@ -295,7 +445,7 @@ mod client_tests { ::std::thread::spawn(move || { let mut worker = init_worker(url); - while !c_worker_should_exit.load(Ordering::Relaxed) { + while !c_worker_should_exit.load(Ordering::Relaxed) { worker.poll(); c_worker_is_ready.store(true, Ordering::Relaxed); } @@ -304,7 +454,7 @@ mod client_tests { while !worker_is_ready.load(Ordering::Relaxed) { } let client = nanoipc::init_duplex_client::>(url).unwrap(); - client.open(DatabaseConfig { prefix_size: Some(8) }, path.as_str().to_owned()).unwrap(); + client.open_default(path.as_str().to_owned()).unwrap(); assert!(client.is_empty().unwrap()); worker_should_exit.store(true, Ordering::Relaxed); } @@ -314,27 +464,16 @@ mod client_tests { let url = "ipc:///tmp/parity-db-ipc-test-30.ipc"; let path = RandomTempPath::create_dir(); - let worker_should_exit = Arc::new(AtomicBool::new(false)); - let worker_is_ready = Arc::new(AtomicBool::new(false)); - let c_worker_should_exit = worker_should_exit.clone(); - let c_worker_is_ready = worker_is_ready.clone(); + crossbeam::scope(move |scope| { + let stop = Arc::new(AtomicBool::new(false)); + run_worker(scope, stop.clone(), url); + let client = nanoipc::init_client::>(url).unwrap(); + client.open_default(path.as_str().to_owned()).unwrap(); + client.put("xxx".as_bytes(), "1".as_bytes()).unwrap(); + client.close().unwrap(); - ::std::thread::spawn(move || { - let mut worker = init_worker(url); - while !c_worker_should_exit.load(Ordering::Relaxed) { - worker.poll(); - c_worker_is_ready.store(true, Ordering::Relaxed); - } + stop.store(true, Ordering::Relaxed); }); - - while !worker_is_ready.load(Ordering::Relaxed) { } - let client = nanoipc::init_duplex_client::>(url).unwrap(); - - client.open(DatabaseConfig { prefix_size: Some(8) }, path.as_str().to_owned()).unwrap(); - client.put("xxx".as_bytes(), "1".as_bytes()).unwrap(); - client.close().unwrap(); - - worker_should_exit.store(true, Ordering::Relaxed); } #[test] @@ -342,29 +481,93 @@ mod client_tests { let url = "ipc:///tmp/parity-db-ipc-test-40.ipc"; let path = RandomTempPath::create_dir(); - let worker_should_exit = Arc::new(AtomicBool::new(false)); - let worker_is_ready = Arc::new(AtomicBool::new(false)); - let c_worker_should_exit = worker_should_exit.clone(); - let c_worker_is_ready = worker_is_ready.clone(); + crossbeam::scope(move |scope| { + let stop = Arc::new(AtomicBool::new(false)); + run_worker(scope, stop.clone(), url); + let client = nanoipc::init_client::>(url).unwrap(); - ::std::thread::spawn(move || { - let mut worker = init_worker(url); - while !c_worker_should_exit.load(Ordering::Relaxed) { - worker.poll(); - c_worker_is_ready.store(true, Ordering::Relaxed); + client.open_default(path.as_str().to_owned()).unwrap(); + client.put("xxx".as_bytes(), "1".as_bytes()).unwrap(); + client.close().unwrap(); + + client.open_default(path.as_str().to_owned()).unwrap(); + assert_eq!(client.get("xxx".as_bytes()).unwrap().unwrap(), "1".as_bytes().to_vec()); + + stop.store(true, Ordering::Relaxed); + }); + } + + #[test] + fn can_read_empty() { + let url = "ipc:///tmp/parity-db-ipc-test-45.ipc"; + let path = RandomTempPath::create_dir(); + + crossbeam::scope(move |scope| { + let stop = Arc::new(AtomicBool::new(false)); + run_worker(scope, stop.clone(), url); + let client = nanoipc::init_client::>(url).unwrap(); + + client.open_default(path.as_str().to_owned()).unwrap(); + assert!(client.get("xxx".as_bytes()).unwrap().is_none()); + + stop.store(true, Ordering::Relaxed); + }); + } + + + #[test] + fn can_commit_client_transaction() { + let url = "ipc:///tmp/parity-db-ipc-test-60.ipc"; + let path = RandomTempPath::create_dir(); + + crossbeam::scope(move |scope| { + let stop = Arc::new(AtomicBool::new(false)); + run_worker(scope, stop.clone(), url); + let client = nanoipc::init_client::>(url).unwrap(); + client.open_default(path.as_str().to_owned()).unwrap(); + + let transaction = DBTransaction::new(); + transaction.put("xxx".as_bytes(), "1".as_bytes()); + client.write(transaction).unwrap(); + + client.close().unwrap(); + + client.open_default(path.as_str().to_owned()).unwrap(); + assert_eq!(client.get("xxx".as_bytes()).unwrap().unwrap(), "1".as_bytes().to_vec()); + + stop.store(true, Ordering::Relaxed); + }); + } + + #[test] + fn key_write_read_ipc() { + let url = "ipc:///tmp/parity-db-ipc-test-70.ipc"; + let path = RandomTempPath::create_dir(); + + crossbeam::scope(|scope| { + let stop = StopGuard::new(); + run_worker(&scope, stop.share(), url); + + let client = nanoipc::init_client::>(url).unwrap(); + + client.open_default(path.as_str().to_owned()).unwrap(); + let mut batch = Vec::new(); + for _ in 0..100 { + batch.push((random_str(256).as_bytes().to_vec(), random_str(256).as_bytes().to_vec())); + batch.push((random_str(256).as_bytes().to_vec(), random_str(2048).as_bytes().to_vec())); + batch.push((random_str(2048).as_bytes().to_vec(), random_str(2048).as_bytes().to_vec())); + batch.push((random_str(2048).as_bytes().to_vec(), random_str(256).as_bytes().to_vec())); + } + + for &(ref k, ref v) in batch.iter() { + client.put(k, v).unwrap(); + } + client.close().unwrap(); + + client.open_default(path.as_str().to_owned()).unwrap(); + for &(ref k, ref v) in batch.iter() { + assert_eq!(v, &client.get(k).unwrap().unwrap()); } }); - - while !worker_is_ready.load(Ordering::Relaxed) { } - let client = nanoipc::init_duplex_client::>(url).unwrap(); - - client.open(DatabaseConfig { prefix_size: Some(8) }, path.as_str().to_owned()).unwrap(); - client.put("xxx".as_bytes(), "1".as_bytes()).unwrap(); - client.close().unwrap(); - - client.open(DatabaseConfig { prefix_size: Some(8) }, path.as_str().to_owned()).unwrap(); - assert_eq!(client.get("xxx".as_bytes()).unwrap().unwrap(), "1".as_bytes().to_vec()); - - worker_should_exit.store(true, Ordering::Relaxed); } } diff --git a/db/src/service.rs b/db/src/service.rs deleted file mode 100644 index e69de29bb..000000000 From a944638b5e51397f6ab439b71fcd93d908c29a84 Mon Sep 17 00:00:00 2001 From: Nikolay Volf Date: Tue, 31 May 2016 13:17:45 +0200 Subject: [PATCH 10/30] version lock --- db/Cargo.toml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/db/Cargo.toml b/db/Cargo.toml index 8bd26d1f9..24d0e6fbe 100644 --- a/db/Cargo.toml +++ b/db/Cargo.toml @@ -8,17 +8,19 @@ authors = ["Ethcore "] build = "build.rs" [build-dependencies] -syntex = "*" +syntex = "0.32" ethcore-ipc-codegen = { path = "../ipc/codegen" } [dependencies] -ethcore-util = { path = "../util" } clippy = { version = "0.0.67", optional = true} ethcore-devtools = { path = "../devtools" } ethcore-ipc = { path = "../ipc/rpc" } rocksdb = { git = "https://github.com/ethcore/rust-rocksdb" } semver = "0.2" ethcore-ipc-nano = { path = "../ipc/nano" } +nanomsg = { git = "https://github.com/ethcore/nanomsg.rs.git" } +crossbeam = "0.2" +ethcore-util = { path = "../util" } [features] dev = ["clippy"] From 1465b0d34c3c18abd12602ec66ac74aa188411c5 Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Mon, 30 May 2016 13:10:33 +0200 Subject: [PATCH 11/30] refactor Miner to not wrap accounts in an RwLock, and to take a generalized AccountProvider --- miner/src/miner.rs | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/miner/src/miner.rs b/miner/src/miner.rs index fc63aec6c..d356a644a 100644 --- a/miner/src/miner.rs +++ b/miner/src/miner.rs @@ -18,14 +18,14 @@ use rayon::prelude::*; use std::sync::atomic::AtomicBool; use util::*; -use util::keys::store::{AccountService, AccountProvider}; +use util::keys::store::AccountProvider; use ethcore::views::{BlockView, HeaderView}; use ethcore::client::{BlockChainClient, BlockID}; use ethcore::block::{ClosedBlock, IsBlock}; use ethcore::error::*; use ethcore::client::{Executive, Executed, EnvInfo, TransactOptions}; use ethcore::transaction::SignedTransaction; -use ethcore::receipt::{Receipt}; +use ethcore::receipt::Receipt; use ethcore::spec::Spec; use ethcore::engine::Engine; use super::{MinerService, MinerStatus, TransactionQueue, AccountDetails, TransactionImportResult, TransactionOrigin}; @@ -44,7 +44,7 @@ pub struct Miner { extra_data: RwLock, spec: Spec, - accounts: RwLock>>, // TODO: this is horrible since AccountService already contains a single RwLock field. refactor. + accounts: Option>, } impl Default for Miner { @@ -58,7 +58,7 @@ impl Default for Miner { gas_floor_target: RwLock::new(U256::zero()), author: RwLock::new(Address::default()), extra_data: RwLock::new(Vec::new()), - accounts: RwLock::new(None), + accounts: None, spec: Spec::new_test(), } } @@ -76,13 +76,13 @@ impl Miner { gas_floor_target: RwLock::new(U256::zero()), author: RwLock::new(Address::default()), extra_data: RwLock::new(Vec::new()), - accounts: RwLock::new(None), + accounts: None, spec: spec, }) } /// Creates new instance of miner - pub fn with_accounts(force_sealing: bool, spec: Spec, accounts: Arc) -> Arc { + pub fn with_accounts(force_sealing: bool, spec: Spec, accounts: Arc) -> Arc { Arc::new(Miner { transaction_queue: Mutex::new(TransactionQueue::new()), force_sealing: force_sealing, @@ -92,7 +92,7 @@ impl Miner { gas_floor_target: RwLock::new(U256::zero()), author: RwLock::new(Address::default()), extra_data: RwLock::new(Vec::new()), - accounts: RwLock::new(Some(accounts)), + accounts: Some(accounts), spec: spec, }) } @@ -177,9 +177,8 @@ impl Miner { if !block.transactions().is_empty() { trace!(target: "miner", "prepare_sealing: block has transaction - attempting internal seal."); // block with transactions - see if we can seal immediately. - let a = self.accounts.read().unwrap(); - let s = self.engine().generate_seal(block.block(), match *a.deref() { - Some(ref x) => Some(x.deref() as &AccountProvider), + let s = self.engine().generate_seal(block.block(), match self.accounts { + Some(ref x) => Some(&**x), None => None, }); if let Some(seal) = s { From 266b4eedaad7d3a9dda7094bf8a7f91341024141 Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Mon, 30 May 2016 17:42:42 +0200 Subject: [PATCH 12/30] correct locked_account_secret docs --- util/src/keys/store.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/util/src/keys/store.rs b/util/src/keys/store.rs index 3ecabc07c..cf5b2a61e 100644 --- a/util/src/keys/store.rs +++ b/util/src/keys/store.rs @@ -87,7 +87,7 @@ struct AccountUnlock { } /// Basic account management trait -pub trait AccountProvider : Send + Sync { +pub trait AccountProvider: Send + Sync { /// Lists all accounts fn accounts(&self) -> Result, ::std::io::Error>; /// Unlocks account with the password provided @@ -325,7 +325,7 @@ impl SecretStore { ret } - /// Returns secret for unlocked account. + /// Returns secret for locked account. pub fn locked_account_secret(&self, account: &Address, pass: &str) -> Result { let secret_id = try!(self.account(&account).ok_or(SigningError::NoAccount)); self.get(&secret_id, pass).or_else(|e| Err(match e { From 5cb58c42694cd1350a18dec3c169f0d34ce89447 Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Mon, 30 May 2016 20:06:10 +0200 Subject: [PATCH 13/30] use Miner in rpc tests, remove chain_harness --- rpc/src/v1/tests/eth.rs | 371 ++++++++++++++++++++++------------------ 1 file changed, 208 insertions(+), 163 deletions(-) diff --git a/rpc/src/v1/tests/eth.rs b/rpc/src/v1/tests/eth.rs index 80a856aca..9b2cb3195 100644 --- a/rpc/src/v1/tests/eth.rs +++ b/rpc/src/v1/tests/eth.rs @@ -19,12 +19,11 @@ use std::collections::HashMap; use std::sync::Arc; use std::str::FromStr; -use ethcore::client::{BlockChainClient, Client, ClientConfig}; -use ethcore::spec::Genesis; +use ethcore::client::{Client, BlockChainClient, ClientConfig}; +use ethcore::spec::{Genesis, Spec}; use ethcore::block::Block; use ethcore::ethereum; -use ethcore::transaction::{Transaction, Action}; -use ethminer::{MinerService, ExternalMiner}; +use ethminer::{Miner, MinerService, ExternalMiner}; use devtools::RandomTempPath; use util::io::IoChannel; use util::hash::Address; @@ -35,135 +34,7 @@ use ethjson::blockchain::BlockChain; use v1::traits::eth::Eth; use v1::impls::EthClient; -use v1::tests::helpers::{TestSyncProvider, Config, TestMinerService}; - -struct EthTester { - _client: Arc, - _miner: Arc, - accounts: Arc, - 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::tests::helpers::{TestSyncProvider, Config}; fn account_provider() -> Arc { let mut accounts = HashMap::new(); @@ -179,51 +50,225 @@ fn sync_provider() -> Arc { })) } -fn miner_service() -> Arc { - Arc::new(TestMinerService::default()) +fn miner_service(spec: Spec, accounts: Arc) -> Arc { + 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(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 { + _miner: Arc, + client: Arc, + accounts: Arc, + 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()); + } } + + assert!(tester.client.chain_info().best_block_hash == chain.best_block.into()); + tester } - assert!(client.chain_info().best_block_hash == chain.best_block.into()); + fn from_spec_provider(spec_provider: F) -> Self + where F: Fn() -> Spec { - let eth_client = EthClient::new(&client, &sync_provider, &account_provider, - &miner_service, &external_miner); + let dir = RandomTempPath::new(); + let client = Client::new(ClientConfig::default(), spec_provider(), dir.as_path(), IoChannel::disconnected()).unwrap(); + let sync_provider = sync_provider(); + let account_provider = account_provider(); + let miner_service = miner_service(spec_provider(), account_provider.clone()); + let external_miner = Arc::new(ExternalMiner::default()); - let handler = IoHandler::new(); - let delegate = eth_client.to_delegate(); - handler.add_delegate(delegate); + let eth_client = EthClient::new(&client, &sync_provider, &account_provider, + &miner_service, &external_miner); - let tester = EthTester { - _miner: miner_service, - _client: client, - accounts: account_provider, - handler: handler, - }; + let handler = IoHandler::new(); + let delegate = eth_client.to_delegate(); + handler.add_delegate(delegate); - cb(&tester) + 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" } + } +} +"#; + +#[cfg(test)] +#[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); +} \ No newline at end of file From b729a381f8f926786dbe4d6a92bd50c6566eb0a1 Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Tue, 31 May 2016 16:29:53 +0200 Subject: [PATCH 14/30] rewrite map macros not to use an intermediate allocation --- util/src/common.rs | 40 ++++++++++++++++++++++++++++------------ 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/util/src/common.rs b/util/src/common.rs index a4ba41f82..7ed660f29 100644 --- a/util/src/common.rs +++ b/util/src/common.rs @@ -26,30 +26,46 @@ pub use sha3::*; #[macro_export] macro_rules! hash_map { - ( $( $x:expr => $y:expr ),* ) => { - vec![ $( ($x, $y) ),* ].into_iter().collect::>() - } + ( $( $x:expr => $y:expr ),* ) => {{ + let mut x = HashMap::new(); + $( + x.insert($x, $y); + )* + x + }} } #[macro_export] macro_rules! hash_mapx { - ( $( $x:expr => $y:expr ),* ) => { - vec![ $( ( From::from($x), From::from($y) ) ),* ].into_iter().collect::>() - } + ( $( $x:expr => $y:expr ),* ) => {{ + let mut x = HashMap::new(); + $( + x.insert($x.into(), $y.into()); + )* + x + }} } #[macro_export] macro_rules! map { - ( $( $x:expr => $y:expr ),* ) => { - vec![ $( ($x, $y) ),* ].into_iter().collect::>() - } + ( $( $x:expr => $y:expr ),* ) => {{ + let mut x = BTreeMap::new(); + $( + x.insert($x, $y); + )* + x + }} } #[macro_export] macro_rules! mapx { - ( $( $x:expr => $y:expr ),* ) => { - vec![ $( ( From::from($x), From::from($y) ) ),* ].into_iter().collect::>() - } + ( $( $x:expr => $y:expr ),* ) => {{ + let mut x = BTreeMap::new(); + $( + x.insert($x.into(), $y.into()); + )* + x + }} } #[macro_export] From 3788b3a149614a3d61cb72d0a3fb41e3f6221c0a Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Tue, 31 May 2016 16:40:48 +0200 Subject: [PATCH 15/30] expunge x! from util --- util/src/hash.rs | 2 +- util/src/json_aid.rs | 2 +- util/src/keys/store.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/util/src/hash.rs b/util/src/hash.rs index ba96f812c..1a16cefa4 100644 --- a/util/src/hash.rs +++ b/util/src/hash.rs @@ -718,7 +718,7 @@ mod tests { #[test] fn from_and_to_u256() { - let u: U256 = x!(0x123456789abcdef0u64); + let u: U256 = 0x123456789abcdef0u64.into(); let h = H256::from(u); assert_eq!(H256::from(u), H256::from("000000000000000000000000000000000000000000000000123456789abcdef0")); let h_ref = H256::from(&u); diff --git a/util/src/json_aid.rs b/util/src/json_aid.rs index 6aed7f09c..7bf940b99 100644 --- a/util/src/json_aid.rs +++ b/util/src/json_aid.rs @@ -48,7 +48,7 @@ impl FromJson for Bytes { impl FromJson for BTreeMap { fn from_json(json: &Json) -> Self { match *json { - Json::Object(ref o) => o.iter().map(|(key, value)| (x!(&u256_from_str(key)), x!(&U256::from_json(value)))).collect(), + Json::Object(ref o) => o.iter().map(|(key, value)| (u256_from_str(key).into(), U256::from_json(value).into())).collect(), _ => BTreeMap::new(), } } diff --git a/util/src/keys/store.rs b/util/src/keys/store.rs index 3ecabc07c..296f7ad37 100644 --- a/util/src/keys/store.rs +++ b/util/src/keys/store.rs @@ -554,7 +554,7 @@ mod tests { H256::from_str("517ead924a9d0dc3124507e3393d175ce3ff7c1e96529c6c555ce9e51205e9b2").unwrap(), 262144, 32)); - key_file.account = Some(x!(i as u64)); + key_file.account = Some((i as u64).into()); result.push(key_file.id.clone()); write_sstore.import_key(key_file).unwrap(); } From ff7c755930c35205e80b2a5f48ff3c731d838c83 Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Tue, 31 May 2016 16:59:01 +0200 Subject: [PATCH 16/30] mostly purge x! from ethcore --- ethcore/src/account.rs | 18 +- ethcore/src/account_db.rs | 4 +- ethcore/src/basic_authority.rs | 14 +- ethcore/src/block.rs | 8 +- ethcore/src/block_queue.rs | 6 +- ethcore/src/client/client.rs | 4 +- ethcore/src/env_info.rs | 14 +- ethcore/src/ethereum/ethash.rs | 28 +-- ethcore/src/executive.rs | 20 +- ethcore/src/externalities.rs | 8 +- ethcore/src/pod_account.rs | 38 +-- ethcore/src/state.rs | 414 ++++++++++++++++----------------- ethcore/src/substate.rs | 6 +- ethcore/src/tests/client.rs | 2 +- ethcore/src/tests/helpers.rs | 6 +- ethcore/src/types/receipt.rs | 6 +- 16 files changed, 298 insertions(+), 298 deletions(-) diff --git a/ethcore/src/account.rs b/ethcore/src/account.rs index 66cceda42..7ba213393 100644 --- a/ethcore/src/account.rs +++ b/ethcore/src/account.rs @@ -261,7 +261,7 @@ mod tests { let mut db = MemoryDB::new(); let mut db = AccountDBMut::new(&mut db, &Address::new()); let rlp = { - let mut a = Account::new_contract(x!(69), x!(0)); + let mut a = Account::new_contract(69.into(), 0.into()); a.set_storage(H256::from(&U256::from(0x00u64)), H256::from(&U256::from(0x1234u64))); a.commit_storage(&mut db); a.init_code(vec![]); @@ -281,7 +281,7 @@ mod tests { let mut db = AccountDBMut::new(&mut db, &Address::new()); let rlp = { - let mut a = Account::new_contract(x!(69), x!(0)); + let mut a = Account::new_contract(69.into(), 0.into()); a.init_code(vec![0x55, 0x44, 0xffu8]); a.commit_code(&mut db); a.rlp() @@ -296,10 +296,10 @@ mod tests { #[test] fn commit_storage() { - let mut a = Account::new_contract(x!(69), x!(0)); + let mut a = Account::new_contract(69.into(), 0.into()); let mut db = MemoryDB::new(); let mut db = AccountDBMut::new(&mut db, &Address::new()); - a.set_storage(x!(0), x!(0x1234)); + a.set_storage(0.into(), 0x1234.into()); assert_eq!(a.storage_root(), None); a.commit_storage(&mut db); assert_eq!(a.storage_root().unwrap().hex(), "c57e1afb758b07f8d2c8f13a3b6e44fa5ff94ab266facc5a4fd3f062426e50b2"); @@ -307,21 +307,21 @@ mod tests { #[test] fn commit_remove_commit_storage() { - let mut a = Account::new_contract(x!(69), x!(0)); + let mut a = Account::new_contract(69.into(), 0.into()); let mut db = MemoryDB::new(); let mut db = AccountDBMut::new(&mut db, &Address::new()); - a.set_storage(x!(0), x!(0x1234)); + a.set_storage(0.into(), 0x1234.into()); a.commit_storage(&mut db); - a.set_storage(x!(1), x!(0x1234)); + a.set_storage(1.into(), 0x1234.into()); a.commit_storage(&mut db); - a.set_storage(x!(1), x!(0)); + a.set_storage(1.into(), 0.into()); a.commit_storage(&mut db); assert_eq!(a.storage_root().unwrap().hex(), "c57e1afb758b07f8d2c8f13a3b6e44fa5ff94ab266facc5a4fd3f062426e50b2"); } #[test] fn commit_code() { - let mut a = Account::new_contract(x!(69), x!(0)); + let mut a = Account::new_contract(69.into(), 0.into()); let mut db = MemoryDB::new(); let mut db = AccountDBMut::new(&mut db, &Address::new()); a.init_code(vec![0x55, 0x44, 0xffu8]); diff --git a/ethcore/src/account_db.rs b/ethcore/src/account_db.rs index f95ec53a1..c21ea2993 100644 --- a/ethcore/src/account_db.rs +++ b/ethcore/src/account_db.rs @@ -20,7 +20,7 @@ impl<'db> AccountDB<'db> { pub fn new(db: &'db HashDB, address: &Address) -> AccountDB<'db> { AccountDB { db: db, - address: x!(address), + address: address.into(), } } } @@ -67,7 +67,7 @@ impl<'db> AccountDBMut<'db> { pub fn new(db: &'db mut HashDB, address: &Address) -> AccountDBMut<'db> { AccountDBMut { db: db, - address: x!(address), + address: address.into(), } } diff --git a/ethcore/src/basic_authority.rs b/ethcore/src/basic_authority.rs index fec23cf54..b4a938642 100644 --- a/ethcore/src/basic_authority.rs +++ b/ethcore/src/basic_authority.rs @@ -86,9 +86,9 @@ impl Engine for BasicAuthority { let gas_limit = parent.gas_limit; let bound_divisor = self.our_params.gas_limit_bound_divisor; if gas_limit < gas_floor_target { - min(gas_floor_target, gas_limit + gas_limit / bound_divisor - x!(1)) + min(gas_floor_target, gas_limit + gas_limit / bound_divisor - 1.into()) } else { - max(gas_floor_target, gas_limit - gas_limit / bound_divisor + x!(1)) + max(gas_floor_target, gas_limit - gas_limit / bound_divisor + 1.into()) } }; header.note_dirty(); @@ -211,12 +211,12 @@ mod tests { let engine = new_test_authority().engine; let schedule = engine.schedule(&EnvInfo { number: 10000000, - author: x!(0), + author: 0.into(), timestamp: 0, - difficulty: x!(0), + difficulty: 0.into(), last_hashes: vec![], - gas_used: x!(0), - gas_limit: x!(0) + gas_used: 0.into(), + gas_limit: 0.into() }); assert!(schedule.stack_limit > 0); @@ -278,7 +278,7 @@ mod tests { spec.ensure_db_good(db.as_hashdb_mut()); let last_hashes = vec![genesis_header.hash()]; let vm_factory = Default::default(); - let b = OpenBlock::new(engine.deref(), &vm_factory, false, db, &genesis_header, last_hashes, addr.clone(), x!(3141562), vec![]); + let b = OpenBlock::new(engine.deref(), &vm_factory, false, db, &genesis_header, last_hashes, addr.clone(), 3141562.into(), vec![]); let b = b.close_and_lock(); let seal = engine.generate_seal(b.block(), Some(&tap)).unwrap(); diff --git a/ethcore/src/block.rs b/ethcore/src/block.rs index 90d4eec2d..0b75f5a7e 100644 --- a/ethcore/src/block.rs +++ b/ethcore/src/block.rs @@ -469,7 +469,7 @@ pub fn enact(header: &Header, transactions: &[SignedTransaction], uncles: &[Head } } - let mut b = OpenBlock::new(engine, vm_factory, tracing, db, parent, last_hashes, header.author().clone(), x!(3141562), header.extra_data().clone()); + let mut b = OpenBlock::new(engine, vm_factory, tracing, db, parent, last_hashes, header.author().clone(), 3141562.into(), header.extra_data().clone()); b.set_difficulty(*header.difficulty()); b.set_gas_limit(*header.gas_limit()); b.set_timestamp(header.timestamp()); @@ -514,7 +514,7 @@ mod tests { spec.ensure_db_good(db.as_hashdb_mut()); let last_hashes = vec![genesis_header.hash()]; let vm_factory = Default::default(); - let b = OpenBlock::new(engine.deref(), &vm_factory, false, db, &genesis_header, last_hashes, Address::zero(), x!(3141562), vec![]); + let b = OpenBlock::new(engine.deref(), &vm_factory, false, db, &genesis_header, last_hashes, Address::zero(), 3141562.into(), vec![]); let b = b.close_and_lock(); let _ = b.seal(engine.deref(), vec![]); } @@ -530,7 +530,7 @@ mod tests { let mut db = db_result.take(); spec.ensure_db_good(db.as_hashdb_mut()); let vm_factory = Default::default(); - let b = OpenBlock::new(engine.deref(), &vm_factory, false, db, &genesis_header, vec![genesis_header.hash()], Address::zero(), x!(3141562), vec![]).close_and_lock().seal(engine.deref(), vec![]).unwrap(); + let b = OpenBlock::new(engine.deref(), &vm_factory, false, db, &genesis_header, vec![genesis_header.hash()], Address::zero(), 3141562.into(), vec![]).close_and_lock().seal(engine.deref(), vec![]).unwrap(); let orig_bytes = b.rlp_bytes(); let orig_db = b.drain(); @@ -557,7 +557,7 @@ mod tests { let mut db = db_result.take(); spec.ensure_db_good(db.as_hashdb_mut()); let vm_factory = Default::default(); - let mut open_block = OpenBlock::new(engine.deref(), &vm_factory, false, db, &genesis_header, vec![genesis_header.hash()], Address::zero(), x!(3141562), vec![]); + let mut open_block = OpenBlock::new(engine.deref(), &vm_factory, false, db, &genesis_header, vec![genesis_header.hash()], Address::zero(), 3141562.into(), vec![]); let mut uncle1_header = Header::new(); uncle1_header.extra_data = b"uncle1".to_vec(); let mut uncle2_header = Header::new(); diff --git a/ethcore/src/block_queue.rs b/ethcore/src/block_queue.rs index bf11f5f1f..5e89641c0 100644 --- a/ethcore/src/block_queue.rs +++ b/ethcore/src/block_queue.rs @@ -311,17 +311,17 @@ impl BlockQueue { let h = header.hash(); { if self.processing.read().unwrap().contains(&h) { - return Err(x!(ImportError::AlreadyQueued)); + return Err(ImportError::AlreadyQueued.into()); } let mut bad = self.verification.bad.lock().unwrap(); if bad.contains(&h) { - return Err(x!(ImportError::KnownBad)); + return Err(ImportError::KnownBad.into()); } if bad.contains(&header.parent_hash) { bad.insert(h.clone()); - return Err(x!(ImportError::KnownBad)); + return Err(ImportError::KnownBad.into()); } } diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index ab1c21af1..8fb0de9eb 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -655,10 +655,10 @@ impl BlockChainClient for Client where V: Verifier { { let header = BlockView::new(&bytes).header_view(); if self.chain.is_known(&header.sha3()) { - return Err(x!(ImportError::AlreadyInChain)); + return Err(ImportError::AlreadyInChain.into()); } if self.block_status(BlockID::Hash(header.parent_hash())) == BlockStatus::Unknown { - return Err(x!(BlockError::UnknownParent(header.parent_hash()))); + return Err(BlockError::UnknownParent(header.parent_hash()).into()); } } self.block_queue.import_block(bytes) diff --git a/ethcore/src/env_info.rs b/ethcore/src/env_info.rs index 18b8af856..e6d15cee6 100644 --- a/ethcore/src/env_info.rs +++ b/ethcore/src/env_info.rs @@ -47,10 +47,10 @@ impl Default for EnvInfo { number: 0, author: Address::new(), timestamp: 0, - difficulty: x!(0), - gas_limit: x!(0), + difficulty: 0.into(), + gas_limit: 0.into(), last_hashes: vec![], - gas_used: x!(0), + gas_used: 0.into(), } } } @@ -92,15 +92,15 @@ mod tests { assert_eq!(env_info.number, 1112339); assert_eq!(env_info.author, Address::from_str("000000f00000000f000000000000f00000000f00").unwrap()); - assert_eq!(env_info.gas_limit, x!(40000)); - assert_eq!(env_info.difficulty, x!(50000)); - assert_eq!(env_info.gas_used, x!(0)); + assert_eq!(env_info.gas_limit, 40000.into()); + assert_eq!(env_info.difficulty, 50000.into()); + assert_eq!(env_info.gas_used, 0.into()); } #[test] fn it_can_be_created_as_default() { let default_env_info = EnvInfo::default(); - assert_eq!(default_env_info.difficulty, x!(0)); + assert_eq!(default_env_info.difficulty, 0.into()); } } diff --git a/ethcore/src/ethereum/ethash.rs b/ethcore/src/ethereum/ethash.rs index cb06959d0..1dadb8a65 100644 --- a/ethcore/src/ethereum/ethash.rs +++ b/ethcore/src/ethereum/ethash.rs @@ -111,9 +111,9 @@ impl Engine for Ethash { let gas_limit = parent.gas_limit; let bound_divisor = self.ethash_params.gas_limit_bound_divisor; if gas_limit < gas_floor_target { - min(gas_floor_target, gas_limit + gas_limit / bound_divisor - x!(1)) + min(gas_floor_target, gas_limit + gas_limit / bound_divisor - 1.into()) } else { - max(gas_floor_target, gas_limit - gas_limit / bound_divisor + x!(1) + (header.gas_used * x!(6) / x!(5)) / bound_divisor) + max(gas_floor_target, gas_limit - gas_limit / bound_divisor + 1.into() + (header.gas_used * 6.into() / 5.into()) / bound_divisor) } }; header.note_dirty(); @@ -255,12 +255,12 @@ impl Ethash { /// Convert an Ethash boundary to its original difficulty. Basically just `f(x) = 2^256 / x`. pub fn boundary_to_difficulty(boundary: &H256) -> U256 { - U256::from((U512::one() << 256) / x!(U256::from(boundary.as_slice()))) + U256::from((U512::one() << 256) / U256::from(boundary.as_slice()).into()) } /// Convert an Ethash difficulty to the target boundary. Basically just `f(x) = 2^256 / x`. pub fn difficulty_to_boundary(difficulty: &U256) -> H256 { - x!(U256::from((U512::one() << 256) / x!(difficulty))) + U256::from((U512::one() << 256) / difficulty.into()).into() } fn to_ethash(hash: H256) -> EH256 { @@ -308,7 +308,7 @@ mod tests { spec.ensure_db_good(db.as_hashdb_mut()); let last_hashes = vec![genesis_header.hash()]; let vm_factory = Default::default(); - let b = OpenBlock::new(engine.deref(), &vm_factory, false, db, &genesis_header, last_hashes, Address::zero(), x!(3141562), vec![]); + let b = OpenBlock::new(engine.deref(), &vm_factory, false, db, &genesis_header, last_hashes, Address::zero(), 3141562.into(), vec![]); let b = b.close(); assert_eq!(b.state().balance(&Address::zero()), U256::from_str("4563918244f40000").unwrap()); } @@ -323,7 +323,7 @@ mod tests { spec.ensure_db_good(db.as_hashdb_mut()); let last_hashes = vec![genesis_header.hash()]; let vm_factory = Default::default(); - let mut b = OpenBlock::new(engine.deref(), &vm_factory, false, db, &genesis_header, last_hashes, Address::zero(), x!(3141562), vec![]); + let mut b = OpenBlock::new(engine.deref(), &vm_factory, false, db, &genesis_header, last_hashes, Address::zero(), 3141562.into(), vec![]); let mut uncle = Header::new(); let uncle_author = address_from_hex("ef2d6d194084c2de36e0dabfce45d046b37d1106"); uncle.author = uncle_author.clone(); @@ -346,24 +346,24 @@ mod tests { let engine = new_morden().engine; let schedule = engine.schedule(&EnvInfo { number: 10000000, - author: x!(0), + author: 0.into(), timestamp: 0, - difficulty: x!(0), + difficulty: 0.into(), last_hashes: vec![], - gas_used: x!(0), - gas_limit: x!(0) + gas_used: 0.into(), + gas_limit: 0.into() }); assert!(schedule.stack_limit > 0); let schedule = engine.schedule(&EnvInfo { number: 100, - author: x!(0), + author: 0.into(), timestamp: 0, - difficulty: x!(0), + difficulty: 0.into(), last_hashes: vec![], - gas_used: x!(0), - gas_limit: x!(0) + gas_used: 0.into(), + gas_limit: 0.into() }); assert!(!schedule.have_delegate_call); diff --git a/ethcore/src/executive.rs b/ethcore/src/executive.rs index 320a89cb8..291b4dba2 100644 --- a/ethcore/src/executive.rs +++ b/ethcore/src/executive.rs @@ -361,7 +361,7 @@ impl<'a> Executive<'a> { let refunds_bound = sstore_refunds + suicide_refunds; // real ammount to refund - let gas_left_prerefund = match result { Ok(x) => x, _ => x!(0) }; + let gas_left_prerefund = match result { Ok(x) => x, _ => 0.into() }; let refunded = cmp::min(refunds_bound, (t.gas - gas_left_prerefund) / U256::from(2)); let gas_left = gas_left_prerefund + refunded; @@ -588,10 +588,10 @@ mod tests { let expected_trace = vec![ Trace { depth: 0, action: trace::Action::Call(trace::Call { - from: x!("cd1722f3947def4cf144679da39c4c32bdc35681"), - to: x!("b010143a42d5980c7e5ef0e4a4416dc098a4fed3"), - value: x!(100), - gas: x!(100000), + from: "cd1722f3947def4cf144679da39c4c32bdc35681".into(), + to: "b010143a42d5980c7e5ef0e4a4416dc098a4fed3".into(), + value: 100.into(), + gas: 100000.into(), input: vec![], }), result: trace::Res::Call(trace::CallResult { @@ -601,9 +601,9 @@ mod tests { subs: vec![Trace { depth: 1, action: trace::Action::Create(trace::Create { - from: x!("b010143a42d5980c7e5ef0e4a4416dc098a4fed3"), - value: x!(23), - gas: x!(67979), + from: "b010143a42d5980c7e5ef0e4a4416dc098a4fed3".into(), + value: 23.into(), + gas: 67979.into(), init: vec![96, 16, 128, 96, 12, 96, 0, 57, 96, 0, 243, 0, 96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53, 85] }), result: trace::Res::Create(trace::CreateResult { @@ -642,7 +642,7 @@ mod tests { params.origin = sender.clone(); params.gas = U256::from(100_000); params.code = Some(code.clone()); - params.value = ActionValue::Transfer(x!(100)); + params.value = ActionValue::Transfer(100.into()); let mut state_result = get_temp_state(); let mut state = state_result.reference_mut(); state.add_balance(&sender, &U256::from(100)); @@ -660,7 +660,7 @@ mod tests { depth: 0, action: trace::Action::Create(trace::Create { from: params.sender, - value: x!(100), + value: 100.into(), gas: params.gas, init: vec![96, 16, 128, 96, 12, 96, 0, 57, 96, 0, 243, 0, 96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53, 85], }), diff --git a/ethcore/src/externalities.rs b/ethcore/src/externalities.rs index c936ac207..99d2eed72 100644 --- a/ethcore/src/externalities.rs +++ b/ethcore/src/externalities.rs @@ -311,12 +311,12 @@ mod tests { fn get_test_env_info() -> EnvInfo { EnvInfo { number: 100, - author: x!(0), + author: 0.into(), timestamp: 0, - difficulty: x!(0), + difficulty: 0.into(), last_hashes: vec![], - gas_used: x!(0), - gas_limit: x!(0) + gas_used: 0.into(), + gas_limit: 0.into() } } diff --git a/ethcore/src/pod_account.rs b/ethcore/src/pod_account.rs index 27ddc8a97..6f3949db3 100644 --- a/ethcore/src/pod_account.rs +++ b/ethcore/src/pod_account.rs @@ -114,11 +114,11 @@ mod test { #[test] fn existence() { - let a = PodAccount{balance: x!(69), nonce: x!(0), code: vec![], storage: map![]}; + let a = PodAccount{balance: 69.into(), nonce: 0.into(), code: vec![], storage: map![]}; assert_eq!(AccountDiff::diff_pod(Some(&a), Some(&a)), None); assert_eq!(AccountDiff::diff_pod(None, Some(&a)), Some(AccountDiff{ - balance: Diff::Born(x!(69)), - nonce: Diff::Born(x!(0)), + balance: Diff::Born(69.into()), + nonce: Diff::Born(0.into()), code: Diff::Born(vec![]), storage: map![], })); @@ -126,11 +126,11 @@ mod test { #[test] fn basic() { - let a = PodAccount{balance: x!(69), nonce: x!(0), code: vec![], storage: map![]}; - let b = PodAccount{balance: x!(42), nonce: x!(1), code: vec![], storage: map![]}; + let a = PodAccount{balance: 69.into(), nonce: 0.into(), code: vec![], storage: map![]}; + let b = PodAccount{balance: 42.into(), nonce: 1.into(), code: vec![], storage: map![]}; assert_eq!(AccountDiff::diff_pod(Some(&a), Some(&b)), Some(AccountDiff { - balance: Diff::Changed(x!(69), x!(42)), - nonce: Diff::Changed(x!(0), x!(1)), + balance: Diff::Changed(69.into(), 42.into()), + nonce: Diff::Changed(0.into(), 1.into()), code: Diff::Same, storage: map![], })); @@ -138,11 +138,11 @@ mod test { #[test] fn code() { - let a = PodAccount{balance: x!(0), nonce: x!(0), code: vec![], storage: map![]}; - let b = PodAccount{balance: x!(0), nonce: x!(1), code: vec![0], storage: map![]}; + let a = PodAccount{balance: 0.into(), nonce: 0.into(), code: vec![], storage: map![]}; + let b = PodAccount{balance: 0.into(), nonce: 1.into(), code: vec![0], storage: map![]}; assert_eq!(AccountDiff::diff_pod(Some(&a), Some(&b)), Some(AccountDiff { balance: Diff::Same, - nonce: Diff::Changed(x!(0), x!(1)), + nonce: Diff::Changed(0.into(), 1.into()), code: Diff::Changed(vec![], vec![0]), storage: map![], })); @@ -151,14 +151,14 @@ mod test { #[test] fn storage() { let a = PodAccount { - balance: x!(0), - nonce: x!(0), + balance: 0.into(), + nonce: 0.into(), code: vec![], storage: mapx![1 => 1, 2 => 2, 3 => 3, 4 => 4, 5 => 0, 6 => 0, 7 => 0] }; let b = PodAccount { - balance: x!(0), - nonce: x!(0), + balance: 0.into(), + nonce: 0.into(), code: vec![], storage: mapx![1 => 1, 2 => 3, 3 => 0, 5 => 0, 7 => 7, 8 => 0, 9 => 9] }; @@ -167,11 +167,11 @@ mod test { nonce: Diff::Same, code: Diff::Same, storage: map![ - x!(2) => Diff::new(x!(2), x!(3)), - x!(3) => Diff::new(x!(3), x!(0)), - x!(4) => Diff::new(x!(4), x!(0)), - x!(7) => Diff::new(x!(0), x!(7)), - x!(9) => Diff::new(x!(0), x!(9)) + 2.into() => Diff::new(2.into(), 3.into()), + 3.into() => Diff::new(3.into(), 0.into()), + 4.into() => Diff::new(4.into(), 0.into()), + 7.into() => Diff::new(0.into(), 7.into()), + 9.into() => Diff::new(0.into(), 9.into()), ], })); } diff --git a/ethcore/src/state.rs b/ethcore/src/state.rs index eabca24a8..6e0e4f2f6 100644 --- a/ethcore/src/state.rs +++ b/ethcore/src/state.rs @@ -214,7 +214,7 @@ impl State { /// Initialise the code of account `a` so that it is `value` for `key`. /// NOTE: Account should have been created with `new_contract`. pub fn init_code(&mut self, a: &Address, code: Bytes) { - self.require_or_from(a, true, || Account::new_contract(x!(0), self.account_start_nonce), |_|{}).init_code(code); + self.require_or_from(a, true, || Account::new_contract(0.into(), self.account_start_nonce), |_|{}).init_code(code); } /// Execute a given transaction. @@ -377,27 +377,27 @@ fn should_apply_create_transaction() { let mut state = get_temp_state_in(temp.as_path()); let mut info = EnvInfo::default(); - info.gas_limit = x!(1_000_000); + info.gas_limit = 1_000_000.into(); let engine = TestEngine::new(5); let t = Transaction { - nonce: x!(0), - gas_price: x!(0), - gas: x!(100_000), + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), action: Action::Create, - value: x!(100), + value: 100.into(), data: FromHex::from_hex("601080600c6000396000f3006000355415600957005b60203560003555").unwrap(), }.sign(&"".sha3()); - state.add_balance(t.sender().as_ref().unwrap(), &x!(100)); + state.add_balance(t.sender().as_ref().unwrap(), &100.into()); let vm_factory = Default::default(); let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap(); let expected_trace = Some(Trace { depth: 0, action: trace::Action::Create(trace::Create { - from: x!("9cce34f7ab185c7aba1b7c8140d620b4bda941d6"), - value: x!(100), - gas: x!(77412), + from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), + value: 100.into(), + gas: 77412.into(), init: vec![96, 16, 128, 96, 12, 96, 0, 57, 96, 0, 243, 0, 96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53, 85], }), result: trace::Res::Create(trace::CreateResult { @@ -438,27 +438,27 @@ fn should_trace_failed_create_transaction() { let mut state = get_temp_state_in(temp.as_path()); let mut info = EnvInfo::default(); - info.gas_limit = x!(1_000_000); + info.gas_limit = 1_000_000.into(); let engine = TestEngine::new(5); let t = Transaction { - nonce: x!(0), - gas_price: x!(0), - gas: x!(100_000), + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), action: Action::Create, - value: x!(100), + value: 100.into(), data: FromHex::from_hex("5b600056").unwrap(), }.sign(&"".sha3()); - state.add_balance(t.sender().as_ref().unwrap(), &x!(100)); + state.add_balance(t.sender().as_ref().unwrap(), &100.into()); let vm_factory = Default::default(); let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap(); let expected_trace = Some(Trace { depth: 0, action: trace::Action::Create(trace::Create { - from: x!("9cce34f7ab185c7aba1b7c8140d620b4bda941d6"), - value: x!(100), - gas: x!(78792), + from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), + value: 100.into(), + gas: 78792.into(), init: vec![91, 96, 0, 86], }), result: trace::Res::FailedCreate, @@ -476,29 +476,29 @@ fn should_trace_call_transaction() { let mut state = get_temp_state_in(temp.as_path()); let mut info = EnvInfo::default(); - info.gas_limit = x!(1_000_000); + info.gas_limit = 1_000_000.into(); let engine = TestEngine::new(5); let t = Transaction { - nonce: x!(0), - gas_price: x!(0), - gas: x!(100_000), - action: Action::Call(x!(0xa)), - value: x!(100), + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Call(0xa.into()), + value: 100.into(), data: vec![], }.sign(&"".sha3()); - state.init_code(&x!(0xa), FromHex::from_hex("6000").unwrap()); - state.add_balance(t.sender().as_ref().unwrap(), &x!(100)); + state.init_code(&0xa.into(), FromHex::from_hex("6000").unwrap()); + state.add_balance(t.sender().as_ref().unwrap(), &100.into()); let vm_factory = Default::default(); let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap(); let expected_trace = Some(Trace { depth: 0, action: trace::Action::Call(trace::Call { - from: x!("9cce34f7ab185c7aba1b7c8140d620b4bda941d6"), - to: x!(0xa), - value: x!(100), - gas: x!(79000), + from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), + to: 0xa.into(), + value: 100.into(), + gas: 79000.into(), input: vec![], }), result: trace::Res::Call(trace::CallResult { @@ -519,28 +519,28 @@ fn should_trace_basic_call_transaction() { let mut state = get_temp_state_in(temp.as_path()); let mut info = EnvInfo::default(); - info.gas_limit = x!(1_000_000); + info.gas_limit = 1_000_000.into(); let engine = TestEngine::new(5); let t = Transaction { - nonce: x!(0), - gas_price: x!(0), - gas: x!(100_000), - action: Action::Call(x!(0xa)), - value: x!(100), + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Call(0xa.into()), + value: 100.into(), data: vec![], }.sign(&"".sha3()); - state.add_balance(t.sender().as_ref().unwrap(), &x!(100)); + state.add_balance(t.sender().as_ref().unwrap(), &100.into()); let vm_factory = Default::default(); let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap(); let expected_trace = Some(Trace { depth: 0, action: trace::Action::Call(trace::Call { - from: x!("9cce34f7ab185c7aba1b7c8140d620b4bda941d6"), - to: x!(0xa), - value: x!(100), - gas: x!(79000), + from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), + to: 0xa.into(), + value: 100.into(), + gas: 79000.into(), input: vec![], }), result: trace::Res::Call(trace::CallResult { @@ -561,15 +561,15 @@ fn should_trace_call_transaction_to_builtin() { let mut state = get_temp_state_in(temp.as_path()); let mut info = EnvInfo::default(); - info.gas_limit = x!(1_000_000); + info.gas_limit = 1_000_000.into(); let engine = Spec::new_test().engine; let t = Transaction { - nonce: x!(0), - gas_price: x!(0), - gas: x!(100_000), - action: Action::Call(x!(0x1)), - value: x!(0), + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Call(0x1.into()), + value: 0.into(), data: vec![], }.sign(&"".sha3()); @@ -579,10 +579,10 @@ fn should_trace_call_transaction_to_builtin() { assert_eq!(result.trace, Some(Trace { depth: 0, action: trace::Action::Call(trace::Call { - from: x!("9cce34f7ab185c7aba1b7c8140d620b4bda941d6"), - to: x!("0000000000000000000000000000000000000001"), - value: x!(0), - gas: x!(79_000), + from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), + to: "0000000000000000000000000000000000000001".into(), + value: 0.into(), + gas: 79_000.into(), input: vec![], }), result: trace::Res::Call(trace::CallResult { @@ -601,29 +601,29 @@ fn should_not_trace_subcall_transaction_to_builtin() { let mut state = get_temp_state_in(temp.as_path()); let mut info = EnvInfo::default(); - info.gas_limit = x!(1_000_000); + info.gas_limit = 1_000_000.into(); let engine = Spec::new_test().engine; let t = Transaction { - nonce: x!(0), - gas_price: x!(0), - gas: x!(100_000), - action: Action::Call(x!(0xa)), - value: x!(0), + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Call(0xa.into()), + value: 0.into(), data: vec![], }.sign(&"".sha3()); - state.init_code(&x!(0xa), FromHex::from_hex("600060006000600060006001610be0f1").unwrap()); + state.init_code(&0xa.into(), FromHex::from_hex("600060006000600060006001610be0f1").unwrap()); let vm_factory = Default::default(); let result = state.apply(&info, engine.deref(), &vm_factory, &t, true).unwrap(); let expected_trace = Some(Trace { depth: 0, action: trace::Action::Call(trace::Call { - from: x!("9cce34f7ab185c7aba1b7c8140d620b4bda941d6"), - to: x!(0xa), - value: x!(0), - gas: x!(79000), + from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), + to: 0xa.into(), + value: 0.into(), + gas: 79000.into(), input: vec![], }), result: trace::Res::Call(trace::CallResult { @@ -643,30 +643,30 @@ fn should_not_trace_callcode() { let mut state = get_temp_state_in(temp.as_path()); let mut info = EnvInfo::default(); - info.gas_limit = x!(1_000_000); + info.gas_limit = 1_000_000.into(); let engine = Spec::new_test().engine; let t = Transaction { - nonce: x!(0), - gas_price: x!(0), - gas: x!(100_000), - action: Action::Call(x!(0xa)), - value: x!(0), + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Call(0xa.into()), + value: 0.into(), data: vec![], }.sign(&"".sha3()); - state.init_code(&x!(0xa), FromHex::from_hex("60006000600060006000600b611000f2").unwrap()); - state.init_code(&x!(0xb), FromHex::from_hex("6000").unwrap()); + state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006000600b611000f2").unwrap()); + state.init_code(&0xb.into(), FromHex::from_hex("6000").unwrap()); let vm_factory = Default::default(); let result = state.apply(&info, engine.deref(), &vm_factory, &t, true).unwrap(); let expected_trace = Some(Trace { depth: 0, action: trace::Action::Call(trace::Call { - from: x!("9cce34f7ab185c7aba1b7c8140d620b4bda941d6"), - to: x!(0xa), - value: x!(0), - gas: x!(79000), + from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), + to: 0xa.into(), + value: 0.into(), + gas: 79000.into(), input: vec![], }), result: trace::Res::Call(trace::CallResult { @@ -686,33 +686,33 @@ fn should_not_trace_delegatecall() { let mut state = get_temp_state_in(temp.as_path()); let mut info = EnvInfo::default(); - info.gas_limit = x!(1_000_000); + info.gas_limit = 1_000_000.into(); info.number = 0x789b0; let engine = Spec::new_test().engine; println!("schedule.have_delegate_call: {:?}", engine.schedule(&info).have_delegate_call); let t = Transaction { - nonce: x!(0), - gas_price: x!(0), - gas: x!(100_000), - action: Action::Call(x!(0xa)), - value: x!(0), + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Call(0xa.into()), + value: 0.into(), data: vec![], }.sign(&"".sha3()); - state.init_code(&x!(0xa), FromHex::from_hex("6000600060006000600b618000f4").unwrap()); - state.init_code(&x!(0xb), FromHex::from_hex("6000").unwrap()); + state.init_code(&0xa.into(), FromHex::from_hex("6000600060006000600b618000f4").unwrap()); + state.init_code(&0xb.into(), FromHex::from_hex("6000").unwrap()); let vm_factory = Default::default(); let result = state.apply(&info, engine.deref(), &vm_factory, &t, true).unwrap(); let expected_trace = Some(Trace { depth: 0, action: trace::Action::Call(trace::Call { - from: x!("9cce34f7ab185c7aba1b7c8140d620b4bda941d6"), - to: x!(0xa), - value: x!(0), - gas: x!(79000), + from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), + to: 0xa.into(), + value: 0.into(), + gas: 79000.into(), input: vec![], }), result: trace::Res::Call(trace::CallResult { @@ -732,29 +732,29 @@ fn should_trace_failed_call_transaction() { let mut state = get_temp_state_in(temp.as_path()); let mut info = EnvInfo::default(); - info.gas_limit = x!(1_000_000); + info.gas_limit = 1_000_000.into(); let engine = TestEngine::new(5); let t = Transaction { - nonce: x!(0), - gas_price: x!(0), - gas: x!(100_000), - action: Action::Call(x!(0xa)), - value: x!(100), + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Call(0xa.into()), + value: 100.into(), data: vec![], }.sign(&"".sha3()); - state.init_code(&x!(0xa), FromHex::from_hex("5b600056").unwrap()); - state.add_balance(t.sender().as_ref().unwrap(), &x!(100)); + state.init_code(&0xa.into(), FromHex::from_hex("5b600056").unwrap()); + state.add_balance(t.sender().as_ref().unwrap(), &100.into()); let vm_factory = Default::default(); let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap(); let expected_trace = Some(Trace { depth: 0, action: trace::Action::Call(trace::Call { - from: x!("9cce34f7ab185c7aba1b7c8140d620b4bda941d6"), - to: x!(0xa), - value: x!(100), - gas: x!(79000), + from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), + to: 0xa.into(), + value: 100.into(), + gas: 79000.into(), input: vec![], }), result: trace::Res::FailedCall, @@ -774,30 +774,30 @@ fn should_trace_call_with_subcall_transaction() { let mut state = get_temp_state_in(temp.as_path()); let mut info = EnvInfo::default(); - info.gas_limit = x!(1_000_000); + info.gas_limit = 1_000_000.into(); let engine = TestEngine::new(5); let t = Transaction { - nonce: x!(0), - gas_price: x!(0), - gas: x!(100_000), - action: Action::Call(x!(0xa)), - value: x!(100), + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Call(0xa.into()), + value: 100.into(), data: vec![], }.sign(&"".sha3()); - state.init_code(&x!(0xa), FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap()); - state.init_code(&x!(0xb), FromHex::from_hex("6000").unwrap()); - state.add_balance(t.sender().as_ref().unwrap(), &x!(100)); + state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap()); + state.init_code(&0xb.into(), FromHex::from_hex("6000").unwrap()); + state.add_balance(t.sender().as_ref().unwrap(), &100.into()); let vm_factory = Default::default(); let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap(); let expected_trace = Some(Trace { depth: 0, action: trace::Action::Call(trace::Call { - from: x!("9cce34f7ab185c7aba1b7c8140d620b4bda941d6"), - to: x!(0xa), - value: x!(100), - gas: x!(79000), + from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), + to: 0xa.into(), + value: 100.into(), + gas: 79000.into(), input: vec![], }), result: trace::Res::Call(trace::CallResult { @@ -807,10 +807,10 @@ fn should_trace_call_with_subcall_transaction() { subs: vec![Trace { depth: 1, action: trace::Action::Call(trace::Call { - from: x!(0xa), - to: x!(0xb), - value: x!(0), - gas: x!(78934), + from: 0xa.into(), + to: 0xb.into(), + value: 0.into(), + gas: 78934.into(), input: vec![], }), result: trace::Res::Call(trace::CallResult { @@ -832,29 +832,29 @@ fn should_trace_call_with_basic_subcall_transaction() { let mut state = get_temp_state_in(temp.as_path()); let mut info = EnvInfo::default(); - info.gas_limit = x!(1_000_000); + info.gas_limit = 1_000_000.into(); let engine = TestEngine::new(5); let t = Transaction { - nonce: x!(0), - gas_price: x!(0), - gas: x!(100_000), - action: Action::Call(x!(0xa)), - value: x!(100), + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Call(0xa.into()), + value: 100.into(), data: vec![], }.sign(&"".sha3()); - state.init_code(&x!(0xa), FromHex::from_hex("60006000600060006045600b6000f1").unwrap()); - state.add_balance(t.sender().as_ref().unwrap(), &x!(100)); + state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006045600b6000f1").unwrap()); + state.add_balance(t.sender().as_ref().unwrap(), &100.into()); let vm_factory = Default::default(); let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap(); let expected_trace = Some(Trace { depth: 0, action: trace::Action::Call(trace::Call { - from: x!("9cce34f7ab185c7aba1b7c8140d620b4bda941d6"), - to: x!(0xa), - value: x!(100), - gas: x!(79000), + from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), + to: 0xa.into(), + value: 100.into(), + gas: 79000.into(), input: vec![], }), result: trace::Res::Call(trace::CallResult { @@ -864,10 +864,10 @@ fn should_trace_call_with_basic_subcall_transaction() { subs: vec![Trace { depth: 1, action: trace::Action::Call(trace::Call { - from: x!(0xa), - to: x!(0xb), - value: x!(69), - gas: x!(2300), + from: 0xa.into(), + to: 0xb.into(), + value: 69.into(), + gas: 2300.into(), input: vec![], }), result: trace::Res::Call(trace::CallResult::default()), @@ -886,29 +886,29 @@ fn should_not_trace_call_with_invalid_basic_subcall_transaction() { let mut state = get_temp_state_in(temp.as_path()); let mut info = EnvInfo::default(); - info.gas_limit = x!(1_000_000); + info.gas_limit = 1_000_000.into(); let engine = TestEngine::new(5); let t = Transaction { - nonce: x!(0), - gas_price: x!(0), - gas: x!(100_000), - action: Action::Call(x!(0xa)), - value: x!(100), + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Call(0xa.into()), + value: 100.into(), data: vec![], }.sign(&"".sha3()); - state.init_code(&x!(0xa), FromHex::from_hex("600060006000600060ff600b6000f1").unwrap()); // not enough funds. - state.add_balance(t.sender().as_ref().unwrap(), &x!(100)); + state.init_code(&0xa.into(), FromHex::from_hex("600060006000600060ff600b6000f1").unwrap()); // not enough funds. + state.add_balance(t.sender().as_ref().unwrap(), &100.into()); let vm_factory = Default::default(); let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap(); let expected_trace = Some(Trace { depth: 0, action: trace::Action::Call(trace::Call { - from: x!("9cce34f7ab185c7aba1b7c8140d620b4bda941d6"), - to: x!(0xa), - value: x!(100), - gas: x!(79000), + from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), + to: 0xa.into(), + value: 100.into(), + gas: 79000.into(), input: vec![], }), result: trace::Res::Call(trace::CallResult { @@ -929,30 +929,30 @@ fn should_trace_failed_subcall_transaction() { let mut state = get_temp_state_in(temp.as_path()); let mut info = EnvInfo::default(); - info.gas_limit = x!(1_000_000); + info.gas_limit = 1_000_000.into(); let engine = TestEngine::new(5); let t = Transaction { - nonce: x!(0), - gas_price: x!(0), - gas: x!(100_000), - action: Action::Call(x!(0xa)), - value: x!(100), + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Call(0xa.into()), + value: 100.into(), data: vec![],//600480600b6000396000f35b600056 }.sign(&"".sha3()); - state.init_code(&x!(0xa), FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap()); - state.init_code(&x!(0xb), FromHex::from_hex("5b600056").unwrap()); - state.add_balance(t.sender().as_ref().unwrap(), &x!(100)); + state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap()); + state.init_code(&0xb.into(), FromHex::from_hex("5b600056").unwrap()); + state.add_balance(t.sender().as_ref().unwrap(), &100.into()); let vm_factory = Default::default(); let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap(); let expected_trace = Some(Trace { depth: 0, action: trace::Action::Call(trace::Call { - from: x!("9cce34f7ab185c7aba1b7c8140d620b4bda941d6"), - to: x!(0xa), - value: x!(100), - gas: x!(79000), + from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), + to: 0xa.into(), + value: 100.into(), + gas: 79000.into(), input: vec![], }), result: trace::Res::Call(trace::CallResult { @@ -962,10 +962,10 @@ fn should_trace_failed_subcall_transaction() { subs: vec![Trace { depth: 1, action: trace::Action::Call(trace::Call { - from: x!(0xa), - to: x!(0xb), - value: x!(0), - gas: x!(78934), + from: 0xa.into(), + to: 0xb.into(), + value: 0.into(), + gas: 78934.into(), input: vec![], }), result: trace::Res::FailedCall, @@ -984,31 +984,31 @@ fn should_trace_call_with_subcall_with_subcall_transaction() { let mut state = get_temp_state_in(temp.as_path()); let mut info = EnvInfo::default(); - info.gas_limit = x!(1_000_000); + info.gas_limit = 1_000_000.into(); let engine = TestEngine::new(5); let t = Transaction { - nonce: x!(0), - gas_price: x!(0), - gas: x!(100_000), - action: Action::Call(x!(0xa)), - value: x!(100), + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Call(0xa.into()), + value: 100.into(), data: vec![], }.sign(&"".sha3()); - state.init_code(&x!(0xa), FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap()); - state.init_code(&x!(0xb), FromHex::from_hex("60006000600060006000600c602b5a03f1").unwrap()); - state.init_code(&x!(0xc), FromHex::from_hex("6000").unwrap()); - state.add_balance(t.sender().as_ref().unwrap(), &x!(100)); + state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap()); + state.init_code(&0xb.into(), FromHex::from_hex("60006000600060006000600c602b5a03f1").unwrap()); + state.init_code(&0xc.into(), FromHex::from_hex("6000").unwrap()); + state.add_balance(t.sender().as_ref().unwrap(), &100.into()); let vm_factory = Default::default(); let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap(); let expected_trace = Some(Trace { depth: 0, action: trace::Action::Call(trace::Call { - from: x!("9cce34f7ab185c7aba1b7c8140d620b4bda941d6"), - to: x!(0xa), - value: x!(100), - gas: x!(79000), + from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), + to: 0xa.into(), + value: 100.into(), + gas: 79000.into(), input: vec![], }), result: trace::Res::Call(trace::CallResult { @@ -1018,10 +1018,10 @@ fn should_trace_call_with_subcall_with_subcall_transaction() { subs: vec![Trace { depth: 1, action: trace::Action::Call(trace::Call { - from: x!(0xa), - to: x!(0xb), - value: x!(0), - gas: x!(78934), + from: 0xa.into(), + to: 0xb.into(), + value: 0.into(), + gas: 78934.into(), input: vec![], }), result: trace::Res::Call(trace::CallResult { @@ -1031,10 +1031,10 @@ fn should_trace_call_with_subcall_with_subcall_transaction() { subs: vec![Trace { depth: 2, action: trace::Action::Call(trace::Call { - from: x!(0xb), - to: x!(0xc), - value: x!(0), - gas: x!(78868), + from: 0xb.into(), + to: 0xb.into(), + value: 0.into(), + gas: 78868.into(), input: vec![], }), result: trace::Res::Call(trace::CallResult { @@ -1057,31 +1057,31 @@ fn should_trace_failed_subcall_with_subcall_transaction() { let mut state = get_temp_state_in(temp.as_path()); let mut info = EnvInfo::default(); - info.gas_limit = x!(1_000_000); + info.gas_limit = 1_000_000.into(); let engine = TestEngine::new(5); let t = Transaction { - nonce: x!(0), - gas_price: x!(0), - gas: x!(100_000), - action: Action::Call(x!(0xa)), - value: x!(100), + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Call(0xa.into()), + value: 100.into(), data: vec![],//600480600b6000396000f35b600056 }.sign(&"".sha3()); - state.init_code(&x!(0xa), FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap()); - state.init_code(&x!(0xb), FromHex::from_hex("60006000600060006000600c602b5a03f1505b601256").unwrap()); - state.init_code(&x!(0xc), FromHex::from_hex("6000").unwrap()); - state.add_balance(t.sender().as_ref().unwrap(), &x!(100)); + state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap()); + state.init_code(&0xb.into(), FromHex::from_hex("60006000600060006000600c602b5a03f1505b601256").unwrap()); + state.init_code(&0xc.into(), FromHex::from_hex("6000").unwrap()); + state.add_balance(t.sender().as_ref().unwrap(), &100.into()); let vm_factory = Default::default(); let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap(); let expected_trace = Some(Trace { depth: 0, action: trace::Action::Call(trace::Call { - from: x!("9cce34f7ab185c7aba1b7c8140d620b4bda941d6"), - to: x!(0xa), - value: x!(100), - gas: x!(79000), + from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), + to: 0xa.into(), + value: 100.into(), + gas: 79000.into(), input: vec![], }), result: trace::Res::Call(trace::CallResult { @@ -1091,21 +1091,21 @@ fn should_trace_failed_subcall_with_subcall_transaction() { subs: vec![Trace { depth: 1, action: trace::Action::Call(trace::Call { - from: x!(0xa), - to: x!(0xb), - value: x!(0), - gas: x!(78934), + from: 0xa.into(), + to: 0xb.into(), + value: 0.into(), + gas: 78934.into(), input: vec![], }), result: trace::Res::FailedCall, subs: vec![Trace { depth: 2, action: trace::Action::Call(trace::Call { - from: x!(0xb), - to: x!(0xc), - value: x!(0), - gas: x!(78868), - input: vec![], + from: 0xb.into(), + to: 0xc.into(), + value: 0.into(), + gas: 78868.into(), + input: vec![], }), result: trace::Res::Call(trace::CallResult { gas_used: U256::from(3), @@ -1125,7 +1125,7 @@ fn code_from_database() { let temp = RandomTempPath::new(); let (root, db) = { let mut state = get_temp_state_in(temp.as_path()); - state.require_or_from(&a, false, ||Account::new_contract(x!(42), x!(0)), |_|{}); + state.require_or_from(&a, false, ||Account::new_contract(42.into(), 0.into()), |_|{}); state.init_code(&a, vec![1, 2, 3]); assert_eq!(state.code(&a), Some([1u8, 2, 3].to_vec())); state.commit(); diff --git a/ethcore/src/substate.rs b/ethcore/src/substate.rs index 65b314663..247bbb398 100644 --- a/ethcore/src/substate.rs +++ b/ethcore/src/substate.rs @@ -74,7 +74,7 @@ mod tests { topics: vec![], data: vec![] }); - sub_state.sstore_clears_count = x!(5); + sub_state.sstore_clears_count = 5.into(); sub_state.suicides.insert(address_from_u64(10u64)); let mut sub_state_2 = Substate::new(); @@ -84,11 +84,11 @@ mod tests { topics: vec![], data: vec![] }); - sub_state_2.sstore_clears_count = x!(7); + sub_state_2.sstore_clears_count = 7.into(); sub_state.accrue(sub_state_2); assert_eq!(sub_state.contracts_created.len(), 2); - assert_eq!(sub_state.sstore_clears_count, x!(12)); + assert_eq!(sub_state.sstore_clears_count, 12.into()); assert_eq!(sub_state.suicides.len(), 1); } } diff --git a/ethcore/src/tests/client.rs b/ethcore/src/tests/client.rs index d734b0b47..4a71bf7d7 100644 --- a/ethcore/src/tests/client.rs +++ b/ethcore/src/tests/client.rs @@ -136,7 +136,7 @@ fn can_mine() { let client_result = get_test_client_with_blocks(vec![dummy_blocks[0].clone()]); let client = client_result.reference(); - let b = client.prepare_sealing(Address::default(), x!(31415926), vec![], vec![]).0.unwrap(); + let b = client.prepare_sealing(Address::default(), 31415926.into(), vec![], vec![]).0.unwrap(); assert_eq!(*b.block().header().parent_hash(), BlockView::new(&dummy_blocks[0]).header_view().sha3()); assert!(client.try_seal(b.lock(), vec![]).is_ok()); diff --git a/ethcore/src/tests/helpers.rs b/ethcore/src/tests/helpers.rs index 7262da9e8..6a370e49f 100644 --- a/ethcore/src/tests/helpers.rs +++ b/ethcore/src/tests/helpers.rs @@ -98,8 +98,8 @@ pub fn create_test_block(header: &Header) -> Bytes { fn create_unverifiable_block_header(order: u32, parent_hash: H256) -> Header { let mut header = Header::new(); - header.gas_limit = x!(0); - header.difficulty = x!(order * 100); + header.gas_limit = 0.into(); + header.difficulty = order * 100.into(); header.timestamp = (order * 10) as u64; header.number = order as u64; header.parent_hash = parent_hash; @@ -335,7 +335,7 @@ pub fn get_bad_state_dummy_block() -> Bytes { block_header.timestamp = 40; block_header.number = 1; block_header.parent_hash = test_spec.genesis_header().hash(); - block_header.state_root = x!(0xbad); + block_header.state_root = 0xbad.into(); create_test_block(&block_header) } diff --git a/ethcore/src/types/receipt.rs b/ethcore/src/types/receipt.rs index f07a12212..78216eb16 100644 --- a/ethcore/src/types/receipt.rs +++ b/ethcore/src/types/receipt.rs @@ -105,10 +105,10 @@ pub struct LocalizedReceipt { fn test_basic() { let expected = ::rustc_serialize::hex::FromHex::from_hex("f90162a02f697d671e9ae4ee24a43c4b0d7e15f1cb4ba6de1561120d43b9a4e8c4a8a6ee83040caeb9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000f838f794dcf421d093428b096ca501a7cd1a740855a7976fc0a00000000000000000000000000000000000000000000000000000000000000000").unwrap(); let r = Receipt::new( - x!("2f697d671e9ae4ee24a43c4b0d7e15f1cb4ba6de1561120d43b9a4e8c4a8a6ee"), - x!(0x40cae), + "2f697d671e9ae4ee24a43c4b0d7e15f1cb4ba6de1561120d43b9a4e8c4a8a6ee".into(), + 0x40cae.into(), vec![LogEntry { - address: x!("dcf421d093428b096ca501a7cd1a740855a7976f"), + address: "dcf421d093428b096ca501a7cd1a740855a7976f".into(), topics: vec![], data: vec![0u8; 32] }] From 3abaeadcf3794708246042d961096ef349d65519 Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Tue, 31 May 2016 17:18:21 +0200 Subject: [PATCH 17/30] finish purging x! from ethcore --- ethcore/src/state_diff.rs | 44 +++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/ethcore/src/state_diff.rs b/ethcore/src/state_diff.rs index 6c41d167c..1c1975e70 100644 --- a/ethcore/src/state_diff.rs +++ b/ethcore/src/state_diff.rs @@ -59,19 +59,19 @@ mod test { #[test] fn create_delete() { - let a = PodState::from(map![ x!(1) => PodAccount::new(x!(69), x!(0), vec![], map![]) ]); + let a = PodState::from(map![ 1.into() => PodAccount::new(69.into(), 0.into(), vec![], map![]) ]); assert_eq!(StateDiff::diff_pod(&a, &PodState::new()), StateDiff(map![ - x!(1) => AccountDiff{ - balance: Diff::Died(x!(69)), - nonce: Diff::Died(x!(0)), + 1.into() => AccountDiff{ + balance: Diff::Died(69.into()), + nonce: Diff::Died(0.into()), code: Diff::Died(vec![]), storage: map![], } ])); assert_eq!(StateDiff::diff_pod(&PodState::new(), &a), StateDiff(map![ - x!(1) => AccountDiff{ - balance: Diff::Born(x!(69)), - nonce: Diff::Born(x!(0)), + 1.into() => AccountDiff{ + balance: Diff::Born(69.into()), + nonce: Diff::Born(0.into()), code: Diff::Born(vec![]), storage: map![], } @@ -80,23 +80,23 @@ mod test { #[test] fn create_delete_with_unchanged() { - let a = PodState::from(map![ x!(1) => PodAccount::new(x!(69), x!(0), vec![], map![]) ]); + let a = PodState::from(map![ 1.into() => PodAccount::new(69.into(), 0.into(), vec![], map![]) ]); let b = PodState::from(map![ - x!(1) => PodAccount::new(x!(69), x!(0), vec![], map![]), - x!(2) => PodAccount::new(x!(69), x!(0), vec![], map![]) + 1.into() => PodAccount::new(69.into(), 0.into(), vec![], map![]), + 2.into() => PodAccount::new(69.into(), 0.into(),, vec![], map![]) ]); assert_eq!(StateDiff::diff_pod(&a, &b), StateDiff(map![ - x!(2) => AccountDiff{ - balance: Diff::Born(x!(69)), - nonce: Diff::Born(x!(0)), + 2.into() => AccountDiff{ + balance: Diff::Born(69.into()), + nonce: Diff::Born(0.into()), code: Diff::Born(vec![]), storage: map![], } ])); assert_eq!(StateDiff::diff_pod(&b, &a), StateDiff(map![ - x!(2) => AccountDiff{ - balance: Diff::Died(x!(69)), - nonce: Diff::Died(x!(0)), + 2.into() => AccountDiff{ + balance: Diff::Died(69.into()), + nonce: Diff::Died(0.into()), code: Diff::Died(vec![]), storage: map![], } @@ -106,17 +106,17 @@ mod test { #[test] fn change_with_unchanged() { let a = PodState::from(map![ - x!(1) => PodAccount::new(x!(69), x!(0), vec![], map![]), - x!(2) => PodAccount::new(x!(69), x!(0), vec![], map![]) + 1.into() => PodAccount::new(69.into(), 0.into(), vec![], map![]), + 2.into() => PodAccount::new(69.into(), 0.into(),, vec![], map![]) ]); let b = PodState::from(map![ - x!(1) => PodAccount::new(x!(69), x!(1), vec![], map![]), - x!(2) => PodAccount::new(x!(69), x!(0), vec![], map![]) + 1.into() => PodAccount::new(69.into(), 1.into(),, vec![], map![]), + 2.into() => PodAccount::new(69.into(), 0.into(),, vec![], map![]) ]); assert_eq!(StateDiff::diff_pod(&a, &b), StateDiff(map![ - x!(1) => AccountDiff{ + 1.into() => AccountDiff{ balance: Diff::Same, - nonce: Diff::Changed(x!(0), x!(1)), + nonce: Diff::Changed(0.into(), 1.into()), code: Diff::Same, storage: map![], } From c62bfcddef1fedaaa76eed2870071cd93846c1c7 Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Tue, 31 May 2016 17:25:25 +0200 Subject: [PATCH 18/30] finish purging x! from parity, remove x! and xx! macros --- miner/src/lib.rs | 4 ++-- miner/src/miner.rs | 6 +++--- sync/src/chain.rs | 6 +++--- util/src/common.rs | 14 -------------- util/src/keys/store.rs | 2 +- 5 files changed, 9 insertions(+), 23 deletions(-) diff --git a/miner/src/lib.rs b/miner/src/lib.rs index a1780efff..f91811948 100644 --- a/miner/src/lib.rs +++ b/miner/src/lib.rs @@ -150,10 +150,10 @@ pub trait MinerService : Send + Sync { fn last_nonce(&self, address: &Address) -> Option; /// Suggested gas price. - fn sensible_gas_price(&self) -> U256 { x!(20000000000u64) } + fn sensible_gas_price(&self) -> U256 { 20000000000u64.into() } /// Suggested gas limit. - fn sensible_gas_limit(&self) -> U256 { x!(21000) } + fn sensible_gas_limit(&self) -> U256 { 21000.into() } /// Latest account balance in pending state. fn balance(&self, chain: &BlockChainClient, address: &Address) -> U256; diff --git a/miner/src/miner.rs b/miner/src/miner.rs index fc63aec6c..d9abb09f4 100644 --- a/miner/src/miner.rs +++ b/miner/src/miner.rs @@ -137,7 +137,7 @@ impl Miner { Err(Error::Execution(ExecutionError::BlockGasLimitReached { gas_limit, gas_used, .. })) => { trace!(target: "miner", "Skipping adding transaction to block because of gas limit: {:?}", hash); // Exit early if gas left is smaller then min_tx_gas - let min_tx_gas: U256 = x!(21000); // TODO: figure this out properly. + let min_tx_gas: U256 = 21000.into(); // TODO: figure this out properly. if gas_limit - gas_used < min_tx_gas { break; } @@ -337,11 +337,11 @@ impl MinerService for Miner { fn sensible_gas_price(&self) -> U256 { // 10% above our minimum. - *self.transaction_queue.lock().unwrap().minimal_gas_price() * x!(110) / x!(100) + *self.transaction_queue.lock().unwrap().minimal_gas_price() * 110.into() / 100.into() } fn sensible_gas_limit(&self) -> U256 { - *self.gas_floor_target.read().unwrap() / x!(5) + *self.gas_floor_target.read().unwrap() / 5.into() } fn transactions_limit(&self) -> usize { diff --git a/sync/src/chain.rs b/sync/src/chain.rs index 2f31f1d47..dcfdc1c16 100644 --- a/sync/src/chain.rs +++ b/sync/src/chain.rs @@ -1310,8 +1310,8 @@ mod tests { fn get_dummy_block(order: u32, parent_hash: H256) -> Bytes { let mut header = Header::new(); - header.gas_limit = x!(0); - header.difficulty = x!(order * 100); + header.gas_limit = 0.into(); + header.difficulty = (order * 100).into(); header.timestamp = (order * 10) as u64; header.number = order as u64; header.parent_hash = parent_hash; @@ -1327,7 +1327,7 @@ mod tests { fn get_dummy_blocks(order: u32, parent_hash: H256) -> Bytes { let mut rlp = RlpStream::new_list(1); rlp.append_raw(&get_dummy_block(order, parent_hash), 1); - let difficulty: U256 = x!(100 * order); + let difficulty: U256 = (100 * order).into(); rlp.append(&difficulty); rlp.out() } diff --git a/util/src/common.rs b/util/src/common.rs index 7ed660f29..c1de582df 100644 --- a/util/src/common.rs +++ b/util/src/common.rs @@ -68,20 +68,6 @@ macro_rules! mapx { }} } -#[macro_export] -macro_rules! x { - ( $x:expr ) => { - From::from($x) - } -} - -#[macro_export] -macro_rules! xx { - ( $x:expr ) => { - From::from(From::from($x)) - } -} - #[macro_export] macro_rules! flush { ($($arg:tt)*) => ($crate::flush(format!("{}", format_args!($($arg)*)))); diff --git a/util/src/keys/store.rs b/util/src/keys/store.rs index 296f7ad37..3402596d3 100644 --- a/util/src/keys/store.rs +++ b/util/src/keys/store.rs @@ -627,7 +627,7 @@ mod tests { sstore.sign(&address, &H256::random()).unwrap() }; - assert!(signature != x!(0)); + assert!(signature != 0.into()); } #[test] From da9d6bf8d5a8824d4ea712403adace4bce5b256b Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Tue, 31 May 2016 17:29:01 +0200 Subject: [PATCH 19/30] rename [hash_]mapx to [hash_]map_into --- ethcore/src/pod_account.rs | 4 ++-- util/src/common.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ethcore/src/pod_account.rs b/ethcore/src/pod_account.rs index 6f3949db3..96642d1f8 100644 --- a/ethcore/src/pod_account.rs +++ b/ethcore/src/pod_account.rs @@ -154,13 +154,13 @@ mod test { balance: 0.into(), nonce: 0.into(), code: vec![], - storage: mapx![1 => 1, 2 => 2, 3 => 3, 4 => 4, 5 => 0, 6 => 0, 7 => 0] + storage: map_into![1 => 1, 2 => 2, 3 => 3, 4 => 4, 5 => 0, 6 => 0, 7 => 0] }; let b = PodAccount { balance: 0.into(), nonce: 0.into(), code: vec![], - storage: mapx![1 => 1, 2 => 3, 3 => 0, 5 => 0, 7 => 7, 8 => 0, 9 => 9] + storage: map_into![1 => 1, 2 => 3, 3 => 0, 5 => 0, 7 => 7, 8 => 0, 9 => 9] }; assert_eq!(AccountDiff::diff_pod(Some(&a), Some(&b)), Some(AccountDiff { balance: Diff::Same, diff --git a/util/src/common.rs b/util/src/common.rs index c1de582df..2eb2ea017 100644 --- a/util/src/common.rs +++ b/util/src/common.rs @@ -36,7 +36,7 @@ macro_rules! hash_map { } #[macro_export] -macro_rules! hash_mapx { +macro_rules! hash_map_into { ( $( $x:expr => $y:expr ),* ) => {{ let mut x = HashMap::new(); $( @@ -58,7 +58,7 @@ macro_rules! map { } #[macro_export] -macro_rules! mapx { +macro_rules! map_into { ( $( $x:expr => $y:expr ),* ) => {{ let mut x = BTreeMap::new(); $( From 4ccaabde40365d0815a9fcd0c69eecc041cea784 Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Tue, 31 May 2016 19:30:40 +0200 Subject: [PATCH 20/30] add transaction count verifier tests --- rpc/src/v1/tests/eth.rs | 96 ++++++++++++++++++++++++++++++++++++----- rpc/src/v1/tests/mod.rs | 25 ++++++++--- 2 files changed, 104 insertions(+), 17 deletions(-) diff --git a/rpc/src/v1/tests/eth.rs b/rpc/src/v1/tests/eth.rs index 9b2cb3195..0c159ceb2 100644 --- a/rpc/src/v1/tests/eth.rs +++ b/rpc/src/v1/tests/eth.rs @@ -19,15 +19,18 @@ use std::collections::HashMap; use std::sync::Arc; use std::str::FromStr; +use ethcore::ids::BlockID; use ethcore::client::{Client, BlockChainClient, ClientConfig}; use ethcore::spec::{Genesis, Spec}; use ethcore::block::Block; +use ethcore::views::BlockView; use ethcore::ethereum; use ethminer::{Miner, MinerService, ExternalMiner}; 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; @@ -72,8 +75,8 @@ struct EthTester { } impl EthTester { - fn from_chain(chain: BlockChain) -> Self { - let tester = Self::from_spec_provider(|| make_spec(&chain)); + 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) { @@ -83,7 +86,9 @@ impl EthTester { } } - assert!(tester.client.chain_info().best_block_hash == chain.best_block.into()); + tester.client.flush_queue(); + + assert!(tester.client.chain_info().best_block_hash == chain.best_block.clone().into()); tester } @@ -116,13 +121,13 @@ impl EthTester { #[test] fn harness_works() { let chain: BlockChain = extract_chain!("BlockchainTests/bcUncleTest"); - let _ = EthTester::from_chain(chain); + let _ = EthTester::from_chain(&chain); } #[test] fn eth_get_balance() { let chain = extract_chain!("BlockchainTests/bcWalletTest", "wallet2outOf3txs"); - let tester = EthTester::from_chain(chain); + let tester = EthTester::from_chain(&chain); // final account state let req_latest = r#"{ "jsonrpc": "2.0", @@ -148,7 +153,7 @@ fn eth_get_balance() { #[test] fn eth_block_number() { let chain = extract_chain!("BlockchainTests/bcRPC_API_Test"); - let tester = EthTester::from_chain(chain); + let tester = EthTester::from_chain(&chain); let req_number = r#"{ "jsonrpc": "2.0", "method": "eth_blockNumber", @@ -206,7 +211,6 @@ const TRANSACTION_COUNT_SPEC: &'static [u8] = br#"{ } "#; -#[cfg(test)] #[test] fn eth_transaction_count() { use util::crypto::Secret; @@ -271,4 +275,76 @@ fn eth_transaction_count() { 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); -} \ No newline at end of file +} + +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); + } + } + } + + let _panic = PanicHandler(name); + + 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 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) + } + + 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#" + }"#; + + 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"); \ No newline at end of file diff --git a/rpc/src/v1/tests/mod.rs b/rpc/src/v1/tests/mod.rs index 3455bfd1f..9e71addd4 100644 --- a/rpc/src/v1/tests/mod.rs +++ b/rpc/src/v1/tests/mod.rs @@ -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,21 @@ 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); + } + } + } +} + #[cfg(test)] mod mocked; #[cfg(test)] From c81e4e24b303a765120fefe15600705918aa9afe Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Tue, 31 May 2016 19:51:24 +0200 Subject: [PATCH 21/30] add ability to have heavy tests --- rpc/src/v1/tests/mod.rs | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/rpc/src/v1/tests/mod.rs b/rpc/src/v1/tests/mod.rs index 9e71addd4..a48727475 100644 --- a/rpc/src/v1/tests/mod.rs +++ b/rpc/src/v1/tests/mod.rs @@ -43,7 +43,27 @@ macro_rules! register_test { $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)] From ec7af964ab75f6c6e12a1449684bf0b5fa233e3a Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Tue, 31 May 2016 21:01:47 +0200 Subject: [PATCH 22/30] correct map macro invocation --- ethcore/src/pod_account.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ethcore/src/pod_account.rs b/ethcore/src/pod_account.rs index 96642d1f8..65146296a 100644 --- a/ethcore/src/pod_account.rs +++ b/ethcore/src/pod_account.rs @@ -171,7 +171,7 @@ mod test { 3.into() => Diff::new(3.into(), 0.into()), 4.into() => Diff::new(4.into(), 0.into()), 7.into() => Diff::new(0.into(), 7.into()), - 9.into() => Diff::new(0.into(), 9.into()), + 9.into() => Diff::new(0.into(), 9.into()) ], })); } From 4146e1f02b45f548834a4e3507420ec18b725046 Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Tue, 31 May 2016 21:18:54 +0200 Subject: [PATCH 23/30] add empty rule to map macros --- util/src/common.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/util/src/common.rs b/util/src/common.rs index 2eb2ea017..0e0cd7757 100644 --- a/util/src/common.rs +++ b/util/src/common.rs @@ -26,6 +26,7 @@ pub use sha3::*; #[macro_export] macro_rules! hash_map { + () => { HashMap::new() }; ( $( $x:expr => $y:expr ),* ) => {{ let mut x = HashMap::new(); $( @@ -37,6 +38,7 @@ macro_rules! hash_map { #[macro_export] macro_rules! hash_map_into { + () => { HashMap::new() }; ( $( $x:expr => $y:expr ),* ) => {{ let mut x = HashMap::new(); $( @@ -48,6 +50,7 @@ macro_rules! hash_map_into { #[macro_export] macro_rules! map { + () => { BTreeMap::new() }; ( $( $x:expr => $y:expr ),* ) => {{ let mut x = BTreeMap::new(); $( @@ -59,6 +62,7 @@ macro_rules! map { #[macro_export] macro_rules! map_into { + () => { BTreeMap::new() }; ( $( $x:expr => $y:expr ),* ) => {{ let mut x = BTreeMap::new(); $( From bbb858b3862856bf1d18b559b263075be0149d89 Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Tue, 31 May 2016 21:38:05 +0200 Subject: [PATCH 24/30] address small syntax breakages --- ethcore/src/state.rs | 24 ++++++++++++------------ ethcore/src/state_diff.rs | 8 ++++---- ethcore/src/tests/helpers.rs | 2 +- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/ethcore/src/state.rs b/ethcore/src/state.rs index 6e0e4f2f6..c099b17a5 100644 --- a/ethcore/src/state.rs +++ b/ethcore/src/state.rs @@ -389,7 +389,7 @@ fn should_apply_create_transaction() { data: FromHex::from_hex("601080600c6000396000f3006000355415600957005b60203560003555").unwrap(), }.sign(&"".sha3()); - state.add_balance(t.sender().as_ref().unwrap(), &100.into()); + state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); let vm_factory = Default::default(); let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap(); let expected_trace = Some(Trace { @@ -450,7 +450,7 @@ fn should_trace_failed_create_transaction() { data: FromHex::from_hex("5b600056").unwrap(), }.sign(&"".sha3()); - state.add_balance(t.sender().as_ref().unwrap(), &100.into()); + state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); let vm_factory = Default::default(); let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap(); let expected_trace = Some(Trace { @@ -489,7 +489,7 @@ fn should_trace_call_transaction() { }.sign(&"".sha3()); state.init_code(&0xa.into(), FromHex::from_hex("6000").unwrap()); - state.add_balance(t.sender().as_ref().unwrap(), &100.into()); + state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); let vm_factory = Default::default(); let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap(); let expected_trace = Some(Trace { @@ -531,7 +531,7 @@ fn should_trace_basic_call_transaction() { data: vec![], }.sign(&"".sha3()); - state.add_balance(t.sender().as_ref().unwrap(), &100.into()); + state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); let vm_factory = Default::default(); let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap(); let expected_trace = Some(Trace { @@ -745,7 +745,7 @@ fn should_trace_failed_call_transaction() { }.sign(&"".sha3()); state.init_code(&0xa.into(), FromHex::from_hex("5b600056").unwrap()); - state.add_balance(t.sender().as_ref().unwrap(), &100.into()); + state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); let vm_factory = Default::default(); let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap(); let expected_trace = Some(Trace { @@ -788,7 +788,7 @@ fn should_trace_call_with_subcall_transaction() { state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap()); state.init_code(&0xb.into(), FromHex::from_hex("6000").unwrap()); - state.add_balance(t.sender().as_ref().unwrap(), &100.into()); + state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); let vm_factory = Default::default(); let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap(); let expected_trace = Some(Trace { @@ -845,7 +845,7 @@ fn should_trace_call_with_basic_subcall_transaction() { }.sign(&"".sha3()); state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006045600b6000f1").unwrap()); - state.add_balance(t.sender().as_ref().unwrap(), &100.into()); + state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); let vm_factory = Default::default(); let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap(); let expected_trace = Some(Trace { @@ -899,7 +899,7 @@ fn should_not_trace_call_with_invalid_basic_subcall_transaction() { }.sign(&"".sha3()); state.init_code(&0xa.into(), FromHex::from_hex("600060006000600060ff600b6000f1").unwrap()); // not enough funds. - state.add_balance(t.sender().as_ref().unwrap(), &100.into()); + state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); let vm_factory = Default::default(); let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap(); let expected_trace = Some(Trace { @@ -943,7 +943,7 @@ fn should_trace_failed_subcall_transaction() { state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap()); state.init_code(&0xb.into(), FromHex::from_hex("5b600056").unwrap()); - state.add_balance(t.sender().as_ref().unwrap(), &100.into()); + state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); let vm_factory = Default::default(); let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap(); let expected_trace = Some(Trace { @@ -999,7 +999,7 @@ fn should_trace_call_with_subcall_with_subcall_transaction() { state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap()); state.init_code(&0xb.into(), FromHex::from_hex("60006000600060006000600c602b5a03f1").unwrap()); state.init_code(&0xc.into(), FromHex::from_hex("6000").unwrap()); - state.add_balance(t.sender().as_ref().unwrap(), &100.into()); + state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); let vm_factory = Default::default(); let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap(); let expected_trace = Some(Trace { @@ -1032,7 +1032,7 @@ fn should_trace_call_with_subcall_with_subcall_transaction() { depth: 2, action: trace::Action::Call(trace::Call { from: 0xb.into(), - to: 0xb.into(), + to: 0xc.into(), value: 0.into(), gas: 78868.into(), input: vec![], @@ -1072,7 +1072,7 @@ fn should_trace_failed_subcall_with_subcall_transaction() { state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap()); state.init_code(&0xb.into(), FromHex::from_hex("60006000600060006000600c602b5a03f1505b601256").unwrap()); state.init_code(&0xc.into(), FromHex::from_hex("6000").unwrap()); - state.add_balance(t.sender().as_ref().unwrap(), &100.into()); + state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); let vm_factory = Default::default(); let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap(); let expected_trace = Some(Trace { diff --git a/ethcore/src/state_diff.rs b/ethcore/src/state_diff.rs index 1c1975e70..c362d96d1 100644 --- a/ethcore/src/state_diff.rs +++ b/ethcore/src/state_diff.rs @@ -83,7 +83,7 @@ mod test { let a = PodState::from(map![ 1.into() => PodAccount::new(69.into(), 0.into(), vec![], map![]) ]); let b = PodState::from(map![ 1.into() => PodAccount::new(69.into(), 0.into(), vec![], map![]), - 2.into() => PodAccount::new(69.into(), 0.into(),, vec![], map![]) + 2.into() => PodAccount::new(69.into(), 0.into(), vec![], map![]) ]); assert_eq!(StateDiff::diff_pod(&a, &b), StateDiff(map![ 2.into() => AccountDiff{ @@ -107,11 +107,11 @@ mod test { fn change_with_unchanged() { let a = PodState::from(map![ 1.into() => PodAccount::new(69.into(), 0.into(), vec![], map![]), - 2.into() => PodAccount::new(69.into(), 0.into(),, vec![], map![]) + 2.into() => PodAccount::new(69.into(), 0.into(), vec![], map![]) ]); let b = PodState::from(map![ - 1.into() => PodAccount::new(69.into(), 1.into(),, vec![], map![]), - 2.into() => PodAccount::new(69.into(), 0.into(),, vec![], map![]) + 1.into() => PodAccount::new(69.into(), 1.into(), vec![], map![]), + 2.into() => PodAccount::new(69.into(), 0.into(), vec![], map![]) ]); assert_eq!(StateDiff::diff_pod(&a, &b), StateDiff(map![ 1.into() => AccountDiff{ diff --git a/ethcore/src/tests/helpers.rs b/ethcore/src/tests/helpers.rs index 6a370e49f..22282ccdd 100644 --- a/ethcore/src/tests/helpers.rs +++ b/ethcore/src/tests/helpers.rs @@ -99,7 +99,7 @@ pub fn create_test_block(header: &Header) -> Bytes { fn create_unverifiable_block_header(order: u32, parent_hash: H256) -> Header { let mut header = Header::new(); header.gas_limit = 0.into(); - header.difficulty = order * 100.into(); + header.difficulty = (order * 100).into(); header.timestamp = (order * 10) as u64; header.number = order as u64; header.parent_hash = parent_hash; From 99573286074b3daebf1690fb30a9397c495e27ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Tue, 31 May 2016 21:39:11 +0200 Subject: [PATCH 25/30] DENY frames from other origins to prevent clickjacking --- dapps/src/apps.rs | 6 ++++-- dapps/src/page/mod.rs | 20 ++++++++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/dapps/src/apps.rs b/dapps/src/apps.rs index 559282584..28f1979bf 100644 --- a/dapps/src/apps.rs +++ b/dapps/src/apps.rs @@ -38,11 +38,13 @@ pub fn utils() -> Box { pub fn all_endpoints() -> Endpoints { let mut pages = Endpoints::new(); - pages.insert("proxy".to_owned(), ProxyPac::boxed()); + pages.insert("proxy".into(), ProxyPac::boxed()); + pages.insert("home".into(), Box::new( + PageEndpoint::new_safe_to_embed(parity_dapps_builtins::App::default()) + )); insert::(&mut pages, "status"); insert::(&mut pages, "parity"); - insert::(&mut pages, "home"); wallet_page(&mut pages); daodapp_page(&mut pages); diff --git a/dapps/src/page/mod.rs b/dapps/src/page/mod.rs index 71989bca7..c7bfd83ec 100644 --- a/dapps/src/page/mod.rs +++ b/dapps/src/page/mod.rs @@ -30,6 +30,8 @@ pub struct PageEndpoint { pub app: Arc, /// Prefix to strip from the path (when `None` deducted from `app_id`) pub prefix: Option, + /// Safe to be loaded in frame by other origin. (use wisely!) + safe_to_embed: bool, } impl PageEndpoint { @@ -37,6 +39,7 @@ impl PageEndpoint { PageEndpoint { app: Arc::new(app), prefix: None, + safe_to_embed: false, } } @@ -44,6 +47,18 @@ impl PageEndpoint { PageEndpoint { app: Arc::new(app), prefix: Some(prefix), + safe_to_embed: false, + } + } + + /// Creates new `PageEndpoint` which can be safely used in iframe + /// even from different origin. It might be dangerous (clickjacking). + /// Use wisely! + pub fn new_safe_to_embed(app: T) -> Self { + PageEndpoint { + app: Arc::new(app), + prefix: None, + safe_to_embed: true, } } } @@ -61,6 +76,7 @@ impl Endpoint for PageEndpoint { path: path, file: None, write_pos: 0, + safe_to_embed: self.safe_to_embed, }) } } @@ -83,6 +99,7 @@ struct PageHandler { path: EndpointPath, file: Option, write_pos: usize, + safe_to_embed: bool, } impl PageHandler { @@ -128,6 +145,9 @@ impl server::Handler for PageHandler { if let Some(f) = self.file.as_ref().and_then(|f| self.app.file(f)) { res.set_status(StatusCode::Ok); res.headers_mut().set(header::ContentType(f.content_type.parse().unwrap())); + if !self.safe_to_embed { + res.headers_mut().set_raw("X-Frame-Options", vec![b"SAMEORIGIN".to_vec()]); + } Next::write() } else { res.set_status(StatusCode::NotFound); From 5fd4b9d7bd74e641779864b05ce007bf23da008f Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Tue, 31 May 2016 21:50:25 +0200 Subject: [PATCH 26/30] formatting fix --- rpc/src/v1/tests/eth.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/rpc/src/v1/tests/eth.rs b/rpc/src/v1/tests/eth.rs index 0c159ceb2..30815b8a8 100644 --- a/rpc/src/v1/tests/eth.rs +++ b/rpc/src/v1/tests/eth.rs @@ -102,8 +102,13 @@ impl EthTester { let miner_service = miner_service(spec_provider(), account_provider.clone()); let external_miner = Arc::new(ExternalMiner::default()); - let eth_client = EthClient::new(&client, &sync_provider, &account_provider, - &miner_service, &external_miner); + let eth_client = EthClient::new( + &client, + &sync_provider, + &account_provider, + &miner_service, + &external_miner + ); let handler = IoHandler::new(); let delegate = eth_client.to_delegate(); From ae1bcd6a5b7880e10dae42b00c90300e3d9de6e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Tue, 31 May 2016 21:50:34 +0200 Subject: [PATCH 27/30] Disabling ethcore APIs for RPC and IPC --- dapps/src/page/mod.rs | 1 + parity/cli.rs | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/dapps/src/page/mod.rs b/dapps/src/page/mod.rs index c7bfd83ec..819988310 100644 --- a/dapps/src/page/mod.rs +++ b/dapps/src/page/mod.rs @@ -212,6 +212,7 @@ fn should_extract_path_with_appid() { }, file: None, write_pos: 0, + safe_to_embed: true, }; // when diff --git a/parity/cli.rs b/parity/cli.rs index 60b622bf7..95b77a00d 100644 --- a/parity/cli.rs +++ b/parity/cli.rs @@ -76,13 +76,13 @@ API and Console Options: interface. APIS is a comma-delimited list of API name. Possible name are web3, eth, net, personal, ethcore, traces. - [default: web3,eth,net,personal,ethcore,traces]. + [default: web3,eth,net,personal,traces]. --ipc-off Disable JSON-RPC over IPC service. --ipc-path PATH Specify custom path for JSON-RPC over IPC service [default: $HOME/.parity/jsonrpc.ipc]. --ipc-apis APIS Specify custom API set available via JSON-RPC over - IPC [default: web3,eth,net,personal,ethcore]. + IPC [default: web3,eth,net,personal,traces]. --dapps-off Disable the Dapps server (e.g. status page). --dapps-port PORT Specify the port portion of the Dapps server From 67421fc69e3053ec5ed0c1d5bdb02711b50084b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Tue, 31 May 2016 21:56:41 +0200 Subject: [PATCH 28/30] Adding explanatory comment for safe_to_embed --- dapps/src/apps.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/dapps/src/apps.rs b/dapps/src/apps.rs index 28f1979bf..130b20fb9 100644 --- a/dapps/src/apps.rs +++ b/dapps/src/apps.rs @@ -40,6 +40,9 @@ pub fn all_endpoints() -> Endpoints { let mut pages = Endpoints::new(); pages.insert("proxy".into(), ProxyPac::boxed()); + // Home page needs to be safe embed + // because we use Cross-Origin LocalStorage. + // TODO [ToDr] Account naming should be moved to parity. pages.insert("home".into(), Box::new( PageEndpoint::new_safe_to_embed(parity_dapps_builtins::App::default()) )); From ae572cb8f5be2b52ec3b5839605de6158637ab0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Wed, 1 Jun 2016 13:25:20 +0200 Subject: [PATCH 29/30] Using ordered hashmap to keep the order of dapps on home screen --- dapps/src/endpoint.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dapps/src/endpoint.rs b/dapps/src/endpoint.rs index 60708f549..28ca6ea11 100644 --- a/dapps/src/endpoint.rs +++ b/dapps/src/endpoint.rs @@ -21,7 +21,7 @@ use hyper::{header, server, Decoder, Encoder, Next}; use hyper::net::HttpStream; use std::io::Write; -use std::collections::HashMap; +use std::collections::BTreeMap; #[derive(Debug, PartialEq, Default, Clone)] pub struct EndpointPath { @@ -45,7 +45,7 @@ pub trait Endpoint : Send + Sync { fn to_handler(&self, path: EndpointPath) -> Box>; } -pub type Endpoints = HashMap>; +pub type Endpoints = BTreeMap>; pub type Handler = server::Handler; pub struct ContentHandler { From 99e26b8480a2678954daa69852aa966d33f8e498 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Wed, 1 Jun 2016 19:37:34 +0200 Subject: [PATCH 30/30] Simple signing queue, confirmation APIs exposed in signer WebSockets. (#1182) * Splitting methods requiring signing into separate trait * Single place where RPC apis are created. * Separating eth_filter * Separating eth_signing * Stubs for Personal Signer methods * Test for EthSigningQueueClient * TransactionConfirmation API * Exposing PersonalSigner API * Defining ApiSets dependent on context * Removing types * Fixing default impl * Fixing un-mocked tests * Update signing_queue.rs [ci skip] * Removing unused import [ci skip] --- Cargo.lock | 5 - dapps/src/lib.rs | 13 +- parity/dapps.rs | 24 +- parity/main.rs | 27 +-- parity/rpc.rs | 79 ++----- parity/rpc_apis.rs | 168 ++++++++++++++ parity/signer.rs | 18 +- rpc/src/lib.rs | 19 +- rpc/src/v1/helpers/mod.rs | 2 + rpc/src/v1/helpers/signing_queue.rs | 108 +++++++++ rpc/src/v1/impls/eth.rs | 246 ++------------------- rpc/src/v1/impls/eth_filter.rs | 214 ++++++++++++++++++ rpc/src/v1/impls/eth_signing.rs | 111 ++++++++++ rpc/src/v1/impls/mod.rs | 10 +- rpc/src/v1/impls/personal.rs | 2 +- rpc/src/v1/impls/personal_signer.rs | 93 ++++++++ rpc/src/v1/mod.rs | 3 +- rpc/src/v1/tests/eth.rs | 15 +- rpc/src/v1/tests/mocked/eth.rs | 5 +- rpc/src/v1/tests/mocked/eth_signing.rs | 75 +++++++ rpc/src/v1/tests/mocked/mod.rs | 2 + rpc/src/v1/tests/mocked/personal.rs | 2 +- rpc/src/v1/tests/mocked/personal_signer.rs | 169 ++++++++++++++ rpc/src/v1/traits/eth.rs | 25 ++- rpc/src/v1/traits/mod.rs | 6 +- rpc/src/v1/traits/personal.rs | 23 ++ rpc/src/v1/types/mod.rs.in | 2 +- rpc/src/v1/types/transaction_request.rs | 41 +++- signer/Cargo.toml | 8 - signer/build.rs | 27 --- signer/src/lib.rs | 8 - signer/src/signing_queue.rs | 74 ------- signer/src/types/mod.rs | 23 -- signer/src/types/mod.rs.in | 25 --- signer/src/ws_server/mod.rs | 12 +- 35 files changed, 1140 insertions(+), 544 deletions(-) create mode 100644 parity/rpc_apis.rs create mode 100644 rpc/src/v1/helpers/signing_queue.rs create mode 100644 rpc/src/v1/impls/eth_filter.rs create mode 100644 rpc/src/v1/impls/eth_signing.rs create mode 100644 rpc/src/v1/impls/personal_signer.rs create mode 100644 rpc/src/v1/tests/mocked/eth_signing.rs create mode 100644 rpc/src/v1/tests/mocked/personal_signer.rs delete mode 100644 signer/src/signing_queue.rs delete mode 100644 signer/src/types/mod.rs delete mode 100644 signer/src/types/mod.rs.in diff --git a/Cargo.lock b/Cargo.lock index cc2a8ef8c..c81020f66 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -358,12 +358,7 @@ dependencies = [ "ethcore-util 1.2.0", "jsonrpc-core 2.0.4 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_codegen 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "syntex 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)", "ws 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", ] diff --git a/dapps/src/lib.rs b/dapps/src/lib.rs index 27c215108..231e7b080 100644 --- a/dapps/src/lib.rs +++ b/dapps/src/lib.rs @@ -52,6 +52,7 @@ extern crate serde_json; extern crate jsonrpc_core; extern crate jsonrpc_http_server; extern crate parity_dapps; +extern crate ethcore_rpc; mod endpoint; mod apps; @@ -66,6 +67,7 @@ use std::net::SocketAddr; use std::collections::HashMap; use jsonrpc_core::{IoHandler, IoDelegate}; use router::auth::{Authorization, NoAuth, HttpBasicAuth}; +use ethcore_rpc::Extendable; static DAPPS_DOMAIN : &'static str = ".parity"; @@ -74,6 +76,12 @@ pub struct ServerBuilder { handler: Arc, } +impl Extendable for ServerBuilder { + fn add_delegate(&self, delegate: IoDelegate) { + self.handler.add_delegate(delegate); + } +} + impl ServerBuilder { /// Construct new dapps server pub fn new() -> Self { @@ -82,11 +90,6 @@ impl ServerBuilder { } } - /// Add io delegate. - pub fn add_delegate(&self, delegate: IoDelegate) where D: Send + Sync + 'static { - self.handler.add_delegate(delegate); - } - /// Asynchronously start server with no authentication, /// returns result with `Server` handle on success or an error. pub fn start_unsecure_http(&self, addr: &SocketAddr) -> Result { diff --git a/parity/dapps.rs b/parity/dapps.rs index 986e3dd07..91742d9e3 100644 --- a/parity/dapps.rs +++ b/parity/dapps.rs @@ -17,14 +17,9 @@ use std::sync::Arc; use std::str::FromStr; use std::net::SocketAddr; -use ethcore::client::Client; -use ethsync::EthSync; -use ethminer::{Miner, ExternalMiner}; -use util::RotatingLogger; use util::panics::PanicHandler; -use util::keys::store::AccountService; -use util::network_settings::NetworkSettings; use die::*; +use rpc_apis; #[cfg(feature = "dapps")] pub use ethcore_dapps::Server as WebappServer; @@ -41,13 +36,7 @@ pub struct Configuration { pub struct Dependencies { pub panic_handler: Arc, - pub client: Arc, - pub sync: Arc, - pub secret_store: Arc, - pub miner: Arc, - pub external_miner: Arc, - pub logger: Arc, - pub settings: Arc, + pub apis: Arc, } pub fn new(configuration: Configuration, deps: Dependencies) -> Option { @@ -92,17 +81,10 @@ pub fn setup_dapps_server( url: &SocketAddr, auth: Option<(String, String)> ) -> WebappServer { - use ethcore_rpc::v1::*; use ethcore_dapps as dapps; let server = dapps::ServerBuilder::new(); - server.add_delegate(Web3Client::new().to_delegate()); - server.add_delegate(NetClient::new(&deps.sync).to_delegate()); - server.add_delegate(EthClient::new(&deps.client, &deps.sync, &deps.secret_store, &deps.miner, &deps.external_miner).to_delegate()); - server.add_delegate(EthFilterClient::new(&deps.client, &deps.miner).to_delegate()); - server.add_delegate(PersonalClient::new(&deps.secret_store, &deps.client, &deps.miner).to_delegate()); - server.add_delegate(EthcoreClient::new(&deps.miner, deps.logger.clone(), deps.settings.clone()).to_delegate()); - + let server = rpc_apis::setup_rpc(server, deps.apis.clone(), rpc_apis::ApiSet::UnsafeContext); let start_result = match auth { None => { server.start_unsecure_http(url) diff --git a/parity/main.rs b/parity/main.rs index 7f16d28a5..cae6aa614 100644 --- a/parity/main.rs +++ b/parity/main.rs @@ -67,6 +67,7 @@ mod cli; mod configuration; mod migration; mod signer; +mod rpc_apis; use std::io::{Write, Read, BufReader, BufRead}; use std::ops::Deref; @@ -195,8 +196,9 @@ fn execute_client(conf: Configuration, spec: Spec, client_config: ClientConfig) // Sync let sync = EthSync::register(service.network(), sync_config, client.clone(), miner.clone()); - let dependencies = Arc::new(rpc::Dependencies { - panic_handler: panic_handler.clone(), + let deps_for_rpc_apis = Arc::new(rpc_apis::Dependencies { + signer_enabled: conf.args.flag_signer, + signer_queue: Arc::new(rpc_apis::ConfirmationsQueue::default()), client: client.clone(), sync: sync.clone(), secret_store: account_service.clone(), @@ -206,6 +208,11 @@ fn execute_client(conf: Configuration, spec: Spec, client_config: ClientConfig) settings: network_settings.clone(), }); + let dependencies = rpc::Dependencies { + panic_handler: panic_handler.clone(), + apis: deps_for_rpc_apis.clone(), + }; + // Setup http rpc let rpc_server = rpc::new_http(rpc::HttpConfiguration { enabled: network_settings.rpc_enabled, @@ -227,26 +234,16 @@ fn execute_client(conf: Configuration, spec: Spec, client_config: ClientConfig) pass: conf.args.flag_dapps_pass.clone(), }, dapps::Dependencies { panic_handler: panic_handler.clone(), - client: client.clone(), - sync: sync.clone(), - secret_store: account_service.clone(), - miner: miner.clone(), - external_miner: external_miner.clone(), - logger: logger.clone(), - settings: network_settings.clone(), + apis: deps_for_rpc_apis.clone(), }); // Set up a signer let signer_server = signer::start(signer::Configuration { - enabled: conf.args.flag_signer, + enabled: deps_for_rpc_apis.signer_enabled, port: conf.args.flag_signer_port, }, signer::Dependencies { panic_handler: panic_handler.clone(), - client: client.clone(), - sync: sync.clone(), - secret_store: account_service.clone(), - miner: miner.clone(), - external_miner: external_miner.clone(), + apis: deps_for_rpc_apis.clone(), }); // Register IO handler diff --git a/parity/rpc.rs b/parity/rpc.rs index 60766263b..66f504408 100644 --- a/parity/rpc.rs +++ b/parity/rpc.rs @@ -15,19 +15,13 @@ // along with Parity. If not, see . -use std::collections::BTreeMap; use std::str::FromStr; use std::sync::Arc; use std::net::SocketAddr; -use ethcore::client::Client; -use ethsync::EthSync; -use ethminer::{Miner, ExternalMiner}; -use util::RotatingLogger; use util::panics::PanicHandler; -use util::keys::store::AccountService; -use util::network_settings::NetworkSettings; use die::*; use jsonipc; +use rpc_apis; #[cfg(feature = "rpc")] pub use ethcore_rpc::Server as RpcServer; @@ -52,16 +46,10 @@ pub struct IpcConfiguration { pub struct Dependencies { pub panic_handler: Arc, - pub client: Arc, - pub sync: Arc, - pub secret_store: Arc, - pub miner: Arc, - pub external_miner: Arc, - pub logger: Arc, - pub settings: Arc, + pub apis: Arc, } -pub fn new_http(conf: HttpConfiguration, deps: &Arc) -> Option { +pub fn new_http(conf: HttpConfiguration, deps: &Dependencies) -> Option { if !conf.enabled { return None; } @@ -78,58 +66,23 @@ pub fn new_http(conf: HttpConfiguration, deps: &Arc) -> Option) -> Option { +pub fn new_ipc(conf: IpcConfiguration, deps: &Dependencies) -> Option { if !conf.enabled { return None; } let apis = conf.apis.split(',').collect(); Some(setup_ipc_rpc_server(deps, &conf.socket_addr, apis)) } -fn setup_rpc_server(apis: Vec<&str>, deps: &Arc) -> Server { - use ethcore_rpc::v1::*; - +fn setup_rpc_server(apis: Vec<&str>, deps: &Dependencies) -> Server { + let apis = rpc_apis::from_str(apis); let server = Server::new(); - let mut modules = BTreeMap::new(); - for api in apis.into_iter() { - match api { - "web3" => { - modules.insert("web3".to_owned(), "1.0".to_owned()); - server.add_delegate(Web3Client::new().to_delegate()); - }, - "net" => { - modules.insert("net".to_owned(), "1.0".to_owned()); - server.add_delegate(NetClient::new(&deps.sync).to_delegate()); - }, - "eth" => { - modules.insert("eth".to_owned(), "1.0".to_owned()); - server.add_delegate(EthClient::new(&deps.client, &deps.sync, &deps.secret_store, &deps.miner, &deps.external_miner).to_delegate()); - server.add_delegate(EthFilterClient::new(&deps.client, &deps.miner).to_delegate()); - }, - "personal" => { - modules.insert("personal".to_owned(), "1.0".to_owned()); - server.add_delegate(PersonalClient::new(&deps.secret_store, &deps.client, &deps.miner).to_delegate()) - }, - "ethcore" => { - modules.insert("ethcore".to_owned(), "1.0".to_owned()); - server.add_delegate(EthcoreClient::new(&deps.miner, deps.logger.clone(), deps.settings.clone()).to_delegate()) - }, - "traces" => { - modules.insert("traces".to_owned(), "1.0".to_owned()); - server.add_delegate(TracesClient::new(&deps.client).to_delegate()) - }, - _ => { - die!("{}: Invalid API name to be enabled.", api); - }, - } - } - server.add_delegate(RpcClient::new(modules).to_delegate()); - server + rpc_apis::setup_rpc(server, deps.apis.clone(), rpc_apis::ApiSet::List(apis)) } #[cfg(not(feature = "rpc"))] pub fn setup_http_rpc_server( - _deps: Dependencies, + _deps: &Dependencies, _url: &SocketAddr, - _cors_domain: Option, + _cors_domain: Vec, _apis: Vec<&str>, ) -> ! { die!("Your Parity version has been compiled without JSON-RPC support.") @@ -137,27 +90,31 @@ pub fn setup_http_rpc_server( #[cfg(feature = "rpc")] pub fn setup_http_rpc_server( - dependencies: &Arc, + dependencies: &Dependencies, url: &SocketAddr, cors_domains: Vec, apis: Vec<&str>, ) -> RpcServer { let server = setup_rpc_server(apis, dependencies); let start_result = server.start_http(url, cors_domains); - let deps = dependencies.clone(); + let ph = dependencies.panic_handler.clone(); match start_result { Err(RpcServerError::IoError(err)) => die_with_io_error("RPC", err), Err(e) => die!("RPC: {:?}", e), Ok(server) => { server.set_panic_handler(move || { - deps.panic_handler.notify_all("Panic in RPC thread.".to_owned()); + ph.notify_all("Panic in RPC thread.".to_owned()); }); server }, } } - -pub fn setup_ipc_rpc_server(dependencies: &Arc, addr: &str, apis: Vec<&str>) -> jsonipc::Server { +#[cfg(not(feature = "rpc"))] +pub fn setup_ipc_rpc_server(_dependencies: &Dependencies, _addr: &str, _apis: Vec<&str>) -> ! { + die!("Your Parity version has been compiled without JSON-RPC support.") +} +#[cfg(feature = "rpc")] +pub fn setup_ipc_rpc_server(dependencies: &Dependencies, addr: &str, apis: Vec<&str>) -> jsonipc::Server { let server = setup_rpc_server(apis, dependencies); match server.start_ipc(addr) { Err(jsonipc::Error::Io(io_error)) => die_with_io_error("RPC", io_error), diff --git a/parity/rpc_apis.rs b/parity/rpc_apis.rs new file mode 100644 index 000000000..c73a70fee --- /dev/null +++ b/parity/rpc_apis.rs @@ -0,0 +1,168 @@ +// 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 . + +use std::collections::BTreeMap; +use std::str::FromStr; +use std::sync::Arc; + +use die::*; +use ethsync::EthSync; +use ethminer::{Miner, ExternalMiner}; +use ethcore::client::Client; +use util::RotatingLogger; +use util::keys::store::AccountService; +use util::network_settings::NetworkSettings; + +#[cfg(feature="rpc")] +pub use ethcore_rpc::ConfirmationsQueue; +#[cfg(not(feature="rpc"))] +#[derive(Default)] +pub struct ConfirmationsQueue; + +#[cfg(feature="rpc")] +use ethcore_rpc::Extendable; + +pub enum Api { + Web3, + Net, + Eth, + Personal, + Ethcore, + Traces, + Rpc, +} + +pub enum ApiError { + UnknownApi(String) +} + +pub enum ApiSet { + SafeContext, + UnsafeContext, + List(Vec), +} + +impl FromStr for Api { + type Err = ApiError; + + fn from_str(s: &str) -> Result { + use self::Api::*; + + match s { + "web3" => Ok(Web3), + "net" => Ok(Net), + "eth" => Ok(Eth), + "personal" => Ok(Personal), + "ethcore" => Ok(Ethcore), + "traces" => Ok(Traces), + "rpc" => Ok(Rpc), + e => Err(ApiError::UnknownApi(e.into())), + } + } +} + +pub struct Dependencies { + pub signer_enabled: bool, + pub signer_queue: Arc, + pub client: Arc, + pub sync: Arc, + pub secret_store: Arc, + pub miner: Arc, + pub external_miner: Arc, + pub logger: Arc, + pub settings: Arc, +} + +fn to_modules(apis: &[Api]) -> BTreeMap { + let mut modules = BTreeMap::new(); + for api in apis { + let (name, version) = match *api { + Api::Web3 => ("web3", "1.0"), + Api::Net => ("net", "1.0"), + Api::Eth => ("eth", "1.0"), + Api::Personal => ("personal", "1.0"), + Api::Ethcore => ("ethcore", "1.0"), + Api::Traces => ("traces", "1.0"), + Api::Rpc => ("rpc", "1.0"), + }; + modules.insert(name.into(), version.into()); + } + modules +} + +pub fn from_str(apis: Vec<&str>) -> Vec { + apis.into_iter() + .map(Api::from_str) + .collect::, ApiError>>() + .unwrap_or_else(|e| match e { + ApiError::UnknownApi(s) => die!("Unknown RPC API specified: {}", s), + }) +} + +fn list_apis(apis: ApiSet, signer_enabled: bool) -> Vec { + match apis { + ApiSet::List(apis) => apis, + ApiSet::UnsafeContext if signer_enabled => { + vec![Api::Web3, Api::Net, Api::Eth, Api::Ethcore, Api::Traces, Api::Rpc] + } + _ => { + vec![Api::Web3, Api::Net, Api::Eth, Api::Personal, Api::Ethcore, Api::Traces, Api::Rpc] + } + } +} + +pub fn setup_rpc(server: T, deps: Arc, apis: ApiSet) -> T { + use ethcore_rpc::v1::*; + + let apis = list_apis(apis, deps.signer_enabled); + for api in &apis { + match *api { + Api::Web3 => { + server.add_delegate(Web3Client::new().to_delegate()); + }, + Api::Net => { + server.add_delegate(NetClient::new(&deps.sync).to_delegate()); + }, + Api::Eth => { + server.add_delegate(EthClient::new(&deps.client, &deps.sync, &deps.secret_store, &deps.miner, &deps.external_miner).to_delegate()); + server.add_delegate(EthFilterClient::new(&deps.client, &deps.miner).to_delegate()); + + if deps.signer_enabled { + server.add_delegate(EthSigningQueueClient::new(&deps.signer_queue).to_delegate()); + } else { + server.add_delegate(EthSigningUnsafeClient::new(&deps.client, &deps.secret_store, &deps.miner).to_delegate()); + } + }, + Api::Personal => { + server.add_delegate(PersonalClient::new(&deps.secret_store, &deps.client, &deps.miner).to_delegate()); + if deps.signer_enabled { + server.add_delegate(SignerClient::new(&deps.secret_store, &deps.client, &deps.miner, &deps.signer_queue).to_delegate()); + } + }, + Api::Ethcore => { + server.add_delegate(EthcoreClient::new(&deps.miner, deps.logger.clone(), deps.settings.clone()).to_delegate()) + }, + Api::Traces => { + server.add_delegate(TracesClient::new(&deps.client).to_delegate()) + }, + Api::Rpc => { + let modules = to_modules(&apis); + server.add_delegate(RpcClient::new(modules).to_delegate()); + } + } + } + server +} diff --git a/parity/signer.rs b/parity/signer.rs index 5e3339bcc..d549b89cb 100644 --- a/parity/signer.rs +++ b/parity/signer.rs @@ -15,12 +15,9 @@ // along with Parity. If not, see . use std::sync::Arc; -use ethcore::client::Client; -use ethsync::EthSync; -use ethminer::{Miner, ExternalMiner}; -use util::keys::store::AccountService; use util::panics::{PanicHandler, ForwardPanic}; use die::*; +use rpc_apis; #[cfg(feature = "ethcore-signer")] use ethcore_signer as signer; @@ -36,11 +33,7 @@ pub struct Configuration { pub struct Dependencies { pub panic_handler: Arc, - pub client: Arc, - pub sync: Arc, - pub secret_store: Arc, - pub miner: Arc, - pub external_miner: Arc, + pub apis: Arc, } pub fn start(conf: Configuration, deps: Dependencies) -> Option { @@ -58,13 +51,8 @@ fn do_start(conf: Configuration, deps: Dependencies) -> SignerServer { }); let start_result = { - use ethcore_rpc::v1::*; let server = signer::ServerBuilder::new(); - server.add_delegate(Web3Client::new().to_delegate()); - server.add_delegate(NetClient::new(&deps.sync).to_delegate()); - server.add_delegate(EthClient::new(&deps.client, &deps.sync, &deps.secret_store, &deps.miner, &deps.external_miner).to_delegate()); - server.add_delegate(EthFilterClient::new(&deps.client, &deps.miner).to_delegate()); - server.add_delegate(PersonalClient::new(&deps.secret_store, &deps.client, &deps.miner).to_delegate()); + let server = rpc_apis::setup_rpc(server, deps.apis, rpc_apis::ApiSet::SafeContext); server.start(addr) }; diff --git a/rpc/src/lib.rs b/rpc/src/lib.rs index 24d58819c..607bcf4bd 100644 --- a/rpc/src/lib.rs +++ b/rpc/src/lib.rs @@ -44,12 +44,26 @@ use self::jsonrpc_core::{IoHandler, IoDelegate}; pub use jsonrpc_http_server::{Server, RpcServerError}; pub mod v1; +pub use v1::{SigningQueue, ConfirmationsQueue}; + +/// An object that can be extended with `IoDelegates` +pub trait Extendable { + /// Add `Delegate` to this object. + fn add_delegate(&self, delegate: IoDelegate); +} /// Http server. pub struct RpcServer { handler: Arc, } +impl Extendable for RpcServer { + /// Add io delegate. + fn add_delegate(&self, delegate: IoDelegate) { + self.handler.add_delegate(delegate); + } +} + impl RpcServer { /// Construct new http server object. pub fn new() -> RpcServer { @@ -58,11 +72,6 @@ impl RpcServer { } } - /// Add io delegate. - pub fn add_delegate(&self, delegate: IoDelegate) where D: Send + Sync + 'static { - self.handler.add_delegate(delegate); - } - /// Start http server asynchronously and returns result with `Server` handle on success or an error. pub fn start_http(&self, addr: &SocketAddr, cors_domains: Vec) -> Result { let cors_domains = cors_domains.into_iter() diff --git a/rpc/src/v1/helpers/mod.rs b/rpc/src/v1/helpers/mod.rs index b1a5c05ba..2acf98bf2 100644 --- a/rpc/src/v1/helpers/mod.rs +++ b/rpc/src/v1/helpers/mod.rs @@ -16,6 +16,8 @@ mod poll_manager; mod poll_filter; +mod signing_queue; pub use self::poll_manager::PollManager; pub use self::poll_filter::PollFilter; +pub use self::signing_queue::{ConfirmationsQueue, SigningQueue}; diff --git a/rpc/src/v1/helpers/signing_queue.rs b/rpc/src/v1/helpers/signing_queue.rs new file mode 100644 index 000000000..eee4328ee --- /dev/null +++ b/rpc/src/v1/helpers/signing_queue.rs @@ -0,0 +1,108 @@ +// 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 . + +use std::sync::Mutex; +use std::collections::HashMap; +use v1::types::{TransactionRequest, TransactionConfirmation}; +use util::U256; + +/// A queue of transactions awaiting to be confirmed and signed. +pub trait SigningQueue: Send + Sync { + /// Add new request to the queue. + fn add_request(&self, transaction: TransactionRequest) -> U256; + + /// Remove request from the queue. + fn remove_request(&self, id: U256) -> Option; + + /// Return copy of all the requests in the queue. + fn requests(&self) -> Vec; +} + +/// Queue for all unconfirmed transactions. +pub struct ConfirmationsQueue { + id: Mutex, + queue: Mutex>, +} + +impl Default for ConfirmationsQueue { + fn default() -> Self { + ConfirmationsQueue { + id: Mutex::new(U256::from(0)), + queue: Mutex::new(HashMap::new()), + } + } +} + +impl SigningQueue for ConfirmationsQueue { + fn add_request(&self, transaction: TransactionRequest) -> U256 { + // Increment id + let id = { + let mut last_id = self.id.lock().unwrap(); + *last_id = *last_id + U256::from(1); + *last_id + }; + let mut queue = self.queue.lock().unwrap(); + queue.insert(id, TransactionConfirmation { + id: id, + transaction: transaction, + }); + id + } + + fn remove_request(&self, id: U256) -> Option { + self.queue.lock().unwrap().remove(&id) + } + + fn requests(&self) -> Vec { + let queue = self.queue.lock().unwrap(); + queue.values().cloned().collect() + } +} + + +#[cfg(test)] +mod test { + use util::hash::Address; + use util::numbers::U256; + use v1::types::TransactionRequest; + use super::*; + + #[test] + fn should_work_for_hashset() { + // given + let queue = ConfirmationsQueue::default(); + + let request = TransactionRequest { + from: Address::from(1), + to: Some(Address::from(2)), + gas_price: None, + gas: None, + value: Some(U256::from(10_000_000)), + data: None, + nonce: None, + }; + + // when + queue.add_request(request.clone()); + let all = queue.requests(); + + // then + assert_eq!(all.len(), 1); + let el = all.get(0).unwrap(); + assert_eq!(el.id, U256::from(1)); + assert_eq!(el.transaction, request); + } +} diff --git a/rpc/src/v1/impls/eth.rs b/rpc/src/v1/impls/eth.rs index a57fc333c..e0225c7e9 100644 --- a/rpc/src/v1/impls/eth.rs +++ b/rpc/src/v1/impls/eth.rs @@ -18,7 +18,6 @@ extern crate ethash; -use std::collections::HashSet; use std::sync::{Arc, Weak, Mutex}; use std::ops::Deref; use ethsync::{SyncProvider, SyncState}; @@ -36,10 +35,9 @@ use ethcore::transaction::{Transaction as EthTransaction, SignedTransaction, Act use ethcore::log_entry::LogEntry; use ethcore::filter::Filter as EthcoreFilter; use self::ethash::SeedHashCompute; -use v1::traits::{Eth, EthFilter}; -use v1::types::{Block, BlockTransactions, BlockNumber, Bytes, SyncStatus, SyncInfo, Transaction, TransactionRequest, CallRequest, OptionalValue, Index, Filter, Log, Receipt}; -use v1::helpers::{PollFilter, PollManager}; -use v1::impls::{dispatch_transaction, sign_and_dispatch}; +use v1::traits::Eth; +use v1::types::{Block, BlockTransactions, BlockNumber, Bytes, SyncStatus, SyncInfo, Transaction, CallRequest, OptionalValue, Index, Filter, Log, Receipt}; +use v1::impls::dispatch_transaction; use serde; /// Eth rpc implementation. @@ -170,6 +168,25 @@ impl EthClient where } } +pub fn pending_logs(miner: &M, filter: &EthcoreFilter) -> Vec where M: MinerService { + let receipts = miner.pending_receipts(); + + let pending_logs = receipts.into_iter() + .flat_map(|(hash, r)| r.logs.into_iter().map(|l| (hash.clone(), l)).collect::>()) + .collect::>(); + + let result = pending_logs.into_iter() + .filter(|pair| filter.matches(&pair.1)) + .map(|pair| { + let mut log = Log::from(pair.1); + log.transaction_hash = Some(pair.0); + log + }) + .collect(); + + result +} + const MAX_QUEUE_SIZE_TO_MINE_ON: usize = 4; // because uncles go back 6. fn params_len(params: &Params) -> usize { @@ -193,25 +210,6 @@ fn from_params_default_third(params: Params) -> Result<(F1, F2, BlockNum } } -fn pending_logs(miner: &M, filter: &EthcoreFilter) -> Vec where M: MinerService { - let receipts = miner.pending_receipts(); - - let pending_logs = receipts.into_iter() - .flat_map(|(hash, r)| r.logs.into_iter().map(|l| (hash.clone(), l)).collect::>()) - .collect::>(); - - let result = pending_logs.into_iter() - .filter(|pair| filter.matches(&pair.1)) - .map(|pair| { - let mut log = Log::from(pair.1); - log.transaction_hash = Some(pair.0); - log - }) - .collect(); - - result -} - // must be in range [-32099, -32000] const UNSUPPORTED_REQUEST_CODE: i64 = -32000; @@ -496,23 +494,6 @@ impl Eth for EthClient where }) } - fn sign(&self, params: Params) -> Result { - from_params::<(Address, H256)>(params).and_then(|(addr, msg)| { - to_value(&take_weak!(self.accounts).sign(&addr, &msg).unwrap_or(H520::zero())) - }) - } - - fn send_transaction(&self, params: Params) -> Result { - from_params::<(TransactionRequest, )>(params) - .and_then(|(request, )| { - let accounts = take_weak!(self.accounts); - match accounts.account_secret(&request.from) { - Ok(secret) => sign_and_dispatch(&self.client, &self.miner, request, secret), - Err(_) => to_value(&H256::zero()) - } - }) - } - fn send_raw_transaction(&self, params: Params) -> Result { from_params::<(Bytes, )>(params) .and_then(|(raw_transaction, )| { @@ -563,186 +544,3 @@ impl Eth for EthClient where rpc_unimplemented!() } } - -/// Eth filter rpc implementation. -pub struct EthFilterClient where - C: BlockChainClient, - M: MinerService { - - client: Weak, - miner: Weak, - polls: Mutex>, -} - -impl EthFilterClient where - C: BlockChainClient, - M: MinerService { - - /// Creates new Eth filter client. - pub fn new(client: &Arc, miner: &Arc) -> Self { - EthFilterClient { - client: Arc::downgrade(client), - miner: Arc::downgrade(miner), - polls: Mutex::new(PollManager::new()), - } - } -} - -impl EthFilter for EthFilterClient where - C: BlockChainClient + 'static, - M: MinerService + 'static { - - fn new_filter(&self, params: Params) -> Result { - from_params::<(Filter,)>(params) - .and_then(|(filter,)| { - let mut polls = self.polls.lock().unwrap(); - let block_number = take_weak!(self.client).chain_info().best_block_number; - let id = polls.create_poll(PollFilter::Logs(block_number, Default::default(), filter)); - to_value(&U256::from(id)) - }) - } - - fn new_block_filter(&self, params: Params) -> Result { - match params { - Params::None => { - let mut polls = self.polls.lock().unwrap(); - let id = polls.create_poll(PollFilter::Block(take_weak!(self.client).chain_info().best_block_number)); - to_value(&U256::from(id)) - }, - _ => Err(Error::invalid_params()) - } - } - - fn new_pending_transaction_filter(&self, params: Params) -> Result { - match params { - Params::None => { - let mut polls = self.polls.lock().unwrap(); - let pending_transactions = take_weak!(self.miner).pending_transactions_hashes(); - let id = polls.create_poll(PollFilter::PendingTransaction(pending_transactions)); - - to_value(&U256::from(id)) - }, - _ => Err(Error::invalid_params()) - } - } - - fn filter_changes(&self, params: Params) -> Result { - let client = take_weak!(self.client); - from_params::<(Index,)>(params) - .and_then(|(index,)| { - let mut polls = self.polls.lock().unwrap(); - match polls.poll_mut(&index.value()) { - None => Ok(Value::Array(vec![] as Vec)), - Some(filter) => match *filter { - PollFilter::Block(ref mut block_number) => { - // + 1, cause we want to return hashes including current block hash. - let current_number = client.chain_info().best_block_number + 1; - let hashes = (*block_number..current_number).into_iter() - .map(BlockID::Number) - .filter_map(|id| client.block_hash(id)) - .collect::>(); - - *block_number = current_number; - - to_value(&hashes) - }, - PollFilter::PendingTransaction(ref mut previous_hashes) => { - // get hashes of pending transactions - let current_hashes = take_weak!(self.miner).pending_transactions_hashes(); - - let new_hashes = - { - let previous_hashes_set = previous_hashes.iter().collect::>(); - - // find all new hashes - current_hashes - .iter() - .filter(|hash| !previous_hashes_set.contains(hash)) - .cloned() - .collect::>() - }; - - // save all hashes of pending transactions - *previous_hashes = current_hashes; - - // return new hashes - to_value(&new_hashes) - }, - PollFilter::Logs(ref mut block_number, ref mut previous_logs, ref filter) => { - // retrive the current block number - let current_number = client.chain_info().best_block_number; - - // check if we need to check pending hashes - let include_pending = filter.to_block == Some(BlockNumber::Pending); - - // build appropriate filter - let mut filter: EthcoreFilter = filter.clone().into(); - filter.from_block = BlockID::Number(*block_number); - filter.to_block = BlockID::Latest; - - // retrieve logs in range from_block..min(BlockID::Latest..to_block) - let mut logs = client.logs(filter.clone()) - .into_iter() - .map(From::from) - .collect::>(); - - // additionally retrieve pending logs - if include_pending { - let pending_logs = pending_logs(take_weak!(self.miner).deref(), &filter); - - // remove logs about which client was already notified about - let new_pending_logs: Vec<_> = pending_logs.iter() - .filter(|p| !previous_logs.contains(p)) - .cloned() - .collect(); - - // save all logs retrieved by client - *previous_logs = pending_logs.into_iter().collect(); - - // append logs array with new pending logs - logs.extend(new_pending_logs); - } - - // save current block number as next from block number - *block_number = current_number; - - to_value(&logs) - } - } - } - }) - } - - fn filter_logs(&self, params: Params) -> Result { - from_params::<(Index,)>(params) - .and_then(|(index,)| { - let mut polls = self.polls.lock().unwrap(); - match polls.poll(&index.value()) { - Some(&PollFilter::Logs(ref _block_number, ref _previous_log, ref filter)) => { - let include_pending = filter.to_block == Some(BlockNumber::Pending); - let filter: EthcoreFilter = filter.clone().into(); - let mut logs = take_weak!(self.client).logs(filter.clone()) - .into_iter() - .map(From::from) - .collect::>(); - - if include_pending { - logs.extend(pending_logs(take_weak!(self.miner).deref(), &filter)); - } - - to_value(&logs) - }, - // just empty array - _ => Ok(Value::Array(vec![] as Vec)), - } - }) - } - - fn uninstall_filter(&self, params: Params) -> Result { - from_params::<(Index,)>(params) - .and_then(|(index,)| { - self.polls.lock().unwrap().remove_poll(&index.value()); - to_value(&true) - }) - } -} diff --git a/rpc/src/v1/impls/eth_filter.rs b/rpc/src/v1/impls/eth_filter.rs new file mode 100644 index 000000000..c6aecdca2 --- /dev/null +++ b/rpc/src/v1/impls/eth_filter.rs @@ -0,0 +1,214 @@ +// 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 . + +//! Eth Filter RPC implementation + +use std::ops::Deref; +use std::sync::{Arc, Weak, Mutex}; +use std::collections::HashSet; +use jsonrpc_core::*; +use util::numbers::*; +use ethminer::MinerService; +use ethcore::filter::Filter as EthcoreFilter; +use ethcore::client::{BlockChainClient, BlockID}; +use v1::traits::EthFilter; +use v1::types::{BlockNumber, Index, Filter, Log}; +use v1::helpers::{PollFilter, PollManager}; +use v1::impls::eth::pending_logs; + + +/// Eth filter rpc implementation. +pub struct EthFilterClient where + C: BlockChainClient, + M: MinerService { + + client: Weak, + miner: Weak, + polls: Mutex>, +} + +impl EthFilterClient where + C: BlockChainClient, + M: MinerService { + + /// Creates new Eth filter client. + pub fn new(client: &Arc, miner: &Arc) -> Self { + EthFilterClient { + client: Arc::downgrade(client), + miner: Arc::downgrade(miner), + polls: Mutex::new(PollManager::new()), + } + } +} + +impl EthFilter for EthFilterClient where + C: BlockChainClient + 'static, + M: MinerService + 'static { + + fn new_filter(&self, params: Params) -> Result { + from_params::<(Filter,)>(params) + .and_then(|(filter,)| { + let mut polls = self.polls.lock().unwrap(); + let block_number = take_weak!(self.client).chain_info().best_block_number; + let id = polls.create_poll(PollFilter::Logs(block_number, Default::default(), filter)); + to_value(&U256::from(id)) + }) + } + + fn new_block_filter(&self, params: Params) -> Result { + match params { + Params::None => { + let mut polls = self.polls.lock().unwrap(); + let id = polls.create_poll(PollFilter::Block(take_weak!(self.client).chain_info().best_block_number)); + to_value(&U256::from(id)) + }, + _ => Err(Error::invalid_params()) + } + } + + fn new_pending_transaction_filter(&self, params: Params) -> Result { + match params { + Params::None => { + let mut polls = self.polls.lock().unwrap(); + let pending_transactions = take_weak!(self.miner).pending_transactions_hashes(); + let id = polls.create_poll(PollFilter::PendingTransaction(pending_transactions)); + + to_value(&U256::from(id)) + }, + _ => Err(Error::invalid_params()) + } + } + + fn filter_changes(&self, params: Params) -> Result { + let client = take_weak!(self.client); + from_params::<(Index,)>(params) + .and_then(|(index,)| { + let mut polls = self.polls.lock().unwrap(); + match polls.poll_mut(&index.value()) { + None => Ok(Value::Array(vec![] as Vec)), + Some(filter) => match *filter { + PollFilter::Block(ref mut block_number) => { + // + 1, cause we want to return hashes including current block hash. + let current_number = client.chain_info().best_block_number + 1; + let hashes = (*block_number..current_number).into_iter() + .map(BlockID::Number) + .filter_map(|id| client.block_hash(id)) + .collect::>(); + + *block_number = current_number; + + to_value(&hashes) + }, + PollFilter::PendingTransaction(ref mut previous_hashes) => { + // get hashes of pending transactions + let current_hashes = take_weak!(self.miner).pending_transactions_hashes(); + + let new_hashes = + { + let previous_hashes_set = previous_hashes.iter().collect::>(); + + // find all new hashes + current_hashes + .iter() + .filter(|hash| !previous_hashes_set.contains(hash)) + .cloned() + .collect::>() + }; + + // save all hashes of pending transactions + *previous_hashes = current_hashes; + + // return new hashes + to_value(&new_hashes) + }, + PollFilter::Logs(ref mut block_number, ref mut previous_logs, ref filter) => { + // retrive the current block number + let current_number = client.chain_info().best_block_number; + + // check if we need to check pending hashes + let include_pending = filter.to_block == Some(BlockNumber::Pending); + + // build appropriate filter + let mut filter: EthcoreFilter = filter.clone().into(); + filter.from_block = BlockID::Number(*block_number); + filter.to_block = BlockID::Latest; + + // retrieve logs in range from_block..min(BlockID::Latest..to_block) + let mut logs = client.logs(filter.clone()) + .into_iter() + .map(From::from) + .collect::>(); + + // additionally retrieve pending logs + if include_pending { + let pending_logs = pending_logs(take_weak!(self.miner).deref(), &filter); + + // remove logs about which client was already notified about + let new_pending_logs: Vec<_> = pending_logs.iter() + .filter(|p| !previous_logs.contains(p)) + .cloned() + .collect(); + + // save all logs retrieved by client + *previous_logs = pending_logs.into_iter().collect(); + + // append logs array with new pending logs + logs.extend(new_pending_logs); + } + + // save current block number as next from block number + *block_number = current_number; + + to_value(&logs) + } + } + } + }) + } + + fn filter_logs(&self, params: Params) -> Result { + from_params::<(Index,)>(params) + .and_then(|(index,)| { + let mut polls = self.polls.lock().unwrap(); + match polls.poll(&index.value()) { + Some(&PollFilter::Logs(ref _block_number, ref _previous_log, ref filter)) => { + let include_pending = filter.to_block == Some(BlockNumber::Pending); + let filter: EthcoreFilter = filter.clone().into(); + let mut logs = take_weak!(self.client).logs(filter.clone()) + .into_iter() + .map(From::from) + .collect::>(); + + if include_pending { + logs.extend(pending_logs(take_weak!(self.miner).deref(), &filter)); + } + + to_value(&logs) + }, + // just empty array + _ => Ok(Value::Array(vec![] as Vec)), + } + }) + } + + fn uninstall_filter(&self, params: Params) -> Result { + from_params::<(Index,)>(params) + .and_then(|(index,)| { + self.polls.lock().unwrap().remove_poll(&index.value()); + to_value(&true) + }) + } +} diff --git a/rpc/src/v1/impls/eth_signing.rs b/rpc/src/v1/impls/eth_signing.rs new file mode 100644 index 000000000..04011902b --- /dev/null +++ b/rpc/src/v1/impls/eth_signing.rs @@ -0,0 +1,111 @@ +// 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 . + +//! Eth Signing RPC implementation. + +use std::sync::{Arc, Weak}; +use jsonrpc_core::*; +use ethminer::MinerService; +use ethcore::client::BlockChainClient; +use util::numbers::*; +use util::keys::store::AccountProvider; +use v1::helpers::{SigningQueue, ConfirmationsQueue}; +use v1::traits::EthSigning; +use v1::types::TransactionRequest; +use v1::impls::sign_and_dispatch; + + +/// Implementation of functions that require signing when no trusted signer is used. +pub struct EthSigningQueueClient { + queue: Weak, +} + +impl EthSigningQueueClient { + /// Creates a new signing queue client given shared signing queue. + pub fn new(queue: &Arc) -> Self { + EthSigningQueueClient { + queue: Arc::downgrade(queue), + } + } +} + +impl EthSigning for EthSigningQueueClient { + + fn sign(&self, _params: Params) -> Result { + // TODO [ToDr] Implement sign when rest of the signing queue is ready. + rpc_unimplemented!() + } + + fn send_transaction(&self, params: Params) -> Result { + from_params::<(TransactionRequest, )>(params) + .and_then(|(request, )| { + let queue = take_weak!(self.queue); + queue.add_request(request); + // TODO [ToDr] Block and wait for confirmation? + to_value(&H256::zero()) + }) + } +} + +/// Implementation of functions that require signing when no trusted signer is used. +pub struct EthSigningUnsafeClient where + C: BlockChainClient, + A: AccountProvider, + M: MinerService { + client: Weak, + accounts: Weak, + miner: Weak, +} + +impl EthSigningUnsafeClient where + C: BlockChainClient, + A: AccountProvider, + M: MinerService { + + /// Creates new EthClient. + pub fn new(client: &Arc, accounts: &Arc, miner: &Arc) + -> Self { + EthSigningUnsafeClient { + client: Arc::downgrade(client), + miner: Arc::downgrade(miner), + accounts: Arc::downgrade(accounts), + } + } +} + +impl EthSigning for EthSigningUnsafeClient where + C: BlockChainClient + 'static, + A: AccountProvider + 'static, + M: MinerService + 'static { + + fn sign(&self, params: Params) -> Result { + from_params::<(Address, H256)>(params).and_then(|(addr, msg)| { + to_value(&take_weak!(self.accounts).sign(&addr, &msg).unwrap_or(H520::zero())) + }) + } + + fn send_transaction(&self, params: Params) -> Result { + from_params::<(TransactionRequest, )>(params) + .and_then(|(request, )| { + let accounts = take_weak!(self.accounts); + match accounts.account_secret(&request.from) { + Ok(secret) => sign_and_dispatch(&self.client, &self.miner, request, secret), + Err(_) => to_value(&H256::zero()) + } + }) + } + +} diff --git a/rpc/src/v1/impls/mod.rs b/rpc/src/v1/impls/mod.rs index 7ee8b8b8a..975a5fa77 100644 --- a/rpc/src/v1/impls/mod.rs +++ b/rpc/src/v1/impls/mod.rs @@ -31,16 +31,22 @@ macro_rules! rpc_unimplemented { mod web3; mod eth; +mod eth_filter; +mod eth_signing; mod net; mod personal; +mod personal_signer; mod ethcore; mod traces; mod rpc; pub use self::web3::Web3Client; -pub use self::eth::{EthClient, EthFilterClient}; +pub use self::eth::EthClient; +pub use self::eth_filter::EthFilterClient; +pub use self::eth_signing::{EthSigningUnsafeClient, EthSigningQueueClient}; pub use self::net::NetClient; pub use self::personal::PersonalClient; +pub use self::personal_signer::SignerClient; pub use self::ethcore::EthcoreClient; pub use self::traces::TracesClient; pub use self::rpc::RpcClient; @@ -92,4 +98,4 @@ fn sign_and_dispatch(client: &Weak, miner: &Weak, request: Transacti trace!(target: "miner", "send_transaction: dispatching tx: {}", encode(&signed_transaction).to_vec().pretty()); dispatch_transaction(&*client, &*miner, signed_transaction) -} \ No newline at end of file +} diff --git a/rpc/src/v1/impls/personal.rs b/rpc/src/v1/impls/personal.rs index 19e902996..30d541772 100644 --- a/rpc/src/v1/impls/personal.rs +++ b/rpc/src/v1/impls/personal.rs @@ -20,7 +20,7 @@ use jsonrpc_core::*; use v1::traits::Personal; use v1::types::TransactionRequest; use v1::impls::sign_and_dispatch; -use util::keys::store::*; +use util::keys::store::AccountProvider; use util::numbers::*; use ethcore::client::BlockChainClient; use ethminer::MinerService; diff --git a/rpc/src/v1/impls/personal_signer.rs b/rpc/src/v1/impls/personal_signer.rs new file mode 100644 index 000000000..cf4e927ac --- /dev/null +++ b/rpc/src/v1/impls/personal_signer.rs @@ -0,0 +1,93 @@ +// 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 . + +//! Transactions Confirmations (personal) rpc implementation + +use std::sync::{Arc, Weak}; +use jsonrpc_core::*; +use v1::traits::PersonalSigner; +use v1::types::TransactionModification; +use v1::impls::sign_and_dispatch; +use v1::helpers::{SigningQueue, ConfirmationsQueue}; +use util::keys::store::AccountProvider; +use util::numbers::*; +use ethcore::client::BlockChainClient; +use ethminer::MinerService; + +/// Transactions confirmation (personal) rpc implementation. +pub struct SignerClient + where A: AccountProvider, C: BlockChainClient, M: MinerService { + queue: Weak, + accounts: Weak, + client: Weak, + miner: Weak, +} + +impl SignerClient + where A: AccountProvider, C: BlockChainClient, M: MinerService { + + /// Create new instance of signer client. + pub fn new(store: &Arc, client: &Arc, miner: &Arc, queue: &Arc) -> Self { + SignerClient { + queue: Arc::downgrade(queue), + accounts: Arc::downgrade(store), + client: Arc::downgrade(client), + miner: Arc::downgrade(miner), + } + } +} + +impl PersonalSigner for SignerClient + where A: AccountProvider, C: BlockChainClient, M: MinerService { + + fn transactions_to_confirm(&self, _params: Params) -> Result { + let queue = take_weak!(self.queue); + to_value(&queue.requests()) + } + + fn confirm_transaction(&self, params: Params) -> Result { + from_params::<(U256, TransactionModification, String)>(params).and_then( + |(id, modification, pass)| { + let accounts = take_weak!(self.accounts); + let queue = take_weak!(self.queue); + queue.remove_request(id) + .and_then(|confirmation| { + let mut request = confirmation.transaction; + // apply modification + if let Some(gas_price) = modification.gas_price { + request.gas_price = Some(gas_price); + } + match accounts.locked_account_secret(&request.from, &pass) { + Ok(secret) => Some(sign_and_dispatch(&self.client, &self.miner, request, secret)), + Err(_) => None + } + }) + .unwrap_or_else(|| to_value(&H256::zero())) + } + ) + } + + fn reject_transaction(&self, params: Params) -> Result { + from_params::<(U256, )>(params).and_then( + |(id, )| { + let queue = take_weak!(self.queue); + let res = queue.remove_request(id); + to_value(&res.is_some()) + } + ) + } +} + diff --git a/rpc/src/v1/mod.rs b/rpc/src/v1/mod.rs index b1ab256c0..54628d892 100644 --- a/rpc/src/v1/mod.rs +++ b/rpc/src/v1/mod.rs @@ -25,5 +25,6 @@ pub mod traits; pub mod tests; pub mod types; -pub use self::traits::{Web3, Eth, EthFilter, Personal, Net, Ethcore, Traces, Rpc}; +pub use self::traits::{Web3, Eth, EthFilter, EthSigning, Personal, PersonalSigner, Net, Ethcore, Traces, Rpc}; pub use self::impls::*; +pub use self::helpers::{SigningQueue, ConfirmationsQueue}; diff --git a/rpc/src/v1/tests/eth.rs b/rpc/src/v1/tests/eth.rs index 30815b8a8..80aa8ce33 100644 --- a/rpc/src/v1/tests/eth.rs +++ b/rpc/src/v1/tests/eth.rs @@ -35,8 +35,8 @@ 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::traits::eth::{Eth, EthSigning}; +use v1::impls::{EthClient, EthSigningUnsafeClient}; use v1::tests::helpers::{TestSyncProvider, Config}; fn account_provider() -> Arc { @@ -109,10 +109,15 @@ impl EthTester { &miner_service, &external_miner ); + let eth_sign = EthSigningUnsafeClient::new( + &client, + &account_provider, + &miner_service + ); let handler = IoHandler::new(); - let delegate = eth_client.to_delegate(); - handler.add_delegate(delegate); + handler.add_delegate(eth_client.to_delegate()); + handler.add_delegate(eth_sign.to_delegate()); EthTester { _miner: miner_service, @@ -352,4 +357,4 @@ fn verify_transaction_counts(name: String, chain: BlockChain) { 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"); \ No newline at end of file +register_test!(eth_transaction_count_3, verify_transaction_counts, "BlockchainTests/bcGasPricerTest"); diff --git a/rpc/src/v1/tests/mocked/eth.rs b/rpc/src/v1/tests/mocked/eth.rs index c51d6d7da..49ca54cef 100644 --- a/rpc/src/v1/tests/mocked/eth.rs +++ b/rpc/src/v1/tests/mocked/eth.rs @@ -27,7 +27,7 @@ use ethcore::receipt::LocalizedReceipt; use ethcore::transaction::{Transaction, Action}; use ethminer::{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, diff --git a/rpc/src/v1/tests/mocked/eth_signing.rs b/rpc/src/v1/tests/mocked/eth_signing.rs new file mode 100644 index 000000000..6eb6e3fd6 --- /dev/null +++ b/rpc/src/v1/tests/mocked/eth_signing.rs @@ -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 . + +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, + 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); + +} diff --git a/rpc/src/v1/tests/mocked/mod.rs b/rpc/src/v1/tests/mocked/mod.rs index dc09d998d..986dbfdef 100644 --- a/rpc/src/v1/tests/mocked/mod.rs +++ b/rpc/src/v1/tests/mocked/mod.rs @@ -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; diff --git a/rpc/src/v1/tests/mocked/personal.rs b/rpc/src/v1/tests/mocked/personal.rs index 991b13cba..8bc3ab3c8 100644 --- a/rpc/src/v1/tests/mocked/personal.rs +++ b/rpc/src/v1/tests/mocked/personal.rs @@ -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)); -} \ No newline at end of file +} diff --git a/rpc/src/v1/tests/mocked/personal_signer.rs b/rpc/src/v1/tests/mocked/personal_signer.rs new file mode 100644 index 000000000..cd1f81d9a --- /dev/null +++ b/rpc/src/v1/tests/mocked/personal_signer.rs @@ -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 . + +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, + accounts: Arc, + io: IoHandler, + miner: Arc, + // these unused fields are necessary to keep the data alive + // as the handler has only weak pointers. + _client: Arc, +} + +fn blockchain_client() -> Arc { + let client = TestBlockChainClient::new(); + Arc::new(client) +} + +fn accounts_provider() -> Arc { + let accounts = HashMap::new(); + let ap = TestAccountProvider::new(accounts); + Arc::new(ap) +} + +fn miner_service() -> Arc { + 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); +} + diff --git a/rpc/src/v1/traits/eth.rs b/rpc/src/v1/traits/eth.rs index 1577053f4..c4369ff2a 100644 --- a/rpc/src/v1/traits/eth.rs +++ b/rpc/src/v1/traits/eth.rs @@ -74,12 +74,6 @@ pub trait Eth: Sized + Send + Sync + 'static { /// Returns the code at given address at given time (block number). fn code_at(&self, _: Params) -> Result; - /// Signs the data with given address signature. - fn sign(&self, _: Params) -> Result; - - /// Sends transaction. - fn send_transaction(&self, _: Params) -> Result; - /// Sends signed transaction. fn send_raw_transaction(&self, _: Params) -> Result; @@ -150,8 +144,6 @@ pub trait Eth: Sized + Send + Sync + 'static { delegate.add_method("eth_getUncleCountByBlockHash", Eth::block_uncles_count_by_hash); delegate.add_method("eth_getUncleCountByBlockNumber", Eth::block_uncles_count_by_number); delegate.add_method("eth_getCode", Eth::code_at); - delegate.add_method("eth_sign", Eth::sign); - delegate.add_method("eth_sendTransaction", Eth::send_transaction); delegate.add_method("eth_sendRawTransaction", Eth::send_raw_transaction); delegate.add_method("eth_call", Eth::call); delegate.add_method("eth_estimateGas", Eth::estimate_gas); @@ -208,3 +200,20 @@ pub trait EthFilter: Sized + Send + Sync + 'static { delegate } } + +/// Signing methods implementation relying on unlocked accounts. +pub trait EthSigning: Sized + Send + Sync + 'static { + /// Signs the data with given address signature. + fn sign(&self, _: Params) -> Result; + + /// Sends transaction. + fn send_transaction(&self, _: Params) -> Result; + + /// Should be used to convert object to io delegate. + fn to_delegate(self) -> IoDelegate { + let mut delegate = IoDelegate::new(Arc::new(self)); + delegate.add_method("eth_sign", EthSigning::sign); + delegate.add_method("eth_sendTransaction", EthSigning::send_transaction); + delegate + } +} diff --git a/rpc/src/v1/traits/mod.rs b/rpc/src/v1/traits/mod.rs index 0728fd06a..d994ffc24 100644 --- a/rpc/src/v1/traits/mod.rs +++ b/rpc/src/v1/traits/mod.rs @@ -25,9 +25,11 @@ pub mod traces; pub mod rpc; pub use self::web3::Web3; -pub use self::eth::{Eth, EthFilter}; +pub use self::eth::{Eth, EthFilter, EthSigning}; pub use self::net::Net; -pub use self::personal::Personal; +pub use self::personal::{Personal, PersonalSigner}; pub use self::ethcore::Ethcore; pub use self::traces::Traces; pub use self::rpc::Rpc; + + diff --git a/rpc/src/v1/traits/personal.rs b/rpc/src/v1/traits/personal.rs index d66161c54..a36358766 100644 --- a/rpc/src/v1/traits/personal.rs +++ b/rpc/src/v1/traits/personal.rs @@ -43,3 +43,26 @@ pub trait Personal: Sized + Send + Sync + 'static { delegate } } + +/// Personal extension for transactions confirmations rpc interface. +pub trait PersonalSigner: Sized + Send + Sync + 'static { + + /// Returns a list of transactions to confirm. + fn transactions_to_confirm(&self, _: Params) -> Result; + + /// Confirm and send a specific transaction. + fn confirm_transaction(&self, _: Params) -> Result; + + /// Reject the transaction request. + fn reject_transaction(&self, _: Params) -> Result; + + /// Should be used to convert object to io delegate. + fn to_delegate(self) -> IoDelegate { + let mut delegate = IoDelegate::new(Arc::new(self)); + delegate.add_method("personal_transactionsToConfirm", PersonalSigner::transactions_to_confirm); + delegate.add_method("personal_confirmTransaction", PersonalSigner::confirm_transaction); + delegate.add_method("personal_rejectTransaction", PersonalSigner::reject_transaction); + delegate + } +} + diff --git a/rpc/src/v1/types/mod.rs.in b/rpc/src/v1/types/mod.rs.in index 824a061ef..b4e82a28b 100644 --- a/rpc/src/v1/types/mod.rs.in +++ b/rpc/src/v1/types/mod.rs.in @@ -38,7 +38,7 @@ pub use self::log::Log; pub use self::optionals::OptionalValue; pub use self::sync::{SyncStatus, SyncInfo}; pub use self::transaction::Transaction; -pub use self::transaction_request::TransactionRequest; +pub use self::transaction_request::{TransactionRequest, TransactionConfirmation, TransactionModification}; pub use self::call_request::CallRequest; pub use self::receipt::Receipt; pub use self::trace::Trace; diff --git a/rpc/src/v1/types/transaction_request.rs b/rpc/src/v1/types/transaction_request.rs index 1b51e6b12..93d6a479b 100644 --- a/rpc/src/v1/types/transaction_request.rs +++ b/rpc/src/v1/types/transaction_request.rs @@ -21,7 +21,7 @@ use util::numbers::U256; use v1::types::bytes::Bytes; /// Transaction request coming from RPC -#[derive(Debug, Clone, Default, Eq, PartialEq, Hash, Deserialize)] +#[derive(Debug, Clone, Default, Eq, PartialEq, Hash, Serialize, Deserialize)] pub struct TransactionRequest { /// Sender pub from: Address, @@ -40,6 +40,24 @@ pub struct TransactionRequest { pub nonce: Option, } +/// Transaction confirmation waiting in a queue +#[derive(Debug, Clone, Default, Eq, PartialEq, Hash, Serialize)] +pub struct TransactionConfirmation { + /// Id of this confirmation + pub id: U256, + /// TransactionRequest + pub transaction: TransactionRequest, +} + +/// Possible modifications to the confirmed transaction sent by SystemUI +#[derive(Debug, PartialEq, Deserialize)] +pub struct TransactionModification { + /// Modified gas price + #[serde(rename="gasPrice")] + pub gas_price: Option, +} + + #[cfg(test)] mod tests { use std::str::FromStr; @@ -135,5 +153,26 @@ mod tests { nonce: None, }); } + + #[test] + fn should_deserialize_modification() { + // given + let s1 = r#"{ + "gasPrice":"0x0ba43b7400" + }"#; + let s2 = r#"{}"#; + + // when + let res1: TransactionModification = serde_json::from_str(s1).unwrap(); + let res2: TransactionModification = serde_json::from_str(s2).unwrap(); + + // then + assert_eq!(res1, TransactionModification { + gas_price: Some(U256::from_str("0ba43b7400").unwrap()), + }); + assert_eq!(res2, TransactionModification { + gas_price: None, + }); + } } diff --git a/signer/Cargo.toml b/signer/Cargo.toml index 51b1b1e8d..170c9320e 100644 --- a/signer/Cargo.toml +++ b/signer/Cargo.toml @@ -9,13 +9,8 @@ build = "build.rs" [build-dependencies] rustc_version = "0.1" -serde_codegen = { version = "0.7.0", optional = true } -syntex = "^0.32.0" [dependencies] -serde = "0.7.0" -serde_json = "0.7.0" -rustc-serialize = "0.3" jsonrpc-core = "2.0" log = "0.3" env_logger = "0.3" @@ -23,10 +18,7 @@ ws = "0.4.7" ethcore-util = { path = "../util" } ethcore-rpc = { path = "../rpc" } -serde_macros = { version = "0.7.0", optional = true } clippy = { version = "0.0.69", optional = true} [features] -default = ["serde_codegen"] -nightly = ["serde_macros"] dev = ["clippy"] diff --git a/signer/build.rs b/signer/build.rs index 2bcfc7da5..41b9a1b3e 100644 --- a/signer/build.rs +++ b/signer/build.rs @@ -19,34 +19,7 @@ extern crate rustc_version; use rustc_version::{version_meta, Channel}; fn main() { - serde::main(); if let Channel::Nightly = version_meta().channel { println!("cargo:rustc-cfg=nightly"); } } - -#[cfg(not(feature = "serde_macros"))] -mod serde { - extern crate syntex; - extern crate serde_codegen; - - use std::env; - use std::path::Path; - - pub fn main() { - let out_dir = env::var_os("OUT_DIR").unwrap(); - - let src = Path::new("src/types/mod.rs.in"); - let dst = Path::new(&out_dir).join("mod.rs"); - - let mut registry = syntex::Registry::new(); - - serde_codegen::register(&mut registry); - registry.expand("", &src, &dst).unwrap(); - } -} - -#[cfg(feature = "serde_macros")] -mod serde { - pub fn main() {} -} diff --git a/signer/src/lib.rs b/signer/src/lib.rs index f3a963ac7..e2df72bcc 100644 --- a/signer/src/lib.rs +++ b/signer/src/lib.rs @@ -17,8 +17,6 @@ #![warn(missing_docs)] #![cfg_attr(all(nightly, feature="dev"), feature(plugin))] #![cfg_attr(all(nightly, feature="dev"), plugin(clippy))] -// Generated by serde -#![cfg_attr(all(nightly, feature="dev"), allow(redundant_closure_call))] //! Signer module //! @@ -45,18 +43,12 @@ extern crate log; extern crate env_logger; -extern crate serde; -extern crate serde_json; -extern crate rustc_serialize; - extern crate ethcore_util as util; extern crate ethcore_rpc as rpc; extern crate jsonrpc_core; extern crate ws; -mod signing_queue; mod ws_server; - pub use ws_server::*; #[cfg(test)] diff --git a/signer/src/signing_queue.rs b/signer/src/signing_queue.rs deleted file mode 100644 index 611d467c2..000000000 --- a/signer/src/signing_queue.rs +++ /dev/null @@ -1,74 +0,0 @@ -// 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 . - -use std::collections::HashSet; -use rpc::v1::types::TransactionRequest; - -pub trait SigningQueue { - fn add_request(&mut self, transaction: TransactionRequest); - - fn remove_request(&mut self, id: TransactionRequest); - - fn requests(&self) -> &HashSet; -} - -impl SigningQueue for HashSet { - fn add_request(&mut self, transaction: TransactionRequest) { - self.insert(transaction); - } - - fn remove_request(&mut self, id: TransactionRequest) { - self.remove(&id); - } - - fn requests(&self) -> &HashSet { - self - } -} - - -#[cfg(test)] -mod test { - use std::collections::HashSet; - use util::hash::Address; - use util::numbers::U256; - use rpc::v1::types::TransactionRequest; - use super::*; - - #[test] - fn should_work_for_hashset() { - // given - let mut queue = HashSet::new(); - - let request = TransactionRequest { - from: Address::from(1), - to: Some(Address::from(2)), - gas_price: None, - gas: None, - value: Some(U256::from(10_000_000)), - data: None, - nonce: None, - }; - - // when - queue.add_request(request.clone()); - let all = queue.requests(); - - // then - assert_eq!(all.len(), 1); - assert!(all.contains(&request)); - } -} diff --git a/signer/src/types/mod.rs b/signer/src/types/mod.rs deleted file mode 100644 index d5e15046a..000000000 --- a/signer/src/types/mod.rs +++ /dev/null @@ -1,23 +0,0 @@ -// 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 . - -//! Reusable types with JSON Serialization. - -#[cfg(feature = "serde_macros")] -include!("mod.rs.in"); - -#[cfg(not(feature = "serde_macros"))] -include!(concat!(env!("OUT_DIR"), "/mod.rs")); diff --git a/signer/src/types/mod.rs.in b/signer/src/types/mod.rs.in deleted file mode 100644 index a59f81ece..000000000 --- a/signer/src/types/mod.rs.in +++ /dev/null @@ -1,25 +0,0 @@ -// 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 . - - - - - - - - - -// TODO [ToDr] Types are empty for now. But they are about to come. diff --git a/signer/src/ws_server/mod.rs b/signer/src/ws_server/mod.rs index bb10bc5c1..bc8fb33f8 100644 --- a/signer/src/ws_server/mod.rs +++ b/signer/src/ws_server/mod.rs @@ -25,6 +25,7 @@ use std::sync::Arc; use std::net::SocketAddr; use util::panics::{PanicHandler, OnPanicListener, MayPanic}; use jsonrpc_core::{IoHandler, IoDelegate}; +use rpc::Extendable; mod session; @@ -57,6 +58,12 @@ impl Default for ServerBuilder { } } +impl Extendable for ServerBuilder { + fn add_delegate(&self, delegate: IoDelegate) { + self.handler.add_delegate(delegate); + } +} + impl ServerBuilder { /// Creates new `ServerBuilder` pub fn new() -> Self { @@ -65,11 +72,6 @@ impl ServerBuilder { } } - /// Adds rpc delegate - pub fn add_delegate(&self, delegate: IoDelegate) where D: Send + Sync + 'static { - self.handler.add_delegate(delegate); - } - /// Starts a new `WebSocket` server in separate thread. /// Returns a `Server` handle which closes the server when droped. pub fn start(self, addr: SocketAddr) -> Result {