mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-07-01 05:27:23 +00:00
Fetching changes proof from remote nodes (#769)
* changes_trie * changs_trie: continue * changes_trie: adding tests * fixed TODO * removed obsolete ExtrinsicChanges * encodable ChangesTrieConfiguration * removed polkadot fle * fixed grumbles * ext_storage_changes_root returns u32 * moved changes trie root to digest * removed commented code * read storage values from native code * fixed grumbles * fixed grumbles * missing comma * key changes proof generation + query * fix grumbles * check that changes trie config is not changed by block.finalize() * fixed changes trie config check
This commit is contained in:
committed by
Gav Wood
parent
fdfd4672c1
commit
c54350661d
@@ -23,7 +23,7 @@ use codec::{Decode, Encode};
|
||||
use hash_db::{HashDB, Hasher};
|
||||
use heapsize::HeapSizeOf;
|
||||
use substrate_trie::{Recorder, MemoryDB};
|
||||
use changes_trie::{Configuration, Storage};
|
||||
use changes_trie::{Configuration, RootsStorage, Storage};
|
||||
use changes_trie::input::{DigestIndex, ExtrinsicIndex, DigestIndexValue, ExtrinsicIndexValue};
|
||||
use changes_trie::storage::{TrieBackendAdapter, InMemoryStorage};
|
||||
use proving_backend::ProvingBackendEssence;
|
||||
@@ -44,6 +44,8 @@ pub fn key_changes<S: Storage<H>, H: Hasher>(
|
||||
key,
|
||||
roots_storage: storage,
|
||||
storage,
|
||||
begin,
|
||||
end,
|
||||
surface: surface_iterator(config, max, begin, end)?,
|
||||
|
||||
extrinsics: Default::default(),
|
||||
@@ -69,6 +71,8 @@ pub fn key_changes_proof<S: Storage<H>, H: Hasher>(
|
||||
key,
|
||||
roots_storage: storage.clone(),
|
||||
storage,
|
||||
begin,
|
||||
end,
|
||||
surface: surface_iterator(config, max, begin, end)?,
|
||||
|
||||
extrinsics: Default::default(),
|
||||
@@ -89,9 +93,9 @@ pub fn key_changes_proof<S: Storage<H>, H: Hasher>(
|
||||
|
||||
/// Check key changes proog and return changes of the key at given blocks range.
|
||||
/// `max` is the number of best known block.
|
||||
pub fn key_changes_proof_check<S: Storage<H>, H: Hasher>(
|
||||
pub fn key_changes_proof_check<S: RootsStorage<H>, H: Hasher>(
|
||||
config: &Configuration,
|
||||
roots_storage: &S, // TODO: use RootsStorage is only used to gather root
|
||||
roots_storage: &S,
|
||||
proof: Vec<Vec<u8>>,
|
||||
begin: u64,
|
||||
end: u64,
|
||||
@@ -109,6 +113,8 @@ pub fn key_changes_proof_check<S: Storage<H>, H: Hasher>(
|
||||
key,
|
||||
roots_storage,
|
||||
storage: &proof_db,
|
||||
begin,
|
||||
end,
|
||||
surface: surface_iterator(config, max, begin, end)?,
|
||||
|
||||
extrinsics: Default::default(),
|
||||
@@ -168,10 +174,12 @@ impl<'a> Iterator for SurfaceIterator<'a> {
|
||||
|
||||
/// Drilldown iterator - receives 'digest points' from surface iterator and explores
|
||||
/// every point until extrinsic is found.
|
||||
pub struct DrilldownIteratorEssence<'a, RS: 'a + Storage<H>, S: 'a + Storage<H>, H: Hasher> {
|
||||
pub struct DrilldownIteratorEssence<'a, RS: 'a + RootsStorage<H>, S: 'a + Storage<H>, H: Hasher> {
|
||||
key: &'a [u8],
|
||||
roots_storage: &'a RS,
|
||||
storage: &'a S,
|
||||
begin: u64,
|
||||
end: u64,
|
||||
surface: SurfaceIterator<'a>,
|
||||
|
||||
extrinsics: VecDeque<(u64, u32)>,
|
||||
@@ -180,7 +188,7 @@ pub struct DrilldownIteratorEssence<'a, RS: 'a + Storage<H>, S: 'a + Storage<H>,
|
||||
_hasher: ::std::marker::PhantomData<H>,
|
||||
}
|
||||
|
||||
impl<'a, RS: 'a + Storage<H>, S: Storage<H>, H: Hasher> DrilldownIteratorEssence<'a, RS, S, H> {
|
||||
impl<'a, RS: 'a + RootsStorage<H>, S: Storage<H>, H: Hasher> DrilldownIteratorEssence<'a, RS, S, H> {
|
||||
pub fn next<F>(&mut self, trie_reader: F) -> Option<Result<(u64, u32), String>>
|
||||
where
|
||||
F: FnMut(&S, H::Out, &[u8]) -> Result<Option<Vec<u8>>, String>,
|
||||
@@ -202,7 +210,17 @@ impl<'a, RS: 'a + Storage<H>, S: Storage<H>, H: Hasher> DrilldownIteratorEssence
|
||||
}
|
||||
|
||||
if let Some((block, level)) = self.blocks.pop_front() {
|
||||
if let Some(trie_root) = self.roots_storage.root(block)? {
|
||||
// not having a changes trie root is an error because:
|
||||
// we never query roots for future blocks
|
||||
// AND trie roots for old blocks are known (both on full + light node)
|
||||
let trie_root = self.roots_storage.root(block)?
|
||||
.ok_or_else(|| format!("Changes trie root for block {} is not found", block))?;
|
||||
|
||||
// only return extrinsics for blocks before self.max
|
||||
// most of blocks will be filtered out beore pushing to `self.blocks`
|
||||
// here we just throwing away changes at digest blocks we're processing
|
||||
debug_assert!(block >= self.begin, "We shall not touch digests earlier than a range' begin");
|
||||
if block <= self.end {
|
||||
let extrinsics_key = ExtrinsicIndex { block, key: self.key.to_vec() }.encode();
|
||||
let extrinsics = trie_reader(&self.storage, trie_root, &extrinsics_key);
|
||||
if let Some(extrinsics) = extrinsics? {
|
||||
@@ -211,14 +229,22 @@ impl<'a, RS: 'a + Storage<H>, S: Storage<H>, H: Hasher> DrilldownIteratorEssence
|
||||
self.extrinsics.extend(extrinsics.into_iter().rev().map(|e| (block, e)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let blocks_key = DigestIndex { block, key: self.key.to_vec() }.encode();
|
||||
let blocks = trie_reader(&self.storage, trie_root, &blocks_key);
|
||||
if let Some(blocks) = blocks? {
|
||||
let blocks: Option<DigestIndexValue> = Decode::decode(&mut &blocks[..]);
|
||||
if let Some(blocks) = blocks {
|
||||
self.blocks.extend(blocks.into_iter().rev().map(|b| (b, level - 1)));
|
||||
}
|
||||
let blocks_key = DigestIndex { block, key: self.key.to_vec() }.encode();
|
||||
let blocks = trie_reader(&self.storage, trie_root, &blocks_key);
|
||||
if let Some(blocks) = blocks? {
|
||||
let blocks: Option<DigestIndexValue> = Decode::decode(&mut &blocks[..]);
|
||||
if let Some(blocks) = blocks {
|
||||
// filter level0 blocks here because we tend to use digest blocks,
|
||||
// AND digest block changes could also include changes for out-of-range blocks
|
||||
let begin = self.begin;
|
||||
let end = self.end;
|
||||
self.blocks.extend(blocks.into_iter()
|
||||
.rev()
|
||||
.filter(|b| level > 1 || (*b >= begin && *b <= end))
|
||||
.map(|b| (b, level - 1))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -235,11 +261,11 @@ impl<'a, RS: 'a + Storage<H>, S: Storage<H>, H: Hasher> DrilldownIteratorEssence
|
||||
}
|
||||
|
||||
/// Exploring drilldown operator.
|
||||
struct DrilldownIterator<'a, RS: 'a + Storage<H>, S: 'a + Storage<H>, H: Hasher> {
|
||||
struct DrilldownIterator<'a, RS: 'a + RootsStorage<H>, S: 'a + Storage<H>, H: Hasher> {
|
||||
essence: DrilldownIteratorEssence<'a, RS, S, H>,
|
||||
}
|
||||
|
||||
impl<'a, RS: 'a + Storage<H>, S: Storage<H>, H: Hasher> Iterator
|
||||
impl<'a, RS: 'a + RootsStorage<H>, S: Storage<H>, H: Hasher> Iterator
|
||||
for DrilldownIterator<'a, RS, S, H>
|
||||
where H::Out: HeapSizeOf
|
||||
{
|
||||
@@ -252,12 +278,12 @@ impl<'a, RS: 'a + Storage<H>, S: Storage<H>, H: Hasher> Iterator
|
||||
}
|
||||
|
||||
/// Proving drilldown iterator.
|
||||
struct ProvingDrilldownIterator<'a, RS: 'a + Storage<H>, S: 'a + Storage<H>, H: Hasher> {
|
||||
struct ProvingDrilldownIterator<'a, RS: 'a + RootsStorage<H>, S: 'a + Storage<H>, H: Hasher> {
|
||||
essence: DrilldownIteratorEssence<'a, RS, S, H>,
|
||||
proof_recorder: RefCell<Recorder<H::Out>>,
|
||||
}
|
||||
|
||||
impl<'a, RS: 'a + Storage<H>, S: Storage<H>, H: Hasher> ProvingDrilldownIterator<'a, RS, S, H> {
|
||||
impl<'a, RS: 'a + RootsStorage<H>, S: Storage<H>, H: Hasher> ProvingDrilldownIterator<'a, RS, S, H> {
|
||||
/// Consume the iterator, extracting the gathered proof in lexicographical order
|
||||
/// by value.
|
||||
pub fn extract_proof(self) -> Vec<Vec<u8>> {
|
||||
@@ -268,7 +294,7 @@ impl<'a, RS: 'a + Storage<H>, S: Storage<H>, H: Hasher> ProvingDrilldownIterator
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, RS: 'a + Storage<H>, S: Storage<H>, H: Hasher> Iterator for ProvingDrilldownIterator<'a, RS, S, H> where H::Out: HeapSizeOf {
|
||||
impl<'a, RS: 'a + RootsStorage<H>, S: Storage<H>, H: Hasher> Iterator for ProvingDrilldownIterator<'a, RS, S, H> where H::Out: HeapSizeOf {
|
||||
type Item = Result<(u64, u32), String>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
@@ -401,9 +427,24 @@ mod tests {
|
||||
fn drilldown_iterator_works() {
|
||||
let (config, storage) = prepare_for_drilldown();
|
||||
let drilldown_result = key_changes::<InMemoryStorage<Blake2Hasher>, Blake2Hasher>(
|
||||
&config, &storage, 0, 100, 1000, &[42]);
|
||||
|
||||
&config, &storage, 0, 16, 16, &[42]);
|
||||
assert_eq!(drilldown_result, Ok(vec![(8, 2), (8, 1), (6, 3), (3, 0)]));
|
||||
|
||||
let drilldown_result = key_changes::<InMemoryStorage<Blake2Hasher>, Blake2Hasher>(
|
||||
&config, &storage, 0, 2, 4, &[42]);
|
||||
assert_eq!(drilldown_result, Ok(vec![]));
|
||||
|
||||
let drilldown_result = key_changes::<InMemoryStorage<Blake2Hasher>, Blake2Hasher>(
|
||||
&config, &storage, 0, 3, 4, &[42]);
|
||||
assert_eq!(drilldown_result, Ok(vec![(3, 0)]));
|
||||
|
||||
let drilldown_result = key_changes::<InMemoryStorage<Blake2Hasher>, Blake2Hasher>(
|
||||
&config, &storage, 7, 8, 8, &[42]);
|
||||
assert_eq!(drilldown_result, Ok(vec![(8, 2), (8, 1)]));
|
||||
|
||||
let drilldown_result = key_changes::<InMemoryStorage<Blake2Hasher>, Blake2Hasher>(
|
||||
&config, &storage, 5, 7, 8, &[42]);
|
||||
assert_eq!(drilldown_result, Ok(vec![(6, 3)]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -433,7 +474,7 @@ mod tests {
|
||||
let (remote_config, remote_storage) = prepare_for_drilldown();
|
||||
let remote_proof = key_changes_proof::<InMemoryStorage<Blake2Hasher>, Blake2Hasher>(
|
||||
&remote_config, &remote_storage,
|
||||
0, 100, 1000, &[42]).unwrap();
|
||||
0, 16, 16, &[42]).unwrap();
|
||||
|
||||
// happens on local light node:
|
||||
|
||||
@@ -442,7 +483,7 @@ mod tests {
|
||||
local_storage.clear_storage();
|
||||
let local_result = key_changes_proof_check::<InMemoryStorage<Blake2Hasher>, Blake2Hasher>(
|
||||
&local_config, &local_storage, remote_proof,
|
||||
0, 100, 1000, &[42]);
|
||||
0, 16, 16, &[42]);
|
||||
|
||||
// check that drilldown result is the same as if it was happening at the full node
|
||||
assert_eq!(local_result, Ok(vec![(8, 2), (8, 1), (6, 3), (3, 0)]));
|
||||
|
||||
@@ -54,10 +54,13 @@ use trie::{DBValue, trie_root};
|
||||
pub const NO_EXTRINSIC_INDEX: u32 = 0xffffffff;
|
||||
|
||||
/// Changes trie storage. Provides access to trie roots and trie nodes.
|
||||
pub trait Storage<H: Hasher>: Send + Sync {
|
||||
pub trait RootsStorage<H: Hasher>: Send + Sync {
|
||||
/// Get changes trie root for given block.
|
||||
fn root(&self, block: u64) -> Result<Option<H::Out>, String>;
|
||||
}
|
||||
|
||||
/// Changes trie storage. Provides access to trie roots and trie nodes.
|
||||
pub trait Storage<H: Hasher>: RootsStorage<H> {
|
||||
/// Get a trie node.
|
||||
fn get(&self, key: &H::Out) -> Result<Option<DBValue>, String>;
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ use trie::DBValue;
|
||||
use heapsize::HeapSizeOf;
|
||||
use trie::MemoryDB;
|
||||
use parking_lot::RwLock;
|
||||
use changes_trie::Storage;
|
||||
use changes_trie::{RootsStorage, Storage};
|
||||
use trie_backend_essence::TrieBackendStorage;
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -94,11 +94,13 @@ impl<H: Hasher> InMemoryStorage<H> where H::Out: HeapSizeOf {
|
||||
}
|
||||
}
|
||||
|
||||
impl<H: Hasher> Storage<H> for InMemoryStorage<H> where H::Out: HeapSizeOf {
|
||||
impl<H: Hasher> RootsStorage<H> for InMemoryStorage<H> where H::Out: HeapSizeOf {
|
||||
fn root(&self, block: u64) -> Result<Option<H::Out>, String> {
|
||||
Ok(self.data.read().roots.get(&block).cloned())
|
||||
}
|
||||
}
|
||||
|
||||
impl<H: Hasher> Storage<H> for InMemoryStorage<H> where H::Out: HeapSizeOf {
|
||||
fn get(&self, key: &H::Out) -> Result<Option<DBValue>, String> {
|
||||
MemoryDB::<H>::get(&self.data.read().mdb, key)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user