mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-09 20:11:09 +00:00
Remove BlockNumber <-> u64 conversions from light-client related code (#2666)
* Remove As usage from CHT * Remove As usage from CHT (continue) * Restrict BN <-> int conversions in CT * more BN <-> u64 conversions removed * upd spec_version * Apply suggestions from code review Co-Authored-By: Gavin Wood <github@gavwood.com> * Apply suggestions from code review Co-Authored-By: Gavin Wood <github@gavwood.com> * more grumbles * fix last grumbles + compilation * too long lines * too long lines
This commit is contained in:
committed by
Gavin Wood
parent
25b88f1a1f
commit
549d9e1da1
@@ -26,15 +26,11 @@
|
||||
use std::collections::HashSet;
|
||||
|
||||
use hash_db;
|
||||
use parity_codec::Encode;
|
||||
use trie;
|
||||
|
||||
use primitives::{H256, convert_hash};
|
||||
// We're using saturatedconversion in order to go back and forth to `u64`. this is stupid.
|
||||
// instead we should just make the CHT generic over the block number.
|
||||
use runtime_primitives::traits::{
|
||||
Header as HeaderT, SimpleArithmetic, One, SaturatedConversion,
|
||||
UniqueSaturatedInto
|
||||
};
|
||||
use runtime_primitives::traits::{Header as HeaderT, SimpleArithmetic, Zero, One};
|
||||
use state_machine::backend::InMemory as InMemoryState;
|
||||
use state_machine::{MemoryDB, TrieBackend, Backend as StateBackend,
|
||||
prove_read_on_trie_backend, read_proof_check, read_proof_check_on_proving_backend};
|
||||
@@ -43,14 +39,19 @@ use crate::error::{Error as ClientError, Result as ClientResult};
|
||||
|
||||
/// The size of each CHT. This value is passed to every CHT-related function from
|
||||
/// production code. Other values are passed from tests.
|
||||
pub const SIZE: u64 = 2048;
|
||||
const SIZE: u32 = 2048;
|
||||
|
||||
/// Gets default CHT size.
|
||||
pub fn size<N: From<u32>>() -> N {
|
||||
SIZE.into()
|
||||
}
|
||||
|
||||
/// Returns Some(cht_number) if CHT is need to be built when the block with given number is canonized.
|
||||
pub fn is_build_required<N>(cht_size: u64, block_num: N) -> Option<N>
|
||||
pub fn is_build_required<N>(cht_size: N, block_num: N) -> Option<N>
|
||||
where
|
||||
N: Clone + SimpleArithmetic,
|
||||
{
|
||||
let block_cht_num = block_to_cht_number(cht_size, block_num.clone())?;
|
||||
let block_cht_num = block_to_cht_number(cht_size.clone(), block_num.clone())?;
|
||||
let two = N::one() + N::one();
|
||||
if block_cht_num < two {
|
||||
return None;
|
||||
@@ -67,7 +68,7 @@ pub fn is_build_required<N>(cht_size: u64, block_num: N) -> Option<N>
|
||||
/// SIZE items. The items are assumed to proceed sequentially from `start_number(cht_num)`.
|
||||
/// Discards the trie's nodes.
|
||||
pub fn compute_root<Header, Hasher, I>(
|
||||
cht_size: u64,
|
||||
cht_size: Header::Number,
|
||||
cht_num: Header::Number,
|
||||
hashes: I,
|
||||
) -> ClientResult<Hasher::Out>
|
||||
@@ -84,7 +85,7 @@ pub fn compute_root<Header, Hasher, I>(
|
||||
|
||||
/// Build CHT-based header proof.
|
||||
pub fn build_proof<Header, Hasher, BlocksI, HashesI>(
|
||||
cht_size: u64,
|
||||
cht_size: Header::Number,
|
||||
cht_num: Header::Number,
|
||||
blocks: BlocksI,
|
||||
hashes: HashesI
|
||||
@@ -175,7 +176,7 @@ fn do_check_proof<Header, Hasher, F>(
|
||||
|
||||
/// Group ordered blocks by CHT number and call functor with blocks of each group.
|
||||
pub fn for_each_cht_group<Header, I, F, P>(
|
||||
cht_size: u64,
|
||||
cht_size: Header::Number,
|
||||
blocks: I,
|
||||
mut functor: F,
|
||||
mut functor_param: P,
|
||||
@@ -188,7 +189,7 @@ pub fn for_each_cht_group<Header, I, F, P>(
|
||||
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.saturated_into()) {
|
||||
let new_cht_num = match block_to_cht_number(cht_size, block) {
|
||||
Some(new_cht_num) => new_cht_num,
|
||||
None => return Err(ClientError::Backend(format!(
|
||||
"Cannot compute CHT root for the block #{}", block)).into()
|
||||
@@ -203,7 +204,7 @@ pub fn for_each_cht_group<Header, I, F, P>(
|
||||
|
||||
functor_param = functor(
|
||||
functor_param,
|
||||
current_cht_num.saturated_into(),
|
||||
current_cht_num,
|
||||
::std::mem::replace(&mut current_cht_blocks, Vec::new()),
|
||||
)?;
|
||||
}
|
||||
@@ -215,7 +216,7 @@ pub fn for_each_cht_group<Header, I, F, P>(
|
||||
if let Some(current_cht_num) = current_cht_num {
|
||||
functor(
|
||||
functor_param,
|
||||
current_cht_num.saturated_into(),
|
||||
current_cht_num,
|
||||
::std::mem::replace(&mut current_cht_blocks, Vec::new()),
|
||||
)?;
|
||||
}
|
||||
@@ -225,7 +226,7 @@ pub fn for_each_cht_group<Header, I, F, P>(
|
||||
|
||||
/// Build pairs for computing CHT.
|
||||
fn build_pairs<Header, I>(
|
||||
cht_size: u64,
|
||||
cht_size: Header::Number,
|
||||
cht_num: Header::Number,
|
||||
hashes: I
|
||||
) -> ClientResult<Vec<(Vec<u8>, Vec<u8>)>>
|
||||
@@ -235,28 +236,25 @@ fn build_pairs<Header, I>(
|
||||
{
|
||||
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 mut hash_index = Header::Number::zero();
|
||||
for hash in hashes.into_iter() {
|
||||
let hash = hash?.ok_or_else(|| ClientError::from(
|
||||
ClientError::MissingHashRequiredForCHT(
|
||||
cht_num.saturated_into::<u64>(),
|
||||
hash_number.saturated_into::<u64>()
|
||||
)
|
||||
ClientError::MissingHashRequiredForCHT
|
||||
))?;
|
||||
pairs.push((
|
||||
encode_cht_key(hash_number).to_vec(),
|
||||
encode_cht_key(start_num + hash_index).to_vec(),
|
||||
encode_cht_value(hash)
|
||||
));
|
||||
hash_number += Header::Number::one();
|
||||
hash_index += Header::Number::one();
|
||||
if hash_index == cht_size {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if pairs.len() as u64 == cht_size {
|
||||
if hash_index == cht_size {
|
||||
Ok(pairs)
|
||||
} else {
|
||||
Err(ClientError::MissingHashRequiredForCHT(
|
||||
cht_num.saturated_into::<u64>(),
|
||||
hash_number.saturated_into::<u64>()
|
||||
))
|
||||
Err(ClientError::MissingHashRequiredForCHT)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -266,39 +264,28 @@ fn build_pairs<Header, I>(
|
||||
/// 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<N: SimpleArithmetic>(cht_size: u64, cht_num: N) -> N {
|
||||
(cht_num * cht_size.saturated_into()) + N::one()
|
||||
pub fn start_number<N: SimpleArithmetic>(cht_size: N, cht_num: N) -> N {
|
||||
(cht_num * cht_size) + N::one()
|
||||
}
|
||||
|
||||
/// Get the ending block of a given CHT.
|
||||
pub fn end_number<N: SimpleArithmetic>(cht_size: u64, cht_num: N) -> N {
|
||||
(cht_num + N::one()) * cht_size.saturated_into()
|
||||
pub fn end_number<N: SimpleArithmetic>(cht_size: N, cht_num: N) -> N {
|
||||
(cht_num + N::one()) * cht_size
|
||||
}
|
||||
|
||||
/// Convert a block number to a CHT number.
|
||||
/// Returns `None` for `block_num` == 0, `Some` otherwise.
|
||||
pub fn block_to_cht_number<N: SimpleArithmetic>(cht_size: u64, block_num: N) -> Option<N> {
|
||||
pub fn block_to_cht_number<N: SimpleArithmetic>(cht_size: N, block_num: N) -> Option<N> {
|
||||
if block_num == N::zero() {
|
||||
None
|
||||
} else {
|
||||
Some((block_num - N::one()) / cht_size.saturated_into())
|
||||
Some((block_num - N::one()) / cht_size)
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert header number into CHT key.
|
||||
pub fn encode_cht_key<N: UniqueSaturatedInto<u64>>(number: N) -> Vec<u8> {
|
||||
// why not just use Encode?
|
||||
let number: u64 = number.saturated_into();
|
||||
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
|
||||
]
|
||||
pub fn encode_cht_key<N: Encode>(number: N) -> Vec<u8> {
|
||||
number.encode()
|
||||
}
|
||||
|
||||
/// Convert header hash into CHT value.
|
||||
@@ -323,8 +310,8 @@ mod tests {
|
||||
|
||||
#[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, 0u32.into()), None);
|
||||
assert_eq!(is_build_required(SIZE, 1u32.into()), 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);
|
||||
@@ -335,73 +322,101 @@ mod tests {
|
||||
|
||||
#[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);
|
||||
assert_eq!(start_number(SIZE, 0u32), 1u32);
|
||||
assert_eq!(start_number(SIZE, 1u32), SIZE + 1);
|
||||
assert_eq!(start_number(SIZE, 2u32), 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);
|
||||
assert_eq!(end_number(SIZE, 0u32), SIZE);
|
||||
assert_eq!(end_number(SIZE, 1u32), SIZE + SIZE);
|
||||
assert_eq!(end_number(SIZE, 2u32), SIZE + SIZE + SIZE);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn build_pairs_fails_when_no_enough_blocks() {
|
||||
assert!(build_pairs::<Header, _>(SIZE, 0,
|
||||
assert!(build_pairs::<Header, _>(SIZE as _, 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::<Header, _>(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());
|
||||
assert!(build_pairs::<Header, _>(
|
||||
SIZE as _,
|
||||
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::<Header, Blake2Hasher, _>(SIZE, 42,
|
||||
::std::iter::repeat_with(|| Ok(Some(H256::from_low_u64_be(1)))).take(SIZE as usize)).is_ok());
|
||||
assert!(compute_root::<Header, Blake2Hasher, _>(
|
||||
SIZE as _,
|
||||
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::<Header, Blake2Hasher, _, _>(
|
||||
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());
|
||||
SIZE as _,
|
||||
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::<Header, Blake2Hasher, _, _>(
|
||||
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());
|
||||
SIZE as _,
|
||||
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::<Header, _, _, _>(SIZE, vec![SIZE * 5, SIZE * 2], |_, _, _| Ok(()), ());
|
||||
let cht_size = SIZE as u64;
|
||||
let _ = for_each_cht_group::<Header, _, _, _>(
|
||||
cht_size,
|
||||
vec![cht_size * 5, cht_size * 2],
|
||||
|_, _, _| Ok(()),
|
||||
(),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn for_each_cht_group_works() {
|
||||
let _ = for_each_cht_group::<Header, _, _, _>(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!(),
|
||||
}
|
||||
let cht_size = SIZE as u64;
|
||||
let _ = for_each_cht_group::<Header, _, _, _>(
|
||||
cht_size,
|
||||
vec![
|
||||
cht_size * 2 + 1, cht_size * 2 + 2, cht_size * 2 + 5,
|
||||
cht_size * 4 + 1, cht_size * 4 + 7,
|
||||
cht_size * 6 + 1
|
||||
], |_, cht_num, blocks| {
|
||||
match cht_num {
|
||||
2 => assert_eq!(blocks, vec![cht_size * 2 + 1, cht_size * 2 + 2, cht_size * 2 + 5]),
|
||||
4 => assert_eq!(blocks, vec![cht_size * 4 + 1, cht_size * 4 + 7]),
|
||||
6 => assert_eq!(blocks, vec![cht_size * 6 + 1]),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}, ());
|
||||
Ok(())
|
||||
}, ()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user