merkle tree prefixes added

Signed-off-by: Uncle Stinky <uncle.stinky@ghostchain.io>
This commit is contained in:
Uncle Stinky 2026-09-08 14:20:56 +03:00
parent 3cf96b1820
commit 43ec3492ef
Signed by: st1nky
GPG Key ID: 016064BD97603B40
11 changed files with 171 additions and 160 deletions

8
Cargo.lock generated
View File

@ -3733,7 +3733,7 @@ dependencies = [
[[package]] [[package]]
name = "ghost-governance" name = "ghost-governance"
version = "0.3.5" version = "0.3.6"
dependencies = [ dependencies = [
"frame-benchmarking", "frame-benchmarking",
"frame-support", "frame-support",
@ -3757,7 +3757,7 @@ dependencies = [
[[package]] [[package]]
name = "ghost-helpers" name = "ghost-helpers"
version = "0.0.7" version = "0.0.8"
dependencies = [ dependencies = [
"frame-support", "frame-support",
"ghost-traits", "ghost-traits",
@ -4085,7 +4085,7 @@ dependencies = [
[[package]] [[package]]
name = "ghost-traits" name = "ghost-traits"
version = "0.4.4" version = "0.4.5"
dependencies = [ dependencies = [
"frame-support", "frame-support",
"frost-core", "frost-core",
@ -4109,7 +4109,7 @@ dependencies = [
[[package]] [[package]]
name = "ghost-weaver" name = "ghost-weaver"
version = "0.0.5" version = "0.0.6"
dependencies = [ dependencies = [
"frame-benchmarking", "frame-benchmarking",
"frame-support", "frame-support",

View File

@ -1,6 +1,6 @@
[package] [package]
name = "ghost-governance" name = "ghost-governance"
version = "0.3.5" version = "0.3.6"
description = "Full-chain and cross-chain governance pallet with early adopter share claims" description = "Full-chain and cross-chain governance pallet with early adopter share claims"
license.workspace = true license.workspace = true
authors.workspace = true authors.workspace = true

View File

@ -40,24 +40,27 @@ mod benchmarks {
}; };
let preimage_init = claim_package_init.get_preimage(&dummy_evm_address, network_id); let preimage_init = claim_package_init.get_preimage(&dummy_evm_address, network_id);
let some_hash = SubstrateKeccakHasher::hash(&preimage_init);
let mut leaf_combined = sp_std::vec![0u8; 1 + preimage_init.len()];
leaf_combined[1..].copy_from_slice(&preimage_init);
let some_hash = SubstrateKeccakHasher::hash(&leaf_combined);
let mut current_hash = some_hash; let mut current_hash = some_hash;
let mut proof_hashes = Vec::with_capacity(max_proof_depth as usize); let mut proof_hashes = Vec::with_capacity(max_proof_depth as usize);
let mut current_index = target_index as usize; let mut current_index = target_index as usize;
let hash_len = SubstrateKeccakHasher::hash_len(); let hash_len = SubstrateKeccakHasher::hash_len();
let mut combined = sp_std::vec![0u8; hash_len * 2]; let mut combined = sp_std::vec![1u8; 1 + hash_len * 2];
for _ in 0..max_proof_depth { for _ in 0..max_proof_depth {
proof_hashes.push(some_hash); proof_hashes.push(some_hash);
if current_index % 2 == 0 { if current_index % 2 == 0 {
combined[..hash_len].copy_from_slice(&current_hash.to_fixed_bytes()); combined[1..1 + hash_len].copy_from_slice(&current_hash.to_fixed_bytes());
combined[hash_len..].copy_from_slice(&some_hash.to_fixed_bytes()); combined[1 + hash_len..1 + hash_len * 2].copy_from_slice(&some_hash.to_fixed_bytes());
} else { } else {
combined[..hash_len].copy_from_slice(&some_hash.to_fixed_bytes()); combined[1..1 + hash_len].copy_from_slice(&some_hash.to_fixed_bytes());
combined[hash_len..].copy_from_slice(&current_hash.to_fixed_bytes()); combined[1 + hash_len..1 + hash_len * 2].copy_from_slice(&current_hash.to_fixed_bytes());
} }
current_hash = SubstrateKeccakHasher::hash(&combined); current_hash = SubstrateKeccakHasher::hash(&combined);

View File

@ -176,18 +176,18 @@ fn claim_happy_path() {
fn verify_proof_from_python_preclaims_for_sepolia() { fn verify_proof_from_python_preclaims_for_sepolia() {
new_test_ext().execute_with(|| { new_test_ext().execute_with(|| {
// =========================================================================================================== // ===========================================================================================================
// === FINAL MERKLE ROOT FOR [SEPOLIA]: 0xd7f7f0061338c2c59a96caa8a2572491315755cdcce534954d4ce30150c3bb7c === // === FINAL MERKLE ROOT FOR [SEPOLIA]: c41c281c8404f5ce0adfca067ef8ed260f28c2cb1d7944c84ce2431936b1919e ===
// =========================================================================================================== // ===========================================================================================================
// [+] Total padded leaves: 128 // [+] Total padded leaves: 128
// [+] Merkle tree size: 255 // [+] Merkle tree size: 255
// [+] Network ID: 11155111 // [+] Network ID: 11155111
// [+] Proof size: 7 // [+] Proof size: 7
// [+] Total shares: 694000000000000000000 // [+] Total shares: 1398900000000000000000
let network_id = 11155111u64; let network_id = 11155111u64;
let merkle_root = EvmHash::from(hex!("d7f7f0061338c2c59a96caa8a2572491315755cdcce534954d4ce30150c3bb7c")); let merkle_root = EvmHash::from(hex!("c41c281c8404f5ce0adfca067ef8ed260f28c2cb1d7944c84ce2431936b1919e"));
let proof_size = 7; let proof_size = 7;
let total_shares = 694000000000000000000; let total_shares = 1398900000000000000000;
let network_state = NetworkShare::new(total_shares, merkle_root, proof_size); let network_state = NetworkShare::new(total_shares, merkle_root, proof_size);
let global_state = ShareState::new(total_shares); let global_state = ShareState::new(total_shares);
@ -198,16 +198,16 @@ fn verify_proof_from_python_preclaims_for_sepolia() {
let claim_package_1 = ClaimPackage { let claim_package_1 = ClaimPackage {
index: 0, index: 0,
token_id: 1, token_id: 1,
shares: 670000000000000000000, shares: 684900000000000000000,
merkle_proof: BoundedVec::try_from( merkle_proof: BoundedVec::try_from(
vec![ vec![
EvmHash::from(hex!("4dba08ba4fae6cbb0a6be6bdce81e91baf913d1857f470e97388157edd4d8d5c")), EvmHash::from(hex!("86d599b297ed8fb56476884b43502e15e7b207835f9a62bd07b4f3338ac63b44")),
EvmHash::from(hex!("7c1a4d2ebe6a500171b0c0f6c624898261da62d09c132106c4acec277660bd01")), EvmHash::from(hex!("85ec1f887b1a32e76cd1b70de6a5b1900045efd7dfe3b218d3b6cef672735da6")),
EvmHash::from(hex!("00c5bca05597b46f72d9ee739bd3c6ff80ebd1d20944879fe0c8d1dc9fda1643")), EvmHash::from(hex!("1e8a291eb2ba08696ca482c7869cab5ad360da4e4df55589adf40b08b020cc5b")),
EvmHash::from(hex!("5cf752cd738db6581182f07395e89cc61cd2ca630d6e85235d506f7395217d5f")), EvmHash::from(hex!("a090ff633d4831ca76640ffce9d9399a0f8409bbbc98814d0d885c538c9ebc08")),
EvmHash::from(hex!("4df068cecd63b6ccfd326df735afbfbe254df62613f7d9b52769f0e99f156957")), EvmHash::from(hex!("85ad79082ed24d2fc6748f131ed39280d402cb7a0844b42b9d768d4639b516a9")),
EvmHash::from(hex!("d7f20777e3a1fed303fe1a459ce115e2561a92d66b34fd16dd793b8b69295082")), EvmHash::from(hex!("2c55408bc86c5a292611bc01ba46ac6c6b37b8409097b752bad884be24249ec4")),
EvmHash::from(hex!("4524fdc1aa98beafabe1341f53ee2373e541ad2efd152963fd9ce94571d283e4")), EvmHash::from(hex!("9e046ea7683ac6dfa95caf833912d806b41296fcaa86718b4822f42ae68a6ff5"))
] ]
).unwrap(), ).unwrap(),
}; };
@ -226,16 +226,16 @@ fn verify_proof_from_python_preclaims_for_sepolia() {
let claim_package_2 = ClaimPackage { let claim_package_2 = ClaimPackage {
index: 2, index: 2,
token_id: 3, token_id: 3,
shares: 8000000000000000000, shares: 14900000000000000000,
merkle_proof: BoundedVec::try_from( merkle_proof: BoundedVec::try_from(
vec![ vec![
EvmHash::from(hex!("a8bbe4e0578964e3e6c40b93380967d69b7a1fd0358bffe789473892ec82872d")), EvmHash::from(hex!("547eb6ce3345e183448c61ee30e4eca0523537114e8d3971894196598651040e")),
EvmHash::from(hex!("9b771d13977930979a26632d74e350885d5750b00646a5b03114b5815afb9f2f")), EvmHash::from(hex!("9e9936d6cd248898747a8d29d2ef1b02ca06c40583873fd7c3a7ddd1554f5472")),
EvmHash::from(hex!("00c5bca05597b46f72d9ee739bd3c6ff80ebd1d20944879fe0c8d1dc9fda1643")), EvmHash::from(hex!("1e8a291eb2ba08696ca482c7869cab5ad360da4e4df55589adf40b08b020cc5b")),
EvmHash::from(hex!("5cf752cd738db6581182f07395e89cc61cd2ca630d6e85235d506f7395217d5f")), EvmHash::from(hex!("a090ff633d4831ca76640ffce9d9399a0f8409bbbc98814d0d885c538c9ebc08")),
EvmHash::from(hex!("4df068cecd63b6ccfd326df735afbfbe254df62613f7d9b52769f0e99f156957")), EvmHash::from(hex!("85ad79082ed24d2fc6748f131ed39280d402cb7a0844b42b9d768d4639b516a9")),
EvmHash::from(hex!("d7f20777e3a1fed303fe1a459ce115e2561a92d66b34fd16dd793b8b69295082")), EvmHash::from(hex!("2c55408bc86c5a292611bc01ba46ac6c6b37b8409097b752bad884be24249ec4")),
EvmHash::from(hex!("4524fdc1aa98beafabe1341f53ee2373e541ad2efd152963fd9ce94571d283e4")), EvmHash::from(hex!("9e046ea7683ac6dfa95caf833912d806b41296fcaa86718b4822f42ae68a6ff5"))
] ]
).unwrap(), ).unwrap(),
}; };
@ -254,16 +254,16 @@ fn verify_proof_from_python_preclaims_for_sepolia() {
let claim_package_3 = ClaimPackage { let claim_package_3 = ClaimPackage {
index: 6, index: 6,
token_id: 7, token_id: 7,
shares: 4000000000000000000, shares: 10900000000000000000,
merkle_proof: BoundedVec::try_from( merkle_proof: BoundedVec::try_from(
vec![ vec![
EvmHash::from(hex!("d28eec1544597d9e050c88a7a84535be82caf9b9e0b0c9b59b39283e7885a0d1")), EvmHash::from(hex!("441f13d353fe0224fe4ab418fe56c66287e0ba1e5e562f56a4b7195ca3d9f741")),
EvmHash::from(hex!("6e939188f98c3ed00931e66fdfc220c036b98edc54a212f5b18e2d3776c7c22f")), EvmHash::from(hex!("17f9ae41fbcbbfc35accf1e4e132a905b0a95e68e085757ae8304b6a0554e908")),
EvmHash::from(hex!("4141baa2d892b0111bbb102078bd3ad5331c74f176b228c588741c1c306cb67d")), EvmHash::from(hex!("a6f6783441ed0d4c5f1da8d1f026c037e15706c4ecb4c400216d485105ec9596")),
EvmHash::from(hex!("5cf752cd738db6581182f07395e89cc61cd2ca630d6e85235d506f7395217d5f")), EvmHash::from(hex!("a090ff633d4831ca76640ffce9d9399a0f8409bbbc98814d0d885c538c9ebc08")),
EvmHash::from(hex!("4df068cecd63b6ccfd326df735afbfbe254df62613f7d9b52769f0e99f156957")), EvmHash::from(hex!("85ad79082ed24d2fc6748f131ed39280d402cb7a0844b42b9d768d4639b516a9")),
EvmHash::from(hex!("d7f20777e3a1fed303fe1a459ce115e2561a92d66b34fd16dd793b8b69295082")), EvmHash::from(hex!("2c55408bc86c5a292611bc01ba46ac6c6b37b8409097b752bad884be24249ec4")),
EvmHash::from(hex!("4524fdc1aa98beafabe1341f53ee2373e541ad2efd152963fd9ce94571d283e4")) EvmHash::from(hex!("9e046ea7683ac6dfa95caf833912d806b41296fcaa86718b4822f42ae68a6ff5"))
] ]
).unwrap(), ).unwrap(),
}; };
@ -282,16 +282,16 @@ fn verify_proof_from_python_preclaims_for_sepolia() {
let claim_package_4 = ClaimPackage { let claim_package_4 = ClaimPackage {
index: 35, index: 35,
token_id: 36, token_id: 36,
shares: 4000000000000000000, shares: 10900000000000000000,
merkle_proof: BoundedVec::try_from( merkle_proof: BoundedVec::try_from(
vec![ vec![
EvmHash::from(hex!("6b405422bcab22b7c28a9b429096e31d664522154856efb0d7b16485080b2e2a")), EvmHash::from(hex!("5ddb291d62d02afab17cee218f2f367d612736b0f1ae59d2d28aac811e2057f3")),
EvmHash::from(hex!("620bb1858eaf6c6bb543fcf5f4b0d0b5adfbb95506403038eb153f075a00f522")), EvmHash::from(hex!("d990cb37076db084b1621bc8327e0f740e242da4c463ea3a0457a6483c07fdbf")),
EvmHash::from(hex!("e0b7058849ff216808963201ad2336f5587ff02eff08aceddb4764cc785413fd")), EvmHash::from(hex!("7bf41c14904c75e189893d05cf99c55516a194882463980cf08468c3bfd7dd8a")),
EvmHash::from(hex!("e23325a502fe4248c8e049400a92a1c655689499c129b0ce6be3fad5d4a12dc1")), EvmHash::from(hex!("1c4c8136fe9e6dc08dc1c69f2da707a271ae510cdb5af6051b5222f2812edee0")),
EvmHash::from(hex!("0cde8f646452003d6c8ff1fd265ea41cd3af2e733e63ec25a9dfc82ddf41e968")), EvmHash::from(hex!("3ec8d2443fd6f9decef06bf930e37cd37d78bf1c6004dc84b9ca1199712a82f8")),
EvmHash::from(hex!("c012942dae89ee6d1e1e44e876ae29b9421aaf0597dd2345f07a1439a98e0cbd")), EvmHash::from(hex!("1951c00e48f734ba95daeb3e8f6a2d971cc6fb0afac471ae39a21013f5756794")),
EvmHash::from(hex!("4524fdc1aa98beafabe1341f53ee2373e541ad2efd152963fd9ce94571d283e4")) EvmHash::from(hex!("9e046ea7683ac6dfa95caf833912d806b41296fcaa86718b4822f42ae68a6ff5"))
] ]
).unwrap(), ).unwrap(),
}; };

View File

@ -1,6 +1,6 @@
[package] [package]
name = "ghost-helpers" name = "ghost-helpers"
version = "0.0.7" version = "0.0.8"
description = "Cryptographic utility suite for custom runtimes: optimized Bitmaps, UTXO parsing, Merkle Tree proofs, and Hash Chain components." description = "Cryptographic utility suite for custom runtimes: optimized Bitmaps, UTXO parsing, Merkle Tree proofs, and Hash Chain components."
license.workspace = true license.workspace = true
authors.workspace = true authors.workspace = true

View File

@ -3,6 +3,29 @@ use sp_arithmetic::traits::AtLeast8BitUnsigned;
use sp_runtime::traits::UniqueSaturatedInto; use sp_runtime::traits::UniqueSaturatedInto;
use sp_std::vec::Vec; 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>( pub fn generate_tree<H, I, S, F, E>(
max_index: I, max_index: I,
raw_values: S, raw_values: S,
@ -17,30 +40,28 @@ where
let num_of_leaves: usize = (max_index + I::one()).unique_saturated_into(); let num_of_leaves: usize = (max_index + I::one()).unique_saturated_into();
let padded_leaves = num_of_leaves.next_power_of_two(); let padded_leaves = num_of_leaves.next_power_of_two();
let total_capacity = (2 * padded_leaves) - 1; let total_capacity = (2 * padded_leaves) - 1;
let mut merkle_tree = sp_std::vec![H::empty(); total_capacity];
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() { for item in raw_values.into_iter() {
let (index, preimage) = generate_preimage(item)?; let (index, preimage) = generate_preimage(item)?;
merkle_tree[index] = H::hash(&preimage); if index >= padded_leaves { return Ok(sp_std::vec![]); }
merkle_tree[index] = leaf_prefixed::<H>(&preimage)
} }
let mut layer_start = 0; let mut layer_start = 0;
let mut current_layer_len = padded_leaves; let mut current_layer_len = padded_leaves;
let mut write_ptr = padded_leaves; let mut write_ptr = padded_leaves;
let hash_len = H::hash_len();
let mut combined = sp_std::vec![0u8; hash_len * 2];
while current_layer_len > 1 { while current_layer_len > 1 {
for i in (0..current_layer_len).step_by(2) { for i in (0..current_layer_len).step_by(2) {
let layer_index = layer_start + i; let layer_index = layer_start + i;
let left_bytes = merkle_tree[layer_index].as_ref(); let left_bytes = merkle_tree[layer_index].as_ref();
let right_bytes = merkle_tree[layer_index | 1].as_ref(); let right_bytes = merkle_tree[layer_index | 1].as_ref();
combined[..hash_len].copy_from_slice(left_bytes); merkle_tree[write_ptr] = node_prefixed::<H>(left_bytes, right_bytes);
combined[hash_len..].copy_from_slice(right_bytes);
merkle_tree[write_ptr] = H::hash(&combined);
write_ptr += 1; write_ptr += 1;
} }
layer_start += current_layer_len; layer_start += current_layer_len;
@ -50,11 +71,17 @@ where
Ok(merkle_tree) Ok(merkle_tree)
} }
pub fn generate_proof<H, I>(merkle_tree: &[H::Hash], max_index: I, index: I) -> Vec<H::Hash> pub fn generate_proof<H, I>(
merkle_tree: &[H::Hash],
max_index: I,
index: I,
) -> Vec<H::Hash>
where where
H: GhostHasher, H: GhostHasher,
I: AtLeast8BitUnsigned + UniqueSaturatedInto<usize>, 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 num_of_leaves: usize = (max_index + I::one()).unique_saturated_into();
let mut current_index: usize = index.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 mut current_layer_len = num_of_leaves.next_power_of_two();
@ -87,29 +114,21 @@ where
H: GhostHasher, H: GhostHasher,
I: PartialOrd + UniqueSaturatedInto<usize>, I: PartialOrd + UniqueSaturatedInto<usize>,
{ {
let mut current_hash = H::hash(preimage);
let mut current_index: usize = index.unique_saturated_into(); let mut current_index: usize = index.unique_saturated_into();
if current_index >= (1 << merkle_proof.len()) { return false; }
if current_index >= (1 << merkle_proof.len()) { let mut current_hash = leaf_prefixed::<H>(preimage);
return false;
}
let hash_len = H::hash_len();
let mut combined = sp_std::vec![0u8; hash_len * 2];
for sibling in merkle_proof.iter() { for sibling in merkle_proof.iter() {
let sibling_bytes = sibling.as_ref(); let sibling_bytes = sibling.as_ref();
let hash_bytes = current_hash.as_ref(); let hash_bytes = current_hash.as_ref();
if current_index % 2 == 0 { if current_index % 2 == 0 {
combined[..hash_len].copy_from_slice(hash_bytes); current_hash = node_prefixed::<H>(hash_bytes, sibling_bytes);
combined[hash_len..].copy_from_slice(sibling_bytes);
} else { } else {
combined[..hash_len].copy_from_slice(sibling_bytes); current_hash = node_prefixed::<H>(sibling_bytes, hash_bytes);
combined[hash_len..].copy_from_slice(hash_bytes);
} }
current_hash = H::hash(&combined);
current_index >>= 1; current_index >>= 1;
} }
@ -157,24 +176,27 @@ mod tests {
assert_eq!(tree.len(), 7); assert_eq!(tree.len(), 7);
let h0 = TestBlake2Hasher::hash(&vec![1]); let h0 = TestBlake2Hasher::hash(&[ &[0u8], [1].as_slice() ].concat());
let h1 = TestBlake2Hasher::hash(&vec![2]); let h1 = TestBlake2Hasher::hash(&[ &[0u8], [2].as_slice() ].concat());
let h2 = TestBlake2Hasher::hash(&vec![3]); let h2 = TestBlake2Hasher::hash(&[ &[0u8], [3].as_slice() ].concat());
let h3 = TestBlake2Hasher::hash(&vec![4]); let h3 = TestBlake2Hasher::hash(&[ &[0u8], [4].as_slice() ].concat());
let mut c1 = [0u8; 64]; let mut c1 = [0u8; 65];
c1[..32].copy_from_slice(h0.as_ref()); c1[0] = NODE_PREFIX;
c1[32..].copy_from_slice(h1.as_ref()); c1[1..33].copy_from_slice(h0.as_ref());
c1[33..].copy_from_slice(h1.as_ref());
let parent_left = TestBlake2Hasher::hash(&c1); let parent_left = TestBlake2Hasher::hash(&c1);
let mut c2 = [0u8; 64]; let mut c2 = [0u8; 65];
c2[..32].copy_from_slice(h2.as_ref()); c2[0] = NODE_PREFIX;
c2[32..].copy_from_slice(h3.as_ref()); c2[1..33].copy_from_slice(h2.as_ref());
c2[33..].copy_from_slice(h3.as_ref());
let parent_right = TestBlake2Hasher::hash(&c2); let parent_right = TestBlake2Hasher::hash(&c2);
let mut c_root = [0u8; 64]; let mut c_root = [0u8; 65];
c_root[..32].copy_from_slice(parent_left.as_ref()); c_root[0] = NODE_PREFIX;
c_root[32..].copy_from_slice(parent_right.as_ref()); 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); let expected_root = TestBlake2Hasher::hash(&c_root);
assert_eq!(tree.last().unwrap(), &expected_root); assert_eq!(tree.last().unwrap(), &expected_root);
@ -196,17 +218,23 @@ mod tests {
) )
.unwrap(); .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.len(), 7);
assert_eq!(tree[3], H256::zero()); assert_eq!(tree[3], empty_hash);
let h2 = TestBlake2Hasher::hash(&vec![3]); let mut leaf2_data = sp_std::vec![LEAF_PREFIX];
let h3_empty = H256::zero(); 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 mut combined_right = [0u8; 64];
combined_right[..32].copy_from_slice(h2.as_ref());
combined_right[32..].copy_from_slice(h3_empty.as_ref());
let expected_parent_right = TestBlake2Hasher::hash(&combined_right); let expected_parent_right = TestBlake2Hasher::hash(&combined_right);
assert_eq!(tree[5], expected_parent_right); assert_eq!(tree[5], expected_parent_right);
} }
@ -251,7 +279,7 @@ mod tests {
.unwrap(); .unwrap();
assert_eq!(tree.len(), 1); assert_eq!(tree.len(), 1);
assert_eq!(tree[0], TestBlake2Hasher::hash(&vec![42])); assert_eq!(tree[0], TestBlake2Hasher::hash(&vec![LEAF_PREFIX, 42]));
} }
#[test] #[test]
@ -372,10 +400,14 @@ mod tests {
raw_data, raw_data,
|(index, bytes)| Ok((index, bytes)), |(index, bytes)| Ok((index, bytes)),
) )
.unwrap(); .unwrap();
let proof = generate_proof::<TestBlake2Hasher, u32>(&tree, max_index, 2); let proof = generate_proof::<TestBlake2Hasher, u32>(&tree, max_index, 2);
assert_eq!(proof[0], H256::zero());
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 root = *tree.last().unwrap();
let is_valid = let is_valid =

View File

@ -1,6 +1,6 @@
[package] [package]
name = "ghost-traits" name = "ghost-traits"
version = "0.4.4" version = "0.4.5"
description = "Shared traits including `GhostHasher`, `NetworkDataBasicHandler`, `BoundedBTreeMap`, `MerkleTree` and more." description = "Shared traits including `GhostHasher`, `NetworkDataBasicHandler`, `BoundedBTreeMap`, `MerkleTree` and more."
license.workspace = true license.workspace = true
authors.workspace = true authors.workspace = true

View File

@ -4,7 +4,7 @@ use sp_core::H256;
pub trait MerkleTree<A, E>: IdentifierConverter<A, E> { pub trait MerkleTree<A, E>: IdentifierConverter<A, E> {
type VerifyingShares; type VerifyingShares;
type Hash; type Hash: AsRef<[u8]>;
fn merkle_build_tree(vs: &Self::VerifyingShares) -> Result<Vec<Self::Hash>, E>; fn merkle_build_tree(vs: &Self::VerifyingShares) -> Result<Vec<Self::Hash>, E>;
fn merkle_generate_proof_from_tree(vsm: &Self::VerifyingShares, t: &[Self::Hash], i: A) -> Result<(Vec<u8>, Vec<H256>), E>; fn merkle_generate_proof_from_tree(vsm: &Self::VerifyingShares, t: &[Self::Hash], i: A) -> Result<(Vec<u8>, Vec<H256>), E>;

View File

@ -1,6 +1,6 @@
[package] [package]
name = "ghost-weaver" name = "ghost-weaver"
version = "0.0.5" version = "0.0.6"
description = "Weaving a secure cryptographic tapestry across different external chains." description = "Weaving a secure cryptographic tapestry across different external chains."
license.workspace = true license.workspace = true
authors.workspace = true authors.workspace = true

View File

@ -354,11 +354,11 @@ mod benchmarks {
let session = 69; let session = 69;
let (network_id, _, _) = prepare_pallet::<T>(1); let (network_id, _, _) = prepare_pallet::<T>(1);
// https://sepolia.etherscan.io/address/0x9cffbdbdf29c67c5dbab1b5e8ae897aeafcaf70c#readContract // https://sepolia.etherscan.io/address/0x81acdf2da6b50d806c96aecd9a498a5359e373c0#readContract
// getRoot: session 0, atBlock 11383380 // getRoot: session 0, atBlock 11660400
let root_string = let root_string =
hex::decode("f72b9ef0b66c8cc3952ffd11dc0b85f3b02e6edfa9236da400138f2fcba4668d") hex::decode("28d1e6b476b03a464f74d531d532572db29d6b57349e4f17f84e92843f6c4ee7")
.expect("EVM Root hex should be valid"); .expect("EVM Root hex should be valid");
let expected_root = H256::from_slice(&root_string); let expected_root = H256::from_slice(&root_string);
LoomStates::<T>::insert(&network_id, &session, expected_root); LoomStates::<T>::insert(&network_id, &session, expected_root);
@ -373,38 +373,14 @@ mod benchmarks {
let transfer_amount: BalanceOf<T> = 420u64.unique_saturated_into(); let transfer_amount: BalanceOf<T> = 420u64.unique_saturated_into();
let proof = vec![ let proof = vec![
H256::from_slice( H256::from_slice(&hex::decode("c5354289f0397c916949373043eb1d5ea99447f67fe014324d96c58ef02a2d11").expect("Proof should be valid")),
&hex::decode("6dfb19f049f557abff273ce9efb9195924d41e5699b1b7755be2c515b25ef7b6") H256::from_slice(&hex::decode("da1fbdfefa1e9aedfedcfc48fb17cb0e731529f7a3695114e0a9caa1989dd420").expect("Proof should be valid")),
.expect("Proof should be valid"), H256::from_slice(&hex::decode("1c792b14bf66f82af36f00f5fba7014fa0c1e2ff3c7c273bfe523c1acf67dc3f").expect("Proof should be valid")),
), H256::from_slice(&hex::decode("5fa080a686a5a0d05c3d4822fd54d632dc9cc04b1616046eba2ce499eb9af79f").expect("Proof should be valid")),
H256::from_slice( H256::from_slice(&hex::decode("5eb949690a0404abf4cebafc7cfffa382191b7dd9e7df778581e6fb78efab35f").expect("Proof should be valid")),
&hex::decode("9e0f9c5b8ffa146b74bb614482c0bc65a1ef961a8fa9a3a031a36c97f8c70733") H256::from_slice(&hex::decode("d364c9d5dadad4569b6dd47f7feabafa3571f842434425548335ac6e690dd071").expect("Proof should be valid")),
.expect("Proof should be valid"), H256::from_slice(&hex::decode("68d8bc5b77979c1a6702334f529f5783f79e942fd2cd03f6e55ac2cf496e849f").expect("Proof should be valid")),
), H256::from_slice(&hex::decode("de9c446fab46a8d27db1e3100f275a777d385b44e3cbc045cabac9da36cae040").expect("Proof should be valid")),
H256::from_slice(
&hex::decode("890740a8eb06ce9be422cb8da5cdafc2b58c0a5e24036c578de2a433c828ff7d")
.expect("Proof should be valid"),
),
H256::from_slice(
&hex::decode("3b8ec09e026fdc305365dfc94e189a81b38c7597b3d941c279f042e8206e0bd8")
.expect("Proof should be valid"),
),
H256::from_slice(
&hex::decode("ecd50eee38e386bd62be9bedb990706951b65fe053bd9d8a521af753d139e2da")
.expect("Proof should be valid"),
),
H256::from_slice(
&hex::decode("defff6d330bb5403f63b14f33b578274160de3a50df4efecf0e0db73bcdd3da5")
.expect("Proof should be valid"),
),
H256::from_slice(
&hex::decode("617bdd11f7c0a11f49db22f629387a12da7596f9d1704d7465177c63d88ec7d7")
.expect("Proof should be valid"),
),
H256::from_slice(
&hex::decode("292c23a9aa1d8bea7e2435e555a4a60e379a5a35f3f452bae60121073fb6eead")
.expect("Proof should be valid"),
),
]; ];
let slots = vec![H256::from_slice( let slots = vec![H256::from_slice(
&hex::decode("2e474dabf492786b3505d7b7cf0bf07c9dbce92c91982fa7344c77e24fd4310d") &hex::decode("2e474dabf492786b3505d7b7cf0bf07c9dbce92c91982fa7344c77e24fd4310d")

View File

@ -572,8 +572,8 @@ fn should_successfully_pull_evm_thread_and_mint_currency() {
// 420 sfDxNicCEPts6CVttVMPCs2eoymDpQahu1GUVyGPWu6dhwADJ - 0x0ae3ba32f8555fe3031461e2cb6feeadb67029ef7490b982824fc90751a4cb3e // 420 sfDxNicCEPts6CVttVMPCs2eoymDpQahu1GUVyGPWu6dhwADJ - 0x0ae3ba32f8555fe3031461e2cb6feeadb67029ef7490b982824fc90751a4cb3e
// 1337 sfH6zJ63fcxrFAZ1RmiHbxgy2t3sEufY26WdsBSS359AzJd4w - 0x962a2794f66c89e55d9b9692ce59f67345b99f6fddb51d90462c7eca8b5a7e1b // 1337 sfH6zJ63fcxrFAZ1RmiHbxgy2t3sEufY26WdsBSS359AzJd4w - 0x962a2794f66c89e55d9b9692ce59f67345b99f6fddb51d90462c7eca8b5a7e1b
// https://sepolia.etherscan.io/address/0x9cffbdbdf29c67c5dbab1b5e8ae897aeafcaf70c#readContract // https://sepolia.etherscan.io/address/0x81acdf2da6b50d806c96aecd9a498a5359e373c0#readContract
// getRoot: atBlock 11383380, globalIndexes from 0 to 2 // getRoot: atBlock 11660400, globalIndexes from 0 to 2
let inserts = vec![ let inserts = vec![
( (
0, 0,
@ -583,49 +583,49 @@ fn should_successfully_pull_evm_thread_and_mint_currency() {
vec![ vec![
H256::from_slice( H256::from_slice(
&hex::decode( &hex::decode(
"c7a710c9acbc193692f34ac1f1979013b04240a4c0d02b49b9178f17721350b1", "675a2e7b05b8b3932cd0faf88f1bc29767b7462667f56d051b8d5325bda0ee73",
) )
.expect("Proof should be valid"), .expect("Proof should be valid"),
), ),
H256::from_slice( H256::from_slice(
&hex::decode( &hex::decode(
"9e0f9c5b8ffa146b74bb614482c0bc65a1ef961a8fa9a3a031a36c97f8c70733", "da1fbdfefa1e9aedfedcfc48fb17cb0e731529f7a3695114e0a9caa1989dd420",
) )
.expect("Proof should be valid"), .expect("Proof should be valid"),
), ),
H256::from_slice( H256::from_slice(
&hex::decode( &hex::decode(
"890740a8eb06ce9be422cb8da5cdafc2b58c0a5e24036c578de2a433c828ff7d", "1c792b14bf66f82af36f00f5fba7014fa0c1e2ff3c7c273bfe523c1acf67dc3f",
) )
.expect("Proof should be valid"), .expect("Proof should be valid"),
), ),
H256::from_slice( H256::from_slice(
&hex::decode( &hex::decode(
"3b8ec09e026fdc305365dfc94e189a81b38c7597b3d941c279f042e8206e0bd8", "5fa080a686a5a0d05c3d4822fd54d632dc9cc04b1616046eba2ce499eb9af79f",
) )
.expect("Proof should be valid"), .expect("Proof should be valid"),
), ),
H256::from_slice( H256::from_slice(
&hex::decode( &hex::decode(
"ecd50eee38e386bd62be9bedb990706951b65fe053bd9d8a521af753d139e2da", "5eb949690a0404abf4cebafc7cfffa382191b7dd9e7df778581e6fb78efab35f",
) )
.expect("Proof should be valid"), .expect("Proof should be valid"),
), ),
H256::from_slice( H256::from_slice(
&hex::decode( &hex::decode(
"defff6d330bb5403f63b14f33b578274160de3a50df4efecf0e0db73bcdd3da5", "d364c9d5dadad4569b6dd47f7feabafa3571f842434425548335ac6e690dd071",
) )
.expect("Proof should be valid"), .expect("Proof should be valid"),
), ),
H256::from_slice( H256::from_slice(
&hex::decode( &hex::decode(
"617bdd11f7c0a11f49db22f629387a12da7596f9d1704d7465177c63d88ec7d7", "68d8bc5b77979c1a6702334f529f5783f79e942fd2cd03f6e55ac2cf496e849f",
) )
.expect("Proof should be valid"), .expect("Proof should be valid"),
), ),
H256::from_slice( H256::from_slice(
&hex::decode( &hex::decode(
"292c23a9aa1d8bea7e2435e555a4a60e379a5a35f3f452bae60121073fb6eead", "de9c446fab46a8d27db1e3100f275a777d385b44e3cbc045cabac9da36cae040",
) )
.expect("Proof should be valid"), .expect("Proof should be valid"),
), ),
@ -643,49 +643,49 @@ fn should_successfully_pull_evm_thread_and_mint_currency() {
vec![ vec![
H256::from_slice( H256::from_slice(
&hex::decode( &hex::decode(
"6dfb19f049f557abff273ce9efb9195924d41e5699b1b7755be2c515b25ef7b6", "c5354289f0397c916949373043eb1d5ea99447f67fe014324d96c58ef02a2d11",
) )
.expect("Proof should be valid"), .expect("Proof should be valid"),
), ),
H256::from_slice( H256::from_slice(
&hex::decode( &hex::decode(
"9e0f9c5b8ffa146b74bb614482c0bc65a1ef961a8fa9a3a031a36c97f8c70733", "da1fbdfefa1e9aedfedcfc48fb17cb0e731529f7a3695114e0a9caa1989dd420",
) )
.expect("Proof should be valid"), .expect("Proof should be valid"),
), ),
H256::from_slice( H256::from_slice(
&hex::decode( &hex::decode(
"890740a8eb06ce9be422cb8da5cdafc2b58c0a5e24036c578de2a433c828ff7d", "1c792b14bf66f82af36f00f5fba7014fa0c1e2ff3c7c273bfe523c1acf67dc3f",
) )
.expect("Proof should be valid"), .expect("Proof should be valid"),
), ),
H256::from_slice( H256::from_slice(
&hex::decode( &hex::decode(
"3b8ec09e026fdc305365dfc94e189a81b38c7597b3d941c279f042e8206e0bd8", "5fa080a686a5a0d05c3d4822fd54d632dc9cc04b1616046eba2ce499eb9af79f",
) )
.expect("Proof should be valid"), .expect("Proof should be valid"),
), ),
H256::from_slice( H256::from_slice(
&hex::decode( &hex::decode(
"ecd50eee38e386bd62be9bedb990706951b65fe053bd9d8a521af753d139e2da", "5eb949690a0404abf4cebafc7cfffa382191b7dd9e7df778581e6fb78efab35f",
) )
.expect("Proof should be valid"), .expect("Proof should be valid"),
), ),
H256::from_slice( H256::from_slice(
&hex::decode( &hex::decode(
"defff6d330bb5403f63b14f33b578274160de3a50df4efecf0e0db73bcdd3da5", "d364c9d5dadad4569b6dd47f7feabafa3571f842434425548335ac6e690dd071",
) )
.expect("Proof should be valid"), .expect("Proof should be valid"),
), ),
H256::from_slice( H256::from_slice(
&hex::decode( &hex::decode(
"617bdd11f7c0a11f49db22f629387a12da7596f9d1704d7465177c63d88ec7d7", "68d8bc5b77979c1a6702334f529f5783f79e942fd2cd03f6e55ac2cf496e849f",
) )
.expect("Proof should be valid"), .expect("Proof should be valid"),
), ),
H256::from_slice( H256::from_slice(
&hex::decode( &hex::decode(
"292c23a9aa1d8bea7e2435e555a4a60e379a5a35f3f452bae60121073fb6eead", "de9c446fab46a8d27db1e3100f275a777d385b44e3cbc045cabac9da36cae040",
) )
.expect("Proof should be valid"), .expect("Proof should be valid"),
), ),
@ -703,49 +703,49 @@ fn should_successfully_pull_evm_thread_and_mint_currency() {
vec![ vec![
H256::from_slice( H256::from_slice(
&hex::decode( &hex::decode(
"290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563", "f39a869f62e75cf5f0bf914688a6b289caf2049435d8e68c5c5e6d05e44913f3",
) )
.expect("Proof should be valid"), .expect("Proof should be valid"),
), ),
H256::from_slice( H256::from_slice(
&hex::decode( &hex::decode(
"82ca531d09faff937c9b68f53d473c9f2c88bdbc8b1629af894479367881db5c", "e0090094ce1d7af8a81ba78bef4b137c1ab32d20d5487224a9f8df95b516c1e4",
) )
.expect("Proof should be valid"), .expect("Proof should be valid"),
), ),
H256::from_slice( H256::from_slice(
&hex::decode( &hex::decode(
"890740a8eb06ce9be422cb8da5cdafc2b58c0a5e24036c578de2a433c828ff7d", "1c792b14bf66f82af36f00f5fba7014fa0c1e2ff3c7c273bfe523c1acf67dc3f",
) )
.expect("Proof should be valid"), .expect("Proof should be valid"),
), ),
H256::from_slice( H256::from_slice(
&hex::decode( &hex::decode(
"3b8ec09e026fdc305365dfc94e189a81b38c7597b3d941c279f042e8206e0bd8", "5fa080a686a5a0d05c3d4822fd54d632dc9cc04b1616046eba2ce499eb9af79f",
) )
.expect("Proof should be valid"), .expect("Proof should be valid"),
), ),
H256::from_slice( H256::from_slice(
&hex::decode( &hex::decode(
"ecd50eee38e386bd62be9bedb990706951b65fe053bd9d8a521af753d139e2da", "5eb949690a0404abf4cebafc7cfffa382191b7dd9e7df778581e6fb78efab35f",
) )
.expect("Proof should be valid"), .expect("Proof should be valid"),
), ),
H256::from_slice( H256::from_slice(
&hex::decode( &hex::decode(
"defff6d330bb5403f63b14f33b578274160de3a50df4efecf0e0db73bcdd3da5", "d364c9d5dadad4569b6dd47f7feabafa3571f842434425548335ac6e690dd071",
) )
.expect("Proof should be valid"), .expect("Proof should be valid"),
), ),
H256::from_slice( H256::from_slice(
&hex::decode( &hex::decode(
"617bdd11f7c0a11f49db22f629387a12da7596f9d1704d7465177c63d88ec7d7", "68d8bc5b77979c1a6702334f529f5783f79e942fd2cd03f6e55ac2cf496e849f",
) )
.expect("Proof should be valid"), .expect("Proof should be valid"),
), ),
H256::from_slice( H256::from_slice(
&hex::decode( &hex::decode(
"292c23a9aa1d8bea7e2435e555a4a60e379a5a35f3f452bae60121073fb6eead", "de9c446fab46a8d27db1e3100f275a777d385b44e3cbc045cabac9da36cae040",
) )
.expect("Proof should be valid"), .expect("Proof should be valid"),
), ),
@ -758,11 +758,11 @@ fn should_successfully_pull_evm_thread_and_mint_currency() {
]; ];
new_test_ext(authorities, networks).execute_with(|| { new_test_ext(authorities, networks).execute_with(|| {
// https://sepolia.etherscan.io/address/0x9cffbdbdf29c67c5dbab1b5e8ae897aeafcaf70c#readContract // https://sepolia.etherscan.io/address/0x81acdf2da6b50d806c96aecd9a498a5359e373c0#readContract
// getRoot: session 0, atBlock 11383380 // getRoot: session 0, atBlock 11660400
let session = 0; let session = 0;
let root_string = let root_string =
hex::decode("f72b9ef0b66c8cc3952ffd11dc0b85f3b02e6edfa9236da400138f2fcba4668d") hex::decode("28d1e6b476b03a464f74d531d532572db29d6b57349e4f17f84e92843f6c4ee7")
.expect("Root hex should be valid"); .expect("Root hex should be valid");
let expected_root = H256::from_slice(&root_string); let expected_root = H256::from_slice(&root_string);
LoomStates::<TestRuntime>::insert(&network_id, &session, expected_root); LoomStates::<TestRuntime>::insert(&network_id, &session, expected_root);