Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 12 additions & 20 deletions src/datum_pow.c
Original file line number Diff line number Diff line change
Expand Up @@ -40,24 +40,6 @@
#include "datum_pow.h"
#include "datum_utils.h"

static int datum_hex_nibble(const char c) {
if (c >= '0' && c <= '9') return c - '0';
if (c >= 'a' && c <= 'f') return c - 'a' + 10;
if (c >= 'A' && c <= 'F') return c - 'A' + 10;
return -1;
}

bool datum_pow_decode_hex_exact(const char *hex, size_t out_len, unsigned char *out) {
size_t i;
if (!hex || !out) return false;
for (i = 0; i < out_len; i++) {
if (datum_hex_nibble(hex[i<<1]) < 0) return false;
if (datum_hex_nibble(hex[(i<<1)+1]) < 0) return false;
out[i] = hex2bin_uchar(&hex[i<<1]);
}
return hex[out_len<<1] == 0;
}

bool datum_blake2b_time_on_wire(uint32_t *out, uint64_t ntime, uint64_t offset, uint8_t flags) {
if (!out) return false;
if (ntime > UINT32_MAX) return false;
Expand Down Expand Up @@ -224,13 +206,23 @@ void datum_blake2b_prevblock_hidden(unsigned char *out, const unsigned char *pre
memset(out, 0, 6);
}

void datum_blake2b_build_work_header(unsigned char *work, const unsigned char *prevhash, const unsigned char *nonce, const unsigned char *ntime, const unsigned char *root) {
datum_blake2b_prevblock_hidden(work, prevhash);
void datum_blake2b_build_work_header_from_hidden(
unsigned char *work, const unsigned char *prevhash_hidden,
const unsigned char *nonce, const unsigned char *ntime,
const unsigned char *root
) {
memcpy(work, prevhash_hidden, 32);
memcpy(work + 32, nonce, 8);
memcpy(work + 40, ntime, 8);
memcpy(work + 48, root, 32);
}

void datum_blake2b_build_work_header(unsigned char *work, const unsigned char *prevhash, const unsigned char *nonce, const unsigned char *ntime, const unsigned char *root) {
unsigned char prevhash_hidden[32];
datum_blake2b_prevblock_hidden(prevhash_hidden, prevhash);
datum_blake2b_build_work_header_from_hidden(work, prevhash_hidden, nonce, ntime, root);
}

bool datum_blake2b_header_commitment_from_key_hash(
unsigned char *commitment,
uint32_t version,
Expand Down
7 changes: 5 additions & 2 deletions src/datum_pow.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,6 @@
#define DATUM_POW_RESERVED_BLAKE2B_USE_TIME_OFFSET 0x01
#define DATUM_POW_FLAG_BLAKE2B 0x08

bool datum_pow_decode_hex_exact(const char *hex, size_t out_len, unsigned char *out);

bool datum_blake2b_time_on_wire(uint32_t *out, uint64_t ntime, uint64_t offset, uint8_t flags);
/* The nTime a node reads from a header-v2 block built from this share: the
* job's wire time plus the hasher's four time-offset bytes when the offset
Expand All @@ -66,6 +64,11 @@ long double datum_blake2b_accounting_difficulty(long double x);
bool datum_blake2b_256(unsigned char *out, const unsigned char *in, size_t len);
void datum_blake2b_coinb1(unsigned char *out, const unsigned char *commitment);
void datum_blake2b_prevblock_hidden(unsigned char *out, const unsigned char *prevhash);
void datum_blake2b_build_work_header_from_hidden(
unsigned char *work, const unsigned char *prevhash_hidden,
const unsigned char *nonce, const unsigned char *ntime,
const unsigned char *root
);
void datum_blake2b_build_work_header(unsigned char *work, const unsigned char *prevhash, const unsigned char *nonce, const unsigned char *ntime, const unsigned char *root);

/* version may be with or without 0x80000000; H1 always includes the v2 bit. */
Expand Down
29 changes: 15 additions & 14 deletions src/datum_pow_tests.c
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ static void datum_pow_blake2b_vector_tests(void) {
"101112131415161718191a1b1c1d1e1f39300000"
"808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f";
unsigned char merkle[32], xor_key[16], rhs[32], extranonce[12], prevhash[32];
unsigned char nonce[8], ntime[8], commitment[32], root[32], work[80], hash_le[32];
unsigned char nonce[8], ntime[8], commitment[32], root[32], work[80], cached_work[80], hash_le[32];
unsigned char share_target[32];
unsigned char coinb1[39], arbitrary_tx[51], leaf_preimage[52] = {0};
unsigned char header[DATUM_BLAKE2B_BLOCK_HEADER_SIZE], expected[DATUM_BLAKE2B_BLOCK_HEADER_SIZE];
Expand Down Expand Up @@ -187,10 +187,10 @@ static void datum_pow_blake2b_vector_tests(void) {
datum_test(datum_blake2b_header_commitment(commitment, 0x20000000, prevhash,
12345, merkle, 0x6553412f, 0x207fffff, 3, DATUM_BLAKE2B_USE_TIME_OFFSET, 13,
xor_key, rhs));
datum_test(datum_pow_decode_hex_exact(expected_commitment_hex, 32, expected));
datum_test(hex_to_bin_exact(expected_commitment_hex, expected, 32));
datum_test(!memcmp(commitment, expected, 32));
datum_test(datum_blake2b_work_root(root, commitment, extranonce));
datum_test(datum_pow_decode_hex_exact(expected_root_hex, 32, expected));
datum_test(hex_to_bin_exact(expected_root_hex, expected, 32));
datum_test(!memcmp(root, expected, 32));
datum_blake2b_coinb1(coinb1, commitment);
memcpy(arbitrary_tx, coinb1, sizeof(coinb1));
Expand All @@ -199,19 +199,20 @@ static void datum_pow_blake2b_vector_tests(void) {
datum_test(datum_blake2b_256(expected, leaf_preimage, sizeof(leaf_preimage)));
datum_test(!memcmp(root, expected, 32));
datum_blake2b_build_work_header(work, prevhash, nonce, ntime, root);
datum_test(datum_pow_decode_hex_exact(expected_work_hex, sizeof(work), expected));
datum_test(hex_to_bin_exact(expected_work_hex, expected, sizeof(work)));
datum_test(!memcmp(work, expected, sizeof(work)));
datum_blake2b_prevblock_hidden(expected, prevhash);
datum_test(!memcmp(expected, work, 32));
datum_blake2b_build_work_header_from_hidden(cached_work, expected, nonce, ntime, root);
datum_test(!memcmp(cached_work, work, sizeof(work)));
datum_test(datum_blake2b_pow_hash_le(hash_le, work, xor_key, 13));
datum_test(datum_pow_decode_hex_exact(expected_hash_le_hex, 32, expected));
datum_test(hex_to_bin_exact(expected_hash_le_hex, expected, 32));
datum_test(!memcmp(hash_le, expected, 32));
datum_blake2b_serialize_block_header(header, 0x20000000, prevhash, merkle,
0x6553412f, 0x207fffff, nonce, ntime, extranonce, 3,
DATUM_BLAKE2B_USE_TIME_OFFSET, 13, xor_key, 12345, rhs);
datum_test(datum_pow_decode_hex_exact(expected_header_hex, sizeof(header), expected));
datum_test(hex_to_bin_exact(expected_header_hex, expected, sizeof(header)));
datum_test(!memcmp(header, expected, sizeof(header)));
datum_test(!datum_pow_decode_hex_exact("xyz", 1, nonce));

/* Canonical profile-0 vector published with Knots' header-v2 implementation:
* profile_0_time_offset from src/test/data/block_header_v2.json. Its "h2",
Expand Down Expand Up @@ -240,9 +241,9 @@ static void datum_pow_blake2b_vector_tests(void) {
unsigned char knots_prevhash[32], knots_merkle[32], knots_rhs[32];
unsigned char knots_nonce[8], knots_ntime[8];

datum_test(datum_pow_decode_hex_exact(knots_prevhash_hex, 32, knots_prevhash));
datum_test(datum_pow_decode_hex_exact(knots_merkle_hex, 32, knots_merkle));
datum_test(datum_pow_decode_hex_exact(knots_rhs_hex, 32, knots_rhs));
datum_test(hex_to_bin_exact(knots_prevhash_hex, knots_prevhash, 32));
datum_test(hex_to_bin_exact(knots_merkle_hex, knots_merkle, 32));
datum_test(hex_to_bin_exact(knots_rhs_hex, knots_rhs, 32));
memset(xor_key, 0, sizeof(xor_key));
pk_u32le(knots_nonce, 0, UINT32_C(0x0badf00d));
pk_u32le(knots_nonce, 4, UINT32_C(0x11223344));
Expand All @@ -252,16 +253,16 @@ static void datum_pow_blake2b_vector_tests(void) {
datum_test(datum_blake2b_header_commitment(commitment, 0x20000000,
knots_prevhash, 840000, knots_merkle, UINT32_C(2000000000) - 600,
0x1d00ffff, 3, 0x1c, 0, xor_key, knots_rhs));
datum_test(datum_pow_decode_hex_exact(knots_commitment_hex, 32, expected));
datum_test(hex_to_bin_exact(knots_commitment_hex, expected, 32));
datum_test(!memcmp(commitment, expected, 32));

datum_test(datum_pow_decode_hex_exact(knots_root_hex, 32, root));
datum_test(hex_to_bin_exact(knots_root_hex, root, 32));
datum_blake2b_build_work_header(work, knots_prevhash, knots_nonce,
knots_ntime, root);
datum_test(datum_pow_decode_hex_exact(knots_work_hex, sizeof(work), expected));
datum_test(hex_to_bin_exact(knots_work_hex, expected, sizeof(work)));
datum_test(!memcmp(work, expected, sizeof(work)));
datum_test(datum_blake2b_pow_hash_le(hash_le, work, xor_key, 0));
datum_test(datum_pow_decode_hex_exact(knots_hash_le_hex, 32, expected));
datum_test(hex_to_bin_exact(knots_hash_le_hex, expected, 32));
datum_test(!memcmp(hash_le, expected, 32));
}
}
Expand Down
47 changes: 35 additions & 12 deletions src/datum_stratum.c
Original file line number Diff line number Diff line change
Expand Up @@ -1005,6 +1005,7 @@ int client_mining_submit(T_DATUM_CLIENT_DATA *c, uint64_t id, json_t *params_obj
unsigned char coinbase_index = 0;
T_DATUM_STRATUM_COINBASE *cb = NULL;
unsigned char extranonce_bin[12];
unsigned char job_id_bin[8];

unsigned char block_header[80];
unsigned char share_hash[40];
Expand Down Expand Up @@ -1046,6 +1047,11 @@ int client_mining_submit(T_DATUM_CLIENT_DATA *c, uint64_t id, json_t *params_obj
return 0;
}
}
if (!hex_to_bin_exact(job_id_s, job_id_bin, sizeof(job_id_bin))) {
send_unknown_work_error(c,id);
stratum_note_share(m, false, m->last_sent_diff); // guestimate here
return 0;
}

// jobID is
// 4 bytes time (who cares)
Expand All @@ -1054,7 +1060,7 @@ int client_mining_submit(T_DATUM_CLIENT_DATA *c, uint64_t id, json_t *params_obj
// 1 byte coinbase index used
// 6625a3d53cc0e500
// 0123456789ABCDEF
g_job_index = (hex2bin_uchar(&job_id_s[0xA])<<8) | hex2bin_uchar(&job_id_s[0xC]);
g_job_index = (job_id_bin[5]<<8) | job_id_bin[6];
g_job_index ^= STRATUM_JOB_INDEX_XOR;
if (g_job_index >= MAX_STRATUM_JOBS) {
send_unknown_work_error(c,id);
Expand Down Expand Up @@ -1101,12 +1107,14 @@ int client_mining_submit(T_DATUM_CLIENT_DATA *c, uint64_t id, json_t *params_obj
stratum_note_share(m, false, job_diff);
return 0;
}
for(i=0;i<8;i++) {
extranonce_bin[i+4] = hex2bin_uchar(&extranonce2_s[i<<1]);
if (!hex_to_bin_exact(extranonce2_s, extranonce_bin + 4, 8)) {
send_unknown_work_error(c, id);
stratum_note_share(m, false, job_diff);
return 0;
}

// need to build the full coinbase txn
coinbase_index = hex2bin_uchar(&job_id_s[0xE]);
coinbase_index = job_id_bin[7];
if (coinbase_index >= MAX_COINBASE_TYPES) {
if (!(empty_work && coinbase_index == DATUM_COINBASE_ID_EMPTY)) {
send_unknown_work_error(c, id);
Expand Down Expand Up @@ -1169,10 +1177,18 @@ int client_mining_submit(T_DATUM_CLIENT_DATA *c, uint64_t id, json_t *params_obj
return 0;
}
if (ntime_len == 8) {
ntime_val = (uint32_t)strtoul(ntime_s, NULL, 16);
if (!hex_to_u32(ntime_s, &ntime_val)) {
send_unknown_work_error(c, id);
stratum_note_share(m, false, job_diff);
return 0;
}
pk_u32le(ntime8, 0, ntime_val);
} else {
for(i=0;i<8;i++) ntime8[i] = hex2bin_uchar(&ntime_s[i << 1]);
if (!hex_to_bin_exact(ntime_s, ntime8, 8)) {
send_unknown_work_error(c, id);
stratum_note_share(m, false, job_diff);
return 0;
}
ntime_val = upk_u32le(ntime8, 0);
}
ntime64 = upk_u64le(ntime8, 0);
Expand All @@ -1197,10 +1213,18 @@ int client_mining_submit(T_DATUM_CLIENT_DATA *c, uint64_t id, json_t *params_obj
return 0;
}
if (nonce_len == 8) {
nonce_val = (uint32_t)strtoul(nonce_s, NULL, 16);
if (!hex_to_u32(nonce_s, &nonce_val)) {
send_unknown_work_error(c, id);
stratum_note_share(m, false, job_diff);
return 0;
}
pk_u32le(nonce8, 0, nonce_val);
} else {
for(i=0;i<8;i++) nonce8[i] = hex2bin_uchar(&nonce_s[i << 1]);
if (!hex_to_bin_exact(nonce_s, nonce8, 8)) {
send_unknown_work_error(c, id);
stratum_note_share(m, false, job_diff);
return 0;
}
nonce_val = upk_u32le(nonce8, 0);
}
nonce64 = upk_u64le(nonce8, 0);
Expand All @@ -1219,7 +1243,7 @@ int client_mining_submit(T_DATUM_CLIENT_DATA *c, uint64_t id, json_t *params_obj
stratum_note_share(m, false, job_diff);
return 0;
}
datum_blake2b_build_work_header(work, job->prevhash_bin, nonce8, ntime8, root);
datum_blake2b_build_work_header_from_hidden(work, job->blake2b_prevblock_hidden, nonce8, ntime8, root);
memcpy(block_header, work, 80);
if (!datum_blake2b_pow_hash_le(share_hash, work, (const unsigned char[16]){0}, 0)) {
send_unknown_work_error(c, id);
Expand Down Expand Up @@ -2049,7 +2073,6 @@ bool datum_stratum_job_blake2b_commitment(T_DATUM_STRATUM_JOB *s, const T_DATUM_

void datum_stratum_job_refresh_blake2b(T_DATUM_STRATUM_JOB *s) {
T_DATUM_TEMPLATE_DATA *block_template;
unsigned char prevblock_hidden[32];
uint32_t time_on_wire;
int i;

Expand All @@ -2075,10 +2098,10 @@ void datum_stratum_job_refresh_blake2b(T_DATUM_STRATUM_JOB *s) {
}
s->blake2b_time_on_wire = time_on_wire;

datum_blake2b_prevblock_hidden(prevblock_hidden, block_template->previousblockhash_bin);
datum_blake2b_prevblock_hidden(s->blake2b_prevblock_hidden, block_template->previousblockhash_bin);

for(i=0;i<32;i++) {
uchar_to_hex(&s->prevhash[i << 1], prevblock_hidden[i]);
uchar_to_hex(&s->prevhash[i << 1], s->blake2b_prevblock_hidden[i]);
}
s->prevhash[64] = 0;
}
Expand Down
2 changes: 2 additions & 0 deletions src/datum_stratum.h
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,8 @@ typedef struct T_DATUM_STRATUM_JOB {
// BLAKE2b job fields
uint32_t blake2b_time_on_wire;
uint8_t blake2b_flags;
// Tagged hash shared by every submitted share for this job.
unsigned char blake2b_prevblock_hidden[32];

T_DATUM_TEMPLATE_DATA *block_template;

Expand Down
50 changes: 50 additions & 0 deletions src/datum_stratum_tests.c
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ int client_mining_submit(T_DATUM_CLIENT_DATA *c, uint64_t id, json_t *params_obj
static void datum_blake2b_refresh_time_offset_tests(void) {
T_DATUM_TEMPLATE_DATA tdata;
T_DATUM_STRATUM_JOB job;
unsigned char expected_hidden[32];
char expected_hex[65];

/* A job snapshots whether miners may submit a time offset. */
memset(&tdata, 0, sizeof(tdata));
Expand All @@ -58,6 +60,13 @@ static void datum_blake2b_refresh_time_offset_tests(void) {
datum_stratum_job_refresh_blake2b(&job);
datum_test(job.blake2b_time_on_wire == 2000000000u);
datum_test(job.blake2b_flags == DATUM_BLAKE2B_USE_TIME_OFFSET);
datum_blake2b_prevblock_hidden(expected_hidden, tdata.previousblockhash_bin);
datum_test(!memcmp(job.blake2b_prevblock_hidden, expected_hidden, sizeof(expected_hidden)));
for (size_t i = 0; i < sizeof(expected_hidden); i++) {
uchar_to_hex(expected_hex + (i << 1), expected_hidden[i]);
}
expected_hex[64] = 0;
datum_test(!strcmp(job.prevhash, expected_hex));

/* Without the flag the offset is ignored and curtime goes on the wire as is. */
tdata.curtime = 2000000000;
Expand Down Expand Up @@ -199,6 +208,46 @@ static void datum_blake2b_h_not_zero_tests(void) {
global_cur_stratum_jobs[0] = saved_job;
}

static void datum_blake2b_malformed_submit_job_tests(void) {
T_DATUM_CLIENT_DATA client = {0};
T_DATUM_MINER_DATA miner = {0};
T_DATUM_STRATUM_JOB job = {0};
T_DATUM_TEMPLATE_DATA tdata = {0};
T_DATUM_STRATUM_JOB *saved_job = global_cur_stratum_jobs[0];
static const char * const submits[] = {
"{\"id\":8,\"method\":\"mining.submit\",\"params\":[\"miner\",\"0000000000c0dg00\",\"0000000000000000\",\"00000000\",\"00000000\"]}",
"{\"id\":8,\"method\":\"mining.submit\",\"params\":[\"miner\",\"0000000000c0de00\",\"000000000000000g\",\"00000000\",\"00000000\"]}",
"{\"id\":8,\"method\":\"mining.submit\",\"params\":[\"miner\",\"0000000000c0de00\",\"0000000000000000\",\"0000000g\",\"00000000\"]}",
"{\"id\":8,\"method\":\"mining.submit\",\"params\":[\"miner\",\"0000000000c0de00\",\"0000000000000000\",\"000000000000000g\",\"00000000\"]}",
"{\"id\":8,\"method\":\"mining.submit\",\"params\":[\"miner\",\"0000000000c0de00\",\"0000000000000000\",\"00000000\",\"0000000g\"]}",
"{\"id\":8,\"method\":\"mining.submit\",\"params\":[\"miner\",\"0000000000c0de00\",\"0000000000000000\",\"00000000\",\"000000000000000g\"]}",
};
static const char expected[] =
"{\"error\":[20,\"unknown-work\",null],\"id\":8,\"result\":null}\n";
char submit[192];

client.app_client_data = &miner;
job.block_template = &tdata;
job.target_pot_index = 0;
job.coinbase[0].coinb1_len = 1;
job.coinbase[0].coinb1_bin[0] = 0xff;
strcpy(job.job_id, "0000000000c0de00");
miner.stratum_job_diffs[0] = 1;
global_cur_stratum_jobs[0] = &job;

for (size_t i = 0; i < sizeof(submits) / sizeof(submits[0]); i++) {
const uint64_t rejected_before = miner.share_count_rejected;
client.out_buf = 0;
strcpy(submit, submits[i]);
datum_test(datum_stratum_v1_socket_thread_client_cmd(&client, submit) == 0);
datum_test(miner.share_count_rejected == rejected_before + 1);
datum_test(client.out_buf == (int)strlen(expected));
datum_test(!memcmp(client.w_buffer, expected, strlen(expected)));
}

global_cur_stratum_jobs[0] = saved_job;
}

static void datum_blake2b_coinbase_selection_tests(void) {
T_DATUM_STRATUM_THREADPOOL_DATA *sdata = calloc(1, sizeof(*sdata));
T_DATUM_STRATUM_JOB job = {0};
Expand Down Expand Up @@ -509,6 +558,7 @@ void datum_stratum_tests(void) {
datum_stratum_string_request_id_tests();
datum_blake2b_coinbase_selection_tests();
datum_blake2b_h_not_zero_tests();
datum_blake2b_malformed_submit_job_tests();
datum_blake2b_client_pot_commitment_tests();
datum_blake2b_unmasked_block_tests();
datum_stratum_abw_block_request_tests();
Expand Down
28 changes: 28 additions & 0 deletions src/datum_utils.c
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,34 @@ void hex_to_bin(const char *hex, unsigned char *bin) {
}
}

static int hex_value(const char c) {
if (c >= '0' && c <= '9') return c - '0';
if (c >= 'a' && c <= 'f') return c - 'a' + 10;
if (c >= 'A' && c <= 'F') return c - 'A' + 10;
return -1;
}

bool hex_to_bin_exact(const char *hex, unsigned char *bin, const size_t bin_len) {
int high, low;
if (!hex || !bin) return false;
for (size_t i = 0; i < bin_len; i++) {
high = hex_value(hex[i<<1]);
if (high < 0) return false;
low = hex_value(hex[(i<<1)+1]);
if (low < 0) return false;
bin[i] = (unsigned char)((high << 4) | low);
}
return hex[bin_len<<1] == 0;
}

bool hex_to_u32(const char *hex, uint32_t *out) {
unsigned char bin[4];
if (!out || !hex_to_bin_exact(hex, bin, sizeof(bin))) return false;
*out = ((uint32_t)bin[0] << 24) | ((uint32_t)bin[1] << 16) |
((uint32_t)bin[2] << 8) | bin[3];
return true;
}

void panic_from_thread(int a) {
// set panic flag
panic_mode = 1;
Expand Down
Loading
Loading