Refactored merge mining code
This commit is contained in:
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user