mirror of
https://github.com/SChernykh/p2pool.git
synced 2024-12-22 19:39:22 +00:00
Added merge mining RPC client
Basic code and one API call implemented
This commit is contained in:
parent
fd20313d65
commit
75bb046f22
5 changed files with 235 additions and 8 deletions
|
@ -77,6 +77,7 @@ set(HEADERS
|
||||||
src/keccak.h
|
src/keccak.h
|
||||||
src/log.h
|
src/log.h
|
||||||
src/mempool.h
|
src/mempool.h
|
||||||
|
src/merge_mining_client.h
|
||||||
src/merkle.h
|
src/merkle.h
|
||||||
src/p2p_server.h
|
src/p2p_server.h
|
||||||
src/p2pool.h
|
src/p2pool.h
|
||||||
|
@ -109,6 +110,7 @@ set(SOURCES
|
||||||
src/main.cpp
|
src/main.cpp
|
||||||
src/memory_leak_debug.cpp
|
src/memory_leak_debug.cpp
|
||||||
src/mempool.cpp
|
src/mempool.cpp
|
||||||
|
src/merge_mining_client.cpp
|
||||||
src/merkle.cpp
|
src/merkle.cpp
|
||||||
src/p2p_server.cpp
|
src/p2p_server.cpp
|
||||||
src/p2pool.cpp
|
src/p2pool.cpp
|
||||||
|
|
|
@ -46,15 +46,17 @@ Reference code: `get_aux_slot` and `find_aux_nonce` in `merkle.cpp`
|
||||||
|
|
||||||
P2Pool must be able to get mining jobs from merge mined chains and submit PoW solutions to them.
|
P2Pool must be able to get mining jobs from merge mined chains and submit PoW solutions to them.
|
||||||
|
|
||||||
### merge_mining_get_id
|
### merge_mining_get_chain_id
|
||||||
|
|
||||||
Request: an empty JSON
|
Example request: `{"jsonrpc":"2.0","id":"0","method":"merge_mining_get_chain_id"}`
|
||||||
|
|
||||||
Response: a JSON containing these fields:
|
Response: a JSON containing these fields:
|
||||||
Field|Description
|
Field|Description
|
||||||
-|-
|
-|-
|
||||||
`result`|`OK` or an error message
|
`chain_id`|A unique 32-byte hex-encoded value that identifies this merge mined chain.
|
||||||
`id`|A unique 32-byte hex-encoded value that identifies this merge mined chain.
|
|
||||||
|
Example response 1: `{"jsonrpc":"2.0","id":"0","result":{"chain_id":"f89175d2ce8ce92eaa062eea5c433d0d70f89f5e1554c066dc27943e8cfc37b0"}}`
|
||||||
|
Example response 2: `{"jsonrpc":"2.0","id":"0","error":"something went wrong"}`
|
||||||
|
|
||||||
### merge_mining_get_job
|
### merge_mining_get_job
|
||||||
|
|
||||||
|
|
164
src/merge_mining_client.cpp
Normal file
164
src/merge_mining_client.cpp
Normal file
|
@ -0,0 +1,164 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||||
|
* Copyright (c) 2021-2023 SChernykh <https://github.com/SChernykh>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, version 3.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
#include "merge_mining_client.h"
|
||||||
|
#include "p2pool.h"
|
||||||
|
#include "params.h"
|
||||||
|
#include "json_rpc_request.h"
|
||||||
|
#include "json_parsers.h"
|
||||||
|
#include <rapidjson/document.h>
|
||||||
|
|
||||||
|
LOG_CATEGORY(MergeMiningClient)
|
||||||
|
|
||||||
|
namespace p2pool {
|
||||||
|
|
||||||
|
MergeMiningClient::MergeMiningClient(p2pool* pool, const std::string& host)
|
||||||
|
: m_host(host)
|
||||||
|
, m_port(80)
|
||||||
|
, m_pool(pool)
|
||||||
|
, m_loop{}
|
||||||
|
, m_loopThread{}
|
||||||
|
, m_shutdownAsync{}
|
||||||
|
{
|
||||||
|
const size_t k = host.find_last_of(':');
|
||||||
|
if (k != std::string::npos) {
|
||||||
|
m_host = host.substr(0, k);
|
||||||
|
m_port = strtoul(host.substr(k + 1).c_str(), nullptr, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_host.empty() || (m_port == 0) || (m_port >= 65536)) {
|
||||||
|
LOGERR(1, "Invalid host " << host);
|
||||||
|
throw std::exception();
|
||||||
|
}
|
||||||
|
|
||||||
|
int err = uv_loop_init(&m_loop);
|
||||||
|
if (err) {
|
||||||
|
LOGERR(1, "failed to create event loop, error " << uv_err_name(err));
|
||||||
|
throw std::exception();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init loop user data before running it
|
||||||
|
GetLoopUserData(&m_loop);
|
||||||
|
|
||||||
|
err = uv_async_init(&m_loop, &m_shutdownAsync, on_shutdown);
|
||||||
|
if (err) {
|
||||||
|
LOGERR(1, "uv_async_init failed, error " << uv_err_name(err));
|
||||||
|
uv_loop_close(&m_loop);
|
||||||
|
throw std::exception();
|
||||||
|
}
|
||||||
|
m_shutdownAsync.data = this;
|
||||||
|
|
||||||
|
err = uv_thread_create(&m_loopThread, loop, this);
|
||||||
|
if (err) {
|
||||||
|
LOGERR(1, "failed to start event loop thread, error " << uv_err_name(err));
|
||||||
|
uv_loop_close(&m_loop);
|
||||||
|
throw std::exception();
|
||||||
|
}
|
||||||
|
|
||||||
|
merge_mining_get_chain_id();
|
||||||
|
}
|
||||||
|
|
||||||
|
MergeMiningClient::~MergeMiningClient()
|
||||||
|
{
|
||||||
|
uv_async_send(&m_shutdownAsync);
|
||||||
|
uv_thread_join(&m_loopThread);
|
||||||
|
|
||||||
|
LOGINFO(1, "stopped");
|
||||||
|
}
|
||||||
|
|
||||||
|
void MergeMiningClient::merge_mining_get_chain_id()
|
||||||
|
{
|
||||||
|
constexpr char req[] = "{\"jsonrpc\":\"2.0\",\"id\":\"0\",\"method\":\"merge_mining_get_chain_id\"}";
|
||||||
|
|
||||||
|
JSONRPCRequest::call(m_host, m_port, req, std::string(), m_pool->params().m_socks5Proxy,
|
||||||
|
[this](const char* data, size_t size, double) {
|
||||||
|
parse_merge_mining_get_chain_id(data, size);
|
||||||
|
},
|
||||||
|
[](const char* data, size_t size, double) {
|
||||||
|
if (size > 0) {
|
||||||
|
LOGERR(1, "couldn't get merge mining id, error " << log::const_buf(data, size));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MergeMiningClient::parse_merge_mining_get_chain_id(const char* data, size_t size)
|
||||||
|
{
|
||||||
|
auto err = [this](const char* msg) {
|
||||||
|
LOGWARN(1, "merge_mining_get_chain_id RPC call failed: " << msg << ". Trying again in 1 second.");
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
|
||||||
|
merge_mining_get_chain_id();
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
rapidjson::Document doc;
|
||||||
|
|
||||||
|
if (doc.Parse(data, size).HasParseError() || !doc.IsObject()) {
|
||||||
|
return err("parsing failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (doc.HasMember("error")) {
|
||||||
|
return err(doc["error"].IsString() ? doc["error"].GetString() : "an unknown error occurred");
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& result = doc["result"];
|
||||||
|
|
||||||
|
if (!result.IsObject() || !result.HasMember("chain_id")) {
|
||||||
|
return err("couldn't parse result");
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& chain_id = result["chain_id"];
|
||||||
|
|
||||||
|
if (!chain_id.IsString() || (chain_id.GetStringLength() != HASH_SIZE * 2)) {
|
||||||
|
return err("invalid chain_id");
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* s = chain_id.GetString();
|
||||||
|
hash id;
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < HASH_SIZE; ++i) {
|
||||||
|
uint8_t d[2];
|
||||||
|
if (!from_hex(s[i * 2], d[0]) || !from_hex(s[i * 2 + 1], d[1])) {
|
||||||
|
return err("chain_id is not hex-encoded");
|
||||||
|
}
|
||||||
|
id.h[i] = (d[0] << 4) | d[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
m_chainID = id;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MergeMiningClient::loop(void* data)
|
||||||
|
{
|
||||||
|
LOGINFO(1, "event loop started");
|
||||||
|
|
||||||
|
MergeMiningClient* client = static_cast<MergeMiningClient*>(data);
|
||||||
|
|
||||||
|
int err = uv_run(&client->m_loop, UV_RUN_DEFAULT);
|
||||||
|
if (err) {
|
||||||
|
LOGWARN(1, "uv_run returned " << err);
|
||||||
|
}
|
||||||
|
|
||||||
|
err = uv_loop_close(&client->m_loop);
|
||||||
|
if (err) {
|
||||||
|
LOGWARN(1, "uv_loop_close returned error " << uv_err_name(err));
|
||||||
|
}
|
||||||
|
|
||||||
|
LOGINFO(1, "event loop stopped");
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace p2pool
|
59
src/merge_mining_client.h
Normal file
59
src/merge_mining_client.h
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||||
|
* Copyright (c) 2021-2023 SChernykh <https://github.com/SChernykh>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, version 3.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "uv_util.h"
|
||||||
|
|
||||||
|
namespace p2pool {
|
||||||
|
|
||||||
|
class p2pool;
|
||||||
|
|
||||||
|
class MergeMiningClient
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
MergeMiningClient(p2pool* pool, const std::string& host);
|
||||||
|
~MergeMiningClient();
|
||||||
|
|
||||||
|
private:
|
||||||
|
static void loop(void* data);
|
||||||
|
|
||||||
|
void merge_mining_get_chain_id();
|
||||||
|
bool parse_merge_mining_get_chain_id(const char* data, size_t size);
|
||||||
|
|
||||||
|
std::string m_host;
|
||||||
|
uint32_t m_port;
|
||||||
|
|
||||||
|
hash m_chainID;
|
||||||
|
|
||||||
|
p2pool* m_pool;
|
||||||
|
|
||||||
|
uv_loop_t m_loop;
|
||||||
|
uv_thread_t m_loopThread;
|
||||||
|
|
||||||
|
uv_async_t m_shutdownAsync;
|
||||||
|
|
||||||
|
static void on_shutdown(uv_async_t* async)
|
||||||
|
{
|
||||||
|
MergeMiningClient* client = reinterpret_cast<MergeMiningClient*>(async->data);
|
||||||
|
uv_close(reinterpret_cast<uv_handle_t*>(&client->m_shutdownAsync), nullptr);
|
||||||
|
|
||||||
|
delete GetLoopUserData(&client->m_loop, false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace p2pool
|
|
@ -335,7 +335,7 @@ void p2pool::handle_miner_data(MinerData& data)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: remove after testing
|
// TODO: remove after testing
|
||||||
#if 1
|
#if 0
|
||||||
{
|
{
|
||||||
data.aux_chains.clear();
|
data.aux_chains.clear();
|
||||||
data.aux_chains.resize(10);
|
data.aux_chains.resize(10);
|
||||||
|
@ -994,7 +994,7 @@ void p2pool::parse_get_info_rpc(const char* data, size_t size)
|
||||||
rapidjson::Document doc;
|
rapidjson::Document doc;
|
||||||
doc.Parse(data, size);
|
doc.Parse(data, size);
|
||||||
|
|
||||||
if (!doc.IsObject() || !doc.HasMember("result")) {
|
if (doc.HasParseError() || !doc.IsObject() || !doc.HasMember("result")) {
|
||||||
LOGWARN(1, "get_info RPC response is invalid (\"result\" not found), trying again in 1 second");
|
LOGWARN(1, "get_info RPC response is invalid (\"result\" not found), trying again in 1 second");
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
|
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
|
||||||
get_info();
|
get_info();
|
||||||
|
@ -1071,7 +1071,7 @@ void p2pool::parse_get_version_rpc(const char* data, size_t size)
|
||||||
rapidjson::Document doc;
|
rapidjson::Document doc;
|
||||||
doc.Parse(data, size);
|
doc.Parse(data, size);
|
||||||
|
|
||||||
if (!doc.IsObject() || !doc.HasMember("result")) {
|
if (doc.HasParseError() || !doc.IsObject() || !doc.HasMember("result")) {
|
||||||
LOGWARN(1, "get_version RPC response is invalid (\"result\" not found), trying again in 1 second");
|
LOGWARN(1, "get_version RPC response is invalid (\"result\" not found), trying again in 1 second");
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
|
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
|
||||||
get_version();
|
get_version();
|
||||||
|
@ -1161,7 +1161,7 @@ void p2pool::parse_get_miner_data_rpc(const char* data, size_t size)
|
||||||
rapidjson::Document doc;
|
rapidjson::Document doc;
|
||||||
doc.Parse(data, size);
|
doc.Parse(data, size);
|
||||||
|
|
||||||
if (!doc.IsObject() || !doc.HasMember("result")) {
|
if (doc.HasParseError() || !doc.IsObject() || !doc.HasMember("result")) {
|
||||||
LOGWARN(1, "get_miner_data RPC response is invalid, skipping it");
|
LOGWARN(1, "get_miner_data RPC response is invalid, skipping it");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue