Skip to content

Commit

Permalink
Merge mining RPC: added merge_mining_submit_solution
Browse files Browse the repository at this point in the history
  • Loading branch information
SChernykh committed Nov 6, 2023
1 parent 40b2c2a commit 9eab833
Show file tree
Hide file tree
Showing 5 changed files with 118 additions and 8 deletions.
9 changes: 9 additions & 0 deletions docs/MERGE_MINING.MD
Original file line number Diff line number Diff line change
Expand Up @@ -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"}`
4 changes: 3 additions & 1 deletion src/json_rpc_request.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}

Expand Down
87 changes: 81 additions & 6 deletions src/merge_mining_client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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{}
Expand Down Expand Up @@ -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));
Expand Down Expand Up @@ -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);
},
Expand Down Expand Up @@ -219,22 +225,91 @@ 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");
}

if (!result.HasMember("aux_diff") || !result["aux_diff"].IsUint64()) {
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;

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");
Expand Down
7 changes: 6 additions & 1 deletion src/merge_mining_client.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ class MergeMiningClient
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);

Expand All @@ -41,15 +43,18 @@ class MergeMiningClient
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;

Expand Down
19 changes: 19 additions & 0 deletions src/util.h
Original file line number Diff line number Diff line change
Expand Up @@ -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); } };
Expand Down

0 comments on commit 9eab833

Please sign in to comment.