1use crate::{
21 mmr::{Node, NodeOf},
22 primitives::{mmr_lib, mmr_lib::helper, utils::NodesUtils, FullLeaf, NodeIndex},
23 BlockHashProvider, Config, Nodes, NumberOfLeaves, Pallet,
24};
25use alloc::{vec, vec::Vec};
26use codec::Encode;
27use core::iter::Peekable;
28use frame::{
29 deps::{
30 sp_core::offchain::StorageKind,
31 sp_io::{offchain, offchain_index},
32 },
33 prelude::*,
34};
35use log::{debug, trace};
36
37pub struct RuntimeStorage;
45
46pub struct OffchainStorage;
52
53impl OffchainStorage {
54 fn get(key: &[u8]) -> Option<Vec<u8>> {
55 offchain::local_storage_get(StorageKind::PERSISTENT, &key)
56 }
57
58 #[cfg(not(feature = "runtime-benchmarks"))]
59 fn set<T: Config<I>, I: 'static>(key: &[u8], value: &[u8]) {
60 offchain_index::set(key, value);
61 }
62
63 #[cfg(feature = "runtime-benchmarks")]
64 fn set<T: Config<I>, I: 'static>(key: &[u8], value: &[u8]) {
65 if crate::pallet::UseLocalStorage::<T, I>::get() {
66 offchain::local_storage_set(StorageKind::PERSISTENT, key, value);
67 } else {
68 offchain_index::set(key, value);
69 }
70 }
71}
72
73pub struct Storage<StorageType, T, I, L>(core::marker::PhantomData<(StorageType, T, I, L)>);
78
79impl<StorageType, T, I, L> Default for Storage<StorageType, T, I, L> {
80 fn default() -> Self {
81 Self(Default::default())
82 }
83}
84
85impl<T, I, L> mmr_lib::MMRStoreReadOps<NodeOf<T, I, L>> for Storage<OffchainStorage, T, I, L>
86where
87 T: Config<I>,
88 I: 'static,
89 L: FullLeaf + Decode,
90{
91 fn get_elem(&self, pos: NodeIndex) -> mmr_lib::Result<Option<NodeOf<T, I, L>>> {
92 let ancestor_leaf_idx = NodesUtils::leaf_index_that_added_node(pos);
94
95 let key = Pallet::<T, I>::node_canon_offchain_key(pos);
99 debug!(
100 target: "runtime::mmr::offchain", "offchain db get {}: leaf idx {:?}, canon key {:?}",
101 pos, ancestor_leaf_idx, key
102 );
103 if let Some(elem) = OffchainStorage::get(&key) {
105 return Ok(codec::Decode::decode(&mut &*elem).ok())
106 }
107
108 let ancestor_parent_block_num =
110 Pallet::<T, I>::leaf_index_to_parent_block_num(ancestor_leaf_idx);
111 let ancestor_parent_hash = T::BlockHashProvider::block_hash(ancestor_parent_block_num);
112 let temp_key = Pallet::<T, I>::node_temp_offchain_key(pos, ancestor_parent_hash);
113 debug!(
114 target: "runtime::mmr::offchain",
115 "offchain db get {}: leaf idx {:?}, hash {:?}, temp key {:?}",
116 pos, ancestor_leaf_idx, ancestor_parent_hash, temp_key
117 );
118 Ok(OffchainStorage::get(&temp_key).and_then(|v| codec::Decode::decode(&mut &*v).ok()))
120 }
121}
122
123impl<T, I, L> mmr_lib::MMRStoreWriteOps<NodeOf<T, I, L>> for Storage<OffchainStorage, T, I, L>
124where
125 T: Config<I>,
126 I: 'static,
127 L: FullLeaf + Decode,
128{
129 fn append(&mut self, _: NodeIndex, _: Vec<NodeOf<T, I, L>>) -> mmr_lib::Result<()> {
130 panic!("MMR must not be altered in the off-chain context.")
131 }
132}
133
134impl<T, I, L> mmr_lib::MMRStoreReadOps<NodeOf<T, I, L>> for Storage<RuntimeStorage, T, I, L>
135where
136 T: Config<I>,
137 I: 'static,
138 L: FullLeaf,
139{
140 fn get_elem(&self, pos: NodeIndex) -> mmr_lib::Result<Option<NodeOf<T, I, L>>> {
141 Ok(Nodes::<T, I>::get(pos).map(Node::Hash))
142 }
143}
144
145impl<T, I, L> mmr_lib::MMRStoreWriteOps<NodeOf<T, I, L>> for Storage<RuntimeStorage, T, I, L>
146where
147 T: Config<I>,
148 I: 'static,
149 L: FullLeaf,
150{
151 fn append(&mut self, pos: NodeIndex, elems: Vec<NodeOf<T, I, L>>) -> mmr_lib::Result<()> {
152 if elems.is_empty() {
153 return Ok(())
154 }
155
156 trace!(
157 target: "runtime::mmr", "elems: {:?}",
158 elems.iter().map(|elem| elem.hash()).collect::<Vec<_>>()
159 );
160
161 let leaves = NumberOfLeaves::<T, I>::get();
162 let size = NodesUtils::new(leaves).size();
163
164 if pos != size {
165 return Err(mmr_lib::Error::InconsistentStore)
166 }
167
168 let new_size = size + elems.len() as NodeIndex;
169
170 let (peaks_to_prune, mut peaks_to_store) = peaks_to_prune_and_store(size, new_size);
172
173 let mut leaf_index = leaves;
176 let mut node_index = size;
177
178 let parent_hash = <frame_system::Pallet<T>>::parent_hash();
181 for elem in elems {
182 if peaks_to_store.next_if_eq(&node_index).is_some() {
184 Nodes::<T, I>::insert(node_index, elem.hash());
185 }
186 Self::store_to_offchain(node_index, parent_hash, &elem);
188
189 if let Node::Data(..) = elem {
191 leaf_index += 1;
192 }
193 node_index += 1;
194 }
195
196 NumberOfLeaves::<T, I>::put(leaf_index);
198
199 for pos in peaks_to_prune {
201 Nodes::<T, I>::remove(pos);
202 }
203
204 Ok(())
205 }
206}
207
208impl<T, I, L> Storage<RuntimeStorage, T, I, L>
209where
210 T: Config<I>,
211 I: 'static,
212 L: FullLeaf,
213{
214 fn store_to_offchain(
215 pos: NodeIndex,
216 parent_hash: <T as frame_system::Config>::Hash,
217 node: &NodeOf<T, I, L>,
218 ) {
219 let encoded_node = node.encode();
220 let temp_key = Pallet::<T, I>::node_temp_offchain_key(pos, parent_hash);
224 debug!(
225 target: "runtime::mmr::offchain", "offchain db set: pos {} parent_hash {:?} key {:?}",
226 pos, parent_hash, temp_key
227 );
228 OffchainStorage::set::<T, I>(&temp_key, &encoded_node);
229 }
230}
231
232fn peaks_to_prune_and_store(
233 old_size: NodeIndex,
234 new_size: NodeIndex,
235) -> (impl Iterator<Item = NodeIndex>, Peekable<impl Iterator<Item = NodeIndex>>) {
236 let peaks_before = if old_size == 0 { vec![] } else { helper::get_peaks(old_size) };
239 let peaks_after = helper::get_peaks(new_size);
240 trace!(target: "runtime::mmr", "peaks_before: {:?}", peaks_before);
241 trace!(target: "runtime::mmr", "peaks_after: {:?}", peaks_after);
242 let mut peaks_before = peaks_before.into_iter().peekable();
243 let mut peaks_after = peaks_after.into_iter().peekable();
244
245 while peaks_before.peek() == peaks_after.peek() {
248 peaks_before.next();
249 peaks_after.next();
250 }
251
252 (peaks_before, peaks_after)
256}