mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-04-29 09:07:57 +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
@@ -127,7 +127,7 @@ pub trait Backend<Block, H>: AuxStore + Send + Sync where
|
||||
/// Associated state backend type.
|
||||
type State: StateBackend<H>;
|
||||
/// Changes trie storage.
|
||||
type ChangesTrieStorage: PrunableStateChangesTrieStorage<H>;
|
||||
type ChangesTrieStorage: PrunableStateChangesTrieStorage<Block, H>;
|
||||
|
||||
/// Begin a new block insertion transaction with given parent block id.
|
||||
/// When constructing the genesis, this is called with all-zero hash.
|
||||
@@ -177,9 +177,15 @@ pub trait Backend<Block, H>: AuxStore + Send + Sync where
|
||||
}
|
||||
|
||||
/// Changes trie storage that supports pruning.
|
||||
pub trait PrunableStateChangesTrieStorage<H: Hasher>: StateChangesTrieStorage<H> {
|
||||
pub trait PrunableStateChangesTrieStorage<Block: BlockT, H: Hasher>:
|
||||
StateChangesTrieStorage<H, NumberFor<Block>>
|
||||
{
|
||||
/// Get number block of oldest, non-pruned changes trie.
|
||||
fn oldest_changes_trie_block(&self, config: &ChangesTrieConfiguration, best_finalized: u64) -> u64;
|
||||
fn oldest_changes_trie_block(
|
||||
&self,
|
||||
config: &ChangesTrieConfiguration,
|
||||
best_finalized: NumberFor<Block>,
|
||||
) -> NumberFor<Block>;
|
||||
}
|
||||
|
||||
/// Mark for all Backend implementations, that are making use of state data, stored locally.
|
||||
|
||||
@@ -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(())
|
||||
}, ()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -448,7 +448,7 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where
|
||||
|
||||
/// Reads given header and generates CHT-based header proof.
|
||||
pub fn header_proof(&self, id: &BlockId<Block>) -> error::Result<(Block::Header, Vec<Vec<u8>>)> {
|
||||
self.header_proof_with_cht_size(id, cht::SIZE)
|
||||
self.header_proof_with_cht_size(id, cht::size())
|
||||
}
|
||||
|
||||
/// Get block hash by number.
|
||||
@@ -459,16 +459,23 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where
|
||||
}
|
||||
|
||||
/// Reads given header and generates CHT-based header proof for CHT of given size.
|
||||
pub fn header_proof_with_cht_size(&self,
|
||||
pub fn header_proof_with_cht_size(
|
||||
&self,
|
||||
id: &BlockId<Block>,
|
||||
cht_size: u64
|
||||
cht_size: NumberFor<Block>,
|
||||
) -> error::Result<(Block::Header, Vec<Vec<u8>>)> {
|
||||
let proof_error = || error::Error::Backend(format!("Failed to generate header proof for {:?}", id));
|
||||
let header = self.backend.blockchain().expect_header(*id)?;
|
||||
let block_num = *header.number();
|
||||
let cht_num = cht::block_to_cht_number(cht_size, block_num).ok_or_else(proof_error)?;
|
||||
let cht_start = cht::start_number(cht_size, cht_num);
|
||||
let headers = (cht_start.saturated_into()..).map(|num| self.block_hash(num.saturated_into()));
|
||||
let mut current_num = cht_start;
|
||||
let cht_range = ::std::iter::from_fn(|| {
|
||||
let old_current_num = current_num;
|
||||
current_num = current_num + One::one();
|
||||
Some(old_current_num)
|
||||
});
|
||||
let headers = cht_range.map(|num| self.block_hash(num));
|
||||
let proof = cht::build_proof::<Block::Header, Blake2Hasher, _, _>(cht_size, cht_num, ::std::iter::once(block_num), headers)?;
|
||||
Ok((header, proof))
|
||||
}
|
||||
@@ -486,14 +493,13 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where
|
||||
Some((config, storage)) => (config, storage),
|
||||
None => return Ok(None),
|
||||
};
|
||||
let first = first.saturated_into::<u64>();
|
||||
let last_num = self.backend.blockchain().expect_block_number_from_id(&last)?.saturated_into::<u64>();
|
||||
let last_num = self.backend.blockchain().expect_block_number_from_id(&last)?;
|
||||
if first > last_num {
|
||||
return Err(error::Error::ChangesTrieAccessFailed("Invalid changes trie range".into()));
|
||||
}
|
||||
let finalized_number = self.backend.blockchain().info()?.finalized_number;
|
||||
let oldest = storage.oldest_changes_trie_block(&config, finalized_number.saturated_into::<u64>());
|
||||
let first = ::std::cmp::max(first, oldest).saturated_into::<NumberFor<Block>>();
|
||||
let oldest = storage.oldest_changes_trie_block(&config, finalized_number);
|
||||
let first = ::std::cmp::max(first, oldest);
|
||||
Ok(Some((first, last)))
|
||||
}
|
||||
|
||||
@@ -506,20 +512,20 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where
|
||||
key: &StorageKey
|
||||
) -> error::Result<Vec<(NumberFor<Block>, u32)>> {
|
||||
let (config, storage) = self.require_changes_trie()?;
|
||||
let last_number = self.backend.blockchain().expect_block_number_from_id(&last)?.saturated_into::<u64>();
|
||||
let last_number = self.backend.blockchain().expect_block_number_from_id(&last)?;
|
||||
let last_hash = self.backend.blockchain().expect_block_hash_from_id(&last)?;
|
||||
|
||||
key_changes::<_, Blake2Hasher>(
|
||||
key_changes::<_, Blake2Hasher, _>(
|
||||
&config,
|
||||
&*storage,
|
||||
first.saturated_into::<u64>(),
|
||||
first,
|
||||
&ChangesTrieAnchorBlockId {
|
||||
hash: convert_hash(&last_hash),
|
||||
number: last_number,
|
||||
},
|
||||
self.backend.blockchain().info()?.best_number.saturated_into::<u64>(),
|
||||
self.backend.blockchain().info()?.best_number,
|
||||
&key.0)
|
||||
.and_then(|r| r.map(|r| r.map(|(block, tx)| (block.saturated_into(), tx))).collect::<Result<_, _>>())
|
||||
.and_then(|r| r.map(|r| r.map(|(block, tx)| (block, tx))).collect::<Result<_, _>>())
|
||||
.map_err(|err| error::Error::ChangesTrieAccessFailed(err))
|
||||
}
|
||||
|
||||
@@ -543,7 +549,7 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where
|
||||
min,
|
||||
max,
|
||||
key,
|
||||
cht::SIZE,
|
||||
cht::size(),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -555,21 +561,29 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where
|
||||
min: Block::Hash,
|
||||
max: Block::Hash,
|
||||
key: &StorageKey,
|
||||
cht_size: u64,
|
||||
cht_size: NumberFor<Block>,
|
||||
) -> error::Result<ChangesProof<Block::Header>> {
|
||||
struct AccessedRootsRecorder<'a, Block: BlockT> {
|
||||
storage: &'a ChangesTrieStorage<Blake2Hasher>,
|
||||
min: u64,
|
||||
storage: &'a ChangesTrieStorage<Blake2Hasher, NumberFor<Block>>,
|
||||
min: NumberFor<Block>,
|
||||
required_roots_proofs: Mutex<BTreeMap<NumberFor<Block>, H256>>,
|
||||
};
|
||||
|
||||
impl<'a, Block: BlockT> ChangesTrieRootsStorage<Blake2Hasher> for AccessedRootsRecorder<'a, Block> {
|
||||
fn root(&self, anchor: &ChangesTrieAnchorBlockId<H256>, block: u64) -> Result<Option<H256>, String> {
|
||||
impl<'a, Block: BlockT> ChangesTrieRootsStorage<Blake2Hasher, NumberFor<Block>> for AccessedRootsRecorder<'a, Block> {
|
||||
fn build_anchor(&self, hash: H256) -> Result<ChangesTrieAnchorBlockId<H256, NumberFor<Block>>, String> {
|
||||
self.storage.build_anchor(hash)
|
||||
}
|
||||
|
||||
fn root(
|
||||
&self,
|
||||
anchor: &ChangesTrieAnchorBlockId<H256, NumberFor<Block>>,
|
||||
block: NumberFor<Block>,
|
||||
) -> Result<Option<H256>, String> {
|
||||
let root = self.storage.root(anchor, block)?;
|
||||
if block < self.min {
|
||||
if let Some(ref root) = root {
|
||||
self.required_roots_proofs.lock().insert(
|
||||
block.saturated_into(),
|
||||
block,
|
||||
root.clone()
|
||||
);
|
||||
}
|
||||
@@ -578,7 +592,7 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, Block: BlockT> ChangesTrieStorage<Blake2Hasher> for AccessedRootsRecorder<'a, Block> {
|
||||
impl<'a, Block: BlockT> ChangesTrieStorage<Blake2Hasher, NumberFor<Block>> for AccessedRootsRecorder<'a, Block> {
|
||||
fn get(&self, key: &H256, prefix: &[u8]) -> Result<Option<DBValue>, String> {
|
||||
self.storage.get(key, prefix)
|
||||
}
|
||||
@@ -589,7 +603,7 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where
|
||||
|
||||
let recording_storage = AccessedRootsRecorder::<Block> {
|
||||
storage,
|
||||
min: min_number.saturated_into::<u64>(),
|
||||
min: min_number,
|
||||
required_roots_proofs: Mutex::new(BTreeMap::new()),
|
||||
};
|
||||
|
||||
@@ -600,12 +614,10 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where
|
||||
|
||||
// fetch key changes proof
|
||||
let first_number = self.backend.blockchain()
|
||||
.expect_block_number_from_id(&BlockId::Hash(first))?
|
||||
.saturated_into::<u64>();
|
||||
.expect_block_number_from_id(&BlockId::Hash(first))?;
|
||||
let last_number = self.backend.blockchain()
|
||||
.expect_block_number_from_id(&BlockId::Hash(last))?
|
||||
.saturated_into::<u64>();
|
||||
let key_changes_proof = key_changes_proof::<_, Blake2Hasher>(
|
||||
.expect_block_number_from_id(&BlockId::Hash(last))?;
|
||||
let key_changes_proof = key_changes_proof::<_, Blake2Hasher, _>(
|
||||
&config,
|
||||
&recording_storage,
|
||||
first_number,
|
||||
@@ -613,7 +625,7 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where
|
||||
hash: convert_hash(&last),
|
||||
number: last_number,
|
||||
},
|
||||
max_number.saturated_into::<u64>(),
|
||||
max_number,
|
||||
&key.0
|
||||
)
|
||||
.map_err(|err| error::Error::from(error::Error::ChangesTrieAccessFailed(err)))?;
|
||||
@@ -634,7 +646,7 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where
|
||||
/// Generate CHT-based proof for roots of changes tries at given blocks.
|
||||
fn changes_trie_roots_proof<I: IntoIterator<Item=NumberFor<Block>>>(
|
||||
&self,
|
||||
cht_size: u64,
|
||||
cht_size: NumberFor<Block>,
|
||||
blocks: I
|
||||
) -> error::Result<Vec<Vec<u8>>> {
|
||||
// most probably we have touched several changes tries that are parts of the single CHT
|
||||
@@ -653,12 +665,19 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where
|
||||
/// Generates CHT-based proof for roots of changes tries at given blocks (that are part of single CHT).
|
||||
fn changes_trie_roots_proof_at_cht(
|
||||
&self,
|
||||
cht_size: u64,
|
||||
cht_size: NumberFor<Block>,
|
||||
cht_num: NumberFor<Block>,
|
||||
blocks: Vec<NumberFor<Block>>
|
||||
) -> error::Result<Vec<Vec<u8>>> {
|
||||
let cht_start = cht::start_number(cht_size, cht_num);
|
||||
let roots = (cht_start.saturated_into()..).map(|num| self.header(&BlockId::Number(num.saturated_into()))
|
||||
let mut current_num = cht_start;
|
||||
let cht_range = ::std::iter::from_fn(|| {
|
||||
let old_current_num = current_num;
|
||||
current_num = current_num + One::one();
|
||||
Some(old_current_num)
|
||||
});
|
||||
let roots = cht_range
|
||||
.map(|num| self.header(&BlockId::Number(num))
|
||||
.map(|block| block.and_then(|block| block.digest().log(DigestItem::as_changes_trie_root).cloned())));
|
||||
let proof = cht::build_proof::<Block::Header, Blake2Hasher, _, _>(cht_size, cht_num, blocks, roots)?;
|
||||
Ok(proof)
|
||||
|
||||
@@ -92,8 +92,8 @@ pub enum Error {
|
||||
#[display(fmt = "Potential long-range attack: block not in finalized chain.")]
|
||||
NotInFinalizedChain,
|
||||
/// Hash that is required for building CHT is missing.
|
||||
#[display(fmt = "Failed to get hash of block#{} for building CHT#{}", _0, _1)]
|
||||
MissingHashRequiredForCHT(u64, u64),
|
||||
#[display(fmt = "Failed to get hash of block for building CHT")]
|
||||
MissingHashRequiredForCHT,
|
||||
/// A convenience variant for String
|
||||
#[display(fmt = "{}", _0)]
|
||||
Msg(String),
|
||||
|
||||
@@ -84,7 +84,7 @@ mod tests {
|
||||
|
||||
state_machine::new(
|
||||
backend,
|
||||
Some(&InMemoryChangesTrieStorage::new()),
|
||||
Some(&InMemoryChangesTrieStorage::<_, u64>::new()),
|
||||
state_machine::NeverOffchainExt::new(),
|
||||
&mut overlay,
|
||||
&executor(),
|
||||
@@ -97,7 +97,7 @@ mod tests {
|
||||
for tx in transactions.iter() {
|
||||
state_machine::new(
|
||||
backend,
|
||||
Some(&InMemoryChangesTrieStorage::new()),
|
||||
Some(&InMemoryChangesTrieStorage::<_, u64>::new()),
|
||||
state_machine::NeverOffchainExt::new(),
|
||||
&mut overlay,
|
||||
&executor(),
|
||||
@@ -110,7 +110,7 @@ mod tests {
|
||||
|
||||
let (ret_data, _, _) = state_machine::new(
|
||||
backend,
|
||||
Some(&InMemoryChangesTrieStorage::new()),
|
||||
Some(&InMemoryChangesTrieStorage::<_, u64>::new()),
|
||||
state_machine::NeverOffchainExt::new(),
|
||||
&mut overlay,
|
||||
&executor(),
|
||||
@@ -157,7 +157,7 @@ mod tests {
|
||||
let mut overlay = OverlayedChanges::default();
|
||||
let _ = state_machine::new(
|
||||
&backend,
|
||||
Some(&InMemoryChangesTrieStorage::new()),
|
||||
Some(&InMemoryChangesTrieStorage::<_, u64>::new()),
|
||||
state_machine::NeverOffchainExt::new(),
|
||||
&mut overlay,
|
||||
&executor(),
|
||||
@@ -186,7 +186,7 @@ mod tests {
|
||||
let mut overlay = OverlayedChanges::default();
|
||||
let _ = state_machine::new(
|
||||
&backend,
|
||||
Some(&InMemoryChangesTrieStorage::new()),
|
||||
Some(&InMemoryChangesTrieStorage::<_, u64>::new()),
|
||||
state_machine::NeverOffchainExt::new(),
|
||||
&mut overlay,
|
||||
&executor(),
|
||||
@@ -215,7 +215,7 @@ mod tests {
|
||||
let mut overlay = OverlayedChanges::default();
|
||||
let r = state_machine::new(
|
||||
&backend,
|
||||
Some(&InMemoryChangesTrieStorage::new()),
|
||||
Some(&InMemoryChangesTrieStorage::<_, u64>::new()),
|
||||
state_machine::NeverOffchainExt::new(),
|
||||
&mut overlay,
|
||||
&Executor::new(None),
|
||||
|
||||
@@ -23,7 +23,7 @@ use primitives::{ChangesTrieConfiguration, storage::well_known_keys};
|
||||
use runtime_primitives::generic::BlockId;
|
||||
use runtime_primitives::traits::{
|
||||
Block as BlockT, Header as HeaderT, Zero,
|
||||
NumberFor, SaturatedConversion, Digest, DigestItem
|
||||
NumberFor, Digest, DigestItem
|
||||
};
|
||||
use runtime_primitives::{Justification, StorageOverlay, ChildrenStorageOverlay};
|
||||
use state_machine::backend::{Backend as StateBackend, InMemory};
|
||||
@@ -415,12 +415,20 @@ impl<Block: BlockT> light::blockchain::Storage<Block> for Blockchain<Block>
|
||||
Blockchain::finalize_header(self, id, None)
|
||||
}
|
||||
|
||||
fn header_cht_root(&self, _cht_size: u64, block: NumberFor<Block>) -> error::Result<Block::Hash> {
|
||||
fn header_cht_root(
|
||||
&self,
|
||||
_cht_size: NumberFor<Block>,
|
||||
block: NumberFor<Block>,
|
||||
) -> error::Result<Block::Hash> {
|
||||
self.storage.read().header_cht_roots.get(&block).cloned()
|
||||
.ok_or_else(|| error::Error::Backend(format!("Header CHT for block {} not exists", block)))
|
||||
}
|
||||
|
||||
fn changes_trie_cht_root(&self, _cht_size: u64, block: NumberFor<Block>) -> error::Result<Block::Hash> {
|
||||
fn changes_trie_cht_root(
|
||||
&self,
|
||||
_cht_size: NumberFor<Block>,
|
||||
block: NumberFor<Block>,
|
||||
) -> error::Result<Block::Hash> {
|
||||
self.storage.read().changes_trie_cht_roots.get(&block).cloned()
|
||||
.ok_or_else(|| error::Error::Backend(format!("Changes trie CHT for block {} not exists", block)))
|
||||
}
|
||||
@@ -531,7 +539,7 @@ where
|
||||
H::Out: Ord,
|
||||
{
|
||||
states: RwLock<HashMap<Block::Hash, InMemory<H>>>,
|
||||
changes_trie_storage: ChangesTrieStorage<H>,
|
||||
changes_trie_storage: ChangesTrieStorage<Block, H>,
|
||||
blockchain: Blockchain<Block>,
|
||||
}
|
||||
|
||||
@@ -581,7 +589,7 @@ where
|
||||
type BlockImportOperation = BlockImportOperation<Block, H>;
|
||||
type Blockchain = Blockchain<Block>;
|
||||
type State = InMemory<H>;
|
||||
type ChangesTrieStorage = ChangesTrieStorage<H>;
|
||||
type ChangesTrieStorage = ChangesTrieStorage<Block, H>;
|
||||
|
||||
fn begin_operation(&self) -> error::Result<Self::BlockImportOperation> {
|
||||
let old_state = self.state_at(BlockId::Hash(Default::default()))?;
|
||||
@@ -622,7 +630,7 @@ where
|
||||
if let Some(changes_trie_update) = operation.changes_trie_update {
|
||||
let changes_trie_root: H::Out = changes_trie_root.into();
|
||||
self.changes_trie_storage.0.insert(
|
||||
(*header.number()).saturated_into::<u64>(),
|
||||
*header.number(),
|
||||
changes_trie_root,
|
||||
changes_trie_update
|
||||
);
|
||||
@@ -699,22 +707,45 @@ where
|
||||
}
|
||||
|
||||
/// Prunable in-memory changes trie storage.
|
||||
pub struct ChangesTrieStorage<H: Hasher>(InMemoryChangesTrieStorage<H>);
|
||||
impl<H: Hasher> backend::PrunableStateChangesTrieStorage<H> for ChangesTrieStorage<H> {
|
||||
fn oldest_changes_trie_block(&self, _config: &ChangesTrieConfiguration, _best_finalized: u64) -> u64 {
|
||||
0
|
||||
pub struct ChangesTrieStorage<Block: BlockT, H: Hasher>(InMemoryChangesTrieStorage<H, NumberFor<Block>>);
|
||||
impl<Block: BlockT, H: Hasher> backend::PrunableStateChangesTrieStorage<Block, H> for ChangesTrieStorage<Block, H> {
|
||||
fn oldest_changes_trie_block(
|
||||
&self,
|
||||
_config: &ChangesTrieConfiguration,
|
||||
_best_finalized: NumberFor<Block>,
|
||||
) -> NumberFor<Block> {
|
||||
Zero::zero()
|
||||
}
|
||||
}
|
||||
|
||||
impl<H: Hasher> state_machine::ChangesTrieRootsStorage<H> for ChangesTrieStorage<H> {
|
||||
fn root(&self, anchor: &ChangesTrieAnchorBlockId<H::Out>, block: u64) -> Result<Option<H::Out>, String> {
|
||||
self.0.root(anchor, block)
|
||||
impl<Block, H> state_machine::ChangesTrieRootsStorage<H, NumberFor<Block>> for ChangesTrieStorage<Block, H>
|
||||
where
|
||||
Block: BlockT,
|
||||
H: Hasher,
|
||||
{
|
||||
fn build_anchor(
|
||||
&self,
|
||||
_hash: H::Out,
|
||||
) -> Result<state_machine::ChangesTrieAnchorBlockId<H::Out, NumberFor<Block>>, String> {
|
||||
Err("Dummy implementation".into())
|
||||
}
|
||||
|
||||
fn root(
|
||||
&self,
|
||||
_anchor: &ChangesTrieAnchorBlockId<H::Out, NumberFor<Block>>,
|
||||
_block: NumberFor<Block>,
|
||||
) -> Result<Option<H::Out>, String> {
|
||||
Err("Dummy implementation".into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<H: Hasher> state_machine::ChangesTrieStorage<H> for ChangesTrieStorage<H> {
|
||||
fn get(&self, key: &H::Out, prefix: &[u8]) -> Result<Option<state_machine::DBValue>, String> {
|
||||
self.0.get(key, prefix)
|
||||
impl<Block, H> state_machine::ChangesTrieStorage<H, NumberFor<Block>> for ChangesTrieStorage<Block, H>
|
||||
where
|
||||
Block: BlockT,
|
||||
H: Hasher,
|
||||
{
|
||||
fn get(&self, _key: &H::Out, _prefix: &[u8]) -> Result<Option<state_machine::DBValue>, String> {
|
||||
Err("Dummy implementation".into())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -112,7 +112,7 @@ impl<S, F, Block, H> ClientBackend<Block, H> for Backend<S, F, H> where
|
||||
type BlockImportOperation = ImportOperation<Block, S, F, H>;
|
||||
type Blockchain = Blockchain<S, F>;
|
||||
type State = OnDemandOrGenesisState<Block, S, F, H>;
|
||||
type ChangesTrieStorage = in_mem::ChangesTrieStorage<H>;
|
||||
type ChangesTrieStorage = in_mem::ChangesTrieStorage<Block, H>;
|
||||
|
||||
fn begin_operation(&self) -> ClientResult<Self::BlockImportOperation> {
|
||||
Ok(ImportOperation {
|
||||
|
||||
@@ -56,10 +56,18 @@ pub trait Storage<Block: BlockT>: AuxStore + BlockchainHeaderBackend<Block> {
|
||||
fn last_finalized(&self) -> ClientResult<Block::Hash>;
|
||||
|
||||
/// Get headers CHT root for given block. Fails if the block is not pruned (not a part of any CHT).
|
||||
fn header_cht_root(&self, cht_size: u64, block: NumberFor<Block>) -> ClientResult<Block::Hash>;
|
||||
fn header_cht_root(
|
||||
&self,
|
||||
cht_size: NumberFor<Block>,
|
||||
block: NumberFor<Block>,
|
||||
) -> ClientResult<Block::Hash>;
|
||||
|
||||
/// Get changes trie CHT root for given block. Fails if the block is not pruned (not a part of any CHT).
|
||||
fn changes_trie_cht_root(&self, cht_size: u64, block: NumberFor<Block>) -> ClientResult<Block::Hash>;
|
||||
fn changes_trie_cht_root(
|
||||
&self,
|
||||
cht_size: NumberFor<Block>,
|
||||
block: NumberFor<Block>,
|
||||
) -> ClientResult<Block::Hash>;
|
||||
|
||||
/// Get storage cache.
|
||||
fn cache(&self) -> Option<Arc<BlockchainCache<Block>>>;
|
||||
@@ -116,7 +124,7 @@ impl<S, F, Block> BlockchainHeaderBackend<Block> for Blockchain<S, F> where Bloc
|
||||
|
||||
self.fetcher().upgrade().ok_or(ClientError::NotAvailableOnLightClient)?
|
||||
.remote_header(RemoteHeaderRequest {
|
||||
cht_root: self.storage.header_cht_root(cht::SIZE, number)?,
|
||||
cht_root: self.storage.header_cht_root(cht::size(), number)?,
|
||||
block: number,
|
||||
retry_count: None,
|
||||
})
|
||||
|
||||
@@ -435,7 +435,7 @@ pub fn check_execution_proof<Header, E, H>(
|
||||
Header: HeaderT,
|
||||
E: CodeExecutor<H>,
|
||||
H: Hasher,
|
||||
H::Out: Ord,
|
||||
H::Out: Ord + 'static,
|
||||
{
|
||||
let local_state_root = request.header.state_root();
|
||||
let root: H::Out = convert_hash(&local_state_root);
|
||||
|
||||
@@ -22,11 +22,11 @@ use std::marker::PhantomData;
|
||||
use futures::IntoFuture;
|
||||
|
||||
use hash_db::{HashDB, Hasher};
|
||||
use parity_codec::Encode;
|
||||
use parity_codec::{Decode, Encode};
|
||||
use primitives::{ChangesTrieConfiguration, convert_hash};
|
||||
use runtime_primitives::traits::{
|
||||
Block as BlockT, Header as HeaderT, Hash, HashFor, NumberFor,
|
||||
UniqueSaturatedInto, UniqueSaturatedFrom, SaturatedConversion
|
||||
SimpleArithmetic, CheckedConversion,
|
||||
};
|
||||
use state_machine::{CodeExecutor, ChangesTrieRootsStorage, ChangesTrieAnchorBlockId,
|
||||
TrieBackend, read_proof_check, key_changes_proof_check,
|
||||
@@ -236,7 +236,7 @@ impl<E, H, B: BlockT, S: BlockchainStorage<B>, F> LightDataChecker<E, H, B, S, F
|
||||
&self,
|
||||
request: &RemoteChangesRequest<B::Header>,
|
||||
remote_proof: ChangesProof<B::Header>,
|
||||
cht_size: u64,
|
||||
cht_size: NumberFor<B>,
|
||||
) -> ClientResult<Vec<(NumberFor<B>, u32)>>
|
||||
where
|
||||
H: Hasher,
|
||||
@@ -284,30 +284,27 @@ impl<E, H, B: BlockT, S: BlockchainStorage<B>, F> LightDataChecker<E, H, B, S, F
|
||||
}
|
||||
|
||||
// and now check the key changes proof + get the changes
|
||||
key_changes_proof_check::<_, H>(
|
||||
key_changes_proof_check::<_, H, _>(
|
||||
&request.changes_trie_config,
|
||||
&RootsStorage {
|
||||
roots: (request.tries_roots.0, &request.tries_roots.2),
|
||||
prev_roots: remote_roots,
|
||||
},
|
||||
remote_proof,
|
||||
request.first_block.0.saturated_into::<u64>(),
|
||||
request.first_block.0,
|
||||
&ChangesTrieAnchorBlockId {
|
||||
hash: convert_hash(&request.last_block.1),
|
||||
number: request.last_block.0.saturated_into::<u64>(),
|
||||
number: request.last_block.0,
|
||||
},
|
||||
remote_max_block.saturated_into::<u64>(),
|
||||
remote_max_block,
|
||||
&request.key)
|
||||
.map(|pairs| pairs.into_iter().map(|(b, x)|
|
||||
(b.saturated_into::<NumberFor<B>>(), x)
|
||||
).collect())
|
||||
.map_err(|err| ClientError::ChangesTrieAccessFailed(err))
|
||||
}
|
||||
|
||||
/// Check CHT-based proof for changes tries roots.
|
||||
fn check_changes_tries_proof(
|
||||
&self,
|
||||
cht_size: u64,
|
||||
cht_size: NumberFor<B>,
|
||||
remote_roots: &BTreeMap<NumberFor<B>, B::Hash>,
|
||||
remote_roots_proof: Vec<Vec<u8>>,
|
||||
) -> ClientResult<()>
|
||||
@@ -363,7 +360,7 @@ impl<E, Block, H, S, F> FetchChecker<Block> for LightDataChecker<E, H, Block, S,
|
||||
Block: BlockT,
|
||||
E: CodeExecutor<H>,
|
||||
H: Hasher,
|
||||
H::Out: Ord,
|
||||
H::Out: Ord + 'static,
|
||||
S: BlockchainStorage<Block>,
|
||||
F: Send + Sync,
|
||||
{
|
||||
@@ -419,7 +416,7 @@ impl<E, Block, H, S, F> FetchChecker<Block> for LightDataChecker<E, H, Block, S,
|
||||
request: &RemoteChangesRequest<Block::Header>,
|
||||
remote_proof: ChangesProof<Block::Header>
|
||||
) -> ClientResult<Vec<(NumberFor<Block>, u32)>> {
|
||||
self.check_changes_proof_with_cht_size(request, remote_proof, cht::SIZE)
|
||||
self.check_changes_proof_with_cht_size(request, remote_proof, cht::size())
|
||||
}
|
||||
|
||||
fn check_body_proof(
|
||||
@@ -443,26 +440,38 @@ impl<E, Block, H, S, F> FetchChecker<Block> for LightDataChecker<E, H, Block, S,
|
||||
}
|
||||
|
||||
/// A view of BTreeMap<Number, Hash> as a changes trie roots storage.
|
||||
struct RootsStorage<'a, Number: UniqueSaturatedInto<u64> + UniqueSaturatedFrom<u64>, Hash: 'a> {
|
||||
struct RootsStorage<'a, Number: SimpleArithmetic, Hash: 'a> {
|
||||
roots: (Number, &'a [Hash]),
|
||||
prev_roots: BTreeMap<Number, Hash>,
|
||||
}
|
||||
|
||||
impl<'a, H, Number, Hash> ChangesTrieRootsStorage<H> for RootsStorage<'a, Number, Hash>
|
||||
impl<'a, H, Number, Hash> ChangesTrieRootsStorage<H, Number> for RootsStorage<'a, Number, Hash>
|
||||
where
|
||||
H: Hasher,
|
||||
Number: Send + Sync + Eq + ::std::cmp::Ord + Copy + UniqueSaturatedInto<u64>
|
||||
+ UniqueSaturatedFrom<u64>,
|
||||
Number: ::std::fmt::Display + Clone + SimpleArithmetic + Encode + Decode + Send + Sync + 'static,
|
||||
Hash: 'a + Send + Sync + Clone + AsRef<[u8]>,
|
||||
{
|
||||
fn root(&self, _anchor: &ChangesTrieAnchorBlockId<H::Out>, block: u64) -> Result<Option<H::Out>, String> {
|
||||
fn build_anchor(
|
||||
&self,
|
||||
_hash: H::Out,
|
||||
) -> Result<state_machine::ChangesTrieAnchorBlockId<H::Out, Number>, String> {
|
||||
Err("build_anchor is only called when building block".into())
|
||||
}
|
||||
|
||||
fn root(
|
||||
&self,
|
||||
_anchor: &ChangesTrieAnchorBlockId<H::Out, Number>,
|
||||
block: Number,
|
||||
) -> Result<Option<H::Out>, String> {
|
||||
// we can't ask for roots from parallel forks here => ignore anchor
|
||||
let root = if block < self.roots.0.saturated_into::<u64>() {
|
||||
let root = if block < self.roots.0 {
|
||||
self.prev_roots.get(&Number::unique_saturated_from(block)).cloned()
|
||||
} else {
|
||||
block.checked_sub(self.roots.0.saturated_into::<u64>())
|
||||
.and_then(|index| self.roots.1.get(index as usize))
|
||||
.cloned()
|
||||
let index: Option<usize> = block.checked_sub(&self.roots.0).and_then(|index| index.checked_into());
|
||||
match index {
|
||||
Some(index) => self.roots.1.get(index as usize).cloned(),
|
||||
None => None,
|
||||
}
|
||||
};
|
||||
|
||||
Ok(root.map(|root| {
|
||||
|
||||
Reference in New Issue
Block a user