From dd9d5b03aa87d147356242032d678170b64440fd Mon Sep 17 00:00:00 2001 From: SChernykh <15806605+SChernykh@users.noreply.github.com> Date: Sun, 5 Oct 2025 16:01:16 +0200 Subject: [PATCH] Merkle tree: added one more helper function --- src/block_template.cpp | 54 ++++++++++++++---- src/merge_mining_client_tari.cpp | 28 ++++++---- src/merkle.cpp | 94 +++++++++++++++++++++++++++++++- src/merkle.h | 3 + tests/src/merkle_tests.cpp | 17 ++++++ 5 files changed, 173 insertions(+), 23 deletions(-) diff --git a/src/block_template.cpp b/src/block_template.cpp index b390d64..b40c1a8 100644 --- a/src/block_template.cpp +++ b/src/block_template.cpp @@ -1359,11 +1359,11 @@ bool BlockTemplate::get_aux_proof(const uint32_t template_id, uint32_t extra_non return false; } - bool found = false; - const hash sidechain_id = calc_sidechain_hash(extra_nonce); const uint32_t n_aux_chains = static_cast(m_poolBlockTemplate->m_auxChains.size() + 1); + uint32_t found_aux_slot = n_aux_chains; + std::vector hashes(n_aux_chains); for (const AuxChainData& aux_data : m_poolBlockTemplate->m_auxChains) { @@ -1371,7 +1371,7 @@ bool BlockTemplate::get_aux_proof(const uint32_t template_id, uint32_t extra_non hashes[aux_slot] = aux_data.data; if (aux_data.data == h) { - found = true; + found_aux_slot = aux_slot; } } @@ -1379,17 +1379,31 @@ bool BlockTemplate::get_aux_proof(const uint32_t template_id, uint32_t extra_non hashes[aux_slot] = sidechain_id; if (sidechain_id == h) { - found = true; + found_aux_slot = aux_slot; } - if (!found) { + if (found_aux_slot >= n_aux_chains) { return false; } - std::vector> tree; - merkle_hash_full_tree(hashes, tree); + root_hash root; + const bool result = merkle_hash_with_proof(hashes, found_aux_slot, proof, path, root); - return get_merkle_proof(tree, h, proof, path); + if (pool_block_debug()) { + std::vector> tree; + merkle_hash_full_tree(hashes, tree); + + std::vector proof2; + uint32_t path2 = 0; + + const bool result2 = get_merkle_proof(tree, h, proof2, path2); + + if ((result2 != result) || (proof2 != proof) || (path2 != path)) { + LOGERR(1, "get_aux_proof: merkle_hash_with_proof and get_merkle_proof returned different results. Fix the code!"); + } + } + + return result; } std::vector BlockTemplate::get_block_template_blob(uint32_t template_id, uint32_t sidechain_extra_nonce, size_t& nonce_offset, size_t& extra_nonce_offset, size_t& merkle_root_offset, hash& merge_mining_root, const BlockTemplate** pThis) const @@ -1529,10 +1543,28 @@ void BlockTemplate::init_merge_mining_merkle_proof() } } - std::vector> tree; - merkle_hash_full_tree(hashes, tree); + root_hash root; + if (!merkle_hash_with_proof(hashes, aux_slot, m_poolBlockTemplate->m_merkleProof, m_poolBlockTemplate->m_merkleProofPath, root)) { + LOGERR(1, "init_merge_mining_merkle_proof: merkle_hash_with_proof failed. Fix the code!"); + return; + } - get_merkle_proof(tree, m_poolBlockTemplate->m_sidechainId, m_poolBlockTemplate->m_merkleProof, m_poolBlockTemplate->m_merkleProofPath); + if (pool_block_debug()) { + std::vector> tree; + merkle_hash_full_tree(hashes, tree); + + std::vector proof; + uint32_t path = 0; + + if (!get_merkle_proof(tree, m_poolBlockTemplate->m_sidechainId, proof, path)) { + LOGERR(1, "init_merge_mining_merkle_proof: get_merkle_proof failed. Fix the code!"); + return; + } + + if ((proof != m_poolBlockTemplate->m_merkleProof) || (path != m_poolBlockTemplate->m_merkleProofPath)) { + LOGERR(1, "init_merge_mining_merkle_proof: merkle_hash_with_proof and get_merkle_proof returned different results. Fix the code!"); + } + } } } // namespace p2pool diff --git a/src/merge_mining_client_tari.cpp b/src/merge_mining_client_tari.cpp index b22755d..cb2a6fc 100644 --- a/src/merge_mining_client_tari.cpp +++ b/src/merge_mining_client_tari.cpp @@ -327,20 +327,23 @@ void MergeMiningClientTari::on_external_block(const PoolBlock& block) LOGINFO(0, log::LightGreen() << "External aux job solution found. Processing it!"); // coinbase_merkle_proof - - std::vector> tree; - - merkle_hash_full_tree(block.m_transactions, tree); - + root_hash root; std::vector proof; uint32_t path; - if (!get_merkle_proof(tree, block.m_transactions[0], proof, path)) { - LOGWARN(3, "on_external_block: get_merkle_proof failed for coinbase transaction"); + if (!merkle_hash_with_proof(block.m_transactions, 0, proof, path, root)) { + LOGWARN(3, "on_external_block: merkle_hash_with_proof failed for coinbase transaction"); + return; + } + + if (!verify_merkle_proof(block.m_transactions[0], proof, path, root)) { + LOGWARN(3, "on_external_block: verify_merkle_proof failed for coinbase transaction"); return; } std::vector coinbase_merkle_proof; + coinbase_merkle_proof.reserve(proof.size() * HASH_SIZE); + for (const hash& h : proof) { coinbase_merkle_proof.insert(coinbase_merkle_proof.end(), h.h, h.h + HASH_SIZE); } @@ -398,15 +401,18 @@ void MergeMiningClientTari::on_external_block(const PoolBlock& block) hashes[aux_slot] = sidechain_id; - merkle_hash_full_tree(hashes, tree); + if (!merkle_hash_with_proof(hashes, chain_params.aux_hash, aux_merkle_proof, aux_merkle_proof_path, root)) { + LOGWARN(3, "on_external_block: merkle_hash_with_proof failed for the aux hash"); + return; + } - if (tree.empty() || tree.back().empty() || (tree.back().front() != block.m_merkleRoot)) { + if (root != block.m_merkleRoot) { LOGWARN(3, "on_external_block: merkle root didn't match"); return; } - if (!get_merkle_proof(tree, chain_params.aux_hash, aux_merkle_proof, aux_merkle_proof_path)) { - LOGWARN(3, "on_external_block: get_merkle_proof failed for the aux hash"); + if (!verify_merkle_proof(chain_params.aux_hash, aux_merkle_proof, aux_merkle_proof_path, root)) { + LOGWARN(3, "on_external_block: verify_merkle_proof failed for the aux hash"); return; } diff --git a/src/merkle.cpp b/src/merkle.cpp index b324102..95367b5 100644 --- a/src/merkle.cpp +++ b/src/merkle.cpp @@ -128,6 +128,98 @@ void merkle_hash_full_tree(const std::vector& hashes, std::vector& hashes, const hash& hash_to_prove, std::vector& proof, uint32_t& path, root_hash& root) +{ + for (size_t i = 0, n = hashes.size(); i < n; ++i) { + if (hashes[i] == hash_to_prove) { + return merkle_hash_with_proof(hashes, i, proof, path, root); + } + } + return false; +} + +bool merkle_hash_with_proof(const std::vector& hashes, size_t index_to_prove, std::vector& proof, uint32_t& path, root_hash& root) +{ + const size_t count = hashes.size(); + + proof.clear(); + path = 0; + + if ((count == 0) || (index_to_prove >= count)) { + root.clear(); + return false; + } + + const uint8_t* h = hashes[0].h; + + if (count == 1) { + root = root_hash(hashes[0]); + } + else if (count == 2) { + keccak(h, HASH_SIZE * 2, root.h); + + proof.reserve(1); + proof.emplace_back(hashes[index_to_prove ^ 1]); + path = static_cast(index_to_prove); + } + else { + hash h2 = hashes[index_to_prove]; + + size_t cnt = 1, proof_max_size = 0; + do { + cnt <<= 1; + ++proof_max_size; + } while (cnt <= count); + cnt >>= 1; + + proof.reserve(proof_max_size); + + std::vector tmp_ints(cnt); + + const size_t k = cnt * 2 - count; + memcpy(tmp_ints.data(), h, k * HASH_SIZE); + + for (size_t i = k, j = k; j < cnt; i += 2, ++j) { + keccak(h + i * HASH_SIZE, HASH_SIZE * 2, tmp_ints[j].h); + + if (hashes[i] == h2) { + proof.emplace_back(hashes[i + 1]); + h2 = tmp_ints[j]; + } + else if (hashes[i + 1] == h2) { + proof.emplace_back(hashes[i]); + h2 = tmp_ints[j]; + path = 1; + } + } + + while (cnt >= 2) { + cnt >>= 1; + for (size_t i = 0, j = 0; j < cnt; i += 2, ++j) { + hash tmp; + keccak(tmp_ints[i].h, HASH_SIZE * 2, tmp.h); + + if (tmp_ints[i] == h2) { + proof.emplace_back(tmp_ints[i + 1]); + h2 = tmp; + path <<= 1; + } + else if (tmp_ints[i + 1] == h2) { + proof.emplace_back(tmp_ints[i]); + h2 = tmp; + path = (path << 1) | 1; + } + + tmp_ints[j] = tmp; + } + } + + root = static_cast(tmp_ints[0]); + } + + return true; +} + bool get_merkle_proof(const std::vector>& tree, const hash& h, std::vector& proof, uint32_t& path) { if (tree.empty()) { @@ -156,7 +248,7 @@ bool get_merkle_proof(const std::vector>& tree, const hash& h, if (count == 2) { proof.emplace_back(hashes[index ^ 1]); - path = index & 1; + path = static_cast(index); } else { size_t cnt = 1; diff --git a/src/merkle.h b/src/merkle.h index d45b570..6a24c37 100644 --- a/src/merkle.h +++ b/src/merkle.h @@ -22,6 +22,9 @@ namespace p2pool { void merkle_hash(const std::vector& hashes, root_hash& root); void merkle_hash_full_tree(const std::vector& hashes, std::vector>& tree); +bool merkle_hash_with_proof(const std::vector& hashes, const hash& hash_to_prove, std::vector& proof, uint32_t& path, root_hash& root); +bool merkle_hash_with_proof(const std::vector& hashes, size_t index_to_prove, std::vector& proof, uint32_t& path, root_hash& root); + bool get_merkle_proof(const std::vector>& tree, const hash& h, std::vector& proof, uint32_t& path); root_hash get_root_from_proof(hash h, const std::vector& proof, size_t index, size_t count); diff --git a/tests/src/merkle_tests.cpp b/tests/src/merkle_tests.cpp index a9caff0..959569e 100644 --- a/tests/src/merkle_tests.cpp +++ b/tests/src/merkle_tests.cpp @@ -144,7 +144,24 @@ TEST(merkle, tree) uint32_t path; ASSERT_TRUE(get_merkle_proof(tree, h, proof, path)); + + root_hash root2; + std::vector proof2; + uint32_t path2; + + ASSERT_TRUE(merkle_hash_with_proof(hashes, i, proof2, path2, root2)); + ASSERT_EQ(root2, root); + ASSERT_EQ(proof2, proof); + ASSERT_EQ(path2, path); + + ASSERT_TRUE(merkle_hash_with_proof(hashes, hashes[i], proof2, path2, root2)); + ASSERT_EQ(root2, root); + ASSERT_EQ(proof2, proof); + ASSERT_EQ(path2, path); + ASSERT_FALSE(get_merkle_proof(tree, empty_hash, proof, path)); + ASSERT_FALSE(merkle_hash_with_proof(hashes, n, proof2, path2, root2)); + ASSERT_FALSE(merkle_hash_with_proof(hashes, empty_hash, proof2, path2, root2)); uint32_t path_monero; ASSERT_TRUE(tree_path(n, i, &path_monero));