use cache in on-demand again
This commit is contained in:
parent
574cfae470
commit
d19232a848
@ -27,11 +27,10 @@ use std::sync::Arc;
|
|||||||
use ethcore::basic_account::BasicAccount;
|
use ethcore::basic_account::BasicAccount;
|
||||||
use ethcore::encoded;
|
use ethcore::encoded;
|
||||||
use ethcore::receipt::Receipt;
|
use ethcore::receipt::Receipt;
|
||||||
use ethcore::state::ProvedExecution;
|
|
||||||
use ethcore::executed::{Executed, ExecutionError};
|
use ethcore::executed::{Executed, ExecutionError};
|
||||||
|
|
||||||
use futures::{Async, Poll, Future};
|
use futures::{future, Async, Poll, Future, BoxFuture};
|
||||||
use futures::sync::oneshot::{self, Sender, Receiver};
|
use futures::sync::oneshot::{self, Sender, Receiver, Canceled};
|
||||||
use network::PeerId;
|
use network::PeerId;
|
||||||
use rlp::RlpStream;
|
use rlp::RlpStream;
|
||||||
use util::{Bytes, RwLock, Mutex, U256, H256};
|
use util::{Bytes, RwLock, Mutex, U256, H256};
|
||||||
@ -39,12 +38,15 @@ use util::sha3::{SHA3_NULL_RLP, SHA3_EMPTY, SHA3_EMPTY_LIST_RLP};
|
|||||||
|
|
||||||
use net::{self, Handler, Status, Capabilities, Announcement, EventContext, BasicContext, ReqId};
|
use net::{self, Handler, Status, Capabilities, Announcement, EventContext, BasicContext, ReqId};
|
||||||
use cache::Cache;
|
use cache::Cache;
|
||||||
use request::{self as basic_request, Request as NetworkRequest, Response as NetworkResponse};
|
use request::{self as basic_request, Request as NetworkRequest};
|
||||||
|
|
||||||
pub mod request;
|
pub mod request;
|
||||||
|
|
||||||
pub use self::request::{CheckedRequest, Request, Response};
|
pub use self::request::{CheckedRequest, Request, Response};
|
||||||
|
|
||||||
|
/// The result of execution
|
||||||
|
pub type ExecutionResult = Result<Executed, ExecutionError>;
|
||||||
|
|
||||||
// relevant peer info.
|
// relevant peer info.
|
||||||
struct Peer {
|
struct Peer {
|
||||||
status: Status,
|
status: Status,
|
||||||
@ -62,13 +64,6 @@ impl Peer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Which portions of a CHT proof should be sent.
|
|
||||||
enum ChtProofSender {
|
|
||||||
Both(Sender<(H256, U256)>),
|
|
||||||
Hash(Sender<H256>),
|
|
||||||
ChainScore(Sender<U256>),
|
|
||||||
}
|
|
||||||
|
|
||||||
// Attempted request info and sender to put received value.
|
// Attempted request info and sender to put received value.
|
||||||
struct Pending {
|
struct Pending {
|
||||||
requests: basic_request::Requests<CheckedRequest>,
|
requests: basic_request::Requests<CheckedRequest>,
|
||||||
@ -127,7 +122,7 @@ pub struct OnDemand {
|
|||||||
cache: Arc<Mutex<Cache>>,
|
cache: Arc<Mutex<Cache>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
const RECEIVER_IN_SCOPE: &'static str = "Receiver is still in scope, so it's not dropped; qed";
|
const RESPONSES_MATCH: &'static str = "N requests always leads to N responses; qed";
|
||||||
|
|
||||||
impl OnDemand {
|
impl OnDemand {
|
||||||
/// Create a new `OnDemand` service with the given cache.
|
/// Create a new `OnDemand` service with the given cache.
|
||||||
@ -140,151 +135,191 @@ impl OnDemand {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// /// Request a header's hash by block number and CHT root hash.
|
/// Request a header's hash by block number and CHT root hash.
|
||||||
// /// Returns the hash.
|
/// Returns the hash.
|
||||||
// pub fn hash_by_number(&self, ctx: &BasicContext, req: request::HeaderProof) -> Receiver<H256> {
|
pub fn hash_by_number(&self, ctx: &BasicContext, req: request::HeaderProof) -> BoxFuture<H256, Canceled> {
|
||||||
// let (sender, receiver) = oneshot::channel();
|
let cached = {
|
||||||
// let cached = {
|
let mut cache = self.cache.lock();
|
||||||
// let mut cache = self.cache.lock();
|
cache.block_hash(&req.num())
|
||||||
// cache.block_hash(&req.num())
|
};
|
||||||
// };
|
|
||||||
|
|
||||||
// match cached {
|
match cached {
|
||||||
// Some(hash) => sender.send(hash).expect(RECEIVER_IN_SCOPE),
|
Some(hash) => future::ok(hash).boxed(),
|
||||||
// None => self.dispatch(ctx, Pending::HeaderProof(req, ChtProofSender::Hash(sender))),
|
None => {
|
||||||
// }
|
self.make_requests(ctx, vec![Request::HeaderProof(req)])
|
||||||
// receiver
|
.expect("request given fully fleshed out; qed")
|
||||||
// }
|
.map(|responses| match responses[0] {
|
||||||
|
Response::HeaderProof(ref hash, _) => *hash,
|
||||||
|
_ => panic!("header proof request leads to header proof response; qed")
|
||||||
|
})
|
||||||
|
.boxed()
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// /// Request a canonical block's chain score.
|
/// Request a canonical block's chain score.
|
||||||
// /// Returns the chain score.
|
/// Returns the chain score.
|
||||||
// pub fn chain_score_by_number(&self, ctx: &BasicContext, req: request::HeaderProof) -> Receiver<U256> {
|
pub fn chain_score_by_number(&self, ctx: &BasicContext, req: request::HeaderProof) -> BoxFuture<U256, Canceled> {
|
||||||
// let (sender, receiver) = oneshot::channel();
|
let cached = {
|
||||||
// let cached = {
|
let mut cache = self.cache.lock();
|
||||||
// let mut cache = self.cache.lock();
|
cache.block_hash(&req.num()).and_then(|hash| cache.chain_score(&hash))
|
||||||
// cache.block_hash(&req.num()).and_then(|hash| cache.chain_score(&hash))
|
};
|
||||||
// };
|
|
||||||
|
|
||||||
// match cached {
|
match cached {
|
||||||
// Some(score) => sender.send(score).expect(RECEIVER_IN_SCOPE),
|
Some(score) => future::ok(score).boxed(),
|
||||||
// None => self.dispatch(ctx, Pending::HeaderProof(req, ChtProofSender::ChainScore(sender))),
|
None => {
|
||||||
// }
|
self.make_requests(ctx, vec![Request::HeaderProof(req)])
|
||||||
|
.expect("request given fully fleshed out; qed")
|
||||||
|
.map(|responses| match responses[0] {
|
||||||
|
Response::HeaderProof(_, ref score) => *score,
|
||||||
|
_ => panic!("header proof request leads to header proof response; qed")
|
||||||
|
})
|
||||||
|
.boxed()
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// receiver
|
/// Request a canonical block's hash and chain score by number.
|
||||||
// }
|
/// Returns the hash and chain score.
|
||||||
|
pub fn hash_and_score_by_number(&self, ctx: &BasicContext, req: request::HeaderProof) -> BoxFuture<(H256, U256), Canceled> {
|
||||||
|
let cached = {
|
||||||
|
let mut cache = self.cache.lock();
|
||||||
|
let hash = cache.block_hash(&req.num());
|
||||||
|
(
|
||||||
|
hash.clone(),
|
||||||
|
hash.and_then(|hash| cache.chain_score(&hash)),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
// /// Request a canonical block's hash and chain score by number.
|
match cached {
|
||||||
// /// Returns the hash and chain score.
|
(Some(hash), Some(score)) => future::ok((hash, score)).boxed(),
|
||||||
// pub fn hash_and_score_by_number(&self, ctx: &BasicContext, req: request::HeaderProof) -> Receiver<(H256, U256)> {
|
_ => {
|
||||||
// let (sender, receiver) = oneshot::channel();
|
self.make_requests(ctx, vec![Request::HeaderProof(req)])
|
||||||
// let cached = {
|
.expect("request given fully fleshed out; qed")
|
||||||
// let mut cache = self.cache.lock();
|
.map(|responses| match responses[0] {
|
||||||
// let hash = cache.block_hash(&req.num());
|
Response::HeaderProof(ref hash, ref score) => (*hash, *score),
|
||||||
// (
|
_ => panic!("header proof request leads to header proof response; qed")
|
||||||
// hash.clone(),
|
})
|
||||||
// hash.and_then(|hash| cache.chain_score(&hash)),
|
.boxed()
|
||||||
// )
|
},
|
||||||
// };
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// match cached {
|
/// Request a header by hash. This is less accurate than by-number because we don't know
|
||||||
// (Some(hash), Some(score)) => sender.send((hash, score)).expect(RECEIVER_IN_SCOPE),
|
/// where in the chain this header lies, and therefore can't find a peer who is supposed to have
|
||||||
// _ => self.dispatch(ctx, Pending::HeaderProof(req, ChtProofSender::Both(sender))),
|
/// it as easily.
|
||||||
// }
|
pub fn header_by_hash(&self, ctx: &BasicContext, req: request::HeaderByHash) -> BoxFuture<encoded::Header, Canceled> {
|
||||||
|
match { self.cache.lock().block_header(&req.0) } {
|
||||||
|
Some(hdr) => future::ok(hdr).boxed(),
|
||||||
|
None => {
|
||||||
|
self.make_requests(ctx, vec![Request::HeaderByHash(req)])
|
||||||
|
.expect("request given fully fleshed out; qed")
|
||||||
|
.map(|mut responses| match responses.pop().expect(RESPONSES_MATCH) {
|
||||||
|
Response::HeaderByHash(header) => header,
|
||||||
|
_ => panic!("header request leads to header response; qed")
|
||||||
|
})
|
||||||
|
.boxed()
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// receiver
|
/// Request a block, given its header. Block bodies are requestable by hash only,
|
||||||
// }
|
/// and the header is required anyway to verify and complete the block body
|
||||||
|
/// -- this just doesn't obscure the network query.
|
||||||
|
pub fn block(&self, ctx: &BasicContext, req: request::Body) -> BoxFuture<encoded::Block, Canceled> {
|
||||||
|
// fast path for empty body.
|
||||||
|
if req.header.transactions_root() == SHA3_NULL_RLP && req.header.uncles_hash() == SHA3_EMPTY_LIST_RLP {
|
||||||
|
let mut stream = RlpStream::new_list(3);
|
||||||
|
stream.append_raw(&req.header.into_inner(), 1);
|
||||||
|
stream.begin_list(0);
|
||||||
|
stream.begin_list(0);
|
||||||
|
|
||||||
// /// Request a header by hash. This is less accurate than by-number because we don't know
|
future::ok(encoded::Block::new(stream.out())).boxed()
|
||||||
// /// where in the chain this header lies, and therefore can't find a peer who is supposed to have
|
} else {
|
||||||
// /// it as easily.
|
match { self.cache.lock().block_body(&req.hash) } {
|
||||||
// pub fn header_by_hash(&self, ctx: &BasicContext, req: request::HeaderByHash) -> Receiver<encoded::Header> {
|
Some(body) => {
|
||||||
// let (sender, receiver) = oneshot::channel();
|
let mut stream = RlpStream::new_list(3);
|
||||||
// match { self.cache.lock().block_header(&req.0) } {
|
let body = body.rlp();
|
||||||
// Some(hdr) => sender.send(hdr).expect(RECEIVER_IN_SCOPE),
|
stream.append_raw(&req.header.into_inner(), 1);
|
||||||
// None => self.dispatch(ctx, Pending::HeaderByHash(req, sender)),
|
stream.append_raw(&body.at(0).as_raw(), 1);
|
||||||
// }
|
stream.append_raw(&body.at(1).as_raw(), 1);
|
||||||
// receiver
|
|
||||||
// }
|
|
||||||
|
|
||||||
// /// Request a block, given its header. Block bodies are requestable by hash only,
|
future::ok(encoded::Block::new(stream.out())).boxed()
|
||||||
// /// and the header is required anyway to verify and complete the block body
|
}
|
||||||
// /// -- this just doesn't obscure the network query.
|
None => {
|
||||||
// pub fn block(&self, ctx: &BasicContext, req: request::Body) -> Receiver<encoded::Block> {
|
self.make_requests(ctx, vec![Request::Body(req)])
|
||||||
// let (sender, receiver) = oneshot::channel();
|
.expect("request given fully fleshed out; qed")
|
||||||
|
.map(|mut responses| match responses.pop().expect(RESPONSES_MATCH) {
|
||||||
|
Response::Body(body) => body,
|
||||||
|
_ => panic!("body request leads to body response; qed")
|
||||||
|
})
|
||||||
|
.boxed()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// // fast path for empty body.
|
/// Request the receipts for a block. The header serves two purposes:
|
||||||
// if req.header.transactions_root() == SHA3_NULL_RLP && req.header.uncles_hash() == SHA3_EMPTY_LIST_RLP {
|
/// provide the block hash to fetch receipts for, and for verification of the receipts root.
|
||||||
// let mut stream = RlpStream::new_list(3);
|
pub fn block_receipts(&self, ctx: &BasicContext, req: request::BlockReceipts) -> BoxFuture<Vec<Receipt>, Canceled> {
|
||||||
// stream.append_raw(&req.header.into_inner(), 1);
|
// fast path for empty receipts.
|
||||||
// stream.begin_list(0);
|
if req.0.receipts_root() == SHA3_NULL_RLP {
|
||||||
// stream.begin_list(0);
|
return future::ok(Vec::new()).boxed()
|
||||||
|
}
|
||||||
|
|
||||||
// sender.send(encoded::Block::new(stream.out())).expect(RECEIVER_IN_SCOPE);
|
match { self.cache.lock().block_receipts(&req.0.hash()) } {
|
||||||
// } else {
|
Some(receipts) => future::ok(receipts).boxed(),
|
||||||
// match { self.cache.lock().block_body(&req.hash) } {
|
None => {
|
||||||
// Some(body) => {
|
self.make_requests(ctx, vec![Request::Receipts(req)])
|
||||||
// let mut stream = RlpStream::new_list(3);
|
.expect("request given fully fleshed out; qed")
|
||||||
// let body = body.rlp();
|
.map(|mut responses| match responses.pop().expect(RESPONSES_MATCH) {
|
||||||
// stream.append_raw(&req.header.into_inner(), 1);
|
Response::Receipts(receipts) => receipts,
|
||||||
// stream.append_raw(&body.at(0).as_raw(), 1);
|
_ => panic!("receipts request leads to receipts response; qed")
|
||||||
// stream.append_raw(&body.at(1).as_raw(), 1);
|
})
|
||||||
|
.boxed()
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// sender.send(encoded::Block::new(stream.out())).expect(RECEIVER_IN_SCOPE);
|
/// Request an account by address and block header -- which gives a hash to query and a state root
|
||||||
// }
|
/// to verify against.
|
||||||
// None => self.dispatch(ctx, Pending::Block(req, sender)),
|
/// `None` here means that no account by the queried key exists in the queried state.
|
||||||
// }
|
pub fn account(&self, ctx: &BasicContext, req: request::Account) -> BoxFuture<Option<BasicAccount>, Canceled> {
|
||||||
// }
|
self.make_requests(ctx, vec![Request::Account(req)])
|
||||||
// receiver
|
.expect("request given fully fleshed out; qed")
|
||||||
// }
|
.map(|mut responses| match responses.pop().expect(RESPONSES_MATCH) {
|
||||||
|
Response::Account(account) => account,
|
||||||
|
_ => panic!("account request leads to account response; qed")
|
||||||
|
})
|
||||||
|
.boxed()
|
||||||
|
}
|
||||||
|
|
||||||
// /// Request the receipts for a block. The header serves two purposes:
|
/// Request code by address, known code hash, and block header.
|
||||||
// /// provide the block hash to fetch receipts for, and for verification of the receipts root.
|
pub fn code(&self, ctx: &BasicContext, req: request::Code) -> BoxFuture<Bytes, Canceled> {
|
||||||
// pub fn block_receipts(&self, ctx: &BasicContext, req: request::BlockReceipts) -> Receiver<Vec<Receipt>> {
|
// fast path for no code.
|
||||||
// let (sender, receiver) = oneshot::channel();
|
if req.code_hash == SHA3_EMPTY {
|
||||||
|
future::ok(Vec::new()).boxed()
|
||||||
|
} else {
|
||||||
|
self.make_requests(ctx, vec![Request::Code(req)])
|
||||||
|
.expect("request given fully fleshed out; qed")
|
||||||
|
.map(|mut responses| match responses.pop().expect(RESPONSES_MATCH) {
|
||||||
|
Response::Code(code) => code,
|
||||||
|
_ => panic!("code request leads to code response; qed")
|
||||||
|
})
|
||||||
|
.boxed()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// // fast path for empty receipts.
|
/// Request proof-of-execution for a transaction.
|
||||||
// if req.0.receipts_root() == SHA3_NULL_RLP {
|
pub fn transaction_proof(&self, ctx: &BasicContext, req: request::TransactionProof) -> BoxFuture<ExecutionResult, Canceled> {
|
||||||
// sender.send(Vec::new()).expect(RECEIVER_IN_SCOPE);
|
self.make_requests(ctx, vec![Request::Execution(req)])
|
||||||
// } else {
|
.expect("request given fully fleshed out; qed")
|
||||||
// match { self.cache.lock().block_receipts(&req.0.hash()) } {
|
.map(|mut responses| match responses.pop().expect(RESPONSES_MATCH) {
|
||||||
// Some(receipts) => sender.send(receipts).expect(RECEIVER_IN_SCOPE),
|
Response::Execution(execution) => execution,
|
||||||
// None => self.dispatch(ctx, Pending::BlockReceipts(req, sender)),
|
_ => panic!("execution request leads to execution response; qed")
|
||||||
// }
|
})
|
||||||
// }
|
.boxed()
|
||||||
|
}
|
||||||
// receiver
|
|
||||||
// }
|
|
||||||
|
|
||||||
// /// Request an account by address and block header -- which gives a hash to query and a state root
|
|
||||||
// /// to verify against.
|
|
||||||
// pub fn account(&self, ctx: &BasicContext, req: request::Account) -> Receiver<BasicAccount> {
|
|
||||||
// let (sender, receiver) = oneshot::channel();
|
|
||||||
// self.dispatch(ctx, Pending::Account(req, sender));
|
|
||||||
// receiver
|
|
||||||
// }
|
|
||||||
|
|
||||||
// /// Request code by address, known code hash, and block header.
|
|
||||||
// pub fn code(&self, ctx: &BasicContext, req: request::Code) -> Receiver<Bytes> {
|
|
||||||
// let (sender, receiver) = oneshot::channel();
|
|
||||||
|
|
||||||
// // fast path for no code.
|
|
||||||
// if req.code_hash == SHA3_EMPTY {
|
|
||||||
// sender.send(Vec::new()).expect(RECEIVER_IN_SCOPE)
|
|
||||||
// } else {
|
|
||||||
// self.dispatch(ctx, Pending::Code(req, sender));
|
|
||||||
// }
|
|
||||||
|
|
||||||
// receiver
|
|
||||||
// }
|
|
||||||
|
|
||||||
// /// Request proof-of-execution for a transaction.
|
|
||||||
// pub fn transaction_proof(&self, ctx: &BasicContext, req: request::TransactionProof) -> Receiver<Result<Executed, ExecutionError>> {
|
|
||||||
// let (sender, receiver) = oneshot::channel();
|
|
||||||
|
|
||||||
// self.dispatch(ctx, Pending::TxProof(req, sender));
|
|
||||||
|
|
||||||
// receiver
|
|
||||||
// }
|
|
||||||
|
|
||||||
/// Submit a batch of requests.
|
/// Submit a batch of requests.
|
||||||
///
|
///
|
||||||
@ -295,6 +330,11 @@ impl OnDemand {
|
|||||||
{
|
{
|
||||||
let (sender, receiver) = oneshot::channel();
|
let (sender, receiver) = oneshot::channel();
|
||||||
|
|
||||||
|
if requests.is_empty() {
|
||||||
|
assert!(sender.send(Vec::new()).is_ok(), "receiver still in scope; qed");
|
||||||
|
return Ok(receiver);
|
||||||
|
}
|
||||||
|
|
||||||
let mut builder = basic_request::RequestBuilder::default();
|
let mut builder = basic_request::RequestBuilder::default();
|
||||||
|
|
||||||
let responses = Vec::with_capacity(requests.len());
|
let responses = Vec::with_capacity(requests.len());
|
||||||
@ -314,6 +354,8 @@ impl OnDemand {
|
|||||||
sender: sender,
|
sender: sender,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
self.dispatch_pending(ctx);
|
||||||
|
|
||||||
Ok(receiver)
|
Ok(receiver)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -350,11 +392,13 @@ impl OnDemand {
|
|||||||
let peers = self.peers.read();
|
let peers = self.peers.read();
|
||||||
*pending = ::std::mem::replace(&mut *pending, Vec::new()).into_iter()
|
*pending = ::std::mem::replace(&mut *pending, Vec::new()).into_iter()
|
||||||
.filter_map(|mut pending| match check_hangup(&mut pending.sender) {
|
.filter_map(|mut pending| match check_hangup(&mut pending.sender) {
|
||||||
true => Some(pending),
|
false => Some(pending),
|
||||||
false => None,
|
true => None,
|
||||||
})
|
})
|
||||||
.filter_map(|pending| {
|
.filter_map(|pending| {
|
||||||
for (peer_id, peer) in peers.iter() { // .shuffle?
|
for (peer_id, peer) in peers.iter() { // .shuffle?
|
||||||
|
// TODO: see which requests can be answered by the cache?
|
||||||
|
|
||||||
if !peer.can_fulfill(&pending.required_capabilities) {
|
if !peer.can_fulfill(&pending.required_capabilities) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -412,19 +456,20 @@ impl Handler for OnDemand {
|
|||||||
fn on_responses(&self, ctx: &EventContext, req_id: ReqId, responses: &[basic_request::Response]) {
|
fn on_responses(&self, ctx: &EventContext, req_id: ReqId, responses: &[basic_request::Response]) {
|
||||||
use request::IncompleteRequest;
|
use request::IncompleteRequest;
|
||||||
|
|
||||||
let peer = ctx.peer();
|
|
||||||
let mut pending = match self.in_transit.write().remove(&req_id) {
|
let mut pending = match self.in_transit.write().remove(&req_id) {
|
||||||
Some(req) => req,
|
Some(req) => req,
|
||||||
None => return,
|
None => return,
|
||||||
};
|
};
|
||||||
|
|
||||||
// for each incoming response
|
// for each incoming response
|
||||||
// 1. ensure verification data filled.
|
// 1. ensure verification data filled. (still TODO since on_demand doesn't use back-references yet)
|
||||||
// 2. pending.requests.supply_response
|
// 2. pending.requests.supply_response
|
||||||
// 3. if extracted on-demand response
|
// 3. if extracted on-demand response
|
||||||
for response in responses {
|
for response in responses {
|
||||||
match pending.requests.supply_response(response) {
|
match pending.requests.supply_response(&*self.cache, response) {
|
||||||
Ok(response) => pending.responses.push(response),
|
Ok(response) => {
|
||||||
|
pending.responses.push(response)
|
||||||
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
let peer = ctx.peer();
|
let peer = ctx.peer();
|
||||||
debug!(target: "on_demand", "Peer {} gave bad response: {:?}", peer, e);
|
debug!(target: "on_demand", "Peer {} gave bad response: {:?}", peer, e);
|
||||||
@ -500,13 +545,13 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn detects_hangup() {
|
fn detects_hangup() {
|
||||||
let cache = Arc::new(Mutex::new(Cache::new(Default::default(), Duration::hours(6))));
|
let cache = Arc::new(Mutex::new(Cache::new(Default::default(), Duration::hours(6))));
|
||||||
let on_demand = OnDemand::new(cache, 0.into());
|
let on_demand = OnDemand::new(cache);
|
||||||
let result = on_demand.header_by_hash(&FakeContext, request::HeaderByHash(H256::default()));
|
let result = on_demand.header_by_hash(&FakeContext, request::HeaderByHash(H256::default()));
|
||||||
|
|
||||||
assert!(on_demand.orphaned_requests.read().len() == 1);
|
assert!(on_demand.pending.read().len() == 1);
|
||||||
drop(result);
|
drop(result);
|
||||||
|
|
||||||
on_demand.dispatch_pending(&FakeContext);
|
on_demand.dispatch_pending(&FakeContext);
|
||||||
assert!(on_demand.orphaned_requests.read().is_empty());
|
assert!(on_demand.pending.read().is_empty());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,12 +29,13 @@ use ethcore::transaction::SignedTransaction;
|
|||||||
use request::{self as net_request, IncompleteRequest, Output, OutputKind};
|
use request::{self as net_request, IncompleteRequest, Output, OutputKind};
|
||||||
|
|
||||||
use rlp::{RlpStream, UntrustedRlp};
|
use rlp::{RlpStream, UntrustedRlp};
|
||||||
use util::{Address, Bytes, DBValue, HashDB, H256, U256};
|
use util::{Address, Bytes, DBValue, HashDB, Mutex, H256, U256};
|
||||||
use util::memorydb::MemoryDB;
|
use util::memorydb::MemoryDB;
|
||||||
use util::sha3::Hashable;
|
use util::sha3::Hashable;
|
||||||
use util::trie::{Trie, TrieDB, TrieError};
|
use util::trie::{Trie, TrieDB, TrieError};
|
||||||
|
|
||||||
/// Core unit of the API: submit batches of these to be answered with `Response`s.
|
/// Core unit of the API: submit batches of these to be answered with `Response`s.
|
||||||
|
#[derive(Clone)]
|
||||||
pub enum Request {
|
pub enum Request {
|
||||||
/// A request for a header proof.
|
/// A request for a header proof.
|
||||||
HeaderProof(HeaderProof),
|
HeaderProof(HeaderProof),
|
||||||
@ -227,27 +228,28 @@ impl IncompleteRequest for CheckedRequest {
|
|||||||
impl net_request::CheckedRequest for CheckedRequest {
|
impl net_request::CheckedRequest for CheckedRequest {
|
||||||
type Extract = Response;
|
type Extract = Response;
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
|
type Environment = Mutex<::cache::Cache>;
|
||||||
|
|
||||||
/// Check whether the response matches (beyond the type).
|
/// Check whether the response matches (beyond the type).
|
||||||
fn check_response(&self, response: &Self::Response) -> Result<Response, Error> {
|
fn check_response(&self, cache: &Mutex<::cache::Cache>, response: &Self::Response) -> Result<Response, Error> {
|
||||||
use ::request::Response as NetResponse;
|
use ::request::Response as NetResponse;
|
||||||
|
|
||||||
// check response against contained prover.
|
// check response against contained prover.
|
||||||
match (self, response) {
|
match (self, response) {
|
||||||
(&CheckedRequest::HeaderProof(ref prover, _), &NetResponse::HeaderProof(ref res)) =>
|
(&CheckedRequest::HeaderProof(ref prover, _), &NetResponse::HeaderProof(ref res)) =>
|
||||||
prover.check_response(&res.proof).map(|(h, s)| Response::HeaderProof(h, s)),
|
prover.check_response(cache, &res.proof).map(|(h, s)| Response::HeaderProof(h, s)),
|
||||||
(&CheckedRequest::HeaderByHash(ref prover, _), &NetResponse::Headers(ref res)) =>
|
(&CheckedRequest::HeaderByHash(ref prover, _), &NetResponse::Headers(ref res)) =>
|
||||||
prover.check_response(&res.headers).map(Response::HeaderByHash),
|
prover.check_response(cache, &res.headers).map(Response::HeaderByHash),
|
||||||
(&CheckedRequest::Receipts(ref prover, _), &NetResponse::Receipts(ref res)) =>
|
(&CheckedRequest::Receipts(ref prover, _), &NetResponse::Receipts(ref res)) =>
|
||||||
prover.check_response(&res.receipts).map(Response::Receipts),
|
prover.check_response(cache, &res.receipts).map(Response::Receipts),
|
||||||
(&CheckedRequest::Body(ref prover, _), &NetResponse::Body(ref res)) =>
|
(&CheckedRequest::Body(ref prover, _), &NetResponse::Body(ref res)) =>
|
||||||
prover.check_response(&res.body).map(Response::Body),
|
prover.check_response(cache, &res.body).map(Response::Body),
|
||||||
(&CheckedRequest::Account(ref prover, _), &NetResponse::Account(ref res)) =>
|
(&CheckedRequest::Account(ref prover, _), &NetResponse::Account(ref res)) =>
|
||||||
prover.check_response(&res.proof).map(Response::Account),
|
prover.check_response(cache, &res.proof).map(Response::Account),
|
||||||
(&CheckedRequest::Code(ref prover, _), &NetResponse::Code(ref res)) =>
|
(&CheckedRequest::Code(ref prover, _), &NetResponse::Code(ref res)) =>
|
||||||
prover.check_response(&res.code).map(Response::Code),
|
prover.check_response(cache, &res.code).map(Response::Code),
|
||||||
(&CheckedRequest::Execution(ref prover, _), &NetResponse::Execution(ref res)) =>
|
(&CheckedRequest::Execution(ref prover, _), &NetResponse::Execution(ref res)) =>
|
||||||
Ok(Response::Execution(prover.check_response(&res.items))),
|
prover.check_response(cache, &res.items).map(Response::Execution),
|
||||||
_ => Err(Error::WrongKind),
|
_ => Err(Error::WrongKind),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -271,7 +273,7 @@ pub enum Response {
|
|||||||
/// Response to a request for code.
|
/// Response to a request for code.
|
||||||
Code(Vec<u8>),
|
Code(Vec<u8>),
|
||||||
/// Response to a request for proved execution.
|
/// Response to a request for proved execution.
|
||||||
Execution(ProvedExecution), // TODO: make into `Result`
|
Execution(super::ExecutionResult),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Errors in verification.
|
/// Errors in verification.
|
||||||
@ -339,9 +341,15 @@ impl HeaderProof {
|
|||||||
pub fn cht_root(&self) -> H256 { self.cht_root }
|
pub fn cht_root(&self) -> H256 { self.cht_root }
|
||||||
|
|
||||||
/// Check a response with a CHT proof, get a hash and total difficulty back.
|
/// Check a response with a CHT proof, get a hash and total difficulty back.
|
||||||
pub fn check_response(&self, proof: &[Bytes]) -> Result<(H256, U256), Error> {
|
pub fn check_response(&self, cache: &Mutex<::cache::Cache>, proof: &[Bytes]) -> Result<(H256, U256), Error> {
|
||||||
match ::cht::check_proof(proof, self.num, self.cht_root) {
|
match ::cht::check_proof(proof, self.num, self.cht_root) {
|
||||||
Some((expected_hash, td)) => Ok((expected_hash, td)),
|
Some((expected_hash, td)) => {
|
||||||
|
let mut cache = cache.lock();
|
||||||
|
cache.insert_block_hash(self.num, expected_hash);
|
||||||
|
cache.insert_chain_score(expected_hash, td);
|
||||||
|
|
||||||
|
Ok((expected_hash, td))
|
||||||
|
}
|
||||||
None => Err(Error::BadProof),
|
None => Err(Error::BadProof),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -353,11 +361,14 @@ pub struct HeaderByHash(pub H256);
|
|||||||
|
|
||||||
impl HeaderByHash {
|
impl HeaderByHash {
|
||||||
/// Check a response for the header.
|
/// Check a response for the header.
|
||||||
pub fn check_response(&self, headers: &[encoded::Header]) -> Result<encoded::Header, Error> {
|
pub fn check_response(&self, cache: &Mutex<::cache::Cache>, headers: &[encoded::Header]) -> Result<encoded::Header, Error> {
|
||||||
let header = headers.get(0).ok_or(Error::Empty)?;
|
let header = headers.get(0).ok_or(Error::Empty)?;
|
||||||
let hash = header.sha3();
|
let hash = header.sha3();
|
||||||
match hash == self.0 {
|
match hash == self.0 {
|
||||||
true => Ok(header.clone()),
|
true => {
|
||||||
|
cache.lock().insert_block_header(hash, header.clone());
|
||||||
|
Ok(header.clone())
|
||||||
|
}
|
||||||
false => Err(Error::WrongHash(self.0, hash)),
|
false => Err(Error::WrongHash(self.0, hash)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -383,7 +394,7 @@ impl Body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Check a response for this block body.
|
/// Check a response for this block body.
|
||||||
pub fn check_response(&self, body: &encoded::Body) -> Result<encoded::Block, Error> {
|
pub fn check_response(&self, cache: &Mutex<::cache::Cache>, body: &encoded::Body) -> Result<encoded::Block, Error> {
|
||||||
// check the integrity of the the body against the header
|
// check the integrity of the the body against the header
|
||||||
let tx_root = ::util::triehash::ordered_trie_root(body.rlp().at(0).iter().map(|r| r.as_raw().to_vec()));
|
let tx_root = ::util::triehash::ordered_trie_root(body.rlp().at(0).iter().map(|r| r.as_raw().to_vec()));
|
||||||
if tx_root != self.header.transactions_root() {
|
if tx_root != self.header.transactions_root() {
|
||||||
@ -401,6 +412,8 @@ impl Body {
|
|||||||
stream.append_raw(body.rlp().at(0).as_raw(), 1);
|
stream.append_raw(body.rlp().at(0).as_raw(), 1);
|
||||||
stream.append_raw(body.rlp().at(1).as_raw(), 1);
|
stream.append_raw(body.rlp().at(1).as_raw(), 1);
|
||||||
|
|
||||||
|
cache.lock().insert_block_body(self.hash, body.clone());
|
||||||
|
|
||||||
Ok(encoded::Block::new(stream.out()))
|
Ok(encoded::Block::new(stream.out()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -411,12 +424,15 @@ pub struct BlockReceipts(pub encoded::Header);
|
|||||||
|
|
||||||
impl BlockReceipts {
|
impl BlockReceipts {
|
||||||
/// Check a response with receipts against the stored header.
|
/// Check a response with receipts against the stored header.
|
||||||
pub fn check_response(&self, receipts: &[Receipt]) -> Result<Vec<Receipt>, Error> {
|
pub fn check_response(&self, cache: &Mutex<::cache::Cache>, receipts: &[Receipt]) -> Result<Vec<Receipt>, Error> {
|
||||||
let receipts_root = self.0.receipts_root();
|
let receipts_root = self.0.receipts_root();
|
||||||
let found_root = ::util::triehash::ordered_trie_root(receipts.iter().map(|r| ::rlp::encode(r).to_vec()));
|
let found_root = ::util::triehash::ordered_trie_root(receipts.iter().map(|r| ::rlp::encode(r).to_vec()));
|
||||||
|
|
||||||
match receipts_root == found_root {
|
match receipts_root == found_root {
|
||||||
true => Ok(receipts.to_vec()),
|
true => {
|
||||||
|
cache.lock().insert_block_receipts(receipts_root, receipts.to_vec());
|
||||||
|
Ok(receipts.to_vec())
|
||||||
|
}
|
||||||
false => Err(Error::WrongTrieRoot(receipts_root, found_root)),
|
false => Err(Error::WrongTrieRoot(receipts_root, found_root)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -433,7 +449,7 @@ pub struct Account {
|
|||||||
|
|
||||||
impl Account {
|
impl Account {
|
||||||
/// Check a response with an account against the stored header.
|
/// Check a response with an account against the stored header.
|
||||||
pub fn check_response(&self, proof: &[Bytes]) -> Result<Option<BasicAccount>, Error> {
|
pub fn check_response(&self, _: &Mutex<::cache::Cache>, proof: &[Bytes]) -> Result<Option<BasicAccount>, Error> {
|
||||||
let state_root = self.header.state_root();
|
let state_root = self.header.state_root();
|
||||||
|
|
||||||
let mut db = MemoryDB::new();
|
let mut db = MemoryDB::new();
|
||||||
@ -465,7 +481,7 @@ pub struct Code {
|
|||||||
|
|
||||||
impl Code {
|
impl Code {
|
||||||
/// Check a response with code against the code hash.
|
/// Check a response with code against the code hash.
|
||||||
pub fn check_response(&self, code: &[u8]) -> Result<Vec<u8>, Error> {
|
pub fn check_response(&self, _: &Mutex<::cache::Cache>, code: &[u8]) -> Result<Vec<u8>, Error> {
|
||||||
let found_hash = code.sha3();
|
let found_hash = code.sha3();
|
||||||
if found_hash == self.code_hash {
|
if found_hash == self.code_hash {
|
||||||
Ok(code.to_vec())
|
Ok(code.to_vec())
|
||||||
@ -490,23 +506,29 @@ pub struct TransactionProof {
|
|||||||
|
|
||||||
impl TransactionProof {
|
impl TransactionProof {
|
||||||
/// Check the proof, returning the proved execution or indicate that the proof was bad.
|
/// Check the proof, returning the proved execution or indicate that the proof was bad.
|
||||||
pub fn check_response(&self, state_items: &[DBValue]) -> ProvedExecution {
|
pub fn check_response(&self, _: &Mutex<::cache::Cache>, state_items: &[DBValue]) -> Result<super::ExecutionResult, Error> {
|
||||||
let root = self.header.state_root();
|
let root = self.header.state_root();
|
||||||
|
|
||||||
state::check_proof(
|
let proved_execution = state::check_proof(
|
||||||
state_items,
|
state_items,
|
||||||
root,
|
root,
|
||||||
&self.tx,
|
&self.tx,
|
||||||
&*self.engine,
|
&*self.engine,
|
||||||
&self.env_info,
|
&self.env_info,
|
||||||
)
|
);
|
||||||
|
|
||||||
|
match proved_execution {
|
||||||
|
ProvedExecution::BadProof => Err(Error::BadProof),
|
||||||
|
ProvedExecution::Failed(e) => Ok(Err(e)),
|
||||||
|
ProvedExecution::Complete(e) => Ok(Ok(e)),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use util::{MemoryDB, Address, H256};
|
use util::{MemoryDB, Address, Mutex, H256};
|
||||||
use util::trie::{Trie, TrieMut, SecTrieDB, SecTrieDBMut};
|
use util::trie::{Trie, TrieMut, SecTrieDB, SecTrieDBMut};
|
||||||
use util::trie::recorder::Recorder;
|
use util::trie::recorder::Recorder;
|
||||||
|
|
||||||
@ -515,6 +537,10 @@ mod tests {
|
|||||||
use ethcore::encoded;
|
use ethcore::encoded;
|
||||||
use ethcore::receipt::Receipt;
|
use ethcore::receipt::Receipt;
|
||||||
|
|
||||||
|
fn make_cache() -> ::cache::Cache {
|
||||||
|
::cache::Cache::new(Default::default(), ::time::Duration::seconds(1))
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn no_invalid_header_by_number() {
|
fn no_invalid_header_by_number() {
|
||||||
assert!(HeaderProof::new(0, Default::default()).is_none())
|
assert!(HeaderProof::new(0, Default::default()).is_none())
|
||||||
@ -544,7 +570,8 @@ mod tests {
|
|||||||
let proof = cht.prove(10_000, 0).unwrap().unwrap();
|
let proof = cht.prove(10_000, 0).unwrap().unwrap();
|
||||||
let req = HeaderProof::new(10_000, cht.root()).unwrap();
|
let req = HeaderProof::new(10_000, cht.root()).unwrap();
|
||||||
|
|
||||||
assert!(req.check_response(&proof[..]).is_ok());
|
let cache = Mutex::new(make_cache());
|
||||||
|
assert!(req.check_response(&cache, &proof[..]).is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -555,7 +582,8 @@ mod tests {
|
|||||||
let hash = header.hash();
|
let hash = header.hash();
|
||||||
let raw_header = encoded::Header::new(::rlp::encode(&header).to_vec());
|
let raw_header = encoded::Header::new(::rlp::encode(&header).to_vec());
|
||||||
|
|
||||||
assert!(HeaderByHash(hash).check_response(&[raw_header]).is_ok())
|
let cache = Mutex::new(make_cache());
|
||||||
|
assert!(HeaderByHash(hash).check_response(&cache, &[raw_header]).is_ok())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -571,8 +599,9 @@ mod tests {
|
|||||||
hash: header.hash(),
|
hash: header.hash(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let cache = Mutex::new(make_cache());
|
||||||
let response = encoded::Body::new(body_stream.drain().to_vec());
|
let response = encoded::Body::new(body_stream.drain().to_vec());
|
||||||
assert!(req.check_response(&response).is_ok())
|
assert!(req.check_response(&cache, &response).is_ok())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -593,7 +622,8 @@ mod tests {
|
|||||||
|
|
||||||
let req = BlockReceipts(encoded::Header::new(::rlp::encode(&header).to_vec()));
|
let req = BlockReceipts(encoded::Header::new(::rlp::encode(&header).to_vec()));
|
||||||
|
|
||||||
assert!(req.check_response(&receipts).is_ok())
|
let cache = Mutex::new(make_cache());
|
||||||
|
assert!(req.check_response(&cache, &receipts).is_ok())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -642,7 +672,8 @@ mod tests {
|
|||||||
address: addr,
|
address: addr,
|
||||||
};
|
};
|
||||||
|
|
||||||
assert!(req.check_response(&proof[..]).is_ok());
|
let cache = Mutex::new(make_cache());
|
||||||
|
assert!(req.check_response(&cache, &proof[..]).is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -653,7 +684,8 @@ mod tests {
|
|||||||
code_hash: ::util::Hashable::sha3(&code),
|
code_hash: ::util::Hashable::sha3(&code),
|
||||||
};
|
};
|
||||||
|
|
||||||
assert!(req.check_response(&code).is_ok());
|
let cache = Mutex::new(make_cache());
|
||||||
assert!(req.check_response(&[]).is_err());
|
assert!(req.check_response(&cache, &code).is_ok());
|
||||||
|
assert!(req.check_response(&cache, &[]).is_err());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -118,7 +118,7 @@ impl<T: IncompleteRequest + Clone> Requests<T> {
|
|||||||
impl<T: super::CheckedRequest> Requests<T> {
|
impl<T: super::CheckedRequest> Requests<T> {
|
||||||
/// Supply a response for the next request.
|
/// Supply a response for the next request.
|
||||||
/// Fails on: wrong request kind, all requests answered already.
|
/// Fails on: wrong request kind, all requests answered already.
|
||||||
pub fn supply_response(&mut self, response: &T::Response)
|
pub fn supply_response(&mut self, env: &T::Environment, response: &T::Response)
|
||||||
-> Result<T::Extract, ResponseError<T::Error>>
|
-> Result<T::Extract, ResponseError<T::Error>>
|
||||||
{
|
{
|
||||||
let idx = self.answered;
|
let idx = self.answered;
|
||||||
@ -126,7 +126,7 @@ impl<T: super::CheckedRequest> Requests<T> {
|
|||||||
// check validity.
|
// check validity.
|
||||||
if idx == self.requests.len() { return Err(ResponseError::Unexpected) }
|
if idx == self.requests.len() { return Err(ResponseError::Unexpected) }
|
||||||
let extracted = self.requests[idx]
|
let extracted = self.requests[idx]
|
||||||
.check_response(&response).map_err(ResponseError::Validity)?;
|
.check_response(env, response).map_err(ResponseError::Validity)?;
|
||||||
|
|
||||||
let outputs = &mut self.outputs;
|
let outputs = &mut self.outputs;
|
||||||
response.fill_outputs(|out_idx, output| {
|
response.fill_outputs(|out_idx, output| {
|
||||||
@ -157,7 +157,7 @@ impl Requests<super::Request> {
|
|||||||
let mut responses = Vec::new();
|
let mut responses = Vec::new();
|
||||||
|
|
||||||
while let Some(response) = self.next_complete().and_then(&responder) {
|
while let Some(response) = self.next_complete().and_then(&responder) {
|
||||||
match self.supply_response(&response) {
|
match self.supply_response(&(), &response) {
|
||||||
Ok(()) => responses.push(response),
|
Ok(()) => responses.push(response),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
debug!(target: "pip", "produced bad response to request: {:?}", e);
|
debug!(target: "pip", "produced bad response to request: {:?}", e);
|
||||||
|
@ -382,8 +382,9 @@ impl IncompleteRequest for Request {
|
|||||||
impl CheckedRequest for Request {
|
impl CheckedRequest for Request {
|
||||||
type Extract = ();
|
type Extract = ();
|
||||||
type Error = WrongKind;
|
type Error = WrongKind;
|
||||||
|
type Environment = ();
|
||||||
|
|
||||||
fn check_response(&self, response: &Response) -> Result<(), WrongKind> {
|
fn check_response(&self, _: &(), response: &Response) -> Result<(), WrongKind> {
|
||||||
if self.kind() == response.kind() {
|
if self.kind() == response.kind() {
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
@ -566,9 +567,11 @@ pub trait CheckedRequest: IncompleteRequest {
|
|||||||
type Extract;
|
type Extract;
|
||||||
/// Error encountered during the check.
|
/// Error encountered during the check.
|
||||||
type Error;
|
type Error;
|
||||||
|
/// Environment passed to response check.
|
||||||
|
type Environment;
|
||||||
|
|
||||||
/// Check whether the response matches (beyond the type).
|
/// Check whether the response matches (beyond the type).
|
||||||
fn check_response(&self, _response: &Self::Response) -> Result<Self::Extract, Self::Error>;
|
fn check_response(&self, &Self::Environment, &Self::Response) -> Result<Self::Extract, Self::Error>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A response-like object.
|
/// A response-like object.
|
||||||
|
Loading…
Reference in New Issue
Block a user