complete initial request changes

This commit is contained in:
Robert Habermeier 2017-03-07 19:48:07 +01:00
parent b396b56e34
commit 04291fe71e
9 changed files with 273 additions and 961 deletions

View File

@ -1,5 +1,5 @@
[package]
description = "Parity LES primitives"
description = "Parity Light Client Implementation"
homepage = "http://parity.io"
license = "GPL-3.0"
name = "ethcore-light"

View File

@ -35,9 +35,10 @@
pub mod client;
pub mod cht;
pub mod net;
pub mod on_demand;
//pub mod on_demand;
pub mod transaction_queue;
pub mod cache;
pub mod request_builder;
#[cfg(not(feature = "ipc"))]
pub mod provider;

View File

@ -89,10 +89,6 @@ pub trait BasicContext {
// TODO: maybe just put this on a timer in LightProtocol?
fn make_announcement(&self, announcement: Announcement);
/// Find the maximum number of requests of a specific type which can be made from
/// supplied peer.
fn max_requests(&self, peer: PeerId, kind: request::Kind) -> usize;
/// Disconnect a peer.
fn disconnect_peer(&self, peer: PeerId);
@ -131,10 +127,6 @@ impl<'a> BasicContext for TickCtx<'a> {
self.proto.make_announcement(self.io, announcement);
}
fn max_requests(&self, peer: PeerId, kind: request::Kind) -> usize {
self.proto.max_requests(peer, kind)
}
fn disconnect_peer(&self, peer: PeerId) {
self.io.disconnect_peer(peer);
}
@ -168,10 +160,6 @@ impl<'a> BasicContext for Ctx<'a> {
self.proto.make_announcement(self.io, announcement);
}
fn max_requests(&self, peer: PeerId, kind: request::Kind) -> usize {
self.proto.max_requests(peer, kind)
}
fn disconnect_peer(&self, peer: PeerId) {
self.io.disconnect_peer(peer);
}

View File

@ -14,10 +14,9 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
//! LES Protocol Version 1 implementation.
//! PIP Protocol Version 1 implementation.
//!
//! This uses a "Provider" to answer requests.
//! See https://github.com/ethcore/parity/wiki/Light-Ethereum-Subprotocol-(LES)
use ethcore::transaction::{Action, UnverifiedTransaction};
use ethcore::receipt::Receipt;
@ -35,7 +34,7 @@ use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};
use provider::Provider;
use request::{self, HashOrNumber, Request};
use request::{self, HashOrNumber, Request, Response};
use self::request_credits::{Credits, FlowParams};
use self::context::{Ctx, TickCtx};
@ -83,43 +82,24 @@ mod packet {
// announcement of new block hashes or capabilities.
pub const ANNOUNCE: u8 = 0x01;
// request and response for block headers
pub const GET_BLOCK_HEADERS: u8 = 0x02;
pub const BLOCK_HEADERS: u8 = 0x03;
// request and response for block bodies
pub const GET_BLOCK_BODIES: u8 = 0x04;
pub const BLOCK_BODIES: u8 = 0x05;
// request and response for transaction receipts.
pub const GET_RECEIPTS: u8 = 0x06;
pub const RECEIPTS: u8 = 0x07;
// request and response for merkle proofs.
pub const GET_PROOFS: u8 = 0x08;
pub const PROOFS: u8 = 0x09;
// request and response for contract code.
pub const GET_CONTRACT_CODES: u8 = 0x0a;
pub const CONTRACT_CODES: u8 = 0x0b;
// request and response.
pub const REQUEST: u8 = 0x02;
pub const RESPONSE: u8 = 0x03;
// relay transactions to peers.
pub const SEND_TRANSACTIONS: u8 = 0x0c;
// request and response for header proofs in a CHT.
pub const GET_HEADER_PROOFS: u8 = 0x0d;
pub const HEADER_PROOFS: u8 = 0x0e;
pub const SEND_TRANSACTIONS: u8 = 0x04;
// request and response for transaction proof.
pub const GET_TRANSACTION_PROOF: u8 = 0x0f;
pub const TRANSACTION_PROOF: u8 = 0x10;
// TODO: merge with request/response.
pub const GET_TRANSACTION_PROOF: u8 = 0x05;
pub const TRANSACTION_PROOF: u8 = 0x06;
}
// timeouts for different kinds of requests. all values are in milliseconds.
// TODO: variable timeouts based on request count.
mod timeout {
pub const HANDSHAKE: i64 = 2500;
pub const HEADERS: i64 = 5000;
pub const HEADERS: i64 = 2500;
pub const BODIES: i64 = 5000;
pub const RECEIPTS: i64 = 3500;
pub const PROOFS: i64 = 4000;
@ -159,17 +139,6 @@ pub struct Peer {
}
impl Peer {
// check the maximum cost of a request, returning an error if there's
// not enough credits left.
// returns the calculated maximum cost.
fn deduct_max(&mut self, flow_params: &FlowParams, kind: request::Kind, max: usize) -> Result<U256, Error> {
flow_params.recharge(&mut self.local_credits);
let max_cost = flow_params.compute_cost(kind, max);
self.local_credits.deduct_cost(max_cost)?;
Ok(max_cost)
}
// refund credits for a request. returns new amount of credits.
fn refund(&mut self, flow_params: &FlowParams, amount: U256) -> U256 {
flow_params.refund(&mut self.local_credits, amount);
@ -197,20 +166,8 @@ pub trait Handler: Send + Sync {
fn on_announcement(&self, _ctx: &EventContext, _announcement: &Announcement) { }
/// Called when a peer requests relay of some transactions.
fn on_transactions(&self, _ctx: &EventContext, _relay: &[UnverifiedTransaction]) { }
/// Called when a peer responds with block bodies.
fn on_block_bodies(&self, _ctx: &EventContext, _req_id: ReqId, _bodies: &[Bytes]) { }
/// Called when a peer responds with block headers.
fn on_block_headers(&self, _ctx: &EventContext, _req_id: ReqId, _headers: &[Bytes]) { }
/// Called when a peer responds with block receipts.
fn on_receipts(&self, _ctx: &EventContext, _req_id: ReqId, _receipts: &[Vec<Receipt>]) { }
/// Called when a peer responds with state proofs. Each proof should be a series of trie
/// nodes in ascending order by distance from the root.
fn on_state_proofs(&self, _ctx: &EventContext, _req_id: ReqId, _proofs: &[Vec<Bytes>]) { }
/// Called when a peer responds with contract code.
fn on_code(&self, _ctx: &EventContext, _req_id: ReqId, _codes: &[Bytes]) { }
/// Called when a peer responds with header proofs. Each proof should be a block header coupled
/// with a series of trie nodes is ascending order by distance from the root.
fn on_header_proofs(&self, _ctx: &EventContext, _req_id: ReqId, _proofs: &[(Bytes, Vec<Bytes>)]) { }
/// Called when a peer responds to requests.
fn on_responses(&self, _ctx: &EventContext, _req_id: ReqId, _relay: &[Response]) { }
/// Called when a peer responds with a transaction proof. Each proof is a vector of state items.
fn on_transaction_proof(&self, _ctx: &EventContext, _req_id: ReqId, _state_items: &[DBValue]) { }
/// Called to "tick" the handler periodically.
@ -307,7 +264,7 @@ pub struct LightProtocol {
impl LightProtocol {
/// Create a new instance of the protocol manager.
pub fn new(provider: Arc<Provider>, params: Params) -> Self {
debug!(target: "les", "Initializing LES handler");
debug!(target: "pip", "Initializing light protocol handler");
let genesis_hash = provider.chain_info().genesis_hash;
LightProtocol {
@ -339,62 +296,15 @@ impl LightProtocol {
)
}
/// Check the maximum amount of requests of a specific type
/// which a peer would be able to serve. Returns zero if the
/// peer is unknown or has no credit parameters.
fn max_requests(&self, peer: PeerId, kind: request::Kind) -> usize {
self.peers.read().get(&peer).and_then(|peer| {
let mut peer = peer.lock();
match peer.remote_flow {
Some((ref mut c, ref flow)) => {
flow.recharge(c);
Some(flow.max_amount(&*c, kind))
}
None => None,
}
}).unwrap_or(0)
}
/// Make a request to a peer.
///
/// Fails on: nonexistent peer, network error, peer not server,
/// insufficient credits. Does not check capabilities before sending.
/// On success, returns a request id which can later be coordinated
/// with an event.
// TODO: pass `Requests`.
pub fn request_from(&self, io: &IoContext, peer_id: &PeerId, request: Request) -> Result<ReqId, Error> {
let peers = self.peers.read();
let peer = peers.get(peer_id).ok_or_else(|| Error::UnknownPeer)?;
let mut peer = peer.lock();
match peer.remote_flow {
Some((ref mut c, ref flow)) => {
flow.recharge(c);
let max = flow.compute_cost(request.kind(), request.amount());
c.deduct_cost(max)?;
}
None => return Err(Error::NotServer),
}
let req_id = self.req_id.fetch_add(1, Ordering::SeqCst);
let packet_data = encode_request(&request, req_id);
trace!(target: "les", "Dispatching request {} to peer {}", req_id, peer_id);
let packet_id = match request.kind() {
request::Kind::Headers => packet::GET_BLOCK_HEADERS,
request::Kind::Bodies => packet::GET_BLOCK_BODIES,
request::Kind::Receipts => packet::GET_RECEIPTS,
request::Kind::StateProofs => packet::GET_PROOFS,
request::Kind::Codes => packet::GET_CONTRACT_CODES,
request::Kind::HeaderProofs => packet::GET_HEADER_PROOFS,
request::Kind::TransactionProof => packet::GET_TRANSACTION_PROOF,
};
io.send(*peer_id, packet_id, packet_data);
peer.pending_requests.insert(ReqId(req_id), request, SteadyTime::now());
Ok(ReqId(req_id))
unimplemented!()
}
/// Make an announcement of new chain head and capabilities to all peers.
@ -427,7 +337,7 @@ impl LightProtocol {
None => {
// both values will always originate locally -- this means something
// has gone really wrong
debug!(target: "les", "couldn't compute reorganization depth between {:?} and {:?}",
debug!(target: "pip", "couldn't compute reorganization depth between {:?} and {:?}",
&announcement.head_hash, &peer_info.sent_head);
0
}
@ -474,11 +384,10 @@ impl LightProtocol {
let req_id = ReqId(raw.val_at(0)?);
let cur_credits: U256 = raw.val_at(1)?;
trace!(target: "les", "pre-verifying response from peer {}, kind={:?}", peer, kind);
trace!(target: "pip", "pre-verifying response from peer {}, kind={:?}", peer, kind);
let mut had_req = false;
let peers = self.peers.read();
let maybe_err = match peers.get(peer) {
let res = match peers.get(peer) {
Some(peer_info) => {
let mut peer_info = peer_info.lock();
let req_info = peer_info.pending_requests.remove(&req_id, SteadyTime::now());
@ -486,69 +395,37 @@ impl LightProtocol {
match (req_info, flow_info) {
(Some(request), Some(flow_info)) => {
had_req = true;
let &mut (ref mut c, ref mut flow) = flow_info;
let actual_credits = ::std::cmp::min(cur_credits, *flow.limit());
c.update_to(actual_credits);
if request.kind() != kind {
Some(Error::UnsolicitedResponse)
} else {
None
Ok(())
}
(None, _) => Err(Error::UnsolicitedResponse),
(_, None) => Err(Error::NotServer), // really should be impossible.
}
}
(None, _) => Some(Error::UnsolicitedResponse),
(_, None) => Some(Error::NotServer), // really should be impossible.
}
}
None => Some(Error::UnknownPeer), // probably only occurs in a race of some kind.
None => Err(Error::UnknownPeer), // probably only occurs in a race of some kind.
};
if had_req {
let id_guard = IdGuard::new(peers, *peer, req_id);
match maybe_err {
Some(err) => Err(err),
None => Ok(id_guard)
}
} else {
Err(maybe_err.expect("every branch without a request leads to error; qed"))
}
res.map(|_| IdGuard::new(peers, *peer, req_id))
}
/// Handle an LES packet using the given io context.
/// Handle a packet using the given io context.
/// Packet data is _untrusted_, which means that invalid data won't lead to
/// issues.
pub fn handle_packet(&self, io: &IoContext, peer: &PeerId, packet_id: u8, data: &[u8]) {
let rlp = UntrustedRlp::new(data);
trace!(target: "les", "Incoming packet {} from peer {}", packet_id, peer);
trace!(target: "pip", "Incoming packet {} from peer {}", packet_id, peer);
// handle the packet
let res = match packet_id {
packet::STATUS => self.status(peer, io, rlp),
packet::ANNOUNCE => self.announcement(peer, io, rlp),
packet::GET_BLOCK_HEADERS => self.get_block_headers(peer, io, rlp),
packet::BLOCK_HEADERS => self.block_headers(peer, io, rlp),
packet::GET_BLOCK_BODIES => self.get_block_bodies(peer, io, rlp),
packet::BLOCK_BODIES => self.block_bodies(peer, io, rlp),
packet::GET_RECEIPTS => self.get_receipts(peer, io, rlp),
packet::RECEIPTS => self.receipts(peer, io, rlp),
packet::GET_PROOFS => self.get_proofs(peer, io, rlp),
packet::PROOFS => self.proofs(peer, io, rlp),
packet::GET_CONTRACT_CODES => self.get_contract_code(peer, io, rlp),
packet::CONTRACT_CODES => self.contract_code(peer, io, rlp),
packet::GET_HEADER_PROOFS => self.get_header_proofs(peer, io, rlp),
packet::HEADER_PROOFS => self.header_proofs(peer, io, rlp),
packet::GET_TRANSACTION_PROOF => self.get_transaction_proof(peer, io, rlp),
packet::TRANSACTION_PROOF => self.transaction_proof(peer, io, rlp),
packet::REQUEST => self.request(peer, io, rlp),
packet::RESPONSE => self.response(peer, io, rlp),
packet::SEND_TRANSACTIONS => self.relay_transactions(peer, io, rlp),
@ -577,7 +454,7 @@ impl LightProtocol {
.collect();
for slowpoke in slowpokes {
debug!(target: "les", "Peer {} handshake timed out", slowpoke);
debug!(target: "pip", "Peer {} handshake timed out", slowpoke);
pending.remove(&slowpoke);
io.disconnect_peer(slowpoke);
}
@ -587,7 +464,7 @@ impl LightProtocol {
{
for (peer_id, peer) in self.peers.read().iter() {
if peer.lock().pending_requests.check_timeout(now) {
debug!(target: "les", "Peer {} request timeout", peer_id);
debug!(target: "pip", "Peer {} request timeout", peer_id);
io.disconnect_peer(*peer_id);
}
}
@ -631,7 +508,7 @@ impl LightProtocol {
/// called when a peer disconnects.
pub fn on_disconnect(&self, peer: PeerId, io: &IoContext) {
trace!(target: "les", "Peer {} disconnecting", peer);
trace!(target: "pip", "Peer {} disconnecting", peer);
self.pending_peers.write().remove(&peer);
let unfulfilled = match self.peers.write().remove(&peer) {
@ -686,7 +563,7 @@ impl LightProtocol {
let (status, capabilities, flow_params) = status::parse_handshake(data)?;
trace!(target: "les", "Connected peer with chain head {:?}", (status.head_hash, status.head_num));
trace!(target: "pip", "Connected peer with chain head {:?}", (status.head_hash, status.head_num));
if (status.network_id, status.genesis_hash) != (self.network_id, self.genesis_hash) {
return Err(Error::WrongNetwork);
@ -723,7 +600,7 @@ impl LightProtocol {
// Handle an announcement.
fn announcement(&self, peer: &PeerId, io: &IoContext, data: UntrustedRlp) -> Result<(), Error> {
if !self.peers.read().contains_key(peer) {
debug!(target: "les", "Ignoring announcement from unknown peer");
debug!(target: "pip", "Ignoring announcement from unknown peer");
return Ok(())
}
@ -765,447 +642,19 @@ impl LightProtocol {
Ok(())
}
// Handle a request for block headers.
fn get_block_headers(&self, peer: &PeerId, io: &IoContext, data: UntrustedRlp) -> Result<(), Error> {
const MAX_HEADERS: usize = 512;
let peers = self.peers.read();
let peer = match peers.get(peer) {
Some(peer) => peer,
None => {
debug!(target: "les", "Ignoring request from unknown peer");
return Ok(())
}
};
let mut peer = peer.lock();
let req_id: u64 = data.val_at(0)?;
let data = data.at(1)?;
let start_block = {
if data.at(0)?.size() == 32 {
HashOrNumber::Hash(data.val_at(0)?)
} else {
HashOrNumber::Number(data.val_at(0)?)
}
};
let req = request::Headers {
start: start_block,
max: ::std::cmp::min(MAX_HEADERS, data.val_at(1)?),
skip: data.val_at(2)?,
reverse: data.val_at(3)?,
};
let max_cost = peer.deduct_max(&self.flow_params, request::Kind::Headers, req.max)?;
let response = self.provider.block_headers(req);
let actual_cost = self.flow_params.compute_cost(request::Kind::Headers, response.len());
assert!(max_cost >= actual_cost, "Actual cost exceeded maximum computed cost.");
let cur_credits = peer.refund(&self.flow_params, max_cost - actual_cost);
io.respond(packet::BLOCK_HEADERS, {
let mut stream = RlpStream::new_list(3);
stream.append(&req_id).append(&cur_credits).begin_list(response.len());
for header in response {
stream.append_raw(&header.into_inner(), 1);
}
stream.out()
});
Ok(())
}
// Receive a response for block headers.
fn block_headers(&self, peer: &PeerId, io: &IoContext, raw: UntrustedRlp) -> Result<(), Error> {
let id_guard = self.pre_verify_response(peer, request::Kind::Headers, &raw)?;
let raw_headers: Vec<_> = raw.at(2)?.iter().map(|x| x.as_raw().to_owned()).collect();
let req_id = id_guard.defuse();
for handler in &self.handlers {
handler.on_block_headers(&Ctx {
peer: *peer,
io: io,
proto: self,
}, req_id, &raw_headers);
}
Ok(())
}
// Handle a request for block bodies.
fn get_block_bodies(&self, peer: &PeerId, io: &IoContext, data: UntrustedRlp) -> Result<(), Error> {
const MAX_BODIES: usize = 256;
let peers = self.peers.read();
let peer = match peers.get(peer) {
Some(peer) => peer,
None => {
debug!(target: "les", "Ignoring request from unknown peer");
return Ok(())
}
};
let mut peer = peer.lock();
let req_id: u64 = data.val_at(0)?;
let req = request::Bodies {
block_hashes: data.at(1)?.iter()
.take(MAX_BODIES)
.map(|x| x.as_val())
.collect::<Result<_, _>>()?
};
let max_cost = peer.deduct_max(&self.flow_params, request::Kind::Bodies, req.block_hashes.len())?;
let response = self.provider.block_bodies(req);
let response_len = response.iter().filter(|x| x.is_some()).count();
let actual_cost = self.flow_params.compute_cost(request::Kind::Bodies, response_len);
assert!(max_cost >= actual_cost, "Actual cost exceeded maximum computed cost.");
let cur_credits = peer.refund(&self.flow_params, max_cost - actual_cost);
io.respond(packet::BLOCK_BODIES, {
let mut stream = RlpStream::new_list(3);
stream.append(&req_id).append(&cur_credits).begin_list(response.len());
for body in response {
match body {
Some(body) => stream.append_raw(&body.into_inner(), 1),
None => stream.append_empty_data(),
};
}
stream.out()
});
Ok(())
}
// Receive a response for block bodies.
fn block_bodies(&self, peer: &PeerId, io: &IoContext, raw: UntrustedRlp) -> Result<(), Error> {
let id_guard = self.pre_verify_response(peer, request::Kind::Bodies, &raw)?;
let raw_bodies: Vec<Bytes> = raw.at(2)?.iter().map(|x| x.as_raw().to_owned()).collect();
let req_id = id_guard.defuse();
for handler in &self.handlers {
handler.on_block_bodies(&Ctx {
peer: *peer,
io: io,
proto: self,
}, req_id, &raw_bodies);
}
Ok(())
}
// Handle a request for receipts.
fn get_receipts(&self, peer: &PeerId, io: &IoContext, data: UntrustedRlp) -> Result<(), Error> {
const MAX_RECEIPTS: usize = 256;
let peers = self.peers.read();
let peer = match peers.get(peer) {
Some(peer) => peer,
None => {
debug!(target: "les", "Ignoring request from unknown peer");
return Ok(())
}
};
let mut peer = peer.lock();
let req_id: u64 = data.val_at(0)?;
let req = request::Receipts {
block_hashes: data.at(1)?.iter()
.take(MAX_RECEIPTS)
.map(|x| x.as_val())
.collect::<Result<_,_>>()?
};
let max_cost = peer.deduct_max(&self.flow_params, request::Kind::Receipts, req.block_hashes.len())?;
let response = self.provider.receipts(req);
let response_len = response.iter().filter(|x| &x[..] != &::rlp::EMPTY_LIST_RLP).count();
let actual_cost = self.flow_params.compute_cost(request::Kind::Receipts, response_len);
assert!(max_cost >= actual_cost, "Actual cost exceeded maximum computed cost.");
let cur_credits = peer.refund(&self.flow_params, max_cost - actual_cost);
io.respond(packet::RECEIPTS, {
let mut stream = RlpStream::new_list(3);
stream.append(&req_id).append(&cur_credits).begin_list(response.len());
for receipts in response {
stream.append_raw(&receipts, 1);
}
stream.out()
});
Ok(())
}
// Receive a response for receipts.
fn receipts(&self, peer: &PeerId, io: &IoContext, raw: UntrustedRlp) -> Result<(), Error> {
let id_guard = self.pre_verify_response(peer, request::Kind::Receipts, &raw)?;
let raw_receipts: Vec<Vec<Receipt>> = raw.at(2)?
.iter()
.map(|x| x.as_val())
.collect::<Result<_,_>>()?;
let req_id = id_guard.defuse();
for handler in &self.handlers {
handler.on_receipts(&Ctx {
peer: *peer,
io: io,
proto: self,
}, req_id, &raw_receipts);
}
Ok(())
}
// Handle a request for proofs.
fn get_proofs(&self, peer: &PeerId, io: &IoContext, data: UntrustedRlp) -> Result<(), Error> {
const MAX_PROOFS: usize = 128;
let peers = self.peers.read();
let peer = match peers.get(peer) {
Some(peer) => peer,
None => {
debug!(target: "les", "Ignoring request from unknown peer");
return Ok(())
}
};
let mut peer = peer.lock();
let req_id: u64 = data.val_at(0)?;
let req = {
let requests: Result<Vec<_>, Error> = data.at(1)?.iter().take(MAX_PROOFS).map(|x| {
Ok(request::StateProof {
block: x.val_at(0)?,
key1: x.val_at(1)?,
key2: if x.at(2)?.is_empty() { None } else { Some(x.val_at(2)?) },
from_level: x.val_at(3)?,
})
}).collect();
request::StateProofs {
requests: requests?,
}
};
let max_cost = peer.deduct_max(&self.flow_params, request::Kind::StateProofs, req.requests.len())?;
let response = self.provider.proofs(req);
let response_len = response.iter().filter(|x| &x[..] != &::rlp::EMPTY_LIST_RLP).count();
let actual_cost = self.flow_params.compute_cost(request::Kind::StateProofs, response_len);
assert!(max_cost >= actual_cost, "Actual cost exceeded maximum computed cost.");
let cur_credits = peer.refund(&self.flow_params, max_cost - actual_cost);
io.respond(packet::PROOFS, {
let mut stream = RlpStream::new_list(3);
stream.append(&req_id).append(&cur_credits).begin_list(response.len());
for proof in response {
stream.append_raw(&proof, 1);
}
stream.out()
});
Ok(())
}
// Receive a response for proofs.
fn proofs(&self, peer: &PeerId, io: &IoContext, raw: UntrustedRlp) -> Result<(), Error> {
let id_guard = self.pre_verify_response(peer, request::Kind::StateProofs, &raw)?;
let raw_proofs: Vec<Vec<Bytes>> = raw.at(2)?.iter()
.map(|x| x.iter().map(|node| node.as_raw().to_owned()).collect())
.collect();
let req_id = id_guard.defuse();
for handler in &self.handlers {
handler.on_state_proofs(&Ctx {
peer: *peer,
io: io,
proto: self,
}, req_id, &raw_proofs);
}
Ok(())
}
// Handle a request for contract code.
fn get_contract_code(&self, peer: &PeerId, io: &IoContext, data: UntrustedRlp) -> Result<(), Error> {
const MAX_CODES: usize = 256;
let peers = self.peers.read();
let peer = match peers.get(peer) {
Some(peer) => peer,
None => {
debug!(target: "les", "Ignoring request from unknown peer");
return Ok(())
}
};
let mut peer = peer.lock();
let req_id: u64 = data.val_at(0)?;
let req = {
let requests: Result<Vec<_>, Error> = data.at(1)?.iter().take(MAX_CODES).map(|x| {
Ok(request::ContractCode {
block_hash: x.val_at(0)?,
account_key: x.val_at(1)?,
})
}).collect();
request::ContractCodes {
code_requests: requests?,
}
};
let max_cost = peer.deduct_max(&self.flow_params, request::Kind::Codes, req.code_requests.len())?;
let response = self.provider.contract_codes(req);
let response_len = response.iter().filter(|x| !x.is_empty()).count();
let actual_cost = self.flow_params.compute_cost(request::Kind::Codes, response_len);
assert!(max_cost >= actual_cost, "Actual cost exceeded maximum computed cost.");
let cur_credits = peer.refund(&self.flow_params, max_cost - actual_cost);
io.respond(packet::CONTRACT_CODES, {
let mut stream = RlpStream::new_list(3);
stream.append(&req_id).append(&cur_credits).begin_list(response.len());
for code in response {
stream.append(&code);
}
stream.out()
});
Ok(())
}
// Receive a response for contract code.
fn contract_code(&self, peer: &PeerId, io: &IoContext, raw: UntrustedRlp) -> Result<(), Error> {
let id_guard = self.pre_verify_response(peer, request::Kind::Codes, &raw)?;
let raw_code: Vec<Bytes> = raw.at(2)?.iter()
.map(|x| x.as_val())
.collect::<Result<_,_>>()?;
let req_id = id_guard.defuse();
for handler in &self.handlers {
handler.on_code(&Ctx {
peer: *peer,
io: io,
proto: self,
}, req_id, &raw_code);
}
Ok(())
}
// Handle a request for header proofs
fn get_header_proofs(&self, peer: &PeerId, io: &IoContext, data: UntrustedRlp) -> Result<(), Error> {
const MAX_PROOFS: usize = 256;
let peers = self.peers.read();
let peer = match peers.get(peer) {
Some(peer) => peer,
None => {
debug!(target: "les", "Ignoring request from unknown peer");
return Ok(())
}
};
let mut peer = peer.lock();
let req_id: u64 = data.val_at(0)?;
let req = {
let requests: Result<Vec<_>, Error> = data.at(1)?.iter().take(MAX_PROOFS).map(|x| {
Ok(request::HeaderProof {
cht_number: x.val_at(0)?,
block_number: x.val_at(1)?,
from_level: x.val_at(2)?,
})
}).collect();
request::HeaderProofs {
requests: requests?,
}
};
let max_cost = peer.deduct_max(&self.flow_params, request::Kind::HeaderProofs, req.requests.len())?;
let response = self.provider.header_proofs(req);
let response_len = response.iter().filter(|x| &x[..] != ::rlp::EMPTY_LIST_RLP).count();
let actual_cost = self.flow_params.compute_cost(request::Kind::HeaderProofs, response_len);
assert!(max_cost >= actual_cost, "Actual cost exceeded maximum computed cost.");
let cur_credits = peer.refund(&self.flow_params, max_cost - actual_cost);
io.respond(packet::HEADER_PROOFS, {
let mut stream = RlpStream::new_list(3);
stream.append(&req_id).append(&cur_credits).begin_list(response.len());
for proof in response {
stream.append_raw(&proof, 1);
}
stream.out()
});
Ok(())
}
// Receive a response for header proofs
fn header_proofs(&self, peer: &PeerId, io: &IoContext, raw: UntrustedRlp) -> Result<(), Error> {
fn decode_res(raw: UntrustedRlp) -> Result<(Bytes, Vec<Bytes>), ::rlp::DecoderError> {
Ok((
raw.val_at(0)?,
raw.at(1)?.iter().map(|x| x.as_raw().to_owned()).collect(),
))
}
let id_guard = self.pre_verify_response(peer, request::Kind::HeaderProofs, &raw)?;
let raw_proofs: Vec<_> = raw.at(2)?.iter()
.map(decode_res)
.collect::<Result<_,_>>()?;
let req_id = id_guard.defuse();
for handler in &self.handlers {
handler.on_header_proofs(&Ctx {
peer: *peer,
io: io,
proto: self,
}, req_id, &raw_proofs);
}
Ok(())
}
// Receive a request for proof-of-execution.
fn get_transaction_proof(&self, peer: &PeerId, io: &IoContext, raw: UntrustedRlp) -> Result<(), Error> {
// refuse to execute more than this amount of gas at once.
// this is appx. the point at which the proof of execution would no longer fit in
// a single Devp2p packet.
fn request(&self, peer: &PeerId, io: &IoContext, raw: UntrustedRlp) -> Result<(), Error> {
// the maximum amount of requests we'll fill in a single packet.
const MAX_REQUESTS: usize = 512;
// the maximum amount of gas we'll prove execution of in a single packet.
const MAX_GAS: usize = 50_000_000;
use util::Uint;
use ::request_builder::RequestBuilder;
let peers = self.peers.read();
let peer = match peers.get(peer) {
Some(peer) => peer,
None => {
debug!(target: "les", "Ignoring request from unknown peer");
debug!(target: "pip", "Ignoring request from unknown peer");
return Ok(())
}
};
@ -1213,68 +662,11 @@ impl LightProtocol {
let req_id: u64 = raw.val_at(0)?;
let req = {
let req_rlp = raw.at(1)?;
request::TransactionProof {
at: req_rlp.val_at(0)?,
from: req_rlp.val_at(1)?,
action: if req_rlp.at(2)?.is_empty() {
Action::Create
} else {
Action::Call(req_rlp.val_at(2)?)
},
gas: ::std::cmp::min(req_rlp.val_at(3)?, MAX_GAS.into()),
gas_price: req_rlp.val_at(4)?,
value: req_rlp.val_at(5)?,
data: req_rlp.val_at(6)?,
}
};
// always charge the peer for all the gas.
peer.deduct_max(&self.flow_params, request::Kind::TransactionProof, req.gas.low_u64() as usize)?;
let response = match self.provider.transaction_proof(req) {
Some(res) => res,
None => vec![],
};
let cur_credits = peer.local_credits.current();
io.respond(packet::TRANSACTION_PROOF, {
let mut stream = RlpStream::new_list(3);
stream.append(&req_id).append(&cur_credits).begin_list(response.len());
for state_item in response {
stream.append(&&state_item[..]);
unimplemented!()
}
stream.out()
});
Ok(())
}
// Receive a response for proof-of-execution.
fn transaction_proof(&self, peer: &PeerId, io: &IoContext, raw: UntrustedRlp) -> Result<(), Error> {
let id_guard = self.pre_verify_response(peer, request::Kind::HeaderProofs, &raw)?;
let raw_proof: Vec<DBValue> = raw.at(2)?.iter()
.map(|rlp| {
let mut db_val = DBValue::new();
db_val.append_slice(rlp.data()?);
Ok(db_val)
})
.collect::<Result<Vec<_>, ::rlp::DecoderError>>()?;
let req_id = id_guard.defuse();
for handler in &self.handlers {
handler.on_transaction_proof(&Ctx {
peer: *peer,
io: io,
proto: self,
}, req_id, &raw_proof);
}
Ok(())
fn response(&self, peer: &PeerId, io: &IoContext, raw: UntrustedRlp) -> Result<(), Error> {
unimplemented!()
}
// Receive a set of transactions to relay.
@ -1286,7 +678,7 @@ impl LightProtocol {
.map(|x| x.as_val::<UnverifiedTransaction>())
.collect::<Result<_,_>>()?;
debug!(target: "les", "Received {} transactions to relay from peer {}", txs.len(), peer);
debug!(target: "pip", "Received {} transactions to relay from peer {}", txs.len(), peer);
for handler in &self.handlers {
handler.on_transactions(&Ctx {
@ -1305,11 +697,11 @@ fn punish(peer: PeerId, io: &IoContext, e: Error) {
match e.punishment() {
Punishment::None => {}
Punishment::Disconnect => {
debug!(target: "les", "Disconnecting peer {}: {}", peer, e);
debug!(target: "pip", "Disconnecting peer {}: {}", peer, e);
io.disconnect_peer(peer)
}
Punishment::Disable => {
debug!(target: "les", "Disabling peer {}: {}", peer, e);
debug!(target: "pip", "Disabling peer {}: {}", peer, e);
io.disable_peer(peer)
}
}
@ -1339,112 +731,7 @@ impl NetworkProtocolHandler for LightProtocol {
match timer {
TIMEOUT => self.timeout_check(io),
TICK_TIMEOUT => self.tick_handlers(io),
_ => warn!(target: "les", "received timeout on unknown token {}", timer),
}
}
}
// Helper for encoding the request to RLP with the given ID.
fn encode_request(req: &Request, req_id: usize) -> Vec<u8> {
match *req {
Request::Headers(ref headers) => {
let mut stream = RlpStream::new_list(2);
stream.append(&req_id).begin_list(4);
match headers.start {
HashOrNumber::Hash(ref hash) => stream.append(hash),
HashOrNumber::Number(ref num) => stream.append(num),
};
stream
.append(&headers.max)
.append(&headers.skip)
.append(&headers.reverse);
stream.out()
}
Request::Bodies(ref request) => {
let mut stream = RlpStream::new_list(2);
stream.append(&req_id).begin_list(request.block_hashes.len());
for hash in &request.block_hashes {
stream.append(hash);
}
stream.out()
}
Request::Receipts(ref request) => {
let mut stream = RlpStream::new_list(2);
stream.append(&req_id).begin_list(request.block_hashes.len());
for hash in &request.block_hashes {
stream.append(hash);
}
stream.out()
}
Request::StateProofs(ref request) => {
let mut stream = RlpStream::new_list(2);
stream.append(&req_id).begin_list(request.requests.len());
for proof_req in &request.requests {
stream.begin_list(4)
.append(&proof_req.block)
.append(&proof_req.key1);
match proof_req.key2 {
Some(ref key2) => stream.append(key2),
None => stream.append_empty_data(),
};
stream.append(&proof_req.from_level);
}
stream.out()
}
Request::Codes(ref request) => {
let mut stream = RlpStream::new_list(2);
stream.append(&req_id).begin_list(request.code_requests.len());
for code_req in &request.code_requests {
stream.begin_list(2)
.append(&code_req.block_hash)
.append(&code_req.account_key);
}
stream.out()
}
Request::HeaderProofs(ref request) => {
let mut stream = RlpStream::new_list(2);
stream.append(&req_id).begin_list(request.requests.len());
for proof_req in &request.requests {
stream.begin_list(3)
.append(&proof_req.cht_number)
.append(&proof_req.block_number)
.append(&proof_req.from_level);
}
stream.out()
}
Request::TransactionProof(ref request) => {
let mut stream = RlpStream::new_list(2);
stream.append(&req_id).begin_list(7)
.append(&request.at)
.append(&request.from);
match request.action {
Action::Create => stream.append_empty_data(),
Action::Call(ref to) => stream.append(to),
};
stream
.append(&request.gas)
.append(&request.gas_price)
.append(&request.value)
.append(&request.data);
stream.out()
_ => warn!(target: "pip", "received timeout on unknown token {}", timer),
}
}
}

View File

@ -26,7 +26,7 @@
//! Current default costs are picked completely arbitrarily, not based
//! on any empirical timings or mathematical models.
use request;
use request::{self, Request};
use super::packet;
use super::error::Error;
@ -34,10 +34,6 @@ use rlp::*;
use util::U256;
use time::{Duration, SteadyTime};
/// A request cost specification.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Cost(pub U256, pub U256);
/// Credits value.
///
/// Produced and recharged using `FlowParams`.
@ -81,93 +77,43 @@ impl Credits {
/// A cost table, mapping requests to base and per-request costs.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CostTable {
headers: Cost, // cost per header
bodies: Cost,
receipts: Cost,
state_proofs: Cost,
contract_codes: Cost,
header_proofs: Cost,
transaction_proof: Cost, // cost per gas.
base: U256, // cost per packet.
headers: U256, // cost per header
body: U256,
receipts: U256,
account: U256,
storage: U256,
code: U256,
header_proof: U256,
transaction_proof: U256, // cost per gas.
}
impl Default for CostTable {
fn default() -> Self {
// arbitrarily chosen constants.
CostTable {
headers: Cost(100000.into(), 10000.into()),
bodies: Cost(150000.into(), 15000.into()),
receipts: Cost(50000.into(), 5000.into()),
state_proofs: Cost(250000.into(), 25000.into()),
contract_codes: Cost(200000.into(), 20000.into()),
header_proofs: Cost(150000.into(), 15000.into()),
transaction_proof: Cost(100000.into(), 2.into()),
base: 100000.into(),
headers: 10000.into(),
body: 15000.into(),
receipts: 5000.into(),
account: 25000.into(),
storage: 25000.into(),
code: 20000.into(),
header_proof: 15000.into(),
transaction_proof: 2.into(),
}
}
}
impl RlpEncodable for CostTable {
fn rlp_append(&self, s: &mut RlpStream) {
fn append_cost(s: &mut RlpStream, msg_id: u8, cost: &Cost) {
s.begin_list(3)
.append(&msg_id)
.append(&cost.0)
.append(&cost.1);
}
s.begin_list(7);
append_cost(s, packet::GET_BLOCK_HEADERS, &self.headers);
append_cost(s, packet::GET_BLOCK_BODIES, &self.bodies);
append_cost(s, packet::GET_RECEIPTS, &self.receipts);
append_cost(s, packet::GET_PROOFS, &self.state_proofs);
append_cost(s, packet::GET_CONTRACT_CODES, &self.contract_codes);
append_cost(s, packet::GET_HEADER_PROOFS, &self.header_proofs);
append_cost(s, packet::GET_TRANSACTION_PROOF, &self.transaction_proof);
unimplemented!()
}
}
impl RlpDecodable for CostTable {
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
let rlp = decoder.as_rlp();
let mut headers = None;
let mut bodies = None;
let mut receipts = None;
let mut state_proofs = None;
let mut contract_codes = None;
let mut header_proofs = None;
let mut transaction_proof = None;
for row in rlp.iter() {
let msg_id: u8 = row.val_at(0)?;
let cost = {
let base = row.val_at(1)?;
let per = row.val_at(2)?;
Cost(base, per)
};
match msg_id {
packet::GET_BLOCK_HEADERS => headers = Some(cost),
packet::GET_BLOCK_BODIES => bodies = Some(cost),
packet::GET_RECEIPTS => receipts = Some(cost),
packet::GET_PROOFS => state_proofs = Some(cost),
packet::GET_CONTRACT_CODES => contract_codes = Some(cost),
packet::GET_HEADER_PROOFS => header_proofs = Some(cost),
packet::GET_TRANSACTION_PROOF => transaction_proof = Some(cost),
_ => return Err(DecoderError::Custom("Unrecognized message in cost table")),
}
}
Ok(CostTable {
headers: headers.ok_or(DecoderError::Custom("No headers cost specified"))?,
bodies: bodies.ok_or(DecoderError::Custom("No bodies cost specified"))?,
receipts: receipts.ok_or(DecoderError::Custom("No receipts cost specified"))?,
state_proofs: state_proofs.ok_or(DecoderError::Custom("No proofs cost specified"))?,
contract_codes: contract_codes.ok_or(DecoderError::Custom("No contract codes specified"))?,
header_proofs: header_proofs.ok_or(DecoderError::Custom("No header proofs cost specified"))?,
transaction_proof: transaction_proof.ok_or(DecoderError::Custom("No transaction proof gas cost specified"))?,
})
unimplemented!()
}
}
@ -192,17 +138,19 @@ impl FlowParams {
/// Create effectively infinite flow params.
pub fn free() -> Self {
let free_cost = Cost(0.into(), 0.into());
let free_cost: U256 = 0.into();
FlowParams {
limit: (!0u64).into(),
recharge: 1.into(),
costs: CostTable {
base: free_cost.clone(),
headers: free_cost.clone(),
bodies: free_cost.clone(),
body: free_cost.clone(),
receipts: free_cost.clone(),
state_proofs: free_cost.clone(),
contract_codes: free_cost.clone(),
header_proofs: free_cost.clone(),
account: free_cost.clone(),
storage: free_cost.clone(),
code: free_cost.clone(),
header_proof: free_cost.clone(),
transaction_proof: free_cost,
}
}
@ -219,56 +167,20 @@ impl FlowParams {
/// Compute the actual cost of a request, given the kind of request
/// and number of requests made.
pub fn compute_cost(&self, kind: request::Kind, amount: usize) -> U256 {
let cost = match kind {
request::Kind::Headers => &self.costs.headers,
request::Kind::Bodies => &self.costs.bodies,
request::Kind::Receipts => &self.costs.receipts,
request::Kind::StateProofs => &self.costs.state_proofs,
request::Kind::Codes => &self.costs.contract_codes,
request::Kind::HeaderProofs => &self.costs.header_proofs,
request::Kind::TransactionProof => &self.costs.transaction_proof,
};
let amount: U256 = amount.into();
cost.0 + (amount * cost.1)
}
/// Compute the maximum number of costs of a specific kind which can be made
/// with the given amount of credits
/// Saturates at `usize::max()`. This is not a problem in practice because
/// this amount of requests is already prohibitively large.
pub fn max_amount(&self, credits: &Credits, kind: request::Kind) -> usize {
use util::Uint;
use std::usize;
let cost = match kind {
request::Kind::Headers => &self.costs.headers,
request::Kind::Bodies => &self.costs.bodies,
request::Kind::Receipts => &self.costs.receipts,
request::Kind::StateProofs => &self.costs.state_proofs,
request::Kind::Codes => &self.costs.contract_codes,
request::Kind::HeaderProofs => &self.costs.header_proofs,
request::Kind::TransactionProof => &self.costs.transaction_proof,
};
let start = credits.current();
if start <= cost.0 {
return 0;
} else if cost.1 == U256::zero() {
return usize::MAX;
}
let max = (start - cost.0) / cost.1;
if max >= usize::MAX.into() {
usize::MAX
} else {
max.as_u64() as usize
pub fn compute_cost(&self, request: &Request) -> U256 {
match *request {
Request::Headers(ref req) => self.costs.headers * req.max.into(),
Request::HeaderProof(_) => self.costs.header_proof,
Request::Body(_) => self.costs.body,
Request::Receipts(_) => self.costs.receipts,
Request::Account(_) => self.costs.account,
Request::Storage(_) => self.costs.storage,
Request::Code(_) => self.costs.code,
Request::Execution(ref req) => self.costs.transaction_proof * req.gas,
}
}
/// Create initial credits..
/// Create initial credits.
pub fn create_credits(&self) -> Credits {
Credits {
estimate: self.limit,

View File

@ -89,22 +89,7 @@ impl RequestSet {
None => return false,
};
let kind = self.reqs.values()
.next()
.map(|r| r.kind())
.expect("base time implies `reqs` non-empty; qed");
let kind_timeout = match kind {
request::Kind::Headers => timeout::HEADERS,
request::Kind::Bodies => timeout::BODIES,
request::Kind::Receipts => timeout::RECEIPTS,
request::Kind::StateProofs => timeout::PROOFS,
request::Kind::Codes => timeout::CONTRACT_CODES,
request::Kind::HeaderProofs => timeout::HEADER_PROOFS,
request::Kind::TransactionProof => timeout::TRANSACTION_PROOF,
};
base + Duration::milliseconds(kind_timeout) <= now
unimplemented!()
}
/// Collect all pending request ids.

View File

@ -121,7 +121,7 @@ pub trait Provider: Send + Sync {
/// Provide a proof-of-execution for the given transaction proof request.
/// Returns a vector of all state items necessary to execute the transaction.
fn transaction_proof(&self, req: request::TransactionProof) -> Option<Vec<DBValue>>;
fn transaction_proof(&self, req: request::CompleteExecutionRequest) -> Option<request::ExecutionResponse>;
}
// Implementation of a light client data provider for a client.
@ -143,12 +143,12 @@ impl<T: ProvingBlockChainClient + ?Sized> Provider for T {
}
fn block_body(&self, req: request::CompleteBodyRequest) -> Option<request::BodyResponse> {
BlockChainClient::block_body(self, id)
BlockChainClient::block_body(self, BlockId::Hash(req.hash))
.map(|body| ::request::BodyResponse { body: body })
}
fn block_receipts(&self, req: request::CompleteReceiptsRequest) -> Option<request::ReceiptsResponse> {
BlockChainClient::block_receipts(self, hash)
BlockChainClient::block_receipts(self, &req.hash)
.map(|x| ::request::ReceiptsResponse { receipts: ::rlp::decode(&x) })
}
@ -165,7 +165,7 @@ impl<T: ProvingBlockChainClient + ?Sized> Provider for T {
}
fn storage_proof(&self, req: request::CompleteStorageRequest) -> Option<request::StorageResponse> {
self.prove_account(req.address_hash, req.key_hash, BlockId::Hash(req.block_hash)).map(|(proof, item) | {
self.prove_storage(req.address_hash, req.key_hash, BlockId::Hash(req.block_hash)).map(|(proof, item) | {
::request::StorageResponse {
proof: proof,
value: item,
@ -173,7 +173,7 @@ impl<T: ProvingBlockChainClient + ?Sized> Provider for T {
})
}
fn contract_code(&self, req: request::ContractCode) -> Option<request::CodeResponse> {
fn contract_code(&self, req: request::CompleteCodeRequest) -> Option<request::CodeResponse> {
self.state_data(&req.code_hash)
.map(|code| ::request::CodeResponse { code: code })
}
@ -239,7 +239,7 @@ impl<T: ProvingBlockChainClient + ?Sized> Provider for T {
fn transaction_proof(&self, req: request::CompleteExecutionRequest) -> Option<request::ExecutionResponse> {
use ethcore::transaction::Transaction;
let id = BlockId::Hash(req.at);
let id = BlockId::Hash(req.block_hash);
let nonce = match self.nonce(&req.from, id.clone()) {
Some(nonce) => nonce,
None => return None,
@ -321,7 +321,7 @@ impl<L: AsLightClient + Send + Sync> Provider for LightProvider<L> {
None
}
fn transaction_proof(&self, _req: request::TransactionProof) -> Option<Vec<DBValue>> {
fn transaction_proof(&self, _req: request::CompleteExecutionRequest) -> Option<request::ExecutionResponse> {
None
}

View File

@ -0,0 +1,116 @@
// 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/>.
//! Request chain builder utility.
//! Push requests with `push`. Back-references and data required to verify responses must be
//! supplied as well.
use std::collections::{HashMap, VecDeque};
use request::{
IncompleteRequest, CompleteRequest, Request,
Field, OutputKind, Output, NoSuchOutput, Response,
};
/// Build chained requests. Push them onto the series with `push`,
/// and produce a `Requests` object with `build`. Outputs are checked for consistency.
#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub struct RequestBuilder {
output_kinds: HashMap<(usize, usize), OutputKind>,
requests: Vec<Request>,
}
impl RequestBuilder {
/// Attempt to push a request onto the request chain. Fails if the request
/// references a non-existant output of a prior request.
pub fn push(&mut self, request: Request) -> Result<(), NoSuchOutput> {
request.check_outputs(|req, idx, kind| {
match self.output_kinds.get(&(req, idx)) {
Some(k) if k == &kind => Ok(()),
_ => Err(NoSuchOutput),
}
})?;
let req_idx = self.requests.len();
request.note_outputs(|idx, kind| { self.output_kinds.insert((req_idx, idx), kind); });
self.requests.push(request);
Ok(())
}
/// Convert this into a "requests" object.
pub fn build(self) -> Requests {
Requests {
output_kinds: self.output_kinds,
outputs: HashMap::new(),
requests: self.requests,
offset: 0,
}
}
}
/// Requests pending responses.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Requests {
output_kinds: HashMap<(usize, usize), OutputKind>,
outputs: HashMap<(usize, usize), Output>,
requests: Vec<Request>,
offset: usize, // offset for splitting.
}
impl Requests {
/// For each request, produce responses for each.
/// The responses vector produced goes up to the point where the responder
/// first returns `None`, an invalid response, or until all requests have been responded to.
pub fn respond_to_all<F>(mut self, responder: F) -> Vec<Response>
where F: Fn(CompleteRequest) -> Option<Response>
{
let mut responses = Vec::new();
let mut found_bad = false;
let offset = self.offset;
let output_kinds = self.output_kinds;
let mut outputs = self.outputs;
for (idx, req) in self.requests.into_iter().enumerate().map(|(idx, req)| (idx + offset, req)) {
let complete = req.fill(|req_idx, out_idx| outputs.get(&(req_idx, out_idx)).cloned().ok_or(NoSuchOutput))
.expect("All outputs checked as invariant of `Requests` object; qed");
match responder(complete) {
Some(response) => {
response.fill_outputs(|out_idx, output| {
match output_kinds.get(&(idx, out_idx)) {
None => {},
Some(out) => if out == &output.kind() {
outputs.insert((idx, out_idx), output);
} else {
// output kind doesn't match expected.
found_bad = true;
}
}
});
if found_bad {
return responses;
}
responses.push(response);
}
None => return responses,
}
}
responses
}
/// Get access to the underlying slice of requests.
pub fn requests(&self) -> &[Request] { &self.requests }
}

View File

@ -19,7 +19,7 @@
use std::collections::HashMap;
use ethcore::transaction::Action;
use rlp::{Encodable, Decodable, Decoder, DecoderError, RlpStream, Stream};
use rlp::{Encodable, Decodable, Decoder, DecoderError, RlpStream, Stream, View};
use util::{Address, H256, U256, Uint};
// re-exports of request types.
@ -65,6 +65,7 @@ pub use self::execution::{
};
/// Error indicating a reference to a non-existent or wrongly-typed output.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct NoSuchOutput;
/// An input to a request.
@ -77,7 +78,7 @@ pub enum Field<T> {
BackReference(usize, usize),
}
impl From<T> for Field<T> {
impl<T> From<T> for Field<T> {
fn from(val: T) -> Self {
Field::Scalar(val)
}
@ -119,7 +120,8 @@ pub enum Output {
}
impl Output {
fn kind(&self) -> OutputKind {
/// Get the output kind.
pub fn kind(&self) -> OutputKind {
match *self {
Output::Hash(_) => OutputKind::Hash,
Output::Number(_) => OutputKind::Number,
@ -158,6 +160,24 @@ impl From<u64> for HashOrNumber {
}
}
impl Decodable for HashOrNumber {
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
let rlp = decoder.as_rlp();
rlp.val_at::<H256>(0).map(HashOrNumber::Hash)
.or_else(|_| rlp.val_at(0).map(HashOrNumber::Number))
}
}
impl Encodable for HashOrNumber {
fn rlp_append(&self, s: &mut RlpStream) {
match *self {
HashOrNumber::Hash(ref hash) => s.append(hash),
HashOrNumber::Number(ref num) => s.append(num),
};
}
}
/// All request types, as they're sent over the network.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Request {
@ -305,13 +325,20 @@ impl IncompleteRequest for Request {
pub enum Kind {
/// A request for headers.
Headers = 0,
/// A request for a header proof.
HeaderProof = 1,
// TransactionIndex = 2,
/// A request for block receipts.
Receipts = 3,
/// A request for a block body.
Body = 4,
/// A request for an account + merkle proof.
Account = 5,
/// A request for contract storage + merkle proof
Storage = 6,
/// A request for contract.
Code = 7,
/// A request for transaction execution + state proof.
Execution = 8,
}
@ -336,7 +363,7 @@ impl Decodable for Kind {
impl Encodable for Kind {
fn rlp_append(&self, s: &mut RlpStream) {
s.append(self as &u8);
s.append(&(*self as u8));
}
}
@ -366,14 +393,14 @@ impl Response {
/// Fill reusable outputs by writing them into the function.
pub fn fill_outputs<F>(&self, mut f: F) where F: FnMut(usize, Output) {
match *self {
Response::Headers(res) => res.fill_outputs(f),
Response::HeaderProof(res) => res.fill_outputs(f),
Response::Receipts(res) => res.fill_outputs(f),
Response::Body(res) => res.fill_outputs(f),
Response::Account(res) => res.fill_outputs(f),
Response::Storage(res) => res.fill_outputs(f),
Response::Code(res) => res.fill_outputs(f),
Response::Execution(res) => res.fill_outputs(f),
Response::Headers(ref res) => res.fill_outputs(f),
Response::HeaderProof(ref res) => res.fill_outputs(f),
Response::Receipts(ref res) => res.fill_outputs(f),
Response::Body(ref res) => res.fill_outputs(f),
Response::Account(ref res) => res.fill_outputs(f),
Response::Storage(ref res) => res.fill_outputs(f),
Response::Code(ref res) => res.fill_outputs(f),
Response::Execution(ref res) => res.fill_outputs(f),
}
}
@ -386,7 +413,7 @@ impl Response {
Response::Account(_) => Kind::Account,
Response::Storage(_) => Kind::Storage,
Response::Code(_) => Kind::Code,
Respnse::Execution(_) => Kind::Execution,
Response::Execution(_) => Kind::Execution,
}
}
}
@ -427,6 +454,7 @@ impl Encodable for Response {
/// A potentially incomplete request.
pub trait IncompleteRequest: Sized {
/// The complete variant of this request.
type Complete;
/// Check prior outputs against the needed inputs.
@ -453,7 +481,6 @@ pub mod header {
use super::{Field, HashOrNumber, NoSuchOutput, OutputKind, Output};
use ethcore::encoded;
use rlp::{Encodable, Decodable, Decoder, DecoderError, RlpStream, Stream, View};
use util::U256;
/// Potentially incomplete headers request.
#[derive(Debug, Clone, PartialEq, Eq)]
@ -461,9 +488,9 @@ pub mod header {
/// Start block.
pub start: Field<HashOrNumber>,
/// Skip between.
pub skip: U256,
pub skip: u64,
/// Maximum to return.
pub max: U256,
pub max: u64,
/// Whether to reverse from start.
pub reverse: bool,
}
@ -499,7 +526,7 @@ pub mod header {
match self.start {
Field::Scalar(_) => Ok(()),
Field::BackReference(req, idx) =>
f(req, idx, OutputKind::Hash).or_else(|| f(req, idx, OutputKind::Number))
f(req, idx, OutputKind::Hash).or_else(|_| f(req, idx, OutputKind::Number))
}
}
@ -532,9 +559,9 @@ pub mod header {
/// Start block.
pub start: HashOrNumber,
/// Skip between.
pub skip: U256,
pub skip: u64,
/// Maximum to return.
pub max: U256,
pub max: u64,
/// Whether to reverse from start.
pub reverse: bool,
}
@ -695,7 +722,7 @@ pub mod block_receipts {
use super::{Field, NoSuchOutput, OutputKind, Output};
use ethcore::receipt::Receipt;
use rlp::{Encodable, Decodable, Decoder, DecoderError, RlpStream, Stream, View};
use util::{Bytes, U256, H256};
use util::H256;
/// Potentially incomplete block receipts request.
#[derive(Debug, Clone, PartialEq, Eq)]
@ -725,7 +752,7 @@ pub mod block_receipts {
fn check_outputs<F>(&self, mut f: F) -> Result<(), NoSuchOutput>
where F: FnMut(usize, usize, OutputKind) -> Result<(), NoSuchOutput>
{
match self.num {
match self.hash {
Field::Scalar(_) => Ok(()),
Field::BackReference(req, idx) => f(req, idx, OutputKind::Hash),
}
@ -791,7 +818,7 @@ pub mod block_body {
use super::{Field, NoSuchOutput, OutputKind, Output};
use ethcore::encoded;
use rlp::{Encodable, Decodable, Decoder, DecoderError, RlpStream, Stream, View};
use util::{Bytes, U256, H256};
use util::H256;
/// Potentially incomplete block body request.
#[derive(Debug, Clone, PartialEq, Eq)]
@ -821,7 +848,7 @@ pub mod block_body {
fn check_outputs<F>(&self, mut f: F) -> Result<(), NoSuchOutput>
where F: FnMut(usize, usize, OutputKind) -> Result<(), NoSuchOutput>
{
match self.num {
match self.hash {
Field::Scalar(_) => Ok(()),
Field::BackReference(req, idx) => f(req, idx, OutputKind::Hash),
}
@ -869,14 +896,14 @@ pub mod block_body {
impl Decodable for Response {
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
use ethcore::header::Header as FullHeader;
use ethcore::transaction::SignedTransaction;
use ethcore::transaction::UnverifiedTransaction;
let rlp = decoder.as_rlp();
let body_rlp = rlp.at(0)?;
// check body validity.
let _: Vec<FullHeader> = rlp.val_at(0)?;
let _: Vec<SignedTransaction> = rlp.val_at(1)?;
let _: Vec<UnverifiedTransaction> = rlp.val_at(1)?;
Ok(Response {
body: encoded::Body::new(body_rlp.as_raw().to_owned()),
@ -895,7 +922,6 @@ pub mod block_body {
/// A request for an account proof.
pub mod account {
use super::{Field, NoSuchOutput, OutputKind, Output};
use ethcore::encoded;
use rlp::{Encodable, Decodable, Decoder, DecoderError, RlpStream, Stream, View};
use util::{Bytes, U256, H256};
@ -1028,7 +1054,7 @@ pub mod account {
.append(&self.nonce)
.append(&self.balance)
.append(&self.code_hash)
.append(&self.storage_root)
.append(&self.storage_root);
}
}
}
@ -1036,9 +1062,8 @@ pub mod account {
/// A request for a storage proof.
pub mod storage {
use super::{Field, NoSuchOutput, OutputKind, Output};
use ethcore::encoded;
use rlp::{Encodable, Decodable, Decoder, DecoderError, RlpStream, Stream, View};
use util::{Bytes, U256, H256};
use util::{Bytes, H256};
/// Potentially incomplete request for an storage proof.
#[derive(Debug, Clone, PartialEq, Eq)]
@ -1182,9 +1207,8 @@ pub mod storage {
/// A request for contract code.
pub mod contract_code {
use super::{Field, NoSuchOutput, OutputKind, Output};
use ethcore::encoded;
use rlp::{Encodable, Decodable, Decoder, DecoderError, RlpStream, Stream, View};
use util::{Bytes, U256, H256};
use util::{Bytes, H256};
/// Potentially incomplete contract code request.
#[derive(Debug, Clone, PartialEq, Eq)]
@ -1299,7 +1323,6 @@ pub mod contract_code {
/// A request for proof of execution.
pub mod execution {
use super::{Field, NoSuchOutput, OutputKind, Output};
use ethcore::encoded;
use ethcore::transaction::Action;
use rlp::{Encodable, Decodable, Decoder, DecoderError, RlpStream, Stream, View};
use util::{Bytes, Address, U256, H256, DBValue};
@ -1328,7 +1351,7 @@ pub mod execution {
let rlp = decoder.as_rlp();
Ok(Incomplete {
block_hash: rlp.val_at(0)?,
address: rlp.val_at(1)?,
from: rlp.val_at(1)?,
action: rlp.val_at(2)?,
gas: rlp.val_at(3)?,
gas_price: rlp.val_at(4)?,
@ -1344,7 +1367,7 @@ pub mod execution {
.append(&self.block_hash)
.append(&self.from);
match *self.action {
match self.action {
Action::Create => s.append_empty_data(),
Action::Call(ref addr) => s.append(addr),
};
@ -1432,7 +1455,7 @@ pub mod execution {
let mut items = Vec::new();
for raw_item in rlp.at(0)?.iter() {
let mut item = DBValue::new();
item.append_slice(raw_item.data());
item.append_slice(raw_item.data()?);
items.push(item);
}
@ -1444,7 +1467,7 @@ pub mod execution {
impl Encodable for Response {
fn rlp_append(&self, s: &mut RlpStream) {
s.begin_list(&self.items.len());
s.begin_list(self.items.len());
for item in &self.items {
s.append(&&**item);