eth_block fetching RPCs
This commit is contained in:
		
							parent
							
								
									d8893b959d
								
							
						
					
					
						commit
						73fa0cdc31
					
				| @ -411,6 +411,28 @@ impl HeaderChain { | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	/// Get a block's chain score.
 | ||||
| 	/// Returns nothing for non-canonical blocks.
 | ||||
| 	pub fn score(&self, id: BlockId) -> Option<U256> { | ||||
| 		let genesis_hash = self.genesis_hash(); | ||||
| 		match id { | ||||
| 			BlockId::Earliest | BlockId::Number(0) => Some(self.genesis_header.difficulty()), | ||||
| 			BlockId::Hash(hash) if hash == genesis_hash => Some(self.genesis_header.difficulty()), | ||||
| 			BlockId::Hash(hash) => match self.block_header(BlockId::Hash(hash)) { | ||||
| 				Some(header) => self.candidates.read().get(&header.number()) | ||||
| 					.and_then(|era| era.candidates.iter().find(|e| e.hash == hash)) | ||||
| 					.map(|c| c.total_difficulty), | ||||
| 				None => None, | ||||
| 			}, | ||||
| 			BlockId::Number(num) => { | ||||
| 				let candidates = self.candidates.read(); | ||||
| 				if self.best_block.read().number < num { return None } | ||||
| 				candidates.get(&num).map(|era| era.candidates[0].total_difficulty) | ||||
| 			} | ||||
| 			BlockId::Latest | BlockId::Pending => Some(self.best_block.read().total_difficulty) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	/// Get the best block's header.
 | ||||
| 	pub fn best_header(&self) -> encoded::Header { | ||||
| 		self.block_header(BlockId::Latest).expect("Header for best block always stored; qed") | ||||
|  | ||||
| @ -31,7 +31,7 @@ use ethcore::service::ClientIoMessage; | ||||
| use ethcore::encoded; | ||||
| use io::IoChannel; | ||||
| 
 | ||||
| use util::{H256, Mutex, RwLock}; | ||||
| use util::{H256, U256, Mutex, RwLock}; | ||||
| use util::kvdb::{KeyValueDB, CompactionProfile}; | ||||
| 
 | ||||
| use self::header_chain::{AncestryIter, HeaderChain}; | ||||
| @ -74,6 +74,9 @@ pub trait LightChainClient: Send + Sync { | ||||
| 	/// Get the best block header.
 | ||||
| 	fn best_block_header(&self) -> encoded::Header; | ||||
| 
 | ||||
| 	/// Get a block's chain score by ID.
 | ||||
| 	fn score(&self, id: BlockId) -> Option<U256>; | ||||
| 
 | ||||
| 	/// Get an iterator over a block and its ancestry.
 | ||||
| 	fn ancestry_iter<'a>(&'a self, start: BlockId) -> Box<Iterator<Item=encoded::Header> + 'a>; | ||||
| 
 | ||||
| @ -199,6 +202,11 @@ impl Client { | ||||
| 		self.chain.best_header() | ||||
| 	} | ||||
| 
 | ||||
| 	/// Get a block's chain score.
 | ||||
| 	pub fn score(&self, id: BlockId) -> Option<U256> { | ||||
| 		self.chain.score(id) | ||||
| 	} | ||||
| 
 | ||||
| 	/// Get an iterator over a block and its ancestry.
 | ||||
| 	pub fn ancestry_iter(&self, start: BlockId) -> AncestryIter { | ||||
| 		self.chain.ancestry_iter(start) | ||||
| @ -328,6 +336,10 @@ impl LightChainClient for Client { | ||||
| 		Client::best_block_header(self) | ||||
| 	} | ||||
| 
 | ||||
| 	fn score(&self, id: BlockId) -> Option<U256> { | ||||
| 		Client::score(self, id) | ||||
| 	} | ||||
| 
 | ||||
| 	fn ancestry_iter<'a>(&'a self, start: BlockId) -> Box<Iterator<Item=encoded::Header> + 'a> { | ||||
| 		Box::new(Client::ancestry_iter(self, start)) | ||||
| 	} | ||||
|  | ||||
| @ -199,6 +199,12 @@ impl Block { | ||||
| 	/// Decode to a full block.
 | ||||
| 	pub fn decode(&self) -> FullBlock { ::rlp::decode(&self.0) } | ||||
| 
 | ||||
| 	/// Decode the header.
 | ||||
| 	pub fn decode_header(&self) -> FullHeader { self.rlp().val_at(0) } | ||||
| 
 | ||||
| 	/// Clone the encoded header.
 | ||||
| 	pub fn header(&self) -> Header { Header(self.rlp().at(0).as_raw().to_vec()) } | ||||
| 
 | ||||
| 	/// Get the rlp of this block.
 | ||||
| 	#[inline] | ||||
| 	pub fn rlp(&self) -> Rlp { | ||||
|  | ||||
| @ -254,6 +254,111 @@ impl EthClient { | ||||
| 			} | ||||
| 		}).boxed() | ||||
| 	} | ||||
| 
 | ||||
| 	fn block(&self, id: BlockId) -> BoxFuture<Option<encoded::Block>, Error> { | ||||
| 		let (on_demand, sync) = (self.on_demand.clone(), self.sync.clone()); | ||||
| 
 | ||||
| 		self.header(id).and_then(move |hdr| { | ||||
| 			let req = match hdr { | ||||
| 				Some(hdr) => request::Body::new(hdr), | ||||
| 				None => return future::ok(None).boxed(), | ||||
| 			}; | ||||
| 
 | ||||
| 			match sync.with_context(move |ctx| on_demand.block(ctx, req)) { | ||||
| 				Some(fut) => fut.map_err(err_premature_cancel).map(Some).boxed(), | ||||
| 				None => future::err(errors::network_disabled()).boxed(), | ||||
| 			} | ||||
| 		}).boxed() | ||||
| 	} | ||||
| 
 | ||||
| 	// get a "rich" block structure
 | ||||
| 	fn rich_block(&self, id: BlockId, include_txs: bool) -> BoxFuture<Option<RichBlock>, Error> { | ||||
| 		let (on_demand, sync) = (self.on_demand.clone(), self.sync.clone()); | ||||
| 		let (client, engine) = (self.client.clone(), self.client.engine().clone()); | ||||
| 
 | ||||
| 		// helper for filling out a rich block once we've got a block and a score.
 | ||||
| 		let fill_rich = move |block: encoded::Block, score: Option<U256>| { | ||||
| 			let header = block.decode_header(); | ||||
| 			let extra_info = engine.extra_info(&header); | ||||
| 			RichBlock { | ||||
| 				block: Block { | ||||
| 					hash: Some(header.hash().into()), | ||||
| 					size: Some(block.rlp().as_raw().len().into()), | ||||
| 					parent_hash: header.parent_hash().clone().into(), | ||||
| 					uncles_hash: header.uncles_hash().clone().into(), | ||||
| 					author: header.author().clone().into(), | ||||
| 					miner: header.author().clone().into(), | ||||
| 					state_root: header.state_root().clone().into(), | ||||
| 					transactions_root: header.transactions_root().clone().into(), | ||||
| 					receipts_root: header.receipts_root().clone().into(), | ||||
| 					number: Some(header.number().into()), | ||||
| 					gas_used: header.gas_used().clone().into(), | ||||
| 					gas_limit: header.gas_limit().clone().into(), | ||||
| 					logs_bloom: header.log_bloom().clone().into(), | ||||
| 					timestamp: header.timestamp().into(), | ||||
| 					difficulty: header.difficulty().clone().into(), | ||||
| 					total_difficulty: score.map(Into::into), | ||||
| 					seal_fields: header.seal().into_iter().cloned().map(Into::into).collect(), | ||||
| 					uncles: block.uncle_hashes().into_iter().map(Into::into).collect(), | ||||
| 					transactions: match include_txs { | ||||
| 						true => BlockTransactions::Full(block.view().localized_transactions().into_iter().map(Into::into).collect()), | ||||
| 						false => BlockTransactions::Hashes(block.transaction_hashes().into_iter().map(Into::into).collect()), | ||||
| 					}, | ||||
| 					extra_data: Bytes::new(header.extra_data().to_vec()), | ||||
| 				}, | ||||
| 				extra_info: extra_info | ||||
| 			} | ||||
| 		}; | ||||
| 
 | ||||
| 		// get the block itself.
 | ||||
| 		self.block(id).and_then(move |block| match block { | ||||
| 			None => return future::ok(None).boxed(), | ||||
| 			Some(block) => { | ||||
| 				// then fetch the total difficulty (this is much easier after getting the block).
 | ||||
| 				match client.score(id) { | ||||
| 					Some(score) => future::ok(fill_rich(block, Some(score))).map(Some).boxed(), | ||||
| 					None => { | ||||
| 						// make a CHT request to fetch the chain score.
 | ||||
| 						let req = cht::block_to_cht_number(block.number()) | ||||
| 							.and_then(|num| client.cht_root(num as usize)) | ||||
| 							.and_then(|root| request::HeaderProof::new(block.number(), root)); | ||||
| 
 | ||||
| 
 | ||||
| 						let req = match req { | ||||
| 							Some(req) => req, | ||||
| 							None => { | ||||
| 								// somehow the genesis block slipped past other checks.
 | ||||
| 								// return it now.
 | ||||
| 								let score = client.block_header(BlockId::Number(0)) | ||||
| 									.expect("genesis always stored; qed") | ||||
| 									.difficulty(); | ||||
| 
 | ||||
| 								return future::ok(fill_rich(block, Some(score))).map(Some).boxed() | ||||
| 							} | ||||
| 						}; | ||||
| 
 | ||||
| 						// three possible outcomes:
 | ||||
| 						//   - network is down.
 | ||||
| 						//   - we get a score, but our hash is non-canonical.
 | ||||
| 						//   - we get ascore, and our hash is canonical.
 | ||||
| 						let maybe_fut = sync.with_context(move |ctx| on_demand.hash_and_score_by_number(ctx, req)); | ||||
| 						match maybe_fut { | ||||
| 							Some(fut) => fut.map(move |(hash, score)| { | ||||
| 									let score = if hash == block.hash() { | ||||
| 										Some(score) | ||||
| 									} else { | ||||
| 										None | ||||
| 									}; | ||||
| 
 | ||||
| 									Some(fill_rich(block, score)) | ||||
| 								}).map_err(err_premature_cancel).boxed(), | ||||
| 							None => return future::err(errors::network_disabled()).boxed(), | ||||
| 						} | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		}).boxed() | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| impl Eth for EthClient { | ||||
| @ -295,7 +400,10 @@ impl Eth for EthClient { | ||||
| 	} | ||||
| 
 | ||||
| 	fn gas_price(&self) -> Result<RpcU256, Error> { | ||||
| 		Ok(Default::default()) | ||||
| 		Ok(self.cache.lock().gas_price_corpus() | ||||
| 			.and_then(|c| c.median().cloned()) | ||||
| 			.map(RpcU256::from) | ||||
| 			.unwrap_or_else(Default::default)) | ||||
| 	} | ||||
| 
 | ||||
| 	fn accounts(&self, meta: Metadata) -> BoxFuture<Vec<RpcH160>, Error> { | ||||
| @ -324,11 +432,11 @@ impl Eth for EthClient { | ||||
| 	} | ||||
| 
 | ||||
| 	fn block_by_hash(&self, hash: RpcH256, include_txs: bool) -> BoxFuture<Option<RichBlock>, Error> { | ||||
| 		future::err(errors::unimplemented(None)).boxed() | ||||
| 		self.rich_block(BlockId::Hash(hash.into()), include_txs) | ||||
| 	} | ||||
| 
 | ||||
| 	fn block_by_number(&self, num: BlockNumber, include_txs: bool) -> BoxFuture<Option<RichBlock>, Error> { | ||||
| 		future::err(errors::unimplemented(None)).boxed() | ||||
| 		self.rich_block(num.into(), include_txs) | ||||
| 	} | ||||
| 
 | ||||
| 	fn transaction_count(&self, address: RpcH160, num: Trailing<BlockNumber>) -> BoxFuture<RpcU256, Error> { | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user