diff --git a/.gitignore b/.gitignore index c2e17c6..6200e5f 100644 --- a/.gitignore +++ b/.gitignore @@ -23,3 +23,4 @@ autom4te.cache config.* ii ar-lib +compile diff --git a/blkmaker.c b/blkmaker.c index f2c0b14..9d2e151 100644 --- a/blkmaker.c +++ b/blkmaker.c @@ -119,6 +119,11 @@ char varintEncode(unsigned char *out, uint64_t n) { return L; } +static uint8_t blkmk_varint_encode_size(const uint64_t n) { + uint8_t dummy[max_varint_size]; + return varintEncode(dummy, n); +} + static int16_t blkmk_count_sigops(const uint8_t * const script, const size_t scriptsz, const bool bip141) { int16_t sigops = 0; @@ -235,9 +240,10 @@ uint64_t blkmk_init_generation3(blktemplate_t * const tmpl, const void * const s memset(&data[off], 0, 4); // lock time off += 4; + const unsigned long pretx_size = libblkmaker_blkheader_size + blkmk_varint_encode_size(1 + tmpl->txncount); const int16_t sigops_counted = blkmk_count_sigops(script, scriptsz, tmpl->_bip141_sigops); const int64_t gentx_weight = blkmk_calc_gentx_weight(data, off); - if (tmpl->txns_datasz + off > tmpl->sizelimit + if (pretx_size + tmpl->txns_datasz + off > tmpl->sizelimit || (tmpl->txns_weight >= 0 && tmpl->txns_weight + gentx_weight > tmpl->weightlimit) || (tmpl->txns_sigops >= 0 && tmpl->txns_sigops + sigops_counted > tmpl->sigoplimit)) { free(data); @@ -320,35 +326,45 @@ bool blkmk_build_merkle_branches(blktemplate_t * const tmpl) } branches = malloc(branchcount * sizeof(*branches)); + if (!branches) { + return false; + } size_t hashcount = tmpl->txncount + 1; - unsigned char hashes[(hashcount + 1) * 32]; + libblkmaker_hash_t * const hashes = malloc((hashcount + 1) * sizeof(*hashes)); // +1 for when the last needs duplicating + if (!hashes) { + free(branches); + return false; + } for (i = 0; i < tmpl->txncount; ++i) { struct blktxn_t * const txn = &tmpl->txns[i]; txnhash_t * const txid = txn->txid ? txn->txid : txn->hash_; - memcpy(&hashes[0x20 * (i + 1)], txid, 0x20); + memcpy(&hashes[i + 1], txid, sizeof(*hashes)); } for (i = 0; i < branchcount; ++i) { - memcpy(&branches[i], &hashes[0x20], 0x20); + memcpy(&branches[i], &hashes[1], sizeof(*hashes)); if (hashcount % 2) { - memcpy(&hashes[32 * hashcount], &hashes[32 * (hashcount - 1)], 32); + memcpy(&hashes[hashcount], &hashes[hashcount - 1], sizeof(*hashes)); ++hashcount; } for (size_t i = 2; i < hashcount; i += 2) // This is where we overlap input and output, on the first pair - if (!dblsha256(&hashes[i / 2 * 32], &hashes[32 * i], 64)) + if (!dblsha256(&hashes[i / 2], &hashes[i], sizeof(*hashes) * 2)) { free(branches); + free(hashes); return false; } hashcount /= 2; } + free(hashes); + tmpl->_mrklbranch = branches; tmpl->_mrklbranchcount = branchcount; @@ -453,7 +469,8 @@ bool _blkmk_append_cb(blktemplate_t * const tmpl, void * const vout, const void if (in[cbScriptSigLen] > libblkmaker_coinbase_size_limit - appendsz) return false; - if (tmpl->cbtxn->datasz + tmpl->txns_datasz + appendsz > tmpl->sizelimit) { + const unsigned long pretx_size = libblkmaker_blkheader_size + blkmk_varint_encode_size(1 + tmpl->txncount); + if (pretx_size + tmpl->cbtxn->datasz + tmpl->txns_datasz + appendsz > tmpl->sizelimit) { return false; } @@ -514,9 +531,10 @@ ssize_t blkmk_append_coinbase_safe2(blktemplate_t * const tmpl, const void * con } size_t availsz = libblkmaker_coinbase_size_limit - extranoncesz - tmpl->cbtxn->data[cbScriptSigLen]; { - const size_t current_blocksize = tmpl->cbtxn->datasz + tmpl->txns_datasz; + const unsigned long pretx_size = libblkmaker_blkheader_size + blkmk_varint_encode_size(1 + tmpl->txncount); + const size_t current_blocksize = pretx_size + tmpl->cbtxn->datasz + tmpl->txns_datasz; if (current_blocksize > tmpl->sizelimit) { - return false; + return -4; } const size_t availsz2 = tmpl->sizelimit - current_blocksize; if (availsz2 < availsz) { @@ -684,6 +702,17 @@ size_t blkmk_get_data(blktemplate_t *tmpl, void *buf, size_t bufsz, time_t useti if (bufsz < 76) return 76; + if (tmpl->cbtxn->datasz > cbScriptSigLen && tmpl->cbtxn->data[cbScriptSigLen] + sizeof(*out_dataid) < libblkmaker_coinbase_size_minimum) { + // Add some padding + const size_t padding_required = libblkmaker_coinbase_size_minimum - (tmpl->cbtxn->data[cbScriptSigLen] + sizeof(*out_dataid)); + uint8_t padding[padding_required]; + static const uint8_t opcode_nop = '\x61'; + memset(padding, opcode_nop, padding_required); + if (padding_required != blkmk_append_coinbase_safe2(tmpl, padding, padding_required, 0, false)) { + return 0; + } + } + unsigned char *cbuf = buf; *out_dataid = tmpl->next_dataid++; @@ -701,6 +730,7 @@ bool blkmk_get_mdata(blktemplate_t * const tmpl, void * const buf, const size_t && tmpl->cbtxn && blkmk_build_merkle_branches(tmpl) && bufsz >= 76 + && (tmpl->mutations & (BMM_CBAPPEND | BMM_CBSET)) )) return false; @@ -708,6 +738,10 @@ bool blkmk_get_mdata(blktemplate_t * const tmpl, void * const buf, const size_t // Avoid overlapping with blkmk_get_data use ++extranoncesz; + if (tmpl->cbtxn->datasz > cbScriptSigLen && tmpl->cbtxn->data[cbScriptSigLen] + extranoncesz < libblkmaker_coinbase_size_minimum) { + extranoncesz = libblkmaker_coinbase_size_minimum - tmpl->cbtxn->data[cbScriptSigLen]; + } + void ** const out_branches = _out_branches; void ** const out_cbtxn = _out_cbtxn; unsigned char *cbuf = buf; @@ -758,9 +792,8 @@ unsigned long blkmk_work_left(const blktemplate_t *tmpl) { if (!tmpl->version) return 0; if (!(tmpl->mutations & (BMM_CBAPPEND | BMM_CBSET))) - return 1; + return (tmpl->next_dataid) ? 0 : 1; return UINT_MAX - tmpl->next_dataid; - return BLKMK_UNLIMITED_WORK_COUNT; } static char *blkmk_assemble_submission2_internal(blktemplate_t * const tmpl, const unsigned char * const data, const void * const extranonce, const size_t extranoncesz, blknonce_t nonce, const bool foreign) @@ -782,10 +815,11 @@ static char *blkmk_assemble_submission2_internal(blktemplate_t * const tmpl, con return NULL; } - memcpy(blk, data, 76); + const size_t header_before_nonce_sz = libblkmaker_blkheader_size - sizeof(nonce); + memcpy(blk, data, header_before_nonce_sz); nonce = htonl(nonce); - memcpy(&blk[76], &nonce, 4); - size_t offs = 80; + memcpy(&blk[header_before_nonce_sz], &nonce, sizeof(nonce)); + size_t offs = libblkmaker_blkheader_size; if (incl_gentxn) { offs += varintEncode(&blk[offs], 1 + tmpl->txncount); diff --git a/blkmaker_jansson.c b/blkmaker_jansson.c index ceedca1..b652ed1 100644 --- a/blkmaker_jansson.c +++ b/blkmaker_jansson.c @@ -135,6 +135,17 @@ json_t *blktmpl_request_jansson(const uint32_t caps, const char * const lpid) { tmpl->key = tmp; \ } while(0) +#define GETNUM_T(key, type) do { \ + GET(key, number); \ + const double tmpd = json_number_value(v); \ + const type tmp = tmpd; \ + /* This checks if it's merely being truncated, and tolerates it */ \ + if (tmpd != tmp && !((tmpd < 0) ? (tmpd < tmp && tmpd + 1 > tmp) : (tmpd > tmp && tmpd - 1 < tmp))) { \ + return "Invalid number value for '" #key "'"; \ + } \ + tmpl->key = tmp; \ +} while(0) + #define GETNUM_O2(key, skey, type) do { \ if ((v = json_object_get(json, #skey)) && json_is_number(v)) { \ const double tmpd = json_number_value(v); \ @@ -147,6 +158,17 @@ json_t *blktmpl_request_jansson(const uint32_t caps, const char * const lpid) { #define GETNUM_O(key, type) GETNUM_O2(key, key, type) +#define GETNUM_OT(key, type) do { \ + if ((v = json_object_get(json, #key)) && json_is_number(v)) { \ + const double tmpd = json_number_value(v); \ + const type tmp = tmpd; \ + /* This checks if it's merely being truncated, and tolerates it */ \ + if (tmpd == tmp || ((tmpd < 0) ? (tmpd < tmp && tmpd + 1 > tmp) : (tmpd > tmp && tmpd - 1 < tmp))) { \ + tmpl->key = tmp; \ + } \ + } \ +} while(0) + #define GETSTR(key, skey) do { \ if ((v = json_object_get(json, #key)) && json_is_string(v)) \ if (!(tmpl->skey = strdup(json_string_value(v)))) \ @@ -300,7 +322,7 @@ const char *blktmpl_add_jansson(blktemplate_t *tmpl, const json_t *json, time_t GETHEX(bits, diffbits); my_flip(tmpl->diffbits, 4); - GETNUM(curtime, blktime_t); + GETNUM_T(curtime, blktime_t); GETNUM(height, blkheight_t); GETHEX(previousblockhash, prevblk); my_flip(tmpl->prevblk, 32); @@ -377,7 +399,11 @@ const char *blktmpl_add_jansson(blktemplate_t *tmpl, const json_t *json, time_t v = json_object_get(json, "vbrequired"); if (v && json_is_number(v)) { - tmpl->vbrequired = json_number_value(v); + double tmpd = json_number_value(v); + tmpl->vbrequired = tmpd; + if (tmpl->vbrequired != tmpd) { + return "Unparsable vbrequired"; + } } } else @@ -394,11 +420,11 @@ const char *blktmpl_add_jansson(blktemplate_t *tmpl, const json_t *json, time_t GETSTR(workid, workid); - GETNUM_O(expires, int16_t); - GETNUM_O(maxtime, blktime_t); - GETNUM_O(maxtimeoff, blktime_diff_t); - GETNUM_O(mintime, blktime_t); - GETNUM_O(mintimeoff, blktime_diff_t); + GETNUM_OT(expires, int16_t); + GETNUM_OT(maxtime, blktime_t); + GETNUM_OT(maxtimeoff, blktime_diff_t); + GETNUM_OT(mintime, blktime_t); + GETNUM_OT(mintimeoff, blktime_diff_t); GETSTR(longpollid, lp.id); GETSTR(longpolluri, lp.uri); @@ -438,6 +464,8 @@ const char *blktmpl_add_jansson(blktemplate_t *tmpl, const json_t *json, time_t tmpl->cbtxn = calloc(1, sizeof(*tmpl->cbtxn)); if ((s = parse_txn(tmpl->cbtxn, v, 0))) return s; + } else if (!tmpl->cbvalue) { + return "Missing either coinbasetxn or coinbasevalue"; } if ((v = json_object_get(json, "coinbaseaux")) && json_is_object(v)) @@ -508,6 +536,7 @@ json_t *blktmpl_propose_jansson(blktemplate_t * const tmpl, const uint32_t caps, goto err; if (!(ja = json_string(blkhex))) goto err; + free(blkhex); if (json_object_set_new(jparams, "data", ja)) goto err; diff --git a/blktemplate.c b/blktemplate.c index db10f00..aedfa13 100644 --- a/blktemplate.c +++ b/blktemplate.c @@ -27,7 +27,7 @@ static const char *capnames[] = { "coinbase/append", "coinbase", - "generate", + "generation", "time/increment", "time/decrement", "transactions/add", @@ -50,10 +50,14 @@ const char *blktmpl_capabilityname(gbt_capabilities_t caps) { return NULL; } -gbt_capabilities_t blktmpl_getcapability(const char *n) { +uint32_t blktmpl_getcapability(const char *n) { for (unsigned int i = 0; i < GBT_CAPABILITY_COUNT; ++i) if (capnames[i] && !strcasecmp(n, capnames[i])) - return 1 << i; + return ((uint32_t)1) << i; + if (!strcasecmp(n, "time")) { + // multi-capability + return BMM_TIMEINC | BMM_TIMEDEC; + } if (!strcasecmp(n, "transactions")) return BMM_TXNADD; // Odd one as it's overloaded w/"transactions/add" per spec return 0; @@ -88,7 +92,6 @@ blktemplate_t *blktmpl_create() { tmpl->maxtime = 0xffffffff; tmpl->maxtimeoff = 0x7fff; tmpl->mintimeoff = -0x7fff; - tmpl->maxnonce = 0xffffffff; tmpl->expires = 0x7fff; return tmpl; diff --git a/blktemplate.h b/blktemplate.h index 5c19fc0..8bc5eb9 100644 --- a/blktemplate.h +++ b/blktemplate.h @@ -25,6 +25,7 @@ typedef int16_t blktime_diff_t; typedef uint32_t blknonce_t; #define libblkmaker_blkheader_size (80) +#define libblkmaker_coinbase_size_minimum (4) #define libblkmaker_coinbase_size_limit (100) struct blktxn_t { @@ -90,7 +91,8 @@ typedef enum { extern const char *blktmpl_capabilityname(gbt_capabilities_t); #define BLKTMPL_LONGEST_CAPABILITY_NAME (16) -extern gbt_capabilities_t blktmpl_getcapability(const char *); +// NOTE: blktmpl_getcapability("time") yields a combination of gbt_capabilities_t +extern uint32_t blktmpl_getcapability(const char *); typedef gbt_capabilities_t blkmutations_t; @@ -134,8 +136,6 @@ typedef struct { blktime_diff_t maxtimeoff; blktime_t mintime; blktime_diff_t mintimeoff; - blknonce_t minnonce; - blknonce_t maxnonce; // TEMPORARY HACK libblkmaker_hash_t *_mrklbranch;