bool MergedMiner::mine(std::string address1, std::string wallet1, std::string address2, std::string wallet2, size_t threads) { epee::net_utils::http::http_simple_client httpClient1; epee::net_utils::http::http_simple_client httpClient2; Miner miner; BlockTemplate blockTemplate1; BlockTemplate blockTemplate2; cryptonote::block block1; cryptonote::block block2; cryptonote::difficulty_type difficulty1; cryptonote::difficulty_type difficulty2; std::future<bool> request1; std::future<bool> request2; std::future<bool> mining; crypto::hash hash; std::chrono::steady_clock::time_point time1; uint64_t prefix1; cryptonote::account_public_address walletAddress1; if (!get_account_address_from_str(prefix1, walletAddress1, wallet1)) { std::lock_guard<std::mutex> lock(m_mutex); m_messages.push("Failed to parse donor wallet address"); return false; } uint64_t prefix2; cryptonote::account_public_address walletAddress2; if (!address2.empty() && !get_account_address_from_str(prefix2, walletAddress2, wallet2)) { std::lock_guard<std::mutex> lock(m_mutex); m_messages.push("Failed to parse acceptor wallet address"); return false; } m_blockCount = 0; for (;;) { miner.getHashCount(); if (m_stopped) { if (mining.valid()) { miner.stop(); mining.wait(); } return true; } request1 = std::async(std::launch::async, getBlockTemplate, &httpClient1, &address1, &wallet1, MERGE_MINING_TAG_RESERVED_SIZE, &blockTemplate1); if (!address2.empty()) { request2 = std::async(std::launch::async, getBlockTemplate, &httpClient2, &address2, &wallet2, MERGE_MINING_TAG_RESERVED_SIZE, &blockTemplate2); } if (!request1.get()) { std::lock_guard<std::mutex> lock(m_mutex); m_messages.push("Failed to get donor block"); continue; } if (!address2.empty()) { if (!request2.get()) { std::lock_guard<std::mutex> lock(m_mutex); m_messages.push("Failed to get acceptor block"); continue; } if (blockTemplate2.block.major_version != BLOCK_MAJOR_VERSION_2) { std::lock_guard<std::mutex> lock(m_mutex); m_messages.push("Unsupported block version received from acceptor network, merged mining is not possible"); if (mining.valid()) { miner.stop(); mining.wait(); } return false; } } if (mining.valid()) { miner.stop(); if (mining.get()) { if (cryptonote::check_hash(hash, difficulty1)) { if (submitBlock(httpClient1, address1, block1)) { std::lock_guard<std::mutex> lock(m_mutex); m_messages.push("Submitted donor block"); } else { std::lock_guard<std::mutex> lock(m_mutex); m_messages.push("Failed to submit donor block"); } } if (!address2.empty() && cryptonote::check_hash(hash, difficulty2)) { if (!mergeBlocks(block1, block2)) { std::lock_guard<std::mutex> lock(m_mutex); m_messages.push("Internal error"); return false; } if (submitBlock(httpClient2, address2, block2)) { std::lock_guard<std::mutex> lock(m_mutex); m_messages.push("Submitted acceptor block"); } else { std::lock_guard<std::mutex> lock(m_mutex); m_messages.push("Failed to submit acceptor block"); } } } std::chrono::steady_clock::time_point time2 = std::chrono::steady_clock::now(); std::ostringstream stream; stream << "Hashrate: " << miner.getHashCount() / std::chrono::duration_cast<std::chrono::duration<double>>(time2 - time1).count(); std::lock_guard<std::mutex> lock(m_mutex); m_messages.push(stream.str()); time1 = time2; miner.start(); } else { time1 = std::chrono::steady_clock::now(); } block1 = blockTemplate1.block; difficulty1 = blockTemplate1.difficulty; cryptonote::difficulty_type difficulty = difficulty1; if (!address2.empty()) { block2 = blockTemplate2.block; difficulty2 = blockTemplate2.difficulty; difficulty = std::min(difficulty, difficulty2); if (!fillExtra(block1, block2)) { std::lock_guard<std::mutex> lock(m_mutex); m_messages.push("Internal error"); return false; } } mining = std::async(std::launch::async, &Miner::findNonce, &miner, &block1, blockTemplate1.height, difficulty, threads, &hash); for (size_t i = 0; i < 50; ++i) { if (m_stopped || mining.wait_for(std::chrono::milliseconds(100)) == std::future_status::ready) { break; } } } }