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::client::{ClientReport, EnvInfo}; | ||||
| use ethcore::engines::Engine; | ||||
| use ethcore::engines::{Engine, EpochChange, Proof, Unsure}; | ||||
| use ethcore::error::BlockImportError; | ||||
| use ethcore::ids::BlockId; | ||||
| use ethcore::header::Header; | ||||
| @ -31,9 +31,12 @@ use ethcore::service::ClientIoMessage; | ||||
| use ethcore::encoded; | ||||
| use io::IoChannel; | ||||
| 
 | ||||
| use futures::{IntoFuture, Future}; | ||||
| 
 | ||||
| use util::{H256, U256, Mutex, RwLock}; | ||||
| use util::kvdb::{KeyValueDB, CompactionProfile}; | ||||
| 
 | ||||
| use self::fetch::ChainDataFetcher; | ||||
| use self::header_chain::{AncestryIter, HeaderChain}; | ||||
| 
 | ||||
| use cache::Cache; | ||||
| @ -43,6 +46,8 @@ pub use self::service::Service; | ||||
| mod header_chain; | ||||
| mod service; | ||||
| 
 | ||||
| pub mod fetch; | ||||
| 
 | ||||
| /// Configuration for the light client.
 | ||||
| #[derive(Debug, Clone)] | ||||
| pub struct Config { | ||||
| @ -154,7 +159,7 @@ impl<T: LightChainClient> AsLightClient for T { | ||||
| } | ||||
| 
 | ||||
| /// Light client implementation.
 | ||||
| pub struct Client { | ||||
| pub struct Client<T> { | ||||
| 	queue: HeaderQueue, | ||||
| 	engine: Arc<Engine>, | ||||
| 	chain: HeaderChain, | ||||
| @ -162,12 +167,21 @@ pub struct Client { | ||||
| 	import_lock: Mutex<()>, | ||||
| 	db: Arc<KeyValueDB>, | ||||
| 	listeners: RwLock<Vec<Weak<LightChainNotify>>>, | ||||
| 	fetcher: T, | ||||
| 	verify_full: bool, | ||||
| } | ||||
| 
 | ||||
| impl Client { | ||||
| impl<T: ChainDataFetcher> Client<T> { | ||||
| 	/// 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()); | ||||
| 
 | ||||
| 		Ok(Client { | ||||
| @ -178,6 +192,7 @@ impl Client { | ||||
| 			import_lock: Mutex::new(()), | ||||
| 			db: db, | ||||
| 			listeners: RwLock::new(vec![]), | ||||
| 			fetcher: fetcher, | ||||
| 			verify_full: config.verify_full, | ||||
| 		}) | ||||
| 	} | ||||
| @ -189,10 +204,24 @@ impl Client { | ||||
| 
 | ||||
| 	/// Create a new `Client` backed purely in-memory.
 | ||||
| 	/// 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); | ||||
| 
 | ||||
| 		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.
 | ||||
| @ -291,9 +320,14 @@ impl Client { | ||||
| 				continue
 | ||||
| 			} | ||||
| 
 | ||||
| 			// TODO: `epoch_end_signal`, `is_epoch_end`.
 | ||||
| 			// proofs we get from the network would be _complete_, whereas we need
 | ||||
| 			// _incomplete_ signals
 | ||||
| 			let _write_proof_result = match self.check_epoch_signal(&verified_header) { | ||||
| 				Ok(Some(proof)) => self.write_pending_proof(&verified_header, proof), | ||||
| 				Ok(None) => Ok(()), | ||||
| 				Err(e) => | ||||
| 					panic!("Unable to fetch epoch transition proof: {:?}", e), | ||||
| 			}; | ||||
| 
 | ||||
| 			// TODO: check epoch end.
 | ||||
| 
 | ||||
| 			let mut tx = self.db.transaction(); | ||||
| 			let pending = match self.chain.insert(&mut tx, verified_header) { | ||||
| @ -419,9 +453,71 @@ impl Client { | ||||
| 
 | ||||
| 		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 queue_header(&self, header: Header) -> Result<H256, BlockImportError> { | ||||
|  | ||||
| @ -30,7 +30,7 @@ use util::kvdb::{Database, DatabaseConfig}; | ||||
| use cache::Cache; | ||||
| use util::Mutex; | ||||
| 
 | ||||
| use super::{Client, Config as ClientConfig}; | ||||
| use super::{ChainDataFetcher, Client, Config as ClientConfig}; | ||||
| 
 | ||||
| /// Errors on service initialization.
 | ||||
| #[derive(Debug)] | ||||
| @ -51,14 +51,14 @@ impl fmt::Display for Error { | ||||
| } | ||||
| 
 | ||||
| /// Light client service.
 | ||||
| pub struct Service { | ||||
| 	client: Arc<Client>, | ||||
| pub struct Service<T> { | ||||
| 	client: Arc<Client<T>>, | ||||
| 	io_service: IoService<ClientIoMessage>, | ||||
| } | ||||
| 
 | ||||
| impl Service { | ||||
| impl<T: ChainDataFetcher> Service<T> { | ||||
| 	/// 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.
 | ||||
| 		let mut db_config = DatabaseConfig::with_columns(db::NUM_COLUMNS); | ||||
| @ -81,6 +81,7 @@ impl Service { | ||||
| 			db, | ||||
| 			db::COL_LIGHT_CHAIN, | ||||
| 			spec, | ||||
| 			fetcher, | ||||
| 			io_service.channel(), | ||||
| 			cache, | ||||
| 		).map_err(Error::Database)?); | ||||
| @ -97,14 +98,14 @@ impl Service { | ||||
| 	} | ||||
| 
 | ||||
| 	/// Get a handle to the client.
 | ||||
| 	pub fn client(&self) -> &Arc<Client> { | ||||
| 	pub fn client(&self) -> &Arc<Client<T>> { | ||||
| 		&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) { | ||||
| 		if let ClientIoMessage::BlockVerified = *message { | ||||
| 			self.0.import_verified(); | ||||
| @ -117,9 +118,10 @@ mod tests { | ||||
| 	use super::Service; | ||||
| 	use devtools::RandomTempPath; | ||||
| 	use ethcore::spec::Spec; | ||||
| 	
 | ||||
| 
 | ||||
| 	use std::sync::Arc; | ||||
| 	use cache::Cache; | ||||
| 	use client::fetch; | ||||
| 	use time::Duration; | ||||
| 	use util::Mutex; | ||||
| 
 | ||||
| @ -129,6 +131,6 @@ mod tests { | ||||
| 		let temp_path = RandomTempPath::new(); | ||||
| 		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) | ||||
| 		}; | ||||
| 
 | ||||
| 		// always report with "self.validators" so that the report actually gets
 | ||||
| 		// to the contract.
 | ||||
| 		let report = |report| match report { | ||||
| 			Report::Benign(address, block_number) => | ||||
| 				self.validators.report_benign(&address, set_number, block_number), | ||||
|  | ||||
| @ -132,7 +132,7 @@ pub trait StateDependentProof { | ||||
| 
 | ||||
| /// Proof generated on epoch change.
 | ||||
| pub enum Proof { | ||||
| 	/// Known proof (exctracted from signal)
 | ||||
| 	/// Known proof (extracted from signal)
 | ||||
| 	Known(Vec<u8>), | ||||
| 	/// State dependent proof.
 | ||||
| 	WithState(Box<StateDependentProof>), | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user