Merge pull request #5419 from paritytech/on-demand-priority

Improve on-demand dispatch and add support for batch requests
This commit is contained in:
Robert Habermeier
2017-05-17 12:28:27 +02:00
committed by GitHub
20 changed files with 1415 additions and 550 deletions

View File

@@ -281,6 +281,7 @@ impl LightDispatcher {
}
let best_header = self.client.best_block_header();
let account_start_nonce = self.client.engine().account_start_nonce();
let nonce_future = self.sync.with_context(|ctx| self.on_demand.account(ctx, request::Account {
header: best_header,
address: addr,
@@ -288,7 +289,7 @@ impl LightDispatcher {
match nonce_future {
Some(x) =>
x.map(|acc| acc.nonce)
x.map(move |acc| acc.map_or(account_start_nonce, |acc| acc.nonce))
.map_err(|_| errors::no_light_peers())
.boxed(),
None => future::err(errors::network_disabled()).boxed()

View File

@@ -57,16 +57,16 @@ 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> {
pub fn header(&self, id: BlockId) -> BoxFuture<encoded::Header, Error> {
if let Some(h) = self.client.block_header(id) {
return future::ok(Some(h)).boxed()
return future::ok(h).boxed()
}
let maybe_future = match id {
BlockId::Number(n) => {
let cht_root = cht::block_to_cht_number(n).and_then(|cn| self.client.cht_root(cn as usize));
match cht_root {
None => return future::ok(None).boxed(),
None => return future::err(errors::unknown_block()).boxed(),
Some(root) => {
let req = request::HeaderProof::new(n, root)
.expect("only fails for 0; client always stores genesis; client already queried; qed");
@@ -82,7 +82,7 @@ impl LightFetch {
Some(fut) => fut.map_err(errors::on_demand_cancel).boxed(),
None => future::err(errors::network_disabled()).boxed(),
}
}).map(Some).boxed()
}).boxed()
})
}
}
@@ -91,7 +91,7 @@ impl LightFetch {
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)),
Ok(h) => Ok(h),
Err(e) => Err(errors::on_demand_cancel(e)),
}))
.boxed()
@@ -106,22 +106,21 @@ impl LightFetch {
}
}
// Get account info at a given block. `None` signifies no such account existing.
/// helper for getting account info at a given block.
/// `None` indicates the account doesn't exist at the given block.
pub fn account(&self, address: Address, id: BlockId) -> BoxFuture<Option<BasicAccount>, Error> {
let (sync, on_demand) = (self.sync.clone(), self.on_demand.clone());
self.header(id).and_then(move |header| {
let header = match header {
None => return future::ok(None).boxed(),
Some(hdr) => hdr,
};
sync.with_context(|ctx| on_demand.account(ctx, request::Account {
let maybe_fut = sync.with_context(|ctx| on_demand.account(ctx, request::Account {
header: header,
address: address,
}).map(Some))
.map(|x| x.map_err(errors::on_demand_cancel).boxed())
.unwrap_or_else(|| future::err(errors::network_disabled()).boxed())
}));
match maybe_fut {
Some(fut) => fut.map_err(errors::on_demand_cancel).boxed(),
None => future::err(errors::network_disabled()).boxed(),
}
}).boxed()
}
@@ -176,10 +175,11 @@ impl LightFetch {
}).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),
let env_info = match client.env_info(id) {
Some(env_info) => env_info,
_ => return future::err(errors::unknown_block()).boxed(),
};
let request = request::TransactionProof {
tx: tx,
header: hdr,
@@ -198,18 +198,13 @@ impl LightFetch {
}).boxed()
}
/// Get a block.
pub fn block(&self, id: BlockId) -> BoxFuture<Option<encoded::Block>, Error> {
/// get a block itself. fails on unknown block ID.
pub fn block(&self, id: BlockId) -> BoxFuture<encoded::Block, Error> {
let (on_demand, sync) = (self.on_demand.clone(), self.sync.clone());
self.header(id).and_then(move |hdr| {
let req = match hdr {
Some(hdr) => request::Body::new(hdr),
None => return future::ok(None).boxed(),
};
self.header(id).map(request::Body::new).and_then(move |req| {
match sync.with_context(move |ctx| on_demand.block(ctx, req)) {
Some(fut) => fut.map_err(errors::on_demand_cancel).map(Some).boxed(),
Some(fut) => fut.map_err(errors::on_demand_cancel).boxed(),
None => future::err(errors::network_disabled()).boxed(),
}
}).boxed()

View File

@@ -115,12 +115,11 @@ impl EthClient {
on_demand: self.on_demand.clone(),
sync: self.sync.clone(),
cache: self.cache.clone(),
}
}
// get a "rich" block structure
fn rich_block(&self, id: BlockId, include_txs: bool) -> BoxFuture<Option<RichBlock>, Error> {
// get a "rich" block structure. Fails on unknown block.
fn rich_block(&self, id: BlockId, include_txs: bool) -> BoxFuture<RichBlock, Error> {
let (on_demand, sync) = (self.on_demand.clone(), self.sync.clone());
let (client, engine) = (self.client.clone(), self.client.engine().clone());
let eip86_transition = self.client.eip86_transition();
@@ -160,49 +159,45 @@ impl EthClient {
};
// get the block itself.
self.fetcher().block(id).and_then(move |block| match block {
None => return future::ok(None).boxed(),
Some(block) => {
// then fetch the total difficulty (this is much easier after getting the block).
match client.score(id) {
Some(score) => future::ok(fill_rich(block, Some(score))).map(Some).boxed(),
None => {
// make a CHT request to fetch the chain score.
let req = cht::block_to_cht_number(block.number())
.and_then(|num| client.cht_root(num as usize))
.and_then(|root| request::HeaderProof::new(block.number(), root));
self.fetcher().block(id).and_then(move |block| {
// then fetch the total difficulty (this is much easier after getting the block).
match client.score(id) {
Some(score) => future::ok(fill_rich(block, Some(score))).boxed(),
None => {
// make a CHT request to fetch the chain score.
let req = cht::block_to_cht_number(block.number())
.and_then(|num| client.cht_root(num as usize))
.and_then(|root| request::HeaderProof::new(block.number(), root));
let req = match req {
Some(req) => req,
None => {
// somehow the genesis block slipped past other checks.
// return it now.
let score = client.block_header(BlockId::Number(0))
.expect("genesis always stored; qed")
.difficulty();
let req = match req {
Some(req) => req,
None => {
// somehow the genesis block slipped past other checks.
// return it now.
let score = client.block_header(BlockId::Number(0))
.expect("genesis always stored; qed")
.difficulty();
return future::ok(fill_rich(block, Some(score))).map(Some).boxed()
}
};
// three possible outcomes:
// - network is down.
// - we get a score, but our hash is non-canonical.
// - we get ascore, and our hash is canonical.
let maybe_fut = sync.with_context(move |ctx| on_demand.hash_and_score_by_number(ctx, req));
match maybe_fut {
Some(fut) => fut.map(move |(hash, score)| {
let score = if hash == block.hash() {
Some(score)
} else {
None
};
Some(fill_rich(block, score))
}).map_err(errors::on_demand_cancel).boxed(),
None => return future::err(errors::network_disabled()).boxed(),
return future::ok(fill_rich(block, Some(score))).boxed()
}
};
// three possible outcomes:
// - network is down.
// - we get a score, but our hash is non-canonical.
// - we get a score, and our hash is canonical.
let maybe_fut = sync.with_context(move |ctx| on_demand.hash_and_score_by_number(ctx, req));
match maybe_fut {
Some(fut) => fut.map(move |(hash, score)| {
let score = if hash == block.hash() {
Some(score)
} else {
None
};
fill_rich(block, score)
}).map_err(errors::on_demand_cancel).boxed(),
None => return future::err(errors::network_disabled()).boxed(),
}
}
}
@@ -281,11 +276,11 @@ impl Eth for EthClient {
}
fn block_by_hash(&self, hash: RpcH256, include_txs: bool) -> BoxFuture<Option<RichBlock>, Error> {
self.rich_block(BlockId::Hash(hash.into()), include_txs)
self.rich_block(BlockId::Hash(hash.into()), include_txs).map(Some).boxed()
}
fn block_by_number(&self, num: BlockNumber, include_txs: bool) -> BoxFuture<Option<RichBlock>, Error> {
self.rich_block(num.into(), include_txs)
self.rich_block(num.into(), include_txs).map(Some).boxed()
}
fn transaction_count(&self, address: RpcH160, num: Trailing<BlockNumber>) -> BoxFuture<RpcU256, Error> {
@@ -297,11 +292,6 @@ impl Eth for EthClient {
let (sync, on_demand) = (self.sync.clone(), self.on_demand.clone());
self.fetcher().header(BlockId::Hash(hash.into())).and_then(move |hdr| {
let hdr = match hdr {
None => return future::ok(None).boxed(),
Some(hdr) => hdr,
};
if hdr.transactions_root() == SHA3_NULL_RLP {
future::ok(Some(U256::from(0).into())).boxed()
} else {
@@ -317,11 +307,6 @@ impl Eth for EthClient {
let (sync, on_demand) = (self.sync.clone(), self.on_demand.clone());
self.fetcher().header(num.into()).and_then(move |hdr| {
let hdr = match hdr {
None => return future::ok(None).boxed(),
Some(hdr) => hdr,
};
if hdr.transactions_root() == SHA3_NULL_RLP {
future::ok(Some(U256::from(0).into())).boxed()
} else {
@@ -337,11 +322,6 @@ impl Eth for EthClient {
let (sync, on_demand) = (self.sync.clone(), self.on_demand.clone());
self.fetcher().header(BlockId::Hash(hash.into())).and_then(move |hdr| {
let hdr = match hdr {
None => return future::ok(None).boxed(),
Some(hdr) => hdr,
};
if hdr.uncles_hash() == SHA3_EMPTY_LIST_RLP {
future::ok(Some(U256::from(0).into())).boxed()
} else {
@@ -357,11 +337,6 @@ impl Eth for EthClient {
let (sync, on_demand) = (self.sync.clone(), self.on_demand.clone());
self.fetcher().header(num.into()).and_then(move |hdr| {
let hdr = match hdr {
None => return future::ok(None).boxed(),
Some(hdr) => hdr,
};
if hdr.uncles_hash() == SHA3_EMPTY_LIST_RLP {
future::ok(Some(U256::from(0).into())).boxed()
} else {

View File

@@ -360,7 +360,7 @@ impl Parity for ParityClient {
})
}
fn block_header(&self, number: Trailing<BlockNumber>) -> BoxFuture<Option<RichHeader>, Error> {
fn block_header(&self, number: Trailing<BlockNumber>) -> BoxFuture<RichHeader, Error> {
use ethcore::encoded;
let engine = self.light_dispatch.client.engine().clone();
@@ -391,7 +391,7 @@ impl Parity for ParityClient {
}
};
self.fetcher().header(number.0.into()).map(move |encoded| encoded.map(from_encoded)).boxed()
self.fetcher().header(number.0.into()).map(from_encoded).boxed()
}
fn ipfs_cid(&self, content: Bytes) -> Result<String, Error> {

View File

@@ -400,17 +400,17 @@ impl<C, M, S: ?Sized, U> Parity for ParityClient<C, M, S, U> where
})
}
fn block_header(&self, number: Trailing<BlockNumber>) -> BoxFuture<Option<RichHeader>, Error> {
fn block_header(&self, number: Trailing<BlockNumber>) -> BoxFuture<RichHeader, Error> {
const EXTRA_INFO_PROOF: &'static str = "Object exists in in blockchain (fetched earlier), extra_info is always available if object exists; qed";
let client = take_weakf!(self.client);
let id: BlockId = number.0.into();
let encoded = match client.block_header(id.clone()) {
Some(encoded) => encoded,
None => return future::ok(None).boxed(),
None => return future::err(errors::unknown_block()).boxed(),
};
future::ok(Some(RichHeader {
future::ok(RichHeader {
inner: Header {
hash: Some(encoded.hash().into()),
size: Some(encoded.rlp().as_raw().len().into()),
@@ -431,7 +431,7 @@ impl<C, M, S: ?Sized, U> Parity for ParityClient<C, M, S, U> where
extra_data: Bytes::new(encoded.extra_data()),
},
extra_info: client.block_extra_info(id).expect(EXTRA_INFO_PROOF),
})).boxed()
}).boxed()
}
fn ipfs_cid(&self, content: Bytes) -> Result<String, Error> {

View File

@@ -202,7 +202,7 @@ build_rpc_trait! {
/// 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>;
fn block_header(&self, Trailing<BlockNumber>) -> BoxFuture<RichHeader, Error>;
/// Get IPFS CIDv0 given protobuf encoded bytes.
#[rpc(name = "parity_cidV0")]