diff --git a/src/common.h b/src/common.h index a27e07e..699030f 100644 --- a/src/common.h +++ b/src/common.h @@ -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(c); + } + } + FORCEINLINE bool operator<(const hash& other) const { const uint64_t* a = u64(); diff --git a/src/merge_mining_client.cpp b/src/merge_mining_client.cpp index 20408a4..ff83526 100644 --- a/src/merge_mining_client.cpp +++ b/src/merge_mining_client.cpp @@ -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::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 aux_ids; + + // All aux chains in this block + std::vector 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& 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::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::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::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 proof; + uint32_t path; + +#ifdef WITH_INDEXED_HASHES + std::vector transactions; + transactions.reserve(block.m_transactions.size()); + + for (const auto& h : block.m_transactions) { + transactions.emplace_back(h); + } +#else + const std::vector& 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 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 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(header_size - NONCE_SIZE); + + // aux_merkle_proof, aux_merkle_proof_path + + std::vector 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(mm_extra_size + 1); + + std::vector 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 diff --git a/src/merge_mining_client.h b/src/merge_mining_client.h index 5d778b5..0fb27c2 100644 --- a/src/merge_mining_client.h +++ b/src/merge_mining_client.h @@ -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 m_previousAuxHashesFoundIndex; +}; + } // namespace p2pool diff --git a/src/merge_mining_client_json_rpc.cpp b/src/merge_mining_client_json_rpc.cpp index 3f4e7ed..d0491fd 100644 --- a/src/merge_mining_client_json_rpc.cpp +++ b/src/merge_mining_client_json_rpc.cpp @@ -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 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& /*coinbase_merkle_proof*/, const uint8_t (&/*hashing_blob*/)[128], size_t /*nonce_offset*/, const hash& seed_hash, const std::vector& blob, const std::vector& merkle_proof, uint32_t merkle_proof_path) { - ReadLock lock(m_lock); + ReadLock lock(m_chainParamsLock); - std::vector 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::max()); + + const std::vector& 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 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& /*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(&m_timer), nullptr); } +const char* MergeMiningClientJSON_RPC::get_log_category() const +{ + return log_category_prefix; +} + } // namespace p2pool diff --git a/src/merge_mining_client_json_rpc.h b/src/merge_mining_client_json_rpc.h index cbedb72..3e1634e 100644 --- a/src/merge_mining_client_json_rpc.h +++ b/src/merge_mining_client_json_rpc.h @@ -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& coinbase_merkle_proof, const uint8_t (&hashing_blob)[128], size_t nonce_offset, const hash& seed_hash, const std::vector& blob, const std::vector& 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 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; diff --git a/src/merge_mining_client_tari.cpp b/src/merge_mining_client_tari.cpp index 51c2d9a..dc2e00c 100644 --- a/src/merge_mining_client_tari.cpp +++ b/src/merge_mining_client_tari.cpp @@ -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::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 aux_ids; - - // All aux chains in this block - std::vector 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& 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::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::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::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 proof; - uint32_t path; - -#ifdef WITH_INDEXED_HASHES - std::vector transactions; - transactions.reserve(block.m_transactions.size()); - - for (const auto& h : block.m_transactions) { - transactions.emplace_back(h); - } -#else - const std::vector& 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 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 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(header_size - NONCE_SIZE); - - // aux_merkle_proof, aux_merkle_proof_path - - std::vector 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(mm_extra_size + 1); - - std::vector 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& coinbase_merkle_proof, const uint8_t (&hashing_blob)[128], size_t nonce_offset, const hash& seed_hash, const std::vector& blob, const std::vector& 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 diff --git a/src/merge_mining_client_tari.h b/src/merge_mining_client_tari.h index 8059b74..7edd622 100644 --- a/src/merge_mining_client_tari.h +++ b/src/merge_mining_client_tari.h @@ -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& coinbase_merkle_proof, const uint8_t (&hashing_blob)[128], size_t nonce_offset, const hash& seed_hash, const std::vector& blob, const std::vector& 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 m_previousAuxHashesFoundIndex; tari::rpc::Block m_tariBlock; - std::string m_auxWallet; - p2pool* m_pool; - struct TariJobParams { uint64_t height; diff --git a/tests/src/hash_tests.cpp b/tests/src/hash_tests.cpp index 6430a8d..b2d9e51 100644 --- a/tests/src/hash_tests.cpp +++ b/tests/src/hash_tests.cpp @@ -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)