// Copyright 2015-2019 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum 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 Ethereum 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 Ethereum. If not, see . //! Request types, verification, and verification errors. use std::{cmp, sync::Arc}; use bytes::Bytes; use common_types::{ basic_account::BasicAccount, encoded, receipt::Receipt, transaction::SignedTransaction, }; use ethcore::{ engines::{EthEngine, StateDependentProof}, machine::EthereumMachine, state::{self, ProvedExecution}, }; use ethereum_types::{Address, H256, U256}; use ethtrie::{TrieDB, TrieError}; use hash::{keccak, KECCAK_EMPTY, KECCAK_EMPTY_LIST_RLP, KECCAK_NULL_RLP}; use hash_db::HashDB; use kvdb::DBValue; use parking_lot::Mutex; use request::{self as net_request, CompleteRequest, Field, IncompleteRequest, Output, OutputKind}; use rlp::{Rlp, RlpStream}; use trie::Trie; use vm::EnvInfo; const SUPPLIED_MATCHES: &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 a header by hash with a range of its ancestors. HeaderWithAncestors(HeaderWithAncestors), /// 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!( HeaderWithAncestors, HeaderWithAncestors, Vec ); 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::{Request, RequestAdapter, RequestArg, 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(_, field) => field, } } // 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, field) => Some((idx, field)), } } } 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), HeaderWithAncestors(HeaderWithAncestors, 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, }; trace!(target: "on_demand", "HeaderByHash Request, {:?}", net_req); CheckedRequest::HeaderByHash(req, net_req) } Request::HeaderWithAncestors(req) => { let net_req = net_request::IncompleteHeadersRequest { start: req.block_hash.map(Into::into), skip: 0, max: req.ancestor_count + 1, reverse: true, }; trace!(target: "on_demand", "HeaderWithAncestors Request, {:?}", net_req); CheckedRequest::HeaderWithAncestors(req, net_req) } Request::HeaderProof(req) => { let net_req = net_request::IncompleteHeaderProofRequest { num: req.num().into(), }; trace!(target: "on_demand", "HeaderProof Request, {:?}", net_req); CheckedRequest::HeaderProof(req, net_req) } Request::TransactionIndex(req) => { let net_req = net_request::IncompleteTransactionIndexRequest { hash: req.0 }; trace!(target: "on_demand", "TransactionIndex Request, {:?}", net_req); CheckedRequest::TransactionIndex(req, net_req) } Request::Body(req) => { let net_req = net_request::IncompleteBodyRequest { hash: req.0.field(), }; trace!(target: "on_demand", "Body Request, {:?}", net_req); CheckedRequest::Body(req, net_req) } Request::Receipts(req) => { let net_req = net_request::IncompleteReceiptsRequest { hash: req.0.field(), }; trace!(target: "on_demand", "Receipt Request, {:?}", net_req); 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(), }; trace!(target: "on_demand", "Account Request, {:?}", net_req); CheckedRequest::Account(req, net_req) } Request::Code(req) => { let net_req = net_request::IncompleteCodeRequest { block_hash: req.header.field(), code_hash: req.code_hash, }; trace!(target: "on_demand", "Code Request, {:?}", net_req); 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(), }; trace!(target: "on_demand", "Execution request, {:?}", net_req); CheckedRequest::Execution(req, net_req) } Request::Signal(req) => { let net_req = net_request::IncompleteSignalRequest { block_hash: req.hash.into(), }; trace!(target: "on_demand", "Signal Request, {:?}", net_req); 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::HeaderWithAncestors(_, 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::HeaderWithAncestors(_, ref req) => { if req.skip != 1 || !req.reverse { return None; } if let Some(&net_request::HashOrNumber::Hash(start)) = req.start.as_ref() { let mut result = Vec::with_capacity(req.max as usize); let mut hash = start; let mut cache = cache.lock(); for _ in 0..req.max { match cache.block_header(&hash) { Some(header) => { hash = header.parent_hash(); result.push(header); } None => return None, } } Some(Response::HeaderWithAncestors(result)) } else { 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 Ok(hdr) = check.0.as_ref() { 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::HeaderWithAncestors($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::HeaderWithAncestors(ref check, ref req) => { req.check_outputs(&mut f)?; // make sure the output given is definitively a hash. match check.block_hash { 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, { match_me!(*self, (_, ref mut req) => req.fill(f)) } fn complete(self) -> Result { match self { CheckedRequest::HeaderProof(_, req) => { trace!(target: "on_demand", "HeaderProof request completed {:?}", req); req.complete().map(CompleteRequest::HeaderProof) } CheckedRequest::HeaderByHash(_, req) => { trace!(target: "on_demand", "HeaderByHash request completed {:?}", req); req.complete().map(CompleteRequest::Headers) } CheckedRequest::HeaderWithAncestors(_, req) => { trace!(target: "on_demand", "HeaderWithAncestors request completed {:?}", req); req.complete().map(CompleteRequest::Headers) } CheckedRequest::TransactionIndex(_, req) => { trace!(target: "on_demand", "TransactionIndex request completed {:?}", req); req.complete().map(CompleteRequest::TransactionIndex) } CheckedRequest::Receipts(_, req) => { trace!(target: "on_demand", "Receipt request completed {:?}", req); req.complete().map(CompleteRequest::Receipts) } CheckedRequest::Body(_, req) => { trace!(target: "on_demand", "Block request completed {:?}", req); req.complete().map(CompleteRequest::Body) } CheckedRequest::Account(_, req) => { trace!(target: "on_demand", "Account request completed {:?}", req); req.complete().map(CompleteRequest::Account) } CheckedRequest::Code(_, req) => { trace!(target: "on_demand", "Code request completed {:?}", req); req.complete().map(CompleteRequest::Code) } CheckedRequest::Execution(_, req) => { trace!(target: "on_demand", "Execution request completed {:?}", req); req.complete().map(CompleteRequest::Execution) } CheckedRequest::Signal(_, req) => { trace!(target: "on_demand", "Signal request completed {:?}", req); req.complete().map(CompleteRequest::Signal) } } } fn adjust_refs(&mut self, mapping: F) where F: FnMut(usize) -> usize, { match_me!(*self, (_, ref mut req) => req.adjust_refs(mapping)) } } impl net_request::CheckedRequest for CheckedRequest { type Extract = Response; type Error = Error; type Environment = Mutex<::cache::Cache>; /// Check whether the response matches (beyond the type). fn check_response( &self, complete: &Self::Complete, cache: &Mutex<::cache::Cache>, response: &Self::Response, ) -> Result { use request::Response as NetResponse; // helper for expecting a specific response for a given request. macro_rules! expect { ($res: pat => $e: expr) => {{ match (response, complete) { $res => $e, _ => Err(Error::WrongKind), } }}; } // check response against contained prover. match *self { CheckedRequest::HeaderProof(ref prover, _) => { expect!((&NetResponse::HeaderProof(ref res), _) => prover.check_response(cache, &res.proof).map(Response::HeaderProof)) } CheckedRequest::HeaderByHash(ref prover, _) => { expect!((&NetResponse::Headers(ref res), &CompleteRequest::Headers(ref req)) => prover.check_response(cache, &req.start, &res.headers).map(Response::HeaderByHash)) } CheckedRequest::HeaderWithAncestors(ref prover, _) => { expect!((&NetResponse::Headers(ref res), &CompleteRequest::Headers(ref req)) => prover.check_response(cache, &req.start, &res.headers).map(Response::HeaderWithAncestors)) } CheckedRequest::TransactionIndex(ref prover, _) => { expect!((&NetResponse::TransactionIndex(ref res), _) => prover.check_response(cache, res).map(Response::TransactionIndex)) } CheckedRequest::Receipts(ref prover, _) => { expect!((&NetResponse::Receipts(ref res), _) => prover.check_response(cache, &res.receipts).map(Response::Receipts)) } CheckedRequest::Body(ref prover, _) => expect!((&NetResponse::Body(ref res), _) => prover.check_response(cache, &res.body).map(Response::Body)), CheckedRequest::Account(ref prover, _) => { expect!((&NetResponse::Account(ref res), _) => prover.check_response(cache, &res.proof).map(Response::Account)) } CheckedRequest::Code(ref prover, _) => { expect!((&NetResponse::Code(ref res), &CompleteRequest::Code(ref req)) => prover.check_response(cache, &req.code_hash, &res.code).map(Response::Code)) } CheckedRequest::Execution(ref prover, _) => { expect!((&NetResponse::Execution(ref res), _) => prover.check_response(cache, &res.items).map(Response::Execution)) } CheckedRequest::Signal(ref prover, _) => expect!((&NetResponse::Signal(ref res), _) => prover.check_response(cache, &res.signal).map(Response::Signal)), } } } /// Responses to on-demand requests. /// All of these are checked. pub enum Response { /// Response to a header proof request. /// Returns the hash and chain score. HeaderProof((H256, U256)), /// Response to a header-by-hash request. HeaderByHash(encoded::Header), /// Response to a header-by-hash with ancestors request. HeaderWithAncestors(Vec), /// Response to a transaction-index request. TransactionIndex(net_request::TransactionIndexResponse), /// Response to a receipts request. Receipts(Vec), /// Response to a block body request. Body(encoded::Block), /// Response to an Account request. // TODO: `unwrap_or(engine_defaults)` Account(Option), /// Response to a request for code. Code(Vec), /// Response to a request for proved execution. Execution(super::ExecutionResult), /// Response to a request for epoch change signal. Signal(Vec), } impl net_request::ResponseLike for Response { fn fill_outputs(&self, mut f: F) where F: FnMut(usize, Output), { match *self { Response::HeaderProof((ref hash, _)) => f(0, Output::Hash(*hash)), Response::Account(None) => { f(0, Output::Hash(KECCAK_EMPTY)); // code hash f(1, Output::Hash(KECCAK_NULL_RLP)); // storage root. } Response::Account(Some(ref acc)) => { f(0, Output::Hash(acc.code_hash)); f(1, Output::Hash(acc.storage_root)); } _ => {} } } } /// Errors in verification. #[derive(Debug, PartialEq)] pub enum Error { /// RLP decoder error. Decoder(::rlp::DecoderError), /// Empty response. Empty, /// Response data length exceeds request max. TooManyResults(u64, u64), /// Response data is incomplete. TooFewResults(u64, u64), /// Trie lookup error (result of bad proof) Trie(TrieError), /// Bad inclusion proof BadProof, /// Header by number instead of hash. HeaderByNumber, /// Unresolved header reference. UnresolvedHeader(usize), /// Wrong header number. WrongNumber(u64, u64), /// Wrong hash. WrongHash(H256, H256), /// Wrong trie root. WrongTrieRoot(H256, H256), /// Wrong response kind. WrongKind, /// Wrong sequence of headers. WrongHeaderSequence, } impl From<::rlp::DecoderError> for Error { fn from(err: ::rlp::DecoderError) -> Self { Error::Decoder(err) } } impl From> for Error { fn from(err: Box) -> Self { Error::Trie(*err) } } /// Request for header proof by number #[derive(Debug, Clone, PartialEq, Eq)] pub struct HeaderProof { /// The header's number. num: u64, /// The cht number for the given block number. cht_num: u64, /// The root of the CHT containing this header. cht_root: H256, } impl HeaderProof { /// Construct a new header-by-number request. Fails if the given number is 0. /// Provide the expected CHT root to compare against. pub fn new(num: u64, cht_root: H256) -> Option { ::cht::block_to_cht_number(num).map(|cht_num| HeaderProof { num, cht_num, cht_root, }) } /// Access the requested block number. pub fn num(&self) -> u64 { self.num } /// Access the CHT number. pub fn cht_num(&self) -> u64 { self.cht_num } /// Access the expected CHT root. pub fn cht_root(&self) -> H256 { self.cht_root } /// Check a response with a CHT proof, get a hash and total difficulty back. pub fn check_response( &self, cache: &Mutex<::cache::Cache>, proof: &[Bytes], ) -> Result<(H256, U256), Error> { match ::cht::check_proof(proof, self.num, self.cht_root) { Some((expected_hash, td)) => { let mut cache = cache.lock(); cache.insert_block_hash(self.num, expected_hash); cache.insert_chain_score(expected_hash, td); Ok((expected_hash, td)) } None => Err(Error::BadProof), } } } /// Request for a header by hash with a range of ancestors. #[derive(Debug, Clone, PartialEq, Eq)] pub struct HeaderWithAncestors { /// Hash of the last block in the range to fetch. pub block_hash: Field, /// Number of headers before the last block to fetch in addition. pub ancestor_count: u64, } impl HeaderWithAncestors { /// Check a response for the headers. pub fn check_response( &self, cache: &Mutex<::cache::Cache>, start: &net_request::HashOrNumber, headers: &[encoded::Header], ) -> Result, Error> { let expected_hash = match (self.block_hash, start) { (Field::Scalar(h), &net_request::HashOrNumber::Hash(h2)) => { if h != h2 { return Err(Error::WrongHash(h, h2)); } h } (_, &net_request::HashOrNumber::Hash(h2)) => h2, _ => return Err(Error::HeaderByNumber), }; let start_header = headers.first().ok_or(Error::Empty)?; let start_hash = start_header.hash(); if start_hash != expected_hash { return Err(Error::WrongHash(expected_hash, start_hash)); } let expected_len = 1 + cmp::min(self.ancestor_count, start_header.number()); let actual_len = headers.len() as u64; match actual_len.cmp(&expected_len) { cmp::Ordering::Less => return Err(Error::TooFewResults(expected_len, actual_len)), cmp::Ordering::Greater => return Err(Error::TooManyResults(expected_len, actual_len)), cmp::Ordering::Equal => (), }; for (header, prev_header) in headers.iter().zip(headers[1..].iter()) { if header.number() != prev_header.number() + 1 || header.parent_hash() != prev_header.hash() { return Err(Error::WrongHeaderSequence); } } let mut cache = cache.lock(); for header in headers { cache.insert_block_header(header.hash(), header.clone()); } Ok(headers.to_vec()) } } /// Request for a header by hash. #[derive(Debug, Clone, PartialEq, Eq)] pub struct HeaderByHash(pub Field); impl HeaderByHash { /// Check a response for the header. pub fn check_response( &self, cache: &Mutex<::cache::Cache>, start: &net_request::HashOrNumber, headers: &[encoded::Header], ) -> Result { let expected_hash = match (self.0, start) { (Field::Scalar(h), &net_request::HashOrNumber::Hash(h2)) => { if h != h2 { return Err(Error::WrongHash(h, h2)); } h } (_, &net_request::HashOrNumber::Hash(h2)) => h2, _ => return Err(Error::HeaderByNumber), }; let header = headers.get(0).ok_or(Error::Empty)?; let hash = header.hash(); if hash == expected_hash { cache.lock().insert_block_header(hash, header.clone()); Ok(header.clone()) } else { Err(Error::WrongHash(expected_hash, hash)) } } } /// Request for a transaction index. #[derive(Debug, Clone, PartialEq, Eq)] pub struct TransactionIndex(pub Field); impl TransactionIndex { /// Check a response for the transaction index. // // TODO: proper checking involves looking at canonicality of the // hash w.r.t. the current best block header. // // unlike all other forms of request, we don't know the header to check // until we make this request. // // This would require lookups in the database or perhaps CHT requests, // which aren't currently possible. // // Also, returning a result that is not locally canonical doesn't necessarily // indicate misbehavior, so the punishment scheme would need to be revised. pub fn check_response( &self, _cache: &Mutex<::cache::Cache>, res: &net_request::TransactionIndexResponse, ) -> Result { Ok(res.clone()) } } /// Request for a block, with header for verification. #[derive(Debug, Clone, PartialEq, Eq)] pub struct Body(pub HeaderRef); impl Body { /// Check a response for this block body. pub fn check_response( &self, cache: &Mutex<::cache::Cache>, body: &encoded::Body, ) -> Result { // check the integrity of the the body against the header let header = self.0.as_ref()?; let tx_root = ::triehash::ordered_trie_root(body.transactions_rlp().iter().map(|r| r.as_raw())); if tx_root != header.transactions_root() { trace!(target: "on_demand", "Body Response: \"WrongTrieRoot\" tx_root: {:?} header_root: {:?}", tx_root, header.transactions_root()); return Err(Error::WrongTrieRoot(header.transactions_root(), tx_root)); } let uncles_hash = keccak(body.uncles_rlp().as_raw()); if uncles_hash != header.uncles_hash() { trace!(target: "on_demand", "Body Response: \"WrongHash\" tx_root: {:?} header_root: {:?}", uncles_hash, header.uncles_hash()); return Err(Error::WrongHash(header.uncles_hash(), uncles_hash)); } // concatenate the header and the body. let block = encoded::Block::new_from_header_and_body(&header.view(), &body.view()); cache.lock().insert_block_body(header.hash(), body.clone()); Ok(block) } } /// Request for a block's receipts with header for verification. #[derive(Debug, Clone, PartialEq, Eq)] pub struct BlockReceipts(pub HeaderRef); impl BlockReceipts { /// Check a response with receipts against the stored header. pub fn check_response( &self, cache: &Mutex<::cache::Cache>, receipts: &[Receipt], ) -> Result, Error> { let receipts_root = self.0.as_ref()?.receipts_root(); let found_root = ::triehash::ordered_trie_root(receipts.iter().map(|r| ::rlp::encode(r))); if receipts_root == found_root { cache .lock() .insert_block_receipts(receipts_root, receipts.to_vec()); Ok(receipts.to_vec()) } else { trace!(target: "on_demand", "Receipt Reponse: \"WrongTrieRoot\" receipts_root: {:?} found_root: {:?}", receipts_root, found_root); Err(Error::WrongTrieRoot(receipts_root, found_root)) } } } /// Request for an account structure. #[derive(Debug, Clone, PartialEq, Eq)] pub struct Account { /// Header for verification. pub header: HeaderRef, /// Address requested. pub address: Address, } impl Account { /// Check a response with an account against the stored header. pub fn check_response( &self, _: &Mutex<::cache::Cache>, proof: &[Bytes], ) -> Result, Error> { let header = self.header.as_ref()?; let state_root = header.state_root(); let mut db = journaldb::new_memory_db(); for node in proof { db.insert(&node[..]); } match TrieDB::new(&db, &state_root).and_then(|t| t.get(&keccak(&self.address)))? { Some(val) => { let rlp = Rlp::new(&val); Ok(Some(BasicAccount { nonce: rlp.val_at(0)?, balance: rlp.val_at(1)?, storage_root: rlp.val_at(2)?, code_hash: rlp.val_at(3)?, })) } None => { trace!(target: "on_demand", "Account {:?} not found", self.address); Ok(None) } } } } /// Request for account code. #[derive(Debug, Clone, PartialEq, Eq)] pub struct Code { /// Header reference. pub header: HeaderRef, /// Account's code hash. pub code_hash: Field, } impl Code { /// Check a response with code against the code hash. pub fn check_response( &self, _: &Mutex<::cache::Cache>, code_hash: &H256, code: &[u8], ) -> Result, Error> { let found_hash = keccak(code); if &found_hash == code_hash { Ok(code.to_vec()) } else { Err(Error::WrongHash(*code_hash, found_hash)) } } } /// Request for transaction execution, along with the parts necessary to verify the proof. #[derive(Clone)] pub struct TransactionProof { /// The transaction to request proof of. pub tx: SignedTransaction, /// Block header. pub header: HeaderRef, /// Transaction environment info. // TODO: it's not really possible to provide this if the header is unknown. pub env_info: EnvInfo, /// Consensus engine. pub engine: Arc, } impl TransactionProof { /// Check the proof, returning the proved execution or indicate that the proof was bad. pub fn check_response( &self, _: &Mutex<::cache::Cache>, state_items: &[DBValue], ) -> Result { let root = self.header.as_ref()?.state_root(); let mut env_info = self.env_info.clone(); env_info.gas_limit = self.tx.gas; let proved_execution = state::check_proof( state_items, root, &self.tx, self.engine.machine(), &self.env_info, ); match proved_execution { ProvedExecution::BadProof => { trace!(target: "on_demand", "BadExecution Proof"); Err(Error::BadProof) } ProvedExecution::Failed(e) => { trace!(target: "on_demand", "Execution Proof failed: {:?}", e); Ok(Err(e)) } ProvedExecution::Complete(e) => { trace!(target: "on_demand", "Execution successful: {:?}", e); Ok(Ok(e)) } } } } /// Request for epoch signal. /// Provide engine and state-dependent proof checker. #[derive(Clone)] pub struct Signal { /// Block hash and number to fetch proof for. pub hash: H256, /// Consensus engine, used to check the proof. pub engine: Arc, /// Special checker for the proof. pub proof_check: Arc>, } impl Signal { /// Check the signal, returning the signal or indicate that it's bad. pub fn check_response( &self, _: &Mutex<::cache::Cache>, signal: &[u8], ) -> Result, Error> { self.proof_check .check_proof(self.engine.machine(), signal) .map(|_| signal.to_owned()) .map_err(|_| Error::BadProof) } } #[cfg(test)] mod tests { use super::*; use ethereum_types::{Address, H256}; use ethtrie::{SecTrieDB, SecTrieDBMut}; use hash::keccak; use parking_lot::Mutex; use std::time::Duration; use trie::{Recorder, Trie, TrieMut}; use common_types::{ encoded, header::Header, receipt::{Receipt, TransactionOutcome}, }; use ethcore::client::{BlockChainClient, BlockInfo, EachBlockWith, TestBlockChainClient}; fn make_cache() -> ::cache::Cache { ::cache::Cache::new(Default::default(), Duration::from_secs(1)) } #[test] fn no_invalid_header_by_number() { assert!(HeaderProof::new(0, Default::default()).is_none()) } #[test] fn check_header_proof() { use cht; let test_client = TestBlockChainClient::new(); test_client.add_blocks(10500, EachBlockWith::Nothing); let cht = { let fetcher = |id| { let hdr = test_client.block_header(id).unwrap(); let td = test_client.block_total_difficulty(id).unwrap(); Some(cht::BlockInfo { hash: hdr.hash(), parent_hash: hdr.parent_hash(), total_difficulty: td, }) }; cht::build(cht::block_to_cht_number(10_000).unwrap(), fetcher).unwrap() }; let proof = cht.prove(10_000, 0).unwrap().unwrap(); let req = HeaderProof::new(10_000, cht.root()).unwrap(); let cache = Mutex::new(make_cache()); assert!(req.check_response(&cache, &proof[..]).is_ok()); } #[test] fn check_header_by_hash() { let mut header = Header::new(); header.set_number(10_000); header.set_extra_data(b"test_header".to_vec()); let hash = header.hash(); let raw_header = encoded::Header::new(::rlp::encode(&header)); let cache = Mutex::new(make_cache()); assert!(HeaderByHash(hash.into()) .check_response(&cache, &hash.into(), &[raw_header]) .is_ok()) } #[test] fn check_header_with_ancestors() { let mut last_header_hash = H256::default(); let mut headers = (0..11) .map(|num| { let mut header = Header::new(); header.set_number(num); header.set_parent_hash(last_header_hash); last_header_hash = header.hash(); header }) .collect::>(); headers.reverse(); // because responses are in reverse order let raw_headers = headers .iter() .map(|hdr| encoded::Header::new(::rlp::encode(hdr))) .collect::>(); let mut invalid_successor = Header::new(); invalid_successor.set_number(11); invalid_successor.set_parent_hash(headers[1].hash()); let raw_invalid_successor = encoded::Header::new(::rlp::encode(&invalid_successor)); let cache = Mutex::new(make_cache()); let header_with_ancestors = |hash, count| HeaderWithAncestors { block_hash: hash, ancestor_count: count, }; // Correct responses assert!(header_with_ancestors(headers[0].hash().into(), 0) .check_response(&cache, &headers[0].hash().into(), &raw_headers[0..1]) .is_ok()); assert!(header_with_ancestors(headers[0].hash().into(), 2) .check_response(&cache, &headers[0].hash().into(), &raw_headers[0..3]) .is_ok()); assert!(header_with_ancestors(headers[0].hash().into(), 10) .check_response(&cache, &headers[0].hash().into(), &raw_headers[0..11]) .is_ok()); assert!(header_with_ancestors(headers[2].hash().into(), 2) .check_response(&cache, &headers[2].hash().into(), &raw_headers[2..5]) .is_ok()); assert!(header_with_ancestors(headers[2].hash().into(), 10) .check_response(&cache, &headers[2].hash().into(), &raw_headers[2..11]) .is_ok()); assert!(header_with_ancestors(invalid_successor.hash().into(), 0) .check_response( &cache, &invalid_successor.hash().into(), &[raw_invalid_successor.clone()] ) .is_ok()); // Incorrect responses assert_eq!( header_with_ancestors(invalid_successor.hash().into(), 0).check_response( &cache, &headers[0].hash().into(), &raw_headers[0..1] ), Err(Error::WrongHash( invalid_successor.hash(), headers[0].hash() )) ); assert_eq!( header_with_ancestors(headers[0].hash().into(), 0).check_response( &cache, &headers[0].hash().into(), &[] ), Err(Error::Empty) ); assert_eq!( header_with_ancestors(headers[0].hash().into(), 10).check_response( &cache, &headers[0].hash().into(), &raw_headers[0..10] ), Err(Error::TooFewResults(11, 10)) ); assert_eq!( header_with_ancestors(headers[0].hash().into(), 9).check_response( &cache, &headers[0].hash().into(), &raw_headers[0..11] ), Err(Error::TooManyResults(10, 11)) ); let response = &[raw_headers[0].clone(), raw_headers[2].clone()]; assert_eq!( header_with_ancestors(headers[0].hash().into(), 1).check_response( &cache, &headers[0].hash().into(), response ), Err(Error::WrongHeaderSequence) ); let response = &[raw_invalid_successor.clone(), raw_headers[0].clone()]; assert_eq!( header_with_ancestors(invalid_successor.hash().into(), 1).check_response( &cache, &invalid_successor.hash().into(), response ), Err(Error::WrongHeaderSequence) ); let response = &[raw_invalid_successor.clone(), raw_headers[1].clone()]; assert_eq!( header_with_ancestors(invalid_successor.hash().into(), 1).check_response( &cache, &invalid_successor.hash().into(), response ), Err(Error::WrongHeaderSequence) ); } #[test] fn check_body() { use rlp::RlpStream; let header = Header::new(); let mut body_stream = RlpStream::new_list(2); body_stream.begin_list(0).begin_list(0); let req = Body(encoded::Header::new(::rlp::encode(&header)).into()); let cache = Mutex::new(make_cache()); let response = encoded::Body::new(body_stream.drain()); assert!(req.check_response(&cache, &response).is_ok()) } #[test] fn check_receipts() { let receipts = (0..5) .map(|_| Receipt { outcome: TransactionOutcome::StateRoot(H256::random()), gas_used: 21_000u64.into(), log_bloom: Default::default(), logs: Vec::new(), }) .collect::>(); let mut header = Header::new(); let receipts_root = ::triehash::ordered_trie_root(receipts.iter().map(|x| ::rlp::encode(x))); header.set_receipts_root(receipts_root); let req = BlockReceipts(encoded::Header::new(::rlp::encode(&header)).into()); let cache = Mutex::new(make_cache()); assert!(req.check_response(&cache, &receipts).is_ok()) } #[test] fn check_state_proof() { use rlp::RlpStream; let mut root = H256::default(); let mut db = journaldb::new_memory_db(); let mut header = Header::new(); header.set_number(123_456); header.set_extra_data(b"test_header".to_vec()); let addr = Address::random(); let rand_acc = || { let mut stream = RlpStream::new_list(4); stream .append(&2u64) .append(&100_000_000u64) .append(&H256::random()) .append(&H256::random()); stream.out() }; { let mut trie = SecTrieDBMut::new(&mut db, &mut root); for _ in 0..100 { let address = Address::random(); trie.insert(&*address, &rand_acc()).unwrap(); } trie.insert(&*addr, &rand_acc()).unwrap(); } let proof = { let trie = SecTrieDB::new(&db, &root).unwrap(); let mut recorder = Recorder::new(); trie.get_with(&*addr, &mut recorder).unwrap().unwrap(); recorder .drain() .into_iter() .map(|r| r.data) .collect::>() }; header.set_state_root(root.clone()); let req = Account { header: encoded::Header::new(::rlp::encode(&header)).into(), address: addr, }; let cache = Mutex::new(make_cache()); assert!(req.check_response(&cache, &proof[..]).is_ok()); } #[test] fn check_code() { let code = vec![1u8; 256]; let code_hash = keccak(&code); let header = Header::new(); let req = Code { header: encoded::Header::new(::rlp::encode(&header)).into(), code_hash: code_hash.into(), }; let cache = Mutex::new(make_cache()); assert!(req.check_response(&cache, &code_hash, &code).is_ok()); assert!(req.check_response(&cache, &code_hash, &[]).is_err()); } }