From f642c515dfdfda80b9a4ff8ece71ab4b0f7afd56 Mon Sep 17 00:00:00 2001 From: Con Kolivas Date: Wed, 10 Jan 2018 13:38:02 +1100 Subject: [PATCH 01/19] Support virtual mask changing of the version bits by clients according to a hex config parameter version_mask. --- README | 4 +++ ckpool.conf | 1 + src/ckpool.c | 5 ++- src/ckpool.h | 3 ++ src/libckpool.h | 6 ++-- src/stratifier.c | 92 +++++++++++++++++++++++++++++++++++++----------- 6 files changed, 87 insertions(+), 24 deletions(-) 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: From 85fb80493d4b898a9cd46b3db5e78beddf9a1275 Mon Sep 17 00:00:00 2001 From: Con Kolivas Date: Wed, 10 Jan 2018 18:14:26 +1100 Subject: [PATCH 02/19] Simplify endian correction of version_mask. --- src/stratifier.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/stratifier.c b/src/stratifier.c index d1ba67e0..e579ea26 100644 --- a/src/stratifier.c +++ b/src/stratifier.c @@ -1862,7 +1862,7 @@ 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 uint32_t version_mask, const char *nonce, + const uint32_t ntime32, uint32_t version_mask, const char *nonce, uchar *hash, uchar *swap, int *cblen) { unsigned char merkle_root[32], merkle_sha[64]; @@ -1897,13 +1897,9 @@ share_diff(char *coinbase, const uchar *enonce1bin, const workbase_t *wb, const /* Update nVersion when version_mask is in use */ if (version_mask) { - uint32_t version; - + version_mask = htobe32(version_mask); data32 = (uint32_t *)data; - version = be32toh(*data32); - version |= version_mask; - LOGDEBUG("Vmask %u version changed to %08x", version_mask, version); - *data32 = htobe32(version); + *data32 |= version_mask; } /* Insert the nonce value into the data */ From ec851fc7c48eb355f200f5b1ac4b949e37d18ede Mon Sep 17 00:00:00 2001 From: Con Kolivas Date: Mon, 5 Feb 2018 21:33:45 +1100 Subject: [PATCH 03/19] Support mining.configure mechanism for setting vmask. --- src/ckpool.h | 3 ++- src/stratifier.c | 32 +++++++++++++++++--------------- 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/src/ckpool.h b/src/ckpool.h index 6a09e83d..e998816f 100644 --- a/src/ckpool.h +++ b/src/ckpool.h @@ -297,7 +297,7 @@ enum stratum_msgtype { SM_SHAREERR, SM_WORKERSTATS, SM_REQTXNS, - SM_VERSIONMASK, + SM_CONFIGURE, SM_NONE }; @@ -324,6 +324,7 @@ static const char __maybe_unused *stratum_msgs[] = { "shareerr", "workerstats", "reqtxns", + "mining.configure", "" }; diff --git a/src/stratifier.c b/src/stratifier.c index e579ea26..796edb15 100644 --- a/src/stratifier.c +++ b/src/stratifier.c @@ -5706,18 +5706,6 @@ 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) { @@ -6678,6 +6666,23 @@ static void parse_method(ckpool_t *ckp, sdata_t *sdata, stratum_instance_t *clie return; } + if (cmdmatch(method, "mining.configure")) { + json_t *val, *result_val; + char version_str[12]; + + LOGINFO("Mining configure requested from %s %s", client->identity, + client->address); + sprintf(version_str, "%08x", ckp->version_mask); + val = json_object(); + JSON_CPACK(result_val, "{sbss}", "version-rolling", json_true(), + "version-rolling.mask", version_str); + json_object_set_new_nocheck(val, "result", result_val); + json_object_set_nocheck(val, "id", id_val); + json_object_set_new_nocheck(val, "error", json_null()); + stratum_add_send(sdata, val, client_id, SM_CONFIGURE); + return; + } + /* We should only accept requests from subscribed and authed users here * on */ if (!client->subscribed) { @@ -7730,9 +7735,6 @@ static void sauth_process(ckpool_t *ckp, json_params_t *jp) } } - /* Set block version mask if needed */ - if (ckp->version_mask) - stratum_send_version_mask(sdata, client); out: dec_instance_ref(sdata, client); out_noclient: From 9610e94e592eae4a725f54027e1f2324faf5eb67 Mon Sep 17 00:00:00 2001 From: Con Kolivas Date: Tue, 6 Feb 2018 09:35:48 +1100 Subject: [PATCH 04/19] Use the RPC output from bitcoind to determine if an address is a script or a segwit bech32 address, currently refusing to mine to a bech32 address. --- src/bitcoin.c | 51 +++++++++++++++++++++--------------------------- src/bitcoin.h | 4 ++-- src/ckpool.h | 4 ++++ src/generator.c | 13 ++++-------- src/generator.h | 4 ++-- src/libckpool.c | 12 ++++++------ src/stratifier.c | 31 ++++++++++++++++------------- 7 files changed, 57 insertions(+), 62 deletions(-) diff --git a/src/bitcoin.c b/src/bitcoin.c index 4381270f..646ad472 100644 --- a/src/bitcoin.c +++ b/src/bitcoin.c @@ -1,5 +1,5 @@ /* - * Copyright 2014-2017 Con Kolivas + * Copyright 2014-2018 Con Kolivas * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -16,7 +16,6 @@ #include "bitcoin.h" #include "stratifier.h" -static const char *b58chars = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; static char* understood_rules[] = {"segwit"}; static bool check_required_rule(const char* rule) @@ -32,37 +31,16 @@ static bool check_required_rule(const char* rule) /* Take a bitcoin address and do some sanity checks on it, then send it to * bitcoind to see if it's a valid address */ -bool validate_address(connsock_t *cs, const char *address) +bool validate_address(connsock_t *cs, const char *address, bool *script, bool *segwit) { - json_t *val, *res_val, *valid_val; + json_t *val, *res_val, *valid_val, *tmp_val; char rpc_req[128]; bool ret = false; - int len, i, j; if (unlikely(!address)) { LOGWARNING("Null address passed to validate_address"); return ret; } - len = strlen(address); - if (len < 27 || len > 36) { - LOGWARNING("Invalid address length %d passed to validate_address", len); - return ret; - } - for (i = 0; i < len; i++) { - char c = address[i]; - bool found = false; - - for (j = 0; j < 58; j++) { - if (c == b58chars[j]) { - found = true; - break; - } - } - if (!found) { - LOGNOTICE("Invalid char %.1s passed to validate_address", &c); - return ret; - } - } snprintf(rpc_req, 128, "{\"method\": \"validateaddress\", \"params\": [\"%s\"]}\n", address); val = json_rpc_call(cs, rpc_req); @@ -80,12 +58,27 @@ bool validate_address(connsock_t *cs, const char *address) LOGERR("Failed to get isvalid json response to validate_address"); goto out; } - if (!json_is_true(valid_val)) + if (!json_is_true(valid_val)) { LOGDEBUG("Bitcoin address %s is NOT valid", address); - else { - LOGDEBUG("Bitcoin address %s IS valid", address); - ret = true; + goto out; } + ret = true; + tmp_val = json_object_get(res_val, "isscript"); + if (unlikely(!tmp_val)) { + /* All recent bitcoinds should support this, if not, look for + * a 3x address to at least support it on mainnet */ + LOGDEBUG("No isscript support from bitcoind"); + if (address[0] == '3') + *script = true; + goto out; + } + *script = json_is_true(tmp_val); + tmp_val = json_object_get(res_val, "iswitness"); + if (unlikely(!tmp_val)) + goto out; + *segwit = json_is_true(tmp_val); + LOGDEBUG("Bitcoin address %s IS valid%s%s", address, *script ? " script" : "", + *segwit ? " segwit" : ""); out: if (val) json_decref(val); diff --git a/src/bitcoin.h b/src/bitcoin.h index 08f6bbe4..782a2073 100644 --- a/src/bitcoin.h +++ b/src/bitcoin.h @@ -1,5 +1,5 @@ /* - * Copyright 2014-2017 Con Kolivas + * Copyright 2014-2018 Con Kolivas * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -12,7 +12,7 @@ typedef struct genwork gbtbase_t; -bool validate_address(connsock_t *cs, const char *address); +bool validate_address(connsock_t *cs, const char *address, bool *script, bool *segwit); bool gen_gbtbase(connsock_t *cs, gbtbase_t *gbt); void clear_gbtbase(gbtbase_t *gbt); int get_blockcount(connsock_t *cs); diff --git a/src/ckpool.h b/src/ckpool.h index e998816f..b8681dd6 100644 --- a/src/ckpool.h +++ b/src/ckpool.h @@ -241,8 +241,12 @@ struct ckpool_instance { /* Coinbase data */ char *btcaddress; // Address to mine to + bool script; // Address is a script address + bool segwit; // Address is a segwit address char *btcsig; // Optional signature to add to coinbase char *donaddress; // Donation address + bool donscript; // Donation is a script + bool donsegwit; // Donation is segwit bool donvalid; // Donation address works on this network /* Stratum options */ diff --git a/src/generator.c b/src/generator.c index 4d7a1947..61cf4464 100644 --- a/src/generator.c +++ b/src/generator.c @@ -253,7 +253,7 @@ static bool server_alive(ckpool_t *ckp, server_instance_t *si, bool pinging) goto out; } clear_gbtbase(&gbt); - if (!ckp->node && !validate_address(cs, ckp->btcaddress)) { + if (!ckp->node && !validate_address(cs, ckp->btcaddress, &ckp->script, &ckp->segwit)) { LOGWARNING("Invalid btcaddress: %s !", ckp->btcaddress); goto out; } @@ -473,11 +473,6 @@ retry: memset(buf + 12 + 64, 0, 1); sprintf(blockmsg, "%sblock:%s", ret ? "" : "no", buf + 12); send_proc(ckp->stratifier, blockmsg); - } else if (cmdmatch(buf, "checkaddr:")) { - if (validate_address(cs, buf + 10)) - send_unix_msg(umsg->sockd, "true"); - else - send_unix_msg(umsg->sockd, "false"); } else if (cmdmatch(buf, "reconnect")) { goto reconnect; } else if (cmdmatch(buf, "loglevel")) { @@ -904,7 +899,7 @@ out: return ret; } -bool generator_checkaddr(ckpool_t *ckp, const char *addr) +bool generator_checkaddr(ckpool_t *ckp, const char *addr, bool *script, bool *segwit) { gdata_t *gdata = ckp->gdata; server_instance_t *si; @@ -917,7 +912,7 @@ bool generator_checkaddr(ckpool_t *ckp, const char *addr) goto out; } cs = &si->cs; - ret = validate_address(cs, addr); + ret = validate_address(cs, addr, script, segwit); out: return ret; } @@ -931,7 +926,7 @@ char *generator_get_txn(ckpool_t *ckp, const char *hash) si = gdata->current_si; if (unlikely(!si)) { - LOGWARNING("No live current server in generator_checkaddr"); + LOGWARNING("No live current server in generator_get_txn"); goto out; } cs = &si->cs; diff --git a/src/generator.h b/src/generator.h index 8b23f989..242466eb 100644 --- a/src/generator.h +++ b/src/generator.h @@ -1,5 +1,5 @@ /* - * Copyright 2014-2017 Con Kolivas + * Copyright 2014-2018 Con Kolivas * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -19,7 +19,7 @@ void generator_add_send(ckpool_t *ckp, json_t *val); struct genwork *generator_getbase(ckpool_t *ckp); int generator_getbest(ckpool_t *ckp, char *hash); -bool generator_checkaddr(ckpool_t *ckp, const char *addr); +bool generator_checkaddr(ckpool_t *ckp, const char *addr, bool *script, bool *segwit); char *generator_get_txn(ckpool_t *ckp, const char *hash); bool generator_submitblock(ckpool_t *ckp, const char *buf); void generator_preciousblock(ckpool_t *ckp, const char *hash); diff --git a/src/libckpool.c b/src/libckpool.c index bac71efa..694444ed 100644 --- a/src/libckpool.c +++ b/src/libckpool.c @@ -1,5 +1,5 @@ /* - * Copyright 2014-2016 Con Kolivas + * Copyright 2014-2018 Con Kolivas * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -1743,15 +1743,15 @@ void address_to_pubkeytxn(char *pkh, const char *addr) pkh[24] = 0xac; } -void address_to_scripttxn(char *pkh, const char *addr) +void address_to_scripttxn(char *psh, const char *addr) { char b58bin[25] = {}; b58tobin(b58bin, addr); - pkh[0] = 0xa9; - pkh[1] = 0x14; - memcpy(&pkh[2], &b58bin[1], 20); - pkh[22] = 0x87; + psh[0] = 0xa9; + psh[1] = 0x14; + memcpy(&psh[2], &b58bin[1], 20); + psh[22] = 0x87; } /* For encoding nHeight into coinbase, return how many bytes were used */ diff --git a/src/stratifier.c b/src/stratifier.c index 796edb15..2b9ff353 100644 --- a/src/stratifier.c +++ b/src/stratifier.c @@ -1,5 +1,5 @@ /* - * Copyright 2014-2017 Con Kolivas + * Copyright 2014-2018 Con Kolivas * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -113,6 +113,8 @@ struct user_instance { int id; char *secondaryuserid; bool btcaddress; + bool script; + bool segwit; /* A linked list of all connected instances of this user */ stratum_instance_t *clients; @@ -5328,7 +5330,9 @@ static user_instance_t *generate_user(ckpool_t *ckp, stratum_instance_t *client, /* Is this a btc address based username? */ if (!ckp->proxy && (new_user || !user->btcaddress) && (len > 26 && len < 35)) - user->btcaddress = generator_checkaddr(ckp, username); + user->btcaddress = generator_checkaddr(ckp, username, &user->script, &user->segwit); + if (user->segwit) /* Bech32 addresses currently unsupported */ + user->btcaddress = false; if (new_user) { LOGNOTICE("Added new user %s%s", username, user->btcaddress ? " as address based registration" : ""); @@ -6834,7 +6838,9 @@ static user_instance_t *generate_remote_user(ckpool_t *ckp, const char *workerna /* Is this a btc address based username? */ if (!ckp->proxy && (new_user || !user->btcaddress) && (len > 26 && len < 35)) - user->btcaddress = generator_checkaddr(ckp, username); + user->btcaddress = generator_checkaddr(ckp, username, &user->script, &user->segwit); + if (user->segwit) + user->btcaddress = false; if (new_user) { LOGNOTICE("Added new remote user %s%s", username, user->btcaddress ? " as address based registration" : ""); @@ -8605,13 +8611,6 @@ static void read_poolstats(ckpool_t *ckp, int *tvsec_diff) } } -/* Braindead check to see if this btcaddress is an M of N script address which - * is currently unsupported as a generation address. */ -static bool script_address(const char *btcaddress) -{ - return btcaddress[0] == '3'; -} - void *stratifier(void *arg) { proc_instance_t *pi = (proc_instance_t *)arg; @@ -8635,14 +8634,18 @@ void *stratifier(void *arg) cksleep_ms(10); if (!ckp->proxy) { - if (!generator_checkaddr(ckp, ckp->btcaddress)) { + if (!generator_checkaddr(ckp, ckp->btcaddress, &ckp->script, &ckp->segwit)) { LOGEMERG("Fatal: btcaddress invalid according to bitcoind"); goto out; } + if (ckp->segwit) { + LOGEMERG("Fatal: bech32 addresses not currently supported"); + goto out; + } /* Store this for use elsewhere */ hex2bin(scriptsig_header_bin, scriptsig_header, 41); - if (script_address(ckp->btcaddress)) { + if (ckp->script) { address_to_scripttxn(sdata->pubkeytxnbin, ckp->btcaddress); sdata->pubkeytxnlen = 23; } else { @@ -8650,9 +8653,9 @@ void *stratifier(void *arg) sdata->pubkeytxnlen = 25; } - if (generator_checkaddr(ckp, ckp->donaddress)) { + if (generator_checkaddr(ckp, ckp->donaddress, &ckp->donscript, &ckp->donsegwit)) { ckp->donvalid = true; - if (script_address(ckp->donaddress)) { + if (ckp->donscript) { sdata->donkeytxnlen = 23; address_to_scripttxn(sdata->donkeytxnbin, ckp->donaddress); } else { From f563d7298b1742b8ae179b735ced608decf5ec3f Mon Sep 17 00:00:00 2001 From: Con Kolivas Date: Tue, 6 Feb 2018 10:12:22 +1100 Subject: [PATCH 05/19] Choose the generation transaction type and return the transaction length within libckpool. --- src/ckpool.h | 2 +- src/libckpool.c | 22 ++++++++++++++++++++-- src/libckpool.h | 5 ++--- src/stratifier.c | 40 ++++++++++++++-------------------------- 4 files changed, 37 insertions(+), 32 deletions(-) diff --git a/src/ckpool.h b/src/ckpool.h index b8681dd6..51ab760a 100644 --- a/src/ckpool.h +++ b/src/ckpool.h @@ -1,5 +1,5 @@ /* - * Copyright 2014-2017 Con Kolivas + * Copyright 2014-2018 Con Kolivas * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/libckpool.c b/src/libckpool.c index 694444ed..e828c4a9 100644 --- a/src/libckpool.c +++ b/src/libckpool.c @@ -1730,7 +1730,9 @@ char *http_base64(const char *src) return (str); } -void address_to_pubkeytxn(char *pkh, const char *addr) +/* It's assumed that there is no chance of sending invalid chars to these + * functions as they should have been checked beforehand. */ +static void address_to_pubkeytxn(char *pkh, const char *addr) { char b58bin[25] = {}; @@ -1743,7 +1745,7 @@ void address_to_pubkeytxn(char *pkh, const char *addr) pkh[24] = 0xac; } -void address_to_scripttxn(char *psh, const char *addr) +static void address_to_scripttxn(char *psh, const char *addr) { char b58bin[25] = {}; @@ -1754,6 +1756,22 @@ void address_to_scripttxn(char *psh, const char *addr) psh[22] = 0x87; } +/* Convert an address to a transaction and return the length of the transaction */ +int address_to_txn(char *p2h, const char *addr, const bool script, const bool segwit) +{ + if (unlikely(segwit)) { + /* It should be impossible to hit this for now */ + LOGEMERG("Segwit bech32 address passed to address_to_txn while unsupported."); + return 0; + } + if (script) { + address_to_scripttxn(p2h, addr); + return 23; + } + address_to_pubkeytxn(p2h, addr); + return 25; +} + /* For encoding nHeight into coinbase, return how many bytes were used */ int ser_number(uchar *s, int32_t val) { diff --git a/src/libckpool.h b/src/libckpool.h index 5d177c7d..6479bf99 100644 --- a/src/libckpool.h +++ b/src/libckpool.h @@ -1,5 +1,5 @@ /* - * Copyright 2014-2017 Con Kolivas + * Copyright 2014-2018 Con Kolivas * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -563,8 +563,7 @@ void b58tobin(char *b58bin, const char *b58); int safecmp(const char *a, const char *b); bool cmdmatch(const char *buf, const char *cmd); -void address_to_pubkeytxn(char *pkh, const char *addr); -void address_to_scripttxn(char *pkh, const char *addr); +int address_to_txn(char *p2h, const char *addr, const bool script, const bool segwit); int ser_number(uchar *s, int32_t val); int get_sernumber(uchar *s); bool fulltest(const uchar *hash, const uchar *target); diff --git a/src/stratifier.c b/src/stratifier.c index 2b9ff353..6e3edf1b 100644 --- a/src/stratifier.c +++ b/src/stratifier.c @@ -389,10 +389,10 @@ static const char *ckdb_seq_names[] = { struct stratifier_data { ckpool_t *ckp; - char pubkeytxnbin[25]; - int pubkeytxnlen; - char donkeytxnbin[25]; - int donkeytxnlen; + char txnbin[40]; + int txnlen; + char dontxnbin[40]; + int dontxnlen; pool_stats_t stats; /* Protects changes to pool stats */ @@ -615,9 +615,9 @@ static void generate_coinbase(const ckpool_t *ckp, workbase_t *wb) *u64 = htole64(g64); wb->coinb2len += 8; - wb->coinb2bin[wb->coinb2len++] = sdata->pubkeytxnlen; - memcpy(wb->coinb2bin + wb->coinb2len, sdata->pubkeytxnbin, sdata->pubkeytxnlen); - wb->coinb2len += sdata->pubkeytxnlen; + wb->coinb2bin[wb->coinb2len++] = sdata->txnlen; + memcpy(wb->coinb2bin + wb->coinb2len, sdata->txnbin, sdata->txnlen); + wb->coinb2len += sdata->txnlen; if (wb->insert_witness) { // 0 value @@ -636,9 +636,9 @@ static void generate_coinbase(const ckpool_t *ckp, workbase_t *wb) *u64 = htole64(d64); wb->coinb2len += 8; - wb->coinb2bin[wb->coinb2len++] = sdata->donkeytxnlen; - memcpy(wb->coinb2bin + wb->coinb2len, sdata->donkeytxnbin, sdata->donkeytxnlen); - wb->coinb2len += sdata->donkeytxnlen; + wb->coinb2bin[wb->coinb2len++] = sdata->dontxnlen; + memcpy(wb->coinb2bin + wb->coinb2len, sdata->dontxnbin, sdata->dontxnlen); + wb->coinb2len += sdata->dontxnlen; } wb->coinb2len += 4; // Blank lock @@ -2369,8 +2369,8 @@ static sdata_t *duplicate_sdata(const sdata_t *sdata) dsdata->ckp = sdata->ckp; /* Copy the transaction binaries for workbase creation */ - memcpy(dsdata->pubkeytxnbin, sdata->pubkeytxnbin, 25); - memcpy(dsdata->donkeytxnbin, sdata->donkeytxnbin, 25); + memcpy(dsdata->txnbin, sdata->txnbin, 40); + memcpy(dsdata->dontxnbin, sdata->dontxnbin, 40); /* Use the same work queues for all subproxies */ dsdata->ssends = sdata->ssends; @@ -8645,23 +8645,11 @@ void *stratifier(void *arg) /* Store this for use elsewhere */ hex2bin(scriptsig_header_bin, scriptsig_header, 41); - if (ckp->script) { - address_to_scripttxn(sdata->pubkeytxnbin, ckp->btcaddress); - sdata->pubkeytxnlen = 23; - } else { - address_to_pubkeytxn(sdata->pubkeytxnbin, ckp->btcaddress); - sdata->pubkeytxnlen = 25; - } + sdata->txnlen = address_to_txn(sdata->txnbin, ckp->btcaddress, ckp->script, ckp->segwit); if (generator_checkaddr(ckp, ckp->donaddress, &ckp->donscript, &ckp->donsegwit)) { ckp->donvalid = true; - if (ckp->donscript) { - sdata->donkeytxnlen = 23; - address_to_scripttxn(sdata->donkeytxnbin, ckp->donaddress); - } else { - sdata->donkeytxnlen = 25; - address_to_pubkeytxn(sdata->donkeytxnbin, ckp->donaddress); - } + sdata->dontxnlen = address_to_txn(sdata->dontxnbin, ckp->donaddress, ckp->script, ckp->segwit); } } From aab51ae9b4c896af7f8810e555960b223a01f4ee Mon Sep 17 00:00:00 2001 From: Con Kolivas Date: Tue, 6 Feb 2018 12:29:57 +1100 Subject: [PATCH 06/19] Implement native bech32 segwit address support. --- src/libckpool.c | 83 ++++++++++++++++++++++++++++++++++++++++-------- src/stratifier.c | 14 ++------ 2 files changed, 73 insertions(+), 24 deletions(-) diff --git a/src/libckpool.c b/src/libckpool.c index e828c4a9..21f77753 100644 --- a/src/libckpool.c +++ b/src/libckpool.c @@ -1730,9 +1730,54 @@ char *http_base64(const char *src) return (str); } +static const int8_t charset_rev[128] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 15, -1, 10, 17, 21, 20, 26, 30, 7, 5, -1, -1, -1, -1, -1, -1, + -1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1, + 1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1, + -1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1, + 1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1 +}; + /* It's assumed that there is no chance of sending invalid chars to these * functions as they should have been checked beforehand. */ -static void address_to_pubkeytxn(char *pkh, const char *addr) +static void bech32_decode(uint8_t *data, int *data_len, const char *input) +{ + int input_len = strlen(input), hrp_len, i; + + *data_len = 0; + while (*data_len < input_len && input[(input_len - 1) - *data_len] != '1') + ++(*data_len); + hrp_len = input_len - (1 + *data_len); + *(data_len) -= 6; + for (i = hrp_len + 1; i < input_len; i++) { + int v = (input[i] & 0x80) ? -1 : charset_rev[(int)input[i]]; + + if (i + 6 < input_len) + data[i - (1 + hrp_len)] = v; + } +} + +static void convert_bits(char *out, int *outlen, const uint8_t *in, + int inlen) +{ + const int outbits = 8, inbits = 5; + uint32_t val = 0, maxv = (((uint32_t)1) << outbits) - 1; + int bits = 0; + + while (inlen--) { + val = (val << inbits) | *(in++); + bits += inbits; + while (bits >= outbits) { + bits -= outbits; + out[(*outlen)++] = (val >> bits) & maxv; + } + } +} + +static int address_to_pubkeytxn(char *pkh, const char *addr) { char b58bin[25] = {}; @@ -1743,9 +1788,10 @@ static void address_to_pubkeytxn(char *pkh, const char *addr) memcpy(&pkh[3], &b58bin[1], 20); pkh[23] = 0x88; pkh[24] = 0xac; + return 25; } -static void address_to_scripttxn(char *psh, const char *addr) +static int address_to_scripttxn(char *psh, const char *addr) { char b58bin[25] = {}; @@ -1754,22 +1800,33 @@ static void address_to_scripttxn(char *psh, const char *addr) psh[1] = 0x14; memcpy(&psh[2], &b58bin[1], 20); psh[22] = 0x87; + return 23; +} + +static int segaddress_to_txn(char *p2h, const char *addr) +{ + int data_len, witdata_len = 0; + char *witdata = &p2h[2]; + uint8_t data[84]; + + bech32_decode(data, &data_len, addr); + p2h[0] = data[0]; + /* Witness version is > 0 */ + if (p2h[0]) + p2h[0] += 0x50; + convert_bits(witdata, &witdata_len, data + 1, data_len - 1); + p2h[1] = witdata_len; + return witdata_len + 2; } /* Convert an address to a transaction and return the length of the transaction */ int address_to_txn(char *p2h, const char *addr, const bool script, const bool segwit) { - if (unlikely(segwit)) { - /* It should be impossible to hit this for now */ - LOGEMERG("Segwit bech32 address passed to address_to_txn while unsupported."); - return 0; - } - if (script) { - address_to_scripttxn(p2h, addr); - return 23; - } - address_to_pubkeytxn(p2h, addr); - return 25; + if (segwit) + return segaddress_to_txn(p2h, addr); + if (script) + return address_to_scripttxn(p2h, addr); + return address_to_pubkeytxn(p2h, addr); } /* For encoding nHeight into coinbase, return how many bytes were used */ diff --git a/src/stratifier.c b/src/stratifier.c index 6e3edf1b..ca4fa258 100644 --- a/src/stratifier.c +++ b/src/stratifier.c @@ -389,9 +389,9 @@ static const char *ckdb_seq_names[] = { struct stratifier_data { ckpool_t *ckp; - char txnbin[40]; + char txnbin[48]; int txnlen; - char dontxnbin[40]; + char dontxnbin[48]; int dontxnlen; pool_stats_t stats; @@ -579,7 +579,7 @@ static void generate_coinbase(const ckpool_t *ckp, workbase_t *wb) len += wb->enonce1varlen; len += wb->enonce2varlen; - wb->coinb2bin = ckzalloc(256); + wb->coinb2bin = ckzalloc(512); memcpy(wb->coinb2bin, "\x0a\x63\x6b\x70\x6f\x6f\x6c", 7); wb->coinb2len = 7; if (ckp->btcsig) { @@ -5331,8 +5331,6 @@ static user_instance_t *generate_user(ckpool_t *ckp, stratum_instance_t *client, /* Is this a btc address based username? */ if (!ckp->proxy && (new_user || !user->btcaddress) && (len > 26 && len < 35)) user->btcaddress = generator_checkaddr(ckp, username, &user->script, &user->segwit); - if (user->segwit) /* Bech32 addresses currently unsupported */ - user->btcaddress = false; if (new_user) { LOGNOTICE("Added new user %s%s", username, user->btcaddress ? " as address based registration" : ""); @@ -6839,8 +6837,6 @@ static user_instance_t *generate_remote_user(ckpool_t *ckp, const char *workerna /* Is this a btc address based username? */ if (!ckp->proxy && (new_user || !user->btcaddress) && (len > 26 && len < 35)) user->btcaddress = generator_checkaddr(ckp, username, &user->script, &user->segwit); - if (user->segwit) - user->btcaddress = false; if (new_user) { LOGNOTICE("Added new remote user %s%s", username, user->btcaddress ? " as address based registration" : ""); @@ -8638,10 +8634,6 @@ void *stratifier(void *arg) LOGEMERG("Fatal: btcaddress invalid according to bitcoind"); goto out; } - if (ckp->segwit) { - LOGEMERG("Fatal: bech32 addresses not currently supported"); - goto out; - } /* Store this for use elsewhere */ hex2bin(scriptsig_header_bin, scriptsig_header, 41); From 2650ac756eb4a2b38e5b714cf71a85aa2c60bbc8 Mon Sep 17 00:00:00 2001 From: Con Kolivas Date: Tue, 6 Feb 2018 14:36:33 +1100 Subject: [PATCH 07/19] Request mining.configure information from upstream pools and store the value per proxy. --- src/ckpool.c | 6 ++--- src/ckpool.h | 2 ++ src/generator.c | 61 ++++++++++++++++++++++++++++++++++++++++++++++++ src/stratifier.c | 25 ++++++++++++++++++++ src/stratifier.h | 3 ++- 5 files changed, 93 insertions(+), 4 deletions(-) diff --git a/src/ckpool.c b/src/ckpool.c index 4e85521e..be13551e 100644 --- a/src/ckpool.c +++ b/src/ckpool.c @@ -1,5 +1,5 @@ /* - * Copyright 2014-2017 Con Kolivas + * Copyright 2014-2018 Con Kolivas * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -980,7 +980,7 @@ bool send_json_msg(connsock_t *cs, const json_t *json_msg) /* Decode a string that should have a json message and return just the contents * of the result key or NULL. */ -static json_t *json_result(json_t *val) +json_t *json_result(json_t *val) { json_t *res_val = NULL, *err_val; @@ -1005,7 +1005,7 @@ static json_t *json_result(json_t *val) } /* Return the error value if one exists */ -static json_t *json_errval(json_t *val) +json_t *json_errval(json_t *val) { json_t *err_val = json_object_get(val, "error"); diff --git a/src/ckpool.h b/src/ckpool.h index 51ab760a..e519176e 100644 --- a/src/ckpool.h +++ b/src/ckpool.h @@ -371,6 +371,8 @@ json_t *json_rpc_call(connsock_t *cs, const char *rpc_req); json_t *json_rpc_response(connsock_t *cs, const char *rpc_req); void json_rpc_msg(connsock_t *cs, const char *rpc_req); bool send_json_msg(connsock_t *cs, const json_t *json_msg); +json_t *json_result(json_t *val); +json_t *json_errval(json_t *val); json_t *json_msg_result(const char *msg, json_t **res_val, json_t **err_val); bool json_get_string(char **store, const json_t *val, const char *res); diff --git a/src/generator.c b/src/generator.c index 61cf4464..17c1130f 100644 --- a/src/generator.c +++ b/src/generator.c @@ -115,6 +115,8 @@ struct proxy_instance { int nonce1len; int nonce2len; + uint32_t version_mask; + tv_t last_message; double diff; @@ -1317,6 +1319,35 @@ static void send_notify(ckpool_t *ckp, proxy_instance_t *proxi, notify_instance_ send_diff(ckp, proxi); } +static void parse_configure(ckpool_t *ckp, proxy_instance_t *proxy, json_t *val) +{ + bool vroll = false; + json_t *res_val; + const char *buf; + + res_val = json_result(val); + if (!res_val) { + LOGDEBUG("Failed to find result response to mining.configure from proxy %d:%s", + proxy->id, proxy->url); + return; + } + vroll = json_is_true(json_object_get(res_val, "version-rolling")); + if (!vroll) { + LOGINFO("No version rolling from compatible proxy %d:%s", proxy->id, + proxy->url); + return; + } + buf = json_string_value(json_object_get(res_val, "version-rolling.mask")); + if (!buf || !strlen(buf)) { + LOGNOTICE("Invalid version-rolling.mask from proxy %d:%s", proxy->id, + proxy->url); + return; + } + sscanf(buf, "%x", &proxy->version_mask); + LOGINFO("Got vmask %s from proxy %d:%d %s", buf, proxy->id, proxy->subid, proxy->url); + stratum_set_proxy_vmask(ckp, proxy->id, proxy->subid, proxy->version_mask); +} + static bool parse_method(ckpool_t *ckp, proxy_instance_t *proxi, const char *msg) { json_t *val = NULL, *method, *err_val, *params; @@ -1346,6 +1377,9 @@ static bool parse_method(ckpool_t *ckp, proxy_instance_t *proxi, const char *msg if (strstr(msg, "mining.suggest")) { LOGINFO("Unhandled suggest_diff from proxy %d:%s", proxi->id, proxi->url); ret = true; + } else if (strstr(msg, "version-rolling")) { + parse_configure(ckp, proxi, val); + ret = true; } else LOGDEBUG("Failed to find method in json for parse_method"); goto out; @@ -2020,6 +2054,30 @@ static void suggest_diff(ckpool_t *ckp, connsock_t *cs, proxy_instance_t *proxy) * if it fails upstream. */ } +static void request_configure(ckpool_t *ckp, connsock_t *cs, proxy_instance_t *proxy) +{ + json_t *req; + bool ret; + + JSON_CPACK(req, "{s:i,s:s, s:[]}", + "id", 40, + "method", "mining.configure", + "params"); + ret = send_json_msg(cs, req); + json_decref(req); + if (!ret) { + LOGNOTICE("Proxy %d:%d %s failed to send message in request_configure", + proxy->id, proxy->subid, proxy->url); + if (cs->fd > 0) { + epoll_ctl(proxy->epfd, EPOLL_CTL_DEL, cs->fd, NULL); + Close(cs->fd); + } + } + /* Response will be parsed by receiver since response can be wildly + * variable. */ +} + + /* Upon failing connnect, subscribe, or auth, back off on the next attempt. * This function should be called on the parent proxy */ static void proxy_backoff(proxy_instance_t *proxy) @@ -2104,6 +2162,9 @@ static bool proxy_alive(ckpool_t *ckp, proxy_instance_t *proxi, connsock_t *cs, proxy_backoff(parent); goto out; } + /* Put a request for mining configure to see if the upstream pool + * supports version_mask */ + request_configure(ckp, cs, proxi); parent->auth_status = STATUS_SUCCESS; proxi->authorised = ret = true; parent->backoff = 0; diff --git a/src/stratifier.c b/src/stratifier.c index ca4fa258..0beea46a 100644 --- a/src/stratifier.c +++ b/src/stratifier.c @@ -309,6 +309,8 @@ struct proxy_base { bool subscribed; bool notified; + uint32_t version_mask; + int64_t clients; /* Incrementing client count */ int64_t max_clients; /* Maximum number of clients per subproxy */ int64_t bound_clients; /* Currently actively bound clients */ @@ -3047,6 +3049,15 @@ out: json_decref(val); } +void stratum_set_proxy_vmask(ckpool_t *ckp, int id, int subid, uint32_t version_mask) +{ + proxy_t *proxy; + + proxy = existing_subproxy(ckp->sdata, id, subid); + proxy->version_mask = version_mask; + LOGWARNING("Stratum Proxy %d:%d had version mask set to %x", id, subid, version_mask); +} + static void stratum_send_diff(sdata_t *sdata, const stratum_instance_t *client); static void update_diff(ckpool_t *ckp, const char *cmd) @@ -5708,6 +5719,20 @@ static void stratum_send_diff(sdata_t *sdata, const stratum_instance_t *client) stratum_add_send(sdata, json_msg, client->id, SM_DIFF); } +#if 0 +/* 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); +} +#endif + /* 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) { diff --git a/src/stratifier.h b/src/stratifier.h index cc201890..1809c023 100644 --- a/src/stratifier.h +++ b/src/stratifier.h @@ -1,5 +1,5 @@ /* - * Copyright 2014-2017 Con Kolivas + * Copyright 2014-2018 Con Kolivas * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -86,6 +86,7 @@ struct genwork { json_t *json; /* getblocktemplate json */ }; +void stratum_set_proxy_vmask(ckpool_t *ckp, int id, int subid, uint32_t version_mask); void parse_remote_txns(ckpool_t *ckp, const json_t *val); #define parse_upstream_txns(ckp, val) parse_remote_txns(ckp, val) void parse_upstream_auth(ckpool_t *ckp, json_t *val); From 23102d1417067ed03b4cf886c942e53a739e90a2 Mon Sep 17 00:00:00 2001 From: Con Kolivas Date: Tue, 6 Feb 2018 17:13:17 +1100 Subject: [PATCH 08/19] Implement basics of vmask support for ckproxy. --- src/ckpool.h | 2 ++ src/generator.c | 15 ++++++++++++-- src/stratifier.c | 52 ++++++++++++++++++++++++++++++------------------ 3 files changed, 48 insertions(+), 21 deletions(-) diff --git a/src/ckpool.h b/src/ckpool.h index e519176e..1594f6cc 100644 --- a/src/ckpool.h +++ b/src/ckpool.h @@ -302,6 +302,7 @@ enum stratum_msgtype { SM_WORKERSTATS, SM_REQTXNS, SM_CONFIGURE, + SM_VERSIONMASK, SM_NONE }; @@ -329,6 +330,7 @@ static const char __maybe_unused *stratum_msgs[] = { "workerstats", "reqtxns", "mining.configure", + "vmask", "" }; diff --git a/src/generator.c b/src/generator.c index 17c1130f..b89abc3d 100644 --- a/src/generator.c +++ b/src/generator.c @@ -1892,7 +1892,7 @@ static void *proxy_send(void *arg) int64_t client_id = 0, id; notify_instance_t *ni; json_t *jobid = NULL; - json_t *val; + json_t *val, *vmask; if (unlikely(msg)) { json_decref(msg->json_msg); @@ -1961,12 +1961,23 @@ static void *proxy_send(void *arg) continue; } - JSON_CPACK(val, "{s[soooo]soss}", "params", subproxy->auth, jobid, + vmask = json_object_get(msg->json_msg, "vmask"); + if (vmask) { + JSON_CPACK(val, "{s[sooooo]soss}", "params", subproxy->auth, jobid, json_object_dup(msg->json_msg, "nonce2"), json_object_dup(msg->json_msg, "ntime"), json_object_dup(msg->json_msg, "nonce"), + json_copy(vmask), "id", json_object_dup(msg->json_msg, "id"), "method", "mining.submit"); + } else { + JSON_CPACK(val, "{s[soooo]soss}", "params", subproxy->auth, jobid, + json_object_dup(msg->json_msg, "nonce2"), + json_object_dup(msg->json_msg, "ntime"), + json_object_dup(msg->json_msg, "nonce"), + "id", json_object_dup(msg->json_msg, "id"), + "method", "mining.submit"); + } add_json_msgq(&csmsgq, subproxy, &val); send_json_msgq(gdata, &csmsgq); } diff --git a/src/stratifier.c b/src/stratifier.c index 0beea46a..9e25b837 100644 --- a/src/stratifier.c +++ b/src/stratifier.c @@ -236,6 +236,9 @@ struct stratum_instance { * or other problem and should be dropped lazily if * this is set to 2 */ + bool vmask; /* Requested vmask */ + uint32_t version_mask; /* Mask to use for this client */ + int latency; /* Latency when on a mining node */ bool reconnect; /* This client really needs to reconnect */ @@ -3055,7 +3058,7 @@ void stratum_set_proxy_vmask(ckpool_t *ckp, int id, int subid, uint32_t version_ proxy = existing_subproxy(ckp->sdata, id, subid); proxy->version_mask = version_mask; - LOGWARNING("Stratum Proxy %d:%d had version mask set to %x", id, subid, version_mask); + LOGWARNING("Stratum Proxy %d:%d had version mask set to %08x", id, subid, version_mask); } static void stratum_send_diff(sdata_t *sdata, const stratum_instance_t *client); @@ -5719,20 +5722,6 @@ static void stratum_send_diff(sdata_t *sdata, const stratum_instance_t *client) stratum_add_send(sdata, json_msg, client->id, SM_DIFF); } -#if 0 -/* 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); -} -#endif - /* 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) { @@ -6027,7 +6016,7 @@ static void update_client(const stratum_instance_t *client, const int64_t client /* Submit a share in proxy mode to the parent pool. workbase_lock is held. * Needs to be entered with client holding a ref count. */ static void submit_share(stratum_instance_t *client, const int64_t jobid, const char *nonce2, - const char *ntime, const char *nonce) + const char *ntime, const char *nonce, const char *version_mask) { ckpool_t *ckp = client->ckp; json_t *json_msg; @@ -6037,6 +6026,8 @@ static void submit_share(stratum_instance_t *client, const int64_t jobid, const JSON_CPACK(json_msg, "{sIsssssssIsIsi}", "jobid", jobid, "nonce2", enonce2, "ntime", ntime, "nonce", nonce, "client_id", client->id, "proxy", client->proxyid, "subproxy", client->subproxyid); + if (version_mask) + json_set_string(json_msg, "vmask", version_mask); generator_add_send(ckp, json_msg); } @@ -6135,7 +6126,7 @@ static json_t *parse_submit(stratum_instance_t *client, json_t *json_msg, 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) { + if (version_mask32 && ((~client->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); @@ -6262,7 +6253,7 @@ out_nowb: * stale shares and filter out the rest. */ if (wb && wb->proxy && submit) { LOGINFO("Submitting share upstream: %s", hexhash); - submit_share(client, id, nonce2, ntime, nonce); + submit_share(client, id, nonce2, ntime, nonce, version_mask); } add_submit(ckp, client, diff, result, submit); @@ -6486,11 +6477,30 @@ static void suggest_diff(ckpool_t *ckp, stratum_instance_t *client, const char * stratum_send_diff(ckp->sdata, client); } +/* Needs to be entered with client holding a ref count */ +static void stratum_send_version_mask(sdata_t *sdata, stratum_instance_t *client) +{ + char version_str[12]; + json_t *json_msg; + + if (unlikely(!client->proxy)) { + LOGERR("stratum_send_version_mask called on a non proxied client"); + return; + } + client->version_mask = client->proxy->version_mask; + sprintf(version_str, "%08x", client->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); +} + /* Send diff first when sending the first stratum template after subscribing */ -static void init_client(const stratum_instance_t *client, const int64_t client_id) +static void init_client(stratum_instance_t *client, const int64_t client_id) { sdata_t *sdata = client->sdata; + if (client->vmask) + stratum_send_version_mask(client->sdata, client); stratum_send_diff(sdata, client); stratum_send_update(sdata, client_id, true); } @@ -6699,7 +6709,11 @@ static void parse_method(ckpool_t *ckp, sdata_t *sdata, stratum_instance_t *clie LOGINFO("Mining configure requested from %s %s", client->identity, client->address); + client->vmask = true; + /* Send a temporary vmask in proxy mode till we know what the + * real vmask will be for the upstream pool. */ sprintf(version_str, "%08x", ckp->version_mask); + client->version_mask = ckp->version_mask; val = json_object(); JSON_CPACK(result_val, "{sbss}", "version-rolling", json_true(), "version-rolling.mask", version_str); From 122232c984eec48451fb0177d2c19b82f0549f97 Mon Sep 17 00:00:00 2001 From: Con Kolivas Date: Wed, 7 Feb 2018 12:09:56 +1100 Subject: [PATCH 09/19] Initialise the psend mutex and conditionals before launching any threads preventing a deadlock on proxy startup. --- src/generator.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/generator.c b/src/generator.c index b89abc3d..68361bc9 100644 --- a/src/generator.c +++ b/src/generator.c @@ -3481,10 +3481,10 @@ static void proxy_mode(ckpool_t *ckp, proc_instance_t *pi) create_pthread(&proxy->pth_precv, passthrough_recv, proxy); proxy->passsends = create_ckmsgq(ckp, "passsend", &passthrough_send); } else { - prepare_proxy(proxy); - create_pthread(&gdata->pth_uprecv, userproxy_recv, ckp); mutex_init(&gdata->psend_lock); cond_init(&gdata->psend_cond); + prepare_proxy(proxy); + create_pthread(&gdata->pth_uprecv, userproxy_recv, ckp); create_pthread(&gdata->pth_psend, proxy_send, ckp); } } From 094920c331e8115d188033111eb3e56741286d94 Mon Sep 17 00:00:00 2001 From: Con Kolivas Date: Wed, 7 Feb 2018 15:16:22 +1100 Subject: [PATCH 10/19] Minor cleanups. --- src/generator.c | 4 ++-- src/stratifier.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/generator.c b/src/generator.c index 68361bc9..33dd9fa0 100644 --- a/src/generator.c +++ b/src/generator.c @@ -2065,7 +2065,7 @@ static void suggest_diff(ckpool_t *ckp, connsock_t *cs, proxy_instance_t *proxy) * if it fails upstream. */ } -static void request_configure(ckpool_t *ckp, connsock_t *cs, proxy_instance_t *proxy) +static void request_configure(connsock_t *cs, proxy_instance_t *proxy) { json_t *req; bool ret; @@ -2175,7 +2175,7 @@ static bool proxy_alive(ckpool_t *ckp, proxy_instance_t *proxi, connsock_t *cs, } /* Put a request for mining configure to see if the upstream pool * supports version_mask */ - request_configure(ckp, cs, proxi); + request_configure(cs, proxi); parent->auth_status = STATUS_SUCCESS; proxi->authorised = ret = true; parent->backoff = 0; diff --git a/src/stratifier.c b/src/stratifier.c index 2dcdfc5d..0a9a71a8 100644 --- a/src/stratifier.c +++ b/src/stratifier.c @@ -3058,7 +3058,7 @@ void stratum_set_proxy_vmask(ckpool_t *ckp, int id, int subid, uint32_t version_ proxy = existing_subproxy(ckp->sdata, id, subid); proxy->version_mask = version_mask; - LOGWARNING("Stratum Proxy %d:%d had version mask set to %08x", id, subid, version_mask); + LOGINFO("Stratum Proxy %d:%d had version mask set to %08x", id, subid, version_mask); } static void stratum_send_diff(sdata_t *sdata, const stratum_instance_t *client); From dc9efd2fd6beeb3b675a2dc95dd5e2ecfe5fe023 Mon Sep 17 00:00:00 2001 From: Con Kolivas Date: Thu, 15 Feb 2018 20:04:43 +1100 Subject: [PATCH 11/19] Fix donation address segwit detection. --- src/stratifier.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stratifier.c b/src/stratifier.c index 0a9a71a8..15def528 100644 --- a/src/stratifier.c +++ b/src/stratifier.c @@ -8680,7 +8680,7 @@ void *stratifier(void *arg) if (generator_checkaddr(ckp, ckp->donaddress, &ckp->donscript, &ckp->donsegwit)) { ckp->donvalid = true; - sdata->dontxnlen = address_to_txn(sdata->dontxnbin, ckp->donaddress, ckp->script, ckp->segwit); + sdata->dontxnlen = address_to_txn(sdata->dontxnbin, ckp->donaddress, ckp->donscript, ckp->donsegwit); } } From cf476a8112453fd72daf26776a5dd1710f7e5f72 Mon Sep 17 00:00:00 2001 From: Con Kolivas Date: Thu, 15 Feb 2018 11:44:45 +1100 Subject: [PATCH 12/19] Whitelist script addresses on testnet when wallet support is missing, and blacklist bech32 addresses. --- src/bitcoin.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/bitcoin.c b/src/bitcoin.c index 646ad472..cc24c219 100644 --- a/src/bitcoin.c +++ b/src/bitcoin.c @@ -65,11 +65,16 @@ bool validate_address(connsock_t *cs, const char *address, bool *script, bool *s ret = true; tmp_val = json_object_get(res_val, "isscript"); if (unlikely(!tmp_val)) { - /* All recent bitcoinds should support this, if not, look for - * a 3x address to at least support it on mainnet */ + /* All recent bitcoinds with wallet support built in should + * support this, if not, look for addresses the braindead way + * to tell if it's a script address. */ LOGDEBUG("No isscript support from bitcoind"); - if (address[0] == '3') + if (address[0] == '3' || address[0] == '2') *script = true; + /* Now look to see this isn't a bech32: We can't support + * bech32 without knowing if it's a pubkey or a script */ + else if (address[0] != '1' && address[0] != 'm') + ret = false; goto out; } *script = json_is_true(tmp_val); From ea7adc39246bb93943e695230b07f2064f99f39e Mon Sep 17 00:00:00 2001 From: Con Kolivas Date: Wed, 7 Mar 2018 09:12:57 +1100 Subject: [PATCH 13/19] Update vmask to 0x1f... --- README | 4 ++-- ckpool.conf | 2 +- src/ckpool.c | 2 ++ 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/README b/README index 3f203750..126cb2f6 100644 --- a/README +++ b/README @@ -287,8 +287,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". +for a client to alter and is expressed as an hex string. Eg "00fff000" +Default is "1fffe000". "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 diff --git a/ckpool.conf b/ckpool.conf index fa461022..d4935da8 100644 --- a/ckpool.conf +++ b/ckpool.conf @@ -20,7 +20,7 @@ "nonce1length" : 4, "nonce2length" : 8, "update_interval" : 30, -"version_mask" : "00000000", +"version_mask" : "1fffe000", "serverurl" : [ "ckpool.org:3333", "node.ckpool.org:3333", diff --git a/src/ckpool.c b/src/ckpool.c index be13551e..2cb0565e 100644 --- a/src/ckpool.c +++ b/src/ckpool.c @@ -1472,6 +1472,8 @@ static void parse_config(ckpool_t *ckp) json_get_string(&vmask, json_conf, "version_mask"); if (vmask && strlen(vmask) && validhex(vmask)) sscanf(vmask, "%x", &ckp->version_mask); + else + ckp->version_mask = 0x1fffe000; /* Look for an array first and then a single entry */ arr_val = json_object_get(json_conf, "serverurl"); if (!parse_serverurls(ckp, arr_val)) { From f6c0a012d6258ef0126df28d8fbae3bc9973340c Mon Sep 17 00:00:00 2001 From: Con Kolivas Date: Fri, 16 Feb 2018 12:10:39 +1100 Subject: [PATCH 14/19] Attempt to blacklist proxies without vmask support if clients mandate a vmask. --- src/stratifier.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/stratifier.c b/src/stratifier.c index 15def528..bb5225b4 100644 --- a/src/stratifier.c +++ b/src/stratifier.c @@ -4749,7 +4749,7 @@ static bool new_enonce1(ckpool_t *ckp, sdata_t *ckp_sdata, sdata_t *sdata, strat static void stratum_send_message(sdata_t *sdata, const stratum_instance_t *client, const char *msg); /* Need to hold sdata->proxy_lock */ -static proxy_t *__best_subproxy(proxy_t *proxy) +static proxy_t *__best_subproxy(proxy_t *proxy, const bool vmask) { proxy_t *subproxy, *best = NULL, *tmp; int64_t max_headroom; @@ -4762,6 +4762,8 @@ static proxy_t *__best_subproxy(proxy_t *proxy) continue; if (!subproxy->sdata->current_workbase) continue; + if (vmask && !subproxy->version_mask) + continue; /* This subproxy data is checked without holding the correct * instance_lock but an incorrect value here is harmless */ subproxy_headroom = subproxy->max_clients - subproxy->clients - subproxy->connecting; @@ -4780,8 +4782,9 @@ static proxy_t *__best_subproxy(proxy_t *proxy) /* Choose the stratifier data for a new client. Use the main ckp_sdata except * in proxy mode where we find a subproxy based on the current proxy with room * for more clients. Signal the generator to recruit more subproxies if we are - * running out of room. */ -static sdata_t *select_sdata(ckpool_t *ckp, sdata_t *ckp_sdata, const int userid) + * running out of room. Needs to be entered with client holding a ref count */ +static sdata_t *select_sdata(ckpool_t *ckp, sdata_t *ckp_sdata, const bool vmask, + const int userid) { proxy_t *global, *proxy, *tmp, *best = NULL; @@ -4791,14 +4794,18 @@ static sdata_t *select_sdata(ckpool_t *ckp, sdata_t *ckp_sdata, const int userid /* Proxies are ordered by priority so first available will be the best * priority */ mutex_lock(&ckp_sdata->proxy_lock); - best = global = ckp_sdata->proxy; + global = ckp_sdata->proxy; + /* If the client needs a version mask, only use sdata from pools with + * one set. */ + if (vmask || ckp->version_mask) + best = global; HASH_ITER(hh, ckp_sdata->proxies, proxy, tmp) { if (proxy->userid < userid) continue; if (proxy->userid > userid) break; - best = __best_subproxy(proxy); + best = __best_subproxy(proxy, vmask); if (best) break; } @@ -4910,7 +4917,7 @@ static json_t *parse_subscribe(stratum_instance_t *client, const int64_t client_ return json_string("params not an array"); } - sdata = select_sdata(ckp, ckp_sdata, 0); + sdata = select_sdata(ckp, ckp_sdata, client->vmask, 0); if (unlikely(!ckp->node && (!sdata || !sdata->current_workbase))) { LOGWARNING("Failed to provide subscription due to no %s", sdata ? "current workbase" : "sdata"); stratum_send_message(ckp_sdata, client, "Pool Initialising"); @@ -4965,7 +4972,7 @@ static json_t *parse_subscribe(stratum_instance_t *client, const int64_t client_ if (userid == -1) userid = userid_from_sessionip(ckp_sdata, client->address); if (userid != -1) { - sdata_t *user_sdata = select_sdata(ckp, ckp_sdata, userid); + sdata_t *user_sdata = select_sdata(ckp, ckp_sdata, client->vmask, userid); if (user_sdata) sdata = user_sdata; From 8d1eac3aea8d02e5cfa5293170731607013430c6 Mon Sep 17 00:00:00 2001 From: Con Kolivas Date: Fri, 16 Feb 2018 14:43:26 +1100 Subject: [PATCH 15/19] Do not reconnect vmask clients to global proxies that don't support it. --- src/stratifier.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/stratifier.c b/src/stratifier.c index bb5225b4..cb8a7df4 100644 --- a/src/stratifier.c +++ b/src/stratifier.c @@ -2646,6 +2646,10 @@ static void reconnect_global_clients(sdata_t *sdata) continue; if (!client->authorised) continue; + /* Does the client mandate a vmask but the best proxy not + * support it? */ + if (client->vmask && !proxy->version_mask) + continue; /* Is this client bound to a dead proxy? */ if (!client->reconnect) { /* This client is bound to a user proxy */ From c20cc58d4851ef59e31948a9569d443e5bdef99b Mon Sep 17 00:00:00 2001 From: Con Kolivas Date: Fri, 16 Feb 2018 14:47:40 +1100 Subject: [PATCH 16/19] Comment --- src/stratifier.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/stratifier.c b/src/stratifier.c index cb8a7df4..57566aeb 100644 --- a/src/stratifier.c +++ b/src/stratifier.c @@ -2647,7 +2647,8 @@ static void reconnect_global_clients(sdata_t *sdata) if (!client->authorised) continue; /* Does the client mandate a vmask but the best proxy not - * support it? */ + * support it? Not ideal because there may be a better priority + * pool below the best priority one that does support it. */ if (client->vmask && !proxy->version_mask) continue; /* Is this client bound to a dead proxy? */ From 984392f8adca314ba251d954446c24c21eb98938 Mon Sep 17 00:00:00 2001 From: Con Kolivas Date: Fri, 16 Feb 2018 15:00:42 +1100 Subject: [PATCH 17/19] Check userproxies for vmask support and remove redundant check again for proxy we already know before calling generator recruit. --- src/stratifier.c | 35 +++++++---------------------------- 1 file changed, 7 insertions(+), 28 deletions(-) diff --git a/src/stratifier.c b/src/stratifier.c index 57566aeb..2ef573f5 100644 --- a/src/stratifier.c +++ b/src/stratifier.c @@ -2898,31 +2898,6 @@ static void update_subscribe(ckpool_t *ckp, const char *cmd) check_proxy(sdata, proxy); } -/* Find the highest priority alive proxy belonging to userid and recruit extra - * subproxies. */ -static void recruit_best_userproxy(sdata_t *sdata, const int userid, const int recruits) -{ - proxy_t *proxy, *subproxy, *tmp, *subtmp; - int id = -1; - - mutex_lock(&sdata->proxy_lock); - HASH_ITER(hh, sdata->proxies, proxy, tmp) { - if (proxy->userid < userid) - continue; - if (proxy->userid > userid) - break; - HASH_ITER(sh, proxy->subproxies, subproxy, subtmp) { - if (subproxy->dead) - continue; - id = proxy->id; - } - } - mutex_unlock(&sdata->proxy_lock); - - if (id != -1) - generator_recruit(sdata->ckp, id, recruits); -} - /* Check how much headroom the userid proxies have and reconnect any clients * that are not bound to it that should be */ static void check_userproxies(sdata_t *sdata, proxy_t *proxy, const int userid) @@ -2944,6 +2919,10 @@ static void check_userproxies(sdata_t *sdata, proxy_t *proxy, const int userid) if (client->proxy->userid == userid && client->proxy->parent->priority <= proxy->parent->priority) continue; + /* Tested proxy doesn't have vmask support while client + * mandates it. */ + if (client->vmask && !proxy->version_mask) + continue; if (headroom-- < 1) continue; reconnects++; @@ -2952,11 +2931,11 @@ static void check_userproxies(sdata_t *sdata, proxy_t *proxy, const int userid) ck_runlock(&sdata->instance_lock); if (reconnects) { - LOGINFO("%d clients flagged for reconnect to user %d proxies", - reconnects, userid); + LOGINFO("%d clients flagged for reconnect to user %d proxy %d", + reconnects, userid, proxy->id); } if (headroom < 0) - recruit_best_userproxy(sdata, userid, -headroom); + generator_recruit(sdata->ckp, proxy->id, -headroom); } static void update_notify(ckpool_t *ckp, const char *cmd) From 88173f7a8bc14f21b47207ccdab6a4826b583918 Mon Sep 17 00:00:00 2001 From: Con Kolivas Date: Mon, 19 Feb 2018 09:33:52 +1100 Subject: [PATCH 18/19] Logic fail. --- src/stratifier.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stratifier.c b/src/stratifier.c index 2ef573f5..343a825a 100644 --- a/src/stratifier.c +++ b/src/stratifier.c @@ -4781,7 +4781,7 @@ static sdata_t *select_sdata(ckpool_t *ckp, sdata_t *ckp_sdata, const bool vmask global = ckp_sdata->proxy; /* If the client needs a version mask, only use sdata from pools with * one set. */ - if (vmask || ckp->version_mask) + if (!vmask || ckp->version_mask) best = global; HASH_ITER(hh, ckp_sdata->proxies, proxy, tmp) { From 455673c4ca0144147bf9a7ef25cfdeeb4827d09e Mon Sep 17 00:00:00 2001 From: Con Kolivas Date: Tue, 20 Feb 2018 00:28:01 +1100 Subject: [PATCH 19/19] Send version mask on init client only on proxies. --- src/stratifier.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/stratifier.c b/src/stratifier.c index 343a825a..bd4afa09 100644 --- a/src/stratifier.c +++ b/src/stratifier.c @@ -6486,11 +6486,11 @@ static void stratum_send_version_mask(sdata_t *sdata, stratum_instance_t *client } /* Send diff first when sending the first stratum template after subscribing */ -static void init_client(stratum_instance_t *client, const int64_t client_id) +static void init_client(ckpool_t *ckp, stratum_instance_t *client, const int64_t client_id) { sdata_t *sdata = client->sdata; - if (client->vmask) + if (ckp->proxy && client->vmask) stratum_send_version_mask(client->sdata, client); stratum_send_diff(sdata, client); stratum_send_update(sdata, client_id, true); @@ -6617,7 +6617,7 @@ static void parse_method(ckpool_t *ckp, sdata_t *sdata, stratum_instance_t *clie json_object_set_new_nocheck(val, "error", json_null()); stratum_add_send(sdata, val, client_id, SM_SUBSCRIBERESULT); if (likely(client->subscribed)) - init_client(client, client_id); + init_client(ckp, client, client_id); return; }