From cd6f565f69d71deead5336be2b71f3ff396ba37a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Tue, 22 Nov 2016 11:56:27 +0100 Subject: [PATCH] RPC Middleware & Get/Set dapp-specific accounts --- Cargo.lock | 92 +++---- Cargo.toml | 1 - dapps/Cargo.toml | 4 +- dapps/src/rpc.rs | 94 ++++++- .../mod.rs} | 136 +++------- ethcore/src/account_provider/stores.rs | 247 ++++++++++++++++++ json/src/misc/account_meta.rs | 2 +- json/src/misc/dapps_settings.rs | 51 ++++ json/src/misc/mod.rs | 2 + parity/main.rs | 2 - parity/rpc.rs | 8 +- rpc/Cargo.toml | 6 +- rpc/src/lib.rs | 7 +- rpc/src/v1/impls/eth.rs | 15 +- rpc/src/v1/impls/parity_accounts.rs | 11 +- rpc/src/v1/tests/eth.rs | 2 +- rpc/src/v1/tests/mocked/eth.rs | 16 +- rpc/src/v1/tests/mocked/net.rs | 2 +- rpc/src/v1/tests/mocked/parity.rs | 2 +- rpc/src/v1/tests/mocked/parity_accounts.rs | 17 +- rpc/src/v1/tests/mocked/parity_set.rs | 2 +- rpc/src/v1/tests/mocked/personal.rs | 2 +- rpc/src/v1/tests/mocked/rpc.rs | 2 +- rpc/src/v1/tests/mocked/signer.rs | 2 +- rpc/src/v1/tests/mocked/signing.rs | 48 ++-- rpc/src/v1/tests/mocked/web3.rs | 2 +- rpc/src/v1/traits/eth.rs | 4 +- rpc/src/v1/traits/parity_accounts.rs | 8 +- rpc/src/v1/types/dapp_id.rs | 60 +++++ rpc/src/v1/types/mod.rs.in | 2 + signer/Cargo.toml | 2 +- signer/src/ws_server/session.rs | 20 +- stratum/Cargo.toml | 4 +- stratum/src/lib.rs | 4 +- 34 files changed, 655 insertions(+), 224 deletions(-) rename ethcore/src/{account_provider.rs => account_provider/mod.rs} (80%) create mode 100644 ethcore/src/account_provider/stores.rs create mode 100644 json/src/misc/dapps_settings.rs create mode 100644 rpc/src/v1/types/dapp_id.rs diff --git a/Cargo.lock b/Cargo.lock index 02e45b49e..eaede16ba 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -27,7 +27,6 @@ dependencies = [ "fdlimit 0.1.0", "hyper 0.9.10 (registry+https://github.com/rust-lang/crates.io-index)", "isatty 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "json-ipc-server 0.2.4 (git+https://github.com/ethcore/json-ipc-server.git)", "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)", "num_cpus 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", @@ -341,8 +340,8 @@ dependencies = [ "ethcore-util 1.5.0", "fetch 0.1.0", "hyper 0.9.4 (git+https://github.com/ethcore/hyper)", - "jsonrpc-core 3.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-http-server 6.1.1 (git+https://github.com/ethcore/jsonrpc-http-server.git)", + "jsonrpc-core 4.0.0 (git+https://github.com/ethcore/jsonrpc.git)", + "jsonrpc-http-server 6.1.1 (git+https://github.com/ethcore/jsonrpc.git)", "linked-hash-map 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "mime 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -503,9 +502,9 @@ dependencies = [ "ethstore 0.1.0", "ethsync 1.5.0", "fetch 0.1.0", - "json-ipc-server 0.2.4 (git+https://github.com/ethcore/json-ipc-server.git)", - "jsonrpc-core 3.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-http-server 6.1.1 (git+https://github.com/ethcore/jsonrpc-http-server.git)", + "jsonrpc-core 4.0.0 (git+https://github.com/ethcore/jsonrpc.git)", + "jsonrpc-http-server 6.1.1 (git+https://github.com/ethcore/jsonrpc.git)", + "jsonrpc-ipc-server 0.2.4 (git+https://github.com/ethcore/jsonrpc.git)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "rlp 0.1.0", "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", @@ -526,7 +525,7 @@ dependencies = [ "ethcore-io 1.5.0", "ethcore-rpc 1.5.0", "ethcore-util 1.5.0", - "jsonrpc-core 3.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpc-core 4.0.0 (git+https://github.com/ethcore/jsonrpc.git)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "parity-ui 1.4.0", @@ -545,8 +544,8 @@ dependencies = [ "ethcore-ipc-codegen 1.4.0", "ethcore-ipc-nano 1.4.0", "ethcore-util 1.5.0", - "json-tcp-server 0.1.0 (git+https://github.com/ethcore/json-tcp-server)", - "jsonrpc-core 3.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpc-core 4.0.0 (git+https://github.com/ethcore/jsonrpc.git)", + "jsonrpc-tcp-server 0.1.0 (git+https://github.com/ethcore/jsonrpc.git)", "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)", "mio 0.5.1 (git+https://github.com/ethcore/mio?branch=v0.5.x)", @@ -831,39 +830,10 @@ name = "itoa" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "json-ipc-server" -version = "0.2.4" -source = "git+https://github.com/ethcore/json-ipc-server.git#4642cd03ec1d23db89df80d22d5a88e7364ab885" -dependencies = [ - "bytes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-core 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)", - "mio 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "miow 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "json-tcp-server" -version = "0.1.0" -source = "git+https://github.com/ethcore/json-tcp-server#c2858522274ae56042472bb5d22845a1b85e5338" -dependencies = [ - "bytes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-core 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)", - "mio 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.1.3 (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" +version = "4.0.0" +source = "git+https://github.com/ethcore/jsonrpc.git#59919f9f0a2ebb675670b72430803605d868904c" 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)", @@ -875,14 +845,44 @@ dependencies = [ [[package]] name = "jsonrpc-http-server" version = "6.1.1" -source = "git+https://github.com/ethcore/jsonrpc-http-server.git#cd6d4cb37d672cc3057aecd0692876f9e85f3ba5" +source = "git+https://github.com/ethcore/jsonrpc.git#59919f9f0a2ebb675670b72430803605d868904c" dependencies = [ "hyper 0.9.4 (git+https://github.com/ethcore/hyper)", - "jsonrpc-core 3.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpc-core 4.0.0 (git+https://github.com/ethcore/jsonrpc.git)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "unicase 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "jsonrpc-ipc-server" +version = "0.2.4" +source = "git+https://github.com/ethcore/jsonrpc.git#59919f9f0a2ebb675670b72430803605d868904c" +dependencies = [ + "bytes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpc-core 4.0.0 (git+https://github.com/ethcore/jsonrpc.git)", + "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)", + "mio 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "miow 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "slab 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "jsonrpc-tcp-server" +version = "0.1.0" +source = "git+https://github.com/ethcore/jsonrpc.git#59919f9f0a2ebb675670b72430803605d868904c" +dependencies = [ + "bytes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpc-core 4.0.0 (git+https://github.com/ethcore/jsonrpc.git)", + "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)", + "mio 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "slab 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "kernel32-sys" version = "0.2.2" @@ -2032,10 +2032,10 @@ 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 json-ipc-server 0.2.4 (git+https://github.com/ethcore/json-ipc-server.git)" = "" -"checksum json-tcp-server 0.1.0 (git+https://github.com/ethcore/json-tcp-server)" = "" -"checksum jsonrpc-core 3.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3c5094610b07f28f3edaf3947b732dadb31dbba4941d4d0c1c7a8350208f4414" -"checksum jsonrpc-http-server 6.1.1 (git+https://github.com/ethcore/jsonrpc-http-server.git)" = "" +"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)" = "" +"checksum jsonrpc-tcp-server 0.1.0 (git+https://github.com/ethcore/jsonrpc.git)" = "" "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" "checksum language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a" "checksum lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "49247ec2a285bb3dcb23cbd9c35193c025e7251bfce77c1d5da97e6362dffe7f" diff --git a/Cargo.toml b/Cargo.toml index a8d7ba794..871e2f0a1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,7 +30,6 @@ serde = "0.8.0" serde_json = "0.8.0" hyper = { version = "0.9", default-features = false } ctrlc = { git = "https://github.com/ethcore/rust-ctrlc.git" } -json-ipc-server = { git = "https://github.com/ethcore/json-ipc-server.git" } fdlimit = { path = "util/fdlimit" } ethcore = { path = "ethcore" } ethcore-util = { path = "util" } diff --git a/dapps/Cargo.toml b/dapps/Cargo.toml index 15e537820..b8b0e3c78 100644 --- a/dapps/Cargo.toml +++ b/dapps/Cargo.toml @@ -12,8 +12,8 @@ build = "build.rs" rand = "0.3.14" log = "0.3" env_logger = "0.3" -jsonrpc-core = "3.0" -jsonrpc-http-server = { git = "https://github.com/ethcore/jsonrpc-http-server.git" } +jsonrpc-core = { git = "https://github.com/ethcore/jsonrpc.git" } +jsonrpc-http-server = { git = "https://github.com/ethcore/jsonrpc.git" } hyper = { default-features = false, git = "https://github.com/ethcore/hyper" } unicase = "1.3" url = "1.0" diff --git a/dapps/src/rpc.rs b/dapps/src/rpc.rs index 625bfc269..cb595e1e1 100644 --- a/dapps/src/rpc.rs +++ b/dapps/src/rpc.rs @@ -16,13 +16,14 @@ use std::sync::{Arc, Mutex}; use hyper; -use jsonrpc_core::IoHandler; -use jsonrpc_http_server::{ServerHandler, PanicHandler, AccessControlAllowOrigin}; + +use jsonrpc_core::{IoHandler, ResponseHandler, Request, Response}; +use jsonrpc_http_server::{ServerHandler, PanicHandler, AccessControlAllowOrigin, RpcHandler}; use endpoint::{Endpoint, EndpointPath, Handler}; pub fn rpc(handler: Arc, panic_handler: Arc () + Send>>>>) -> Box { Box::new(RpcEndpoint { - handler: handler, + handler: Arc::new(RpcMiddleware::new(handler)), panic_handler: panic_handler, cors_domain: None, // NOTE [ToDr] We don't need to do any hosts validation here. It's already done in router. @@ -31,7 +32,7 @@ pub fn rpc(handler: Arc, panic_handler: Arc } struct RpcEndpoint { - handler: Arc, + handler: Arc, panic_handler: Arc () + Send>>>>, cors_domain: Option>, allowed_hosts: Option>, @@ -49,3 +50,88 @@ impl Endpoint for RpcEndpoint { )) } } + +const MIDDLEWARE_METHOD: &'static str = "eth_accounts"; + +struct RpcMiddleware { + handler: Arc, +} + +impl RpcMiddleware { + fn new(handler: Arc) -> Self { + RpcMiddleware { + handler: handler, + } + } + + /// Appends additional parameter for specific calls. + fn augment_request(&self, request: &mut Request, meta: Option) { + use jsonrpc_core::{Call, Params, to_value}; + + fn augment_call(call: &mut Call, meta: Option<&Meta>) { + match (call, meta) { + (&mut Call::MethodCall(ref mut method_call), Some(meta)) if &method_call.method == MIDDLEWARE_METHOD => { + let session = to_value(&meta.app_id); + + let params = match method_call.params { + Some(Params::Array(ref vec)) if vec.len() == 0 => Some(Params::Array(vec![session])), + // invalid params otherwise + _ => None, + }; + + method_call.params = params; + }, + _ => {} + } + } + + match *request { + Request::Single(ref mut call) => augment_call(call, meta.as_ref()), + Request::Batch(ref mut vec) => { + for mut call in vec { + augment_call(call, meta.as_ref()) + } + }, + } + } +} + +#[derive(Debug)] +struct Meta { + app_id: String, +} + +impl RpcHandler for RpcMiddleware { + type Metadata = Meta; + + fn read_metadata(&self, request: &hyper::server::Request) -> Option { + let meta = request.headers().get::() + .and_then(|referer| hyper::Url::parse(referer).ok()) + .and_then(|url| { + url.path_segments() + .and_then(|mut split| split.next()) + .map(|app_id| Meta { + app_id: app_id.to_owned(), + }) + }); + println!("{:?}, {:?}", meta, request.headers()); + meta + } + + fn handle_request(&self, request_str: &str, response_handler: H, meta: Option) where + H: ResponseHandler, Option> + 'static + { + let handler = IoHandler::convert_handler(response_handler); + let request = IoHandler::read_request(request_str); + trace!(target: "rpc", "Request metadata: {:?}", meta); + + match request { + Ok(mut request) => { + self.augment_request(&mut request, meta); + self.handler.request_handler().handle_request(request, handler, None) + }, + Err(error) => handler.send(Some(Response::from(error))), + } + } +} + diff --git a/ethcore/src/account_provider.rs b/ethcore/src/account_provider/mod.rs similarity index 80% rename from ethcore/src/account_provider.rs rename to ethcore/src/account_provider/mod.rs index e906aefe9..75c3e2683 100644 --- a/ethcore/src/account_provider.rs +++ b/ethcore/src/account_provider/mod.rs @@ -16,9 +16,12 @@ //! Account management. -use std::{fs, fmt}; +mod stores; + +use self::stores::{AddressBook, DappsSettingsStore}; + +use std::fmt; use std::collections::HashMap; -use std::path::PathBuf; use std::time::{Instant, Duration}; use util::{Mutex, RwLock}; use ethstore::{SecretStore, Error as SSError, SafeAccount, EthStore}; @@ -91,84 +94,16 @@ impl KeyDirectory for NullDir { } } -/// Disk-backed map from Address to String. Uses JSON. -struct AddressBook { - path: PathBuf, - cache: HashMap, - transient: bool, -} - -impl AddressBook { - pub fn new(path: String) -> Self { - trace!(target: "addressbook", "new({})", path); - let mut path: PathBuf = path.into(); - path.push("address_book.json"); - trace!(target: "addressbook", "path={:?}", path); - let mut r = AddressBook { - path: path, - cache: HashMap::new(), - transient: false, - }; - r.revert(); - r - } - - pub fn transient() -> Self { - let mut book = AddressBook::new(Default::default()); - book.transient = true; - book - } - - pub fn get(&self) -> HashMap { - self.cache.clone() - } - - pub fn set_name(&mut self, a: Address, name: String) { - let mut x = self.cache.get(&a) - .cloned() - .unwrap_or_else(|| AccountMeta {name: Default::default(), meta: "{}".to_owned(), uuid: None}); - x.name = name; - self.cache.insert(a, x); - self.save(); - } - - pub fn set_meta(&mut self, a: Address, meta: String) { - let mut x = self.cache.get(&a) - .cloned() - .unwrap_or_else(|| AccountMeta {name: "Anonymous".to_owned(), meta: Default::default(), uuid: None}); - x.meta = meta; - self.cache.insert(a, x); - self.save(); - } - - fn revert(&mut self) { - if self.transient { return; } - trace!(target: "addressbook", "revert"); - let _ = fs::File::open(self.path.clone()) - .map_err(|e| trace!(target: "addressbook", "Couldn't open address book: {}", e)) - .and_then(|f| AccountMeta::read_address_map(&f) - .map_err(|e| warn!(target: "addressbook", "Couldn't read address book: {}", e)) - .and_then(|m| { self.cache = m; Ok(()) }) - ); - } - - fn save(&mut self) { - if self.transient { return; } - trace!(target: "addressbook", "save"); - let _ = fs::File::create(self.path.clone()) - .map_err(|e| warn!(target: "addressbook", "Couldn't open address book for writing: {}", e)) - .and_then(|mut f| AccountMeta::write_address_map(&self.cache, &mut f) - .map_err(|e| warn!(target: "addressbook", "Couldn't write to address book: {}", e)) - ); - } -} +/// Dapp identifier +pub type DappId = String; /// Account management. /// Responsible for unlocking accounts. pub struct AccountProvider { unlocked: Mutex>, sstore: Box, - address_book: Mutex, + address_book: RwLock, + dapps_settings: RwLock, } impl AccountProvider { @@ -176,7 +111,8 @@ impl AccountProvider { pub fn new(sstore: Box) -> Self { AccountProvider { unlocked: Mutex::new(HashMap::new()), - address_book: Mutex::new(AddressBook::new(sstore.local_path().into())), + address_book: RwLock::new(AddressBook::new(sstore.local_path().into())), + dapps_settings: RwLock::new(DappsSettingsStore::new(sstore.local_path().into())), sstore: sstore, } } @@ -185,7 +121,8 @@ impl AccountProvider { pub fn transient_provider() -> Self { AccountProvider { unlocked: Mutex::new(HashMap::new()), - address_book: Mutex::new(AddressBook::transient()), + address_book: RwLock::new(AddressBook::transient()), + dapps_settings: RwLock::new(DappsSettingsStore::transient()), sstore: Box::new(EthStore::open(Box::new(NullDir::default())) .expect("NullDir load always succeeds; qed")) } @@ -230,19 +167,31 @@ impl AccountProvider { Ok(accounts) } + /// 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)) + } + + /// Sets addresses visile for dapp. + pub fn set_dapps_addresses(&self, dapp: DappId, addresses: Vec
) -> Result<(), Error> { + self.dapps_settings.write().set_accounts(dapp, addresses); + Ok(()) + } + /// Returns each address along with metadata. pub fn addresses_info(&self) -> Result, Error> { - Ok(self.address_book.lock().get()) + Ok(self.address_book.read().get()) } /// Returns each address along with metadata. pub fn set_address_name(&self, account: Address, name: String) -> Result<(), Error> { - Ok(self.address_book.lock().set_name(account, name)) + Ok(self.address_book.write().set_name(account, name)) } /// Returns each address along with metadata. pub fn set_address_meta(&self, account: Address, meta: String) -> Result<(), Error> { - Ok(self.address_book.lock().set_meta(account, meta)) + Ok(self.address_book.write().set_meta(account, meta)) } /// Returns each account along with name and meta. @@ -373,23 +322,9 @@ impl AccountProvider { #[cfg(test)] mod tests { - use super::{AccountProvider, AddressBook, Unlock}; - use std::collections::HashMap; + use super::{AccountProvider, Unlock}; use std::time::Instant; - use ethjson::misc::AccountMeta; use ethstore::ethkey::{Generator, Random}; - use devtools::RandomTempPath; - - #[test] - fn should_save_and_reload_address_book() { - let temp = RandomTempPath::create_dir(); - let path = temp.as_str().to_owned(); - let mut b = AddressBook::new(path.clone()); - b.set_name(1.into(), "One".to_owned()); - b.set_meta(1.into(), "{1:1}".to_owned()); - let b = AddressBook::new(path); - assert_eq!(b.get(), hash_map![1.into() => AccountMeta{name: "One".to_owned(), meta: "{1:1}".to_owned(), uuid: None}]); - } #[test] fn unlock_account_temp() { @@ -427,4 +362,17 @@ mod tests { ap.unlocked.lock().get_mut(&kp.address()).unwrap().unlock = Unlock::Timed(Instant::now()); assert!(ap.sign(kp.address(), None, Default::default()).is_err()); } + + #[test] + fn should_set_dapps_addresses() { + // given + let ap = AccountProvider::transient_provider(); + let app = "app1".to_owned(); + + // when + ap.set_dapps_addresses(app.clone(), vec![1.into(), 2.into()]).unwrap(); + + // then + assert_eq!(ap.dapps_addresses(app.clone()).unwrap(), vec![1.into(), 2.into()]); + } } diff --git a/ethcore/src/account_provider/stores.rs b/ethcore/src/account_provider/stores.rs new file mode 100644 index 000000000..cfc81f495 --- /dev/null +++ b/ethcore/src/account_provider/stores.rs @@ -0,0 +1,247 @@ +// Copyright 2015, 2016 Ethcore (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +//! Address Book and Dapps Settings Store + +use std::{fs, fmt, hash, ops}; +use std::collections::HashMap; +use std::path::PathBuf; + +use ethstore::ethkey::Address; +use ethjson::misc::{AccountMeta, DappsSettings as JsonSettings}; +use account_provider::DappId; + +/// Disk-backed map from Address to String. Uses JSON. +pub struct AddressBook { + cache: DiskMap, +} + +impl AddressBook { + /// Creates new address book at given directory. + pub fn new(path: String) -> Self { + let mut r = AddressBook { + cache: DiskMap::new(path, "address_book.json".into()) + }; + r.cache.revert(AccountMeta::read_address_map); + r + } + + /// Creates transient address book (no changes are saved to disk). + pub fn transient() -> Self { + AddressBook { + cache: DiskMap::transient() + } + } + + /// Get the address book. + pub fn get(&self) -> HashMap { + self.cache.clone() + } + + fn save(&self) { + self.cache.save(AccountMeta::write_address_map) + } + + /// Sets new name for given address. + pub fn set_name(&mut self, a: Address, name: String) { + { + let mut x = self.cache.entry(a) + .or_insert_with(|| AccountMeta {name: Default::default(), meta: "{}".to_owned(), uuid: None}); + x.name = name; + } + self.save(); + } + + /// Sets new meta for given address. + pub fn set_meta(&mut self, a: Address, meta: String) { + { + let mut x = self.cache.entry(a) + .or_insert_with(|| AccountMeta {name: "Anonymous".to_owned(), meta: Default::default(), uuid: None}); + x.meta = meta; + } + self.save(); + } +} + +/// Dapps user settings +#[derive(Debug, Default, Clone, Eq, PartialEq)] +pub struct DappsSettings { + /// A list of visible accounts + pub accounts: Vec
, +} + +impl From for DappsSettings { + fn from(s: JsonSettings) -> Self { + DappsSettings { + accounts: s.accounts.into_iter().map(Into::into).collect(), + } + } +} + +impl From for JsonSettings { + fn from(s: DappsSettings) -> Self { + JsonSettings { + accounts: s.accounts.into_iter().map(Into::into).collect(), + } + } +} + +/// Disk-backed map from DappId to Settings. Uses JSON. +pub struct DappsSettingsStore { + cache: DiskMap, +} + +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()) + }; + r.cache.revert(JsonSettings::read_dapps_settings); + r + } + + /// Creates transient store (no changes are saved to disk). + pub fn transient() -> Self { + DappsSettingsStore { + cache: DiskMap::transient() + } + } + + /// Get copy of the dapps settings + pub fn get(&self) -> HashMap { + self.cache.clone() + } + + fn save(&self) { + self.cache.save(JsonSettings::write_dapps_settings) + } + + pub fn set_accounts(&mut self, id: DappId, accounts: Vec
) { + { + let mut settings = self.cache.entry(id).or_insert_with(DappsSettings::default); + settings.accounts = accounts; + } + self.save(); + } +} + +/// Disk-serializable HashMap +#[derive(Debug)] +struct DiskMap { + path: PathBuf, + cache: HashMap, + transient: bool, +} + +impl ops::Deref for DiskMap { + type Target = HashMap; + fn deref(&self) -> &Self::Target { + &self.cache + } +} + +impl ops::DerefMut for DiskMap { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.cache + } +} + +impl DiskMap { + pub fn new(path: String, file_name: String) -> Self { + trace!(target: "diskmap", "new({})", path); + let mut path: PathBuf = path.into(); + path.push(file_name); + trace!(target: "diskmap", "path={:?}", path); + DiskMap { + path: path, + cache: HashMap::new(), + transient: false, + } + } + + pub fn transient() -> Self { + let mut map = DiskMap::new(Default::default(), "diskmap.json".into()); + map.transient = true; + map + } + + fn revert(&mut self, read: F) where + F: Fn(fs::File) -> Result, E>, + E: fmt::Display, + { + if self.transient { return; } + trace!(target: "diskmap", "revert {:?}", self.path); + let _ = fs::File::open(self.path.clone()) + .map_err(|e| trace!(target: "diskmap", "Couldn't open disk map: {}", e)) + .and_then(|f| read(f).map_err(|e| warn!(target: "diskmap", "Couldn't read disk map: {}", e))) + .and_then(|m| { + self.cache = m; + Ok(()) + }); + } + + fn save(&self, write: F) where + F: Fn(&HashMap, &mut fs::File) -> Result<(), E>, + E: fmt::Display, + { + if self.transient { return; } + trace!(target: "diskmap", "save {:?}", self.path); + let _ = fs::File::create(self.path.clone()) + .map_err(|e| warn!(target: "diskmap", "Couldn't open disk map for writing: {}", e)) + .and_then(|mut f| { + write(&self.cache, &mut f).map_err(|e| warn!(target: "diskmap", "Couldn't write to disk map: {}", e)) + }); + } +} + +#[cfg(test)] +mod tests { + use super::{AddressBook, DappsSettingsStore, DappsSettings}; + use std::collections::HashMap; + use ethjson::misc::AccountMeta; + use devtools::RandomTempPath; + + #[test] + fn should_save_and_reload_address_book() { + let temp = RandomTempPath::create_dir(); + let path = temp.as_str().to_owned(); + let mut b = AddressBook::new(path.clone()); + b.set_name(1.into(), "One".to_owned()); + b.set_meta(1.into(), "{1:1}".to_owned()); + let b = AddressBook::new(path); + 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()], + } + ]); + } +} diff --git a/json/src/misc/account_meta.rs b/json/src/misc/account_meta.rs index 242b58a01..400f9b8df 100644 --- a/json/src/misc/account_meta.rs +++ b/json/src/misc/account_meta.rs @@ -51,7 +51,7 @@ impl AccountMeta { ) } - /// Write a hash map of Address -> AccountMeta. + /// Write a hash map of Address -> AccountMeta. pub fn write_address_map(m: &HashMap, writer: &mut W) -> Result<(), serde_json::Error> where W: Write { serde_json::to_writer(writer, &m.iter().map(|(a, m)| (a.clone().into(), m)).collect::>()) } diff --git a/json/src/misc/dapps_settings.rs b/json/src/misc/dapps_settings.rs new file mode 100644 index 000000000..893e7e93e --- /dev/null +++ b/json/src/misc/dapps_settings.rs @@ -0,0 +1,51 @@ +// Copyright 2015, 2016 Ethcore (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +//! Dapps settings de/serialization. + +use std::io; +use std::collections::HashMap; +use serde_json; +use hash; + +type DappId = String; + +/// Settings for specific dapp. +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct DappsSettings { + /// A list of accounts this Dapp can see. + pub accounts: Vec, +} + +impl DappsSettings { + /// Read a hash map of DappId -> DappsSettings + pub fn read_dapps_settings(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 DappId -> DappsSettings + pub fn write_dapps_settings(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 5db868d03..baab83d08 100644 --- a/json/src/misc/mod.rs +++ b/json/src/misc/mod.rs @@ -17,5 +17,7 @@ //! Misc deserialization. mod account_meta; +mod dapps_settings; +pub use self::dapps_settings::DappsSettings; pub use self::account_meta::AccountMeta; diff --git a/parity/main.rs b/parity/main.rs index 274d29de2..c125e87f6 100644 --- a/parity/main.rs +++ b/parity/main.rs @@ -44,8 +44,6 @@ extern crate serde_json; extern crate rlp; extern crate ethcore_hash_fetch as hash_fetch; -extern crate json_ipc_server as jsonipc; - extern crate ethcore_ipc_hypervisor as hypervisor; extern crate ethcore_rpc; diff --git a/parity/rpc.rs b/parity/rpc.rs index 59279eaea..52a5bcc0f 100644 --- a/parity/rpc.rs +++ b/parity/rpc.rs @@ -19,14 +19,12 @@ use std::sync::Arc; use std::net::SocketAddr; use std::io; use io::PanicHandler; -use ethcore_rpc::{RpcServerError, RpcServer as Server}; -use jsonipc; +use ethcore_rpc::{RpcServerError, RpcServer as Server, IpcServerError}; use rpc_apis; use rpc_apis::ApiSet; use helpers::parity_ipc_path; -pub use jsonipc::Server as IpcServer; -pub use ethcore_rpc::Server as HttpServer; +pub use ethcore_rpc::{IpcServer, Server as HttpServer}; #[derive(Debug, PartialEq)] pub struct HttpConfiguration { @@ -126,7 +124,7 @@ pub fn new_ipc(conf: IpcConfiguration, deps: &Dependencies) -> Result Result { let server = try!(setup_rpc_server(apis, dependencies)); match server.start_ipc(addr) { - Err(jsonipc::Error::Io(io_error)) => Err(format!("RPC io error: {}", io_error)), + Err(IpcServerError::Io(io_error)) => Err(format!("RPC io error: {}", io_error)), Err(any_error) => Err(format!("Rpc error: {:?}", any_error)), Ok(server) => Ok(server) } diff --git a/rpc/Cargo.toml b/rpc/Cargo.toml index 4a8c4d76a..68470b963 100644 --- a/rpc/Cargo.toml +++ b/rpc/Cargo.toml @@ -12,8 +12,9 @@ build = "build.rs" log = "0.3" serde = "0.8" serde_json = "0.8" -jsonrpc-core = "3.0" -jsonrpc-http-server = { git = "https://github.com/ethcore/jsonrpc-http-server.git" } +jsonrpc-core = { git = "https://github.com/ethcore/jsonrpc.git" } +jsonrpc-http-server = { git = "https://github.com/ethcore/jsonrpc.git" } +jsonrpc-ipc-server = { git = "https://github.com/ethcore/jsonrpc.git" } ethcore-io = { path = "../util/io" } ethcore-util = { path = "../util" } ethcore = { path = "../ethcore" } @@ -30,7 +31,6 @@ rustc-serialize = "0.3" transient-hashmap = "0.1" serde_macros = { version = "0.8.0", optional = true } clippy = { version = "0.0.96", optional = true} -json-ipc-server = { git = "https://github.com/ethcore/json-ipc-server.git" } ethcore-ipc = { path = "../ipc/rpc" } time = "0.1" diff --git a/rpc/src/lib.rs b/rpc/src/lib.rs index 3b67ae7f0..e02a18509 100644 --- a/rpc/src/lib.rs +++ b/rpc/src/lib.rs @@ -32,7 +32,7 @@ extern crate ethcrypto as crypto; extern crate ethstore; extern crate ethsync; extern crate transient_hashmap; -extern crate json_ipc_server as ipc; +extern crate jsonrpc_ipc_server as ipc; extern crate ethcore_ipc; extern crate time; extern crate rlp; @@ -51,8 +51,9 @@ extern crate ethcore_devtools as devtools; use std::sync::Arc; use std::net::SocketAddr; use io::PanicHandler; -use self::jsonrpc_core::{IoHandler, IoDelegate}; +use jsonrpc_core::{IoHandler, IoDelegate}; +pub use ipc::{Server as IpcServer, Error as IpcServerError}; pub use jsonrpc_http_server::{ServerBuilder, Server, RpcServerError}; pub mod v1; pub use v1::{SigningQueue, SignerService, ConfirmationsQueue, NetworkSettings}; @@ -66,7 +67,7 @@ pub trait Extendable { /// Http server. pub struct RpcServer { - handler: Arc, + handler: Arc, } impl Extendable for RpcServer { diff --git a/rpc/src/v1/impls/eth.rs b/rpc/src/v1/impls/eth.rs index 5f1449e07..6b9f47de3 100644 --- a/rpc/src/v1/impls/eth.rs +++ b/rpc/src/v1/impls/eth.rs @@ -20,7 +20,6 @@ extern crate ethash; use std::io::{Write}; use std::process::{Command, Stdio}; -use std::collections::BTreeSet; use std::thread; use std::time::{Instant, Duration}; use std::sync::{Arc, Weak}; @@ -46,7 +45,7 @@ use self::ethash::SeedHashCompute; use v1::traits::Eth; use v1::types::{ RichBlock, Block, BlockTransactions, BlockNumber, Bytes, SyncStatus, SyncInfo, - Transaction, CallRequest, Index, Filter, Log, Receipt, Work, + Transaction, CallRequest, Index, Filter, Log, Receipt, Work, DappId, H64 as RpcH64, H256 as RpcH256, H160 as RpcH160, U256 as RpcU256, }; use v1::helpers::{CallRequest as CRequest, errors, limit_logs}; @@ -335,15 +334,15 @@ impl Eth for EthClient where Ok(RpcU256::from(default_gas_price(&*client, &*miner))) } - fn accounts(&self) -> Result, Error> { + fn accounts(&self, id: Trailing) -> Result, Error> { try!(self.active()); - let store = take_weak!(self.accounts); - let accounts = try!(store.accounts().map_err(|e| errors::internal("Could not fetch accounts.", e))); - let addresses = try!(store.addresses_info().map_err(|e| errors::internal("Could not fetch accounts.", e))); + let dapp = id.0; - let set: BTreeSet
= accounts.into_iter().chain(addresses.keys().cloned()).collect(); - Ok(set.into_iter().map(Into::into).collect()) + let store = take_weak!(self.accounts); + let accounts = try!(store.dapps_addresses(dapp.into()).map_err(|e| errors::internal("Could not fetch accounts.", e))); + + Ok(accounts.into_iter().map(Into::into).collect()) } fn block_number(&self) -> Result { diff --git a/rpc/src/v1/impls/parity_accounts.rs b/rpc/src/v1/impls/parity_accounts.rs index 2644c59e3..25d3c0904 100644 --- a/rpc/src/v1/impls/parity_accounts.rs +++ b/rpc/src/v1/impls/parity_accounts.rs @@ -25,7 +25,7 @@ use ethcore::client::MiningBlockChainClient; use jsonrpc_core::{Value, Error, to_value}; use v1::traits::ParityAccounts; -use v1::types::{H160 as RpcH160, H256 as RpcH256}; +use v1::types::{H160 as RpcH160, H256 as RpcH256, DappId}; use v1::helpers::errors; /// Account management (personal) rpc implementation. @@ -143,6 +143,15 @@ impl ParityAccounts for ParityAccountsClient where C: MiningBlock Ok(false) } + 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) + .map_err(|e| errors::account("Couldn't set dapps addresses.", e)) + .map(|_| true) + } + fn import_geth_accounts(&self, addresses: Vec) -> Result, Error> { let store = take_weak!(self.accounts); diff --git a/rpc/src/v1/tests/eth.rs b/rpc/src/v1/tests/eth.rs index 2f5131f32..7894fa111 100644 --- a/rpc/src/v1/tests/eth.rs +++ b/rpc/src/v1/tests/eth.rs @@ -30,7 +30,7 @@ use devtools::RandomTempPath; use util::Hashable; use io::IoChannel; use util::{U256, H256, Uint, Address}; -use jsonrpc_core::IoHandler; +use jsonrpc_core::{IoHandler, GenericIoHandler}; use ethjson::blockchain::BlockChain; use v1::impls::{EthClient, SigningUnsafeClient}; diff --git a/rpc/src/v1/tests/mocked/eth.rs b/rpc/src/v1/tests/mocked/eth.rs index 861bb5234..2f31aa4e1 100644 --- a/rpc/src/v1/tests/mocked/eth.rs +++ b/rpc/src/v1/tests/mocked/eth.rs @@ -31,7 +31,7 @@ use ethcore::transaction::{Transaction, Action}; use ethcore::miner::{ExternalMiner, MinerService}; use ethsync::SyncState; -use jsonrpc_core::IoHandler; +use jsonrpc_core::{IoHandler, GenericIoHandler}; use v1::{Eth, EthClient, EthClientOptions, EthFilter, EthFilterClient, EthSigning, SigningUnsafeClient}; use v1::tests::helpers::{TestSyncProvider, Config, TestMinerService, TestSnapshotService}; @@ -357,15 +357,15 @@ fn rpc_eth_accounts() { let address = tester.accounts_provider.new_account("").unwrap(); let address2 = Address::default(); - tester.accounts_provider.set_address_name(address2, "Test Account".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":[""#.to_owned() - + &format!("0x{:?}", address2) - + r#"",""# - + &format!("0x{:?}", address) - + r#""],"id":1}"#; + let response = r#"{"jsonrpc":"2.0","result":[],"id":1}"#; + assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned())); + // when we add visible address it should return that. + tester.accounts_provider.set_dapps_addresses("app1".into(), vec![10.into()]).unwrap(); + let request = r#"{"jsonrpc": "2.0", "method": "eth_accounts", "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())); } diff --git a/rpc/src/v1/tests/mocked/net.rs b/rpc/src/v1/tests/mocked/net.rs index 0a5eb43e7..37ef84fca 100644 --- a/rpc/src/v1/tests/mocked/net.rs +++ b/rpc/src/v1/tests/mocked/net.rs @@ -15,7 +15,7 @@ // along with Parity. If not, see . use std::sync::Arc; -use jsonrpc_core::IoHandler; +use jsonrpc_core::{IoHandler, GenericIoHandler}; use v1::{Net, NetClient}; use v1::tests::helpers::{Config, TestSyncProvider}; diff --git a/rpc/src/v1/tests/mocked/parity.rs b/rpc/src/v1/tests/mocked/parity.rs index 5226e2f96..9b4daaccd 100644 --- a/rpc/src/v1/tests/mocked/parity.rs +++ b/rpc/src/v1/tests/mocked/parity.rs @@ -23,7 +23,7 @@ use ethcore::client::{TestBlockChainClient}; use ethcore::miner::LocalTransactionStatus; use ethstore::ethkey::{Generator, Random}; -use jsonrpc_core::IoHandler; +use jsonrpc_core::{IoHandler, GenericIoHandler}; use v1::{Parity, ParityClient}; use v1::helpers::{SignerService, NetworkSettings}; use v1::tests::helpers::{TestSyncProvider, Config, TestMinerService}; diff --git a/rpc/src/v1/tests/mocked/parity_accounts.rs b/rpc/src/v1/tests/mocked/parity_accounts.rs index c5ed4172e..bd6e1ffba 100644 --- a/rpc/src/v1/tests/mocked/parity_accounts.rs +++ b/rpc/src/v1/tests/mocked/parity_accounts.rs @@ -19,7 +19,7 @@ use std::sync::Arc; use ethcore::account_provider::AccountProvider; use ethcore::client::TestBlockChainClient; -use jsonrpc_core::IoHandler; +use jsonrpc_core::{IoHandler, GenericIoHandler}; use v1::{ParityAccounts, ParityAccountsClient}; struct ParityAccountsTester { @@ -116,3 +116,18 @@ fn should_be_able_to_set_meta() { assert_eq!(res, Some(response)); } + +#[test] +fn rpc_parity_set_dapps_accounts() { + // given + let tester = setup(); + assert_eq!(tester.accounts.dapps_addresses("app1".into()).unwrap(), vec![]); + + // when + let request = r#"{"jsonrpc": "2.0", "method": "parity_setDappsAddresses","params":["app1",["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.dapps_addresses("app1".into()).unwrap(), vec![10.into()]); +} diff --git a/rpc/src/v1/tests/mocked/parity_set.rs b/rpc/src/v1/tests/mocked/parity_set.rs index 3202374a7..01f33e251 100644 --- a/rpc/src/v1/tests/mocked/parity_set.rs +++ b/rpc/src/v1/tests/mocked/parity_set.rs @@ -23,7 +23,7 @@ use ethcore::miner::MinerService; use ethcore::client::TestBlockChainClient; use ethsync::ManageNetwork; -use jsonrpc_core::IoHandler; +use jsonrpc_core::{IoHandler, GenericIoHandler}; use v1::{ParitySet, ParitySetClient}; use v1::tests::helpers::{TestMinerService, TestFetch}; use super::manage_network::TestManageNetwork; diff --git a/rpc/src/v1/tests/mocked/personal.rs b/rpc/src/v1/tests/mocked/personal.rs index 6e2de1e2e..a1e8fe982 100644 --- a/rpc/src/v1/tests/mocked/personal.rs +++ b/rpc/src/v1/tests/mocked/personal.rs @@ -16,7 +16,7 @@ use std::sync::Arc; use std::str::FromStr; -use jsonrpc_core::IoHandler; +use jsonrpc_core::{IoHandler, GenericIoHandler}; use util::{U256, Uint, Address}; use ethcore::account_provider::AccountProvider; use v1::{PersonalClient, Personal}; diff --git a/rpc/src/v1/tests/mocked/rpc.rs b/rpc/src/v1/tests/mocked/rpc.rs index b2c340d94..44406f4e3 100644 --- a/rpc/src/v1/tests/mocked/rpc.rs +++ b/rpc/src/v1/tests/mocked/rpc.rs @@ -15,7 +15,7 @@ // along with Parity. If not, see . use std::collections::BTreeMap; -use jsonrpc_core::IoHandler; +use jsonrpc_core::{IoHandler, GenericIoHandler}; use v1::{Rpc, RpcClient}; diff --git a/rpc/src/v1/tests/mocked/signer.rs b/rpc/src/v1/tests/mocked/signer.rs index e2ba580e0..912dddc81 100644 --- a/rpc/src/v1/tests/mocked/signer.rs +++ b/rpc/src/v1/tests/mocked/signer.rs @@ -23,7 +23,7 @@ use ethcore::client::TestBlockChainClient; use ethcore::transaction::{Transaction, Action}; use rlp::encode; -use jsonrpc_core::IoHandler; +use jsonrpc_core::{IoHandler, GenericIoHandler}; use v1::{SignerClient, Signer}; use v1::tests::helpers::TestMinerService; use v1::helpers::{SigningQueue, SignerService, FilledTransactionRequest, ConfirmationPayload}; diff --git a/rpc/src/v1/tests/mocked/signing.rs b/rpc/src/v1/tests/mocked/signing.rs index 7431bc45e..629fbe707 100644 --- a/rpc/src/v1/tests/mocked/signing.rs +++ b/rpc/src/v1/tests/mocked/signing.rs @@ -15,10 +15,10 @@ // along with Parity. If not, see . use std::str::FromStr; -use std::sync::Arc; +use std::sync::{mpsc, Arc}; use rlp; -use jsonrpc_core::{IoHandler, Success}; +use jsonrpc_core::{IoHandler, Success, GenericIoHandler}; use v1::impls::SigningQueueClient; use v1::traits::{EthSigning, ParitySigning, Parity}; use v1::helpers::{SignerService, SigningQueue}; @@ -87,13 +87,16 @@ fn should_add_sign_to_queue() { let response = r#"{"jsonrpc":"2.0","result":"0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","id":1}"#; // then - let async_result = tester.io.handle_request(&request).unwrap(); + let (tx, rx) = mpsc::channel(); + tester.io.handle_request(&request, move |response| { + tx.send(response).unwrap(); + }); assert_eq!(tester.signer.requests().len(), 1); // respond tester.signer.request_confirmed(1.into(), Ok(ConfirmationResponse::Signature(0.into()))); - assert!(async_result.on_result(move |res| { - assert_eq!(res, response.to_owned()); - })); + + let res = rx.try_recv().unwrap(); + assert_eq!(res, Some(response.to_owned())); } #[test] @@ -227,13 +230,16 @@ fn should_add_transaction_to_queue() { let response = r#"{"jsonrpc":"2.0","result":"0x0000000000000000000000000000000000000000000000000000000000000000","id":1}"#; // then - let async_result = tester.io.handle_request(&request).unwrap(); + let (tx, rx) = mpsc::channel(); + tester.io.handle_request(&request, move |response| { + tx.send(response).unwrap(); + }); assert_eq!(tester.signer.requests().len(), 1); // respond tester.signer.request_confirmed(1.into(), Ok(ConfirmationResponse::SendTransaction(0.into()))); - assert!(async_result.on_result(move |res| { - assert_eq!(res, response.to_owned()); - })); + + let res = rx.try_recv().unwrap(); + assert_eq!(res, Some(response.to_owned())); } #[test] @@ -289,14 +295,17 @@ fn should_add_sign_transaction_to_the_queue() { r#"}},"id":1}"#; // then + let (tx, rx) = mpsc::channel(); tester.miner.last_nonces.write().insert(address.clone(), U256::zero()); - let async_result = tester.io.handle_request(&request).unwrap(); + tester.io.handle_request(&request, move |response| { + tx.send(response).unwrap(); + }); assert_eq!(tester.signer.requests().len(), 1); // respond tester.signer.request_confirmed(1.into(), Ok(ConfirmationResponse::SignTransaction(t.into()))); - assert!(async_result.on_result(move |res| { - assert_eq!(res, response.to_owned()); - })); + + let res = rx.try_recv().unwrap(); + assert_eq!(res, Some(response.to_owned())); } #[test] @@ -387,11 +396,14 @@ fn should_add_decryption_to_the_queue() { let response = r#"{"jsonrpc":"2.0","result":"0x0102","id":1}"#; // then - let async_result = tester.io.handle_request(&request).unwrap(); + let (tx, rx) = mpsc::channel(); + tester.io.handle_request(&request, move |response| { + tx.send(response).unwrap(); + }); assert_eq!(tester.signer.requests().len(), 1); // respond tester.signer.request_confirmed(1.into(), Ok(ConfirmationResponse::Decrypt(vec![0x1, 0x2].into()))); - assert!(async_result.on_result(move |res| { - assert_eq!(res, response.to_owned()); - })); + + let res = rx.try_recv().unwrap(); + assert_eq!(res, Some(response.to_owned())); } diff --git a/rpc/src/v1/tests/mocked/web3.rs b/rpc/src/v1/tests/mocked/web3.rs index b9f80b0a8..c3bd79110 100644 --- a/rpc/src/v1/tests/mocked/web3.rs +++ b/rpc/src/v1/tests/mocked/web3.rs @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -use jsonrpc_core::IoHandler; +use jsonrpc_core::{IoHandler, GenericIoHandler}; use util::version; use v1::{Web3, Web3Client}; diff --git a/rpc/src/v1/traits/eth.rs b/rpc/src/v1/traits/eth.rs index 6308be324..64a87c175 100644 --- a/rpc/src/v1/traits/eth.rs +++ b/rpc/src/v1/traits/eth.rs @@ -17,7 +17,7 @@ //! Eth rpc interface. use jsonrpc_core::Error; -use v1::types::{RichBlock, BlockNumber, Bytes, CallRequest, Filter, FilterChanges, Index}; +use v1::types::{RichBlock, BlockNumber, Bytes, CallRequest, Filter, FilterChanges, Index, DappId}; use v1::types::{Log, Receipt, SyncStatus, Transaction, Work}; use v1::types::{H64, H160, H256, U256}; @@ -52,7 +52,7 @@ build_rpc_trait! { /// Returns accounts list. #[rpc(name = "eth_accounts")] - fn accounts(&self) -> Result, Error>; + fn accounts(&self, Trailing) -> Result, Error>; /// Returns highest block number. #[rpc(name = "eth_blockNumber")] diff --git a/rpc/src/v1/traits/parity_accounts.rs b/rpc/src/v1/traits/parity_accounts.rs index 0f62f59d1..e8a24bbaf 100644 --- a/rpc/src/v1/traits/parity_accounts.rs +++ b/rpc/src/v1/traits/parity_accounts.rs @@ -19,7 +19,7 @@ use std::collections::BTreeMap; use jsonrpc_core::{Value, Error}; use v1::helpers::auto_args::Wrap; -use v1::types::{H160, H256}; +use v1::types::{H160, H256, DappId}; build_rpc_trait! { /// Personal Parity rpc interface. @@ -61,10 +61,14 @@ build_rpc_trait! { #[rpc(name = "parity_setAccountMeta")] fn set_account_meta(&self, H160, String) -> Result; - /// Returns accounts information. + /// Sets account visibility #[rpc(name = "parity_setAccountVisiblity")] fn set_account_visibility(&self, H160, H256, bool) -> Result; + /// Sets accounts exposed for particular dapp. + #[rpc(name = "parity_setDappsAddresses")] + fn set_dapps_addresses(&self, DappId, Vec) -> Result; + /// 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 new file mode 100644 index 000000000..04aa80e3a --- /dev/null +++ b/rpc/src/v1/types/dapp_id.rs @@ -0,0 +1,60 @@ +// Copyright 2015, 2016 Ethcore (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +//! Dapp Id type + +/// Dapplication Internal Id +#[derive(Debug, Default, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)] +pub struct DappId(pub String); + +impl Into for DappId { + fn into(self) -> String { + self.0 + } +} + +#[cfg(test)] +mod tests { + + use serde_json; + use super::DappId; + + #[test] + fn should_serialize_dapp_id() { + // given + let id = DappId("testapp".into()); + + // when + let res = serde_json::to_string(&id).unwrap(); + + // then + assert_eq!(res, r#""testapp""#); + } + + #[test] + fn should_deserialize_dapp_id() { + // given + let id = r#""testapp""#; + + // when + let res: DappId = serde_json::from_str(id).unwrap(); + + // then + assert_eq!(res, DappId("testapp".into())); + } + + +} diff --git a/rpc/src/v1/types/mod.rs.in b/rpc/src/v1/types/mod.rs.in index c10d6e36f..282d70c27 100644 --- a/rpc/src/v1/types/mod.rs.in +++ b/rpc/src/v1/types/mod.rs.in @@ -19,6 +19,7 @@ mod block; mod block_number; mod call_request; mod confirmations; +mod dapp_id; mod filter; mod hash; mod index; @@ -39,6 +40,7 @@ pub use self::block::{RichBlock, Block, BlockTransactions}; pub use self::block_number::BlockNumber; pub use self::call_request::CallRequest; pub use self::confirmations::{ConfirmationPayload, ConfirmationRequest, ConfirmationResponse, TransactionModification, SignRequest, DecryptRequest, Either}; +pub use self::dapp_id::DappId; pub use self::filter::{Filter, FilterChanges}; pub use self::hash::{H64, H160, H256, H512, H520, H2048}; pub use self::index::Index; diff --git a/signer/Cargo.toml b/signer/Cargo.toml index 2a3742ec8..1b91e1a33 100644 --- a/signer/Cargo.toml +++ b/signer/Cargo.toml @@ -12,7 +12,7 @@ rustc_version = "0.1" [dependencies] rand = "0.3.14" -jsonrpc-core = "3.0" +jsonrpc-core = { git = "https://github.com/ethcore/jsonrpc.git" } log = "0.3" env_logger = "0.3" parity-dapps-glue = { version = "1.4", optional = true } diff --git a/signer/src/ws_server/session.rs b/signer/src/ws_server/session.rs index 5adc3fa80..0c9283f6d 100644 --- a/signer/src/ws_server/session.rs +++ b/signer/src/ws_server/session.rs @@ -21,8 +21,8 @@ use authcode_store::AuthCodes; use std::path::{PathBuf, Path}; use std::sync::Arc; use std::str::FromStr; -use jsonrpc_core::IoHandler; -use util::{H256, Mutex, version}; +use jsonrpc_core::{IoHandler, GenericIoHandler}; +use util::{H256, version}; #[cfg(feature = "parity-ui")] mod ui { @@ -130,7 +130,7 @@ fn add_headers(mut response: ws::Response, mime: &str) -> ws::Response { } pub struct Session { - out: Arc>, + out: ws::Sender, skip_origin_validation: bool, self_origin: String, authcodes_path: PathBuf, @@ -208,15 +208,15 @@ impl ws::Handler for Session { fn on_message(&mut self, msg: ws::Message) -> ws::Result<()> { let req = try!(msg.as_text()); - if let Some(async) = self.handler.handle_request(req) { - let out = self.out.clone(); - async.on_result(move |result| { - let res = out.lock().send(result); + let out = self.out.clone(); + self.handler.handle_request(req, move |response| { + if let Some(result) = response { + let res = out.send(result); if let Err(e) = res { warn!(target: "signer", "Error while sending response: {:?}", e); } - }); - } + } + }); Ok(()) } } @@ -246,7 +246,7 @@ impl ws::Factory for Factory { fn connection_made(&mut self, sender: ws::Sender) -> Self::Handler { Session { - out: Arc::new(Mutex::new(sender)), + out: sender, handler: self.handler.clone(), skip_origin_validation: self.skip_origin_validation, self_origin: self.self_origin.clone(), diff --git a/stratum/Cargo.toml b/stratum/Cargo.toml index d300106aa..28f5208dd 100644 --- a/stratum/Cargo.toml +++ b/stratum/Cargo.toml @@ -11,8 +11,8 @@ ethcore-ipc-codegen = { path = "../ipc/codegen" } [dependencies] log = "0.3" -json-tcp-server = { git = "https://github.com/ethcore/json-tcp-server" } -jsonrpc-core = "3.0" +jsonrpc-core = { git = "https://github.com/ethcore/jsonrpc.git" } +jsonrpc-tcp-server = { git = "https://github.com/ethcore/jsonrpc.git" } mio = { git = "https://github.com/ethcore/mio", branch = "v0.5.x" } ethcore-util = { path = "../util" } ethcore-devtools = { path = "../devtools" } diff --git a/stratum/src/lib.rs b/stratum/src/lib.rs index ecec535c2..0743fba6d 100644 --- a/stratum/src/lib.rs +++ b/stratum/src/lib.rs @@ -16,7 +16,7 @@ //! Stratum protocol implementation for parity ethereum/bitcoin clients -extern crate json_tcp_server; +extern crate jsonrpc_tcp_server; extern crate jsonrpc_core; #[macro_use] extern crate log; extern crate ethcore_util as util; @@ -44,7 +44,7 @@ pub use traits::{ RemoteWorkHandler, RemoteJobDispatcher, }; -use json_tcp_server::Server as JsonRpcServer; +use jsonrpc_tcp_server::Server as JsonRpcServer; use jsonrpc_core::{IoHandler, Params, IoDelegate, to_value, from_params}; use std::sync::Arc;