From b38488dd0789a6ee817cf6ba87116c3ee83d53b9 Mon Sep 17 00:00:00 2001 From: arkpar Date: Fri, 17 Jun 2016 12:58:28 +0200 Subject: [PATCH 01/22] Network shutdown --- ethcore/src/service.rs | 23 +++++--- parity/cli.rs | 2 + parity/io_handler.rs | 19 ++++++- parity/main.rs | 11 ++-- rpc/src/v1/impls/net.rs | 10 ++++ rpc/src/v1/tests/helpers/sync_provider.rs | 6 +++ rpc/src/v1/traits/net.rs | 8 +++ sync/src/lib.rs | 24 +++++++-- util/src/io/service.rs | 65 +++++++++++++++-------- util/src/network/host.rs | 47 ++++++++++++---- util/src/network/service.rs | 55 +++++++++++++------ util/src/network/stats.rs | 2 +- 12 files changed, 205 insertions(+), 67 deletions(-) diff --git a/ethcore/src/service.rs b/ethcore/src/service.rs index 13f1c9f74..6adca7b52 100644 --- a/ethcore/src/service.rs +++ b/ethcore/src/service.rs @@ -41,6 +41,10 @@ pub enum SyncMessage { NewChainHead, /// A block is ready BlockVerified, + /// Start network command. + StartNetwork, + /// Stop network command. + StopNetwork, } /// IO Message type used for Network service @@ -48,17 +52,20 @@ pub type NetSyncMessage = NetworkIoMessage; /// Client service setup. Creates and registers client and network services with the IO subsystem. pub struct ClientService { - net_service: NetworkService, + net_service: Arc>, client: Arc, panic_handler: Arc } impl ClientService { /// Start the service in a separate thread. - pub fn start(config: ClientConfig, spec: Spec, net_config: NetworkConfiguration, db_path: &Path, miner: Arc) -> Result { + pub fn start(config: ClientConfig, spec: Spec, net_config: NetworkConfiguration, db_path: &Path, miner: Arc, enable_network: bool) -> Result { let panic_handler = PanicHandler::new_in_arc(); - let mut net_service = try!(NetworkService::start(net_config)); + let net_service = try!(NetworkService::new(net_config)); panic_handler.forward_from(&net_service); + if enable_network { + try!(net_service.start()); + } info!("Starting {}", net_service.host_info()); info!("Configured for {} using {:?} engine", spec.name, spec.engine.name()); @@ -70,7 +77,7 @@ impl ClientService { try!(net_service.io().register_handler(client_io)); Ok(ClientService { - net_service: net_service, + net_service: Arc::new(net_service), client: client, panic_handler: panic_handler, }) @@ -82,8 +89,8 @@ impl ClientService { } /// Get general IO interface - pub fn io(&mut self) -> &mut IoService { - self.net_service.io() + pub fn register_io_handler(&self, handler: Arc + Send>) -> Result<(), IoError> { + self.net_service.io().register_handler(handler) } /// Get client interface @@ -92,8 +99,8 @@ impl ClientService { } /// Get network service component - pub fn network(&mut self) -> &mut NetworkService { - &mut self.net_service + pub fn network(&mut self) -> Arc> { + self.net_service.clone() } } diff --git a/parity/cli.rs b/parity/cli.rs index b8aa88299..7e2a1d6a0 100644 --- a/parity/cli.rs +++ b/parity/cli.rs @@ -52,6 +52,7 @@ Account Options: --no-import-keys Do not import keys from legacy clients. Networking Options: + --no-network Disable p2p networking. --port PORT Override the port on which the node should listen [default: 30303]. --peers NUM Try to maintain that many peers [default: 25]. @@ -266,6 +267,7 @@ pub struct Args { pub flag_format: Option, pub flag_jitvm: bool, pub flag_no_color: bool, + pub flag_no_network: bool, // legacy... pub flag_geth: bool, pub flag_nodekey: Option, diff --git a/parity/io_handler.rs b/parity/io_handler.rs index d130d584c..569a11716 100644 --- a/parity/io_handler.rs +++ b/parity/io_handler.rs @@ -16,10 +16,10 @@ use std::sync::Arc; use ethcore::client::Client; -use ethcore::service::NetSyncMessage; +use ethcore::service::{NetSyncMessage, SyncMessage}; use ethsync::EthSync; use util::keys::store::AccountService; -use util::{TimerToken, IoHandler, IoContext}; +use util::{TimerToken, IoHandler, IoContext, NetworkService, NetworkIoMessage}; use informant::Informant; @@ -33,6 +33,7 @@ pub struct ClientIoHandler { pub sync: Arc, pub accounts: Arc, pub info: Informant, + pub network: Arc>, } impl IoHandler for ClientIoHandler { @@ -48,6 +49,20 @@ impl IoHandler for ClientIoHandler { _ => {} } } + + fn message(&self, _io: &IoContext, message: &NetSyncMessage) { + match *message { + NetworkIoMessage::User(SyncMessage::StartNetwork) => { + info!("Starting network"); + self.network.start().unwrap_or_else(|e| warn!("Error starting network: {:?}", e)); + }, + NetworkIoMessage::User(SyncMessage::StopNetwork) => { + info!("Stopping network"); + self.network.stop().unwrap_or_else(|e| warn!("Error stopping network: {:?}", e)); + }, + _ => {/* Ignore other messages */}, + } + } } diff --git a/parity/main.rs b/parity/main.rs index 284dac673..80ea29576 100644 --- a/parity/main.rs +++ b/parity/main.rs @@ -198,7 +198,7 @@ fn execute_client(conf: Configuration, spec: Spec, client_config: ClientConfig) // Build client let mut service = ClientService::start( - client_config, spec, net_settings, Path::new(&conf.path()), miner.clone() + client_config, spec, net_settings, Path::new(&conf.path()), miner.clone(), !conf.args.flag_no_network ).unwrap_or_else(|e| die_with_error("Client", e)); panic_handler.forward_from(&service); @@ -208,7 +208,7 @@ fn execute_client(conf: Configuration, spec: Spec, client_config: ClientConfig) let network_settings = Arc::new(conf.network_settings()); // Sync - let sync = EthSync::register(service.network(), sync_config, client.clone()); + let sync = EthSync::register(service.network().deref(), sync_config, client.clone()); let deps_for_rpc_apis = Arc::new(rpc_apis::Dependencies { signer_port: conf.signer_port(), @@ -269,8 +269,9 @@ fn execute_client(conf: Configuration, spec: Spec, client_config: ClientConfig) info: Informant::new(conf.have_color()), sync: sync.clone(), accounts: account_service.clone(), + network: service.network(), }); - service.io().register_handler(io_handler).expect("Error registering IO handler"); + service.register_io_handler(io_handler).expect("Error registering IO handler"); // Handle exit wait_for_exit(panic_handler, rpc_server, dapps_server, signer_server); @@ -309,7 +310,7 @@ fn execute_export(conf: Configuration) { // Build client let service = ClientService::start( - client_config, spec, net_settings, Path::new(&conf.path()), Arc::new(Miner::default()), + client_config, spec, net_settings, Path::new(&conf.path()), Arc::new(Miner::default()), false ).unwrap_or_else(|e| die_with_error("Client", e)); panic_handler.forward_from(&service); @@ -380,7 +381,7 @@ fn execute_import(conf: Configuration) { // Build client let service = ClientService::start( - client_config, spec, net_settings, Path::new(&conf.path()), Arc::new(Miner::default()), + client_config, spec, net_settings, Path::new(&conf.path()), Arc::new(Miner::default()), false ).unwrap_or_else(|e| die_with_error("Client", e)); panic_handler.forward_from(&service); diff --git a/rpc/src/v1/impls/net.rs b/rpc/src/v1/impls/net.rs index fb502e6cf..977561ede 100644 --- a/rpc/src/v1/impls/net.rs +++ b/rpc/src/v1/impls/net.rs @@ -47,4 +47,14 @@ impl Net for NetClient where S: SyncProvider + 'static { // right now (11 march 2016), we are always listening for incoming connections Ok(Value::Bool(true)) } + + fn start_network(&self, _: Params) -> Result { + take_weak!(self.sync).start_network(); + Ok(Value::Bool(true)) + } + + fn stop_network(&self, _: Params) -> Result { + take_weak!(self.sync).stop_network(); + Ok(Value::Bool(true)) + } } diff --git a/rpc/src/v1/tests/helpers/sync_provider.rs b/rpc/src/v1/tests/helpers/sync_provider.rs index 114b5b08f..11fa1cb9e 100644 --- a/rpc/src/v1/tests/helpers/sync_provider.rs +++ b/rpc/src/v1/tests/helpers/sync_provider.rs @@ -59,5 +59,11 @@ impl SyncProvider for TestSyncProvider { fn status(&self) -> SyncStatus { self.status.read().unwrap().clone() } + + fn start_network(&self) { + } + + fn stop_network(&self) { + } } diff --git a/rpc/src/v1/traits/net.rs b/rpc/src/v1/traits/net.rs index 56fba3e32..b65f53491 100644 --- a/rpc/src/v1/traits/net.rs +++ b/rpc/src/v1/traits/net.rs @@ -30,12 +30,20 @@ pub trait Net: Sized + Send + Sync + 'static { /// Otherwise false. fn is_listening(&self, _: Params) -> Result; + /// Start the network. + fn start_network(&self, _: Params) -> Result; + + /// Stop the network. + fn stop_network(&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("net_version", Net::version); delegate.add_method("net_peerCount", Net::peer_count); delegate.add_method("net_listening", Net::is_listening); + delegate.add_method("net_start", Net::start_network); + delegate.add_method("net_stop", Net::stop_network); delegate } } diff --git a/sync/src/lib.rs b/sync/src/lib.rs index 9c7da71ab..ca31ea020 100644 --- a/sync/src/lib.rs +++ b/sync/src/lib.rs @@ -66,8 +66,10 @@ use util::network::{NetworkProtocolHandler, NetworkService, NetworkContext, Peer use util::TimerToken; use util::{U256, ONE_U256}; use ethcore::client::Client; -use ethcore::service::SyncMessage; +use ethcore::service::{SyncMessage, NetSyncMessage}; use io::NetSyncIo; +use util::io::IoChannel; +use util::NetworkIoMessage; use chain::ChainSync; mod chain; @@ -98,6 +100,10 @@ impl Default for SyncConfig { pub trait SyncProvider: Send + Sync { /// Get sync status fn status(&self) -> SyncStatus; + /// Start the network + fn start_network(&self); + /// Stop the network + fn stop_network(&self); } /// Ethereum network protocol handler @@ -105,18 +111,21 @@ pub struct EthSync { /// Shared blockchain client. TODO: this should evetually become an IPC endpoint chain: Arc, /// Sync strategy - sync: RwLock + sync: RwLock, + /// IO communication chnnel. + io_channel: RwLock>, } pub use self::chain::{SyncStatus, SyncState}; impl EthSync { /// Creates and register protocol with the network service - pub fn register(service: &mut NetworkService, config: SyncConfig, chain: Arc) -> Arc { + pub fn register(service: &NetworkService, config: SyncConfig, chain: Arc) -> Arc { let sync = ChainSync::new(config, chain.deref()); let sync = Arc::new(EthSync { chain: chain, sync: RwLock::new(sync), + io_channel: RwLock::new(IoChannel::disconnected()), }); service.register_protocol(sync.clone(), "eth", &[62u8, 63u8]).expect("Error registering eth protocol handler"); sync @@ -138,11 +147,20 @@ impl SyncProvider for EthSync { fn status(&self) -> SyncStatus { self.sync.read().unwrap().status() } + + fn start_network(&self) { + self.io_channel.read().unwrap().send(NetworkIoMessage::User(SyncMessage::StartNetwork)).expect("Error sending IO notification"); + } + + fn stop_network(&self) { + self.io_channel.read().unwrap().send(NetworkIoMessage::User(SyncMessage::StopNetwork)).expect("Error sending IO notification"); + } } impl NetworkProtocolHandler for EthSync { fn initialize(&self, io: &NetworkContext) { io.register_timer(0, 1000).expect("Error registering sync timer"); + *self.io_channel.write().unwrap() = io.io_channel(); } fn read(&self, io: &NetworkContext, peer: &PeerId, packet_id: u8, data: &[u8]) { diff --git a/util/src/io/service.rs b/util/src/io/service.rs index 409667c46..b924bbe44 100644 --- a/util/src/io/service.rs +++ b/util/src/io/service.rs @@ -17,10 +17,12 @@ use std::sync::*; use std::thread::{self, JoinHandle}; use std::collections::HashMap; +use std::ops::Deref; use mio::*; +use crossbeam::sync::chase_lev; +use slab::Slab; use error::*; use io::{IoError, IoHandler}; -use crossbeam::sync::chase_lev; use io::worker::{Worker, Work, WorkType}; use panics::*; @@ -33,6 +35,14 @@ pub type HandlerId = usize; /// Maximum number of tokens a handler can use pub const TOKENS_PER_HANDLER: usize = 16384; +const MAX_HANDLERS: usize = 8; + +fn compare_arcs(a: Arc, b: Arc) -> bool { + let p1 = &*a as *const T; + let p2 = &*b as *const T; + info!("{:p} == {:p} : {}", p1, p2 , p1 == p2); + p1 == p2 +} /// Messages used to communicate with the event loop from other threads. #[derive(Clone)] @@ -43,6 +53,9 @@ pub enum IoMessage where Message: Send + Clone + Sized { AddHandler { handler: Arc+Send>, }, + RemoveHandler { + handler_id: HandlerId, + }, AddTimer { handler_id: HandlerId, token: TimerToken, @@ -138,6 +151,15 @@ impl IoContext where Message: Send + Clone + 'static { pub fn channel(&self) -> IoChannel { self.channel.clone() } + + /// Unregister current IO handler. + pub fn unregister_handler(&self) -> Result<(), IoError> { + try!(self.channel.send_io(IoMessage::RemoveHandler { + handler_id: self.handler, + })); + Ok(()) + } + } #[derive(Clone)] @@ -149,7 +171,7 @@ struct UserTimer { /// Root IO handler. Manages user handlers, messages and IO timers. pub struct IoManager where Message: Send + Sync { timers: Arc>>, - handlers: Vec>>, + handlers: Slab>, HandlerId>, workers: Vec, worker_channel: chase_lev::Worker>, work_ready: Arc, @@ -175,7 +197,7 @@ impl IoManager where Message: Send + Sync + Clone + 'static { let mut io = IoManager { timers: Arc::new(RwLock::new(HashMap::new())), - handlers: Vec::new(), + handlers: Slab::new(MAX_HANDLERS), worker_channel: worker, workers: workers, work_ready: work_ready, @@ -192,10 +214,7 @@ impl Handler for IoManager where Message: Send + Clone + Sync fn ready(&mut self, _event_loop: &mut EventLoop, token: Token, events: EventSet) { let handler_index = token.as_usize() / TOKENS_PER_HANDLER; let token_id = token.as_usize() % TOKENS_PER_HANDLER; - if handler_index >= self.handlers.len() { - panic!("Unexpected stream token: {}", token.as_usize()); - } - let handler = self.handlers[handler_index].clone(); + let handler = self.handlers.get(handler_index).unwrap_or_else(|| panic!("Unexpected stream token: {}", token.as_usize())).clone(); if events.is_hup() { self.worker_channel.push(Work { work_type: WorkType::Hup, token: token_id, handler: handler.clone(), handler_id: handler_index }); @@ -214,12 +233,9 @@ impl Handler for IoManager where Message: Send + Clone + Sync fn timeout(&mut self, event_loop: &mut EventLoop, token: Token) { let handler_index = token.as_usize() / TOKENS_PER_HANDLER; let token_id = token.as_usize() % TOKENS_PER_HANDLER; - if handler_index >= self.handlers.len() { - panic!("Unexpected timer token: {}", token.as_usize()); - } + let handler = self.handlers.get(handler_index).unwrap_or_else(|| panic!("Unexpected stream token: {}", token.as_usize())).clone(); if let Some(timer) = self.timers.read().unwrap().get(&token.as_usize()) { event_loop.timeout_ms(token, timer.delay).expect("Error re-registering user timer"); - let handler = self.handlers[handler_index].clone(); self.worker_channel.push(Work { work_type: WorkType::Timeout, token: token_id, handler: handler, handler_id: handler_index }); self.work_ready.notify_all(); } @@ -232,12 +248,14 @@ impl Handler for IoManager where Message: Send + Clone + Sync event_loop.shutdown(); }, IoMessage::AddHandler { handler } => { - let handler_id = { - self.handlers.push(handler.clone()); - self.handlers.len() - 1 - }; + let handler_id = self.handlers.insert(handler.clone()).unwrap_or_else(|_| panic!("Too many handlers registered")); handler.initialize(&IoContext::new(IoChannel::new(event_loop.channel()), handler_id)); }, + IoMessage::RemoveHandler { handler_id } => { + // TODO: flush event loop + self.handlers.remove(handler_id); + info!("{} left", self.handlers.count()); + }, IoMessage::AddTimer { handler_id, token, delay } => { let timer_id = token + handler_id * TOKENS_PER_HANDLER; let timeout = event_loop.timeout_ms(Token(timer_id), delay).expect("Error registering user timer"); @@ -267,9 +285,12 @@ impl Handler for IoManager where Message: Send + Clone + Sync handler.update_stream(token, Token(token + handler_id * TOKENS_PER_HANDLER), event_loop); }, IoMessage::UserMessage(data) => { - for n in 0 .. self.handlers.len() { - let handler = self.handlers[n].clone(); - self.worker_channel.push(Work { work_type: WorkType::Message(data.clone()), token: 0, handler: handler, handler_id: n }); + //TODO: better way to iterate the slab + for id in 0 .. MAX_HANDLERS { + if let Some(h) = self.handlers.get(id) { + let handler = h.clone(); + self.worker_channel.push(Work { work_type: WorkType::Message(data.clone()), token: 0, handler: handler, handler_id: id }); + } } self.work_ready.notify_all(); } @@ -351,8 +372,8 @@ impl IoService where Message: Send + Sync + Clone + 'static { }) } - /// Regiter a IO hadnler with the event loop. - pub fn register_handler(&mut self, handler: Arc+Send>) -> Result<(), IoError> { + /// Regiter an IO handler with the event loop. + pub fn register_handler(&self, handler: Arc+Send>) -> Result<(), IoError> { try!(self.host_channel.send(IoMessage::AddHandler { handler: handler, })); @@ -360,13 +381,13 @@ impl IoService where Message: Send + Sync + Clone + 'static { } /// Send a message over the network. Normaly `HostIo::send` should be used. This can be used from non-io threads. - pub fn send_message(&mut self, message: Message) -> Result<(), IoError> { + pub fn send_message(&self, message: Message) -> Result<(), IoError> { try!(self.host_channel.send(IoMessage::UserMessage(message))); Ok(()) } /// Create a new message channel - pub fn channel(&mut self) -> IoChannel { + pub fn channel(&self) -> IoChannel { IoChannel { channel: Some(self.host_channel.clone()) } } } diff --git a/util/src/network/host.rs b/util/src/network/host.rs index abace1983..6001769fe 100644 --- a/util/src/network/host.rs +++ b/util/src/network/host.rs @@ -49,7 +49,7 @@ const MAX_HANDSHAKES: usize = 80; const MAX_HANDSHAKES_PER_ROUND: usize = 32; const MAINTENANCE_TIMEOUT: u64 = 1000; -#[derive(Debug)] +#[derive(Debug, Clone)] /// Network service configuration pub struct NetworkConfiguration { /// Directory path to store network configuration. None means nothing will be saved @@ -233,6 +233,11 @@ impl<'s, Message> NetworkContext<'s, Message> where Message: Send + Sync + Clone self.io.message(NetworkIoMessage::User(msg)); } + /// Send an IO message + pub fn io_channel(&self) -> IoChannel> { + self.io.channel() + } + /// Disable current protocol capability for given peer. If no capabilities left peer gets disconnected. pub fn disable_peer(&self, peer: PeerId) { //TODO: remove capability, disconnect if no capabilities left @@ -327,7 +332,7 @@ pub struct Host where Message: Send + Sync + Clone { impl Host where Message: Send + Sync + Clone { /// Create a new instance - pub fn new(config: NetworkConfiguration) -> Result, UtilError> { + pub fn new(config: NetworkConfiguration, stats: Arc) -> Result, UtilError> { let mut listen_address = match config.listen_address { None => SocketAddr::from_str("0.0.0.0:30304").unwrap(), Some(addr) => addr, @@ -371,7 +376,7 @@ impl Host where Message: Send + Sync + Clone { handlers: RwLock::new(HashMap::new()), timers: RwLock::new(HashMap::new()), timer_counter: RwLock::new(USER_TIMER), - stats: Arc::new(NetworkStats::default()), + stats: stats, pinned_nodes: Vec::new(), num_sessions: AtomicUsize::new(0), }; @@ -383,10 +388,6 @@ impl Host where Message: Send + Sync + Clone { Ok(host) } - pub fn stats(&self) -> Arc { - self.stats.clone() - } - pub fn add_node(&mut self, id: &str) { match Node::from_str(id) { Err(e) => { debug!(target: "network", "Could not add node {}: {:?}", id, e); }, @@ -401,8 +402,8 @@ impl Host where Message: Send + Sync + Clone { } } - pub fn client_version(&self) -> String { - self.info.read().unwrap().client_version.clone() + pub fn client_version() -> String { + version() } pub fn external_url(&self) -> Option { @@ -415,6 +416,23 @@ impl Host where Message: Send + Sync + Clone { r } + pub fn stop(&self, io: &IoContext>) -> Result<(), UtilError> { + let mut to_kill = Vec::new(); + for e in self.sessions.write().unwrap().iter_mut() { + let mut s = e.lock().unwrap(); + if !s.keep_alive(io) { + s.disconnect(io, DisconnectReason::PingTimeout); + to_kill.push(s.token()); + } + } + for p in to_kill { + trace!(target: "network", "Ping timeout: {}", p); + self.kill_connection(p, io, true); + } + io.unregister_handler(); + Ok(()) + } + fn init_public_interface(&self, io: &IoContext>) -> Result<(), UtilError> { io.clear_timer(INIT_PUBLIC).unwrap(); if self.info.read().unwrap().public_endpoint.is_some() { @@ -767,6 +785,13 @@ impl Host where Message: Send + Sync + Clone { } } + +impl Drop for Host where Message: Send + Sync + Clone { + fn drop(&mut self) { + info!("Dropping host"); + } +} + impl IoHandler> for Host where Message: Send + Sync + Clone + 'static { /// Initialize networking fn initialize(&self, io: &IoContext>) { @@ -831,8 +856,8 @@ impl IoHandler> for Host where Messa }, _ => match self.timers.read().unwrap().get(&token).cloned() { Some(timer) => match self.handlers.read().unwrap().get(timer.protocol).cloned() { - None => { warn!(target: "network", "No handler found for protocol: {:?}", timer.protocol) }, - Some(h) => { h.timeout(&NetworkContext::new(io, timer.protocol, None, self.sessions.clone()), timer.token); } + None => { warn!(target: "network", "No handler found for protocol: {:?}", timer.protocol) }, + Some(h) => { h.timeout(&NetworkContext::new(io, timer.protocol, None, self.sessions.clone()), timer.token); } }, None => { warn!("Unknown timer token: {}", token); } // timer is not registerd through us } diff --git a/util/src/network/service.rs b/util/src/network/service.rs index 49957f7e7..406126425 100644 --- a/util/src/network/service.rs +++ b/util/src/network/service.rs @@ -28,33 +28,33 @@ use io::*; pub struct NetworkService where Message: Send + Sync + Clone + 'static { io_service: IoService>, host_info: String, - host: Arc>, + host: RwLock>>>, stats: Arc, - panic_handler: Arc + panic_handler: Arc, + config: NetworkConfiguration, } impl NetworkService where Message: Send + Sync + Clone + 'static { /// Starts IO event loop - pub fn start(config: NetworkConfiguration) -> Result, UtilError> { + pub fn new(config: NetworkConfiguration) -> Result, UtilError> { let panic_handler = PanicHandler::new_in_arc(); - let mut io_service = try!(IoService::>::start()); + let io_service = try!(IoService::>::start()); panic_handler.forward_from(&io_service); - let host = Arc::new(try!(Host::new(config))); - let stats = host.stats().clone(); - let host_info = host.client_version(); - try!(io_service.register_handler(host.clone())); + let stats = Arc::new(NetworkStats::new()); + let host_info = Host::::client_version(); Ok(NetworkService { io_service: io_service, host_info: host_info, stats: stats, panic_handler: panic_handler, - host: host, + host: RwLock::new(None), + config: config, }) } /// Regiter a new protocol handler with the event loop. - pub fn register_protocol(&mut self, handler: Arc+Send + Sync>, protocol: ProtocolId, versions: &[u8]) -> Result<(), NetworkError> { + pub fn register_protocol(&self, handler: Arc+Send + Sync>, protocol: ProtocolId, versions: &[u8]) -> Result<(), NetworkError> { try!(self.io_service.send_message(NetworkIoMessage::AddHandler { handler: handler, protocol: protocol, @@ -69,8 +69,8 @@ impl NetworkService where Message: Send + Sync + Clone + 'stat } /// Returns underlying io service. - pub fn io(&mut self) -> &mut IoService> { - &mut self.io_service + pub fn io(&self) -> &IoService> { + &self.io_service } /// Returns network statistics. @@ -80,12 +80,37 @@ impl NetworkService where Message: Send + Sync + Clone + 'stat /// Returns external url if available. pub fn external_url(&self) -> Option { - self.host.external_url() + let host = self.host.read().unwrap(); + host.as_ref().and_then(|h| h.external_url()) } /// Returns external url if available. - pub fn local_url(&self) -> String { - self.host.local_url() + pub fn local_url(&self) -> Option { + let host = self.host.read().unwrap(); + host.as_ref().map(|h| h.local_url()) + } + + /// Start network IO + pub fn start(&self) -> Result<(), UtilError> { + let mut host = self.host.write().unwrap(); + if host.is_none() { + let h = Arc::new(try!(Host::new(self.config.clone(), self.stats.clone()))); + try!(self.io_service.register_handler(h.clone())); + *host = Some(h); + } + Ok(()) + } + + /// Stop network IO + pub fn stop(&self) -> Result<(), UtilError> { + let mut host = self.host.write().unwrap(); + if let Some(ref host) = *host { + info!("Unregistering handler"); + let io = IoContext::new(self.io_service.channel(), 0); //TODO: take token id from host + host.stop(&io); + } + *host = None; + Ok(()) } } diff --git a/util/src/network/stats.rs b/util/src/network/stats.rs index 30e751a81..cc3b845da 100644 --- a/util/src/network/stats.rs +++ b/util/src/network/stats.rs @@ -65,7 +65,7 @@ impl NetworkStats { self.sessions.load(Ordering::Relaxed) } - #[cfg(test)] + /// Create a new empty instance. pub fn new() -> NetworkStats { NetworkStats { recv: AtomicUsize::new(0), From 67ffac1df932db92f416c83b0d81d9bb176159a0 Mon Sep 17 00:00:00 2001 From: arkpar Date: Fri, 17 Jun 2016 16:01:33 +0200 Subject: [PATCH 02/22] Check for session expiration on peer registration --- sync/src/chain.rs | 4 ++++ sync/src/io.rs | 6 ++++++ sync/src/tests/chain.rs | 10 ++++++++++ util/src/network/host.rs | 7 ++++++- 4 files changed, 26 insertions(+), 1 deletion(-) diff --git a/sync/src/chain.rs b/sync/src/chain.rs index 90e18d8e8..7e23601e4 100644 --- a/sync/src/chain.rs +++ b/sync/src/chain.rs @@ -356,6 +356,10 @@ impl ChainSync { }; trace!(target: "sync", "New peer {} (protocol: {}, network: {:?}, difficulty: {:?}, latest:{}, genesis:{})", peer_id, peer.protocol_version, peer.network_id, peer.difficulty, peer.latest_hash, peer.genesis); + if io.is_expired() { + info!("Status packet from expired session {}:{}", peer_id, io.peer_info(peer_id)); + return Ok(()); + } if self.peers.contains_key(&peer_id) { warn!("Unexpected status packet from {}:{}", peer_id, io.peer_info(peer_id)); diff --git a/sync/src/io.rs b/sync/src/io.rs index 53a546e1c..5d4b32464 100644 --- a/sync/src/io.rs +++ b/sync/src/io.rs @@ -41,6 +41,8 @@ pub trait SyncIo { fn is_chain_queue_empty(&self) -> bool { self.chain().queue_info().is_empty() } + /// Check if the session is expired + fn is_expired(&self) -> bool; } /// Wraps `NetworkContext` and the blockchain client @@ -83,6 +85,10 @@ impl<'s, 'h> SyncIo for NetSyncIo<'s, 'h> { fn peer_info(&self, peer_id: PeerId) -> String { self.network.peer_info(peer_id) } + + fn is_expired(&self) -> bool { + self.network.is_expired() + } } diff --git a/sync/src/tests/chain.rs b/sync/src/tests/chain.rs index 09e83e358..7c7d70dde 100644 --- a/sync/src/tests/chain.rs +++ b/sync/src/tests/chain.rs @@ -192,3 +192,13 @@ fn restart_on_broken_chain() { assert_eq!(net.peer(0).chain.chain_info().best_block_number, 5); } + +#[test] +fn high_td_attach() { + let mut net = TestNet::new(2); + net.peer_mut(1).chain.add_blocks(10, EachBlockWith::Uncle); + net.peer_mut(1).chain.corrupt_block_parent(6); + net.sync_steps(20); + + assert_eq!(net.peer(0).chain.chain_info().best_block_number, 5); +} diff --git a/util/src/network/host.rs b/util/src/network/host.rs index 6001769fe..e6e76c472 100644 --- a/util/src/network/host.rs +++ b/util/src/network/host.rs @@ -18,7 +18,7 @@ use std::net::{SocketAddr}; use std::collections::{HashMap}; use std::str::{FromStr}; use std::sync::*; -use std::sync::atomic::{AtomicUsize, Ordering as AtomicOrdering}; +use std::sync::atomic::{AtomicUsize, AtomicBool, Ordering as AtomicOrdering}; use std::ops::*; use std::cmp::min; use std::path::{Path, PathBuf}; @@ -249,6 +249,11 @@ impl<'s, Message> NetworkContext<'s, Message> where Message: Send + Sync + Clone self.io.message(NetworkIoMessage::Disconnect(peer)); } + /// Sheck if the session is till active. + pub fn is_expired(&self) -> bool { + self.session.as_ref().map(|s| s.lock().unwrap().expired()).unwrap_or(false) + } + /// Register a new IO timer. 'IoHandler::timeout' will be called with the token. pub fn register_timer(&self, token: TimerToken, ms: u64) -> Result<(), UtilError> { self.io.message(NetworkIoMessage::AddTimer { From c340d8a34f2e500a87e79a150026fb18f235cfc9 Mon Sep 17 00:00:00 2001 From: arkpar Date: Fri, 17 Jun 2016 18:26:54 +0200 Subject: [PATCH 03/22] Tests --- ethcore/src/service.rs | 2 +- parity/io_handler.rs | 1 + parity/main.rs | 5 +-- sync/src/chain.rs | 2 +- sync/src/lib.rs | 16 ++++++--- sync/src/tests/helpers.rs | 4 +++ util/src/io/mod.rs | 2 +- util/src/io/service.rs | 65 +++++++++++++++++-------------------- util/src/network/host.rs | 34 +++++++++++-------- util/src/network/mod.rs | 3 +- util/src/network/service.rs | 2 +- util/src/network/tests.rs | 33 ++++++++++++++----- 12 files changed, 99 insertions(+), 70 deletions(-) diff --git a/ethcore/src/service.rs b/ethcore/src/service.rs index 6adca7b52..d9040113f 100644 --- a/ethcore/src/service.rs +++ b/ethcore/src/service.rs @@ -156,7 +156,7 @@ mod tests { fn it_can_be_started() { let spec = get_test_spec(); let temp_path = RandomTempPath::new(); - let service = ClientService::start(ClientConfig::default(), spec, NetworkConfiguration::new_local(), &temp_path.as_path(), Arc::new(Miner::default())); + let service = ClientService::start(ClientConfig::default(), spec, NetworkConfiguration::new_local(), &temp_path.as_path(), Arc::new(Miner::default()), false); assert!(service.is_ok()); } } diff --git a/parity/io_handler.rs b/parity/io_handler.rs index 569a11716..2460cfec7 100644 --- a/parity/io_handler.rs +++ b/parity/io_handler.rs @@ -55,6 +55,7 @@ impl IoHandler for ClientIoHandler { NetworkIoMessage::User(SyncMessage::StartNetwork) => { info!("Starting network"); self.network.start().unwrap_or_else(|e| warn!("Error starting network: {:?}", e)); + EthSync::register(&*self.network, self.sync.clone()).unwrap_or_else(|e| warn!("Error registering eth protocol handler: {}", e)); }, NetworkIoMessage::User(SyncMessage::StopNetwork) => { info!("Stopping network"); diff --git a/parity/main.rs b/parity/main.rs index 80ea29576..f600d4de0 100644 --- a/parity/main.rs +++ b/parity/main.rs @@ -79,7 +79,7 @@ use std::thread::sleep; use std::time::Duration; use rustc_serialize::hex::FromHex; use ctrlc::CtrlC; -use util::{H256, ToPretty, NetworkConfiguration, PayloadInfo, Bytes}; +use util::{H256, ToPretty, NetworkConfiguration, PayloadInfo, Bytes, UtilError}; use util::panics::{MayPanic, ForwardPanic, PanicHandler}; use ethcore::client::{BlockID, BlockChainClient, ClientConfig, get_db_path}; use ethcore::error::{Error, ImportError}; @@ -208,7 +208,8 @@ fn execute_client(conf: Configuration, spec: Spec, client_config: ClientConfig) let network_settings = Arc::new(conf.network_settings()); // Sync - let sync = EthSync::register(service.network().deref(), sync_config, client.clone()); + let sync = EthSync::new(sync_config, client.clone()); + EthSync::register(&*service.network(), sync.clone()).unwrap_or_else(|e| die_with_error("Error registering eth protocol handler", UtilError::from(e).into())); let deps_for_rpc_apis = Arc::new(rpc_apis::Dependencies { signer_port: conf.signer_port(), diff --git a/sync/src/chain.rs b/sync/src/chain.rs index 7e23601e4..8b6aa9d7c 100644 --- a/sync/src/chain.rs +++ b/sync/src/chain.rs @@ -357,7 +357,7 @@ impl ChainSync { trace!(target: "sync", "New peer {} (protocol: {}, network: {:?}, difficulty: {:?}, latest:{}, genesis:{})", peer_id, peer.protocol_version, peer.network_id, peer.difficulty, peer.latest_hash, peer.genesis); if io.is_expired() { - info!("Status packet from expired session {}:{}", peer_id, io.peer_info(peer_id)); + trace!("Status packet from expired session {}:{}", peer_id, io.peer_info(peer_id)); return Ok(()); } diff --git a/sync/src/lib.rs b/sync/src/lib.rs index ca31ea020..cabc55ace 100644 --- a/sync/src/lib.rs +++ b/sync/src/lib.rs @@ -41,11 +41,13 @@ //! use ethcore::miner::Miner; //! //! fn main() { -//! let mut service = NetworkService::start(NetworkConfiguration::new()).unwrap(); +//! let mut service = NetworkService::new(NetworkConfiguration::new()).unwrap(); +//! service.start().unwrap(); //! let dir = env::temp_dir(); //! let client = Client::new(ClientConfig::default(), ethereum::new_frontier(), &dir, Arc::new(Miner::default()), service.io().channel()).unwrap(); //! let miner = Miner::new(false, ethereum::new_frontier()); -//! EthSync::register(&mut service, SyncConfig::default(), client); +//! let sync = EthSync::new(SyncConfig::default(), client); +//! EthSync::register(&mut service, sync); //! } //! ``` @@ -69,7 +71,7 @@ use ethcore::client::Client; use ethcore::service::{SyncMessage, NetSyncMessage}; use io::NetSyncIo; use util::io::IoChannel; -use util::NetworkIoMessage; +use util::{NetworkIoMessage, NetworkError}; use chain::ChainSync; mod chain; @@ -120,17 +122,21 @@ pub use self::chain::{SyncStatus, SyncState}; impl EthSync { /// Creates and register protocol with the network service - pub fn register(service: &NetworkService, config: SyncConfig, chain: Arc) -> Arc { + pub fn new(config: SyncConfig, chain: Arc) -> Arc { let sync = ChainSync::new(config, chain.deref()); let sync = Arc::new(EthSync { chain: chain, sync: RwLock::new(sync), io_channel: RwLock::new(IoChannel::disconnected()), }); - service.register_protocol(sync.clone(), "eth", &[62u8, 63u8]).expect("Error registering eth protocol handler"); sync } + /// Register protocol with the network service + pub fn register(service: &NetworkService, sync: Arc) -> Result<(), NetworkError> { + service.register_protocol(sync.clone(), "eth", &[62u8, 63u8]) + } + /// Stop sync pub fn stop(&mut self, io: &mut NetworkContext) { self.sync.write().unwrap().abort(&mut NetSyncIo::new(io, self.chain.deref())); diff --git a/sync/src/tests/helpers.rs b/sync/src/tests/helpers.rs index a9163b52e..6496a43d5 100644 --- a/sync/src/tests/helpers.rs +++ b/sync/src/tests/helpers.rs @@ -43,6 +43,10 @@ impl<'p> SyncIo for TestIo<'p> { fn disconnect_peer(&mut self, _peer_id: PeerId) { } + fn is_expired(&self) -> bool { + false + } + fn respond(&mut self, packet_id: PacketId, data: Vec) -> Result<(), UtilError> { self.queue.push_back(TestPacket { data: data, diff --git a/util/src/io/mod.rs b/util/src/io/mod.rs index 93805a46a..3229c95d5 100644 --- a/util/src/io/mod.rs +++ b/util/src/io/mod.rs @@ -142,7 +142,7 @@ mod tests { #[test] fn test_service_register_handler () { - let mut service = IoService::::start().expect("Error creating network service"); + let service = IoService::::start().expect("Error creating network service"); service.register_handler(Arc::new(MyHandler)).unwrap(); } diff --git a/util/src/io/service.rs b/util/src/io/service.rs index b924bbe44..65be55540 100644 --- a/util/src/io/service.rs +++ b/util/src/io/service.rs @@ -17,7 +17,6 @@ use std::sync::*; use std::thread::{self, JoinHandle}; use std::collections::HashMap; -use std::ops::Deref; use mio::*; use crossbeam::sync::chase_lev; use slab::Slab; @@ -37,13 +36,6 @@ pub type HandlerId = usize; pub const TOKENS_PER_HANDLER: usize = 16384; const MAX_HANDLERS: usize = 8; -fn compare_arcs(a: Arc, b: Arc) -> bool { - let p1 = &*a as *const T; - let p2 = &*b as *const T; - info!("{:p} == {:p} : {}", p1, p2 , p1 == p2); - p1 == p2 -} - /// Messages used to communicate with the event loop from other threads. #[derive(Clone)] pub enum IoMessage where Message: Send + Clone + Sized { @@ -214,30 +206,31 @@ impl Handler for IoManager where Message: Send + Clone + Sync fn ready(&mut self, _event_loop: &mut EventLoop, token: Token, events: EventSet) { let handler_index = token.as_usize() / TOKENS_PER_HANDLER; let token_id = token.as_usize() % TOKENS_PER_HANDLER; - let handler = self.handlers.get(handler_index).unwrap_or_else(|| panic!("Unexpected stream token: {}", token.as_usize())).clone(); - - if events.is_hup() { - self.worker_channel.push(Work { work_type: WorkType::Hup, token: token_id, handler: handler.clone(), handler_id: handler_index }); - } - else { - if events.is_readable() { - self.worker_channel.push(Work { work_type: WorkType::Readable, token: token_id, handler: handler.clone(), handler_id: handler_index }); + if let Some(handler) = self.handlers.get(handler_index) { + if events.is_hup() { + self.worker_channel.push(Work { work_type: WorkType::Hup, token: token_id, handler: handler.clone(), handler_id: handler_index }); } - if events.is_writable() { - self.worker_channel.push(Work { work_type: WorkType::Writable, token: token_id, handler: handler.clone(), handler_id: handler_index }); + else { + if events.is_readable() { + self.worker_channel.push(Work { work_type: WorkType::Readable, token: token_id, handler: handler.clone(), handler_id: handler_index }); + } + if events.is_writable() { + self.worker_channel.push(Work { work_type: WorkType::Writable, token: token_id, handler: handler.clone(), handler_id: handler_index }); + } } + self.work_ready.notify_all(); } - self.work_ready.notify_all(); } fn timeout(&mut self, event_loop: &mut EventLoop, token: Token) { let handler_index = token.as_usize() / TOKENS_PER_HANDLER; let token_id = token.as_usize() % TOKENS_PER_HANDLER; - let handler = self.handlers.get(handler_index).unwrap_or_else(|| panic!("Unexpected stream token: {}", token.as_usize())).clone(); - if let Some(timer) = self.timers.read().unwrap().get(&token.as_usize()) { - event_loop.timeout_ms(token, timer.delay).expect("Error re-registering user timer"); - self.worker_channel.push(Work { work_type: WorkType::Timeout, token: token_id, handler: handler, handler_id: handler_index }); - self.work_ready.notify_all(); + if let Some(handler) = self.handlers.get(handler_index) { + if let Some(timer) = self.timers.read().unwrap().get(&token.as_usize()) { + event_loop.timeout_ms(token, timer.delay).expect("Error re-registering user timer"); + self.worker_channel.push(Work { work_type: WorkType::Timeout, token: token_id, handler: handler.clone(), handler_id: handler_index }); + self.work_ready.notify_all(); + } } } @@ -254,7 +247,6 @@ impl Handler for IoManager where Message: Send + Clone + Sync IoMessage::RemoveHandler { handler_id } => { // TODO: flush event loop self.handlers.remove(handler_id); - info!("{} left", self.handlers.count()); }, IoMessage::AddTimer { handler_id, token, delay } => { let timer_id = token + handler_id * TOKENS_PER_HANDLER; @@ -268,21 +260,24 @@ impl Handler for IoManager where Message: Send + Clone + Sync } }, IoMessage::RegisterStream { handler_id, token } => { - let handler = self.handlers.get(handler_id).expect("Unknown handler id").clone(); - handler.register_stream(token, Token(token + handler_id * TOKENS_PER_HANDLER), event_loop); + if let Some(handler) = self.handlers.get(handler_id) { + handler.register_stream(token, Token(token + handler_id * TOKENS_PER_HANDLER), event_loop); + } }, IoMessage::DeregisterStream { handler_id, token } => { - let handler = self.handlers.get(handler_id).expect("Unknown handler id").clone(); - handler.deregister_stream(token, event_loop); - // unregister a timer associated with the token (if any) - let timer_id = token + handler_id * TOKENS_PER_HANDLER; - if let Some(timer) = self.timers.write().unwrap().remove(&timer_id) { - event_loop.clear_timeout(timer.timeout); + if let Some(handler) = self.handlers.get(handler_id) { + handler.deregister_stream(token, event_loop); + // unregister a timer associated with the token (if any) + let timer_id = token + handler_id * TOKENS_PER_HANDLER; + if let Some(timer) = self.timers.write().unwrap().remove(&timer_id) { + event_loop.clear_timeout(timer.timeout); + } } }, IoMessage::UpdateStreamRegistration { handler_id, token } => { - let handler = self.handlers.get(handler_id).expect("Unknown handler id").clone(); - handler.update_stream(token, Token(token + handler_id * TOKENS_PER_HANDLER), event_loop); + if let Some(handler) = self.handlers.get(handler_id) { + handler.update_stream(token, Token(token + handler_id * TOKENS_PER_HANDLER), event_loop); + } }, IoMessage::UserMessage(data) => { //TODO: better way to iterate the slab diff --git a/util/src/network/host.rs b/util/src/network/host.rs index e6e76c472..161fcfe3f 100644 --- a/util/src/network/host.rs +++ b/util/src/network/host.rs @@ -333,6 +333,7 @@ pub struct Host where Message: Send + Sync + Clone { stats: Arc, pinned_nodes: Vec, num_sessions: AtomicUsize, + stopping: AtomicBool, } impl Host where Message: Send + Sync + Clone { @@ -384,6 +385,7 @@ impl Host where Message: Send + Sync + Clone { stats: stats, pinned_nodes: Vec::new(), num_sessions: AtomicUsize::new(0), + stopping: AtomicBool::new(false), }; let boot_nodes = host.info.read().unwrap().config.boot_nodes.clone(); @@ -422,19 +424,18 @@ impl Host where Message: Send + Sync + Clone { } pub fn stop(&self, io: &IoContext>) -> Result<(), UtilError> { + self.stopping.store(true, AtomicOrdering::Release); let mut to_kill = Vec::new(); for e in self.sessions.write().unwrap().iter_mut() { let mut s = e.lock().unwrap(); - if !s.keep_alive(io) { - s.disconnect(io, DisconnectReason::PingTimeout); - to_kill.push(s.token()); - } + s.disconnect(io, DisconnectReason::ClientQuit); + to_kill.push(s.token()); } for p in to_kill { - trace!(target: "network", "Ping timeout: {}", p); + trace!(target: "network", "Disconnecting on shutdown: {}", p); self.kill_connection(p, io, true); } - io.unregister_handler(); + try!(io.unregister_handler()); Ok(()) } @@ -790,13 +791,6 @@ impl Host where Message: Send + Sync + Clone { } } - -impl Drop for Host where Message: Send + Sync + Clone { - fn drop(&mut self) { - info!("Dropping host"); - } -} - impl IoHandler> for Host where Message: Send + Sync + Clone + 'static { /// Initialize networking fn initialize(&self, io: &IoContext>) { @@ -814,6 +808,9 @@ impl IoHandler> for Host where Messa } fn stream_readable(&self, io: &IoContext>, stream: StreamToken) { + if self.stopping.load(AtomicOrdering::Acquire) { + return; + } match stream { FIRST_SESSION ... LAST_SESSION => self.session_readable(stream, io), DISCOVERY => { @@ -829,6 +826,9 @@ impl IoHandler> for Host where Messa } fn stream_writable(&self, io: &IoContext>, stream: StreamToken) { + if self.stopping.load(AtomicOrdering::Acquire) { + return; + } match stream { FIRST_SESSION ... LAST_SESSION => self.session_writable(stream, io), DISCOVERY => { @@ -840,6 +840,9 @@ impl IoHandler> for Host where Messa } fn timeout(&self, io: &IoContext>, token: TimerToken) { + if self.stopping.load(AtomicOrdering::Acquire) { + return; + } match token { IDLE => self.maintain_network(io), INIT_PUBLIC => self.init_public_interface(io).unwrap_or_else(|e| @@ -870,6 +873,9 @@ impl IoHandler> for Host where Messa } fn message(&self, io: &IoContext>, message: &NetworkIoMessage) { + if self.stopping.load(AtomicOrdering::Acquire) { + return; + } match *message { NetworkIoMessage::AddHandler { ref handler, @@ -1031,6 +1037,6 @@ fn host_client_url() { let mut config = NetworkConfiguration::new(); let key = h256_from_hex("6f7b0d801bc7b5ce7bbd930b84fd0369b3eb25d09be58d64ba811091046f3aa2"); config.use_secret = Some(key); - let host: Host = Host::new(config).unwrap(); + let host: Host = Host::new(config, Arc::new(NetworkStats::new())).unwrap(); assert!(host.local_url().starts_with("enode://101b3ef5a4ea7a1c7928e24c4c75fd053c235d7b80c22ae5c03d145d0ac7396e2a4ffff9adee3133a7b05044a5cee08115fd65145e5165d646bde371010d803c@")); } diff --git a/util/src/network/mod.rs b/util/src/network/mod.rs index 29f3d166c..d074e6631 100644 --- a/util/src/network/mod.rs +++ b/util/src/network/mod.rs @@ -56,8 +56,9 @@ //! } //! //! fn main () { -//! let mut service = NetworkService::::start(NetworkConfiguration::new_local()).expect("Error creating network service"); +//! let mut service = NetworkService::::new(NetworkConfiguration::new_local()).expect("Error creating network service"); //! service.register_protocol(Arc::new(MyHandler), "myproto", &[1u8]); +//! service.start().expect("Error starting service"); //! //! // Wait for quit condition //! // ... diff --git a/util/src/network/service.rs b/util/src/network/service.rs index 406126425..36f05d283 100644 --- a/util/src/network/service.rs +++ b/util/src/network/service.rs @@ -107,7 +107,7 @@ impl NetworkService where Message: Send + Sync + Clone + 'stat if let Some(ref host) = *host { info!("Unregistering handler"); let io = IoContext::new(self.io_service.channel(), 0); //TODO: take token id from host - host.stop(&io); + try!(host.stop(&io)); } *host = None; Ok(()) diff --git a/util/src/network/tests.rs b/util/src/network/tests.rs index b43da9320..861edc144 100644 --- a/util/src/network/tests.rs +++ b/util/src/network/tests.rs @@ -97,7 +97,8 @@ impl NetworkProtocolHandler for TestProtocol { #[test] fn net_service() { - let mut service = NetworkService::::start(NetworkConfiguration::new_local()).expect("Error creating network service"); + let service = NetworkService::::new(NetworkConfiguration::new_local()).expect("Error creating network service"); + service.start().unwrap(); service.register_protocol(Arc::new(TestProtocol::new(false)), "myproto", &[1u8]).unwrap(); } @@ -108,12 +109,14 @@ fn net_connect() { let mut config1 = NetworkConfiguration::new_local(); config1.use_secret = Some(key1.secret().clone()); config1.boot_nodes = vec![ ]; - let mut service1 = NetworkService::::start(config1).unwrap(); + let mut service1 = NetworkService::::new(config1).unwrap(); + service1.start().unwrap(); let handler1 = TestProtocol::register(&mut service1, false); let mut config2 = NetworkConfiguration::new_local(); - info!("net_connect: local URL: {}", service1.local_url()); - config2.boot_nodes = vec![ service1.local_url() ]; - let mut service2 = NetworkService::::start(config2).unwrap(); + info!("net_connect: local URL: {}", service1.local_url().unwrap()); + config2.boot_nodes = vec![ service1.local_url().unwrap() ]; + let mut service2 = NetworkService::::new(config2).unwrap(); + service2.start().unwrap(); let handler2 = TestProtocol::register(&mut service2, false); while !handler1.got_packet() && !handler2.got_packet() && (service1.stats().sessions() == 0 || service2.stats().sessions() == 0) { thread::sleep(Duration::from_millis(50)); @@ -122,17 +125,28 @@ fn net_connect() { assert!(service2.stats().sessions() >= 1); } +#[test] +fn net_start_stop() { + let config = NetworkConfiguration::new_local(); + let service = NetworkService::::new(config).unwrap(); + service.start().unwrap(); + service.stop().unwrap(); + service.start().unwrap(); +} + #[test] fn net_disconnect() { let key1 = KeyPair::create().unwrap(); let mut config1 = NetworkConfiguration::new_local(); config1.use_secret = Some(key1.secret().clone()); config1.boot_nodes = vec![ ]; - let mut service1 = NetworkService::::start(config1).unwrap(); + let mut service1 = NetworkService::::new(config1).unwrap(); + service1.start().unwrap(); let handler1 = TestProtocol::register(&mut service1, false); let mut config2 = NetworkConfiguration::new_local(); - config2.boot_nodes = vec![ service1.local_url() ]; - let mut service2 = NetworkService::::start(config2).unwrap(); + config2.boot_nodes = vec![ service1.local_url().unwrap() ]; + let mut service2 = NetworkService::::new(config2).unwrap(); + service2.start().unwrap(); let handler2 = TestProtocol::register(&mut service2, true); while !(handler1.got_disconnect() && handler2.got_disconnect()) { thread::sleep(Duration::from_millis(50)); @@ -144,7 +158,8 @@ fn net_disconnect() { #[test] fn net_timeout() { let config = NetworkConfiguration::new_local(); - let mut service = NetworkService::::start(config).unwrap(); + let mut service = NetworkService::::new(config).unwrap(); + service.start().unwrap(); let handler = TestProtocol::register(&mut service, false); while !handler.got_timeout() { thread::sleep(Duration::from_millis(50)); From fc60ed92e64aacfb9d56cc99da4706452645f9c6 Mon Sep 17 00:00:00 2001 From: arkpar Date: Fri, 17 Jun 2016 18:28:19 +0200 Subject: [PATCH 04/22] Removed info --- util/src/network/service.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/util/src/network/service.rs b/util/src/network/service.rs index 36f05d283..e8db354d4 100644 --- a/util/src/network/service.rs +++ b/util/src/network/service.rs @@ -105,7 +105,6 @@ impl NetworkService where Message: Send + Sync + Clone + 'stat pub fn stop(&self) -> Result<(), UtilError> { let mut host = self.host.write().unwrap(); if let Some(ref host) = *host { - info!("Unregistering handler"); let io = IoContext::new(self.io_service.channel(), 0); //TODO: take token id from host try!(host.stop(&io)); } From 3f77f7ce1f7a48069b63015259022cb9989d7072 Mon Sep 17 00:00:00 2001 From: arkpar Date: Sat, 18 Jun 2016 09:50:05 +0200 Subject: [PATCH 05/22] Removed RPC methods --- rpc/src/v1/traits/net.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/rpc/src/v1/traits/net.rs b/rpc/src/v1/traits/net.rs index b65f53491..2df8c8f9c 100644 --- a/rpc/src/v1/traits/net.rs +++ b/rpc/src/v1/traits/net.rs @@ -42,8 +42,6 @@ pub trait Net: Sized + Send + Sync + 'static { delegate.add_method("net_version", Net::version); delegate.add_method("net_peerCount", Net::peer_count); delegate.add_method("net_listening", Net::is_listening); - delegate.add_method("net_start", Net::start_network); - delegate.add_method("net_stop", Net::stop_network); delegate } } From 29e286572c772ff9f946b427909e7d99001d98d2 Mon Sep 17 00:00:00 2001 From: Arkadiy Paronyan Date: Wed, 15 Jun 2016 19:01:58 +0200 Subject: [PATCH 06/22] Fixed loosing peers on incoming connections. (#1293) * Deactivate peer if it has no new data * Fixed node table timer registration * Fixed handshake timeout expiration * Extra trace * Fixed session count calculation * Only deactivate incapable peers in ChainHead state * Timer registration is not needed --- sync/src/chain.rs | 8 +++++++- util/src/network/handshake.rs | 5 +++-- util/src/network/host.rs | 7 +++++-- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/sync/src/chain.rs b/sync/src/chain.rs index 90e18d8e8..ae58c4513 100644 --- a/sync/src/chain.rs +++ b/sync/src/chain.rs @@ -405,6 +405,7 @@ impl ChainSync { } if item_count == 0 && (self.state == SyncState::Blocks || self.state == SyncState::NewBlocks) { self.deactivate_peer(io, peer_id); //TODO: is this too harsh? + self.continue_sync(io); return Ok(()); } @@ -452,7 +453,12 @@ impl ChainSync { // Disable the peer for this syncing round if it gives invalid chain if !valid_response { - trace!(target: "sync", "{} Disabled for invalid headers response", peer_id); + trace!(target: "sync", "{} Deactivated for invalid headers response", peer_id); + self.deactivate_peer(io, peer_id); + } + if headers.is_empty() { + // Peer does not have any new subchain heads, deactivate it nd try with another + trace!(target: "sync", "{} Deactivated for no data", peer_id); self.deactivate_peer(io, peer_id); } match self.state { diff --git a/util/src/network/handshake.rs b/util/src/network/handshake.rs index 90e3bc67d..179309a6f 100644 --- a/util/src/network/handshake.rs +++ b/util/src/network/handshake.rs @@ -128,7 +128,6 @@ impl Handshake { /// Readable IO handler. Drives the state change. pub fn readable(&mut self, io: &IoContext, host: &HostInfo) -> Result<(), UtilError> where Message: Send + Clone { if !self.expired() { - io.clear_timer(self.connection.token).ok(); match self.state { HandshakeState::New => {} HandshakeState::ReadingAuth => { @@ -151,7 +150,9 @@ impl Handshake { try!(self.read_ack_eip8(host.secret(), &data)); }; }, - HandshakeState::StartSession => {}, + HandshakeState::StartSession => { + io.clear_timer(self.connection.token).ok(); + }, } } Ok(()) diff --git a/util/src/network/host.rs b/util/src/network/host.rs index b37538c9c..de6908200 100644 --- a/util/src/network/host.rs +++ b/util/src/network/host.rs @@ -523,11 +523,13 @@ impl Host where Message: Send + Sync + Clone { } let nodes = if pin { self.pinned_nodes.clone() } else { self.nodes.read().unwrap().nodes() }; + let mut started: usize = 0; for id in nodes.iter().filter(|ref id| !self.have_session(id) && !self.connecting_to(id)) .take(min(MAX_HANDSHAKES_PER_ROUND, handshake_limit - handshake_count)) { self.connect_peer(&id, io); + started += 1; } - debug!(target: "network", "Connecting peers: {} sessions, {} pending", self.session_count(), self.handshake_count()); + debug!(target: "network", "Connecting peers: {} sessions, {} pending, {} started", self.session_count(), self.handshake_count(), started); } #[cfg_attr(feature="dev", allow(single_match))] @@ -650,6 +652,7 @@ impl Host where Message: Send + Sync + Clone { break; }, Ok(SessionData::Ready) => { + self.num_sessions.fetch_add(1, AtomicOrdering::SeqCst); if !s.info.originated { let session_count = self.session_count(); let ideal_peers = { self.info.read().unwrap().deref().config.ideal_peers }; @@ -667,7 +670,6 @@ impl Host where Message: Send + Sync + Clone { } } } - self.num_sessions.fetch_add(1, AtomicOrdering::SeqCst); for (p, _) in self.handlers.read().unwrap().iter() { if s.have_capability(p) { ready_data.push(p); @@ -828,6 +830,7 @@ impl IoHandler> for Host where Messa io.update_registration(DISCOVERY).expect("Error updating discovery registration"); }, NODE_TABLE => { + trace!(target: "network", "Refreshing node table"); self.nodes.write().unwrap().clear_useless(); }, _ => match self.timers.read().unwrap().get(&token).cloned() { From 55fec2dcc3934273693ac888f3fbb7194c4b200c Mon Sep 17 00:00:00 2001 From: NikVolf Date: Wed, 15 Jun 2016 20:52:00 +0300 Subject: [PATCH 07/22] x64 path --- nsis/installer.nsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nsis/installer.nsi b/nsis/installer.nsi index 2c17a2543..f352e0b87 100644 --- a/nsis/installer.nsi +++ b/nsis/installer.nsi @@ -13,7 +13,7 @@ RequestExecutionLevel admin ;Require admin rights on NT6+ (When UAC is turned on) -InstallDir "$PROGRAMFILES\${COMPANYNAME}\${APPNAME}" +InstallDir "$PROGRAMFILES64\${COMPANYNAME}\${APPNAME}" LicenseData "..\LICENSE" Name "${COMPANYNAME} ${APPNAME}" From 232274546b2886305cc857ada7bd3512f5e5e433 Mon Sep 17 00:00:00 2001 From: NikVolf Date: Wed, 15 Jun 2016 21:40:41 +0300 Subject: [PATCH 08/22] firewall rules --- nsis/installer.nsi | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/nsis/installer.nsi b/nsis/installer.nsi index f352e0b87..10143306a 100644 --- a/nsis/installer.nsi +++ b/nsis/installer.nsi @@ -56,6 +56,18 @@ section "install" createDirectory "$SMPROGRAMS\${COMPANYNAME}" createShortCut "$SMPROGRAMS\${COMPANYNAME}\${APPNAME}.lnk" "$INSTDIR\parity.exe" "ui" "$INSTDIR\logo.ico" + # Firewall remove rules if exists + SimpleFC::AdvRemoveRule "Parity incoming peers (TCP:30303)" + SimpleFC::AdvRemoveRule "Parity outgoing peers (TCP:30303)" + SimpleFC::AdvRemoveRule "Parity web queries (TCP:80)" + SimpleFC::AdvRemoveRule "Parity UDP discovery (UDP:30303)" + + # Firewall exception rules + SimpleFC::AdvAddRule "Parity incoming peers (TCP:30303)" "" 6 1 1 2147483647 1 "$INSTDIR\parity.exe" "" "" "Parity" 30303 "" "" "" + SimpleFC::AdvAddRule "Parity outgoing peers (TCP:30303)" "" 6 2 1 2147483647 1 "$INSTDIR\parity.exe" "" "" "Parity" "" 30303 "" "" + SimpleFC::AdvAddRule "Parity web queries (TCP:80)" "" 6 2 1 2147483647 1 "$INSTDIR\parity.exe" "" "" "Parity" "" 80 "" "" + SimpleFC::AdvAddRule "Parity UDP discovery (UDP:30303)" "" 17 2 1 2147483647 1 "$INSTDIR\parity.exe" "" "" "Parity" "" 30303 "" "" + # Registry information for add/remove programs WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "DisplayName" "${COMPANYNAME} - ${APPNAME} - ${DESCRIPTION}" WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "UninstallString" "$\"$INSTDIR\uninstall.exe$\"" @@ -106,6 +118,12 @@ section "uninstall" # Try to remove the install directory - this will only happen if it is empty rmDir $INSTDIR + # Firewall exception rules + SimpleFC::AdvRemoveRule "Parity incoming peers (TCP:30303)" + SimpleFC::AdvRemoveRule "Parity outgoing peers (TCP:30303)" + SimpleFC::AdvRemoveRule "Parity web queries (TCP:80)" + SimpleFC::AdvRemoveRule "Parity UDP discovery (UDP:30303)" + # Remove uninstaller information from the registry DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" From da058d0e86d47ddabe2f5fcc7dfe44a7c67ad4ed Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 15 Jun 2016 21:31:19 +0200 Subject: [PATCH 09/22] Fix read-ahead bug. Re-ahead 8 bytes rather than 3 to ensure large blocks import fine. --- parity/main.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/parity/main.rs b/parity/main.rs index 1467eaa50..e1890ff0f 100644 --- a/parity/main.rs +++ b/parity/main.rs @@ -398,7 +398,7 @@ fn execute_import(conf: Configuration) { Box::new(::std::io::stdin()) }; - let mut first_bytes: Bytes = vec![0; 3]; + let mut first_bytes: Bytes = vec![0; 8]; let mut first_read = 0; let format = match conf.args.flag_format { @@ -438,13 +438,13 @@ fn execute_import(conf: Configuration) { match format { DataFormat::Binary => { loop { - let mut bytes: Bytes = if first_read > 0 {first_bytes.clone()} else {vec![0; 3]}; + let mut bytes: Bytes = if first_read > 0 {first_bytes.clone()} else {vec![0; 8]}; let n = if first_read > 0 {first_read} else {instream.read(&mut(bytes[..])).unwrap_or_else(|_| die!("Error reading from the file/stream."))}; if n == 0 { break; } first_read = 0; let s = PayloadInfo::from(&(bytes[..])).unwrap_or_else(|e| die!("Invalid RLP in the file/stream: {:?}", e)).total(); bytes.resize(s, 0); - instream.read_exact(&mut(bytes[3..])).unwrap_or_else(|_| die!("Error reading from the file/stream.")); + instream.read_exact(&mut(bytes[8..])).unwrap_or_else(|_| die!("Error reading from the file/stream.")); do_import(bytes); } } From d6701065eaa15b3de2c3cd01ae1e8642ec5281a7 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 15 Jun 2016 23:12:43 +0200 Subject: [PATCH 10/22] Refactor to use a const. --- parity/main.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/parity/main.rs b/parity/main.rs index e1890ff0f..8b99f463b 100644 --- a/parity/main.rs +++ b/parity/main.rs @@ -398,7 +398,9 @@ fn execute_import(conf: Configuration) { Box::new(::std::io::stdin()) }; - let mut first_bytes: Bytes = vec![0; 8]; + const READAHEAD_BYTES: usize = 8; + + let mut first_bytes: Bytes = vec![0; READAHEAD_BYTES]; let mut first_read = 0; let format = match conf.args.flag_format { @@ -438,13 +440,13 @@ fn execute_import(conf: Configuration) { match format { DataFormat::Binary => { loop { - let mut bytes: Bytes = if first_read > 0 {first_bytes.clone()} else {vec![0; 8]}; + let mut bytes: Bytes = if first_read > 0 {first_bytes.clone()} else {vec![0; READAHEAD_BYTES]}; let n = if first_read > 0 {first_read} else {instream.read(&mut(bytes[..])).unwrap_or_else(|_| die!("Error reading from the file/stream."))}; if n == 0 { break; } first_read = 0; let s = PayloadInfo::from(&(bytes[..])).unwrap_or_else(|e| die!("Invalid RLP in the file/stream: {:?}", e)).total(); bytes.resize(s, 0); - instream.read_exact(&mut(bytes[8..])).unwrap_or_else(|_| die!("Error reading from the file/stream.")); + instream.read_exact(&mut(bytes[READAHEAD_BYTES..])).unwrap_or_else(|_| die!("Error reading from the file/stream.")); do_import(bytes); } } From 6026dd3657e72d309a646807f4e4022da29130bc Mon Sep 17 00:00:00 2001 From: Arkadiy Paronyan Date: Thu, 16 Jun 2016 11:21:35 +0200 Subject: [PATCH 11/22] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index acd873b74..e942943ec 100644 --- a/README.md +++ b/README.md @@ -50,7 +50,7 @@ We recommend installing Rust through [rustup](https://www.rustup.rs/). If you do - Windows Make sure you have Visual Studio 2015 with C++ support installed. Next, download and run the rustup installer from - https://static.rust-lang.org/rustup/dist/x86_64-pc-windows-msvc/rustup-init.exe and use the following command to install and set up the msvc toolchain: + https://static.rust-lang.org/rustup/dist/x86_64-pc-windows-msvc/rustup-init.exe, start "VS2015 x64 Native Tools Command Prompt", and use the following command to install and set up the msvc toolchain: ``` $ rustup default stable-x86_64-pc-windows-msvc ``` @@ -73,4 +73,4 @@ To get started, just run $ ./target/release/parity ``` -and parity will begin syncing the Ethereum blockchain. \ No newline at end of file +and parity will begin syncing the Ethereum blockchain. From 335bce85e86b0b966e02b47acf0b204d7437dd5a Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Thu, 16 Jun 2016 12:44:08 +0200 Subject: [PATCH 12/22] Gas price statistics. (#1291) * Gas price statistics. Affects eth_gasPrice. Added ethcore_gasPriceStatistics. Closes #1265 * Fix a bug in eth_gasPrice * Fix tests. * Revert minor alteration. * Tests for gas_price_statistics. - Tests; - Additional infrastructure for generating test blocks with transactions. --- ethcore/res/null.json | 33 +++++++++++ ethcore/src/client/client.rs | 3 +- ethcore/src/client/mod.rs | 28 +++++++++ ethcore/src/spec/spec.rs | 5 ++ ethcore/src/tests/client.rs | 12 ++++ ethcore/src/tests/helpers.rs | 75 +++++++++++++++++++----- ethcore/src/verification/verification.rs | 2 +- parity/rpc_apis.rs | 2 +- rpc/src/v1/impls/eth.rs | 14 ++++- rpc/src/v1/impls/ethcore.rs | 37 +++++++++--- rpc/src/v1/tests/mocked/ethcore.rs | 62 +++++++++++++------- rpc/src/v1/traits/ethcore.rs | 4 ++ 12 files changed, 225 insertions(+), 52 deletions(-) create mode 100644 ethcore/res/null.json diff --git a/ethcore/res/null.json b/ethcore/res/null.json new file mode 100644 index 000000000..b9b2b1260 --- /dev/null +++ b/ethcore/res/null.json @@ -0,0 +1,33 @@ +{ + "name": "Morden", + "engine": { + "Null": null + }, + "params": { + "accountStartNonce": "0x0", + "maximumExtraDataSize": "0x20", + "minGasLimit": "0x1388", + "networkID" : "0x2" + }, + "genesis": { + "seal": { + "ethereum": { + "nonce": "0x00006d6f7264656e", + "mixHash": "0x00000000000000000000000000000000000000647572616c65787365646c6578" + } + }, + "difficulty": "0x20000", + "author": "0x0000000000000000000000000000000000000000", + "timestamp": "0x00", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "extraData": "0x", + "gasLimit": "0x2fefd8" + }, + "accounts": { + "0000000000000000000000000000000000000001": { "balance": "1", "nonce": "0", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } }, + "0000000000000000000000000000000000000002": { "balance": "1", "nonce": "0", "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } }, + "0000000000000000000000000000000000000003": { "balance": "1", "nonce": "0", "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } }, + "0000000000000000000000000000000000000004": { "balance": "1", "nonce": "0", "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } }, + "9cce34f7ab185c7aba1b7c8140d620b4bda941d6": { "balance": "1606938044258990275541962092341162602522202993782792835301376", "nonce": "0" } + } +} diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index 75a60204f..85a3d693d 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -770,8 +770,7 @@ impl MiningBlockChainClient for Client where V: Verifier { author, gas_floor_target, extra_data, - ).expect("OpenBlock::new only fails if parent state root invalid. State root of best block's header is never invalid. \ - Therefore creating an OpenBlock with the best block's header will not fail."); + ).expect("OpenBlock::new only fails if parent state root invalid; state root of best block's header is never invalid; qed"); // Add uncles self.chain diff --git a/ethcore/src/client/mod.rs b/ethcore/src/client/mod.rs index dbd09ea4a..3fec68815 100644 --- a/ethcore/src/client/mod.rs +++ b/ethcore/src/client/mod.rs @@ -34,6 +34,7 @@ pub use env_info::{LastHashes, EnvInfo}; use util::bytes::Bytes; use util::hash::{Address, H256, H2048}; use util::numbers::U256; +use util::Itertools; use blockchain::TreeRoute; use block_queue::BlockQueueInfo; use block::OpenBlock; @@ -41,6 +42,7 @@ use header::{BlockNumber, Header}; use transaction::{LocalizedTransaction, SignedTransaction}; use log_entry::LocalizedLogEntry; use filter::Filter; +use views::BlockView; use error::{ImportResult, ExecutionError}; use receipt::LocalizedReceipt; use trace::LocalizedTrace; @@ -193,6 +195,32 @@ pub trait BlockChainClient : Sync + Send { /// list all transactions fn all_transactions(&self) -> Vec; + + /// Get the gas price distribution. + fn gas_price_statistics(&self, sample_size: usize, distribution_size: usize) -> Result, ()> { + let mut h = self.chain_info().best_block_hash; + let mut corpus = Vec::new(); + for _ in 0..sample_size { + let block_bytes = self.block(BlockID::Hash(h)).expect("h is either the best_block_hash or an ancestor; qed"); + let block = BlockView::new(&block_bytes); + let header = block.header_view(); + if header.number() == 0 { + break; + } + block.transaction_views().iter().foreach(|t| corpus.push(t.gas_price())); + h = header.parent_hash().clone(); + } + corpus.sort(); + let n = corpus.len(); + if n > 0 { + Ok((0..(distribution_size + 1)) + .map(|i| corpus[i * (n - 1) / distribution_size]) + .collect::>() + ) + } else { + Err(()) + } + } } /// Extended client interface used for mining diff --git a/ethcore/src/spec/spec.rs b/ethcore/src/spec/spec.rs index 743f1bd9b..02b311b24 100644 --- a/ethcore/src/spec/spec.rs +++ b/ethcore/src/spec/spec.rs @@ -244,6 +244,11 @@ impl Spec { pub fn new_test() -> Spec { Spec::load(include_bytes!("../../res/null_morden.json")) } + + /// Create a new Spec which is a NullEngine consensus with a premine of address whose secret is sha3(''). + pub fn new_null() -> Spec { + Spec::load(include_bytes!("../../res/null.json")) + } } #[cfg(test)] diff --git a/ethcore/src/tests/client.rs b/ethcore/src/tests/client.rs index d87b41b62..a5459035e 100644 --- a/ethcore/src/tests/client.rs +++ b/ethcore/src/tests/client.rs @@ -112,6 +112,18 @@ fn can_collect_garbage() { assert!(client.blockchain_cache_info().blocks < 100 * 1024); } +#[test] +fn can_generate_gas_price_statistics() { + let client_result = generate_dummy_client_with_data(16, 1, &vec_into![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]); + let client = client_result.reference(); + let s = client.gas_price_statistics(8, 8).unwrap(); + assert_eq!(s, vec_into![8, 8, 9, 10, 11, 12, 13, 14, 15]); + let s = client.gas_price_statistics(16, 8).unwrap(); + assert_eq!(s, vec_into![0, 1, 3, 5, 7, 9, 11, 13, 15]); + let s = client.gas_price_statistics(32, 8).unwrap(); + assert_eq!(s, vec_into![0, 1, 3, 5, 7, 9, 11, 13, 15]); +} + #[test] fn can_handle_long_fork() { let client_result = generate_dummy_client(1200); diff --git a/ethcore/src/tests/helpers.rs b/ethcore/src/tests/helpers.rs index 3b27ef4a4..151d53bba 100644 --- a/ethcore/src/tests/helpers.rs +++ b/ethcore/src/tests/helpers.rs @@ -17,6 +17,7 @@ use client::{BlockChainClient, Client, ClientConfig}; use common::*; use spec::*; +use block::{OpenBlock}; use blockchain::{BlockChain, Config as BlockChainConfig}; use state::*; use evm::Schedule; @@ -85,6 +86,7 @@ impl Engine for TestEngine { } } +// TODO: move everything over to get_null_spec. pub fn get_test_spec() -> Spec { Spec::new_test() } @@ -126,7 +128,7 @@ fn create_unverifiable_block(order: u32, parent_hash: H256) -> Bytes { create_test_block(&create_unverifiable_block_header(order, parent_hash)) } -pub fn create_test_block_with_data(header: &Header, transactions: &[&SignedTransaction], uncles: &[Header]) -> Bytes { +pub fn create_test_block_with_data(header: &Header, transactions: &[SignedTransaction], uncles: &[Header]) -> Bytes { let mut rlp = RlpStream::new_list(3); rlp.append(header); rlp.begin_list(transactions.len()); @@ -138,33 +140,74 @@ pub fn create_test_block_with_data(header: &Header, transactions: &[&SignedTrans } pub fn generate_dummy_client(block_number: u32) -> GuardedTempResult> { + generate_dummy_client_with_spec_and_data(Spec::new_test, block_number, 0, &(vec![])) +} + +pub fn generate_dummy_client_with_data(block_number: u32, txs_per_block: usize, tx_gas_prices: &[U256]) -> GuardedTempResult> { + generate_dummy_client_with_spec_and_data(Spec::new_null, block_number, txs_per_block, tx_gas_prices) +} + +pub fn generate_dummy_client_with_spec_and_data(get_test_spec: F, block_number: u32, txs_per_block: usize, tx_gas_prices: &[U256]) -> GuardedTempResult> where F: Fn()->Spec { let dir = RandomTempPath::new(); - let client = Client::new(ClientConfig::default(), get_test_spec(), dir.as_path(), Arc::new(Miner::default()), IoChannel::disconnected()).unwrap(); let test_spec = get_test_spec(); + let client = Client::new(ClientConfig::default(), get_test_spec(), dir.as_path(), Arc::new(Miner::default()), IoChannel::disconnected()).unwrap(); let test_engine = &test_spec.engine; - let state_root = test_spec.genesis_header().state_root; - let mut rolling_hash = test_spec.genesis_header().hash(); - let mut rolling_block_number = 1; + + let mut db_result = get_temp_journal_db(); + let mut db = db_result.take(); + test_spec.ensure_db_good(db.as_hashdb_mut()); + let vm_factory = Default::default(); + let genesis_header = test_spec.genesis_header(); + let mut rolling_timestamp = 40; + let mut last_hashes = vec![]; + let mut last_header = genesis_header.clone(); + let kp = KeyPair::from_secret("".sha3()).unwrap() ; + let author = kp.address(); + + let mut n = 0; for _ in 0..block_number { - let mut header = Header::new(); + last_hashes.push(last_header.hash()); - header.gas_limit = test_engine.params().min_gas_limit; - header.difficulty = U256::from(0x20000); - header.timestamp = rolling_timestamp; - header.number = rolling_block_number; - header.parent_hash = rolling_hash; - header.state_root = state_root.clone(); + // forge block. + let mut b = OpenBlock::new( + test_engine.deref(), + &vm_factory, + false, + db, + &last_header, + last_hashes.clone(), + author.clone(), + 3141562.into(), + vec![] + ).unwrap(); + b.set_difficulty(U256::from(0x20000)); + rolling_timestamp += 10; + b.set_timestamp(rolling_timestamp); - rolling_hash = header.hash(); - rolling_block_number = rolling_block_number + 1; - rolling_timestamp = rolling_timestamp + 10; + // first block we don't have any balance, so can't send any transactions. + for _ in 0..txs_per_block { + b.push_transaction(Transaction { + nonce: n.into(), + gas_price: tx_gas_prices[n % tx_gas_prices.len()], + gas: 100000.into(), + action: Action::Create, + data: vec![], + value: U256::zero(), + }.sign(kp.secret()), None).unwrap(); + n += 1; + } - if let Err(e) = client.import_block(create_test_block(&header)) { + let b = b.close_and_lock().seal(test_engine.deref(), vec![]).unwrap(); + + if let Err(e) = client.import_block(b.rlp_bytes()) { panic!("error importing block which is valid by definition: {:?}", e); } + + last_header = BlockView::new(&b.rlp_bytes()).header(); + db = b.drain(); } client.flush_queue(); client.import_verified_blocks(&IoChannel::disconnected()); diff --git a/ethcore/src/verification/verification.rs b/ethcore/src/verification/verification.rs index 6511a8ddd..c6b75416b 100644 --- a/ethcore/src/verification/verification.rs +++ b/ethcore/src/verification/verification.rs @@ -361,7 +361,7 @@ mod tests { nonce: U256::from(2) }.sign(&keypair.secret()); - let good_transactions = [ &tr1, &tr2 ]; + let good_transactions = [ tr1.clone(), tr2.clone() ]; let diff_inc = U256::from(0x40); diff --git a/parity/rpc_apis.rs b/parity/rpc_apis.rs index d9085fc31..f4b2d067f 100644 --- a/parity/rpc_apis.rs +++ b/parity/rpc_apis.rs @@ -159,7 +159,7 @@ pub fn setup_rpc(server: T, deps: Arc, apis: ApiSet 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()) + server.add_delegate(EthcoreClient::new(&deps.client, &deps.miner, deps.logger.clone(), deps.settings.clone()).to_delegate()) }, Api::EthcoreSet => { server.add_delegate(EthcoreSetClient::new(&deps.miner).to_delegate()) diff --git a/rpc/src/v1/impls/eth.rs b/rpc/src/v1/impls/eth.rs index 2d337e73c..80245708f 100644 --- a/rpc/src/v1/impls/eth.rs +++ b/rpc/src/v1/impls/eth.rs @@ -153,15 +153,23 @@ impl EthClient where } } + fn default_gas_price(&self) -> Result { + let miner = take_weak!(self.miner); + Ok(take_weak!(self.client) + .gas_price_statistics(100, 8) + .map(|x| x[4]) + .unwrap_or_else(|_| miner.sensible_gas_price()) + ) + } + fn sign_call(&self, request: CallRequest) -> Result { let client = take_weak!(self.client); - let miner = take_weak!(self.miner); let from = request.from.unwrap_or(Address::zero()); Ok(EthTransaction { nonce: request.nonce.unwrap_or_else(|| client.latest_nonce(&from)), action: request.to.map_or(Action::Create, Action::Call), gas: request.gas.unwrap_or(U256::from(50_000_000)), - gas_price: request.gas_price.unwrap_or_else(|| miner.sensible_gas_price()), + gas_price: request.gas_price.unwrap_or_else(|| self.default_gas_price().expect("call only fails if client or miner are unavailable; client and miner are both available to be here; qed")), value: request.value.unwrap_or_else(U256::zero), data: request.data.map_or_else(Vec::new, |d| d.to_vec()) }.fake_sign(from)) @@ -293,7 +301,7 @@ impl Eth for EthClient where fn gas_price(&self, params: Params) -> Result { match params { - Params::None => to_value(&take_weak!(self.miner).sensible_gas_price()), + Params::None => to_value(&try!(self.default_gas_price())), _ => Err(Error::invalid_params()) } } diff --git a/rpc/src/v1/impls/ethcore.rs b/rpc/src/v1/impls/ethcore.rs index b3fe894c6..e77f46f5d 100644 --- a/rpc/src/v1/impls/ethcore.rs +++ b/rpc/src/v1/impls/ethcore.rs @@ -15,30 +15,34 @@ // along with Parity. If not, see . //! Ethcore-specific rpc implementation. -use util::RotatingLogger; +use util::{RotatingLogger}; use util::network_settings::NetworkSettings; use util::misc::version_data; use std::sync::{Arc, Weak}; use std::ops::Deref; -use std::collections::BTreeMap; +use std::collections::{BTreeMap}; +use ethcore::client::{MiningBlockChainClient}; use jsonrpc_core::*; use ethcore::miner::MinerService; use v1::traits::Ethcore; use v1::types::{Bytes}; /// Ethcore implementation. -pub struct EthcoreClient where +pub struct EthcoreClient where + C: MiningBlockChainClient, M: MinerService { + client: Weak, miner: Weak, logger: Arc, settings: Arc, } -impl EthcoreClient where M: MinerService { +impl EthcoreClient where C: MiningBlockChainClient, M: MinerService { /// Creates new `EthcoreClient`. - pub fn new(miner: &Arc, logger: Arc, settings: Arc) -> Self { + pub fn new(client: &Arc, miner: &Arc, logger: Arc, settings: Arc) -> Self { EthcoreClient { + client: Arc::downgrade(client), miner: Arc::downgrade(miner), logger: logger, settings: settings, @@ -46,7 +50,7 @@ impl EthcoreClient where M: MinerService { } } -impl Ethcore for EthcoreClient where M: MinerService + 'static { +impl Ethcore for EthcoreClient where M: MinerService + 'static, C: MiningBlockChainClient + 'static { fn transactions_limit(&self, _: Params) -> Result { to_value(&take_weak!(self.miner).transactions_limit()) @@ -97,8 +101,23 @@ impl Ethcore for EthcoreClient where M: MinerService + 'static { Ok(Value::Object(map)) } - fn default_extra_data(&self, _params: Params) -> Result { - let version = version_data(); - to_value(&Bytes::new(version)) + fn default_extra_data(&self, params: Params) -> Result { + match params { + Params::None => to_value(&Bytes::new(version_data())), + _ => Err(Error::invalid_params()), + } + } + + fn gas_price_statistics(&self, params: Params) -> Result { + match params { + Params::None => match take_weak!(self.client).gas_price_statistics(100, 8) { + Ok(stats) => to_value(&stats + .iter() + .map(|x| to_value(&x).expect("x must be U256; qed")) + .collect::>()), + _ => Err(Error::internal_error()), + }, + _ => Err(Error::invalid_params()), + } } } diff --git a/rpc/src/v1/tests/mocked/ethcore.rs b/rpc/src/v1/tests/mocked/ethcore.rs index 90103adef..68c33ecce 100644 --- a/rpc/src/v1/tests/mocked/ethcore.rs +++ b/rpc/src/v1/tests/mocked/ethcore.rs @@ -20,6 +20,7 @@ use jsonrpc_core::IoHandler; use v1::{Ethcore, EthcoreClient, EthcoreSet, EthcoreSetClient}; use ethcore::miner::MinerService; use v1::tests::helpers::TestMinerService; +use ethcore::client::{TestBlockChainClient}; use util::numbers::*; use rustc_serialize::hex::FromHex; use util::log::RotatingLogger; @@ -29,6 +30,10 @@ fn miner_service() -> Arc { Arc::new(TestMinerService::default()) } +fn client_service() -> Arc { + Arc::new(TestBlockChainClient::default()) +} + fn logger() -> Arc { Arc::new(RotatingLogger::new("rpc=trace".to_owned())) } @@ -45,19 +50,20 @@ fn settings() -> Arc { }) } -fn ethcore_client(miner: &Arc) -> EthcoreClient { - EthcoreClient::new(&miner, logger(), settings()) +fn ethcore_client(client: &Arc, miner: &Arc) -> EthcoreClient { + EthcoreClient::new(client, miner, logger(), settings()) } fn ethcore_set_client(miner: &Arc) -> EthcoreSetClient { - EthcoreSetClient::new(&miner) + EthcoreSetClient::new(miner) } #[test] fn rpc_ethcore_extra_data() { let miner = miner_service(); + let client = client_service(); let io = IoHandler::new(); - io.add_delegate(ethcore_client(&miner).to_delegate()); + io.add_delegate(ethcore_client(&client, &miner).to_delegate()); io.add_delegate(ethcore_set_client(&miner).to_delegate()); let request = r#"{"jsonrpc": "2.0", "method": "ethcore_extraData", "params": [], "id": 1}"#; @@ -72,8 +78,9 @@ fn rpc_ethcore_default_extra_data() { use util::ToPretty; let miner = miner_service(); + let client = client_service(); let io = IoHandler::new(); - io.add_delegate(ethcore_client(&miner).to_delegate()); + io.add_delegate(ethcore_client(&client, &miner).to_delegate()); io.add_delegate(ethcore_set_client(&miner).to_delegate()); let request = r#"{"jsonrpc": "2.0", "method": "ethcore_defaultExtraData", "params": [], "id": 1}"#; @@ -85,8 +92,9 @@ fn rpc_ethcore_default_extra_data() { #[test] fn rpc_ethcore_gas_floor_target() { let miner = miner_service(); + let client = client_service(); let io = IoHandler::new(); - io.add_delegate(ethcore_client(&miner).to_delegate()); + io.add_delegate(ethcore_client(&client, &miner).to_delegate()); io.add_delegate(ethcore_set_client(&miner).to_delegate()); let request = r#"{"jsonrpc": "2.0", "method": "ethcore_gasFloorTarget", "params": [], "id": 1}"#; @@ -98,8 +106,9 @@ fn rpc_ethcore_gas_floor_target() { #[test] fn rpc_ethcore_min_gas_price() { let miner = miner_service(); + let client = client_service(); let io = IoHandler::new(); - io.add_delegate(ethcore_client(&miner).to_delegate()); + io.add_delegate(ethcore_client(&client, &miner).to_delegate()); io.add_delegate(ethcore_set_client(&miner).to_delegate()); let request = r#"{"jsonrpc": "2.0", "method": "ethcore_minGasPrice", "params": [], "id": 1}"#; @@ -111,8 +120,9 @@ fn rpc_ethcore_min_gas_price() { #[test] fn rpc_ethcore_set_min_gas_price() { let miner = miner_service(); + let client = client_service(); let io = IoHandler::new(); - io.add_delegate(ethcore_client(&miner).to_delegate()); + io.add_delegate(ethcore_client(&client, &miner).to_delegate()); io.add_delegate(ethcore_set_client(&miner).to_delegate()); let request = r#"{"jsonrpc": "2.0", "method": "ethcore_setMinGasPrice", "params":["0xcd1722f3947def4cf144679da39c4c32bdc35681"], "id": 1}"#; @@ -125,8 +135,9 @@ fn rpc_ethcore_set_min_gas_price() { #[test] fn rpc_ethcore_set_gas_floor_target() { let miner = miner_service(); + let client = client_service(); let io = IoHandler::new(); - io.add_delegate(ethcore_client(&miner).to_delegate()); + io.add_delegate(ethcore_client(&client, &miner).to_delegate()); io.add_delegate(ethcore_set_client(&miner).to_delegate()); let request = r#"{"jsonrpc": "2.0", "method": "ethcore_setGasFloorTarget", "params":["0xcd1722f3947def4cf144679da39c4c32bdc35681"], "id": 1}"#; @@ -139,8 +150,9 @@ fn rpc_ethcore_set_gas_floor_target() { #[test] fn rpc_ethcore_set_extra_data() { let miner = miner_service(); + let client = client_service(); let io = IoHandler::new(); - io.add_delegate(ethcore_client(&miner).to_delegate()); + io.add_delegate(ethcore_client(&client, &miner).to_delegate()); io.add_delegate(ethcore_set_client(&miner).to_delegate()); let request = r#"{"jsonrpc": "2.0", "method": "ethcore_setExtraData", "params":["0xcd1722f3947def4cf144679da39c4c32bdc35681"], "id": 1}"#; @@ -153,8 +165,9 @@ fn rpc_ethcore_set_extra_data() { #[test] fn rpc_ethcore_set_author() { let miner = miner_service(); + let client = client_service(); let io = IoHandler::new(); - io.add_delegate(ethcore_client(&miner).to_delegate()); + io.add_delegate(ethcore_client(&client, &miner).to_delegate()); io.add_delegate(ethcore_set_client(&miner).to_delegate()); let request = r#"{"jsonrpc": "2.0", "method": "ethcore_setAuthor", "params":["0xcd1722f3947def4cf144679da39c4c32bdc35681"], "id": 1}"#; @@ -167,10 +180,11 @@ fn rpc_ethcore_set_author() { #[test] fn rpc_ethcore_dev_logs() { let miner = miner_service(); + let client = client_service(); let logger = logger(); logger.append("a".to_owned()); logger.append("b".to_owned()); - let ethcore = EthcoreClient::new(&miner, logger.clone(), settings()).to_delegate(); + let ethcore = EthcoreClient::new(&client, &miner, logger.clone(), settings()).to_delegate(); let io = IoHandler::new(); io.add_delegate(ethcore); io.add_delegate(ethcore_set_client(&miner).to_delegate()); @@ -184,8 +198,9 @@ fn rpc_ethcore_dev_logs() { #[test] fn rpc_ethcore_dev_logs_levels() { let miner = miner_service(); + let client = client_service(); let io = IoHandler::new(); - io.add_delegate(ethcore_client(&miner).to_delegate()); + io.add_delegate(ethcore_client(&client, &miner).to_delegate()); io.add_delegate(ethcore_set_client(&miner).to_delegate()); let request = r#"{"jsonrpc": "2.0", "method": "ethcore_devLogsLevels", "params":[], "id": 1}"#; @@ -196,8 +211,9 @@ fn rpc_ethcore_dev_logs_levels() { #[test] fn rpc_ethcore_set_transactions_limit() { let miner = miner_service(); + let client = client_service(); let io = IoHandler::new(); - io.add_delegate(ethcore_client(&miner).to_delegate()); + io.add_delegate(ethcore_client(&client, &miner).to_delegate()); io.add_delegate(ethcore_set_client(&miner).to_delegate()); let request = r#"{"jsonrpc": "2.0", "method": "ethcore_setTransactionsLimit", "params":[10240240], "id": 1}"#; @@ -210,8 +226,9 @@ fn rpc_ethcore_set_transactions_limit() { #[test] fn rpc_ethcore_transactions_limit() { let miner = miner_service(); + let client = client_service(); let io = IoHandler::new(); - io.add_delegate(ethcore_client(&miner).to_delegate()); + io.add_delegate(ethcore_client(&client, &miner).to_delegate()); io.add_delegate(ethcore_set_client(&miner).to_delegate()); let request = r#"{"jsonrpc": "2.0", "method": "ethcore_transactionsLimit", "params":[], "id": 1}"#; @@ -223,8 +240,9 @@ fn rpc_ethcore_transactions_limit() { #[test] fn rpc_ethcore_net_chain() { let miner = miner_service(); + let client = client_service(); let io = IoHandler::new(); - io.add_delegate(ethcore_client(&miner).to_delegate()); + io.add_delegate(ethcore_client(&client, &miner).to_delegate()); io.add_delegate(ethcore_set_client(&miner).to_delegate()); let request = r#"{"jsonrpc": "2.0", "method": "ethcore_netChain", "params":[], "id": 1}"#; @@ -236,8 +254,9 @@ fn rpc_ethcore_net_chain() { #[test] fn rpc_ethcore_net_max_peers() { let miner = miner_service(); + let client = client_service(); let io = IoHandler::new(); - io.add_delegate(ethcore_client(&miner).to_delegate()); + io.add_delegate(ethcore_client(&client, &miner).to_delegate()); io.add_delegate(ethcore_set_client(&miner).to_delegate()); let request = r#"{"jsonrpc": "2.0", "method": "ethcore_netMaxPeers", "params":[], "id": 1}"#; @@ -249,8 +268,9 @@ fn rpc_ethcore_net_max_peers() { #[test] fn rpc_ethcore_net_port() { let miner = miner_service(); + let client = client_service(); let io = IoHandler::new(); - io.add_delegate(ethcore_client(&miner).to_delegate()); + io.add_delegate(ethcore_client(&client, &miner).to_delegate()); io.add_delegate(ethcore_set_client(&miner).to_delegate()); let request = r#"{"jsonrpc": "2.0", "method": "ethcore_netPort", "params":[], "id": 1}"#; @@ -262,8 +282,9 @@ fn rpc_ethcore_net_port() { #[test] fn rpc_ethcore_rpc_settings() { let miner = miner_service(); + let client = client_service(); let io = IoHandler::new(); - io.add_delegate(ethcore_client(&miner).to_delegate()); + io.add_delegate(ethcore_client(&client, &miner).to_delegate()); io.add_delegate(ethcore_set_client(&miner).to_delegate()); let request = r#"{"jsonrpc": "2.0", "method": "ethcore_rpcSettings", "params":[], "id": 1}"#; @@ -275,8 +296,9 @@ fn rpc_ethcore_rpc_settings() { #[test] fn rpc_ethcore_node_name() { let miner = miner_service(); + let client = client_service(); let io = IoHandler::new(); - io.add_delegate(ethcore_client(&miner).to_delegate()); + io.add_delegate(ethcore_client(&client, &miner).to_delegate()); io.add_delegate(ethcore_set_client(&miner).to_delegate()); let request = r#"{"jsonrpc": "2.0", "method": "ethcore_nodeName", "params":[], "id": 1}"#; diff --git a/rpc/src/v1/traits/ethcore.rs b/rpc/src/v1/traits/ethcore.rs index 4ce3eca59..1dd00ff73 100644 --- a/rpc/src/v1/traits/ethcore.rs +++ b/rpc/src/v1/traits/ethcore.rs @@ -57,6 +57,9 @@ pub trait Ethcore: Sized + Send + Sync + 'static { /// Returns default extra data fn default_extra_data(&self, _: Params) -> Result; + /// Returns distribution of gas price in latest blocks. + fn gas_price_statistics(&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)); @@ -73,6 +76,7 @@ pub trait Ethcore: Sized + Send + Sync + 'static { delegate.add_method("ethcore_rpcSettings", Ethcore::rpc_settings); delegate.add_method("ethcore_nodeName", Ethcore::node_name); delegate.add_method("ethcore_defaultExtraData", Ethcore::default_extra_data); + delegate.add_method("ethcore_gasPriceStatistics", Ethcore::gas_price_statistics); delegate } From 2c9a5fc0a99aa2795f0c6f78c02aaa88db04ebd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Wed, 15 Jun 2016 00:17:23 +0200 Subject: [PATCH 13/22] More meaningful errors when sending transaction --- Cargo.lock | 42 +++++++++++----------- rpc/src/v1/helpers/signing_queue.rs | 30 +++++++++------- rpc/src/v1/impls/eth_signing.rs | 22 ++++++++---- rpc/src/v1/impls/mod.rs | 55 +++++++++++++++++++++++++++-- rpc/src/v1/impls/personal_signer.rs | 6 ++-- 5 files changed, 109 insertions(+), 46 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b9749217a..b95500540 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -72,7 +72,7 @@ dependencies = [ "rand 0.3.14 (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.7 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -234,7 +234,7 @@ dependencies = [ "libc 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 0.7.7 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -280,7 +280,7 @@ dependencies = [ "ethcore-rpc 1.2.0", "ethcore-util 1.2.0", "hyper 0.9.3 (git+https://github.com/ethcore/hyper)", - "jsonrpc-core 2.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpc-core 2.0.7 (registry+https://github.com/rust-lang/crates.io-index)", "jsonrpc-http-server 5.1.0 (git+https://github.com/ethcore/jsonrpc-http-server.git)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "mime_guess 1.6.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -291,8 +291,8 @@ dependencies = [ "parity-dapps-status 0.5.0 (git+https://github.com/ethcore/parity-dapps-status-rs.git)", "parity-dapps-wallet 0.6.1 (git+https://github.com/ethcore/parity-dapps-wallet-rs.git)", "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 0.7.7 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_codegen 0.7.7 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_codegen 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "syntex 0.33.0 (registry+https://github.com/rust-lang/crates.io-index)", "url 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -331,7 +331,7 @@ name = "ethcore-ipc-nano" version = "1.2.0" dependencies = [ "ethcore-ipc 1.2.0", - "jsonrpc-core 2.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpc-core 2.0.7 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "nanomsg 0.5.1 (git+https://github.com/ethcore/nanomsg.rs.git)", ] @@ -348,12 +348,12 @@ dependencies = [ "ethjson 0.1.0", "ethsync 1.2.0", "json-ipc-server 0.2.3 (git+https://github.com/ethcore/json-ipc-server.git)", - "jsonrpc-core 2.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpc-core 2.0.7 (registry+https://github.com/rust-lang/crates.io-index)", "jsonrpc-http-server 5.1.0 (git+https://github.com/ethcore/jsonrpc-http-server.git)", "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)", - "serde 0.7.7 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_codegen 0.7.7 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_codegen 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "syntex 0.33.0 (registry+https://github.com/rust-lang/crates.io-index)", "transient-hashmap 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -367,7 +367,7 @@ dependencies = [ "env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "ethcore-rpc 1.2.0", "ethcore-util 1.2.0", - "jsonrpc-core 2.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpc-core 2.0.7 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "parity-minimal-sysui 0.1.0 (git+https://github.com/ethcore/parity-dapps-minimal-sysui-rs.git)", "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", @@ -402,7 +402,7 @@ dependencies = [ "rust-crypto 0.2.36 (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.7 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)", "sha3 0.1.0", "slab 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "target_info 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -417,8 +417,8 @@ version = "0.1.0" dependencies = [ "ethcore-util 1.2.0", "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 0.7.7 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_codegen 0.7.7 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_codegen 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "syntex 0.33.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -576,7 +576,7 @@ source = "git+https://github.com/ethcore/json-ipc-server.git#b224bdbcb53cab349c2 dependencies = [ "bytes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-core 2.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpc-core 2.0.7 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -594,11 +594,11 @@ dependencies = [ [[package]] name = "jsonrpc-core" -version = "2.0.5" +version = "2.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde 0.7.7 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_codegen 0.7.7 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_codegen 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "syntex 0.33.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -609,7 +609,7 @@ version = "5.1.0" source = "git+https://github.com/ethcore/jsonrpc-http-server.git#6117b1d77b5a60d6fa2dc884f12aa7f5fd4585ca" dependencies = [ "hyper 0.9.3 (git+https://github.com/ethcore/hyper)", - "jsonrpc-core 2.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpc-core 2.0.7 (registry+https://github.com/rust-lang/crates.io-index)", "unicase 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1181,12 +1181,12 @@ dependencies = [ [[package]] name = "serde" -version = "0.7.7" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "serde_codegen" -version = "0.7.7" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "aster 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1202,7 +1202,7 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "num-traits 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 0.7.7 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] diff --git a/rpc/src/v1/helpers/signing_queue.rs b/rpc/src/v1/helpers/signing_queue.rs index 0ded8998c..dd59ce967 100644 --- a/rpc/src/v1/helpers/signing_queue.rs +++ b/rpc/src/v1/helpers/signing_queue.rs @@ -19,8 +19,11 @@ use std::time::{Instant, Duration}; use std::sync::{mpsc, Mutex, RwLock, Arc}; use std::collections::HashMap; use v1::types::{TransactionRequest, TransactionConfirmation}; -use util::{U256, H256}; +use util::U256; +use jsonrpc_core; +/// Result that can be returned from JSON RPC. +pub type RpcResult = Result; /// Possible events happening in the queue that can be listened to. #[derive(Debug, PartialEq)] @@ -59,7 +62,7 @@ pub trait SigningQueue: Send + Sync { /// Removes a request from the queue. /// Notifies possible token holders that transaction was confirmed and given hash was assigned. - fn request_confirmed(&self, id: U256, hash: H256) -> Option; + fn request_confirmed(&self, id: U256, result: RpcResult) -> Option; /// Returns a request if it is contained in the queue. fn peek(&self, id: &U256) -> Option; @@ -75,7 +78,7 @@ enum ConfirmationResult { /// The transaction has been rejected. Rejected, /// The transaction has been confirmed. - Confirmed(H256), + Confirmed(RpcResult), } /// Time you need to confirm the transaction in UI. @@ -100,7 +103,7 @@ pub struct ConfirmationPromise { impl ConfirmationToken { /// Submit solution to all listeners - fn resolve(&self, result: Option) { + fn resolve(&self, result: Option) { let mut res = self.result.lock().unwrap(); *res = result.map_or(ConfirmationResult::Rejected, |h| ConfirmationResult::Confirmed(h)); // Notify listener @@ -119,8 +122,8 @@ impl ConfirmationPromise { /// Blocks current thread and awaits for /// resolution of the transaction (rejected / confirmed) /// Returns `None` if transaction was rejected or timeout reached. - /// Returns `Some(hash)` if transaction was confirmed. - pub fn wait_with_timeout(&self) -> Option { + /// Returns `Some(result)` if transaction was confirmed. + pub fn wait_with_timeout(&self) -> Option { let timeout = Duration::from_secs(QUEUE_TIMEOUT_DURATION_SEC); let deadline = Instant::now() + timeout; @@ -137,7 +140,7 @@ impl ConfirmationPromise { // Check the result match *res { ConfirmationResult::Rejected => return None, - ConfirmationResult::Confirmed(h) => return Some(h), + ConfirmationResult::Confirmed(ref h) => return Some(h.clone()), ConfirmationResult::Waiting => continue, } } @@ -204,12 +207,12 @@ impl ConfirmationsQueue { /// Removes transaction from this queue and notifies `ConfirmationPromise` holders about the result. /// Notifies also a receiver about that event. - fn remove(&self, id: U256, result: Option) -> Option { + fn remove(&self, id: U256, result: Option) -> Option { let token = self.queue.write().unwrap().remove(&id); if let Some(token) = token { // notify receiver about the event - self.notify(result.map_or_else( + self.notify(result.clone().map_or_else( || QueueEvent::RequestRejected(id), |_| QueueEvent::RequestConfirmed(id) )); @@ -265,9 +268,9 @@ impl SigningQueue for ConfirmationsQueue { self.remove(id, None) } - fn request_confirmed(&self, id: U256, hash: H256) -> Option { + fn request_confirmed(&self, id: U256, result: RpcResult) -> Option { debug!(target: "own_tx", "Signer: Transaction confirmed ({:?}).", id); - self.remove(id, Some(hash)) + self.remove(id, Some(result)) } fn requests(&self) -> Vec { @@ -286,6 +289,7 @@ mod test { use util::numbers::{U256, H256}; use v1::types::TransactionRequest; use super::*; + use jsonrpc_core::to_value; fn request() -> TransactionRequest { TransactionRequest { @@ -317,10 +321,10 @@ mod test { // Just wait for the other thread to start thread::sleep(Duration::from_millis(100)); } - queue.request_confirmed(id, H256::from(1)); + queue.request_confirmed(id, to_value(&H256::from(1))); // then - assert_eq!(handle.join().expect("Thread should finish nicely"), H256::from(1)); + assert_eq!(handle.join().expect("Thread should finish nicely"), to_value(&H256::from(1))); } #[test] diff --git a/rpc/src/v1/impls/eth_signing.rs b/rpc/src/v1/impls/eth_signing.rs index f8c3c343d..591eae059 100644 --- a/rpc/src/v1/impls/eth_signing.rs +++ b/rpc/src/v1/impls/eth_signing.rs @@ -25,7 +25,7 @@ use util::keys::store::AccountProvider; use v1::helpers::{SigningQueue, ConfirmationsQueue}; use v1::traits::EthSigning; use v1::types::TransactionRequest; -use v1::impls::sign_and_dispatch; +use v1::impls::{sign_and_dispatch, error_codes}; /// Implementation of functions that require signing when no trusted signer is used. @@ -45,6 +45,7 @@ impl EthSigningQueueClient { impl EthSigning for EthSigningQueueClient { fn sign(&self, _params: Params) -> Result { + warn!("Invoking eth_sign is not yet supported with signer enabled."); // TODO [ToDr] Implement sign when rest of the signing queue is ready. rpc_unimplemented!() } @@ -55,7 +56,7 @@ impl EthSigning for EthSigningQueueClient { let queue = take_weak!(self.queue); let id = queue.add_request(request); let result = id.wait_with_timeout(); - to_value(&result.unwrap_or_else(H256::new)) + result.unwrap_or_else(|| to_value(&H256::new())) }) } } @@ -93,7 +94,9 @@ impl EthSigning for EthSigningUnsafeClient 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())) + take_weak!(self.accounts).sign(&addr, &msg) + .map(|v| to_value(&v)) + .unwrap_or_else(|e| Err(account_locked(format!("Error: {:?}", e)))) }) } @@ -102,10 +105,17 @@ impl EthSigning for EthSigningUnsafeClient where .and_then(|(request, )| { let accounts = take_weak!(self.accounts); match accounts.account_secret(&request.from) { - Ok(secret) => to_value(&sign_and_dispatch(&*take_weak!(self.client), &*take_weak!(self.miner), request, secret)), - Err(_) => to_value(&H256::zero()) + Ok(secret) => sign_and_dispatch(&*take_weak!(self.client), &*take_weak!(self.miner), request, secret), + Err(e) => Err(account_locked(format!("Error: {:?}", e))), } }) } - +} + +fn account_locked(data: String) -> Error { + Error { + code: ErrorCode::ServerError(error_codes::ACCOUNT_LOCKED), + message: "Your account is locked. Unlock the account via CLI, personal_unlockAccount or use Trusted Signer.".into(), + data: Some(Value::String(data)), + } } diff --git a/rpc/src/v1/impls/mod.rs b/rpc/src/v1/impls/mod.rs index 7fdf57249..03f4b34ed 100644 --- a/rpc/src/v1/impls/mod.rs +++ b/rpc/src/v1/impls/mod.rs @@ -54,14 +54,16 @@ pub use self::traces::TracesClient; pub use self::rpc::RpcClient; use v1::types::TransactionRequest; +use ethcore::error::Error as EthcoreError; use ethcore::miner::{AccountDetails, MinerService}; use ethcore::client::MiningBlockChainClient; use ethcore::transaction::{Action, SignedTransaction, Transaction}; use util::numbers::*; use util::rlp::encode; use util::bytes::ToPretty; +use jsonrpc_core::{Error, ErrorCode, Value, to_value}; -fn dispatch_transaction(client: &C, miner: &M, signed_transaction: SignedTransaction) -> H256 +fn dispatch_transaction(client: &C, miner: &M, signed_transaction: SignedTransaction) -> Result where C: MiningBlockChainClient, M: MinerService { let hash = signed_transaction.hash(); @@ -72,10 +74,12 @@ fn dispatch_transaction(client: &C, miner: &M, signed_transaction: SignedT } }); - import.map(|_| hash).unwrap_or(H256::zero()) + import + .map_err(transaction_error) + .and_then(|_| to_value(&hash)) } -fn sign_and_dispatch(client: &C, miner: &M, request: TransactionRequest, secret: H256) -> H256 +fn sign_and_dispatch(client: &C, miner: &M, request: TransactionRequest, secret: H256) -> Result where C: MiningBlockChainClient, M: MinerService { let signed_transaction = { @@ -97,3 +101,48 @@ fn sign_and_dispatch(client: &C, miner: &M, request: TransactionRequest, s trace!(target: "miner", "send_transaction: dispatching tx: {}", encode(&signed_transaction).to_vec().pretty()); dispatch_transaction(&*client, &*miner, signed_transaction) } + +mod error_codes { + // NOTE [ToDr] Codes from -32000 to -32099 + pub const UNKNOWN_ERROR: i64 = -32002; + pub const TRANSACTION_ERROR: i64 = -32010; + pub const ACCOUNT_LOCKED: i64 = -32020; +} + +fn transaction_error(error: EthcoreError) -> Error { + use ethcore::error::TransactionError::*; + + if let EthcoreError::Transaction(e) = error { + let msg = match e { + AlreadyImported => "Transaction with the same hash was already imported.".into(), + Old => "Transaction nonce is too low. Try incrementing the nonce.".into(), + TooCheapToReplace => { + "Transaction fee is too low. There is another transaction with same nonce in the queue. Try increasing the fee or incrementing the nonce.".into() + }, + LimitReached => { + "There is too many transactions in the queue. Your transaction was dropped due to limit. Try increasing the fee.".into() + }, + InsufficientGasPrice { minimal, got } => { + format!("Transaction fee is to low. It does not satisfy your node's minimal fee (minimal: {}, got: {}). Try increasing the fee.", minimal, got) + }, + InsufficientBalance { balance, cost } => { + format!("Insufficient funds. Account you try to send transaction from does not have enough funds. Required {} and got: {}.", cost, balance) + }, + GasLimitExceeded { limit, got } => { + format!("Transaction cost exceeds current gas limit. Limit: {}, got: {}. Try decreasing supplied gas.", limit, got) + }, + InvalidGasLimit(_) => "Supplied gas is beyond limit.".into(), + }; + Error { + code: ErrorCode::ServerError(error_codes::TRANSACTION_ERROR), + message: msg, + data: None, + } + } else { + Error { + code: ErrorCode::ServerError(error_codes::UNKNOWN_ERROR), + message: "Unknown error when sending transaction.".into(), + data: Some(Value::String(format!("{:?}", error))), + } + } +} diff --git a/rpc/src/v1/impls/personal_signer.rs b/rpc/src/v1/impls/personal_signer.rs index 88bf9a5d1..0c1fcb7f1 100644 --- a/rpc/src/v1/impls/personal_signer.rs +++ b/rpc/src/v1/impls/personal_signer.rs @@ -73,9 +73,9 @@ impl PersonalSigner for SignerClient { - let hash = sign_and_dispatch(&*client, &*miner, request, secret); - queue.request_confirmed(id, hash); - Some(to_value(&hash)) + let res = sign_and_dispatch(&*client, &*miner, request, secret); + queue.request_confirmed(id, res.clone()); + Some(res) }, Err(_) => None } From defe688797c31866f885f490d3908c899b78fbf9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Wed, 15 Jun 2016 09:01:57 +0200 Subject: [PATCH 14/22] Fixing returned value --- rpc/src/v1/impls/eth.rs | 2 +- rpc/src/v1/impls/personal.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/rpc/src/v1/impls/eth.rs b/rpc/src/v1/impls/eth.rs index 80245708f..fb512d91a 100644 --- a/rpc/src/v1/impls/eth.rs +++ b/rpc/src/v1/impls/eth.rs @@ -516,7 +516,7 @@ impl Eth for EthClient where .and_then(|(raw_transaction, )| { let raw_transaction = raw_transaction.to_vec(); match UntrustedRlp::new(&raw_transaction).as_val() { - Ok(signed_transaction) => to_value(&dispatch_transaction(&*take_weak!(self.client), &*take_weak!(self.miner), signed_transaction)), + Ok(signed_transaction) => dispatch_transaction(&*take_weak!(self.client), &*take_weak!(self.miner), signed_transaction), Err(_) => to_value(&H256::zero()), } }) diff --git a/rpc/src/v1/impls/personal.rs b/rpc/src/v1/impls/personal.rs index c62c71453..4f9e307cb 100644 --- a/rpc/src/v1/impls/personal.rs +++ b/rpc/src/v1/impls/personal.rs @@ -92,7 +92,7 @@ impl Personal for PersonalClient .and_then(|(request, password)| { let accounts = take_weak!(self.accounts); match accounts.locked_account_secret(&request.from, &password) { - Ok(secret) => to_value(&sign_and_dispatch(&*take_weak!(self.client), &*take_weak!(self.miner), request, secret)), + Ok(secret) => sign_and_dispatch(&*take_weak!(self.client), &*take_weak!(self.miner), request, secret), Err(_) => to_value(&H256::zero()), } }) From 1baa15008674c29776e7a8067fd87e0fe6a4b97c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Wed, 15 Jun 2016 18:32:21 +0200 Subject: [PATCH 15/22] Consolidating all RPC error codes --- rpc/src/v1/impls/eth.rs | 10 +++------- rpc/src/v1/impls/mod.rs | 16 +++++++++------- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/rpc/src/v1/impls/eth.rs b/rpc/src/v1/impls/eth.rs index fb512d91a..dc5d8c081 100644 --- a/rpc/src/v1/impls/eth.rs +++ b/rpc/src/v1/impls/eth.rs @@ -37,7 +37,7 @@ use ethcore::filter::Filter as EthcoreFilter; use self::ethash::SeedHashCompute; 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 v1::impls::{dispatch_transaction, error_codes}; use serde; /// Eth rpc implementation. @@ -218,13 +218,9 @@ fn from_params_default_third(params: Params) -> Result<(F1, F2, BlockNum } } -// must be in range [-32099, -32000] -const UNSUPPORTED_REQUEST_CODE: i64 = -32000; -const NO_WORK_CODE: i64 = -32001; - fn make_unsupported_err() -> Error { Error { - code: ErrorCode::ServerError(UNSUPPORTED_REQUEST_CODE), + code: ErrorCode::ServerError(error_codes::UNSUPPORTED_REQUEST_CODE), message: "Unsupported request.".into(), data: None } @@ -232,7 +228,7 @@ fn make_unsupported_err() -> Error { fn no_work_err() -> Error { Error { - code: ErrorCode::ServerError(NO_WORK_CODE), + code: ErrorCode::ServerError(error_codes::NO_WORK_CODE), message: "Still syncing.".into(), data: None } diff --git a/rpc/src/v1/impls/mod.rs b/rpc/src/v1/impls/mod.rs index 03f4b34ed..8ea02b8b3 100644 --- a/rpc/src/v1/impls/mod.rs +++ b/rpc/src/v1/impls/mod.rs @@ -63,6 +63,15 @@ use util::rlp::encode; use util::bytes::ToPretty; use jsonrpc_core::{Error, ErrorCode, Value, to_value}; +mod error_codes { + // NOTE [ToDr] Codes from [-32099, -32000] + pub const UNSUPPORTED_REQUEST_CODE: i64 = -32000; + pub const NO_WORK_CODE: i64 = -32001; + pub const UNKNOWN_ERROR: i64 = -32002; + pub const TRANSACTION_ERROR: i64 = -32010; + pub const ACCOUNT_LOCKED: i64 = -32020; +} + fn dispatch_transaction(client: &C, miner: &M, signed_transaction: SignedTransaction) -> Result where C: MiningBlockChainClient, M: MinerService { let hash = signed_transaction.hash(); @@ -102,13 +111,6 @@ fn sign_and_dispatch(client: &C, miner: &M, request: TransactionRequest, s dispatch_transaction(&*client, &*miner, signed_transaction) } -mod error_codes { - // NOTE [ToDr] Codes from -32000 to -32099 - pub const UNKNOWN_ERROR: i64 = -32002; - pub const TRANSACTION_ERROR: i64 = -32010; - pub const ACCOUNT_LOCKED: i64 = -32020; -} - fn transaction_error(error: EthcoreError) -> Error { use ethcore::error::TransactionError::*; From b4aae6bd9c7b4c88de12afabdabb3e38b71af392 Mon Sep 17 00:00:00 2001 From: Nikolay Volf Date: Thu, 16 Jun 2016 16:14:22 +0400 Subject: [PATCH 16/22] Key load avoid warning (#1303) * avoid warning with key * fix intendations * more intendation fix * ok() instead of expect() --- util/src/keys/directory.rs | 11 +++++++---- util/src/keys/store.rs | 12 ++++++++---- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/util/src/keys/directory.rs b/util/src/keys/directory.rs index 32ac14b55..385a400c7 100644 --- a/util/src/keys/directory.rs +++ b/util/src/keys/directory.rs @@ -428,9 +428,9 @@ impl KeyFileContent { let crypto = match as_object.get("crypto") { None => { return Err(KeyFileParseError::NoCryptoSection); } Some(crypto_json) => match KeyFileCrypto::from_json(crypto_json) { - Ok(crypto) => crypto, - Err(crypto_error) => { return Err(KeyFileParseError::Crypto(crypto_error)); } - } + Ok(crypto) => crypto, + Err(crypto_error) => { return Err(KeyFileParseError::Crypto(crypto_error)); } + } }; Ok(KeyFileContent { @@ -627,7 +627,10 @@ impl KeyDirectory { } } - + /// Checks if key exists + pub fn exists(&self, id: &Uuid) -> bool { + KeyDirectory::load_key(&self.key_path(id)).is_ok() + } } diff --git a/util/src/keys/store.rs b/util/src/keys/store.rs index f6f41745f..b8a1de272 100644 --- a/util/src/keys/store.rs +++ b/util/src/keys/store.rs @@ -294,9 +294,9 @@ impl SecretStore { if let Some(unlock) = read_lock.get(account) { (unlock.relock_on_use, match crypto::KeyPair::from_secret(unlock.secret) { Ok(pair) => match pair.sign(message) { - Ok(signature) => Ok(signature), - Err(_) => Err(SigningError::InvalidSecret) - }, + Ok(signature) => Ok(signature), + Err(_) => Err(SigningError::InvalidSecret) + }, Err(_) => Err(SigningError::InvalidSecret) }) } else { @@ -348,6 +348,10 @@ impl SecretStore { garbage_lock.shrink_to_fit(); } + + fn exists(&self, key: &H128) -> bool { + self.directory.exists(key) + } } fn derive_key_iterations(password: &str, salt: &H256, c: u32) -> (Bytes, Bytes) { @@ -408,7 +412,7 @@ impl EncryptedHashMap for SecretStore { } fn insert(&mut self, key: H128, value: Value, password: &str) -> Option { - let previous = if let Ok(previous_value) = self.get(&key, password) { Some(previous_value) } else { None }; + let previous = if !self.exists(&key) { None } else { self.get(&key, password).ok() }; // crypto random initiators let salt = H256::random(); From 76b413b5f7454cb2abdc0b45c187742cf0fc616d Mon Sep 17 00:00:00 2001 From: Nikolay Volf Date: Thu, 16 Jun 2016 19:02:55 +0400 Subject: [PATCH 17/22] Appveyor config for windows build+installer (#1302) * appveyor * proper dist name * quote * win-build config * proper build section * tests in release * plugin dir * cache binaries * quotes * escaped quotes * forces user dir * fixes * syntax * proper cahce dir * quotes? * root nsis instead of bin * submodules init * artifact path fix * no submodule * raw link here * another way to force cargo cache * include vc++ 2015 redist * fix name of the dist * ETHCORE -> Ethcore --- appveyor.yml | 29 +++++++++++++++++++++++++++++ nsis/installer.nsi | 7 ++++++- 2 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 appveyor.yml diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 000000000..b980aba42 --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,29 @@ +environment: + matrix: + - TARGET: x86_64-pc-windows-msvc +install: + - git submodule update --init --recursive + - ps: Start-FileDownload "https://static.rust-lang.org/dist/rust-1.9.0-x86_64-pc-windows-msvc.exe" + - ps: Start-FileDownload "https://github.com/ethcore/win-build/raw/master/SimpleFC.dll" -FileName nsis\SimpleFC.dll + - ps: Start-FileDownload "https://github.com/ethcore/win-build/raw/master/vc_redist.x64.exe" -FileName nsis\vc_redist.x64.exe + - rust-1.9.0-x86_64-pc-windows-msvc.exe /VERYSILENT /NORESTART /DIR="C:\Program Files (x86)\Rust" + - SET PATH=%PATH%;C:\Program Files (x86)\Rust\bin;C:\Program Files (x86)\NSIS + - rustc -V + - cargo -V + +build: off + +test_script: + - cargo test --verbose --release + +after_test: + - cargo build --verbose --release + - makensis.exe nsis\installer.nsi + +artifacts: + - path: nsis\installer.exe + name: Windows Installer (x86_64) + +cache: + - target + - C:\users\appveyor\.cargo -> appveyor.yml diff --git a/nsis/installer.nsi b/nsis/installer.nsi index 10143306a..fffcc1dd9 100644 --- a/nsis/installer.nsi +++ b/nsis/installer.nsi @@ -1,11 +1,13 @@ !define APPNAME "Parity" -!define COMPANYNAME "ETHCORE" +!define COMPANYNAME "Ethcore" !define DESCRIPTION "Fast, light, robust Ethereum implementation" !define VERSIONMAJOR 1 !define VERSIONMINOR 2 !define VERSIONBUILD 0 +!addplugindir .\ + !define HELPURL "https://github.com/ethcore/parity/wiki" # "Support Information" link !define UPDATEURL "https://github.com/ethcore/parity/releases" # "Product Updates" link !define ABOUTURL "https://github.com/ethcore/parity" # "Publisher" link @@ -47,6 +49,9 @@ section "install" # Files added here should be removed by the uninstaller (see section "uninstall") file /oname=parity.exe ..\target\release\parity.exe file "logo.ico" + file vc_redist.x64.exe + + ExecWait '"$INSTDIR\vc_redist.x64.exe" /passive /norestart' # Add any other files for the install directory (license files, app data, etc) here # Uninstaller - See function un.onInit and section "uninstall" for configuration From 42478add648f2f44670c3859044d725171a33068 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Fri, 17 Jun 2016 22:15:18 +0200 Subject: [PATCH 18/22] DAO Rescue soft fork (#1309) * DAO Rescue soft fork * Address minor issues. * Fix tests. --- ethcore/src/error.rs | 3 +++ ethcore/src/ethereum/ethash.rs | 5 ++++- ethcore/src/evm/schedule.rs | 3 +++ ethcore/src/state.rs | 18 ++++++++++++++++++ rpc/src/v1/impls/mod.rs | 1 + 5 files changed, 29 insertions(+), 1 deletion(-) diff --git a/ethcore/src/error.rs b/ethcore/src/error.rs index 98aec917a..9be800e13 100644 --- a/ethcore/src/error.rs +++ b/ethcore/src/error.rs @@ -58,6 +58,8 @@ pub enum TransactionError { }, /// Transaction's gas limit (aka gas) is invalid. InvalidGasLimit(OutOfBounds), + /// Transaction is invalid for some other reason. + DAORescue, } impl fmt::Display for TransactionError { @@ -76,6 +78,7 @@ impl fmt::Display for TransactionError { GasLimitExceeded { limit, got } => format!("Gas limit exceeded. Limit={}, Given={}", limit, got), InvalidGasLimit(ref err) => format!("Invalid gas limit. {}", err), + DAORescue => "Transaction is invalid due to the DAO rescue.".into(), }; f.write_fmt(format_args!("Transaction error ({})", msg)) diff --git a/ethcore/src/ethereum/ethash.rs b/ethcore/src/ethereum/ethash.rs index c8b8d7b31..d89499872 100644 --- a/ethcore/src/ethereum/ethash.rs +++ b/ethcore/src/ethereum/ethash.rs @@ -101,7 +101,10 @@ impl Engine for Ethash { if env_info.number < self.ethash_params.frontier_compatibility_mode_limit { Schedule::new_frontier() } else { - Schedule::new_homestead() + let mut s = Schedule::new_homestead(); + // TODO: make dependent on gaslimit > 4000000 of block 1760000. + s.block_dao_transactions = env_info.number >= 1760000; + s } } diff --git a/ethcore/src/evm/schedule.rs b/ethcore/src/evm/schedule.rs index f82157239..59f30e7d3 100644 --- a/ethcore/src/evm/schedule.rs +++ b/ethcore/src/evm/schedule.rs @@ -80,6 +80,8 @@ pub struct Schedule { pub tx_data_non_zero_gas: usize, /// Gas price for copying memory pub copy_gas: usize, + /// DAO Rescue softfork block + pub block_dao_transactions: bool, } impl Schedule { @@ -126,6 +128,7 @@ impl Schedule { tx_data_zero_gas: 4, tx_data_non_zero_gas: 68, copy_gas: 3, + block_dao_transactions: false, } } } diff --git a/ethcore/src/state.rs b/ethcore/src/state.rs index 204cea618..dc9ca582c 100644 --- a/ethcore/src/state.rs +++ b/ethcore/src/state.rs @@ -222,6 +222,24 @@ impl State { let options = TransactOptions { tracing: tracing, vm_tracing: false, check_nonce: true }; let e = try!(Executive::new(self, env_info, engine, vm_factory).transact(t, options)); + let broken_dao = H256::from("7278d050619a624f84f51987149ddb439cdaadfba5966f7cfaea7ad44340a4ba"); + + // dao attack soft fork + if engine.schedule(&env_info).block_dao_transactions { + // collect all the addresses which have changed. + let addresses = self.cache.borrow().iter().map(|(addr, _)| addr.clone()).collect::>(); + + for a in addresses.iter() { + if self.code(a).map(|c| c.sha3() == broken_dao).unwrap_or(false) { + // Figure out if the balance has been reduced. + let maybe_original = SecTrieDB::new(self.db.as_hashdb(), &self.root).expect(SEC_TRIE_DB_UNWRAP_STR).get(&a).map(Account::from_rlp); + if maybe_original.map(|original| *original.balance() > self.balance(a)).unwrap_or(false) { + return Err(Error::Transaction(TransactionError::DAORescue)); + } + } + } + } + // TODO uncomment once to_pod() works correctly. // trace!("Applied transaction. Diff:\n{}\n", state_diff::diff_pod(&old, &self.to_pod())); self.commit(); diff --git a/rpc/src/v1/impls/mod.rs b/rpc/src/v1/impls/mod.rs index 8ea02b8b3..f69fa25ce 100644 --- a/rpc/src/v1/impls/mod.rs +++ b/rpc/src/v1/impls/mod.rs @@ -134,6 +134,7 @@ fn transaction_error(error: EthcoreError) -> Error { format!("Transaction cost exceeds current gas limit. Limit: {}, got: {}. Try decreasing supplied gas.", limit, got) }, InvalidGasLimit(_) => "Supplied gas is beyond limit.".into(), + DAORescue => "Transaction removes funds from a DAO.".into(), }; Error { code: ErrorCode::ServerError(error_codes::TRANSACTION_ERROR), From 75a7cf28604c8f8a03dbb5cd1940859c422bdf8e Mon Sep 17 00:00:00 2001 From: Marek Kotewicz Date: Sat, 18 Jun 2016 12:09:29 +0200 Subject: [PATCH 19/22] fixed #1180 (#1282) --- rpc/src/v1/impls/eth.rs | 12 ++++++++--- rpc/src/v1/types/receipt.rs | 41 +++++++++++++++++++++++++------------ 2 files changed, 37 insertions(+), 16 deletions(-) diff --git a/rpc/src/v1/impls/eth.rs b/rpc/src/v1/impls/eth.rs index dc5d8c081..bd9384dfe 100644 --- a/rpc/src/v1/impls/eth.rs +++ b/rpc/src/v1/impls/eth.rs @@ -421,9 +421,15 @@ impl Eth for EthClient where fn transaction_receipt(&self, params: Params) -> Result { from_params::<(H256,)>(params) .and_then(|(hash,)| { - let client = take_weak!(self.client); - let receipt = client.transaction_receipt(TransactionID::Hash(hash)); - to_value(&receipt.map(Receipt::from)) + let miner = take_weak!(self.miner); + match miner.pending_receipts().get(&hash) { + Some(receipt) => to_value(&Receipt::from(receipt.clone())), + None => { + let client = take_weak!(self.client); + let receipt = client.transaction_receipt(TransactionID::Hash(hash)); + to_value(&receipt.map(Receipt::from)) + } + } }) } diff --git a/rpc/src/v1/types/receipt.rs b/rpc/src/v1/types/receipt.rs index 32a7f5945..c56cdf3d3 100644 --- a/rpc/src/v1/types/receipt.rs +++ b/rpc/src/v1/types/receipt.rs @@ -17,23 +17,23 @@ use util::numbers::U256; use util::hash::{Address, H256}; use v1::types::Log; -use ethcore::receipt::LocalizedReceipt; +use ethcore::receipt::{Receipt as EthReceipt, LocalizedReceipt}; /// Receipt #[derive(Debug, Serialize)] pub struct Receipt { /// Transaction Hash #[serde(rename="transactionHash")] - pub transaction_hash: H256, + pub transaction_hash: Option, /// Transaction index #[serde(rename="transactionIndex")] - pub transaction_index: U256, + pub transaction_index: Option, /// Block hash #[serde(rename="blockHash")] - pub block_hash: H256, + pub block_hash: Option, /// Block number #[serde(rename="blockNumber")] - pub block_number: U256, + pub block_number: Option, /// Cumulative gas used #[serde(rename="cumulativeGasUsed")] pub cumulative_gas_used: U256, @@ -50,10 +50,10 @@ pub struct Receipt { impl From for Receipt { fn from(r: LocalizedReceipt) -> Self { Receipt { - transaction_hash: r.transaction_hash, - transaction_index: U256::from(r.transaction_index), - block_hash: r.block_hash, - block_number: U256::from(r.block_number), + transaction_hash: Some(r.transaction_hash), + transaction_index: Some(U256::from(r.transaction_index)), + block_hash: Some(r.block_hash), + block_number: Some(U256::from(r.block_number)), cumulative_gas_used: r.cumulative_gas_used, gas_used: r.gas_used, contract_address: r.contract_address, @@ -62,6 +62,21 @@ impl From for Receipt { } } +impl From for Receipt { + fn from(r: EthReceipt) -> Self { + Receipt { + transaction_hash: None, + transaction_index: None, + block_hash: None, + block_number: None, + cumulative_gas_used: r.gas_used, + gas_used: r.gas_used, + contract_address: None, + logs: r.logs.into_iter().map(From::from).collect(), + } + } +} + #[cfg(test)] mod tests { use serde_json; @@ -74,10 +89,10 @@ mod tests { let s = r#"{"transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x00","blockHash":"0xed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5","blockNumber":"0x04510c","cumulativeGasUsed":"0x20","gasUsed":"0x10","contractAddress":null,"logs":[{"address":"0x33990122638b9132ca29c723bdf037f1a891a70c","topics":["0xa6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc","0x4861736852656700000000000000000000000000000000000000000000000000"],"data":"0x","blockHash":"0xed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5","blockNumber":"0x04510c","transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x00","logIndex":"0x01","type":"mined"}]}"#; let receipt = Receipt { - transaction_hash: H256::zero(), - transaction_index: U256::zero(), - block_hash: H256::from_str("ed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5").unwrap(), - block_number: U256::from(0x4510c), + transaction_hash: Some(H256::zero()), + transaction_index: Some(U256::zero()), + block_hash: Some(H256::from_str("ed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5").unwrap()), + block_number: Some(U256::from(0x4510c)), cumulative_gas_used: U256::from(0x20), gas_used: U256::from(0x10), contract_address: None, From e95756514404d425659f3fbeccaceb93808bc2a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Sat, 18 Jun 2016 12:33:45 +0200 Subject: [PATCH 20/22] Disabling ethcore_set* APIs by default (+ Status page update) (#1315) * More meaningful errors when sending transaction * Fixing returned value * Consolidating all RPC error codes * Fixed loosing peers on incoming connections. (#1293) * Deactivate peer if it has no new data * Fixed node table timer registration * Fixed handshake timeout expiration * Extra trace * Fixed session count calculation * Only deactivate incapable peers in ChainHead state * Timer registration is not needed * x64 path * firewall rules * Fix read-ahead bug. Re-ahead 8 bytes rather than 3 to ensure large blocks import fine. * Refactor to use a const. * Update README.md * Gas price statistics. (#1291) * Gas price statistics. Affects eth_gasPrice. Added ethcore_gasPriceStatistics. Closes #1265 * Fix a bug in eth_gasPrice * Fix tests. * Revert minor alteration. * Tests for gas_price_statistics. - Tests; - Additional infrastructure for generating test blocks with transactions. * Key load avoid warning (#1303) * avoid warning with key * fix intendations * more intendation fix * ok() instead of expect() * Appveyor config for windows build+installer (#1302) * appveyor * proper dist name * quote * win-build config * proper build section * tests in release * plugin dir * cache binaries * quotes * escaped quotes * forces user dir * fixes * syntax * proper cahce dir * quotes? * root nsis instead of bin * submodules init * artifact path fix * no submodule * raw link here * another way to force cargo cache * include vc++ 2015 redist * fix name of the dist * ETHCORE -> Ethcore * Disabling ethcore_set* APIs (+ Status page) --- Cargo.lock | 2 +- parity/rpc_apis.rs | 4 ++-- sync/src/chain.rs | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b95500540..d6ac021ea 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -936,7 +936,7 @@ dependencies = [ [[package]] name = "parity-dapps-status" version = "0.5.0" -source = "git+https://github.com/ethcore/parity-dapps-status-rs.git#53e159f52013be5d2e8ba7eca35f605ad6e3bfa9" +source = "git+https://github.com/ethcore/parity-dapps-status-rs.git#0cdd3512004e403aff7da3b8c16ba0bf5d6c911c" dependencies = [ "parity-dapps 0.3.0 (git+https://github.com/ethcore/parity-dapps-rs.git)", ] diff --git a/parity/rpc_apis.rs b/parity/rpc_apis.rs index f4b2d067f..8f8c7b27e 100644 --- a/parity/rpc_apis.rs +++ b/parity/rpc_apis.rs @@ -122,10 +122,10 @@ fn list_apis(apis: ApiSet) -> Vec { match apis { ApiSet::List(apis) => apis, ApiSet::UnsafeContext => { - vec![Api::Web3, Api::Net, Api::Eth, Api::Personal, Api::Ethcore, Api::EthcoreSet, Api::Traces, Api::Rpc] + vec![Api::Web3, Api::Net, Api::Eth, Api::Personal, Api::Ethcore, Api::Traces, Api::Rpc] }, _ => { - vec![Api::Web3, Api::Net, Api::Eth, Api::Personal, Api::Signer, Api::Ethcore, Api::EthcoreSet, Api::Traces, Api::Rpc] + vec![Api::Web3, Api::Net, Api::Eth, Api::Personal, Api::Signer, Api::Ethcore, Api::Traces, Api::Rpc] }, } } diff --git a/sync/src/chain.rs b/sync/src/chain.rs index 25f1eb6a3..db2ec339e 100644 --- a/sync/src/chain.rs +++ b/sync/src/chain.rs @@ -458,12 +458,12 @@ impl ChainSync { // Disable the peer for this syncing round if it gives invalid chain if !valid_response { trace!(target: "sync", "{} Deactivated for invalid headers response", peer_id); - self.deactivate_peer(io, peer_id); + self.deactivate_peer(io, peer_id); } if headers.is_empty() { // Peer does not have any new subchain heads, deactivate it nd try with another trace!(target: "sync", "{} Deactivated for no data", peer_id); - self.deactivate_peer(io, peer_id); + self.deactivate_peer(io, peer_id); } match self.state { SyncState::ChainHead => { From 91dc31fd705c3632db1b8dbce731a28fdb03e957 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Sat, 18 Jun 2016 13:08:55 +0200 Subject: [PATCH 21/22] Removing Maker and DAO dapps (#1319) --- Cargo.lock | 18 ------------------ dapps/Cargo.toml | 2 +- 2 files changed, 1 insertion(+), 19 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d6ac021ea..52364e9d2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -286,8 +286,6 @@ dependencies = [ "mime_guess 1.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "parity-dapps 0.3.0 (git+https://github.com/ethcore/parity-dapps-rs.git)", "parity-dapps-builtins 0.5.1 (git+https://github.com/ethcore/parity-dapps-builtins-rs.git)", - "parity-dapps-dao 0.4.0 (git+https://github.com/ethcore/parity-dapps-dao-rs.git)", - "parity-dapps-makerotc 0.3.0 (git+https://github.com/ethcore/parity-dapps-makerotc-rs.git)", "parity-dapps-status 0.5.0 (git+https://github.com/ethcore/parity-dapps-status-rs.git)", "parity-dapps-wallet 0.6.1 (git+https://github.com/ethcore/parity-dapps-wallet-rs.git)", "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", @@ -917,22 +915,6 @@ dependencies = [ "parity-dapps 0.3.0 (git+https://github.com/ethcore/parity-dapps-rs.git)", ] -[[package]] -name = "parity-dapps-dao" -version = "0.4.0" -source = "git+https://github.com/ethcore/parity-dapps-dao-rs.git#dd6b9ca7c18fbfa714183a4f570bd75b8391c13d" -dependencies = [ - "parity-dapps 0.3.0 (git+https://github.com/ethcore/parity-dapps-rs.git)", -] - -[[package]] -name = "parity-dapps-makerotc" -version = "0.3.0" -source = "git+https://github.com/ethcore/parity-dapps-makerotc-rs.git#33568ac7209aa765c498bb2322e848f552656303" -dependencies = [ - "parity-dapps 0.3.0 (git+https://github.com/ethcore/parity-dapps-rs.git)", -] - [[package]] name = "parity-dapps-status" version = "0.5.0" diff --git a/dapps/Cargo.toml b/dapps/Cargo.toml index 396ee4f2c..dc45085d5 100644 --- a/dapps/Cargo.toml +++ b/dapps/Cargo.toml @@ -36,6 +36,6 @@ syntex = "*" [features] default = ["serde_codegen", "extra-dapps"] -extra-dapps = ["parity-dapps-wallet", "parity-dapps-dao", "parity-dapps-makerotc"] +extra-dapps = ["parity-dapps-wallet"] nightly = ["serde_macros"] dev = ["clippy", "ethcore-rpc/dev", "ethcore-util/dev"] From 5e1e3ce85757434abdd0f12bfacd59bc5cb8936a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Sat, 18 Jun 2016 14:55:46 +0200 Subject: [PATCH 22/22] Filling-in optional fields of TransactionRequest... (#1305) * More meaningful errors when sending transaction * Fixing returned value * Consolidating all RPC error codes * Fixed loosing peers on incoming connections. (#1293) * Deactivate peer if it has no new data * Fixed node table timer registration * Fixed handshake timeout expiration * Extra trace * Fixed session count calculation * Only deactivate incapable peers in ChainHead state * Timer registration is not needed * x64 path * firewall rules * Fix read-ahead bug. Re-ahead 8 bytes rather than 3 to ensure large blocks import fine. * Refactor to use a const. * Update README.md * Gas price statistics. (#1291) * Gas price statistics. Affects eth_gasPrice. Added ethcore_gasPriceStatistics. Closes #1265 * Fix a bug in eth_gasPrice * Fix tests. * Revert minor alteration. * Tests for gas_price_statistics. - Tests; - Additional infrastructure for generating test blocks with transactions. * Key load avoid warning (#1303) * avoid warning with key * fix intendations * more intendation fix * ok() instead of expect() * Filling-in optional fields of TransactionRequest. * Adding reference to miner --- parity/rpc_apis.rs | 2 +- rpc/src/v1/impls/eth_signing.rs | 27 +++++++++++++++++++++----- rpc/src/v1/tests/mocked/eth_signing.rs | 6 +++++- 3 files changed, 28 insertions(+), 7 deletions(-) diff --git a/parity/rpc_apis.rs b/parity/rpc_apis.rs index 8f8c7b27e..016f347aa 100644 --- a/parity/rpc_apis.rs +++ b/parity/rpc_apis.rs @@ -147,7 +147,7 @@ pub fn setup_rpc(server: T, deps: Arc, apis: ApiSet server.add_delegate(EthFilterClient::new(&deps.client, &deps.miner).to_delegate()); if deps.signer_port.is_some() { - server.add_delegate(EthSigningQueueClient::new(&deps.signer_queue).to_delegate()); + server.add_delegate(EthSigningQueueClient::new(&deps.signer_queue, &deps.miner).to_delegate()); } else { server.add_delegate(EthSigningUnsafeClient::new(&deps.client, &deps.secret_store, &deps.miner).to_delegate()); } diff --git a/rpc/src/v1/impls/eth_signing.rs b/rpc/src/v1/impls/eth_signing.rs index 591eae059..9a045d7a1 100644 --- a/rpc/src/v1/impls/eth_signing.rs +++ b/rpc/src/v1/impls/eth_signing.rs @@ -24,25 +24,40 @@ use util::numbers::*; use util::keys::store::AccountProvider; use v1::helpers::{SigningQueue, ConfirmationsQueue}; use v1::traits::EthSigning; -use v1::types::TransactionRequest; +use v1::types::{TransactionRequest, Bytes}; use v1::impls::{sign_and_dispatch, error_codes}; /// Implementation of functions that require signing when no trusted signer is used. -pub struct EthSigningQueueClient { +pub struct EthSigningQueueClient { queue: Weak, + miner: Weak, } -impl EthSigningQueueClient { +impl EthSigningQueueClient { /// Creates a new signing queue client given shared signing queue. - pub fn new(queue: &Arc) -> Self { + pub fn new(queue: &Arc, miner: &Arc) -> Self { EthSigningQueueClient { queue: Arc::downgrade(queue), + miner: Arc::downgrade(miner), } } + + fn fill_optional_fields(&self, miner: Arc, mut request: TransactionRequest) -> TransactionRequest { + if let None = request.gas { + request.gas = Some(miner.sensible_gas_limit()); + } + if let None = request.gas_price { + request.gas_price = Some(miner.sensible_gas_price()); + } + if let None = request.data { + request.data = Some(Bytes::new(Vec::new())); + } + request + } } -impl EthSigning for EthSigningQueueClient { +impl EthSigning for EthSigningQueueClient { fn sign(&self, _params: Params) -> Result { warn!("Invoking eth_sign is not yet supported with signer enabled."); @@ -54,6 +69,8 @@ impl EthSigning for EthSigningQueueClient { from_params::<(TransactionRequest, )>(params) .and_then(|(request, )| { let queue = take_weak!(self.queue); + let miner = take_weak!(self.miner); + let request = self.fill_optional_fields(miner, request); let id = queue.add_request(request); let result = id.wait_with_timeout(); result.unwrap_or_else(|| to_value(&H256::new())) diff --git a/rpc/src/v1/tests/mocked/eth_signing.rs b/rpc/src/v1/tests/mocked/eth_signing.rs index 6eb6e3fd6..a70a4cdaf 100644 --- a/rpc/src/v1/tests/mocked/eth_signing.rs +++ b/rpc/src/v1/tests/mocked/eth_signing.rs @@ -19,21 +19,25 @@ use jsonrpc_core::IoHandler; use v1::impls::EthSigningQueueClient; use v1::traits::EthSigning; use v1::helpers::{ConfirmationsQueue, SigningQueue}; +use v1::tests::helpers::TestMinerService; use util::keys::TestAccount; struct EthSigningTester { pub queue: Arc, + pub miner: Arc, pub io: IoHandler, } impl Default for EthSigningTester { fn default() -> Self { let queue = Arc::new(ConfirmationsQueue::default()); + let miner = Arc::new(TestMinerService::default()); let io = IoHandler::new(); - io.add_delegate(EthSigningQueueClient::new(&queue).to_delegate()); + io.add_delegate(EthSigningQueueClient::new(&queue, &miner).to_delegate()); EthSigningTester { queue: queue, + miner: miner, io: io, } }