From 4e3f8bab10387bd7b1d4baa1b4715d4d2d84a9a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristoffer=20Str=C3=B6m?= Date: Tue, 20 Sep 2016 12:19:07 +0200 Subject: [PATCH 01/25] Add Ws Json rpc client and command line utils --- parity/cli/mod.rs | 3 + parity/cli/usage.txt | 3 + parity/configuration.rs | 51 +++- parity/main.rs | 8 + rpc/src/v1/types/confirmations.rs | 25 +- rpc/src/v1/types/transaction_request.rs | 12 +- rpc/src/v1/types/uint.rs | 8 + rpc_cli/Cargo.toml | 15 ++ rpc_cli/src/lib.rs | 168 ++++++++++++ rpc_client/Cargo.toml | 27 ++ rpc_client/src/client.rs | 332 ++++++++++++++++++++++++ rpc_client/src/lib.rs | 74 ++++++ rpc_client/src/mock.rs | 30 +++ rpc_client/src/signer.rs | 47 ++++ 14 files changed, 796 insertions(+), 7 deletions(-) create mode 100644 rpc_cli/Cargo.toml create mode 100644 rpc_cli/src/lib.rs create mode 100644 rpc_client/Cargo.toml create mode 100644 rpc_client/src/client.rs create mode 100644 rpc_client/src/lib.rs create mode 100644 rpc_client/src/mock.rs create mode 100644 rpc_client/src/signer.rs diff --git a/parity/cli/mod.rs b/parity/cli/mod.rs index f5e2cbf4a..a535d4195 100644 --- a/parity/cli/mod.rs +++ b/parity/cli/mod.rs @@ -31,6 +31,8 @@ usage! { cmd_import: bool, cmd_signer: bool, cmd_new_token: bool, + cmd_sign: bool, + cmd_reject: bool, cmd_snapshot: bool, cmd_restore: bool, cmd_ui: bool, @@ -41,6 +43,7 @@ usage! { arg_pid_file: String, arg_file: Option, arg_path: Vec, + arg_id: Option, // Flags // -- Legacy Options diff --git a/parity/cli/usage.txt b/parity/cli/usage.txt index 58518bd48..34efad9c6 100644 --- a/parity/cli/usage.txt +++ b/parity/cli/usage.txt @@ -12,6 +12,9 @@ Usage: parity import [ ] [options] parity export (blocks | state) [ ] [options] parity signer new-token [options] + parity signer list [options] + parity signer sign [ ] [ --password FILE ] [options] + parity signer reject [options] parity snapshot [options] parity restore [ ] [options] parity tools hash diff --git a/parity/configuration.rs b/parity/configuration.rs index 60116ef99..0d1c2c690 100644 --- a/parity/configuration.rs +++ b/parity/configuration.rs @@ -51,6 +51,21 @@ pub enum Cmd { ImportPresaleWallet(ImportWallet), Blockchain(BlockchainCmd), SignerToken(SignerConfiguration), + SignerSign { + id: Option, + pwfile: Option, + port: u16, + authfile: PathBuf, + }, + SignerList { + port: u16, + authfile: PathBuf + }, + SignerReject { + id: usize, + port: u16, + authfile: PathBuf + }, Snapshot(SnapshotCommand), Hash(Option), } @@ -103,8 +118,43 @@ impl Configuration { let cmd = if self.args.flag_version { Cmd::Version +<<<<<<< HEAD } else if self.args.cmd_signer && self.args.cmd_new_token { Cmd::SignerToken(signer_conf) +======= + } else if self.args.cmd_signer { + let mut authfile = PathBuf::from(signer_conf.signer_path); + authfile.push("authcodes"); + + if self.args.cmd_new_token { + Cmd::SignerToken(dirs.signer) + } else if self.args.cmd_sign { + let pwfile = match self.args.flag_password.get(0) { + Some(pwfile) => Some(PathBuf::from(pwfile)), + None => None, + }; + Cmd::SignerSign { + id: self.args.arg_id, + pwfile: pwfile, + port: signer_conf.port, + authfile: authfile, + } + } else if self.args.cmd_reject { + Cmd::SignerReject { + // id is a required field for this command + id: self.args.arg_id.unwrap(), + port: signer_conf.port, + authfile: authfile, + } + } else if self.args.cmd_list { + Cmd::SignerList { + port: signer_conf.port, + authfile: authfile, + } + } else { + unreachable!(); + } +>>>>>>> Add Ws Json rpc client and command line utils } else if self.args.cmd_tools && self.args.cmd_hash { Cmd::Hash(self.args.arg_file) } else if self.args.cmd_account { @@ -1126,4 +1176,3 @@ mod tests { assert!(conf.init_reserved_nodes().is_ok()); } } - diff --git a/parity/main.rs b/parity/main.rs index c125e87f6..6d1ddbffb 100644 --- a/parity/main.rs +++ b/parity/main.rs @@ -69,6 +69,8 @@ extern crate ethcore_stratum; #[cfg(feature = "dapps")] extern crate ethcore_dapps; +extern crate rpc_cli; + macro_rules! dependency { ($dep_ty:ident, $url:expr) => { { @@ -145,6 +147,12 @@ fn execute(command: Execute) -> Result { Cmd::ImportPresaleWallet(presale_cmd) => presale::execute(presale_cmd), Cmd::Blockchain(blockchain_cmd) => blockchain::execute(blockchain_cmd), Cmd::SignerToken(signer_cmd) => signer::execute(signer_cmd), + Cmd::SignerSign { id, pwfile, port, authfile } => + rpc_cli::cmd_signer_sign(id, pwfile, port, authfile), + Cmd::SignerList { port, authfile } => + rpc_cli::cmd_signer_list(port, authfile), + Cmd::SignerReject { id, port, authfile } => + rpc_cli::cmd_signer_reject(id, port, authfile), Cmd::Snapshot(snapshot_cmd) => snapshot::execute(snapshot_cmd), } } diff --git a/rpc/src/v1/types/confirmations.rs b/rpc/src/v1/types/confirmations.rs index d8cfa14d6..6ea01db64 100644 --- a/rpc/src/v1/types/confirmations.rs +++ b/rpc/src/v1/types/confirmations.rs @@ -22,7 +22,7 @@ use v1::types::{U256, TransactionRequest, RichRawTransaction, H160, H256, H520, use v1::helpers; /// Confirmation waiting in a queue -#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize)] +#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)] pub struct ConfirmationRequest { /// Id of this confirmation pub id: U256, @@ -39,8 +39,24 @@ impl From for ConfirmationRequest { } } +impl fmt::Display for ConfirmationRequest { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "id: {:?}, {}", self.id, self.payload) + } +} + +impl fmt::Display for ConfirmationPayload { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + &ConfirmationPayload::Transaction(ref transaction) + => write!(f, "{}", transaction), + &ConfirmationPayload::Sign(_) => write!(f, "TODO: data"), + } + } +} + /// Sign request -#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize)] +#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)] pub struct SignRequest { /// Address pub address: H160, @@ -102,7 +118,7 @@ impl Serialize for ConfirmationResponse { } /// Confirmation payload, i.e. the thing to be confirmed -#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize)] +#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)] pub enum ConfirmationPayload { /// Send Transaction #[serde(rename="sendTransaction")] @@ -136,7 +152,7 @@ impl From for ConfirmationPayload { } /// Possible modifications to the confirmed transaction sent by `Trusted Signer` -#[derive(Debug, PartialEq, Deserialize)] +#[derive(Debug, PartialEq, Serialize, Deserialize)] #[serde(deny_unknown_fields)] pub struct TransactionModification { /// Modified gas price @@ -290,4 +306,3 @@ mod tests { }); } } - diff --git a/rpc/src/v1/types/transaction_request.rs b/rpc/src/v1/types/transaction_request.rs index a4f8e6387..5ee484a01 100644 --- a/rpc/src/v1/types/transaction_request.rs +++ b/rpc/src/v1/types/transaction_request.rs @@ -19,6 +19,8 @@ use v1::types::{Bytes, H160, U256}; use v1::helpers; +use std::fmt; + /// Transaction request coming from RPC #[derive(Debug, Clone, Default, Eq, PartialEq, Hash, Serialize, Deserialize)] #[serde(deny_unknown_fields)] @@ -40,6 +42,15 @@ pub struct TransactionRequest { pub nonce: Option, } +impl fmt::Display for TransactionRequest { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{:?} from {:?} to {:?}", + self.value.unwrap_or(U256::from(0)), + self.from, + self.to) + } +} + impl From for TransactionRequest { fn from(r: helpers::TransactionRequest) -> Self { TransactionRequest { @@ -192,4 +203,3 @@ mod tests { assert!(deserialized.is_err(), "Should be error because to is empty"); } } - diff --git a/rpc/src/v1/types/uint.rs b/rpc/src/v1/types/uint.rs index 245348709..e7363275e 100644 --- a/rpc/src/v1/types/uint.rs +++ b/rpc/src/v1/types/uint.rs @@ -15,6 +15,8 @@ // along with Parity. If not, see . use std::str::FromStr; +use std::fmt; +use rustc_serialize::hex::ToHex; use serde; use util::{U256 as EthU256, U128 as EthU128, Uint}; @@ -46,6 +48,12 @@ macro_rules! impl_uint { } } + impl fmt::LowerHex for $name { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{:#x}", self.0) + } + } + impl serde::Serialize for $name { fn serialize(&self, serializer: &mut S) -> Result<(), S::Error> where S: serde::Serializer { serializer.serialize_str(&format!("0x{}", self.0.to_hex())) diff --git a/rpc_cli/Cargo.toml b/rpc_cli/Cargo.toml new file mode 100644 index 000000000..8169d3b71 --- /dev/null +++ b/rpc_cli/Cargo.toml @@ -0,0 +1,15 @@ +[package] +authors = ["Ethcore "] +description = "Parity Cli Tool" +homepage = "http://ethcore.io" +license = "GPL-3.0" +name = "rpc-cli" +version = "1.4.0" + +[dependencies] +futures = "0.1" +rpassword = "0.3.0" +ethcore-bigint = { path = "../util/bigint" } +ethcore-rpc = { path = "../rpc" } +parity-rpc-client = { path = "../rpc_client" } +ethcore-util = { path = "../util" } diff --git a/rpc_cli/src/lib.rs b/rpc_cli/src/lib.rs new file mode 100644 index 000000000..a69351fd0 --- /dev/null +++ b/rpc_cli/src/lib.rs @@ -0,0 +1,168 @@ +extern crate futures; + +extern crate ethcore_util as util; +extern crate ethcore_rpc as rpc; +extern crate ethcore_bigint as bigint; +extern crate rpassword; + +extern crate parity_rpc_client as client; + +use rpc::v1::types::{U256, ConfirmationRequest}; +use client::signer::SignerRpc; +use std::io::{Write, BufRead, BufReader, stdout, stdin}; +use std::path::PathBuf; +use std::fs::File; + +use futures::Future; + +fn sign_interactive(signer: &mut SignerRpc, pwd: &String, request: ConfirmationRequest) + -> Result +{ + print!("\n{}\nSign this transaction? (y)es/(N)o/(r)eject: ", request); + stdout().flush(); + match BufReader::new(stdin()).lines().next() { + Some(Ok(line)) => { + match line.to_lowercase().chars().nth(0) { + Some('y') => { + match sign_transaction(signer, request.id, pwd) { + Ok(s) | Err(s) => println!("{}", s), + } + } + Some('r') => { + match reject_transaction(signer, request.id) { + Ok(s) | Err(s) => println!("{}", s), + } + } + _ => () + } + } + _ => return Err("Could not read from stdin".to_string()) + } + Ok("Finished".to_string()) +} + +fn sign_transactions(signer: &mut SignerRpc, pwd: String) -> Result { + signer.requests_to_confirm().map(|reqs| { + match reqs { + Ok(reqs) => { + if reqs.len() == 0 { + Ok("No transactions in signing queue".to_string()) + } else { + for r in reqs { + sign_interactive(signer, &pwd, r); + } + Ok("".to_string()) + } + } + Err(err) => { + Err(format!("error: {:?}", err)) + } + } + }).wait().unwrap() +} + +fn list_transactions(signer: &mut SignerRpc) -> Result { + signer.requests_to_confirm().map(|reqs| { + match reqs { + Ok(reqs) => { + let mut s = "Transaction queue:".to_string(); + if reqs.len() == 0 { + s = s + &"No transactions in signing queue"; + } else { + for r in reqs { + s = s + &format!("\n{}", r); + } + } + Ok(s) + } + Err(err) => { + Err(format!("error: {:?}", err)) + } + } + }).wait().unwrap() +} + +fn sign_transaction(signer: &mut SignerRpc, + id: U256, + pwd: &String) -> Result { + signer.confirm_request(id, None, &pwd).map(|res| { + match res { + Ok(u) => Ok(format!("Signed transaction id: {:#x}", u)), + Err(e) => Err(format!("{:?}", e)), + } + }).wait().unwrap() +} + +fn reject_transaction(signer: &mut SignerRpc, + id: U256) -> Result { + signer.reject_request(id).map(|res| { + match res { + Ok(true) => Ok(format!("Rejected transaction id {:#x}", id)), + Ok(false) => Err(format!("No such request")), + Err(e) => Err(format!("{:?}", e)), + } + }).wait().unwrap() +} + +// cmds + +pub fn cmd_signer_list(signerport: u16, + authfile: PathBuf) -> Result { + match SignerRpc::new(&format!("ws://127.0.0.1:{}", signerport), + &authfile) { + Ok(mut signer) => { + list_transactions(&mut signer) + } + Err(e) => Err(format!("{:?}", e)) + } +} + +pub fn cmd_signer_reject(id: usize, + signerport: u16, + authfile: PathBuf) -> Result { + match SignerRpc::new(&format!("ws://127.0.0.1:{}", signerport), + &authfile) { + Ok(mut signer) => { + reject_transaction(&mut signer, U256::from(id)) + }, + Err(e) => Err(format!("{:?}", e)) + } +} + +pub fn cmd_signer_sign(id: Option, + pwfile: Option, + signerport: u16, + authfile: PathBuf) -> Result { + let pwd; + match pwfile { + Some(pwfile) => { + match File::open(pwfile) { + Ok(fd) => { + match BufReader::new(fd).lines().next() { + Some(Ok(line)) => pwd = line, + _ => return Err(format!("No password in file")) + } + }, + Err(e) => return Err(format!("Could not open pwfile: {}", e)) + } + } + None => { + pwd = rpassword::prompt_password_stdout("Password: ").unwrap(); + } + } + + match SignerRpc::new(&format!("ws://127.0.0.1:{}", signerport), + &authfile) { + Ok(mut signer) => { + match id { + Some(id) => { + sign_transaction(&mut signer, U256::from(id), &pwd) + }, + None => { + sign_transactions(&mut signer, pwd) + } + } + } + Err(e) => return Err(format!("{:?}", e)) + } +} diff --git a/rpc_client/Cargo.toml b/rpc_client/Cargo.toml new file mode 100644 index 000000000..24f8b8522 --- /dev/null +++ b/rpc_client/Cargo.toml @@ -0,0 +1,27 @@ +[package] +authors = ["Ethcore "] +description = "Parity Rpc Client" +homepage = "http://ethcore.io" +license = "GPL-3.0" +name = "parity-rpc-client" +version = "1.4.0" + +[dependencies] +futures = "0.1" +lazy_static = "0.2.1" +matches = "0.1.2" +rand = "0.3.14" +serde = "0.8" +serde_json = "0.8" +tempdir = "0.3.5" +url = "1.2.0" +ws = "0.5.3" + +[dependencies.ethcore-rpc] +path = "../rpc" + +[dependencies.ethcore-signer] +path = "../signer" + +[dependencies.ethcore-util] +path = "../util" diff --git a/rpc_client/src/client.rs b/rpc_client/src/client.rs new file mode 100644 index 000000000..15d83ee18 --- /dev/null +++ b/rpc_client/src/client.rs @@ -0,0 +1,332 @@ +use std::fmt::{Debug, Formatter, Error as FmtError}; +use std::io::{BufReader, BufRead}; +use std::sync::{Arc, Mutex}; +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::collections::BTreeMap; +use std::thread; +use std::mem; +use std::time; + +use std::path::PathBuf; +use util::Hashable; +use url::Url; +use std::fs::File; + +use ws::{connect, + Request, + Handler, + Sender, + Handshake, + Error as WsError, + ErrorKind as WsErrorKind, + Message, + Result as WsResult}; + +use serde::Serialize; +use serde::Deserialize; +use serde::ser::Serializer; +use serde_json::{from_str, + to_string, + from_value, + Value as JsonValue, + Error as JsonError}; + +use futures::{BoxFuture, Canceled, Complete, Future, oneshot, done}; + +/// The actual websocket connection handler, passed into the +/// event loop of ws-rs +struct RpcHandler { + pending: Pending, + // Option is used here as + // temporary storage until + // connection is setup + // and the values are moved into + // the new `Rpc` + complete: Option>>, + auth_code: String, + out: Option, +} + +impl RpcHandler { + fn new(out: Sender, + auth_code: String, + complete: Complete>) + -> Self { + RpcHandler { + out: Some(out), + auth_code: auth_code, + pending: Pending::new(), + complete: Some(complete), + } + } +} + +impl Handler for RpcHandler { + fn build_request(&mut self, url: &Url) -> WsResult { + match Request::from_url(url) { + Ok(mut r) => { + let timestamp = time::UNIX_EPOCH.elapsed().unwrap().as_secs(); + let hashed = format!("{}:{}", self.auth_code, timestamp).sha3(); + let proto = format!("{:?}_{}", hashed, timestamp); + r.add_protocol(&proto); + Ok(r) + }, + Err(e) => Err(WsError::new(WsErrorKind::Internal, format!("{}", e))), + } + } + fn on_error(&mut self, err: WsError) { + match mem::replace(&mut self.complete, None) { + Some(c) => c.complete(Err(RpcError::WsError(err))), + None => println!("warning: unexpected error"), + } + } + fn on_open(&mut self, _: Handshake) -> WsResult<()> { + match mem::replace(&mut self.complete, None) { + Some(c) => c.complete(Ok(Rpc { + out: mem::replace(&mut self.out, None).unwrap(), + auth_code: self.auth_code.clone(), + counter: AtomicUsize::new(0), + pending: self.pending.clone(), + })), + // Should not be reachable + None => (), + } + Ok(()) + } + fn on_message(&mut self, msg: Message) -> WsResult<()> { + match parse_response(&msg.to_string()) { + (Some(id), response) => { + match self.pending.remove(id) { + Some(c) => c.complete(response), + None => println!("warning: unexpected id: {}", id), + } + } + (None, response) => println!("warning: error: {:?}, {}", response, msg.to_string()), + } + Ok(()) + } +} + +/// Keeping track of issued requests to be matched up with responses +#[derive(Clone)] +struct Pending(Arc>>>>); + +impl Pending { + fn new() -> Self { + Pending(Arc::new(Mutex::new(BTreeMap::new()))) + } + fn insert(&mut self, k: usize, v: Complete>) { + self.0.lock().unwrap().insert(k, v); + } + fn remove(&mut self, k: usize) -> Option>> { + self.0.lock().unwrap().remove(&k) + } +} + +fn get_authcode(path: &PathBuf) -> Result { + match File::open(path) { + Ok(fd) => match BufReader::new(fd).lines().next() { + Some(Ok(code)) => Ok(code), + _ => Err(RpcError::NoAuthCode), + }, + Err(_) => Err(RpcError::NoAuthCode) + } +} + +/// The handle to the connection +pub struct Rpc { + out: Sender, + counter: AtomicUsize, + pending: Pending, + auth_code: String, +} + +impl Rpc { + /// Blocking, returns a new initialized connection or RpcError + pub fn new(url: &str, authpath: &PathBuf) -> Result { + let rpc = try!(Self::connect(url, authpath).map(|rpc| rpc).wait()); + rpc + } + /// Non-blocking, returns a future + pub fn connect(url: &str, authpath: &PathBuf) + -> BoxFuture, Canceled> { + let (c, p) = oneshot::>(); + match get_authcode(authpath) { + Err(e) => return done(Ok(Err(e))).boxed(), + Ok(code) => { + let url = String::from(url); + thread::spawn(move || { + // mem:replace Option hack to move `c` out + // of the FnMut closure + let mut swap = Some(c); + match connect(url, |out| { + let c = mem::replace(&mut swap, None).unwrap(); + RpcHandler::new(out, code.clone(), c) + }) { + Err(err) => { + let c = mem::replace(&mut swap, None).unwrap(); + c.complete(Err(RpcError::WsError(err))); + }, + // c will complete on the `on_open` event in the Handler + _ => () + } + }); + p.boxed() + } + } + } + /// Non-blocking, returns a future of the request response + pub fn request(&mut self, method: &'static str, params: Vec) + -> BoxFuture, Canceled> + where T: Deserialize + Send + Sized { + + let (c, p) = oneshot::>(); + + let id = self.counter.fetch_add(1, Ordering::Relaxed); + self.pending.insert(id, c); + + let serialized = to_string(&RpcRequest::new(id, method, params)).unwrap(); + let _ = self.out.send(serialized); + + p.map(|result| { + match result { + Ok(json) => { + let t: T = try!(from_value(json)); + Ok(t) + }, + Err(err) => Err(err) + } + }).boxed() + } +} + + +struct RpcRequest { + method: &'static str, + params: Vec, + id: usize, +} + +impl RpcRequest { + fn new(id: usize, method: &'static str, params: Vec) -> Self { + RpcRequest { + method: method, + id: id, + params: params, + } + } +} + +impl Serialize for RpcRequest { + fn serialize(&self, s: &mut S) + -> Result<(), S::Error> + where S: Serializer { + let mut state = try!(s.serialize_struct("RpcRequest" , 3)); + try!(s.serialize_struct_elt(&mut state ,"jsonrpc", "2.0")); + try!(s.serialize_struct_elt(&mut state ,"id" , &self.id)); + try!(s.serialize_struct_elt(&mut state ,"method" , &self.method)); + try!(s.serialize_struct_elt(&mut state ,"params" , &self.params)); + s.serialize_struct_end(state) + } +} + +pub enum RpcError { + WrongVersion(String), + ParseError(JsonError), + MalformedResponse(String), + Remote(String), + WsError(WsError), + Canceled(Canceled), + UnexpectedId, + NoAuthCode, +} + +impl Debug for RpcError { + fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> { + match self { + &RpcError::WrongVersion(ref s) + => write!(f, "Expected version 2.0, got {}", s), + &RpcError::ParseError(ref err) + => write!(f, "ParseError: {}", err), + &RpcError::MalformedResponse(ref s) + => write!(f, "Malformed response: {}", s), + &RpcError::Remote(ref s) + => write!(f, "Remote error: {}", s), + &RpcError::WsError(ref s) + => write!(f, "Websocket error: {}", s), + &RpcError::Canceled(ref s) + => write!(f, "Futures error: {:?}", s), + &RpcError::UnexpectedId + => write!(f, "Unexpected response id"), + &RpcError::NoAuthCode + => write!(f, "No authcodes available"), + } + } +} + +impl From for RpcError { + fn from(err: JsonError) -> RpcError { + RpcError::ParseError(err) + } +} + +impl From for RpcError { + fn from(err: WsError) -> RpcError { + RpcError::WsError(err) + } +} + +impl From for RpcError { + fn from(err: Canceled) -> RpcError { + RpcError::Canceled(err) + } +} + +fn parse_response(s: &str) -> (Option, Result) { + let mut json: JsonValue = match from_str(s) { + Err(e) => return (None, Err(RpcError::ParseError(e))), + Ok(json) => json, + }; + + let obj = match json.as_object_mut() { + Some(o) => o, + None => return + (None, + Err(RpcError::MalformedResponse("Not a JSON object".to_string()))), + }; + + let id; + match obj.get("id") { + Some(&JsonValue::U64(u)) => { + id = u as usize; + }, + _ => return (None, + Err(RpcError::MalformedResponse("Missing id".to_string()))), + } + + match obj.get("jsonrpc") { + Some(&JsonValue::String(ref s)) => { + if *s != "2.0".to_string() { + return (Some(id), + Err(RpcError::WrongVersion(s.clone()))) + } + }, + _ => return + (Some(id), + Err(RpcError::MalformedResponse("Not a jsonrpc object".to_string()))), + } + + match obj.get("error") { + Some(err) => return + (Some(id), + Err(RpcError::Remote(format!("{}", err)))), + None => (), + }; + + match obj.remove("result") { + None => (Some(id), + Err(RpcError::MalformedResponse("No result".to_string()))), + Some(result) => (Some(id), + Ok(result)), + } +} diff --git a/rpc_client/src/lib.rs b/rpc_client/src/lib.rs new file mode 100644 index 000000000..345526828 --- /dev/null +++ b/rpc_client/src/lib.rs @@ -0,0 +1,74 @@ +pub mod client; +pub mod signer; +mod mock; + +extern crate ws; +extern crate ethcore_signer; +extern crate url; +extern crate futures; +extern crate ethcore_util as util; +extern crate ethcore_rpc as rpc; +extern crate serde; +extern crate serde_json; +extern crate rand; +extern crate tempdir; + +#[macro_use] +extern crate lazy_static; +#[macro_use] +extern crate matches; + +mod test { + use futures::Future; + use url::Url; + use std::path::PathBuf; + + use client::{Rpc, RpcError}; + + use mock; + + #[test] + fn test_connection_refused() { + let (srv, port, tmpdir, _) = mock::serve(); + + let mut path = PathBuf::from(tmpdir.path()); + path.push("authcodes"); + let connect = Rpc::connect(&format!("ws://127.0.0.1:{}", port - 1), &path); + + connect.map(|conn| { + assert!(matches!(&conn, &Err(RpcError::WsError(_)))); + }).wait(); + + drop(srv); + } + + #[test] + fn test_authcode_fail() { + let (srv, port, _, _) = mock::serve(); + let path = PathBuf::from("nonexist"); + + let connect = Rpc::connect(&format!("ws://127.0.0.1:{}", port), &path); + + connect.map(|conn| { + assert!(matches!(&conn, &Err(RpcError::NoAuthCode))); + }).wait(); + + drop(srv); + } + + #[test] + fn test_authcode_correct() { + let (srv, port, tmpdir, _) = mock::serve(); + + let mut path = PathBuf::from(tmpdir.path()); + path.push("authcodes"); + let connect = Rpc::connect(&format!("ws://127.0.0.1:{}", port), &path); + + connect.map(|conn| { + assert!(conn.is_ok()) + }).wait(); + + drop(srv); + } + +} diff --git a/rpc_client/src/mock.rs b/rpc_client/src/mock.rs new file mode 100644 index 000000000..9ac52f605 --- /dev/null +++ b/rpc_client/src/mock.rs @@ -0,0 +1,30 @@ +use ethcore_signer::ServerBuilder; +use ethcore_signer::Server; +use rpc::ConfirmationsQueue; +use std::sync::Arc; +use std::time::{Duration}; +use std::thread; +use rand; +use tempdir::TempDir; +use std::path::PathBuf; +use std::fs::{File, create_dir_all}; +use std::io::Write; + +// mock server +pub fn serve() -> (Server, usize, TempDir, Arc) { + let queue = Arc::new(ConfirmationsQueue::default()); + let dir = TempDir::new("auth").unwrap(); + + let mut authpath = PathBuf::from(dir.path()); + create_dir_all(&authpath).unwrap(); + authpath.push("authcodes"); + let mut authfile = File::create(&authpath).unwrap(); + authfile.write_all(b"zzzRo0IzGi04mzzz\n").unwrap(); + + let builder = ServerBuilder::new(queue.clone(), authpath); + let port = 35000 + rand::random::() % 10000; + let res = builder.start(format!("127.0.0.1:{}", port).parse().unwrap()).unwrap(); + + thread::sleep(Duration::from_millis(25)); + (res, port, dir, queue) +} diff --git a/rpc_client/src/signer.rs b/rpc_client/src/signer.rs new file mode 100644 index 000000000..8ca4724a1 --- /dev/null +++ b/rpc_client/src/signer.rs @@ -0,0 +1,47 @@ + +use client::{Rpc, RpcError}; +use rpc::v1::types::{ConfirmationRequest, + ConfirmationPayload, + TransactionModification, + U256}; +use serde_json::{Value as JsonValue, to_value}; +use std::path::PathBuf; +use futures::{BoxFuture, Canceled}; + +pub struct SignerRpc { + rpc: Rpc, +} + +impl SignerRpc { + pub fn new(url: &str, authfile: &PathBuf) -> Result { + match Rpc::new(&url, authfile) { + Ok(rpc) => Ok(SignerRpc { rpc: rpc }), + Err(e) => Err(e), + } + } + pub fn requests_to_confirm(&mut self) -> + BoxFuture, RpcError>, Canceled> + { + self.rpc.request::> + ("personal_requestsToConfirm", vec![]) + } + pub fn confirm_request(&mut self, + id: U256, + new_gas_price: Option, + pwd: &str) -> + BoxFuture, Canceled> + { + self.rpc.request::("personal_confirmRequest", vec![ + to_value(&format!("{:#x}", id)), + to_value(&TransactionModification { gas_price: new_gas_price }), + to_value(&pwd), + ]) + } + pub fn reject_request(&mut self, id: U256) -> + BoxFuture, Canceled> + { + self.rpc.request::("personal_rejectRequest", vec![ + JsonValue::String(format!("{:#x}", id)) + ]) + } +} From 273d7c00c32213bc7a42b8d7d030898a49693454 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristoffer=20Str=C3=B6m?= Date: Thu, 29 Sep 2016 14:48:44 +0200 Subject: [PATCH 02/25] Pass cli tests, remove compiler warnings --- parity/cli/mod.rs | 3 +++ rpc_cli/src/lib.rs | 8 +++----- rpc_client/src/client.rs | 2 -- rpc_client/src/lib.rs | 10 +++++----- rpc_client/src/signer.rs | 1 - 5 files changed, 11 insertions(+), 13 deletions(-) diff --git a/parity/cli/mod.rs b/parity/cli/mod.rs index a535d4195..4916d117d 100644 --- a/parity/cli/mod.rs +++ b/parity/cli/mod.rs @@ -506,6 +506,8 @@ mod tests { cmd_blocks: false, cmd_import: false, cmd_signer: false, + cmd_sign: false, + cmd_reject: false, cmd_new_token: false, cmd_snapshot: false, cmd_restore: false, @@ -516,6 +518,7 @@ mod tests { // Arguments arg_pid_file: "".into(), arg_file: None, + arg_id: None, arg_path: vec![], // -- Operating Options diff --git a/rpc_cli/src/lib.rs b/rpc_cli/src/lib.rs index a69351fd0..dba4d681f 100644 --- a/rpc_cli/src/lib.rs +++ b/rpc_cli/src/lib.rs @@ -16,10 +16,9 @@ use std::fs::File; use futures::Future; fn sign_interactive(signer: &mut SignerRpc, pwd: &String, request: ConfirmationRequest) - -> Result { print!("\n{}\nSign this transaction? (y)es/(N)o/(r)eject: ", request); - stdout().flush(); + let _ = stdout().flush(); match BufReader::new(stdin()).lines().next() { Some(Ok(line)) => { match line.to_lowercase().chars().nth(0) { @@ -36,9 +35,8 @@ fn sign_interactive(signer: &mut SignerRpc, pwd: &String, request: ConfirmationR _ => () } } - _ => return Err("Could not read from stdin".to_string()) + _ => println!("Could not read from stdin") } - Ok("Finished".to_string()) } fn sign_transactions(signer: &mut SignerRpc, pwd: String) -> Result { @@ -49,7 +47,7 @@ fn sign_transactions(signer: &mut SignerRpc, pwd: String) -> Result c.complete(Ok(Rpc { out: mem::replace(&mut self.out, None).unwrap(), - auth_code: self.auth_code.clone(), counter: AtomicUsize::new(0), pending: self.pending.clone(), })), @@ -138,7 +137,6 @@ pub struct Rpc { out: Sender, counter: AtomicUsize, pending: Pending, - auth_code: String, } impl Rpc { diff --git a/rpc_client/src/lib.rs b/rpc_client/src/lib.rs index 345526828..eae26e57b 100644 --- a/rpc_client/src/lib.rs +++ b/rpc_client/src/lib.rs @@ -18,18 +18,18 @@ extern crate lazy_static; #[macro_use] extern crate matches; +#[cfg(test)] mod test { use futures::Future; - use url::Url; use std::path::PathBuf; use client::{Rpc, RpcError}; - use mock; + use mock::serve; #[test] fn test_connection_refused() { - let (srv, port, tmpdir, _) = mock::serve(); + let (srv, port, tmpdir, _) = serve(); let mut path = PathBuf::from(tmpdir.path()); path.push("authcodes"); @@ -44,7 +44,7 @@ mod test { #[test] fn test_authcode_fail() { - let (srv, port, _, _) = mock::serve(); + let (srv, port, _, _) = serve(); let path = PathBuf::from("nonexist"); let connect = Rpc::connect(&format!("ws://127.0.0.1:{}", port), &path); @@ -58,7 +58,7 @@ mod test { #[test] fn test_authcode_correct() { - let (srv, port, tmpdir, _) = mock::serve(); + let (srv, port, tmpdir, _) = serve(); let mut path = PathBuf::from(tmpdir.path()); path.push("authcodes"); diff --git a/rpc_client/src/signer.rs b/rpc_client/src/signer.rs index 8ca4724a1..1fac588a5 100644 --- a/rpc_client/src/signer.rs +++ b/rpc_client/src/signer.rs @@ -1,7 +1,6 @@ use client::{Rpc, RpcError}; use rpc::v1::types::{ConfirmationRequest, - ConfirmationPayload, TransactionModification, U256}; use serde_json::{Value as JsonValue, to_value}; From 7a176094d5998d7c802c5ceee532a9106bf2b5a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristoffer=20Str=C3=B6m?= Date: Fri, 30 Sep 2016 15:30:17 +0200 Subject: [PATCH 03/25] * Use try!/map_err instead of match * Use jsonrpc_core for serializing/deserializing rpc messages * Factor out unwraps * Remove mem::replace * Add nicer formating of ConfirmationRequests --- parity/configuration.rs | 16 +-- rpc/src/v1/types/confirmations.rs | 8 +- rpc/src/v1/types/transaction_request.rs | 14 ++- rpc/src/v1/types/uint.rs | 19 ++++ rpc_cli/src/lib.rs | 123 ++++++++++++------------ rpc_client/Cargo.toml | 13 +-- rpc_client/src/client.rs | 72 ++++++++------ rpc_client/src/lib.rs | 21 ++-- rpc_client/src/signer.rs | 13 +-- 9 files changed, 162 insertions(+), 137 deletions(-) diff --git a/parity/configuration.rs b/parity/configuration.rs index 0d1c2c690..f43cf3098 100644 --- a/parity/configuration.rs +++ b/parity/configuration.rs @@ -43,6 +43,8 @@ use presale::ImportWallet; use account::{AccountCmd, NewAccount, ImportAccounts, ImportFromGethAccounts}; use snapshot::{self, SnapshotCommand}; +const AUTHCODE_FILENAME: &'static str = "authcodes"; + #[derive(Debug, PartialEq)] pub enum Cmd { Run(RunCmd), @@ -62,7 +64,7 @@ pub enum Cmd { authfile: PathBuf }, SignerReject { - id: usize, + id: Option, port: u16, authfile: PathBuf }, @@ -124,15 +126,14 @@ impl Configuration { ======= } else if self.args.cmd_signer { let mut authfile = PathBuf::from(signer_conf.signer_path); - authfile.push("authcodes"); + authfile.push(AUTHCODE_FILENAME); if self.args.cmd_new_token { Cmd::SignerToken(dirs.signer) } else if self.args.cmd_sign { - let pwfile = match self.args.flag_password.get(0) { - Some(pwfile) => Some(PathBuf::from(pwfile)), - None => None, - }; + let pwfile = self.args.flag_password.get(0).map(|pwfile| { + PathBuf::from(pwfile) + }); Cmd::SignerSign { id: self.args.arg_id, pwfile: pwfile, @@ -141,8 +142,7 @@ impl Configuration { } } else if self.args.cmd_reject { Cmd::SignerReject { - // id is a required field for this command - id: self.args.arg_id.unwrap(), + id: self.args.arg_id, port: signer_conf.port, authfile: authfile, } diff --git a/rpc/src/v1/types/confirmations.rs b/rpc/src/v1/types/confirmations.rs index 6ea01db64..0c27056d4 100644 --- a/rpc/src/v1/types/confirmations.rs +++ b/rpc/src/v1/types/confirmations.rs @@ -41,16 +41,16 @@ impl From for ConfirmationRequest { impl fmt::Display for ConfirmationRequest { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "id: {:?}, {}", self.id, self.payload) + write!(f, "#{}: {}", self.id, self.payload) } } impl fmt::Display for ConfirmationPayload { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - &ConfirmationPayload::Transaction(ref transaction) + match *self { + ConfirmationPayload::Transaction(ref transaction) => write!(f, "{}", transaction), - &ConfirmationPayload::Sign(_) => write!(f, "TODO: data"), + ConfirmationPayload::Sign(_) => write!(f, "TODO: data"), } } } diff --git a/rpc/src/v1/types/transaction_request.rs b/rpc/src/v1/types/transaction_request.rs index 5ee484a01..b71902043 100644 --- a/rpc/src/v1/types/transaction_request.rs +++ b/rpc/src/v1/types/transaction_request.rs @@ -44,10 +44,16 @@ pub struct TransactionRequest { impl fmt::Display for TransactionRequest { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{:?} from {:?} to {:?}", - self.value.unwrap_or(U256::from(0)), - self.from, - self.to) + let eth = self.value.unwrap_or(U256::from(0)); + match self.to { + Some(ref to) => write!(f, "{} Ether from {:?} to {:?}", + eth.format_ether(), + self.from, + to), + None => write!(f, "{} Ether from {:?}", + eth.format_ether(), + self.from), + } } } diff --git a/rpc/src/v1/types/uint.rs b/rpc/src/v1/types/uint.rs index e7363275e..b92498994 100644 --- a/rpc/src/v1/types/uint.rs +++ b/rpc/src/v1/types/uint.rs @@ -48,6 +48,25 @@ macro_rules! impl_uint { } } + impl $name { + /// Human readable formatting + pub fn format_ether(&self) -> String { + let divisor = $other::from(10u64.pow(18)); + let ether = self.0 / divisor; + let rest = self.0 - ether * divisor; + let string = format!("{}.{}", ether, rest); + string.trim_right_matches('0') + .trim_right_matches('.') + .to_string() + } + } + + impl fmt::Display for $name { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.0) + } + } + impl fmt::LowerHex for $name { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{:#x}", self.0) diff --git a/rpc_cli/src/lib.rs b/rpc_cli/src/lib.rs index dba4d681f..0fd90e333 100644 --- a/rpc_cli/src/lib.rs +++ b/rpc_cli/src/lib.rs @@ -15,7 +15,7 @@ use std::fs::File; use futures::Future; -fn sign_interactive(signer: &mut SignerRpc, pwd: &String, request: ConfirmationRequest) +fn sign_interactive(signer: &mut SignerRpc, password: &str, request: ConfirmationRequest) { print!("\n{}\nSign this transaction? (y)es/(N)o/(r)eject: ", request); let _ = stdout().flush(); @@ -23,7 +23,7 @@ fn sign_interactive(signer: &mut SignerRpc, pwd: &String, request: ConfirmationR Some(Ok(line)) => { match line.to_lowercase().chars().nth(0) { Some('y') => { - match sign_transaction(signer, request.id, pwd) { + match sign_transaction(signer, request.id, password) { Ok(s) | Err(s) => println!("{}", s), } } @@ -39,128 +39,129 @@ fn sign_interactive(signer: &mut SignerRpc, pwd: &String, request: ConfirmationR } } -fn sign_transactions(signer: &mut SignerRpc, pwd: String) -> Result { - signer.requests_to_confirm().map(|reqs| { +fn sign_transactions(signer: &mut SignerRpc, password: String) -> Result { + try!(signer.requests_to_confirm().map(|reqs| { match reqs { + Ok(ref reqs) if reqs.is_empty() => { + Ok("No transactions in signing queue".to_owned()) + } Ok(reqs) => { - if reqs.len() == 0 { - Ok("No transactions in signing queue".to_string()) - } else { - for r in reqs { - sign_interactive(signer, &pwd, r) - } - Ok("".to_string()) + for r in reqs { + sign_interactive(signer, &password, r) } + Ok("".to_owned()) } Err(err) => { Err(format!("error: {:?}", err)) } } - }).wait().unwrap() + }).map_err(|err| { + format!("{:?}", err) + }).wait()) } fn list_transactions(signer: &mut SignerRpc) -> Result { - signer.requests_to_confirm().map(|reqs| { + try!(signer.requests_to_confirm().map(|reqs| { match reqs { - Ok(reqs) => { - let mut s = "Transaction queue:".to_string(); - if reqs.len() == 0 { - s = s + &"No transactions in signing queue"; - } else { - for r in reqs { - s = s + &format!("\n{}", r); - } - } - Ok(s) + Ok(ref reqs) if reqs.is_empty() => { + Ok("No transactions in signing queue".to_owned()) + } + Ok(ref reqs) => { + Ok(format!("Transaction queue:\n{}", reqs + .iter() + .map(|r| format!("{}", r)) + .collect::>() + .join("\n"))) } Err(err) => { Err(format!("error: {:?}", err)) } } - }).wait().unwrap() + }).map_err(|err| { + format!("{:?}", err) + }).wait()) } fn sign_transaction(signer: &mut SignerRpc, id: U256, - pwd: &String) -> Result { - signer.confirm_request(id, None, &pwd).map(|res| { + password: &str) -> Result { + try!(signer.confirm_request(id, None, password).map(|res| { match res { Ok(u) => Ok(format!("Signed transaction id: {:#x}", u)), Err(e) => Err(format!("{:?}", e)), } - }).wait().unwrap() + }).map_err(|err| { + format!("{:?}", err) + }).wait()) } fn reject_transaction(signer: &mut SignerRpc, id: U256) -> Result { - signer.reject_request(id).map(|res| { + try!(signer.reject_request(id).map(|res| { match res { Ok(true) => Ok(format!("Rejected transaction id {:#x}", id)), Ok(false) => Err(format!("No such request")), Err(e) => Err(format!("{:?}", e)), } - }).wait().unwrap() + }).map_err(|err| { + format!("{:?}", err) + }).wait()) } // cmds pub fn cmd_signer_list(signerport: u16, authfile: PathBuf) -> Result { - match SignerRpc::new(&format!("ws://127.0.0.1:{}", signerport), - &authfile) { - Ok(mut signer) => { - list_transactions(&mut signer) - } - Err(e) => Err(format!("{:?}", e)) - } + let mut signer = try!(SignerRpc::new(&format!("ws://127.0.0.1:{}", signerport), &authfile).map_err(|err| { + format!("{:?}", err) + })); + list_transactions(&mut signer) } -pub fn cmd_signer_reject(id: usize, - signerport: u16, +pub fn cmd_signer_reject(id: Option, signerport: u16, authfile: PathBuf) -> Result { - match SignerRpc::new(&format!("ws://127.0.0.1:{}", signerport), - &authfile) { - Ok(mut signer) => { - reject_transaction(&mut signer, U256::from(id)) - }, - Err(e) => Err(format!("{:?}", e)) - } + let id = try!(id.ok_or(format!("id required for signer reject"))); + let mut signer = try!(SignerRpc::new(&format!("ws://127.0.0.1:{}", signerport), &authfile).map_err(|err| { + format!("{:?}", err) + })); + reject_transaction(&mut signer, U256::from(id)) } pub fn cmd_signer_sign(id: Option, pwfile: Option, signerport: u16, authfile: PathBuf) -> Result { - let pwd; + let password; match pwfile { Some(pwfile) => { match File::open(pwfile) { Ok(fd) => { match BufReader::new(fd).lines().next() { - Some(Ok(line)) => pwd = line, + Some(Ok(line)) => password = line, _ => return Err(format!("No password in file")) } }, - Err(e) => return Err(format!("Could not open pwfile: {}", e)) + Err(e) => return Err(format!("Could not open password file: {}", e)) } } None => { - pwd = rpassword::prompt_password_stdout("Password: ").unwrap(); + password = match rpassword::prompt_password_stdout("Password: ") { + Ok(p) => p, + Err(e) => return Err(format!("{}", e)), + } } } - match SignerRpc::new(&format!("ws://127.0.0.1:{}", signerport), - &authfile) { - Ok(mut signer) => { - match id { - Some(id) => { - sign_transaction(&mut signer, U256::from(id), &pwd) - }, - None => { - sign_transactions(&mut signer, pwd) - } - } + let mut signer = try!(SignerRpc::new(&format!("ws://127.0.0.1:{}", signerport), &authfile).map_err(|err| { + format!("{:?}", err) + })); + + match id { + Some(id) => { + sign_transaction(&mut signer, U256::from(id), &password) + }, + None => { + sign_transactions(&mut signer, password) } - Err(e) => return Err(format!("{:?}", e)) } } diff --git a/rpc_client/Cargo.toml b/rpc_client/Cargo.toml index 24f8b8522..1550ff4a3 100644 --- a/rpc_client/Cargo.toml +++ b/rpc_client/Cargo.toml @@ -8,6 +8,7 @@ version = "1.4.0" [dependencies] futures = "0.1" +jsonrpc-core = "3.0.2" lazy_static = "0.2.1" matches = "0.1.2" rand = "0.3.14" @@ -16,12 +17,6 @@ serde_json = "0.8" tempdir = "0.3.5" url = "1.2.0" ws = "0.5.3" - -[dependencies.ethcore-rpc] -path = "../rpc" - -[dependencies.ethcore-signer] -path = "../signer" - -[dependencies.ethcore-util] -path = "../util" +ethcore-rpc = { path = "../rpc" } +ethcore-signer = { path = "../signer" } +ethcore-util = { path = "../util" } diff --git a/rpc_client/src/client.rs b/rpc_client/src/client.rs index 35a724532..c3c6edb0c 100644 --- a/rpc_client/src/client.rs +++ b/rpc_client/src/client.rs @@ -4,7 +4,6 @@ use std::sync::{Arc, Mutex}; use std::sync::atomic::{AtomicUsize, Ordering}; use std::collections::BTreeMap; use std::thread; -use std::mem; use std::time; use std::path::PathBuf; @@ -12,7 +11,7 @@ use util::Hashable; use url::Url; use std::fs::File; -use ws::{connect, +use ws::{self, Request, Handler, Sender, @@ -25,12 +24,12 @@ use ws::{connect, use serde::Serialize; use serde::Deserialize; use serde::ser::Serializer; -use serde_json::{from_str, - to_string, - from_value, +use serde_json::{self as json, Value as JsonValue, Error as JsonError}; +//use jsonrpc_core:: + use futures::{BoxFuture, Canceled, Complete, Future, oneshot, done}; /// The actual websocket connection handler, passed into the @@ -65,9 +64,12 @@ impl Handler for RpcHandler { fn build_request(&mut self, url: &Url) -> WsResult { match Request::from_url(url) { Ok(mut r) => { - let timestamp = time::UNIX_EPOCH.elapsed().unwrap().as_secs(); - let hashed = format!("{}:{}", self.auth_code, timestamp).sha3(); - let proto = format!("{:?}_{}", hashed, timestamp); + let timestamp = try!(time::UNIX_EPOCH.elapsed().map_err(|err| { + WsError::new(WsErrorKind::Internal, format!("{}", err)) + })); + let secs = timestamp.as_secs(); + let hashed = format!("{}:{}", self.auth_code, secs).sha3(); + let proto = format!("{:?}_{}", hashed, secs); r.add_protocol(&proto); Ok(r) }, @@ -75,22 +77,25 @@ impl Handler for RpcHandler { } } fn on_error(&mut self, err: WsError) { - match mem::replace(&mut self.complete, None) { + match self.complete.take() { Some(c) => c.complete(Err(RpcError::WsError(err))), - None => println!("warning: unexpected error"), + None => println!("unexpected error: {}", err), } } fn on_open(&mut self, _: Handshake) -> WsResult<()> { - match mem::replace(&mut self.complete, None) { - Some(c) => c.complete(Ok(Rpc { - out: mem::replace(&mut self.out, None).unwrap(), - counter: AtomicUsize::new(0), - pending: self.pending.clone(), - })), - // Should not be reachable - None => (), + match (self.complete.take(), self.out.take()) { + (Some(c), Some(out)) => { + c.complete(Ok(Rpc { + out: out, + counter: AtomicUsize::new(0), + pending: self.pending.clone(), + })); + Ok(()) + }, + _ => { + Err(WsError::new(WsErrorKind::Internal, format!("on_open called twice"))) + } } - Ok(()) } fn on_message(&mut self, msg: Message) -> WsResult<()> { match parse_response(&msg.to_string()) { @@ -153,16 +158,25 @@ impl Rpc { Err(e) => return done(Ok(Err(e))).boxed(), Ok(code) => { let url = String::from(url); + // The ws::connect takes a FnMut closure, + // which means c cannot be moved into it, + // since it's consumed on complete. + // Therefore we wrap it in an option + // and pick it out once. + let mut once = Some(c); thread::spawn(move || { - // mem:replace Option hack to move `c` out - // of the FnMut closure - let mut swap = Some(c); - match connect(url, |out| { - let c = mem::replace(&mut swap, None).unwrap(); + let conn = ws::connect(url, |out| { + // this will panic if the closure + // is called twice, which it should never + // be. + let c = once.take().expect("connection closure called only once"); RpcHandler::new(out, code.clone(), c) - }) { + }); + match conn { Err(err) => { - let c = mem::replace(&mut swap, None).unwrap(); + // since ws::connect is only called once, it cannot + // both fail and succeed. + let c = once.take().expect("connection closure called only once"); c.complete(Err(RpcError::WsError(err))); }, // c will complete on the `on_open` event in the Handler @@ -183,13 +197,13 @@ impl Rpc { let id = self.counter.fetch_add(1, Ordering::Relaxed); self.pending.insert(id, c); - let serialized = to_string(&RpcRequest::new(id, method, params)).unwrap(); + let serialized = json::to_string(&RpcRequest::new(id, method, params)).unwrap(); let _ = self.out.send(serialized); p.map(|result| { match result { Ok(json) => { - let t: T = try!(from_value(json)); + let t: T = try!(json::from_value(json)); Ok(t) }, Err(err) => Err(err) @@ -281,7 +295,7 @@ impl From for RpcError { } fn parse_response(s: &str) -> (Option, Result) { - let mut json: JsonValue = match from_str(s) { + let mut json: JsonValue = match json::from_str(s) { Err(e) => return (None, Err(RpcError::ParseError(e))), Ok(json) => json, }; diff --git a/rpc_client/src/lib.rs b/rpc_client/src/lib.rs index eae26e57b..718dff6d2 100644 --- a/rpc_client/src/lib.rs +++ b/rpc_client/src/lib.rs @@ -12,6 +12,7 @@ extern crate serde; extern crate serde_json; extern crate rand; extern crate tempdir; +extern crate jsonrpc_core; #[macro_use] extern crate lazy_static; @@ -19,7 +20,7 @@ extern crate lazy_static; extern crate matches; #[cfg(test)] -mod test { +mod tests { use futures::Future; use std::path::PathBuf; @@ -29,46 +30,40 @@ mod test { #[test] fn test_connection_refused() { - let (srv, port, tmpdir, _) = serve(); + let (_srv, port, tmpdir, _) = serve(); let mut path = PathBuf::from(tmpdir.path()); path.push("authcodes"); let connect = Rpc::connect(&format!("ws://127.0.0.1:{}", port - 1), &path); - connect.map(|conn| { + let _ = connect.map(|conn| { assert!(matches!(&conn, &Err(RpcError::WsError(_)))); }).wait(); - - drop(srv); } #[test] fn test_authcode_fail() { - let (srv, port, _, _) = serve(); + let (_srv, port, _, _) = serve(); let path = PathBuf::from("nonexist"); let connect = Rpc::connect(&format!("ws://127.0.0.1:{}", port), &path); - connect.map(|conn| { + let _ = connect.map(|conn| { assert!(matches!(&conn, &Err(RpcError::NoAuthCode))); }).wait(); - - drop(srv); } #[test] fn test_authcode_correct() { - let (srv, port, tmpdir, _) = serve(); + let (_srv, port, tmpdir, _) = serve(); let mut path = PathBuf::from(tmpdir.path()); path.push("authcodes"); let connect = Rpc::connect(&format!("ws://127.0.0.1:{}", port), &path); - connect.map(|conn| { + let _ = connect.map(|conn| { assert!(conn.is_ok()) }).wait(); - - drop(srv); } } diff --git a/rpc_client/src/signer.rs b/rpc_client/src/signer.rs index 1fac588a5..3a135d32b 100644 --- a/rpc_client/src/signer.rs +++ b/rpc_client/src/signer.rs @@ -1,4 +1,3 @@ - use client::{Rpc, RpcError}; use rpc::v1::types::{ConfirmationRequest, TransactionModification, @@ -13,16 +12,12 @@ pub struct SignerRpc { impl SignerRpc { pub fn new(url: &str, authfile: &PathBuf) -> Result { - match Rpc::new(&url, authfile) { - Ok(rpc) => Ok(SignerRpc { rpc: rpc }), - Err(e) => Err(e), - } + Ok(SignerRpc { rpc: try!(Rpc::new(&url, authfile)) }) } pub fn requests_to_confirm(&mut self) -> BoxFuture, RpcError>, Canceled> { - self.rpc.request::> - ("personal_requestsToConfirm", vec![]) + self.rpc.request("personal_requestsToConfirm", vec![]) } pub fn confirm_request(&mut self, id: U256, @@ -30,7 +25,7 @@ impl SignerRpc { pwd: &str) -> BoxFuture, Canceled> { - self.rpc.request::("personal_confirmRequest", vec![ + self.rpc.request("personal_confirmRequest", vec![ to_value(&format!("{:#x}", id)), to_value(&TransactionModification { gas_price: new_gas_price }), to_value(&pwd), @@ -39,7 +34,7 @@ impl SignerRpc { pub fn reject_request(&mut self, id: U256) -> BoxFuture, Canceled> { - self.rpc.request::("personal_rejectRequest", vec![ + self.rpc.request("personal_rejectRequest", vec![ JsonValue::String(format!("{:#x}", id)) ]) } From 91b7780eb55f509d1bbcea63ca81c837162f1f44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristoffer=20Str=C3=B6m?= Date: Thu, 6 Oct 2016 12:07:09 +0200 Subject: [PATCH 04/25] comment formating --- rpc_client/src/client.rs | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/rpc_client/src/client.rs b/rpc_client/src/client.rs index c3c6edb0c..3b7d35447 100644 --- a/rpc_client/src/client.rs +++ b/rpc_client/src/client.rs @@ -158,17 +158,14 @@ impl Rpc { Err(e) => return done(Ok(Err(e))).boxed(), Ok(code) => { let url = String::from(url); - // The ws::connect takes a FnMut closure, - // which means c cannot be moved into it, - // since it's consumed on complete. - // Therefore we wrap it in an option - // and pick it out once. + // The ws::connect takes a FnMut closure, which means c cannot be + // moved into it, since it's consumed on complete. + // Therefore we wrap it in an option and pick it out once. let mut once = Some(c); thread::spawn(move || { let conn = ws::connect(url, |out| { - // this will panic if the closure - // is called twice, which it should never - // be. + // this will panic if the closure is called twice, + // which it should never be. let c = once.take().expect("connection closure called only once"); RpcHandler::new(out, code.clone(), c) }); From f700aa39fe9513f88d21ce0ad4663be6291e9c73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristoffer=20Str=C3=B6m?= Date: Mon, 17 Oct 2016 14:59:41 +0200 Subject: [PATCH 05/25] Switch to jsonrpc request/response types --- rpc_client/Cargo.toml | 13 +++- rpc_client/src/client.rs | 164 +++++++++++++-------------------------- rpc_client/src/lib.rs | 3 + 3 files changed, 68 insertions(+), 112 deletions(-) diff --git a/rpc_client/Cargo.toml b/rpc_client/Cargo.toml index 1550ff4a3..0f51fd050 100644 --- a/rpc_client/Cargo.toml +++ b/rpc_client/Cargo.toml @@ -10,6 +10,7 @@ version = "1.4.0" futures = "0.1" jsonrpc-core = "3.0.2" lazy_static = "0.2.1" +log = "0.3.6" matches = "0.1.2" rand = "0.3.14" serde = "0.8" @@ -17,6 +18,12 @@ serde_json = "0.8" tempdir = "0.3.5" url = "1.2.0" ws = "0.5.3" -ethcore-rpc = { path = "../rpc" } -ethcore-signer = { path = "../signer" } -ethcore-util = { path = "../util" } + +[dependencies.ethcore-rpc] +path = "../rpc" + +[dependencies.ethcore-signer] +path = "../signer" + +[dependencies.ethcore-util] +path = "../util" diff --git a/rpc_client/src/client.rs b/rpc_client/src/client.rs index 3b7d35447..04a0e03ae 100644 --- a/rpc_client/src/client.rs +++ b/rpc_client/src/client.rs @@ -1,3 +1,5 @@ +extern crate jsonrpc_core; + use std::fmt::{Debug, Formatter, Error as FmtError}; use std::io::{BufReader, BufRead}; use std::sync::{Arc, Mutex}; @@ -21,26 +23,23 @@ use ws::{self, Message, Result as WsResult}; -use serde::Serialize; use serde::Deserialize; -use serde::ser::Serializer; use serde_json::{self as json, Value as JsonValue, Error as JsonError}; -//use jsonrpc_core:: - use futures::{BoxFuture, Canceled, Complete, Future, oneshot, done}; +use jsonrpc_core::{Id, Version, Params, Error as JsonRpcError}; +use jsonrpc_core::request::MethodCall; +use jsonrpc_core::response::{SyncOutput, Success, Failure}; + /// The actual websocket connection handler, passed into the /// event loop of ws-rs struct RpcHandler { pending: Pending, - // Option is used here as - // temporary storage until - // connection is setup - // and the values are moved into - // the new `Rpc` + // Option is used here as temporary storage until connection + // is setup and the values are moved into the new `Rpc` complete: Option>>, auth_code: String, out: Option, @@ -98,14 +97,33 @@ impl Handler for RpcHandler { } } fn on_message(&mut self, msg: Message) -> WsResult<()> { - match parse_response(&msg.to_string()) { - (Some(id), response) => { - match self.pending.remove(id) { - Some(c) => c.complete(response), - None => println!("warning: unexpected id: {}", id), - } + let ret: Result; + let response_id; + let string = &msg.to_string(); + match json::from_str::(&string) { + Ok(SyncOutput::Success(Success { result, id: Id::Num(id), .. })) => { + ret = Ok(result); + response_id = id as usize; } - (None, response) => println!("warning: error: {:?}, {}", response, msg.to_string()), + Ok(SyncOutput::Failure(Failure { error, id: Id::Num(id), .. })) => { + ret = Err(error); + response_id = id as usize; + } + Err(e) => { + warn!(target: "rpc-client", "recieved invalid message: {}\n {:?}", string, e); + return Ok(()) + }, + _ => { + warn!(target: "rpc-client", "recieved invalid message: {}", string); + return Ok(()) + } + } + + match self.pending.remove(response_id) { + Some(c) => c.complete(ret.map_err(|err| { + RpcError::JsonRpc(err) + })), + None => warn!(target: "rpc-client", "warning: unexpected id: {}", response_id), } Ok(()) } @@ -120,10 +138,10 @@ impl Pending { Pending(Arc::new(Mutex::new(BTreeMap::new()))) } fn insert(&mut self, k: usize, v: Complete>) { - self.0.lock().unwrap().insert(k, v); + self.0.lock().expect("no panics in mutex guard").insert(k, v); } fn remove(&mut self, k: usize) -> Option>> { - self.0.lock().unwrap().remove(&k) + self.0.lock().expect("no panics in mutex guard").remove(&k) } } @@ -194,7 +212,14 @@ impl Rpc { let id = self.counter.fetch_add(1, Ordering::Relaxed); self.pending.insert(id, c); - let serialized = json::to_string(&RpcRequest::new(id, method, params)).unwrap(); + let request = MethodCall { + jsonrpc: Version::V2, + method: method.to_owned(), + params: Some(Params::Array(params)), + id: Id::Num(id as u64), + }; + + let serialized = json::to_string(&request).expect("request is serializable"); let _ = self.out.send(serialized); p.map(|result| { @@ -209,41 +234,11 @@ impl Rpc { } } - -struct RpcRequest { - method: &'static str, - params: Vec, - id: usize, -} - -impl RpcRequest { - fn new(id: usize, method: &'static str, params: Vec) -> Self { - RpcRequest { - method: method, - id: id, - params: params, - } - } -} - -impl Serialize for RpcRequest { - fn serialize(&self, s: &mut S) - -> Result<(), S::Error> - where S: Serializer { - let mut state = try!(s.serialize_struct("RpcRequest" , 3)); - try!(s.serialize_struct_elt(&mut state ,"jsonrpc", "2.0")); - try!(s.serialize_struct_elt(&mut state ,"id" , &self.id)); - try!(s.serialize_struct_elt(&mut state ,"method" , &self.method)); - try!(s.serialize_struct_elt(&mut state ,"params" , &self.params)); - s.serialize_struct_end(state) - } -} - pub enum RpcError { WrongVersion(String), ParseError(JsonError), MalformedResponse(String), - Remote(String), + JsonRpc(JsonRpcError), WsError(WsError), Canceled(Canceled), UnexpectedId, @@ -252,22 +247,22 @@ pub enum RpcError { impl Debug for RpcError { fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> { - match self { - &RpcError::WrongVersion(ref s) + match *self { + RpcError::WrongVersion(ref s) => write!(f, "Expected version 2.0, got {}", s), - &RpcError::ParseError(ref err) + RpcError::ParseError(ref err) => write!(f, "ParseError: {}", err), - &RpcError::MalformedResponse(ref s) + RpcError::MalformedResponse(ref s) => write!(f, "Malformed response: {}", s), - &RpcError::Remote(ref s) - => write!(f, "Remote error: {}", s), - &RpcError::WsError(ref s) + RpcError::JsonRpc(ref json) + => write!(f, "JsonRpc error: {:?}", json), + RpcError::WsError(ref s) => write!(f, "Websocket error: {}", s), - &RpcError::Canceled(ref s) + RpcError::Canceled(ref s) => write!(f, "Futures error: {:?}", s), - &RpcError::UnexpectedId + RpcError::UnexpectedId => write!(f, "Unexpected response id"), - &RpcError::NoAuthCode + RpcError::NoAuthCode => write!(f, "No authcodes available"), } } @@ -290,52 +285,3 @@ impl From for RpcError { RpcError::Canceled(err) } } - -fn parse_response(s: &str) -> (Option, Result) { - let mut json: JsonValue = match json::from_str(s) { - Err(e) => return (None, Err(RpcError::ParseError(e))), - Ok(json) => json, - }; - - let obj = match json.as_object_mut() { - Some(o) => o, - None => return - (None, - Err(RpcError::MalformedResponse("Not a JSON object".to_string()))), - }; - - let id; - match obj.get("id") { - Some(&JsonValue::U64(u)) => { - id = u as usize; - }, - _ => return (None, - Err(RpcError::MalformedResponse("Missing id".to_string()))), - } - - match obj.get("jsonrpc") { - Some(&JsonValue::String(ref s)) => { - if *s != "2.0".to_string() { - return (Some(id), - Err(RpcError::WrongVersion(s.clone()))) - } - }, - _ => return - (Some(id), - Err(RpcError::MalformedResponse("Not a jsonrpc object".to_string()))), - } - - match obj.get("error") { - Some(err) => return - (Some(id), - Err(RpcError::Remote(format!("{}", err)))), - None => (), - }; - - match obj.remove("result") { - None => (Some(id), - Err(RpcError::MalformedResponse("No result".to_string()))), - Some(result) => (Some(id), - Ok(result)), - } -} diff --git a/rpc_client/src/lib.rs b/rpc_client/src/lib.rs index 718dff6d2..2e2415047 100644 --- a/rpc_client/src/lib.rs +++ b/rpc_client/src/lib.rs @@ -19,6 +19,9 @@ extern crate lazy_static; #[macro_use] extern crate matches; +#[macro_use] +extern crate log; + #[cfg(test)] mod tests { use futures::Future; From b2b00e9dbe7eae34ad9533e1a703c02eafabc378 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristoffer=20Str=C3=B6m?= Date: Mon, 17 Oct 2016 15:06:45 +0200 Subject: [PATCH 06/25] Use Mutex from util --- rpc_client/src/client.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/rpc_client/src/client.rs b/rpc_client/src/client.rs index 04a0e03ae..72036c8c8 100644 --- a/rpc_client/src/client.rs +++ b/rpc_client/src/client.rs @@ -2,14 +2,14 @@ extern crate jsonrpc_core; use std::fmt::{Debug, Formatter, Error as FmtError}; use std::io::{BufReader, BufRead}; -use std::sync::{Arc, Mutex}; +use std::sync::Arc; use std::sync::atomic::{AtomicUsize, Ordering}; use std::collections::BTreeMap; use std::thread; use std::time; use std::path::PathBuf; -use util::Hashable; +use util::{Hashable, Mutex}; use url::Url; use std::fs::File; @@ -138,10 +138,10 @@ impl Pending { Pending(Arc::new(Mutex::new(BTreeMap::new()))) } fn insert(&mut self, k: usize, v: Complete>) { - self.0.lock().expect("no panics in mutex guard").insert(k, v); + self.0.lock().insert(k, v); } fn remove(&mut self, k: usize) -> Option>> { - self.0.lock().expect("no panics in mutex guard").remove(&k) + self.0.lock().remove(&k) } } From 68df68ca1d7522e528574d654f1208f4ae559a79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristoffer=20Str=C3=B6m?= Date: Mon, 17 Oct 2016 15:53:33 +0200 Subject: [PATCH 07/25] Use Signer mock from the signer crate --- rpc/src/v1/types/confirmations.rs | 3 +- rpc_client/src/lib.rs | 34 +++++++++++-------- rpc_client/src/mock.rs | 30 ---------------- .../src/{signer.rs => signer_client.rs} | 0 signer/src/lib.rs | 6 ++-- signer/src/tests/mod.rs | 20 ++++++----- 6 files changed, 35 insertions(+), 58 deletions(-) delete mode 100644 rpc_client/src/mock.rs rename rpc_client/src/{signer.rs => signer_client.rs} (100%) diff --git a/rpc/src/v1/types/confirmations.rs b/rpc/src/v1/types/confirmations.rs index 0c27056d4..fb4a23e57 100644 --- a/rpc/src/v1/types/confirmations.rs +++ b/rpc/src/v1/types/confirmations.rs @@ -51,6 +51,7 @@ impl fmt::Display for ConfirmationPayload { ConfirmationPayload::Transaction(ref transaction) => write!(f, "{}", transaction), ConfirmationPayload::Sign(_) => write!(f, "TODO: data"), + ConfirmationPayload::Decrypt(_) => write!(f, "TODO: decrypt"), } } } @@ -74,7 +75,7 @@ impl From<(H160, H256)> for SignRequest { } /// Decrypt request -#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize)] +#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)] pub struct DecryptRequest { /// Address pub address: H160, diff --git a/rpc_client/src/lib.rs b/rpc_client/src/lib.rs index 2e2415047..897a38d37 100644 --- a/rpc_client/src/lib.rs +++ b/rpc_client/src/lib.rs @@ -1,6 +1,5 @@ pub mod client; -pub mod signer; -mod mock; +pub mod signer_client; extern crate ws; extern crate ethcore_signer; @@ -26,18 +25,18 @@ extern crate log; mod tests { use futures::Future; use std::path::PathBuf; - use client::{Rpc, RpcError}; - - use mock::serve; + use ethcore_signer; #[test] fn test_connection_refused() { - let (_srv, port, tmpdir, _) = serve(); + let (_srv, port, mut authcodes) = ethcore_signer::tests::serve(); - let mut path = PathBuf::from(tmpdir.path()); - path.push("authcodes"); - let connect = Rpc::connect(&format!("ws://127.0.0.1:{}", port - 1), &path); + let _ = authcodes.generate_new(); + authcodes.to_file(&authcodes.path).unwrap(); + + let connect = Rpc::connect(&format!("ws://127.0.0.1:{}", port - 1), + authcodes.path.as_path()); let _ = connect.map(|conn| { assert!(matches!(&conn, &Err(RpcError::WsError(_)))); @@ -46,7 +45,7 @@ mod tests { #[test] fn test_authcode_fail() { - let (_srv, port, _, _) = serve(); + let (_srv, port, _) = ethcore_signer::tests::serve(); let path = PathBuf::from("nonexist"); let connect = Rpc::connect(&format!("ws://127.0.0.1:{}", port), &path); @@ -58,14 +57,19 @@ mod tests { #[test] fn test_authcode_correct() { - let (_srv, port, tmpdir, _) = serve(); + let (_srv, port, mut authcodes) = ethcore_signer::tests::serve(); - let mut path = PathBuf::from(tmpdir.path()); - path.push("authcodes"); - let connect = Rpc::connect(&format!("ws://127.0.0.1:{}", port), &path); + let _ = authcodes.generate_new(); + authcodes.to_file(&authcodes.path).unwrap(); + + let connect = Rpc::connect(&format!("ws://127.0.0.1:{}", port), + authcodes.path.as_path()); let _ = connect.map(|conn| { - assert!(conn.is_ok()) + if let Err(e) = conn { + println!("debug: {:?}", e); + }; + // assert!(conn.is_ok()) }).wait(); } diff --git a/rpc_client/src/mock.rs b/rpc_client/src/mock.rs deleted file mode 100644 index 9ac52f605..000000000 --- a/rpc_client/src/mock.rs +++ /dev/null @@ -1,30 +0,0 @@ -use ethcore_signer::ServerBuilder; -use ethcore_signer::Server; -use rpc::ConfirmationsQueue; -use std::sync::Arc; -use std::time::{Duration}; -use std::thread; -use rand; -use tempdir::TempDir; -use std::path::PathBuf; -use std::fs::{File, create_dir_all}; -use std::io::Write; - -// mock server -pub fn serve() -> (Server, usize, TempDir, Arc) { - let queue = Arc::new(ConfirmationsQueue::default()); - let dir = TempDir::new("auth").unwrap(); - - let mut authpath = PathBuf::from(dir.path()); - create_dir_all(&authpath).unwrap(); - authpath.push("authcodes"); - let mut authfile = File::create(&authpath).unwrap(); - authfile.write_all(b"zzzRo0IzGi04mzzz\n").unwrap(); - - let builder = ServerBuilder::new(queue.clone(), authpath); - let port = 35000 + rand::random::() % 10000; - let res = builder.start(format!("127.0.0.1:{}", port).parse().unwrap()).unwrap(); - - thread::sleep(Duration::from_millis(25)); - (res, port, dir, queue) -} diff --git a/rpc_client/src/signer.rs b/rpc_client/src/signer_client.rs similarity index 100% rename from rpc_client/src/signer.rs rename to rpc_client/src/signer_client.rs diff --git a/signer/src/lib.rs b/signer/src/lib.rs index f05d8c625..b805cfb92 100644 --- a/signer/src/lib.rs +++ b/signer/src/lib.rs @@ -52,13 +52,13 @@ extern crate ethcore_io as io; extern crate ethcore_rpc as rpc; extern crate jsonrpc_core; extern crate ws; -#[cfg(test)] + extern crate ethcore_devtools as devtools; mod authcode_store; mod ws_server; -#[cfg(test)] -mod tests; +/// Exported tests for use in signer RPC client testing +pub mod tests; pub use authcode_store::*; pub use ws_server::*; diff --git a/signer/src/tests/mod.rs b/signer/src/tests/mod.rs index e80b5778d..0a3cf51c9 100644 --- a/signer/src/tests/mod.rs +++ b/signer/src/tests/mod.rs @@ -15,20 +15,26 @@ // along with Parity. If not, see . use std::ops::{Deref, DerefMut}; -use std::time; +use std::thread; +use std::time::{self, Duration}; use std::sync::Arc; -use devtools::{http_client, RandomTempPath}; + +#[cfg(test)] +use devtools::http_client; +use devtools::RandomTempPath; + use rpc::ConfirmationsQueue; -use util::Hashable; use rand; use ServerBuilder; use Server; use AuthCodes; +/// Struct representing authcodes pub struct GuardedAuthCodes { authcodes: AuthCodes, - path: RandomTempPath, + /// The path to the mock authcodes + pub path: RandomTempPath, } impl Deref for GuardedAuthCodes { type Target = AuthCodes; @@ -42,6 +48,7 @@ impl DerefMut for GuardedAuthCodes { } } +/// Setup a mock signer for testsp pub fn serve() -> (Server, usize, GuardedAuthCodes) { let mut path = RandomTempPath::new(); path.panic_on_drop_failure = false; @@ -56,10 +63,6 @@ pub fn serve() -> (Server, usize, GuardedAuthCodes) { }) } -pub fn request(server: Server, request: &str) -> http_client::Response { - http_client::request(server.addr(), request) -} - #[test] fn should_reject_invalid_host() { // given @@ -246,4 +249,3 @@ fn should_allow_initial_connection_but_only_once() { assert_eq!(response2.status, "HTTP/1.1 403 FORBIDDEN".to_owned()); http_client::assert_security_headers_present(&response2.headers, None); } - From e202b9c79a1826b3444d06011da8be49702036af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristoffer=20Str=C3=B6m?= Date: Mon, 17 Oct 2016 16:42:57 +0200 Subject: [PATCH 08/25] Fix Cargo.toml formating --- rpc_client/Cargo.toml | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/rpc_client/Cargo.toml b/rpc_client/Cargo.toml index 0f51fd050..e8477a802 100644 --- a/rpc_client/Cargo.toml +++ b/rpc_client/Cargo.toml @@ -18,12 +18,6 @@ serde_json = "0.8" tempdir = "0.3.5" url = "1.2.0" ws = "0.5.3" - -[dependencies.ethcore-rpc] -path = "../rpc" - -[dependencies.ethcore-signer] -path = "../signer" - -[dependencies.ethcore-util] -path = "../util" +ethcore-rpc = { path = "../rpc" } +ethcore-signer = { path = "../signer" } +ethcore-util = { path = "../util" } From a7e10cebbd0049579a37d0982a4c06012a760eca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristoffer=20Str=C3=B6m?= Date: Tue, 18 Oct 2016 16:10:06 +0200 Subject: [PATCH 09/25] Remove forgotten test debug --- rpc_client/src/lib.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/rpc_client/src/lib.rs b/rpc_client/src/lib.rs index 897a38d37..3164480e5 100644 --- a/rpc_client/src/lib.rs +++ b/rpc_client/src/lib.rs @@ -66,10 +66,7 @@ mod tests { authcodes.path.as_path()); let _ = connect.map(|conn| { - if let Err(e) = conn { - println!("debug: {:?}", e); - }; - // assert!(conn.is_ok()) + assert!(conn.is_ok()) }).wait(); } From 1d0ccb1c30364927d2df82eebdefc9bfefccbcd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristoffer=20Str=C3=B6m?= Date: Tue, 18 Oct 2016 19:13:43 +0200 Subject: [PATCH 10/25] Move format_ether into transaction_request.rs --- rpc/src/v1/types/transaction_request.rs | 37 +++++++++++++++++++++---- rpc/src/v1/types/uint.rs | 13 --------- rpc_cli/src/lib.rs | 2 +- 3 files changed, 33 insertions(+), 19 deletions(-) diff --git a/rpc/src/v1/types/transaction_request.rs b/rpc/src/v1/types/transaction_request.rs index b71902043..492f2354d 100644 --- a/rpc/src/v1/types/transaction_request.rs +++ b/rpc/src/v1/types/transaction_request.rs @@ -42,17 +42,33 @@ pub struct TransactionRequest { pub nonce: Option, } +pub fn format_ether(i: U256) -> String { + let mut string = format!("{}", i); + let idx = string.len() as isize - 18; + if idx <= 0 { + let mut prefix = String::from("0."); + for _ in 0..idx.abs() { + prefix.push('0'); + } + string = prefix + &string; + } else { + string.insert(idx as usize, '.'); + } + String::from(string.trim_right_matches('0') + .trim_right_matches('.')) +} + impl fmt::Display for TransactionRequest { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let eth = self.value.unwrap_or(U256::from(0)); match self.to { Some(ref to) => write!(f, "{} Ether from {:?} to {:?}", - eth.format_ether(), - self.from, - to), + format_ether(eth), + self.from, + to), None => write!(f, "{} Ether from {:?}", - eth.format_ether(), - self.from), + format_ether(eth), + self.from), } } } @@ -208,4 +224,15 @@ mod tests { assert!(deserialized.is_err(), "Should be error because to is empty"); } + + #[test] + fn test_format_ether() { + assert_eq!(&format_ether(U256::from(1000000000000000000u64)), "1"); + assert_eq!(&format_ether(U256::from(500000000000000000u64)), "0.5"); + assert_eq!(&format_ether(U256::from(50000000000000000u64)), "0.05"); + assert_eq!(&format_ether(U256::from(5000000000000000u64)), "0.005"); + assert_eq!(&format_ether(U256::from(2000000000000000000u64)), "2"); + assert_eq!(&format_ether(U256::from(2500000000000000000u64)), "2.5"); + assert_eq!(&format_ether(U256::from(10000000000000000000u64)), "10"); + } } diff --git a/rpc/src/v1/types/uint.rs b/rpc/src/v1/types/uint.rs index b92498994..60aae8f1d 100644 --- a/rpc/src/v1/types/uint.rs +++ b/rpc/src/v1/types/uint.rs @@ -48,19 +48,6 @@ macro_rules! impl_uint { } } - impl $name { - /// Human readable formatting - pub fn format_ether(&self) -> String { - let divisor = $other::from(10u64.pow(18)); - let ether = self.0 / divisor; - let rest = self.0 - ether * divisor; - let string = format!("{}.{}", ether, rest); - string.trim_right_matches('0') - .trim_right_matches('.') - .to_string() - } - } - impl fmt::Display for $name { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.0) diff --git a/rpc_cli/src/lib.rs b/rpc_cli/src/lib.rs index 0fd90e333..7461e8abd 100644 --- a/rpc_cli/src/lib.rs +++ b/rpc_cli/src/lib.rs @@ -8,7 +8,7 @@ extern crate rpassword; extern crate parity_rpc_client as client; use rpc::v1::types::{U256, ConfirmationRequest}; -use client::signer::SignerRpc; +use client::signer_client::SignerRpc; use std::io::{Write, BufRead, BufReader, stdout, stdin}; use std::path::PathBuf; use std::fs::File; From 65523190742f25cbb488e72edb66c5945c10b26e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristoffer=20Str=C3=B6m?= Date: Wed, 2 Nov 2016 15:55:03 +0100 Subject: [PATCH 11/25] Style rewrite --- rpc_cli/src/lib.rs | 47 +++++++++++++++++++++------------ rpc_client/src/client.rs | 19 +++++++------ rpc_client/src/signer_client.rs | 11 ++++---- 3 files changed, 47 insertions(+), 30 deletions(-) diff --git a/rpc_cli/src/lib.rs b/rpc_cli/src/lib.rs index 7461e8abd..b30c134ef 100644 --- a/rpc_cli/src/lib.rs +++ b/rpc_cli/src/lib.rs @@ -15,7 +15,10 @@ use std::fs::File; use futures::Future; -fn sign_interactive(signer: &mut SignerRpc, password: &str, request: ConfirmationRequest) +fn sign_interactive( + signer: &mut SignerRpc, + password: &str, + request: ConfirmationRequest) { print!("\n{}\nSign this transaction? (y)es/(N)o/(r)eject: ", request); let _ = stdout().flush(); @@ -39,7 +42,9 @@ fn sign_interactive(signer: &mut SignerRpc, password: &str, request: Confirmatio } } -fn sign_transactions(signer: &mut SignerRpc, password: String) -> Result { +fn sign_transactions( + signer: &mut SignerRpc, password: String +) -> Result { try!(signer.requests_to_confirm().map(|reqs| { match reqs { Ok(ref reqs) if reqs.is_empty() => { @@ -82,9 +87,9 @@ fn list_transactions(signer: &mut SignerRpc) -> Result { }).wait()) } -fn sign_transaction(signer: &mut SignerRpc, - id: U256, - password: &str) -> Result { +fn sign_transaction( + signer: &mut SignerRpc, id: U256, password: &str +) -> Result { try!(signer.confirm_request(id, None, password).map(|res| { match res { Ok(u) => Ok(format!("Signed transaction id: {:#x}", u)), @@ -95,8 +100,9 @@ fn sign_transaction(signer: &mut SignerRpc, }).wait()) } -fn reject_transaction(signer: &mut SignerRpc, - id: U256) -> Result { +fn reject_transaction( + signer: &mut SignerRpc, id: U256) -> Result +{ try!(signer.reject_request(id).map(|res| { match res { Ok(true) => Ok(format!("Rejected transaction id {:#x}", id)), @@ -110,27 +116,34 @@ fn reject_transaction(signer: &mut SignerRpc, // cmds -pub fn cmd_signer_list(signerport: u16, - authfile: PathBuf) -> Result { - let mut signer = try!(SignerRpc::new(&format!("ws://127.0.0.1:{}", signerport), &authfile).map_err(|err| { +pub fn cmd_signer_list( + signerport: u16, authfile: PathBuf +) -> Result { + let addr = &format!("ws://127.0.0.1:{}", signerport); + let mut signer = try!(SignerRpc::new(addr, &authfile)); + signer.map_err(|err| { format!("{:?}", err) })); list_transactions(&mut signer) } -pub fn cmd_signer_reject(id: Option, signerport: u16, - authfile: PathBuf) -> Result { +pub fn cmd_signer_reject( + id: Option, signerport: u16, authfile: PathBuf +) -> Result { let id = try!(id.ok_or(format!("id required for signer reject"))); - let mut signer = try!(SignerRpc::new(&format!("ws://127.0.0.1:{}", signerport), &authfile).map_err(|err| { + let addr = &format!("ws://127.0.0.1:{}", signerport); + let mut signer = try!(SignerRpc::new(addr, &authfile).map_err(|err| { format!("{:?}", err) })); reject_transaction(&mut signer, U256::from(id)) } -pub fn cmd_signer_sign(id: Option, - pwfile: Option, - signerport: u16, - authfile: PathBuf) -> Result { +pub fn cmd_signer_sign( + id: Option, + pwfile: Option, + signerport: u16, + authfile: PathBuf +) -> Result { let password; match pwfile { Some(pwfile) => { diff --git a/rpc_client/src/client.rs b/rpc_client/src/client.rs index 72036c8c8..dddea729f 100644 --- a/rpc_client/src/client.rs +++ b/rpc_client/src/client.rs @@ -46,10 +46,11 @@ struct RpcHandler { } impl RpcHandler { - fn new(out: Sender, - auth_code: String, - complete: Complete>) - -> Self { + fn new( + out: Sender, + auth_code: String, + complete: Complete> + ) -> Self { RpcHandler { out: Some(out), auth_code: auth_code, @@ -169,8 +170,9 @@ impl Rpc { rpc } /// Non-blocking, returns a future - pub fn connect(url: &str, authpath: &PathBuf) - -> BoxFuture, Canceled> { + pub fn connect( + url: &str, authpath: &PathBuf + ) -> BoxFuture, Canceled> { let (c, p) = oneshot::>(); match get_authcode(authpath) { Err(e) => return done(Ok(Err(e))).boxed(), @@ -203,8 +205,9 @@ impl Rpc { } } /// Non-blocking, returns a future of the request response - pub fn request(&mut self, method: &'static str, params: Vec) - -> BoxFuture, Canceled> + pub fn request( + &mut self, method: &'static str, params: Vec + ) -> BoxFuture, Canceled> where T: Deserialize + Send + Sized { let (c, p) = oneshot::>(); diff --git a/rpc_client/src/signer_client.rs b/rpc_client/src/signer_client.rs index 3a135d32b..587fa42b7 100644 --- a/rpc_client/src/signer_client.rs +++ b/rpc_client/src/signer_client.rs @@ -19,11 +19,12 @@ impl SignerRpc { { self.rpc.request("personal_requestsToConfirm", vec![]) } - pub fn confirm_request(&mut self, - id: U256, - new_gas_price: Option, - pwd: &str) -> - BoxFuture, Canceled> + pub fn confirm_request( + &mut self, + id: U256, + new_gas_price: Option, + pwd: &str + ) -> BoxFuture, Canceled> { self.rpc.request("personal_confirmRequest", vec![ to_value(&format!("{:#x}", id)), From 0e516162c37cc7fe399bbd6222b5179ba550eea9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristoffer=20Str=C3=B6m?= Date: Wed, 2 Nov 2016 17:14:05 +0100 Subject: [PATCH 12/25] Rebased on master --- Cargo.lock | 78 +++++++++++++++++++++++++++++++ Cargo.toml | 2 + parity/configuration.rs | 9 +--- rpc/src/v1/types/confirmations.rs | 5 +- rpc_cli/src/lib.rs | 16 ++++--- rpc_client/src/client.rs | 70 ++++++++++++++++++--------- 6 files changed, 142 insertions(+), 38 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 36be7cbc8..2c9af0621 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -31,9 +31,11 @@ dependencies = [ "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "number_prefix 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-rpc-client 1.4.0", "regex 0.1.68 (registry+https://github.com/rust-lang/crates.io-index)", "rlp 0.1.0", "rpassword 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rpc-cli 1.4.0", "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)", "semver 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -711,6 +713,14 @@ dependencies = [ "miniz-sys 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "futures" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "gcc" version = "0.3.35" @@ -1254,6 +1264,26 @@ dependencies = [ "syntex_syntax 0.33.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "parity-rpc-client" +version = "1.4.0" +dependencies = [ + "ethcore-rpc 1.5.0", + "ethcore-signer 1.5.0", + "ethcore-util 1.5.0", + "futures 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpc-core 3.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "matches 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "url 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ws 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "parity-ui" version = "1.5.0" @@ -1533,6 +1563,29 @@ dependencies = [ "winapi 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "rpassword" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)", + "termios 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rpc-cli" +version = "1.4.0" +dependencies = [ + "ethcore-bigint 0.1.2", + "ethcore-rpc 1.5.0", + "ethcore-util 1.5.0", + "futures 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-rpc-client 1.4.0", + "rpassword 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "rust-crypto" version = "0.2.36" @@ -1763,6 +1816,14 @@ name = "target_info" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "tempdir" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "term" version = "0.2.14" @@ -1963,6 +2024,19 @@ dependencies = [ "url 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "ws" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "httparse 1.1.2 (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)", + "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", + "sha1 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "url 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "ws2_32-sys" version = "0.2.1" @@ -2032,6 +2106,7 @@ dependencies = [ "checksum ethabi 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f7b0c53453517f620847be51943db329276ae52f2e210cfc659e81182864be2f" "checksum fdlimit 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b1ee15a7050e5580b3712877157068ea713b245b080ff302ae2ca973cfcd9baa" "checksum flate2 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "3eeb481e957304178d2e782f2da1257f1434dfecbae883bafb61ada2a9fea3bb" +"checksum futures 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dd89497091f8c5d3a65c6b4baf6d2f0731937a7c9217d2f89141b21437a9d96" "checksum gcc 0.3.35 (registry+https://github.com/rust-lang/crates.io-index)" = "91ecd03771effb0c968fd6950b37e89476a578aaf1c70297d8e92b6516ec3312" "checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb" "checksum hamming 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "65043da274378d68241eb9a8f8f8aa54e349136f7b8e12f63e3ef44043cc30e1" @@ -2118,6 +2193,7 @@ dependencies = [ "checksum rocksdb-sys 0.3.0 (git+https://github.com/ethcore/rust-rocksdb)" = "" "checksum rotor 0.6.3 (git+https://github.com/ethcore/rotor)" = "" "checksum rpassword 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5d3a99497c5c544e629cc8b359ae5ede321eba5fa8e5a8078f3ced727a976c3f" +"checksum rpassword 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ab6e42be826e215f30ff830904f8f4a0933c6e2ae890e1af8b408f5bae60081e" "checksum rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)" = "f76d05d3993fd5f4af9434e8e436db163a12a9d40e1a58a726f27a01dfd12a2a" "checksum rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)" = "6159e4e6e559c81bd706afe9c8fd68f547d3e851ce12e76b1de7914bab61691b" "checksum rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "c5f5376ea5e30ce23c03eb77cbe4962b988deead10910c372b226388b594c084" @@ -2146,6 +2222,7 @@ dependencies = [ "checksum syntex_syntax 0.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "44bded3cabafc65c90b663b1071bd2d198a9ab7515e6ce729e4570aaf53c407e" "checksum syntex_syntax 0.42.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7628a0506e8f9666fdabb5f265d0059b059edac9a3f810bda077abb5d826bd8d" "checksum target_info 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c63f48baada5c52e65a29eef93ab4f8982681b67f9e8d29c7b05abcfec2b9ffe" +"checksum tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "87974a6f5c1dfb344d733055601650059a3363de2a6104819293baff662132d6" "checksum term 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "f2077e54d38055cf1ca0fd7933a2e00cd3ec8f6fed352b2a377f06dcdaaf3281" "checksum term 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "3deff8a2b3b6607d6d7cc32ac25c0b33709453ca9cceac006caac51e963cf94a" "checksum termios 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d5d9cf598a6d7ce700a4e6a9199da127e6819a61e64b68609683cc9a01b5683a" @@ -2172,6 +2249,7 @@ dependencies = [ "checksum winapi 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "4dfaaa8fbdaa618fa6914b59b2769d690dd7521920a18d84b42d254678dd5fd4" "checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" "checksum ws 0.5.3 (git+https://github.com/ethcore/ws-rs.git?branch=mio-upstream-stable)" = "" +"checksum ws 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7c47e9ca2f5c47d27f731b1bb9bb50cc05f9886bb84fbd52afa0ff97f4f61b06" "checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" "checksum xml-rs 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "65e74b96bd3179209dc70a980da6df843dff09e46eee103a0376c0949257e3ef" "checksum xmltree 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "472a9d37c7c53ab2391161df5b89b1f3bf76dab6ab150d7941ecbdd832282082" diff --git a/Cargo.toml b/Cargo.toml index 65bb0dbc6..85e55ed21 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,6 +47,8 @@ rlp = { path = "util/rlp" } ethcore-stratum = { path = "stratum" } ethcore-dapps = { path = "dapps", optional = true } clippy = { version = "0.0.103", optional = true} +rpc-cli = { path = "rpc_cli" } +parity-rpc-client = { path = "rpc_client" } [target.'cfg(windows)'.dependencies] winapi = "0.2" diff --git a/parity/configuration.rs b/parity/configuration.rs index f43cf3098..b72655daa 100644 --- a/parity/configuration.rs +++ b/parity/configuration.rs @@ -120,16 +120,12 @@ impl Configuration { let cmd = if self.args.flag_version { Cmd::Version -<<<<<<< HEAD - } else if self.args.cmd_signer && self.args.cmd_new_token { - Cmd::SignerToken(signer_conf) -======= } else if self.args.cmd_signer { - let mut authfile = PathBuf::from(signer_conf.signer_path); + let mut authfile = PathBuf::from(signer_conf.signer_path.clone()); authfile.push(AUTHCODE_FILENAME); if self.args.cmd_new_token { - Cmd::SignerToken(dirs.signer) + Cmd::SignerToken(signer_conf) } else if self.args.cmd_sign { let pwfile = self.args.flag_password.get(0).map(|pwfile| { PathBuf::from(pwfile) @@ -154,7 +150,6 @@ impl Configuration { } else { unreachable!(); } ->>>>>>> Add Ws Json rpc client and command line utils } else if self.args.cmd_tools && self.args.cmd_hash { Cmd::Hash(self.args.arg_file) } else if self.args.cmd_account { diff --git a/rpc/src/v1/types/confirmations.rs b/rpc/src/v1/types/confirmations.rs index fb4a23e57..582763914 100644 --- a/rpc/src/v1/types/confirmations.rs +++ b/rpc/src/v1/types/confirmations.rs @@ -48,10 +48,11 @@ impl fmt::Display for ConfirmationRequest { impl fmt::Display for ConfirmationPayload { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { - ConfirmationPayload::Transaction(ref transaction) + ConfirmationPayload::SendTransaction(ref transaction) => write!(f, "{}", transaction), - ConfirmationPayload::Sign(_) => write!(f, "TODO: data"), + ConfirmationPayload::SignTransaction(_) => write!(f, "TODO: data"), ConfirmationPayload::Decrypt(_) => write!(f, "TODO: decrypt"), + ConfirmationPayload::Signature(_) => write!(f, "TODO: signature"), } } } diff --git a/rpc_cli/src/lib.rs b/rpc_cli/src/lib.rs index b30c134ef..47f2947ee 100644 --- a/rpc_cli/src/lib.rs +++ b/rpc_cli/src/lib.rs @@ -18,8 +18,8 @@ use futures::Future; fn sign_interactive( signer: &mut SignerRpc, password: &str, - request: ConfirmationRequest) -{ + request: ConfirmationRequest +) { print!("\n{}\nSign this transaction? (y)es/(N)o/(r)eject: ", request); let _ = stdout().flush(); match BufReader::new(stdin()).lines().next() { @@ -43,7 +43,8 @@ fn sign_interactive( } fn sign_transactions( - signer: &mut SignerRpc, password: String + signer: &mut SignerRpc, + password: String ) -> Result { try!(signer.requests_to_confirm().map(|reqs| { match reqs { @@ -120,8 +121,7 @@ pub fn cmd_signer_list( signerport: u16, authfile: PathBuf ) -> Result { let addr = &format!("ws://127.0.0.1:{}", signerport); - let mut signer = try!(SignerRpc::new(addr, &authfile)); - signer.map_err(|err| { + let mut signer = try!(SignerRpc::new(addr, &authfile).map_err(|err| { format!("{:?}", err) })); list_transactions(&mut signer) @@ -154,7 +154,8 @@ pub fn cmd_signer_sign( _ => return Err(format!("No password in file")) } }, - Err(e) => return Err(format!("Could not open password file: {}", e)) + Err(e) => + return Err(format!("Could not open password file: {}", e)) } } None => { @@ -165,7 +166,8 @@ pub fn cmd_signer_sign( } } - let mut signer = try!(SignerRpc::new(&format!("ws://127.0.0.1:{}", signerport), &authfile).map_err(|err| { + let addr = &format!("ws://127.0.0.1:{}", signerport); + let mut signer = try!(SignerRpc::new(addr, &authfile).map_err(|err| { format!("{:?}", err) })); diff --git a/rpc_client/src/client.rs b/rpc_client/src/client.rs index dddea729f..84e9cedb9 100644 --- a/rpc_client/src/client.rs +++ b/rpc_client/src/client.rs @@ -13,15 +13,17 @@ use util::{Hashable, Mutex}; use url::Url; use std::fs::File; -use ws::{self, - Request, - Handler, - Sender, - Handshake, - Error as WsError, - ErrorKind as WsErrorKind, - Message, - Result as WsResult}; +use ws::{ + self, + Request, + Handler, + Sender, + Handshake, + Error as WsError, + ErrorKind as WsErrorKind, + Message, + Result as WsResult +}; use serde::Deserialize; use serde_json::{self as json, @@ -73,7 +75,8 @@ impl Handler for RpcHandler { r.add_protocol(&proto); Ok(r) }, - Err(e) => Err(WsError::new(WsErrorKind::Internal, format!("{}", e))), + Err(e) => + Err(WsError::new(WsErrorKind::Internal, format!("{}", e))), } } fn on_error(&mut self, err: WsError) { @@ -93,7 +96,8 @@ impl Handler for RpcHandler { Ok(()) }, _ => { - Err(WsError::new(WsErrorKind::Internal, format!("on_open called twice"))) + let msg = format!("on_open called twice"); + Err(WsError::new(WsErrorKind::Internal, msg)) } } } @@ -102,7 +106,8 @@ impl Handler for RpcHandler { let response_id; let string = &msg.to_string(); match json::from_str::(&string) { - Ok(SyncOutput::Success(Success { result, id: Id::Num(id), .. })) => { + Ok(SyncOutput::Success(Success { result, id: Id::Num(id), .. })) => + { ret = Ok(result); response_id = id as usize; } @@ -111,11 +116,20 @@ impl Handler for RpcHandler { response_id = id as usize; } Err(e) => { - warn!(target: "rpc-client", "recieved invalid message: {}\n {:?}", string, e); + warn!( + target: "rpc-client", + "recieved invalid message: {}\n {:?}", + string, + e + ); return Ok(()) }, _ => { - warn!(target: "rpc-client", "recieved invalid message: {}", string); + warn!( + target: "rpc-client", + "recieved invalid message: {}", + string + ); return Ok(()) } } @@ -124,7 +138,11 @@ impl Handler for RpcHandler { Some(c) => c.complete(ret.map_err(|err| { RpcError::JsonRpc(err) })), - None => warn!(target: "rpc-client", "warning: unexpected id: {}", response_id), + None => warn!( + target: "rpc-client", + "warning: unexpected id: {}", + response_id + ), } Ok(()) } @@ -132,7 +150,9 @@ impl Handler for RpcHandler { /// Keeping track of issued requests to be matched up with responses #[derive(Clone)] -struct Pending(Arc>>>>); +struct Pending( + Arc>>>> +); impl Pending { fn new() -> Self { @@ -141,7 +161,10 @@ impl Pending { fn insert(&mut self, k: usize, v: Complete>) { self.0.lock().insert(k, v); } - fn remove(&mut self, k: usize) -> Option>> { + fn remove( + &mut self, + k: usize + ) -> Option>> { self.0.lock().remove(&k) } } @@ -178,22 +201,24 @@ impl Rpc { Err(e) => return done(Ok(Err(e))).boxed(), Ok(code) => { let url = String::from(url); - // The ws::connect takes a FnMut closure, which means c cannot be - // moved into it, since it's consumed on complete. + // The ws::connect takes a FnMut closure, which means c cannot + // be moved into it, since it's consumed on complete. // Therefore we wrap it in an option and pick it out once. let mut once = Some(c); thread::spawn(move || { let conn = ws::connect(url, |out| { // this will panic if the closure is called twice, // which it should never be. - let c = once.take().expect("connection closure called only once"); + let c = once.take() + .expect("connection closure called only once"); RpcHandler::new(out, code.clone(), c) }); match conn { Err(err) => { // since ws::connect is only called once, it cannot // both fail and succeed. - let c = once.take().expect("connection closure called only once"); + let c = once.take() + .expect("connection closure called only once"); c.complete(Err(RpcError::WsError(err))); }, // c will complete on the `on_open` event in the Handler @@ -222,7 +247,8 @@ impl Rpc { id: Id::Num(id as u64), }; - let serialized = json::to_string(&request).expect("request is serializable"); + let serialized = json::to_string(&request) + .expect("request is serializable"); let _ = self.out.send(serialized); p.map(|result| { From 4696d7f60656a83ee5f479f10e586688f19c63d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Sat, 10 Dec 2016 12:34:20 +0100 Subject: [PATCH 13/25] Additional RPCs for dapps accounts management --- ethcore/src/account_provider/mod.rs | 58 +++++++- ethcore/src/account_provider/stores.rs | 161 +++++++++++++++++---- json/src/misc/dapps_settings.rs | 29 ++++ json/src/misc/mod.rs | 2 +- rpc/src/v1/impls/eth.rs | 6 +- rpc/src/v1/impls/parity_accounts.rs | 52 +++++-- rpc/src/v1/tests/mocked/eth.rs | 10 +- rpc/src/v1/tests/mocked/parity_accounts.rs | 48 +++++- rpc/src/v1/traits/parity_accounts.rs | 18 +++ rpc/src/v1/types/dapp_id.rs | 6 + 10 files changed, 341 insertions(+), 49 deletions(-) diff --git a/ethcore/src/account_provider/mod.rs b/ethcore/src/account_provider/mod.rs index a2c83f1ce..72a546b1f 100644 --- a/ethcore/src/account_provider/mod.rs +++ b/ethcore/src/account_provider/mod.rs @@ -18,7 +18,7 @@ mod stores; -use self::stores::{AddressBook, DappsSettingsStore}; +use self::stores::{AddressBook, DappsSettingsStore, NewDappsPolicy}; use std::fmt; use std::collections::HashMap; @@ -167,10 +167,49 @@ impl AccountProvider { Ok(accounts) } + /// Sets a whitelist of accounts exposed for unknown dapps. + /// `None` means that all accounts will be visible. + pub fn set_new_dapps_whitelist(&self, accounts: Option>) -> Result<(), Error> { + self.dapps_settings.write().set_policy(match accounts { + None => NewDappsPolicy::AllAccounts, + Some(accounts) => NewDappsPolicy::Whitelist(accounts), + }); + Ok(()) + } + + /// Gets a whitelist of accounts exposed for unknown dapps. + /// `None` means that all accounts will be visible. + pub fn new_dapps_whitelist(&self) -> Result>, Error> { + Ok(match self.dapps_settings.read().policy() { + NewDappsPolicy::AllAccounts => None, + NewDappsPolicy::Whitelist(accounts) => Some(accounts), + }) + } + + /// Gets a list of dapps recently requesting accounts. + pub fn recent_dapps(&self) -> Result, Error> { + Ok(self.dapps_settings.read().recent_dapps()) + } + + /// Marks dapp as recently used. + pub fn note_dapp_used(&self, dapp: DappId) -> Result<(), Error> { + let mut dapps = self.dapps_settings.write(); + dapps.mark_dapp_used(dapp.clone()); + Ok(()) + } + /// Gets addresses visile for dapp. pub fn dapps_addresses(&self, dapp: DappId) -> Result, Error> { - let accounts = self.dapps_settings.read().get(); - Ok(accounts.get(&dapp).map(|settings| settings.accounts.clone()).unwrap_or_else(Vec::new)) + let dapps = self.dapps_settings.read(); + + let accounts = dapps.settings().get(&dapp).map(|settings| settings.accounts.clone()); + match accounts { + Some(accounts) => Ok(accounts), + None => match dapps.policy() { + NewDappsPolicy::AllAccounts => self.accounts(), + NewDappsPolicy::Whitelist(accounts) => Ok(accounts), + } + } } /// Sets addresses visile for dapp. @@ -386,4 +425,17 @@ mod tests { // then assert_eq!(ap.dapps_addresses(app.clone()).unwrap(), vec![1.into(), 2.into()]); } + + #[test] + fn should_set_dapps_policy() { + // given + let ap = AccountProvider::transient_provider(); + let address = ap.new_account("test").unwrap(); + // Default policy should be to return all + assert_eq!(ap.dapps_addresses("app1".into()).unwrap(), vec![address]); + + // change policy + ap.set_new_dapps_whitelist(Some(vec![1.into()])).unwrap(); + assert_eq!(ap.dapps_addresses("app1".into()).unwrap(), vec![1.into()]); + } } diff --git a/ethcore/src/account_provider/stores.rs b/ethcore/src/account_provider/stores.rs index 8bf555d68..69cf29468 100644 --- a/ethcore/src/account_provider/stores.rs +++ b/ethcore/src/account_provider/stores.rs @@ -17,11 +17,11 @@ //! Address Book and Dapps Settings Store use std::{fs, fmt, hash, ops}; -use std::collections::HashMap; +use std::collections::{HashMap, VecDeque}; use std::path::PathBuf; use ethstore::ethkey::Address; -use ethjson::misc::{AccountMeta, DappsSettings as JsonSettings}; +use ethjson::misc::{AccountMeta, DappsSettings as JsonSettings, NewDappsPolicy as JsonNewDappsPolicy}; use account_provider::DappId; /// Disk-backed map from Address to String. Uses JSON. @@ -105,43 +105,106 @@ impl From for JsonSettings { } } +/// Dapps user settings +#[derive(Debug, Clone, Eq, PartialEq)] +pub enum NewDappsPolicy { + AllAccounts, + Whitelist(Vec
), +} + +impl From for NewDappsPolicy { + fn from(s: JsonNewDappsPolicy) -> Self { + match s { + JsonNewDappsPolicy::AllAccounts => NewDappsPolicy::AllAccounts, + JsonNewDappsPolicy::Whitelist(accounts) => NewDappsPolicy::Whitelist( + accounts.into_iter().map(Into::into).collect() + ), + } + } +} + +impl From for JsonNewDappsPolicy { + fn from(s: NewDappsPolicy) -> Self { + match s { + NewDappsPolicy::AllAccounts => JsonNewDappsPolicy::AllAccounts, + NewDappsPolicy::Whitelist(accounts) => JsonNewDappsPolicy::Whitelist( + accounts.into_iter().map(Into::into).collect() + ), + } + } +} + +const MAX_RECENT_DAPPS: usize = 10; + /// Disk-backed map from DappId to Settings. Uses JSON. pub struct DappsSettingsStore { - cache: DiskMap, + /// Dapps Settings + settings: DiskMap, + /// New Dapps Policy + policy: DiskMap, + /// Recently Accessed Dapps (transient) + recent: VecDeque, } impl DappsSettingsStore { /// Creates new store at given directory path. pub fn new(path: String) -> Self { let mut r = DappsSettingsStore { - cache: DiskMap::new(path, "dapps_accounts.json".into()) + settings: DiskMap::new(path.clone(), "dapps_accounts.json".into()), + policy: DiskMap::new(path.clone(), "dapps_policy.json".into()), + recent: VecDeque::with_capacity(MAX_RECENT_DAPPS), }; - r.cache.revert(JsonSettings::read_dapps_settings); + r.settings.revert(JsonSettings::read_dapps_settings); + r.policy.revert(JsonNewDappsPolicy::read_new_dapps_policy); r } /// Creates transient store (no changes are saved to disk). pub fn transient() -> Self { DappsSettingsStore { - cache: DiskMap::transient() + settings: DiskMap::transient(), + policy: DiskMap::transient(), + recent: VecDeque::with_capacity(MAX_RECENT_DAPPS), } } /// Get copy of the dapps settings - pub fn get(&self) -> HashMap { - self.cache.clone() + pub fn settings(&self) -> HashMap { + self.settings.clone() } - fn save(&self) { - self.cache.save(JsonSettings::write_dapps_settings) + /// Returns current new dapps policy + pub fn policy(&self) -> NewDappsPolicy { + self.policy.get("default").cloned().unwrap_or(NewDappsPolicy::AllAccounts) } + /// Returns recent dapps (in order of last request) + pub fn recent_dapps(&self) -> Vec { + self.recent.iter().cloned().collect() + } + + /// Marks recent dapp as used + pub fn mark_dapp_used(&mut self, dapp: DappId) { + self.recent.retain(|id| id != &dapp); + self.recent.push_front(dapp); + while self.recent.len() > MAX_RECENT_DAPPS { + self.recent.pop_back(); + } + } + + /// Sets current new dapps policy + pub fn set_policy(&mut self, policy: NewDappsPolicy) { + self.policy.insert("default".into(), policy); + self.policy.save(JsonNewDappsPolicy::write_new_dapps_policy); + } + + /// Sets accounts for specific dapp. pub fn set_accounts(&mut self, id: DappId, accounts: Vec
) { { - let mut settings = self.cache.entry(id).or_insert_with(DappsSettings::default); + let mut settings = self.settings.entry(id).or_insert_with(DappsSettings::default); settings.accounts = accounts; } - self.save(); + self.settings.save(JsonSettings::write_dapps_settings); } } @@ -216,7 +279,7 @@ impl DiskMap { #[cfg(test)] mod tests { - use super::{AddressBook, DappsSettingsStore, DappsSettings}; + use super::{AddressBook, DappsSettingsStore, DappsSettings, NewDappsPolicy}; use std::collections::HashMap; use ethjson::misc::AccountMeta; use devtools::RandomTempPath; @@ -232,25 +295,6 @@ mod tests { assert_eq!(b.get(), hash_map![1.into() => AccountMeta{name: "One".to_owned(), meta: "{1:1}".to_owned(), uuid: None}]); } - #[test] - fn should_save_and_reload_dapps_settings() { - // given - let temp = RandomTempPath::create_dir(); - let path = temp.as_str().to_owned(); - let mut b = DappsSettingsStore::new(path.clone()); - - // when - b.set_accounts("dappOne".into(), vec![1.into(), 2.into()]); - - // then - let b = DappsSettingsStore::new(path); - assert_eq!(b.get(), hash_map![ - "dappOne".into() => DappsSettings { - accounts: vec![1.into(), 2.into()], - } - ]); - } - #[test] fn should_remove_address() { let temp = RandomTempPath::create_dir(); @@ -268,4 +312,57 @@ mod tests { 3.into() => AccountMeta{name: "Three".to_owned(), meta: "{}".to_owned(), uuid: None} ]); } + + #[test] + fn should_save_and_reload_dapps_settings() { + // given + let temp = RandomTempPath::create_dir(); + let path = temp.as_str().to_owned(); + let mut b = DappsSettingsStore::new(path.clone()); + + // when + b.set_accounts("dappOne".into(), vec![1.into(), 2.into()]); + + // then + let b = DappsSettingsStore::new(path); + assert_eq!(b.settings(), hash_map![ + "dappOne".into() => DappsSettings { + accounts: vec![1.into(), 2.into()], + } + ]); + } + + #[test] + fn should_maintain_a_list_of_recent_dapps() { + let mut store = DappsSettingsStore::transient(); + assert!(store.recent_dapps().is_empty(), "Initially recent dapps should be empty."); + + store.mark_dapp_used("dapp1".into()); + assert_eq!(store.recent_dapps(), vec!["dapp1".to_owned()]); + + store.mark_dapp_used("dapp2".into()); + assert_eq!(store.recent_dapps(), vec!["dapp2".to_owned(), "dapp1".to_owned()]); + + store.mark_dapp_used("dapp1".into()); + assert_eq!(store.recent_dapps(), vec!["dapp1".to_owned(), "dapp2".to_owned()]); + } + + #[test] + fn should_store_dapps_policy() { + // given + let temp = RandomTempPath::create_dir(); + let path = temp.as_str().to_owned(); + let mut store = DappsSettingsStore::new(path.clone()); + // Test default policy + assert_eq!(store.policy(), NewDappsPolicy::AllAccounts); + + // when + store.set_policy(NewDappsPolicy::Whitelist(vec![1.into(), 2.into()])); + + // then + let store = DappsSettingsStore::new(path); + assert_eq!(store.policy.clone(), hash_map![ + "default".into() => NewDappsPolicy::Whitelist(vec![1.into(), 2.into()]) + ]); + } } diff --git a/json/src/misc/dapps_settings.rs b/json/src/misc/dapps_settings.rs index 893e7e93e..dc140fd29 100644 --- a/json/src/misc/dapps_settings.rs +++ b/json/src/misc/dapps_settings.rs @@ -49,3 +49,32 @@ impl DappsSettings { serde_json::to_writer(writer, &m.iter().map(|(a, m)| (a.clone().into(), m.clone().into())).collect::>()) } } + +/// Accounts policy for new dapps. +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub enum NewDappsPolicy { + /// All accounts are exposed by default. + AllAccounts, + /// Only accounts listed here are exposed by default for new dapps. + Whitelist(Vec), +} + +impl NewDappsPolicy { + /// Read a hash map of `String -> NewDappsPolicy` + pub fn read_new_dapps_policy(reader: R) -> Result, serde_json::Error> where + R: io::Read, + S: From + Clone, + { + serde_json::from_reader(reader).map(|ok: HashMap| + ok.into_iter().map(|(a, m)| (a.into(), m.into())).collect() + ) + } + + /// Write a hash map of `String -> NewDappsPolicy` + pub fn write_new_dapps_policy(m: &HashMap, writer: &mut W) -> Result<(), serde_json::Error> where + W: io::Write, + S: Into + Clone, + { + serde_json::to_writer(writer, &m.iter().map(|(a, m)| (a.clone().into(), m.clone().into())).collect::>()) + } +} diff --git a/json/src/misc/mod.rs b/json/src/misc/mod.rs index baab83d08..0fe89e850 100644 --- a/json/src/misc/mod.rs +++ b/json/src/misc/mod.rs @@ -19,5 +19,5 @@ mod account_meta; mod dapps_settings; -pub use self::dapps_settings::DappsSettings; +pub use self::dapps_settings::{DappsSettings, NewDappsPolicy}; pub use self::account_meta::AccountMeta; diff --git a/rpc/src/v1/impls/eth.rs b/rpc/src/v1/impls/eth.rs index 6b9f47de3..44e7d9ead 100644 --- a/rpc/src/v1/impls/eth.rs +++ b/rpc/src/v1/impls/eth.rs @@ -340,7 +340,11 @@ impl Eth for EthClient where let dapp = id.0; let store = take_weak!(self.accounts); - let accounts = try!(store.dapps_addresses(dapp.into()).map_err(|e| errors::internal("Could not fetch accounts.", e))); + let accounts = try!(store + .note_dapp_used(dapp.clone().into()) + .and_then(|_| store.dapps_addresses(dapp.into())) + .map_err(|e| errors::internal("Could not fetch accounts.", e)) + ); Ok(accounts.into_iter().map(Into::into).collect()) } diff --git a/rpc/src/v1/impls/parity_accounts.rs b/rpc/src/v1/impls/parity_accounts.rs index 188e2290e..b420548f2 100644 --- a/rpc/src/v1/impls/parity_accounts.rs +++ b/rpc/src/v1/impls/parity_accounts.rs @@ -164,19 +164,51 @@ impl ParityAccounts for ParityAccountsClient where C: MiningBlock fn set_dapps_addresses(&self, dapp: DappId, addresses: Vec) -> Result { let store = take_weak!(self.accounts); - let addresses = addresses.into_iter().map(Into::into).collect(); - store.set_dapps_addresses(dapp.into(), addresses) + store.set_dapps_addresses(dapp.into(), into_vec(addresses)) .map_err(|e| errors::account("Couldn't set dapps addresses.", e)) .map(|_| true) } + fn dapps_addresses(&self, dapp: DappId) -> Result, Error> { + let store = take_weak!(self.accounts); + + store.dapps_addresses(dapp.into()) + .map_err(|e| errors::account("Couldn't get dapps addresses.", e)) + .map(into_vec) + } + + fn set_new_dapps_whitelist(&self, whitelist: Option>) -> Result { + let store = take_weak!(self.accounts); + + store + .set_new_dapps_whitelist(whitelist.map(into_vec)) + .map_err(|e| errors::account("Couldn't set dapps whitelist.", e)) + .map(|_| true) + } + + fn new_dapps_whitelist(&self) -> Result>, Error> { + let store = take_weak!(self.accounts); + + store.new_dapps_whitelist() + .map_err(|e| errors::account("Couldn't get dapps whitelist.", e)) + .map(|accounts| accounts.map(into_vec)) + } + + fn recent_dapps(&self) -> Result, Error> { + let store = take_weak!(self.accounts); + + store.recent_dapps() + .map_err(|e| errors::account("Couldn't get recent dapps.", e)) + .map(into_vec) + } + fn import_geth_accounts(&self, addresses: Vec) -> Result, Error> { let store = take_weak!(self.accounts); store - .import_geth_accounts(addresses.into_iter().map(Into::into).collect(), false) - .map(|imported| imported.into_iter().map(Into::into).collect()) + .import_geth_accounts(into_vec(addresses), false) + .map(into_vec) .map_err(|e| errors::account("Couldn't import Geth accounts", e)) } @@ -184,10 +216,12 @@ impl ParityAccounts for ParityAccountsClient where C: MiningBlock try!(self.active()); let store = take_weak!(self.accounts); - Ok(store.list_geth_accounts(false) - .into_iter() - .map(Into::into) - .collect() - ) + Ok(into_vec(store.list_geth_accounts(false))) } } + +fn into_vec(a: Vec) -> Vec where + A: Into +{ + a.into_iter().map(Into::into).collect() +} diff --git a/rpc/src/v1/tests/mocked/eth.rs b/rpc/src/v1/tests/mocked/eth.rs index 05c95346d..aa60aeb2e 100644 --- a/rpc/src/v1/tests/mocked/eth.rs +++ b/rpc/src/v1/tests/mocked/eth.rs @@ -354,11 +354,17 @@ fn rpc_eth_gas_price() { #[test] fn rpc_eth_accounts() { let tester = EthTester::default(); - let _address = tester.accounts_provider.new_account("").unwrap(); + let address = tester.accounts_provider.new_account("").unwrap(); + // with default policy it should return the account + let request = r#"{"jsonrpc": "2.0", "method": "eth_accounts", "params": [], "id": 1}"#; + let response = r#"{"jsonrpc":"2.0","result":[""#.to_owned() + &format!("0x{:?}", address) + r#""],"id":1}"#; + assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned())); + + tester.accounts_provider.set_new_dapps_whitelist(Some(vec![1.into()])).unwrap(); // even with some account it should return empty list (no dapp detected) let request = r#"{"jsonrpc": "2.0", "method": "eth_accounts", "params": [], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":[],"id":1}"#; + let response = r#"{"jsonrpc":"2.0","result":["0x0000000000000000000000000000000000000001"],"id":1}"#; assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned())); // when we add visible address it should return that. diff --git a/rpc/src/v1/tests/mocked/parity_accounts.rs b/rpc/src/v1/tests/mocked/parity_accounts.rs index 8b42500e6..66597ca6f 100644 --- a/rpc/src/v1/tests/mocked/parity_accounts.rs +++ b/rpc/src/v1/tests/mocked/parity_accounts.rs @@ -117,7 +117,7 @@ fn should_be_able_to_set_meta() { } #[test] -fn rpc_parity_set_dapps_accounts() { +fn rpc_parity_set_and_get_dapps_accounts() { // given let tester = setup(); assert_eq!(tester.accounts.dapps_addresses("app1".into()).unwrap(), vec![]); @@ -129,6 +129,52 @@ fn rpc_parity_set_dapps_accounts() { // then assert_eq!(tester.accounts.dapps_addresses("app1".into()).unwrap(), vec![10.into()]); + let request = r#"{"jsonrpc": "2.0", "method": "parity_getDappsAddresses","params":["app1"], "id": 1}"#; + let response = r#"{"jsonrpc":"2.0","result":["0x000000000000000000000000000000000000000a"],"id":1}"#; + assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned())); +} + +#[test] +fn rpc_parity_set_and_get_new_dapps_whitelist() { + // given + let tester = setup(); + + // when set to whitelist + let request = r#"{"jsonrpc": "2.0", "method": "parity_setNewDappsWhitelist","params":[["0x000000000000000000000000000000000000000a"]], "id": 1}"#; + let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#; + assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned())); + + // then + assert_eq!(tester.accounts.new_dapps_whitelist().unwrap(), Some(vec![10.into()])); + let request = r#"{"jsonrpc": "2.0", "method": "parity_getNewDappsWhitelist","params":[], "id": 1}"#; + let response = r#"{"jsonrpc":"2.0","result":["0x000000000000000000000000000000000000000a"],"id":1}"#; + assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned())); + + // when set to empty + let request = r#"{"jsonrpc": "2.0", "method": "parity_setNewDappsWhitelist","params":[null], "id": 1}"#; + let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#; + assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned())); + + // then + assert_eq!(tester.accounts.new_dapps_whitelist().unwrap(), None); + let request = r#"{"jsonrpc": "2.0", "method": "parity_getNewDappsWhitelist","params":[], "id": 1}"#; + let response = r#"{"jsonrpc":"2.0","result":null,"id":1}"#; + assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned())); +} + +#[test] +fn rpc_parity_recent_dapps() { + // given + let tester = setup(); + + // when + // trigger dapp usage + tester.accounts.note_dapp_used("dapp1".into()).unwrap(); + + // then + let request = r#"{"jsonrpc": "2.0", "method": "parity_listRecentDapps","params":[], "id": 1}"#; + let response = r#"{"jsonrpc":"2.0","result":["dapp1"],"id":1}"#; + assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned())); } #[test] diff --git a/rpc/src/v1/traits/parity_accounts.rs b/rpc/src/v1/traits/parity_accounts.rs index 6cfe1bf7b..95377540b 100644 --- a/rpc/src/v1/traits/parity_accounts.rs +++ b/rpc/src/v1/traits/parity_accounts.rs @@ -79,6 +79,24 @@ build_rpc_trait! { #[rpc(name = "parity_setDappsAddresses")] fn set_dapps_addresses(&self, DappId, Vec) -> Result; + /// Gets accounts exposed for particular dapp. + #[rpc(name = "parity_getDappsAddresses")] + fn dapps_addresses(&self, DappId) -> Result, Error>; + + /// Sets accounts exposed for new dapps. + /// `None` means that all accounts will be exposed. + #[rpc(name = "parity_setNewDappsWhitelist")] + fn set_new_dapps_whitelist(&self, Option>) -> Result; + + /// Gets accounts exposed for new dapps. + /// `None` means that all accounts will be exposed. + #[rpc(name = "parity_getNewDappsWhitelist")] + fn new_dapps_whitelist(&self) -> Result>, Error>; + + /// Sets accounts exposed for particular dapp. + #[rpc(name = "parity_listRecentDapps")] + fn recent_dapps(&self) -> Result, Error>; + /// Imports a number of Geth accounts, with the list provided as the argument. #[rpc(name = "parity_importGethAccounts")] fn import_geth_accounts(&self, Vec) -> Result, Error>; diff --git a/rpc/src/v1/types/dapp_id.rs b/rpc/src/v1/types/dapp_id.rs index 04aa80e3a..a21007892 100644 --- a/rpc/src/v1/types/dapp_id.rs +++ b/rpc/src/v1/types/dapp_id.rs @@ -26,6 +26,12 @@ impl Into for DappId { } } +impl From for DappId { + fn from(s: String) -> Self { + DappId(s) + } +} + #[cfg(test)] mod tests { From 2c653d2c2efe0d367c0db14aa157d3a4281e26b6 Mon Sep 17 00:00:00 2001 From: arkpar Date: Sun, 11 Dec 2016 13:11:08 +0100 Subject: [PATCH 14/25] Merge w master --- Cargo.lock | 29 ++++++++++++++--------------- rpc/src/v1/types/uint.rs | 1 - rpc_client/Cargo.toml | 2 +- signer/src/tests/mod.rs | 2 -- 4 files changed, 15 insertions(+), 19 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2c9af0621..4ec3404ae 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -850,6 +850,18 @@ name = "itoa" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "jsonrpc-core" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_codegen 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "jsonrpc-core" version = "4.0.0" @@ -1281,7 +1293,7 @@ dependencies = [ "serde_json 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "url 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ws 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", + "ws 0.5.3 (git+https://github.com/ethcore/ws-rs.git?branch=mio-upstream-stable)", ] [[package]] @@ -2024,19 +2036,6 @@ dependencies = [ "url 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "ws" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "httparse 1.1.2 (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)", - "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", - "sha1 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "url 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "ws2_32-sys" version = "0.2.1" @@ -2120,6 +2119,7 @@ dependencies = [ "checksum isatty 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7408a548dc0e406b7912d9f84c261cc533c1866e047644a811c133c56041ac0c" "checksum itertools 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)" = "086e1fa5fe48840b1cfdef3a20c7e3115599f8d5c4c87ef32a794a7cdd184d76" "checksum itoa 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ae3088ea4baeceb0284ee9eea42f591226e6beaecf65373e41b38d95a1b8e7a1" +"checksum jsonrpc-core 3.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3c5094610b07f28f3edaf3947b732dadb31dbba4941d4d0c1c7a8350208f4414" "checksum jsonrpc-core 4.0.0 (git+https://github.com/ethcore/jsonrpc.git)" = "" "checksum jsonrpc-http-server 6.1.1 (git+https://github.com/ethcore/jsonrpc.git)" = "" "checksum jsonrpc-ipc-server 0.2.4 (git+https://github.com/ethcore/jsonrpc.git)" = "" @@ -2249,7 +2249,6 @@ dependencies = [ "checksum winapi 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "4dfaaa8fbdaa618fa6914b59b2769d690dd7521920a18d84b42d254678dd5fd4" "checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" "checksum ws 0.5.3 (git+https://github.com/ethcore/ws-rs.git?branch=mio-upstream-stable)" = "" -"checksum ws 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7c47e9ca2f5c47d27f731b1bb9bb50cc05f9886bb84fbd52afa0ff97f4f61b06" "checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" "checksum xml-rs 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "65e74b96bd3179209dc70a980da6df843dff09e46eee103a0376c0949257e3ef" "checksum xmltree 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "472a9d37c7c53ab2391161df5b89b1f3bf76dab6ab150d7941ecbdd832282082" diff --git a/rpc/src/v1/types/uint.rs b/rpc/src/v1/types/uint.rs index 60aae8f1d..fa68b9729 100644 --- a/rpc/src/v1/types/uint.rs +++ b/rpc/src/v1/types/uint.rs @@ -16,7 +16,6 @@ use std::str::FromStr; use std::fmt; -use rustc_serialize::hex::ToHex; use serde; use util::{U256 as EthU256, U128 as EthU128, Uint}; diff --git a/rpc_client/Cargo.toml b/rpc_client/Cargo.toml index e8477a802..9b93a1a5b 100644 --- a/rpc_client/Cargo.toml +++ b/rpc_client/Cargo.toml @@ -17,7 +17,7 @@ serde = "0.8" serde_json = "0.8" tempdir = "0.3.5" url = "1.2.0" -ws = "0.5.3" +ws = { git = "https://github.com/ethcore/ws-rs.git", branch = "mio-upstream-stable" } ethcore-rpc = { path = "../rpc" } ethcore-signer = { path = "../signer" } ethcore-util = { path = "../util" } diff --git a/signer/src/tests/mod.rs b/signer/src/tests/mod.rs index 0a3cf51c9..8b0e3bdde 100644 --- a/signer/src/tests/mod.rs +++ b/signer/src/tests/mod.rs @@ -15,8 +15,6 @@ // along with Parity. If not, see . use std::ops::{Deref, DerefMut}; -use std::thread; -use std::time::{self, Duration}; use std::sync::Arc; #[cfg(test)] From 05dcf951d11dc2c8821d0275b7a7e2de5b38a2ac Mon Sep 17 00:00:00 2001 From: arkpar Date: Sun, 11 Dec 2016 14:59:57 +0100 Subject: [PATCH 15/25] Merge w master --- rpc_client/src/signer_client.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rpc_client/src/signer_client.rs b/rpc_client/src/signer_client.rs index 587fa42b7..75bf85119 100644 --- a/rpc_client/src/signer_client.rs +++ b/rpc_client/src/signer_client.rs @@ -22,13 +22,14 @@ impl SignerRpc { pub fn confirm_request( &mut self, id: U256, + new_gas: Option, new_gas_price: Option, pwd: &str ) -> BoxFuture, Canceled> { self.rpc.request("personal_confirmRequest", vec![ to_value(&format!("{:#x}", id)), - to_value(&TransactionModification { gas_price: new_gas_price }), + to_value(&TransactionModification { gas_price: new_gas_price, gas: new_gas }), to_value(&pwd), ]) } From 627b8a8414b92eaaee666b79f9bb9ae7e7578f89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Sun, 11 Dec 2016 17:51:34 +0100 Subject: [PATCH 16/25] Changing default policy --- ethcore/src/account_provider/mod.rs | 11 +++++++++-- ethcore/src/account_provider/stores.rs | 4 ++-- rpc/src/v1/tests/mocked/eth.rs | 3 ++- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/ethcore/src/account_provider/mod.rs b/ethcore/src/account_provider/mod.rs index 72a546b1f..9177fe0be 100644 --- a/ethcore/src/account_provider/mod.rs +++ b/ethcore/src/account_provider/mod.rs @@ -418,6 +418,8 @@ mod tests { // given let ap = AccountProvider::transient_provider(); let app = "app1".to_owned(); + // set `AllAccounts` policy + ap.set_new_dapps_whitelist(None).unwrap(); // when ap.set_dapps_addresses(app.clone(), vec![1.into(), 2.into()]).unwrap(); @@ -431,10 +433,15 @@ mod tests { // given let ap = AccountProvider::transient_provider(); let address = ap.new_account("test").unwrap(); - // Default policy should be to return all + + // Default policy should be to return nothing + assert_eq!(ap.dapps_addresses("app1".into()).unwrap(), vec![]); + + // change to all + ap.set_new_dapps_whitelist(None).unwrap(); assert_eq!(ap.dapps_addresses("app1".into()).unwrap(), vec![address]); - // change policy + // change to a whitelist ap.set_new_dapps_whitelist(Some(vec![1.into()])).unwrap(); assert_eq!(ap.dapps_addresses("app1".into()).unwrap(), vec![1.into()]); } diff --git a/ethcore/src/account_provider/stores.rs b/ethcore/src/account_provider/stores.rs index 69cf29468..f1f98e2cb 100644 --- a/ethcore/src/account_provider/stores.rs +++ b/ethcore/src/account_provider/stores.rs @@ -175,7 +175,7 @@ impl DappsSettingsStore { /// Returns current new dapps policy pub fn policy(&self) -> NewDappsPolicy { - self.policy.get("default").cloned().unwrap_or(NewDappsPolicy::AllAccounts) + self.policy.get("default").cloned().unwrap_or(NewDappsPolicy::Whitelist(vec![])) } /// Returns recent dapps (in order of last request) @@ -354,7 +354,7 @@ mod tests { let path = temp.as_str().to_owned(); let mut store = DappsSettingsStore::new(path.clone()); // Test default policy - assert_eq!(store.policy(), NewDappsPolicy::AllAccounts); + assert_eq!(store.policy(), NewDappsPolicy::Whitelist(vec![])); // when store.set_policy(NewDappsPolicy::Whitelist(vec![1.into(), 2.into()])); diff --git a/rpc/src/v1/tests/mocked/eth.rs b/rpc/src/v1/tests/mocked/eth.rs index aa60aeb2e..36eb7eb3b 100644 --- a/rpc/src/v1/tests/mocked/eth.rs +++ b/rpc/src/v1/tests/mocked/eth.rs @@ -355,8 +355,9 @@ fn rpc_eth_gas_price() { fn rpc_eth_accounts() { let tester = EthTester::default(); let address = tester.accounts_provider.new_account("").unwrap(); + tester.accounts_provider.set_new_dapps_whitelist(None).unwrap(); - // with default policy it should return the account + // with current policy it should return the account let request = r#"{"jsonrpc": "2.0", "method": "eth_accounts", "params": [], "id": 1}"#; let response = r#"{"jsonrpc":"2.0","result":[""#.to_owned() + &format!("0x{:?}", address) + r#""],"id":1}"#; assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned())); From 5affefc7636b627e2f474316f022eec5edcb250e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Tue, 13 Dec 2016 12:17:01 +0100 Subject: [PATCH 17/25] Fixing CLI signer --- rpc/src/v1/types/confirmations.rs | 32 +++++++++++++++++++++---- rpc/src/v1/types/transaction_request.rs | 21 ++++++++++------ rpc_client/src/client.rs | 26 ++++++++++++-------- rpc_client/src/signer_client.rs | 6 ++--- 4 files changed, 60 insertions(+), 25 deletions(-) diff --git a/rpc/src/v1/types/confirmations.rs b/rpc/src/v1/types/confirmations.rs index fa7741fd1..a3cf02a9e 100644 --- a/rpc/src/v1/types/confirmations.rs +++ b/rpc/src/v1/types/confirmations.rs @@ -18,6 +18,8 @@ use std::fmt; use serde::{Serialize, Serializer}; +use util::log::Colour; + use v1::types::{U256, TransactionRequest, RichRawTransaction, H160, H256, H520, Bytes}; use v1::helpers; @@ -48,11 +50,10 @@ impl fmt::Display for ConfirmationRequest { impl fmt::Display for ConfirmationPayload { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { - ConfirmationPayload::SendTransaction(ref transaction) - => write!(f, "{}", transaction), - ConfirmationPayload::SignTransaction(_) => write!(f, "TODO: data"), - ConfirmationPayload::Decrypt(_) => write!(f, "TODO: decrypt"), - ConfirmationPayload::Signature(_) => write!(f, "TODO: signature"), + ConfirmationPayload::SendTransaction(ref transaction) => write!(f, "{}", transaction), + ConfirmationPayload::SignTransaction(ref transaction) => write!(f, "(Sign only) {}", transaction), + ConfirmationPayload::Signature(ref sign) => write!(f, "{}", sign), + ConfirmationPayload::Decrypt(ref decrypt) => write!(f, "{}", decrypt), } } } @@ -75,6 +76,17 @@ impl From<(H160, H256)> for SignRequest { } } +impl fmt::Display for SignRequest { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "sign 0x{:?} with {}", + self.hash, + Colour::White.bold().paint(format!("0x{:?}", self.address)), + ) + } +} + /// Decrypt request #[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)] pub struct DecryptRequest { @@ -93,6 +105,16 @@ impl From<(H160, Bytes)> for DecryptRequest { } } +impl fmt::Display for DecryptRequest { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "decrypt data with {}", + Colour::White.bold().paint(format!("0x{:?}", self.address)), + ) + } +} + /// Confirmation response for particular payload #[derive(Debug, Clone, PartialEq)] pub enum ConfirmationResponse { diff --git a/rpc/src/v1/types/transaction_request.rs b/rpc/src/v1/types/transaction_request.rs index 492f2354d..a91a16858 100644 --- a/rpc/src/v1/types/transaction_request.rs +++ b/rpc/src/v1/types/transaction_request.rs @@ -18,6 +18,7 @@ use v1::types::{Bytes, H160, U256}; use v1::helpers; +use util::log::Colour; use std::fmt; @@ -62,13 +63,19 @@ impl fmt::Display for TransactionRequest { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let eth = self.value.unwrap_or(U256::from(0)); match self.to { - Some(ref to) => write!(f, "{} Ether from {:?} to {:?}", - format_ether(eth), - self.from, - to), - None => write!(f, "{} Ether from {:?}", - format_ether(eth), - self.from), + Some(ref to) => write!( + f, + "{} ETH from {} to 0x{:?}", + Colour::White.bold().paint(format_ether(eth)), + Colour::White.bold().paint(format!("0x{:?}", self.from)), + to + ), + None => write!( + f, + "{} ETH from {} for contract creation", + Colour::White.bold().paint(format_ether(eth)), + Colour::White.bold().paint(format!("0x{:?}", self.from)), + ), } } } diff --git a/rpc_client/src/client.rs b/rpc_client/src/client.rs index 84e9cedb9..bc4c7aa8b 100644 --- a/rpc_client/src/client.rs +++ b/rpc_client/src/client.rs @@ -22,13 +22,15 @@ use ws::{ Error as WsError, ErrorKind as WsErrorKind, Message, - Result as WsResult + Result as WsResult, }; use serde::Deserialize; -use serde_json::{self as json, - Value as JsonValue, - Error as JsonError}; +use serde_json::{ + self as json, + Value as JsonValue, + Error as JsonError, +}; use futures::{BoxFuture, Canceled, Complete, Future, oneshot, done}; @@ -170,13 +172,17 @@ impl Pending { } fn get_authcode(path: &PathBuf) -> Result { - match File::open(path) { - Ok(fd) => match BufReader::new(fd).lines().next() { - Some(Ok(code)) => Ok(code), - _ => Err(RpcError::NoAuthCode), - }, - Err(_) => Err(RpcError::NoAuthCode) + if let Ok(fd) = File::open(path) { + if let Some(Ok(line)) = BufReader::new(fd).lines().next() { + let mut parts = line.split(';'); + let token = parts.next(); + + if let Some(code) = token { + return Ok(code.into()); + } + } } + Err(RpcError::NoAuthCode) } /// The handle to the connection diff --git a/rpc_client/src/signer_client.rs b/rpc_client/src/signer_client.rs index 75bf85119..8a2eccd5d 100644 --- a/rpc_client/src/signer_client.rs +++ b/rpc_client/src/signer_client.rs @@ -17,7 +17,7 @@ impl SignerRpc { pub fn requests_to_confirm(&mut self) -> BoxFuture, RpcError>, Canceled> { - self.rpc.request("personal_requestsToConfirm", vec![]) + self.rpc.request("signer_requestsToConfirm", vec![]) } pub fn confirm_request( &mut self, @@ -27,7 +27,7 @@ impl SignerRpc { pwd: &str ) -> BoxFuture, Canceled> { - self.rpc.request("personal_confirmRequest", vec![ + self.rpc.request("signer_confirmRequest", vec![ to_value(&format!("{:#x}", id)), to_value(&TransactionModification { gas_price: new_gas_price, gas: new_gas }), to_value(&pwd), @@ -36,7 +36,7 @@ impl SignerRpc { pub fn reject_request(&mut self, id: U256) -> BoxFuture, Canceled> { - self.rpc.request("personal_rejectRequest", vec![ + self.rpc.request("signer_rejectRequest", vec![ JsonValue::String(format!("{:#x}", id)) ]) } From 366f9a8cd73dc864d197e7d95d984ff5bdfd2f87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Wed, 14 Dec 2016 14:18:48 +0100 Subject: [PATCH 18/25] Fixing tests --- Cargo.lock | 20 ++- signer/src/tests/mod.rs | 376 +++++++++++++++++++++------------------- 2 files changed, 210 insertions(+), 186 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5b85ac564..ad23083ae 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -733,7 +733,7 @@ dependencies = [ [[package]] name = "futures" -version = "0.1.3" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1310,7 +1310,7 @@ dependencies = [ "ethcore-rpc 1.5.0", "ethcore-signer 1.5.0", "ethcore-util 1.5.0", - "futures 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "jsonrpc-core 3.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1347,6 +1347,17 @@ dependencies = [ "parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "parking_lot" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "parking_lot" version = "0.3.5" @@ -1609,7 +1620,7 @@ dependencies = [ "ethcore-bigint 0.1.2", "ethcore-rpc 1.5.0", "ethcore-util 1.5.0", - "futures 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "parity-rpc-client 1.4.0", "rpassword 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2121,7 +2132,7 @@ dependencies = [ "checksum ethabi 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f7b0c53453517f620847be51943db329276ae52f2e210cfc659e81182864be2f" "checksum fdlimit 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b1ee15a7050e5580b3712877157068ea713b245b080ff302ae2ca973cfcd9baa" "checksum flate2 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "3eeb481e957304178d2e782f2da1257f1434dfecbae883bafb61ada2a9fea3bb" -"checksum futures 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dd89497091f8c5d3a65c6b4baf6d2f0731937a7c9217d2f89141b21437a9d96" +"checksum futures 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0bad0a2ac64b227fdc10c254051ae5af542cf19c9328704fd4092f7914196897" "checksum gcc 0.3.35 (registry+https://github.com/rust-lang/crates.io-index)" = "91ecd03771effb0c968fd6950b37e89476a578aaf1c70297d8e92b6516ec3312" "checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb" "checksum hamming 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "65043da274378d68241eb9a8f8f8aa54e349136f7b8e12f63e3ef44043cc30e1" @@ -2182,6 +2193,7 @@ dependencies = [ "checksum owning_ref 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8d91377085359426407a287ab16884a0111ba473aa6844ff01d4ec20ce3d75e7" "checksum parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "98378dec0a185da2b7180308752f0bad73aaa949c3e0a3b0528d0e067945f7ab" "checksum parity-ui-precompiled 1.4.0 (git+https://github.com/ethcore/js-precompiled.git)" = "" +"checksum parking_lot 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "968f685642555d2f7e202c48b8b11de80569e9bfea817f7f12d7c61aac62d4e6" "checksum parking_lot 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "dbc5847584161f273e69edc63c1a86254a22f570a0b5dd87aa6f9773f6f7d125" "checksum parking_lot_core 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fb1b97670a2ffadce7c397fb80a3d687c4f3060140b885621ef1653d0e5d5068" "checksum phf 0.7.14 (registry+https://github.com/rust-lang/crates.io-index)" = "447d9d45f2e0b4a9b532e808365abf18fc211be6ca217202fcd45236ef12f026" diff --git a/signer/src/tests/mod.rs b/signer/src/tests/mod.rs index 767f84a62..ab46a0e6f 100644 --- a/signer/src/tests/mod.rs +++ b/signer/src/tests/mod.rs @@ -17,7 +17,6 @@ use std::ops::{Deref, DerefMut}; use std::sync::Arc; -#[cfg(test)] use devtools::http_client; use devtools::RandomTempPath; @@ -61,189 +60,202 @@ pub fn serve() -> (Server, usize, GuardedAuthCodes) { }) } -#[test] -fn should_reject_invalid_host() { - // given - let server = serve().0; - - // when - let response = request(server, - "\ - GET / HTTP/1.1\r\n\ - Host: test:8180\r\n\ - Connection: close\r\n\ - \r\n\ - {} - " - ); - - // then - assert_eq!(response.status, "HTTP/1.1 403 FORBIDDEN".to_owned()); - assert!(response.body.contains("URL Blocked")); - http_client::assert_security_headers_present(&response.headers, None); +/// Test a single request to running server +pub fn request(server: Server, request: &str) -> http_client::Response { + http_client::request(server.addr(), request) } -#[test] -fn should_allow_home_parity_host() { - // given - let server = serve().0; +#[cfg(test)] +mod testing { + use std::time; + use util::Hashable; + use devtools::http_client; + use super::{serve, request}; - // when - let response = request(server, - "\ - GET http://home.parity/ HTTP/1.1\r\n\ - Host: home.parity\r\n\ - Connection: close\r\n\ - \r\n\ - {} - " - ); + #[test] + fn should_reject_invalid_host() { + // given + let server = serve().0; - // then - assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned()); - http_client::assert_security_headers_present(&response.headers, None); -} - -#[test] -fn should_serve_styles_even_on_disallowed_domain() { - // given - let server = serve().0; - - // when - let response = request(server, - "\ - GET /styles.css HTTP/1.1\r\n\ - Host: test:8180\r\n\ - Connection: close\r\n\ - \r\n\ - {} - " - ); - - // then - assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned()); - http_client::assert_security_headers_present(&response.headers, None); -} - -#[test] -fn should_return_200_ok_for_connect_requests() { - // given - let server = serve().0; - - // when - let response = request(server, - "\ - CONNECT home.parity:8080 HTTP/1.1\r\n\ - Host: home.parity\r\n\ - Connection: close\r\n\ - \r\n\ - {} - " - ); - - // then - assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned()); -} - -#[test] -fn should_block_if_authorization_is_incorrect() { - // given - let (server, port, _) = serve(); - - // when - let response = request(server, - &format!("\ - GET / HTTP/1.1\r\n\ - Host: 127.0.0.1:{}\r\n\ - Connection: Upgrade\r\n\ - Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==\r\n\ - Sec-WebSocket-Protocol: wrong\r\n\ - Sec-WebSocket-Version: 13\r\n\ - \r\n\ - {{}} - ", port) - ); - - // then - assert_eq!(response.status, "HTTP/1.1 403 FORBIDDEN".to_owned()); - http_client::assert_security_headers_present(&response.headers, None); -} - -#[test] -fn should_allow_if_authorization_is_correct() { - // given - let (server, port, mut authcodes) = serve(); - let code = authcodes.generate_new().unwrap().replace("-", ""); - authcodes.to_file(&authcodes.path).unwrap(); - let timestamp = time::UNIX_EPOCH.elapsed().unwrap().as_secs(); - - // when - let response = request(server, - &format!("\ - GET / HTTP/1.1\r\n\ - Host: 127.0.0.1:{}\r\n\ - Connection: Close\r\n\ - Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==\r\n\ - Sec-WebSocket-Protocol: {:?}_{}\r\n\ - Sec-WebSocket-Version: 13\r\n\ - \r\n\ - {{}} - ", - port, - format!("{}:{}", code, timestamp).sha3(), - timestamp, - ) - ); - - // then - assert_eq!(response.status, "HTTP/1.1 101 Switching Protocols".to_owned()); -} - -#[test] -fn should_allow_initial_connection_but_only_once() { - // given - let (server, port, authcodes) = serve(); - let code = "initial"; - let timestamp = time::UNIX_EPOCH.elapsed().unwrap().as_secs(); - assert!(authcodes.is_empty()); - - // when - let response1 = http_client::request(server.addr(), - &format!("\ - GET / HTTP/1.1\r\n\ - Host: 127.0.0.1:{}\r\n\ - Connection: Close\r\n\ - Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==\r\n\ - Sec-WebSocket-Protocol:{:?}_{}\r\n\ - Sec-WebSocket-Version: 13\r\n\ - \r\n\ - {{}} - ", - port, - format!("{}:{}", code, timestamp).sha3(), - timestamp, - ) - ); - let response2 = http_client::request(server.addr(), - &format!("\ - GET / HTTP/1.1\r\n\ - Host: 127.0.0.1:{}\r\n\ - Connection: Close\r\n\ - Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==\r\n\ - Sec-WebSocket-Protocol:{:?}_{}\r\n\ - Sec-WebSocket-Version: 13\r\n\ - \r\n\ - {{}} - ", - port, - format!("{}:{}", code, timestamp).sha3(), - timestamp, - ) - ); - - - // then - assert_eq!(response1.status, "HTTP/1.1 101 Switching Protocols".to_owned()); - assert_eq!(response2.status, "HTTP/1.1 403 FORBIDDEN".to_owned()); - http_client::assert_security_headers_present(&response2.headers, None); + // when + let response = request(server, + "\ + GET / HTTP/1.1\r\n\ + Host: test:8180\r\n\ + Connection: close\r\n\ + \r\n\ + {} + " + ); + + // then + assert_eq!(response.status, "HTTP/1.1 403 FORBIDDEN".to_owned()); + assert!(response.body.contains("URL Blocked")); + http_client::assert_security_headers_present(&response.headers, None); + } + + #[test] + fn should_allow_home_parity_host() { + // given + let server = serve().0; + + // when + let response = request(server, + "\ + GET http://home.parity/ HTTP/1.1\r\n\ + Host: home.parity\r\n\ + Connection: close\r\n\ + \r\n\ + {} + " + ); + + // then + assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned()); + http_client::assert_security_headers_present(&response.headers, None); + } + + #[test] + fn should_serve_styles_even_on_disallowed_domain() { + // given + let server = serve().0; + + // when + let response = request(server, + "\ + GET /styles.css HTTP/1.1\r\n\ + Host: test:8180\r\n\ + Connection: close\r\n\ + \r\n\ + {} + " + ); + + // then + assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned()); + http_client::assert_security_headers_present(&response.headers, None); + } + + #[test] + fn should_return_200_ok_for_connect_requests() { + // given + let server = serve().0; + + // when + let response = request(server, + "\ + CONNECT home.parity:8080 HTTP/1.1\r\n\ + Host: home.parity\r\n\ + Connection: close\r\n\ + \r\n\ + {} + " + ); + + // then + assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned()); + } + + #[test] + fn should_block_if_authorization_is_incorrect() { + // given + let (server, port, _) = serve(); + + // when + let response = request(server, + &format!("\ + GET / HTTP/1.1\r\n\ + Host: 127.0.0.1:{}\r\n\ + Connection: Upgrade\r\n\ + Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==\r\n\ + Sec-WebSocket-Protocol: wrong\r\n\ + Sec-WebSocket-Version: 13\r\n\ + \r\n\ + {{}} + ", port) + ); + + // then + assert_eq!(response.status, "HTTP/1.1 403 FORBIDDEN".to_owned()); + http_client::assert_security_headers_present(&response.headers, None); + } + + #[test] + fn should_allow_if_authorization_is_correct() { + // given + let (server, port, mut authcodes) = serve(); + let code = authcodes.generate_new().unwrap().replace("-", ""); + authcodes.to_file(&authcodes.path).unwrap(); + let timestamp = time::UNIX_EPOCH.elapsed().unwrap().as_secs(); + + // when + let response = request(server, + &format!("\ + GET / HTTP/1.1\r\n\ + Host: 127.0.0.1:{}\r\n\ + Connection: Close\r\n\ + Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==\r\n\ + Sec-WebSocket-Protocol: {:?}_{}\r\n\ + Sec-WebSocket-Version: 13\r\n\ + \r\n\ + {{}} + ", + port, + format!("{}:{}", code, timestamp).sha3(), + timestamp, + ) + ); + + // then + assert_eq!(response.status, "HTTP/1.1 101 Switching Protocols".to_owned()); + } + + #[test] + fn should_allow_initial_connection_but_only_once() { + // given + let (server, port, authcodes) = serve(); + let code = "initial"; + let timestamp = time::UNIX_EPOCH.elapsed().unwrap().as_secs(); + assert!(authcodes.is_empty()); + + // when + let response1 = http_client::request(server.addr(), + &format!("\ + GET / HTTP/1.1\r\n\ + Host: 127.0.0.1:{}\r\n\ + Connection: Close\r\n\ + Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==\r\n\ + Sec-WebSocket-Protocol:{:?}_{}\r\n\ + Sec-WebSocket-Version: 13\r\n\ + \r\n\ + {{}} + ", + port, + format!("{}:{}", code, timestamp).sha3(), + timestamp, + ) + ); + let response2 = http_client::request(server.addr(), + &format!("\ + GET / HTTP/1.1\r\n\ + Host: 127.0.0.1:{}\r\n\ + Connection: Close\r\n\ + Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==\r\n\ + Sec-WebSocket-Protocol:{:?}_{}\r\n\ + Sec-WebSocket-Version: 13\r\n\ + \r\n\ + {{}} + ", + port, + format!("{}:{}", code, timestamp).sha3(), + timestamp, + ) + ); + + + // then + assert_eq!(response1.status, "HTTP/1.1 101 Switching Protocols".to_owned()); + assert_eq!(response2.status, "HTTP/1.1 403 FORBIDDEN".to_owned()); + http_client::assert_security_headers_present(&response2.headers, None); + } } From 69eec105a74872f532d5e5acfa6720ae8fffc2ac Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Thu, 15 Dec 2016 13:40:44 +0100 Subject: [PATCH 19/25] Change default back to permissive for now. --- ethcore/res/ethereum/tests | 2 +- ethcore/src/account_provider/mod.rs | 3 ++- ethcore/src/account_provider/stores.rs | 5 +++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/ethcore/res/ethereum/tests b/ethcore/res/ethereum/tests index 9028c4801..e8f4624b7 160000 --- a/ethcore/res/ethereum/tests +++ b/ethcore/res/ethereum/tests @@ -1 +1 @@ -Subproject commit 9028c4801fd39fbb71a9796979182549a24e81c8 +Subproject commit e8f4624b7f1a15c63674eecf577c7ab76c3b16be diff --git a/ethcore/src/account_provider/mod.rs b/ethcore/src/account_provider/mod.rs index 649cb3162..dab19dbc0 100644 --- a/ethcore/src/account_provider/mod.rs +++ b/ethcore/src/account_provider/mod.rs @@ -478,7 +478,8 @@ mod tests { let ap = AccountProvider::transient_provider(); let address = ap.new_account("test").unwrap(); - // Default policy should be to return nothing + // When returning nothing + ap.set_new_dapps_whitelist(Some(vec![])).unwrap(); assert_eq!(ap.dapps_addresses("app1".into()).unwrap(), vec![]); // change to all diff --git a/ethcore/src/account_provider/stores.rs b/ethcore/src/account_provider/stores.rs index 722370426..d4f2093ee 100644 --- a/ethcore/src/account_provider/stores.rs +++ b/ethcore/src/account_provider/stores.rs @@ -175,7 +175,7 @@ impl DappsSettingsStore { /// Returns current new dapps policy pub fn policy(&self) -> NewDappsPolicy { - self.policy.get("default").cloned().unwrap_or(NewDappsPolicy::Whitelist(vec![])) + self.policy.get("default").cloned().unwrap_or(NewDappsPolicy::AllAccounts) } /// Returns recent dapps (in order of last request) @@ -353,8 +353,9 @@ mod tests { let temp = RandomTempPath::create_dir(); let path = temp.as_str().to_owned(); let mut store = DappsSettingsStore::new(path.clone()); + // Test default policy - assert_eq!(store.policy(), NewDappsPolicy::Whitelist(vec![])); + assert_eq!(store.policy(), NewDappsPolicy::AllAccounts); // when store.set_policy(NewDappsPolicy::Whitelist(vec![1.into(), 2.into()])); From ca63bc2f4430f516e4a7f95a7c31d6a32b8b9714 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Thu, 15 Dec 2016 14:46:31 +0100 Subject: [PATCH 20/25] Update parking_lot dep --- Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 54fbfdb08..e06f56aa2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -886,7 +886,7 @@ version = "3.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "serde 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)", "serde_codegen 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", From 8f5e0cf7d9468b6dc24704f92a8a8b44694ee8d9 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Thu, 15 Dec 2016 14:54:02 +0100 Subject: [PATCH 21/25] Fix build --- Cargo.lock | 1 - 1 file changed, 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index e06f56aa2..cf557d488 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1620,7 +1620,6 @@ dependencies = [ "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)", "termios 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] From d1040e4127072956b79642800c174eefa96adb05 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Thu, 15 Dec 2016 15:03:34 +0100 Subject: [PATCH 22/25] Whitespace --- rpc_client/src/client.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/rpc_client/src/client.rs b/rpc_client/src/client.rs index bc4c7aa8b..9ee9f5c9d 100644 --- a/rpc_client/src/client.rs +++ b/rpc_client/src/client.rs @@ -65,7 +65,7 @@ impl RpcHandler { } impl Handler for RpcHandler { - fn build_request(&mut self, url: &Url) -> WsResult { + fn build_request(&mut self, url: &Url) -> WsResult { match Request::from_url(url) { Ok(mut r) => { let timestamp = try!(time::UNIX_EPOCH.elapsed().map_err(|err| { @@ -80,14 +80,14 @@ impl Handler for RpcHandler { Err(e) => Err(WsError::new(WsErrorKind::Internal, format!("{}", e))), } - } - fn on_error(&mut self, err: WsError) { + } + fn on_error(&mut self, err: WsError) { match self.complete.take() { Some(c) => c.complete(Err(RpcError::WsError(err))), None => println!("unexpected error: {}", err), } - } - fn on_open(&mut self, _: Handshake) -> WsResult<()> { + } + fn on_open(&mut self, _: Handshake) -> WsResult<()> { match (self.complete.take(), self.out.take()) { (Some(c), Some(out)) => { c.complete(Ok(Rpc { @@ -103,7 +103,7 @@ impl Handler for RpcHandler { } } } - fn on_message(&mut self, msg: Message) -> WsResult<()> { + fn on_message(&mut self, msg: Message) -> WsResult<()> { let ret: Result; let response_id; let string = &msg.to_string(); @@ -147,7 +147,7 @@ impl Handler for RpcHandler { ), } Ok(()) - } + } } /// Keeping track of issued requests to be matched up with responses From 17781a5ac4083126cc0343a3393b252d37bf1ad1 Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Thu, 15 Dec 2016 17:52:28 +0100 Subject: [PATCH 23/25] Fix typo in method call --- js/src/api/contract/contract.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/src/api/contract/contract.js b/js/src/api/contract/contract.js index 95dcf2e72..2a090e8c6 100644 --- a/js/src/api/contract/contract.js +++ b/js/src/api/contract/contract.js @@ -458,7 +458,7 @@ export default class Contract { } try { - this.sendData(subscriptionId, null, this.parseEventLogs(logs)); + this._sendData(subscriptionId, null, this.parseEventLogs(logs)); } catch (error) { console.error('_sendSubscriptionChanges', error); } From 87f1486907c2b047d0d70b2d48be2af3efa1cd28 Mon Sep 17 00:00:00 2001 From: GitLab Build Bot Date: Thu, 15 Dec 2016 17:03:27 +0000 Subject: [PATCH 24/25] [ci skip] js-precompiled 20161215-170041 --- Cargo.lock | 2 +- js/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 511218335..2626a8c96 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1321,7 +1321,7 @@ dependencies = [ [[package]] name = "parity-ui-precompiled" version = "1.4.0" -source = "git+https://github.com/ethcore/js-precompiled.git#762c6d10f8640a6c4b875d776490282680bfe3e2" +source = "git+https://github.com/ethcore/js-precompiled.git#ad6617a73dbb17c53dddc0fc567e70ea5b8e882f" dependencies = [ "parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] diff --git a/js/package.json b/js/package.json index 83d7994e7..66a60385b 100644 --- a/js/package.json +++ b/js/package.json @@ -1,6 +1,6 @@ { "name": "parity.js", - "version": "0.2.124", + "version": "0.2.125", "main": "release/index.js", "jsnext:main": "src/index.js", "author": "Parity Team ", From 1f1dc8b89d0f15ab7c6bdec186c257a71fb64a10 Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Thu, 15 Dec 2016 18:09:59 +0100 Subject: [PATCH 25/25] Store subscriptionId, use it to send changes --- js/src/api/contract/contract.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/js/src/api/contract/contract.js b/js/src/api/contract/contract.js index 2a090e8c6..68c0371a1 100644 --- a/js/src/api/contract/contract.js +++ b/js/src/api/contract/contract.js @@ -342,7 +342,8 @@ export default class Contract { options: _options, autoRemove, callback, - filterId + filterId, + id: subscriptionId }; if (skipInitFetch) { @@ -452,13 +453,13 @@ export default class Contract { }) ) .then((logsArray) => { - logsArray.forEach((logs, subscriptionId) => { + logsArray.forEach((logs, index) => { if (!logs || !logs.length) { return; } try { - this._sendData(subscriptionId, null, this.parseEventLogs(logs)); + this._sendData(subscriptions[index].id, null, this.parseEventLogs(logs)); } catch (error) { console.error('_sendSubscriptionChanges', error); }