use ghost_traits::hashing::GhostHasher; fn combine_values(left: V, right: V) -> H::Hash where H: GhostHasher, V: AsRef<[u8]>, { let hash_len = H::hash_len(); let mut combined = sp_std::vec![0u8; hash_len * 2]; combined[..hash_len].copy_from_slice(left.as_ref()); combined[hash_len..].copy_from_slice(right.as_ref()); H::hash(&combined) } pub fn cumulative_hash(raw_values: S) -> H::Hash where H: GhostHasher, S: IntoIterator, S::Item: AsRef<[u8]>, { raw_values .into_iter() .fold(H::empty(), |acc_hash, raw_value| { let hashed_value = H::hash(raw_value.as_ref()); combine_values::(acc_hash, hashed_value) }) } pub fn sequential_hash(raw_values: S) -> H::Hash where H: GhostHasher, S: IntoIterator, S::Item: AsRef<[u8]>, { let mut iter = raw_values.into_iter().peekable(); let Some(first_value) = iter.next() else { return H::empty(); }; if iter.peek().is_none() { return H::hash(first_value.as_ref()); } let current_hash = H::from_slice(first_value.as_ref()); iter.fold(current_hash, |acc_hash, raw_value| { let next_hash = H::from_slice(raw_value.as_ref()); combine_values::(acc_hash, next_hash) }) } pub fn verify_hash_ancestry( tx_block_header_bytes: &[u8], ancestry_headers: S, trusted_top_block_hash: H::Hash, ) -> bool where H: GhostHasher, H::Hash: PartialEq + Copy + AsRef<[u8]> + AsMut<[u8]>, S: IntoIterator, S::Item: AsRef<[u8]>, { ancestry_headers .into_iter() .try_fold( sequential_hash::(core::iter::once(tx_block_header_bytes)), |acc_parent_hash, current_header| { let header_ref = current_header.as_ref(); if header_ref.len() < 36 { return None; } let mut current_prev_hash = [0u8; 32]; current_prev_hash.copy_from_slice(&header_ref[4..36]); if current_prev_hash != acc_parent_hash.as_ref() { return None; } let current_header_iter = core::iter::once(header_ref); Some(sequential_hash::(current_header_iter)) }, ) .map(|mut final_le_hash| { final_le_hash.as_mut().reverse(); final_le_hash == trusted_top_block_hash }) .unwrap_or(false) } #[cfg(test)] mod tests { use super::*; use sp_core::H256; use sp_io::hashing::{blake2_256, sha2_256}; use sp_std::vec::Vec; 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() } } pub struct TestSha2Hasher; impl GhostHasher for TestSha2Hasher { type Hash = H256; type HashBytes = [u8; 32]; fn hash(data: &[u8]) -> Self::Hash { let first_pass: [u8; 32] = sha2_256(data); let second_pass: [u8; 32] = sha2_256(&first_pass); H256::from(second_pass) } fn hash_len() -> usize { H256::len_bytes() } fn empty() -> Self::Hash { H256::zero() } } fn mock_sequential_hash(header: &[u8; 80]) -> H256 { sequential_hash::(core::iter::once(header)) } fn create_header(prev_hash: [u8; 32]) -> [u8; 80] { let mut header = [0u8; 80]; header[4..36].copy_from_slice(&prev_hash); header } #[test] fn test_generate_empty_values() { let empty_list: Vec> = sp_std::vec![]; let result = cumulative_hash::(empty_list); assert_eq!(H256::zero(), result); } #[test] fn test_cumulative_hash_determinism_and_avalanche() { let dataset_1 = sp_std::vec![vec![1, 2, 3], vec![4, 5, 6], vec![7, 8, 9]]; let dataset_2 = sp_std::vec![vec![1, 2, 3], vec![4, 5, 6], vec![7, 8, 9]]; let dataset_3 = sp_std::vec![vec![9, 2, 3], vec![4, 5, 6], vec![7, 8, 9]]; let hash_1 = cumulative_hash::(dataset_1); let hash_2 = cumulative_hash::(dataset_2); let hash_3 = cumulative_hash::(dataset_3); assert_eq!(hash_1, hash_2); assert_ne!(hash_1, hash_3); assert_ne!(hash_2, hash_3); } #[test] fn test_cumulative_hash_order_sensitive() { let dataset_forward = sp_std::vec![sp_std::vec![1, 1, 1], sp_std::vec![2, 2, 2]]; let dataset_reversed = sp_std::vec![sp_std::vec![2, 2, 2], sp_std::vec![1, 1, 1]]; let hash_forward = cumulative_hash::(dataset_forward); let hash_reversed = cumulative_hash::(dataset_reversed); assert_ne!(hash_forward, hash_reversed); } #[test] fn test_cumulative_hash_with_slices() { let slice_data: &[&[u8]] = &[&[1, 2, 3], &[4, 5, 6]]; let hash_from_slice = cumulative_hash::(slice_data.iter()); assert_ne!(hash_from_slice, H256::zero()); } #[test] fn test_sequential_hash_empty() { let empty_list: Vec> = sp_std::vec![]; let result = sequential_hash::(empty_list); assert_eq!(H256::zero(), result); } #[test] fn test_sequential_hash_single_element() { let single_item = sp_std::vec![sp_std::vec![1, 2, 3]]; let result = sequential_hash::(single_item); let expected = TestBlake2Hasher::hash(&[1, 2, 3]); assert_eq!(result, expected); } #[test] fn test_sequential_hash_determinism_and_order() { let data_1 = sp_std::vec![sp_std::vec![1], sp_std::vec![2], sp_std::vec![3]]; let data_2 = sp_std::vec![sp_std::vec![1], sp_std::vec![2], sp_std::vec![3]]; let data_reversed = sp_std::vec![sp_std::vec![3], sp_std::vec![2], sp_std::vec![1]]; let hash_1 = sequential_hash::(data_1); let hash_2 = sequential_hash::(data_2); let hash_reversed = sequential_hash::(data_reversed); assert_eq!(hash_1, hash_2); assert_ne!(hash_1, hash_reversed); } #[test] fn test_sequential_vs_cumulative_hash() { let dataset = sp_std::vec![sp_std::vec![1, 2, 3], sp_std::vec![4, 5, 6]]; let seq_hash = sequential_hash::(dataset.clone()); let cum_hash = cumulative_hash::(dataset); assert_ne!(seq_hash, cum_hash); } #[test] fn test_sequential_hash_with_slices() { let slice_data: &[&[u8]] = &[&[10, 20], &[30, 40]]; let result = sequential_hash::(slice_data.iter()); assert_ne!(result, H256::zero()); } #[test] fn test_verify_in_current_block_with_empty_ancestry() { let tx_header = [0x11u8; 80]; let tx_block_le_hash = mock_sequential_hash(&tx_header); let mut trusted_bytes = tx_block_le_hash.0; trusted_bytes.reverse(); let trusted_top_block_hash = H256::from(trusted_bytes); let result = verify_hash_ancestry::( &tx_header, &[], trusted_top_block_hash, ); assert!(result); } #[test] fn test_verify_with_single_ancestor_header() { let tx_header_100 = [0x22u8; 80]; let hash_100_le = mock_sequential_hash(&tx_header_100); let header_101 = create_header(hash_100_le.0); let hash_101_le = mock_sequential_hash(&header_101); let mut trusted_bytes = hash_101_le.0; trusted_bytes.reverse(); let trusted_top_block_hash = H256::from(trusted_bytes); let ancestry = [header_101]; let result = verify_hash_ancestry::( &tx_header_100, &ancestry, trusted_top_block_hash, ); assert!(result); } #[test] fn test_verify_with_multiple_ancestor_headers_chain() { let header_100 = [0x55u8; 80]; let hash_100_le = mock_sequential_hash(&header_100); let header_101 = create_header(hash_100_le.0); let hash_101_le = mock_sequential_hash(&header_101); let header_102 = create_header(hash_101_le.0); let hash_102_le = mock_sequential_hash(&header_102); let header_103 = create_header(hash_102_le.0); let hash_103_le = mock_sequential_hash(&header_103); let mut trusted_bytes = hash_103_le.0; trusted_bytes.reverse(); let trusted_top_block_hash = H256::from(trusted_bytes); let ancestry = [header_101, header_102, header_103]; let result = verify_hash_ancestry::( &header_100, &ancestry, trusted_top_block_hash, ); assert!(result); } #[test] fn test_verify_fails_if_chain_is_broken() { let header_100 = [0xAAu8; 80]; let broken_header_101 = create_header([0xFF; 32]); let hash_101_le = mock_sequential_hash(&broken_header_101); let mut trusted_bytes = hash_101_le.0; trusted_bytes.reverse(); let trusted_top_block_hash = H256::from(trusted_bytes); let ancestry = [broken_header_101]; let result = verify_hash_ancestry::( &header_100, &ancestry, trusted_top_block_hash, ); assert!(!result); } }