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