mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-12 20:31:13 +00:00
Changes tries: support forks (#950)
* forks support in changes trie storage * moved convert_hash to primitives * optimize ChangesTrieRootsStorage::root when anchor is canonicalized
This commit is contained in:
committed by
Gav Wood
parent
037f9dde10
commit
6e3c56c135
@@ -25,7 +25,7 @@ use overlayed_changes::OverlayedChanges;
|
||||
use trie_backend_essence::{TrieBackendStorage, TrieBackendEssence};
|
||||
use changes_trie::build_iterator::digest_build_iterator;
|
||||
use changes_trie::input::{InputKey, InputPair, DigestIndex, ExtrinsicIndex};
|
||||
use changes_trie::{Configuration, Storage};
|
||||
use changes_trie::{AnchorBlockId, Configuration, Storage};
|
||||
|
||||
/// Prepare input pairs for building a changes trie of given block.
|
||||
///
|
||||
@@ -37,7 +37,7 @@ pub fn prepare_input<'a, B, S, H>(
|
||||
backend: &B,
|
||||
storage: Option<&'a S>,
|
||||
changes: &OverlayedChanges,
|
||||
block: u64,
|
||||
parent: &'a AnchorBlockId<H::Out>,
|
||||
) -> Result<Option<Vec<InputPair>>, String>
|
||||
where
|
||||
B: Backend<H>,
|
||||
@@ -54,10 +54,10 @@ pub fn prepare_input<'a, B, S, H>(
|
||||
let mut input = Vec::new();
|
||||
input.extend(prepare_extrinsics_input(
|
||||
backend,
|
||||
block,
|
||||
parent.number + 1,
|
||||
changes)?);
|
||||
input.extend(prepare_digest_input::<_, H>(
|
||||
block,
|
||||
parent,
|
||||
config,
|
||||
storage)?);
|
||||
|
||||
@@ -73,7 +73,6 @@ fn prepare_extrinsics_input<B, H>(
|
||||
where
|
||||
B: Backend<H>,
|
||||
H: Hasher,
|
||||
|
||||
{
|
||||
let mut extrinsic_map = BTreeMap::<Vec<u8>, BTreeSet<u32>>::new();
|
||||
for (key, val) in changes.prospective.top.iter().chain(changes.committed.top.iter()) {
|
||||
@@ -103,19 +102,19 @@ fn prepare_extrinsics_input<B, H>(
|
||||
|
||||
/// Prepare DigestIndex input pairs.
|
||||
fn prepare_digest_input<'a, S, H>(
|
||||
block: u64,
|
||||
parent: &'a AnchorBlockId<H::Out>,
|
||||
config: &Configuration,
|
||||
storage: &'a S
|
||||
) -> Result<impl Iterator<Item=InputPair>, String>
|
||||
) -> Result<impl Iterator<Item=InputPair> + 'a, String>
|
||||
where
|
||||
S: Storage<H>,
|
||||
&'a S: TrieBackendStorage<H>,
|
||||
H: Hasher,
|
||||
H::Out: HeapSizeOf,
|
||||
H::Out: 'a + HeapSizeOf,
|
||||
{
|
||||
let mut digest_map = BTreeMap::<Vec<u8>, BTreeSet<u64>>::new();
|
||||
for digest_build_block in digest_build_iterator(config, block) {
|
||||
let trie_root = storage.root(digest_build_block)?;
|
||||
for digest_build_block in digest_build_iterator(config, parent.number + 1) {
|
||||
let trie_root = storage.root(parent, digest_build_block)?;
|
||||
let trie_root = trie_root.ok_or_else(|| format!("No changes trie root for block {}", digest_build_block))?;
|
||||
let trie_storage = TrieBackendEssence::<_, H>::new(storage, trie_root);
|
||||
|
||||
@@ -136,7 +135,7 @@ fn prepare_digest_input<'a, S, H>(
|
||||
|
||||
Ok(digest_map.into_iter()
|
||||
.map(move |(key, set)| InputPair::DigestIndex(DigestIndex {
|
||||
block,
|
||||
block: parent.number + 1,
|
||||
key
|
||||
}, set.into_iter().collect())))
|
||||
}
|
||||
@@ -228,7 +227,7 @@ mod test {
|
||||
#[test]
|
||||
fn build_changes_trie_nodes_on_non_digest_block() {
|
||||
let (backend, storage, changes) = prepare_for_build();
|
||||
let changes_trie_nodes = prepare_input(&backend, Some(&storage), &changes, 5).unwrap();
|
||||
let changes_trie_nodes = prepare_input(&backend, Some(&storage), &changes, &AnchorBlockId { hash: Default::default(), number: 4 }).unwrap();
|
||||
assert_eq!(changes_trie_nodes, Some(vec![
|
||||
InputPair::ExtrinsicIndex(ExtrinsicIndex { block: 5, key: vec![100] }, vec![0, 2, 3]),
|
||||
InputPair::ExtrinsicIndex(ExtrinsicIndex { block: 5, key: vec![101] }, vec![1]),
|
||||
@@ -239,7 +238,7 @@ mod test {
|
||||
#[test]
|
||||
fn build_changes_trie_nodes_on_digest_block_l1() {
|
||||
let (backend, storage, changes) = prepare_for_build();
|
||||
let changes_trie_nodes = prepare_input(&backend, Some(&storage), &changes, 4).unwrap();
|
||||
let changes_trie_nodes = prepare_input(&backend, Some(&storage), &changes, &AnchorBlockId { hash: Default::default(), number: 3 }).unwrap();
|
||||
assert_eq!(changes_trie_nodes, Some(vec![
|
||||
InputPair::ExtrinsicIndex(ExtrinsicIndex { block: 4, key: vec![100] }, vec![0, 2, 3]),
|
||||
InputPair::ExtrinsicIndex(ExtrinsicIndex { block: 4, key: vec![101] }, vec![1]),
|
||||
@@ -255,7 +254,7 @@ mod test {
|
||||
#[test]
|
||||
fn build_changes_trie_nodes_on_digest_block_l2() {
|
||||
let (backend, storage, changes) = prepare_for_build();
|
||||
let changes_trie_nodes = prepare_input(&backend, Some(&storage), &changes, 16).unwrap();
|
||||
let changes_trie_nodes = prepare_input(&backend, Some(&storage), &changes, &AnchorBlockId { hash: Default::default(), number: 15 }).unwrap();
|
||||
assert_eq!(changes_trie_nodes, Some(vec![
|
||||
InputPair::ExtrinsicIndex(ExtrinsicIndex { block: 16, key: vec![100] }, vec![0, 2, 3]),
|
||||
InputPair::ExtrinsicIndex(ExtrinsicIndex { block: 16, key: vec![101] }, vec![1]),
|
||||
@@ -279,7 +278,7 @@ mod test {
|
||||
extrinsics: Some(vec![1].into_iter().collect())
|
||||
});
|
||||
|
||||
let changes_trie_nodes = prepare_input(&backend, Some(&storage), &changes, 4).unwrap();
|
||||
let changes_trie_nodes = prepare_input(&backend, Some(&storage), &changes, &AnchorBlockId { hash: Default::default(), number: 3 }).unwrap();
|
||||
assert_eq!(changes_trie_nodes, Some(vec![
|
||||
InputPair::ExtrinsicIndex(ExtrinsicIndex { block: 4, key: vec![100] }, vec![0, 2, 3]),
|
||||
InputPair::ExtrinsicIndex(ExtrinsicIndex { block: 4, key: vec![101] }, vec![1]),
|
||||
|
||||
@@ -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, RootsStorage, Storage};
|
||||
use changes_trie::{AnchorBlockId, Configuration, RootsStorage, Storage};
|
||||
use changes_trie::input::{DigestIndex, ExtrinsicIndex, DigestIndexValue, ExtrinsicIndexValue};
|
||||
use changes_trie::storage::{TrieBackendAdapter, InMemoryStorage};
|
||||
use proving_backend::ProvingBackendEssence;
|
||||
@@ -35,7 +35,7 @@ pub fn key_changes<S: Storage<H>, H: Hasher>(
|
||||
config: &Configuration,
|
||||
storage: &S,
|
||||
begin: u64,
|
||||
end: u64,
|
||||
end: &AnchorBlockId<H::Out>,
|
||||
max: u64,
|
||||
key: &[u8],
|
||||
) -> Result<Vec<(u64, u32)>, String> where H::Out: HeapSizeOf {
|
||||
@@ -46,7 +46,7 @@ pub fn key_changes<S: Storage<H>, H: Hasher>(
|
||||
storage,
|
||||
begin,
|
||||
end,
|
||||
surface: surface_iterator(config, max, begin, end)?,
|
||||
surface: surface_iterator(config, max, begin, end.number)?,
|
||||
|
||||
extrinsics: Default::default(),
|
||||
blocks: Default::default(),
|
||||
@@ -62,7 +62,7 @@ pub fn key_changes_proof<S: Storage<H>, H: Hasher>(
|
||||
config: &Configuration,
|
||||
storage: &S,
|
||||
begin: u64,
|
||||
end: u64,
|
||||
end: &AnchorBlockId<H::Out>,
|
||||
max: u64,
|
||||
key: &[u8],
|
||||
) -> Result<Vec<Vec<u8>>, String> where H::Out: HeapSizeOf {
|
||||
@@ -73,7 +73,7 @@ pub fn key_changes_proof<S: Storage<H>, H: Hasher>(
|
||||
storage,
|
||||
begin,
|
||||
end,
|
||||
surface: surface_iterator(config, max, begin, end)?,
|
||||
surface: surface_iterator(config, max, begin, end.number)?,
|
||||
|
||||
extrinsics: Default::default(),
|
||||
blocks: Default::default(),
|
||||
@@ -98,7 +98,7 @@ pub fn key_changes_proof_check<S: RootsStorage<H>, H: Hasher>(
|
||||
roots_storage: &S,
|
||||
proof: Vec<Vec<u8>>,
|
||||
begin: u64,
|
||||
end: u64,
|
||||
end: &AnchorBlockId<H::Out>,
|
||||
max: u64,
|
||||
key: &[u8]
|
||||
) -> Result<Vec<(u64, u32)>, String> where H::Out: HeapSizeOf {
|
||||
@@ -115,7 +115,7 @@ pub fn key_changes_proof_check<S: RootsStorage<H>, H: Hasher>(
|
||||
storage: &proof_db,
|
||||
begin,
|
||||
end,
|
||||
surface: surface_iterator(config, max, begin, end)?,
|
||||
surface: surface_iterator(config, max, begin, end.number)?,
|
||||
|
||||
extrinsics: Default::default(),
|
||||
blocks: Default::default(),
|
||||
@@ -174,12 +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 + RootsStorage<H>, S: 'a + Storage<H>, H: Hasher> {
|
||||
pub struct DrilldownIteratorEssence<'a, RS: 'a + RootsStorage<H>, S: 'a + Storage<H>, H: Hasher> where H::Out: 'a {
|
||||
key: &'a [u8],
|
||||
roots_storage: &'a RS,
|
||||
storage: &'a S,
|
||||
begin: u64,
|
||||
end: u64,
|
||||
end: &'a AnchorBlockId<H::Out>,
|
||||
surface: SurfaceIterator<'a>,
|
||||
|
||||
extrinsics: VecDeque<(u64, u32)>,
|
||||
@@ -213,14 +213,14 @@ impl<'a, RS: 'a + RootsStorage<H>, S: Storage<H>, H: Hasher> DrilldownIteratorEs
|
||||
// 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)?
|
||||
let trie_root = self.roots_storage.root(&self.end, 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 {
|
||||
if block <= self.end.number {
|
||||
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? {
|
||||
@@ -239,7 +239,7 @@ impl<'a, RS: 'a + RootsStorage<H>, S: Storage<H>, H: Hasher> DrilldownIteratorEs
|
||||
// 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;
|
||||
let end = self.end.number;
|
||||
self.blocks.extend(blocks.into_iter()
|
||||
.rev()
|
||||
.filter(|b| level > 1 || (*b >= begin && *b <= end))
|
||||
@@ -261,7 +261,7 @@ impl<'a, RS: 'a + RootsStorage<H>, S: Storage<H>, H: Hasher> DrilldownIteratorEs
|
||||
}
|
||||
|
||||
/// Exploring drilldown operator.
|
||||
struct DrilldownIterator<'a, RS: 'a + RootsStorage<H>, S: 'a + Storage<H>, H: Hasher> {
|
||||
struct DrilldownIterator<'a, RS: 'a + RootsStorage<H>, S: 'a + Storage<H>, H: Hasher> where H::Out: 'a {
|
||||
essence: DrilldownIteratorEssence<'a, RS, S, H>,
|
||||
}
|
||||
|
||||
@@ -278,7 +278,7 @@ impl<'a, RS: 'a + RootsStorage<H>, S: Storage<H>, H: Hasher> Iterator
|
||||
}
|
||||
|
||||
/// Proving drilldown iterator.
|
||||
struct ProvingDrilldownIterator<'a, RS: 'a + RootsStorage<H>, S: 'a + Storage<H>, H: Hasher> {
|
||||
struct ProvingDrilldownIterator<'a, RS: 'a + RootsStorage<H>, S: 'a + Storage<H>, H: Hasher> where H::Out: 'a {
|
||||
essence: DrilldownIteratorEssence<'a, RS, S, H>,
|
||||
proof_recorder: RefCell<Recorder<H::Out>>,
|
||||
}
|
||||
@@ -427,23 +427,23 @@ mod tests {
|
||||
fn drilldown_iterator_works() {
|
||||
let (config, storage) = prepare_for_drilldown();
|
||||
let drilldown_result = key_changes::<InMemoryStorage<Blake2Hasher>, Blake2Hasher>(
|
||||
&config, &storage, 0, 16, 16, &[42]);
|
||||
&config, &storage, 0, &AnchorBlockId { hash: Default::default(), number: 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]);
|
||||
&config, &storage, 0, &AnchorBlockId { hash: Default::default(), number: 2 }, 4, &[42]);
|
||||
assert_eq!(drilldown_result, Ok(vec![]));
|
||||
|
||||
let drilldown_result = key_changes::<InMemoryStorage<Blake2Hasher>, Blake2Hasher>(
|
||||
&config, &storage, 0, 3, 4, &[42]);
|
||||
&config, &storage, 0, &AnchorBlockId { hash: Default::default(), number: 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]);
|
||||
&config, &storage, 7, &AnchorBlockId { hash: Default::default(), number: 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]);
|
||||
&config, &storage, 5, &AnchorBlockId { hash: Default::default(), number: 7 }, 8, &[42]);
|
||||
assert_eq!(drilldown_result, Ok(vec![(6, 3)]));
|
||||
}
|
||||
|
||||
@@ -453,16 +453,16 @@ mod tests {
|
||||
storage.clear_storage();
|
||||
|
||||
assert!(key_changes::<InMemoryStorage<Blake2Hasher>, Blake2Hasher>(
|
||||
&config, &storage, 0, 100, 1000, &[42]).is_err());
|
||||
&config, &storage, 0, &AnchorBlockId { hash: Default::default(), number: 100 }, 1000, &[42]).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn drilldown_iterator_fails_when_range_is_invalid() {
|
||||
let (config, storage) = prepare_for_drilldown();
|
||||
assert!(key_changes::<InMemoryStorage<Blake2Hasher>, Blake2Hasher>(
|
||||
&config, &storage, 0, 100, 50, &[42]).is_err());
|
||||
&config, &storage, 0, &AnchorBlockId { hash: Default::default(), number: 100 }, 50, &[42]).is_err());
|
||||
assert!(key_changes::<InMemoryStorage<Blake2Hasher>, Blake2Hasher>(
|
||||
&config, &storage, 20, 10, 100, &[42]).is_err());
|
||||
&config, &storage, 20, &AnchorBlockId { hash: Default::default(), number: 10 }, 100, &[42]).is_err());
|
||||
}
|
||||
|
||||
|
||||
@@ -474,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, 16, 16, &[42]).unwrap();
|
||||
0, &AnchorBlockId { hash: Default::default(), number: 16 }, 16, &[42]).unwrap();
|
||||
|
||||
// happens on local light node:
|
||||
|
||||
@@ -483,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, 16, 16, &[42]);
|
||||
0, &AnchorBlockId { hash: Default::default(), number: 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)]));
|
||||
|
||||
@@ -58,10 +58,20 @@ use trie::{DBValue, trie_root};
|
||||
/// Changes that are made outside of extrinsics are marked with this index;
|
||||
pub const NO_EXTRINSIC_INDEX: u32 = 0xffffffff;
|
||||
|
||||
/// Block identifier that could be used to determine fork of this block.
|
||||
#[derive(Debug)]
|
||||
pub struct AnchorBlockId<Hash: ::std::fmt::Debug> {
|
||||
/// Hash of this block.
|
||||
pub hash: Hash,
|
||||
/// Number of this block.
|
||||
pub number: u64,
|
||||
}
|
||||
|
||||
/// Changes trie storage. Provides access to trie roots and trie nodes.
|
||||
pub trait RootsStorage<H: Hasher>: Send + Sync {
|
||||
/// Get changes trie root for given block.
|
||||
fn root(&self, block: u64) -> Result<Option<H::Out>, String>;
|
||||
/// Get changes trie root for the block with given number which is an ancestor (or the block
|
||||
/// itself) of the anchor_block (i.e. anchor_block.number >= block).
|
||||
fn root(&self, anchor: &AnchorBlockId<H::Out>, block: u64) -> Result<Option<H::Out>, String>;
|
||||
}
|
||||
|
||||
/// Changes trie storage. Provides access to trie roots and trie nodes.
|
||||
@@ -79,13 +89,13 @@ pub fn compute_changes_trie_root<'a, B: Backend<H>, S: Storage<H>, H: Hasher>(
|
||||
backend: &B,
|
||||
storage: Option<&'a S>,
|
||||
changes: &OverlayedChanges,
|
||||
block: u64,
|
||||
parent: &'a AnchorBlockId<H::Out>,
|
||||
) -> Option<(H::Out, Vec<(Vec<u8>, Vec<u8>)>)>
|
||||
where
|
||||
&'a S: TrieBackendStorage<H>,
|
||||
H::Out: Ord + HeapSizeOf,
|
||||
{
|
||||
let input_pairs = prepare_input::<B, S, H>(backend, storage, changes, block)
|
||||
let input_pairs = prepare_input::<B, S, H>(backend, storage, changes, parent)
|
||||
.expect("storage is not allowed to fail within runtime")?;
|
||||
let transaction = input_pairs.into_iter()
|
||||
.map(Into::into)
|
||||
|
||||
@@ -21,7 +21,7 @@ use heapsize::HeapSizeOf;
|
||||
use substrate_trie::Recorder;
|
||||
use proving_backend::ProvingBackendEssence;
|
||||
use trie_backend_essence::TrieBackendEssence;
|
||||
use changes_trie::{Configuration, Storage};
|
||||
use changes_trie::{AnchorBlockId, Configuration, Storage};
|
||||
use changes_trie::storage::TrieBackendAdapter;
|
||||
|
||||
/// Prune obslete changes tries. Puning happens at the same block, where highest
|
||||
@@ -33,21 +33,21 @@ pub fn prune<S: Storage<H>, H: Hasher, F: FnMut(H::Out)>(
|
||||
config: &Configuration,
|
||||
storage: &S,
|
||||
min_blocks_to_keep: u64,
|
||||
current_block: u64,
|
||||
current_block: &AnchorBlockId<H::Out>,
|
||||
mut remove_trie_node: F,
|
||||
)
|
||||
where
|
||||
H::Out: HeapSizeOf,
|
||||
{
|
||||
// we only CAN prune at block where max-level-digest is created
|
||||
let digest_interval = match config.digest_level_at_block(current_block) {
|
||||
let digest_interval = match config.digest_level_at_block(current_block.number) {
|
||||
Some((digest_level, digest_interval, _)) if digest_level == config.digest_levels =>
|
||||
digest_interval,
|
||||
_ => return,
|
||||
};
|
||||
|
||||
// select range for pruning
|
||||
let (first, last) = match pruning_range(min_blocks_to_keep, current_block, digest_interval) {
|
||||
let (first, last) = match pruning_range(min_blocks_to_keep, current_block.number, digest_interval) {
|
||||
Some((first, last)) => (first, last),
|
||||
None => return,
|
||||
};
|
||||
@@ -55,7 +55,7 @@ pub fn prune<S: Storage<H>, H: Hasher, F: FnMut(H::Out)>(
|
||||
// delete changes trie for every block in range
|
||||
// TODO: limit `max_digest_interval` so that this cycle won't involve huge ranges
|
||||
for block in first..last+1 {
|
||||
let root = match storage.root(block) {
|
||||
let root = match storage.root(current_block, block) {
|
||||
Ok(Some(root)) => root,
|
||||
Ok(None) => continue,
|
||||
Err(error) => {
|
||||
@@ -139,7 +139,7 @@ mod tests {
|
||||
H::Out: HeapSizeOf,
|
||||
{
|
||||
let mut pruned_trie_nodes = HashSet::new();
|
||||
prune(config, storage, min_blocks_to_keep, current_block,
|
||||
prune(config, storage, min_blocks_to_keep, &AnchorBlockId { hash: Default::default(), number: current_block },
|
||||
|node| { pruned_trie_nodes.insert(node); });
|
||||
pruned_trie_nodes
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ use trie::DBValue;
|
||||
use heapsize::HeapSizeOf;
|
||||
use trie::MemoryDB;
|
||||
use parking_lot::RwLock;
|
||||
use changes_trie::{RootsStorage, Storage};
|
||||
use changes_trie::{AnchorBlockId, RootsStorage, Storage};
|
||||
use trie_backend_essence::TrieBackendStorage;
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -110,7 +110,7 @@ impl<H: Hasher> 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> {
|
||||
fn root(&self, _anchor_block: &AnchorBlockId<H::Out>, block: u64) -> Result<Option<H::Out>, String> {
|
||||
Ok(self.data.read().roots.get(&block).cloned())
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user