Skip to content

Commit

Permalink
Merge mining RPC: added merge_mining_get_job
Browse files Browse the repository at this point in the history
  • Loading branch information
SChernykh committed Nov 5, 2023
1 parent 75bb046 commit c839371
Show file tree
Hide file tree
Showing 4 changed files with 113 additions and 16 deletions.
18 changes: 12 additions & 6 deletions docs/MERGE_MINING.MD
Original file line number Diff line number Diff line change
Expand Up @@ -55,28 +55,34 @@ Field|Description
-|-
`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 1: `{"jsonrpc":"2.0","id":"0","result":{"chain_id":"0f28c4960d96647e77e7ab6d13b85bd16c7ca56f45df802cdc763a5e5c0c7863"}}`

Example response 2: `{"jsonrpc":"2.0","id":"0","error":"something went wrong"}`

### merge_mining_get_job

Example request: `{"jsonrpc":"2.0","id":"0","method":"merge_mining_get_job","params":{"address":"MERGE_MINED_CHAIN_ADDRESS","aux_hash":"f6952d6eef555ddd87aca66e56b91530222d6e318414816f3ba7cf5bf694bf0f","height":3000000,"prev_id":"ad505b0be8a49b89273e307106fa42133cbd804456724c5e7635bd953215d92a"}}`

Request: a JSON containing these fields:
Field|Description
-|-
`height`|Monero height
`prev_id`|Hash of the previous Monero block
`address`|A wallet address on the merge mined chain
`aux_hash`|Merge mining job that is currently being used
`height`|Monero height
`prev_id`|Hash of the previous Monero block

Response: a JSON containing these fields:
Field|Description
-|-
`result`|`OK` or an error message
`aux_blob`|A hex-encoded blob of data. Merge mined chain defines the contents of this blob. It's opaque to P2Pool and will not be changed by it.
`aux_hash`|A 32-byte hex-encoded hash of the `aux_blob`. Merge mined chain defines how exactly this hash is calculated. It's opaque to P2Pool.
`aux_diff`|Mining difficulty (decimal number).
`aux_hash`|A 32-byte hex-encoded hash of the `aux_blob`. Merge mined chain defines how exactly this hash is calculated. It's opaque to P2Pool.

If `aux_hash` is the same as in the request, all other fields will be ignored by P2Pool, so they don't have to be included in the response. Moreover, empty response will be interpreted as a response having the same `aux_hash` as in the request. This enables an efficient polling.

Example response 1: `{"jsonrpc":"2.0","id":"0","result":{"aux_blob":"4c6f72656d20697073756d","aux_diff":123456,"aux_hash":"f6952d6eef555ddd87aca66e56b91530222d6e318414816f3ba7cf5bf694bf0f"}}`

If `aux_hash` is the same as in the request, all other fields will be ignored by P2Pool, so they don't have to be included in the response. Moreover, `{"result":"OK"}` response will be interpreted as a response having the same `aux_hash` as in the request. This enables an efficient polling.
Example response 2: `{"jsonrpc":"2.0","id":"0","result":{}}`

### merge_mining_submit_solution

Expand Down
85 changes: 75 additions & 10 deletions src/merge_mining_client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -123,22 +123,87 @@ bool MergeMiningClient::parse_merge_mining_get_chain_id(const char* data, size_t

const auto& chain_id = result["chain_id"];

if (!chain_id.IsString() || (chain_id.GetStringLength() != HASH_SIZE * 2)) {
if (!chain_id.IsString() || !from_hex(chain_id.GetString(), chain_id.GetStringLength(), m_chainID)) {
return err("invalid chain_id");
}

const char* s = chain_id.GetString();
hash id;
return true;
}

void MergeMiningClient::merge_mining_get_job(uint64_t height, const hash& prev_id, const std::string& address, const hash& aux_hash)
{
char buf[log::Stream::BUF_SIZE + 1];
log::Stream s(buf);

s << "{\"jsonrpc\":\"2.0\",\"id\":\"0\",\"method\":\"merge_mining_get_job\",\"params\":{"
<< "\"address\":\"" << address << '"'
<< ",\"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,
[this](const char* data, size_t size, double) {
parse_merge_mining_get_job(data, size);
},
[](const char* data, size_t size, double) {
if (size > 0) {
LOGERR(1, "couldn't get merge mining job, error " << log::const_buf(data, size));
}
});
}

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];
bool MergeMiningClient::parse_merge_mining_get_job(const char* data, size_t size)
{
auto err = [this](const char* msg) {
LOGWARN(1, "merge_mining_get_job RPC call failed: " << msg);
return false;
};

rapidjson::Document doc;

if (doc.Parse(data, size).HasParseError() || !doc.IsObject()) {
return err("parsing failed");
}

m_chainID = id;
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("aux_hash")) {
return true;
}

const auto& aux_hash = result["aux_hash"];

hash h;
if (!aux_hash.IsString() || !from_hex(aux_hash.GetString(), aux_hash.GetStringLength(), h)) {
return err("invalid aux_hash");
}

if (h == m_auxHash) {
return true;
}

if (!result.HasMember("aux_blob") || !result["aux_blob"].IsString()) {
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_auxHash = h;
m_auxDiff.lo = result["aux_diff"].GetUint64();
m_auxDiff.hi = 0;

return true;
}

Expand Down
7 changes: 7 additions & 0 deletions src/merge_mining_client.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,16 @@ class MergeMiningClient
void merge_mining_get_chain_id();
bool parse_merge_mining_get_chain_id(const char* data, size_t size);

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);

std::string m_host;
uint32_t m_port;

std::string m_auxBlob;
hash m_auxHash;
difficulty_type m_auxDiff;

hash m_chainID;

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 @@ -99,6 +99,25 @@ static FORCEINLINE bool from_hex(char c, T& out_value) {
return false;
}

static FORCEINLINE bool from_hex(const char* s, size_t len, hash& h) {
if (len != HASH_SIZE * 2) {
return false;
}

hash result;

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.h[i] = (d[0] << 4) | d[1];
}

h = 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 c839371

Please sign in to comment.