// Copyright 2015-2018 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 .
//! Light protocol request types.
use rlp::{Encodable, Decodable, DecoderError, RlpStream, Rlp};
use ethereum_types::H256;
mod batch;
// re-exports of request types.
pub use self::header::{
Complete as CompleteHeadersRequest,
Incomplete as IncompleteHeadersRequest,
Response as HeadersResponse
};
pub use self::header_proof::{
Complete as CompleteHeaderProofRequest,
Incomplete as IncompleteHeaderProofRequest,
Response as HeaderProofResponse
};
pub use self::transaction_index::{
Complete as CompleteTransactionIndexRequest,
Incomplete as IncompleteTransactionIndexRequest,
Response as TransactionIndexResponse
};
pub use self::block_body::{
Complete as CompleteBodyRequest,
Incomplete as IncompleteBodyRequest,
Response as BodyResponse
};
pub use self::block_receipts::{
Complete as CompleteReceiptsRequest,
Incomplete as IncompleteReceiptsRequest,
Response as ReceiptsResponse
};
pub use self::account::{
Complete as CompleteAccountRequest,
Incomplete as IncompleteAccountRequest,
Response as AccountResponse,
};
pub use self::storage::{
Complete as CompleteStorageRequest,
Incomplete as IncompleteStorageRequest,
Response as StorageResponse
};
pub use self::contract_code::{
Complete as CompleteCodeRequest,
Incomplete as IncompleteCodeRequest,
Response as CodeResponse,
};
pub use self::execution::{
Complete as CompleteExecutionRequest,
Incomplete as IncompleteExecutionRequest,
Response as ExecutionResponse,
};
pub use self::epoch_signal::{
Complete as CompleteSignalRequest,
Incomplete as IncompleteSignalRequest,
Response as SignalResponse,
};
pub use self::batch::{Batch, Builder};
/// Error indicating a reference to a non-existent or wrongly-typed output.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct NoSuchOutput;
/// Wrong kind of response corresponding to request.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct WrongKind;
/// Error on processing a response.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ResponseError {
/// Error in validity.
Validity(T),
/// No responses expected.
Unexpected,
}
/// An input to a request.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Field {
/// A pre-specified input.
Scalar(T),
/// An input which can be resolved later on.
/// (Request index, output index)
BackReference(usize, usize),
}
impl Field {
/// Helper for creating a new back-reference field.
pub fn back_ref(idx: usize, req: usize) -> Self {
Field::BackReference(idx, req)
}
/// map a scalar into some other item.
pub fn map(self, f: F) -> Field where F: FnOnce(T) -> U {
match self {
Field::Scalar(x) => Field::Scalar(f(x)),
Field::BackReference(req, idx) => Field::BackReference(req, idx),
}
}
/// Attempt to get a reference to the inner scalar.
pub fn as_ref(&self) -> Option<&T> {
match *self {
Field::Scalar(ref x) => Some(x),
Field::BackReference(_, _) => None,
}
}
// attempt conversion into scalar value.
fn into_scalar(self) -> Result {
match self {
Field::Scalar(val) => Ok(val),
_ => Err(NoSuchOutput),
}
}
fn adjust_req(&mut self, mut mapping: F) where F: FnMut(usize) -> usize {
if let Field::BackReference(ref mut req_idx, _) = *self {
*req_idx = mapping(*req_idx)
}
}
}
impl From for Field {
fn from(val: T) -> Self {
Field::Scalar(val)
}
}
impl Decodable for Field {
fn decode(rlp: &Rlp) -> Result {
match rlp.val_at::(0)? {
0 => Ok(Field::Scalar(rlp.val_at::(1)?)),
1 => Ok({
let inner_rlp = rlp.at(1)?;
Field::BackReference(inner_rlp.val_at(0)?, inner_rlp.val_at(1)?)
}),
_ => Err(DecoderError::Custom("Unknown discriminant for PIP field.")),
}
}
}
impl Encodable for Field {
fn rlp_append(&self, s: &mut RlpStream) {
s.begin_list(2);
match *self {
Field::Scalar(ref data) => {
s.append(&0u8).append(data);
}
Field::BackReference(ref req, ref idx) => {
s.append(&1u8).begin_list(2).append(req).append(idx);
}
}
}
}
/// Request outputs which can be reused as inputs.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Output {
/// A 32-byte hash output.
Hash(H256),
/// An unsigned-integer output.
Number(u64),
}
impl Output {
/// Get the output kind.
pub fn kind(&self) -> OutputKind {
match *self {
Output::Hash(_) => OutputKind::Hash,
Output::Number(_) => OutputKind::Number,
}
}
}
/// Response output kinds which can be used as back-references.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum OutputKind {
/// A 32-byte hash output.
Hash,
/// An unsigned-integer output.
Number,
}
/// Either a hash or a number.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum HashOrNumber {
/// Block hash variant.
Hash(H256),
/// Block number variant.
Number(u64),
}
impl From for HashOrNumber {
fn from(hash: H256) -> Self {
HashOrNumber::Hash(hash)
}
}
impl From for HashOrNumber {
fn from(num: u64) -> Self {
HashOrNumber::Number(num)
}
}
impl Decodable for HashOrNumber {
fn decode(rlp: &Rlp) -> Result {
rlp.as_val::().map(HashOrNumber::Hash)
.or_else(|_| rlp.as_val().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),
};
}
}
/// Type alias for "network requests".
pub type NetworkRequests = Batch;
/// All request types, as they're sent over the network.
/// They may be incomplete, with back-references to outputs
/// of prior requests.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Request {
/// A request for block headers.
Headers(IncompleteHeadersRequest),
/// A request for a header proof (from a CHT)
HeaderProof(IncompleteHeaderProofRequest),
/// A request for a transaction index by hash.
TransactionIndex(IncompleteTransactionIndexRequest),
/// A request for a block's receipts.
Receipts(IncompleteReceiptsRequest),
/// A request for a block body.
Body(IncompleteBodyRequest),
/// A request for a merkle proof of an account.
Account(IncompleteAccountRequest),
/// A request for a merkle proof of contract storage.
Storage(IncompleteStorageRequest),
/// A request for contract code.
Code(IncompleteCodeRequest),
/// A request for proof of execution,
Execution(IncompleteExecutionRequest),
/// A request for an epoch signal.
Signal(IncompleteSignalRequest),
}
/// All request types, in an answerable state.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum CompleteRequest {
/// A request for block headers.
Headers(CompleteHeadersRequest),
/// A request for a header proof (from a CHT)
HeaderProof(CompleteHeaderProofRequest),
/// A request for a transaction index by hash.
TransactionIndex(CompleteTransactionIndexRequest),
/// A request for a block's receipts.
Receipts(CompleteReceiptsRequest),
/// A request for a block body.
Body(CompleteBodyRequest),
/// A request for a merkle proof of an account.
Account(CompleteAccountRequest),
/// A request for a merkle proof of contract storage.
Storage(CompleteStorageRequest),
/// A request for contract code.
Code(CompleteCodeRequest),
/// A request for proof of execution,
Execution(CompleteExecutionRequest),
/// A request for an epoch signal.
Signal(CompleteSignalRequest),
}
impl CompleteRequest {
/// Inspect the kind of this response.
pub fn kind(&self) -> Kind {
match *self {
CompleteRequest::Headers(_) => Kind::Headers,
CompleteRequest::HeaderProof(_) => Kind::HeaderProof,
CompleteRequest::TransactionIndex(_) => Kind::TransactionIndex,
CompleteRequest::Receipts(_) => Kind::Receipts,
CompleteRequest::Body(_) => Kind::Body,
CompleteRequest::Account(_) => Kind::Account,
CompleteRequest::Storage(_) => Kind::Storage,
CompleteRequest::Code(_) => Kind::Code,
CompleteRequest::Execution(_) => Kind::Execution,
CompleteRequest::Signal(_) => Kind::Signal,
}
}
}
impl Request {
/// Get the request kind.
pub fn kind(&self) -> Kind {
match *self {
Request::Headers(_) => Kind::Headers,
Request::HeaderProof(_) => Kind::HeaderProof,
Request::TransactionIndex(_) => Kind::TransactionIndex,
Request::Receipts(_) => Kind::Receipts,
Request::Body(_) => Kind::Body,
Request::Account(_) => Kind::Account,
Request::Storage(_) => Kind::Storage,
Request::Code(_) => Kind::Code,
Request::Execution(_) => Kind::Execution,
Request::Signal(_) => Kind::Signal,
}
}
}
impl Decodable for Request {
fn decode(rlp: &Rlp) -> Result {
match rlp.val_at::(0)? {
Kind::Headers => Ok(Request::Headers(rlp.val_at(1)?)),
Kind::HeaderProof => Ok(Request::HeaderProof(rlp.val_at(1)?)),
Kind::TransactionIndex => Ok(Request::TransactionIndex(rlp.val_at(1)?)),
Kind::Receipts => Ok(Request::Receipts(rlp.val_at(1)?)),
Kind::Body => Ok(Request::Body(rlp.val_at(1)?)),
Kind::Account => Ok(Request::Account(rlp.val_at(1)?)),
Kind::Storage => Ok(Request::Storage(rlp.val_at(1)?)),
Kind::Code => Ok(Request::Code(rlp.val_at(1)?)),
Kind::Execution => Ok(Request::Execution(rlp.val_at(1)?)),
Kind::Signal => Ok(Request::Signal(rlp.val_at(1)?)),
}
}
}
impl Encodable for Request {
fn rlp_append(&self, s: &mut RlpStream) {
s.begin_list(2);
// hack around https://github.com/paritytech/parity-ethereum/issues/4356
Encodable::rlp_append(&self.kind(), s);
match *self {
Request::Headers(ref req) => s.append(req),
Request::HeaderProof(ref req) => s.append(req),
Request::TransactionIndex(ref req) => s.append(req),
Request::Receipts(ref req) => s.append(req),
Request::Body(ref req) => s.append(req),
Request::Account(ref req) => s.append(req),
Request::Storage(ref req) => s.append(req),
Request::Code(ref req) => s.append(req),
Request::Execution(ref req) => s.append(req),
Request::Signal(ref req) => s.append(req),
};
}
}
impl IncompleteRequest for Request {
type Complete = CompleteRequest;
type Response = Response;
fn check_outputs(&self, f: F) -> Result<(), NoSuchOutput>
where F: FnMut(usize, usize, OutputKind) -> Result<(), NoSuchOutput>
{
match *self {
Request::Headers(ref req) => req.check_outputs(f),
Request::HeaderProof(ref req) => req.check_outputs(f),
Request::TransactionIndex(ref req) => req.check_outputs(f),
Request::Receipts(ref req) => req.check_outputs(f),
Request::Body(ref req) => req.check_outputs(f),
Request::Account(ref req) => req.check_outputs(f),
Request::Storage(ref req) => req.check_outputs(f),
Request::Code(ref req) => req.check_outputs(f),
Request::Execution(ref req) => req.check_outputs(f),
Request::Signal(ref req) => req.check_outputs(f),
}
}
fn note_outputs(&self, f: F) where F: FnMut(usize, OutputKind) {
match *self {
Request::Headers(ref req) => req.note_outputs(f),
Request::HeaderProof(ref req) => req.note_outputs(f),
Request::TransactionIndex(ref req) => req.note_outputs(f),
Request::Receipts(ref req) => req.note_outputs(f),
Request::Body(ref req) => req.note_outputs(f),
Request::Account(ref req) => req.note_outputs(f),
Request::Storage(ref req) => req.note_outputs(f),
Request::Code(ref req) => req.note_outputs(f),
Request::Execution(ref req) => req.note_outputs(f),
Request::Signal(ref req) => req.note_outputs(f),
}
}
fn fill(&mut self, oracle: F) where F: Fn(usize, usize) -> Result