parity_getBlockHeaderByNumber and LightFetch
This commit is contained in:
parent
41700a6996
commit
6aa9005785
@ -68,6 +68,13 @@ pub trait LightChainClient: Send + Sync {
|
|||||||
/// Get the signing network ID.
|
/// Get the signing network ID.
|
||||||
fn signing_network_id(&self) -> Option<u64>;
|
fn signing_network_id(&self) -> Option<u64>;
|
||||||
|
|
||||||
|
/// Get environment info for execution at a given block.
|
||||||
|
/// Fails if that block's header is not stored.
|
||||||
|
fn env_info(&self, id: BlockId) -> Option<EnvInfo>;
|
||||||
|
|
||||||
|
/// Get a handle to the consensus engine.
|
||||||
|
fn engine(&self) -> &Arc<Engine>;
|
||||||
|
|
||||||
/// Query whether a block is known.
|
/// Query whether a block is known.
|
||||||
fn is_known(&self, hash: &H256) -> bool;
|
fn is_known(&self, hash: &H256) -> bool;
|
||||||
|
|
||||||
@ -295,6 +302,14 @@ impl LightChainClient for Client {
|
|||||||
Client::signing_network_id(self)
|
Client::signing_network_id(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn env_info(&self, id: BlockId) -> Option<EnvInfo> {
|
||||||
|
Client::env_info(self, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn engine(&self) -> &Arc<Engine> {
|
||||||
|
Client::engine(self)
|
||||||
|
}
|
||||||
|
|
||||||
fn is_known(&self, hash: &H256) -> bool {
|
fn is_known(&self, hash: &H256) -> bool {
|
||||||
self.status(hash) == BlockStatus::InChain
|
self.status(hash) == BlockStatus::InChain
|
||||||
}
|
}
|
||||||
|
@ -346,3 +346,8 @@ pub fn deprecated<T: Into<Option<String>>>(message: T) -> Error {
|
|||||||
data: message.into().map(Value::String),
|
data: message.into().map(Value::String),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// on-demand sender cancelled.
|
||||||
|
pub fn on_demand_cancel(_cancel: ::futures::sync::oneshot::Canceled) -> Error {
|
||||||
|
internal("on-demand sender cancelled", "")
|
||||||
|
}
|
||||||
|
200
rpc/src/v1/helpers/light_fetch.rs
Normal file
200
rpc/src/v1/helpers/light_fetch.rs
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//! Helpers for fetching blockchain data either from the light client or the network.
|
||||||
|
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use ethcore::basic_account::BasicAccount;
|
||||||
|
use ethcore::encoded;
|
||||||
|
use ethcore::executed::{Executed, ExecutionError};
|
||||||
|
use ethcore::ids::BlockId;
|
||||||
|
use ethcore::transaction::{Action, Transaction as EthTransaction};
|
||||||
|
|
||||||
|
use futures::{future, Future, BoxFuture};
|
||||||
|
use jsonrpc_core::Error;
|
||||||
|
use jsonrpc_macros::Trailing;
|
||||||
|
|
||||||
|
use light::cache::Cache;
|
||||||
|
use light::client::LightChainClient;
|
||||||
|
use light::cht;
|
||||||
|
use light::on_demand::{OnDemand, request};
|
||||||
|
|
||||||
|
use ethsync::LightSync;
|
||||||
|
use util::{Address, Mutex, Uint, U256};
|
||||||
|
|
||||||
|
use v1::helpers::{CallRequest as CRequest, errors, dispatch};
|
||||||
|
use v1::types::{BlockNumber, CallRequest};
|
||||||
|
|
||||||
|
/// Helper for fetching blockchain data either from the light client or the network
|
||||||
|
/// as necessary.
|
||||||
|
pub struct LightFetch {
|
||||||
|
/// The light client.
|
||||||
|
pub client: Arc<LightChainClient>,
|
||||||
|
/// The on-demand request service.
|
||||||
|
pub on_demand: Arc<OnDemand>,
|
||||||
|
/// Handle to the network.
|
||||||
|
pub sync: Arc<LightSync>,
|
||||||
|
/// The light data cache.
|
||||||
|
pub cache: Arc<Mutex<Cache>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Type alias for convenience.
|
||||||
|
pub type ExecutionResult = Result<Executed, ExecutionError>;
|
||||||
|
|
||||||
|
impl LightFetch {
|
||||||
|
/// Get a block header from the on demand service or client, or error.
|
||||||
|
pub fn header(&self, id: BlockId) -> BoxFuture<Option<encoded::Header>, 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<Option<BasicAccount>, 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<BlockNumber>) -> BoxFuture<ExecutionResult, Error> {
|
||||||
|
const DEFAULT_GAS_PRICE: U256 = U256([0, 0, 0, 21_000_000]);
|
||||||
|
|
||||||
|
let (sync, on_demand, client) = (self.sync.clone(), self.on_demand.clone(), self.client.clone());
|
||||||
|
let req: CRequest = req.into();
|
||||||
|
let id = num.0.into();
|
||||||
|
|
||||||
|
let from = req.from.unwrap_or(Address::zero());
|
||||||
|
let nonce_fut = match req.nonce {
|
||||||
|
Some(nonce) => future::ok(Some(nonce)).boxed(),
|
||||||
|
None => self.account(from, id).map(|acc| acc.map(|a| a.nonce)).boxed(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let gas_price_fut = match req.gas_price {
|
||||||
|
Some(price) => future::ok(price).boxed(),
|
||||||
|
None => dispatch::fetch_gas_price_corpus(
|
||||||
|
self.sync.clone(),
|
||||||
|
self.client.clone(),
|
||||||
|
self.on_demand.clone(),
|
||||||
|
self.cache.clone(),
|
||||||
|
).map(|corp| match corp.median() {
|
||||||
|
Some(median) => *median,
|
||||||
|
None => DEFAULT_GAS_PRICE,
|
||||||
|
}).boxed()
|
||||||
|
};
|
||||||
|
|
||||||
|
// if nonce resolves, this should too since it'll be in the LRU-cache.
|
||||||
|
let header_fut = self.header(id);
|
||||||
|
|
||||||
|
// fetch missing transaction fields from the network.
|
||||||
|
nonce_fut.join(gas_price_fut).and_then(move |(nonce, gas_price)| {
|
||||||
|
let action = req.to.map_or(Action::Create, Action::Call);
|
||||||
|
let gas = req.gas.unwrap_or(U256::from(10_000_000)); // better gas amount?
|
||||||
|
let value = req.value.unwrap_or_else(U256::zero);
|
||||||
|
let data = req.data.map_or_else(Vec::new, |d| d.to_vec());
|
||||||
|
|
||||||
|
future::done(match nonce {
|
||||||
|
Some(n) => Ok(EthTransaction {
|
||||||
|
nonce: n,
|
||||||
|
action: action,
|
||||||
|
gas: gas,
|
||||||
|
gas_price: gas_price,
|
||||||
|
value: value,
|
||||||
|
data: data,
|
||||||
|
}.fake_sign(from)),
|
||||||
|
None => Err(errors::unknown_block()),
|
||||||
|
})
|
||||||
|
}).join(header_fut).and_then(move |(tx, hdr)| {
|
||||||
|
// then request proved execution.
|
||||||
|
// TODO: get last-hashes from network.
|
||||||
|
let (env_info, 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()
|
||||||
|
}
|
||||||
|
}
|
@ -21,6 +21,7 @@ pub mod accounts;
|
|||||||
pub mod block_import;
|
pub mod block_import;
|
||||||
pub mod dispatch;
|
pub mod dispatch;
|
||||||
pub mod fake_sign;
|
pub mod fake_sign;
|
||||||
|
pub mod light_fetch;
|
||||||
pub mod informant;
|
pub mod informant;
|
||||||
pub mod oneshot;
|
pub mod oneshot;
|
||||||
|
|
||||||
|
@ -146,7 +146,7 @@ impl<C, SN: ?Sized, S: ?Sized, M, EM> EthClient<C, SN, S, M, EM> where
|
|||||||
(Some(block), Some(total_difficulty)) => {
|
(Some(block), Some(total_difficulty)) => {
|
||||||
let view = block.header_view();
|
let view = block.header_view();
|
||||||
Ok(Some(RichBlock {
|
Ok(Some(RichBlock {
|
||||||
block: Block {
|
inner: Block {
|
||||||
hash: Some(view.sha3().into()),
|
hash: Some(view.sha3().into()),
|
||||||
size: Some(block.rlp().as_raw().len().into()),
|
size: Some(block.rlp().as_raw().len().into()),
|
||||||
parent_hash: view.parent_hash().into(),
|
parent_hash: view.parent_hash().into(),
|
||||||
@ -202,7 +202,7 @@ impl<C, SN: ?Sized, S: ?Sized, M, EM> EthClient<C, SN, S, M, EM> where
|
|||||||
.map(Into::into);
|
.map(Into::into);
|
||||||
|
|
||||||
let block = RichBlock {
|
let block = RichBlock {
|
||||||
block: Block {
|
inner: Block {
|
||||||
hash: Some(uncle.hash().into()),
|
hash: Some(uncle.hash().into()),
|
||||||
size: size,
|
size: size,
|
||||||
parent_hash: uncle.parent_hash().clone().into(),
|
parent_hash: uncle.parent_hash().clone().into(),
|
||||||
|
@ -45,6 +45,7 @@ use futures::sync::oneshot;
|
|||||||
|
|
||||||
use v1::helpers::{CallRequest as CRequest, errors, limit_logs, dispatch};
|
use v1::helpers::{CallRequest as CRequest, errors, limit_logs, dispatch};
|
||||||
use v1::helpers::block_import::is_major_importing;
|
use v1::helpers::block_import::is_major_importing;
|
||||||
|
use v1::helpers::light_fetch::LightFetch;
|
||||||
use v1::traits::Eth;
|
use v1::traits::Eth;
|
||||||
use v1::types::{
|
use v1::types::{
|
||||||
RichBlock, Block, BlockTransactions, BlockNumber, Bytes, SyncStatus, SyncInfo,
|
RichBlock, Block, BlockTransactions, BlockNumber, Bytes, SyncStatus, SyncInfo,
|
||||||
@ -65,12 +66,6 @@ pub struct EthClient {
|
|||||||
cache: Arc<Mutex<LightDataCache>>,
|
cache: Arc<Mutex<LightDataCache>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// helper for internal error: on demand sender cancelled.
|
|
||||||
fn err_premature_cancel(_cancel: oneshot::Canceled) -> Error {
|
|
||||||
errors::internal("on-demand sender prematurely cancelled", "")
|
|
||||||
}
|
|
||||||
|
|
||||||
type ExecutionResult = Result<Executed, ExecutionError>;
|
|
||||||
|
|
||||||
impl EthClient {
|
impl EthClient {
|
||||||
/// Create a new `EthClient` with a handle to the light sync instance, client,
|
/// Create a new `EthClient` with a handle to the light sync instance, client,
|
||||||
@ -93,147 +88,15 @@ impl EthClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a block header from the on demand service or client, or error.
|
/// Create a light data fetcher instance.
|
||||||
fn header(&self, id: BlockId) -> BoxFuture<Option<encoded::Header>, Error> {
|
fn fetcher(&self) -> LightFetch {
|
||||||
if let Some(h) = self.client.block_header(id) {
|
LightFetch {
|
||||||
return future::ok(Some(h)).boxed()
|
client: self.client.clone(),
|
||||||
|
on_demand: self.on_demand.clone(),
|
||||||
|
sync: self.sync.clone(),
|
||||||
|
cache: self.cache.clone(),
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let maybe_future = match id {
|
|
||||||
BlockId::Number(n) => {
|
|
||||||
let cht_root = cht::block_to_cht_number(n).and_then(|cn| self.client.cht_root(cn as usize));
|
|
||||||
match cht_root {
|
|
||||||
None => return future::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<Option<BasicAccount>, 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<BlockNumber>) -> BoxFuture<ExecutionResult, Error> {
|
|
||||||
const DEFAULT_GAS_PRICE: U256 = U256([0, 0, 0, 21_000_000]);
|
|
||||||
|
|
||||||
|
|
||||||
let (sync, on_demand, client) = (self.sync.clone(), self.on_demand.clone(), self.client.clone());
|
|
||||||
let req: CRequest = req.into();
|
|
||||||
let id = num.0.into();
|
|
||||||
|
|
||||||
let from = req.from.unwrap_or(Address::zero());
|
|
||||||
let nonce_fut = match req.nonce {
|
|
||||||
Some(nonce) => future::ok(Some(nonce)).boxed(),
|
|
||||||
None => self.account(from, id).map(|acc| acc.map(|a| a.nonce)).boxed(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let gas_price_fut = match req.gas_price {
|
|
||||||
Some(price) => future::ok(price).boxed(),
|
|
||||||
None => dispatch::fetch_gas_price_corpus(
|
|
||||||
self.sync.clone(),
|
|
||||||
self.client.clone(),
|
|
||||||
self.on_demand.clone(),
|
|
||||||
self.cache.clone(),
|
|
||||||
).map(|corp| match corp.median() {
|
|
||||||
Some(median) => *median,
|
|
||||||
None => DEFAULT_GAS_PRICE,
|
|
||||||
}).boxed()
|
|
||||||
};
|
|
||||||
|
|
||||||
// if nonce resolves, this should too since it'll be in the LRU-cache.
|
|
||||||
let header_fut = self.header(id);
|
|
||||||
|
|
||||||
// fetch missing transaction fields from the network.
|
|
||||||
nonce_fut.join(gas_price_fut).and_then(move |(nonce, gas_price)| {
|
|
||||||
let action = req.to.map_or(Action::Create, Action::Call);
|
|
||||||
let gas = req.gas.unwrap_or(U256::from(10_000_000)); // better gas amount?
|
|
||||||
let value = req.value.unwrap_or_else(U256::zero);
|
|
||||||
let data = req.data.map_or_else(Vec::new, |d| d.to_vec());
|
|
||||||
|
|
||||||
future::done(match nonce {
|
|
||||||
Some(n) => Ok(EthTransaction {
|
|
||||||
nonce: n,
|
|
||||||
action: action,
|
|
||||||
gas: gas,
|
|
||||||
gas_price: gas_price,
|
|
||||||
value: value,
|
|
||||||
data: data,
|
|
||||||
}.fake_sign(from)),
|
|
||||||
None => Err(errors::unknown_block()),
|
|
||||||
})
|
|
||||||
}).join(header_fut).and_then(move |(tx, hdr)| {
|
|
||||||
// then request proved execution.
|
|
||||||
// TODO: get last-hashes from network.
|
|
||||||
let (env_info, 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<BlockNumber>) -> BoxFuture<RpcU256, Error> {
|
fn balance(&self, address: RpcH160, num: Trailing<BlockNumber>) -> BoxFuture<RpcU256, Error> {
|
||||||
self.account(address.into(), num.0.into())
|
self.fetcher().account(address.into(), num.0.into())
|
||||||
.map(|acc| acc.map_or(0.into(), |a| a.balance).into()).boxed()
|
.map(|acc| acc.map_or(0.into(), |a| a.balance).into()).boxed()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -298,14 +161,14 @@ impl Eth for EthClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn transaction_count(&self, address: RpcH160, num: Trailing<BlockNumber>) -> BoxFuture<RpcU256, Error> {
|
fn transaction_count(&self, address: RpcH160, num: Trailing<BlockNumber>) -> BoxFuture<RpcU256, Error> {
|
||||||
self.account(address.into(), num.0.into())
|
self.fetcher().account(address.into(), num.0.into())
|
||||||
.map(|acc| acc.map_or(0.into(), |a| a.nonce).into()).boxed()
|
.map(|acc| acc.map_or(0.into(), |a| a.nonce).into()).boxed()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn block_transaction_count_by_hash(&self, hash: RpcH256) -> BoxFuture<Option<RpcU256>, Error> {
|
fn block_transaction_count_by_hash(&self, hash: RpcH256) -> BoxFuture<Option<RpcU256>, Error> {
|
||||||
let (sync, on_demand) = (self.sync.clone(), self.on_demand.clone());
|
let (sync, on_demand) = (self.sync.clone(), self.on_demand.clone());
|
||||||
|
|
||||||
self.header(BlockId::Hash(hash.into())).and_then(move |hdr| {
|
self.fetcher().header(BlockId::Hash(hash.into())).and_then(move |hdr| {
|
||||||
let hdr = match hdr {
|
let hdr = match hdr {
|
||||||
None => return future::ok(None).boxed(),
|
None => return future::ok(None).boxed(),
|
||||||
Some(hdr) => hdr,
|
Some(hdr) => hdr,
|
||||||
@ -316,7 +179,7 @@ impl Eth for EthClient {
|
|||||||
} else {
|
} else {
|
||||||
sync.with_context(|ctx| on_demand.block(ctx, request::Body::new(hdr)))
|
sync.with_context(|ctx| on_demand.block(ctx, request::Body::new(hdr)))
|
||||||
.map(|x| x.map(|b| Some(U256::from(b.transactions_count()).into())))
|
.map(|x| x.map(|b| Some(U256::from(b.transactions_count()).into())))
|
||||||
.map(|x| x.map_err(err_premature_cancel).boxed())
|
.map(|x| x.map_err(errors::on_demand_cancel).boxed())
|
||||||
.unwrap_or_else(|| future::err(errors::network_disabled()).boxed())
|
.unwrap_or_else(|| future::err(errors::network_disabled()).boxed())
|
||||||
}
|
}
|
||||||
}).boxed()
|
}).boxed()
|
||||||
@ -325,7 +188,7 @@ impl Eth for EthClient {
|
|||||||
fn block_transaction_count_by_number(&self, num: BlockNumber) -> BoxFuture<Option<RpcU256>, Error> {
|
fn block_transaction_count_by_number(&self, num: BlockNumber) -> BoxFuture<Option<RpcU256>, Error> {
|
||||||
let (sync, on_demand) = (self.sync.clone(), self.on_demand.clone());
|
let (sync, on_demand) = (self.sync.clone(), self.on_demand.clone());
|
||||||
|
|
||||||
self.header(num.into()).and_then(move |hdr| {
|
self.fetcher().header(num.into()).and_then(move |hdr| {
|
||||||
let hdr = match hdr {
|
let hdr = match hdr {
|
||||||
None => return future::ok(None).boxed(),
|
None => return future::ok(None).boxed(),
|
||||||
Some(hdr) => hdr,
|
Some(hdr) => hdr,
|
||||||
@ -336,7 +199,7 @@ impl Eth for EthClient {
|
|||||||
} else {
|
} else {
|
||||||
sync.with_context(|ctx| on_demand.block(ctx, request::Body::new(hdr)))
|
sync.with_context(|ctx| on_demand.block(ctx, request::Body::new(hdr)))
|
||||||
.map(|x| x.map(|b| Some(U256::from(b.transactions_count()).into())))
|
.map(|x| x.map(|b| Some(U256::from(b.transactions_count()).into())))
|
||||||
.map(|x| x.map_err(err_premature_cancel).boxed())
|
.map(|x| x.map_err(errors::on_demand_cancel).boxed())
|
||||||
.unwrap_or_else(|| future::err(errors::network_disabled()).boxed())
|
.unwrap_or_else(|| future::err(errors::network_disabled()).boxed())
|
||||||
}
|
}
|
||||||
}).boxed()
|
}).boxed()
|
||||||
@ -345,7 +208,7 @@ impl Eth for EthClient {
|
|||||||
fn block_uncles_count_by_hash(&self, hash: RpcH256) -> BoxFuture<Option<RpcU256>, Error> {
|
fn block_uncles_count_by_hash(&self, hash: RpcH256) -> BoxFuture<Option<RpcU256>, Error> {
|
||||||
let (sync, on_demand) = (self.sync.clone(), self.on_demand.clone());
|
let (sync, on_demand) = (self.sync.clone(), self.on_demand.clone());
|
||||||
|
|
||||||
self.header(BlockId::Hash(hash.into())).and_then(move |hdr| {
|
self.fetcher().header(BlockId::Hash(hash.into())).and_then(move |hdr| {
|
||||||
let hdr = match hdr {
|
let hdr = match hdr {
|
||||||
None => return future::ok(None).boxed(),
|
None => return future::ok(None).boxed(),
|
||||||
Some(hdr) => hdr,
|
Some(hdr) => hdr,
|
||||||
@ -356,7 +219,7 @@ impl Eth for EthClient {
|
|||||||
} else {
|
} else {
|
||||||
sync.with_context(|ctx| on_demand.block(ctx, request::Body::new(hdr)))
|
sync.with_context(|ctx| on_demand.block(ctx, request::Body::new(hdr)))
|
||||||
.map(|x| x.map(|b| Some(U256::from(b.uncles_count()).into())))
|
.map(|x| x.map(|b| Some(U256::from(b.uncles_count()).into())))
|
||||||
.map(|x| x.map_err(err_premature_cancel).boxed())
|
.map(|x| x.map_err(errors::on_demand_cancel).boxed())
|
||||||
.unwrap_or_else(|| future::err(errors::network_disabled()).boxed())
|
.unwrap_or_else(|| future::err(errors::network_disabled()).boxed())
|
||||||
}
|
}
|
||||||
}).boxed()
|
}).boxed()
|
||||||
@ -365,7 +228,7 @@ impl Eth for EthClient {
|
|||||||
fn block_uncles_count_by_number(&self, num: BlockNumber) -> BoxFuture<Option<RpcU256>, Error> {
|
fn block_uncles_count_by_number(&self, num: BlockNumber) -> BoxFuture<Option<RpcU256>, Error> {
|
||||||
let (sync, on_demand) = (self.sync.clone(), self.on_demand.clone());
|
let (sync, on_demand) = (self.sync.clone(), self.on_demand.clone());
|
||||||
|
|
||||||
self.header(num.into()).and_then(move |hdr| {
|
self.fetcher().header(num.into()).and_then(move |hdr| {
|
||||||
let hdr = match hdr {
|
let hdr = match hdr {
|
||||||
None => return future::ok(None).boxed(),
|
None => return future::ok(None).boxed(),
|
||||||
Some(hdr) => hdr,
|
Some(hdr) => hdr,
|
||||||
@ -376,7 +239,7 @@ impl Eth for EthClient {
|
|||||||
} else {
|
} else {
|
||||||
sync.with_context(|ctx| on_demand.block(ctx, request::Body::new(hdr)))
|
sync.with_context(|ctx| on_demand.block(ctx, request::Body::new(hdr)))
|
||||||
.map(|x| x.map(|b| Some(U256::from(b.uncles_count()).into())))
|
.map(|x| x.map(|b| Some(U256::from(b.uncles_count()).into())))
|
||||||
.map(|x| x.map_err(err_premature_cancel).boxed())
|
.map(|x| x.map_err(errors::on_demand_cancel).boxed())
|
||||||
.unwrap_or_else(|| future::err(errors::network_disabled()).boxed())
|
.unwrap_or_else(|| future::err(errors::network_disabled()).boxed())
|
||||||
}
|
}
|
||||||
}).boxed()
|
}).boxed()
|
||||||
@ -411,7 +274,7 @@ impl Eth for EthClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn call(&self, req: CallRequest, num: Trailing<BlockNumber>) -> BoxFuture<Bytes, Error> {
|
fn call(&self, req: CallRequest, num: Trailing<BlockNumber>) -> BoxFuture<Bytes, Error> {
|
||||||
self.proved_execution(req, num).and_then(|res| {
|
self.fetcher().proved_execution(req, num).and_then(|res| {
|
||||||
match res {
|
match res {
|
||||||
Ok(exec) => Ok(exec.output.into()),
|
Ok(exec) => Ok(exec.output.into()),
|
||||||
Err(e) => Err(errors::execution(e)),
|
Err(e) => Err(errors::execution(e)),
|
||||||
@ -421,7 +284,7 @@ impl Eth for EthClient {
|
|||||||
|
|
||||||
fn estimate_gas(&self, req: CallRequest, num: Trailing<BlockNumber>) -> BoxFuture<RpcU256, Error> {
|
fn estimate_gas(&self, req: CallRequest, num: Trailing<BlockNumber>) -> BoxFuture<RpcU256, Error> {
|
||||||
// TODO: binary chop for more accurate estimates.
|
// TODO: binary chop for more accurate estimates.
|
||||||
self.proved_execution(req, num).and_then(|res| {
|
self.fetcher().proved_execution(req, num).and_then(|res| {
|
||||||
match res {
|
match res {
|
||||||
Ok(exec) => Ok((exec.refunded + exec.gas_used).into()),
|
Ok(exec) => Ok((exec.refunded + exec.gas_used).into()),
|
||||||
Err(e) => Err(errors::execution(e)),
|
Err(e) => Err(errors::execution(e)),
|
||||||
|
@ -32,6 +32,7 @@ use jsonrpc_core::Error;
|
|||||||
use jsonrpc_macros::Trailing;
|
use jsonrpc_macros::Trailing;
|
||||||
use v1::helpers::{errors, SigningQueue, SignerService, NetworkSettings};
|
use v1::helpers::{errors, SigningQueue, SignerService, NetworkSettings};
|
||||||
use v1::helpers::dispatch::{LightDispatcher, DEFAULT_MAC};
|
use v1::helpers::dispatch::{LightDispatcher, DEFAULT_MAC};
|
||||||
|
use v1::helpers::light_fetch::LightFetch;
|
||||||
use v1::metadata::Metadata;
|
use v1::metadata::Metadata;
|
||||||
use v1::traits::Parity;
|
use v1::traits::Parity;
|
||||||
use v1::types::{
|
use v1::types::{
|
||||||
@ -40,7 +41,7 @@ use v1::types::{
|
|||||||
TransactionStats, LocalTransactionStatus,
|
TransactionStats, LocalTransactionStatus,
|
||||||
BlockNumber, ConsensusCapability, VersionInfo,
|
BlockNumber, ConsensusCapability, VersionInfo,
|
||||||
OperationsInfo, DappId, ChainStatus,
|
OperationsInfo, DappId, ChainStatus,
|
||||||
AccountInfo, HwAccountInfo,
|
AccountInfo, HwAccountInfo, Header, RichHeader,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Parity implementation for light client.
|
/// Parity implementation for light client.
|
||||||
@ -75,6 +76,16 @@ impl ParityClient {
|
|||||||
dapps_port: dapps_port,
|
dapps_port: dapps_port,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a light blockchain data fetcher.
|
||||||
|
fn fetcher(&self) -> LightFetch {
|
||||||
|
LightFetch {
|
||||||
|
client: self.light_dispatch.client.clone(),
|
||||||
|
on_demand: self.light_dispatch.on_demand.clone(),
|
||||||
|
sync: self.light_dispatch.sync.clone(),
|
||||||
|
cache: self.light_dispatch.cache.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Parity for ParityClient {
|
impl Parity for ParityClient {
|
||||||
@ -342,4 +353,38 @@ impl Parity for ParityClient {
|
|||||||
capability: Capability::Light,
|
capability: Capability::Light,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn block_header(&self, number: Trailing<BlockNumber>) -> BoxFuture<Option<RichHeader>, Error> {
|
||||||
|
use ethcore::encoded;
|
||||||
|
|
||||||
|
let engine = self.light_dispatch.client.engine().clone();
|
||||||
|
let from_encoded = move |encoded: encoded::Header| {
|
||||||
|
let header = encoded.decode();
|
||||||
|
let extra_info = engine.extra_info(&header);
|
||||||
|
RichHeader {
|
||||||
|
inner: Header {
|
||||||
|
hash: Some(header.hash().into()),
|
||||||
|
size: Some(encoded.rlp().as_raw().len().into()),
|
||||||
|
parent_hash: header.parent_hash().clone().into(),
|
||||||
|
uncles_hash: header.uncles_hash().clone().into(),
|
||||||
|
author: header.author().clone().into(),
|
||||||
|
miner: header.author().clone().into(),
|
||||||
|
state_root: header.state_root().clone().into(),
|
||||||
|
transactions_root: header.transactions_root().clone().into(),
|
||||||
|
receipts_root: header.receipts_root().clone().into(),
|
||||||
|
number: Some(header.number().into()),
|
||||||
|
gas_used: header.gas_used().clone().into(),
|
||||||
|
gas_limit: header.gas_limit().clone().into(),
|
||||||
|
logs_bloom: header.log_bloom().clone().into(),
|
||||||
|
timestamp: header.timestamp().into(),
|
||||||
|
difficulty: header.difficulty().clone().into(),
|
||||||
|
seal_fields: header.seal().iter().cloned().map(Into::into).collect(),
|
||||||
|
extra_data: Bytes::new(header.extra_data().clone()),
|
||||||
|
},
|
||||||
|
extra_info: extra_info,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
self.fetcher().header(number.0.into()).map(move |encoded| encoded.map(from_encoded)).boxed()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,7 @@ use crypto::ecies;
|
|||||||
use ethkey::{Brain, Generator};
|
use ethkey::{Brain, Generator};
|
||||||
use ethstore::random_phrase;
|
use ethstore::random_phrase;
|
||||||
use ethsync::{SyncProvider, ManageNetwork};
|
use ethsync::{SyncProvider, ManageNetwork};
|
||||||
|
use ethcore::ids::BlockId;
|
||||||
use ethcore::miner::MinerService;
|
use ethcore::miner::MinerService;
|
||||||
use ethcore::client::{MiningBlockChainClient};
|
use ethcore::client::{MiningBlockChainClient};
|
||||||
use ethcore::mode::Mode;
|
use ethcore::mode::Mode;
|
||||||
@ -47,7 +48,7 @@ use v1::types::{
|
|||||||
TransactionStats, LocalTransactionStatus,
|
TransactionStats, LocalTransactionStatus,
|
||||||
BlockNumber, ConsensusCapability, VersionInfo,
|
BlockNumber, ConsensusCapability, VersionInfo,
|
||||||
OperationsInfo, DappId, ChainStatus,
|
OperationsInfo, DappId, ChainStatus,
|
||||||
AccountInfo, HwAccountInfo,
|
AccountInfo, HwAccountInfo, Header, RichHeader
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Parity implementation.
|
/// Parity implementation.
|
||||||
@ -393,4 +394,38 @@ impl<C, M, S: ?Sized, U> Parity for ParityClient<C, M, S, U> where
|
|||||||
capability: Capability::Full,
|
capability: Capability::Full,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn block_header(&self, number: Trailing<BlockNumber>) -> BoxFuture<Option<RichHeader>, Error> {
|
||||||
|
const EXTRA_INFO_PROOF: &'static str = "Object exists in in blockchain (fetched earlier), extra_info is always available if object exists; qed";
|
||||||
|
|
||||||
|
let client = take_weakf!(self.client);
|
||||||
|
let id: BlockId = number.0.into();
|
||||||
|
let encoded = match client.block_header(id.clone()) {
|
||||||
|
Some(encoded) => encoded,
|
||||||
|
None => return future::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()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,7 @@ use v1::types::{
|
|||||||
TransactionStats, LocalTransactionStatus,
|
TransactionStats, LocalTransactionStatus,
|
||||||
BlockNumber, ConsensusCapability, VersionInfo,
|
BlockNumber, ConsensusCapability, VersionInfo,
|
||||||
OperationsInfo, DappId, ChainStatus,
|
OperationsInfo, DappId, ChainStatus,
|
||||||
AccountInfo, HwAccountInfo,
|
AccountInfo, HwAccountInfo, RichHeader,
|
||||||
};
|
};
|
||||||
|
|
||||||
build_rpc_trait! {
|
build_rpc_trait! {
|
||||||
@ -198,5 +198,10 @@ build_rpc_trait! {
|
|||||||
/// Get node kind info.
|
/// Get node kind info.
|
||||||
#[rpc(name = "parity_nodeKind")]
|
#[rpc(name = "parity_nodeKind")]
|
||||||
fn node_kind(&self) -> Result<::v1::types::NodeKind, Error>;
|
fn node_kind(&self) -> Result<::v1::types::NodeKind, Error>;
|
||||||
|
|
||||||
|
/// Get block header.
|
||||||
|
/// Same as `eth_getBlockByNumber` but without uncles and transactions.
|
||||||
|
#[rpc(async, name = "parity_getBlockHeaderByNumber")]
|
||||||
|
fn block_header(&self, Trailing<BlockNumber>) -> BoxFuture<Option<RichHeader>, Error>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -96,34 +96,90 @@ pub struct Block {
|
|||||||
pub size: Option<U256>,
|
pub size: Option<U256>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Block representation with additional info
|
/// Block header representation.
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
pub struct Header {
|
||||||
|
/// Hash of the block
|
||||||
|
pub hash: Option<H256>,
|
||||||
|
/// Hash of the parent
|
||||||
|
#[serde(rename="parentHash")]
|
||||||
|
pub parent_hash: H256,
|
||||||
|
/// Hash of the uncles
|
||||||
|
#[serde(rename="sha3Uncles")]
|
||||||
|
pub uncles_hash: H256,
|
||||||
|
/// Authors address
|
||||||
|
pub author: H160,
|
||||||
|
// TODO: get rid of this one
|
||||||
|
/// ?
|
||||||
|
pub miner: H160,
|
||||||
|
/// State root hash
|
||||||
|
#[serde(rename="stateRoot")]
|
||||||
|
pub state_root: H256,
|
||||||
|
/// Transactions root hash
|
||||||
|
#[serde(rename="transactionsRoot")]
|
||||||
|
pub transactions_root: H256,
|
||||||
|
/// Transactions receipts root hash
|
||||||
|
#[serde(rename="receiptsRoot")]
|
||||||
|
pub receipts_root: H256,
|
||||||
|
/// Block number
|
||||||
|
pub number: Option<U256>,
|
||||||
|
/// Gas Used
|
||||||
|
#[serde(rename="gasUsed")]
|
||||||
|
pub gas_used: U256,
|
||||||
|
/// Gas Limit
|
||||||
|
#[serde(rename="gasLimit")]
|
||||||
|
pub gas_limit: U256,
|
||||||
|
/// Extra data
|
||||||
|
#[serde(rename="extraData")]
|
||||||
|
pub extra_data: Bytes,
|
||||||
|
/// Logs bloom
|
||||||
|
#[serde(rename="logsBloom")]
|
||||||
|
pub logs_bloom: H2048,
|
||||||
|
/// Timestamp
|
||||||
|
pub timestamp: U256,
|
||||||
|
/// Difficulty
|
||||||
|
pub difficulty: U256,
|
||||||
|
/// Seal fields
|
||||||
|
#[serde(rename="sealFields")]
|
||||||
|
pub seal_fields: Vec<Bytes>,
|
||||||
|
/// Size in bytes
|
||||||
|
pub size: Option<U256>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Block representation with additional info.
|
||||||
|
pub type RichBlock = Rich<Block>;
|
||||||
|
|
||||||
|
/// Header representation with additional info.
|
||||||
|
pub type RichHeader = Rich<Header>;
|
||||||
|
|
||||||
|
/// Value representation with additional info
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct RichBlock {
|
pub struct Rich<T> {
|
||||||
/// Standard block
|
/// Standard value.
|
||||||
pub block: Block,
|
pub inner: T,
|
||||||
/// Engine-specific fields with additional description.
|
/// Engine-specific fields with additional description.
|
||||||
/// Should be included directly to serialized block object.
|
/// Should be included directly to serialized block object.
|
||||||
// TODO [ToDr] #[serde(skip_serializing)]
|
// TODO [ToDr] #[serde(skip_serializing)]
|
||||||
pub extra_info: BTreeMap<String, String>,
|
pub extra_info: BTreeMap<String, String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Deref for RichBlock {
|
impl<T> Deref for Rich<T> {
|
||||||
type Target = Block;
|
type Target = T;
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
&self.block
|
&self.inner
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Serialize for RichBlock {
|
impl<T: Serialize> Serialize for Rich<T> {
|
||||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer {
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer {
|
||||||
use serde_json::{to_value, Value};
|
use serde_json::{to_value, Value};
|
||||||
|
|
||||||
let serialized = (to_value(&self.block), to_value(&self.extra_info));
|
let serialized = (to_value(&self.inner), to_value(&self.extra_info));
|
||||||
if let (Ok(Value::Object(mut block)), Ok(Value::Object(extras))) = serialized {
|
if let (Ok(Value::Object(mut value)), Ok(Value::Object(extras))) = serialized {
|
||||||
// join two objects
|
// join two objects
|
||||||
block.extend(extras);
|
value.extend(extras);
|
||||||
// and serialize
|
// and serialize
|
||||||
block.serialize(serializer)
|
value.serialize(serializer)
|
||||||
} else {
|
} else {
|
||||||
Err(S::Error::custom("Unserializable structures."))
|
Err(S::Error::custom("Unserializable structures."))
|
||||||
}
|
}
|
||||||
@ -135,7 +191,7 @@ mod tests {
|
|||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use serde_json;
|
use serde_json;
|
||||||
use v1::types::{Transaction, H64, H160, H256, H2048, Bytes, U256};
|
use v1::types::{Transaction, H64, H160, H256, H2048, Bytes, U256};
|
||||||
use super::{Block, RichBlock, BlockTransactions};
|
use super::{Block, RichBlock, BlockTransactions, Header, RichHeader};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_serialize_block_transactions() {
|
fn test_serialize_block_transactions() {
|
||||||
@ -174,7 +230,7 @@ mod tests {
|
|||||||
};
|
};
|
||||||
let serialized_block = serde_json::to_string(&block).unwrap();
|
let serialized_block = serde_json::to_string(&block).unwrap();
|
||||||
let rich_block = RichBlock {
|
let rich_block = RichBlock {
|
||||||
block: block,
|
inner: block,
|
||||||
extra_info: map![
|
extra_info: map![
|
||||||
"mixHash".into() => format!("0x{:?}", H256::default()),
|
"mixHash".into() => format!("0x{:?}", H256::default()),
|
||||||
"nonce".into() => format!("0x{:?}", H64::default())
|
"nonce".into() => format!("0x{:?}", H64::default())
|
||||||
@ -212,7 +268,7 @@ mod tests {
|
|||||||
};
|
};
|
||||||
let serialized_block = serde_json::to_string(&block).unwrap();
|
let serialized_block = serde_json::to_string(&block).unwrap();
|
||||||
let rich_block = RichBlock {
|
let rich_block = RichBlock {
|
||||||
block: block,
|
inner: block,
|
||||||
extra_info: map![
|
extra_info: map![
|
||||||
"mixHash".into() => format!("0x{:?}", H256::default()),
|
"mixHash".into() => format!("0x{:?}", H256::default()),
|
||||||
"nonce".into() => format!("0x{:?}", H64::default())
|
"nonce".into() => format!("0x{:?}", H64::default())
|
||||||
@ -223,4 +279,39 @@ mod tests {
|
|||||||
assert_eq!(serialized_block, r#"{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","sha3Uncles":"0x0000000000000000000000000000000000000000000000000000000000000000","author":"0x0000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","stateRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionsRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","receiptsRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","number":"0x0","gasUsed":"0x0","gasLimit":"0x0","extraData":"0x","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","timestamp":"0x0","difficulty":"0x0","totalDifficulty":"0x0","sealFields":["0x","0x"],"uncles":[],"transactions":[],"size":null}"#);
|
assert_eq!(serialized_block, r#"{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","sha3Uncles":"0x0000000000000000000000000000000000000000000000000000000000000000","author":"0x0000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","stateRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionsRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","receiptsRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","number":"0x0","gasUsed":"0x0","gasLimit":"0x0","extraData":"0x","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","timestamp":"0x0","difficulty":"0x0","totalDifficulty":"0x0","sealFields":["0x","0x"],"uncles":[],"transactions":[],"size":null}"#);
|
||||||
assert_eq!(serialized_rich_block, r#"{"author":"0x0000000000000000000000000000000000000000","difficulty":"0x0","extraData":"0x","gasLimit":"0x0","gasUsed":"0x0","hash":"0x0000000000000000000000000000000000000000000000000000000000000000","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000000","number":"0x0","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","receiptsRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","sealFields":["0x","0x"],"sha3Uncles":"0x0000000000000000000000000000000000000000000000000000000000000000","size":null,"stateRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","timestamp":"0x0","totalDifficulty":"0x0","transactions":[],"transactionsRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","uncles":[]}"#);
|
assert_eq!(serialized_rich_block, r#"{"author":"0x0000000000000000000000000000000000000000","difficulty":"0x0","extraData":"0x","gasLimit":"0x0","gasUsed":"0x0","hash":"0x0000000000000000000000000000000000000000000000000000000000000000","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000000","number":"0x0","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","receiptsRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","sealFields":["0x","0x"],"sha3Uncles":"0x0000000000000000000000000000000000000000000000000000000000000000","size":null,"stateRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","timestamp":"0x0","totalDifficulty":"0x0","transactions":[],"transactionsRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","uncles":[]}"#);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_serialize_header() {
|
||||||
|
let header = Header {
|
||||||
|
hash: Some(H256::default()),
|
||||||
|
parent_hash: H256::default(),
|
||||||
|
uncles_hash: H256::default(),
|
||||||
|
author: H160::default(),
|
||||||
|
miner: H160::default(),
|
||||||
|
state_root: H256::default(),
|
||||||
|
transactions_root: H256::default(),
|
||||||
|
receipts_root: H256::default(),
|
||||||
|
number: Some(U256::default()),
|
||||||
|
gas_used: U256::default(),
|
||||||
|
gas_limit: U256::default(),
|
||||||
|
extra_data: Bytes::default(),
|
||||||
|
logs_bloom: H2048::default(),
|
||||||
|
timestamp: U256::default(),
|
||||||
|
difficulty: U256::default(),
|
||||||
|
seal_fields: vec![Bytes::default(), Bytes::default()],
|
||||||
|
size: Some(69.into()),
|
||||||
|
};
|
||||||
|
let serialized_header = serde_json::to_string(&header).unwrap();
|
||||||
|
let rich_header = RichHeader {
|
||||||
|
inner: header,
|
||||||
|
extra_info: map![
|
||||||
|
"mixHash".into() => format!("0x{:?}", H256::default()),
|
||||||
|
"nonce".into() => format!("0x{:?}", H64::default())
|
||||||
|
],
|
||||||
|
};
|
||||||
|
let serialized_rich_header = serde_json::to_string(&rich_header).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(serialized_header, r#"{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","sha3Uncles":"0x0000000000000000000000000000000000000000000000000000000000000000","author":"0x0000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","stateRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionsRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","receiptsRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","number":"0x0","gasUsed":"0x0","gasLimit":"0x0","extraData":"0x","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","timestamp":"0x0","difficulty":"0x0","sealFields":["0x","0x"],"size":"0x45"}"#);
|
||||||
|
assert_eq!(serialized_rich_header, r#"{"author":"0x0000000000000000000000000000000000000000","difficulty":"0x0","extraData":"0x","gasLimit":"0x0","gasUsed":"0x0","hash":"0x0000000000000000000000000000000000000000000000000000000000000000","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000000","number":"0x0","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","receiptsRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","sealFields":["0x","0x"],"sha3Uncles":"0x0000000000000000000000000000000000000000000000000000000000000000","size":"0x45","stateRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","timestamp":"0x0","transactionsRoot":"0x0000000000000000000000000000000000000000000000000000000000000000"}"#);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -45,7 +45,7 @@ mod work;
|
|||||||
|
|
||||||
pub use self::account_info::{AccountInfo, HwAccountInfo};
|
pub use self::account_info::{AccountInfo, HwAccountInfo};
|
||||||
pub use self::bytes::Bytes;
|
pub use self::bytes::Bytes;
|
||||||
pub use self::block::{RichBlock, Block, BlockTransactions};
|
pub use self::block::{RichBlock, Block, BlockTransactions, Header, RichHeader, Rich};
|
||||||
pub use self::block_number::BlockNumber;
|
pub use self::block_number::BlockNumber;
|
||||||
pub use self::call_request::CallRequest;
|
pub use self::call_request::CallRequest;
|
||||||
pub use self::confirmations::{
|
pub use self::confirmations::{
|
||||||
|
Loading…
Reference in New Issue
Block a user