Merge mining RPC: added `merge_mining_submit_solution`

merge-mining
SChernykh 2023-11-06 16:33:56 +01:00
parent 40b2c2a858
commit 9eab833f66
5 changed files with 118 additions and 8 deletions

View File

@ -100,3 +100,12 @@ Note that `merkle_proof` only contains a vector of 32-byte hashes for `aux_hash`
`aux_nonce` and `n_aux_chains` can be extracted from the Merkle tree parameters (see above).
`merkle_root_hash` can be extracted from the merge mining tag (see above).
Response: a JSON containing these fields:
Field|Description
-|-
`status`|Block submit status
Example response 1: `{"jsonrpc":"2.0","id":"0","result":{"status":"accepted"}}`
Example response 2: `{"jsonrpc":"2.0","id":"0","error":"something went wrong"}`

View File

@ -499,7 +499,9 @@ void Call(const std::string& address, int port, const std::string& req, const st
});
if (!result) {
LOGERR(1, "JSON RPC \"" << req << "\" failed");
static constexpr char err[] = "CallOnLoop failed";
LOGERR(1, err);
(*close_cb)(err, sizeof(err) - 1, 0.0);
}
}

View File

@ -31,6 +31,7 @@ MergeMiningClient::MergeMiningClient(p2pool* pool, const std::string& host, cons
: m_host(host)
, m_port(80)
, m_auxAddress(address)
, m_ping(0.0)
, m_pool(pool)
, m_loop{}
, m_loopThread{}
@ -99,11 +100,16 @@ void MergeMiningClient::on_timer()
void MergeMiningClient::merge_mining_get_chain_id()
{
constexpr char req[] = "{\"jsonrpc\":\"2.0\",\"id\":\"0\",\"method\":\"merge_mining_get_chain_id\"}";
const std::string 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) {
[this](const char* data, size_t size, double ping) {
if (parse_merge_mining_get_chain_id(data, size)) {
if (ping > 0.0) {
m_ping = ping;
}
// 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);
if (err) {
LOGERR(1, "failed to start timer, error " << uv_err_name(err));
@ -167,9 +173,9 @@ void MergeMiningClient::merge_mining_get_job(uint64_t height, const hash& prev_i
<< ",\"aux_hash\":\"" << aux_hash << '"'
<< ",\"height\":" << height
<< ",\"prev_id\":\"" << prev_id << '"'
<< "}}\0";
<< "}}";
JSONRPCRequest::call(m_host, m_port, buf, std::string(), m_pool->params().m_socks5Proxy,
JSONRPCRequest::call(m_host, m_port, std::string(buf, s.m_pos), std::string(), m_pool->params().m_socks5Proxy,
[this](const char* data, size_t size, double) {
parse_merge_mining_get_job(data, size);
},
@ -219,7 +225,9 @@ bool MergeMiningClient::parse_merge_mining_get_job(const char* data, size_t size
return true;
}
if (!result.HasMember("aux_blob") || !result["aux_blob"].IsString()) {
std::vector<uint8_t> aux_blob;
if (!result.HasMember("aux_blob") || !result["aux_blob"].IsString() || !from_hex(result["aux_blob"].GetString(), result["aux_blob"].GetStringLength(), aux_blob)) {
return err("invalid aux_blob");
}
@ -227,7 +235,7 @@ bool MergeMiningClient::parse_merge_mining_get_job(const char* data, size_t size
return err("invalid aux_diff");
}
m_auxBlob = result["aux_blob"].GetString();
m_auxBlob = std::move(aux_blob);
m_auxHash = h;
m_auxDiff.lo = result["aux_diff"].GetUint64();
m_auxDiff.hi = 0;
@ -235,6 +243,73 @@ bool MergeMiningClient::parse_merge_mining_get_job(const char* data, size_t size
return true;
}
void MergeMiningClient::merge_mining_submit_solution(const std::vector<uint8_t>& blob, const std::vector<hash>& merkle_proof)
{
std::vector<char> buf((m_auxBlob.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_auxBlob.data(), m_auxBlob.size()) << '"'
<< ",\"aux_hash\":\"" << m_auxHash << '"'
<< ",\"blob\":" << log::hex_buf(blob.data(), blob.size())
<< ",\"merkle_proof\":[";
for (size_t i = 0, n = merkle_proof.size(); i < n; ++i) {
if (i > 0) {
s << ',';
}
s << '"' << merkle_proof[i] << '"';
}
s << "]}}";
JSONRPCRequest::call(m_host, m_port, std::string(buf.data(), s.m_pos), std::string(), m_pool->params().m_socks5Proxy,
[this](const char* data, size_t size, double) {
parse_merge_mining_submit_solution(data, size);
},
[](const char* data, size_t size, double) {
if (size > 0) {
LOGERR(1, "couldn't submit merge mining solution, error " << log::const_buf(data, size));
}
}, &m_loop);
}
bool MergeMiningClient::parse_merge_mining_submit_solution(const char* data, size_t size)
{
auto err = [](const char* msg) {
LOGWARN(1, "merge_mining_submit_solution RPC call failed: " << msg);
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()) {
return err("couldn't parse result");
}
if (!result.HasMember("status") || !result["status"].IsString()) {
return err("invalid status");
}
const char* status = result["status"].GetString();
LOGINFO(0, log::LightGreen() << "merge_mining_submit_solution: " << status);
// Get new mining job
on_timer();
return true;
}
void MergeMiningClient::loop(void* data)
{
LOGINFO(1, "event loop started");

View File

@ -29,6 +29,8 @@ public:
MergeMiningClient(p2pool* pool, const std::string& host, const std::string& address);
~MergeMiningClient();
void merge_mining_submit_solution(const std::vector<uint8_t>& blob, const std::vector<hash>& merkle_proof);
private:
static void loop(void* data);
@ -41,15 +43,18 @@ private:
void merge_mining_get_job(uint64_t height, const hash& prev_id, const std::string& address, const hash& aux_hash);
bool parse_merge_mining_get_job(const char* data, size_t size);
bool parse_merge_mining_submit_solution(const char* data, size_t size);
std::string m_host;
uint32_t m_port;
std::string m_auxAddress;
std::string m_auxBlob;
std::vector<uint8_t> m_auxBlob;
hash m_auxHash;
difficulty_type m_auxDiff;
hash m_chainID;
double m_ping;
p2pool* m_pool;

View File

@ -118,6 +118,25 @@ static FORCEINLINE bool from_hex(const char* s, size_t len, hash& h) {
return true;
}
static FORCEINLINE bool from_hex(const char* s, size_t len, std::vector<uint8_t>& data) {
if (len % 2) {
return false;
}
std::vector<uint8_t> result(len / 2);
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 false;
}
result[i] = (d[0] << 4) | d[1];
}
data = std::move(result);
return true;
}
template<typename T, bool is_signed> struct is_negative_helper {};
template<typename T> struct is_negative_helper<T, false> { static FORCEINLINE bool value(T) { return false; } };
template<typename T> struct is_negative_helper<T, true> { static FORCEINLINE bool value(T x) { return (x < 0); } };