From 2ff3dff6ea5da71803278e7ad98ee74ccfe851e7 Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Wed, 23 Aug 2017 15:37:35 +0200 Subject: [PATCH] serve epoch signals over network and check them --- ethcore/light/src/client/fetch.rs | 8 +- ethcore/light/src/client/mod.rs | 13 +- ethcore/light/src/net/load_timer.rs | 1 + ethcore/light/src/net/mod.rs | 13 +- ethcore/light/src/net/request_credits.rs | 13 +- ethcore/light/src/net/request_set.rs | 1 + ethcore/light/src/net/tests/mod.rs | 50 ++++++ ethcore/light/src/on_demand/mod.rs | 2 + ethcore/light/src/on_demand/request.rs | 42 ++++- ethcore/light/src/provider.rs | 13 ++ ethcore/light/src/types/request/mod.rs | 143 ++++++++++++++++++ ethcore/src/client/client.rs | 6 + ethcore/src/client/test_client.rs | 4 + ethcore/src/client/traits.rs | 3 + ethcore/src/engines/mod.rs | 4 +- .../engines/validator_set/safe_contract.rs | 12 +- 16 files changed, 298 insertions(+), 30 deletions(-) diff --git a/ethcore/light/src/client/fetch.rs b/ethcore/light/src/client/fetch.rs index 0f11ef2f4..2e125bb42 100644 --- a/ethcore/light/src/client/fetch.rs +++ b/ethcore/light/src/client/fetch.rs @@ -16,9 +16,13 @@ //! Trait for fetching chain data. +use std::sync::Arc; + +use ethcore::engines::{Engine, StateDependentProof}; use ethcore::header::Header; use ethcore::receipt::Receipt; use futures::future::IntoFuture; +use util::H256; /// Provides full chain data. pub trait ChainDataFetcher: Send + Sync + 'static { @@ -39,7 +43,7 @@ pub trait ChainDataFetcher: Send + Sync + 'static { fn block_receipts(&self, header: &Header) -> Self::Receipts; /// Fetch epoch transition proof at given header. - fn epoch_transition(&self, header: &Header) -> Self::Transition; + fn epoch_transition(&self, hash: H256, engine: Arc, checker: Arc) -> Self::Transition; } /// Fetcher implementation which cannot fetch anything. @@ -63,7 +67,7 @@ impl ChainDataFetcher for Unavailable { Err("fetching block receipts unavailable") } - fn epoch_transition(&self, _header: &Header) -> Self::Body { + fn epoch_transition(&self, _h: H256, _e: Arc, _check: Arc) -> Self::Transition { Err("fetching epoch transition proofs unavailable") } } diff --git a/ethcore/light/src/client/mod.rs b/ethcore/light/src/client/mod.rs index e880ef3ba..ec15f79a7 100644 --- a/ethcore/light/src/client/mod.rs +++ b/ethcore/light/src/client/mod.rs @@ -508,14 +508,11 @@ impl Client { let proof = match proof { Proof::Known(known) => known, Proof::WithState(state_dependent) => { - loop { - let proof = self.fetcher.epoch_transition(header).into_future().wait()?; - match state_dependent.check_proof(&*self.engine, &proof) { - Ok(()) => break proof, - Err(e) => - debug!(target: "client", "Fetched bad epoch transition proof from network: {}", e), - } - } + self.fetcher.epoch_transition( + header.hash(), + self.engine.clone(), + state_dependent + ).into_future().wait()? } }; diff --git a/ethcore/light/src/net/load_timer.rs b/ethcore/light/src/net/load_timer.rs index a169b86d9..2cdbb300f 100644 --- a/ethcore/light/src/net/load_timer.rs +++ b/ethcore/light/src/net/load_timer.rs @@ -62,6 +62,7 @@ fn hardcoded_serve_time(kind: Kind) -> u64 { Kind::Storage => 2_000_000, Kind::Code => 1_500_000, Kind::Execution => 250, // per gas. + Kind::Signal => 500_000, } } diff --git a/ethcore/light/src/net/mod.rs b/ethcore/light/src/net/mod.rs index 046dc68bd..2449d4568 100644 --- a/ethcore/light/src/net/mod.rs +++ b/ethcore/light/src/net/mod.rs @@ -102,9 +102,8 @@ mod packet { // relay transactions to peers. pub const SEND_TRANSACTIONS: u8 = 0x06; - // request and respond with epoch transition proof - pub const REQUEST_EPOCH_PROOF: u8 = 0x07; - pub const EPOCH_PROOF: u8 = 0x08; + // two packets were previously meant to be reserved for epoch proofs. + // these have since been moved to requests. } // timeouts for different kinds of requests. all values are in milliseconds. @@ -122,6 +121,7 @@ mod timeout { pub const CONTRACT_CODE: i64 = 100; pub const HEADER_PROOF: i64 = 100; pub const TRANSACTION_PROOF: i64 = 1000; // per gas? + pub const EPOCH_SIGNAL: i64 = 200; } /// A request id. @@ -582,12 +582,6 @@ impl LightProtocol { packet::SEND_TRANSACTIONS => self.relay_transactions(peer, io, rlp), - packet::REQUEST_EPOCH_PROOF | packet::EPOCH_PROOF => { - // ignore these for now, but leave them specified. - debug!(target: "pip", "Ignoring request/response for epoch proof"); - Ok(()) - } - other => { Err(Error::UnrecognizedPacket(other)) } @@ -950,6 +944,7 @@ impl LightProtocol { CompleteRequest::Storage(req) => self.provider.storage_proof(req).map(Response::Storage), CompleteRequest::Code(req) => self.provider.contract_code(req).map(Response::Code), CompleteRequest::Execution(req) => self.provider.transaction_proof(req).map(Response::Execution), + CompleteRequest::Signal(req) => self.provider.epoch_signal(req).map(Response::Signal), } }); diff --git a/ethcore/light/src/net/request_credits.rs b/ethcore/light/src/net/request_credits.rs index 950fa24d8..b1d83aaa6 100644 --- a/ethcore/light/src/net/request_credits.rs +++ b/ethcore/light/src/net/request_credits.rs @@ -91,6 +91,7 @@ pub struct CostTable { code: U256, header_proof: U256, transaction_proof: U256, // cost per gas. + epoch_signal: U256, } impl Default for CostTable { @@ -107,6 +108,7 @@ impl Default for CostTable { code: 20000.into(), header_proof: 15000.into(), transaction_proof: 2.into(), + epoch_signal: 10000.into(), } } } @@ -121,7 +123,7 @@ impl Encodable for CostTable { s.append(cost); } - s.begin_list(10).append(&self.base); + s.begin_list(11).append(&self.base); append_cost(s, &self.headers, request::Kind::Headers); append_cost(s, &self.transaction_index, request::Kind::TransactionIndex); append_cost(s, &self.body, request::Kind::Body); @@ -131,6 +133,7 @@ impl Encodable for CostTable { append_cost(s, &self.code, request::Kind::Code); append_cost(s, &self.header_proof, request::Kind::HeaderProof); append_cost(s, &self.transaction_proof, request::Kind::Execution); + append_cost(s, &self.epoch_signal, request::Kind::Signal); } } @@ -147,6 +150,7 @@ impl Decodable for CostTable { let mut code = None; let mut header_proof = None; let mut transaction_proof = None; + let mut epoch_signal = None; for cost_list in rlp.iter().skip(1) { let cost = cost_list.val_at(1)?; @@ -160,6 +164,7 @@ impl Decodable for CostTable { request::Kind::Code => code = Some(cost), request::Kind::HeaderProof => header_proof = Some(cost), request::Kind::Execution => transaction_proof = Some(cost), + request::Kind::Signal => epoch_signal = Some(cost), } } @@ -176,6 +181,7 @@ impl Decodable for CostTable { code: unwrap_cost(code)?, header_proof: unwrap_cost(header_proof)?, transaction_proof: unwrap_cost(transaction_proof)?, + epoch_signal: unwrap_cost(epoch_signal)?, }) } } @@ -238,6 +244,7 @@ impl FlowParams { code: cost_for_kind(Kind::Code), header_proof: cost_for_kind(Kind::HeaderProof), transaction_proof: cost_for_kind(Kind::Execution), + epoch_signal: cost_for_kind(Kind::Signal), }; FlowParams { @@ -263,7 +270,8 @@ impl FlowParams { storage: free_cost.clone(), code: free_cost.clone(), header_proof: free_cost.clone(), - transaction_proof: free_cost, + transaction_proof: free_cost.clone(), + epoch_signal: free_cost, } } } @@ -293,6 +301,7 @@ impl FlowParams { Request::Storage(_) => self.costs.storage, Request::Code(_) => self.costs.code, Request::Execution(ref req) => self.costs.transaction_proof * req.gas, + Request::Signal(_) => self.costs.epoch_signal, } } diff --git a/ethcore/light/src/net/request_set.rs b/ethcore/light/src/net/request_set.rs index 35182f0bf..43051df4a 100644 --- a/ethcore/light/src/net/request_set.rs +++ b/ethcore/light/src/net/request_set.rs @@ -139,6 +139,7 @@ fn compute_timeout(reqs: &Requests) -> Duration { Request::Storage(_) => timeout::PROOF, Request::Code(_) => timeout::CONTRACT_CODE, Request::Execution(_) => timeout::TRANSACTION_PROOF, + Request::Signal(_) => timeout::EPOCH_SIGNAL, } })) } diff --git a/ethcore/light/src/net/tests/mod.rs b/ethcore/light/src/net/tests/mod.rs index ef86c08f5..0de79ed00 100644 --- a/ethcore/light/src/net/tests/mod.rs +++ b/ethcore/light/src/net/tests/mod.rs @@ -156,6 +156,12 @@ impl Provider for TestProvider { None } + fn epoch_signal(&self, _req: request::CompleteSignalRequest) -> Option { + Some(request::SignalResponse { + signal: vec![1, 2, 3, 4], + }) + } + fn ready_transactions(&self) -> Vec { self.0.client.ready_transactions() } @@ -521,6 +527,50 @@ fn get_contract_code() { proto.handle_packet(&expected, &1, packet::REQUEST, &request_body); } +#[test] +fn epoch_signal() { + let capabilities = capabilities(); + + let (provider, proto) = setup(capabilities.clone()); + let flow_params = proto.flow_params.read().clone(); + + let cur_status = status(provider.client.chain_info()); + + { + let packet_body = write_handshake(&cur_status, &capabilities, &proto); + proto.on_connect(&1, &Expect::Send(1, packet::STATUS, packet_body.clone())); + proto.handle_packet(&Expect::Nothing, &1, packet::STATUS, &packet_body); + } + + let req_id = 112; + let request = Request::Signal(request::IncompleteSignalRequest { + block_hash: H256([1; 32]).into(), + }); + + let requests = encode_single(request.clone()); + let request_body = make_packet(req_id, &requests); + + let response = { + let response = vec![Response::Signal(SignalResponse { + signal: vec![1, 2, 3, 4], + })]; + + let limit = *flow_params.limit(); + let cost = flow_params.compute_cost_multi(requests.requests()); + + println!("limit = {}, cost = {}", limit, cost); + let new_creds = limit - cost; + + let mut response_stream = RlpStream::new_list(3); + response_stream.append(&req_id).append(&new_creds).append_list(&response); + + response_stream.out() + }; + + let expected = Expect::Respond(packet::RESPONSE, response); + proto.handle_packet(&expected, &1, packet::REQUEST, &request_body); +} + #[test] fn proof_of_execution() { let capabilities = capabilities(); diff --git a/ethcore/light/src/on_demand/mod.rs b/ethcore/light/src/on_demand/mod.rs index 42469725b..6d575a026 100644 --- a/ethcore/light/src/on_demand/mod.rs +++ b/ethcore/light/src/on_demand/mod.rs @@ -195,6 +195,8 @@ fn guess_capabilities(requests: &[CheckedRequest]) -> Capabilities { caps.serve_headers = true, CheckedRequest::HeaderByHash(_, _) => caps.serve_headers = true, + CheckedRequest::Signal(_, _) => + caps.serve_headers = true, CheckedRequest::Body(ref req, _) => if let Ok(ref hdr) = req.0.as_ref() { update_since(&mut caps.serve_chain_since, hdr.number()); }, diff --git a/ethcore/light/src/on_demand/request.rs b/ethcore/light/src/on_demand/request.rs index 9587dd8ed..09dd6cc89 100644 --- a/ethcore/light/src/on_demand/request.rs +++ b/ethcore/light/src/on_demand/request.rs @@ -20,7 +20,7 @@ use std::sync::Arc; use ethcore::basic_account::BasicAccount; use ethcore::encoded; -use ethcore::engines::Engine; +use ethcore::engines::{Engine, StateDependentProof}; use ethcore::receipt::Receipt; use ethcore::state::{self, ProvedExecution}; use ethcore::transaction::SignedTransaction; @@ -53,6 +53,8 @@ pub enum Request { Code(Code), /// A request for proof of execution. Execution(TransactionProof), + /// A request for epoch change signal. + Signal(Signal), } /// A request argument. @@ -133,6 +135,7 @@ 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 { () => { @@ -241,6 +244,7 @@ pub enum CheckedRequest { Account(Account, net_request::IncompleteAccountRequest), Code(Code, net_request::IncompleteCodeRequest), Execution(TransactionProof, net_request::IncompleteExecutionRequest), + Signal(Signal, net_request::IncompleteSignalRequest) } impl From for CheckedRequest { @@ -299,6 +303,12 @@ impl From for CheckedRequest { }; CheckedRequest::Execution(req, net_req) } + Request::Signal(req) => { + let net_req = net_request::IncompleteSignalRequest { + block_hash: req.hash.into(), + }; + CheckedRequest::Signal(req, net_req) + } } } } @@ -316,6 +326,7 @@ impl CheckedRequest { CheckedRequest::Account(_, req) => NetRequest::Account(req), CheckedRequest::Code(_, req) => NetRequest::Code(req), CheckedRequest::Execution(_, req) => NetRequest::Execution(req), + CheckedRequest::Signal(_, req) => NetRequest::Signal(req), } } @@ -443,6 +454,7 @@ macro_rules! match_me { CheckedRequest::Account($check, $req) => $e, CheckedRequest::Code($check, $req) => $e, CheckedRequest::Execution($check, $req) => $e, + CheckedRequest::Signal($check, $req) => $e, } } } @@ -470,6 +482,7 @@ impl IncompleteRequest for CheckedRequest { 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), } } @@ -490,6 +503,7 @@ impl IncompleteRequest for CheckedRequest { 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), } } @@ -541,6 +555,9 @@ impl net_request::CheckedRequest for CheckedRequest { 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)), } } } @@ -564,6 +581,8 @@ pub enum Response { 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 { @@ -847,6 +866,27 @@ impl TransactionProof { } } +/// 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, signal) + .map(|_| signal.to_owned()) + .map_err(|_| Error::BadProof) + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/ethcore/light/src/provider.rs b/ethcore/light/src/provider.rs index 3632783ca..4050a73df 100644 --- a/ethcore/light/src/provider.rs +++ b/ethcore/light/src/provider.rs @@ -126,6 +126,9 @@ pub trait Provider: Send + Sync { /// Provide a proof-of-execution for the given transaction proof request. /// Returns a vector of all state items necessary to execute the transaction. fn transaction_proof(&self, req: request::CompleteExecutionRequest) -> Option; + + /// Provide epoch signal data at given block hash. This should be just the + fn epoch_signal(&self, req: request::CompleteSignalRequest) -> Option; } // Implementation of a light client data provider for a client. @@ -264,6 +267,12 @@ impl Provider for T { fn ready_transactions(&self) -> Vec { BlockChainClient::ready_transactions(self) } + + fn epoch_signal(&self, req: request::CompleteSignalRequest) -> Option { + self.epoch_signal(req.block_hash).map(|signal| request::SignalResponse { + signal: signal, + }) + } } /// The light client "provider" implementation. This wraps a `LightClient` and @@ -329,6 +338,10 @@ impl Provider for LightProvider { None } + fn epoch_signal(&self, _req: request::CompleteSignalRequest) -> Option { + None + } + fn ready_transactions(&self) -> Vec { let chain_info = self.chain_info(); self.txqueue.read().ready_transactions(chain_info.best_block_number, chain_info.best_block_timestamp) diff --git a/ethcore/light/src/types/request/mod.rs b/ethcore/light/src/types/request/mod.rs index ff4a32535..c9863a354 100644 --- a/ethcore/light/src/types/request/mod.rs +++ b/ethcore/light/src/types/request/mod.rs @@ -67,6 +67,11 @@ pub use self::execution::{ Incomplete as IncompleteExecutionRequest, Response as ExecutionResponse, }; +pub use self::epoch_signal::{ + Complete as CompleteSignalRequest, + Incomplete as IncompleteSignalRequest, + Response as SignalResponse, +}; pub use self::builder::{RequestBuilder, Requests}; @@ -261,6 +266,8 @@ pub enum Request { 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. @@ -284,6 +291,8 @@ pub enum CompleteRequest { Code(CompleteCodeRequest), /// A request for proof of execution, Execution(CompleteExecutionRequest), + /// A request for an epoch signal. + Signal(CompleteSignalRequest), } impl CompleteRequest { @@ -299,6 +308,7 @@ impl CompleteRequest { CompleteRequest::Storage(_) => Kind::Storage, CompleteRequest::Code(_) => Kind::Code, CompleteRequest::Execution(_) => Kind::Execution, + CompleteRequest::Signal(_) => Kind::Signal, } } } @@ -316,6 +326,7 @@ impl Request { Request::Storage(_) => Kind::Storage, Request::Code(_) => Kind::Code, Request::Execution(_) => Kind::Execution, + Request::Signal(_) => Kind::Signal, } } } @@ -332,6 +343,7 @@ impl Decodable for Request { 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)?)), } } } @@ -353,6 +365,7 @@ impl Encodable for Request { 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), }; } } @@ -374,6 +387,7 @@ impl IncompleteRequest for Request { 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), } } @@ -388,6 +402,7 @@ impl IncompleteRequest for Request { 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), } } @@ -402,6 +417,7 @@ impl IncompleteRequest for Request { 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), } } @@ -416,6 +432,7 @@ impl IncompleteRequest for Request { 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), } } @@ -430,6 +447,7 @@ impl IncompleteRequest for Request { 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), } } } @@ -471,6 +489,8 @@ pub enum Kind { Code = 7, /// A request for transaction execution + state proof. Execution = 8, + /// A request for epoch transition signal. + Signal = 9, } impl Decodable for Kind { @@ -485,6 +505,7 @@ impl Decodable for Kind { 6 => Ok(Kind::Storage), 7 => Ok(Kind::Code), 8 => Ok(Kind::Execution), + 9 => Ok(Kind::Signal), _ => Err(DecoderError::Custom("Unknown PIP request ID.")), } } @@ -517,6 +538,8 @@ pub enum Response { Code(CodeResponse), /// A response for proof of execution, Execution(ExecutionResponse), + /// A response for epoch change signal. + Signal(SignalResponse), } impl ResponseLike for Response { @@ -532,6 +555,7 @@ impl ResponseLike for Response { 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), } } } @@ -549,6 +573,7 @@ impl Response { Response::Storage(_) => Kind::Storage, Response::Code(_) => Kind::Code, Response::Execution(_) => Kind::Execution, + Response::Signal(_) => Kind::Signal, } } } @@ -565,6 +590,7 @@ impl Decodable for Response { 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)?)), } } } @@ -586,6 +612,7 @@ impl Encodable for Response { 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), }; } } @@ -1756,6 +1783,104 @@ pub mod execution { } } +/// A request for epoch signal data. +pub mod epoch_signal { + use super::{Field, NoSuchOutput, OutputKind, Output}; + use rlp::{Encodable, Decodable, DecoderError, RlpStream, UntrustedRlp}; + use util::{Bytes, H256}; + + /// 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, + } + + impl Decodable for Incomplete { + fn decode(rlp: &UntrustedRlp) -> Result { + 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(&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(&self, _: F) where F: FnMut(usize, OutputKind) {} + + fn fill(&mut self, oracle: F) where F: Fn(usize, usize) -> Result { + 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 { + Ok(Complete { + block_hash: self.block_hash.into_scalar()?, + }) + } + + fn adjust_refs(&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(&self, _: F) where F: FnMut(usize, Output) {} + } + + impl Decodable for Response { + fn decode(rlp: &UntrustedRlp) -> Result { + + 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::*; @@ -2044,4 +2169,22 @@ mod tests { let raw = ::rlp::encode_list(&reqs); assert_eq!(::rlp::decode_list::(&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); + } } diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index e3c7be66f..dafef82f6 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -1918,6 +1918,12 @@ impl ProvingBlockChainClient for Client { false, ) } + + fn epoch_signal(&self, hash: H256) -> Option> { + // pending transitions are never deleted, and do not contain + // finality proofs by definition. + self.chain.read().get_pending_transition(hash).map(|pending| pending.proof) + } } impl Drop for Client { diff --git a/ethcore/src/client/test_client.rs b/ethcore/src/client/test_client.rs index 52e89fd0c..1d30c5800 100644 --- a/ethcore/src/client/test_client.rs +++ b/ethcore/src/client/test_client.rs @@ -778,6 +778,10 @@ impl ProvingBlockChainClient for TestBlockChainClient { fn prove_transaction(&self, _: SignedTransaction, _: BlockId) -> Option<(Bytes, Vec)> { None } + + fn epoch_signal(&self, _: H256) -> Option> { + None + } } impl super::traits::EngineClient for TestBlockChainClient { diff --git a/ethcore/src/client/traits.rs b/ethcore/src/client/traits.rs index 294648e8e..47cb51e3f 100644 --- a/ethcore/src/client/traits.rs +++ b/ethcore/src/client/traits.rs @@ -353,4 +353,7 @@ pub trait ProvingBlockChainClient: BlockChainClient { /// Returns the output of the call and a vector of database items necessary /// to reproduce it. fn prove_transaction(&self, transaction: SignedTransaction, id: BlockId) -> Option<(Bytes, Vec)>; + + /// Get an epoch change signal by block hash. + fn epoch_signal(&self, hash: H256) -> Option>; } diff --git a/ethcore/src/engines/mod.rs b/ethcore/src/engines/mod.rs index 0324c2651..fc7a7186e 100644 --- a/ethcore/src/engines/mod.rs +++ b/ethcore/src/engines/mod.rs @@ -121,7 +121,7 @@ pub type Headers<'a> = Fn(H256) -> Option
+ 'a; pub type PendingTransitionStore<'a> = Fn(H256) -> Option + 'a; /// Proof dependent on state. -pub trait StateDependentProof { +pub trait StateDependentProof: Send + Sync { /// Generate a proof, given the state. fn generate_proof(&self, caller: &Call) -> Result, String>; /// Check a proof generated elsewhere (potentially by a peer). @@ -135,7 +135,7 @@ pub enum Proof { /// Known proof (extracted from signal) Known(Vec), /// State dependent proof. - WithState(Box), + WithState(Arc), } /// Generated epoch verifier. diff --git a/ethcore/src/engines/validator_set/safe_contract.rs b/ethcore/src/engines/validator_set/safe_contract.rs index 065c17ef9..9c08ab963 100644 --- a/ethcore/src/engines/validator_set/safe_contract.rs +++ b/ethcore/src/engines/validator_set/safe_contract.rs @@ -47,19 +47,19 @@ lazy_static! { // state-dependent proofs for the safe contract: // only "first" proofs are such. struct StateProof { - header: Header, + header: Mutex
, provider: Provider, } impl ::engines::StateDependentProof for StateProof { fn generate_proof(&self, caller: &Call) -> Result, String> { - prove_initial(&self.provider, &self.header, caller) + prove_initial(&self.provider, &*self.header.lock(), caller) } fn check_proof(&self, engine: &Engine, proof: &[u8]) -> Result<(), String> { let (header, state_items) = decode_first_proof(&UntrustedRlp::new(proof)) .map_err(|e| format!("proof incorrectly encoded: {}", e))?; - if header != self.header { + if &header != &*self.header.lock(){ return Err("wrong header in proof".into()); } @@ -330,11 +330,11 @@ impl ValidatorSet for ValidatorSafeContract { // transition to the first block of a contract requires finality but has no log event. if first { debug!(target: "engine", "signalling transition to fresh contract."); - let state_proof = Box::new(StateProof { - header: header.clone(), + let state_proof = Arc::new(StateProof { + header: Mutex::new(header.clone()), provider: self.provider.clone(), }); - return ::engines::EpochChange::Yes(::engines::Proof::WithState(state_proof as Box<_>)); + return ::engines::EpochChange::Yes(::engines::Proof::WithState(state_proof as Arc<_>)); } // otherwise, we're checking for logs.