* 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
This commit is contained in:
Kristoffer Ström 2016-09-30 15:30:17 +02:00 committed by arkpar
parent 273d7c00c3
commit 7a176094d5
9 changed files with 162 additions and 137 deletions

View File

@ -43,6 +43,8 @@ use presale::ImportWallet;
use account::{AccountCmd, NewAccount, ImportAccounts, ImportFromGethAccounts}; use account::{AccountCmd, NewAccount, ImportAccounts, ImportFromGethAccounts};
use snapshot::{self, SnapshotCommand}; use snapshot::{self, SnapshotCommand};
const AUTHCODE_FILENAME: &'static str = "authcodes";
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub enum Cmd { pub enum Cmd {
Run(RunCmd), Run(RunCmd),
@ -62,7 +64,7 @@ pub enum Cmd {
authfile: PathBuf authfile: PathBuf
}, },
SignerReject { SignerReject {
id: usize, id: Option<usize>,
port: u16, port: u16,
authfile: PathBuf authfile: PathBuf
}, },
@ -124,15 +126,14 @@ impl Configuration {
======= =======
} else if self.args.cmd_signer { } else if self.args.cmd_signer {
let mut authfile = PathBuf::from(signer_conf.signer_path); let mut authfile = PathBuf::from(signer_conf.signer_path);
authfile.push("authcodes"); authfile.push(AUTHCODE_FILENAME);
if self.args.cmd_new_token { if self.args.cmd_new_token {
Cmd::SignerToken(dirs.signer) Cmd::SignerToken(dirs.signer)
} else if self.args.cmd_sign { } else if self.args.cmd_sign {
let pwfile = match self.args.flag_password.get(0) { let pwfile = self.args.flag_password.get(0).map(|pwfile| {
Some(pwfile) => Some(PathBuf::from(pwfile)), PathBuf::from(pwfile)
None => None, });
};
Cmd::SignerSign { Cmd::SignerSign {
id: self.args.arg_id, id: self.args.arg_id,
pwfile: pwfile, pwfile: pwfile,
@ -141,8 +142,7 @@ impl Configuration {
} }
} else if self.args.cmd_reject { } else if self.args.cmd_reject {
Cmd::SignerReject { Cmd::SignerReject {
// id is a required field for this command id: self.args.arg_id,
id: self.args.arg_id.unwrap(),
port: signer_conf.port, port: signer_conf.port,
authfile: authfile, authfile: authfile,
} }

View File

@ -41,16 +41,16 @@ impl From<helpers::ConfirmationRequest> for ConfirmationRequest {
impl fmt::Display for ConfirmationRequest { impl fmt::Display for ConfirmationRequest {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 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 { impl fmt::Display for ConfirmationPayload {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self { match *self {
&ConfirmationPayload::Transaction(ref transaction) ConfirmationPayload::Transaction(ref transaction)
=> write!(f, "{}", transaction), => write!(f, "{}", transaction),
&ConfirmationPayload::Sign(_) => write!(f, "TODO: data"), ConfirmationPayload::Sign(_) => write!(f, "TODO: data"),
} }
} }
} }

View File

@ -44,10 +44,16 @@ pub struct TransactionRequest {
impl fmt::Display for TransactionRequest { impl fmt::Display for TransactionRequest {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?} from {:?} to {:?}", let eth = self.value.unwrap_or(U256::from(0));
self.value.unwrap_or(U256::from(0)), match self.to {
self.from, Some(ref to) => write!(f, "{} Ether from {:?} to {:?}",
self.to) eth.format_ether(),
self.from,
to),
None => write!(f, "{} Ether from {:?}",
eth.format_ether(),
self.from),
}
} }
} }

View File

@ -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 { impl fmt::LowerHex for $name {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:#x}", self.0) write!(f, "{:#x}", self.0)

View File

@ -15,7 +15,7 @@ use std::fs::File;
use futures::Future; 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); print!("\n{}\nSign this transaction? (y)es/(N)o/(r)eject: ", request);
let _ = stdout().flush(); let _ = stdout().flush();
@ -23,7 +23,7 @@ fn sign_interactive(signer: &mut SignerRpc, pwd: &String, request: ConfirmationR
Some(Ok(line)) => { Some(Ok(line)) => {
match line.to_lowercase().chars().nth(0) { match line.to_lowercase().chars().nth(0) {
Some('y') => { Some('y') => {
match sign_transaction(signer, request.id, pwd) { match sign_transaction(signer, request.id, password) {
Ok(s) | Err(s) => println!("{}", s), 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<String, String> { fn sign_transactions(signer: &mut SignerRpc, password: String) -> Result<String, String> {
signer.requests_to_confirm().map(|reqs| { try!(signer.requests_to_confirm().map(|reqs| {
match reqs { match reqs {
Ok(ref reqs) if reqs.is_empty() => {
Ok("No transactions in signing queue".to_owned())
}
Ok(reqs) => { Ok(reqs) => {
if reqs.len() == 0 { for r in reqs {
Ok("No transactions in signing queue".to_string()) sign_interactive(signer, &password, r)
} else {
for r in reqs {
sign_interactive(signer, &pwd, r)
}
Ok("".to_string())
} }
Ok("".to_owned())
} }
Err(err) => { Err(err) => {
Err(format!("error: {:?}", err)) Err(format!("error: {:?}", err))
} }
} }
}).wait().unwrap() }).map_err(|err| {
format!("{:?}", err)
}).wait())
} }
fn list_transactions(signer: &mut SignerRpc) -> Result<String, String> { fn list_transactions(signer: &mut SignerRpc) -> Result<String, String> {
signer.requests_to_confirm().map(|reqs| { try!(signer.requests_to_confirm().map(|reqs| {
match reqs { match reqs {
Ok(reqs) => { Ok(ref reqs) if reqs.is_empty() => {
let mut s = "Transaction queue:".to_string(); Ok("No transactions in signing queue".to_owned())
if reqs.len() == 0 { }
s = s + &"No transactions in signing queue"; Ok(ref reqs) => {
} else { Ok(format!("Transaction queue:\n{}", reqs
for r in reqs { .iter()
s = s + &format!("\n{}", r); .map(|r| format!("{}", r))
} .collect::<Vec<String>>()
} .join("\n")))
Ok(s)
} }
Err(err) => { Err(err) => {
Err(format!("error: {:?}", err)) Err(format!("error: {:?}", err))
} }
} }
}).wait().unwrap() }).map_err(|err| {
format!("{:?}", err)
}).wait())
} }
fn sign_transaction(signer: &mut SignerRpc, fn sign_transaction(signer: &mut SignerRpc,
id: U256, id: U256,
pwd: &String) -> Result<String, String> { password: &str) -> Result<String, String> {
signer.confirm_request(id, None, &pwd).map(|res| { try!(signer.confirm_request(id, None, password).map(|res| {
match res { match res {
Ok(u) => Ok(format!("Signed transaction id: {:#x}", u)), Ok(u) => Ok(format!("Signed transaction id: {:#x}", u)),
Err(e) => Err(format!("{:?}", e)), Err(e) => Err(format!("{:?}", e)),
} }
}).wait().unwrap() }).map_err(|err| {
format!("{:?}", err)
}).wait())
} }
fn reject_transaction(signer: &mut SignerRpc, fn reject_transaction(signer: &mut SignerRpc,
id: U256) -> Result<String, String> { id: U256) -> Result<String, String> {
signer.reject_request(id).map(|res| { try!(signer.reject_request(id).map(|res| {
match res { match res {
Ok(true) => Ok(format!("Rejected transaction id {:#x}", id)), Ok(true) => Ok(format!("Rejected transaction id {:#x}", id)),
Ok(false) => Err(format!("No such request")), Ok(false) => Err(format!("No such request")),
Err(e) => Err(format!("{:?}", e)), Err(e) => Err(format!("{:?}", e)),
} }
}).wait().unwrap() }).map_err(|err| {
format!("{:?}", err)
}).wait())
} }
// cmds // cmds
pub fn cmd_signer_list(signerport: u16, pub fn cmd_signer_list(signerport: u16,
authfile: PathBuf) -> Result<String, String> { authfile: PathBuf) -> Result<String, String> {
match SignerRpc::new(&format!("ws://127.0.0.1:{}", signerport), let mut signer = try!(SignerRpc::new(&format!("ws://127.0.0.1:{}", signerport), &authfile).map_err(|err| {
&authfile) { format!("{:?}", err)
Ok(mut signer) => { }));
list_transactions(&mut signer) list_transactions(&mut signer)
}
Err(e) => Err(format!("{:?}", e))
}
} }
pub fn cmd_signer_reject(id: usize, pub fn cmd_signer_reject(id: Option<usize>, signerport: u16,
signerport: u16,
authfile: PathBuf) -> Result<String, String> { authfile: PathBuf) -> Result<String, String> {
match SignerRpc::new(&format!("ws://127.0.0.1:{}", signerport), let id = try!(id.ok_or(format!("id required for signer reject")));
&authfile) { let mut signer = try!(SignerRpc::new(&format!("ws://127.0.0.1:{}", signerport), &authfile).map_err(|err| {
Ok(mut signer) => { format!("{:?}", err)
reject_transaction(&mut signer, U256::from(id)) }));
}, reject_transaction(&mut signer, U256::from(id))
Err(e) => Err(format!("{:?}", e))
}
} }
pub fn cmd_signer_sign(id: Option<usize>, pub fn cmd_signer_sign(id: Option<usize>,
pwfile: Option<PathBuf>, pwfile: Option<PathBuf>,
signerport: u16, signerport: u16,
authfile: PathBuf) -> Result<String, String> { authfile: PathBuf) -> Result<String, String> {
let pwd; let password;
match pwfile { match pwfile {
Some(pwfile) => { Some(pwfile) => {
match File::open(pwfile) { match File::open(pwfile) {
Ok(fd) => { Ok(fd) => {
match BufReader::new(fd).lines().next() { match BufReader::new(fd).lines().next() {
Some(Ok(line)) => pwd = line, Some(Ok(line)) => password = line,
_ => return Err(format!("No password in file")) _ => 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 => { 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), let mut signer = try!(SignerRpc::new(&format!("ws://127.0.0.1:{}", signerport), &authfile).map_err(|err| {
&authfile) { format!("{:?}", err)
Ok(mut signer) => { }));
match id {
Some(id) => { match id {
sign_transaction(&mut signer, U256::from(id), &pwd) Some(id) => {
}, sign_transaction(&mut signer, U256::from(id), &password)
None => { },
sign_transactions(&mut signer, pwd) None => {
} sign_transactions(&mut signer, password)
}
} }
Err(e) => return Err(format!("{:?}", e))
} }
} }

View File

@ -8,6 +8,7 @@ version = "1.4.0"
[dependencies] [dependencies]
futures = "0.1" futures = "0.1"
jsonrpc-core = "3.0.2"
lazy_static = "0.2.1" lazy_static = "0.2.1"
matches = "0.1.2" matches = "0.1.2"
rand = "0.3.14" rand = "0.3.14"
@ -16,12 +17,6 @@ serde_json = "0.8"
tempdir = "0.3.5" tempdir = "0.3.5"
url = "1.2.0" url = "1.2.0"
ws = "0.5.3" ws = "0.5.3"
ethcore-rpc = { path = "../rpc" }
[dependencies.ethcore-rpc] ethcore-signer = { path = "../signer" }
path = "../rpc" ethcore-util = { path = "../util" }
[dependencies.ethcore-signer]
path = "../signer"
[dependencies.ethcore-util]
path = "../util"

View File

@ -4,7 +4,6 @@ use std::sync::{Arc, Mutex};
use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::atomic::{AtomicUsize, Ordering};
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::thread; use std::thread;
use std::mem;
use std::time; use std::time;
use std::path::PathBuf; use std::path::PathBuf;
@ -12,7 +11,7 @@ use util::Hashable;
use url::Url; use url::Url;
use std::fs::File; use std::fs::File;
use ws::{connect, use ws::{self,
Request, Request,
Handler, Handler,
Sender, Sender,
@ -25,12 +24,12 @@ use ws::{connect,
use serde::Serialize; use serde::Serialize;
use serde::Deserialize; use serde::Deserialize;
use serde::ser::Serializer; use serde::ser::Serializer;
use serde_json::{from_str, use serde_json::{self as json,
to_string,
from_value,
Value as JsonValue, Value as JsonValue,
Error as JsonError}; Error as JsonError};
//use jsonrpc_core::
use futures::{BoxFuture, Canceled, Complete, Future, oneshot, done}; use futures::{BoxFuture, Canceled, Complete, Future, oneshot, done};
/// The actual websocket connection handler, passed into the /// The actual websocket connection handler, passed into the
@ -65,9 +64,12 @@ impl Handler for RpcHandler {
fn build_request(&mut self, url: &Url) -> WsResult<Request> { fn build_request(&mut self, url: &Url) -> WsResult<Request> {
match Request::from_url(url) { match Request::from_url(url) {
Ok(mut r) => { Ok(mut r) => {
let timestamp = time::UNIX_EPOCH.elapsed().unwrap().as_secs(); let timestamp = try!(time::UNIX_EPOCH.elapsed().map_err(|err| {
let hashed = format!("{}:{}", self.auth_code, timestamp).sha3(); WsError::new(WsErrorKind::Internal, format!("{}", err))
let proto = format!("{:?}_{}", hashed, timestamp); }));
let secs = timestamp.as_secs();
let hashed = format!("{}:{}", self.auth_code, secs).sha3();
let proto = format!("{:?}_{}", hashed, secs);
r.add_protocol(&proto); r.add_protocol(&proto);
Ok(r) Ok(r)
}, },
@ -75,22 +77,25 @@ impl Handler for RpcHandler {
} }
} }
fn on_error(&mut self, err: WsError) { 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))), 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<()> { fn on_open(&mut self, _: Handshake) -> WsResult<()> {
match mem::replace(&mut self.complete, None) { match (self.complete.take(), self.out.take()) {
Some(c) => c.complete(Ok(Rpc { (Some(c), Some(out)) => {
out: mem::replace(&mut self.out, None).unwrap(), c.complete(Ok(Rpc {
counter: AtomicUsize::new(0), out: out,
pending: self.pending.clone(), counter: AtomicUsize::new(0),
})), pending: self.pending.clone(),
// Should not be reachable }));
None => (), Ok(())
},
_ => {
Err(WsError::new(WsErrorKind::Internal, format!("on_open called twice")))
}
} }
Ok(())
} }
fn on_message(&mut self, msg: Message) -> WsResult<()> { fn on_message(&mut self, msg: Message) -> WsResult<()> {
match parse_response(&msg.to_string()) { match parse_response(&msg.to_string()) {
@ -153,16 +158,25 @@ impl Rpc {
Err(e) => return done(Ok(Err(e))).boxed(), Err(e) => return done(Ok(Err(e))).boxed(),
Ok(code) => { Ok(code) => {
let url = String::from(url); 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 || { thread::spawn(move || {
// mem:replace Option hack to move `c` out let conn = ws::connect(url, |out| {
// of the FnMut closure // this will panic if the closure
let mut swap = Some(c); // is called twice, which it should never
match connect(url, |out| { // be.
let c = mem::replace(&mut swap, None).unwrap(); let c = once.take().expect("connection closure called only once");
RpcHandler::new(out, code.clone(), c) RpcHandler::new(out, code.clone(), c)
}) { });
match conn {
Err(err) => { 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.complete(Err(RpcError::WsError(err)));
}, },
// c will complete on the `on_open` event in the Handler // 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); let id = self.counter.fetch_add(1, Ordering::Relaxed);
self.pending.insert(id, c); 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); let _ = self.out.send(serialized);
p.map(|result| { p.map(|result| {
match result { match result {
Ok(json) => { Ok(json) => {
let t: T = try!(from_value(json)); let t: T = try!(json::from_value(json));
Ok(t) Ok(t)
}, },
Err(err) => Err(err) Err(err) => Err(err)
@ -281,7 +295,7 @@ impl From<Canceled> for RpcError {
} }
fn parse_response(s: &str) -> (Option<usize>, Result<JsonValue, RpcError>) { fn parse_response(s: &str) -> (Option<usize>, Result<JsonValue, RpcError>) {
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))), Err(e) => return (None, Err(RpcError::ParseError(e))),
Ok(json) => json, Ok(json) => json,
}; };

View File

@ -12,6 +12,7 @@ extern crate serde;
extern crate serde_json; extern crate serde_json;
extern crate rand; extern crate rand;
extern crate tempdir; extern crate tempdir;
extern crate jsonrpc_core;
#[macro_use] #[macro_use]
extern crate lazy_static; extern crate lazy_static;
@ -19,7 +20,7 @@ extern crate lazy_static;
extern crate matches; extern crate matches;
#[cfg(test)] #[cfg(test)]
mod test { mod tests {
use futures::Future; use futures::Future;
use std::path::PathBuf; use std::path::PathBuf;
@ -29,46 +30,40 @@ mod test {
#[test] #[test]
fn test_connection_refused() { fn test_connection_refused() {
let (srv, port, tmpdir, _) = serve(); let (_srv, port, tmpdir, _) = serve();
let mut path = PathBuf::from(tmpdir.path()); let mut path = PathBuf::from(tmpdir.path());
path.push("authcodes"); path.push("authcodes");
let connect = Rpc::connect(&format!("ws://127.0.0.1:{}", port - 1), &path); 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(_)))); assert!(matches!(&conn, &Err(RpcError::WsError(_))));
}).wait(); }).wait();
drop(srv);
} }
#[test] #[test]
fn test_authcode_fail() { fn test_authcode_fail() {
let (srv, port, _, _) = serve(); let (_srv, port, _, _) = serve();
let path = PathBuf::from("nonexist"); let path = PathBuf::from("nonexist");
let connect = Rpc::connect(&format!("ws://127.0.0.1:{}", port), &path); 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))); assert!(matches!(&conn, &Err(RpcError::NoAuthCode)));
}).wait(); }).wait();
drop(srv);
} }
#[test] #[test]
fn test_authcode_correct() { fn test_authcode_correct() {
let (srv, port, tmpdir, _) = serve(); let (_srv, port, tmpdir, _) = serve();
let mut path = PathBuf::from(tmpdir.path()); let mut path = PathBuf::from(tmpdir.path());
path.push("authcodes"); path.push("authcodes");
let connect = Rpc::connect(&format!("ws://127.0.0.1:{}", port), &path); let connect = Rpc::connect(&format!("ws://127.0.0.1:{}", port), &path);
connect.map(|conn| { let _ = connect.map(|conn| {
assert!(conn.is_ok()) assert!(conn.is_ok())
}).wait(); }).wait();
drop(srv);
} }
} }

View File

@ -1,4 +1,3 @@
use client::{Rpc, RpcError}; use client::{Rpc, RpcError};
use rpc::v1::types::{ConfirmationRequest, use rpc::v1::types::{ConfirmationRequest,
TransactionModification, TransactionModification,
@ -13,16 +12,12 @@ pub struct SignerRpc {
impl SignerRpc { impl SignerRpc {
pub fn new(url: &str, authfile: &PathBuf) -> Result<Self, RpcError> { pub fn new(url: &str, authfile: &PathBuf) -> Result<Self, RpcError> {
match Rpc::new(&url, authfile) { Ok(SignerRpc { rpc: try!(Rpc::new(&url, authfile)) })
Ok(rpc) => Ok(SignerRpc { rpc: rpc }),
Err(e) => Err(e),
}
} }
pub fn requests_to_confirm(&mut self) -> pub fn requests_to_confirm(&mut self) ->
BoxFuture<Result<Vec<ConfirmationRequest>, RpcError>, Canceled> BoxFuture<Result<Vec<ConfirmationRequest>, RpcError>, Canceled>
{ {
self.rpc.request::<Vec<ConfirmationRequest>> self.rpc.request("personal_requestsToConfirm", vec![])
("personal_requestsToConfirm", vec![])
} }
pub fn confirm_request(&mut self, pub fn confirm_request(&mut self,
id: U256, id: U256,
@ -30,7 +25,7 @@ impl SignerRpc {
pwd: &str) -> pwd: &str) ->
BoxFuture<Result<U256, RpcError>, Canceled> BoxFuture<Result<U256, RpcError>, Canceled>
{ {
self.rpc.request::<U256>("personal_confirmRequest", vec![ self.rpc.request("personal_confirmRequest", vec![
to_value(&format!("{:#x}", id)), to_value(&format!("{:#x}", id)),
to_value(&TransactionModification { gas_price: new_gas_price }), to_value(&TransactionModification { gas_price: new_gas_price }),
to_value(&pwd), to_value(&pwd),
@ -39,7 +34,7 @@ impl SignerRpc {
pub fn reject_request(&mut self, id: U256) -> pub fn reject_request(&mut self, id: U256) ->
BoxFuture<Result<bool, RpcError>, Canceled> BoxFuture<Result<bool, RpcError>, Canceled>
{ {
self.rpc.request::<bool>("personal_rejectRequest", vec![ self.rpc.request("personal_rejectRequest", vec![
JsonValue::String(format!("{:#x}", id)) JsonValue::String(format!("{:#x}", id))
]) ])
} }