Merge branch 'block_header_rpc' into on-demand-priority
This commit is contained in:
commit
cf75a19e8d
5
Cargo.lock
generated
5
Cargo.lock
generated
@ -637,6 +637,8 @@ name = "ethcore-secretstore"
|
|||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"ethabi 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"ethcore 1.7.0",
|
||||||
"ethcore-devtools 1.7.0",
|
"ethcore-devtools 1.7.0",
|
||||||
"ethcore-ipc 1.7.0",
|
"ethcore-ipc 1.7.0",
|
||||||
"ethcore-ipc-codegen 1.7.0",
|
"ethcore-ipc-codegen 1.7.0",
|
||||||
@ -648,6 +650,7 @@ dependencies = [
|
|||||||
"futures-cpupool 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"futures-cpupool 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"hyper 0.10.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
"hyper 0.10.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"native-contracts 0.1.0",
|
||||||
"parking_lot 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"parking_lot 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"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)",
|
||||||
"serde 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -1754,7 +1757,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "parity-ui-precompiled"
|
name = "parity-ui-precompiled"
|
||||||
version = "1.4.0"
|
version = "1.4.0"
|
||||||
source = "git+https://github.com/paritytech/js-precompiled.git#6028c355854797a5938c26f5d2b2faf10d8833d7"
|
source = "git+https://github.com/paritytech/js-precompiled.git#9bfc6f3dfca2c337c53084bedcc65c2b526927a1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"parity-dapps-glue 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"parity-dapps-glue 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
# [Parity](https://ethcore.io/parity.html)
|
# [Parity](https://ethcore.io/parity.html)
|
||||||
### Fast, light, and robust Ethereum implementation
|
### Fast, light, and robust Ethereum implementation
|
||||||
|
|
||||||
|
### [Download latest release](https://github.com/paritytech/parity/releases)
|
||||||
|
|
||||||
[![build status](https://gitlab.ethcore.io/parity/parity/badges/master/build.svg)](https://gitlab.ethcore.io/parity/parity/commits/master) [![Coverage Status][coveralls-image]][coveralls-url] [![GPLv3][license-image]][license-url]
|
[![build status](https://gitlab.ethcore.io/parity/parity/badges/master/build.svg)](https://gitlab.ethcore.io/parity/parity/commits/master) [![Coverage Status][coveralls-image]][coveralls-url] [![GPLv3][license-image]][license-url]
|
||||||
|
|
||||||
### Join the chat!
|
### Join the chat!
|
||||||
@ -22,7 +24,6 @@ Be sure to check out [our wiki][wiki-url] for more information.
|
|||||||
[doc-url]: https://paritytech.github.io/parity/ethcore/index.html
|
[doc-url]: https://paritytech.github.io/parity/ethcore/index.html
|
||||||
[wiki-url]: https://github.com/paritytech/parity/wiki
|
[wiki-url]: https://github.com/paritytech/parity/wiki
|
||||||
|
|
||||||
**Parity requires Rust version 1.15.0 to build**
|
|
||||||
|
|
||||||
----
|
----
|
||||||
|
|
||||||
@ -45,14 +46,14 @@ of RPC APIs.
|
|||||||
If you run into an issue while using parity, feel free to file one in this repository
|
If you run into an issue while using parity, feel free to file one in this repository
|
||||||
or hop on our [gitter chat room][gitter-url] to ask a question. We are glad to help!
|
or hop on our [gitter chat room][gitter-url] to ask a question. We are glad to help!
|
||||||
|
|
||||||
Parity's current release is 1.5. You can download it at https://parity.io or follow the instructions
|
Parity's current release is 1.6. You can download it at https://github.com/paritytech/parity/releases or follow the instructions
|
||||||
below to build from source.
|
below to build from source.
|
||||||
|
|
||||||
----
|
----
|
||||||
|
|
||||||
## Build dependencies
|
## Build dependencies
|
||||||
|
|
||||||
Parity is fully compatible with Stable Rust.
|
**Parity requires Rust version 1.16.0 to build**
|
||||||
|
|
||||||
We recommend installing Rust through [rustup](https://www.rustup.rs/). If you don't already have rustup, you can install it like this:
|
We recommend installing Rust through [rustup](https://www.rustup.rs/). If you don't already have rustup, you can install it like this:
|
||||||
|
|
||||||
@ -80,7 +81,7 @@ Once you have rustup, install parity or download and build from source
|
|||||||
|
|
||||||
----
|
----
|
||||||
|
|
||||||
## Quick install
|
## Quick build and install
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cargo install --git https://github.com/paritytech/parity.git parity
|
cargo install --git https://github.com/paritytech/parity.git parity
|
||||||
|
@ -119,7 +119,7 @@ impl Decodable for Entry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn cht_key(number: u64) -> String {
|
fn cht_key(number: u64) -> String {
|
||||||
format!("canonical_{}", number)
|
format!("{:08x}_canonical", number)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn era_key(number: u64) -> String {
|
fn era_key(number: u64) -> String {
|
||||||
|
@ -83,6 +83,13 @@ pub trait LightChainClient: Send + Sync {
|
|||||||
/// Get the signing network ID.
|
/// Get the signing network ID.
|
||||||
fn signing_network_id(&self) -> Option<u64>;
|
fn signing_network_id(&self) -> Option<u64>;
|
||||||
|
|
||||||
|
/// Get environment info for execution at a given block.
|
||||||
|
/// Fails if that block's header is not stored.
|
||||||
|
fn env_info(&self, id: BlockId) -> Option<EnvInfo>;
|
||||||
|
|
||||||
|
/// Get a handle to the consensus engine.
|
||||||
|
fn engine(&self) -> &Arc<Engine>;
|
||||||
|
|
||||||
/// Query whether a block is known.
|
/// Query whether a block is known.
|
||||||
fn is_known(&self, hash: &H256) -> bool;
|
fn is_known(&self, hash: &H256) -> bool;
|
||||||
|
|
||||||
@ -348,6 +355,14 @@ impl LightChainClient for Client {
|
|||||||
Client::signing_network_id(self)
|
Client::signing_network_id(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn env_info(&self, id: BlockId) -> Option<EnvInfo> {
|
||||||
|
Client::env_info(self, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn engine(&self) -> &Arc<Engine> {
|
||||||
|
Client::engine(self)
|
||||||
|
}
|
||||||
|
|
||||||
fn is_known(&self, hash: &H256) -> bool {
|
fn is_known(&self, hash: &H256) -> bool {
|
||||||
self.status(hash) == BlockStatus::InChain
|
self.status(hash) == BlockStatus::InChain
|
||||||
}
|
}
|
||||||
|
@ -509,6 +509,9 @@ impl TransactionProof {
|
|||||||
pub fn check_response(&self, _: &Mutex<::cache::Cache>, state_items: &[DBValue]) -> Result<super::ExecutionResult, Error> {
|
pub fn check_response(&self, _: &Mutex<::cache::Cache>, state_items: &[DBValue]) -> Result<super::ExecutionResult, Error> {
|
||||||
let root = self.header.state_root();
|
let root = self.header.state_root();
|
||||||
|
|
||||||
|
let mut env_info = self.env_info.clone();
|
||||||
|
env_info.gas_limit = self.tx.gas.clone();
|
||||||
|
|
||||||
let proved_execution = state::check_proof(
|
let proved_execution = state::check_proof(
|
||||||
state_items,
|
state_items,
|
||||||
root,
|
root,
|
||||||
|
@ -23,6 +23,7 @@ use std::io::Write;
|
|||||||
// TODO: `include!` these from files where they're pretty-printed?
|
// TODO: `include!` these from files where they're pretty-printed?
|
||||||
const REGISTRY_ABI: &'static str = r#"[{"constant":true,"inputs":[{"name":"_data","type":"address"}],"name":"canReverse","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_new","type":"address"}],"name":"setOwner","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_key","type":"string"},{"name":"_value","type":"bytes32"}],"name":"setData","outputs":[{"name":"success","type":"bool"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_name","type":"string"}],"name":"confirmReverse","outputs":[{"name":"success","type":"bool"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_name","type":"bytes32"}],"name":"reserve","outputs":[{"name":"success","type":"bool"}],"payable":true,"type":"function"},{"constant":false,"inputs":[{"name":"_name","type":"bytes32"}],"name":"drop","outputs":[{"name":"success","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_key","type":"string"}],"name":"getAddress","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_amount","type":"uint256"}],"name":"setFee","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_to","type":"address"}],"name":"transfer","outputs":[{"name":"success","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_key","type":"string"}],"name":"getData","outputs":[{"name":"","type":"bytes32"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"_name","type":"bytes32"}],"name":"reserved","outputs":[{"name":"reserved","type":"bool"}],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"drain","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_name","type":"string"},{"name":"_who","type":"address"}],"name":"proposeReverse","outputs":[{"name":"success","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"_name","type":"bytes32"}],"name":"hasReverse","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_key","type":"string"}],"name":"getUint","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"fee","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"_name","type":"bytes32"}],"name":"getOwner","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"_name","type":"bytes32"}],"name":"getReverse","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"_data","type":"address"}],"name":"reverse","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_key","type":"string"},{"name":"_value","type":"uint256"}],"name":"setUint","outputs":[{"name":"success","type":"bool"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_name","type":"string"},{"name":"_who","type":"address"}],"name":"confirmReverseAs","outputs":[{"name":"success","type":"bool"}],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"removeReverse","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_key","type":"string"},{"name":"_value","type":"address"}],"name":"setAddress","outputs":[{"name":"success","type":"bool"}],"payable":false,"type":"function"}]"#;
|
const REGISTRY_ABI: &'static str = r#"[{"constant":true,"inputs":[{"name":"_data","type":"address"}],"name":"canReverse","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_new","type":"address"}],"name":"setOwner","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_key","type":"string"},{"name":"_value","type":"bytes32"}],"name":"setData","outputs":[{"name":"success","type":"bool"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_name","type":"string"}],"name":"confirmReverse","outputs":[{"name":"success","type":"bool"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_name","type":"bytes32"}],"name":"reserve","outputs":[{"name":"success","type":"bool"}],"payable":true,"type":"function"},{"constant":false,"inputs":[{"name":"_name","type":"bytes32"}],"name":"drop","outputs":[{"name":"success","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_key","type":"string"}],"name":"getAddress","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_amount","type":"uint256"}],"name":"setFee","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_to","type":"address"}],"name":"transfer","outputs":[{"name":"success","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_key","type":"string"}],"name":"getData","outputs":[{"name":"","type":"bytes32"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"_name","type":"bytes32"}],"name":"reserved","outputs":[{"name":"reserved","type":"bool"}],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"drain","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_name","type":"string"},{"name":"_who","type":"address"}],"name":"proposeReverse","outputs":[{"name":"success","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"_name","type":"bytes32"}],"name":"hasReverse","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_key","type":"string"}],"name":"getUint","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"fee","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"_name","type":"bytes32"}],"name":"getOwner","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"_name","type":"bytes32"}],"name":"getReverse","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"_data","type":"address"}],"name":"reverse","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_key","type":"string"},{"name":"_value","type":"uint256"}],"name":"setUint","outputs":[{"name":"success","type":"bool"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_name","type":"string"},{"name":"_who","type":"address"}],"name":"confirmReverseAs","outputs":[{"name":"success","type":"bool"}],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"removeReverse","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_key","type":"string"},{"name":"_value","type":"address"}],"name":"setAddress","outputs":[{"name":"success","type":"bool"}],"payable":false,"type":"function"}]"#;
|
||||||
const SERVICE_TRANSACTION_ABI: &'static str = r#"[{"constant":false,"inputs":[{"name":"_new","type":"address"}],"name":"setOwner","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_who","type":"address"}],"name":"certify","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"_who","type":"address"},{"name":"_field","type":"string"}],"name":"getAddress","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_who","type":"address"}],"name":"revoke","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"delegate","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"_who","type":"address"},{"name":"_field","type":"string"}],"name":"getUint","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_new","type":"address"}],"name":"setDelegate","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"_who","type":"address"}],"name":"certified","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"_who","type":"address"},{"name":"_field","type":"string"}],"name":"get","outputs":[{"name":"","type":"bytes32"}],"payable":false,"type":"function"}]"#;
|
const SERVICE_TRANSACTION_ABI: &'static str = r#"[{"constant":false,"inputs":[{"name":"_new","type":"address"}],"name":"setOwner","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_who","type":"address"}],"name":"certify","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"_who","type":"address"},{"name":"_field","type":"string"}],"name":"getAddress","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_who","type":"address"}],"name":"revoke","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"delegate","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"_who","type":"address"},{"name":"_field","type":"string"}],"name":"getUint","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_new","type":"address"}],"name":"setDelegate","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"_who","type":"address"}],"name":"certified","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"_who","type":"address"},{"name":"_field","type":"string"}],"name":"get","outputs":[{"name":"","type":"bytes32"}],"payable":false,"type":"function"}]"#;
|
||||||
|
const SECRETSTORE_ACL_STORAGE_ABI: &'static str = r#"[{"constant":true,"inputs":[{"name":"user","type":"address"},{"name":"document","type":"bytes32"}],"name":"checkPermissions","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"}]"#;
|
||||||
|
|
||||||
fn build_file(name: &str, abi: &str, filename: &str) {
|
fn build_file(name: &str, abi: &str, filename: &str) {
|
||||||
let code = ::native_contract_generator::generate_module(name, abi).unwrap();
|
let code = ::native_contract_generator::generate_module(name, abi).unwrap();
|
||||||
@ -37,4 +38,5 @@ fn build_file(name: &str, abi: &str, filename: &str) {
|
|||||||
fn main() {
|
fn main() {
|
||||||
build_file("Registry", REGISTRY_ABI, "registry.rs");
|
build_file("Registry", REGISTRY_ABI, "registry.rs");
|
||||||
build_file("ServiceTransactionChecker", SERVICE_TRANSACTION_ABI, "service_transaction.rs");
|
build_file("ServiceTransactionChecker", SERVICE_TRANSACTION_ABI, "service_transaction.rs");
|
||||||
|
build_file("SecretStoreAclStorage", SECRETSTORE_ACL_STORAGE_ABI, "secretstore_acl_storage.rs");
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,8 @@ extern crate ethcore_util as util;
|
|||||||
|
|
||||||
mod registry;
|
mod registry;
|
||||||
mod service_transaction;
|
mod service_transaction;
|
||||||
|
mod secretstore_acl_storage;
|
||||||
|
|
||||||
pub use self::registry::Registry;
|
pub use self::registry::Registry;
|
||||||
pub use self::service_transaction::ServiceTransactionChecker;
|
pub use self::service_transaction::ServiceTransactionChecker;
|
||||||
|
pub use self::secretstore_acl_storage::SecretStoreAclStorage;
|
||||||
|
22
ethcore/native_contracts/src/secretstore_acl_storage.rs
Normal file
22
ethcore/native_contracts/src/secretstore_acl_storage.rs
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
// 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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
#![allow(unused_mut, unused_variables, unused_imports)]
|
||||||
|
|
||||||
|
//! Secret store ACL storage contract.
|
||||||
|
// TODO: testing.
|
||||||
|
|
||||||
|
include!(concat!(env!("OUT_DIR"), "/secretstore_acl_storage.rs"));
|
@ -1627,10 +1627,12 @@ impl ::client::ProvingBlockChainClient for Client {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn prove_transaction(&self, transaction: SignedTransaction, id: BlockId) -> Option<Vec<DBValue>> {
|
fn prove_transaction(&self, transaction: SignedTransaction, id: BlockId) -> Option<Vec<DBValue>> {
|
||||||
let (state, env_info) = match (self.state_at(id), self.env_info(id)) {
|
let (state, mut env_info) = match (self.state_at(id), self.env_info(id)) {
|
||||||
(Some(s), Some(e)) => (s, e),
|
(Some(s), Some(e)) => (s, e),
|
||||||
_ => return None,
|
_ => return None,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
env_info.gas_limit = transaction.gas.clone();
|
||||||
let mut jdb = self.state_db.lock().journal_db().boxed_clone();
|
let mut jdb = self.state_db.lock().journal_db().boxed_clone();
|
||||||
let backend = state::backend::Proving::new(jdb.as_hashdb_mut());
|
let backend = state::backend::Proving::new(jdb.as_hashdb_mut());
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "parity.js",
|
"name": "parity.js",
|
||||||
"version": "1.7.43",
|
"version": "1.7.46",
|
||||||
"main": "release/index.js",
|
"main": "release/index.js",
|
||||||
"jsnext:main": "src/index.js",
|
"jsnext:main": "src/index.js",
|
||||||
"author": "Parity Team <admin@parity.io>",
|
"author": "Parity Team <admin@parity.io>",
|
||||||
@ -176,7 +176,7 @@
|
|||||||
"geopattern": "1.2.3",
|
"geopattern": "1.2.3",
|
||||||
"isomorphic-fetch": "2.2.1",
|
"isomorphic-fetch": "2.2.1",
|
||||||
"js-sha3": "0.5.5",
|
"js-sha3": "0.5.5",
|
||||||
"keythereum": "0.4.3",
|
"keythereum": "0.4.6",
|
||||||
"lodash": "4.17.2",
|
"lodash": "4.17.2",
|
||||||
"loglevel": "1.4.1",
|
"loglevel": "1.4.1",
|
||||||
"marked": "0.3.6",
|
"marked": "0.3.6",
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
// 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/>.
|
||||||
|
|
||||||
import { keythereum } from '../ethkey';
|
import { createKeyObject, decryptPrivateKey } from '../ethkey';
|
||||||
|
|
||||||
export default class Account {
|
export default class Account {
|
||||||
constructor (persist, data) {
|
constructor (persist, data) {
|
||||||
@ -31,12 +31,14 @@ export default class Account {
|
|||||||
}
|
}
|
||||||
|
|
||||||
isValidPassword (password) {
|
isValidPassword (password) {
|
||||||
try {
|
return decryptPrivateKey(this._keyObject, password)
|
||||||
keythereum.recover(Buffer.from(password), this._keyObject);
|
.then((privateKey) => {
|
||||||
return true;
|
if (!privateKey) {
|
||||||
} catch (e) {
|
return false;
|
||||||
return false;
|
}
|
||||||
}
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
get address () {
|
get address () {
|
||||||
@ -68,21 +70,23 @@ export default class Account {
|
|||||||
}
|
}
|
||||||
|
|
||||||
decryptPrivateKey (password) {
|
decryptPrivateKey (password) {
|
||||||
return keythereum.recover(Buffer.from(password), this._keyObject);
|
return decryptPrivateKey(this._keyObject, password);
|
||||||
|
}
|
||||||
|
|
||||||
|
changePassword (key, password) {
|
||||||
|
return createKeyObject(key, password).then((keyObject) => {
|
||||||
|
this._keyObject = keyObject;
|
||||||
|
|
||||||
|
this._persist();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromPrivateKey (persist, key, password) {
|
static fromPrivateKey (persist, key, password) {
|
||||||
const iv = keythereum.crypto.randomBytes(16);
|
return createKeyObject(key, password).then((keyObject) => {
|
||||||
const salt = keythereum.crypto.randomBytes(32);
|
const account = new Account(persist, { keyObject });
|
||||||
|
|
||||||
// Keythereum will fail if `password` is an empty string
|
return account;
|
||||||
password = Buffer.from(password);
|
});
|
||||||
|
|
||||||
const keyObject = keythereum.dump(password, key, salt, iv);
|
|
||||||
|
|
||||||
const account = new Account(persist, { keyObject });
|
|
||||||
|
|
||||||
return account;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
toJSON () {
|
toJSON () {
|
||||||
|
@ -38,14 +38,23 @@ export default class Accounts {
|
|||||||
|
|
||||||
create (secret, password) {
|
create (secret, password) {
|
||||||
const privateKey = Buffer.from(secret.slice(2), 'hex');
|
const privateKey = Buffer.from(secret.slice(2), 'hex');
|
||||||
const account = Account.fromPrivateKey(this.persist, privateKey, password);
|
|
||||||
|
|
||||||
this._store.push(account);
|
return Account
|
||||||
this.lastAddress = account.address;
|
.fromPrivateKey(this.persist, privateKey, password)
|
||||||
|
.then((account) => {
|
||||||
|
const { address } = account;
|
||||||
|
|
||||||
this.persist();
|
if (this._store.find((account) => account.address === address)) {
|
||||||
|
throw new Error(`Account ${address} already exists!`);
|
||||||
|
}
|
||||||
|
|
||||||
return account.address;
|
this._store.push(account);
|
||||||
|
this.lastAddress = address;
|
||||||
|
|
||||||
|
this.persist();
|
||||||
|
|
||||||
|
return account.address;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
set lastAddress (value) {
|
set lastAddress (value) {
|
||||||
@ -73,28 +82,41 @@ export default class Accounts {
|
|||||||
remove (address, password) {
|
remove (address, password) {
|
||||||
address = address.toLowerCase();
|
address = address.toLowerCase();
|
||||||
|
|
||||||
|
const account = this.get(address);
|
||||||
|
|
||||||
|
if (!account) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return account
|
||||||
|
.isValidPassword(password)
|
||||||
|
.then((isValid) => {
|
||||||
|
if (!isValid) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (address === this.lastAddress) {
|
||||||
|
this.lastAddress = NULL_ADDRESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.removeUnsafe(address);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
removeUnsafe (address) {
|
||||||
|
address = address.toLowerCase();
|
||||||
|
|
||||||
const index = this._store.findIndex((account) => account.address === address);
|
const index = this._store.findIndex((account) => account.address === address);
|
||||||
|
|
||||||
if (index === -1) {
|
if (index === -1) {
|
||||||
return false;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
const account = this._store[index];
|
|
||||||
|
|
||||||
if (!account.isValidPassword(password)) {
|
|
||||||
console.log('invalid password');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (address === this.lastAddress) {
|
|
||||||
this.lastAddress = NULL_ADDRESS;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this._store.splice(index, 1);
|
this._store.splice(index, 1);
|
||||||
|
|
||||||
this.persist();
|
this.persist();
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mapArray (mapper) {
|
mapArray (mapper) {
|
||||||
|
19
js/src/api/local/ethkey/dummy.js
Normal file
19
js/src/api/local/ethkey/dummy.js
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
// 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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
export default function () {
|
||||||
|
// empty file included while building parity.js (don't include local keygen)
|
||||||
|
}
|
@ -14,31 +14,35 @@
|
|||||||
// 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/>.
|
||||||
|
|
||||||
// Allow a web worker in the browser, with a fallback for Node.js
|
import workerPool from './workerPool';
|
||||||
const hasWebWorkers = typeof Worker !== 'undefined';
|
|
||||||
const KeyWorker = hasWebWorkers ? require('worker-loader!./worker')
|
|
||||||
: require('./worker').KeyWorker;
|
|
||||||
|
|
||||||
// Local accounts should never be used outside of the browser
|
export function createKeyObject (key, password) {
|
||||||
export let keythereum = null;
|
return workerPool.getWorker().action('createKeyObject', { key, password })
|
||||||
|
.then((obj) => JSON.parse(obj));
|
||||||
|
}
|
||||||
|
|
||||||
if (hasWebWorkers) {
|
export function decryptPrivateKey (keyObject, password) {
|
||||||
require('keythereum/dist/keythereum');
|
return workerPool
|
||||||
|
.getWorker()
|
||||||
|
.action('decryptPrivateKey', { keyObject, password })
|
||||||
|
.then((privateKey) => {
|
||||||
|
if (privateKey) {
|
||||||
|
return Buffer.from(privateKey);
|
||||||
|
}
|
||||||
|
|
||||||
keythereum = window.keythereum;
|
return null;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function phraseToAddress (phrase) {
|
export function phraseToAddress (phrase) {
|
||||||
return phraseToWallet(phrase).then((wallet) => wallet.address);
|
return phraseToWallet(phrase)
|
||||||
|
.then((wallet) => wallet.address);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function phraseToWallet (phrase) {
|
export function phraseToWallet (phrase) {
|
||||||
return new Promise((resolve, reject) => {
|
return workerPool.getWorker().action('phraseToWallet', phrase);
|
||||||
const worker = new KeyWorker();
|
}
|
||||||
|
|
||||||
worker.postMessage(phrase);
|
export function verifySecret (secret) {
|
||||||
worker.onmessage = ({ data }) => {
|
return workerPool.getWorker().action('verifySecret', secret);
|
||||||
resolve(data);
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
@ -14,58 +14,104 @@
|
|||||||
// 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/>.
|
||||||
|
|
||||||
import { keccak_256 as keccak256 } from 'js-sha3';
|
|
||||||
import secp256k1 from 'secp256k1/js';
|
import secp256k1 from 'secp256k1/js';
|
||||||
|
import { keccak_256 as keccak256 } from 'js-sha3';
|
||||||
|
import { bytesToHex } from '~/api/util/format';
|
||||||
|
|
||||||
|
const isWorker = typeof self !== 'undefined';
|
||||||
|
|
||||||
// Stay compatible between environments
|
// Stay compatible between environments
|
||||||
if (typeof self !== 'object') {
|
if (!isWorker) {
|
||||||
const scope = typeof global === 'undefined' ? window : global;
|
const scope = typeof global === 'undefined' ? window : global;
|
||||||
|
|
||||||
scope.self = scope;
|
scope.self = scope;
|
||||||
}
|
}
|
||||||
|
|
||||||
function bytesToHex (bytes) {
|
// keythereum should never be used outside of the browser
|
||||||
return '0x' + Array.from(bytes).map(n => ('0' + n.toString(16)).slice(-2)).join('');
|
let keythereum = null;
|
||||||
|
|
||||||
|
if (isWorker) {
|
||||||
|
require('keythereum/dist/keythereum');
|
||||||
|
|
||||||
|
keythereum = self.keythereum;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Logic ported from /ethkey/src/brain.rs
|
function route ({ action, payload }) {
|
||||||
function phraseToWallet (phrase) {
|
if (action in actions) {
|
||||||
let secret = keccak256.array(phrase);
|
return actions[action](payload);
|
||||||
|
|
||||||
for (let i = 0; i < 16384; i++) {
|
|
||||||
secret = keccak256.array(secret);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
while (true) {
|
return null;
|
||||||
secret = keccak256.array(secret);
|
}
|
||||||
|
|
||||||
const secretBuf = Buffer.from(secret);
|
const actions = {
|
||||||
|
phraseToWallet (phrase) {
|
||||||
|
let secret = keccak256.array(phrase);
|
||||||
|
|
||||||
if (secp256k1.privateKeyVerify(secretBuf)) {
|
for (let i = 0; i < 16384; i++) {
|
||||||
// No compression, slice out last 64 bytes
|
secret = keccak256.array(secret);
|
||||||
const publicBuf = secp256k1.publicKeyCreate(secretBuf, false).slice(-64);
|
}
|
||||||
const address = keccak256.array(publicBuf).slice(12);
|
|
||||||
|
|
||||||
if (address[0] !== 0) {
|
while (true) {
|
||||||
continue;
|
secret = keccak256.array(secret);
|
||||||
|
|
||||||
|
const secretBuf = Buffer.from(secret);
|
||||||
|
|
||||||
|
if (secp256k1.privateKeyVerify(secretBuf)) {
|
||||||
|
// No compression, slice out last 64 bytes
|
||||||
|
const publicBuf = secp256k1.publicKeyCreate(secretBuf, false).slice(-64);
|
||||||
|
const address = keccak256.array(publicBuf).slice(12);
|
||||||
|
|
||||||
|
if (address[0] !== 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const wallet = {
|
||||||
|
secret: bytesToHex(secretBuf),
|
||||||
|
public: bytesToHex(publicBuf),
|
||||||
|
address: bytesToHex(address)
|
||||||
|
};
|
||||||
|
|
||||||
|
return wallet;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
const wallet = {
|
verifySecret (secret) {
|
||||||
secret: bytesToHex(secretBuf),
|
const key = Buffer.from(secret.slice(2), 'hex');
|
||||||
public: bytesToHex(publicBuf),
|
|
||||||
address: bytesToHex(address)
|
|
||||||
};
|
|
||||||
|
|
||||||
return wallet;
|
return secp256k1.privateKeyVerify(key);
|
||||||
|
},
|
||||||
|
|
||||||
|
createKeyObject ({ key, password }) {
|
||||||
|
key = Buffer.from(key);
|
||||||
|
password = Buffer.from(password);
|
||||||
|
|
||||||
|
const iv = keythereum.crypto.randomBytes(16);
|
||||||
|
const salt = keythereum.crypto.randomBytes(32);
|
||||||
|
const keyObject = keythereum.dump(password, key, salt, iv);
|
||||||
|
|
||||||
|
return JSON.stringify(keyObject);
|
||||||
|
},
|
||||||
|
|
||||||
|
decryptPrivateKey ({ keyObject, password }) {
|
||||||
|
password = Buffer.from(password);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const key = keythereum.recover(password, keyObject);
|
||||||
|
|
||||||
|
// Convert to array to safely send from the worker
|
||||||
|
return Array.from(key);
|
||||||
|
} catch (e) {
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
self.onmessage = function ({ data }) {
|
self.onmessage = function ({ data }) {
|
||||||
const wallet = phraseToWallet(data);
|
const result = route(data);
|
||||||
|
|
||||||
postMessage(wallet);
|
postMessage(result);
|
||||||
close();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Emulate a web worker in Node.js
|
// Emulate a web worker in Node.js
|
||||||
@ -73,9 +119,9 @@ class KeyWorker {
|
|||||||
postMessage (data) {
|
postMessage (data) {
|
||||||
// Force async
|
// Force async
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
const wallet = phraseToWallet(data);
|
const result = route(data);
|
||||||
|
|
||||||
this.onmessage({ data: wallet });
|
this.onmessage({ data: result });
|
||||||
}, 0);
|
}, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
61
js/src/api/local/ethkey/workerPool.js
Normal file
61
js/src/api/local/ethkey/workerPool.js
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
// 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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
// Allow a web worker in the browser, with a fallback for Node.js
|
||||||
|
const hasWebWorkers = typeof Worker !== 'undefined';
|
||||||
|
const KeyWorker = hasWebWorkers ? require('worker-loader!./worker')
|
||||||
|
: require('./worker').KeyWorker;
|
||||||
|
|
||||||
|
class WorkerContainer {
|
||||||
|
busy = false;
|
||||||
|
_worker = new KeyWorker();
|
||||||
|
|
||||||
|
action (action, payload) {
|
||||||
|
if (this.busy) {
|
||||||
|
throw new Error('Cannot issue an action on a busy worker!');
|
||||||
|
}
|
||||||
|
|
||||||
|
this.busy = true;
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
this._worker.postMessage({ action, payload });
|
||||||
|
this._worker.onmessage = ({ data }) => {
|
||||||
|
this.busy = false;
|
||||||
|
resolve(data);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class WorkerPool {
|
||||||
|
pool = [];
|
||||||
|
|
||||||
|
getWorker () {
|
||||||
|
let container = this.pool.find((container) => !container.busy);
|
||||||
|
|
||||||
|
if (container) {
|
||||||
|
return container;
|
||||||
|
}
|
||||||
|
|
||||||
|
container = new WorkerContainer();
|
||||||
|
|
||||||
|
this.pool.push(container);
|
||||||
|
|
||||||
|
return container;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new WorkerPool();
|
@ -19,7 +19,7 @@ import accounts from './accounts';
|
|||||||
import transactions from './transactions';
|
import transactions from './transactions';
|
||||||
import { Middleware } from '../transport';
|
import { Middleware } from '../transport';
|
||||||
import { inNumber16 } from '../format/input';
|
import { inNumber16 } from '../format/input';
|
||||||
import { phraseToWallet, phraseToAddress } from './ethkey';
|
import { phraseToWallet, phraseToAddress, verifySecret } from './ethkey';
|
||||||
import { randomPhrase } from '@parity/wordlist';
|
import { randomPhrase } from '@parity/wordlist';
|
||||||
|
|
||||||
export default class LocalAccountsMiddleware extends Middleware {
|
export default class LocalAccountsMiddleware extends Middleware {
|
||||||
@ -57,6 +57,22 @@ export default class LocalAccountsMiddleware extends Middleware {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
register('parity_changePassword', ([address, oldPassword, newPassword]) => {
|
||||||
|
const account = accounts.get(address);
|
||||||
|
|
||||||
|
return account
|
||||||
|
.decryptPrivateKey(oldPassword)
|
||||||
|
.then((privateKey) => {
|
||||||
|
if (!privateKey) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
account.changePassword(privateKey, newPassword);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
register('parity_checkRequest', ([id]) => {
|
register('parity_checkRequest', ([id]) => {
|
||||||
return transactions.hash(id) || Promise.resolve(null);
|
return transactions.hash(id) || Promise.resolve(null);
|
||||||
});
|
});
|
||||||
@ -84,6 +100,17 @@ export default class LocalAccountsMiddleware extends Middleware {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
register('parity_newAccountFromSecret', ([secret, password]) => {
|
||||||
|
return verifySecret(secret)
|
||||||
|
.then((isValid) => {
|
||||||
|
if (!isValid) {
|
||||||
|
throw new Error('Invalid secret key');
|
||||||
|
}
|
||||||
|
|
||||||
|
return accounts.create(secret, password);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
register('parity_setAccountMeta', ([address, meta]) => {
|
register('parity_setAccountMeta', ([address, meta]) => {
|
||||||
accounts.get(address).meta = meta;
|
accounts.get(address).meta = meta;
|
||||||
|
|
||||||
@ -127,6 +154,12 @@ export default class LocalAccountsMiddleware extends Middleware {
|
|||||||
return accounts.remove(address, password);
|
return accounts.remove(address, password);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
register('parity_testPassword', ([address, password]) => {
|
||||||
|
const account = accounts.get(address);
|
||||||
|
|
||||||
|
return account.isValidPassword(password);
|
||||||
|
});
|
||||||
|
|
||||||
register('signer_confirmRequest', ([id, modify, password]) => {
|
register('signer_confirmRequest', ([id, modify, password]) => {
|
||||||
const {
|
const {
|
||||||
gasPrice,
|
gasPrice,
|
||||||
@ -137,30 +170,33 @@ export default class LocalAccountsMiddleware extends Middleware {
|
|||||||
data
|
data
|
||||||
} = Object.assign(transactions.get(id), modify);
|
} = Object.assign(transactions.get(id), modify);
|
||||||
|
|
||||||
return this
|
const account = accounts.get(from);
|
||||||
.rpcRequest('parity_nextNonce', [from])
|
|
||||||
.then((nonce) => {
|
|
||||||
const tx = new EthereumTx({
|
|
||||||
nonce,
|
|
||||||
to,
|
|
||||||
data,
|
|
||||||
gasLimit: inNumber16(gasLimit),
|
|
||||||
gasPrice: inNumber16(gasPrice),
|
|
||||||
value: inNumber16(value)
|
|
||||||
});
|
|
||||||
const account = accounts.get(from);
|
|
||||||
|
|
||||||
tx.sign(account.decryptPrivateKey(password));
|
return Promise.all([
|
||||||
|
this.rpcRequest('parity_nextNonce', [from]),
|
||||||
const serializedTx = `0x${tx.serialize().toString('hex')}`;
|
account.decryptPrivateKey(password)
|
||||||
|
])
|
||||||
return this.rpcRequest('eth_sendRawTransaction', [serializedTx]);
|
.then(([nonce, privateKey]) => {
|
||||||
})
|
const tx = new EthereumTx({
|
||||||
.then((hash) => {
|
nonce,
|
||||||
transactions.confirm(id, hash);
|
to,
|
||||||
|
data,
|
||||||
return {};
|
gasLimit: inNumber16(gasLimit),
|
||||||
|
gasPrice: inNumber16(gasPrice),
|
||||||
|
value: inNumber16(value)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
tx.sign(privateKey);
|
||||||
|
|
||||||
|
const serializedTx = `0x${tx.serialize().toString('hex')}`;
|
||||||
|
|
||||||
|
return this.rpcRequest('eth_sendRawTransaction', [serializedTx]);
|
||||||
|
})
|
||||||
|
.then((hash) => {
|
||||||
|
transactions.confirm(id, hash);
|
||||||
|
|
||||||
|
return {};
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
register('signer_rejectRequest', ([id]) => {
|
register('signer_rejectRequest', ([id]) => {
|
||||||
|
@ -80,12 +80,16 @@ export default class JsonRpcBase extends EventEmitter {
|
|||||||
const res = middleware.handle(method, params);
|
const res = middleware.handle(method, params);
|
||||||
|
|
||||||
if (res != null) {
|
if (res != null) {
|
||||||
const result = this._wrapSuccessResult(res);
|
// If `res` isn't a promise, we need to wrap it
|
||||||
const json = this.encode(method, params);
|
return Promise.resolve(res)
|
||||||
|
.then((res) => {
|
||||||
|
const result = this._wrapSuccessResult(res);
|
||||||
|
const json = this.encode(method, params);
|
||||||
|
|
||||||
Logging.send(method, params, { json, result });
|
Logging.send(method, params, { json, result });
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
import { range } from 'lodash';
|
import { range } from 'lodash';
|
||||||
|
|
||||||
export function bytesToHex (bytes) {
|
export function bytesToHex (bytes) {
|
||||||
return '0x' + bytes.map((b) => ('0' + b.toString(16)).slice(-2)).join('');
|
return '0x' + Buffer.from(bytes).toString('hex');
|
||||||
}
|
}
|
||||||
|
|
||||||
export function cleanupValue (value, type) {
|
export function cleanupValue (value, type) {
|
||||||
|
@ -23,6 +23,7 @@ import { RadioButton, RadioButtonGroup } from 'material-ui/RadioButton';
|
|||||||
import { Form, Input, IdentityIcon } from '~/ui';
|
import { Form, Input, IdentityIcon } from '~/ui';
|
||||||
import PasswordStrength from '~/ui/Form/PasswordStrength';
|
import PasswordStrength from '~/ui/Form/PasswordStrength';
|
||||||
import { RefreshIcon } from '~/ui/Icons';
|
import { RefreshIcon } from '~/ui/Icons';
|
||||||
|
import Loading from '~/ui/Loading';
|
||||||
|
|
||||||
import ChangeVault from '../ChangeVault';
|
import ChangeVault from '../ChangeVault';
|
||||||
import styles from '../createAccount.css';
|
import styles from '../createAccount.css';
|
||||||
@ -170,7 +171,9 @@ export default class CreateAccount extends Component {
|
|||||||
const { accounts } = this.state;
|
const { accounts } = this.state;
|
||||||
|
|
||||||
if (!accounts) {
|
if (!accounts) {
|
||||||
return null;
|
return (
|
||||||
|
<Loading className={ styles.selector } size={ 1 } />
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const identities = Object
|
const identities = Object
|
||||||
@ -205,6 +208,14 @@ export default class CreateAccount extends Component {
|
|||||||
createIdentities = () => {
|
createIdentities = () => {
|
||||||
const { createStore } = this.props;
|
const { createStore } = this.props;
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
accounts: null,
|
||||||
|
selectedAddress: ''
|
||||||
|
});
|
||||||
|
|
||||||
|
createStore.setAddress('');
|
||||||
|
createStore.setPhrase('');
|
||||||
|
|
||||||
return createStore
|
return createStore
|
||||||
.createIdentities()
|
.createIdentities()
|
||||||
.then((accounts) => {
|
.then((accounts) => {
|
||||||
|
@ -58,12 +58,12 @@ describe('modals/CreateAccount/NewAccount', () => {
|
|||||||
return instance.componentWillMount();
|
return instance.componentWillMount();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('creates initial accounts', () => {
|
it('resets the accounts', () => {
|
||||||
expect(Object.keys(instance.state.accounts).length).to.equal(7);
|
expect(instance.state.accounts).to.be.null;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('sets the initial selected value', () => {
|
it('resets the initial selected value', () => {
|
||||||
expect(instance.state.selectedAddress).to.equal(Object.keys(instance.state.accounts)[0]);
|
expect(instance.state.selectedAddress).to.equal('');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -69,7 +69,7 @@ export default class Store {
|
|||||||
return !(this.nameError || this.walletFileError);
|
return !(this.nameError || this.walletFileError);
|
||||||
|
|
||||||
case 'fromNew':
|
case 'fromNew':
|
||||||
return !(this.nameError || this.passwordRepeatError);
|
return !(this.nameError || this.passwordRepeatError) && this.hasAddress;
|
||||||
|
|
||||||
case 'fromPhrase':
|
case 'fromPhrase':
|
||||||
return !(this.nameError || this.passwordRepeatError);
|
return !(this.nameError || this.passwordRepeatError);
|
||||||
@ -85,6 +85,10 @@ export default class Store {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@computed get hasAddress () {
|
||||||
|
return !!(this.address);
|
||||||
|
}
|
||||||
|
|
||||||
@computed get passwordRepeatError () {
|
@computed get passwordRepeatError () {
|
||||||
return this.password === this.passwordRepeat
|
return this.password === this.passwordRepeat
|
||||||
? null
|
? null
|
||||||
|
@ -329,6 +329,7 @@ describe('modals/CreateAccount/Store', () => {
|
|||||||
describe('createType === fromNew', () => {
|
describe('createType === fromNew', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
store.setCreateType('fromNew');
|
store.setCreateType('fromNew');
|
||||||
|
store.setAddress('0x0000000000000000000000000000000000000000');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns true on no errors', () => {
|
it('returns true on no errors', () => {
|
||||||
@ -337,11 +338,13 @@ describe('modals/CreateAccount/Store', () => {
|
|||||||
|
|
||||||
it('returns false on nameError', () => {
|
it('returns false on nameError', () => {
|
||||||
store.setName('');
|
store.setName('');
|
||||||
|
|
||||||
expect(store.canCreate).to.be.false;
|
expect(store.canCreate).to.be.false;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns false on passwordRepeatError', () => {
|
it('returns false on passwordRepeatError', () => {
|
||||||
store.setPassword('testing');
|
store.setPassword('testing');
|
||||||
|
|
||||||
expect(store.canCreate).to.be.false;
|
expect(store.canCreate).to.be.false;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -92,9 +92,9 @@ export function generateQr (from, tx, hash, rlp) {
|
|||||||
account: from.substr(2),
|
account: from.substr(2),
|
||||||
hash: hash.substr(2),
|
hash: hash.substr(2),
|
||||||
details: {
|
details: {
|
||||||
gasPrice: inNumber10(inHex(tx.gasPrice.toString('hex'))),
|
gasPrice: inNumber10(inHex(tx.gasPrice.toString('hex') || '0')),
|
||||||
gas: inNumber10(inHex(tx.gasLimit.toString('hex'))),
|
gas: inNumber10(inHex(tx.gasLimit.toString('hex') || '0')),
|
||||||
nonce: inNumber10(inHex(tx.nonce.toString('hex'))),
|
nonce: inNumber10(inHex(tx.nonce.toString('hex') || '0')),
|
||||||
to: inAddress(tx.to.toString('hex')),
|
to: inAddress(tx.to.toString('hex')),
|
||||||
value: inHex(tx.value.toString('hex') || '0')
|
value: inHex(tx.value.toString('hex') || '0')
|
||||||
}
|
}
|
||||||
|
@ -24,9 +24,11 @@ const ENV = process.env.NODE_ENV || 'development';
|
|||||||
const isProd = ENV === 'production';
|
const isProd = ENV === 'production';
|
||||||
|
|
||||||
const LIBRARY = process.env.LIBRARY;
|
const LIBRARY = process.env.LIBRARY;
|
||||||
|
|
||||||
if (!LIBRARY) {
|
if (!LIBRARY) {
|
||||||
process.exit(-1);
|
process.exit(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
const SRC = LIBRARY.toLowerCase();
|
const SRC = LIBRARY.toLowerCase();
|
||||||
const OUTPUT_PATH = path.join(__dirname, '../.npmjs', SRC);
|
const OUTPUT_PATH = path.join(__dirname, '../.npmjs', SRC);
|
||||||
|
|
||||||
@ -63,12 +65,18 @@ module.exports = {
|
|||||||
'babel-loader?cacheDirectory=true'
|
'babel-loader?cacheDirectory=true'
|
||||||
],
|
],
|
||||||
exclude: /node_modules/
|
exclude: /node_modules/
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.js$/,
|
||||||
|
include: /node_modules\/(ethereumjs-tx|@parity\/wordlist)/,
|
||||||
|
use: 'babel-loader'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: {
|
alias: {
|
||||||
|
'secp256k1/js': path.resolve(__dirname, '../src/api/local/ethkey/dummy.js'),
|
||||||
'~': path.resolve(__dirname, '../src')
|
'~': path.resolve(__dirname, '../src')
|
||||||
},
|
},
|
||||||
modules: [
|
modules: [
|
||||||
@ -85,15 +93,12 @@ module.exports = {
|
|||||||
to: 'package.json',
|
to: 'package.json',
|
||||||
transform: function (content, path) {
|
transform: function (content, path) {
|
||||||
const json = JSON.parse(content.toString());
|
const json = JSON.parse(content.toString());
|
||||||
json.version = packageJson.version;
|
|
||||||
|
|
||||||
// Add tests dependencies to Dev Deps
|
|
||||||
json.devDependencies.chai = packageJson.devDependencies.chai;
|
json.devDependencies.chai = packageJson.devDependencies.chai;
|
||||||
json.devDependencies.mocha = packageJson.devDependencies.mocha;
|
json.devDependencies.mocha = packageJson.devDependencies.mocha;
|
||||||
json.devDependencies.nock = packageJson.devDependencies.nock;
|
json.devDependencies.nock = packageJson.devDependencies.nock;
|
||||||
|
|
||||||
// Add test script
|
|
||||||
json.scripts.test = 'mocha \'test/*.spec.js\'';
|
json.scripts.test = 'mocha \'test/*.spec.js\'';
|
||||||
|
json.version = packageJson.version;
|
||||||
|
|
||||||
return new Buffer(JSON.stringify(json, null, ' '), 'utf-8');
|
return new Buffer(JSON.stringify(json, null, ' '), 'utf-8');
|
||||||
}
|
}
|
||||||
|
@ -67,7 +67,7 @@ impl IoHandler<ClientIoMessage> for QueueCull {
|
|||||||
|
|
||||||
let (sync, on_demand, txq) = (self.sync.clone(), self.on_demand.clone(), self.txq.clone());
|
let (sync, on_demand, txq) = (self.sync.clone(), self.on_demand.clone(), self.txq.clone());
|
||||||
let best_header = self.client.best_block_header();
|
let best_header = self.client.best_block_header();
|
||||||
let start_nonce = self.client.engine().account_start_nonce;
|
let start_nonce = self.client.engine().account_start_nonce();
|
||||||
|
|
||||||
info!(target: "cull", "Attempting to cull queued transactions from {} senders.", senders.len());
|
info!(target: "cull", "Attempting to cull queued transactions from {} senders.", senders.len());
|
||||||
self.remote.spawn_with_timeout(move || {
|
self.remote.spawn_with_timeout(move || {
|
||||||
@ -77,7 +77,7 @@ impl IoHandler<ClientIoMessage> for QueueCull {
|
|||||||
.map(|&address| request::Account { header: best_header.clone(), address: address })
|
.map(|&address| request::Account { header: best_header.clone(), address: address })
|
||||||
.map(move |request| {
|
.map(move |request| {
|
||||||
on_demand.account(ctx, request)
|
on_demand.account(ctx, request)
|
||||||
.map(move |maybe_acc| maybe_acc.map_or(start_nonce, |acc.nonce|))
|
.map(move |maybe_acc| maybe_acc.map_or(start_nonce, |acc| acc.nonce))
|
||||||
})
|
})
|
||||||
.zip(senders.iter())
|
.zip(senders.iter())
|
||||||
.map(|(fut, &addr)| fut.map(move |nonce| (addr, nonce)));
|
.map(|(fut, &addr)| fut.map(move |nonce| (addr, nonce)));
|
||||||
|
@ -648,7 +648,9 @@ pub fn execute(cmd: RunCmd, can_restart: bool, logger: Arc<RotatingLogger>) -> R
|
|||||||
let signer_server = signer::start(cmd.signer_conf.clone(), signing_queue, signer_deps)?;
|
let signer_server = signer::start(cmd.signer_conf.clone(), signing_queue, signer_deps)?;
|
||||||
|
|
||||||
// secret store key server
|
// secret store key server
|
||||||
let secretstore_deps = secretstore::Dependencies { };
|
let secretstore_deps = secretstore::Dependencies {
|
||||||
|
client: client.clone(),
|
||||||
|
};
|
||||||
let secretstore_key_server = secretstore::start(cmd.secretstore_conf.clone(), secretstore_deps);
|
let secretstore_key_server = secretstore::start(cmd.secretstore_conf.clone(), secretstore_deps);
|
||||||
|
|
||||||
// the ipfs server
|
// the ipfs server
|
||||||
|
@ -14,7 +14,9 @@
|
|||||||
// 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::sync::Arc;
|
||||||
use dir::default_data_path;
|
use dir::default_data_path;
|
||||||
|
use ethcore::client::Client;
|
||||||
use helpers::replace_home;
|
use helpers::replace_home;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
@ -30,10 +32,10 @@ pub struct Configuration {
|
|||||||
pub data_path: String,
|
pub data_path: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
|
||||||
/// Secret store dependencies
|
/// Secret store dependencies
|
||||||
pub struct Dependencies {
|
pub struct Dependencies {
|
||||||
// the only dependency will be BlockChainClient
|
/// Blockchain client.
|
||||||
|
pub client: Arc<Client>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "secretstore"))]
|
#[cfg(not(feature = "secretstore"))]
|
||||||
@ -64,7 +66,7 @@ mod server {
|
|||||||
|
|
||||||
impl KeyServer {
|
impl KeyServer {
|
||||||
/// Create new key server
|
/// Create new key server
|
||||||
pub fn new(conf: Configuration, _deps: Dependencies) -> Result<Self, String> {
|
pub fn new(conf: Configuration, deps: Dependencies) -> Result<Self, String> {
|
||||||
let key_pairs = vec![
|
let key_pairs = vec![
|
||||||
ethkey::KeyPair::from_secret("6c26a76e9b31048d170873a791401c7e799a11f0cefc0171cc31a49800967509".parse().unwrap()).unwrap(),
|
ethkey::KeyPair::from_secret("6c26a76e9b31048d170873a791401c7e799a11f0cefc0171cc31a49800967509".parse().unwrap()).unwrap(),
|
||||||
ethkey::KeyPair::from_secret("7e94018b3731afdb3b4e6f4c3e179475640166da12e1d1b0c7d80729b1a5b452".parse().unwrap()).unwrap(),
|
ethkey::KeyPair::from_secret("7e94018b3731afdb3b4e6f4c3e179475640166da12e1d1b0c7d80729b1a5b452".parse().unwrap()).unwrap(),
|
||||||
@ -96,7 +98,7 @@ mod server {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let key_server = ethcore_secretstore::start(conf)
|
let key_server = ethcore_secretstore::start(deps.client, conf)
|
||||||
.map_err(Into::<String>::into)?;
|
.map_err(Into::<String>::into)?;
|
||||||
|
|
||||||
Ok(KeyServer {
|
Ok(KeyServer {
|
||||||
|
@ -269,7 +269,7 @@ impl LightDispatcher {
|
|||||||
|
|
||||||
match nonce_future {
|
match nonce_future {
|
||||||
Some(x) =>
|
Some(x) =>
|
||||||
x.map(|acc| acc.map_or(account_start_nonce, |acc| acc.nonce))
|
x.map(move |acc| acc.map_or(account_start_nonce, |acc| acc.nonce))
|
||||||
.map_err(|_| errors::no_light_peers())
|
.map_err(|_| errors::no_light_peers())
|
||||||
.boxed(),
|
.boxed(),
|
||||||
None => future::err(errors::network_disabled()).boxed()
|
None => future::err(errors::network_disabled()).boxed()
|
||||||
|
@ -346,3 +346,8 @@ pub fn deprecated<T: Into<Option<String>>>(message: T) -> Error {
|
|||||||
data: message.into().map(Value::String),
|
data: message.into().map(Value::String),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// on-demand sender cancelled.
|
||||||
|
pub fn on_demand_cancel(_cancel: ::futures::sync::oneshot::Canceled) -> Error {
|
||||||
|
internal("on-demand sender cancelled", "")
|
||||||
|
}
|
||||||
|
212
rpc/src/v1/helpers/light_fetch.rs
Normal file
212
rpc/src/v1/helpers/light_fetch.rs
Normal file
@ -0,0 +1,212 @@
|
|||||||
|
// 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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//! Helpers for fetching blockchain data either from the light client or the network.
|
||||||
|
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use ethcore::basic_account::BasicAccount;
|
||||||
|
use ethcore::encoded;
|
||||||
|
use ethcore::executed::{Executed, ExecutionError};
|
||||||
|
use ethcore::ids::BlockId;
|
||||||
|
use ethcore::transaction::{Action, Transaction as EthTransaction};
|
||||||
|
|
||||||
|
use futures::{future, Future, BoxFuture};
|
||||||
|
use jsonrpc_core::Error;
|
||||||
|
use jsonrpc_macros::Trailing;
|
||||||
|
|
||||||
|
use light::cache::Cache;
|
||||||
|
use light::client::LightChainClient;
|
||||||
|
use light::cht;
|
||||||
|
use light::on_demand::{OnDemand, request};
|
||||||
|
|
||||||
|
use ethsync::LightSync;
|
||||||
|
use util::{Address, Mutex, Uint, U256};
|
||||||
|
|
||||||
|
use v1::helpers::{CallRequest as CRequest, errors, dispatch};
|
||||||
|
use v1::types::{BlockNumber, CallRequest};
|
||||||
|
|
||||||
|
/// Helper for fetching blockchain data either from the light client or the network
|
||||||
|
/// as necessary.
|
||||||
|
pub struct LightFetch {
|
||||||
|
/// The light client.
|
||||||
|
pub client: Arc<LightChainClient>,
|
||||||
|
/// The on-demand request service.
|
||||||
|
pub on_demand: Arc<OnDemand>,
|
||||||
|
/// Handle to the network.
|
||||||
|
pub sync: Arc<LightSync>,
|
||||||
|
/// The light data cache.
|
||||||
|
pub cache: Arc<Mutex<Cache>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Type alias for convenience.
|
||||||
|
pub type ExecutionResult = Result<Executed, ExecutionError>;
|
||||||
|
|
||||||
|
impl LightFetch {
|
||||||
|
/// Get a block header from the on demand service or client, or error.
|
||||||
|
pub fn header(&self, id: BlockId) -> BoxFuture<encoded::Header, Error> {
|
||||||
|
if let Some(h) = self.client.block_header(id) {
|
||||||
|
return future::ok(h).boxed()
|
||||||
|
}
|
||||||
|
|
||||||
|
let maybe_future = match id {
|
||||||
|
BlockId::Number(n) => {
|
||||||
|
let cht_root = cht::block_to_cht_number(n).and_then(|cn| self.client.cht_root(cn as usize));
|
||||||
|
match cht_root {
|
||||||
|
None => return future::err(errors::unknown_block()).boxed(),
|
||||||
|
Some(root) => {
|
||||||
|
let req = request::HeaderProof::new(n, root)
|
||||||
|
.expect("only fails for 0; client always stores genesis; client already queried; qed");
|
||||||
|
|
||||||
|
let (sync, on_demand) = (self.sync.clone(), self.on_demand.clone());
|
||||||
|
self.sync.with_context(|ctx| {
|
||||||
|
let fut = self.on_demand.hash_by_number(ctx, req)
|
||||||
|
.map(request::HeaderByHash)
|
||||||
|
.map_err(errors::on_demand_cancel);
|
||||||
|
|
||||||
|
fut.and_then(move |req| {
|
||||||
|
match sync.with_context(|ctx| on_demand.header_by_hash(ctx, req)) {
|
||||||
|
Some(fut) => fut.map_err(errors::on_demand_cancel).boxed(),
|
||||||
|
None => future::err(errors::network_disabled()).boxed(),
|
||||||
|
}
|
||||||
|
}).boxed()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BlockId::Hash(h) => {
|
||||||
|
self.sync.with_context(|ctx|
|
||||||
|
self.on_demand.header_by_hash(ctx, request::HeaderByHash(h))
|
||||||
|
.then(|res| future::done(match res {
|
||||||
|
Ok(h) => Ok(h),
|
||||||
|
Err(e) => Err(errors::on_demand_cancel(e)),
|
||||||
|
}))
|
||||||
|
.boxed()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
_ => None, // latest, earliest, and pending will have all already returned.
|
||||||
|
};
|
||||||
|
|
||||||
|
match maybe_future {
|
||||||
|
Some(recv) => recv,
|
||||||
|
None => future::err(errors::network_disabled()).boxed()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// helper for getting account info at a given block.
|
||||||
|
/// `None` indicates the account doesn't exist at the given block.
|
||||||
|
pub fn account(&self, address: Address, id: BlockId) -> BoxFuture<Option<BasicAccount>, Error> {
|
||||||
|
let (sync, on_demand) = (self.sync.clone(), self.on_demand.clone());
|
||||||
|
|
||||||
|
self.header(id).and_then(move |header| {
|
||||||
|
let maybe_fut = sync.with_context(|ctx| on_demand.account(ctx, request::Account {
|
||||||
|
header: header,
|
||||||
|
address: address,
|
||||||
|
}));
|
||||||
|
|
||||||
|
match maybe_fut {
|
||||||
|
Some(fut) => fut.map_err(errors::on_demand_cancel).boxed(),
|
||||||
|
None => future::err(errors::network_disabled()).boxed(),
|
||||||
|
}
|
||||||
|
}).boxed()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// helper for getting proved execution.
|
||||||
|
pub fn proved_execution(&self, req: CallRequest, num: Trailing<BlockNumber>) -> BoxFuture<ExecutionResult, Error> {
|
||||||
|
const DEFAULT_GAS_PRICE: U256 = U256([0, 0, 0, 21_000_000]);
|
||||||
|
|
||||||
|
let (sync, on_demand, client) = (self.sync.clone(), self.on_demand.clone(), self.client.clone());
|
||||||
|
let req: CRequest = req.into();
|
||||||
|
let id = num.0.into();
|
||||||
|
|
||||||
|
let from = req.from.unwrap_or(Address::zero());
|
||||||
|
let nonce_fut = match req.nonce {
|
||||||
|
Some(nonce) => future::ok(Some(nonce)).boxed(),
|
||||||
|
None => self.account(from, id).map(|acc| acc.map(|a| a.nonce)).boxed(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let gas_price_fut = match req.gas_price {
|
||||||
|
Some(price) => future::ok(price).boxed(),
|
||||||
|
None => dispatch::fetch_gas_price_corpus(
|
||||||
|
self.sync.clone(),
|
||||||
|
self.client.clone(),
|
||||||
|
self.on_demand.clone(),
|
||||||
|
self.cache.clone(),
|
||||||
|
).map(|corp| match corp.median() {
|
||||||
|
Some(median) => *median,
|
||||||
|
None => DEFAULT_GAS_PRICE,
|
||||||
|
}).boxed()
|
||||||
|
};
|
||||||
|
|
||||||
|
// if nonce resolves, this should too since it'll be in the LRU-cache.
|
||||||
|
let header_fut = self.header(id);
|
||||||
|
|
||||||
|
// fetch missing transaction fields from the network.
|
||||||
|
nonce_fut.join(gas_price_fut).and_then(move |(nonce, gas_price)| {
|
||||||
|
let action = req.to.map_or(Action::Create, Action::Call);
|
||||||
|
let gas = req.gas.unwrap_or(U256::from(10_000_000)); // better gas amount?
|
||||||
|
let value = req.value.unwrap_or_else(U256::zero);
|
||||||
|
let data = req.data.map_or_else(Vec::new, |d| d.to_vec());
|
||||||
|
|
||||||
|
future::done(match nonce {
|
||||||
|
Some(n) => Ok(EthTransaction {
|
||||||
|
nonce: n,
|
||||||
|
action: action,
|
||||||
|
gas: gas,
|
||||||
|
gas_price: gas_price,
|
||||||
|
value: value,
|
||||||
|
data: data,
|
||||||
|
}.fake_sign(from)),
|
||||||
|
None => Err(errors::unknown_block()),
|
||||||
|
})
|
||||||
|
}).join(header_fut).and_then(move |(tx, hdr)| {
|
||||||
|
// then request proved execution.
|
||||||
|
// TODO: get last-hashes from network.
|
||||||
|
let env_info = match client.env_info(id) {
|
||||||
|
Some(env_info) => env_info,
|
||||||
|
_ => return future::err(errors::unknown_block()).boxed(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let request = request::TransactionProof {
|
||||||
|
tx: tx,
|
||||||
|
header: hdr,
|
||||||
|
env_info: env_info,
|
||||||
|
engine: client.engine().clone(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let proved_future = sync.with_context(move |ctx| {
|
||||||
|
on_demand.transaction_proof(ctx, request).map_err(errors::on_demand_cancel).boxed()
|
||||||
|
});
|
||||||
|
|
||||||
|
match proved_future {
|
||||||
|
Some(fut) => fut.boxed(),
|
||||||
|
None => future::err(errors::network_disabled()).boxed(),
|
||||||
|
}
|
||||||
|
}).boxed()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// get a block itself. fails on unknown block ID.
|
||||||
|
pub fn block(&self, id: BlockId) -> BoxFuture<encoded::Block, Error> {
|
||||||
|
let (on_demand, sync) = (self.on_demand.clone(), self.sync.clone());
|
||||||
|
|
||||||
|
self.header(id).map(request::Body::new).and_then(move |req| {
|
||||||
|
match sync.with_context(move |ctx| on_demand.block(ctx, req)) {
|
||||||
|
Some(fut) => fut.map_err(errors::on_demand_cancel).boxed(),
|
||||||
|
None => future::err(errors::network_disabled()).boxed(),
|
||||||
|
}
|
||||||
|
}).boxed()
|
||||||
|
}
|
||||||
|
}
|
@ -21,6 +21,7 @@ pub mod accounts;
|
|||||||
pub mod block_import;
|
pub mod block_import;
|
||||||
pub mod dispatch;
|
pub mod dispatch;
|
||||||
pub mod fake_sign;
|
pub mod fake_sign;
|
||||||
|
pub mod light_fetch;
|
||||||
pub mod informant;
|
pub mod informant;
|
||||||
pub mod oneshot;
|
pub mod oneshot;
|
||||||
|
|
||||||
|
@ -146,7 +146,7 @@ impl<C, SN: ?Sized, S: ?Sized, M, EM> EthClient<C, SN, S, M, EM> where
|
|||||||
(Some(block), Some(total_difficulty)) => {
|
(Some(block), Some(total_difficulty)) => {
|
||||||
let view = block.header_view();
|
let view = block.header_view();
|
||||||
Ok(Some(RichBlock {
|
Ok(Some(RichBlock {
|
||||||
block: Block {
|
inner: Block {
|
||||||
hash: Some(view.sha3().into()),
|
hash: Some(view.sha3().into()),
|
||||||
size: Some(block.rlp().as_raw().len().into()),
|
size: Some(block.rlp().as_raw().len().into()),
|
||||||
parent_hash: view.parent_hash().into(),
|
parent_hash: view.parent_hash().into(),
|
||||||
@ -202,7 +202,7 @@ impl<C, SN: ?Sized, S: ?Sized, M, EM> EthClient<C, SN, S, M, EM> where
|
|||||||
.map(Into::into);
|
.map(Into::into);
|
||||||
|
|
||||||
let block = RichBlock {
|
let block = RichBlock {
|
||||||
block: Block {
|
inner: Block {
|
||||||
hash: Some(uncle.hash().into()),
|
hash: Some(uncle.hash().into()),
|
||||||
size: size,
|
size: size,
|
||||||
parent_hash: uncle.parent_hash().clone().into(),
|
parent_hash: uncle.parent_hash().clone().into(),
|
||||||
|
@ -48,6 +48,7 @@ use v1::impls::eth_filter::Filterable;
|
|||||||
use v1::helpers::{CallRequest as CRequest, errors, limit_logs, dispatch};
|
use v1::helpers::{CallRequest as CRequest, errors, limit_logs, dispatch};
|
||||||
use v1::helpers::{PollFilter, PollManager};
|
use v1::helpers::{PollFilter, PollManager};
|
||||||
use v1::helpers::block_import::is_major_importing;
|
use v1::helpers::block_import::is_major_importing;
|
||||||
|
use v1::helpers::light_fetch::LightFetch;
|
||||||
use v1::traits::Eth;
|
use v1::traits::Eth;
|
||||||
use v1::types::{
|
use v1::types::{
|
||||||
RichBlock, Block, BlockTransactions, BlockNumber, Bytes, SyncStatus, SyncInfo,
|
RichBlock, Block, BlockTransactions, BlockNumber, Bytes, SyncStatus, SyncInfo,
|
||||||
@ -84,12 +85,6 @@ impl Clone for EthClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// helper for internal error: on demand sender cancelled.
|
|
||||||
fn err_premature_cancel(_cancel: oneshot::Canceled) -> Error {
|
|
||||||
errors::internal("on-demand sender prematurely cancelled", "")
|
|
||||||
}
|
|
||||||
|
|
||||||
type ExecutionResult = Result<Executed, ExecutionError>;
|
|
||||||
|
|
||||||
impl EthClient {
|
impl EthClient {
|
||||||
/// Create a new `EthClient` with a handle to the light sync instance, client,
|
/// Create a new `EthClient` with a handle to the light sync instance, client,
|
||||||
@ -113,158 +108,15 @@ impl EthClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a block header from the on demand service or client, or error.
|
/// Create a light data fetcher instance.
|
||||||
fn header(&self, id: BlockId) -> BoxFuture<encoded::Header, Error> {
|
fn fetcher(&self) -> LightFetch {
|
||||||
if let Some(h) = self.client.block_header(id) {
|
LightFetch {
|
||||||
return future::ok(h).boxed()
|
client: self.client.clone(),
|
||||||
|
on_demand: self.on_demand.clone(),
|
||||||
|
sync: self.sync.clone(),
|
||||||
|
cache: self.cache.clone(),
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let maybe_future = match id {
|
|
||||||
BlockId::Number(n) => {
|
|
||||||
let cht_root = cht::block_to_cht_number(n).and_then(|cn| self.client.cht_root(cn as usize));
|
|
||||||
match cht_root {
|
|
||||||
None => return future::err(errors::unknown_block()).boxed(),
|
|
||||||
Some(root) => {
|
|
||||||
let req = request::HeaderProof::new(n, root)
|
|
||||||
.expect("only fails for 0; client always stores genesis; client already queried; qed");
|
|
||||||
|
|
||||||
let (sync, on_demand) = (self.sync.clone(), self.on_demand.clone());
|
|
||||||
self.sync.with_context(|ctx| {
|
|
||||||
let fut = self.on_demand.hash_by_number(ctx, req)
|
|
||||||
.map(request::HeaderByHash)
|
|
||||||
.map_err(err_premature_cancel);
|
|
||||||
|
|
||||||
fut.and_then(move |req| {
|
|
||||||
match sync.with_context(|ctx| on_demand.header_by_hash(ctx, req)) {
|
|
||||||
Some(fut) => fut.map_err(err_premature_cancel).boxed(),
|
|
||||||
None => future::err(errors::network_disabled()).boxed(),
|
|
||||||
}
|
|
||||||
}).boxed()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
BlockId::Hash(h) => {
|
|
||||||
self.sync.with_context(|ctx|
|
|
||||||
self.on_demand.header_by_hash(ctx, request::HeaderByHash(h))
|
|
||||||
.then(|res| future::done(match res {
|
|
||||||
Ok(h) => Ok(h),
|
|
||||||
Err(e) => Err(err_premature_cancel(e)),
|
|
||||||
}))
|
|
||||||
.boxed()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
_ => None, // latest, earliest, and pending will have all already returned.
|
|
||||||
};
|
|
||||||
|
|
||||||
match maybe_future {
|
|
||||||
Some(recv) => recv,
|
|
||||||
None => future::err(errors::network_disabled()).boxed()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// helper for getting account info at a given block.
|
|
||||||
// `None` indicates the account doesn't exist at the given block.
|
|
||||||
fn account(&self, address: Address, id: BlockId) -> BoxFuture<Option<BasicAccount>, Error> {
|
|
||||||
let (sync, on_demand) = (self.sync.clone(), self.on_demand.clone());
|
|
||||||
|
|
||||||
self.header(id).and_then(move |header| {
|
|
||||||
let maybe_fut = sync.with_context(|ctx| on_demand.account(ctx, request::Account {
|
|
||||||
header: header,
|
|
||||||
address: address,
|
|
||||||
}));
|
|
||||||
|
|
||||||
match maybe_fut {
|
|
||||||
Some(fut) => fut.map_err(err_premature_cancel).boxed(),
|
|
||||||
None => future::err(errors::network_disabled()).boxed(),
|
|
||||||
}
|
|
||||||
}).boxed()
|
|
||||||
}
|
|
||||||
|
|
||||||
// helper for getting proved execution.
|
|
||||||
fn proved_execution(&self, req: CallRequest, num: Trailing<BlockNumber>) -> BoxFuture<ExecutionResult, Error> {
|
|
||||||
const DEFAULT_GAS_PRICE: U256 = U256([0, 0, 0, 21_000_000]);
|
|
||||||
|
|
||||||
let (sync, on_demand, client) = (self.sync.clone(), self.on_demand.clone(), self.client.clone());
|
|
||||||
let req: CRequest = req.into();
|
|
||||||
let id = num.0.into();
|
|
||||||
|
|
||||||
let from = req.from.unwrap_or(Address::zero());
|
|
||||||
let nonce_fut = match req.nonce {
|
|
||||||
Some(nonce) => future::ok(Some(nonce)).boxed(),
|
|
||||||
None => self.account(from, id).map(|acc| acc.map(|a| a.nonce)).boxed(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let gas_price_fut = match req.gas_price {
|
|
||||||
Some(price) => future::ok(price).boxed(),
|
|
||||||
None => dispatch::fetch_gas_price_corpus(
|
|
||||||
self.sync.clone(),
|
|
||||||
self.client.clone(),
|
|
||||||
self.on_demand.clone(),
|
|
||||||
self.cache.clone(),
|
|
||||||
).map(|corp| match corp.median() {
|
|
||||||
Some(median) => *median,
|
|
||||||
None => DEFAULT_GAS_PRICE,
|
|
||||||
}).boxed()
|
|
||||||
};
|
|
||||||
|
|
||||||
// if nonce resolves, this should too since it'll be in the LRU-cache.
|
|
||||||
let header_fut = self.header(id);
|
|
||||||
|
|
||||||
// fetch missing transaction fields from the network.
|
|
||||||
nonce_fut.join(gas_price_fut).and_then(move |(nonce, gas_price)| {
|
|
||||||
let action = req.to.map_or(Action::Create, Action::Call);
|
|
||||||
let gas = req.gas.unwrap_or(U256::from(10_000_000)); // better gas amount?
|
|
||||||
let value = req.value.unwrap_or_else(U256::zero);
|
|
||||||
let data = req.data.map_or_else(Vec::new, |d| d.to_vec());
|
|
||||||
|
|
||||||
future::done(match nonce {
|
|
||||||
Some(n) => Ok(EthTransaction {
|
|
||||||
nonce: n,
|
|
||||||
action: action,
|
|
||||||
gas: gas,
|
|
||||||
gas_price: gas_price,
|
|
||||||
value: value,
|
|
||||||
data: data,
|
|
||||||
}.fake_sign(from)),
|
|
||||||
None => Err(errors::unknown_block()),
|
|
||||||
})
|
|
||||||
}).join(header_fut).and_then(move |(tx, hdr)| {
|
|
||||||
// then request proved execution.
|
|
||||||
// TODO: get last-hashes from network.
|
|
||||||
let env_info = match client.env_info(id) {
|
|
||||||
Some(env_info) => env_info,
|
|
||||||
_ => return future::err(errors::unknown_block()).boxed(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let request = request::TransactionProof {
|
|
||||||
tx: tx,
|
|
||||||
header: hdr,
|
|
||||||
env_info: env_info,
|
|
||||||
engine: client.engine().clone(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let proved_future = sync.with_context(move |ctx| {
|
|
||||||
on_demand.transaction_proof(ctx, request).map_err(err_premature_cancel).boxed()
|
|
||||||
});
|
|
||||||
|
|
||||||
match proved_future {
|
|
||||||
Some(fut) => fut.boxed(),
|
|
||||||
None => future::err(errors::network_disabled()).boxed(),
|
|
||||||
}
|
|
||||||
}).boxed()
|
|
||||||
}
|
|
||||||
|
|
||||||
// get a block itself. fails on unknown block ID.
|
|
||||||
fn block(&self, id: BlockId) -> BoxFuture<encoded::Block, Error> {
|
|
||||||
let (on_demand, sync) = (self.on_demand.clone(), self.sync.clone());
|
|
||||||
|
|
||||||
self.header(id).map(request::Body::new).and_then(move |req| {
|
|
||||||
match sync.with_context(move |ctx| on_demand.block(ctx, req)) {
|
|
||||||
Some(fut) => fut.map_err(err_premature_cancel).boxed(),
|
|
||||||
None => future::err(errors::network_disabled()).boxed(),
|
|
||||||
}
|
|
||||||
}).boxed()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// get a "rich" block structure. Fails on unknown block.
|
// get a "rich" block structure. Fails on unknown block.
|
||||||
@ -277,7 +129,7 @@ impl EthClient {
|
|||||||
let header = block.decode_header();
|
let header = block.decode_header();
|
||||||
let extra_info = engine.extra_info(&header);
|
let extra_info = engine.extra_info(&header);
|
||||||
RichBlock {
|
RichBlock {
|
||||||
block: Block {
|
inner: Block {
|
||||||
hash: Some(header.hash().into()),
|
hash: Some(header.hash().into()),
|
||||||
size: Some(block.rlp().as_raw().len().into()),
|
size: Some(block.rlp().as_raw().len().into()),
|
||||||
parent_hash: header.parent_hash().clone().into(),
|
parent_hash: header.parent_hash().clone().into(),
|
||||||
@ -307,7 +159,7 @@ impl EthClient {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// get the block itself.
|
// get the block itself.
|
||||||
self.block(id).and_then(move |block| {
|
self.fetcher().block(id).and_then(move |block| {
|
||||||
// then fetch the total difficulty (this is much easier after getting the block).
|
// then fetch the total difficulty (this is much easier after getting the block).
|
||||||
match client.score(id) {
|
match client.score(id) {
|
||||||
Some(score) => future::ok(fill_rich(block, Some(score))).boxed(),
|
Some(score) => future::ok(fill_rich(block, Some(score))).boxed(),
|
||||||
@ -344,7 +196,7 @@ impl EthClient {
|
|||||||
};
|
};
|
||||||
|
|
||||||
fill_rich(block, score)
|
fill_rich(block, score)
|
||||||
}).map_err(err_premature_cancel).boxed(),
|
}).map_err(errors::on_demand_cancel).boxed(),
|
||||||
None => return future::err(errors::network_disabled()).boxed(),
|
None => return future::err(errors::network_disabled()).boxed(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -415,7 +267,7 @@ impl Eth for EthClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn balance(&self, address: RpcH160, num: Trailing<BlockNumber>) -> BoxFuture<RpcU256, Error> {
|
fn balance(&self, address: RpcH160, num: Trailing<BlockNumber>) -> BoxFuture<RpcU256, Error> {
|
||||||
self.account(address.into(), num.0.into())
|
self.fetcher().account(address.into(), num.0.into())
|
||||||
.map(|acc| acc.map_or(0.into(), |a| a.balance).into()).boxed()
|
.map(|acc| acc.map_or(0.into(), |a| a.balance).into()).boxed()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -432,20 +284,20 @@ impl Eth for EthClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn transaction_count(&self, address: RpcH160, num: Trailing<BlockNumber>) -> BoxFuture<RpcU256, Error> {
|
fn transaction_count(&self, address: RpcH160, num: Trailing<BlockNumber>) -> BoxFuture<RpcU256, Error> {
|
||||||
self.account(address.into(), num.0.into())
|
self.fetcher().account(address.into(), num.0.into())
|
||||||
.map(|acc| acc.map_or(0.into(), |a| a.nonce).into()).boxed()
|
.map(|acc| acc.map_or(0.into(), |a| a.nonce).into()).boxed()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn block_transaction_count_by_hash(&self, hash: RpcH256) -> BoxFuture<Option<RpcU256>, Error> {
|
fn block_transaction_count_by_hash(&self, hash: RpcH256) -> BoxFuture<Option<RpcU256>, Error> {
|
||||||
let (sync, on_demand) = (self.sync.clone(), self.on_demand.clone());
|
let (sync, on_demand) = (self.sync.clone(), self.on_demand.clone());
|
||||||
|
|
||||||
self.header(BlockId::Hash(hash.into())).and_then(move |hdr| {
|
self.fetcher().header(BlockId::Hash(hash.into())).and_then(move |hdr| {
|
||||||
if hdr.transactions_root() == SHA3_NULL_RLP {
|
if hdr.transactions_root() == SHA3_NULL_RLP {
|
||||||
future::ok(Some(U256::from(0).into())).boxed()
|
future::ok(Some(U256::from(0).into())).boxed()
|
||||||
} else {
|
} else {
|
||||||
sync.with_context(|ctx| on_demand.block(ctx, request::Body::new(hdr)))
|
sync.with_context(|ctx| on_demand.block(ctx, request::Body::new(hdr)))
|
||||||
.map(|x| x.map(|b| Some(U256::from(b.transactions_count()).into())))
|
.map(|x| x.map(|b| Some(U256::from(b.transactions_count()).into())))
|
||||||
.map(|x| x.map_err(err_premature_cancel).boxed())
|
.map(|x| x.map_err(errors::on_demand_cancel).boxed())
|
||||||
.unwrap_or_else(|| future::err(errors::network_disabled()).boxed())
|
.unwrap_or_else(|| future::err(errors::network_disabled()).boxed())
|
||||||
}
|
}
|
||||||
}).boxed()
|
}).boxed()
|
||||||
@ -454,13 +306,13 @@ impl Eth for EthClient {
|
|||||||
fn block_transaction_count_by_number(&self, num: BlockNumber) -> BoxFuture<Option<RpcU256>, Error> {
|
fn block_transaction_count_by_number(&self, num: BlockNumber) -> BoxFuture<Option<RpcU256>, Error> {
|
||||||
let (sync, on_demand) = (self.sync.clone(), self.on_demand.clone());
|
let (sync, on_demand) = (self.sync.clone(), self.on_demand.clone());
|
||||||
|
|
||||||
self.header(num.into()).and_then(move |hdr| {
|
self.fetcher().header(num.into()).and_then(move |hdr| {
|
||||||
if hdr.transactions_root() == SHA3_NULL_RLP {
|
if hdr.transactions_root() == SHA3_NULL_RLP {
|
||||||
future::ok(Some(U256::from(0).into())).boxed()
|
future::ok(Some(U256::from(0).into())).boxed()
|
||||||
} else {
|
} else {
|
||||||
sync.with_context(|ctx| on_demand.block(ctx, request::Body::new(hdr)))
|
sync.with_context(|ctx| on_demand.block(ctx, request::Body::new(hdr)))
|
||||||
.map(|x| x.map(|b| Some(U256::from(b.transactions_count()).into())))
|
.map(|x| x.map(|b| Some(U256::from(b.transactions_count()).into())))
|
||||||
.map(|x| x.map_err(err_premature_cancel).boxed())
|
.map(|x| x.map_err(errors::on_demand_cancel).boxed())
|
||||||
.unwrap_or_else(|| future::err(errors::network_disabled()).boxed())
|
.unwrap_or_else(|| future::err(errors::network_disabled()).boxed())
|
||||||
}
|
}
|
||||||
}).boxed()
|
}).boxed()
|
||||||
@ -469,13 +321,13 @@ impl Eth for EthClient {
|
|||||||
fn block_uncles_count_by_hash(&self, hash: RpcH256) -> BoxFuture<Option<RpcU256>, Error> {
|
fn block_uncles_count_by_hash(&self, hash: RpcH256) -> BoxFuture<Option<RpcU256>, Error> {
|
||||||
let (sync, on_demand) = (self.sync.clone(), self.on_demand.clone());
|
let (sync, on_demand) = (self.sync.clone(), self.on_demand.clone());
|
||||||
|
|
||||||
self.header(BlockId::Hash(hash.into())).and_then(move |hdr| {
|
self.fetcher().header(BlockId::Hash(hash.into())).and_then(move |hdr| {
|
||||||
if hdr.uncles_hash() == SHA3_EMPTY_LIST_RLP {
|
if hdr.uncles_hash() == SHA3_EMPTY_LIST_RLP {
|
||||||
future::ok(Some(U256::from(0).into())).boxed()
|
future::ok(Some(U256::from(0).into())).boxed()
|
||||||
} else {
|
} else {
|
||||||
sync.with_context(|ctx| on_demand.block(ctx, request::Body::new(hdr)))
|
sync.with_context(|ctx| on_demand.block(ctx, request::Body::new(hdr)))
|
||||||
.map(|x| x.map(|b| Some(U256::from(b.uncles_count()).into())))
|
.map(|x| x.map(|b| Some(U256::from(b.uncles_count()).into())))
|
||||||
.map(|x| x.map_err(err_premature_cancel).boxed())
|
.map(|x| x.map_err(errors::on_demand_cancel).boxed())
|
||||||
.unwrap_or_else(|| future::err(errors::network_disabled()).boxed())
|
.unwrap_or_else(|| future::err(errors::network_disabled()).boxed())
|
||||||
}
|
}
|
||||||
}).boxed()
|
}).boxed()
|
||||||
@ -484,13 +336,13 @@ impl Eth for EthClient {
|
|||||||
fn block_uncles_count_by_number(&self, num: BlockNumber) -> BoxFuture<Option<RpcU256>, Error> {
|
fn block_uncles_count_by_number(&self, num: BlockNumber) -> BoxFuture<Option<RpcU256>, Error> {
|
||||||
let (sync, on_demand) = (self.sync.clone(), self.on_demand.clone());
|
let (sync, on_demand) = (self.sync.clone(), self.on_demand.clone());
|
||||||
|
|
||||||
self.header(num.into()).and_then(move |hdr| {
|
self.fetcher().header(num.into()).and_then(move |hdr| {
|
||||||
if hdr.uncles_hash() == SHA3_EMPTY_LIST_RLP {
|
if hdr.uncles_hash() == SHA3_EMPTY_LIST_RLP {
|
||||||
future::ok(Some(U256::from(0).into())).boxed()
|
future::ok(Some(U256::from(0).into())).boxed()
|
||||||
} else {
|
} else {
|
||||||
sync.with_context(|ctx| on_demand.block(ctx, request::Body::new(hdr)))
|
sync.with_context(|ctx| on_demand.block(ctx, request::Body::new(hdr)))
|
||||||
.map(|x| x.map(|b| Some(U256::from(b.uncles_count()).into())))
|
.map(|x| x.map(|b| Some(U256::from(b.uncles_count()).into())))
|
||||||
.map(|x| x.map_err(err_premature_cancel).boxed())
|
.map(|x| x.map_err(errors::on_demand_cancel).boxed())
|
||||||
.unwrap_or_else(|| future::err(errors::network_disabled()).boxed())
|
.unwrap_or_else(|| future::err(errors::network_disabled()).boxed())
|
||||||
}
|
}
|
||||||
}).boxed()
|
}).boxed()
|
||||||
@ -525,7 +377,7 @@ impl Eth for EthClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn call(&self, req: CallRequest, num: Trailing<BlockNumber>) -> BoxFuture<Bytes, Error> {
|
fn call(&self, req: CallRequest, num: Trailing<BlockNumber>) -> BoxFuture<Bytes, Error> {
|
||||||
self.proved_execution(req, num).and_then(|res| {
|
self.fetcher().proved_execution(req, num).and_then(|res| {
|
||||||
match res {
|
match res {
|
||||||
Ok(exec) => Ok(exec.output.into()),
|
Ok(exec) => Ok(exec.output.into()),
|
||||||
Err(e) => Err(errors::execution(e)),
|
Err(e) => Err(errors::execution(e)),
|
||||||
@ -535,7 +387,7 @@ impl Eth for EthClient {
|
|||||||
|
|
||||||
fn estimate_gas(&self, req: CallRequest, num: Trailing<BlockNumber>) -> BoxFuture<RpcU256, Error> {
|
fn estimate_gas(&self, req: CallRequest, num: Trailing<BlockNumber>) -> BoxFuture<RpcU256, Error> {
|
||||||
// TODO: binary chop for more accurate estimates.
|
// TODO: binary chop for more accurate estimates.
|
||||||
self.proved_execution(req, num).and_then(|res| {
|
self.fetcher().proved_execution(req, num).and_then(|res| {
|
||||||
match res {
|
match res {
|
||||||
Ok(exec) => Ok((exec.refunded + exec.gas_used).into()),
|
Ok(exec) => Ok((exec.refunded + exec.gas_used).into()),
|
||||||
Err(e) => Err(errors::execution(e)),
|
Err(e) => Err(errors::execution(e)),
|
||||||
@ -662,7 +514,7 @@ impl Filterable for EthClient {
|
|||||||
future::ok(matches)
|
future::ok(matches)
|
||||||
}) // and then collect them into a vector.
|
}) // and then collect them into a vector.
|
||||||
.map(|matches| matches.into_iter().map(|(_, v)| v).collect())
|
.map(|matches| matches.into_iter().map(|(_, v)| v).collect())
|
||||||
.map_err(err_premature_cancel)
|
.map_err(errors::on_demand_cancel)
|
||||||
});
|
});
|
||||||
|
|
||||||
match maybe_future {
|
match maybe_future {
|
||||||
|
@ -32,6 +32,7 @@ use jsonrpc_core::Error;
|
|||||||
use jsonrpc_macros::Trailing;
|
use jsonrpc_macros::Trailing;
|
||||||
use v1::helpers::{errors, SigningQueue, SignerService, NetworkSettings};
|
use v1::helpers::{errors, SigningQueue, SignerService, NetworkSettings};
|
||||||
use v1::helpers::dispatch::{LightDispatcher, DEFAULT_MAC};
|
use v1::helpers::dispatch::{LightDispatcher, DEFAULT_MAC};
|
||||||
|
use v1::helpers::light_fetch::LightFetch;
|
||||||
use v1::metadata::Metadata;
|
use v1::metadata::Metadata;
|
||||||
use v1::traits::Parity;
|
use v1::traits::Parity;
|
||||||
use v1::types::{
|
use v1::types::{
|
||||||
@ -40,7 +41,7 @@ use v1::types::{
|
|||||||
TransactionStats, LocalTransactionStatus,
|
TransactionStats, LocalTransactionStatus,
|
||||||
BlockNumber, ConsensusCapability, VersionInfo,
|
BlockNumber, ConsensusCapability, VersionInfo,
|
||||||
OperationsInfo, DappId, ChainStatus,
|
OperationsInfo, DappId, ChainStatus,
|
||||||
AccountInfo, HwAccountInfo,
|
AccountInfo, HwAccountInfo, Header, RichHeader,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Parity implementation for light client.
|
/// Parity implementation for light client.
|
||||||
@ -75,6 +76,16 @@ impl ParityClient {
|
|||||||
dapps_port: dapps_port,
|
dapps_port: dapps_port,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a light blockchain data fetcher.
|
||||||
|
fn fetcher(&self) -> LightFetch {
|
||||||
|
LightFetch {
|
||||||
|
client: self.light_dispatch.client.clone(),
|
||||||
|
on_demand: self.light_dispatch.on_demand.clone(),
|
||||||
|
sync: self.light_dispatch.sync.clone(),
|
||||||
|
cache: self.light_dispatch.cache.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Parity for ParityClient {
|
impl Parity for ParityClient {
|
||||||
@ -342,4 +353,38 @@ impl Parity for ParityClient {
|
|||||||
capability: Capability::Light,
|
capability: Capability::Light,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn block_header(&self, number: Trailing<BlockNumber>) -> BoxFuture<RichHeader, Error> {
|
||||||
|
use ethcore::encoded;
|
||||||
|
|
||||||
|
let engine = self.light_dispatch.client.engine().clone();
|
||||||
|
let from_encoded = move |encoded: encoded::Header| {
|
||||||
|
let header = encoded.decode();
|
||||||
|
let extra_info = engine.extra_info(&header);
|
||||||
|
RichHeader {
|
||||||
|
inner: Header {
|
||||||
|
hash: Some(header.hash().into()),
|
||||||
|
size: Some(encoded.rlp().as_raw().len().into()),
|
||||||
|
parent_hash: header.parent_hash().clone().into(),
|
||||||
|
uncles_hash: header.uncles_hash().clone().into(),
|
||||||
|
author: header.author().clone().into(),
|
||||||
|
miner: header.author().clone().into(),
|
||||||
|
state_root: header.state_root().clone().into(),
|
||||||
|
transactions_root: header.transactions_root().clone().into(),
|
||||||
|
receipts_root: header.receipts_root().clone().into(),
|
||||||
|
number: Some(header.number().into()),
|
||||||
|
gas_used: header.gas_used().clone().into(),
|
||||||
|
gas_limit: header.gas_limit().clone().into(),
|
||||||
|
logs_bloom: header.log_bloom().clone().into(),
|
||||||
|
timestamp: header.timestamp().into(),
|
||||||
|
difficulty: header.difficulty().clone().into(),
|
||||||
|
seal_fields: header.seal().iter().cloned().map(Into::into).collect(),
|
||||||
|
extra_data: Bytes::new(header.extra_data().clone()),
|
||||||
|
},
|
||||||
|
extra_info: extra_info,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
self.fetcher().header(number.0.into()).map(from_encoded).boxed()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,7 @@ use crypto::ecies;
|
|||||||
use ethkey::{Brain, Generator};
|
use ethkey::{Brain, Generator};
|
||||||
use ethstore::random_phrase;
|
use ethstore::random_phrase;
|
||||||
use ethsync::{SyncProvider, ManageNetwork};
|
use ethsync::{SyncProvider, ManageNetwork};
|
||||||
|
use ethcore::ids::BlockId;
|
||||||
use ethcore::miner::MinerService;
|
use ethcore::miner::MinerService;
|
||||||
use ethcore::client::{MiningBlockChainClient};
|
use ethcore::client::{MiningBlockChainClient};
|
||||||
use ethcore::mode::Mode;
|
use ethcore::mode::Mode;
|
||||||
@ -47,7 +48,7 @@ use v1::types::{
|
|||||||
TransactionStats, LocalTransactionStatus,
|
TransactionStats, LocalTransactionStatus,
|
||||||
BlockNumber, ConsensusCapability, VersionInfo,
|
BlockNumber, ConsensusCapability, VersionInfo,
|
||||||
OperationsInfo, DappId, ChainStatus,
|
OperationsInfo, DappId, ChainStatus,
|
||||||
AccountInfo, HwAccountInfo,
|
AccountInfo, HwAccountInfo, Header, RichHeader
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Parity implementation.
|
/// Parity implementation.
|
||||||
@ -393,4 +394,38 @@ impl<C, M, S: ?Sized, U> Parity for ParityClient<C, M, S, U> where
|
|||||||
capability: Capability::Full,
|
capability: Capability::Full,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn block_header(&self, number: Trailing<BlockNumber>) -> BoxFuture<RichHeader, Error> {
|
||||||
|
const EXTRA_INFO_PROOF: &'static str = "Object exists in in blockchain (fetched earlier), extra_info is always available if object exists; qed";
|
||||||
|
|
||||||
|
let client = take_weakf!(self.client);
|
||||||
|
let id: BlockId = number.0.into();
|
||||||
|
let encoded = match client.block_header(id.clone()) {
|
||||||
|
Some(encoded) => encoded,
|
||||||
|
None => return future::err(errors::unknown_block()).boxed(),
|
||||||
|
};
|
||||||
|
|
||||||
|
future::ok(RichHeader {
|
||||||
|
inner: Header {
|
||||||
|
hash: Some(encoded.hash().into()),
|
||||||
|
size: Some(encoded.rlp().as_raw().len().into()),
|
||||||
|
parent_hash: encoded.parent_hash().into(),
|
||||||
|
uncles_hash: encoded.uncles_hash().into(),
|
||||||
|
author: encoded.author().into(),
|
||||||
|
miner: encoded.author().into(),
|
||||||
|
state_root: encoded.state_root().into(),
|
||||||
|
transactions_root: encoded.transactions_root().into(),
|
||||||
|
receipts_root: encoded.receipts_root().into(),
|
||||||
|
number: Some(encoded.number().into()),
|
||||||
|
gas_used: encoded.gas_used().into(),
|
||||||
|
gas_limit: encoded.gas_limit().into(),
|
||||||
|
logs_bloom: encoded.log_bloom().into(),
|
||||||
|
timestamp: encoded.timestamp().into(),
|
||||||
|
difficulty: encoded.difficulty().into(),
|
||||||
|
seal_fields: encoded.seal().into_iter().map(Into::into).collect(),
|
||||||
|
extra_data: Bytes::new(encoded.extra_data()),
|
||||||
|
},
|
||||||
|
extra_info: client.block_extra_info(id).expect(EXTRA_INFO_PROOF),
|
||||||
|
}).boxed()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,7 @@ use v1::types::{
|
|||||||
TransactionStats, LocalTransactionStatus,
|
TransactionStats, LocalTransactionStatus,
|
||||||
BlockNumber, ConsensusCapability, VersionInfo,
|
BlockNumber, ConsensusCapability, VersionInfo,
|
||||||
OperationsInfo, DappId, ChainStatus,
|
OperationsInfo, DappId, ChainStatus,
|
||||||
AccountInfo, HwAccountInfo,
|
AccountInfo, HwAccountInfo, RichHeader,
|
||||||
};
|
};
|
||||||
|
|
||||||
build_rpc_trait! {
|
build_rpc_trait! {
|
||||||
@ -198,5 +198,10 @@ build_rpc_trait! {
|
|||||||
/// Get node kind info.
|
/// Get node kind info.
|
||||||
#[rpc(name = "parity_nodeKind")]
|
#[rpc(name = "parity_nodeKind")]
|
||||||
fn node_kind(&self) -> Result<::v1::types::NodeKind, Error>;
|
fn node_kind(&self) -> Result<::v1::types::NodeKind, Error>;
|
||||||
|
|
||||||
|
/// Get block header.
|
||||||
|
/// Same as `eth_getBlockByNumber` but without uncles and transactions.
|
||||||
|
#[rpc(async, name = "parity_getBlockHeaderByNumber")]
|
||||||
|
fn block_header(&self, Trailing<BlockNumber>) -> BoxFuture<RichHeader, Error>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -96,34 +96,90 @@ pub struct Block {
|
|||||||
pub size: Option<U256>,
|
pub size: Option<U256>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Block representation with additional info
|
/// Block header representation.
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
pub struct Header {
|
||||||
|
/// Hash of the block
|
||||||
|
pub hash: Option<H256>,
|
||||||
|
/// Hash of the parent
|
||||||
|
#[serde(rename="parentHash")]
|
||||||
|
pub parent_hash: H256,
|
||||||
|
/// Hash of the uncles
|
||||||
|
#[serde(rename="sha3Uncles")]
|
||||||
|
pub uncles_hash: H256,
|
||||||
|
/// Authors address
|
||||||
|
pub author: H160,
|
||||||
|
// TODO: get rid of this one
|
||||||
|
/// ?
|
||||||
|
pub miner: H160,
|
||||||
|
/// State root hash
|
||||||
|
#[serde(rename="stateRoot")]
|
||||||
|
pub state_root: H256,
|
||||||
|
/// Transactions root hash
|
||||||
|
#[serde(rename="transactionsRoot")]
|
||||||
|
pub transactions_root: H256,
|
||||||
|
/// Transactions receipts root hash
|
||||||
|
#[serde(rename="receiptsRoot")]
|
||||||
|
pub receipts_root: H256,
|
||||||
|
/// Block number
|
||||||
|
pub number: Option<U256>,
|
||||||
|
/// Gas Used
|
||||||
|
#[serde(rename="gasUsed")]
|
||||||
|
pub gas_used: U256,
|
||||||
|
/// Gas Limit
|
||||||
|
#[serde(rename="gasLimit")]
|
||||||
|
pub gas_limit: U256,
|
||||||
|
/// Extra data
|
||||||
|
#[serde(rename="extraData")]
|
||||||
|
pub extra_data: Bytes,
|
||||||
|
/// Logs bloom
|
||||||
|
#[serde(rename="logsBloom")]
|
||||||
|
pub logs_bloom: H2048,
|
||||||
|
/// Timestamp
|
||||||
|
pub timestamp: U256,
|
||||||
|
/// Difficulty
|
||||||
|
pub difficulty: U256,
|
||||||
|
/// Seal fields
|
||||||
|
#[serde(rename="sealFields")]
|
||||||
|
pub seal_fields: Vec<Bytes>,
|
||||||
|
/// Size in bytes
|
||||||
|
pub size: Option<U256>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Block representation with additional info.
|
||||||
|
pub type RichBlock = Rich<Block>;
|
||||||
|
|
||||||
|
/// Header representation with additional info.
|
||||||
|
pub type RichHeader = Rich<Header>;
|
||||||
|
|
||||||
|
/// Value representation with additional info
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct RichBlock {
|
pub struct Rich<T> {
|
||||||
/// Standard block
|
/// Standard value.
|
||||||
pub block: Block,
|
pub inner: T,
|
||||||
/// Engine-specific fields with additional description.
|
/// Engine-specific fields with additional description.
|
||||||
/// Should be included directly to serialized block object.
|
/// Should be included directly to serialized block object.
|
||||||
// TODO [ToDr] #[serde(skip_serializing)]
|
// TODO [ToDr] #[serde(skip_serializing)]
|
||||||
pub extra_info: BTreeMap<String, String>,
|
pub extra_info: BTreeMap<String, String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Deref for RichBlock {
|
impl<T> Deref for Rich<T> {
|
||||||
type Target = Block;
|
type Target = T;
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
&self.block
|
&self.inner
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Serialize for RichBlock {
|
impl<T: Serialize> Serialize for Rich<T> {
|
||||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer {
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer {
|
||||||
use serde_json::{to_value, Value};
|
use serde_json::{to_value, Value};
|
||||||
|
|
||||||
let serialized = (to_value(&self.block), to_value(&self.extra_info));
|
let serialized = (to_value(&self.inner), to_value(&self.extra_info));
|
||||||
if let (Ok(Value::Object(mut block)), Ok(Value::Object(extras))) = serialized {
|
if let (Ok(Value::Object(mut value)), Ok(Value::Object(extras))) = serialized {
|
||||||
// join two objects
|
// join two objects
|
||||||
block.extend(extras);
|
value.extend(extras);
|
||||||
// and serialize
|
// and serialize
|
||||||
block.serialize(serializer)
|
value.serialize(serializer)
|
||||||
} else {
|
} else {
|
||||||
Err(S::Error::custom("Unserializable structures."))
|
Err(S::Error::custom("Unserializable structures."))
|
||||||
}
|
}
|
||||||
@ -135,7 +191,7 @@ mod tests {
|
|||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use serde_json;
|
use serde_json;
|
||||||
use v1::types::{Transaction, H64, H160, H256, H2048, Bytes, U256};
|
use v1::types::{Transaction, H64, H160, H256, H2048, Bytes, U256};
|
||||||
use super::{Block, RichBlock, BlockTransactions};
|
use super::{Block, RichBlock, BlockTransactions, Header, RichHeader};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_serialize_block_transactions() {
|
fn test_serialize_block_transactions() {
|
||||||
@ -174,7 +230,7 @@ mod tests {
|
|||||||
};
|
};
|
||||||
let serialized_block = serde_json::to_string(&block).unwrap();
|
let serialized_block = serde_json::to_string(&block).unwrap();
|
||||||
let rich_block = RichBlock {
|
let rich_block = RichBlock {
|
||||||
block: block,
|
inner: block,
|
||||||
extra_info: map![
|
extra_info: map![
|
||||||
"mixHash".into() => format!("0x{:?}", H256::default()),
|
"mixHash".into() => format!("0x{:?}", H256::default()),
|
||||||
"nonce".into() => format!("0x{:?}", H64::default())
|
"nonce".into() => format!("0x{:?}", H64::default())
|
||||||
@ -212,7 +268,7 @@ mod tests {
|
|||||||
};
|
};
|
||||||
let serialized_block = serde_json::to_string(&block).unwrap();
|
let serialized_block = serde_json::to_string(&block).unwrap();
|
||||||
let rich_block = RichBlock {
|
let rich_block = RichBlock {
|
||||||
block: block,
|
inner: block,
|
||||||
extra_info: map![
|
extra_info: map![
|
||||||
"mixHash".into() => format!("0x{:?}", H256::default()),
|
"mixHash".into() => format!("0x{:?}", H256::default()),
|
||||||
"nonce".into() => format!("0x{:?}", H64::default())
|
"nonce".into() => format!("0x{:?}", H64::default())
|
||||||
@ -223,4 +279,39 @@ mod tests {
|
|||||||
assert_eq!(serialized_block, r#"{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","sha3Uncles":"0x0000000000000000000000000000000000000000000000000000000000000000","author":"0x0000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","stateRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionsRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","receiptsRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","number":"0x0","gasUsed":"0x0","gasLimit":"0x0","extraData":"0x","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","timestamp":"0x0","difficulty":"0x0","totalDifficulty":"0x0","sealFields":["0x","0x"],"uncles":[],"transactions":[],"size":null}"#);
|
assert_eq!(serialized_block, r#"{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","sha3Uncles":"0x0000000000000000000000000000000000000000000000000000000000000000","author":"0x0000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","stateRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionsRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","receiptsRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","number":"0x0","gasUsed":"0x0","gasLimit":"0x0","extraData":"0x","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","timestamp":"0x0","difficulty":"0x0","totalDifficulty":"0x0","sealFields":["0x","0x"],"uncles":[],"transactions":[],"size":null}"#);
|
||||||
assert_eq!(serialized_rich_block, r#"{"author":"0x0000000000000000000000000000000000000000","difficulty":"0x0","extraData":"0x","gasLimit":"0x0","gasUsed":"0x0","hash":"0x0000000000000000000000000000000000000000000000000000000000000000","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000000","number":"0x0","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","receiptsRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","sealFields":["0x","0x"],"sha3Uncles":"0x0000000000000000000000000000000000000000000000000000000000000000","size":null,"stateRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","timestamp":"0x0","totalDifficulty":"0x0","transactions":[],"transactionsRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","uncles":[]}"#);
|
assert_eq!(serialized_rich_block, r#"{"author":"0x0000000000000000000000000000000000000000","difficulty":"0x0","extraData":"0x","gasLimit":"0x0","gasUsed":"0x0","hash":"0x0000000000000000000000000000000000000000000000000000000000000000","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000000","number":"0x0","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","receiptsRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","sealFields":["0x","0x"],"sha3Uncles":"0x0000000000000000000000000000000000000000000000000000000000000000","size":null,"stateRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","timestamp":"0x0","totalDifficulty":"0x0","transactions":[],"transactionsRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","uncles":[]}"#);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_serialize_header() {
|
||||||
|
let header = Header {
|
||||||
|
hash: Some(H256::default()),
|
||||||
|
parent_hash: H256::default(),
|
||||||
|
uncles_hash: H256::default(),
|
||||||
|
author: H160::default(),
|
||||||
|
miner: H160::default(),
|
||||||
|
state_root: H256::default(),
|
||||||
|
transactions_root: H256::default(),
|
||||||
|
receipts_root: H256::default(),
|
||||||
|
number: Some(U256::default()),
|
||||||
|
gas_used: U256::default(),
|
||||||
|
gas_limit: U256::default(),
|
||||||
|
extra_data: Bytes::default(),
|
||||||
|
logs_bloom: H2048::default(),
|
||||||
|
timestamp: U256::default(),
|
||||||
|
difficulty: U256::default(),
|
||||||
|
seal_fields: vec![Bytes::default(), Bytes::default()],
|
||||||
|
size: Some(69.into()),
|
||||||
|
};
|
||||||
|
let serialized_header = serde_json::to_string(&header).unwrap();
|
||||||
|
let rich_header = RichHeader {
|
||||||
|
inner: header,
|
||||||
|
extra_info: map![
|
||||||
|
"mixHash".into() => format!("0x{:?}", H256::default()),
|
||||||
|
"nonce".into() => format!("0x{:?}", H64::default())
|
||||||
|
],
|
||||||
|
};
|
||||||
|
let serialized_rich_header = serde_json::to_string(&rich_header).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(serialized_header, r#"{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","sha3Uncles":"0x0000000000000000000000000000000000000000000000000000000000000000","author":"0x0000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","stateRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionsRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","receiptsRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","number":"0x0","gasUsed":"0x0","gasLimit":"0x0","extraData":"0x","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","timestamp":"0x0","difficulty":"0x0","sealFields":["0x","0x"],"size":"0x45"}"#);
|
||||||
|
assert_eq!(serialized_rich_header, r#"{"author":"0x0000000000000000000000000000000000000000","difficulty":"0x0","extraData":"0x","gasLimit":"0x0","gasUsed":"0x0","hash":"0x0000000000000000000000000000000000000000000000000000000000000000","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000000","number":"0x0","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","receiptsRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","sealFields":["0x","0x"],"sha3Uncles":"0x0000000000000000000000000000000000000000000000000000000000000000","size":"0x45","stateRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","timestamp":"0x0","transactionsRoot":"0x0000000000000000000000000000000000000000000000000000000000000000"}"#);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -45,7 +45,7 @@ mod work;
|
|||||||
|
|
||||||
pub use self::account_info::{AccountInfo, HwAccountInfo};
|
pub use self::account_info::{AccountInfo, HwAccountInfo};
|
||||||
pub use self::bytes::Bytes;
|
pub use self::bytes::Bytes;
|
||||||
pub use self::block::{RichBlock, Block, BlockTransactions};
|
pub use self::block::{RichBlock, Block, BlockTransactions, Header, RichHeader, Rich};
|
||||||
pub use self::block_number::BlockNumber;
|
pub use self::block_number::BlockNumber;
|
||||||
pub use self::call_request::CallRequest;
|
pub use self::call_request::CallRequest;
|
||||||
pub use self::confirmations::{
|
pub use self::confirmations::{
|
||||||
|
@ -32,7 +32,6 @@ $HOME/.cargo,\
|
|||||||
$HOME/.multirust,\
|
$HOME/.multirust,\
|
||||||
rocksdb,\
|
rocksdb,\
|
||||||
secp256k1,\
|
secp256k1,\
|
||||||
src/tests,\
|
|
||||||
util/json-tests,\
|
util/json-tests,\
|
||||||
util/src/network/tests,\
|
util/src/network/tests,\
|
||||||
ethcore/src/evm/tests,\
|
ethcore/src/evm/tests,\
|
||||||
|
@ -24,12 +24,15 @@ tokio-core = "0.1"
|
|||||||
tokio-service = "0.1"
|
tokio-service = "0.1"
|
||||||
tokio-proto = "0.1"
|
tokio-proto = "0.1"
|
||||||
url = "1.0"
|
url = "1.0"
|
||||||
|
ethabi = "1.0.0"
|
||||||
|
ethcore = { path = "../ethcore" }
|
||||||
ethcore-devtools = { path = "../devtools" }
|
ethcore-devtools = { path = "../devtools" }
|
||||||
ethcore-util = { path = "../util" }
|
ethcore-util = { path = "../util" }
|
||||||
ethcore-ipc = { path = "../ipc/rpc" }
|
ethcore-ipc = { path = "../ipc/rpc" }
|
||||||
ethcore-ipc-nano = { path = "../ipc/nano" }
|
ethcore-ipc-nano = { path = "../ipc/nano" }
|
||||||
ethcrypto = { path = "../ethcrypto" }
|
ethcrypto = { path = "../ethcrypto" }
|
||||||
ethkey = { path = "../ethkey" }
|
ethkey = { path = "../ethkey" }
|
||||||
|
native-contracts = { path = "../ethcore/native_contracts" }
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
debug = true
|
debug = true
|
||||||
|
@ -14,38 +14,92 @@
|
|||||||
// 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::collections::{HashMap, HashSet};
|
use std::sync::Arc;
|
||||||
use parking_lot::RwLock;
|
use futures::{future, Future};
|
||||||
|
use parking_lot::Mutex;
|
||||||
|
use ethkey::public_to_address;
|
||||||
|
use ethcore::client::{Client, BlockChainClient, BlockId};
|
||||||
|
use native_contracts::SecretStoreAclStorage;
|
||||||
use types::all::{Error, DocumentAddress, Public};
|
use types::all::{Error, DocumentAddress, Public};
|
||||||
|
|
||||||
|
const ACL_CHECKER_CONTRACT_REGISTRY_NAME: &'static str = "secretstore_acl_checker";
|
||||||
|
|
||||||
/// ACL storage of Secret Store
|
/// ACL storage of Secret Store
|
||||||
pub trait AclStorage: Send + Sync {
|
pub trait AclStorage: Send + Sync {
|
||||||
/// Check if requestor with `public` key can access document with hash `document`
|
/// Check if requestor with `public` key can access document with hash `document`
|
||||||
fn check(&self, public: &Public, document: &DocumentAddress) -> Result<bool, Error>;
|
fn check(&self, public: &Public, document: &DocumentAddress) -> Result<bool, Error>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Dummy ACL storage implementation
|
/// On-chain ACL storage implementation.
|
||||||
#[derive(Default, Debug)]
|
pub struct OnChainAclStorage {
|
||||||
pub struct DummyAclStorage {
|
/// Blockchain client.
|
||||||
prohibited: RwLock<HashMap<Public, HashSet<DocumentAddress>>>,
|
client: Arc<Client>,
|
||||||
|
/// On-chain contract.
|
||||||
|
contract: Mutex<Option<SecretStoreAclStorage>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DummyAclStorage {
|
impl OnChainAclStorage {
|
||||||
#[cfg(test)]
|
pub fn new(client: Arc<Client>) -> Self {
|
||||||
/// Prohibit given requestor access to given document
|
OnChainAclStorage {
|
||||||
pub fn prohibit(&self, public: Public, document: DocumentAddress) {
|
client: client,
|
||||||
self.prohibited.write()
|
contract: Mutex::new(None),
|
||||||
.entry(public)
|
}
|
||||||
.or_insert_with(Default::default)
|
|
||||||
.insert(document);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AclStorage for DummyAclStorage {
|
impl AclStorage for OnChainAclStorage {
|
||||||
fn check(&self, public: &Public, document: &DocumentAddress) -> Result<bool, Error> {
|
fn check(&self, public: &Public, document: &DocumentAddress) -> Result<bool, Error> {
|
||||||
Ok(self.prohibited.read()
|
let mut contract = self.contract.lock();
|
||||||
.get(public)
|
if !contract.is_some() {
|
||||||
.map(|docs| !docs.contains(document))
|
*contract = self.client.registry_address(ACL_CHECKER_CONTRACT_REGISTRY_NAME.to_owned())
|
||||||
.unwrap_or(true))
|
.and_then(|contract_addr| {
|
||||||
|
trace!(target: "secretstore", "Configuring for ACL checker contract from {}", contract_addr);
|
||||||
|
|
||||||
|
Some(SecretStoreAclStorage::new(contract_addr))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if let Some(ref contract) = *contract {
|
||||||
|
let address = public_to_address(&public);
|
||||||
|
let do_call = |a, d| future::done(self.client.call_contract(BlockId::Latest, a, d));
|
||||||
|
contract.check_permissions(do_call, address, document.clone())
|
||||||
|
.map_err(|err| Error::Internal(err))
|
||||||
|
.wait()
|
||||||
|
} else {
|
||||||
|
Err(Error::Internal("ACL checker contract is not configured".to_owned()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
pub mod tests {
|
||||||
|
use std::collections::{HashMap, HashSet};
|
||||||
|
use parking_lot::RwLock;
|
||||||
|
use types::all::{Error, DocumentAddress, Public};
|
||||||
|
use super::AclStorage;
|
||||||
|
|
||||||
|
#[derive(Default, Debug)]
|
||||||
|
/// Dummy ACL storage implementation
|
||||||
|
pub struct DummyAclStorage {
|
||||||
|
prohibited: RwLock<HashMap<Public, HashSet<DocumentAddress>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DummyAclStorage {
|
||||||
|
#[cfg(test)]
|
||||||
|
/// Prohibit given requestor access to given document
|
||||||
|
pub fn prohibit(&self, public: Public, document: DocumentAddress) {
|
||||||
|
self.prohibited.write()
|
||||||
|
.entry(public)
|
||||||
|
.or_insert_with(Default::default)
|
||||||
|
.insert(document);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AclStorage for DummyAclStorage {
|
||||||
|
fn check(&self, public: &Public, document: &DocumentAddress) -> Result<bool, Error> {
|
||||||
|
Ok(self.prohibited.read()
|
||||||
|
.get(public)
|
||||||
|
.map(|docs| !docs.contains(document))
|
||||||
|
.unwrap_or(true))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -147,7 +147,7 @@ mod tests {
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use ethcrypto;
|
use ethcrypto;
|
||||||
use ethkey::{self, Random, Generator};
|
use ethkey::{self, Random, Generator};
|
||||||
use acl_storage::DummyAclStorage;
|
use acl_storage::tests::DummyAclStorage;
|
||||||
use key_storage::tests::DummyKeyStorage;
|
use key_storage::tests::DummyKeyStorage;
|
||||||
use types::all::{ClusterConfiguration, NodeAddress, EncryptionConfiguration, DocumentEncryptedKey, DocumentKey};
|
use types::all::{ClusterConfiguration, NodeAddress, EncryptionConfiguration, DocumentEncryptedKey, DocumentKey};
|
||||||
use super::super::{RequestSignature, DocumentAddress};
|
use super::super::{RequestSignature, DocumentAddress};
|
||||||
|
@ -220,7 +220,7 @@ impl SessionImpl {
|
|||||||
self.completed.notify_all();
|
self.completed.notify_all();
|
||||||
},
|
},
|
||||||
// we can not decrypt data
|
// we can not decrypt data
|
||||||
SessionState::Failed => (),
|
SessionState::Failed => self.completed.notify_all(),
|
||||||
// cannot reach other states
|
// cannot reach other states
|
||||||
_ => unreachable!("process_initialization_response can change state to WaitingForPartialDecryption or Failed; checked that we are in WaitingForInitializationConfirm state above; qed"),
|
_ => unreachable!("process_initialization_response can change state to WaitingForPartialDecryption or Failed; checked that we are in WaitingForInitializationConfirm state above; qed"),
|
||||||
}
|
}
|
||||||
@ -285,7 +285,10 @@ impl SessionImpl {
|
|||||||
SessionState::WaitingForPartialDecryption =>
|
SessionState::WaitingForPartialDecryption =>
|
||||||
SessionImpl::start_waiting_for_partial_decryption(self.node().clone(), self.id.clone(), self.access_key.clone(), &self.cluster, &self.encrypted_data, &mut *data),
|
SessionImpl::start_waiting_for_partial_decryption(self.node().clone(), self.id.clone(), self.access_key.clone(), &self.cluster, &self.encrypted_data, &mut *data),
|
||||||
// we can not have enough nodes for decryption
|
// we can not have enough nodes for decryption
|
||||||
SessionState::Failed => Ok(()),
|
SessionState::Failed => {
|
||||||
|
self.completed.notify_all();
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
// cannot reach other states
|
// cannot reach other states
|
||||||
_ => unreachable!("process_initialization_response can change state to WaitingForPartialDecryption or Failed; checked that we are in WaitingForInitializationConfirm state above; qed"),
|
_ => unreachable!("process_initialization_response can change state to WaitingForPartialDecryption or Failed; checked that we are in WaitingForInitializationConfirm state above; qed"),
|
||||||
}
|
}
|
||||||
@ -480,6 +483,7 @@ fn process_initialization_response(encrypted_data: &DocumentKeyShare, data: &mut
|
|||||||
|
|
||||||
// check if we still can receive enough confirmations to do a decryption?
|
// check if we still can receive enough confirmations to do a decryption?
|
||||||
if encrypted_data.id_numbers.len() - data.rejected_nodes.len() < encrypted_data.threshold + 1 {
|
if encrypted_data.id_numbers.len() - data.rejected_nodes.len() < encrypted_data.threshold + 1 {
|
||||||
|
data.decrypted_secret = Some(Err(Error::AccessDenied));
|
||||||
data.state = SessionState::Failed;
|
data.state = SessionState::Failed;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -503,7 +507,7 @@ fn do_partial_decryption(node: &NodeId, _requestor_public: &Public, participants
|
|||||||
mod tests {
|
mod tests {
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use super::super::super::acl_storage::DummyAclStorage;
|
use super::super::super::acl_storage::tests::DummyAclStorage;
|
||||||
use ethkey::{self, Random, Generator, Public, Secret};
|
use ethkey::{self, Random, Generator, Public, Secret};
|
||||||
use key_server_cluster::{NodeId, DocumentKeyShare, SessionId, Error};
|
use key_server_cluster::{NodeId, DocumentKeyShare, SessionId, Error};
|
||||||
use key_server_cluster::cluster::tests::DummyCluster;
|
use key_server_cluster::cluster::tests::DummyCluster;
|
||||||
|
@ -21,7 +21,7 @@ use ethcrypto;
|
|||||||
use super::types::all::DocumentAddress;
|
use super::types::all::DocumentAddress;
|
||||||
|
|
||||||
pub use super::types::all::{NodeId, EncryptionConfiguration};
|
pub use super::types::all::{NodeId, EncryptionConfiguration};
|
||||||
pub use super::acl_storage::{AclStorage, DummyAclStorage};
|
pub use super::acl_storage::AclStorage;
|
||||||
pub use super::key_storage::{KeyStorage, DocumentKeyShare};
|
pub use super::key_storage::{KeyStorage, DocumentKeyShare};
|
||||||
pub use super::serialization::{SerializableSignature, SerializableH256, SerializableSecret, SerializablePublic};
|
pub use super::serialization::{SerializableSignature, SerializableH256, SerializableSecret, SerializablePublic};
|
||||||
pub use self::cluster::{ClusterCore, ClusterConfiguration, ClusterClient};
|
pub use self::cluster::{ClusterCore, ClusterConfiguration, ClusterClient};
|
||||||
@ -30,6 +30,8 @@ pub use self::decryption_session::Session as DecryptionSession;
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub use super::key_storage::tests::DummyKeyStorage;
|
pub use super::key_storage::tests::DummyKeyStorage;
|
||||||
|
#[cfg(test)]
|
||||||
|
pub use super::acl_storage::tests::DummyAclStorage;
|
||||||
|
|
||||||
pub type SessionId = DocumentAddress;
|
pub type SessionId = DocumentAddress;
|
||||||
|
|
||||||
@ -72,6 +74,8 @@ pub enum Error {
|
|||||||
Serde(String),
|
Serde(String),
|
||||||
/// Key storage error.
|
/// Key storage error.
|
||||||
KeyStorage(String),
|
KeyStorage(String),
|
||||||
|
/// Acl storage error.
|
||||||
|
AccessDenied,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ethkey::Error> for Error {
|
impl From<ethkey::Error> for Error {
|
||||||
@ -110,6 +114,7 @@ impl fmt::Display for Error {
|
|||||||
Error::Io(ref e) => write!(f, "i/o error {}", e),
|
Error::Io(ref e) => write!(f, "i/o error {}", e),
|
||||||
Error::Serde(ref e) => write!(f, "serde error {}", e),
|
Error::Serde(ref e) => write!(f, "serde error {}", e),
|
||||||
Error::KeyStorage(ref e) => write!(f, "key storage error {}", e),
|
Error::KeyStorage(ref e) => write!(f, "key storage error {}", e),
|
||||||
|
Error::AccessDenied => write!(f, "Access denied"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,11 +32,14 @@ extern crate tokio_service;
|
|||||||
extern crate tokio_proto;
|
extern crate tokio_proto;
|
||||||
extern crate url;
|
extern crate url;
|
||||||
|
|
||||||
|
extern crate ethabi;
|
||||||
|
extern crate ethcore;
|
||||||
extern crate ethcore_devtools as devtools;
|
extern crate ethcore_devtools as devtools;
|
||||||
extern crate ethcore_util as util;
|
extern crate ethcore_util as util;
|
||||||
extern crate ethcore_ipc as ipc;
|
extern crate ethcore_ipc as ipc;
|
||||||
extern crate ethcrypto;
|
extern crate ethcrypto;
|
||||||
extern crate ethkey;
|
extern crate ethkey;
|
||||||
|
extern crate native_contracts;
|
||||||
|
|
||||||
mod key_server_cluster;
|
mod key_server_cluster;
|
||||||
mod types;
|
mod types;
|
||||||
@ -52,15 +55,18 @@ mod key_server;
|
|||||||
mod key_storage;
|
mod key_storage;
|
||||||
mod serialization;
|
mod serialization;
|
||||||
|
|
||||||
|
use std::sync::Arc;
|
||||||
|
use ethcore::client::Client;
|
||||||
|
|
||||||
pub use types::all::{DocumentAddress, DocumentKey, DocumentEncryptedKey, RequestSignature, Public,
|
pub use types::all::{DocumentAddress, DocumentKey, DocumentEncryptedKey, RequestSignature, Public,
|
||||||
Error, NodeAddress, ServiceConfiguration, ClusterConfiguration, EncryptionConfiguration};
|
Error, NodeAddress, ServiceConfiguration, ClusterConfiguration, EncryptionConfiguration};
|
||||||
pub use traits::{KeyServer};
|
pub use traits::{KeyServer};
|
||||||
|
|
||||||
/// Start new key server instance
|
/// Start new key server instance
|
||||||
pub fn start(config: ServiceConfiguration) -> Result<Box<KeyServer>, Error> {
|
pub fn start(client: Arc<Client>, config: ServiceConfiguration) -> Result<Box<KeyServer>, Error> {
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
let acl_storage = Arc::new(acl_storage::DummyAclStorage::default());
|
let acl_storage = Arc::new(acl_storage::OnChainAclStorage::new(client));
|
||||||
let key_storage = Arc::new(key_storage::PersistentKeyStorage::new(&config)?);
|
let key_storage = Arc::new(key_storage::PersistentKeyStorage::new(&config)?);
|
||||||
let key_server = key_server::KeyServerImpl::new(&config.cluster_config, acl_storage, key_storage)?;
|
let key_server = key_server::KeyServerImpl::new(&config.cluster_config, acl_storage, key_storage)?;
|
||||||
let listener = http_listener::KeyServerHttpListener::start(config, key_server)?;
|
let listener = http_listener::KeyServerHttpListener::start(config, key_server)?;
|
||||||
|
@ -119,7 +119,10 @@ impl From<ethkey::Error> for Error {
|
|||||||
|
|
||||||
impl From<key_server_cluster::Error> for Error {
|
impl From<key_server_cluster::Error> for Error {
|
||||||
fn from(err: key_server_cluster::Error) -> Self {
|
fn from(err: key_server_cluster::Error) -> Self {
|
||||||
Error::Internal(err.into())
|
match err {
|
||||||
|
key_server_cluster::Error::AccessDenied => Error::AccessDenied,
|
||||||
|
_ => Error::Internal(err.into()),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user