checking for signals in the light client
This commit is contained in:
parent
2bd5c3dba7
commit
0abf2abc81
69
ethcore/light/src/client/fetch.rs
Normal file
69
ethcore/light/src/client/fetch.rs
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//! Trait for fetching chain data.
|
||||||
|
|
||||||
|
use ethcore::header::Header;
|
||||||
|
use ethcore::receipt::Receipt;
|
||||||
|
use futures::future::IntoFuture;
|
||||||
|
|
||||||
|
/// Provides full chain data.
|
||||||
|
pub trait ChainDataFetcher: Send + Sync + 'static {
|
||||||
|
/// Error type when data unavailable.
|
||||||
|
type Error: ::std::fmt::Debug;
|
||||||
|
|
||||||
|
/// Future for fetching block body.
|
||||||
|
type Body: IntoFuture<Item=Vec<u8>,Error=Self::Error>;
|
||||||
|
/// Future for fetching block receipts.
|
||||||
|
type Receipts: IntoFuture<Item=Vec<Receipt>,Error=Self::Error>;
|
||||||
|
/// Future for fetching epoch transition
|
||||||
|
type Transition: IntoFuture<Item=Vec<u8>,Error=Self::Error>;
|
||||||
|
|
||||||
|
/// Fetch a block body.
|
||||||
|
fn block_body(&self, header: &Header) -> Self::Body;
|
||||||
|
|
||||||
|
/// Fetch block receipts.
|
||||||
|
fn block_receipts(&self, header: &Header) -> Self::Receipts;
|
||||||
|
|
||||||
|
/// Fetch epoch transition proof at given header.
|
||||||
|
fn epoch_transition(&self, header: &Header) -> Self::Transition;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fetcher implementation which cannot fetch anything.
|
||||||
|
pub struct Unavailable;
|
||||||
|
|
||||||
|
/// Create a fetcher which has all data unavailable.
|
||||||
|
pub fn unavailable() -> Unavailable { Unavailable }
|
||||||
|
|
||||||
|
impl ChainDataFetcher for Unavailable {
|
||||||
|
type Error = &'static str;
|
||||||
|
|
||||||
|
type Body = Result<Vec<u8>, &'static str>;
|
||||||
|
type Receipts = Result<Vec<Receipt>, &'static str>;
|
||||||
|
type Transition = Result<Vec<u8>, &'static str>;
|
||||||
|
|
||||||
|
fn block_body(&self, _header: &Header) -> Self::Body {
|
||||||
|
Err("fetching block bodies unavailable")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn block_receipts(&self, _header: &Header) -> Self::Receipts {
|
||||||
|
Err("fetching block receipts unavailable")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn epoch_transition(&self, _header: &Header) -> Self::Body {
|
||||||
|
Err("fetching epoch transition proofs unavailable")
|
||||||
|
}
|
||||||
|
}
|
@ -20,7 +20,7 @@ use std::sync::{Weak, Arc};
|
|||||||
|
|
||||||
use ethcore::block_status::BlockStatus;
|
use ethcore::block_status::BlockStatus;
|
||||||
use ethcore::client::{ClientReport, EnvInfo};
|
use ethcore::client::{ClientReport, EnvInfo};
|
||||||
use ethcore::engines::Engine;
|
use ethcore::engines::{Engine, EpochChange, Proof, Unsure};
|
||||||
use ethcore::error::BlockImportError;
|
use ethcore::error::BlockImportError;
|
||||||
use ethcore::ids::BlockId;
|
use ethcore::ids::BlockId;
|
||||||
use ethcore::header::Header;
|
use ethcore::header::Header;
|
||||||
@ -31,9 +31,12 @@ use ethcore::service::ClientIoMessage;
|
|||||||
use ethcore::encoded;
|
use ethcore::encoded;
|
||||||
use io::IoChannel;
|
use io::IoChannel;
|
||||||
|
|
||||||
|
use futures::{IntoFuture, Future};
|
||||||
|
|
||||||
use util::{H256, U256, Mutex, RwLock};
|
use util::{H256, U256, Mutex, RwLock};
|
||||||
use util::kvdb::{KeyValueDB, CompactionProfile};
|
use util::kvdb::{KeyValueDB, CompactionProfile};
|
||||||
|
|
||||||
|
use self::fetch::ChainDataFetcher;
|
||||||
use self::header_chain::{AncestryIter, HeaderChain};
|
use self::header_chain::{AncestryIter, HeaderChain};
|
||||||
|
|
||||||
use cache::Cache;
|
use cache::Cache;
|
||||||
@ -43,6 +46,8 @@ pub use self::service::Service;
|
|||||||
mod header_chain;
|
mod header_chain;
|
||||||
mod service;
|
mod service;
|
||||||
|
|
||||||
|
pub mod fetch;
|
||||||
|
|
||||||
/// Configuration for the light client.
|
/// Configuration for the light client.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
@ -154,7 +159,7 @@ impl<T: LightChainClient> AsLightClient for T {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Light client implementation.
|
/// Light client implementation.
|
||||||
pub struct Client {
|
pub struct Client<T> {
|
||||||
queue: HeaderQueue,
|
queue: HeaderQueue,
|
||||||
engine: Arc<Engine>,
|
engine: Arc<Engine>,
|
||||||
chain: HeaderChain,
|
chain: HeaderChain,
|
||||||
@ -162,12 +167,21 @@ pub struct Client {
|
|||||||
import_lock: Mutex<()>,
|
import_lock: Mutex<()>,
|
||||||
db: Arc<KeyValueDB>,
|
db: Arc<KeyValueDB>,
|
||||||
listeners: RwLock<Vec<Weak<LightChainNotify>>>,
|
listeners: RwLock<Vec<Weak<LightChainNotify>>>,
|
||||||
|
fetcher: T,
|
||||||
verify_full: bool,
|
verify_full: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Client {
|
impl<T: ChainDataFetcher> Client<T> {
|
||||||
/// Create a new `Client`.
|
/// Create a new `Client`.
|
||||||
pub fn new(config: Config, db: Arc<KeyValueDB>, chain_col: Option<u32>, spec: &Spec, io_channel: IoChannel<ClientIoMessage>, cache: Arc<Mutex<Cache>>) -> Result<Self, String> {
|
pub fn new(
|
||||||
|
config: Config,
|
||||||
|
db: Arc<KeyValueDB>,
|
||||||
|
chain_col: Option<u32>,
|
||||||
|
spec: &Spec,
|
||||||
|
fetcher: T,
|
||||||
|
io_channel: IoChannel<ClientIoMessage>,
|
||||||
|
cache: Arc<Mutex<Cache>>
|
||||||
|
) -> Result<Self, String> {
|
||||||
let gh = ::rlp::encode(&spec.genesis_header());
|
let gh = ::rlp::encode(&spec.genesis_header());
|
||||||
|
|
||||||
Ok(Client {
|
Ok(Client {
|
||||||
@ -178,6 +192,7 @@ impl Client {
|
|||||||
import_lock: Mutex::new(()),
|
import_lock: Mutex::new(()),
|
||||||
db: db,
|
db: db,
|
||||||
listeners: RwLock::new(vec![]),
|
listeners: RwLock::new(vec![]),
|
||||||
|
fetcher: fetcher,
|
||||||
verify_full: config.verify_full,
|
verify_full: config.verify_full,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -189,10 +204,24 @@ impl Client {
|
|||||||
|
|
||||||
/// Create a new `Client` backed purely in-memory.
|
/// Create a new `Client` backed purely in-memory.
|
||||||
/// This will ignore all database options in the configuration.
|
/// This will ignore all database options in the configuration.
|
||||||
pub fn in_memory(config: Config, spec: &Spec, io_channel: IoChannel<ClientIoMessage>, cache: Arc<Mutex<Cache>>) -> Self {
|
pub fn in_memory(
|
||||||
|
config: Config,
|
||||||
|
spec: &Spec,
|
||||||
|
fetcher: T,
|
||||||
|
io_channel: IoChannel<ClientIoMessage>,
|
||||||
|
cache: Arc<Mutex<Cache>>
|
||||||
|
) -> Self {
|
||||||
let db = ::util::kvdb::in_memory(0);
|
let db = ::util::kvdb::in_memory(0);
|
||||||
|
|
||||||
Client::new(config, Arc::new(db), None, spec, io_channel, cache).expect("New DB creation infallible; qed")
|
Client::new(
|
||||||
|
config,
|
||||||
|
Arc::new(db),
|
||||||
|
None,
|
||||||
|
spec,
|
||||||
|
fetcher,
|
||||||
|
io_channel,
|
||||||
|
cache
|
||||||
|
).expect("New DB creation infallible; qed")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Import a header to the queue for additional verification.
|
/// Import a header to the queue for additional verification.
|
||||||
@ -291,9 +320,14 @@ impl Client {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: `epoch_end_signal`, `is_epoch_end`.
|
let _write_proof_result = match self.check_epoch_signal(&verified_header) {
|
||||||
// proofs we get from the network would be _complete_, whereas we need
|
Ok(Some(proof)) => self.write_pending_proof(&verified_header, proof),
|
||||||
// _incomplete_ signals
|
Ok(None) => Ok(()),
|
||||||
|
Err(e) =>
|
||||||
|
panic!("Unable to fetch epoch transition proof: {:?}", e),
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: check epoch end.
|
||||||
|
|
||||||
let mut tx = self.db.transaction();
|
let mut tx = self.db.transaction();
|
||||||
let pending = match self.chain.insert(&mut tx, verified_header) {
|
let pending = match self.chain.insert(&mut tx, verified_header) {
|
||||||
@ -419,9 +453,71 @@ impl Client {
|
|||||||
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn check_epoch_signal(&self, verified_header: &Header) -> Result<Option<Proof>, T::Error> {
|
||||||
|
let (mut block, mut receipts) = (None, None);
|
||||||
|
|
||||||
|
// First, check without providing auxiliary data.
|
||||||
|
match self.engine.signals_epoch_end(verified_header, None, None) {
|
||||||
|
EpochChange::No => return Ok(None),
|
||||||
|
EpochChange::Yes(proof) => return Ok(Some(proof)),
|
||||||
|
EpochChange::Unsure(unsure) => {
|
||||||
|
let (b, r) = match unsure {
|
||||||
|
Unsure::NeedsBody =>
|
||||||
|
(Some(self.fetcher.block_body(verified_header)), None),
|
||||||
|
Unsure::NeedsReceipts =>
|
||||||
|
(None, Some(self.fetcher.block_receipts(verified_header))),
|
||||||
|
Unsure::NeedsBoth => (
|
||||||
|
Some(self.fetcher.block_body(verified_header)),
|
||||||
|
Some(self.fetcher.block_receipts(verified_header)),
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(b) = b {
|
||||||
|
block = Some(b.into_future().wait()?);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(r) = r {
|
||||||
|
receipts = Some(r.into_future().wait()?);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let block = block.as_ref().map(|x| &x[..]);
|
||||||
|
let receipts = receipts.as_ref().map(|x| &x[..]);
|
||||||
|
|
||||||
|
// Check again now that required data has been fetched.
|
||||||
|
match self.engine.signals_epoch_end(verified_header, block, receipts) {
|
||||||
|
EpochChange::No => return Ok(None),
|
||||||
|
EpochChange::Yes(proof) => return Ok(Some(proof)),
|
||||||
|
EpochChange::Unsure(_) =>
|
||||||
|
panic!("Detected faulty engine implementation: requests additional \
|
||||||
|
data to check epoch end signal when everything necessary provided"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// attempts to fetch the epoch proof from the network until successful.
|
||||||
|
fn write_pending_proof(&self, header: &Header, proof: Proof) -> Result<(), T::Error> {
|
||||||
|
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),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: actually write it
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LightChainClient for Client {
|
impl<T: ChainDataFetcher> LightChainClient for Client<T> {
|
||||||
fn chain_info(&self) -> BlockChainInfo { Client::chain_info(self) }
|
fn chain_info(&self) -> BlockChainInfo { Client::chain_info(self) }
|
||||||
|
|
||||||
fn queue_header(&self, header: Header) -> Result<H256, BlockImportError> {
|
fn queue_header(&self, header: Header) -> Result<H256, BlockImportError> {
|
||||||
|
@ -30,7 +30,7 @@ use util::kvdb::{Database, DatabaseConfig};
|
|||||||
use cache::Cache;
|
use cache::Cache;
|
||||||
use util::Mutex;
|
use util::Mutex;
|
||||||
|
|
||||||
use super::{Client, Config as ClientConfig};
|
use super::{ChainDataFetcher, Client, Config as ClientConfig};
|
||||||
|
|
||||||
/// Errors on service initialization.
|
/// Errors on service initialization.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -51,14 +51,14 @@ impl fmt::Display for Error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Light client service.
|
/// Light client service.
|
||||||
pub struct Service {
|
pub struct Service<T> {
|
||||||
client: Arc<Client>,
|
client: Arc<Client<T>>,
|
||||||
io_service: IoService<ClientIoMessage>,
|
io_service: IoService<ClientIoMessage>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Service {
|
impl<T: ChainDataFetcher> Service<T> {
|
||||||
/// Start the service: initialize I/O workers and client itself.
|
/// Start the service: initialize I/O workers and client itself.
|
||||||
pub fn start(config: ClientConfig, spec: &Spec, path: &Path, cache: Arc<Mutex<Cache>>) -> Result<Self, Error> {
|
pub fn start(config: ClientConfig, spec: &Spec, fetcher: T, path: &Path, cache: Arc<Mutex<Cache>>) -> Result<Self, Error> {
|
||||||
|
|
||||||
// initialize database.
|
// initialize database.
|
||||||
let mut db_config = DatabaseConfig::with_columns(db::NUM_COLUMNS);
|
let mut db_config = DatabaseConfig::with_columns(db::NUM_COLUMNS);
|
||||||
@ -81,6 +81,7 @@ impl Service {
|
|||||||
db,
|
db,
|
||||||
db::COL_LIGHT_CHAIN,
|
db::COL_LIGHT_CHAIN,
|
||||||
spec,
|
spec,
|
||||||
|
fetcher,
|
||||||
io_service.channel(),
|
io_service.channel(),
|
||||||
cache,
|
cache,
|
||||||
).map_err(Error::Database)?);
|
).map_err(Error::Database)?);
|
||||||
@ -97,14 +98,14 @@ impl Service {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Get a handle to the client.
|
/// Get a handle to the client.
|
||||||
pub fn client(&self) -> &Arc<Client> {
|
pub fn client(&self) -> &Arc<Client<T>> {
|
||||||
&self.client
|
&self.client
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ImportBlocks(Arc<Client>);
|
struct ImportBlocks<T>(Arc<Client<T>>);
|
||||||
|
|
||||||
impl IoHandler<ClientIoMessage> for ImportBlocks {
|
impl<T: ChainDataFetcher> IoHandler<ClientIoMessage> for ImportBlocks<T> {
|
||||||
fn message(&self, _io: &IoContext<ClientIoMessage>, message: &ClientIoMessage) {
|
fn message(&self, _io: &IoContext<ClientIoMessage>, message: &ClientIoMessage) {
|
||||||
if let ClientIoMessage::BlockVerified = *message {
|
if let ClientIoMessage::BlockVerified = *message {
|
||||||
self.0.import_verified();
|
self.0.import_verified();
|
||||||
@ -117,9 +118,10 @@ mod tests {
|
|||||||
use super::Service;
|
use super::Service;
|
||||||
use devtools::RandomTempPath;
|
use devtools::RandomTempPath;
|
||||||
use ethcore::spec::Spec;
|
use ethcore::spec::Spec;
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use cache::Cache;
|
use cache::Cache;
|
||||||
|
use client::fetch;
|
||||||
use time::Duration;
|
use time::Duration;
|
||||||
use util::Mutex;
|
use util::Mutex;
|
||||||
|
|
||||||
@ -129,6 +131,6 @@ mod tests {
|
|||||||
let temp_path = RandomTempPath::new();
|
let temp_path = RandomTempPath::new();
|
||||||
let cache = Arc::new(Mutex::new(Cache::new(Default::default(), Duration::hours(6))));
|
let cache = Arc::new(Mutex::new(Cache::new(Default::default(), Duration::hours(6))));
|
||||||
|
|
||||||
Service::start(Default::default(), &spec, temp_path.as_path(), cache).unwrap();
|
Service::start(Default::default(), &spec, fetch::unavailable(), temp_path.as_path(), cache).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -666,6 +666,8 @@ impl Engine for AuthorityRound {
|
|||||||
(&active_set as &_, epoch_manager.epoch_transition_number)
|
(&active_set as &_, epoch_manager.epoch_transition_number)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// always report with "self.validators" so that the report actually gets
|
||||||
|
// to the contract.
|
||||||
let report = |report| match report {
|
let report = |report| match report {
|
||||||
Report::Benign(address, block_number) =>
|
Report::Benign(address, block_number) =>
|
||||||
self.validators.report_benign(&address, set_number, block_number),
|
self.validators.report_benign(&address, set_number, block_number),
|
||||||
|
@ -132,7 +132,7 @@ pub trait StateDependentProof {
|
|||||||
|
|
||||||
/// Proof generated on epoch change.
|
/// Proof generated on epoch change.
|
||||||
pub enum Proof {
|
pub enum Proof {
|
||||||
/// Known proof (exctracted from signal)
|
/// Known proof (extracted from signal)
|
||||||
Known(Vec<u8>),
|
Known(Vec<u8>),
|
||||||
/// State dependent proof.
|
/// State dependent proof.
|
||||||
WithState(Box<StateDependentProof>),
|
WithState(Box<StateDependentProof>),
|
||||||
|
Loading…
Reference in New Issue
Block a user