Resolving URLs from contract (#1964)
* Fetching dapp from github. * Unpacking dapp * Removing hardcodes * Proper Host validation * Randomizing paths * Splitting into files * Serving donwloaded apps from different path * Extracting URLHint to separate module * Whitespace and docs * Resolving from URLHint contract * Fixing test * Resolving githubhint url from registrar * Proper redirections * Fixing test * fixing ethstore [ci skip] * Correct version of registrar * Removing superfluous Box
This commit is contained in:
parent
dda57d9294
commit
124a5da75e
15
Cargo.lock
generated
15
Cargo.lock
generated
@ -229,6 +229,19 @@ dependencies = [
|
|||||||
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ethabi"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"docopt 0.6.80 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"serde 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"serde_codegen 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"serde_json 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"tiny-keccak 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ethash"
|
name = "ethash"
|
||||||
version = "1.4.0"
|
version = "1.4.0"
|
||||||
@ -275,6 +288,7 @@ name = "ethcore-dapps"
|
|||||||
version = "1.4.0"
|
version = "1.4.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clippy 0.0.85 (registry+https://github.com/rust-lang/crates.io-index)",
|
"clippy 0.0.85 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"ethabi 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"ethcore-rpc 1.4.0",
|
"ethcore-rpc 1.4.0",
|
||||||
"ethcore-util 1.4.0",
|
"ethcore-util 1.4.0",
|
||||||
"hyper 0.9.4 (git+https://github.com/ethcore/hyper)",
|
"hyper 0.9.4 (git+https://github.com/ethcore/hyper)",
|
||||||
@ -1642,6 +1656,7 @@ dependencies = [
|
|||||||
"checksum elastic-array 0.4.0 (git+https://github.com/ethcore/elastic-array)" = "<none>"
|
"checksum elastic-array 0.4.0 (git+https://github.com/ethcore/elastic-array)" = "<none>"
|
||||||
"checksum env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "aba65b63ffcc17ffacd6cf5aa843da7c5a25e3bd4bbe0b7def8b214e411250e5"
|
"checksum env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "aba65b63ffcc17ffacd6cf5aa843da7c5a25e3bd4bbe0b7def8b214e411250e5"
|
||||||
"checksum eth-secp256k1 0.5.4 (git+https://github.com/ethcore/rust-secp256k1)" = "<none>"
|
"checksum eth-secp256k1 0.5.4 (git+https://github.com/ethcore/rust-secp256k1)" = "<none>"
|
||||||
|
"checksum ethabi 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bc7789d1518abba0c61606826a5229284d47a9d0934feb62a1ee218882780a9b"
|
||||||
"checksum flate2 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "3eeb481e957304178d2e782f2da1257f1434dfecbae883bafb61ada2a9fea3bb"
|
"checksum flate2 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "3eeb481e957304178d2e782f2da1257f1434dfecbae883bafb61ada2a9fea3bb"
|
||||||
"checksum gcc 0.3.28 (registry+https://github.com/rust-lang/crates.io-index)" = "3da3a2cbaeb01363c8e3704fd9fd0eb2ceb17c6f27abd4c1ef040fb57d20dc79"
|
"checksum gcc 0.3.28 (registry+https://github.com/rust-lang/crates.io-index)" = "3da3a2cbaeb01363c8e3704fd9fd0eb2ceb17c6f27abd4c1ef040fb57d20dc79"
|
||||||
"checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb"
|
"checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb"
|
||||||
|
@ -21,6 +21,7 @@ serde = "0.7.0"
|
|||||||
serde_json = "0.7.0"
|
serde_json = "0.7.0"
|
||||||
serde_macros = { version = "0.7.0", optional = true }
|
serde_macros = { version = "0.7.0", optional = true }
|
||||||
zip = { version = "0.1", default-features = false }
|
zip = { version = "0.1", default-features = false }
|
||||||
|
ethabi = "0.2.1"
|
||||||
ethcore-rpc = { path = "../rpc" }
|
ethcore-rpc = { path = "../rpc" }
|
||||||
ethcore-util = { path = "../util" }
|
ethcore-util = { path = "../util" }
|
||||||
parity-dapps = { git = "https://github.com/ethcore/parity-ui.git", version = "1.4" }
|
parity-dapps = { git = "https://github.com/ethcore/parity-ui.git", version = "1.4" }
|
||||||
|
@ -24,6 +24,7 @@ use std::io::{self, Read, Write};
|
|||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use rustc_serialize::hex::FromHex;
|
||||||
|
|
||||||
use hyper::Control;
|
use hyper::Control;
|
||||||
use hyper::status::StatusCode;
|
use hyper::status::StatusCode;
|
||||||
@ -54,12 +55,6 @@ impl<R: URLHint> Drop for AppFetcher<R> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for AppFetcher<URLHintContract> {
|
|
||||||
fn default() -> Self {
|
|
||||||
AppFetcher::new(URLHintContract)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<R: URLHint> AppFetcher<R> {
|
impl<R: URLHint> AppFetcher<R> {
|
||||||
|
|
||||||
pub fn new(resolver: R) -> Self {
|
pub fn new(resolver: R) -> Self {
|
||||||
@ -84,7 +79,10 @@ impl<R: URLHint> AppFetcher<R> {
|
|||||||
// Check if we already have the app
|
// Check if we already have the app
|
||||||
Some(_) => true,
|
Some(_) => true,
|
||||||
// fallback to resolver
|
// fallback to resolver
|
||||||
None => self.resolver.resolve(app_id).is_some(),
|
None => match app_id.from_hex() {
|
||||||
|
Ok(app_id) => self.resolver.resolve(app_id).is_some(),
|
||||||
|
_ => false,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -103,16 +101,22 @@ impl<R: URLHint> AppFetcher<R> {
|
|||||||
Some(&AppStatus::Fetching) => {
|
Some(&AppStatus::Fetching) => {
|
||||||
(None, Box::new(ContentHandler::html(
|
(None, Box::new(ContentHandler::html(
|
||||||
StatusCode::ServiceUnavailable,
|
StatusCode::ServiceUnavailable,
|
||||||
"<h1>This dapp is already being downloaded.</h1>".into()
|
format!(
|
||||||
|
"<html><head>{}</head><body>{}</body></html>",
|
||||||
|
"<meta http-equiv=\"refresh\" content=\"1\">",
|
||||||
|
"<h1>This dapp is already being downloaded.</h1><h2>Please wait...</h2>",
|
||||||
|
)
|
||||||
)) as Box<Handler>)
|
)) as Box<Handler>)
|
||||||
},
|
},
|
||||||
// We need to start fetching app
|
// We need to start fetching app
|
||||||
None => {
|
None => {
|
||||||
// TODO [todr] Keep only last N dapps available!
|
// TODO [todr] Keep only last N dapps available!
|
||||||
let app = self.resolver.resolve(&app_id).expect("to_handler is called only when `contains` returns true.");
|
let app_hex = app_id.from_hex().expect("to_handler is called only when `contains` returns true.");
|
||||||
|
let app = self.resolver.resolve(app_hex).expect("to_handler is called only when `contains` returns true.");
|
||||||
(Some(AppStatus::Fetching), Box::new(AppFetcherHandler::new(
|
(Some(AppStatus::Fetching), Box::new(AppFetcherHandler::new(
|
||||||
app,
|
app,
|
||||||
control,
|
control,
|
||||||
|
path.using_dapps_domains,
|
||||||
DappInstaller {
|
DappInstaller {
|
||||||
dapp_id: app_id.clone(),
|
dapp_id: app_id.clone(),
|
||||||
dapps_path: self.dapps_path.clone(),
|
dapps_path: self.dapps_path.clone(),
|
||||||
@ -265,10 +269,11 @@ mod tests {
|
|||||||
use apps::urlhint::{GithubApp, URLHint};
|
use apps::urlhint::{GithubApp, URLHint};
|
||||||
use endpoint::EndpointInfo;
|
use endpoint::EndpointInfo;
|
||||||
use page::LocalPageEndpoint;
|
use page::LocalPageEndpoint;
|
||||||
|
use util::Bytes;
|
||||||
|
|
||||||
struct FakeResolver;
|
struct FakeResolver;
|
||||||
impl URLHint for FakeResolver {
|
impl URLHint for FakeResolver {
|
||||||
fn resolve(&self, _app_id: &str) -> Option<GithubApp> {
|
fn resolve(&self, _app_id: Bytes) -> Option<GithubApp> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,7 +33,14 @@ pub const API_PATH : &'static str = "api";
|
|||||||
pub const UTILS_PATH : &'static str = "parity-utils";
|
pub const UTILS_PATH : &'static str = "parity-utils";
|
||||||
|
|
||||||
pub fn main_page() -> &'static str {
|
pub fn main_page() -> &'static str {
|
||||||
"/home/"
|
"home"
|
||||||
|
}
|
||||||
|
pub fn redirection_address(using_dapps_domains: bool, app_id: &str) -> String {
|
||||||
|
if using_dapps_domains {
|
||||||
|
format!("http://{}{}/", app_id, DAPPS_DOMAIN)
|
||||||
|
} else {
|
||||||
|
format!("/{}/", app_id)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn utils() -> Box<Endpoint> {
|
pub fn utils() -> Box<Endpoint> {
|
||||||
|
21
dapps/src/apps/registrar.json
Normal file
21
dapps/src/apps/registrar.json
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
[
|
||||||
|
{"constant":false,"inputs":[{"name":"_new","type":"address"}],"name":"setOwner","outputs":[],"type":"function"},
|
||||||
|
{"constant":false,"inputs":[{"name":"_name","type":"string"}],"name":"confirmReverse","outputs":[{"name":"success","type":"bool"}],"type":"function"},
|
||||||
|
{"constant":false,"inputs":[{"name":"_name","type":"bytes32"}],"name":"reserve","outputs":[{"name":"success","type":"bool"}],"type":"function"},
|
||||||
|
{"constant":false,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_key","type":"string"},{"name":"_value","type":"bytes32"}],"name":"set","outputs":[{"name":"success","type":"bool"}],"type":"function"},
|
||||||
|
{"constant":false,"inputs":[{"name":"_name","type":"bytes32"}],"name":"drop","outputs":[{"name":"success","type":"bool"}],"type":"function"},
|
||||||
|
{"constant":true,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_key","type":"string"}],"name":"getAddress","outputs":[{"name":"","type":"address"}],"type":"function"},
|
||||||
|
{"constant":false,"inputs":[{"name":"_amount","type":"uint256"}],"name":"setFee","outputs":[],"type":"function"},
|
||||||
|
{"constant":false,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_to","type":"address"}],"name":"transfer","outputs":[{"name":"success","type":"bool"}],"type":"function"},
|
||||||
|
{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"type":"function"},
|
||||||
|
{"constant":true,"inputs":[{"name":"_name","type":"bytes32"}],"name":"reserved","outputs":[{"name":"reserved","type":"bool"}],"type":"function"},
|
||||||
|
{"constant":false,"inputs":[],"name":"drain","outputs":[],"type":"function"},
|
||||||
|
{"constant":false,"inputs":[{"name":"_name","type":"string"},{"name":"_who","type":"address"}],"name":"proposeReverse","outputs":[{"name":"success","type":"bool"}],"type":"function"},
|
||||||
|
{"constant":true,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_key","type":"string"}],"name":"getUint","outputs":[{"name":"","type":"uint256"}],"type":"function"},
|
||||||
|
{"constant":true,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_key","type":"string"}],"name":"get","outputs":[{"name":"","type":"bytes32"}],"type":"function"},
|
||||||
|
{"constant":true,"inputs":[],"name":"fee","outputs":[{"name":"","type":"uint256"}],"type":"function"},
|
||||||
|
{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"reverse","outputs":[{"name":"","type":"string"}],"type":"function"},
|
||||||
|
{"constant":false,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_key","type":"string"},{"name":"_value","type":"uint256"}],"name":"setUint","outputs":[{"name":"success","type":"bool"}],"type":"function"},
|
||||||
|
{"constant":false,"inputs":[],"name":"removeReverse","outputs":[],"type":"function"},
|
||||||
|
{"constant":false,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_key","type":"string"},{"name":"_value","type":"address"}],"name":"setAddress","outputs":[{"name":"success","type":"bool"}],"type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"amount","type":"uint256"}],"name":"Drained","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"amount","type":"uint256"}],"name":"FeeChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"name","type":"bytes32"},{"indexed":true,"name":"owner","type":"address"}],"name":"Reserved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"name","type":"bytes32"},{"indexed":true,"name":"oldOwner","type":"address"},{"indexed":true,"name":"newOwner","type":"address"}],"name":"Transferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"name","type":"bytes32"},{"indexed":true,"name":"owner","type":"address"}],"name":"Dropped","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"name","type":"bytes32"},{"indexed":true,"name":"owner","type":"address"},{"indexed":true,"name":"key","type":"string"}],"name":"DataChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"name","type":"string"},{"indexed":true,"name":"reverse","type":"address"}],"name":"ReverseProposed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"name","type":"string"},{"indexed":true,"name":"reverse","type":"address"}],"name":"ReverseConfirmed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"name","type":"string"},{"indexed":true,"name":"reverse","type":"address"}],"name":"ReverseRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"old","type":"address"},{"indexed":true,"name":"current","type":"address"}],"name":"NewOwner","type":"event"}
|
||||||
|
]
|
6
dapps/src/apps/urlhint.json
Normal file
6
dapps/src/apps/urlhint.json
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
[
|
||||||
|
{"constant":false,"inputs":[{"name":"_content","type":"bytes32"},{"name":"_url","type":"string"}],"name":"hintURL","outputs":[],"type":"function"},
|
||||||
|
{"constant":false,"inputs":[{"name":"_content","type":"bytes32"},{"name":"_accountSlashRepo","type":"string"},{"name":"_commit","type":"bytes20"}],"name":"hint","outputs":[],"type":"function"},
|
||||||
|
{"constant":true,"inputs":[{"name":"","type":"bytes32"}],"name":"entries","outputs":[{"name":"accountSlashRepo","type":"string"},{"name":"commit","type":"bytes20"},{"name":"owner","type":"address"}],"type":"function"},
|
||||||
|
{"constant":false,"inputs":[{"name":"_content","type":"bytes32"}],"name":"unhint","outputs":[],"type":"function"}
|
||||||
|
]
|
@ -14,13 +14,16 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
use std::fmt;
|
||||||
|
use std::sync::Arc;
|
||||||
use rustc_serialize::hex::ToHex;
|
use rustc_serialize::hex::ToHex;
|
||||||
|
|
||||||
use util::{Address, FromHex};
|
use ethabi::{Interface, Contract, Token};
|
||||||
|
use util::{Address, Bytes, Hashable};
|
||||||
|
|
||||||
const COMMIT_LEN: usize = 20;
|
const COMMIT_LEN: usize = 20;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub struct GithubApp {
|
pub struct GithubApp {
|
||||||
pub account: String,
|
pub account: String,
|
||||||
pub repo: String,
|
pub repo: String,
|
||||||
@ -48,42 +51,240 @@ impl GithubApp {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait URLHint {
|
/// RAW Contract interface.
|
||||||
fn resolve(&self, app_id: &str) -> Option<GithubApp>;
|
/// Should execute transaction using current blockchain state.
|
||||||
|
pub trait ContractClient: Send + Sync {
|
||||||
|
/// Get registrar address
|
||||||
|
fn registrar(&self) -> Result<Address, String>;
|
||||||
|
/// Call Contract
|
||||||
|
fn call(&self, address: Address, data: Bytes) -> Result<Bytes, String>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct URLHintContract;
|
/// URLHint Contract interface
|
||||||
|
pub trait URLHint {
|
||||||
|
/// Resolves given id to registrar entry.
|
||||||
|
fn resolve(&self, app_id: Bytes) -> Option<GithubApp>;
|
||||||
|
}
|
||||||
|
|
||||||
impl URLHint for URLHintContract {
|
pub struct URLHintContract {
|
||||||
fn resolve(&self, app_id: &str) -> Option<GithubApp> {
|
urlhint: Contract,
|
||||||
// TODO [todr] use GithubHint contract to check the details
|
registrar: Contract,
|
||||||
// For now we are just accepting patterns: <commithash>.<repo>.<account>.parity
|
client: Arc<ContractClient>,
|
||||||
let mut app_parts = app_id.split('.');
|
}
|
||||||
|
|
||||||
let hash = app_parts.next()
|
impl URLHintContract {
|
||||||
.and_then(|h| h.from_hex().ok())
|
pub fn new(client: Arc<ContractClient>) -> Self {
|
||||||
.and_then(|h| GithubApp::commit(&h));
|
let urlhint = Interface::load(include_bytes!("./urlhint.json")).expect("urlhint.json is valid ABI");
|
||||||
let repo = app_parts.next();
|
let registrar = Interface::load(include_bytes!("./registrar.json")).expect("registrar.json is valid ABI");
|
||||||
let account = app_parts.next();
|
|
||||||
|
|
||||||
match (hash, repo, account) {
|
URLHintContract {
|
||||||
(Some(hash), Some(repo), Some(account)) => {
|
urlhint: Contract::new(urlhint),
|
||||||
Some(GithubApp {
|
registrar: Contract::new(registrar),
|
||||||
account: account.into(),
|
client: client,
|
||||||
repo: repo.into(),
|
}
|
||||||
commit: hash,
|
}
|
||||||
owner: Address::default(),
|
|
||||||
})
|
fn urlhint_address(&self) -> Option<Address> {
|
||||||
|
let res = || {
|
||||||
|
let get_address = try!(self.registrar.function("getAddress".into()).map_err(as_string));
|
||||||
|
let params = try!(get_address.encode_call(
|
||||||
|
vec![Token::FixedBytes((*"githubhint".sha3()).to_vec()), Token::String("A".into())]
|
||||||
|
).map_err(as_string));
|
||||||
|
let output = try!(self.client.call(try!(self.client.registrar()), params));
|
||||||
|
let result = try!(get_address.decode_output(output).map_err(as_string));
|
||||||
|
|
||||||
|
match result.get(0) {
|
||||||
|
Some(&Token::Address(address)) if address != *Address::default() => Ok(address.into()),
|
||||||
|
Some(&Token::Address(_)) => Err(format!("Contract not found.")),
|
||||||
|
e => Err(format!("Invalid result: {:?}", e)),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
match res() {
|
||||||
|
Ok(res) => Some(res),
|
||||||
|
Err(e) => {
|
||||||
|
warn!(target: "dapps", "Error while calling registrar: {:?}", e);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn encode_urlhint_call(&self, app_id: Bytes) -> Option<Bytes> {
|
||||||
|
let call = self.urlhint
|
||||||
|
.function("entries".into())
|
||||||
|
.and_then(|f| f.encode_call(vec![Token::FixedBytes(app_id)]));
|
||||||
|
|
||||||
|
match call {
|
||||||
|
Ok(res) => {
|
||||||
|
Some(res)
|
||||||
},
|
},
|
||||||
_ => None,
|
Err(e) => {
|
||||||
|
warn!(target: "dapps", "Error while encoding urlhint call: {:?}", e);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn decode_urlhint_output(&self, output: Bytes) -> Option<GithubApp> {
|
||||||
|
trace!(target: "dapps", "Output: {:?}", output.to_hex());
|
||||||
|
let output = self.urlhint
|
||||||
|
.function("entries".into())
|
||||||
|
.and_then(|f| f.decode_output(output));
|
||||||
|
|
||||||
|
if let Ok(vec) = output {
|
||||||
|
if vec.len() != 3 {
|
||||||
|
warn!(target: "dapps", "Invalid contract output: {:?}", vec);
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut it = vec.into_iter();
|
||||||
|
let account_slash_repo = it.next().unwrap();
|
||||||
|
let commit = it.next().unwrap();
|
||||||
|
let owner = it.next().unwrap();
|
||||||
|
|
||||||
|
match (account_slash_repo, commit, owner) {
|
||||||
|
(Token::String(account_slash_repo), Token::FixedBytes(commit), Token::Address(owner)) => {
|
||||||
|
let owner = owner.into();
|
||||||
|
if owner == Address::default() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let (account, repo) = {
|
||||||
|
let mut it = account_slash_repo.split('/');
|
||||||
|
match (it.next(), it.next()) {
|
||||||
|
(Some(account), Some(repo)) => (account.into(), repo.into()),
|
||||||
|
_ => return None,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
GithubApp::commit(&commit).map(|commit| GithubApp {
|
||||||
|
account: account,
|
||||||
|
repo: repo,
|
||||||
|
commit: commit,
|
||||||
|
owner: owner,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
e => {
|
||||||
|
warn!(target: "dapps", "Invalid contract output parameters: {:?}", e);
|
||||||
|
None
|
||||||
|
},
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
warn!(target: "dapps", "Invalid contract output: {:?}", output);
|
||||||
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl URLHint for URLHintContract {
|
||||||
|
fn resolve(&self, app_id: Bytes) -> Option<GithubApp> {
|
||||||
|
self.urlhint_address().and_then(|address| {
|
||||||
|
// Prepare contract call
|
||||||
|
self.encode_urlhint_call(app_id)
|
||||||
|
.and_then(|data| {
|
||||||
|
let call = self.client.call(address, data);
|
||||||
|
if let Err(ref e) = call {
|
||||||
|
warn!(target: "dapps", "Error while calling urlhint: {:?}", e);
|
||||||
|
}
|
||||||
|
call.ok()
|
||||||
|
})
|
||||||
|
.and_then(|output| self.decode_urlhint_output(output))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_string<T: fmt::Debug>(e: T) -> String {
|
||||||
|
format!("{:?}", e)
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::GithubApp;
|
use std::sync::Arc;
|
||||||
use util::Address;
|
use std::str::FromStr;
|
||||||
|
use rustc_serialize::hex::{ToHex, FromHex};
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
use util::{Bytes, Address, Mutex, ToPretty};
|
||||||
|
|
||||||
|
struct FakeRegistrar {
|
||||||
|
pub calls: Arc<Mutex<Vec<(String, String)>>>,
|
||||||
|
pub responses: Mutex<Vec<Result<Bytes, String>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
const REGISTRAR: &'static str = "8e4e9b13d4b45cb0befc93c3061b1408f67316b2";
|
||||||
|
const URLHINT: &'static str = "deadbeefcafe0000000000000000000000000000";
|
||||||
|
|
||||||
|
impl FakeRegistrar {
|
||||||
|
fn new() -> Self {
|
||||||
|
FakeRegistrar {
|
||||||
|
calls: Arc::new(Mutex::new(Vec::new())),
|
||||||
|
responses: Mutex::new(
|
||||||
|
vec![
|
||||||
|
Ok(format!("000000000000000000000000{}", URLHINT).from_hex().unwrap()),
|
||||||
|
Ok(Vec::new())
|
||||||
|
]
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ContractClient for FakeRegistrar {
|
||||||
|
|
||||||
|
fn registrar(&self) -> Result<Address, String> {
|
||||||
|
Ok(REGISTRAR.parse().unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn call(&self, address: Address, data: Bytes) -> Result<Bytes, String> {
|
||||||
|
self.calls.lock().push((address.to_hex(), data.to_hex()));
|
||||||
|
self.responses.lock().remove(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_call_registrar_and_urlhint_contracts() {
|
||||||
|
// given
|
||||||
|
let registrar = FakeRegistrar::new();
|
||||||
|
let calls = registrar.calls.clone();
|
||||||
|
let urlhint = URLHintContract::new(Arc::new(registrar));
|
||||||
|
|
||||||
|
// when
|
||||||
|
let res = urlhint.resolve("test".bytes().collect());
|
||||||
|
let calls = calls.lock();
|
||||||
|
let call0 = calls.get(0).expect("Registrar resolve called");
|
||||||
|
let call1 = calls.get(1).expect("URLHint Resolve called");
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert!(res.is_none());
|
||||||
|
assert_eq!(call0.0, REGISTRAR);
|
||||||
|
assert_eq!(call0.1,
|
||||||
|
"6795dbcd058740ee9a5a3fb9f1cfa10752baec87e09cc45cd7027fd54708271aca300c75000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000014100000000000000000000000000000000000000000000000000000000000000".to_owned()
|
||||||
|
);
|
||||||
|
assert_eq!(call1.0, URLHINT);
|
||||||
|
assert_eq!(call1.1,
|
||||||
|
"267b69227465737400000000000000000000000000000000000000000000000000000000".to_owned()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_decode_urlhint_output() {
|
||||||
|
// given
|
||||||
|
let mut registrar = FakeRegistrar::new();
|
||||||
|
registrar.responses = Mutex::new(vec![
|
||||||
|
Ok(format!("000000000000000000000000{}", URLHINT).from_hex().unwrap()),
|
||||||
|
Ok("0000000000000000000000000000000000000000000000000000000000000060ec4c1fe06c808fe3739858c347109b1f5f1ed4b5000000000000000000000000000000000000000000000000deadcafebeefbeefcafedeaddeedfeedffffffff0000000000000000000000000000000000000000000000000000000000000011657468636f72652f64616f2e636c61696d000000000000000000000000000000".from_hex().unwrap()),
|
||||||
|
]);
|
||||||
|
let urlhint = URLHintContract::new(Arc::new(registrar));
|
||||||
|
|
||||||
|
// when
|
||||||
|
let res = urlhint.resolve("test".bytes().collect());
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert_eq!(res, Some(GithubApp {
|
||||||
|
account: "ethcore".into(),
|
||||||
|
repo: "dao.claim".into(),
|
||||||
|
commit: GithubApp::commit(&"ec4c1fe06c808fe3739858c347109b1f5f1ed4b5".from_hex().unwrap()).unwrap(),
|
||||||
|
owner: Address::from_str("deadcafebeefbeefcafedeaddeedfeedffffffff").unwrap(),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn should_return_valid_url() {
|
fn should_return_valid_url() {
|
||||||
|
@ -24,6 +24,7 @@ pub struct EndpointPath {
|
|||||||
pub app_id: String,
|
pub app_id: String,
|
||||||
pub host: String,
|
pub host: String,
|
||||||
pub port: u16,
|
pub port: u16,
|
||||||
|
pub using_dapps_domains: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
|
@ -27,7 +27,7 @@ use hyper::status::StatusCode;
|
|||||||
|
|
||||||
use handlers::ContentHandler;
|
use handlers::ContentHandler;
|
||||||
use handlers::client::{Fetch, FetchResult};
|
use handlers::client::{Fetch, FetchResult};
|
||||||
use apps::DAPPS_DOMAIN;
|
use apps::redirection_address;
|
||||||
use apps::urlhint::GithubApp;
|
use apps::urlhint::GithubApp;
|
||||||
use apps::manifest::Manifest;
|
use apps::manifest::Manifest;
|
||||||
|
|
||||||
@ -54,6 +54,7 @@ pub struct AppFetcherHandler<H: DappHandler> {
|
|||||||
control: Option<Control>,
|
control: Option<Control>,
|
||||||
status: FetchState,
|
status: FetchState,
|
||||||
client: Option<Client<Fetch>>,
|
client: Option<Client<Fetch>>,
|
||||||
|
using_dapps_domains: bool,
|
||||||
dapp: H,
|
dapp: H,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -72,6 +73,7 @@ impl<H: DappHandler> AppFetcherHandler<H> {
|
|||||||
pub fn new(
|
pub fn new(
|
||||||
app: GithubApp,
|
app: GithubApp,
|
||||||
control: Control,
|
control: Control,
|
||||||
|
using_dapps_domains: bool,
|
||||||
handler: H) -> Self {
|
handler: H) -> Self {
|
||||||
|
|
||||||
let client = Client::new().expect("Failed to create a Client");
|
let client = Client::new().expect("Failed to create a Client");
|
||||||
@ -79,6 +81,7 @@ impl<H: DappHandler> AppFetcherHandler<H> {
|
|||||||
control: Some(control),
|
control: Some(control),
|
||||||
client: Some(client),
|
client: Some(client),
|
||||||
status: FetchState::NotStarted(app),
|
status: FetchState::NotStarted(app),
|
||||||
|
using_dapps_domains: using_dapps_domains,
|
||||||
dapp: handler,
|
dapp: handler,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -207,8 +210,7 @@ impl<H: DappHandler> server::Handler<HttpStream> for AppFetcherHandler<H> {
|
|||||||
FetchState::Done(ref manifest) => {
|
FetchState::Done(ref manifest) => {
|
||||||
trace!(target: "dapps", "Fetching dapp finished. Redirecting to {}", manifest.id);
|
trace!(target: "dapps", "Fetching dapp finished. Redirecting to {}", manifest.id);
|
||||||
res.set_status(StatusCode::Found);
|
res.set_status(StatusCode::Found);
|
||||||
// TODO [todr] should detect if its using nice-urls
|
res.headers_mut().set(header::Location(redirection_address(self.using_dapps_domains, &manifest.id)));
|
||||||
res.headers_mut().set(header::Location(format!("http://{}{}", manifest.id, DAPPS_DOMAIN)));
|
|
||||||
Next::write()
|
Next::write()
|
||||||
},
|
},
|
||||||
FetchState::Error(ref mut handler) => handler.on_response(res),
|
FetchState::Error(ref mut handler) => handler.on_response(res),
|
||||||
|
@ -21,13 +21,13 @@ use hyper::net::HttpStream;
|
|||||||
use hyper::status::StatusCode;
|
use hyper::status::StatusCode;
|
||||||
|
|
||||||
pub struct Redirection {
|
pub struct Redirection {
|
||||||
to_url: &'static str
|
to_url: String
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Redirection {
|
impl Redirection {
|
||||||
pub fn new(url: &'static str) -> Box<Self> {
|
pub fn new(url: &str) -> Box<Self> {
|
||||||
Box::new(Redirection {
|
Box::new(Redirection {
|
||||||
to_url: url
|
to_url: url.to_owned()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -52,6 +52,7 @@ extern crate serde;
|
|||||||
extern crate serde_json;
|
extern crate serde_json;
|
||||||
extern crate zip;
|
extern crate zip;
|
||||||
extern crate rand;
|
extern crate rand;
|
||||||
|
extern crate ethabi;
|
||||||
extern crate jsonrpc_core;
|
extern crate jsonrpc_core;
|
||||||
extern crate jsonrpc_http_server;
|
extern crate jsonrpc_http_server;
|
||||||
extern crate parity_dapps;
|
extern crate parity_dapps;
|
||||||
@ -70,6 +71,8 @@ mod api;
|
|||||||
mod proxypac;
|
mod proxypac;
|
||||||
mod url;
|
mod url;
|
||||||
|
|
||||||
|
pub use self::apps::urlhint::ContractClient;
|
||||||
|
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
@ -84,6 +87,7 @@ static DAPPS_DOMAIN : &'static str = ".parity";
|
|||||||
pub struct ServerBuilder {
|
pub struct ServerBuilder {
|
||||||
dapps_path: String,
|
dapps_path: String,
|
||||||
handler: Arc<IoHandler>,
|
handler: Arc<IoHandler>,
|
||||||
|
registrar: Arc<ContractClient>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Extendable for ServerBuilder {
|
impl Extendable for ServerBuilder {
|
||||||
@ -94,23 +98,24 @@ impl Extendable for ServerBuilder {
|
|||||||
|
|
||||||
impl ServerBuilder {
|
impl ServerBuilder {
|
||||||
/// Construct new dapps server
|
/// Construct new dapps server
|
||||||
pub fn new(dapps_path: String) -> Self {
|
pub fn new(dapps_path: String, registrar: Arc<ContractClient>) -> Self {
|
||||||
ServerBuilder {
|
ServerBuilder {
|
||||||
dapps_path: dapps_path,
|
dapps_path: dapps_path,
|
||||||
handler: Arc::new(IoHandler::new())
|
handler: Arc::new(IoHandler::new()),
|
||||||
|
registrar: registrar,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Asynchronously start server with no authentication,
|
/// Asynchronously start server with no authentication,
|
||||||
/// returns result with `Server` handle on success or an error.
|
/// returns result with `Server` handle on success or an error.
|
||||||
pub fn start_unsecure_http(&self, addr: &SocketAddr) -> Result<Server, ServerError> {
|
pub fn start_unsecure_http(&self, addr: &SocketAddr) -> Result<Server, ServerError> {
|
||||||
Server::start_http(addr, NoAuth, self.handler.clone(), self.dapps_path.clone())
|
Server::start_http(addr, NoAuth, self.handler.clone(), self.dapps_path.clone(), self.registrar.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Asynchronously start server with `HTTP Basic Authentication`,
|
/// Asynchronously start server with `HTTP Basic Authentication`,
|
||||||
/// return result with `Server` handle on success or an error.
|
/// return result with `Server` handle on success or an error.
|
||||||
pub fn start_basic_auth_http(&self, addr: &SocketAddr, username: &str, password: &str) -> Result<Server, ServerError> {
|
pub fn start_basic_auth_http(&self, addr: &SocketAddr, username: &str, password: &str) -> Result<Server, ServerError> {
|
||||||
Server::start_http(addr, HttpBasicAuth::single_user(username, password), self.handler.clone(), self.dapps_path.clone())
|
Server::start_http(addr, HttpBasicAuth::single_user(username, password), self.handler.clone(), self.dapps_path.clone(), self.registrar.clone())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -121,10 +126,16 @@ pub struct Server {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Server {
|
impl Server {
|
||||||
fn start_http<A: Authorization + 'static>(addr: &SocketAddr, authorization: A, handler: Arc<IoHandler>, dapps_path: String) -> Result<Server, ServerError> {
|
fn start_http<A: Authorization + 'static>(
|
||||||
|
addr: &SocketAddr,
|
||||||
|
authorization: A,
|
||||||
|
handler: Arc<IoHandler>,
|
||||||
|
dapps_path: String,
|
||||||
|
registrar: Arc<ContractClient>,
|
||||||
|
) -> Result<Server, ServerError> {
|
||||||
let panic_handler = Arc::new(Mutex::new(None));
|
let panic_handler = Arc::new(Mutex::new(None));
|
||||||
let authorization = Arc::new(authorization);
|
let authorization = Arc::new(authorization);
|
||||||
let apps_fetcher = Arc::new(apps::fetcher::AppFetcher::default());
|
let apps_fetcher = Arc::new(apps::fetcher::AppFetcher::new(apps::urlhint::URLHintContract::new(registrar)));
|
||||||
let endpoints = Arc::new(apps::all_endpoints(dapps_path));
|
let endpoints = Arc::new(apps::all_endpoints(dapps_path));
|
||||||
let special = Arc::new({
|
let special = Arc::new({
|
||||||
let mut special = HashMap::new();
|
let mut special = HashMap::new();
|
||||||
|
@ -187,7 +187,8 @@ fn should_extract_path_with_appid() {
|
|||||||
path: EndpointPath {
|
path: EndpointPath {
|
||||||
app_id: "app".to_owned(),
|
app_id: "app".to_owned(),
|
||||||
host: "".to_owned(),
|
host: "".to_owned(),
|
||||||
port: 8080
|
port: 8080,
|
||||||
|
using_dapps_domains: true,
|
||||||
},
|
},
|
||||||
file: None,
|
file: None,
|
||||||
safe_to_embed: true,
|
safe_to_embed: true,
|
||||||
|
@ -86,9 +86,10 @@ impl<A: Authorization + 'static> server::Handler<HttpStream> for Router<A> {
|
|||||||
let control = self.control.take().expect("on_request is called only once, thus control is always defined.");
|
let control = self.control.take().expect("on_request is called only once, thus control is always defined.");
|
||||||
self.fetch.to_handler(path.clone(), control)
|
self.fetch.to_handler(path.clone(), control)
|
||||||
},
|
},
|
||||||
// Redirection to main page
|
// Redirection to main page (maybe 404 instead?)
|
||||||
_ if *req.method() == hyper::method::Method::Get => {
|
(Some(ref path), _) if *req.method() == hyper::method::Method::Get => {
|
||||||
Redirection::new(self.main_page)
|
let address = apps::redirection_address(path.using_dapps_domains, self.main_page);
|
||||||
|
Redirection::new(address.as_str())
|
||||||
},
|
},
|
||||||
// RPC by default
|
// RPC by default
|
||||||
_ => {
|
_ => {
|
||||||
@ -165,6 +166,7 @@ fn extract_endpoint(url: &Option<Url>) -> (Option<EndpointPath>, SpecialEndpoint
|
|||||||
app_id: id,
|
app_id: id,
|
||||||
host: domain.clone(),
|
host: domain.clone(),
|
||||||
port: url.port,
|
port: url.port,
|
||||||
|
using_dapps_domains: true,
|
||||||
}), special_endpoint(url))
|
}), special_endpoint(url))
|
||||||
},
|
},
|
||||||
_ if url.path.len() > 1 => {
|
_ if url.path.len() > 1 => {
|
||||||
@ -173,6 +175,7 @@ fn extract_endpoint(url: &Option<Url>) -> (Option<EndpointPath>, SpecialEndpoint
|
|||||||
app_id: id.clone(),
|
app_id: id.clone(),
|
||||||
host: format!("{}", url.host),
|
host: format!("{}", url.host),
|
||||||
port: url.port,
|
port: url.port,
|
||||||
|
using_dapps_domains: false,
|
||||||
}), special_endpoint(url))
|
}), special_endpoint(url))
|
||||||
},
|
},
|
||||||
_ => (None, special_endpoint(url)),
|
_ => (None, special_endpoint(url)),
|
||||||
@ -192,6 +195,7 @@ fn should_extract_endpoint() {
|
|||||||
app_id: "status".to_owned(),
|
app_id: "status".to_owned(),
|
||||||
host: "localhost".to_owned(),
|
host: "localhost".to_owned(),
|
||||||
port: 8080,
|
port: 8080,
|
||||||
|
using_dapps_domains: false,
|
||||||
}), SpecialEndpoint::None)
|
}), SpecialEndpoint::None)
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -202,6 +206,7 @@ fn should_extract_endpoint() {
|
|||||||
app_id: "rpc".to_owned(),
|
app_id: "rpc".to_owned(),
|
||||||
host: "localhost".to_owned(),
|
host: "localhost".to_owned(),
|
||||||
port: 8080,
|
port: 8080,
|
||||||
|
using_dapps_domains: false,
|
||||||
}), SpecialEndpoint::Rpc)
|
}), SpecialEndpoint::Rpc)
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -211,6 +216,7 @@ fn should_extract_endpoint() {
|
|||||||
app_id: "my.status".to_owned(),
|
app_id: "my.status".to_owned(),
|
||||||
host: "my.status.parity".to_owned(),
|
host: "my.status.parity".to_owned(),
|
||||||
port: 80,
|
port: 80,
|
||||||
|
using_dapps_domains: true,
|
||||||
}), SpecialEndpoint::Utils)
|
}), SpecialEndpoint::Utils)
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -221,6 +227,7 @@ fn should_extract_endpoint() {
|
|||||||
app_id: "my.status".to_owned(),
|
app_id: "my.status".to_owned(),
|
||||||
host: "my.status.parity".to_owned(),
|
host: "my.status.parity".to_owned(),
|
||||||
port: 80,
|
port: 80,
|
||||||
|
using_dapps_domains: true,
|
||||||
}), SpecialEndpoint::None)
|
}), SpecialEndpoint::None)
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -231,6 +238,7 @@ fn should_extract_endpoint() {
|
|||||||
app_id: "my.status".to_owned(),
|
app_id: "my.status".to_owned(),
|
||||||
host: "my.status.parity".to_owned(),
|
host: "my.status.parity".to_owned(),
|
||||||
port: 80,
|
port: 80,
|
||||||
|
using_dapps_domains: true,
|
||||||
}), SpecialEndpoint::Rpc)
|
}), SpecialEndpoint::Rpc)
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -241,6 +249,7 @@ fn should_extract_endpoint() {
|
|||||||
app_id: "my.status".to_owned(),
|
app_id: "my.status".to_owned(),
|
||||||
host: "my.status.parity".to_owned(),
|
host: "my.status.parity".to_owned(),
|
||||||
port: 80,
|
port: 80,
|
||||||
|
using_dapps_domains: true,
|
||||||
}), SpecialEndpoint::Api)
|
}), SpecialEndpoint::Api)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -723,8 +723,8 @@ impl MinerService for Miner {
|
|||||||
.position(|t| t == *hash)
|
.position(|t| t == *hash)
|
||||||
.map(|index| {
|
.map(|index| {
|
||||||
let prev_gas = if index == 0 { Default::default() } else { pending.receipts()[index - 1].gas_used };
|
let prev_gas = if index == 0 { Default::default() } else { pending.receipts()[index - 1].gas_used };
|
||||||
let ref tx = txs[index];
|
let tx = &txs[index];
|
||||||
let ref receipt = pending.receipts()[index];
|
let receipt = &pending.receipts()[index];
|
||||||
RichReceipt {
|
RichReceipt {
|
||||||
transaction_hash: hash.clone(),
|
transaction_hash: hash.clone(),
|
||||||
transaction_index: index,
|
transaction_index: index,
|
||||||
|
@ -20,7 +20,6 @@ extern crate ethstore;
|
|||||||
|
|
||||||
use std::{env, process, fs};
|
use std::{env, process, fs};
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
use std::ops::Deref;
|
|
||||||
use docopt::Docopt;
|
use docopt::Docopt;
|
||||||
use ethstore::ethkey::Address;
|
use ethstore::ethkey::Address;
|
||||||
use ethstore::dir::{KeyDirectory, ParityDirectory, DiskDirectory, GethDirectory, DirectoryType};
|
use ethstore::dir::{KeyDirectory, ParityDirectory, DiskDirectory, GethDirectory, DirectoryType};
|
||||||
@ -142,7 +141,7 @@ fn execute<S, I>(command: I) -> Result<String, Error> where I: IntoIterator<Item
|
|||||||
} else if args.cmd_import {
|
} else if args.cmd_import {
|
||||||
let src = try!(key_dir(&args.flag_src));
|
let src = try!(key_dir(&args.flag_src));
|
||||||
let dst = try!(key_dir(&args.flag_dir));
|
let dst = try!(key_dir(&args.flag_dir));
|
||||||
let accounts = try!(import_accounts(*src, *dst));
|
let accounts = try!(import_accounts(&*src, &*dst));
|
||||||
Ok(format_accounts(&accounts))
|
Ok(format_accounts(&accounts))
|
||||||
} else if args.cmd_import_wallet {
|
} else if args.cmd_import_wallet {
|
||||||
let wallet = try!(PresaleWallet::open(&args.arg_path));
|
let wallet = try!(PresaleWallet::open(&args.arg_path));
|
||||||
@ -162,7 +161,7 @@ fn execute<S, I>(command: I) -> Result<String, Error> where I: IntoIterator<Item
|
|||||||
let signature = try!(store.sign(&address, &password, &message));
|
let signature = try!(store.sign(&address, &password, &message));
|
||||||
Ok(format!("{}", signature))
|
Ok(format!("{}", signature))
|
||||||
} else {
|
} else {
|
||||||
unreachable!();
|
Ok(format!("{}", USAGE))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
139
parity/dapps.rs
139
parity/dapps.rs
@ -15,16 +15,11 @@
|
|||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::net::SocketAddr;
|
|
||||||
use io::PanicHandler;
|
use io::PanicHandler;
|
||||||
use rpc_apis;
|
use rpc_apis;
|
||||||
|
use ethcore::client::Client;
|
||||||
use helpers::replace_home;
|
use helpers::replace_home;
|
||||||
|
|
||||||
#[cfg(feature = "dapps")]
|
|
||||||
pub use ethcore_dapps::Server as WebappServer;
|
|
||||||
#[cfg(not(feature = "dapps"))]
|
|
||||||
pub struct WebappServer;
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
pub struct Configuration {
|
pub struct Configuration {
|
||||||
pub enabled: bool,
|
pub enabled: bool,
|
||||||
@ -51,6 +46,7 @@ impl Default for Configuration {
|
|||||||
pub struct Dependencies {
|
pub struct Dependencies {
|
||||||
pub panic_handler: Arc<PanicHandler>,
|
pub panic_handler: Arc<PanicHandler>,
|
||||||
pub apis: Arc<rpc_apis::Dependencies>,
|
pub apis: Arc<rpc_apis::Dependencies>,
|
||||||
|
pub client: Arc<Client>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new(configuration: Configuration, deps: Dependencies) -> Result<Option<WebappServer>, String> {
|
pub fn new(configuration: Configuration, deps: Dependencies) -> Result<Option<WebappServer>, String> {
|
||||||
@ -75,45 +71,102 @@ pub fn new(configuration: Configuration, deps: Dependencies) -> Result<Option<We
|
|||||||
Ok(Some(try!(setup_dapps_server(deps, configuration.dapps_path, &addr, auth))))
|
Ok(Some(try!(setup_dapps_server(deps, configuration.dapps_path, &addr, auth))))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub use self::server::WebappServer;
|
||||||
|
pub use self::server::setup_dapps_server;
|
||||||
|
|
||||||
#[cfg(not(feature = "dapps"))]
|
#[cfg(not(feature = "dapps"))]
|
||||||
pub fn setup_dapps_server(
|
mod server {
|
||||||
_deps: Dependencies,
|
use super::Dependencies;
|
||||||
_dapps_path: String,
|
use std::net::SocketAddr;
|
||||||
_url: &SocketAddr,
|
|
||||||
_auth: Option<(String, String)>,
|
|
||||||
) -> Result<WebappServer, String> {
|
|
||||||
Err("Your Parity version has been compiled without WebApps support.".into())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "dapps")]
|
pub struct WebappServer;
|
||||||
pub fn setup_dapps_server(
|
pub fn setup_dapps_server(
|
||||||
deps: Dependencies,
|
_deps: Dependencies,
|
||||||
dapps_path: String,
|
_dapps_path: String,
|
||||||
url: &SocketAddr,
|
_url: &SocketAddr,
|
||||||
auth: Option<(String, String)>
|
_auth: Option<(String, String)>,
|
||||||
) -> Result<WebappServer, String> {
|
) -> Result<WebappServer, String> {
|
||||||
use ethcore_dapps as dapps;
|
Err("Your Parity version has been compiled without WebApps support.".into())
|
||||||
|
|
||||||
let server = dapps::ServerBuilder::new(dapps_path);
|
|
||||||
let server = rpc_apis::setup_rpc(server, deps.apis.clone(), rpc_apis::ApiSet::UnsafeContext);
|
|
||||||
let start_result = match auth {
|
|
||||||
None => {
|
|
||||||
server.start_unsecure_http(url)
|
|
||||||
},
|
|
||||||
Some((username, password)) => {
|
|
||||||
server.start_basic_auth_http(url, &username, &password)
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
match start_result {
|
|
||||||
Err(dapps::ServerError::IoError(err)) => Err(format!("WebApps io error: {}", err)),
|
|
||||||
Err(e) => Err(format!("WebApps error: {:?}", e)),
|
|
||||||
Ok(server) => {
|
|
||||||
server.set_panic_handler(move || {
|
|
||||||
deps.panic_handler.notify_all("Panic in WebApp thread.".to_owned());
|
|
||||||
});
|
|
||||||
Ok(server)
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "dapps")]
|
||||||
|
mod server {
|
||||||
|
use super::Dependencies;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::net::SocketAddr;
|
||||||
|
use util::{Bytes, Address, U256};
|
||||||
|
|
||||||
|
use ethcore::transaction::{Transaction, Action};
|
||||||
|
use ethcore::client::{Client, BlockChainClient, BlockID};
|
||||||
|
|
||||||
|
use rpc_apis;
|
||||||
|
use ethcore_dapps::ContractClient;
|
||||||
|
|
||||||
|
pub use ethcore_dapps::Server as WebappServer;
|
||||||
|
|
||||||
|
pub fn setup_dapps_server(
|
||||||
|
deps: Dependencies,
|
||||||
|
dapps_path: String,
|
||||||
|
url: &SocketAddr,
|
||||||
|
auth: Option<(String, String)>
|
||||||
|
) -> Result<WebappServer, String> {
|
||||||
|
use ethcore_dapps as dapps;
|
||||||
|
|
||||||
|
let server = dapps::ServerBuilder::new(dapps_path, Arc::new(Registrar {
|
||||||
|
client: deps.client.clone(),
|
||||||
|
}));
|
||||||
|
let server = rpc_apis::setup_rpc(server, deps.apis.clone(), rpc_apis::ApiSet::UnsafeContext);
|
||||||
|
let start_result = match auth {
|
||||||
|
None => {
|
||||||
|
server.start_unsecure_http(url)
|
||||||
|
},
|
||||||
|
Some((username, password)) => {
|
||||||
|
server.start_basic_auth_http(url, &username, &password)
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
match start_result {
|
||||||
|
Err(dapps::ServerError::IoError(err)) => Err(format!("WebApps io error: {}", err)),
|
||||||
|
Err(e) => Err(format!("WebApps error: {:?}", e)),
|
||||||
|
Ok(server) => {
|
||||||
|
server.set_panic_handler(move || {
|
||||||
|
deps.panic_handler.notify_all("Panic in WebApp thread.".to_owned());
|
||||||
|
});
|
||||||
|
Ok(server)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Registrar {
|
||||||
|
client: Arc<Client>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ContractClient for Registrar {
|
||||||
|
fn registrar(&self) -> Result<Address, String> {
|
||||||
|
self.client.additional_params().get("registrar")
|
||||||
|
.ok_or_else(|| "Registrar not defined.".into())
|
||||||
|
.and_then(|registrar| {
|
||||||
|
registrar.parse().map_err(|e| format!("Invalid registrar address: {:?}", e))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn call(&self, address: Address, data: Bytes) -> Result<Bytes, String> {
|
||||||
|
let from = Address::default();
|
||||||
|
let transaction = Transaction {
|
||||||
|
nonce: self.client.latest_nonce(&from),
|
||||||
|
action: Action::Call(address),
|
||||||
|
gas: U256::from(50_000_000),
|
||||||
|
gas_price: U256::default(),
|
||||||
|
value: U256::default(),
|
||||||
|
data: data,
|
||||||
|
}.fake_sign(from);
|
||||||
|
|
||||||
|
self.client.call(&transaction, BlockID::Latest, Default::default())
|
||||||
|
.map_err(|e| format!("{:?}", e))
|
||||||
|
.map(|executed| {
|
||||||
|
executed.output
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -223,6 +223,7 @@ pub fn execute(cmd: RunCmd) -> Result<(), String> {
|
|||||||
let dapps_deps = dapps::Dependencies {
|
let dapps_deps = dapps::Dependencies {
|
||||||
panic_handler: panic_handler.clone(),
|
panic_handler: panic_handler.clone(),
|
||||||
apis: deps_for_rpc_apis.clone(),
|
apis: deps_for_rpc_apis.clone(),
|
||||||
|
client: client.clone(),
|
||||||
};
|
};
|
||||||
|
|
||||||
// start dapps server
|
// start dapps server
|
||||||
|
Loading…
Reference in New Issue
Block a user