From 2a01b43bd1733a6a66be17b591c430de7e8c371c Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Tue, 13 Dec 2016 20:13:16 +0100 Subject: [PATCH] light: block status and CHT module --- ethcore/light/src/client/cht.rs | 22 +++++++++++++ ethcore/light/src/client/header_chain.rs | 39 ++++++++++++++++-------- ethcore/light/src/lib.rs | 6 ++-- 3 files changed, 52 insertions(+), 15 deletions(-) create mode 100644 ethcore/light/src/client/cht.rs diff --git a/ethcore/light/src/client/cht.rs b/ethcore/light/src/client/cht.rs new file mode 100644 index 000000000..8424e0c31 --- /dev/null +++ b/ethcore/light/src/client/cht.rs @@ -0,0 +1,22 @@ +// 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 +} diff --git a/ethcore/light/src/client/header_chain.rs b/ethcore/light/src/client/header_chain.rs index 299df04f3..f350f19ca 100644 --- a/ethcore/light/src/client/header_chain.rs +++ b/ethcore/light/src/client/header_chain.rs @@ -28,6 +28,9 @@ use std::collections::{BTreeMap, HashMap}; +use client::cht; + +use ethcore::block_status::BlockStatus; use ethcore::error::BlockError; use ethcore::ids::BlockId; use ethcore::views::HeaderView; @@ -35,13 +38,10 @@ use util::{Bytes, H256, U256, Mutex, RwLock}; use smallvec::SmallVec; -/// Delay this many blocks before producing a CHT. required to be at -/// least 1 but should be more in order to be resilient against reorgs. -const CHT_DELAY: u64 = 2048; - -/// Generate CHT roots of this size. -// TODO: move CHT definition/creation into more generic module. -const CHT_SIZE: u64 = 2048; +/// Store at least this many candidate headers at all times. +/// Also functions as the delay for computing CHTs as they aren't +/// relevant to any blocks we've got in memory. +const HISTORY: u64 = 2048; /// Information about a block. #[derive(Debug, Clone)] @@ -94,6 +94,10 @@ impl HeaderChain { } /// Insert a pre-verified header. + /// + /// This blindly trusts that the data given to it is + /// a) valid RLP encoding of a header and + /// b) has sensible data contained within it. pub fn insert(&self, header: Bytes) -> Result<(), BlockError> { let view = HeaderView::new(&header); let hash = view.hash(); @@ -140,7 +144,8 @@ impl HeaderChain { let canon = entry.candidates.iter().find(|x| x.hash == canon_hash) .expect("blocks are only inserted if parent is present; or this is the block we just added; qed"); - // what about reorgs > CHT_SIZE + CHT_DELAY? + // what about reorgs > cht::SIZE + HISTORY? + // resetting to the last block of a given CHT should be possible. canon_hash = canon.parent_hash; } @@ -152,11 +157,11 @@ impl HeaderChain { // produce next CHT root if it's time. let earliest_era = *candidates.keys().next().expect("at least one era just created; qed"); - if earliest_era + CHT_DELAY + CHT_SIZE <= number { - let mut values = Vec::with_capacity(CHT_SIZE as usize); + if earliest_era + HISTORY + cht::SIZE <= number { + let mut values = Vec::with_capacity(cht::SIZE as usize); { let mut headers = self.headers.write(); - for i in (0..CHT_SIZE).map(|x| x + earliest_era) { + for i in (0..cht::SIZE).map(|x| x + earliest_era) { let era_entry = candidates.remove(&i) .expect("all eras are sequential with no gaps; qed"); @@ -172,7 +177,7 @@ impl HeaderChain { } let cht_root = ::util::triehash::trie_root(values); - debug!(target: "chain", "Produced CHT {} root: {:?}", (earliest_era - 1) % CHT_SIZE, cht_root); + debug!(target: "chain", "Produced CHT {} root: {:?}", (earliest_era - 1) % cht::SIZE, cht_root); self.cht_roots.lock().push(cht_root); } @@ -238,6 +243,14 @@ impl HeaderChain { }) } } + + /// Get block status. + pub fn status(&self, hash: &H256) -> BlockStatus { + match self.headers.read().contains_key(hash) { + true => BlockStatus::InChain, + false => BlockStatus::Unknown, + } + } } #[cfg(test)] @@ -248,7 +261,7 @@ mod tests { use ethcore::spec::Spec; #[test] - fn it_works() { + fn basic_chain() { let spec = Spec::new_test(); let genesis_header = spec.genesis_header(); diff --git a/ethcore/light/src/lib.rs b/ethcore/light/src/lib.rs index ddd012075..5cdc3addc 100644 --- a/ethcore/light/src/lib.rs +++ b/ethcore/light/src/lib.rs @@ -25,8 +25,10 @@ //! low-latency applications, but perfectly suitable for simple everyday //! use-cases like sending transactions from a personal account. //! -//! It starts by performing a header-only sync, verifying random samples -//! of members of the chain to varying degrees. +//! The light client performs a header-only sync, doing verification and pruning +//! historical blocks. Upon pruning, batches of 2048 blocks have a number => hash +//! mapping sealed into "canonical hash tries" which can later be used to verify +//! historical block queries from peers. #![deny(missing_docs)]