diff --git a/ethcore/light/src/cht.rs b/ethcore/light/src/cht.rs index 537a3ed6a..13edadee1 100644 --- a/ethcore/light/src/cht.rs +++ b/ethcore/light/src/cht.rs @@ -17,11 +17,38 @@ 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 +/// Returns `None` for `block_num` == 0, `Some` otherwise. +pub fn block_to_cht_number(block_num: u64) -> Option { + 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); + } +} diff --git a/ethcore/light/src/on_demand/mod.rs b/ethcore/light/src/on_demand/mod.rs index dfc78a629..a01de7598 100644 --- a/ethcore/light/src/on_demand/mod.rs +++ b/ethcore/light/src/on_demand/mod.rs @@ -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) { let num = req.num; - let cht_num = ::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, diff --git a/ethcore/light/src/provider.rs b/ethcore/light/src/provider.rs index 14f8a148d..a2ee36f82 100644 --- a/ethcore/light/src/provider.rs +++ b/ethcore/light/src/provider.rs @@ -230,7 +230,7 @@ impl Provider for T { use util::MemoryDB; use util::trie::{Trie, TrieMut, TrieDB, TrieDBMut, Recorder}; - if req.cht_number != ::cht::block_to_cht_number(req.block_number) { + 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; } @@ -257,7 +257,7 @@ impl Provider for T { } let needed_hdr = needed_hdr.expect("`needed_hdr` always set in loop, number checked before; qed"); - let mut recorder = Recorder::new(); + 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"); @@ -275,3 +275,27 @@ impl Provider for T { 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()); + } +} diff --git a/ethcore/light/src/types/les_request.rs b/ethcore/light/src/types/les_request.rs index dde8ac2f8..6dced2731 100644 --- a/ethcore/light/src/types/les_request.rs +++ b/ethcore/light/src/types/les_request.rs @@ -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,