Init and update merkle tree root

merge-mining
SChernykh 2023-10-25 20:06:12 +02:00
parent 2d1158af64
commit 0b711cbe65
8 changed files with 158 additions and 18 deletions

View File

@ -28,6 +28,7 @@
#include "side_chain.h"
#include "pool_block.h"
#include "params.h"
#include "merkle.h"
#include <zmq.hpp>
#include <ctime>
#include <numeric>
@ -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<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_auxChains = data.aux_chains;
m_poolBlockTemplate->m_auxNonce = data.aux_nonce;
const std::vector<uint8_t> sidechain_data = m_poolBlockTemplate->serialize_sidechain_data();
const std::vector<uint8_t>& 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<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);
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<uint8_t> 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<uint32_t>(m_poolBlockTemplate->m_auxChains.size() + 1);
if (n_aux_chains == 1) {
m_poolBlockTemplate->m_merkleRoot = m_poolBlockTemplate->m_sidechainId;
return;
}
std::vector<hash> 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<std::vector<hash>> tree;
merkle_hash_full_tree(hashes, tree);
m_poolBlockTemplate->m_merkleRoot = tree.back().front();
std::vector<std::pair<bool, hash>> 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

View File

@ -132,6 +132,8 @@ private:
std::vector<uint32_t> m_knapsack;
#endif
void init_merge_mining_merkle_root();
};
} // namespace p2pool

View File

@ -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<TxMempoolData> tx_backlog;
std::vector<AuxChainData> aux_chains;
uint32_t aux_nonce;
std::chrono::high_resolution_clock::time_point time_received;
};

View File

@ -194,10 +194,10 @@ bool verify_merkle_proof(hash h, const std::vector<std::pair<bool, hash>>& proof
return (h == root);
}
bool verify_merkle_proof(hash h, const std::vector<hash>& proof, size_t index, size_t count, const hash& root)
hash get_root_from_proof(hash h, const std::vector<hash>& 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<hash>& 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<hash>& 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<hash>& 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<hash>& proof, size_t index, s
}
}
return (h == root);
return h;
}
bool verify_merkle_proof(hash h, const std::vector<hash>& 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<uint32_t*>(res.h) % n_aux_chains;
}
bool find_aux_nonce(const std::vector<hash>& aux_id, uint32_t& nonce, uint32_t max_nonce)
{
const uint32_t n_aux_chains = static_cast<uint32_t>(aux_id.size());
if (n_aux_chains <= 1) {
nonce = 0;
return true;
}
std::vector<bool> 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

View File

@ -24,8 +24,11 @@ void merkle_hash_full_tree(const std::vector<hash>& hashes, std::vector<std::vec
bool get_merkle_proof(const std::vector<std::vector<hash>>& tree, const hash& h, std::vector<std::pair<bool, hash>>& proof);
bool verify_merkle_proof(hash h, const std::vector<std::pair<bool, hash>>& proof, const hash& root);
hash get_root_from_proof(hash h, const std::vector<hash>& proof, size_t index, size_t count);
bool verify_merkle_proof(hash h, const std::vector<hash>& 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<hash>& aux_id, uint32_t& nonce, uint32_t max_nonce = 0xFFFF);
} // namespace p2pool

View File

@ -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)

View File

@ -148,6 +148,9 @@ struct PoolBlock
uint64_t m_localTimestamp;
uint64_t m_receivedTimestamp;
std::vector<AuxChainData> m_auxChains;
uint32_t m_auxNonce;
std::vector<uint8_t> 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<uint8_t> 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<uint64_t>(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<uint32_t>(m_merkleTreeData);

View File

@ -243,4 +243,34 @@ TEST(merkle, aux_slot)
ASSERT_EQ(get_aux_slot(id, 1, std::numeric_limits<uint32_t>::max()), 1080669337U);
}
TEST(merkle, aux_nonce)
{
std::vector<hash> 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));
}
}