diff --git a/src/block_template.cpp b/src/block_template.cpp index 860ec77..97188cb 100644 --- a/src/block_template.cpp +++ b/src/block_template.cpp @@ -28,6 +28,7 @@ #include "side_chain.h" #include "pool_block.h" #include "params.h" +#include "merkle.h" #include #include #include @@ -662,11 +663,14 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, const m_poolBlockTemplate->m_extraNonce = 0; m_poolBlockTemplate->m_sidechainId = {}; - // TODO: fill in merkle tree data here - m_poolBlockTemplate->m_merkleTreeDataSize = 1; - m_poolBlockTemplate->m_merkleTreeData = 0; + m_poolBlockTemplate->m_merkleTreeData = PoolBlock::encode_merkle_tree_data(static_cast(data.aux_chains.size() + 1), data.aux_nonce); + m_poolBlockTemplate->m_merkleTreeDataSize = 0; + writeVarint(m_poolBlockTemplate->m_merkleTreeData, [this](uint8_t) { ++m_poolBlockTemplate->m_merkleTreeDataSize; }); m_poolBlockTemplate->m_merkleRoot = {}; + m_poolBlockTemplate->m_auxChains = data.aux_chains; + m_poolBlockTemplate->m_auxNonce = data.aux_nonce; + const std::vector sidechain_data = m_poolBlockTemplate->serialize_sidechain_data(); const std::vector& consensus_id = m_sidechain->consensus_id(); @@ -697,11 +701,7 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, const LOGINFO(6, "blob size = " << m_fullDataBlob.size()); m_poolBlockTemplate->m_sidechainId = calc_sidechain_hash(0); - - // TODO: fill in merkle tree data here - m_poolBlockTemplate->m_merkleTreeDataSize = 1; - m_poolBlockTemplate->m_merkleTreeData = 0; - m_poolBlockTemplate->m_merkleRoot = m_poolBlockTemplate->m_sidechainId; + init_merge_mining_merkle_root(); if (pool_block_debug()) { const size_t sidechain_hash_offset = m_extraNonceOffsetInTemplate + m_poolBlockTemplate->m_extraNonceSize + 2 + m_poolBlockTemplate->m_merkleTreeDataSize; @@ -1312,10 +1312,10 @@ bool BlockTemplate::submit_sidechain_block(uint32_t template_id, uint32_t nonce, m_poolBlockTemplate->m_sidechainId = calc_sidechain_hash(extra_nonce); m_poolBlockTemplate->m_sidechainExtraBuf[3] = extra_nonce; - // TODO: fill in merkle tree data here - m_poolBlockTemplate->m_merkleTreeDataSize = 1; - m_poolBlockTemplate->m_merkleTreeData = 0; - m_poolBlockTemplate->m_merkleRoot = m_poolBlockTemplate->m_sidechainId; + const uint32_t n_aux_chains = static_cast(m_poolBlockTemplate->m_auxChains.size() + 1); + const uint32_t aux_slot = get_aux_slot(m_sidechain->consensus_hash(), m_poolBlockTemplate->m_auxNonce, n_aux_chains); + + m_poolBlockTemplate->m_merkleRoot = get_root_from_proof(m_poolBlockTemplate->m_sidechainId, m_poolBlockTemplate->m_merkleProof, aux_slot, n_aux_chains); if (pool_block_debug()) { std::vector buf = m_poolBlockTemplate->serialize_mainchain_data(); @@ -1368,4 +1368,38 @@ bool BlockTemplate::submit_sidechain_block(uint32_t template_id, uint32_t nonce, return false; } +void BlockTemplate::init_merge_mining_merkle_root() +{ + const uint32_t n_aux_chains = static_cast(m_poolBlockTemplate->m_auxChains.size() + 1); + + if (n_aux_chains == 1) { + m_poolBlockTemplate->m_merkleRoot = m_poolBlockTemplate->m_sidechainId; + return; + } + + std::vector hashes(n_aux_chains); + + for (const AuxChainData& aux_data : m_poolBlockTemplate->m_auxChains) { + const uint32_t aux_slot = get_aux_slot(aux_data.unique_id, m_poolBlockTemplate->m_auxNonce, n_aux_chains); + hashes[aux_slot] = aux_data.data; + } + + const uint32_t aux_slot = get_aux_slot(m_sidechain->consensus_hash(), m_poolBlockTemplate->m_auxNonce, n_aux_chains); + hashes[aux_slot] = m_poolBlockTemplate->m_sidechainId; + + std::vector> tree; + merkle_hash_full_tree(hashes, tree); + m_poolBlockTemplate->m_merkleRoot = tree.back().front(); + + std::vector> proof; + get_merkle_proof(tree, m_poolBlockTemplate->m_sidechainId, proof); + + m_poolBlockTemplate->m_merkleProof.clear(); + m_poolBlockTemplate->m_merkleProof.reserve(proof.size()); + + for (const auto& p : proof) { + m_poolBlockTemplate->m_merkleProof.push_back(p.second); + } +} + } // namespace p2pool diff --git a/src/block_template.h b/src/block_template.h index bffef0e..3318906 100644 --- a/src/block_template.h +++ b/src/block_template.h @@ -132,6 +132,8 @@ private: std::vector m_knapsack; #endif + + void init_merge_mining_merkle_root(); }; } // namespace p2pool diff --git a/src/common.h b/src/common.h index a876317..c5468ad 100644 --- a/src/common.h +++ b/src/common.h @@ -383,6 +383,13 @@ struct TxMempoolData uint64_t time_received; }; +struct AuxChainData +{ + hash unique_id; + hash data; + difficulty_type difficulty; +}; + struct MinerData { FORCEINLINE MinerData() @@ -394,6 +401,7 @@ struct MinerData , median_weight(0) , already_generated_coins(0) , median_timestamp(0) + , aux_nonce(0) {} uint8_t major_version; @@ -406,6 +414,9 @@ struct MinerData uint64_t median_timestamp; std::vector tx_backlog; + std::vector aux_chains; + uint32_t aux_nonce; + std::chrono::high_resolution_clock::time_point time_received; }; diff --git a/src/merkle.cpp b/src/merkle.cpp index 2a77966..bafce98 100644 --- a/src/merkle.cpp +++ b/src/merkle.cpp @@ -194,10 +194,10 @@ bool verify_merkle_proof(hash h, const std::vector>& proof return (h == root); } -bool verify_merkle_proof(hash h, const std::vector& proof, size_t index, size_t count, const hash& root) +hash get_root_from_proof(hash h, const std::vector& proof, size_t index, size_t count) { if (index >= count) { - return false; + return hash(); } hash tmp[2]; @@ -206,7 +206,7 @@ bool verify_merkle_proof(hash h, const std::vector& proof, size_t index, s } else if (count == 2) { if (proof.empty()) { - return false; + return hash(); } if (index & 1) { @@ -233,7 +233,7 @@ bool verify_merkle_proof(hash h, const std::vector& proof, size_t index, s index -= k; if (proof.empty()) { - return false; + return hash(); } if (index & 1) { @@ -253,7 +253,7 @@ bool verify_merkle_proof(hash h, const std::vector& proof, size_t index, s for (; cnt >= 2; ++proof_index, index >>= 1, cnt >>= 1) { if (proof_index >= proof.size()) { - return false; + return hash(); } if (index & 1) { @@ -269,7 +269,12 @@ bool verify_merkle_proof(hash h, const std::vector& proof, size_t index, s } } - return (h == root); + return h; +} + +bool verify_merkle_proof(hash h, const std::vector& proof, size_t index, size_t count, const hash& root) +{ + return get_root_from_proof(h, proof, index, count) == root; } uint32_t get_aux_slot(const hash &id, uint32_t nonce, uint32_t n_aux_chains) @@ -292,4 +297,38 @@ uint32_t get_aux_slot(const hash &id, uint32_t nonce, uint32_t n_aux_chains) return *reinterpret_cast(res.h) % n_aux_chains; } +bool find_aux_nonce(const std::vector& aux_id, uint32_t& nonce, uint32_t max_nonce) +{ + const uint32_t n_aux_chains = static_cast(aux_id.size()); + + if (n_aux_chains <= 1) { + nonce = 0; + return true; + } + + std::vector slots; + + for (uint32_t i = 0;; ++i) { + slots.assign(n_aux_chains, false); + + uint32_t j; + for (j = 0; j < n_aux_chains; ++j) { + const uint32_t k = get_aux_slot(aux_id[j], i, n_aux_chains); + if (slots[k]) { + break; + } + slots[k] = true; + } + + if (j >= n_aux_chains) { + nonce = i; + return true; + } + + if (i == max_nonce) { + return false; + } + } +} + } // namespace p2pool diff --git a/src/merkle.h b/src/merkle.h index 8c36a65..d3a187d 100644 --- a/src/merkle.h +++ b/src/merkle.h @@ -24,8 +24,11 @@ void merkle_hash_full_tree(const std::vector& hashes, std::vector>& tree, const hash& h, std::vector>& proof); bool verify_merkle_proof(hash h, const std::vector>& proof, const hash& root); + +hash get_root_from_proof(hash h, const std::vector& proof, size_t index, size_t count); bool verify_merkle_proof(hash h, const std::vector& proof, size_t index, size_t count, const hash& root); uint32_t get_aux_slot(const hash &id, uint32_t nonce, uint32_t n_aux_chains); +bool find_aux_nonce(const std::vector& aux_id, uint32_t& nonce, uint32_t max_nonce = 0xFFFF); } // namespace p2pool diff --git a/src/pool_block.cpp b/src/pool_block.cpp index 83d9386..38c941c 100644 --- a/src/pool_block.cpp +++ b/src/pool_block.cpp @@ -58,6 +58,7 @@ PoolBlock::PoolBlock() , m_precalculated(false) , m_localTimestamp(seconds_since_epoch()) , m_receivedTimestamp(0) + , m_auxNonce(0) { } @@ -113,6 +114,9 @@ PoolBlock& PoolBlock::operator=(const PoolBlock& b) m_localTimestamp = seconds_since_epoch(); m_receivedTimestamp = b.m_receivedTimestamp; + m_auxChains = b.m_auxChains; + m_auxNonce = b.m_auxNonce; + return *this; } @@ -286,6 +290,11 @@ void PoolBlock::reset_offchain_data() m_localTimestamp = seconds_since_epoch(); m_receivedTimestamp = 0; + + m_auxChains.clear(); + m_auxChains.shrink_to_fit(); + + m_auxNonce = 0; } bool PoolBlock::get_pow_hash(RandomX_Hasher_Base* hasher, uint64_t height, const hash& seed_hash, hash& pow_hash, bool force_light_mode) diff --git a/src/pool_block.h b/src/pool_block.h index ce8ac20..33b8b71 100644 --- a/src/pool_block.h +++ b/src/pool_block.h @@ -148,6 +148,9 @@ struct PoolBlock uint64_t m_localTimestamp; uint64_t m_receivedTimestamp; + std::vector m_auxChains; + uint32_t m_auxNonce; + std::vector serialize_mainchain_data(size_t* header_size = nullptr, size_t* miner_tx_size = nullptr, int* outputs_offset = nullptr, int* outputs_blob_size = nullptr, const uint32_t* nonce = nullptr, const uint32_t* extra_nonce = nullptr) const; std::vector serialize_sidechain_data() const; @@ -176,6 +179,15 @@ struct PoolBlock hash calculate_tx_key_seed() const; + static FORCEINLINE uint64_t encode_merkle_tree_data(uint32_t n_aux_chains, uint32_t nonce) + { + uint32_t n_bits = 1U; + while (((1U << n_bits) < n_aux_chains) && (n_bits < 8)) { + ++n_bits; + } + return (n_bits - 1U) | ((n_aux_chains - 1U) << 3U) | (static_cast(nonce) << (3U + n_bits)); + } + FORCEINLINE void decode_merkle_tree_data(uint32_t& mm_n_aux_chains, uint32_t& mm_nonce) const { const uint32_t k = static_cast(m_merkleTreeData); diff --git a/tests/src/merkle_tests.cpp b/tests/src/merkle_tests.cpp index f02c08d..609a7ae 100644 --- a/tests/src/merkle_tests.cpp +++ b/tests/src/merkle_tests.cpp @@ -243,4 +243,34 @@ TEST(merkle, aux_slot) ASSERT_EQ(get_aux_slot(id, 1, std::numeric_limits::max()), 1080669337U); } +TEST(merkle, aux_nonce) +{ + std::vector aux_id; + uint32_t nonce; + + ASSERT_TRUE(find_aux_nonce(aux_id, nonce)); + ASSERT_EQ(nonce, 0); + + uint8_t data[] = "aux0"; + + const uint32_t nonces[] = { 0, 0, 0, 7, 16, 56, 1, 287, 1423, 1074 }; + hash h; + + for (size_t i = 0; i < 10; ++i, ++data[sizeof(data) - 2]) { + keccak(data, sizeof(data) - 1, h.h); + aux_id.push_back(h); + + ASSERT_TRUE(find_aux_nonce(aux_id, nonce)); + ASSERT_EQ(nonce, nonces[i]); + } + + h = aux_id.front(); + + aux_id.clear(); + aux_id.push_back(h); + aux_id.push_back(h); + + ASSERT_FALSE(find_aux_nonce(aux_id, nonce)); +} + }