Merge pull request #4181 from ethcore/cht-full-nodes
CHT calculations for full nodes
This commit is contained in:
		
						commit
						5830e03201
					
				
							
								
								
									
										54
									
								
								ethcore/light/src/cht.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								ethcore/light/src/cht.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,54 @@ | ||||
| // Copyright 2015, 2016 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.
 | ||||
| 
 | ||||
| //! Canonical hash trie definitions and helper functions.
 | ||||
| 
 | ||||
| /// The size of each CHT.
 | ||||
| pub const SIZE: u64 = 2048; | ||||
| 
 | ||||
| /// Convert a block number to a CHT number.
 | ||||
| /// Returns `None` for `block_num` == 0, `Some` otherwise.
 | ||||
| pub fn block_to_cht_number(block_num: u64) -> Option<u64> { | ||||
| 	match block_num { | ||||
| 		0 => None, | ||||
| 		n => Some((n - 1) / SIZE), | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| /// Get the starting block of a given CHT.
 | ||||
| /// CHT 0 includes block 1...SIZE,
 | ||||
| /// CHT 1 includes block SIZE + 1 ... 2*SIZE
 | ||||
| /// More generally: CHT N includes block (1 + N*SIZE)...((N+1)*SIZE).
 | ||||
| /// This is because the genesis hash is assumed to be known
 | ||||
| /// and including it would be redundant.
 | ||||
| pub fn start_number(cht_num: u64) -> u64 { | ||||
| 	(cht_num * SIZE) + 1 | ||||
| } | ||||
| 
 | ||||
| #[cfg(test)] | ||||
| mod tests { | ||||
| 	#[test] | ||||
| 	fn block_to_cht_number() { | ||||
| 		assert!(::cht::block_to_cht_number(0).is_none()); | ||||
| 		assert_eq!(::cht::block_to_cht_number(1).unwrap(), 0); | ||||
| 		assert_eq!(::cht::block_to_cht_number(::cht::SIZE + 1).unwrap(), 1); | ||||
| 		assert_eq!(::cht::block_to_cht_number(::cht::SIZE).unwrap(), 0); | ||||
| 	} | ||||
| 
 | ||||
| 	#[test] | ||||
| 	fn start_number() { | ||||
| 		assert_eq!(::cht::start_number(0), 1); | ||||
| 		assert_eq!(::cht::start_number(1), ::cht::SIZE + 1); | ||||
| 		assert_eq!(::cht::start_number(2), ::cht::SIZE * 2 + 1); | ||||
| 	} | ||||
| } | ||||
| @ -1,22 +0,0 @@ | ||||
| // Copyright 2015, 2016 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.
 | ||||
| 
 | ||||
| //! Canonical hash trie definitions and helper functions.
 | ||||
| 
 | ||||
| /// The size of each CHT.
 | ||||
| pub const SIZE: u64 = 2048; | ||||
| 
 | ||||
| /// Convert a block number to a CHT number.
 | ||||
| pub fn block_to_cht_number(block_num: u64) -> u64 { | ||||
| 	(block_num + 1) / SIZE | ||||
| } | ||||
| @ -28,7 +28,7 @@ | ||||
| 
 | ||||
| use std::collections::{BTreeMap, HashMap}; | ||||
| 
 | ||||
| use client::cht; | ||||
| use cht; | ||||
| 
 | ||||
| use ethcore::block_status::BlockStatus; | ||||
| use ethcore::error::BlockError; | ||||
|  | ||||
| @ -39,8 +39,6 @@ use self::header_chain::HeaderChain; | ||||
| 
 | ||||
| pub use self::service::Service; | ||||
| 
 | ||||
| pub mod cht; | ||||
| 
 | ||||
| mod header_chain; | ||||
| mod service; | ||||
| 
 | ||||
|  | ||||
| @ -33,6 +33,7 @@ | ||||
| #![deny(missing_docs)] | ||||
| 
 | ||||
| pub mod client; | ||||
| pub mod cht; | ||||
| pub mod net; | ||||
| pub mod on_demand; | ||||
| 
 | ||||
|  | ||||
| @ -41,6 +41,8 @@ pub enum Error { | ||||
| 	Canceled, | ||||
| 	/// No suitable peers available to serve the request.
 | ||||
| 	NoPeersAvailable, | ||||
| 	/// Invalid request.
 | ||||
| 	InvalidRequest, | ||||
| 	/// Request timed out.
 | ||||
| 	TimedOut, | ||||
| } | ||||
| @ -112,7 +114,15 @@ impl OnDemand { | ||||
| 	// dispatch the request, completing the request if no peers available.
 | ||||
| 	fn dispatch_header_by_number(&self, ctx: &BasicContext, req: request::HeaderByNumber, sender: Sender<encoded::Header>) { | ||||
| 		let num = req.num; | ||||
| 		let cht_num = ::client::cht::block_to_cht_number(req.num); | ||||
| 		let cht_num = match ::cht::block_to_cht_number(req.num) { | ||||
| 			Some(cht_num) => cht_num, | ||||
| 			None => { | ||||
| 				warn!(target: "on_demand", "Attempted to dispatch invalid header proof: req.num == 0"); | ||||
| 				sender.complete(Err(Error::InvalidRequest)); | ||||
| 				return; | ||||
| 			} | ||||
| 		}; | ||||
| 
 | ||||
| 		let les_req = LesRequest::HeaderProofs(les_request::HeaderProofs { | ||||
| 			requests: vec![les_request::HeaderProof { | ||||
| 				cht_number: cht_num, | ||||
|  | ||||
| @ -226,11 +226,76 @@ impl<T: ProvingBlockChainClient + ?Sized> Provider for T { | ||||
| 		self.code_by_hash(req.account_key, BlockId::Hash(req.block_hash)) | ||||
| 	} | ||||
| 
 | ||||
| 	fn header_proof(&self, _req: request::HeaderProof) -> Option<(encoded::Header, Vec<Bytes>)> { | ||||
| 		None | ||||
| 	fn header_proof(&self, req: request::HeaderProof) -> Option<(encoded::Header, Vec<Bytes>)> { | ||||
| 		use util::MemoryDB; | ||||
| 		use util::trie::{Trie, TrieMut, TrieDB, TrieDBMut, Recorder}; | ||||
| 
 | ||||
| 		if Some(req.cht_number) != ::cht::block_to_cht_number(req.block_number) { | ||||
| 			debug!(target: "les_provider", "Requested CHT number mismatch with block number."); | ||||
| 			return None; | ||||
| 		} | ||||
| 
 | ||||
| 		let mut memdb = MemoryDB::new(); | ||||
| 		let mut root = H256::default(); | ||||
| 		let mut needed_hdr = None; | ||||
| 		{ | ||||
| 			let mut t = TrieDBMut::new(&mut memdb, &mut root); | ||||
| 			let start_num = ::cht::start_number(req.cht_number); | ||||
| 			for i in (0..::cht::SIZE).map(|x| x + start_num) { | ||||
| 				match self.block_header(BlockId::Number(i)) { | ||||
| 					None => return None, | ||||
| 					Some(hdr) => { | ||||
| 						t.insert( | ||||
| 							&*::rlp::encode(&i), | ||||
| 							&*::rlp::encode(&hdr.hash()), | ||||
| 						).expect("fresh in-memory database is infallible; qed"); | ||||
| 
 | ||||
| 						if i == req.block_number { needed_hdr = Some(hdr) } | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		let needed_hdr = needed_hdr.expect("`needed_hdr` always set in loop, number checked before; qed"); | ||||
| 
 | ||||
| 		let mut recorder = Recorder::with_depth(req.from_level); | ||||
| 		let t = TrieDB::new(&memdb, &root) | ||||
| 			.expect("Same DB and root as just produced by TrieDBMut; qed"); | ||||
| 
 | ||||
| 		if let Err(e) = t.get_with(&*::rlp::encode(&req.block_number), &mut recorder) { | ||||
| 			debug!(target: "les_provider", "Error looking up number in freshly-created CHT: {}", e); | ||||
| 			return None; | ||||
| 		} | ||||
| 
 | ||||
| 		// TODO: cache calculated CHT if possible.
 | ||||
| 		let proof = recorder.drain().into_iter().map(|x| x.data).collect(); | ||||
| 		Some((needed_hdr, proof)) | ||||
| 	} | ||||
| 
 | ||||
| 	fn ready_transactions(&self) -> Vec<PendingTransaction> { | ||||
| 		BlockChainClient::ready_transactions(self) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| #[cfg(test)] | ||||
| mod tests { | ||||
| 	use ethcore::client::{EachBlockWith, TestBlockChainClient}; | ||||
| 	use super::Provider; | ||||
| 
 | ||||
| 	#[test] | ||||
| 	fn cht_proof() { | ||||
| 		let client = TestBlockChainClient::new(); | ||||
| 		client.add_blocks(2000, EachBlockWith::Nothing); | ||||
| 
 | ||||
| 		let req = ::request::HeaderProof { | ||||
| 			cht_number: 0, | ||||
| 			block_number: 1500, | ||||
| 			from_level: 0, | ||||
| 		}; | ||||
| 
 | ||||
| 		assert!(client.header_proof(req.clone()).is_none()); | ||||
| 
 | ||||
| 		client.add_blocks(48, EachBlockWith::Nothing); | ||||
| 
 | ||||
| 		assert!(client.header_proof(req.clone()).is_some()); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @ -120,7 +120,7 @@ pub struct ContractCodes { | ||||
| pub struct HeaderProof { | ||||
| 	/// Number of the CHT.
 | ||||
| 	pub cht_number: u64, | ||||
| 	/// Block number requested.
 | ||||
| 	/// Block number requested. May not be 0: genesis isn't included in any CHT.
 | ||||
| 	pub block_number: u64, | ||||
| 	/// If greater than zero, trie nodes beyond this level may be omitted.
 | ||||
| 	pub from_level: u32, | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user