serve epoch signals over network and check them

This commit is contained in:
Robert Habermeier 2017-08-23 15:37:35 +02:00
parent 7f3e718851
commit 2ff3dff6ea
16 changed files with 298 additions and 30 deletions

View File

@ -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<Engine>, checker: Arc<StateDependentProof>) -> 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<Engine>, _check: Arc<StateDependentProof>) -> Self::Transition {
Err("fetching epoch transition proofs unavailable")
}
}

View File

@ -508,14 +508,11 @@ impl<T: ChainDataFetcher> Client<T> {
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()?
}
};

View File

@ -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,
}
}

View File

@ -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),
}
});

View File

@ -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,
}
}

View File

@ -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,
}
}))
}

View File

@ -156,6 +156,12 @@ impl Provider for TestProvider {
None
}
fn epoch_signal(&self, _req: request::CompleteSignalRequest) -> Option<request::SignalResponse> {
Some(request::SignalResponse {
signal: vec![1, 2, 3, 4],
})
}
fn ready_transactions(&self) -> Vec<PendingTransaction> {
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();

View File

@ -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());
},

View File

@ -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<BasicAccount>);
impl_single!(Code, Code, Bytes);
impl_single!(Execution, TransactionProof, super::ExecutionResult);
impl_single!(Signal, Signal, Vec<u8>);
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<Request> for CheckedRequest {
@ -299,6 +303,12 @@ impl From<Request> 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<u8>),
/// Response to a request for proved execution.
Execution(super::ExecutionResult),
/// Response to a request for epoch change signal.
Signal(Vec<u8>),
}
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<Engine>,
/// Special checker for the proof.
pub proof_check: Arc<StateDependentProof>,
}
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<Vec<u8>, Error> {
self.proof_check.check_proof(&*self.engine, signal)
.map(|_| signal.to_owned())
.map_err(|_| Error::BadProof)
}
}
#[cfg(test)]
mod tests {
use super::*;

View File

@ -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<request::ExecutionResponse>;
/// Provide epoch signal data at given block hash. This should be just the
fn epoch_signal(&self, req: request::CompleteSignalRequest) -> Option<request::SignalResponse>;
}
// Implementation of a light client data provider for a client.
@ -264,6 +267,12 @@ impl<T: ProvingBlockChainClient + ?Sized> Provider for T {
fn ready_transactions(&self) -> Vec<PendingTransaction> {
BlockChainClient::ready_transactions(self)
}
fn epoch_signal(&self, req: request::CompleteSignalRequest) -> Option<request::SignalResponse> {
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<L: AsLightClient + Send + Sync> Provider for LightProvider<L> {
None
}
fn epoch_signal(&self, _req: request::CompleteSignalRequest) -> Option<request::SignalResponse> {
None
}
fn ready_transactions(&self) -> Vec<PendingTransaction> {
let chain_info = self.chain_info();
self.txqueue.read().ready_transactions(chain_info.best_block_number, chain_info.best_block_timestamp)

View File

@ -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<H256>,
}
impl Decodable for Incomplete {
fn decode(rlp: &UntrustedRlp) -> Result<Self, DecoderError> {
Ok(Incomplete {
block_hash: rlp.val_at(0)?,
})
}
}
impl Encodable for Incomplete {
fn rlp_append(&self, s: &mut RlpStream) {
s.begin_list(1).append(&self.block_hash);
}
}
impl super::IncompleteRequest for Incomplete {
type Complete = Complete;
type Response = Response;
fn check_outputs<F>(&self, mut f: F) -> Result<(), NoSuchOutput>
where F: FnMut(usize, usize, OutputKind) -> Result<(), NoSuchOutput>
{
if let Field::BackReference(req, idx) = self.block_hash {
f(req, idx, OutputKind::Hash)?;
}
Ok(())
}
fn note_outputs<F>(&self, _: F) where F: FnMut(usize, OutputKind) {}
fn fill<F>(&mut self, oracle: F) where F: Fn(usize, usize) -> Result<Output, NoSuchOutput> {
if let Field::BackReference(req, idx) = self.block_hash {
self.block_hash = match oracle(req, idx) {
Ok(Output::Hash(block_hash)) => Field::Scalar(block_hash.into()),
_ => Field::BackReference(req, idx),
}
}
}
fn complete(self) -> Result<Self::Complete, NoSuchOutput> {
Ok(Complete {
block_hash: self.block_hash.into_scalar()?,
})
}
fn adjust_refs<F>(&mut self, mut mapping: F) where F: FnMut(usize) -> usize {
self.block_hash.adjust_req(&mut mapping);
}
}
/// A complete request.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Complete {
/// The block hash to request the epoch signal for.
pub block_hash: H256,
}
/// The output of a request for an epoch signal.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Response {
/// The requested epoch signal.
pub signal: Bytes,
}
impl super::ResponseLike for Response {
/// Fill reusable outputs by providing them to the function.
fn fill_outputs<F>(&self, _: F) where F: FnMut(usize, Output) {}
}
impl Decodable for Response {
fn decode(rlp: &UntrustedRlp) -> Result<Self, DecoderError> {
Ok(Response {
signal: rlp.as_val()?,
})
}
}
impl Encodable for Response {
fn rlp_append(&self, s: &mut RlpStream) {
s.append(&self.signal);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
@ -2044,4 +2169,22 @@ mod tests {
let raw = ::rlp::encode_list(&reqs);
assert_eq!(::rlp::decode_list::<Response>(&raw), reqs);
}
#[test]
fn epoch_signal_roundtrip() {
let req = IncompleteSignalRequest {
block_hash: Field::Scalar(Default::default()),
};
let full_req = Request::Signal(req.clone());
let res = SignalResponse {
signal: vec![1, 2, 3, 4, 5, 6, 7, 6, 5, 4],
};
let full_res = Response::Signal(res.clone());
check_roundtrip(req);
check_roundtrip(full_req);
check_roundtrip(res);
check_roundtrip(full_res);
}
}

View File

@ -1918,6 +1918,12 @@ impl ProvingBlockChainClient for Client {
false,
)
}
fn epoch_signal(&self, hash: H256) -> Option<Vec<u8>> {
// 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 {

View File

@ -778,6 +778,10 @@ impl ProvingBlockChainClient for TestBlockChainClient {
fn prove_transaction(&self, _: SignedTransaction, _: BlockId) -> Option<(Bytes, Vec<DBValue>)> {
None
}
fn epoch_signal(&self, _: H256) -> Option<Vec<u8>> {
None
}
}
impl super::traits::EngineClient for TestBlockChainClient {

View File

@ -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<DBValue>)>;
/// Get an epoch change signal by block hash.
fn epoch_signal(&self, hash: H256) -> Option<Vec<u8>>;
}

View File

@ -121,7 +121,7 @@ pub type Headers<'a> = Fn(H256) -> Option<Header> + 'a;
pub type PendingTransitionStore<'a> = Fn(H256) -> Option<PendingTransition> + '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<Vec<u8>, String>;
/// Check a proof generated elsewhere (potentially by a peer).
@ -135,7 +135,7 @@ pub enum Proof {
/// Known proof (extracted from signal)
Known(Vec<u8>),
/// State dependent proof.
WithState(Box<StateDependentProof>),
WithState(Arc<StateDependentProof>),
}
/// Generated epoch verifier.

View File

@ -47,19 +47,19 @@ lazy_static! {
// state-dependent proofs for the safe contract:
// only "first" proofs are such.
struct StateProof {
header: Header,
header: Mutex<Header>,
provider: Provider,
}
impl ::engines::StateDependentProof for StateProof {
fn generate_proof(&self, caller: &Call) -> Result<Vec<u8>, 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.