diff --git a/Cargo.lock b/Cargo.lock index 830978ce2..3c350befe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1984,6 +1984,7 @@ dependencies = [ "path 0.1.0", "pretty_assertions 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "regex 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", + "registrar 0.0.1", "rlp 0.2.1", "rpassword 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "rpc-cli 1.4.0", @@ -2029,6 +2030,7 @@ dependencies = [ "parity-version 1.11.0", "parking_lot 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "registrar 0.0.1", "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2083,6 +2085,7 @@ dependencies = [ "parity-reactor 0.1.0", "parking_lot 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "registrar 0.0.1", "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2643,6 +2646,17 @@ name = "regex-syntax" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "registrar" +version = "0.0.1" +dependencies = [ + "ethabi 5.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ethabi-contract 5.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "ethabi-derive 5.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", + "keccak-hash 0.1.0", +] + [[package]] name = "reqwest" version = "0.8.1" diff --git a/Cargo.toml b/Cargo.toml index 4876e314b..dad49a073 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -72,6 +72,8 @@ journaldb = { path = "util/journaldb" } parity-dapps = { path = "dapps", optional = true } ethcore-secretstore = { path = "secret_store", optional = true } +registrar = { path = "registrar" } + [build-dependencies] rustc_version = "0.2" diff --git a/dapps/Cargo.toml b/dapps/Cargo.toml index 610d320c7..081867fbd 100644 --- a/dapps/Cargo.toml +++ b/dapps/Cargo.toml @@ -37,6 +37,7 @@ parity-reactor = { path = "../util/reactor" } parity-ui = { path = "./ui" } keccak-hash = { path = "../util/hash" } parity-version = { path = "../util/version" } +registrar = { path = "../registrar" } [dev-dependencies] env_logger = "0.4" diff --git a/dapps/src/lib.rs b/dapps/src/lib.rs index 6f901e4be..482ee3959 100644 --- a/dapps/src/lib.rs +++ b/dapps/src/lib.rs @@ -41,6 +41,7 @@ extern crate parity_hash_fetch as hash_fetch; extern crate parity_ui; extern crate keccak_hash as hash; extern crate parity_version; +extern crate registrar; #[macro_use] extern crate futures; @@ -80,7 +81,7 @@ use parking_lot::RwLock; use fetch::Fetch; use node_health::NodeHealth; -pub use hash_fetch::urlhint::ContractClient; +pub use registrar::{RegistrarClient, Asynchronous}; pub use node_health::SyncStatus; @@ -155,7 +156,7 @@ impl Middleware { pool: CpuPool, health: NodeHealth, dapps_domain: &str, - registrar: Arc, + registrar: Arc>, sync_status: Arc, fetch: F, ) -> Self { @@ -198,7 +199,7 @@ impl Middleware { dapps_path: PathBuf, extra_dapps: Vec, dapps_domain: &str, - registrar: Arc, + registrar: Arc>, sync_status: Arc, web_proxy_tokens: Arc, fetch: F, diff --git a/dapps/src/tests/helpers/mod.rs b/dapps/src/tests/helpers/mod.rs index fd60e5bf4..30017a718 100644 --- a/dapps/src/tests/helpers/mod.rs +++ b/dapps/src/tests/helpers/mod.rs @@ -24,7 +24,7 @@ use jsonrpc_http_server::{self as http, Host, DomainsValidation}; use parity_reactor::Remote; use devtools::http_client; -use hash_fetch::urlhint::ContractClient; +use registrar::{RegistrarClient, Asynchronous}; use fetch::{Fetch, Client as FetchClient}; use node_health::{NodeHealth, TimeChecker, CpuPool}; @@ -144,7 +144,7 @@ pub fn assert_security_headers_for_embed(headers: &[String]) { /// Webapps HTTP+RPC server build. pub struct ServerBuilder { dapps_path: PathBuf, - registrar: Arc, + registrar: Arc>, sync_status: Arc, web_proxy_tokens: Arc, signer_address: Option<(String, u16)>, @@ -155,7 +155,7 @@ pub struct ServerBuilder { impl ServerBuilder { /// Construct new dapps server - pub fn new>(dapps_path: P, registrar: Arc) -> Self { + pub fn new>(dapps_path: P, registrar: Arc>) -> Self { ServerBuilder { dapps_path: dapps_path.as_ref().to_owned(), registrar: registrar, @@ -227,7 +227,7 @@ impl Server { signer_address: Option<(String, u16)>, dapps_path: PathBuf, extra_dapps: Vec, - registrar: Arc, + registrar: Arc>, sync_status: Arc, web_proxy_tokens: Arc, remote: Remote, diff --git a/dapps/src/tests/helpers/registrar.rs b/dapps/src/tests/helpers/registrar.rs index 139128f9a..8668d43b1 100644 --- a/dapps/src/tests/helpers/registrar.rs +++ b/dapps/src/tests/helpers/registrar.rs @@ -18,10 +18,9 @@ use std::str; use std::sync::Arc; use std::collections::HashMap; -use futures::Future; use ethereum_types::{H256, Address}; use bytes::{Bytes, ToPretty}; -use hash_fetch::urlhint::ContractClient; +use registrar::{RegistrarClient, Asynchronous}; use parking_lot::Mutex; use rustc_hex::FromHex; @@ -62,12 +61,14 @@ impl FakeRegistrar { } } -impl ContractClient for FakeRegistrar { - fn registrar(&self) -> Result { +impl RegistrarClient for FakeRegistrar { + type Call = Asynchronous; + + fn registrar_address(&self) -> Result { Ok(REGISTRAR.parse().unwrap()) } - fn call(&self, address: Address, data: Bytes) -> Box + Send> { + fn call_contract(&self, address: Address, data: Bytes) -> Self::Call { let call = (address.to_hex(), data.to_hex()); self.calls.lock().push(call.clone()); let res = self.responses.lock().get(&call).cloned().expect(&format!("No response for call: {:?}", call)); diff --git a/hash-fetch/Cargo.toml b/hash-fetch/Cargo.toml index 85650225a..19b846c1c 100644 --- a/hash-fetch/Cargo.toml +++ b/hash-fetch/Cargo.toml @@ -18,6 +18,7 @@ ethcore-bytes = { path = "../util/bytes" } ethereum-types = "0.2" parity-reactor = { path = "../util/reactor" } keccak-hash = { path = "../util/hash" } +registrar = { path = "../registrar" } ethabi = "5.1" ethabi-derive = "5.0" diff --git a/hash-fetch/src/client.rs b/hash-fetch/src/client.rs index 9d2a5c838..3ee58e3ff 100644 --- a/hash-fetch/src/client.rs +++ b/hash-fetch/src/client.rs @@ -25,7 +25,8 @@ use hash::keccak_buffer; use fetch::{Fetch, Response, Error as FetchError, Client as FetchClient}; use futures::{Future, IntoFuture}; use parity_reactor::Remote; -use urlhint::{ContractClient, URLHintContract, URLHint, URLHintResult}; +use urlhint::{URLHintContract, URLHint, URLHintResult}; +use registrar::{RegistrarClient, Asynchronous}; use ethereum_types::H256; /// API for fetching by hash. @@ -120,14 +121,14 @@ pub struct Client { impl Client { /// Creates new instance of the `Client` given on-chain contract client and task runner. - pub fn new(contract: Arc, remote: Remote) -> Self { + pub fn new(contract: Arc>, remote: Remote) -> Self { Client::with_fetch(contract, FetchClient::new().unwrap(), remote) } } impl Client { /// Creates new instance of the `Client` given on-chain contract client, fetch service and task runner. - pub fn with_fetch(contract: Arc, fetch: F, remote: Remote) -> Self { + pub fn with_fetch(contract: Arc>, fetch: F, remote: Remote) -> Self { Client { contract: URLHintContract::new(contract), fetch: fetch, diff --git a/hash-fetch/src/lib.rs b/hash-fetch/src/lib.rs index 15c6031ce..a33639b46 100644 --- a/hash-fetch/src/lib.rs +++ b/hash-fetch/src/lib.rs @@ -31,6 +31,7 @@ extern crate mime_guess; extern crate parity_reactor; extern crate rand; extern crate rustc_hex; +extern crate registrar; pub extern crate fetch; diff --git a/hash-fetch/src/urlhint.rs b/hash-fetch/src/urlhint.rs index 39c12347a..d05dd40a2 100644 --- a/hash-fetch/src/urlhint.rs +++ b/hash-fetch/src/urlhint.rs @@ -20,31 +20,21 @@ use std::sync::Arc; use rustc_hex::ToHex; use mime::{self, Mime}; use mime_guess; -use hash::keccak; use futures::{future, Future}; use futures::future::Either; use ethereum_types::{H256, Address}; -use bytes::Bytes; +use registrar::{Registrar, RegistrarClient, Asynchronous}; -use_contract!(registry, "Registry", "res/registrar.json"); use_contract!(urlhint, "Urlhint", "res/urlhint.json"); const COMMIT_LEN: usize = 20; +const GITHUB_HINT: &'static str = "githubhint"; /// GithubHint entries with commit set as `0x0..01` should be treated /// as Github Dapp, downloadable zip files, than can be extracted, containing /// the manifest.json file along with the dapp static GITHUB_DAPP_COMMIT: &[u8; COMMIT_LEN] = &[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]; -/// RAW Contract interface. -/// Should execute transaction using current blockchain state. -pub trait ContractClient: Send + Sync { - /// Get registrar address - fn registrar(&self) -> Result; - /// Call Contract - fn call(&self, address: Address, data: Bytes) -> Box + Send>; -} - /// Github-hosted dapp. #[derive(Debug, PartialEq)] pub struct GithubApp { @@ -111,17 +101,17 @@ pub trait URLHint: Send + Sync { /// `URLHintContract` API pub struct URLHintContract { urlhint: urlhint::Urlhint, - registrar: registry::Registry, - client: Arc, + registrar: Registrar, + client: Arc>, } impl URLHintContract { /// Creates new `URLHintContract` - pub fn new(client: Arc) -> Self { + pub fn new(client: Arc>) -> Self { URLHintContract { urlhint: urlhint::Urlhint::default(), - registrar: registry::Registry::default(), - client, + registrar: Registrar::new(client.clone()), + client: client, } } } @@ -172,28 +162,19 @@ fn decode_urlhint_output(output: (String, [u8; 20], Address)) -> Option Box, Error = String> + Send> { - let address = match self.client.registrar() { - Ok(a) => a, - Err(e) => return Box::new(future::err(e)), - }; - - let client = self.client.clone(); - let get_address = self.registrar.functions().get_address(); let entries = self.urlhint.functions().entries(); - let data = get_address.input(keccak("githubhint"), "A"); + let client = self.client.clone(); - let future = client.call(address, data) - .and_then(move |output| get_address.output(&output).map_err(|e| e.to_string())) + let future = self.registrar.get_address(GITHUB_HINT) .and_then(move |addr| if !addr.is_zero() { let data = entries.input(id); - let result = client.call(addr, data) + let result = client.call_contract(addr, data) .and_then(move |output| entries.output(&output).map_err(|e| e.to_string())) .map(decode_urlhint_output); Either::B(result) } else { Either::A(future::ok(None)) - }); - + }); Box::new(future) } } @@ -257,12 +238,14 @@ pub mod tests { } } - impl ContractClient for FakeRegistrar { - fn registrar(&self) -> Result { + impl RegistrarClient for FakeRegistrar { + type Call = Asynchronous; + + fn registrar_address(&self) -> Result { Ok(REGISTRAR.parse().unwrap()) } - fn call(&self, address: Address, data: Bytes) -> Box + Send> { + fn call_contract(&self, address: Address, data: Bytes) -> Self::Call { self.calls.lock().push((address.to_hex(), data.to_hex())); let res = self.responses.lock().remove(0); Box::new(res.into_future()) diff --git a/parity/dapps.rs b/parity/dapps.rs index 449fc41bf..b1004276c 100644 --- a/parity/dapps.rs +++ b/parity/dapps.rs @@ -24,7 +24,7 @@ use ethcore::client::{Client, BlockChainClient, BlockId, CallContract}; use ethsync::LightSync; use futures::{Future, future, IntoFuture}; use hash_fetch::fetch::Client as FetchClient; -use hash_fetch::urlhint::ContractClient; +use registrar::{RegistrarClient, Asynchronous}; use light::client::LightChainClient; use light::on_demand::{self, OnDemand}; use node_health::{SyncStatus, NodeHealth}; @@ -78,13 +78,15 @@ impl FullRegistrar { } } -impl ContractClient for FullRegistrar { - fn registrar(&self) -> Result { +impl RegistrarClient for FullRegistrar { + type Call = Asynchronous; + + fn registrar_address(&self) -> Result { self.client.registrar_address() .ok_or_else(|| "Registrar not defined.".into()) } - fn call(&self, address: Address, data: Bytes) -> Box + Send> { + fn call_contract(&self, address: Address, data: Bytes) -> Self::Call { Box::new(self.client.call_contract(BlockId::Latest, address, data).into_future()) } } @@ -99,8 +101,10 @@ pub struct LightRegistrar { pub sync: Arc, } -impl ContractClient for LightRegistrar { - fn registrar(&self) -> Result { +impl RegistrarClient for LightRegistrar { + type Call = Box + Send>; + + fn registrar_address(&self) -> Result { self.client.engine().additional_params().get("registrar") .ok_or_else(|| "Registrar not defined.".into()) .and_then(|registrar| { @@ -108,7 +112,7 @@ impl ContractClient for LightRegistrar { }) } - fn call(&self, address: Address, data: Bytes) -> Box + Send> { + fn call_contract(&self, address: Address, data: Bytes) -> Self::Call { let header = self.client.best_block_header(); let env_info = self.client.env_info(BlockId::Hash(header.hash())) .ok_or_else(|| format!("Cannot fetch env info for header {}", header.hash())); @@ -154,7 +158,7 @@ impl ContractClient for LightRegistrar { pub struct Dependencies { pub node_health: NodeHealth, pub sync_status: Arc, - pub contract_client: Arc, + pub contract_client: Arc>, pub fetch: FetchClient, pub signer: Arc, pub ui_address: Option<(String, u16)>, diff --git a/parity/main.rs b/parity/main.rs index 1a1eccf12..38a502a7a 100644 --- a/parity/main.rs +++ b/parity/main.rs @@ -76,6 +76,7 @@ extern crate rpc_cli; extern crate node_filter; extern crate keccak_hash as hash; extern crate journaldb; +extern crate registrar; #[macro_use] extern crate log as rlog; diff --git a/registrar/Cargo.toml b/registrar/Cargo.toml new file mode 100644 index 000000000..3ba26e456 --- /dev/null +++ b/registrar/Cargo.toml @@ -0,0 +1,13 @@ +[package] +description = "Registar for Parity" +name = "registrar" +version = "0.0.1" +license = "GPL-3.0" +authors = ["Parity Technologies "] + +[dependencies] +futures = "0.1" +ethabi = "5.1.0" +ethabi-derive = "5.0.5" +ethabi-contract = "5.0.3" +keccak-hash = { path = "../util/hash" } diff --git a/hash-fetch/res/registrar.json b/registrar/res/registrar.json similarity index 100% rename from hash-fetch/res/registrar.json rename to registrar/res/registrar.json diff --git a/registrar/src/lib.rs b/registrar/src/lib.rs new file mode 100644 index 000000000..961fbb17e --- /dev/null +++ b/registrar/src/lib.rs @@ -0,0 +1,27 @@ +// Copyright 2015-2017 Parity Technologies (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 . + +extern crate futures; +extern crate ethabi; +extern crate keccak_hash; + +#[macro_use] +extern crate ethabi_derive; +#[macro_use] +extern crate ethabi_contract; + +mod registrar; +pub use registrar::{Registrar, RegistrarClient, Synchronous, Asynchronous}; diff --git a/registrar/src/registrar.rs b/registrar/src/registrar.rs new file mode 100644 index 000000000..c4128660d --- /dev/null +++ b/registrar/src/registrar.rs @@ -0,0 +1,77 @@ +// Copyright 2015-2017 Parity Technologies (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 . + +use futures::{Future, future, IntoFuture}; +use ethabi::{Address, Bytes}; +use std::sync::Arc; +use keccak_hash::keccak; + +use_contract!(registry, "Registry", "res/registrar.json"); + +// Maps a domain name to an Ethereum address +const DNS_A_RECORD: &'static str = "A"; + +pub type Asynchronous = Box + Send>; +pub type Synchronous = Result; + +/// Registrar is dedicated interface to access the registrar contract +/// which in turn generates an address when a client requests one +pub struct Registrar { + registrar: registry::Registry, + client: Arc>, +} + +impl Registrar { + /// Registrar constructor + pub fn new(client: Arc>) -> Self { + Self { + registrar: registry::Registry::default(), + client: client, + } + } + + /// Generate an address for the given key + pub fn get_address<'a>(&self, key: &'a str) -> Box + Send> { + // Address of the registrar itself + let registrar_address = match self.client.registrar_address() { + Ok(a) => a, + Err(e) => return Box::new(future::err(e)), + }; + + let address_fetcher = self.registrar.functions().get_address(); + let id = address_fetcher.input(keccak(key), DNS_A_RECORD); + + let future = self.client.call_contract(registrar_address, id).and_then(move |address| { + address_fetcher.output(&address) + } + .map_err(|e| e.to_string())); + + Box::new(future) + } +} + +/// Registrar contract interface +/// Should execute transaction using current blockchain state. +pub trait RegistrarClient: Send + Sync { + /// Specifies synchronous or asynchronous communication + type Call: IntoFuture; + + /// Get registrar address + fn registrar_address(&self) -> Result; + /// Call Contract + fn call_contract(&self, address: Address, data: Bytes) -> Self::Call; +} +