diff --git a/README b/README index a8ad8e70..3f203750 100644 --- a/README +++ b/README @@ -286,6 +286,10 @@ from 2 to 8. Default 8 miners and is set to 30 seconds by default to help perpetuate transactions for the health of the bitcoin network. +"version_mask" : This is a mask of which bits in the version number it is valid +for a client to alter and is expressed as an hex string. Eg "7FFFFFFB" +Default is "00000000". + "serverurl" : This is the IP(s) to try to bind ckpool uniquely to, otherwise it will attempt to bind to all interfaces in port 3333 by default in pool mode and 3334 in proxy mode. Multiple entries can be specified as an array by diff --git a/ckpool.conf b/ckpool.conf index 465c5627..fa461022 100644 --- a/ckpool.conf +++ b/ckpool.conf @@ -20,6 +20,7 @@ "nonce1length" : 4, "nonce2length" : 8, "update_interval" : 30, +"version_mask" : "00000000", "serverurl" : [ "ckpool.org:3333", "node.ckpool.org:3333", diff --git a/src/ckpool.c b/src/ckpool.c index 784a6a1f..4e85521e 100644 --- a/src/ckpool.c +++ b/src/ckpool.c @@ -1444,8 +1444,8 @@ static void parse_config(ckpool_t *ckp) { json_t *json_conf, *arr_val; json_error_t err_val; + char *url, *vmask; int arr_size; - char *url; json_conf = json_load_file(ckp->config, JSON_DISABLE_EOF_CHECK, &err_val); if (!json_conf) { @@ -1469,6 +1469,9 @@ static void parse_config(ckpool_t *ckp) json_get_int(&ckp->nonce1length, json_conf, "nonce1length"); json_get_int(&ckp->nonce2length, json_conf, "nonce2length"); json_get_int(&ckp->update_interval, json_conf, "update_interval"); + json_get_string(&vmask, json_conf, "version_mask"); + if (vmask && strlen(vmask) && validhex(vmask)) + sscanf(vmask, "%x", &ckp->version_mask); /* Look for an array first and then a single entry */ arr_val = json_object_get(json_conf, "serverurl"); if (!parse_serverurls(ckp, arr_val)) { diff --git a/src/ckpool.h b/src/ckpool.h index dc8716d0..6a09e83d 100644 --- a/src/ckpool.h +++ b/src/ckpool.h @@ -255,6 +255,8 @@ struct ckpool_instance { int update_interval; // Seconds between stratum updates + uint32_t version_mask; // Bits which set to true means allow miner to modify those bits + /* Proxy options */ int proxies; char **proxyurl; @@ -295,6 +297,7 @@ enum stratum_msgtype { SM_SHAREERR, SM_WORKERSTATS, SM_REQTXNS, + SM_VERSIONMASK, SM_NONE }; diff --git a/src/libckpool.h b/src/libckpool.h index 306f1c6c..5d177c7d 100644 --- a/src/libckpool.h +++ b/src/libckpool.h @@ -267,7 +267,8 @@ enum share_err { SE_STALE, SE_NTIME_INVALID, SE_DUPE, - SE_HIGH_DIFF + SE_HIGH_DIFF, + SE_INVALID_VERSION_MASK }; static const char __maybe_unused *share_errs[] = { @@ -285,7 +286,8 @@ static const char __maybe_unused *share_errs[] = { "Stale", "Ntime out of range", "Duplicate", - "Above target" + "Above target", + "Invalid version mask" }; #define SHARE_ERR(x) share_errs[((x) + 9)] diff --git a/src/stratifier.c b/src/stratifier.c index 2e91dda7..d1ba67e0 100644 --- a/src/stratifier.c +++ b/src/stratifier.c @@ -1862,7 +1862,8 @@ static void add_node_base(ckpool_t *ckp, json_t *val, bool trusted, int64_t clie /* Calculate share diff and fill in hash and swap. Need to hold workbase read count */ static double share_diff(char *coinbase, const uchar *enonce1bin, const workbase_t *wb, const char *nonce2, - const uint32_t ntime32, const char *nonce, uchar *hash, uchar *swap, int *cblen) + const uint32_t ntime32, const uint32_t version_mask, const char *nonce, + uchar *hash, uchar *swap, int *cblen) { unsigned char merkle_root[32], merkle_sha[64]; uint32_t *data32, *swap32, benonce32; @@ -1894,6 +1895,17 @@ share_diff(char *coinbase, const uchar *enonce1bin, const workbase_t *wb, const memcpy(data, wb->headerbin, 80); memcpy(data + 36, merkle_root, 32); + /* Update nVersion when version_mask is in use */ + if (version_mask) { + uint32_t version; + + data32 = (uint32_t *)data; + version = be32toh(*data32); + version |= version_mask; + LOGDEBUG("Vmask %u version changed to %08x", version_mask, version); + *data32 = htobe32(version); + } + /* Insert the nonce value into the data */ hex2bin(&benonce32, nonce, 4); data32 = (uint32_t *)(data + 64 + 12); @@ -1972,8 +1984,8 @@ static void send_nodes_block(sdata_t *sdata, const json_t *block_val, const int6 /* Entered with workbase readcount. */ static void send_node_block(ckpool_t *ckp, sdata_t *sdata, const char *enonce1, const char *nonce, - const char *nonce2, const uint32_t ntime32, const int64_t jobid, - const double diff, const int64_t client_id, + const char *nonce2, const uint32_t ntime32, const uint32_t version_mask, + const int64_t jobid, const double diff, const int64_t client_id, const char *coinbase, const int cblen, const uchar *data) { if (sdata->node_instances) { @@ -1983,6 +1995,7 @@ static void send_node_block(ckpool_t *ckp, sdata_t *sdata, const char *enonce1, json_set_string(val, "nonce", nonce); json_set_string(val, "nonce2", nonce2); json_set_uint32(val, "ntime32", ntime32); + json_set_uint32(val, "version_mask", version_mask); json_set_int64(val, "jobid", jobid); json_set_double(val, "diff", diff); add_remote_blockdata(ckp, val, cblen, coinbase, data); @@ -2110,11 +2123,11 @@ static void submit_node_block(ckpool_t *ckp, sdata_t *sdata, json_t *val) char *coinbase = NULL, *enonce1 = NULL, *nonce = NULL, *nonce2 = NULL, *gbt_block, *coinbasehex, *swaphex; uchar *enonce1bin = NULL, hash[32], swap[80], flip32[32]; + uint32_t ntime32, version_mask = 0; char blockhash[68], cdfield[64]; json_t *bval, *bval_copy; int enonce1len, cblen; workbase_t *wb = NULL; - uint32_t ntime32; double diff; ts_t ts_now; int64_t id; @@ -2145,6 +2158,11 @@ static void submit_node_block(ckpool_t *ckp, sdata_t *sdata, json_t *val) goto out; } + if (!json_get_uint32(&version_mask, val, "version_mask")) { + /* No version mask is not fatal, assume it to be zero */ + LOGINFO("No version mask in node method block"); + } + LOGWARNING("Possible upstream block solve diff %lf !", diff); ts_realtime(&ts_now); @@ -2176,20 +2194,21 @@ static void submit_node_block(ckpool_t *ckp, sdata_t *sdata, json_t *val) hex2bin(enonce1bin, enonce1, enonce1len); coinbase = alloca(wb->coinb1len + wb->enonce1constlen + wb->enonce1varlen + wb->enonce2varlen + wb->coinb2len); /* Fill in the hashes */ - share_diff(coinbase, enonce1bin, wb, nonce2, ntime32, nonce, hash, swap, &cblen); + share_diff(coinbase, enonce1bin, wb, nonce2, ntime32, version_mask, nonce, hash, swap, &cblen); } /* Now we have enough to assemble a block */ gbt_block = process_block(wb, coinbase, cblen, swap, hash, flip32, blockhash); ret = local_block_submit(ckp, gbt_block, flip32, wb->height); - JSON_CPACK(bval, "{si,ss,ss,sI,ss,ss,ss,sI,sf,ss,ss,ss,ss}", + JSON_CPACK(bval, "{si,ss,ss,sI,ss,ss,si,ss,sI,sf,ss,ss,ss,ss}", "height", wb->height, "blockhash", blockhash, "confirmed", "n", "workinfoid", wb->id, "enonce1", enonce1, "nonce2", nonce2, + "version_mask", version_mask, "nonce", nonce, "reward", wb->coinbasevalue, "diff", diff, @@ -5691,6 +5710,18 @@ static void stratum_send_diff(sdata_t *sdata, const stratum_instance_t *client) stratum_add_send(sdata, json_msg, client->id, SM_DIFF); } +/* Needs to be entered with client holding a ref count. */ +static void stratum_send_version_mask(sdata_t *sdata, const stratum_instance_t *client) +{ + char version_str[12]; + json_t *json_msg; + + sprintf(version_str, "%08x", client->ckp->version_mask); + JSON_CPACK(json_msg, "{s[s]soss}", "params", version_str, "id", json_null(), + "method", "mining.set_version_mask"); + stratum_add_send(sdata, json_msg, client->id, SM_VERSIONMASK); +} + /* Needs to be entered with client holding a ref count. */ static void stratum_send_message(sdata_t *sdata, const stratum_instance_t *client, const char *msg) { @@ -5864,7 +5895,8 @@ downstream_block(ckpool_t *ckp, sdata_t *sdata, const json_t *val, const int cbl static void test_blocksolve(const stratum_instance_t *client, const workbase_t *wb, const uchar *data, const uchar *hash, const double diff, const char *coinbase, int cblen, - const char *nonce2, const char *nonce, const uint32_t ntime32, const bool stale) + const char *nonce2, const char *nonce, const uint32_t ntime32, const uint32_t version_mask, + const bool stale) { char blockhash[68], cdfield[64], *gbt_block; sdata_t *sdata = client->sdata; @@ -5887,8 +5919,8 @@ test_blocksolve(const stratum_instance_t *client, const workbase_t *wb, const uc sprintf(cdfield, "%lu,%lu", ts_now.tv_sec, ts_now.tv_nsec); gbt_block = process_block(wb, coinbase, cblen, data, hash, flip32, blockhash); - send_node_block(ckp, sdata, client->enonce1, nonce, nonce2, ntime32, wb->id, - diff, client->id, coinbase, cblen, data); + send_node_block(ckp, sdata, client->enonce1, nonce, nonce2, ntime32, version_mask, + wb->id, diff, client->id, coinbase, cblen, data); val = json_object(); json_set_int(val, "height", wb->height); @@ -5905,6 +5937,7 @@ test_blocksolve(const stratum_instance_t *client, const workbase_t *wb, const uc json_set_string(val, "nonce2", nonce2); json_set_string(val, "nonce", nonce); json_set_uint32(val, "ntime32", ntime32); + json_set_uint32(val, "version_mask", version_mask); json_set_int64(val, "reward", wb->coinbasevalue); json_set_double(val, "diff", diff); json_set_string(val, "createdate", cdfield); @@ -5934,7 +5967,8 @@ test_blocksolve(const stratum_instance_t *client, const workbase_t *wb, const uc /* Needs to be entered with workbase readcount and client holding a ref count. */ static double submission_diff(const stratum_instance_t *client, const workbase_t *wb, const char *nonce2, - const uint32_t ntime32, const char *nonce, uchar *hash, const bool stale) + const uint32_t ntime32, const uint32_t version_mask, + const char *nonce, uchar *hash, const bool stale) { char *coinbase; uchar swap[80]; @@ -5944,10 +5978,10 @@ static double submission_diff(const stratum_instance_t *client, const workbase_t coinbase = ckalloc(wb->coinb1len + wb->enonce1constlen + wb->enonce1varlen + wb->enonce2varlen + wb->coinb2len); /* Calculate the diff of the share here */ - ret = share_diff(coinbase, client->enonce1bin, wb, nonce2, ntime32, nonce, hash, swap, &cblen); + ret = share_diff(coinbase, client->enonce1bin, wb, nonce2, ntime32, version_mask, nonce, hash, swap, &cblen); /* Test we haven't solved a block regardless of share status */ - test_blocksolve(client, wb, swap, hash, ret, coinbase, cblen, nonce2, nonce, ntime32, stale); + test_blocksolve(client, wb, swap, hash, ret, coinbase, cblen, nonce2, nonce, ntime32, version_mask, stale); free(coinbase); @@ -6022,17 +6056,17 @@ static json_t *parse_submit(stratum_instance_t *client, json_t *json_msg, const json_t *params_val, json_t **err_val) { bool share = false, result = false, invalid = true, submit = false, stale = false; + const char *workername, *job_id, *ntime, *nonce, *version_mask; double diff = client->diff, wdiff = 0, sdiff = -1; char hexhash[68] = {}, sharehash[32], cdfield[64]; - const char *workername, *job_id, *ntime, *nonce; user_instance_t *user = client->user_instance; + uint32_t ntime32, version_mask32 = 0; char *fname = NULL, *s, *nonce2; sdata_t *sdata = client->sdata; enum share_err err = SE_NONE; ckpool_t *ckp = client->ckp; char idstring[20] = {}; workbase_t *wb = NULL; - uint32_t ntime32; uchar hash[32]; int nlen, len; time_t now_t; @@ -6085,6 +6119,18 @@ static json_t *parse_submit(stratum_instance_t *client, json_t *json_msg, *err_val = JSON_ERR(err); goto out; } + + version_mask = json_string_value(json_array_get(params_val, 5)); + if (version_mask && strlen(version_mask) && validhex(version_mask)) { + sscanf(version_mask, "%x", &version_mask32); + // check version mask + if (version_mask32 && ((~ckp->version_mask) & version_mask32) != 0) { + // means client changed some bits which server doesn't allow to change + err = SE_INVALID_VERSION_MASK; + *err_val = JSON_ERR(err); + goto out; + } + } if (safecmp(workername, client->workername)) { err = SE_WORKER_MISMATCH; *err_val = JSON_ERR(err); @@ -6126,7 +6172,7 @@ static json_t *parse_submit(stratum_instance_t *client, json_t *json_msg, } if (id < sdata->blockchange_id) stale = true; - sdiff = submission_diff(client, wb, nonce2, ntime32, nonce, hash, stale); + sdiff = submission_diff(client, wb, nonce2, ntime32, version_mask32, nonce, hash, stale); if (sdiff > client->best_diff) { worker_instance_t *worker = client->worker_instance; @@ -7680,13 +7726,17 @@ static void sauth_process(ckpool_t *ckp, json_params_t *jp) mindiff = client->suggest_diff; else mindiff = client->worker_instance->mindiff; - if (!mindiff) - goto out; - mindiff = MAX(ckp->mindiff, mindiff); - if (mindiff != client->diff) { - client->diff = mindiff; - stratum_send_diff(sdata, client); + if (mindiff) { + mindiff = MAX(ckp->mindiff, mindiff); + if (mindiff != client->diff) { + client->diff = mindiff; + stratum_send_diff(sdata, client); + } } + + /* Set block version mask if needed */ + if (ckp->version_mask) + stratum_send_version_mask(sdata, client); out: dec_instance_ref(sdata, client); out_noclient: