Merge pull request #5419 from paritytech/on-demand-priority
Improve on-demand dispatch and add support for batch requests
This commit is contained in:
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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")]
|
||||
|
||||
Reference in New Issue
Block a user