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)) ]) }