Add a non-canonical SCALE derivations feature

Enables representing IUMT within `StorageValues`. Applied to a variety of
values.

Fixes a bug where `Some([0; 32])` would be considered a valid block anchor.
This commit is contained in:
Luke Parker
2025-03-06 03:19:29 -05:00
parent 35db2924b4
commit b08ae8e6a7
6 changed files with 40 additions and 41 deletions

View File

@@ -1,7 +1,6 @@
use core::marker::PhantomData;
use alloc::{vec, vec::Vec};
use borsh::{BorshSerialize, BorshDeserialize};
use borsh::BorshSerialize;
use frame_support::pallet_prelude::*;
@@ -10,13 +9,14 @@ use serai_abi::{
*,
};
/// A wrapper around a `StorageValue` which offers a high-level API as an IUMT.
struct IncrementalUnbalancedMerkleTree<
T: frame_support::StorageValue<Vec<u8>, Query = Option<Vec<u8>>>,
T: frame_support::StorageValue<Iumt, Query = Option<Iumt>>,
const BRANCH_TAG: u8 = 1,
const LEAF_TAG: u8 = 0,
>(PhantomData<T>);
impl<
T: frame_support::StorageValue<Vec<u8>, Query = Option<Vec<u8>>>,
T: frame_support::StorageValue<Iumt, Query = Option<Iumt>>,
const BRANCH_TAG: u8,
const LEAF_TAG: u8,
> IncrementalUnbalancedMerkleTree<T, BRANCH_TAG, LEAF_TAG>
@@ -27,7 +27,7 @@ impl<
fn new_expecting_none() {
T::mutate(|value| {
assert!(value.is_none());
*value = Some(borsh::to_vec(&Iumt::new()).unwrap());
*value = Some(Iumt::new());
});
}
/// Append a leaf to the Merkle tree.
@@ -37,26 +37,21 @@ impl<
let leaf = sp_core::blake2_256(&borsh::to_vec(&(LEAF_TAG, leaf)).unwrap());
T::mutate(|value| {
let mut tree = Iumt::deserialize_reader(&mut value.as_ref().unwrap().as_slice()).unwrap();
let tree = value.as_mut().unwrap();
tree.append(BRANCH_TAG, leaf);
*value = Some(borsh::to_vec(&tree).unwrap());
})
}
/// Get the unbalanced merkle tree.
///
/// Panics if no Merkle tree was present.
fn get() -> UnbalancedMerkleTree {
Iumt::deserialize_reader(&mut T::get().unwrap().as_slice()).unwrap().calculate(BRANCH_TAG)
T::get().unwrap().calculate(BRANCH_TAG)
}
/// Take the Merkle tree.
///
/// Panics if no Merkle tree was present.
fn take() -> UnbalancedMerkleTree {
T::mutate(|value| {
let tree = Iumt::deserialize_reader(&mut value.as_ref().unwrap().as_slice()).unwrap();
*value = None;
tree.calculate(BRANCH_TAG)
})
T::mutate(|value| value.take().unwrap().calculate(BRANCH_TAG))
}
}
@@ -70,20 +65,20 @@ mod pallet {
/// The Merkle tree of all blocks added to the blockchain.
#[pallet::storage]
#[pallet::unbounded]
pub(super) type BlocksCommitment<T: Config> = StorageValue<_, Vec<u8>, OptionQuery>;
pub(super) type BlocksCommitment<T: Config> = StorageValue<_, Iumt, OptionQuery>;
pub(super) type BlocksCommitmentMerkle<T> = IncrementalUnbalancedMerkleTree<BlocksCommitment<T>>;
/// The Merkle tree of all transactions within the current block.
#[pallet::storage]
#[pallet::unbounded]
pub(super) type BlockTransactionsCommitment<T: Config> = StorageValue<_, Vec<u8>, OptionQuery>;
pub(super) type BlockTransactionsCommitment<T: Config> = StorageValue<_, Iumt, OptionQuery>;
pub(super) type BlockTransactionsCommitmentMerkle<T> =
IncrementalUnbalancedMerkleTree<BlockTransactionsCommitment<T>>;
/// The hashes of events caused by the current transaction.
#[pallet::storage]
#[pallet::unbounded]
pub(super) type TransactionEvents<T: Config> = StorageValue<_, Vec<u8>, OptionQuery>;
pub(super) type TransactionEvents<T: Config> = StorageValue<_, Iumt, OptionQuery>;
pub(super) type TransactionEventsMerkle<T> = IncrementalUnbalancedMerkleTree<
TransactionEvents<T>,
TRANSACTION_EVENTS_COMMITMENT_BRANCH_TAG,
@@ -92,7 +87,7 @@ mod pallet {
/// The roots of the Merkle trees of each transaction's events.
#[pallet::storage]
#[pallet::unbounded]
pub(super) type BlockEventsCommitment<T: Config> = StorageValue<_, Vec<u8>, OptionQuery>;
pub(super) type BlockEventsCommitment<T: Config> = StorageValue<_, Iumt, OptionQuery>;
pub(super) type BlockEventsCommitmentMerkle<T> = IncrementalUnbalancedMerkleTree<
BlockEventsCommitment<T>,
EVENTS_COMMITMENT_BRANCH_TAG,
@@ -105,12 +100,7 @@ mod pallet {
StorageMap<_, Blake2_128Concat, T::AccountId, T::Nonce, ValueQuery>;
#[pallet::config]
pub trait Config:
frame_system::Config<
Block: sp_runtime::traits::Block<Header: sp_runtime::traits::Header<Hash: Into<[u8; 32]>>>,
>
{
}
pub trait Config: frame_system::Config<Hash: Into<[u8; 32]>> {}
#[pallet::pallet]
pub struct Pallet<T>(_);
@@ -141,12 +131,11 @@ pub(super) use pallet::*;
pub struct StartOfBlock<T: Config>(PhantomData<T>);
impl<T: Config> frame_support::traits::PreInherents for StartOfBlock<T> {
fn pre_inherents() {
let parent_hash = frame_system::Pallet::<T>::parent_hash();
Blocks::<T>::set(parent_hash, Some(()));
// TODO: Better detection of genesis
if parent_hash == Default::default() {
if frame_system::Pallet::<T>::block_number().is_zero() {
BlocksCommitmentMerkle::<T>::new_expecting_none();
} else {
let parent_hash = frame_system::Pallet::<T>::parent_hash();
Blocks::<T>::set(parent_hash, Some(()));
let parent_hash: [u8; 32] = parent_hash.into();
BlocksCommitmentMerkle::<T>::append(&parent_hash);
}