Refactored merge mining code

This commit is contained in:
SChernykh
2026-04-24 11:01:59 +02:00
parent eaee503d05
commit 54e0d3f3eb
8 changed files with 372 additions and 292 deletions
+16
View File
@@ -172,6 +172,22 @@ struct alignas(uint64_t) hash
}
}
constexpr hash(const char (&s)[HASH_SIZE * 2 + 1]) : h{} {
for (size_t i = 0; i < HASH_SIZE * 2; ++i) {
char c = s[i];
if ('0' <= c && c <= '9') {
c -= '0';
} else if ('a' <= c && c <= 'f') {
c = (c - 'a') + 10;
} else if ('A' <= c && c <= 'F') {
c = (c - 'A') + 10;
}
h[i / 2] = (h[i / 2] << 4) | static_cast<uint8_t>(c);
}
}
FORCEINLINE bool operator<(const hash& other) const
{
const uint64_t* a = u64();
+263 -1
View File
@@ -23,10 +23,19 @@
#include "merge_mining_client_tari.h"
#endif
LOG_CATEGORY(MergeMiningClient)
#include "p2pool.h"
#include "params.h"
#include "pool_block.h"
#include "keccak_constexpr.h"
#include "side_chain.h"
#include "merkle.h"
static thread_local const char* log_category_prefix = "MergeMiningClient ";
namespace p2pool {
static constexpr hash Tari_ChainID{ "01f0cf665bd4cd31cbb2b2470236389c483522b350335e10a4a5dca34cb85990" };
IMergeMiningClient* IMergeMiningClient::create(p2pool* pool, const std::string& host, const std::string& wallet) noexcept
{
try {
@@ -43,4 +52,257 @@ IMergeMiningClient* IMergeMiningClient::create(p2pool* pool, const std::string&
return nullptr;
}
MergeMiningClientShared::MergeMiningClientShared(p2pool* pool, const std::string& wallet)
: m_chainParamsLock{}
, m_chainParams{}
, m_chainParamsTimestamp(0)
, m_auxWallet(wallet)
, m_pool(pool)
, m_previousAuxHashes{}
, m_previousAuxHashesIndex(0)
, m_previousAuxHashesFoundIndex(std::numeric_limits<uint32_t>::max())
{
uv_rwlock_init_checked(&m_chainParamsLock);
}
MergeMiningClientShared::~MergeMiningClientShared()
{
uv_rwlock_destroy(&m_chainParamsLock);
}
void MergeMiningClientShared::on_external_block(const PoolBlock& block)
{
#ifdef WITH_MERGE_MINING_DONATION
// The rest of the code is needed only when this node is sending donation messages
if (m_pool->params().m_authorKeyFile.empty()) {
return;
}
const char* old_log_category_prefix = log_category_prefix;
log_category_prefix = get_log_category();
ON_SCOPE_LEAVE([old_log_category_prefix]() { log_category_prefix = old_log_category_prefix; });
// Sanity check
if (block.m_transactions.empty() || block.m_hashingBlob.empty() || (block.m_hashingBlob.size() > 128)) {
LOGWARN(3, "on_external_block: sanity check failed - " << block.m_transactions.size() << " transactions, hashing blob size = " << block.m_hashingBlob.size());
return;
}
ChainParameters chain_params;
hash previous_aux_hashes[NUM_PREVIOUS_HASHES];
{
ReadLock lock(m_chainParamsLock);
chain_params = m_chainParams;
std::copy(m_previousAuxHashes, m_previousAuxHashes + NUM_PREVIOUS_HASHES, previous_aux_hashes);
}
// Don't continue if our aux chain is not there
if (block.m_mergeMiningExtra.find(chain_params.aux_id) == block.m_mergeMiningExtra.end()) {
return;
}
// All aux chains in this block + the P2Pool sidechain
std::vector<hash> aux_ids;
// All aux chains in this block
std::vector<AuxChainData> aux_chains;
aux_ids.reserve(block.m_mergeMiningExtra.size() + 1);
aux_chains.reserve(block.m_mergeMiningExtra.size() + 1);
uint64_t mm_extra_size = 0;
for (const auto& i : block.m_mergeMiningExtra) {
// Filter aux chain data only
if ((i.first == keccak_subaddress_viewpub) || (i.first == keccak_onion_address_v3) || (i.first == keccak_i2p_b32_address)) {
continue;
}
++mm_extra_size;
hash data;
difficulty_type diff;
{
const std::vector<uint8_t>& v = i.second;
const uint8_t* p = v.data();
const uint8_t* e = v.data() + v.size();
if (p + HASH_SIZE > e) {
LOGWARN(3, "on_external_block: sanity check failed - invalid merge mining extra data " << '1');
return;
}
memcpy(data.h, p, HASH_SIZE);
p += HASH_SIZE;
p = readVarint(p, e, diff.lo);
if (!p) {
LOGWARN(3, "on_external_block: sanity check failed - invalid merge mining extra data " << '2');
diff.lo = 0;
}
else {
p = readVarint(p, e, diff.hi);
if (!p) {
LOGWARN(3, "on_external_block: sanity check failed - invalid merge mining extra data " << '3');
diff.hi = 0;
}
}
}
// If it's our aux chain, check that it's the same job and that there is enough PoW
if (i.first == chain_params.aux_id) {
const bool different_hash = (data != chain_params.aux_hash);
if (different_hash || (diff != chain_params.aux_diff)) {
uint32_t index = std::numeric_limits<uint32_t>::max();
if (different_hash) {
for (uint32_t k = 0; k < NUM_PREVIOUS_HASHES; ++k) {
if (previous_aux_hashes[k] == data) {
index = k;
break;
}
}
}
m_previousAuxHashesFoundIndex = index;
if (different_hash && (index == std::numeric_limits<uint32_t>::max())) {
LOGINFO(4, "External aux job solution found, but it's for another miner");
return;
}
LOGINFO(4, "External aux job solution found, but it's stale");
chain_params.aux_hash = data;
chain_params.aux_diff = diff;
}
else {
m_previousAuxHashesFoundIndex = std::numeric_limits<uint32_t>::max();
}
if (!diff.check_pow(block.m_powHash)) {
#ifndef P2POOL_LOG_DISABLE
const char* name = ((chain_params.aux_id == Tari_ChainID) ? "Tari" : "aux");
#endif
LOGINFO(4, "External aux job solution found, but it doesn't have enough PoW (block diff = " << block.m_difficulty << ", " << name << " diff = " << diff << ')');
return;
}
}
aux_ids.emplace_back(i.first);
aux_chains.emplace_back(i.first, data, diff);
}
aux_ids.emplace_back(m_pool->side_chain().consensus_hash());
LOGINFO(0, log::LightGreen() << "External aux job solution found. Processing it!");
// coinbase_merkle_proof
root_hash root;
std::vector<hash> proof;
uint32_t path;
#ifdef WITH_INDEXED_HASHES
std::vector<hash> transactions;
transactions.reserve(block.m_transactions.size());
for (const auto& h : block.m_transactions) {
transactions.emplace_back(h);
}
#else
const std::vector<hash>& transactions = block.m_transactions;
#endif
if (!merkle_hash_with_proof(transactions, 0, proof, path, root)) {
LOGWARN(3, "on_external_block: merkle_hash_with_proof failed for coinbase transaction");
return;
}
if (!verify_merkle_proof(transactions[0], proof, path, root)) {
LOGWARN(3, "on_external_block: verify_merkle_proof failed for coinbase transaction");
return;
}
std::vector<uint8_t> 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);
}
// hashing_blob
uint8_t hashing_blob[128] = {};
memcpy(hashing_blob, block.m_hashingBlob.data(), block.m_hashingBlob.size());
// nonce_offset and blob
size_t header_size = 0;
const std::vector<uint8_t> blob = block.serialize_mainchain_data(&header_size);
if (header_size <= NONCE_SIZE) {
LOGWARN(3, "on_external_block: invalid header_size");
return;
}
const uint32_t nonce_offset = static_cast<uint32_t>(header_size - NONCE_SIZE);
// aux_merkle_proof, aux_merkle_proof_path
std::vector<hash> aux_merkle_proof;
uint32_t aux_merkle_proof_path = 0;
const hash sidechain_id = block.m_sidechainId;
const uint32_t n_aux_chains = static_cast<uint32_t>(mm_extra_size + 1);
std::vector<hash> hashes(n_aux_chains);
uint32_t aux_nonce;
if (!find_aux_nonce(aux_ids, aux_nonce, 1000)) {
LOGWARN(3, "on_external_block: failed to find aux_nonce");
return;
}
for (const AuxChainData& aux_data : aux_chains) {
const uint32_t aux_slot = get_aux_slot(aux_data.unique_id, aux_nonce, n_aux_chains);
if (!hashes[aux_slot].empty()) {
LOGWARN(3, "on_external_block: found an incorrect aux_nonce " << '1');
return;
}
hashes[aux_slot] = aux_data.data;
}
const uint32_t aux_slot = get_aux_slot(m_pool->side_chain().consensus_hash(), aux_nonce, n_aux_chains);
if (!hashes[aux_slot].empty()) {
LOGWARN(3, "on_external_block: found an incorrect aux_nonce " << '2');
return;
}
hashes[aux_slot] = sidechain_id;
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 (root != block.m_merkleRoot) {
LOGWARN(3, "on_external_block: merkle root didn't match");
return;
}
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;
}
submit_solution(coinbase_merkle_proof, hashing_blob, nonce_offset, block.m_seed, blob, aux_merkle_proof, aux_merkle_proof_path);
#else // WITH_MERGE_MINING_DONATION
(void)block;
#endif // WITH_MERGE_MINING_DONATION
}
} // namespace p2pool
+32
View File
@@ -17,6 +17,8 @@
#pragma once
#include "uv_util.h"
namespace p2pool {
class p2pool;
@@ -53,4 +55,34 @@ public:
virtual void api_status(log::Stream&) const = 0;
};
class MergeMiningClientShared : public IMergeMiningClient, public nocopy_nomove
{
public:
MergeMiningClientShared(p2pool* pool, const std::string& wallet);
~MergeMiningClientShared() override;
void on_external_block(const PoolBlock& block) override;
virtual const char* get_log_category() const = 0;
protected:
mutable uv_rwlock_t m_chainParamsLock;
ChainParameters m_chainParams;
uint64_t m_chainParamsTimestamp;
std::string m_auxWallet;
p2pool* m_pool;
enum {
NUM_PREVIOUS_HASHES = 8,
};
hash m_previousAuxHashes[NUM_PREVIOUS_HASHES];
uint32_t m_previousAuxHashesIndex;
std::atomic<uint32_t> m_previousAuxHashesFoundIndex;
};
} // namespace p2pool
+41 -24
View File
@@ -29,12 +29,9 @@ LOG_CATEGORY(MergeMiningClientJSON_RPC)
namespace p2pool {
MergeMiningClientJSON_RPC::MergeMiningClientJSON_RPC(p2pool* pool, const std::string& host, const std::string& wallet)
: m_host(host)
: MergeMiningClientShared(pool, wallet)
, m_host(host)
, m_port(80)
, m_chainParamsTimestamp(0)
, m_auxWallet(wallet)
, m_ping(0.0)
, m_pool(pool)
, m_loop{}
, m_loopThread{}
, m_timer{}
@@ -77,8 +74,6 @@ MergeMiningClientJSON_RPC::MergeMiningClientJSON_RPC(p2pool* pool, const std::st
}
m_timer.data = this;
uv_rwlock_init_checked(&m_lock);
err = uv_thread_create(&m_loopThread, loop, this);
if (err) {
LOGERR(1, "failed to start event loop thread, error " << uv_err_name(err));
@@ -94,8 +89,6 @@ MergeMiningClientJSON_RPC::~MergeMiningClientJSON_RPC()
uv_async_send(&m_shutdownAsync);
uv_thread_join(&m_loopThread);
uv_rwlock_destroy(&m_lock);
LOGINFO(1, "stopped");
}
@@ -115,15 +108,14 @@ void MergeMiningClientJSON_RPC::merge_mining_get_chain_id()
JSONRPCRequest::call(m_host, m_port, req, std::string(), m_pool->params().m_socks5Proxy, false, std::string(),
[this](const char* data, size_t size, double ping) {
WriteLock lock(m_lock);
WriteLock lock(m_chainParamsLock);
if (parse_merge_mining_get_chain_id(data, size)) {
if (ping > 0.0) {
m_ping = ping;
}
LOGINFO(1, m_host << ':' << m_port << " uses chain_id " << log::LightCyan() << m_chainParams.aux_id);
LOGINFO(1, m_host << ':' << m_port << " ping is " << m_ping << " ms");
if (ping > 0.0) {
LOGINFO(1, m_host << ':' << m_port << " ping is " << ping << " ms");
}
// Chain ID received successfully, we can start polling for new mining jobs now
const int err = uv_timer_start(&m_timer, on_timer, 0, 500);
@@ -191,7 +183,7 @@ void MergeMiningClientJSON_RPC::merge_mining_get_aux_block(uint64_t height, cons
hash aux_hash;
{
ReadLock lock(m_lock);
ReadLock lock(m_chainParamsLock);
aux_hash = m_chainParams.aux_hash;
}
@@ -207,14 +199,29 @@ void MergeMiningClientJSON_RPC::merge_mining_get_aux_block(uint64_t height, cons
bool changed = false;
hash chain_id;
hash prev_aux_hash;
std::vector<uint8_t> prev_aux_blob;
{
WriteLock lock(m_lock);
WriteLock lock(m_chainParamsLock);
prev_aux_hash = m_chainParams.aux_hash;
prev_aux_blob = m_chainParams.aux_blob;
if (parse_merge_mining_get_aux_block(data, size, changed)) {
chain_id = m_chainParams.aux_id;
}
}
if (changed && !chain_id.empty()) {
{
WriteLock lock(m_chainParamsLock);
const uint32_t index = (m_previousAuxHashesIndex++) % NUM_PREVIOUS_HASHES;
m_previousAuxHashes[index] = prev_aux_hash;
m_previousAuxBlobs[index] = prev_aux_blob;
}
m_pool->update_aux_data(chain_id);
}
},
@@ -293,14 +300,19 @@ bool MergeMiningClientJSON_RPC::parse_merge_mining_get_aux_block(const char* dat
void MergeMiningClientJSON_RPC::submit_solution(const std::vector<uint8_t>& /*coinbase_merkle_proof*/, const uint8_t (&/*hashing_blob*/)[128], size_t /*nonce_offset*/, const hash& seed_hash, const std::vector<uint8_t>& blob, const std::vector<hash>& merkle_proof, uint32_t merkle_proof_path)
{
ReadLock lock(m_lock);
ReadLock lock(m_chainParamsLock);
std::vector<char> buf((m_chainParams.aux_blob.size() + HASH_SIZE + blob.size()) * 2 + merkle_proof.size() * (HASH_SIZE * 2 + 3) + 256);
const uint32_t index = m_previousAuxHashesFoundIndex.exchange(std::numeric_limits<uint32_t>::max());
const std::vector<uint8_t>& aux_blob = (index < NUM_PREVIOUS_HASHES) ? m_previousAuxBlobs[index] : m_chainParams.aux_blob;
const hash& aux_hash = (index < NUM_PREVIOUS_HASHES) ? m_previousAuxHashes[index] : m_chainParams.aux_hash;
std::vector<char> buf((aux_blob.size() + HASH_SIZE + blob.size()) * 2 + merkle_proof.size() * (HASH_SIZE * 2 + 3) + 256);
log::Stream s(buf.data(), buf.size());
s << "{\"jsonrpc\":\"2.0\",\"id\":\"0\",\"method\":\"merge_mining_submit_solution\",\"params\":{"
<< "\"aux_blob\":\"" << log::hex_buf(m_chainParams.aux_blob.data(), m_chainParams.aux_blob.size()) << '"'
<< ",\"aux_hash\":\"" << m_chainParams.aux_hash << '"'
<< "\"aux_blob\":\"" << log::hex_buf(aux_blob.data(), aux_blob.size()) << '"'
<< ",\"aux_hash\":\"" << aux_hash << '"'
<< ",\"blob\":\"" << log::hex_buf(blob.data(), blob.size()) << '"'
<< ",\"merkle_proof\":[";
@@ -329,7 +341,7 @@ void MergeMiningClientJSON_RPC::submit_solution(const std::vector<uint8_t>& /*co
void MergeMiningClientJSON_RPC::print_status() const
{
ReadLock lock(m_lock);
ReadLock lock(m_chainParamsLock);
LOGINFO(0, "status" <<
"\nHost = " << m_host << ':' << m_port <<
@@ -340,7 +352,7 @@ void MergeMiningClientJSON_RPC::print_status() const
void MergeMiningClientJSON_RPC::api_status(log::Stream& s) const
{
ReadLock lock(m_lock);
ReadLock lock(m_chainParamsLock);
s << '{'
<< "\"api\":\"JSON RPC\","
@@ -356,7 +368,7 @@ bool MergeMiningClientJSON_RPC::get_params(ChainParameters& out_params) const
{
const uint64_t t = seconds_since_epoch();
ReadLock lock(m_lock);
ReadLock lock(m_chainParamsLock);
if (m_chainParams.aux_id.empty() || m_chainParams.aux_diff.empty()) {
return false;
@@ -448,4 +460,9 @@ void MergeMiningClientJSON_RPC::on_shutdown()
uv_close(reinterpret_cast<uv_handle_t*>(&m_timer), nullptr);
}
const char* MergeMiningClientJSON_RPC::get_log_category() const
{
return log_category_prefix;
}
} // namespace p2pool
+5 -13
View File
@@ -24,19 +24,20 @@ namespace p2pool {
class p2pool;
struct PoolBlock;
class MergeMiningClientJSON_RPC : public IMergeMiningClient
class MergeMiningClientJSON_RPC : public MergeMiningClientShared
{
public:
MergeMiningClientJSON_RPC(p2pool* pool, const std::string& host, const std::string& wallet);
~MergeMiningClientJSON_RPC() override;
bool get_params(ChainParameters& out_params) const override;
void on_external_block(const PoolBlock& /*block*/) override {}
void submit_solution(const std::vector<uint8_t>& coinbase_merkle_proof, const uint8_t (&hashing_blob)[128], size_t nonce_offset, const hash& seed_hash, const std::vector<uint8_t>& blob, const std::vector<hash>& merkle_proof, uint32_t merkle_proof_path) override;
void print_status() const override;
void api_status(log::Stream&) const override;
const char* get_log_category() const override;
private:
static void loop(void* data);
@@ -51,20 +52,11 @@ private:
bool parse_merge_mining_submit_solution(const char* data, size_t size) const;
std::vector<uint8_t> m_previousAuxBlobs[NUM_PREVIOUS_HASHES];
std::string m_host;
int32_t m_port;
mutable uv_rwlock_t m_lock;
ChainParameters m_chainParams;
uint64_t m_chainParamsTimestamp;
std::string m_auxWallet;
double m_ping;
p2pool* m_pool;
uv_loop_t m_loop;
uv_thread_t m_loopThread;
+7 -236
View File
@@ -34,13 +34,7 @@ using namespace tari::rpc;
namespace p2pool {
MergeMiningClientTari::MergeMiningClientTari(p2pool* pool, std::string host, const std::string& wallet)
: m_chainParams{}
, m_chainParamsTimestamp(0)
, m_previousAuxHashes{}
, m_previousAuxHashesIndex(0)
, m_previousAuxHashesFoundIndex(std::numeric_limits<uint32_t>::max())
, m_auxWallet(wallet)
, m_pool(pool)
: MergeMiningClientShared(pool, wallet)
, m_tariJobParams{}
, m_server(new TariServer(pool->params().m_socks5Proxy, pool->params().m_socks5ProxyType))
, m_hostStr(host)
@@ -78,8 +72,6 @@ MergeMiningClientTari::MergeMiningClientTari(p2pool* pool, std::string host, con
throw std::exception();
}
uv_rwlock_init_checked(&m_chainParamsLock);
if (!m_server->start()) {
throw std::exception();
}
@@ -170,8 +162,6 @@ MergeMiningClientTari::~MergeMiningClientTari()
m_TariNode.reset();
m_channel.reset();
uv_rwlock_destroy(&m_chainParamsLock);
uv_mutex_destroy(&m_workerLock);
uv_cond_destroy(&m_workerCond);
@@ -207,230 +197,6 @@ bool MergeMiningClientTari::get_params(ChainParameters& out_params) const
return true;
}
void MergeMiningClientTari::on_external_block(const PoolBlock& block)
{
#ifdef WITH_MERGE_MINING_DONATION
// The rest of the code is needed only when this node is sending donation messages
if (m_pool->params().m_authorKeyFile.empty()) {
return;
}
// Sanity check
if (block.m_transactions.empty() || block.m_hashingBlob.empty() || (block.m_hashingBlob.size() > 128)) {
LOGWARN(3, "on_external_block: sanity check failed - " << block.m_transactions.size() << " transactions, hashing blob size = " << block.m_hashingBlob.size());
return;
}
ChainParameters chain_params;
uint64_t previous_aux_hashes[NUM_PREVIOUS_HASHES];
{
ReadLock lock(m_chainParamsLock);
chain_params = m_chainParams;
memcpy(previous_aux_hashes, m_previousAuxHashes, sizeof(m_previousAuxHashes));
}
// Don't continue if our aux chain is not there
if (block.m_mergeMiningExtra.find(chain_params.aux_id) == block.m_mergeMiningExtra.end()) {
return;
}
// All aux chains in this block + the P2Pool sidechain
std::vector<hash> aux_ids;
// All aux chains in this block
std::vector<AuxChainData> aux_chains;
aux_ids.reserve(block.m_mergeMiningExtra.size() + 1);
aux_chains.reserve(block.m_mergeMiningExtra.size() + 1);
uint64_t mm_extra_size = 0;
for (const auto& i : block.m_mergeMiningExtra) {
// Filter aux chain data only
if ((i.first == keccak_subaddress_viewpub) || (i.first == keccak_onion_address_v3) || (i.first == keccak_i2p_b32_address)) {
continue;
}
++mm_extra_size;
hash data;
difficulty_type diff;
{
const std::vector<uint8_t>& v = i.second;
const uint8_t* p = v.data();
const uint8_t* e = v.data() + v.size();
if (p + HASH_SIZE > e) {
LOGWARN(3, "on_external_block: sanity check failed - invalid merge mining extra data " << '1');
return;
}
memcpy(data.h, p, HASH_SIZE);
p += HASH_SIZE;
p = readVarint(p, e, diff.lo);
if (!p) {
LOGWARN(3, "on_external_block: sanity check failed - invalid merge mining extra data " << '2');
diff.lo = 0;
}
else {
p = readVarint(p, e, diff.hi);
if (!p) {
LOGWARN(3, "on_external_block: sanity check failed - invalid merge mining extra data " << '3');
diff.hi = 0;
}
}
}
// If it's our aux chain, check that it's the same job and that there is enough PoW
if (i.first == chain_params.aux_id) {
if ((data != chain_params.aux_hash) || (diff != chain_params.aux_diff)) {
uint32_t index = std::numeric_limits<uint32_t>::max();
for (uint32_t k = 0; k < NUM_PREVIOUS_HASHES; ++k) {
if (previous_aux_hashes[k] == *data.u64()) {
index = k;
break;
}
}
m_previousAuxHashesFoundIndex = index;
if (index == std::numeric_limits<uint32_t>::max()) {
LOGINFO(4, "External aux job solution found, but it's for another miner");
return;
}
LOGINFO(4, "External aux job solution found, but it's stale");
chain_params.aux_hash = data;
chain_params.aux_diff = diff;
}
else {
m_previousAuxHashesFoundIndex = std::numeric_limits<uint32_t>::max();
}
if (!diff.check_pow(block.m_powHash)) {
LOGINFO(4, "External aux job solution found, but it doesn't have enough PoW (block diff = " << block.m_difficulty << ", Tari diff = " << diff << ')');
return;
}
}
aux_ids.emplace_back(i.first);
aux_chains.emplace_back(i.first, data, diff);
}
aux_ids.emplace_back(m_pool->side_chain().consensus_hash());
LOGINFO(0, log::LightGreen() << "External aux job solution found. Processing it!");
// coinbase_merkle_proof
root_hash root;
std::vector<hash> proof;
uint32_t path;
#ifdef WITH_INDEXED_HASHES
std::vector<hash> transactions;
transactions.reserve(block.m_transactions.size());
for (const auto& h : block.m_transactions) {
transactions.emplace_back(h);
}
#else
const std::vector<hash>& transactions = block.m_transactions;
#endif
if (!merkle_hash_with_proof(transactions, 0, proof, path, root)) {
LOGWARN(3, "on_external_block: merkle_hash_with_proof failed for coinbase transaction");
return;
}
if (!verify_merkle_proof(transactions[0], proof, path, root)) {
LOGWARN(3, "on_external_block: verify_merkle_proof failed for coinbase transaction");
return;
}
std::vector<uint8_t> 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);
}
// hashing_blob
uint8_t hashing_blob[128] = {};
memcpy(hashing_blob, block.m_hashingBlob.data(), block.m_hashingBlob.size());
// nonce_offset and blob
size_t header_size = 0;
const std::vector<uint8_t> blob = block.serialize_mainchain_data(&header_size);
if (header_size <= NONCE_SIZE) {
LOGWARN(3, "on_external_block: invalid header_size");
return;
}
const uint32_t nonce_offset = static_cast<uint32_t>(header_size - NONCE_SIZE);
// aux_merkle_proof, aux_merkle_proof_path
std::vector<hash> aux_merkle_proof;
uint32_t aux_merkle_proof_path = 0;
const hash sidechain_id = block.m_sidechainId;
const uint32_t n_aux_chains = static_cast<uint32_t>(mm_extra_size + 1);
std::vector<hash> hashes(n_aux_chains);
uint32_t aux_nonce;
if (!find_aux_nonce(aux_ids, aux_nonce, 1000)) {
LOGWARN(3, "on_external_block: failed to find aux_nonce");
return;
}
for (const AuxChainData& aux_data : aux_chains) {
const uint32_t aux_slot = get_aux_slot(aux_data.unique_id, aux_nonce, n_aux_chains);
if (!hashes[aux_slot].empty()) {
LOGWARN(3, "on_external_block: found an incorrect aux_nonce " << '1');
return;
}
hashes[aux_slot] = aux_data.data;
}
const uint32_t aux_slot = get_aux_slot(m_pool->side_chain().consensus_hash(), aux_nonce, n_aux_chains);
if (!hashes[aux_slot].empty()) {
LOGWARN(3, "on_external_block: found an incorrect aux_nonce " << '2');
return;
}
hashes[aux_slot] = sidechain_id;
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 (root != block.m_merkleRoot) {
LOGWARN(3, "on_external_block: merkle root didn't match");
return;
}
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;
}
submit_solution(coinbase_merkle_proof, hashing_blob, nonce_offset, block.m_seed, blob, aux_merkle_proof, aux_merkle_proof_path);
#else // WITH_MERGE_MINING_DONATION
(void) block;
#endif // WITH_MERGE_MINING_DONATION
}
void MergeMiningClientTari::submit_solution(const std::vector<uint8_t>& coinbase_merkle_proof, const uint8_t (&hashing_blob)[128], size_t nonce_offset, const hash& seed_hash, const std::vector<uint8_t>& blob, const std::vector<hash>& merkle_proof, uint32_t merkle_proof_path)
{
Block block;
@@ -820,7 +586,7 @@ void MergeMiningClientTari::run()
const uint32_t index = (m_previousAuxHashesIndex++) % NUM_PREVIOUS_HASHES;
m_previousAuxHashes[index] = *m_chainParams.aux_hash.u64();
m_previousAuxHashes[index] = m_chainParams.aux_hash;
m_previousTariBlocks[index] = std::move(m_tariBlock);
std::copy(mm_hash.begin(), mm_hash.end(), m_chainParams.aux_hash.h);
@@ -1119,4 +885,9 @@ void MergeMiningClientTari::push_blocks_to(const std::string& node_address)
}
}
const char* MergeMiningClientTari::get_log_category() const
{
return log_category_prefix;
}
} // namespace p2pool
+3 -18
View File
@@ -25,7 +25,7 @@ namespace p2pool {
class p2pool;
struct PoolBlock;
class MergeMiningClientTari : public IMergeMiningClient, public nocopy_nomove
class MergeMiningClientTari : public MergeMiningClientShared
{
public:
MergeMiningClientTari(p2pool* pool, std::string host, const std::string& wallet);
@@ -34,33 +34,18 @@ public:
static constexpr char TARI_PREFIX[] = "tari://";
bool get_params(ChainParameters& out_params) const override;
void on_external_block(const PoolBlock& block) override;
void submit_solution(const std::vector<uint8_t>& coinbase_merkle_proof, const uint8_t (&hashing_blob)[128], size_t nonce_offset, const hash& seed_hash, const std::vector<uint8_t>& blob, const std::vector<hash>& merkle_proof, uint32_t merkle_proof_path) override;
void print_status() const override;
void api_status(log::Stream&) const override;
const char* get_log_category() const override;
private:
mutable uv_rwlock_t m_chainParamsLock;
ChainParameters m_chainParams;
uint64_t m_chainParamsTimestamp;
enum {
NUM_PREVIOUS_HASHES = 8,
};
uint64_t m_previousAuxHashes[NUM_PREVIOUS_HASHES];
tari::rpc::Block m_previousTariBlocks[NUM_PREVIOUS_HASHES];
uint32_t m_previousAuxHashesIndex;
std::atomic<uint32_t> m_previousAuxHashesFoundIndex;
tari::rpc::Block m_tariBlock;
std::string m_auxWallet;
p2pool* m_pool;
struct TariJobParams
{
uint64_t height;
+5
View File
@@ -33,6 +33,11 @@ TEST(hash, constructor)
memset(h.h, -1, HASH_SIZE);
h = {};
ASSERT_EQ(memcmp(h.h, buf, HASH_SIZE), 0);
h = { "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f" };
for (uint8_t i = 0; i < HASH_SIZE; ++i) {
ASSERT_EQ(h.h[i], i);
}
}
TEST(hash, compare)