use codec::Encode;
use frame_support::log::{debug, trace};
use sp_core::offchain::StorageKind;
use sp_io::offchain_index;
use sp_mmr_primitives::{mmr_lib, mmr_lib::helper, utils::NodesUtils};
use sp_std::iter::Peekable;
#[cfg(not(feature = "std"))]
use sp_std::prelude::*;
use crate::{
mmr::{Node, NodeOf},
primitives::{self, NodeIndex},
Config, Nodes, NumberOfLeaves, Pallet,
};
pub struct RuntimeStorage;
pub struct OffchainStorage;
pub struct Storage<StorageType, T, I, L>(sp_std::marker::PhantomData<(StorageType, T, I, L)>);
impl<StorageType, T, I, L> Default for Storage<StorageType, T, I, L> {
fn default() -> Self {
Self(Default::default())
}
}
impl<T, I, L> mmr_lib::MMRStore<NodeOf<T, I, L>> for Storage<OffchainStorage, T, I, L>
where
T: Config<I>,
I: 'static,
L: primitives::FullLeaf + codec::Decode,
{
fn get_elem(&self, pos: NodeIndex) -> mmr_lib::Result<Option<NodeOf<T, I, L>>> {
let leaves = NumberOfLeaves::<T, I>::get();
let ancestor_leaf_idx = NodesUtils::leaf_index_that_added_node(pos);
let key = Pallet::<T, I>::node_canon_offchain_key(pos);
debug!(
target: "runtime::mmr::offchain", "offchain db get {}: leaf idx {:?}, canon key {:?}",
pos, ancestor_leaf_idx, key
);
if let Some(elem) = sp_io::offchain::local_storage_get(StorageKind::PERSISTENT, &key) {
return Ok(codec::Decode::decode(&mut &*elem).ok())
}
let ancestor_parent_block_num =
Pallet::<T, I>::leaf_index_to_parent_block_num(ancestor_leaf_idx, leaves);
let ancestor_parent_hash = <frame_system::Pallet<T>>::block_hash(ancestor_parent_block_num);
let temp_key = Pallet::<T, I>::node_temp_offchain_key(pos, ancestor_parent_hash);
debug!(
target: "runtime::mmr::offchain",
"offchain db get {}: leaf idx {:?}, hash {:?}, temp key {:?}",
pos, ancestor_leaf_idx, ancestor_parent_hash, temp_key
);
Ok(sp_io::offchain::local_storage_get(StorageKind::PERSISTENT, &temp_key)
.and_then(|v| codec::Decode::decode(&mut &*v).ok()))
}
fn append(&mut self, _: NodeIndex, _: Vec<NodeOf<T, I, L>>) -> mmr_lib::Result<()> {
panic!("MMR must not be altered in the off-chain context.")
}
}
impl<T, I, L> mmr_lib::MMRStore<NodeOf<T, I, L>> for Storage<RuntimeStorage, T, I, L>
where
T: Config<I>,
I: 'static,
L: primitives::FullLeaf,
{
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: NodeIndex, elems: Vec<NodeOf<T, I, L>>) -> mmr_lib::Result<()> {
if elems.is_empty() {
return Ok(())
}
trace!(
target: "runtime::mmr", "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)
}
let new_size = size + elems.len() as NodeIndex;
let (peaks_to_prune, mut peaks_to_store) = peaks_to_prune_and_store(size, new_size);
let mut leaf_index = leaves;
let mut node_index = size;
let parent_hash = <frame_system::Pallet<T>>::parent_hash();
for elem in elems {
if peaks_to_store.next_if_eq(&node_index).is_some() {
<Nodes<T, I>>::insert(node_index, elem.hash());
}
Self::store_to_offchain(node_index, parent_hash, &elem);
if let Node::Data(..) = elem {
leaf_index += 1;
}
node_index += 1;
}
NumberOfLeaves::<T, I>::put(leaf_index);
for pos in peaks_to_prune {
<Nodes<T, I>>::remove(pos);
}
Ok(())
}
}
impl<T, I, L> Storage<RuntimeStorage, T, I, L>
where
T: Config<I>,
I: 'static,
L: primitives::FullLeaf,
{
fn store_to_offchain(
pos: NodeIndex,
parent_hash: <T as frame_system::Config>::Hash,
node: &NodeOf<T, I, L>,
) {
let encoded_node = node.encode();
let temp_key = Pallet::<T, I>::node_temp_offchain_key(pos, parent_hash);
debug!(
target: "runtime::mmr::offchain", "offchain db set: pos {} parent_hash {:?} key {:?}",
pos, parent_hash, temp_key
);
offchain_index::set(&temp_key, &encoded_node);
}
}
fn peaks_to_prune_and_store(
old_size: NodeIndex,
new_size: NodeIndex,
) -> (impl Iterator<Item = NodeIndex>, Peekable<impl Iterator<Item = NodeIndex>>) {
let peaks_before = if old_size == 0 { vec![] } else { helper::get_peaks(old_size) };
let peaks_after = helper::get_peaks(new_size);
trace!(target: "runtime::mmr", "peaks_before: {:?}", peaks_before);
trace!(target: "runtime::mmr", "peaks_after: {:?}", peaks_after);
let mut peaks_before = peaks_before.into_iter().peekable();
let mut peaks_after = peaks_after.into_iter().peekable();
while peaks_before.peek() == peaks_after.peek() {
peaks_before.next();
peaks_after.next();
}
(peaks_before, peaks_after)
}