diff --git a/src/datum_api.c b/src/datum_api.c index 7904f04f..c128e6e3 100644 --- a/src/datum_api.c +++ b/src/datum_api.c @@ -64,15 +64,6 @@ const char * const homepage_html_end = ""; #define DATUM_API_HOMEPAGE_MAX_SIZE 128000 -const char *cbnames[] = { - "Blank", - "Tiny", - "Default", - "Respect", - "Yuge", - "Antmain2" -}; - typedef struct MHD_Response *(*create_response_func_t)(); static struct MHD_Response *datum_api_create_empty_mhd_response() { @@ -923,7 +914,7 @@ int datum_api_client_dashboard(struct MHD_Connection *connection) { return MHD_YES; } - sz += snprintf(&output[sz], max_sz-1-sz, "
", datum_config.api_csrf_token); + sz += snprintf(&output[sz], max_sz-1-sz, "
TID/CID RemHost Auth Username Subbed Last Accepted VDiff DiffA (A) DiffR (R) Hashrate (age) Coinbase UserAgent Command
", datum_config.api_csrf_token); for (j = 0; j < max_threads; ++j) { for(ii=0;iimax_clients_thread;ii++) { @@ -969,17 +960,11 @@ int datum_api_client_dashboard(struct MHD_Connection *connection) { sz += snprintf(&output[sz], max_sz-1-sz, ""); } - if (m->coinbase_selection < (sizeof(cbnames) / sizeof(cbnames[0]))) { - sz += snprintf(&output[sz], max_sz-1-sz, "", cbnames[m->coinbase_selection]); - } else { - sz += snprintf(&output[sz], max_sz-1-sz, ""); - } - sz += snprintf(&output[sz], max_sz-1-sz, ""); } else { - sz += snprintf(&output[sz], max_sz-1-sz, ""); + sz += snprintf(&output[sz], max_sz-1-sz, ""); } sz += snprintf(&output[sz], max_sz-1-sz, "", j, ii, (unsigned long)m->connect_tsms, (unsigned long)m->unique_id, j, ii, (unsigned long)m->connect_tsms, (unsigned long)m->unique_id); @@ -1344,16 +1329,6 @@ bool datum_api_config_set(const char * const key, const char * const val, struct // TODO: apply change without restarting // TODO: switch pools smoother (keep old connection alive for share submissions until those jobs expire) status->need_restart = true; - } else if (0 == strcmp(key, "stratum_fingerprint_miners")) { - bool val_bool; - if (!datum_str_to_bool_strict(val, &val_bool)) { - json_array_append_new(errors, json_string_nocheck("\"Fingerprint and workaround known miner bugs\" must be 0 or 1")); - return false; - } - if (val_bool == datum_config.stratum_v1_fingerprint_miners) return true; - datum_config.stratum_v1_fingerprint_miners = val_bool; - datum_api_json_modify_new("stratum", "fingerprint_miners", json_boolean(val_bool)); - // TODO: apply change to connected miners? } else if (0 == strcmp(key, "datum_always_pay_self")) { bool val_bool; if (!datum_str_to_bool_strict(val, &val_bool)) { diff --git a/src/datum_coinbaser.c b/src/datum_coinbaser.c index 9ff4f019..e9bd67eb 100644 --- a/src/datum_coinbaser.c +++ b/src/datum_coinbaser.c @@ -184,39 +184,38 @@ int generate_coinbase_input(int height, char *cb, int *target_pot_index) { return cb_input_sz; } -void generate_coinbase_txns_for_stratum_job_subtypebysize(T_DATUM_STRATUM_JOB *s, int coinbase_index, int remaining_size, bool space_for_en_in_coinbase, int *cb1idx, int *cb2idx, bool special_coinb1) { +void generate_coinbase_txns_for_stratum_job_subtypebysize(T_DATUM_STRATUM_JOB *s, int coinbase_index, int remaining_size, bool space_for_en_in_coinbase, int *cb1idx, int *cb2idx) { // This function finishes off the stratum coinb1+coinb2 using the available outputs in the job and other flags specified. // it does not attempt to maximize coinb1's size to any specific size - int i, j, k, m, i2 = 0, c1cnt = 0; + int i, j, k, m; uint64_t mval = 0; - bool c1full = false; - bool en_done = false; // chicken and egg problem. we need to know the output count before we can close off coinb1 if !space_for_en_in_coinbase // either way, we want to start out coinb2 with outputs i = remaining_size; j = remaining_size; - if (special_coinb1) { - i2 = (300 - cb1idx[coinbase_index])>>1; - if (i2 < 0) i2 = 0; - space_for_en_in_coinbase = false; - } m = 0; mval = 0; // technically an output script could be > 0x4B, meaning an extra byte would be eaten here... but that's not currently the standard // this needs to match the loop lower in this function, as the count will get thrown off if it does not. - // TODO: Enforce max sigops! Note: This is not currently enforced in eloipool, either, so punting for now and will monitor network stats to determine priority. + // The sigop cost available to these outputs: the template's sigoplimit + // (from GBT, in sigop cost units, where one legacy CHECKSIG counts 4) minus + // the cost of the template's transactions and minus the cost of the pool's + // own output. available_coinbase_outputs[].sigops is set by the coinbaser + // parser: 4 for a script whose first byte is OP_DUP (0x76, P2PKH) and 0 + // for every other script. The pool output is charged the same way. An + // output whose cost exceeds the remaining budget is skipped, the same as an + // output that exceeds the remaining size, in both this counting pass and + // the writing pass below. + int64_t sigops_budget = (int64_t)s->block_template->sigoplimit - (int64_t)s->block_template->txn_total_sigops; + if ((s->pool_addr_script_len > 0) && (s->pool_addr_script[0] == 0x76)) sigops_budget -= 4; + if (sigops_budget < 0) sigops_budget = 0; + int64_t sigops_left = sigops_budget; for(k=0;kavailable_coinbase_outputs_count;k++) { - if (((s->available_coinbase_outputs[k].output_script_len+9) <= i) && ((mval + s->available_coinbase_outputs[k].value_sats) <= s->coinbase_value)) { - if ((special_coinb1) && (!c1full) && ((s->available_coinbase_outputs[k].output_script_len+9) <= i2)) { - i2 -= (s->available_coinbase_outputs[k].output_script_len+9); - c1cnt++; - } else { - c1full = true; - } - + if (((s->available_coinbase_outputs[k].output_script_len+9) <= i) && ((mval + s->available_coinbase_outputs[k].value_sats) <= s->coinbase_value) && (s->available_coinbase_outputs[k].sigops <= sigops_left)) { i -= (s->available_coinbase_outputs[k].output_script_len+9); + sigops_left -= s->available_coinbase_outputs[k].sigops; m++; mval += s->available_coinbase_outputs[k].value_sats; if (i < 30) break; @@ -235,44 +234,27 @@ void generate_coinbase_txns_for_stratum_job_subtypebysize(T_DATUM_STRATUM_JOB *s m+=3; cb1idx[coinbase_index] += append_bitcoin_varint_hex(m, &s->coinbase[coinbase_index].coinb1[cb1idx[coinbase_index]]); // extranonce, us, witness commit, and "m" outputs - if (!special_coinb1) { - // append extranonce op_return - cb1idx[coinbase_index] += sprintf(&s->coinbase[coinbase_index].coinb1[cb1idx[coinbase_index]], "0000000000000000106a0e%04" PRIx16, s->enprefix); - en_done = true; - } + // append extranonce op_return + cb1idx[coinbase_index] += sprintf(&s->coinbase[coinbase_index].coinb1[cb1idx[coinbase_index]], "0000000000000000106a0e%04" PRIx16, s->enprefix); } // append "m" payouts. find them the same way we did before mval = 0; + sigops_left = sigops_budget; for(k=0;kavailable_coinbase_outputs_count;k++) { - if (((s->available_coinbase_outputs[k].output_script_len+9) <= j) && ((mval + s->available_coinbase_outputs[k].value_sats) <= s->coinbase_value)) { + if (((s->available_coinbase_outputs[k].output_script_len+9) <= j) && ((mval + s->available_coinbase_outputs[k].value_sats) <= s->coinbase_value) && (s->available_coinbase_outputs[k].sigops <= sigops_left)) { j -= (s->available_coinbase_outputs[k].output_script_len+9); + sigops_left -= s->available_coinbase_outputs[k].sigops; m--; mval += s->available_coinbase_outputs[k].value_sats; - if ((special_coinb1) && (k < c1cnt)) { - // put in coinb1 - cb1idx[coinbase_index] += sprintf(&s->coinbase[coinbase_index].coinb1[cb1idx[coinbase_index]], "%016llx", (unsigned long long)__builtin_bswap64(s->available_coinbase_outputs[k].value_sats)); // TODO: Profile a faster way to do this - cb1idx[coinbase_index] += append_bitcoin_varint_hex(s->available_coinbase_outputs[k].output_script_len, &s->coinbase[coinbase_index].coinb1[cb1idx[coinbase_index]]); // Append script length - for(i=0;iavailable_coinbase_outputs[k].output_script_len;i++) { - uchar_to_hex(&s->coinbase[coinbase_index].coinb1[cb1idx[coinbase_index]], s->available_coinbase_outputs[k].output_script[i]); - cb1idx[coinbase_index]+=2; - } - } else { - if ((special_coinb1) && (k == c1cnt)) { - // append extranonce op_return - cb1idx[coinbase_index] += sprintf(&s->coinbase[coinbase_index].coinb1[cb1idx[coinbase_index]], "0000000000000000106a0e%04" PRIx16, s->enprefix); - en_done = true; - } - - // put in coinb2 - cb2idx[coinbase_index] += sprintf(&s->coinbase[coinbase_index].coinb2[cb2idx[coinbase_index]], "%016llx", (unsigned long long)__builtin_bswap64(s->available_coinbase_outputs[k].value_sats)); // TODO: Profile a faster way to do this - cb2idx[coinbase_index] += append_bitcoin_varint_hex(s->available_coinbase_outputs[k].output_script_len, &s->coinbase[coinbase_index].coinb2[cb2idx[coinbase_index]]); // Append script length - for(i=0;iavailable_coinbase_outputs[k].output_script_len;i++) { - uchar_to_hex(&s->coinbase[coinbase_index].coinb2[cb2idx[coinbase_index]], s->available_coinbase_outputs[k].output_script[i]); - cb2idx[coinbase_index]+=2; - } + // put in coinb2 + cb2idx[coinbase_index] += sprintf(&s->coinbase[coinbase_index].coinb2[cb2idx[coinbase_index]], "%016llx", (unsigned long long)__builtin_bswap64(s->available_coinbase_outputs[k].value_sats)); // TODO: Profile a faster way to do this + cb2idx[coinbase_index] += append_bitcoin_varint_hex(s->available_coinbase_outputs[k].output_script_len, &s->coinbase[coinbase_index].coinb2[cb2idx[coinbase_index]]); // Append script length + for(i=0;iavailable_coinbase_outputs[k].output_script_len;i++) { + uchar_to_hex(&s->coinbase[coinbase_index].coinb2[cb2idx[coinbase_index]], s->available_coinbase_outputs[k].output_script[i]); + cb2idx[coinbase_index]+=2; } if (!m) break; if (j < 30) break; @@ -285,11 +267,6 @@ void generate_coinbase_txns_for_stratum_job_subtypebysize(T_DATUM_STRATUM_JOB *s DLOG_ERROR("Attempting to pay more than we have available in the generation txn! --- %"PRIu64" sats available, %"PRIu64" sats to miners", s->coinbase_value, mval); } - if ((!space_for_en_in_coinbase) && (!en_done)) { - cb1idx[coinbase_index] += sprintf(&s->coinbase[coinbase_index].coinb1[cb1idx[coinbase_index]], "0000000000000000106a0e%04" PRIx16, s->enprefix); - en_done = true; - } - if (s->coinbase_value > mval) { // append our payout output value and script, since there are leftover funds cb2idx[coinbase_index] += sprintf(&s->coinbase[coinbase_index].coinb2[cb2idx[coinbase_index]], "%016llx", (unsigned long long)__builtin_bswap64(s->coinbase_value - mval)); // TODO: Profile a faster way to do this @@ -307,7 +284,7 @@ void generate_coinbase_txns_for_stratum_job_subtypebysize(T_DATUM_STRATUM_JOB *s cb2idx[coinbase_index] += sprintf(&s->coinbase[coinbase_index].coinb2[cb2idx[coinbase_index]], "0000000000000000036a0100"); // TODO: Is a naked OP_RETURN without any bytes after safe? Above TODO is probably better than investigating. } - // witness commit output costs 46 bytes + // witness commitment output costs 47 bytes (8 value, 1 length, 38 script) // append the default_witness_commitment cb2idx[coinbase_index] += sprintf(&s->coinbase[coinbase_index].coinb2[cb2idx[coinbase_index]], "0000000000000000%2.2x%s", (unsigned int)strlen(s->block_template->default_witness_commitment)>>1, s->block_template->default_witness_commitment); // lock time @@ -326,8 +303,13 @@ int datum_stratum_coinbase_fit_to_template(int max_sz, int fixed_bytes, T_DATUM_ msz1 = j; } - if (((i<<2)+s->block_template->txn_total_weight+340+36) > s->block_template->weightlimit) { - j = ((s->block_template->weightlimit - (s->block_template->txn_total_weight+340+36))>>2) - fixed_bytes; + // Block weight: four units a byte for the header and the transaction count + // (at most five bytes), four a byte for the coinbase (no witness data of + // its own) plus the 36 bytes of witness the node adds to it (marker, flag, + // one 32-byte item), and the template's transactions at their weight. The + // original 340 covered the 80-byte SHA256d header. + if (((i<<2)+s->block_template->txn_total_weight+((DATUM_BLAKE2B_BLOCK_HEADER_SIZE+5)<<2)+36) > s->block_template->weightlimit) { + j = ((s->block_template->weightlimit - (s->block_template->txn_total_weight+((DATUM_BLAKE2B_BLOCK_HEADER_SIZE+5)<<2)+36))>>2) - fixed_bytes; if (j < 0) return 0; msz1 = j; } @@ -455,7 +437,7 @@ void generate_base_coinbase_txns_for_stratum_job(T_DATUM_STRATUM_JOB *s, bool ne k = cb2idx[0]; } - // witness commit output costs 46 bytes + // witness commitment output costs 47 bytes (8 value, 1 length, 38 script) // append the default_witness_commitment cb2idx[0] += sprintf(&s->coinbase[0].coinb2[cb2idx[0]], "0000000000000000%2.2x%s", (unsigned int)strlen(s->block_template->default_witness_commitment)>>1, s->block_template->default_witness_commitment); // lock time @@ -506,8 +488,8 @@ void generate_coinbase_txns_for_stratum_job(T_DATUM_STRATUM_JOB *s, bool empty_o // Account for available vsize, sigops, size, weight, etc // Note: - // With a minimum payout of 10 TBC, the largest likely coinbase as of height 840000 is around 16 KB if we paid every miner the minimum to a long address type. - // This seems highly unlikely. 16KB is more than sufficient. + // MAX_DICTATED_COINBASE_SIZE bounds the generation transaction; the + // template's room and MAX_COINBASER_OUTPUTS usually bound it first. int i, j, k; char cb[300]; @@ -516,10 +498,10 @@ void generate_coinbase_txns_for_stratum_job(T_DATUM_STRATUM_JOB *s, bool empty_o bool space_for_en_in_coinbase = false; - int cb1idx[MAX_COINBASE_TYPES] = { 0,0,0,0,0,0 }; - int cb2idx[MAX_COINBASE_TYPES] = { 0,0,0,0,0,0 }; + int cb1idx[MAX_COINBASE_TYPES] = { 0 }; + int cb2idx[MAX_COINBASE_TYPES] = { 0 }; - int cb_req_sz[MAX_COINBASE_TYPES] = { 0,0,0,0,0 }; + int cb_req_sz[MAX_COINBASE_TYPES] = { 0 }; //////////////// @@ -564,18 +546,14 @@ void generate_coinbase_txns_for_stratum_job(T_DATUM_STRATUM_JOB *s, bool empty_o space_for_en_in_coinbase = true; } - // multiple coinbase options - // 0 = "empty" --- just pays pool addr, and possibly TIDES data. extranonce in coinbase if fits, or in first output if not. - // 1 = "nicehash" --- roughly 500 bytes total... smaller than antminer... has nothing before the extranonce OP_RETURN (or no extranonce OP_RETURN if enough space in the coinbase) - // 2 = "antminer" --- roughly 730 bytes max size, using a larger coinb1 and UART sync bits. This also works as a good default. - // 3 = "whatsminer" --- max 6500 bytes tested. does not need the extranonce OP_RETURN unless there's no space in the coinbase itself after tags - // 4 = "huge" --- max 16kB --- this is probably the most we should reasonably attempt to do in the coinbase... something like 380 to 530 outputs, depending on the type of output - // 5 = "antminer2" --- max 2250 bytes --- latest S21s appear to support this + // two coinbase options + // 0 = COINBASE_TYPE_TINY --- just pays pool addr. extranonce in coinbase if fits, or in first output if not. + // 1 = COINBASE_TYPE_YUGE --- carries the coinbaser's outputs, up to MAX_DICTATED_COINBASE_SIZE bytes - // only type 2 *needs* the OP_RETURN extranonce, unless the coinbase itself is too long + // the OP_RETURN extranonce is needed only when the coinbase itself is too long // set the len, and copy over the rest of the coinbase for(i=0;icoinbase[i].coinb1[cb1idx[i]]); } else { cb1idx[i] += append_bitcoin_varint_hex(cb_input_sz, &s->coinbase[i].coinb1[cb1idx[i]]); @@ -586,7 +564,7 @@ void generate_coinbase_txns_for_stratum_job(T_DATUM_STRATUM_JOB *s, bool empty_o s->target_pot_index = target_pot_index + (cb1idx[i]>>1); cb1idx[i] += cb_input_sz*2; - if ((i!=2) && (space_for_en_in_coinbase)) { + if (space_for_en_in_coinbase) { // if we are doing extranonce in the coinbase, then this is ALMOST the end of coinbase1 // we need a PUSH 14 and our enprefix in the coinbase uchar_to_hex(&s->coinbase[i].coinb1[cb1idx[i]], 0x0E); @@ -661,7 +639,7 @@ void generate_coinbase_txns_for_stratum_job(T_DATUM_STRATUM_JOB *s, bool empty_o k = cb2idx[0]; } - // witness commit output costs 46 bytes + // witness commitment output costs 47 bytes (8 value, 1 length, 38 script) // append the default_witness_commitment cb2idx[0] += sprintf(&s->coinbase[0].coinb2[cb2idx[0]], "0000000000000000%2.2x%s", (unsigned int)strlen(s->block_template->default_witness_commitment)>>1, s->block_template->default_witness_commitment); // lock time @@ -678,7 +656,7 @@ void generate_coinbase_txns_for_stratum_job(T_DATUM_STRATUM_JOB *s, bool empty_o ////////////////////////////// if (empty_only) { - // copy empty coinbaser to the others + // copy empty coinbaser to the other class for (i=1;icoinbase[i].coinb1, s->coinbase[0].coinb1); strcpy(s->coinbase[i].coinb2, s->coinbase[0].coinb2); @@ -687,7 +665,7 @@ void generate_coinbase_txns_for_stratum_job(T_DATUM_STRATUM_JOB *s, bool empty_o // ok, let's figure out how much space, if any, we have for miner payout outputs // we first need to figure out how much space we are using for each type after required data, so let's do that - // witness output = 46 bytes + // witness commitment output = 47 bytes (8 value, 1 length, 38 script) // pool output = pool_addr_script_len + 9 // coinbase itself = cb_input_sz // coinbase len = 1 @@ -695,39 +673,27 @@ void generate_coinbase_txns_for_stratum_job(T_DATUM_STRATUM_JOB *s, bool empty_o // lock time = 4 bytes // "sequence" = 4 bytes // extranonce size = 15 bytes (w/len push needed for either coinbase or OP_RETURN formats) - // output count... could technically be up to three bytes for types 3 + 4, most likely 1 byte for 0,1,2. - // --- lets give ourselves the wiggle room and say 3 bytes + // output count: one byte up to 252 outputs, three bytes past that (the + // largest class holds several hundred); counted at three so the coinbase + // never exceeds what datum_stratum_coinbase_fit_to_template allowed. // - // total static bytes = 46+9+1+41+4+3+4+15 = 123 bytes + // total static bytes = 47+9+1+41+4+3+4+15 = 124 bytes // not-static bytes = pool_addr_script_len + cb_input_sz + (space_for_en_in_coinbase?0:10) // --- it costs 10 extra bytes to do the OP_RETURN based extranonce + // + // This was 119, three bytes under the transaction with a one-byte output + // count and five under it with a three-byte count; a coinbase built to a + // template's room then exceeded the block's weight limit by up to 20 + // weight units. if (!space_for_en_in_coinbase) { - cb_req_sz[1] = cb_req_sz[2] = cb_req_sz[3] = cb_req_sz[4] = cb_req_sz[5] = 119 + s->pool_addr_script_len + cb_input_sz + 10; + cb_req_sz[COINBASE_TYPE_YUGE] = 124 + s->pool_addr_script_len + cb_input_sz + 10; } else { - cb_req_sz[1] = cb_req_sz[2] = cb_req_sz[3] = cb_req_sz[4] = cb_req_sz[5] = 119 + s->pool_addr_script_len + cb_input_sz; - cb_req_sz[2] += 10; // always OP_RETURN extranonce for type 2 + cb_req_sz[COINBASE_TYPE_YUGE] = 124 + s->pool_addr_script_len + cb_input_sz; } - // TYPE 1 - "Nicehash" friendly, max 500 bytes - i = datum_stratum_coinbase_fit_to_template(500, cb_req_sz[1], s); - generate_coinbase_txns_for_stratum_job_subtypebysize(s, 1, i, space_for_en_in_coinbase, cb1idx, cb2idx, false); - - // TYPE 3 - "Whatsminer" friendly, max 6500 bytes - i = datum_stratum_coinbase_fit_to_template(6500, cb_req_sz[3], s); - generate_coinbase_txns_for_stratum_job_subtypebysize(s, 3, i, space_for_en_in_coinbase, cb1idx, cb2idx, false); - - // TYPE 4 - "YUGE", max 16KB - i = datum_stratum_coinbase_fit_to_template(16000, cb_req_sz[4], s); - generate_coinbase_txns_for_stratum_job_subtypebysize(s, 4, i, space_for_en_in_coinbase, cb1idx, cb2idx, false); - - // TYPE 5 - "Antminer 2", max 2250 bytes - i = datum_stratum_coinbase_fit_to_template(2250, cb_req_sz[5], s); - generate_coinbase_txns_for_stratum_job_subtypebysize(s, 5, i, space_for_en_in_coinbase, cb1idx, cb2idx, false); - - // TYPE 2 - Older Antminer stock (S19) - i = datum_stratum_coinbase_fit_to_template(755, cb_req_sz[2], s); - generate_coinbase_txns_for_stratum_job_subtypebysize(s, 2, i, false, cb1idx, cb2idx, true); + i = datum_stratum_coinbase_fit_to_template(MAX_DICTATED_COINBASE_SIZE, cb_req_sz[COINBASE_TYPE_YUGE], s); + generate_coinbase_txns_for_stratum_job_subtypebysize(s, COINBASE_TYPE_YUGE, i, space_for_en_in_coinbase, cb1idx, cb2idx); } // prep binary versions of the coinbase for speeding up later @@ -823,7 +789,7 @@ int datum_coinbaser_v2_parse(T_DATUM_STRATUM_JOB *s, unsigned char *coinbaser, i cbvalid++; - if (cbvalid >= 512) break; // limitation of datum for now + if (cbvalid >= MAX_COINBASER_OUTPUTS) break; // the job's output list is this long } s->datum_coinbaser_id = datum_id; diff --git a/src/datum_coinbaser.h b/src/datum_coinbaser.h index ad38683d..835c9731 100644 --- a/src/datum_coinbaser.h +++ b/src/datum_coinbaser.h @@ -42,7 +42,7 @@ typedef struct T_DATUM_STRATUM_JOB T_DATUM_STRATUM_JOB; int datum_coinbaser_init(void); int generate_coinbase_input(int height, char *cb, int *target_pot_index); -void generate_coinbase_txns_for_stratum_job_subtypebysize(T_DATUM_STRATUM_JOB *s, int coinbase_index, int remaining_size, bool space_for_en_in_coinbase, int *cb1idx, int *cb2idx, bool special_coinb1); +void generate_coinbase_txns_for_stratum_job_subtypebysize(T_DATUM_STRATUM_JOB *s, int coinbase_index, int remaining_size, bool space_for_en_in_coinbase, int *cb1idx, int *cb2idx); void generate_coinbase_txns_for_stratum_job(T_DATUM_STRATUM_JOB *s, bool empty_only); void generate_base_coinbase_txns_for_stratum_job(T_DATUM_STRATUM_JOB *s, bool new_block); int datum_coinbaser_v2_parse(T_DATUM_STRATUM_JOB *s, unsigned char *coinbaser, int cblen, bool must_free); diff --git a/src/datum_coinbaser_tests.c b/src/datum_coinbaser_tests.c index d6b1dbcb..95af1864 100644 --- a/src/datum_coinbaser_tests.c +++ b/src/datum_coinbaser_tests.c @@ -33,12 +33,14 @@ * */ +#include #include #include "datum_conf.h" #include "datum_stratum.h" #include "datum_coinbaser.h" #include "datum_utils.h" +#include "datum_pow.h" int datum_stratum_coinbase_fit_to_template( int max_sz, int fixed_bytes, T_DATUM_STRATUM_JOB *s); @@ -73,9 +75,130 @@ static void datum_blake2b_coinbase_limit_tests(void) { /* The 164-byte header shrinks the coinbase leftover by 84 bytes. */ datum_test(datum_stratum_coinbase_fit_to_template(1000, 0, &job) == 866); + + /* The weight limit: the header and a five-byte count at four units a byte, + * the coinbase's 36 witness bytes, then 950 bytes of coinbase at four + * each. With the 80-byte header's 340 the leftover was 1000 (unbound). */ + tdata.sizelimit = 4000000; + tdata.weightlimit = ((DATUM_BLAKE2B_BLOCK_HEADER_SIZE + 5) * 4) + 36 + (4 * 950); + datum_test(datum_stratum_coinbase_fit_to_template(1000, 0, &job) == 950); + /* The transactions' weight counts the same way. */ + tdata.txn_total_weight = 4000; + tdata.weightlimit += 4000; + datum_test(datum_stratum_coinbase_fit_to_template(1000, 0, &job) == 950); + /* Fixed bytes are subtracted from the leftover. */ + datum_test(datum_stratum_coinbase_fit_to_template(1000, 100, &job) == 850); +} + +/* P2PKH: OP_DUP OP_HASH160 <20 bytes> OP_EQUALVERIFY OP_CHECKSIG, 25 bytes. */ +static const unsigned char datum_test_p2pkh_script[25] = {0x76, 0xa9, 0x14, [23] = 0x88, 0xac}; +/* P2WPKH: OP_0 <20 bytes>, 22 bytes. */ +static const unsigned char datum_test_p2wpkh_script[22] = {0x00, 0x14}; + +/* Builds one coinbase class with the template's used sigop cost set to + * sigops_used and the pool script set to P2PKH or P2WPKH, and returns the hex + * output count that follows the 8-character sequence at the start of coinb2. + * The count covers the included outputs plus the pool output and the witness + * commitment. The job's candidate outputs are two P2PKH outputs (cost 4 each) + * and one P2WPKH output (cost 0). */ +static const char *datum_coinbase_output_count_hex(T_DATUM_STRATUM_JOB *job, uint32_t sigops_used, bool pool_p2pkh) { + int cb1idx[MAX_COINBASE_TYPES] = {0}; + int cb2idx[MAX_COINBASE_TYPES] = {0}; + + job->block_template->txn_total_sigops = sigops_used; + if (pool_p2pkh) { + memcpy(job->pool_addr_script, datum_test_p2pkh_script, sizeof(datum_test_p2pkh_script)); + job->pool_addr_script_len = sizeof(datum_test_p2pkh_script); + } else { + memcpy(job->pool_addr_script, datum_test_p2wpkh_script, sizeof(datum_test_p2wpkh_script)); + job->pool_addr_script_len = sizeof(datum_test_p2wpkh_script); + } + memset(job->coinbase[1].coinb2, 0, sizeof(job->coinbase[1].coinb2)); + generate_coinbase_txns_for_stratum_job_subtypebysize(job, 1, 1000, true, cb1idx, cb2idx); + return job->coinbase[1].coinb2 + 8; +} + +static void datum_blake2b_coinbase_sigops_tests(void) { + T_DATUM_TEMPLATE_DATA tdata; + T_DATUM_STRATUM_JOB *job = calloc(1, sizeof(*job)); + int k; + + datum_test(job != NULL); + if (!job) return; + memset(&tdata, 0, sizeof(tdata)); + tdata.sigoplimit = 80000; + job->block_template = &tdata; + job->coinbase_value = 5000000000ULL; + for (k = 0; k < 3; k++) { + job->available_coinbase_outputs[k].value_sats = 100000000; + if (k < 2) { + memcpy(job->available_coinbase_outputs[k].output_script, datum_test_p2pkh_script, sizeof(datum_test_p2pkh_script)); + job->available_coinbase_outputs[k].output_script_len = sizeof(datum_test_p2pkh_script); + job->available_coinbase_outputs[k].sigops = 4; + } else { + memcpy(job->available_coinbase_outputs[k].output_script, datum_test_p2wpkh_script, sizeof(datum_test_p2wpkh_script)); + job->available_coinbase_outputs[k].output_script_len = sizeof(datum_test_p2wpkh_script); + job->available_coinbase_outputs[k].sigops = 0; + } + } + job->available_coinbase_outputs_count = 3; + + /* Space for every output: all three, the pool output and the witness commitment. */ + datum_test(!strncmp(datum_coinbase_output_count_hex(job, 0, false), "05", 2)); + /* Budget for one P2PKH output: the first P2PKH is included, the second is + * skipped, and the P2WPKH is included. */ + datum_test(!strncmp(datum_coinbase_output_count_hex(job, 80000 - 4, false), "04", 2)); + /* Budget of 0: only the P2WPKH output is included. */ + datum_test(!strncmp(datum_coinbase_output_count_hex(job, 80000, false), "03", 2)); + /* A P2PKH pool output takes the remaining 4 units of the budget, so neither + * P2PKH candidate is included. */ + datum_test(!strncmp(datum_coinbase_output_count_hex(job, 80000 - 4, true), "03", 2)); + free(job); +} + +/* A split of MAX_COINBASER_OUTPUTS P2WPKH outputs, 31 bytes each, fits one + * coinbase: the output count is written as a three-byte varint and the hex of + * coinb2 stays inside STRATUM_COINBASE2_MAX_LEN. */ +static void datum_blake2b_large_coinbase_tests(void) { + T_DATUM_TEMPLATE_DATA tdata; + T_DATUM_STRATUM_JOB *job = calloc(1, sizeof(*job)); + int cb1idx[MAX_COINBASE_TYPES] = {0}; + int cb2idx[MAX_COINBASE_TYPES] = {0}; + size_t cb_bytes; + int k; + + datum_test(job != NULL); + if (!job) return; + memset(&tdata, 0, sizeof(tdata)); + tdata.sigoplimit = 80000; + job->block_template = &tdata; + job->coinbase_value = 5000000000ULL; + memcpy(job->pool_addr_script, datum_test_p2wpkh_script, sizeof(datum_test_p2wpkh_script)); + job->pool_addr_script_len = sizeof(datum_test_p2wpkh_script); + for (k = 0; k < MAX_COINBASER_OUTPUTS; k++) { + job->available_coinbase_outputs[k].value_sats = 1000; + memcpy(job->available_coinbase_outputs[k].output_script, datum_test_p2wpkh_script, sizeof(datum_test_p2wpkh_script)); + job->available_coinbase_outputs[k].output_script_len = sizeof(datum_test_p2wpkh_script); + job->available_coinbase_outputs[k].sigops = 0; + } + job->available_coinbase_outputs_count = MAX_COINBASER_OUTPUTS; + + generate_coinbase_txns_for_stratum_job_subtypebysize( + job, COINBASE_TYPE_YUGE, MAX_COINBASER_OUTPUTS * 31, true, cb1idx, cb2idx); + + /* MAX_COINBASER_OUTPUTS dictated outputs, the pool output and the witness + * commitment: 1026, written as fd followed by 0x0402 little-endian. */ + datum_test(!strncmp(job->coinbase[COINBASE_TYPE_YUGE].coinb2 + 8, "fd0204", 6)); + datum_test(strlen(job->coinbase[COINBASE_TYPE_YUGE].coinb2) < STRATUM_COINBASE2_MAX_LEN); + cb_bytes = (strlen(job->coinbase[COINBASE_TYPE_YUGE].coinb1) + + strlen(job->coinbase[COINBASE_TYPE_YUGE].coinb2)) / 2 + 12; + datum_test(cb_bytes > 30000 && cb_bytes <= MAX_DICTATED_COINBASE_SIZE); + free(job); } void datum_coinbaser_tests(void) { datum_prime_id_64bit_tests(); datum_blake2b_coinbase_limit_tests(); + datum_blake2b_coinbase_sigops_tests(); + datum_blake2b_large_coinbase_tests(); } diff --git a/src/datum_conf.c b/src/datum_conf.c index 6801cb1c..d56176ed 100644 --- a/src/datum_conf.c +++ b/src/datum_conf.c @@ -103,8 +103,6 @@ const T_DATUM_CONFIG_ITEM datum_config_options[] = { .required = false, .ptr = &datum_config.stratum_v1_vardiff_quickdiff_delta, .default_int = 8 }, { .var_type = DATUM_CONF_INT, .category = "stratum", .name = "share_stale_seconds", .description = "How many seconds after a job is generated before a share submission is considered stale?", .required = false, .ptr = &datum_config.stratum_v1_share_stale_seconds, .default_int = 120 }, - { .var_type = DATUM_CONF_BOOL, .category = "stratum", .name = "fingerprint_miners", .description = "Attempt to fingerprint miners for better use of coinbase space", - .required = false, .ptr = &datum_config.stratum_v1_fingerprint_miners, .default_bool = true }, { .var_type = DATUM_CONF_INT, .category = "stratum", .name = "idle_timeout_no_subscribe",.description = "Seconds we allow a connection to be idle without seeing a work subscription? (0 disables)", .required = false, .ptr = &datum_config.stratum_v1_idle_timeout_no_subscribe, .default_int = 15 }, { .var_type = DATUM_CONF_INT, .category = "stratum", .name = "idle_timeout_no_shares", .description = "Seconds we allow a subscribed connection to be idle without seeing at least one accepted share? (0 disables)", diff --git a/src/datum_conf.h b/src/datum_conf.h index 85f014e2..37ee5ae1 100644 --- a/src/datum_conf.h +++ b/src/datum_conf.h @@ -120,7 +120,6 @@ typedef struct { int stratum_v1_vardiff_quickdiff_count; int stratum_v1_vardiff_quickdiff_delta; int stratum_v1_share_stale_seconds; - bool stratum_v1_fingerprint_miners; int stratum_v1_idle_timeout_no_subscribe; int stratum_v1_idle_timeout_no_share; int stratum_v1_idle_timeout_max_last_work; diff --git a/src/datum_gateway.h b/src/datum_gateway.h index f2e25bbd..cdf45579 100644 --- a/src/datum_gateway.h +++ b/src/datum_gateway.h @@ -45,16 +45,25 @@ #endif // For SV1 -// client buffer must be large enough to hold entire coinbase in hex at max size +// BLAKE2b work sends the miner a 39-byte coinb1 and an empty coinb2, so this +// no longer has to hold a coinbase in hex. // TODO: Make somewhat more dynamic without having to hammer [cm]alloc #define CLIENT_BUFFER ((16384*3)+1024) -// in ascii hex +// in ascii hex. coinb1 holds the fixed prefix; every dictated output goes in +// coinb2, so only coinb2 grows with the payout count. The share's section +// (0x02) writes each length as a uint16, which caps either part at 65535 bytes; +// coinb2 here is at most 32767 bytes. #define STRATUM_COINBASE1_MAX_LEN 1024 -#define STRATUM_COINBASE2_MAX_LEN 32768 +#define STRATUM_COINBASE2_MAX_LEN 65536 #define MAX_COINBASE_TXN_SIZE_BYTES (((STRATUM_COINBASE1_MAX_LEN+STRATUM_COINBASE2_MAX_LEN)>>1)+64) +// A share message (0x27) with its job and coinbase sections: the coinbase's two +// parts reach MAX_COINBASE_TXN_SIZE_BYTES, the rest is the merkle branches, the +// username and the pad. +#define MAX_POW_MESSAGE_SIZE (MAX_COINBASE_TXN_SIZE_BYTES+4096) + #define STRATUM_JOB_INDEX_XOR ((uint16_t)0xC0DE) void datum_print_banner(void); diff --git a/src/datum_protocol.c b/src/datum_protocol.c index ff069ba1..9528965e 100644 --- a/src/datum_protocol.c +++ b/src/datum_protocol.c @@ -471,7 +471,7 @@ void datum_protocol_replay_clear(void) { T_DATUM_REPLAY_PENDING *datum_protocol_replay_add( const T_DATUM_PROTOCOL_POW *pow, const unsigned char *message, size_t message_size) { - if (!pow || !message || !message_size || message_size > 32768) + if (!pow || !message || !message_size || message_size > MAX_POW_MESSAGE_SIZE) return NULL; T_DATUM_REPLAY_PENDING *pending = calloc(1, sizeof(*pending)); @@ -1218,7 +1218,7 @@ int datum_protocol_abw_reveal(int len, unsigned char *data) { pthread_mutex_t datum_protocol_coinbaser_fetch_mutex = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t datum_protocol_coinbaser_fetch_cond = PTHREAD_COND_INITIALIZER; -unsigned char datum_coinbaser_v2_response_buf[2][32768] = { 0 }; +unsigned char datum_coinbaser_v2_response_buf[2][DATUM_PROTOCOL_COINBASER_BUFFER_SIZE] = { 0 }; unsigned char *datum_coinbaser_v2_response = NULL; unsigned char datum_coinbaser_v2_response_buf_idx = 0; uint64_t datum_coinbaser_v2_response_value[2] = { 0, 0 }; @@ -1263,7 +1263,7 @@ int datum_protocol_coinbaser_fetch_response(int len, unsigned char *data) { v = upk_u64le(data, 0); x = upk_u32le(data, 8); - if ((x > 32768-1) || (x<1) || x > (unsigned int)(len - 12)) { + if ((x > DATUM_PROTOCOL_COINBASER_BUFFER_SIZE-1) || (x<1) || x > (unsigned int)(len - 12)) { DLOG_DEBUG("Invalid coinbaser received! %lu %lu", (unsigned long)x, (unsigned long)(len-12)); return 0; } @@ -2867,8 +2867,8 @@ static void datum_protocol_pow_forget_failed_send( // {"params": ["mzjP9Hn7aqaCLM5pSgMSQzgs3gnxSFv91B", "662599770700", "f40c000000000000", "66259976", "48220d13", "00d30000"], "id": 182, "method": "mining.submit"} int datum_protocol_pow(void *arg) { T_DATUM_PROTOCOL_POW *pow = arg; - unsigned char msg[32768 + crypto_box_MACBYTES]; - unsigned char replay_message[32768]; + unsigned char msg[MAX_POW_MESSAGE_SIZE + crypto_box_MACBYTES]; + unsigned char replay_message[MAX_POW_MESSAGE_SIZE]; T_DATUM_REPLAY_PENDING *pending; int i, j; diff --git a/src/datum_protocol.h b/src/datum_protocol.h index 0ba64c03..aa094d86 100644 --- a/src/datum_protocol.h +++ b/src/datum_protocol.h @@ -57,6 +57,12 @@ #define DATUM_PROTOCOL_CONNECT_TIMEOUT 30 #define DATUM_PROTOCOL_MAX_CMD_DATA_SIZE 4194304 // 2^22 - protocol limit! + +// The coinbaser blob (server subcmd 0x10 response) the gateway accepts. Its +// wire length field is a uint32, so this buffer is the only limit; at 43 bytes +// an output it holds MAX_COINBASER_OUTPUTS taproot payouts. Matches RATUM's +// MAX_COINBASER_BLOB_LEN + 1. +#define DATUM_PROTOCOL_COINBASER_BUFFER_SIZE 65536 #define DATUM_PROTOCOL_BUFFER_SIZE (DATUM_PROTOCOL_MAX_CMD_DATA_SIZE*3) #define DATUM_PROTOCOL_MAX_USERNAME_LEN 384 diff --git a/src/datum_protocol_tests.c b/src/datum_protocol_tests.c index babc7eba..bf71d887 100644 --- a/src/datum_protocol_tests.c +++ b/src/datum_protocol_tests.c @@ -699,10 +699,10 @@ static void datum_pow_recycled_protocol_job_test(void) { job->datum_coinbaser_id = (unsigned char)i; job->prevhash_bin[0] = (unsigned char)(0xa0 + i); job->nbits_bin[0] = (unsigned char)(0xb0 + i); - job->coinbase[2].coinb1_len = 1; - job->coinbase[2].coinb2_len = 1; - job->coinbase[2].coinb1_bin[0] = (unsigned char)(0xc0 + i); - job->coinbase[2].coinb2_bin[0] = (unsigned char)(0xd0 + i); + job->coinbase[COINBASE_TYPE_YUGE].coinb1_len = 1; + job->coinbase[COINBASE_TYPE_YUGE].coinb2_len = 1; + job->coinbase[COINBASE_TYPE_YUGE].coinb1_bin[0] = (unsigned char)(0xc0 + i); + job->coinbase[COINBASE_TYPE_YUGE].coinb2_bin[0] = (unsigned char)(0xd0 + i); job->subsidy_only_coinbase.coinb1_len = 1; job->subsidy_only_coinbase.coinb2_len = 1; job->subsidy_only_coinbase.coinb1_bin[0] = (unsigned char)(0xe0 + i); @@ -713,7 +713,7 @@ static void datum_pow_recycled_protocol_job_test(void) { pow.datum_job_id = datum_protocol_setup_new_job_idx(&jobs[0]); pow.sjob = &jobs[0]; memcpy(pow.stratum_job_id, jobs[0].job_id, sizeof(pow.stratum_job_id)); - pow.coinbase_id = 2; + pow.coinbase_id = COINBASE_TYPE_YUGE; pow.blake2b_use_time_offset = true; pow.ntime = UINT64_C(0x1817161514131211); pow.nonce = UINT64_C(0x0807060504030201); @@ -755,7 +755,7 @@ static void datum_pow_recycled_protocol_job_test(void) { // Malformed local state must not index beyond the six generated variants. pow.coinbase_id = MAX_COINBASE_TYPES; datum_test(datum_protocol_pow_build_message(&pow, msg, sizeof(msg)) == 0); - pow.coinbase_id = 2; + pow.coinbase_id = COINBASE_TYPE_YUGE; pow.subsidy_only = true; datum_test(datum_protocol_pow_build_message(&pow, msg, sizeof(msg)) == 0); pow.coinbase_id = DATUM_COINBASE_ID_EMPTY; @@ -765,7 +765,7 @@ static void datum_pow_recycled_protocol_job_test(void) { datum_test(msg[70] == 0xe0 && msg[71] == 0xf0); datum_test(datum_jobs[0].server_has_coinbase_empty); pow.subsidy_only = false; - pow.coinbase_id = 2; + pow.coinbase_id = COINBASE_TYPE_YUGE; // snprintf returns the untruncated length. Ensure a long address+worker is // capped to the actual bytes in the protocol username field. diff --git a/src/datum_stratum.c b/src/datum_stratum.c index a1818faf..f994e119 100644 --- a/src/datum_stratum.c +++ b/src/datum_stratum.c @@ -822,9 +822,6 @@ void stratum_update_vardiff(T_DATUM_CLIENT_DATA *c, bool no_quick) { if (delta_tsms > 60000) { // 60s with no shares seems sufficient to bump diff down next round. m->current_diff = m->current_diff >> 1; - if (m->current_diff < m->forced_high_min_diff) { - m->current_diff = m->forced_high_min_diff; - } if (m->current_diff < datum_config.stratum_v1_vardiff_min) { m->current_diff = datum_config.stratum_v1_vardiff_min; } @@ -873,9 +870,6 @@ void stratum_update_vardiff(T_DATUM_CLIENT_DATA *c, bool no_quick) { if (ms_per_share > (target_ms_share*2)) { // adjust diff downward a tick m->current_diff = m->current_diff >> 1; - if (m->current_diff < m->forced_high_min_diff) { - m->current_diff = m->forced_high_min_diff; - } if (m->current_diff < datum_config.stratum_v1_vardiff_min) { m->current_diff = datum_config.stratum_v1_vardiff_min; } @@ -1450,15 +1444,20 @@ int client_mining_authorize(T_DATUM_CLIENT_DATA *c, uint64_t id, json_t *params_ return 0; } +// The coinbase a BLAKE2b job commits to, the same for every miner: the +// subsidy-only one for new-block work, COINBASE_TYPE_TINY (pays only the pool) +// while the job state is below JOB_STATE_FULL_PRIORITY_WAIT_COINBASER or +// full_coinbase_ready is unset, and COINBASE_TYPE_YUGE after that. The classes +// were sized to what SHA256d firmware could accept (see the COINBASE_TYPE_ +// defines); on BLAKE2b work the miner never receives the coinbase, so there is +// no per-miner selection. unsigned int datum_stratum_coinbase_index( - const T_DATUM_STRATUM_THREADPOOL_DATA *sdata, - const T_DATUM_MINER_DATA *miner, bool new_block) { + const T_DATUM_STRATUM_THREADPOOL_DATA *sdata, bool new_block) { if (new_block) return DATUM_COINBASE_ID_EMPTY; - if (!sdata || !miner || !sdata->cur_stratum_job || + if (!sdata || !sdata->cur_stratum_job || sdata->cur_stratum_job->job_state < JOB_STATE_FULL_PRIORITY_WAIT_COINBASER || - !sdata->full_coinbase_ready || - miner->coinbase_selection >= MAX_COINBASE_TYPES) return 0; - return miner->coinbase_selection; + !sdata->full_coinbase_ready) return 0; + return COINBASE_TYPE_YUGE; } int send_mining_notify(T_DATUM_CLIENT_DATA *c, bool clean, bool quickdiff, bool new_block) { @@ -1552,7 +1551,7 @@ int send_mining_notify(T_DATUM_CLIENT_DATA *c, bool clean, bool quickdiff, bool const int notify_out_buf_start = c->out_buf; datum_socket_send_string_to_client(c, "{\"id\":null,\"method\":\"mining.notify\",\"params\":["); - cbselect = datum_stratum_coinbase_index(sdata, m, new_block); + cbselect = datum_stratum_coinbase_index(sdata, new_block); const bool subsidy_only = cbselect == DATUM_COINBASE_ID_EMPTY; cb = subsidy_only ? &j->subsidy_only_coinbase : &j->coinbase[cbselect]; @@ -1619,71 +1618,6 @@ int send_mining_set_difficulty(T_DATUM_CLIENT_DATA *c) { return 0; } -void datum_stratum_fingerprint_by_UA(T_DATUM_MINER_DATA *m) { - // TODO: Make this a little more efficient. perhaps move to a loadable definitions file of some kind. - - if (strstr(m->useragent, "Antminer A3") == m->useragent) { - m->coinbase_selection = 0; - return; - } - - // S21 tested to handle 2.25KB coinbase work on all versions released - // UA starts with: Antminer S21/ - // S21 Pro NOT confirmed to work this way (yet)... so keep the / - if (strstr(m->useragent, "Antminer S21/") == m->useragent) { - m->coinbase_selection = 5; // ANTMAIN2 - return; - } - - // the ePIC control boards can handle almost any size coinbase - // UA starts with: PowerPlay-BM/ - if (strstr(m->useragent, "PowerPlay-BM/") == m->useragent) { - m->coinbase_selection = 4; // YUGE - return; - } - - // "vinsh" reports as xminer - // Tested to handle up to 16KB - if (strstr(m->useragent, "xminer-1.") == m->useragent) { - m->coinbase_selection = 4; // YUGE - return; - } - - // whatsminer works fine with about a 6.5 KB coinbase - // UA starts with: whatsminer/v1 - if (strstr(m->useragent, "whatsminer/v1") == m->useragent) { - m->coinbase_selection = 3; // RESPECTABLE - return; - } - - // Braiins firmware - // Appears to handle arbitrary coinbase sizes, however not extensively tested on all firmware versions - // feed the S21-like coinbase for now, which is at least moderately sized - // UA contains: bosminer-plus-tuner - if (strstr(m->useragent, "bosminer-plus-tuner") != NULL) { // match anywhere in string, not just beginning - m->coinbase_selection = 5; // ANTMAIN2 - return; - } - - // Nicehash, sadly needs a smaller coinbase than even antminer s19s - // they also need a high minimum difficulty - if (strstr(m->useragent, "NiceHash/") == m->useragent) { - m->current_diff=524288; - m->forced_high_min_diff=524288; - m->coinbase_selection = 1; // TINY - return; - } - - // The Bitaxe is tested to work with a large coinbase - // However, it does slow work changes slightly when they're YUGE, so we'll go with - // the whatsminer tested size as a compromise. also should save some bandwidth, which - // is probably not a bad plan, given the low odds of a bitaxe finding a block. - if (strstr(m->useragent, "bitaxe") == m->useragent) { - m->coinbase_selection = 3; // RESPECTABLE - return; - } -} - int client_mining_subscribe(T_DATUM_CLIENT_DATA *c, uint64_t id, json_t *params_obj) { uint32_t sid; char s[1024]; @@ -1704,10 +1638,6 @@ int client_mining_subscribe(T_DATUM_CLIENT_DATA *c, uint64_t id, json_t *params_ // set default diff m->current_diff = datum_config.stratum_v1_vardiff_min; - // default to the antminer workaround, which appears to be universally compatible - // except for NiceHash. - m->coinbase_selection = 2; - m->useragent[0] = 0; if (params_obj) { if (json_is_array(params_obj)) { @@ -1719,13 +1649,6 @@ int client_mining_subscribe(T_DATUM_CLIENT_DATA *c, uint64_t id, json_t *params_ } } - if ((datum_config.stratum_v1_fingerprint_miners) && (m->useragent[0])) { - datum_stratum_fingerprint_by_UA(m); - if (m->current_diff < datum_config.stratum_v1_vardiff_min) { - m->current_diff = datum_config.stratum_v1_vardiff_min; - } - } - // get a new unique session ID for this connection (extranonce1) sid = get_new_session_id(c); m->sid = sid; diff --git a/src/datum_stratum.h b/src/datum_stratum.h index 3cb1fd43..3cd9ff13 100644 --- a/src/datum_stratum.h +++ b/src/datum_stratum.h @@ -49,14 +49,35 @@ #define MAX_STRATUM_JOBS 256 -#define MAX_COINBASE_TYPES 6 +#define MAX_COINBASE_TYPES 2 #define DATUM_COINBASE_ID_EMPTY 0xff #define COINBASE_TYPE_TINY 0 // "empty", just pays pool -#define COINBASE_TYPE_SMALL 1 // Nicehash needs a tiny coinb1, among other things. Max 500 bytes. -#define COINBASE_TYPE_ANTMAIN 2 // Hack for antminer stock firmware to 750 bytes -#define COINBASE_TYPE_RESPECTABLE 3 // 6500 byte max (whatsminers) -#define COINBASE_TYPE_YUGE 4 // 16KB max (ePIC, bitaxe) -#define COINBASE_TYPE_ANTMAIN2 5 // 2.25KB max (S21, +?) +#define COINBASE_TYPE_YUGE 1 // MAX_DICTATED_COINBASE_SIZE bytes, every miner + +// The largest generation transaction this gateway builds, in bytes. It holds +// about 1030 P2WPKH outputs (31 bytes each) or 740 taproot outputs (43 bytes +// each), and keeps coinb2 inside STRATUM_COINBASE2_MAX_LEN. +// datum_stratum_coinbase_fit_to_template cuts it to what the template's size, +// weight and the block's sigop limit leave, which with the node's default +// -blockreservedweight of 8000 weight units is about 1800 bytes on a full +// block: raise -blockreservedweight to 4x the coinbase wanted. +#define MAX_DICTATED_COINBASE_SIZE 32000 + +// The dictated outputs one coinbaser response may carry, and the size of the +// job's parsed output list. Matches RATUM's MAX_COINBASER_OUTPUTS. +#define MAX_COINBASER_OUTPUTS 1024 + +// SHA256d firmware set the class sizes, since those miners receive +// coinb1/coinb2 and hash the coinbase. On BLAKE2b work the miner receives +// 000000 || H2 || 00000000 as coinb1 (H2 is the "Merge-mining hook" tagged +// hash, which commits to the coinbase) and an empty coinb2, and the work root +// is blake2b(0x00 || coinb1 || extranonce), so the coinbase itself never +// reaches the miner and no firmware size applies to it. The classes sized for +// NiceHash, Antminer and Whatsminer firmware are gone; what remains is +// COINBASE_TYPE_YUGE, served to every miner once the full coinbase is ready, +// and COINBASE_TYPE_TINY, served before that +// (datum_stratum_coinbase_index). An output the coinbase has no room for is +// left out and its value paid to the pool's address as the remainder. // Submitblock json rpc command max size is max block size * 2 for ascii plus some breathing room #define MAX_SUBMITBLOCK_SIZE 8500000 @@ -146,18 +167,14 @@ typedef struct T_DATUM_STRATUM_JOB { char merklebranches_full[4096]; // when fetching the coinbaser, we'll just stash all of the possible and valid output scripts here - T_DATUM_TXN_OUTPUT available_coinbase_outputs[512]; + T_DATUM_TXN_OUTPUT available_coinbase_outputs[MAX_COINBASER_OUTPUTS]; int available_coinbase_outputs_count; uint8_t pool_addr_script[MAX_OUTPUT_SCRIPT_LEN]; uint8_t pool_addr_script_len; - // multiple coinbase options - // 0 = "empty" --- just pays pool addr, and possibly TIDES data. extranonce in coinbase if fits, or in first output if not. - // 1 = "nicehash" --- roughly 500 bytes total... smaller than antminer... has nothing before the extranonce OP_RETURN (or no extranonce OP_RETURN if enough space in the coinbase) - // 2 = "antminer" --- roughly 730 bytes max size, using a larger coinb1 and UART sync bits. This also works as a good default. - // 3 = "whatsminer" --- max 6500 bytes tested. does not need the extranonce OP_RETURN unless there's no space in the coinbase itself after tags - // 4 = "huge" --- max 16kB --- this is probably the most we should reasonably attempt to do in the coinbase... something like 380 to 530 outputs, depending on the type of output - // 5 = "antminer2" --- max 2250 bytes --- latest S21s appear to support this + // two coinbase options + // 0 = COINBASE_TYPE_TINY --- just pays pool addr. extranonce in coinbase if fits, or in first output if not. + // 1 = COINBASE_TYPE_YUGE --- MAX_DICTATED_COINBASE_SIZE bytes, carries the coinbaser's outputs T_DATUM_STRATUM_COINBASE coinbase[MAX_COINBASE_TYPES]; T_DATUM_STRATUM_COINBASE subsidy_only_coinbase; int target_pot_index; // where in coinb1 do we put our per-user vardiff pot value? @@ -234,8 +251,6 @@ typedef struct { uint8_t stratum_job_targets[MAX_STRATUM_JOBS][32]; uint64_t stratum_job_diffs[MAX_STRATUM_JOBS]; - unsigned char coinbase_selection; - uint64_t share_diff_accepted; uint64_t share_count_accepted; @@ -251,8 +266,6 @@ typedef struct { uint64_t quickdiff_value; uint8_t quickdiff_target[32]; - uint64_t forced_high_min_diff; - int last_sent_stratum_job_index; T_DATUM_STRATUM_USER_STATS stats; @@ -273,7 +286,7 @@ bool datum_stratum_job_blake2b_commitment_from_txn(const T_DATUM_STRATUM_JOB *s, bool datum_stratum_job_blake2b_commitment(T_DATUM_STRATUM_JOB *s, const T_DATUM_STRATUM_COINBASE *cb, bool subsidy_only, unsigned char pot, unsigned char *commitment, unsigned char *coinb1); bool datum_stratum_share_is_unmasked_block( const T_DATUM_STRATUM_JOB *job, const unsigned char *share_hash); -unsigned int datum_stratum_coinbase_index(const T_DATUM_STRATUM_THREADPOOL_DATA *sdata, const T_DATUM_MINER_DATA *miner, bool new_block); +unsigned int datum_stratum_coinbase_index(const T_DATUM_STRATUM_THREADPOOL_DATA *sdata, bool new_block); void stratum_job_merkle_root_calc(T_DATUM_STRATUM_JOB *s, unsigned char *coinbase_txn_hash, unsigned char *merkle_root_output); int assembleBlockAndSubmit(uint8_t *block_header, uint8_t *coinbase_txn, size_t coinbase_txn_size, T_DATUM_STRATUM_JOB *job, T_DATUM_STRATUM_THREADPOOL_DATA *sdata, const char *block_hash_hex, bool empty_work, const unsigned char *extranonce); size_t datum_stratum_coinbase_for_block_hex(char *out, size_t out_size, const uint8_t *coinbase_txn, size_t coinbase_txn_size, bool add_witness); diff --git a/src/datum_stratum_tests.c b/src/datum_stratum_tests.c index 17199bba..07f824ad 100644 --- a/src/datum_stratum_tests.c +++ b/src/datum_stratum_tests.c @@ -99,8 +99,8 @@ static void datum_blake2b_client_pot_commitment_tests(void) { memset(job.coinbase[0].coinb1_bin, 0x11, 20); memset(job.coinbase[0].coinb2_bin, 0x22, 8); job.coinbase[0].coinb1_bin[4] = 0xFF; - job.coinbase[2] = job.coinbase[0]; - job.coinbase[2].coinb2_bin[0] ^= 0x55; + job.coinbase[1] = job.coinbase[0]; + job.coinbase[1].coinb2_bin[0] ^= 0x55; job.subsidy_only_coinbase = job.coinbase[0]; job.subsidy_only_coinbase.coinb2_bin[0] ^= 0xaa; job.target_pot_index = 4; @@ -110,7 +110,7 @@ static void datum_blake2b_client_pot_commitment_tests(void) { datum_test(datum_stratum_job_blake2b_commitment(&job, &job.coinbase[0], false, 14, c_pot, pot)); datum_test(memcmp(c_ff, c_pot, 32) != 0); datum_test(memcmp(ff, pot, 39) != 0); - datum_test(datum_stratum_job_blake2b_commitment(&job, &job.coinbase[2], false, 14, c_variant, NULL)); + datum_test(datum_stratum_job_blake2b_commitment(&job, &job.coinbase[1], false, 14, c_variant, NULL)); datum_test(datum_stratum_job_blake2b_commitment(&job, &job.subsidy_only_coinbase, true, 14, c_subsidy, NULL)); datum_test(memcmp(c_variant, c_pot, 32) != 0); datum_test(memcmp(c_subsidy, c_pot, 32) != 0); @@ -202,19 +202,21 @@ static void datum_blake2b_h_not_zero_tests(void) { static void datum_blake2b_coinbase_selection_tests(void) { T_DATUM_STRATUM_THREADPOOL_DATA *sdata = calloc(1, sizeof(*sdata)); T_DATUM_STRATUM_JOB job = {0}; - T_DATUM_MINER_DATA miner = {.coinbase_selection = 3}; datum_test(sdata != NULL); if (!sdata) return; - datum_test(datum_stratum_coinbase_index(sdata, &miner, true) == DATUM_COINBASE_ID_EMPTY); - datum_test(datum_stratum_coinbase_index(sdata, &miner, false) == 0); + datum_test(datum_stratum_coinbase_index(sdata, true) == DATUM_COINBASE_ID_EMPTY); + datum_test(datum_stratum_coinbase_index(sdata, false) == 0); sdata->cur_stratum_job = &job; sdata->full_coinbase_ready = true; - datum_test(datum_stratum_coinbase_index(sdata, &miner, false) == 0); + datum_test(datum_stratum_coinbase_index(sdata, false) == 0); job.job_state = JOB_STATE_FULL_PRIORITY_WAIT_COINBASER; - datum_test(datum_stratum_coinbase_index(sdata, &miner, false) == 3); - miner.coinbase_selection = MAX_COINBASE_TYPES; - datum_test(datum_stratum_coinbase_index(sdata, &miner, false) == 0); + // With the job state at or above JOB_STATE_FULL_PRIORITY_WAIT_COINBASER and + // full_coinbase_ready set, every miner is served the largest class: there + // is no per-miner selection on BLAKE2b work. + datum_test(datum_stratum_coinbase_index(sdata, false) == COINBASE_TYPE_YUGE); + sdata->full_coinbase_ready = false; + datum_test(datum_stratum_coinbase_index(sdata, false) == 0); free(sdata); } diff --git a/www/config.html b/www/config.html index 358e2d3b..dbc6b64c 100644 --- a/www/config.html +++ b/www/config.html @@ -174,13 +174,7 @@

Pool

Advanced

- -
-
- - -
-
+
TID/CID RemHost Auth Username Subbed Last Accepted VDiff DiffA (A) DiffR (R) Hashrate (age) UserAgent Command
N/A%sUnknown"); sz += strncpy_html_escape(&output[sz], m->useragent, max_sz-1-sz); sz += snprintf(&output[sz], max_sz-1-sz, "Not SubscribedNot Subscribed