light: block status and CHT module

This commit is contained in:
Robert Habermeier 2016-12-13 20:13:16 +01:00
parent 45ef986c04
commit 2a01b43bd1
3 changed files with 52 additions and 15 deletions

View File

@ -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
}

View File

@ -28,6 +28,9 @@
use std::collections::{BTreeMap, HashMap}; use std::collections::{BTreeMap, HashMap};
use client::cht;
use ethcore::block_status::BlockStatus;
use ethcore::error::BlockError; use ethcore::error::BlockError;
use ethcore::ids::BlockId; use ethcore::ids::BlockId;
use ethcore::views::HeaderView; use ethcore::views::HeaderView;
@ -35,13 +38,10 @@ use util::{Bytes, H256, U256, Mutex, RwLock};
use smallvec::SmallVec; use smallvec::SmallVec;
/// Delay this many blocks before producing a CHT. required to be at /// Store at least this many candidate headers at all times.
/// least 1 but should be more in order to be resilient against reorgs. /// Also functions as the delay for computing CHTs as they aren't
const CHT_DELAY: u64 = 2048; /// relevant to any blocks we've got in memory.
const HISTORY: u64 = 2048;
/// Generate CHT roots of this size.
// TODO: move CHT definition/creation into more generic module.
const CHT_SIZE: u64 = 2048;
/// Information about a block. /// Information about a block.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -94,6 +94,10 @@ impl HeaderChain {
} }
/// Insert a pre-verified header. /// 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> { pub fn insert(&self, header: Bytes) -> Result<(), BlockError> {
let view = HeaderView::new(&header); let view = HeaderView::new(&header);
let hash = view.hash(); let hash = view.hash();
@ -140,7 +144,8 @@ impl HeaderChain {
let canon = entry.candidates.iter().find(|x| x.hash == canon_hash) 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"); .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; canon_hash = canon.parent_hash;
} }
@ -152,11 +157,11 @@ impl HeaderChain {
// produce next CHT root if it's time. // produce next CHT root if it's time.
let earliest_era = *candidates.keys().next().expect("at least one era just created; qed"); let earliest_era = *candidates.keys().next().expect("at least one era just created; qed");
if earliest_era + CHT_DELAY + CHT_SIZE <= number { if earliest_era + HISTORY + cht::SIZE <= number {
let mut values = Vec::with_capacity(CHT_SIZE as usize); let mut values = Vec::with_capacity(cht::SIZE as usize);
{ {
let mut headers = self.headers.write(); 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) let era_entry = candidates.remove(&i)
.expect("all eras are sequential with no gaps; qed"); .expect("all eras are sequential with no gaps; qed");
@ -172,7 +177,7 @@ impl HeaderChain {
} }
let cht_root = ::util::triehash::trie_root(values); 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); 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)] #[cfg(test)]
@ -248,7 +261,7 @@ mod tests {
use ethcore::spec::Spec; use ethcore::spec::Spec;
#[test] #[test]
fn it_works() { fn basic_chain() {
let spec = Spec::new_test(); let spec = Spec::new_test();
let genesis_header = spec.genesis_header(); let genesis_header = spec.genesis_header();

View File

@ -25,8 +25,10 @@
//! low-latency applications, but perfectly suitable for simple everyday //! low-latency applications, but perfectly suitable for simple everyday
//! use-cases like sending transactions from a personal account. //! use-cases like sending transactions from a personal account.
//! //!
//! It starts by performing a header-only sync, verifying random samples //! The light client performs a header-only sync, doing verification and pruning
//! of members of the chain to varying degrees. //! 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)] #![deny(missing_docs)]