Fixed Merkle tree root update

This commit is contained in:
SChernykh 2023-10-25 22:54:25 +02:00
parent a852b4e3ad
commit fc31ac6cd0
5 changed files with 83 additions and 27 deletions

View file

@ -413,6 +413,10 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, const
}; };
uint64_t max_reward_amounts_weight = get_reward_amounts_weight(); uint64_t max_reward_amounts_weight = get_reward_amounts_weight();
m_poolBlockTemplate->m_merkleTreeData = PoolBlock::encode_merkle_tree_data(static_cast<uint32_t>(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) { if (create_miner_tx(data, m_shares, max_reward_amounts_weight, true) < 0) {
use_old_template(); use_old_template();
return; return;
@ -656,21 +660,16 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, const
sidechain_extra[2] = static_cast<uint32_t>(m_rng() >> 32); sidechain_extra[2] = static_cast<uint32_t>(m_rng() >> 32);
sidechain_extra[3] = 0; 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_nonce = 0;
m_poolBlockTemplate->m_extraNonce = 0; m_poolBlockTemplate->m_extraNonce = 0;
m_poolBlockTemplate->m_sidechainId = {}; m_poolBlockTemplate->m_sidechainId = {};
m_poolBlockTemplate->m_merkleTreeData = PoolBlock::encode_merkle_tree_data(static_cast<uint32_t>(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_merkleRoot = {};
m_poolBlockTemplate->m_auxChains = data.aux_chains; m_poolBlockTemplate->m_auxChains = data.aux_chains;
m_poolBlockTemplate->m_auxNonce = data.aux_nonce; m_poolBlockTemplate->m_auxNonce = data.aux_nonce;
init_merge_mining_merkle_proof();
const std::vector<uint8_t> sidechain_data = m_poolBlockTemplate->serialize_sidechain_data(); const std::vector<uint8_t> sidechain_data = m_poolBlockTemplate->serialize_sidechain_data();
const std::vector<uint8_t>& consensus_id = m_sidechain->consensus_id(); const std::vector<uint8_t>& 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()); LOGINFO(6, "blob size = " << m_fullDataBlob.size());
m_poolBlockTemplate->m_sidechainId = calc_sidechain_hash(0); m_poolBlockTemplate->m_sidechainId = calc_sidechain_hash(0);
init_merge_mining_merkle_root(); {
const uint32_t n_aux_chains = static_cast<uint32_t>(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()) { 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_blockTemplateBlob.data() + merkle_root_offset, m_poolBlockTemplate->m_merkleRoot.h, HASH_SIZE);
memcpy(m_fullDataBlob.data() + sidechain_hash_offset, m_poolBlockTemplate->m_sidechainId.h, HASH_SIZE); memcpy(m_fullDataBlob.data() + merkle_root_offset, m_poolBlockTemplate->m_merkleRoot.h, HASH_SIZE);
memcpy(m_minerTx.data() + sidechain_hash_offset - m_minerTxOffsetInTemplate, m_poolBlockTemplate->m_sidechainId.h, HASH_SIZE); memcpy(m_minerTx.data() + merkle_root_offset - m_minerTxOffsetInTemplate, m_poolBlockTemplate->m_merkleRoot.h, HASH_SIZE);
const std::vector<uint8_t> mainchain_data = m_poolBlockTemplate->serialize_mainchain_data(); const std::vector<uint8_t> mainchain_data = m_poolBlockTemplate->serialize_mainchain_data();
if (mainchain_data != m_blockTemplateBlob) { if (mainchain_data != m_blockTemplateBlob) {
@ -1029,9 +1032,15 @@ hash BlockTemplate::calc_miner_tx_hash(uint32_t extra_nonce) const
static_cast<uint8_t>(extra_nonce >> 24) static_cast<uint8_t>(extra_nonce >> 24)
}; };
// Calculate sidechain id with this extra_nonce // 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 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; const uint32_t n_aux_chains = static_cast<uint32_t>(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) // 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 // 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) // Slow path: O(N)
if (!b || pool_block_debug()) 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<uint32_t>(offset - static_cast<int>(extra_nonce_offset)); uint32_t k = static_cast<uint32_t>(offset - static_cast<int>(extra_nonce_offset));
if (k < EXTRA_NONCE_SIZE) { if (k < EXTRA_NONCE_SIZE) {
return extra_nonce_buf[k]; return extra_nonce_buf[k];
} }
k = static_cast<uint32_t>(offset - static_cast<int>(sidechain_hash_offset)); k = static_cast<uint32_t>(offset - static_cast<int>(merkle_root_offset));
if (k < HASH_SIZE) { if (k < HASH_SIZE) {
return sidechain_id.h[k]; return merge_mining_root.h[k];
} }
return data[offset]; 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, data + N, inlen);
memcpy(tx_buf + extra_nonce_offset - N, extra_nonce_buf, EXTRA_NONCE_SIZE); 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]; uint64_t st[25];
memcpy(st, m_minerTxKeccakState, sizeof(st)); memcpy(st, m_minerTxKeccakState, sizeof(st));
@ -1368,33 +1377,42 @@ bool BlockTemplate::submit_sidechain_block(uint32_t template_id, uint32_t nonce,
return false; return false;
} }
void BlockTemplate::init_merge_mining_merkle_root() void BlockTemplate::init_merge_mining_merkle_proof()
{ {
const uint32_t n_aux_chains = static_cast<uint32_t>(m_poolBlockTemplate->m_auxChains.size() + 1); const uint32_t n_aux_chains = static_cast<uint32_t>(m_poolBlockTemplate->m_auxChains.size() + 1);
m_poolBlockTemplate->m_merkleProof.clear();
if (n_aux_chains == 1) { if (n_aux_chains == 1) {
m_poolBlockTemplate->m_merkleRoot = m_poolBlockTemplate->m_sidechainId;
return; return;
} }
std::vector<hash> hashes(n_aux_chains); std::vector<hash> hashes(n_aux_chains);
std::vector<bool> used(n_aux_chains);
for (const AuxChainData& aux_data : m_poolBlockTemplate->m_auxChains) { 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); 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; 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); 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; 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<std::vector<hash>> tree; std::vector<std::vector<hash>> tree;
merkle_hash_full_tree(hashes, tree); merkle_hash_full_tree(hashes, tree);
m_poolBlockTemplate->m_merkleRoot = tree.back().front();
std::vector<std::pair<bool, hash>> proof; std::vector<std::pair<bool, hash>> proof;
get_merkle_proof(tree, m_poolBlockTemplate->m_sidechainId, proof); get_merkle_proof(tree, m_poolBlockTemplate->m_sidechainId, proof);
m_poolBlockTemplate->m_merkleProof.clear();
m_poolBlockTemplate->m_merkleProof.reserve(proof.size()); m_poolBlockTemplate->m_merkleProof.reserve(proof.size());
for (const auto& p : proof) { for (const auto& p : proof) {

View file

@ -133,7 +133,7 @@ private:
std::vector<uint32_t> m_knapsack; std::vector<uint32_t> m_knapsack;
#endif #endif
void init_merge_mining_merkle_root(); void init_merge_mining_merkle_proof();
}; };
} // namespace p2pool } // namespace p2pool

View file

@ -42,6 +42,18 @@ FORCEINLINE void keccak(const uint8_t* in, int inlen, uint8_t (&md)[N])
template<typename T> template<typename T>
FORCEINLINE void keccak_custom(T&& in, int inlen, uint8_t* md, int mdlen) 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<uint8_t>(in(i)));
}
printf("\n");
}
#endif
uint64_t st[25] = {}; uint64_t st[25] = {};
const int rsiz = sizeof(st) == mdlen ? KeccakParams::HASH_DATA_AREA : 200 - 2 * mdlen; const int rsiz = sizeof(st) == mdlen ? KeccakParams::HASH_DATA_AREA : 200 - 2 * mdlen;

View file

@ -196,15 +196,17 @@ bool verify_merkle_proof(hash h, const std::vector<std::pair<bool, hash>>& proof
hash get_root_from_proof(hash h, const std::vector<hash>& proof, size_t index, size_t count) hash get_root_from_proof(hash h, const std::vector<hash>& proof, size_t index, size_t count)
{ {
if (count == 1) {
return h;
}
if (index >= count) { if (index >= count) {
return hash(); return hash();
} }
hash tmp[2]; hash tmp[2];
if (count == 1) { if (count == 2) {
}
else if (count == 2) {
if (proof.empty()) { if (proof.empty()) {
return hash(); return hash();
} }

View file

@ -36,6 +36,7 @@
#include "p2pool_api.h" #include "p2pool_api.h"
#include "pool_block.h" #include "pool_block.h"
#include "keccak.h" #include "keccak.h"
#include "merkle.h"
#include <thread> #include <thread>
#include <fstream> #include <fstream>
#include <numeric> #include <numeric>
@ -333,6 +334,29 @@ void p2pool::handle_miner_data(MinerData& data)
cleanup_mainchain_data(data.height); cleanup_mainchain_data(data.height);
} }
// TODO: remove after testing
#if 1
{
data.aux_chains.clear();
data.aux_chains.resize(10);
std::vector<hash> 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.tx_backlog.clear();
data.time_received = std::chrono::high_resolution_clock::now(); data.time_received = std::chrono::high_resolution_clock::now();
{ {