feat: Rebrand Polkadot/Substrate references to PezkuwiChain
This commit systematically rebrands various references from Parity Technologies' Polkadot/Substrate ecosystem to PezkuwiChain within the kurdistan-sdk. Key changes include: - Updated external repository URLs (zombienet-sdk, parity-db, parity-scale-codec, wasm-instrument) to point to pezkuwichain forks. - Modified internal documentation and code comments to reflect PezkuwiChain naming and structure. - Replaced direct references to with or specific paths within the for XCM, Pezkuwi, and other modules. - Cleaned up deprecated issue and PR references in various and files, particularly in and modules. - Adjusted image and logo URLs in documentation to point to PezkuwiChain assets. - Removed or rephrased comments related to external Polkadot/Substrate PRs and issues. This is a significant step towards fully customizing the SDK for the PezkuwiChain ecosystem.
This commit is contained in:
@@ -0,0 +1,312 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::{
|
||||
mmr::{
|
||||
storage::{OffchainStorage, RuntimeStorage, Storage},
|
||||
Hasher, Node, NodeOf,
|
||||
},
|
||||
primitives::{
|
||||
mmr_lib, mmr_lib::MMRStoreReadOps, utils::NodesUtils, AncestryProof, Error, FullLeaf,
|
||||
LeafIndex, LeafProof, NodeIndex,
|
||||
},
|
||||
Config, HashOf, HashingOf,
|
||||
};
|
||||
use alloc::vec::Vec;
|
||||
use frame::prelude::*;
|
||||
|
||||
/// Stateless verification of the proof for a batch of leaves.
|
||||
/// Note, the leaves should be sorted such that corresponding leaves and leaf indices have the
|
||||
/// same position in both the `leaves` vector and the `leaf_indices` vector contained in the
|
||||
/// [primitives::LeafProof]
|
||||
pub fn verify_leaves_proof<H, L>(
|
||||
root: H::Output,
|
||||
leaves: Vec<Node<H, L>>,
|
||||
proof: LeafProof<H::Output>,
|
||||
) -> Result<bool, Error>
|
||||
where
|
||||
H: Hash,
|
||||
L: FullLeaf,
|
||||
{
|
||||
let size = NodesUtils::new(proof.leaf_count).size();
|
||||
|
||||
if leaves.len() != proof.leaf_indices.len() {
|
||||
return Err(Error::Verify.log_debug("Proof leaf_indices not same length with leaves"));
|
||||
}
|
||||
|
||||
let leaves_and_position_data = proof
|
||||
.leaf_indices
|
||||
.into_iter()
|
||||
.map(|index| mmr_lib::leaf_index_to_pos(index))
|
||||
.zip(leaves.into_iter())
|
||||
.collect();
|
||||
|
||||
let p = mmr_lib::MerkleProof::<Node<H, L>, Hasher<H, L>>::new(
|
||||
size,
|
||||
proof.items.into_iter().map(Node::Hash).collect(),
|
||||
);
|
||||
p.verify(Node::Hash(root), leaves_and_position_data)
|
||||
.map_err(|e| Error::Verify.log_debug(e))
|
||||
}
|
||||
|
||||
pub fn is_ancestry_proof_optimal<H>(ancestry_proof: &AncestryProof<H::Output>) -> bool
|
||||
where
|
||||
H: frame::traits::Hash,
|
||||
{
|
||||
let prev_mmr_size = NodesUtils::new(ancestry_proof.prev_leaf_count).size();
|
||||
let mmr_size = NodesUtils::new(ancestry_proof.leaf_count).size();
|
||||
|
||||
let expected_proof_size =
|
||||
mmr_lib::ancestry_proof::expected_ancestry_proof_size(prev_mmr_size, mmr_size);
|
||||
ancestry_proof.items.len() == expected_proof_size
|
||||
}
|
||||
|
||||
pub fn verify_ancestry_proof<H, L>(
|
||||
root: H::Output,
|
||||
ancestry_proof: AncestryProof<H::Output>,
|
||||
) -> Result<H::Output, Error>
|
||||
where
|
||||
H: Hash,
|
||||
L: FullLeaf,
|
||||
{
|
||||
let mmr_size = NodesUtils::new(ancestry_proof.leaf_count).size();
|
||||
|
||||
let prev_peaks_proof = mmr_lib::NodeMerkleProof::<Node<H, L>, Hasher<H, L>>::new(
|
||||
mmr_size,
|
||||
ancestry_proof
|
||||
.items
|
||||
.into_iter()
|
||||
.map(|(index, hash)| (index, Node::Hash(hash)))
|
||||
.collect(),
|
||||
);
|
||||
|
||||
let raw_ancestry_proof = mmr_lib::AncestryProof::<Node<H, L>, Hasher<H, L>> {
|
||||
prev_mmr_size: mmr_lib::helper::leaf_index_to_mmr_size(ancestry_proof.prev_leaf_count - 1),
|
||||
prev_peaks: ancestry_proof.prev_peaks.into_iter().map(|hash| Node::Hash(hash)).collect(),
|
||||
prev_peaks_proof,
|
||||
};
|
||||
|
||||
let prev_root = mmr_lib::ancestry_proof::bagging_peaks_hashes::<Node<H, L>, Hasher<H, L>>(
|
||||
raw_ancestry_proof.prev_peaks.clone(),
|
||||
)
|
||||
.map_err(|e| Error::Verify.log_debug(e))?;
|
||||
raw_ancestry_proof
|
||||
.verify_ancestor(Node::Hash(root), prev_root.clone())
|
||||
.map_err(|e| Error::Verify.log_debug(e))?;
|
||||
|
||||
Ok(prev_root.hash())
|
||||
}
|
||||
|
||||
/// A wrapper around an MMR library to expose limited functionality.
|
||||
///
|
||||
/// Available functions depend on the storage kind ([Runtime](crate::mmr::storage::RuntimeStorage)
|
||||
/// vs [Off-chain](crate::mmr::storage::OffchainStorage)).
|
||||
pub struct Mmr<StorageType, T, I, L>
|
||||
where
|
||||
T: Config<I>,
|
||||
I: 'static,
|
||||
L: FullLeaf,
|
||||
Storage<StorageType, T, I, L>:
|
||||
MMRStoreReadOps<NodeOf<T, I, L>> + mmr_lib::MMRStoreWriteOps<NodeOf<T, I, L>>,
|
||||
{
|
||||
mmr: mmr_lib::MMR<NodeOf<T, I, L>, Hasher<HashingOf<T, I>, L>, Storage<StorageType, T, I, L>>,
|
||||
leaves: NodeIndex,
|
||||
}
|
||||
|
||||
impl<StorageType, T, I, L> Mmr<StorageType, T, I, L>
|
||||
where
|
||||
T: Config<I>,
|
||||
I: 'static,
|
||||
L: FullLeaf,
|
||||
Storage<StorageType, T, I, L>:
|
||||
MMRStoreReadOps<NodeOf<T, I, L>> + mmr_lib::MMRStoreWriteOps<NodeOf<T, I, L>>,
|
||||
{
|
||||
/// Create a pointer to an existing MMR with given number of leaves.
|
||||
pub fn new(leaves: NodeIndex) -> Self {
|
||||
let size = NodesUtils::new(leaves).size();
|
||||
Self { mmr: mmr_lib::MMR::new(size, Default::default()), leaves }
|
||||
}
|
||||
|
||||
/// Verify proof for a set of leaves.
|
||||
/// Note, the leaves should be sorted such that corresponding leaves and leaf indices have
|
||||
/// the same position in both the `leaves` vector and the `leaf_indices` vector contained in the
|
||||
/// [primitives::LeafProof]
|
||||
pub fn verify_leaves_proof(
|
||||
&self,
|
||||
leaves: Vec<L>,
|
||||
proof: LeafProof<HashOf<T, I>>,
|
||||
) -> Result<bool, Error> {
|
||||
let p = mmr_lib::MerkleProof::<NodeOf<T, I, L>, Hasher<HashingOf<T, I>, L>>::new(
|
||||
self.mmr.mmr_size(),
|
||||
proof.items.into_iter().map(Node::Hash).collect(),
|
||||
);
|
||||
|
||||
if leaves.len() != proof.leaf_indices.len() {
|
||||
return Err(Error::Verify.log_debug("Proof leaf_indices not same length with leaves"));
|
||||
}
|
||||
|
||||
let leaves_positions_and_data = proof
|
||||
.leaf_indices
|
||||
.into_iter()
|
||||
.map(|index| mmr_lib::leaf_index_to_pos(index))
|
||||
.zip(leaves.into_iter().map(|leaf| Node::Data(leaf)))
|
||||
.collect();
|
||||
let root = self.mmr.get_root().map_err(|e| Error::GetRoot.log_error(e))?;
|
||||
p.verify(root, leaves_positions_and_data)
|
||||
.map_err(|e| Error::Verify.log_debug(e))
|
||||
}
|
||||
|
||||
/// Return the internal size of the MMR (number of nodes).
|
||||
#[cfg(any(test, feature = "runtime-benchmarks"))]
|
||||
pub fn size(&self) -> NodeIndex {
|
||||
self.mmr.mmr_size()
|
||||
}
|
||||
}
|
||||
|
||||
/// Runtime specific MMR functions.
|
||||
impl<T, I, L> Mmr<RuntimeStorage, T, I, L>
|
||||
where
|
||||
T: Config<I>,
|
||||
I: 'static,
|
||||
L: FullLeaf,
|
||||
{
|
||||
/// Push another item to the MMR.
|
||||
///
|
||||
/// Returns element position (index) in the MMR.
|
||||
pub fn push(&mut self, leaf: L) -> Option<NodeIndex> {
|
||||
let position =
|
||||
self.mmr.push(Node::Data(leaf)).map_err(|e| Error::Push.log_error(e)).ok()?;
|
||||
|
||||
self.leaves += 1;
|
||||
|
||||
Some(position)
|
||||
}
|
||||
|
||||
/// Commit the changes to underlying storage, return current number of leaves and
|
||||
/// calculate the new MMR's root hash.
|
||||
pub fn finalize(mut self) -> Result<(NodeIndex, HashOf<T, I>), Error> {
|
||||
let root = self.mmr.get_root().map_err(|e| Error::GetRoot.log_error(e))?;
|
||||
self.mmr.commit().map_err(|e| Error::Commit.log_error(e))?;
|
||||
Ok((self.leaves, root.hash()))
|
||||
}
|
||||
}
|
||||
|
||||
/// Off-chain specific MMR functions.
|
||||
impl<T, I, L> Mmr<OffchainStorage, T, I, L>
|
||||
where
|
||||
T: Config<I>,
|
||||
I: 'static,
|
||||
L: FullLeaf + codec::Decode,
|
||||
{
|
||||
/// Generate a proof for given leaf indices.
|
||||
///
|
||||
/// Proof generation requires all the nodes (or their hashes) to be available in the storage.
|
||||
/// (i.e. you can't run the function in the pruned storage).
|
||||
pub fn generate_proof(
|
||||
&self,
|
||||
leaf_indices: Vec<NodeIndex>,
|
||||
) -> Result<(Vec<L>, LeafProof<HashOf<T, I>>), Error> {
|
||||
let positions = leaf_indices
|
||||
.iter()
|
||||
.map(|index| mmr_lib::leaf_index_to_pos(*index))
|
||||
.collect::<Vec<_>>();
|
||||
let store = <Storage<OffchainStorage, T, I, L>>::default();
|
||||
let leaves = positions
|
||||
.iter()
|
||||
.map(|pos| match store.get_elem(*pos) {
|
||||
Ok(Some(Node::Data(leaf))) => Ok(leaf),
|
||||
e => Err(Error::LeafNotFound.log_debug(e)),
|
||||
})
|
||||
.collect::<Result<Vec<_>, Error>>()?;
|
||||
|
||||
let leaf_count = self.leaves;
|
||||
self.mmr
|
||||
.gen_proof(positions)
|
||||
.map_err(|e| Error::GenerateProof.log_error(e))
|
||||
.map(|p| LeafProof {
|
||||
leaf_indices,
|
||||
leaf_count,
|
||||
items: p.proof_items().iter().map(|x| x.hash()).collect(),
|
||||
})
|
||||
.map(|p| (leaves, p))
|
||||
}
|
||||
|
||||
pub fn generate_ancestry_proof(
|
||||
&self,
|
||||
prev_leaf_count: LeafIndex,
|
||||
) -> Result<AncestryProof<HashOf<T, I>>, Error> {
|
||||
let prev_mmr_size = NodesUtils::new(prev_leaf_count).size();
|
||||
let raw_ancestry_proof = self
|
||||
.mmr
|
||||
.gen_ancestry_proof(prev_mmr_size)
|
||||
.map_err(|e| Error::GenerateProof.log_error(e))?;
|
||||
|
||||
Ok(AncestryProof {
|
||||
prev_peaks: raw_ancestry_proof.prev_peaks.into_iter().map(|p| p.hash()).collect(),
|
||||
prev_leaf_count,
|
||||
leaf_count: self.leaves,
|
||||
items: raw_ancestry_proof
|
||||
.prev_peaks_proof
|
||||
.proof_items()
|
||||
.iter()
|
||||
.map(|(index, item)| (*index, item.hash()))
|
||||
.collect(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Generate an inflated ancestry proof for the latest leaf in the MMR.
|
||||
///
|
||||
/// The generated proof contains all the leafs in the MMR, so this way we can generate a proof
|
||||
/// with exactly `leaf_count` items.
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
pub fn generate_mock_ancestry_proof(&self) -> Result<AncestryProof<HashOf<T, I>>, Error> {
|
||||
use crate::ModuleMmr;
|
||||
use alloc::vec;
|
||||
use mmr_lib::helper;
|
||||
|
||||
let mmr: ModuleMmr<OffchainStorage, T, I> = Mmr::new(self.leaves);
|
||||
let store = <Storage<OffchainStorage, T, I, L>>::default();
|
||||
|
||||
let mut prev_peaks = vec![];
|
||||
for peak_pos in helper::get_peaks(mmr.size()) {
|
||||
let peak = store
|
||||
.get_elem(peak_pos)
|
||||
.map_err(|_| Error::GenerateProof)?
|
||||
.ok_or(Error::GenerateProof)?
|
||||
.hash();
|
||||
prev_peaks.push(peak);
|
||||
}
|
||||
|
||||
let mut proof_items = vec![];
|
||||
for leaf_idx in 0..self.leaves {
|
||||
let leaf_pos = NodesUtils::leaf_index_to_leaf_node_index(leaf_idx);
|
||||
let leaf = store
|
||||
.get_elem(leaf_pos)
|
||||
.map_err(|_| Error::GenerateProof)?
|
||||
.ok_or(Error::GenerateProof)?
|
||||
.hash();
|
||||
proof_items.push((leaf_pos, leaf));
|
||||
}
|
||||
|
||||
Ok(AncestryProof {
|
||||
prev_peaks,
|
||||
prev_leaf_count: self.leaves,
|
||||
leaf_count: self.leaves,
|
||||
items: proof_items,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
mod mmr;
|
||||
pub mod storage;
|
||||
|
||||
pub use self::mmr::{is_ancestry_proof_optimal, verify_ancestry_proof, verify_leaves_proof, Mmr};
|
||||
use crate::primitives::{mmr_lib, DataOrHash, FullLeaf};
|
||||
use frame::traits;
|
||||
|
||||
/// Node type for runtime `T`.
|
||||
pub type NodeOf<T, I, L> = Node<<T as crate::Config<I>>::Hashing, L>;
|
||||
|
||||
/// A node stored in the MMR.
|
||||
pub type Node<H, L> = DataOrHash<H, L>;
|
||||
|
||||
/// Default Merging & Hashing behavior for MMR.
|
||||
pub struct Hasher<H, L>(core::marker::PhantomData<(H, L)>);
|
||||
|
||||
impl<H: traits::Hash, L: FullLeaf> mmr_lib::Merge for Hasher<H, L> {
|
||||
type Item = Node<H, L>;
|
||||
|
||||
fn merge(left: &Self::Item, right: &Self::Item) -> mmr_lib::Result<Self::Item> {
|
||||
let mut concat = left.hash().as_ref().to_vec();
|
||||
concat.extend_from_slice(right.hash().as_ref());
|
||||
|
||||
Ok(Node::Hash(<H as traits::Hash>::hash(&concat)))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,256 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! An MMR storage implementation.
|
||||
|
||||
use crate::{
|
||||
mmr::{Node, NodeOf},
|
||||
primitives::{mmr_lib, mmr_lib::helper, utils::NodesUtils, FullLeaf, NodeIndex},
|
||||
BlockHashProvider, Config, Nodes, NumberOfLeaves, Pallet,
|
||||
};
|
||||
use alloc::{vec, vec::Vec};
|
||||
use codec::Encode;
|
||||
use core::iter::Peekable;
|
||||
use frame::{
|
||||
deps::{
|
||||
pezsp_core::offchain::StorageKind,
|
||||
pezsp_io::{offchain, offchain_index},
|
||||
},
|
||||
prelude::*,
|
||||
};
|
||||
use log::{debug, trace};
|
||||
|
||||
/// 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 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;
|
||||
|
||||
/// A marker type for offchain-specific storage implementation.
|
||||
///
|
||||
/// Allows proof generation and verification, but does not support appending new items.
|
||||
/// MMR nodes are assumed to be stored in the Off-Chain DB. Note this storage type
|
||||
/// DOES NOT support adding new items to the MMR.
|
||||
pub struct OffchainStorage;
|
||||
|
||||
impl OffchainStorage {
|
||||
fn get(key: &[u8]) -> Option<Vec<u8>> {
|
||||
offchain::local_storage_get(StorageKind::PERSISTENT, &key)
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "runtime-benchmarks"))]
|
||||
fn set<T: Config<I>, I: 'static>(key: &[u8], value: &[u8]) {
|
||||
offchain_index::set(key, value);
|
||||
}
|
||||
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
fn set<T: Config<I>, I: 'static>(key: &[u8], value: &[u8]) {
|
||||
if crate::pallet::UseLocalStorage::<T, I>::get() {
|
||||
offchain::local_storage_set(StorageKind::PERSISTENT, key, value);
|
||||
} else {
|
||||
offchain_index::set(key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A storage layer for MMR.
|
||||
///
|
||||
/// There are two different implementations depending on the use case.
|
||||
/// See docs for [RuntimeStorage] and [OffchainStorage].
|
||||
pub struct Storage<StorageType, T, I, L>(core::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::MMRStoreReadOps<NodeOf<T, I, L>> for Storage<OffchainStorage, T, I, L>
|
||||
where
|
||||
T: Config<I>,
|
||||
I: 'static,
|
||||
L: FullLeaf + Decode,
|
||||
{
|
||||
fn get_elem(&self, pos: NodeIndex) -> mmr_lib::Result<Option<NodeOf<T, I, L>>> {
|
||||
// Find out which leaf added node `pos` in the MMR.
|
||||
let ancestor_leaf_idx = NodesUtils::leaf_index_that_added_node(pos);
|
||||
|
||||
// We should only get here when trying to generate proofs. The client requests
|
||||
// for proofs for finalized blocks, which should usually be already canonicalized,
|
||||
// unless the MMR client gadget has a delay.
|
||||
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
|
||||
);
|
||||
// Try to retrieve the element from Off-chain DB.
|
||||
if let Some(elem) = OffchainStorage::get(&key) {
|
||||
return Ok(codec::Decode::decode(&mut &*elem).ok());
|
||||
}
|
||||
|
||||
// Fall through to searching node using fork-specific key.
|
||||
let ancestor_parent_block_num =
|
||||
Pallet::<T, I>::leaf_index_to_parent_block_num(ancestor_leaf_idx);
|
||||
let ancestor_parent_hash = T::BlockHashProvider::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
|
||||
);
|
||||
// Retrieve the element from Off-chain DB.
|
||||
Ok(OffchainStorage::get(&temp_key).and_then(|v| codec::Decode::decode(&mut &*v).ok()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, I, L> mmr_lib::MMRStoreWriteOps<NodeOf<T, I, L>> for Storage<OffchainStorage, T, I, L>
|
||||
where
|
||||
T: Config<I>,
|
||||
I: 'static,
|
||||
L: FullLeaf + Decode,
|
||||
{
|
||||
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::MMRStoreReadOps<NodeOf<T, I, L>> for Storage<RuntimeStorage, T, I, L>
|
||||
where
|
||||
T: Config<I>,
|
||||
I: 'static,
|
||||
L: FullLeaf,
|
||||
{
|
||||
fn get_elem(&self, pos: NodeIndex) -> mmr_lib::Result<Option<NodeOf<T, I, L>>> {
|
||||
Ok(Nodes::<T, I>::get(pos).map(Node::Hash))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, I, L> mmr_lib::MMRStoreWriteOps<NodeOf<T, I, L>> for Storage<RuntimeStorage, T, I, L>
|
||||
where
|
||||
T: Config<I>,
|
||||
I: 'static,
|
||||
L: FullLeaf,
|
||||
{
|
||||
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;
|
||||
|
||||
// A sorted (ascending) iterator over peak indices to prune and persist.
|
||||
let (peaks_to_prune, mut peaks_to_store) = peaks_to_prune_and_store(size, new_size);
|
||||
|
||||
// Now we are going to iterate over elements to insert
|
||||
// and keep track of the current `node_index` and `leaf_index`.
|
||||
let mut leaf_index = leaves;
|
||||
let mut node_index = size;
|
||||
|
||||
// Use parent hash of block adding new nodes (this block) as extra identifier
|
||||
// in offchain DB to avoid DB collisions and overwrites in case of forks.
|
||||
let parent_hash = <pezframe_system::Pallet<T>>::parent_hash();
|
||||
for elem in elems {
|
||||
// On-chain we are going to only store new peaks.
|
||||
if peaks_to_store.next_if_eq(&node_index).is_some() {
|
||||
Nodes::<T, I>::insert(node_index, elem.hash());
|
||||
}
|
||||
// We are storing full node off-chain (using indexing API).
|
||||
Self::store_to_offchain(node_index, parent_hash, &elem);
|
||||
|
||||
// Increase the indices.
|
||||
if let Node::Data(..) = elem {
|
||||
leaf_index += 1;
|
||||
}
|
||||
node_index += 1;
|
||||
}
|
||||
|
||||
// Update current number of leaves.
|
||||
NumberOfLeaves::<T, I>::put(leaf_index);
|
||||
|
||||
// And remove all remaining items from `peaks_before` collection.
|
||||
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: FullLeaf,
|
||||
{
|
||||
fn store_to_offchain(
|
||||
pos: NodeIndex,
|
||||
parent_hash: <T as pezframe_system::Config>::Hash,
|
||||
node: &NodeOf<T, I, L>,
|
||||
) {
|
||||
let encoded_node = node.encode();
|
||||
// We store this leaf offchain keyed by `(parent_hash, node_index)` to make it
|
||||
// fork-resistant. The MMR client gadget task will "canonicalize" it on the first
|
||||
// finality notification that follows, when we are not worried about forks anymore.
|
||||
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
|
||||
);
|
||||
OffchainStorage::set::<T, I>(&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>>) {
|
||||
// A sorted (ascending) collection of peak indices before and after insertion.
|
||||
// both collections may share a common prefix.
|
||||
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();
|
||||
|
||||
// Consume a common prefix between `peaks_before` and `peaks_after`,
|
||||
// since that's something we will not be touching anyway.
|
||||
while peaks_before.peek() == peaks_after.peek() {
|
||||
peaks_before.next();
|
||||
peaks_after.next();
|
||||
}
|
||||
|
||||
// what's left in both collections is:
|
||||
// 1. Old peaks to remove from storage
|
||||
// 2. New peaks to persist in storage
|
||||
(peaks_before, peaks_after)
|
||||
}
|
||||
Reference in New Issue
Block a user