diff --git a/src/block_template.cpp b/src/block_template.cpp index 97188cb..159bb1f 100644 --- a/src/block_template.cpp +++ b/src/block_template.cpp @@ -413,6 +413,10 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, const }; uint64_t max_reward_amounts_weight = get_reward_amounts_weight(); + 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; }); + if (create_miner_tx(data, m_shares, max_reward_amounts_weight, true) < 0) { use_old_template(); return; @@ -656,21 +660,16 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, const sidechain_extra[2] = static_cast(m_rng() >> 32); sidechain_extra[3] = 0; - // Merkle proof (empty for now, fill it in when other merge-mined chains are included in the block template) - m_poolBlockTemplate->m_merkleProof.clear(); - m_poolBlockTemplate->m_nonce = 0; m_poolBlockTemplate->m_extraNonce = 0; m_poolBlockTemplate->m_sidechainId = {}; - - 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; + init_merge_mining_merkle_proof(); + const std::vector sidechain_data = m_poolBlockTemplate->serialize_sidechain_data(); const std::vector& consensus_id = m_sidechain->consensus_id(); @@ -701,14 +700,18 @@ 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); - init_merge_mining_merkle_root(); + { + const uint32_t n_aux_chains = static_cast(data.aux_chains.size() + 1); + const uint32_t aux_slot = get_aux_slot(m_sidechain->consensus_hash(), data.aux_nonce, 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()) { - const size_t sidechain_hash_offset = m_extraNonceOffsetInTemplate + m_poolBlockTemplate->m_extraNonceSize + 2 + m_poolBlockTemplate->m_merkleTreeDataSize; + const size_t merkle_root_offset = m_extraNonceOffsetInTemplate + m_poolBlockTemplate->m_extraNonceSize + 2 + m_poolBlockTemplate->m_merkleTreeDataSize; - memcpy(m_blockTemplateBlob.data() + sidechain_hash_offset, m_poolBlockTemplate->m_sidechainId.h, HASH_SIZE); - memcpy(m_fullDataBlob.data() + sidechain_hash_offset, m_poolBlockTemplate->m_sidechainId.h, HASH_SIZE); - memcpy(m_minerTx.data() + sidechain_hash_offset - m_minerTxOffsetInTemplate, m_poolBlockTemplate->m_sidechainId.h, HASH_SIZE); + memcpy(m_blockTemplateBlob.data() + merkle_root_offset, m_poolBlockTemplate->m_merkleRoot.h, HASH_SIZE); + memcpy(m_fullDataBlob.data() + merkle_root_offset, m_poolBlockTemplate->m_merkleRoot.h, HASH_SIZE); + memcpy(m_minerTx.data() + merkle_root_offset - m_minerTxOffsetInTemplate, m_poolBlockTemplate->m_merkleRoot.h, HASH_SIZE); const std::vector mainchain_data = m_poolBlockTemplate->serialize_mainchain_data(); if (mainchain_data != m_blockTemplateBlob) { @@ -1029,9 +1032,15 @@ hash BlockTemplate::calc_miner_tx_hash(uint32_t extra_nonce) const static_cast(extra_nonce >> 24) }; - // Calculate sidechain id with this extra_nonce - const hash sidechain_id = calc_sidechain_hash(extra_nonce); - const size_t sidechain_hash_offset = extra_nonce_offset + m_poolBlockTemplate->m_extraNonceSize + 2 + m_poolBlockTemplate->m_merkleTreeDataSize; + // Calculate sidechain id and merge mining root hash with this extra_nonce + hash merge_mining_root; + { + const hash sidechain_id = calc_sidechain_hash(extra_nonce); + 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); + merge_mining_root = get_root_from_proof(sidechain_id, m_poolBlockTemplate->m_merkleProof, aux_slot, n_aux_chains); + } + const size_t merkle_root_offset = extra_nonce_offset + m_poolBlockTemplate->m_extraNonceSize + 2 + m_poolBlockTemplate->m_merkleTreeDataSize; // 1. Prefix (everything except vin_rct_type byte in the end) // Apply extra_nonce in-place because we can't write to the block template here @@ -1046,15 +1055,15 @@ hash BlockTemplate::calc_miner_tx_hash(uint32_t extra_nonce) const // Slow path: O(N) if (!b || pool_block_debug()) { - keccak_custom([data, extra_nonce_offset, &extra_nonce_buf, sidechain_hash_offset, &sidechain_id](int offset) { + keccak_custom([data, extra_nonce_offset, &extra_nonce_buf, merkle_root_offset, &merge_mining_root](int offset) { uint32_t k = static_cast(offset - static_cast(extra_nonce_offset)); if (k < EXTRA_NONCE_SIZE) { return extra_nonce_buf[k]; } - k = static_cast(offset - static_cast(sidechain_hash_offset)); + k = static_cast(offset - static_cast(merkle_root_offset)); if (k < HASH_SIZE) { - return sidechain_id.h[k]; + return merge_mining_root.h[k]; } return data[offset]; @@ -1068,7 +1077,7 @@ hash BlockTemplate::calc_miner_tx_hash(uint32_t extra_nonce) const memcpy(tx_buf, data + N, inlen); memcpy(tx_buf + extra_nonce_offset - N, extra_nonce_buf, EXTRA_NONCE_SIZE); - memcpy(tx_buf + sidechain_hash_offset - N, sidechain_id.h, HASH_SIZE); + memcpy(tx_buf + merkle_root_offset - N, merge_mining_root.h, HASH_SIZE); uint64_t st[25]; memcpy(st, m_minerTxKeccakState, sizeof(st)); @@ -1368,33 +1377,42 @@ bool BlockTemplate::submit_sidechain_block(uint32_t template_id, uint32_t nonce, return false; } -void BlockTemplate::init_merge_mining_merkle_root() +void BlockTemplate::init_merge_mining_merkle_proof() { const uint32_t n_aux_chains = static_cast(m_poolBlockTemplate->m_auxChains.size() + 1); + m_poolBlockTemplate->m_merkleProof.clear(); + if (n_aux_chains == 1) { - m_poolBlockTemplate->m_merkleRoot = m_poolBlockTemplate->m_sidechainId; return; } std::vector hashes(n_aux_chains); + std::vector used(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; + used[aux_slot] = true; } 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; + used[aux_slot] = true; + + for (bool b : used) { + if (!b) { + LOGERR(1, "aux nonce is invalid. Fix the code!"); + break; + } + } 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) { diff --git a/src/block_template.h b/src/block_template.h index 3318906..0eb8b3b 100644 --- a/src/block_template.h +++ b/src/block_template.h @@ -133,7 +133,7 @@ private: std::vector m_knapsack; #endif - void init_merge_mining_merkle_root(); + void init_merge_mining_merkle_proof(); }; } // namespace p2pool diff --git a/src/keccak.h b/src/keccak.h index 7274d61..145df55 100644 --- a/src/keccak.h +++ b/src/keccak.h @@ -42,6 +42,18 @@ FORCEINLINE void keccak(const uint8_t* in, int inlen, uint8_t (&md)[N]) template FORCEINLINE void keccak_custom(T&& in, int inlen, uint8_t* md, int mdlen) { + // TODO: remove after testing +#if 0 + if (inlen > 100) { + printf("\nkeccak_custom %d", inlen); + for (int i = 0; i < inlen; ++i) { + if ((i & 31) == 0) printf("\n"); + printf("%02X ", static_cast(in(i))); + } + printf("\n"); + } +#endif + uint64_t st[25] = {}; const int rsiz = sizeof(st) == mdlen ? KeccakParams::HASH_DATA_AREA : 200 - 2 * mdlen; diff --git a/src/merkle.cpp b/src/merkle.cpp index bafce98..640ea90 100644 --- a/src/merkle.cpp +++ b/src/merkle.cpp @@ -196,15 +196,17 @@ bool verify_merkle_proof(hash h, const std::vector>& proof hash get_root_from_proof(hash h, const std::vector& proof, size_t index, size_t count) { + if (count == 1) { + return h; + } + if (index >= count) { return hash(); } hash tmp[2]; - if (count == 1) { - } - else if (count == 2) { + if (count == 2) { if (proof.empty()) { return hash(); } diff --git a/src/p2pool.cpp b/src/p2pool.cpp index 6f79998..460d89c 100644 --- a/src/p2pool.cpp +++ b/src/p2pool.cpp @@ -36,6 +36,7 @@ #include "p2pool_api.h" #include "pool_block.h" #include "keccak.h" +#include "merkle.h" #include #include #include @@ -333,6 +334,29 @@ void p2pool::handle_miner_data(MinerData& data) cleanup_mainchain_data(data.height); } + // TODO: remove after testing +#if 1 + { + data.aux_chains.clear(); + data.aux_chains.resize(10); + + std::vector tmp(11); + + uint8_t id[] = "aux0"; + uint8_t aux_data[] = "data0"; + + for (int i = 0; i < 10; ++i, ++id[sizeof(id) - 2], ++aux_data[sizeof(aux_data) - 2]) { + keccak(id, sizeof(id) - 1, tmp[i].h); + + data.aux_chains[i].unique_id = tmp[i]; + keccak(aux_data, sizeof(aux_data) - 1, data.aux_chains[i].data.h); + } + + tmp[10] = m_sideChain->consensus_hash(); + find_aux_nonce(tmp, data.aux_nonce); + } +#endif + data.tx_backlog.clear(); data.time_received = std::chrono::high_resolution_clock::now(); {