From 6aa9005785a1c112247f626b5228d942ae26ee32 Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Mon, 3 Apr 2017 11:37:07 +0200 Subject: [PATCH 01/13] parity_getBlockHeaderByNumber and LightFetch --- ethcore/light/src/client/mod.rs | 15 +++ rpc/src/v1/helpers/errors.rs | 5 + rpc/src/v1/helpers/light_fetch.rs | 200 ++++++++++++++++++++++++++++++ rpc/src/v1/helpers/mod.rs | 1 + rpc/src/v1/impls/eth.rs | 4 +- rpc/src/v1/impls/light/eth.rs | 179 ++++---------------------- rpc/src/v1/impls/light/parity.rs | 47 ++++++- rpc/src/v1/impls/parity.rs | 37 +++++- rpc/src/v1/traits/parity.rs | 7 +- rpc/src/v1/types/block.rs | 121 +++++++++++++++--- rpc/src/v1/types/mod.rs | 2 +- 11 files changed, 439 insertions(+), 179 deletions(-) create mode 100644 rpc/src/v1/helpers/light_fetch.rs diff --git a/ethcore/light/src/client/mod.rs b/ethcore/light/src/client/mod.rs index c791caed1..f340b5bcf 100644 --- a/ethcore/light/src/client/mod.rs +++ b/ethcore/light/src/client/mod.rs @@ -68,6 +68,13 @@ pub trait LightChainClient: Send + Sync { /// Get the signing network ID. fn signing_network_id(&self) -> Option; + /// 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; + + /// Get a handle to the consensus engine. + fn engine(&self) -> &Arc; + /// Query whether a block is known. fn is_known(&self, hash: &H256) -> bool; @@ -295,6 +302,14 @@ impl LightChainClient for Client { Client::signing_network_id(self) } + fn env_info(&self, id: BlockId) -> Option { + Client::env_info(self, id) + } + + fn engine(&self) -> &Arc { + Client::engine(self) + } + fn is_known(&self, hash: &H256) -> bool { self.status(hash) == BlockStatus::InChain } diff --git a/rpc/src/v1/helpers/errors.rs b/rpc/src/v1/helpers/errors.rs index 7a7e940b8..aae6a97f3 100644 --- a/rpc/src/v1/helpers/errors.rs +++ b/rpc/src/v1/helpers/errors.rs @@ -346,3 +346,8 @@ pub fn deprecated>>(message: T) -> Error { 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", "") +} diff --git a/rpc/src/v1/helpers/light_fetch.rs b/rpc/src/v1/helpers/light_fetch.rs new file mode 100644 index 000000000..e2ce6880f --- /dev/null +++ b/rpc/src/v1/helpers/light_fetch.rs @@ -0,0 +1,200 @@ +// 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 . + +//! 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, + /// The on-demand request service. + pub on_demand: Arc, + /// Handle to the network. + pub sync: Arc, + /// The light data cache. + pub cache: Arc>, +} + +/// Type alias for convenience. +pub type ExecutionResult = Result; + +impl LightFetch { + /// Get a block header from the on demand service or client, or error. + pub fn header(&self, id: BlockId) -> BoxFuture, Error> { + if let Some(h) = self.client.block_header(id) { + return future::ok(Some(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::ok(None).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(), + } + }).map(Some).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(Some(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() + } + } + + // Get account info at a given block. `None` signifies no such account existing. + pub fn account(&self, address: Address, id: BlockId) -> BoxFuture, Error> { + let (sync, on_demand) = (self.sync.clone(), self.on_demand.clone()); + + self.header(id).and_then(move |header| { + let header = match header { + None => return future::ok(None).boxed(), + Some(hdr) => hdr, + }; + + sync.with_context(|ctx| on_demand.account(ctx, request::Account { + header: header, + address: address, + })) + .map(|x| x.map_err(errors::on_demand_cancel).boxed()) + .unwrap_or_else(|| future::err(errors::network_disabled()).boxed()) + }).boxed() + } + + /// helper for getting proved execution. + pub fn proved_execution(&self, req: CallRequest, num: Trailing) -> BoxFuture { + 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, hdr) = match (client.env_info(id), hdr) { + (Some(env_info), Some(hdr)) => (env_info, hdr), + _ => 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() + } +} diff --git a/rpc/src/v1/helpers/mod.rs b/rpc/src/v1/helpers/mod.rs index 9d2064aa8..839d6b090 100644 --- a/rpc/src/v1/helpers/mod.rs +++ b/rpc/src/v1/helpers/mod.rs @@ -21,6 +21,7 @@ pub mod accounts; pub mod block_import; pub mod dispatch; pub mod fake_sign; +pub mod light_fetch; pub mod informant; pub mod oneshot; diff --git a/rpc/src/v1/impls/eth.rs b/rpc/src/v1/impls/eth.rs index 73d4d9f9b..d4265233a 100644 --- a/rpc/src/v1/impls/eth.rs +++ b/rpc/src/v1/impls/eth.rs @@ -146,7 +146,7 @@ impl EthClient where (Some(block), Some(total_difficulty)) => { let view = block.header_view(); Ok(Some(RichBlock { - block: Block { + inner: Block { hash: Some(view.sha3().into()), size: Some(block.rlp().as_raw().len().into()), parent_hash: view.parent_hash().into(), @@ -202,7 +202,7 @@ impl EthClient where .map(Into::into); let block = RichBlock { - block: Block { + inner: Block { hash: Some(uncle.hash().into()), size: size, parent_hash: uncle.parent_hash().clone().into(), diff --git a/rpc/src/v1/impls/light/eth.rs b/rpc/src/v1/impls/light/eth.rs index 251daf90d..6f283af02 100644 --- a/rpc/src/v1/impls/light/eth.rs +++ b/rpc/src/v1/impls/light/eth.rs @@ -45,6 +45,7 @@ use futures::sync::oneshot; use v1::helpers::{CallRequest as CRequest, errors, limit_logs, dispatch}; use v1::helpers::block_import::is_major_importing; +use v1::helpers::light_fetch::LightFetch; use v1::traits::Eth; use v1::types::{ RichBlock, Block, BlockTransactions, BlockNumber, Bytes, SyncStatus, SyncInfo, @@ -65,12 +66,6 @@ pub struct EthClient { cache: Arc>, } -// 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; impl EthClient { /// Create a new `EthClient` with a handle to the light sync instance, client, @@ -93,147 +88,15 @@ impl EthClient { } } - /// Get a block header from the on demand service or client, or error. - fn header(&self, id: BlockId) -> BoxFuture, Error> { - if let Some(h) = self.client.block_header(id) { - return future::ok(Some(h)).boxed() + /// Create a light data fetcher instance. + fn fetcher(&self) -> LightFetch { + LightFetch { + 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::ok(None).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(), - } - }).map(Some).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(Some(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. - fn account(&self, address: Address, id: BlockId) -> BoxFuture, Error> { - let (sync, on_demand) = (self.sync.clone(), self.on_demand.clone()); - - self.header(id).and_then(move |header| { - let header = match header { - None => return future::ok(None).boxed(), - Some(hdr) => hdr, - }; - - sync.with_context(|ctx| on_demand.account(ctx, request::Account { - header: header, - address: address, - })) - .map(|x| x.map_err(err_premature_cancel).boxed()) - .unwrap_or_else(|| future::err(errors::network_disabled()).boxed()) - }).boxed() - } - - // helper for getting proved execution. - fn proved_execution(&self, req: CallRequest, num: Trailing) -> BoxFuture { - 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, hdr) = match (client.env_info(id), hdr) { - (Some(env_info), Some(hdr)) => (env_info, hdr), - _ => 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() } } @@ -281,7 +144,7 @@ impl Eth for EthClient { } fn balance(&self, address: RpcH160, num: Trailing) -> BoxFuture { - 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() } @@ -298,14 +161,14 @@ impl Eth for EthClient { } fn transaction_count(&self, address: RpcH160, num: Trailing) -> BoxFuture { - 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() } fn block_transaction_count_by_hash(&self, hash: RpcH256) -> BoxFuture, Error> { 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| { let hdr = match hdr { None => return future::ok(None).boxed(), Some(hdr) => hdr, @@ -316,7 +179,7 @@ impl Eth for EthClient { } else { 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_err(err_premature_cancel).boxed()) + .map(|x| x.map_err(errors::on_demand_cancel).boxed()) .unwrap_or_else(|| future::err(errors::network_disabled()).boxed()) } }).boxed() @@ -325,7 +188,7 @@ impl Eth for EthClient { fn block_transaction_count_by_number(&self, num: BlockNumber) -> BoxFuture, Error> { 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| { let hdr = match hdr { None => return future::ok(None).boxed(), Some(hdr) => hdr, @@ -336,7 +199,7 @@ impl Eth for EthClient { } else { 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_err(err_premature_cancel).boxed()) + .map(|x| x.map_err(errors::on_demand_cancel).boxed()) .unwrap_or_else(|| future::err(errors::network_disabled()).boxed()) } }).boxed() @@ -345,7 +208,7 @@ impl Eth for EthClient { fn block_uncles_count_by_hash(&self, hash: RpcH256) -> BoxFuture, Error> { 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| { let hdr = match hdr { None => return future::ok(None).boxed(), Some(hdr) => hdr, @@ -356,7 +219,7 @@ impl Eth for EthClient { } else { 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_err(err_premature_cancel).boxed()) + .map(|x| x.map_err(errors::on_demand_cancel).boxed()) .unwrap_or_else(|| future::err(errors::network_disabled()).boxed()) } }).boxed() @@ -365,7 +228,7 @@ impl Eth for EthClient { fn block_uncles_count_by_number(&self, num: BlockNumber) -> BoxFuture, Error> { 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| { let hdr = match hdr { None => return future::ok(None).boxed(), Some(hdr) => hdr, @@ -376,7 +239,7 @@ impl Eth for EthClient { } else { 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_err(err_premature_cancel).boxed()) + .map(|x| x.map_err(errors::on_demand_cancel).boxed()) .unwrap_or_else(|| future::err(errors::network_disabled()).boxed()) } }).boxed() @@ -411,7 +274,7 @@ impl Eth for EthClient { } fn call(&self, req: CallRequest, num: Trailing) -> BoxFuture { - self.proved_execution(req, num).and_then(|res| { + self.fetcher().proved_execution(req, num).and_then(|res| { match res { Ok(exec) => Ok(exec.output.into()), Err(e) => Err(errors::execution(e)), @@ -421,7 +284,7 @@ impl Eth for EthClient { fn estimate_gas(&self, req: CallRequest, num: Trailing) -> BoxFuture { // 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 { Ok(exec) => Ok((exec.refunded + exec.gas_used).into()), Err(e) => Err(errors::execution(e)), diff --git a/rpc/src/v1/impls/light/parity.rs b/rpc/src/v1/impls/light/parity.rs index 569398193..65cc90966 100644 --- a/rpc/src/v1/impls/light/parity.rs +++ b/rpc/src/v1/impls/light/parity.rs @@ -32,6 +32,7 @@ use jsonrpc_core::Error; use jsonrpc_macros::Trailing; use v1::helpers::{errors, SigningQueue, SignerService, NetworkSettings}; use v1::helpers::dispatch::{LightDispatcher, DEFAULT_MAC}; +use v1::helpers::light_fetch::LightFetch; use v1::metadata::Metadata; use v1::traits::Parity; use v1::types::{ @@ -40,7 +41,7 @@ use v1::types::{ TransactionStats, LocalTransactionStatus, BlockNumber, ConsensusCapability, VersionInfo, OperationsInfo, DappId, ChainStatus, - AccountInfo, HwAccountInfo, + AccountInfo, HwAccountInfo, Header, RichHeader, }; /// Parity implementation for light client. @@ -75,6 +76,16 @@ impl ParityClient { 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 { @@ -342,4 +353,38 @@ impl Parity for ParityClient { capability: Capability::Light, }) } + + fn block_header(&self, number: Trailing) -> BoxFuture, 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(move |encoded| encoded.map(from_encoded)).boxed() + } } diff --git a/rpc/src/v1/impls/parity.rs b/rpc/src/v1/impls/parity.rs index 83e0a505d..d2934313e 100644 --- a/rpc/src/v1/impls/parity.rs +++ b/rpc/src/v1/impls/parity.rs @@ -28,6 +28,7 @@ use crypto::ecies; use ethkey::{Brain, Generator}; use ethstore::random_phrase; use ethsync::{SyncProvider, ManageNetwork}; +use ethcore::ids::BlockId; use ethcore::miner::MinerService; use ethcore::client::{MiningBlockChainClient}; use ethcore::mode::Mode; @@ -47,7 +48,7 @@ use v1::types::{ TransactionStats, LocalTransactionStatus, BlockNumber, ConsensusCapability, VersionInfo, OperationsInfo, DappId, ChainStatus, - AccountInfo, HwAccountInfo, + AccountInfo, HwAccountInfo, Header, RichHeader }; /// Parity implementation. @@ -393,4 +394,38 @@ impl Parity for ParityClient where capability: Capability::Full, }) } + + fn block_header(&self, number: Trailing) -> BoxFuture, 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::ok(None).boxed(), + }; + + future::ok(Some(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() + } } diff --git a/rpc/src/v1/traits/parity.rs b/rpc/src/v1/traits/parity.rs index 8a15addae..b374ebd53 100644 --- a/rpc/src/v1/traits/parity.rs +++ b/rpc/src/v1/traits/parity.rs @@ -28,7 +28,7 @@ use v1::types::{ TransactionStats, LocalTransactionStatus, BlockNumber, ConsensusCapability, VersionInfo, OperationsInfo, DappId, ChainStatus, - AccountInfo, HwAccountInfo, + AccountInfo, HwAccountInfo, RichHeader, }; build_rpc_trait! { @@ -198,5 +198,10 @@ build_rpc_trait! { /// Get node kind info. #[rpc(name = "parity_nodeKind")] 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) -> BoxFuture, Error>; } } diff --git a/rpc/src/v1/types/block.rs b/rpc/src/v1/types/block.rs index e63137520..4077d7221 100644 --- a/rpc/src/v1/types/block.rs +++ b/rpc/src/v1/types/block.rs @@ -96,34 +96,90 @@ pub struct Block { pub size: Option, } -/// Block representation with additional info +/// Block header representation. +#[derive(Debug, Serialize)] +pub struct Header { + /// Hash of the block + pub hash: Option, + /// 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, + /// 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, + /// Size in bytes + pub size: Option, +} + +/// Block representation with additional info. +pub type RichBlock = Rich; + +/// Header representation with additional info. +pub type RichHeader = Rich
; + +/// Value representation with additional info #[derive(Debug)] -pub struct RichBlock { - /// Standard block - pub block: Block, +pub struct Rich { + /// Standard value. + pub inner: T, /// Engine-specific fields with additional description. /// Should be included directly to serialized block object. // TODO [ToDr] #[serde(skip_serializing)] pub extra_info: BTreeMap, } -impl Deref for RichBlock { - type Target = Block; +impl Deref for Rich { + type Target = T; fn deref(&self) -> &Self::Target { - &self.block + &self.inner } } -impl Serialize for RichBlock { +impl Serialize for Rich { fn serialize(&self, serializer: S) -> Result where S: Serializer { use serde_json::{to_value, Value}; - let serialized = (to_value(&self.block), to_value(&self.extra_info)); - if let (Ok(Value::Object(mut block)), Ok(Value::Object(extras))) = serialized { + let serialized = (to_value(&self.inner), to_value(&self.extra_info)); + if let (Ok(Value::Object(mut value)), Ok(Value::Object(extras))) = serialized { // join two objects - block.extend(extras); + value.extend(extras); // and serialize - block.serialize(serializer) + value.serialize(serializer) } else { Err(S::Error::custom("Unserializable structures.")) } @@ -135,7 +191,7 @@ mod tests { use std::collections::BTreeMap; use serde_json; use v1::types::{Transaction, H64, H160, H256, H2048, Bytes, U256}; - use super::{Block, RichBlock, BlockTransactions}; + use super::{Block, RichBlock, BlockTransactions, Header, RichHeader}; #[test] fn test_serialize_block_transactions() { @@ -174,7 +230,7 @@ mod tests { }; let serialized_block = serde_json::to_string(&block).unwrap(); let rich_block = RichBlock { - block: block, + inner: block, extra_info: map![ "mixHash".into() => format!("0x{:?}", H256::default()), "nonce".into() => format!("0x{:?}", H64::default()) @@ -212,7 +268,7 @@ mod tests { }; let serialized_block = serde_json::to_string(&block).unwrap(); let rich_block = RichBlock { - block: block, + inner: block, extra_info: map![ "mixHash".into() => format!("0x{:?}", H256::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_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"}"#); + } } diff --git a/rpc/src/v1/types/mod.rs b/rpc/src/v1/types/mod.rs index 0ec60a74f..d76c92deb 100644 --- a/rpc/src/v1/types/mod.rs +++ b/rpc/src/v1/types/mod.rs @@ -45,7 +45,7 @@ mod work; pub use self::account_info::{AccountInfo, HwAccountInfo}; 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::call_request::CallRequest; pub use self::confirmations::{ From 2447875b269ac47ffe14b653d4a42b6da0fc89e5 Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Mon, 3 Apr 2017 12:16:41 +0200 Subject: [PATCH 02/13] Update npm build for new inclusions (#5381) * Update npm build for new inclusions * Add dummy interface for secp256k1 --- js/src/api/local/ethkey/dummy.js | 19 +++++++++++++++++++ js/webpack/npm.js | 13 +++++++++---- 2 files changed, 28 insertions(+), 4 deletions(-) create mode 100644 js/src/api/local/ethkey/dummy.js diff --git a/js/src/api/local/ethkey/dummy.js b/js/src/api/local/ethkey/dummy.js new file mode 100644 index 000000000..38f7c84de --- /dev/null +++ b/js/src/api/local/ethkey/dummy.js @@ -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 . + +export default function () { + // empty file included while building parity.js (don't include local keygen) +} diff --git a/js/webpack/npm.js b/js/webpack/npm.js index b1f41d805..b526b2f0f 100644 --- a/js/webpack/npm.js +++ b/js/webpack/npm.js @@ -24,9 +24,11 @@ const ENV = process.env.NODE_ENV || 'development'; const isProd = ENV === 'production'; const LIBRARY = process.env.LIBRARY; + if (!LIBRARY) { process.exit(-1); } + const SRC = LIBRARY.toLowerCase(); const OUTPUT_PATH = path.join(__dirname, '../.npmjs', SRC); @@ -63,12 +65,18 @@ module.exports = { 'babel-loader?cacheDirectory=true' ], exclude: /node_modules/ + }, + { + test: /\.js$/, + include: /node_modules\/(ethereumjs-tx|@parity\/wordlist)/, + use: 'babel-loader' } ] }, resolve: { alias: { + 'secp256k1/js': path.resolve(__dirname, '../src/api/local/ethkey/dummy.js'), '~': path.resolve(__dirname, '../src') }, modules: [ @@ -85,15 +93,12 @@ module.exports = { to: 'package.json', transform: function (content, path) { const json = JSON.parse(content.toString()); - json.version = packageJson.version; - // Add tests dependencies to Dev Deps json.devDependencies.chai = packageJson.devDependencies.chai; json.devDependencies.mocha = packageJson.devDependencies.mocha; json.devDependencies.nock = packageJson.devDependencies.nock; - - // Add test script json.scripts.test = 'mocha \'test/*.spec.js\''; + json.version = packageJson.version; return new Buffer(JSON.stringify(json, null, ' '), 'utf-8'); } From 0a90f235de18d381a938a91a6cc76fab25bd88e5 Mon Sep 17 00:00:00 2001 From: GitLab Build Bot Date: Mon, 3 Apr 2017 10:39:53 +0000 Subject: [PATCH 03/13] [ci skip] js-precompiled 20170403-103604 --- Cargo.lock | 2 +- js/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index aecf8960f..36d76cc01 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1752,7 +1752,7 @@ dependencies = [ [[package]] name = "parity-ui-precompiled" version = "1.4.0" -source = "git+https://github.com/paritytech/js-precompiled.git#6028c355854797a5938c26f5d2b2faf10d8833d7" +source = "git+https://github.com/paritytech/js-precompiled.git#6867dd71b2064f32ff53c84663897da6b4b13927" dependencies = [ "parity-dapps-glue 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] diff --git a/js/package.json b/js/package.json index 62803cc70..d2d5ec0ca 100644 --- a/js/package.json +++ b/js/package.json @@ -1,6 +1,6 @@ { "name": "parity.js", - "version": "1.7.43", + "version": "1.7.44", "main": "release/index.js", "jsnext:main": "src/index.js", "author": "Parity Team ", From 848779e465655268da4640fbcf6dae5378a16977 Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Mon, 3 Apr 2017 12:56:07 +0200 Subject: [PATCH 04/13] Allow empty-encoded values from encoding (#5385) --- js/src/util/qrscan.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/js/src/util/qrscan.js b/js/src/util/qrscan.js index eabc95409..f3cf2f9e9 100644 --- a/js/src/util/qrscan.js +++ b/js/src/util/qrscan.js @@ -92,9 +92,9 @@ export function generateQr (from, tx, hash, rlp) { account: from.substr(2), hash: hash.substr(2), details: { - gasPrice: inNumber10(inHex(tx.gasPrice.toString('hex'))), - gas: inNumber10(inHex(tx.gasLimit.toString('hex'))), - nonce: inNumber10(inHex(tx.nonce.toString('hex'))), + gasPrice: inNumber10(inHex(tx.gasPrice.toString('hex') || '0')), + gas: inNumber10(inHex(tx.gasLimit.toString('hex') || '0')), + nonce: inNumber10(inHex(tx.nonce.toString('hex') || '0')), to: inAddress(tx.to.toString('hex')), value: inHex(tx.value.toString('hex') || '0') } From ee4f9da3859d3f4dc7620ebd5d0b2092ede066e9 Mon Sep 17 00:00:00 2001 From: GitLab Build Bot Date: Mon, 3 Apr 2017 11:24:51 +0000 Subject: [PATCH 05/13] [ci skip] js-precompiled 20170403-112007 --- Cargo.lock | 2 +- js/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 36d76cc01..409dbc418 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1752,7 +1752,7 @@ dependencies = [ [[package]] name = "parity-ui-precompiled" version = "1.4.0" -source = "git+https://github.com/paritytech/js-precompiled.git#6867dd71b2064f32ff53c84663897da6b4b13927" +source = "git+https://github.com/paritytech/js-precompiled.git#04143247380a7a9bce112c9467636684d8214973" dependencies = [ "parity-dapps-glue 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] diff --git a/js/package.json b/js/package.json index d2d5ec0ca..82807e820 100644 --- a/js/package.json +++ b/js/package.json @@ -1,6 +1,6 @@ { "name": "parity.js", - "version": "1.7.44", + "version": "1.7.45", "main": "release/index.js", "jsnext:main": "src/index.js", "author": "Parity Team ", From abec06f50c95dc4382a5988096ff0135b607ee23 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Mon, 3 Apr 2017 18:46:51 +0300 Subject: [PATCH 06/13] On-chain ACL checker for secretstore (#5015) * ECDKG protocol prototype * added test for enc/dec math * get rid of decryption_session * added licenses * fix after merge * get rid of unused serde dependency * doc * decryption session [without commutative enc] * failed_dec_session * fixed tests * added commen * added more decryption session tests * helper to localize an issue * more computations to localize error * decryption_session::SessionParams * added tests for EC math to localize problem * secretstore network transport * encryption_session_works_over_network * network errors processing * connecting to KeyServer * licenses * get rid of debug println-s * fixed secretstore args * encryption results are stored in KS database * decryption protocol works over network * enc/dec Session traits * fixing warnings * fix after merge * on-chain ACL checker proto * fixed compilation * fixed compilation * finally fixed -of-N-scheme * temporary commented test * 1-of-N works in math * scheme 1-of-N works * updated AclStorage with real contract ABI * remove unnecessary unsafety * fixed grumbles * wakeup on access denied * fix after merge * fix after merge * moved contract to native-contracts lib --- Cargo.lock | 3 + ethcore/native_contracts/build.rs | 2 + ethcore/native_contracts/src/lib.rs | 2 + .../src/secretstore_acl_storage.rs | 22 +++++ parity/run.rs | 4 +- parity/secretstore.rs | 10 +- secret_store/Cargo.toml | 3 + secret_store/src/acl_storage.rs | 92 +++++++++++++++---- secret_store/src/key_server.rs | 2 +- .../key_server_cluster/decryption_session.rs | 10 +- secret_store/src/key_server_cluster/mod.rs | 7 +- secret_store/src/lib.rs | 10 +- secret_store/src/types/all.rs | 5 +- 13 files changed, 140 insertions(+), 32 deletions(-) create mode 100644 ethcore/native_contracts/src/secretstore_acl_storage.rs diff --git a/Cargo.lock b/Cargo.lock index 409dbc418..f36aa132e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -635,6 +635,8 @@ name = "ethcore-secretstore" version = "1.0.0" dependencies = [ "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-ipc 1.7.0", "ethcore-ipc-codegen 1.7.0", @@ -646,6 +648,7 @@ dependencies = [ "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)", "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)", "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)", diff --git a/ethcore/native_contracts/build.rs b/ethcore/native_contracts/build.rs index a8488617a..91eaa86cd 100644 --- a/ethcore/native_contracts/build.rs +++ b/ethcore/native_contracts/build.rs @@ -23,6 +23,7 @@ use std::io::Write; // 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 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) { 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() { build_file("Registry", REGISTRY_ABI, "registry.rs"); build_file("ServiceTransactionChecker", SERVICE_TRANSACTION_ABI, "service_transaction.rs"); + build_file("SecretStoreAclStorage", SECRETSTORE_ACL_STORAGE_ABI, "secretstore_acl_storage.rs"); } diff --git a/ethcore/native_contracts/src/lib.rs b/ethcore/native_contracts/src/lib.rs index 55c6446b7..e894a636f 100644 --- a/ethcore/native_contracts/src/lib.rs +++ b/ethcore/native_contracts/src/lib.rs @@ -25,6 +25,8 @@ extern crate ethcore_util as util; mod registry; mod service_transaction; +mod secretstore_acl_storage; pub use self::registry::Registry; pub use self::service_transaction::ServiceTransactionChecker; +pub use self::secretstore_acl_storage::SecretStoreAclStorage; diff --git a/ethcore/native_contracts/src/secretstore_acl_storage.rs b/ethcore/native_contracts/src/secretstore_acl_storage.rs new file mode 100644 index 000000000..3ebfcfb75 --- /dev/null +++ b/ethcore/native_contracts/src/secretstore_acl_storage.rs @@ -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 . + +#![allow(unused_mut, unused_variables, unused_imports)] + +//! Secret store ACL storage contract. +// TODO: testing. + +include!(concat!(env!("OUT_DIR"), "/secretstore_acl_storage.rs")); diff --git a/parity/run.rs b/parity/run.rs index a85bcc39b..8da5130d4 100644 --- a/parity/run.rs +++ b/parity/run.rs @@ -463,7 +463,9 @@ pub fn execute(cmd: RunCmd, can_restart: bool, logger: Arc) -> R let signer_server = signer::start(cmd.signer_conf.clone(), signer_deps)?; // 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); // the ipfs server diff --git a/parity/secretstore.rs b/parity/secretstore.rs index 13d6d28d2..d31614193 100644 --- a/parity/secretstore.rs +++ b/parity/secretstore.rs @@ -14,7 +14,9 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . +use std::sync::Arc; use dir::default_data_path; +use ethcore::client::Client; use helpers::replace_home; #[derive(Debug, PartialEq, Clone)] @@ -30,10 +32,10 @@ pub struct Configuration { pub data_path: String, } -#[derive(Debug, PartialEq, Clone)] /// Secret store dependencies pub struct Dependencies { - // the only dependency will be BlockChainClient + /// Blockchain client. + pub client: Arc, } #[cfg(not(feature = "secretstore"))] @@ -64,7 +66,7 @@ mod server { impl KeyServer { /// Create new key server - pub fn new(conf: Configuration, _deps: Dependencies) -> Result { + pub fn new(conf: Configuration, deps: Dependencies) -> Result { let key_pairs = vec![ ethkey::KeyPair::from_secret("6c26a76e9b31048d170873a791401c7e799a11f0cefc0171cc31a49800967509".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::::into)?; Ok(KeyServer { diff --git a/secret_store/Cargo.toml b/secret_store/Cargo.toml index fba76804b..539f15f1f 100644 --- a/secret_store/Cargo.toml +++ b/secret_store/Cargo.toml @@ -24,12 +24,15 @@ tokio-core = "0.1" tokio-service = "0.1" tokio-proto = "0.1" url = "1.0" +ethabi = "1.0.0" +ethcore = { path = "../ethcore" } ethcore-devtools = { path = "../devtools" } ethcore-util = { path = "../util" } ethcore-ipc = { path = "../ipc/rpc" } ethcore-ipc-nano = { path = "../ipc/nano" } ethcrypto = { path = "../ethcrypto" } ethkey = { path = "../ethkey" } +native-contracts = { path = "../ethcore/native_contracts" } [profile.release] debug = true diff --git a/secret_store/src/acl_storage.rs b/secret_store/src/acl_storage.rs index 47ec3d44a..fea45c920 100644 --- a/secret_store/src/acl_storage.rs +++ b/secret_store/src/acl_storage.rs @@ -14,38 +14,92 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -use std::collections::{HashMap, HashSet}; -use parking_lot::RwLock; +use std::sync::Arc; +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}; +const ACL_CHECKER_CONTRACT_REGISTRY_NAME: &'static str = "secretstore_acl_checker"; + /// ACL storage of Secret Store pub trait AclStorage: Send + Sync { /// Check if requestor with `public` key can access document with hash `document` fn check(&self, public: &Public, document: &DocumentAddress) -> Result; } -/// Dummy ACL storage implementation -#[derive(Default, Debug)] -pub struct DummyAclStorage { - prohibited: RwLock>>, +/// On-chain ACL storage implementation. +pub struct OnChainAclStorage { + /// Blockchain client. + client: Arc, + /// On-chain contract. + contract: Mutex>, } -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 OnChainAclStorage { + pub fn new(client: Arc) -> Self { + OnChainAclStorage { + client: client, + contract: Mutex::new(None), + } } } -impl AclStorage for DummyAclStorage { +impl AclStorage for OnChainAclStorage { fn check(&self, public: &Public, document: &DocumentAddress) -> Result { - Ok(self.prohibited.read() - .get(public) - .map(|docs| !docs.contains(document)) - .unwrap_or(true)) + let mut contract = self.contract.lock(); + if !contract.is_some() { + *contract = self.client.registry_address(ACL_CHECKER_CONTRACT_REGISTRY_NAME.to_owned()) + .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>>, + } + + 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 { + Ok(self.prohibited.read() + .get(public) + .map(|docs| !docs.contains(document)) + .unwrap_or(true)) + } } } diff --git a/secret_store/src/key_server.rs b/secret_store/src/key_server.rs index 553b49bfe..598f06338 100644 --- a/secret_store/src/key_server.rs +++ b/secret_store/src/key_server.rs @@ -147,7 +147,7 @@ mod tests { use std::sync::Arc; use ethcrypto; use ethkey::{self, Random, Generator}; - use acl_storage::DummyAclStorage; + use acl_storage::tests::DummyAclStorage; use key_storage::tests::DummyKeyStorage; use types::all::{ClusterConfiguration, NodeAddress, EncryptionConfiguration, DocumentEncryptedKey, DocumentKey}; use super::super::{RequestSignature, DocumentAddress}; diff --git a/secret_store/src/key_server_cluster/decryption_session.rs b/secret_store/src/key_server_cluster/decryption_session.rs index 71d8ad26f..652ed5c5a 100644 --- a/secret_store/src/key_server_cluster/decryption_session.rs +++ b/secret_store/src/key_server_cluster/decryption_session.rs @@ -220,7 +220,7 @@ impl SessionImpl { self.completed.notify_all(); }, // we can not decrypt data - SessionState::Failed => (), + SessionState::Failed => self.completed.notify_all(), // cannot reach other states _ => 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 => 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 - SessionState::Failed => Ok(()), + SessionState::Failed => { + self.completed.notify_all(); + Ok(()) + }, // cannot reach other states _ => 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? 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; } }, @@ -503,7 +507,7 @@ fn do_partial_decryption(node: &NodeId, _requestor_public: &Public, participants mod tests { use std::sync::Arc; 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 key_server_cluster::{NodeId, DocumentKeyShare, SessionId, Error}; use key_server_cluster::cluster::tests::DummyCluster; diff --git a/secret_store/src/key_server_cluster/mod.rs b/secret_store/src/key_server_cluster/mod.rs index 8b33e06f7..e889ef322 100644 --- a/secret_store/src/key_server_cluster/mod.rs +++ b/secret_store/src/key_server_cluster/mod.rs @@ -21,7 +21,7 @@ use ethcrypto; use super::types::all::DocumentAddress; 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::serialization::{SerializableSignature, SerializableH256, SerializableSecret, SerializablePublic}; pub use self::cluster::{ClusterCore, ClusterConfiguration, ClusterClient}; @@ -30,6 +30,8 @@ pub use self::decryption_session::Session as DecryptionSession; #[cfg(test)] pub use super::key_storage::tests::DummyKeyStorage; +#[cfg(test)] +pub use super::acl_storage::tests::DummyAclStorage; pub type SessionId = DocumentAddress; @@ -72,6 +74,8 @@ pub enum Error { Serde(String), /// Key storage error. KeyStorage(String), + /// Acl storage error. + AccessDenied, } impl From for Error { @@ -110,6 +114,7 @@ impl fmt::Display for Error { Error::Io(ref e) => write!(f, "i/o error {}", e), Error::Serde(ref e) => write!(f, "serde error {}", e), Error::KeyStorage(ref e) => write!(f, "key storage error {}", e), + Error::AccessDenied => write!(f, "Access denied"), } } } diff --git a/secret_store/src/lib.rs b/secret_store/src/lib.rs index bbb8474d4..7de957991 100644 --- a/secret_store/src/lib.rs +++ b/secret_store/src/lib.rs @@ -32,11 +32,14 @@ extern crate tokio_service; extern crate tokio_proto; extern crate url; +extern crate ethabi; +extern crate ethcore; extern crate ethcore_devtools as devtools; extern crate ethcore_util as util; extern crate ethcore_ipc as ipc; extern crate ethcrypto; extern crate ethkey; +extern crate native_contracts; mod key_server_cluster; mod types; @@ -52,15 +55,18 @@ mod key_server; mod key_storage; mod serialization; +use std::sync::Arc; +use ethcore::client::Client; + pub use types::all::{DocumentAddress, DocumentKey, DocumentEncryptedKey, RequestSignature, Public, Error, NodeAddress, ServiceConfiguration, ClusterConfiguration, EncryptionConfiguration}; pub use traits::{KeyServer}; /// Start new key server instance -pub fn start(config: ServiceConfiguration) -> Result, Error> { +pub fn start(client: Arc, config: ServiceConfiguration) -> Result, Error> { 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_server = key_server::KeyServerImpl::new(&config.cluster_config, acl_storage, key_storage)?; let listener = http_listener::KeyServerHttpListener::start(config, key_server)?; diff --git a/secret_store/src/types/all.rs b/secret_store/src/types/all.rs index 514b4eb6b..23e07e994 100644 --- a/secret_store/src/types/all.rs +++ b/secret_store/src/types/all.rs @@ -119,7 +119,10 @@ impl From for Error { impl From for Error { 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()), + } } } From 50e0221dd147b64e0c7ac7e50c1c5611ce1a3b2a Mon Sep 17 00:00:00 2001 From: maciejhirsz Date: Mon, 3 Apr 2017 18:50:11 +0200 Subject: [PATCH 07/13] Perf and fixes --- js/package.json | 2 +- js/src/api/local/accounts/account.js | 40 +++--- js/src/api/local/accounts/accounts.js | 58 +++++--- js/src/api/local/ethkey/index.js | 39 +++--- js/src/api/local/ethkey/worker.js | 129 ++++++++++++------ js/src/api/local/ethkey/workerPool.js | 61 +++++++++ js/src/api/local/middleware.js | 81 +++++++---- js/src/api/transport/jsonRpcBase.js | 12 +- .../CreateAccount/NewAccount/newAccount.js | 13 +- .../NewAccount/newAccount.spec.js | 9 +- js/src/modals/CreateAccount/store.js | 6 +- js/src/modals/CreateAccount/store.spec.js | 3 + 12 files changed, 324 insertions(+), 129 deletions(-) create mode 100644 js/src/api/local/ethkey/workerPool.js diff --git a/js/package.json b/js/package.json index 82807e820..0585a1681 100644 --- a/js/package.json +++ b/js/package.json @@ -176,7 +176,7 @@ "geopattern": "1.2.3", "isomorphic-fetch": "2.2.1", "js-sha3": "0.5.5", - "keythereum": "0.4.3", + "keythereum": "0.4.6", "lodash": "4.17.2", "loglevel": "1.4.1", "marked": "0.3.6", diff --git a/js/src/api/local/accounts/account.js b/js/src/api/local/accounts/account.js index da9de1359..94e923f45 100644 --- a/js/src/api/local/accounts/account.js +++ b/js/src/api/local/accounts/account.js @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -import { keythereum } from '../ethkey'; +import { createKeyObject, decryptPrivateKey } from '../ethkey'; export default class Account { constructor (persist, data) { @@ -31,12 +31,14 @@ export default class Account { } isValidPassword (password) { - try { - keythereum.recover(Buffer.from(password), this._keyObject); - return true; - } catch (e) { - return false; - } + return decryptPrivateKey(this._keyObject, password) + .then((privateKey) => { + if (!privateKey) { + return false; + } + + return true; + }); } get address () { @@ -68,21 +70,23 @@ export default class Account { } 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) { - const iv = keythereum.crypto.randomBytes(16); - const salt = keythereum.crypto.randomBytes(32); + return createKeyObject(key, password).then((keyObject) => { + const account = new Account(persist, { keyObject }); - // Keythereum will fail if `password` is an empty string - password = Buffer.from(password); - - const keyObject = keythereum.dump(password, key, salt, iv); - - const account = new Account(persist, { keyObject }); - - return account; + return account; + }); } toJSON () { diff --git a/js/src/api/local/accounts/accounts.js b/js/src/api/local/accounts/accounts.js index 576addcb1..1bce1329a 100644 --- a/js/src/api/local/accounts/accounts.js +++ b/js/src/api/local/accounts/accounts.js @@ -38,14 +38,22 @@ export default class Accounts { create (secret, password) { const privateKey = Buffer.from(secret.slice(2), 'hex'); - const account = Account.fromPrivateKey(this.persist, privateKey, password); - this._store.push(account); - this.lastAddress = account.address; + return Account.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) { @@ -73,28 +81,40 @@ export default class Accounts { remove (address, password) { 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); if (index === -1) { - return false; - } - - const account = this._store[index]; - - if (!account.isValidPassword(password)) { - console.log('invalid password'); - return false; - } - - if (address === this.lastAddress) { - this.lastAddress = NULL_ADDRESS; + return; } this._store.splice(index, 1); this.persist(); - - return true; } mapArray (mapper) { diff --git a/js/src/api/local/ethkey/index.js b/js/src/api/local/ethkey/index.js index ac2efa72e..4539c8c50 100644 --- a/js/src/api/local/ethkey/index.js +++ b/js/src/api/local/ethkey/index.js @@ -14,31 +14,34 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -// 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; +import workerPool from './workerPool'; -// Local accounts should never be used outside of the browser -export let keythereum = null; +export function createKeyObject (key, password) { + return workerPool.getWorker().action('createKeyObject', { key, password }) + .then((obj) => JSON.parse(obj)); +} -if (hasWebWorkers) { - require('keythereum/dist/keythereum'); +export function decryptPrivateKey (keyObject, password) { + return workerPool.getWorker() + .action('decryptPrivateKey', { keyObject, password }) + .then((privateKey) => { + if (privateKey) { + return Buffer.from(privateKey); + } - keythereum = window.keythereum; + return null; + }); } export function phraseToAddress (phrase) { - return phraseToWallet(phrase).then((wallet) => wallet.address); + return phraseToWallet(phrase) + .then((wallet) => wallet.address); } export function phraseToWallet (phrase) { - return new Promise((resolve, reject) => { - const worker = new KeyWorker(); - - worker.postMessage(phrase); - worker.onmessage = ({ data }) => { - resolve(data); - }; - }); + return workerPool.getWorker().action('phraseToWallet', phrase); +} + +export function verifySecret (secret) { + return workerPool.getWorker().action('verifySecret', secret); } diff --git a/js/src/api/local/ethkey/worker.js b/js/src/api/local/ethkey/worker.js index a472ee29a..3a0c34f7d 100644 --- a/js/src/api/local/ethkey/worker.js +++ b/js/src/api/local/ethkey/worker.js @@ -14,58 +14,107 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -import { keccak_256 as keccak256 } from 'js-sha3'; import secp256k1 from 'secp256k1/js'; +import { keccak_256 as keccak256 } from 'js-sha3'; + +const isWorker = typeof self !== 'undefined'; // Stay compatible between environments -if (typeof self !== 'object') { +if (!isWorker) { const scope = typeof global === 'undefined' ? window : global; scope.self = scope; } +// keythereum should never be used outside of the browser +let keythereum = null; + +if (isWorker) { + require('keythereum/dist/keythereum'); + + keythereum = self.keythereum; +} + +function route ({ action, payload }) { + if (action in actions) { + return actions[action](payload); + } + + return null; +} + +const actions = { + phraseToWallet (phrase) { + let secret = keccak256.array(phrase); + + for (let i = 0; i < 16384; i++) { + secret = keccak256.array(secret); + } + + while (true) { + 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; + } + } + }, + + verifySecret (secret) { + const key = Buffer.from(secret.slice(2), 'hex'); + + 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; + } + } +}; + function bytesToHex (bytes) { return '0x' + Array.from(bytes).map(n => ('0' + n.toString(16)).slice(-2)).join(''); } -// Logic ported from /ethkey/src/brain.rs -function phraseToWallet (phrase) { - let secret = keccak256.array(phrase); - - for (let i = 0; i < 16384; i++) { - secret = keccak256.array(secret); - } - - while (true) { - 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; - } - } -} - self.onmessage = function ({ data }) { - const wallet = phraseToWallet(data); + const result = route(data); - postMessage(wallet); - close(); + postMessage(result); }; // Emulate a web worker in Node.js @@ -73,9 +122,9 @@ class KeyWorker { postMessage (data) { // Force async setTimeout(() => { - const wallet = phraseToWallet(data); + const result = route(data); - this.onmessage({ data: wallet }); + this.onmessage({ data: result }); }, 0); } diff --git a/js/src/api/local/ethkey/workerPool.js b/js/src/api/local/ethkey/workerPool.js new file mode 100644 index 000000000..ff5315898 --- /dev/null +++ b/js/src/api/local/ethkey/workerPool.js @@ -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 . + +// 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(); diff --git a/js/src/api/local/middleware.js b/js/src/api/local/middleware.js index d5997c60a..ece3fa17d 100644 --- a/js/src/api/local/middleware.js +++ b/js/src/api/local/middleware.js @@ -19,7 +19,7 @@ import accounts from './accounts'; import transactions from './transactions'; import { Middleware } from '../transport'; import { inNumber16 } from '../format/input'; -import { phraseToWallet, phraseToAddress } from './ethkey'; +import { phraseToWallet, phraseToAddress, verifySecret } from './ethkey'; import { randomPhrase } from '@parity/wordlist'; export default class LocalAccountsMiddleware extends Middleware { @@ -57,6 +57,21 @@ 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]) => { return transactions.hash(id) || Promise.resolve(null); }); @@ -84,6 +99,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]) => { accounts.get(address).meta = meta; @@ -127,6 +153,12 @@ export default class LocalAccountsMiddleware extends Middleware { 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]) => { const { gasPrice, @@ -137,30 +169,33 @@ export default class LocalAccountsMiddleware extends Middleware { data } = Object.assign(transactions.get(id), modify); - return this - .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); + const account = accounts.get(from); - tx.sign(account.decryptPrivateKey(password)); - - const serializedTx = `0x${tx.serialize().toString('hex')}`; - - return this.rpcRequest('eth_sendRawTransaction', [serializedTx]); - }) - .then((hash) => { - transactions.confirm(id, hash); - - return {}; + return Promise.all([ + this.rpcRequest('parity_nextNonce', [from]), + account.decryptPrivateKey(password) + ]) + .then(([nonce, privateKey]) => { + const tx = new EthereumTx({ + nonce, + to, + data, + 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]) => { diff --git a/js/src/api/transport/jsonRpcBase.js b/js/src/api/transport/jsonRpcBase.js index 46df718a7..573204c3e 100644 --- a/js/src/api/transport/jsonRpcBase.js +++ b/js/src/api/transport/jsonRpcBase.js @@ -80,12 +80,16 @@ export default class JsonRpcBase extends EventEmitter { const res = middleware.handle(method, params); if (res != null) { - const result = this._wrapSuccessResult(res); - const json = this.encode(method, params); + // If `res` isn't a promise, we need to wrap it + 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; + }); } } diff --git a/js/src/modals/CreateAccount/NewAccount/newAccount.js b/js/src/modals/CreateAccount/NewAccount/newAccount.js index 04f2f272a..9c6be9f6e 100644 --- a/js/src/modals/CreateAccount/NewAccount/newAccount.js +++ b/js/src/modals/CreateAccount/NewAccount/newAccount.js @@ -23,6 +23,7 @@ import { RadioButton, RadioButtonGroup } from 'material-ui/RadioButton'; import { Form, Input, IdentityIcon } from '~/ui'; import PasswordStrength from '~/ui/Form/PasswordStrength'; import { RefreshIcon } from '~/ui/Icons'; +import Loading from '~/ui/Loading'; import ChangeVault from '../ChangeVault'; import styles from '../createAccount.css'; @@ -170,7 +171,9 @@ export default class CreateAccount extends Component { const { accounts } = this.state; if (!accounts) { - return null; + return ( + + ); } const identities = Object @@ -205,6 +208,14 @@ export default class CreateAccount extends Component { createIdentities = () => { const { createStore } = this.props; + this.setState({ + accounts: null, + selectedAddress: '' + }); + + createStore.setAddress(''); + createStore.setPhrase(''); + return createStore .createIdentities() .then((accounts) => { diff --git a/js/src/modals/CreateAccount/NewAccount/newAccount.spec.js b/js/src/modals/CreateAccount/NewAccount/newAccount.spec.js index 87c7ba3fc..d6d38779f 100644 --- a/js/src/modals/CreateAccount/NewAccount/newAccount.spec.js +++ b/js/src/modals/CreateAccount/NewAccount/newAccount.spec.js @@ -58,12 +58,13 @@ describe('modals/CreateAccount/NewAccount', () => { return instance.componentWillMount(); }); - it('creates initial accounts', () => { - expect(Object.keys(instance.state.accounts).length).to.equal(7); + it('resets the accounts', () => { + expect(instance.state.accounts).to.be.null; + // expect(Object.keys(instance.state.accounts).length).to.equal(7); }); - it('sets the initial selected value', () => { - expect(instance.state.selectedAddress).to.equal(Object.keys(instance.state.accounts)[0]); + it('resets the initial selected value', () => { + expect(instance.state.selectedAddress).to.equal(''); }); }); }); diff --git a/js/src/modals/CreateAccount/store.js b/js/src/modals/CreateAccount/store.js index 52dddac80..9bc60d9af 100644 --- a/js/src/modals/CreateAccount/store.js +++ b/js/src/modals/CreateAccount/store.js @@ -69,7 +69,7 @@ export default class Store { return !(this.nameError || this.walletFileError); case 'fromNew': - return !(this.nameError || this.passwordRepeatError); + return !(this.nameError || this.passwordRepeatError) && this.hasAddress; case 'fromPhrase': return !(this.nameError || this.passwordRepeatError); @@ -85,6 +85,10 @@ export default class Store { } } + @computed get hasAddress () { + return !!(this.address); + } + @computed get passwordRepeatError () { return this.password === this.passwordRepeat ? null diff --git a/js/src/modals/CreateAccount/store.spec.js b/js/src/modals/CreateAccount/store.spec.js index b02f013b6..9d7bc10a2 100644 --- a/js/src/modals/CreateAccount/store.spec.js +++ b/js/src/modals/CreateAccount/store.spec.js @@ -329,6 +329,7 @@ describe('modals/CreateAccount/Store', () => { describe('createType === fromNew', () => { beforeEach(() => { store.setCreateType('fromNew'); + store.setAddress('0x0000000000000000000000000000000000000000'); }); it('returns true on no errors', () => { @@ -337,11 +338,13 @@ describe('modals/CreateAccount/Store', () => { it('returns false on nameError', () => { store.setName(''); + expect(store.canCreate).to.be.false; }); it('returns false on passwordRepeatError', () => { store.setPassword('testing'); + expect(store.canCreate).to.be.false; }); }); From 94bfe116aa2b5908803b0f11ec2c11e79a32accd Mon Sep 17 00:00:00 2001 From: maciejhirsz Date: Tue, 4 Apr 2017 11:49:36 +0200 Subject: [PATCH 08/13] CR fixes --- js/src/api/local/accounts/accounts.js | 6 ++++-- js/src/api/local/ethkey/index.js | 3 ++- js/src/api/local/ethkey/worker.js | 5 +---- js/src/api/local/middleware.js | 3 ++- js/src/api/util/format.js | 2 +- js/src/modals/CreateAccount/NewAccount/newAccount.spec.js | 1 - 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/js/src/api/local/accounts/accounts.js b/js/src/api/local/accounts/accounts.js index 1bce1329a..e7e5cc397 100644 --- a/js/src/api/local/accounts/accounts.js +++ b/js/src/api/local/accounts/accounts.js @@ -39,7 +39,8 @@ export default class Accounts { create (secret, password) { const privateKey = Buffer.from(secret.slice(2), 'hex'); - return Account.fromPrivateKey(this.persist, privateKey, password) + return Account + .fromPrivateKey(this.persist, privateKey, password) .then((account) => { const { address } = account; @@ -87,7 +88,8 @@ export default class Accounts { return false; } - return account.isValidPassword(password) + return account + .isValidPassword(password) .then((isValid) => { if (!isValid) { return false; diff --git a/js/src/api/local/ethkey/index.js b/js/src/api/local/ethkey/index.js index 4539c8c50..a6967da25 100644 --- a/js/src/api/local/ethkey/index.js +++ b/js/src/api/local/ethkey/index.js @@ -22,7 +22,8 @@ export function createKeyObject (key, password) { } export function decryptPrivateKey (keyObject, password) { - return workerPool.getWorker() + return workerPool + .getWorker() .action('decryptPrivateKey', { keyObject, password }) .then((privateKey) => { if (privateKey) { diff --git a/js/src/api/local/ethkey/worker.js b/js/src/api/local/ethkey/worker.js index 3a0c34f7d..00f4a0bed 100644 --- a/js/src/api/local/ethkey/worker.js +++ b/js/src/api/local/ethkey/worker.js @@ -16,6 +16,7 @@ import secp256k1 from 'secp256k1/js'; import { keccak_256 as keccak256 } from 'js-sha3'; +import { bytesToHex } from '~/api/util/format'; const isWorker = typeof self !== 'undefined'; @@ -107,10 +108,6 @@ const actions = { } }; -function bytesToHex (bytes) { - return '0x' + Array.from(bytes).map(n => ('0' + n.toString(16)).slice(-2)).join(''); -} - self.onmessage = function ({ data }) { const result = route(data); diff --git a/js/src/api/local/middleware.js b/js/src/api/local/middleware.js index ece3fa17d..36a8cd2cf 100644 --- a/js/src/api/local/middleware.js +++ b/js/src/api/local/middleware.js @@ -60,7 +60,8 @@ export default class LocalAccountsMiddleware extends Middleware { register('parity_changePassword', ([address, oldPassword, newPassword]) => { const account = accounts.get(address); - return account.decryptPrivateKey(oldPassword) + return account + .decryptPrivateKey(oldPassword) .then((privateKey) => { if (!privateKey) { return false; diff --git a/js/src/api/util/format.js b/js/src/api/util/format.js index c7594b692..61fc9d32c 100644 --- a/js/src/api/util/format.js +++ b/js/src/api/util/format.js @@ -17,7 +17,7 @@ import { range } from 'lodash'; 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) { diff --git a/js/src/modals/CreateAccount/NewAccount/newAccount.spec.js b/js/src/modals/CreateAccount/NewAccount/newAccount.spec.js index d6d38779f..935fe5b80 100644 --- a/js/src/modals/CreateAccount/NewAccount/newAccount.spec.js +++ b/js/src/modals/CreateAccount/NewAccount/newAccount.spec.js @@ -60,7 +60,6 @@ describe('modals/CreateAccount/NewAccount', () => { it('resets the accounts', () => { expect(instance.state.accounts).to.be.null; - // expect(Object.keys(instance.state.accounts).length).to.equal(7); }); it('resets the initial selected value', () => { From 9b212dc518a8c3d579e8be4b6da748e916351377 Mon Sep 17 00:00:00 2001 From: "Denis S. Soldatov aka General-Beck" Date: Tue, 4 Apr 2017 12:51:54 +0300 Subject: [PATCH 09/13] Update cov.sh remove src/test from EXCLUDE --- scripts/cov.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/cov.sh b/scripts/cov.sh index 13ab792c7..13d042905 100755 --- a/scripts/cov.sh +++ b/scripts/cov.sh @@ -32,7 +32,6 @@ $HOME/.cargo,\ $HOME/.multirust,\ rocksdb,\ secp256k1,\ -src/tests,\ util/json-tests,\ util/src/network/tests,\ ethcore/src/evm/tests,\ From 5f9dc132074c6c2b33d5e9eb632c932925fab6f1 Mon Sep 17 00:00:00 2001 From: Arkadiy Paronyan Date: Tue, 4 Apr 2017 14:46:42 +0200 Subject: [PATCH 10/13] Straight download path in the readme (#5393) --- README.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index d92b68c58..f668f3218 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ # [Parity](https://ethcore.io/parity.html) ### 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] ### 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 [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 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. ---- ## 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: @@ -80,7 +81,7 @@ Once you have rustup, install parity or download and build from source ---- -## Quick install +## Quick build and install ```bash cargo install --git https://github.com/paritytech/parity.git parity From 8d0fde6f608f7794e8c18b18e762f77741473013 Mon Sep 17 00:00:00 2001 From: GitLab Build Bot Date: Wed, 5 Apr 2017 09:06:09 +0000 Subject: [PATCH 11/13] [ci skip] js-precompiled 20170405-090226 --- Cargo.lock | 2 +- js/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f36aa132e..d50bf5c3e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1755,7 +1755,7 @@ dependencies = [ [[package]] name = "parity-ui-precompiled" version = "1.4.0" -source = "git+https://github.com/paritytech/js-precompiled.git#04143247380a7a9bce112c9467636684d8214973" +source = "git+https://github.com/paritytech/js-precompiled.git#9bfc6f3dfca2c337c53084bedcc65c2b526927a1" dependencies = [ "parity-dapps-glue 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] diff --git a/js/package.json b/js/package.json index 0585a1681..6e8b84f6d 100644 --- a/js/package.json +++ b/js/package.json @@ -1,6 +1,6 @@ { "name": "parity.js", - "version": "1.7.45", + "version": "1.7.46", "main": "release/index.js", "jsnext:main": "src/index.js", "author": "Parity Team ", From 0d8a2c8c44cf1655e4580bbe6bae0314662ffc49 Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Wed, 5 Apr 2017 11:30:51 +0200 Subject: [PATCH 12/13] CHT key optimization --- ethcore/light/src/client/header_chain.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ethcore/light/src/client/header_chain.rs b/ethcore/light/src/client/header_chain.rs index 1c218204b..d4ea8d107 100644 --- a/ethcore/light/src/client/header_chain.rs +++ b/ethcore/light/src/client/header_chain.rs @@ -119,7 +119,7 @@ impl Decodable for Entry { } fn cht_key(number: u64) -> String { - format!("canonical_{}", number) + format!("{:08x}_canonical", number) } fn era_key(number: u64) -> String { From e2dfea8c12978619db30d8f44183ff3909f215b7 Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Wed, 5 Apr 2017 11:57:29 +0200 Subject: [PATCH 13/13] set gas limit before proving transactions --- ethcore/light/src/on_demand/request.rs | 4 +++- ethcore/src/client/client.rs | 6 ++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/ethcore/light/src/on_demand/request.rs b/ethcore/light/src/on_demand/request.rs index cda1d6feb..d3bb06888 100644 --- a/ethcore/light/src/on_demand/request.rs +++ b/ethcore/light/src/on_demand/request.rs @@ -243,12 +243,14 @@ impl TransactionProof { pub fn check_response(&self, state_items: &[DBValue]) -> ProvedExecution { let root = self.header.state_root(); + let mut env_info = self.env_info.clone(); + env_info.gas_limit = self.tx.gas.clone(); state::check_proof( state_items, root, &self.tx, &*self.engine, - &self.env_info, + &env_info, ) } } diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index 3accc777f..4bd29d100 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -395,7 +395,7 @@ impl Client { if header.number() < self.engine().params().validate_receipts_transition && header.receipts_root() != locked_block.block().header().receipts_root() { locked_block = locked_block.strip_receipts(); } - + // Final Verification if let Err(e) = self.verifier.verify_block_final(header, locked_block.block().header()) { warn!(target: "client", "Stage 4 block verification failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e); @@ -1627,10 +1627,12 @@ impl ::client::ProvingBlockChainClient for Client { } fn prove_transaction(&self, transaction: SignedTransaction, id: BlockId) -> Option> { - 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), _ => return None, }; + + env_info.gas_limit = transaction.gas.clone(); let mut jdb = self.state_db.lock().journal_db().boxed_clone(); let backend = state::backend::Proving::new(jdb.as_hashdb_mut());