// 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 .
//! Request types, verification, and verification errors.
use std::sync::Arc;
use ethcore::basic_account::BasicAccount;
use ethcore::encoded;
use ethcore::engines::{EthEngine, StateDependentProof};
use ethcore::machine::EthereumMachine;
use ethcore::receipt::Receipt;
use ethcore::state::{self, ProvedExecution};
use transaction::SignedTransaction;
use vm::EnvInfo;
use hash::{KECCAK_NULL_RLP, KECCAK_EMPTY, KECCAK_EMPTY_LIST_RLP, keccak};
use request::{self as net_request, IncompleteRequest, CompleteRequest, Output, OutputKind, Field};
use rlp::{RlpStream, Rlp};
use ethereum_types::{H256, U256, Address};
use parking_lot::Mutex;
use hashdb::HashDB;
use kvdb::DBValue;
use bytes::Bytes;
use memorydb::MemoryDB;
use trie::{Trie, TrieDB, TrieError};
const SUPPLIED_MATCHES: &'static str = "supplied responses always match produced requests; enforced by `check_response`; qed";
/// Core unit of the API: submit batches of these to be answered with `Response`s.
#[derive(Clone)]
pub enum Request {
/// A request for a header proof.
HeaderProof(HeaderProof),
/// A request for a header by hash.
HeaderByHash(HeaderByHash),
/// A request for the index of a transaction.
TransactionIndex(TransactionIndex),
/// A request for block receipts.
Receipts(BlockReceipts),
/// A request for a block body.
Body(Body),
/// A request for an account.
Account(Account),
/// A request for a contract's code.
Code(Code),
/// A request for proof of execution.
Execution(TransactionProof),
/// A request for epoch change signal.
Signal(Signal),
}
/// A request argument.
pub trait RequestArg {
/// the response type.
type Out;
/// Create the request type.
/// `extract` must not fail when presented with the corresponding
/// `Response`.
fn make(self) -> Request;
/// May not panic if the response corresponds with the request
/// from `make`.
/// Is free to panic otherwise.
fn extract(r: Response) -> Self::Out;
}
/// An adapter can be thought of as a grouping of request argument types.
/// This is implemented for various tuples and convenient types.
pub trait RequestAdapter {
/// The output type.
type Out;
/// Infallibly produce requests. When `extract_from` is presented
/// with the corresponding response vector, it may not fail.
fn make_requests(self) -> Vec;
/// Extract the output type from the given responses.
/// If they are the corresponding responses to the requests
/// made by `make_requests`, do not panic.
fn extract_from(Vec) -> Self::Out;
}
impl RequestAdapter for Vec {
type Out = Vec;
fn make_requests(self) -> Vec {
self.into_iter().map(RequestArg::make).collect()
}
fn extract_from(r: Vec) -> Self::Out {
r.into_iter().map(T::extract).collect()
}
}
// helper to implement `RequestArg` and `From` for a single request kind.
macro_rules! impl_single {
($variant: ident, $me: ty, $out: ty) => {
impl RequestArg for $me {
type Out = $out;
fn make(self) -> Request {
Request::$variant(self)
}
fn extract(r: Response) -> $out {
match r {
Response::$variant(x) => x,
_ => panic!(SUPPLIED_MATCHES),
}
}
}
impl From<$me> for Request {
fn from(me: $me) -> Request {
Request::$variant(me)
}
}
}
}
// implement traits for each kind of request.
impl_single!(HeaderProof, HeaderProof, (H256, U256));
impl_single!(HeaderByHash, HeaderByHash, encoded::Header);
impl_single!(TransactionIndex, TransactionIndex, net_request::TransactionIndexResponse);
impl_single!(Receipts, BlockReceipts, Vec);
impl_single!(Body, Body, encoded::Block);
impl_single!(Account, Account, Option);
impl_single!(Code, Code, Bytes);
impl_single!(Execution, TransactionProof, super::ExecutionResult);
impl_single!(Signal, Signal, Vec);
macro_rules! impl_args {
() => {
impl RequestAdapter for T {
type Out = T::Out;
fn make_requests(self) -> Vec {
vec![self.make()]
}
fn extract_from(mut responses: Vec) -> Self::Out {
T::extract(responses.pop().expect(SUPPLIED_MATCHES))
}
}
};
($first: ident, $($next: ident,)*) => {
impl<
$first: RequestArg,
$($next: RequestArg,)*
>
RequestAdapter for ($first, $($next,)*) {
type Out = ($first::Out, $($next::Out,)*);
fn make_requests(self) -> Vec {
let ($first, $($next,)*) = self;
vec![
$first.make(),
$($next.make(),)*
]
}
fn extract_from(responses: Vec) -> Self::Out {
let mut iter = responses.into_iter();
(
$first::extract(iter.next().expect(SUPPLIED_MATCHES)),
$($next::extract(iter.next().expect(SUPPLIED_MATCHES)),)*
)
}
}
impl_args!($($next,)*);
}
}
mod impls {
#![allow(non_snake_case)]
use super::{RequestAdapter, RequestArg, Request, Response, SUPPLIED_MATCHES};
impl_args!(A, B, C, D, E, F, G, H, I, J, K, L,);
}
/// A block header to be used for verification.
/// May be stored or an unresolved output of a prior request.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum HeaderRef {
/// A stored header.
Stored(encoded::Header),
/// An unresolved header. The first item here is the index of the request which
/// will return the header. The second is a back-reference pointing to a block hash
/// which can be used to make requests until that header is resolved.
Unresolved(usize, Field),
}
impl HeaderRef {
/// Attempt to inspect the header.
pub fn as_ref(&self) -> Result<&encoded::Header, Error> {
match *self {
HeaderRef::Stored(ref hdr) => Ok(hdr),
HeaderRef::Unresolved(idx, _) => Err(Error::UnresolvedHeader(idx)),
}
}
// get the blockhash field to be used in requests.
fn field(&self) -> Field {
match *self {
HeaderRef::Stored(ref hdr) => Field::Scalar(hdr.hash()),
HeaderRef::Unresolved(_, ref field) => field.clone(),
}
}
// yield the index of the request which will produce the header.
fn needs_header(&self) -> Option<(usize, Field)> {
match *self {
HeaderRef::Stored(_) => None,
HeaderRef::Unresolved(idx, ref field) => Some((idx, field.clone())),
}
}
}
impl From for HeaderRef {
fn from(header: encoded::Header) -> Self {
HeaderRef::Stored(header)
}
}
/// Requests coupled with their required data for verification.
/// This is used internally but not part of the public API.
#[derive(Clone)]
#[allow(missing_docs)]
pub enum CheckedRequest {
HeaderProof(HeaderProof, net_request::IncompleteHeaderProofRequest),
HeaderByHash(HeaderByHash, net_request::IncompleteHeadersRequest),
TransactionIndex(TransactionIndex, net_request::IncompleteTransactionIndexRequest),
Receipts(BlockReceipts, net_request::IncompleteReceiptsRequest),
Body(Body, net_request::IncompleteBodyRequest),
Account(Account, net_request::IncompleteAccountRequest),
Code(Code, net_request::IncompleteCodeRequest),
Execution(TransactionProof, net_request::IncompleteExecutionRequest),
Signal(Signal, net_request::IncompleteSignalRequest)
}
impl From for CheckedRequest {
fn from(req: Request) -> Self {
match req {
Request::HeaderByHash(req) => {
let net_req = net_request::IncompleteHeadersRequest {
start: req.0.map(Into::into),
skip: 0,
max: 1,
reverse: false,
};
CheckedRequest::HeaderByHash(req, net_req)
}
Request::HeaderProof(req) => {
let net_req = net_request::IncompleteHeaderProofRequest {
num: req.num().into(),
};
CheckedRequest::HeaderProof(req, net_req)
}
Request::TransactionIndex(req) => {
let net_req = net_request::IncompleteTransactionIndexRequest {
hash: req.0.clone(),
};
CheckedRequest::TransactionIndex(req, net_req)
}
Request::Body(req) => {
let net_req = net_request::IncompleteBodyRequest {
hash: req.0.field(),
};
CheckedRequest::Body(req, net_req)
}
Request::Receipts(req) => {
let net_req = net_request::IncompleteReceiptsRequest {
hash: req.0.field(),
};
CheckedRequest::Receipts(req, net_req)
}
Request::Account(req) => {
let net_req = net_request::IncompleteAccountRequest {
block_hash: req.header.field(),
address_hash: ::hash::keccak(&req.address).into(),
};
CheckedRequest::Account(req, net_req)
}
Request::Code(req) => {
let net_req = net_request::IncompleteCodeRequest {
block_hash: req.header.field(),
code_hash: req.code_hash.into(),
};
CheckedRequest::Code(req, net_req)
}
Request::Execution(req) => {
let net_req = net_request::IncompleteExecutionRequest {
block_hash: req.header.field(),
from: req.tx.sender(),
gas: req.tx.gas,
gas_price: req.tx.gas_price,
action: req.tx.action.clone(),
value: req.tx.value,
data: req.tx.data.clone(),
};
CheckedRequest::Execution(req, net_req)
}
Request::Signal(req) => {
let net_req = net_request::IncompleteSignalRequest {
block_hash: req.hash.into(),
};
CheckedRequest::Signal(req, net_req)
}
}
}
}
impl CheckedRequest {
/// Convert this into a network request.
pub fn into_net_request(self) -> net_request::Request {
use ::request::Request as NetRequest;
match self {
CheckedRequest::HeaderProof(_, req) => NetRequest::HeaderProof(req),
CheckedRequest::HeaderByHash(_, req) => NetRequest::Headers(req),
CheckedRequest::TransactionIndex(_, req) => NetRequest::TransactionIndex(req),
CheckedRequest::Receipts(_, req) => NetRequest::Receipts(req),
CheckedRequest::Body(_, req) => NetRequest::Body(req),
CheckedRequest::Account(_, req) => NetRequest::Account(req),
CheckedRequest::Code(_, req) => NetRequest::Code(req),
CheckedRequest::Execution(_, req) => NetRequest::Execution(req),
CheckedRequest::Signal(_, req) => NetRequest::Signal(req),
}
}
/// Whether this needs a header from a prior request.
/// Returns `Some` with the index of the request returning the header
/// and the field giving the hash
/// if so, `None` otherwise.
pub fn needs_header(&self) -> Option<(usize, Field)> {
match *self {
CheckedRequest::Receipts(ref x, _) => x.0.needs_header(),
CheckedRequest::Body(ref x, _) => x.0.needs_header(),
CheckedRequest::Account(ref x, _) => x.header.needs_header(),
CheckedRequest::Code(ref x, _) => x.header.needs_header(),
CheckedRequest::Execution(ref x, _) => x.header.needs_header(),
_ => None,
}
}
/// Provide a header where one was needed. Should only be called if `needs_header`
/// returns `Some`, and for correctness, only use the header yielded by the correct
/// request.
pub fn provide_header(&mut self, header: encoded::Header) {
match *self {
CheckedRequest::Receipts(ref mut x, _) => x.0 = HeaderRef::Stored(header),
CheckedRequest::Body(ref mut x, _) => x.0 = HeaderRef::Stored(header),
CheckedRequest::Account(ref mut x, _) => x.header = HeaderRef::Stored(header),
CheckedRequest::Code(ref mut x, _) => x.header = HeaderRef::Stored(header),
CheckedRequest::Execution(ref mut x, _) => x.header = HeaderRef::Stored(header),
_ => {},
}
}
/// Attempt to complete the request based on data in the cache.
pub fn respond_local(&self, cache: &Mutex<::cache::Cache>) -> Option {
match *self {
CheckedRequest::HeaderProof(ref check, _) => {
let mut cache = cache.lock();
cache.block_hash(&check.num)
.and_then(|h| cache.chain_score(&h).map(|s| (h, s)))
.map(|(h, s)| Response::HeaderProof((h, s)))
}
CheckedRequest::HeaderByHash(_, ref req) => {
if let Some(&net_request::HashOrNumber::Hash(ref h)) = req.start.as_ref() {
return cache.lock().block_header(h).map(Response::HeaderByHash);
}
None
}
CheckedRequest::Receipts(ref check, ref req) => {
// empty transactions -> no receipts
if check.0.as_ref().ok().map_or(false, |hdr| hdr.receipts_root() == KECCAK_NULL_RLP) {
return Some(Response::Receipts(Vec::new()));
}
req.hash.as_ref()
.and_then(|hash| cache.lock().block_receipts(hash))
.map(Response::Receipts)
}
CheckedRequest::Body(ref check, ref req) => {
// check for empty body.
if let Some(hdr) = check.0.as_ref().ok() {
if hdr.transactions_root() == KECCAK_NULL_RLP && hdr.uncles_hash() == KECCAK_EMPTY_LIST_RLP {
let mut stream = RlpStream::new_list(3);
stream.append_raw(hdr.rlp().as_raw(), 1);
stream.begin_list(0);
stream.begin_list(0);
return Some(Response::Body(encoded::Block::new(stream.out())));
}
}
// otherwise, check for cached body and header.
let block_hash = req.hash.as_ref()
.cloned()
.or_else(|| check.0.as_ref().ok().map(|hdr| hdr.hash()));
let block_hash = match block_hash {
Some(hash) => hash,
None => return None,
};
let mut cache = cache.lock();
let cached_header;
// can't use as_ref here although it seems like you would be able to:
// it complains about uninitialized `cached_header`.
let block_header = match check.0.as_ref().ok() {
Some(hdr) => Some(hdr),
None => {
cached_header = cache.block_header(&block_hash);
cached_header.as_ref()
}
};
block_header
.and_then(|hdr| cache.block_body(&block_hash).map(|b| (hdr, b)))
.map(|(hdr, body)| {
Response::Body(encoded::Block::new_from_header_and_body(&hdr.view(), &body.view()))
})
}
CheckedRequest::Code(_, ref req) => {
if req.code_hash.as_ref().map_or(false, |&h| h == KECCAK_EMPTY) {
Some(Response::Code(Vec::new()))
} else {
None
}
}
_ => None,
}
}
}
macro_rules! match_me {
($me: expr, ($check: pat, $req: pat) => $e: expr) => {
match $me {
CheckedRequest::HeaderProof($check, $req) => $e,
CheckedRequest::HeaderByHash($check, $req) => $e,
CheckedRequest::TransactionIndex($check, $req) => $e,
CheckedRequest::Receipts($check, $req) => $e,
CheckedRequest::Body($check, $req) => $e,
CheckedRequest::Account($check, $req) => $e,
CheckedRequest::Code($check, $req) => $e,
CheckedRequest::Execution($check, $req) => $e,
CheckedRequest::Signal($check, $req) => $e,
}
}
}
impl IncompleteRequest for CheckedRequest {
type Complete = CompleteRequest;
type Response = net_request::Response;
fn check_outputs(&self, mut f: F) -> Result<(), net_request::NoSuchOutput>
where F: FnMut(usize, usize, OutputKind) -> Result<(), net_request::NoSuchOutput>
{
match *self {
CheckedRequest::HeaderProof(_, ref req) => req.check_outputs(f),
CheckedRequest::HeaderByHash(ref check, ref req) => {
req.check_outputs(&mut f)?;
// make sure the output given is definitively a hash.
match check.0 {
Field::BackReference(r, idx) => f(r, idx, OutputKind::Hash),
_ => Ok(()),
}
}
CheckedRequest::TransactionIndex(_, ref req) => req.check_outputs(f),
CheckedRequest::Receipts(_, ref req) => req.check_outputs(f),
CheckedRequest::Body(_, ref req) => req.check_outputs(f),
CheckedRequest::Account(_, ref req) => req.check_outputs(f),
CheckedRequest::Code(_, ref req) => req.check_outputs(f),
CheckedRequest::Execution(_, ref req) => req.check_outputs(f),
CheckedRequest::Signal(_, ref req) => req.check_outputs(f),
}
}
fn note_outputs(&self, f: F) where F: FnMut(usize, OutputKind) {
match_me!(*self, (_, ref req) => req.note_outputs(f))
}
fn fill(&mut self, f: F) where F: Fn(usize, usize) -> Result