This repository was archived by the owner on Nov 15, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 2.7k
Support MMR Pruning #9700
Merged
paritytech-processbot
merged 23 commits into
paritytech:master
from
darwinia-network:xavier-mmr-pruning
Nov 23, 2021
Merged
Support MMR Pruning #9700
Changes from 13 commits
Commits
Show all changes
23 commits
Select commit
Hold shift + click to select a range
d99bb52
Use `0.3.2`
5cb4a96
Replace `u64` with `NodeIndex`
f84ccda
Fix Typo
fbc7804
Add Pruning Logic
3c748e7
Fix Some Tests
687eab7
Remove Comment
1cbe99a
Log Only Under STD
6b71e32
Return while No Element to Append
15b5a3f
Optimize Pruning Algorithm
32207f1
Update Doc
7ccee87
Update Doc
7028381
Zero Copy Algorithm
95849c3
Merge from Master
feba71e
Merge remote-tracking branch 'paritytech/master' into xavier-mmr-pruning
06e06f0
Import Missing Type
ed2645f
Fix Merge Mistake
88df73e
Import Missing Item
58fb0a2
Make `verify` Off-Chain
998cc36
`cargo fmt`
6ea225c
Avoid using NodeIndex in incorrect places.
tomusdrw 7b580bb
Simplify pruning.
tomusdrw a6903e1
Merge pull request #123 from tomusdrw/td-mmr-pruning
aurexav 2b7f205
Format
aurexav File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -18,19 +18,23 @@ | |
| //! A MMR storage implementations. | ||
|
|
||
| use codec::Encode; | ||
| use frame_support::log; | ||
| use mmr_lib::helper; | ||
| use sp_io::offchain_index; | ||
| #[cfg(not(feature = "std"))] | ||
| use sp_std::prelude::Vec; | ||
|
|
||
| use crate::{ | ||
| mmr::{Node, NodeOf}, | ||
| primitives, Config, Nodes, NumberOfLeaves, Pallet, | ||
| mmr::{utils::NodesUtils, Node, NodeOf}, | ||
| primitives::{self, DataOrHash, NodeIndex}, | ||
| Config, Nodes, NumberOfLeaves, Pallet, | ||
| }; | ||
|
|
||
| /// A marker type for runtime-specific storage implementation. | ||
| /// | ||
| /// Allows appending new items to the MMR and proof verification. | ||
| /// MMR nodes are appended to two different storages: | ||
| /// 1. We add nodes (leaves) hashes to the on-chain storge (see [crate::Nodes]). | ||
| /// 1. We add nodes (leaves) hashes to the on-chain storage (see [crate::Nodes]). | ||
| /// 2. We add full leaves (and all inner nodes as well) into the `IndexingAPI` during block | ||
| /// processing, so the values end up in the Offchain DB if indexing is enabled. | ||
| pub struct RuntimeStorage; | ||
|
|
@@ -60,14 +64,14 @@ where | |
| I: 'static, | ||
| L: primitives::FullLeaf + codec::Decode, | ||
| { | ||
| fn get_elem(&self, pos: u64) -> mmr_lib::Result<Option<NodeOf<T, I, L>>> { | ||
| fn get_elem(&self, pos: NodeIndex) -> mmr_lib::Result<Option<NodeOf<T, I, L>>> { | ||
| let key = Pallet::<T, I>::offchain_key(pos); | ||
| // Retrieve the element from Off-chain DB. | ||
| Ok(sp_io::offchain::local_storage_get(sp_core::offchain::StorageKind::PERSISTENT, &key) | ||
| .and_then(|v| codec::Decode::decode(&mut &*v).ok())) | ||
| } | ||
|
|
||
| fn append(&mut self, _: u64, _: Vec<NodeOf<T, I, L>>) -> mmr_lib::Result<()> { | ||
| fn append(&mut self, _: NodeIndex, _: Vec<NodeOf<T, I, L>>) -> mmr_lib::Result<()> { | ||
| panic!("MMR must not be altered in the off-chain context.") | ||
| } | ||
| } | ||
|
|
@@ -78,32 +82,136 @@ where | |
| I: 'static, | ||
| L: primitives::FullLeaf, | ||
| { | ||
| fn get_elem(&self, pos: u64) -> mmr_lib::Result<Option<NodeOf<T, I, L>>> { | ||
| fn get_elem(&self, pos: NodeIndex) -> mmr_lib::Result<Option<NodeOf<T, I, L>>> { | ||
| Ok(<Nodes<T, I>>::get(pos).map(Node::Hash)) | ||
| } | ||
|
|
||
| fn append(&mut self, pos: u64, elems: Vec<NodeOf<T, I, L>>) -> mmr_lib::Result<()> { | ||
| let mut leaves = crate::NumberOfLeaves::<T, I>::get(); | ||
| let mut size = crate::mmr::utils::NodesUtils::new(leaves).size(); | ||
| fn append(&mut self, pos: NodeIndex, elems: Vec<NodeOf<T, I, L>>) -> mmr_lib::Result<()> { | ||
| if elems.is_empty() { | ||
| return Ok(()) | ||
| } | ||
|
|
||
| sp_std::if_std! { | ||
| log::trace!("elems: {:?}", elems.iter().map(|elem| elem.hash()).collect::<Vec<_>>()); | ||
| } | ||
|
|
||
| let leaves = NumberOfLeaves::<T, I>::get(); | ||
| let size = NodesUtils::new(leaves).size(); | ||
|
|
||
| if pos != size { | ||
| return Err(mmr_lib::Error::InconsistentStore) | ||
| } | ||
|
|
||
| for elem in elems { | ||
| // on-chain we only store the hash (even if it's a leaf) | ||
| <Nodes<T, I>>::insert(size, elem.hash()); | ||
| let elems = elems | ||
| .into_iter() | ||
| .enumerate() | ||
| .map(|(i, elem)| (size + i as NodeIndex, elem)) | ||
| .collect::<Vec<_>>(); | ||
| let leaves = elems.iter().fold(leaves, |acc, (pos, elem)| { | ||
| // Indexing API is used to store the full leaf content. | ||
| let key = Pallet::<T, I>::offchain_key(size); | ||
| elem.using_encoded(|elem| sp_io::offchain_index::set(&key, elem)); | ||
| size += 1; | ||
| elem.using_encoded(|elem| { | ||
| offchain_index::set(&Pallet::<T, I>::offchain_key(*pos), elem) | ||
| }); | ||
|
|
||
| if let Node::Data(..) = elem { | ||
| leaves += 1; | ||
| acc + 1 | ||
| } else { | ||
| acc | ||
| } | ||
| } | ||
| }); | ||
|
||
|
|
||
| NumberOfLeaves::<T, I>::put(leaves); | ||
|
|
||
| let peaks_before = if size == 0 { vec![] } else { helper::get_peaks(size) }; | ||
| let peaks_after = helper::get_peaks(size + elems.len() as NodeIndex); | ||
aurexav marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| sp_std::if_std! { | ||
| log::trace!("peaks_before: {:?}", peaks_before); | ||
| log::trace!("peaks_after: {:?}", peaks_after); | ||
| } | ||
|
|
||
| let store = |peaks_to_store: &[_], elems: Vec<(_, DataOrHash<_, _>)>| { | ||
| let mut peak_to_store = | ||
| peaks_to_store.get(0).expect("`peaks_to_store` can not be empty; qed"); | ||
| let mut i = 1; | ||
|
|
||
| for (pos, elem) in elems { | ||
| if &pos == peak_to_store { | ||
| i += 1; | ||
|
|
||
| <Nodes<T, I>>::insert(peak_to_store, elem.hash()); | ||
|
|
||
| if let Some(next_peak_to_store) = peaks_to_store.get(i) { | ||
| peak_to_store = next_peak_to_store; | ||
| } else { | ||
| // No more peaks to store. | ||
| break | ||
| } | ||
| } | ||
| } | ||
| }; | ||
|
|
||
| // A new tree to build, no need to prune. | ||
| if peaks_before.is_empty() { | ||
| store(&peaks_after, elems); | ||
|
|
||
| return Ok(()) | ||
| } | ||
|
|
||
| let mut pivot = None; | ||
|
|
||
| // `peaks_before` and `peaks_after` have a common prefix. | ||
| for i in 0.. { | ||
| if let Some(peak_before) = peaks_before.get(i) { | ||
| if let Some(peak_after) = peaks_after.get(i) { | ||
| if peak_before == peak_after { | ||
| pivot = Some(i); | ||
| } else { | ||
| break | ||
| } | ||
| } else { | ||
| break | ||
| } | ||
| } else { | ||
| break | ||
| } | ||
| } | ||
|
|
||
| sp_std::if_std! { | ||
| log::trace!("pivot: {:?}", pivot); | ||
| } | ||
|
|
||
| // According to the MMR specification | ||
| // `nodes_to_prune` might be empty | ||
| // `peaks_to_store` must not be empty | ||
| let mut nodes_to_prune = &[][..]; | ||
| let mut peaks_to_store = &[][..]; | ||
|
|
||
| if let Some(pivot) = pivot { | ||
| let pivot = pivot + 1; | ||
|
|
||
| if pivot < peaks_before.len() { | ||
| nodes_to_prune = &peaks_before[pivot..]; | ||
| } | ||
| if pivot < peaks_after.len() { | ||
| peaks_to_store = &peaks_after[pivot..]; | ||
| } | ||
| } else { | ||
| nodes_to_prune = &peaks_before; | ||
| peaks_to_store = &peaks_after; | ||
| }; | ||
|
|
||
| sp_std::if_std! { | ||
| log::trace!("nodes_to_prune: {:?}", nodes_to_prune.to_vec()); | ||
| log::trace!("peaks_to_store: {:?}", peaks_to_store.to_vec()); | ||
| } | ||
|
|
||
| store(peaks_to_store, elems); | ||
|
|
||
| for pos in nodes_to_prune { | ||
| <Nodes<T, I>>::remove(pos); | ||
| } | ||
|
|
||
| Ok(()) | ||
| } | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.