ghost-node/pallets/helpers/src/merkle_tree.rs
Uncle Stinky 43ec3492ef
merkle tree prefixes added
Signed-off-by: Uncle Stinky <uncle.stinky@ghostchain.io>
2026-09-08 14:20:56 +03:00

604 lines
18 KiB
Rust

use ghost_traits::hashing::GhostHasher;
use sp_arithmetic::traits::AtLeast8BitUnsigned;
use sp_runtime::traits::UniqueSaturatedInto;
use sp_std::vec::Vec;
const LEAF_PREFIX: u8 = 0;
const NODE_PREFIX: u8 = 1;
fn leaf_prefixed<H>(preimage: &[u8]) -> H::Hash
where
H: GhostHasher,
{
let mut prefixed_data = sp_std::vec![LEAF_PREFIX; 1 + preimage.len()];
prefixed_data[1..].copy_from_slice(preimage);
H::hash(&prefixed_data)
}
fn node_prefixed<H>(preimage1: &[u8], preimage2: &[u8]) -> H::Hash
where
H: GhostHasher,
{
let total_len = preimage1.len() + preimage2.len();
let mut prefixed_data = sp_std::vec![NODE_PREFIX; 1 + total_len];
prefixed_data[1..1 + preimage1.len()].copy_from_slice(preimage1);
prefixed_data[1 + preimage1.len()..].copy_from_slice(preimage2);
H::hash(&prefixed_data)
}
pub fn generate_tree<H, I, S, F, E>(
max_index: I,
raw_values: S,
generate_preimage: F,
) -> Result<Vec<H::Hash>, E>
where
H: GhostHasher,
S: IntoIterator,
I: AtLeast8BitUnsigned + UniqueSaturatedInto<usize>,
F: Fn(S::Item) -> Result<(usize, Vec<u8>), E>,
{
let num_of_leaves: usize = (max_index + I::one()).unique_saturated_into();
let padded_leaves = num_of_leaves.next_power_of_two();
let total_capacity = (2 * padded_leaves) - 1;
let empty_hash = H::empty();
let empty_leaf_hash = leaf_prefixed::<H>(empty_hash.as_ref());
let mut merkle_tree = sp_std::vec![empty_leaf_hash; total_capacity];
for item in raw_values.into_iter() {
let (index, preimage) = generate_preimage(item)?;
if index >= padded_leaves { return Ok(sp_std::vec![]); }
merkle_tree[index] = leaf_prefixed::<H>(&preimage)
}
let mut layer_start = 0;
let mut current_layer_len = padded_leaves;
let mut write_ptr = padded_leaves;
while current_layer_len > 1 {
for i in (0..current_layer_len).step_by(2) {
let layer_index = layer_start + i;
let left_bytes = merkle_tree[layer_index].as_ref();
let right_bytes = merkle_tree[layer_index | 1].as_ref();
merkle_tree[write_ptr] = node_prefixed::<H>(left_bytes, right_bytes);
write_ptr += 1;
}
layer_start += current_layer_len;
current_layer_len >>= 1;
}
Ok(merkle_tree)
}
pub fn generate_proof<H, I>(
merkle_tree: &[H::Hash],
max_index: I,
index: I,
) -> Vec<H::Hash>
where
H: GhostHasher,
I: AtLeast8BitUnsigned + UniqueSaturatedInto<usize>,
{
if index > max_index { return sp_std::vec![]; }
let num_of_leaves: usize = (max_index + I::one()).unique_saturated_into();
let mut current_index: usize = index.unique_saturated_into();
let mut current_layer_len = num_of_leaves.next_power_of_two();
let tree_height = current_layer_len.trailing_zeros() as usize;
let mut proof = Vec::with_capacity(tree_height);
let mut layer_start = 0;
while current_layer_len > 1 {
let sibling_idx_in_layer = current_index ^ 1;
let sibling_hash = merkle_tree[layer_start + sibling_idx_in_layer];
proof.push(sibling_hash);
layer_start += current_layer_len;
current_layer_len >>= 1;
current_index >>= 1;
}
proof
}
pub fn verify_tree_proof<H, I>(
preimage: &[u8],
merkle_proof: &[H::Hash],
merkle_root: H::Hash,
index: I,
) -> bool
where
H: GhostHasher,
I: PartialOrd + UniqueSaturatedInto<usize>,
{
let mut current_index: usize = index.unique_saturated_into();
if current_index >= (1 << merkle_proof.len()) { return false; }
let mut current_hash = leaf_prefixed::<H>(preimage);
for sibling in merkle_proof.iter() {
let sibling_bytes = sibling.as_ref();
let hash_bytes = current_hash.as_ref();
if current_index % 2 == 0 {
current_hash = node_prefixed::<H>(hash_bytes, sibling_bytes);
} else {
current_hash = node_prefixed::<H>(sibling_bytes, hash_bytes);
}
current_index >>= 1;
}
current_hash == merkle_root
}
#[cfg(test)]
mod tests {
use super::*;
use sp_core::H256;
use sp_io::hashing::blake2_256;
pub struct TestBlake2Hasher;
impl GhostHasher for TestBlake2Hasher {
type Hash = H256;
type HashBytes = [u8; 32];
fn hash(data: &[u8]) -> Self::Hash {
H256::from(blake2_256(data))
}
fn empty() -> Self::Hash {
H256::zero()
}
fn hash_len() -> usize {
H256::len_bytes()
}
}
#[test]
fn test_generate_tree_perfect_power_of_two() {
let max_index = 3u32;
let raw_data = sp_std::vec![
(0, sp_std::vec![1]),
(1, sp_std::vec![2]),
(2, sp_std::vec![3]),
(3, sp_std::vec![4]),
];
let tree = generate_tree::<TestBlake2Hasher, u32, _, _, ()>(
max_index,
raw_data,
|(index, bytes)| Ok((index, bytes)),
)
.unwrap();
assert_eq!(tree.len(), 7);
let h0 = TestBlake2Hasher::hash(&[ &[0u8], [1].as_slice() ].concat());
let h1 = TestBlake2Hasher::hash(&[ &[0u8], [2].as_slice() ].concat());
let h2 = TestBlake2Hasher::hash(&[ &[0u8], [3].as_slice() ].concat());
let h3 = TestBlake2Hasher::hash(&[ &[0u8], [4].as_slice() ].concat());
let mut c1 = [0u8; 65];
c1[0] = NODE_PREFIX;
c1[1..33].copy_from_slice(h0.as_ref());
c1[33..].copy_from_slice(h1.as_ref());
let parent_left = TestBlake2Hasher::hash(&c1);
let mut c2 = [0u8; 65];
c2[0] = NODE_PREFIX;
c2[1..33].copy_from_slice(h2.as_ref());
c2[33..].copy_from_slice(h3.as_ref());
let parent_right = TestBlake2Hasher::hash(&c2);
let mut c_root = [0u8; 65];
c_root[0] = NODE_PREFIX;
c_root[1..33].copy_from_slice(parent_left.as_ref());
c_root[33..].copy_from_slice(parent_right.as_ref());
let expected_root = TestBlake2Hasher::hash(&c_root);
assert_eq!(tree.last().unwrap(), &expected_root);
}
#[test]
fn test_generate_tree_with_padding_hashing() {
let max_index = 2u32;
let raw_data = sp_std::vec![
(0, sp_std::vec![1]),
(1, sp_std::vec![2]),
(2, sp_std::vec![3]),
];
let tree = generate_tree::<TestBlake2Hasher, u32, _, _, ()>(
max_index,
raw_data,
|(index, bytes)| Ok((index, bytes)),
)
.unwrap();
let mut empty_data = sp_std::vec![LEAF_PREFIX];
empty_data.extend_from_slice(&H256::zero().to_fixed_bytes());
let empty_hash = TestBlake2Hasher::hash(&empty_data);
assert_eq!(tree.len(), 7);
assert_eq!(tree[3], empty_hash);
let mut leaf2_data = sp_std::vec![LEAF_PREFIX];
leaf2_data.extend_from_slice(&[3]);
let h2 = TestBlake2Hasher::hash(&leaf2_data);
let mut combined_right = [0u8; 65];
combined_right[0] = NODE_PREFIX;
combined_right[1..33].copy_from_slice(h2.as_ref());
combined_right[33..].copy_from_slice(empty_hash.as_ref());
let expected_parent_right = TestBlake2Hasher::hash(&combined_right);
assert_eq!(tree[5], expected_parent_right);
}
#[test]
fn test_generate_tree_determinism() {
let max_index = 1u32;
let data_1 = sp_std::vec![(0, sp_std::vec![100]), (1, sp_std::vec![200])];
let data_2 = sp_std::vec![(0, sp_std::vec![100]), (1, sp_std::vec![200])];
let data_3 = sp_std::vec![(0, sp_std::vec![101]), (1, sp_std::vec![200])];
let tree_1 =
generate_tree::<TestBlake2Hasher, u32, _, _, ()>(max_index, data_1, |(i, b)| {
Ok((i, b))
})
.unwrap();
let tree_2 =
generate_tree::<TestBlake2Hasher, u32, _, _, ()>(max_index, data_2, |(i, b)| {
Ok((i, b))
})
.unwrap();
let tree_3 =
generate_tree::<TestBlake2Hasher, u32, _, _, ()>(max_index, data_3, |(i, b)| {
Ok((i, b))
})
.unwrap();
assert_eq!(tree_1.last().unwrap(), tree_2.last().unwrap());
assert_ne!(tree_1.last().unwrap(), tree_3.last().unwrap());
}
#[test]
fn test_generate_tree_single_element() {
let max_index = 0u32;
let raw_data = sp_std::vec![(0, sp_std::vec![42])];
let tree = generate_tree::<TestBlake2Hasher, u32, _, _, ()>(
max_index,
raw_data,
|(index, bytes)| Ok((index, bytes)),
)
.unwrap();
assert_eq!(tree.len(), 1);
assert_eq!(tree[0], TestBlake2Hasher::hash(&vec![LEAF_PREFIX, 42]));
}
#[test]
fn test_proof_and_verification_end_to_end() {
let max_index = 3u32;
let raw_data = sp_std::vec![
(0, sp_std::vec![1]),
(1, sp_std::vec![2]),
(2, sp_std::vec![3]),
(3, sp_std::vec![4]),
];
let tree = generate_tree::<TestBlake2Hasher, u32, _, _, ()>(
max_index,
raw_data,
|(index, bytes)| Ok((index, bytes)),
)
.unwrap();
let root = *tree.last().unwrap();
let target_index = 2u32;
let proof = generate_proof::<TestBlake2Hasher, u32>(&tree, max_index, target_index);
assert_eq!(proof.len(), 2);
let preimage = sp_std::vec![3];
let is_valid =
verify_tree_proof::<TestBlake2Hasher, u32>(&preimage, &proof, root, target_index);
assert!(is_valid);
}
#[test]
fn test_proof_index_out_of_bounds() {
let max_index = 3u32;
let raw_data = sp_std::vec![
(0, sp_std::vec![1]),
(1, sp_std::vec![2]),
(2, sp_std::vec![3]),
(3, sp_std::vec![4]),
];
let tree = generate_tree::<TestBlake2Hasher, u32, _, _, ()>(
max_index,
raw_data,
|(index, bytes)| Ok((index, bytes)),
)
.unwrap();
let root = *tree.last().unwrap();
let target_index = 2u32;
let proof = generate_proof::<TestBlake2Hasher, u32>(&tree, max_index, target_index);
assert_eq!(proof.len(), 2);
let preimage = sp_std::vec![3];
let is_valid =
verify_tree_proof::<TestBlake2Hasher, u32>(&preimage, &proof, root, target_index + 1);
assert!(!is_valid);
}
#[test]
fn test_proof_contains_correct_siblings() {
let max_index = 3u32;
let raw_data = sp_std::vec![
(0, sp_std::vec![10]),
(1, sp_std::vec![20]),
(2, sp_std::vec![30]),
(3, sp_std::vec![40]),
];
let tree = generate_tree::<TestBlake2Hasher, u32, _, _, ()>(
max_index,
raw_data,
|(index, bytes)| Ok((index, bytes)),
)
.unwrap();
let h0 = tree[0];
let h1 = tree[1];
let h2 = tree[2];
let h3 = tree[3];
let parent_left = tree[4]; // hash(h0 + h1)
let parent_right = tree[5]; // hash(h2 + h3)
let proof_for_0 = generate_proof::<TestBlake2Hasher, u32>(&tree, max_index, 0);
assert_eq!(proof_for_0[0], h1);
assert_eq!(proof_for_0[1], parent_right);
let proof_for_1 = generate_proof::<TestBlake2Hasher, u32>(&tree, max_index, 1);
assert_eq!(proof_for_1[0], h0);
assert_eq!(proof_for_1[1], parent_right);
let proof_for_2 = generate_proof::<TestBlake2Hasher, u32>(&tree, max_index, 2);
assert_eq!(proof_for_2[0], h3);
assert_eq!(proof_for_2[1], parent_left);
let proof_for_3 = generate_proof::<TestBlake2Hasher, u32>(&tree, max_index, 3);
assert_eq!(proof_for_3[0], h2);
assert_eq!(proof_for_3[1], parent_left);
}
#[test]
fn test_proof_for_padded_tree_leaf() {
let max_index = 2u32;
let raw_data = sp_std::vec![
(0, sp_std::vec![1]),
(1, sp_std::vec![2]),
(2, sp_std::vec![3]),
];
let tree = generate_tree::<TestBlake2Hasher, u32, _, _, ()>(
max_index,
raw_data,
|(index, bytes)| Ok((index, bytes)),
)
.unwrap();
let proof = generate_proof::<TestBlake2Hasher, u32>(&tree, max_index, 2);
let mut empty_data = sp_std::vec![LEAF_PREFIX];
empty_data.extend_from_slice(&H256::zero().to_fixed_bytes());
let empty_hash = TestBlake2Hasher::hash(&empty_data);
assert_eq!(proof[0], empty_hash);
let root = *tree.last().unwrap();
let is_valid =
verify_tree_proof::<TestBlake2Hasher, u32>(&sp_std::vec![3], &proof, root, 2);
assert!(is_valid);
}
#[test]
fn test_proof_for_single_element_tree() {
let max_index = 0u32;
let raw_data = sp_std::vec![(0, sp_std::vec![99])];
let tree = generate_tree::<TestBlake2Hasher, u32, _, _, ()>(
max_index,
raw_data,
|(index, bytes)| Ok((index, bytes)),
)
.unwrap();
let proof = generate_proof::<TestBlake2Hasher, u32>(&tree, max_index, 0);
assert!(proof.is_empty());
}
#[test]
fn test_verify_tree_proof_with_h0_h3_and_random_values() {
let max_index = 3u32;
let leaf_0 = sp_std::vec![10u8];
let leaf_1 = sp_std::vec![20u8];
let leaf_2 = sp_std::vec![30u8];
let leaf_3 = sp_std::vec![40u8];
let raw_data = sp_std::vec![
(0, leaf_0.clone()),
(1, leaf_1.clone()),
(2, leaf_2.clone()),
(3, leaf_3.clone())
];
let tree = generate_tree::<TestBlake2Hasher, u32, _, _, ()>(
max_index,
raw_data,
|(index, bytes)| Ok((index, bytes)),
)
.unwrap();
let root = *tree.last().unwrap();
let h0 = tree[0];
let h1 = tree[1];
let h2 = tree[2];
let h3 = tree[3];
let proof_for_1 = generate_proof::<TestBlake2Hasher, u32>(&tree, max_index, 1);
assert_eq!(proof_for_1[0], h0);
let is_valid_1 = verify_tree_proof::<TestBlake2Hasher, u32>(&leaf_1, &proof_for_1, root, 1);
assert!(is_valid_1);
let proof_for_2 = generate_proof::<TestBlake2Hasher, u32>(&tree, max_index, 2);
assert_eq!(proof_for_2[0], h3);
let is_valid_2 = verify_tree_proof::<TestBlake2Hasher, u32>(&leaf_2, &proof_for_2, root, 2);
assert!(is_valid_2);
let proof_for_0 = generate_proof::<TestBlake2Hasher, u32>(&tree, max_index, 0);
assert_eq!(proof_for_0[0], h1);
assert!(verify_tree_proof::<TestBlake2Hasher, u32>(
&leaf_0,
&proof_for_0,
root,
0
));
let proof_for_3 = generate_proof::<TestBlake2Hasher, u32>(&tree, max_index, 3);
assert_eq!(proof_for_3[0], h2);
assert!(verify_tree_proof::<TestBlake2Hasher, u32>(
&leaf_3,
&proof_for_3,
root,
3
));
let mut bad_proof = proof_for_1.clone();
bad_proof[0] = H256::random();
let is_valid_bad_proof =
verify_tree_proof::<TestBlake2Hasher, u32>(&leaf_1, &bad_proof, root, 1);
assert!(!is_valid_bad_proof);
let bad_preimage = vec![99u8];
let is_valid_bad_preimage =
verify_tree_proof::<TestBlake2Hasher, u32>(&bad_preimage, &proof_for_1, root, 1);
assert!(!is_valid_bad_preimage);
}
#[test]
fn test_verify_tree_proof_deep_tree_depth_4() {
let max_index = 15u32;
let mut raw_data = sp_std::vec![];
for i in 0..16 {
raw_data.push((i, sp_std::vec![i as u8]));
}
let tree = generate_tree::<TestBlake2Hasher, u32, _, _, ()>(
max_index,
raw_data,
|(index, bytes)| Ok((index, bytes)),
)
.unwrap();
let root = *tree.last().unwrap();
assert_eq!(tree.len(), 31);
let target_index = 11u32;
let preimage = vec![target_index as u8];
let proof = generate_proof::<TestBlake2Hasher, u32>(&tree, max_index, target_index);
assert_eq!(proof.len(), 4);
let h10 = tree[10];
assert_eq!(proof[0], h10);
let is_valid =
verify_tree_proof::<TestBlake2Hasher, u32>(&preimage, &proof, root, target_index);
assert!(is_valid);
let is_valid_wrong_index =
verify_tree_proof::<TestBlake2Hasher, u32>(&preimage, &proof, root, 4);
assert!(!is_valid_wrong_index);
}
#[test]
fn test_verification_index_at_upper_bound() {
let max_index = 3u32;
let raw_data = sp_std::vec![
(0, sp_std::vec![1]),
(1, sp_std::vec![2]),
(2, sp_std::vec![3]),
(3, sp_std::vec![4]),
];
let tree = generate_tree::<TestBlake2Hasher, u32, _, _, ()>(
max_index,
raw_data,
|(index, bytes)| Ok((index, bytes)),
)
.unwrap();
let root = *tree.last().unwrap();
let target_index = 3u32;
let proof = generate_proof::<TestBlake2Hasher, u32>(&tree, max_index, target_index);
assert_eq!(proof.len(), 2);
let preimage = sp_std::vec![4];
let is_valid =
verify_tree_proof::<TestBlake2Hasher, u32>(&preimage, &proof, root, target_index);
assert!(is_valid);
}
#[test]
fn test_verification_fails_when_index_out_of_bounds() {
let max_index = 3u32;
let raw_data = sp_std::vec![
(0, sp_std::vec![1]),
(1, sp_std::vec![2]),
(2, sp_std::vec![3]),
(3, sp_std::vec![4]),
];
let tree = generate_tree::<TestBlake2Hasher, u32, _, _, ()>(
max_index,
raw_data,
|(index, bytes)| Ok((index, bytes)),
)
.unwrap();
let root = *tree.last().unwrap();
let proof = generate_proof::<TestBlake2Hasher, u32>(&tree, max_index, 3u32);
let target_index = 4u32;
let preimage = sp_std::vec![4];
let is_valid =
verify_tree_proof::<TestBlake2Hasher, u32>(&preimage, &proof, root, target_index);
assert!(!is_valid);
}
}