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:
Svyatoslav Nikolsky
2019-05-28 16:07:16 +03:00
committed by Gavin Wood
parent 25b88f1a1f
commit 549d9e1da1
41 changed files with 1087 additions and 654 deletions
+9 -3
View File
@@ -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.
+95 -80
View File
@@ -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(())
}, ()
);
}
}
+50 -31
View File
@@ -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)
+2 -2
View File
@@ -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),
+6 -6
View File
@@ -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),
+47 -16
View File
@@ -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())
}
}
+1 -1
View File
@@ -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 {
+11 -3
View File
@@ -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);
+31 -22
View File
@@ -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| {