) -> ClientResult,
{
let mut current_cht_num = None;
let mut current_cht_blocks = Vec::new();
for block in blocks {
let new_cht_num = match block_to_cht_number(cht_size, block.as_()) {
Some(new_cht_num) => new_cht_num,
None => return Err(ClientErrorKind::Backend(format!(
"Cannot compute CHT root for the block #{}", block)).into()
),
};
let advance_to_next_cht = current_cht_num.is_some() && current_cht_num != Some(new_cht_num);
if advance_to_next_cht {
let current_cht_num = current_cht_num.expect("advance_to_next_cht is true;
it is true only when current_cht_num is Some; qed");
assert!(new_cht_num > current_cht_num, "for_each_cht_group only supports ordered iterators");
functor_param = functor(
functor_param,
As::sa(current_cht_num),
::std::mem::replace(&mut current_cht_blocks, Vec::new()),
)?;
}
current_cht_blocks.push(block);
current_cht_num = Some(new_cht_num);
}
if let Some(current_cht_num) = current_cht_num {
functor(
functor_param,
As::sa(current_cht_num),
::std::mem::replace(&mut current_cht_blocks, Vec::new()),
)?;
}
Ok(())
}
/// Build pairs for computing CHT.
fn build_pairs(
cht_size: u64,
cht_num: Header::Number,
hashes: I
) -> ClientResult, Vec)>>
where
Header: HeaderT,
I: IntoIterator- >>,
{
let start_num = start_number(cht_size, cht_num);
let mut pairs = Vec::new();
let mut hash_number = start_num;
for hash in hashes.into_iter().take(cht_size as usize) {
let hash = hash?.ok_or_else(|| ClientError::from(
ClientErrorKind::MissingHashRequiredForCHT(cht_num.as_(), hash_number.as_())
))?;
pairs.push((
encode_cht_key(hash_number).to_vec(),
encode_cht_value(hash)
));
hash_number += Header::Number::one();
}
if pairs.len() as u64 == cht_size {
Ok(pairs)
} else {
Err(ClientErrorKind::MissingHashRequiredForCHT(cht_num.as_(), hash_number.as_()).into())
}
}
/// 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_size: u64, cht_num: N) -> N {
(cht_num * As::sa(cht_size)) + N::one()
}
/// Get the ending block of a given CHT.
pub fn end_number(cht_size: u64, cht_num: N) -> N {
(cht_num + N::one()) * As::sa(cht_size)
}
/// Convert a block number to a CHT number.
/// Returns `None` for `block_num` == 0, `Some` otherwise.
pub fn block_to_cht_number(cht_size: u64, block_num: N) -> Option {
if block_num == N::zero() {
None
} else {
Some((block_num - N::one()) / As::sa(cht_size))
}
}
/// Convert header number into CHT key.
pub fn encode_cht_key>(number: N) -> Vec {
let number: u64 = number.as_();
vec![
(number >> 56) as u8,
((number >> 48) & 0xff) as u8,
((number >> 40) & 0xff) as u8,
((number >> 32) & 0xff) as u8,
((number >> 24) & 0xff) as u8,
((number >> 16) & 0xff) as u8,
((number >> 8) & 0xff) as u8,
(number & 0xff) as u8
]
}
/// Convert header hash into CHT value.
fn encode_cht_value>(hash: Hash) -> Vec {
hash.as_ref().to_vec()
}
/// Convert CHT value into block header hash.
pub fn decode_cht_value(value: &[u8]) -> Option {
match value.len() {
32 => Some(H256::from_slice(&value[0..32])),
_ => None,
}
}
#[cfg(test)]
mod tests {
use primitives::{Blake2Hasher};
use test_client::runtime::Header;
use super::*;
#[test]
fn is_build_required_works() {
assert_eq!(is_build_required(SIZE, 0u64), None);
assert_eq!(is_build_required(SIZE, 1u64), None);
assert_eq!(is_build_required(SIZE, SIZE), None);
assert_eq!(is_build_required(SIZE, SIZE + 1), None);
assert_eq!(is_build_required(SIZE, 2 * SIZE), None);
assert_eq!(is_build_required(SIZE, 2 * SIZE + 1), Some(0));
assert_eq!(is_build_required(SIZE, 3 * SIZE), None);
assert_eq!(is_build_required(SIZE, 3 * SIZE + 1), Some(1));
}
#[test]
fn start_number_works() {
assert_eq!(start_number(SIZE, 0u64), 1u64);
assert_eq!(start_number(SIZE, 1u64), SIZE + 1);
assert_eq!(start_number(SIZE, 2u64), SIZE + SIZE + 1);
}
#[test]
fn end_number_works() {
assert_eq!(end_number(SIZE, 0u64), SIZE);
assert_eq!(end_number(SIZE, 1u64), SIZE + SIZE);
assert_eq!(end_number(SIZE, 2u64), SIZE + SIZE + SIZE);
}
#[test]
fn build_pairs_fails_when_no_enough_blocks() {
assert!(build_pairs::(SIZE, 0,
::std::iter::repeat_with(|| Ok(Some(H256::from_low_u64_be(1)))).take(SIZE as usize / 2)).is_err());
}
#[test]
fn build_pairs_fails_when_missing_block() {
assert!(build_pairs::(SIZE, 0, ::std::iter::repeat_with(|| Ok(Some(H256::from_low_u64_be(1)))).take(SIZE as usize / 2)
.chain(::std::iter::once(Ok(None)))
.chain(::std::iter::repeat_with(|| Ok(Some(H256::from_low_u64_be(2)))).take(SIZE as usize / 2 - 1))).is_err());
}
#[test]
fn compute_root_works() {
assert!(compute_root::(SIZE, 42,
::std::iter::repeat_with(|| Ok(Some(H256::from_low_u64_be(1)))).take(SIZE as usize)).is_ok());
}
#[test]
#[should_panic]
fn build_proof_panics_when_querying_wrong_block() {
assert!(build_proof::(
SIZE, 0, vec![(SIZE * 1000) as u64],
::std::iter::repeat_with(|| Ok(Some(H256::from_low_u64_be(1)))).take(SIZE as usize)).is_err());
}
#[test]
fn build_proof_works() {
assert!(build_proof::(
SIZE, 0, vec![(SIZE / 2) as u64],
::std::iter::repeat_with(|| Ok(Some(H256::from_low_u64_be(1)))).take(SIZE as usize)).is_ok());
}
#[test]
#[should_panic]
fn for_each_cht_group_panics() {
let _ = for_each_cht_group::(SIZE, vec![SIZE * 5, SIZE * 2], |_, _, _| Ok(()), ());
}
#[test]
fn for_each_cht_group_works() {
let _ = for_each_cht_group::(SIZE, vec![
SIZE * 2 + 1, SIZE * 2 + 2, SIZE * 2 + 5,
SIZE * 4 + 1, SIZE * 4 + 7,
SIZE * 6 + 1
], |_, cht_num, blocks| {
match cht_num {
2 => assert_eq!(blocks, vec![SIZE * 2 + 1, SIZE * 2 + 2, SIZE * 2 + 5]),
4 => assert_eq!(blocks, vec![SIZE * 4 + 1, SIZE * 4 + 7]),
6 => assert_eq!(blocks, vec![SIZE * 6 + 1]),
_ => unreachable!(),
}
Ok(())
}, ());
}
}