openethereum/ethcore/light/src/types/request/mod.rs

1943 lines
55 KiB
Rust

// 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 <http://www.gnu.org/licenses/>.
//! 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<T> {
/// Error in validity.
Validity(T),
/// No responses expected.
Unexpected,
}
/// An input to a request.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Field<T> {
/// A pre-specified input.
Scalar(T),
/// An input which can be resolved later on.
/// (Request index, output index)
BackReference(usize, usize),
}
impl<T> Field<T> {
/// 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<F, U>(self, f: F) -> Field<U> 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<T, NoSuchOutput> {
match self {
Field::Scalar(val) => Ok(val),
_ => Err(NoSuchOutput),
}
}
fn adjust_req<F>(&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<T> From<T> for Field<T> {
fn from(val: T) -> Self {
Field::Scalar(val)
}
}
impl<T: Decodable> Decodable for Field<T> {
fn decode(rlp: &Rlp) -> Result<Self, DecoderError> {
match rlp.val_at::<u8>(0)? {
0 => Ok(Field::Scalar(rlp.val_at::<T>(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<T: Encodable> Encodable for Field<T> {
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<H256> for HashOrNumber {
fn from(hash: H256) -> Self {
HashOrNumber::Hash(hash)
}
}
impl From<u64> for HashOrNumber {
fn from(num: u64) -> Self {
HashOrNumber::Number(num)
}
}
impl Decodable for HashOrNumber {
fn decode(rlp: &Rlp) -> Result<Self, DecoderError> {
rlp.as_val::<H256>().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<Request>;
/// 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<Self, DecoderError> {
match rlp.val_at::<Kind>(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<F>(&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<F>(&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<F>(&mut self, oracle: F) where F: Fn(usize, usize) -> Result<Output, NoSuchOutput> {
match *self {
Request::Headers(ref mut req) => req.fill(oracle),
Request::HeaderProof(ref mut req) => req.fill(oracle),
Request::TransactionIndex(ref mut req) => req.fill(oracle),
Request::Receipts(ref mut req) => req.fill(oracle),
Request::Body(ref mut req) => req.fill(oracle),
Request::Account(ref mut req) => req.fill(oracle),
Request::Storage(ref mut req) => req.fill(oracle),
Request::Code(ref mut req) => req.fill(oracle),
Request::Execution(ref mut req) => req.fill(oracle),
Request::Signal(ref mut req) => req.fill(oracle),
}
}
fn complete(self) -> Result<Self::Complete, NoSuchOutput> {
match self {
Request::Headers(req) => req.complete().map(CompleteRequest::Headers),
Request::HeaderProof(req) => req.complete().map(CompleteRequest::HeaderProof),
Request::TransactionIndex(req) => req.complete().map(CompleteRequest::TransactionIndex),
Request::Receipts(req) => req.complete().map(CompleteRequest::Receipts),
Request::Body(req) => req.complete().map(CompleteRequest::Body),
Request::Account(req) => req.complete().map(CompleteRequest::Account),
Request::Storage(req) => req.complete().map(CompleteRequest::Storage),
Request::Code(req) => req.complete().map(CompleteRequest::Code),
Request::Execution(req) => req.complete().map(CompleteRequest::Execution),
Request::Signal(req) => req.complete().map(CompleteRequest::Signal),
}
}
fn adjust_refs<F>(&mut self, mapping: F) where F: FnMut(usize) -> usize {
match *self {
Request::Headers(ref mut req) => req.adjust_refs(mapping),
Request::HeaderProof(ref mut req) => req.adjust_refs(mapping),
Request::TransactionIndex(ref mut req) => req.adjust_refs(mapping),
Request::Receipts(ref mut req) => req.adjust_refs(mapping),
Request::Body(ref mut req) => req.adjust_refs(mapping),
Request::Account(ref mut req) => req.adjust_refs(mapping),
Request::Storage(ref mut req) => req.adjust_refs(mapping),
Request::Code(ref mut req) => req.adjust_refs(mapping),
Request::Execution(ref mut req) => req.adjust_refs(mapping),
Request::Signal(ref mut req) => req.adjust_refs(mapping),
}
}
}
impl CheckedRequest for Request {
type Extract = ();
type Error = WrongKind;
type Environment = ();
fn check_response(&self, _: &Self::Complete, _: &(), response: &Response) -> Result<(), WrongKind> {
if self.kind() == response.kind() {
Ok(())
} else {
Err(WrongKind)
}
}
}
/// Kinds of requests.
/// Doubles as the "ID" field of the request.
#[repr(u8)]
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Serialize, Deserialize)]
pub enum Kind {
/// A request for headers.
Headers = 0,
/// A request for a header proof.
HeaderProof = 1,
/// A request for a transaction index.
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,
/// A request for epoch transition signal.
Signal = 9,
}
impl Decodable for Kind {
fn decode(rlp: &Rlp) -> Result<Self, DecoderError> {
match rlp.as_val::<u8>()? {
0 => Ok(Kind::Headers),
1 => Ok(Kind::HeaderProof),
2 => Ok(Kind::TransactionIndex),
3 => Ok(Kind::Receipts),
4 => Ok(Kind::Body),
5 => Ok(Kind::Account),
6 => Ok(Kind::Storage),
7 => Ok(Kind::Code),
8 => Ok(Kind::Execution),
9 => Ok(Kind::Signal),
_ => Err(DecoderError::Custom("Unknown PIP request ID.")),
}
}
}
impl Encodable for Kind {
fn rlp_append(&self, s: &mut RlpStream) {
s.append(&(*self as u8));
}
}
/// All response types.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Response {
/// A response for block headers.
Headers(HeadersResponse),
/// A response for a header proof (from a CHT)
HeaderProof(HeaderProofResponse),
/// A response for a transaction index.
TransactionIndex(TransactionIndexResponse),
/// A response for a block's receipts.
Receipts(ReceiptsResponse),
/// A response for a block body.
Body(BodyResponse),
/// A response for a merkle proof of an account.
Account(AccountResponse),
/// A response for a merkle proof of contract storage.
Storage(StorageResponse),
/// A response for contract code.
Code(CodeResponse),
/// A response for proof of execution,
Execution(ExecutionResponse),
/// A response for epoch change signal.
Signal(SignalResponse),
}
impl ResponseLike for Response {
/// Fill reusable outputs by writing them into the function.
fn fill_outputs<F>(&self, f: F) where F: FnMut(usize, Output) {
match *self {
Response::Headers(ref res) => res.fill_outputs(f),
Response::HeaderProof(ref res) => res.fill_outputs(f),
Response::TransactionIndex(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),
Response::Signal(ref res) => res.fill_outputs(f),
}
}
}
impl Response {
/// Inspect the kind of this response.
pub fn kind(&self) -> Kind {
match *self {
Response::Headers(_) => Kind::Headers,
Response::HeaderProof(_) => Kind::HeaderProof,
Response::TransactionIndex(_) => Kind::TransactionIndex,
Response::Receipts(_) => Kind::Receipts,
Response::Body(_) => Kind::Body,
Response::Account(_) => Kind::Account,
Response::Storage(_) => Kind::Storage,
Response::Code(_) => Kind::Code,
Response::Execution(_) => Kind::Execution,
Response::Signal(_) => Kind::Signal,
}
}
}
impl Decodable for Response {
fn decode(rlp: &Rlp) -> Result<Self, DecoderError> {
match rlp.val_at::<Kind>(0)? {
Kind::Headers => Ok(Response::Headers(rlp.val_at(1)?)),
Kind::HeaderProof => Ok(Response::HeaderProof(rlp.val_at(1)?)),
Kind::TransactionIndex => Ok(Response::TransactionIndex(rlp.val_at(1)?)),
Kind::Receipts => Ok(Response::Receipts(rlp.val_at(1)?)),
Kind::Body => Ok(Response::Body(rlp.val_at(1)?)),
Kind::Account => Ok(Response::Account(rlp.val_at(1)?)),
Kind::Storage => Ok(Response::Storage(rlp.val_at(1)?)),
Kind::Code => Ok(Response::Code(rlp.val_at(1)?)),
Kind::Execution => Ok(Response::Execution(rlp.val_at(1)?)),
Kind::Signal => Ok(Response::Signal(rlp.val_at(1)?)),
}
}
}
impl Encodable for Response {
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 {
Response::Headers(ref res) => s.append(res),
Response::HeaderProof(ref res) => s.append(res),
Response::TransactionIndex(ref res) => s.append(res),
Response::Receipts(ref res) => s.append(res),
Response::Body(ref res) => s.append(res),
Response::Account(ref res) => s.append(res),
Response::Storage(ref res) => s.append(res),
Response::Code(ref res) => s.append(res),
Response::Execution(ref res) => s.append(res),
Response::Signal(ref res) => s.append(res),
};
}
}
/// A potentially incomplete request.
pub trait IncompleteRequest: Sized {
/// The complete variant of this request.
type Complete;
/// The response to this request.
type Response: ResponseLike;
/// Check prior outputs against the needed inputs.
///
/// This is called to ensure consistency of this request with
/// others in the same packet.
fn check_outputs<F>(&self, f: F) -> Result<(), NoSuchOutput>
where F: FnMut(usize, usize, OutputKind) -> Result<(), NoSuchOutput>;
/// Note that this request will produce the following outputs.
fn note_outputs<F>(&self, f: F) where F: FnMut(usize, OutputKind);
/// Fill fields of the request.
///
/// This function is provided an "output oracle" which allows fetching of
/// prior request outputs.
/// Only outputs previously checked with `check_outputs` may be available.
fn fill<F>(&mut self, oracle: F) where F: Fn(usize, usize) -> Result<Output, NoSuchOutput>;
/// Attempt to convert this request into its complete variant.
/// Will succeed if all fields have been filled, will fail otherwise.
fn complete(self) -> Result<Self::Complete, NoSuchOutput>;
/// Adjust back-reference request indices.
fn adjust_refs<F>(&mut self, mapping: F) where F: FnMut(usize) -> usize;
}
/// A request which can be checked against its response for more validity.
pub trait CheckedRequest: IncompleteRequest {
/// Data extracted during the check.
type Extract;
/// Error encountered during the check.
type Error;
/// Environment passed to response check.
type Environment;
/// Check whether the response matches (beyond the type).
fn check_response(&self, &Self::Complete, &Self::Environment, &Self::Response) -> Result<Self::Extract, Self::Error>;
}
/// A response-like object.
///
/// These contain re-usable outputs.
pub trait ResponseLike {
/// Write all re-usable outputs into the provided function.
fn fill_outputs<F>(&self, output_store: F) where F: FnMut(usize, Output);
}
/// Header request.
pub mod header {
use super::{Field, HashOrNumber, NoSuchOutput, OutputKind, Output};
use ethcore::encoded;
use rlp::{Encodable, Decodable, DecoderError, RlpStream, Rlp};
/// Potentially incomplete headers request.
#[derive(Debug, Clone, PartialEq, Eq, RlpEncodable, RlpDecodable)]
pub struct Incomplete {
/// Start block.
pub start: Field<HashOrNumber>,
/// Skip between.
pub skip: u64,
/// Maximum to return.
pub max: u64,
/// Whether to reverse from start.
pub reverse: bool,
}
impl super::IncompleteRequest for Incomplete {
type Complete = Complete;
type Response = Response;
fn check_outputs<F>(&self, mut f: F) -> Result<(), NoSuchOutput>
where F: FnMut(usize, usize, OutputKind) -> Result<(), NoSuchOutput>
{
match self.start {
Field::Scalar(_) => Ok(()),
Field::BackReference(req, idx) =>
f(req, idx, OutputKind::Hash).or_else(|_| f(req, idx, OutputKind::Number))
}
}
fn note_outputs<F>(&self, _: F) where F: FnMut(usize, OutputKind) { }
fn fill<F>(&mut self, oracle: F) where F: Fn(usize, usize) -> Result<Output, NoSuchOutput> {
if let Field::BackReference(req, idx) = self.start {
self.start = match oracle(req, idx) {
Ok(Output::Hash(hash)) => Field::Scalar(hash.into()),
Ok(Output::Number(num)) => Field::Scalar(num.into()),
Err(_) => Field::BackReference(req, idx),
}
}
}
fn complete(self) -> Result<Self::Complete, NoSuchOutput> {
Ok(Complete {
start: self.start.into_scalar()?,
skip: self.skip,
max: self.max,
reverse: self.reverse,
})
}
fn adjust_refs<F>(&mut self, mapping: F) where F: FnMut(usize) -> usize {
self.start.adjust_req(mapping)
}
}
/// A complete header request.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Complete {
/// Start block.
pub start: HashOrNumber,
/// Skip between.
pub skip: u64,
/// Maximum to return.
pub max: u64,
/// Whether to reverse from start.
pub reverse: bool,
}
/// The output of a request for headers.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Response {
/// The headers requested.
pub headers: Vec<encoded::Header>,
}
impl super::ResponseLike for Response {
/// Fill reusable outputs by writing them into the function.
fn fill_outputs<F>(&self, _: F) where F: FnMut(usize, Output) { }
}
impl Decodable for Response {
fn decode(rlp: &Rlp) -> Result<Self, DecoderError> {
use ethcore::header::Header as FullHeader;
let mut headers = Vec::new();
for item in rlp.iter() {
// check that it's a valid encoding.
// TODO: just return full headers here?
let _: FullHeader = item.as_val()?;
headers.push(encoded::Header::new(item.as_raw().to_owned()));
}
Ok(Response {
headers: headers,
})
}
}
impl Encodable for Response {
fn rlp_append(&self, s: &mut RlpStream) {
s.begin_list(self.headers.len());
for header in &self.headers {
s.append_raw(header.rlp().as_raw(), 1);
}
}
}
}
/// Request and response for header proofs.
pub mod header_proof {
use super::{Field, NoSuchOutput, OutputKind, Output};
use rlp::{Encodable, Decodable, DecoderError, RlpStream, Rlp};
use ethereum_types::{H256, U256};
use bytes::Bytes;
/// Potentially incomplete header proof request.
#[derive(Debug, Clone, PartialEq, Eq, RlpEncodable, RlpDecodable)]
pub struct Incomplete {
/// Block number.
pub num: Field<u64>,
}
impl super::IncompleteRequest for Incomplete {
type Complete = Complete;
type Response = Response;
fn check_outputs<F>(&self, mut f: F) -> Result<(), NoSuchOutput>
where F: FnMut(usize, usize, OutputKind) -> Result<(), NoSuchOutput>
{
match self.num {
Field::Scalar(_) => Ok(()),
Field::BackReference(req, idx) => f(req, idx, OutputKind::Number),
}
}
fn note_outputs<F>(&self, mut note: F) where F: FnMut(usize, OutputKind) {
note(0, OutputKind::Hash);
}
fn fill<F>(&mut self, oracle: F) where F: Fn(usize, usize) -> Result<Output, NoSuchOutput> {
if let Field::BackReference(req, idx) = self.num {
self.num = match oracle(req, idx) {
Ok(Output::Number(num)) => Field::Scalar(num.into()),
_ => Field::BackReference(req, idx),
}
}
}
fn complete(self) -> Result<Self::Complete, NoSuchOutput> {
Ok(Complete {
num: self.num.into_scalar()?,
})
}
fn adjust_refs<F>(&mut self, mapping: F) where F: FnMut(usize) -> usize {
self.num.adjust_req(mapping)
}
}
/// A complete header proof request.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Complete {
/// The number to get a header proof for.
pub num: u64,
}
/// The output of a request for a header proof.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Response {
/// Inclusion proof of the header and total difficulty in the CHT.
pub proof: Vec<Bytes>,
/// The proved header's hash.
pub hash: H256,
/// The proved header's total difficulty.
pub td: U256,
}
impl super::ResponseLike for Response {
/// Fill reusable outputs by providing them to the function.
fn fill_outputs<F>(&self, mut f: F) where F: FnMut(usize, Output) {
f(0, Output::Hash(self.hash));
}
}
impl Decodable for Response {
fn decode(rlp: &Rlp) -> Result<Self, DecoderError> {
Ok(Response {
proof: rlp.list_at(0)?,
hash: rlp.val_at(1)?,
td: rlp.val_at(2)?,
})
}
}
impl Encodable for Response {
fn rlp_append(&self, s: &mut RlpStream) {
s.begin_list(3)
.append_list::<Vec<u8>,_>(&self.proof[..])
.append(&self.hash)
.append(&self.td);
}
}
}
/// Request and response for transaction index.
pub mod transaction_index {
use super::{Field, NoSuchOutput, OutputKind, Output};
use ethereum_types::H256;
/// Potentially incomplete transaction index request.
#[derive(Debug, Clone, PartialEq, Eq, RlpEncodable, RlpDecodable)]
pub struct Incomplete {
/// Transaction hash to get index for.
pub hash: Field<H256>,
}
impl super::IncompleteRequest for Incomplete {
type Complete = Complete;
type Response = Response;
fn check_outputs<F>(&self, mut f: F) -> Result<(), NoSuchOutput>
where F: FnMut(usize, usize, OutputKind) -> Result<(), NoSuchOutput>
{
match self.hash {
Field::Scalar(_) => Ok(()),
Field::BackReference(req, idx) => f(req, idx, OutputKind::Hash),
}
}
fn note_outputs<F>(&self, mut f: F) where F: FnMut(usize, OutputKind) {
f(0, OutputKind::Number);
f(1, OutputKind::Hash);
}
fn fill<F>(&mut self, oracle: F) where F: Fn(usize, usize) -> Result<Output, NoSuchOutput> {
if let Field::BackReference(req, idx) = self.hash {
self.hash = match oracle(req, idx) {
Ok(Output::Number(hash)) => Field::Scalar(hash.into()),
_ => Field::BackReference(req, idx),
}
}
}
fn complete(self) -> Result<Self::Complete, NoSuchOutput> {
Ok(Complete {
hash: self.hash.into_scalar()?,
})
}
fn adjust_refs<F>(&mut self, mapping: F) where F: FnMut(usize) -> usize {
self.hash.adjust_req(mapping)
}
}
/// A complete transaction index request.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Complete {
/// The transaction hash to get index for.
pub hash: H256,
}
/// The output of a request for transaction index.
#[derive(Debug, Clone, PartialEq, Eq, RlpEncodable, RlpDecodable)]
pub struct Response {
/// Block number.
pub num: u64,
/// Block hash
pub hash: H256,
/// Index in block.
pub index: u64,
}
impl super::ResponseLike for Response {
/// Fill reusable outputs by providing them to the function.
fn fill_outputs<F>(&self, mut f: F) where F: FnMut(usize, Output) {
f(0, Output::Number(self.num));
f(1, Output::Hash(self.hash));
}
}
}
/// Request and response for block receipts
pub mod block_receipts {
use super::{Field, NoSuchOutput, OutputKind, Output};
use ethcore::receipt::Receipt;
use ethereum_types::H256;
/// Potentially incomplete block receipts request.
#[derive(Debug, Clone, PartialEq, Eq, RlpEncodable, RlpDecodable)]
pub struct Incomplete {
/// Block hash to get receipts for.
pub hash: Field<H256>,
}
impl super::IncompleteRequest for Incomplete {
type Complete = Complete;
type Response = Response;
fn check_outputs<F>(&self, mut f: F) -> Result<(), NoSuchOutput>
where F: FnMut(usize, usize, OutputKind) -> Result<(), NoSuchOutput>
{
match self.hash {
Field::Scalar(_) => Ok(()),
Field::BackReference(req, idx) => f(req, idx, OutputKind::Hash),
}
}
fn note_outputs<F>(&self, _: F) where F: FnMut(usize, OutputKind) {}
fn fill<F>(&mut self, oracle: F) where F: Fn(usize, usize) -> Result<Output, NoSuchOutput> {
if let Field::BackReference(req, idx) = self.hash {
self.hash = match oracle(req, idx) {
Ok(Output::Number(hash)) => Field::Scalar(hash.into()),
_ => Field::BackReference(req, idx),
}
}
}
fn complete(self) -> Result<Self::Complete, NoSuchOutput> {
Ok(Complete {
hash: self.hash.into_scalar()?,
})
}
fn adjust_refs<F>(&mut self, mapping: F) where F: FnMut(usize) -> usize {
self.hash.adjust_req(mapping)
}
}
/// A complete block receipts request.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Complete {
/// The number to get block receipts for.
pub hash: H256,
}
/// The output of a request for block receipts.
#[derive(Debug, Clone, PartialEq, Eq, RlpEncodableWrapper, RlpDecodableWrapper)]
pub struct Response {
/// The block receipts.
pub receipts: Vec<Receipt>
}
impl super::ResponseLike for Response {
/// Fill reusable outputs by providing them to the function.
fn fill_outputs<F>(&self, _: F) where F: FnMut(usize, Output) {}
}
}
/// Request and response for a block body
pub mod block_body {
use super::{Field, NoSuchOutput, OutputKind, Output};
use ethcore::encoded;
use rlp::{Encodable, Decodable, DecoderError, RlpStream, Rlp};
use ethereum_types::H256;
/// Potentially incomplete block body request.
#[derive(Debug, Clone, PartialEq, Eq, RlpEncodable, RlpDecodable)]
pub struct Incomplete {
/// Block hash to get receipts for.
pub hash: Field<H256>,
}
impl super::IncompleteRequest for Incomplete {
type Complete = Complete;
type Response = Response;
fn check_outputs<F>(&self, mut f: F) -> Result<(), NoSuchOutput>
where F: FnMut(usize, usize, OutputKind) -> Result<(), NoSuchOutput>
{
match self.hash {
Field::Scalar(_) => Ok(()),
Field::BackReference(req, idx) => f(req, idx, OutputKind::Hash),
}
}
fn note_outputs<F>(&self, _: F) where F: FnMut(usize, OutputKind) {}
fn fill<F>(&mut self, oracle: F) where F: Fn(usize, usize) -> Result<Output, NoSuchOutput> {
if let Field::BackReference(req, idx) = self.hash {
self.hash = match oracle(req, idx) {
Ok(Output::Hash(hash)) => Field::Scalar(hash.into()),
_ => Field::BackReference(req, idx),
}
}
}
fn complete(self) -> Result<Self::Complete, NoSuchOutput> {
Ok(Complete {
hash: self.hash.into_scalar()?,
})
}
fn adjust_refs<F>(&mut self, mapping: F) where F: FnMut(usize) -> usize {
self.hash.adjust_req(mapping)
}
}
/// A complete block body request.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Complete {
/// The hash to get a block body for.
pub hash: H256,
}
/// The output of a request for block body.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Response {
/// The block body.
pub body: encoded::Body,
}
impl super::ResponseLike for Response {
/// Fill reusable outputs by providing them to the function.
fn fill_outputs<F>(&self, _: F) where F: FnMut(usize, Output) {}
}
impl Decodable for Response {
fn decode(rlp: &Rlp) -> Result<Self, DecoderError> {
use ethcore::header::Header as FullHeader;
use transaction::UnverifiedTransaction;
// check body validity.
let _: Vec<UnverifiedTransaction> = rlp.list_at(0)?;
let _: Vec<FullHeader> = rlp.list_at(1)?;
Ok(Response {
body: encoded::Body::new(rlp.as_raw().to_owned()),
})
}
}
impl Encodable for Response {
fn rlp_append(&self, s: &mut RlpStream) {
s.append_raw(&self.body.rlp().as_raw(), 1);
}
}
}
/// A request for an account proof.
pub mod account {
use super::{Field, NoSuchOutput, OutputKind, Output};
use ethereum_types::{H256, U256};
use bytes::Bytes;
/// Potentially incomplete request for an account proof.
#[derive(Debug, Clone, PartialEq, Eq, RlpEncodable, RlpDecodable)]
pub struct Incomplete {
/// Block hash to request state proof for.
pub block_hash: Field<H256>,
/// Hash of the account's address.
pub address_hash: Field<H256>,
}
impl super::IncompleteRequest for Incomplete {
type Complete = Complete;
type Response = Response;
fn check_outputs<F>(&self, mut f: F) -> Result<(), NoSuchOutput>
where F: FnMut(usize, usize, OutputKind) -> Result<(), NoSuchOutput>
{
if let Field::BackReference(req, idx) = self.block_hash {
f(req, idx, OutputKind::Hash)?
}
if let Field::BackReference(req, idx) = self.address_hash {
f(req, idx, OutputKind::Hash)?
}
Ok(())
}
fn note_outputs<F>(&self, mut f: F) where F: FnMut(usize, OutputKind) {
f(0, OutputKind::Hash);
f(1, OutputKind::Hash);
}
fn fill<F>(&mut self, oracle: F) where F: Fn(usize, usize) -> Result<Output, NoSuchOutput> {
if let Field::BackReference(req, idx) = self.block_hash {
self.block_hash = match oracle(req, idx) {
Ok(Output::Hash(block_hash)) => Field::Scalar(block_hash.into()),
_ => Field::BackReference(req, idx),
}
}
if let Field::BackReference(req, idx) = self.address_hash {
self.address_hash = match oracle(req, idx) {
Ok(Output::Hash(address_hash)) => Field::Scalar(address_hash.into()),
_ => Field::BackReference(req, idx),
}
}
}
fn complete(self) -> Result<Self::Complete, NoSuchOutput> {
Ok(Complete {
block_hash: self.block_hash.into_scalar()?,
address_hash: self.address_hash.into_scalar()?,
})
}
fn adjust_refs<F>(&mut self, mut mapping: F) where F: FnMut(usize) -> usize {
self.block_hash.adjust_req(&mut mapping);
self.address_hash.adjust_req(&mut mapping);
}
}
/// A complete request for an account.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Complete {
/// Block hash to request state proof for.
pub block_hash: H256,
/// Hash of the account's address.
pub address_hash: H256,
}
/// The output of a request for an account state proof.
#[derive(Debug, Clone, PartialEq, Eq, RlpEncodable, RlpDecodable)]
pub struct Response {
/// Inclusion/exclusion proof
pub proof: Vec<Bytes>,
/// Account nonce.
pub nonce: U256,
/// Account balance.
pub balance: U256,
/// Account's code hash.
pub code_hash: H256,
/// Account's storage trie root.
pub storage_root: H256,
}
impl super::ResponseLike for Response {
/// Fill reusable outputs by providing them to the function.
fn fill_outputs<F>(&self, mut f: F) where F: FnMut(usize, Output) {
f(0, Output::Hash(self.code_hash));
f(1, Output::Hash(self.storage_root));
}
}
}
/// A request for a storage proof.
pub mod storage {
use super::{Field, NoSuchOutput, OutputKind, Output};
use ethereum_types::H256;
use bytes::Bytes;
/// Potentially incomplete request for an storage proof.
#[derive(Debug, Clone, PartialEq, Eq, RlpEncodable, RlpDecodable)]
pub struct Incomplete {
/// Block hash to request state proof for.
pub block_hash: Field<H256>,
/// Hash of the account's address.
pub address_hash: Field<H256>,
/// Hash of the storage key.
pub key_hash: Field<H256>,
}
impl super::IncompleteRequest for Incomplete {
type Complete = Complete;
type Response = Response;
fn check_outputs<F>(&self, mut f: F) -> Result<(), NoSuchOutput>
where F: FnMut(usize, usize, OutputKind) -> Result<(), NoSuchOutput>
{
if let Field::BackReference(req, idx) = self.block_hash {
f(req, idx, OutputKind::Hash)?
}
if let Field::BackReference(req, idx) = self.address_hash {
f(req, idx, OutputKind::Hash)?
}
if let Field::BackReference(req, idx) = self.key_hash {
f(req, idx, OutputKind::Hash)?
}
Ok(())
}
fn note_outputs<F>(&self, mut f: F) where F: FnMut(usize, OutputKind) {
f(0, OutputKind::Hash);
}
fn fill<F>(&mut self, oracle: F) where F: Fn(usize, usize) -> Result<Output, NoSuchOutput> {
if let Field::BackReference(req, idx) = self.block_hash {
self.block_hash = match oracle(req, idx) {
Ok(Output::Hash(block_hash)) => Field::Scalar(block_hash.into()),
_ => Field::BackReference(req, idx),
}
}
if let Field::BackReference(req, idx) = self.address_hash {
self.address_hash = match oracle(req, idx) {
Ok(Output::Hash(address_hash)) => Field::Scalar(address_hash.into()),
_ => Field::BackReference(req, idx),
}
}
if let Field::BackReference(req, idx) = self.key_hash {
self.key_hash = match oracle(req, idx) {
Ok(Output::Hash(key_hash)) => Field::Scalar(key_hash.into()),
_ => Field::BackReference(req, idx),
}
}
}
fn complete(self) -> Result<Self::Complete, NoSuchOutput> {
Ok(Complete {
block_hash: self.block_hash.into_scalar()?,
address_hash: self.address_hash.into_scalar()?,
key_hash: self.key_hash.into_scalar()?,
})
}
fn adjust_refs<F>(&mut self, mut mapping: F) where F: FnMut(usize) -> usize {
self.block_hash.adjust_req(&mut mapping);
self.address_hash.adjust_req(&mut mapping);
self.key_hash.adjust_req(&mut mapping);
}
}
/// A complete request for a storage proof.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Complete {
/// Block hash to request state proof for.
pub block_hash: H256,
/// Hash of the account's address.
pub address_hash: H256,
/// Storage key hash.
pub key_hash: H256,
}
/// The output of a request for an account state proof.
#[derive(Debug, Clone, PartialEq, Eq, RlpEncodable, RlpDecodable)]
pub struct Response {
/// Inclusion/exclusion proof
pub proof: Vec<Bytes>,
/// Storage value.
pub value: H256,
}
impl super::ResponseLike for Response {
/// Fill reusable outputs by providing them to the function.
fn fill_outputs<F>(&self, mut f: F) where F: FnMut(usize, Output) {
f(0, Output::Hash(self.value));
}
}
}
/// A request for contract code.
pub mod contract_code {
use super::{Field, NoSuchOutput, OutputKind, Output};
use ethereum_types::H256;
use bytes::Bytes;
/// Potentially incomplete contract code request.
#[derive(Debug, Clone, PartialEq, Eq, RlpEncodable, RlpDecodable)]
pub struct Incomplete {
/// The block hash to request the state for.
pub block_hash: Field<H256>,
/// The code hash.
pub code_hash: Field<H256>,
}
impl super::IncompleteRequest for Incomplete {
type Complete = Complete;
type Response = Response;
fn check_outputs<F>(&self, mut f: F) -> Result<(), NoSuchOutput>
where F: FnMut(usize, usize, OutputKind) -> Result<(), NoSuchOutput>
{
if let Field::BackReference(req, idx) = self.block_hash {
f(req, idx, OutputKind::Hash)?;
}
if let Field::BackReference(req, idx) = self.code_hash {
f(req, idx, OutputKind::Hash)?;
}
Ok(())
}
fn note_outputs<F>(&self, _: F) where F: FnMut(usize, OutputKind) {}
fn fill<F>(&mut self, oracle: F) where F: Fn(usize, usize) -> Result<Output, NoSuchOutput> {
if let Field::BackReference(req, idx) = self.block_hash {
self.block_hash = match oracle(req, idx) {
Ok(Output::Hash(block_hash)) => Field::Scalar(block_hash.into()),
_ => Field::BackReference(req, idx),
}
}
if let Field::BackReference(req, idx) = self.code_hash {
self.code_hash = match oracle(req, idx) {
Ok(Output::Hash(code_hash)) => Field::Scalar(code_hash.into()),
_ => Field::BackReference(req, idx),
}
}
}
fn complete(self) -> Result<Self::Complete, NoSuchOutput> {
Ok(Complete {
block_hash: self.block_hash.into_scalar()?,
code_hash: self.code_hash.into_scalar()?,
})
}
fn adjust_refs<F>(&mut self, mut mapping: F) where F: FnMut(usize) -> usize {
self.block_hash.adjust_req(&mut mapping);
self.code_hash.adjust_req(&mut mapping);
}
}
/// A complete request.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Complete {
/// The block hash to request the state for.
pub block_hash: H256,
/// The code hash.
pub code_hash: H256,
}
/// The output of a request for
#[derive(Debug, Clone, PartialEq, Eq, RlpEncodableWrapper, RlpDecodableWrapper)]
pub struct Response {
/// The requested code.
pub code: Bytes,
}
impl super::ResponseLike for Response {
/// Fill reusable outputs by providing them to the function.
fn fill_outputs<F>(&self, _: F) where F: FnMut(usize, Output) {}
}
}
/// A request for proof of execution.
pub mod execution {
use super::{Field, NoSuchOutput, OutputKind, Output};
use transaction::Action;
use rlp::{Encodable, Decodable, DecoderError, RlpStream, Rlp};
use ethereum_types::{H256, U256, Address};
use kvdb::DBValue;
use bytes::Bytes;
/// Potentially incomplete execution proof request.
#[derive(Debug, Clone, PartialEq, Eq, RlpEncodable, RlpDecodable)]
pub struct Incomplete {
/// The block hash to request the state for.
pub block_hash: Field<H256>,
/// The address the transaction should be from.
pub from: Address,
/// The action of the transaction.
pub action: Action,
/// The amount of gas to prove.
pub gas: U256,
/// The gas price.
pub gas_price: U256,
/// The value to transfer.
pub value: U256,
/// Call data.
pub data: Bytes,
}
impl super::IncompleteRequest for Incomplete {
type Complete = Complete;
type Response = Response;
fn check_outputs<F>(&self, mut f: F) -> Result<(), NoSuchOutput>
where F: FnMut(usize, usize, OutputKind) -> Result<(), NoSuchOutput>
{
if let Field::BackReference(req, idx) = self.block_hash {
f(req, idx, OutputKind::Hash)?;
}
Ok(())
}
fn note_outputs<F>(&self, _: F) where F: FnMut(usize, OutputKind) {}
fn fill<F>(&mut self, oracle: F) where F: Fn(usize, usize) -> Result<Output, NoSuchOutput> {
if let Field::BackReference(req, idx) = self.block_hash {
self.block_hash = match oracle(req, idx) {
Ok(Output::Hash(block_hash)) => Field::Scalar(block_hash.into()),
_ => Field::BackReference(req, idx),
}
}
}
fn complete(self) -> Result<Self::Complete, NoSuchOutput> {
Ok(Complete {
block_hash: self.block_hash.into_scalar()?,
from: self.from,
action: self.action,
gas: self.gas,
gas_price: self.gas_price,
value: self.value,
data: self.data,
})
}
fn adjust_refs<F>(&mut self, mapping: F) where F: FnMut(usize) -> usize {
self.block_hash.adjust_req(mapping);
}
}
/// A complete request.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Complete {
/// The block hash to request the state for.
pub block_hash: H256,
/// The address the transaction should be from.
pub from: Address,
/// The action of the transaction.
pub action: Action,
/// The amount of gas to prove.
pub gas: U256,
/// The gas price.
pub gas_price: U256,
/// The value to transfer.
pub value: U256,
/// Call data.
pub data: Bytes,
}
/// The output of a request for proof of execution
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Response {
/// All state items (trie nodes, code) necessary to re-prove the transaction.
pub items: Vec<DBValue>,
}
impl super::ResponseLike for Response {
/// Fill reusable outputs by providing them to the function.
fn fill_outputs<F>(&self, _: F) where F: FnMut(usize, Output) {}
}
impl Decodable for Response {
fn decode(rlp: &Rlp) -> Result<Self, DecoderError> {
let mut items = Vec::new();
for raw_item in rlp.iter() {
let mut item = DBValue::new();
item.append_slice(raw_item.data()?);
items.push(item);
}
Ok(Response {
items: items,
})
}
}
impl Encodable for Response {
fn rlp_append(&self, s: &mut RlpStream) {
s.begin_list(self.items.len());
for item in &self.items {
s.append(&&**item);
}
}
}
}
/// A request for epoch signal data.
pub mod epoch_signal {
use super::{Field, NoSuchOutput, OutputKind, Output};
use rlp::{Encodable, Decodable, DecoderError, RlpStream, Rlp};
use ethereum_types::H256;
use bytes::Bytes;
/// Potentially incomplete epoch signal request.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Incomplete {
/// The block hash to request the signal for.
pub block_hash: Field<H256>,
}
impl Decodable for Incomplete {
fn decode(rlp: &Rlp) -> Result<Self, DecoderError> {
Ok(Incomplete {
block_hash: rlp.val_at(0)?,
})
}
}
impl Encodable for Incomplete {
fn rlp_append(&self, s: &mut RlpStream) {
s.begin_list(1).append(&self.block_hash);
}
}
impl super::IncompleteRequest for Incomplete {
type Complete = Complete;
type Response = Response;
fn check_outputs<F>(&self, mut f: F) -> Result<(), NoSuchOutput>
where F: FnMut(usize, usize, OutputKind) -> Result<(), NoSuchOutput>
{
if let Field::BackReference(req, idx) = self.block_hash {
f(req, idx, OutputKind::Hash)?;
}
Ok(())
}
fn note_outputs<F>(&self, _: F) where F: FnMut(usize, OutputKind) {}
fn fill<F>(&mut self, oracle: F) where F: Fn(usize, usize) -> Result<Output, NoSuchOutput> {
if let Field::BackReference(req, idx) = self.block_hash {
self.block_hash = match oracle(req, idx) {
Ok(Output::Hash(block_hash)) => Field::Scalar(block_hash.into()),
_ => Field::BackReference(req, idx),
}
}
}
fn complete(self) -> Result<Self::Complete, NoSuchOutput> {
Ok(Complete {
block_hash: self.block_hash.into_scalar()?,
})
}
fn adjust_refs<F>(&mut self, mut mapping: F) where F: FnMut(usize) -> usize {
self.block_hash.adjust_req(&mut mapping);
}
}
/// A complete request.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Complete {
/// The block hash to request the epoch signal for.
pub block_hash: H256,
}
/// The output of a request for an epoch signal.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Response {
/// The requested epoch signal.
pub signal: Bytes,
}
impl super::ResponseLike for Response {
/// Fill reusable outputs by providing them to the function.
fn fill_outputs<F>(&self, _: F) where F: FnMut(usize, Output) {}
}
impl Decodable for Response {
fn decode(rlp: &Rlp) -> Result<Self, DecoderError> {
Ok(Response {
signal: rlp.as_val()?,
})
}
}
impl Encodable for Response {
fn rlp_append(&self, s: &mut RlpStream) {
s.append(&self.signal);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use ethcore::header::Header;
fn check_roundtrip<T>(val: T)
where T: ::rlp::Encodable + ::rlp::Decodable + PartialEq + ::std::fmt::Debug
{
// check as single value.
let bytes = ::rlp::encode(&val);
let new_val: T = ::rlp::decode(&bytes).unwrap();
assert_eq!(val, new_val);
// check as list containing single value.
let list = [val];
let bytes = ::rlp::encode_list(&list);
let new_list: Vec<T> = ::rlp::decode_list(&bytes);
assert_eq!(&list, &new_list[..]);
}
#[test]
fn hash_or_number_roundtrip() {
let hash = HashOrNumber::Hash(H256::default());
let number = HashOrNumber::Number(5);
check_roundtrip(hash);
check_roundtrip(number);
}
#[test]
fn field_roundtrip() {
let field_scalar = Field::Scalar(5usize);
let field_back: Field<usize> = Field::BackReference(1, 2);
check_roundtrip(field_scalar);
check_roundtrip(field_back);
}
#[test]
fn headers_roundtrip() {
let req = IncompleteHeadersRequest {
start: Field::Scalar(5u64.into()),
skip: 0,
max: 100,
reverse: false,
};
let full_req = Request::Headers(req.clone());
let res = HeadersResponse {
headers: vec![
::ethcore::encoded::Header::new(::rlp::encode(&Header::default()).into_vec())
]
};
let full_res = Response::Headers(res.clone());
check_roundtrip(req);
check_roundtrip(full_req);
check_roundtrip(res);
check_roundtrip(full_res);
}
#[test]
fn header_proof_roundtrip() {
let req = IncompleteHeaderProofRequest {
num: Field::BackReference(1, 234),
};
let full_req = Request::HeaderProof(req.clone());
let res = HeaderProofResponse {
proof: vec![vec![1, 2, 3], vec![4, 5, 6]],
hash: Default::default(),
td: 100.into(),
};
let full_res = Response::HeaderProof(res.clone());
check_roundtrip(req);
check_roundtrip(full_req);
check_roundtrip(res);
check_roundtrip(full_res);
}
#[test]
fn transaction_index_roundtrip() {
let req = IncompleteTransactionIndexRequest {
hash: Field::Scalar(Default::default()),
};
let full_req = Request::TransactionIndex(req.clone());
let res = TransactionIndexResponse {
num: 1000,
hash: ::ethereum_types::H256::random(),
index: 4,
};
let full_res = Response::TransactionIndex(res.clone());
check_roundtrip(req);
check_roundtrip(full_req);
check_roundtrip(res);
check_roundtrip(full_res);
}
#[test]
fn receipts_roundtrip() {
use ethcore::receipt::{Receipt, TransactionOutcome};
let req = IncompleteReceiptsRequest {
hash: Field::Scalar(Default::default()),
};
let full_req = Request::Receipts(req.clone());
let receipt = Receipt::new(TransactionOutcome::Unknown, Default::default(), Vec::new());
let res = ReceiptsResponse {
receipts: vec![receipt.clone(), receipt],
};
let full_res = Response::Receipts(res.clone());
check_roundtrip(req);
check_roundtrip(full_req);
check_roundtrip(res);
check_roundtrip(full_res);
}
#[test]
fn body_roundtrip() {
use transaction::{Transaction, UnverifiedTransaction};
let req = IncompleteBodyRequest {
hash: Field::Scalar(Default::default()),
};
let full_req = Request::Body(req.clone());
let res = BodyResponse {
body: {
let header = ::ethcore::header::Header::default();
let tx = UnverifiedTransaction::from(Transaction::default().fake_sign(Default::default()));
let mut stream = RlpStream::new_list(2);
stream.begin_list(2).append(&tx).append(&tx)
.begin_list(1).append(&header);
::ethcore::encoded::Body::new(stream.out())
},
};
let full_res = Response::Body(res.clone());
check_roundtrip(req);
check_roundtrip(full_req);
check_roundtrip(res);
check_roundtrip(full_res);
}
#[test]
fn account_roundtrip() {
let req = IncompleteAccountRequest {
block_hash: Field::Scalar(Default::default()),
address_hash: Field::BackReference(1, 2),
};
let full_req = Request::Account(req.clone());
let res = AccountResponse {
proof: vec![vec![1, 2, 3], vec![4, 5, 6]],
nonce: 100.into(),
balance: 123456.into(),
code_hash: Default::default(),
storage_root: Default::default(),
};
let full_res = Response::Account(res.clone());
check_roundtrip(req);
check_roundtrip(full_req);
check_roundtrip(res);
check_roundtrip(full_res);
}
#[test]
fn storage_roundtrip() {
let req = IncompleteStorageRequest {
block_hash: Field::Scalar(Default::default()),
address_hash: Field::BackReference(1, 2),
key_hash: Field::BackReference(3, 2),
};
let full_req = Request::Storage(req.clone());
let res = StorageResponse {
proof: vec![vec![1, 2, 3], vec![4, 5, 6]],
value: H256::default(),
};
let full_res = Response::Storage(res.clone());
check_roundtrip(req);
check_roundtrip(full_req);
check_roundtrip(res);
check_roundtrip(full_res);
}
#[test]
fn code_roundtrip() {
let req = IncompleteCodeRequest {
block_hash: Field::Scalar(Default::default()),
code_hash: Field::BackReference(3, 2),
};
let full_req = Request::Code(req.clone());
let res = CodeResponse {
code: vec![1, 2, 3, 4, 5, 6, 7, 6, 5, 4],
};
let full_res = Response::Code(res.clone());
check_roundtrip(req);
check_roundtrip(full_req);
check_roundtrip(res);
check_roundtrip(full_res);
}
#[test]
fn execution_roundtrip() {
use kvdb::DBValue;
let req = IncompleteExecutionRequest {
block_hash: Field::Scalar(Default::default()),
from: Default::default(),
action: ::transaction::Action::Create,
gas: 100_000.into(),
gas_price: 0.into(),
value: 100_000_001.into(),
data: vec![1, 2, 3, 2, 1],
};
let full_req = Request::Execution(req.clone());
let res = ExecutionResponse {
items: vec![DBValue::new(), {
let mut value = DBValue::new();
value.append_slice(&[1, 1, 1, 2, 3]);
value
}],
};
let full_res = Response::Execution(res.clone());
check_roundtrip(req);
check_roundtrip(full_req);
check_roundtrip(res);
check_roundtrip(full_res);
}
#[test]
fn vec_test() {
use rlp::*;
let reqs: Vec<_> = (0..10).map(|_| IncompleteExecutionRequest {
block_hash: Field::Scalar(Default::default()),
from: Default::default(),
action: ::transaction::Action::Create,
gas: 100_000.into(),
gas_price: 0.into(),
value: 100_000_001.into(),
data: vec![1, 2, 3, 2, 1],
}).map(Request::Execution).collect();
let mut stream = RlpStream::new_list(2);
stream.append(&100usize).append_list(&reqs);
let out = stream.out();
let rlp = Rlp::new(&out);
assert_eq!(rlp.val_at::<usize>(0).unwrap(), 100usize);
assert_eq!(rlp.list_at::<Request>(1).unwrap(), reqs);
}
#[test]
fn responses_vec() {
use ethcore::receipt::{Receipt, TransactionOutcome};
let mut stream = RlpStream::new_list(2);
stream.begin_list(0).begin_list(0);
let body = ::ethcore::encoded::Body::new(stream.out());
let reqs = vec![
Response::Headers(HeadersResponse { headers: vec![] }),
Response::HeaderProof(HeaderProofResponse { proof: vec![], hash: Default::default(), td: 100.into()}),
Response::Receipts(ReceiptsResponse { receipts: vec![Receipt::new(TransactionOutcome::Unknown, Default::default(), Vec::new())] }),
Response::Body(BodyResponse { body: body }),
Response::Account(AccountResponse {
proof: vec![],
nonce: 100.into(),
balance: 123.into(),
code_hash: Default::default(),
storage_root: Default::default()
}),
Response::Storage(StorageResponse { proof: vec![], value: H256::default() }),
Response::Code(CodeResponse { code: vec![1, 2, 3, 4, 5] }),
Response::Execution(ExecutionResponse { items: vec![] }),
];
let raw = ::rlp::encode_list(&reqs);
assert_eq!(::rlp::decode_list::<Response>(&raw), reqs);
}
#[test]
fn epoch_signal_roundtrip() {
let req = IncompleteSignalRequest {
block_hash: Field::Scalar(Default::default()),
};
let full_req = Request::Signal(req.clone());
let res = SignalResponse {
signal: vec![1, 2, 3, 4, 5, 6, 7, 6, 5, 4],
};
let full_res = Response::Signal(res.clone());
check_roundtrip(req);
check_roundtrip(full_req);
check_roundtrip(res);
check_roundtrip(full_res);
}
}