// 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 . //! 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 { match_me!(*self, (_, ref mut req) => req.fill(f)) } fn complete(self) -> Result { match self { CheckedRequest::HeaderProof(_, req) => req.complete().map(CompleteRequest::HeaderProof), CheckedRequest::HeaderByHash(_, req) => req.complete().map(CompleteRequest::Headers), CheckedRequest::TransactionIndex(_, req) => req.complete().map(CompleteRequest::TransactionIndex), CheckedRequest::Receipts(_, req) => req.complete().map(CompleteRequest::Receipts), CheckedRequest::Body(_, req) => req.complete().map(CompleteRequest::Body), CheckedRequest::Account(_, req) => req.complete().map(CompleteRequest::Account), CheckedRequest::Code(_, req) => req.complete().map(CompleteRequest::Code), CheckedRequest::Execution(_, req) => req.complete().map(CompleteRequest::Execution), CheckedRequest::Signal(_, 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::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 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, /// 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, } 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: num, cht_num: cht_num, cht_root: 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. #[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(ref h), &net_request::HashOrNumber::Hash(ref 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(); match hash == expected_hash { true => { cache.lock().insert_block_header(hash, header.clone()); Ok(header.clone()) } false => 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() { return Err(Error::WrongTrieRoot(header.transactions_root(), tx_root)); } let uncles_hash = keccak(body.uncles_rlp().as_raw()); if 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))); match receipts_root == found_root { true => { cache.lock().insert_block_receipts(receipts_root, receipts.to_vec()); Ok(receipts.to_vec()) } false => 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 = MemoryDB::new(); 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 => 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.clone(); let proved_execution = state::check_proof( state_items, root, &self.tx, self.engine.machine(), &self.env_info, ); match proved_execution { ProvedExecution::BadProof => Err(Error::BadProof), ProvedExecution::Failed(e) => Ok(Err(e)), ProvedExecution::Complete(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 std::time::Duration; use ethereum_types::{H256, Address}; use memorydb::MemoryDB; use parking_lot::Mutex; use trie::{Trie, TrieMut, SecTrieDB, SecTrieDBMut}; use trie::recorder::Recorder; use hash::keccak; use ethcore::client::{BlockChainClient, BlockInfo, TestBlockChainClient, EachBlockWith}; use ethcore::header::Header; use ethcore::encoded; use ethcore::receipt::{Receipt, TransactionOutcome}; 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).into_vec()); let cache = Mutex::new(make_cache()); assert!(HeaderByHash(hash.into()).check_response(&cache, &hash.into(), &[raw_header]).is_ok()) } #[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_vec()).into()); let cache = Mutex::new(make_cache()); let response = encoded::Body::new(body_stream.drain().into_vec()); 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_vec()).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 = MemoryDB::new(); 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_vec()).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_vec()).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()); } }