Browse Source

Merge branch 'vmask'

master
Con Kolivas 7 years ago
parent
commit
fc60b6d67f
  1. 4
      README
  2. 1
      ckpool.conf
  3. 13
      src/ckpool.c
  4. 8
      src/ckpool.h
  5. 76
      src/generator.c
  6. 6
      src/libckpool.h
  7. 198
      src/stratifier.c
  8. 3
      src/stratifier.h

4
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 miners and is set to 30 seconds by default to help perpetuate transactions for
the health of the bitcoin network. 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 "00fff000"
Default is "1fffe000".
"serverurl" : This is the IP(s) to try to bind ckpool uniquely to, otherwise it "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 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 and 3334 in proxy mode. Multiple entries can be specified as an array by

1
ckpool.conf

@ -20,6 +20,7 @@
"nonce1length" : 4, "nonce1length" : 4,
"nonce2length" : 8, "nonce2length" : 8,
"update_interval" : 30, "update_interval" : 30,
"version_mask" : "1fffe000",
"serverurl" : [ "serverurl" : [
"ckpool.org:3333", "ckpool.org:3333",
"node.ckpool.org:3333", "node.ckpool.org:3333",

13
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 * 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 * 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 /* Decode a string that should have a json message and return just the contents
* of the result key or NULL. */ * 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; 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 */ /* 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"); json_t *err_val = json_object_get(val, "error");
@ -1444,8 +1444,8 @@ static void parse_config(ckpool_t *ckp)
{ {
json_t *json_conf, *arr_val; json_t *json_conf, *arr_val;
json_error_t err_val; json_error_t err_val;
char *url, *vmask;
int arr_size; int arr_size;
char *url;
json_conf = json_load_file(ckp->config, JSON_DISABLE_EOF_CHECK, &err_val); json_conf = json_load_file(ckp->config, JSON_DISABLE_EOF_CHECK, &err_val);
if (!json_conf) { if (!json_conf) {
@ -1469,6 +1469,11 @@ static void parse_config(ckpool_t *ckp)
json_get_int(&ckp->nonce1length, json_conf, "nonce1length"); json_get_int(&ckp->nonce1length, json_conf, "nonce1length");
json_get_int(&ckp->nonce2length, json_conf, "nonce2length"); json_get_int(&ckp->nonce2length, json_conf, "nonce2length");
json_get_int(&ckp->update_interval, json_conf, "update_interval"); 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);
else
ckp->version_mask = 0x1fffe000;
/* Look for an array first and then a single entry */ /* Look for an array first and then a single entry */
arr_val = json_object_get(json_conf, "serverurl"); arr_val = json_object_get(json_conf, "serverurl");
if (!parse_serverurls(ckp, arr_val)) { if (!parse_serverurls(ckp, arr_val)) {

8
src/ckpool.h

@ -259,6 +259,8 @@ struct ckpool_instance {
int update_interval; // Seconds between stratum updates 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 */ /* Proxy options */
int proxies; int proxies;
char **proxyurl; char **proxyurl;
@ -299,6 +301,8 @@ enum stratum_msgtype {
SM_SHAREERR, SM_SHAREERR,
SM_WORKERSTATS, SM_WORKERSTATS,
SM_REQTXNS, SM_REQTXNS,
SM_CONFIGURE,
SM_VERSIONMASK,
SM_NONE SM_NONE
}; };
@ -325,6 +329,8 @@ static const char __maybe_unused *stratum_msgs[] = {
"shareerr", "shareerr",
"workerstats", "workerstats",
"reqtxns", "reqtxns",
"mining.configure",
"vmask",
"" ""
}; };
@ -367,6 +373,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); json_t *json_rpc_response(connsock_t *cs, const char *rpc_req);
void json_rpc_msg(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); 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); 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); bool json_get_string(char **store, const json_t *val, const char *res);

76
src/generator.c

@ -115,6 +115,8 @@ struct proxy_instance {
int nonce1len; int nonce1len;
int nonce2len; int nonce2len;
uint32_t version_mask;
tv_t last_message; tv_t last_message;
double diff; double diff;
@ -1317,6 +1319,35 @@ static void send_notify(ckpool_t *ckp, proxy_instance_t *proxi, notify_instance_
send_diff(ckp, proxi); 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) static bool parse_method(ckpool_t *ckp, proxy_instance_t *proxi, const char *msg)
{ {
json_t *val = NULL, *method, *err_val, *params; 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")) { if (strstr(msg, "mining.suggest")) {
LOGINFO("Unhandled suggest_diff from proxy %d:%s", proxi->id, proxi->url); LOGINFO("Unhandled suggest_diff from proxy %d:%s", proxi->id, proxi->url);
ret = true; ret = true;
} else if (strstr(msg, "version-rolling")) {
parse_configure(ckp, proxi, val);
ret = true;
} else } else
LOGDEBUG("Failed to find method in json for parse_method"); LOGDEBUG("Failed to find method in json for parse_method");
goto out; goto out;
@ -1858,7 +1892,7 @@ static void *proxy_send(void *arg)
int64_t client_id = 0, id; int64_t client_id = 0, id;
notify_instance_t *ni; notify_instance_t *ni;
json_t *jobid = NULL; json_t *jobid = NULL;
json_t *val; json_t *val, *vmask;
if (unlikely(msg)) { if (unlikely(msg)) {
json_decref(msg->json_msg); json_decref(msg->json_msg);
@ -1927,12 +1961,23 @@ static void *proxy_send(void *arg)
continue; 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, "nonce2"),
json_object_dup(msg->json_msg, "ntime"), json_object_dup(msg->json_msg, "ntime"),
json_object_dup(msg->json_msg, "nonce"), json_object_dup(msg->json_msg, "nonce"),
"id", json_object_dup(msg->json_msg, "id"), "id", json_object_dup(msg->json_msg, "id"),
"method", "mining.submit"); "method", "mining.submit");
}
add_json_msgq(&csmsgq, subproxy, &val); add_json_msgq(&csmsgq, subproxy, &val);
send_json_msgq(gdata, &csmsgq); send_json_msgq(gdata, &csmsgq);
} }
@ -2020,6 +2065,30 @@ static void suggest_diff(ckpool_t *ckp, connsock_t *cs, proxy_instance_t *proxy)
* if it fails upstream. */ * if it fails upstream. */
} }
static void request_configure(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. /* Upon failing connnect, subscribe, or auth, back off on the next attempt.
* This function should be called on the parent proxy */ * This function should be called on the parent proxy */
static void proxy_backoff(proxy_instance_t *proxy) static void proxy_backoff(proxy_instance_t *proxy)
@ -2104,6 +2173,9 @@ static bool proxy_alive(ckpool_t *ckp, proxy_instance_t *proxi, connsock_t *cs,
proxy_backoff(parent); proxy_backoff(parent);
goto out; goto out;
} }
/* Put a request for mining configure to see if the upstream pool
* supports version_mask */
request_configure(cs, proxi);
parent->auth_status = STATUS_SUCCESS; parent->auth_status = STATUS_SUCCESS;
proxi->authorised = ret = true; proxi->authorised = ret = true;
parent->backoff = 0; parent->backoff = 0;

6
src/libckpool.h

@ -267,7 +267,8 @@ enum share_err {
SE_STALE, SE_STALE,
SE_NTIME_INVALID, SE_NTIME_INVALID,
SE_DUPE, SE_DUPE,
SE_HIGH_DIFF SE_HIGH_DIFF,
SE_INVALID_VERSION_MASK
}; };
static const char __maybe_unused *share_errs[] = { static const char __maybe_unused *share_errs[] = {
@ -285,7 +286,8 @@ static const char __maybe_unused *share_errs[] = {
"Stale", "Stale",
"Ntime out of range", "Ntime out of range",
"Duplicate", "Duplicate",
"Above target" "Above target",
"Invalid version mask"
}; };
#define SHARE_ERR(x) share_errs[((x) + 9)] #define SHARE_ERR(x) share_errs[((x) + 9)]

198
src/stratifier.c

@ -236,6 +236,9 @@ struct stratum_instance {
* or other problem and should be dropped lazily if * or other problem and should be dropped lazily if
* this is set to 2 */ * 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 */ int latency; /* Latency when on a mining node */
bool reconnect; /* This client really needs to reconnect */ bool reconnect; /* This client really needs to reconnect */
@ -309,6 +312,8 @@ struct proxy_base {
bool subscribed; bool subscribed;
bool notified; bool notified;
uint32_t version_mask;
int64_t clients; /* Incrementing client count */ int64_t clients; /* Incrementing client count */
int64_t max_clients; /* Maximum number of clients per subproxy */ int64_t max_clients; /* Maximum number of clients per subproxy */
int64_t bound_clients; /* Currently actively bound clients */ int64_t bound_clients; /* Currently actively bound clients */
@ -1864,7 +1869,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 */ /* Calculate share diff and fill in hash and swap. Need to hold workbase read count */
static double static double
share_diff(char *coinbase, const uchar *enonce1bin, const workbase_t *wb, const char *nonce2, 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, uint32_t version_mask, const char *nonce,
uchar *hash, uchar *swap, int *cblen)
{ {
unsigned char merkle_root[32], merkle_sha[64]; unsigned char merkle_root[32], merkle_sha[64];
uint32_t *data32, *swap32, benonce32; uint32_t *data32, *swap32, benonce32;
@ -1896,6 +1902,13 @@ share_diff(char *coinbase, const uchar *enonce1bin, const workbase_t *wb, const
memcpy(data, wb->headerbin, 80); memcpy(data, wb->headerbin, 80);
memcpy(data + 36, merkle_root, 32); memcpy(data + 36, merkle_root, 32);
/* Update nVersion when version_mask is in use */
if (version_mask) {
version_mask = htobe32(version_mask);
data32 = (uint32_t *)data;
*data32 |= version_mask;
}
/* Insert the nonce value into the data */ /* Insert the nonce value into the data */
hex2bin(&benonce32, nonce, 4); hex2bin(&benonce32, nonce, 4);
data32 = (uint32_t *)(data + 64 + 12); data32 = (uint32_t *)(data + 64 + 12);
@ -1974,8 +1987,8 @@ static void send_nodes_block(sdata_t *sdata, const json_t *block_val, const int6
/* Entered with workbase readcount. */ /* Entered with workbase readcount. */
static void send_node_block(ckpool_t *ckp, sdata_t *sdata, const char *enonce1, const char *nonce, 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 char *nonce2, const uint32_t ntime32, const uint32_t version_mask,
const double diff, const int64_t client_id, const int64_t jobid, const double diff, const int64_t client_id,
const char *coinbase, const int cblen, const uchar *data) const char *coinbase, const int cblen, const uchar *data)
{ {
if (sdata->node_instances) { if (sdata->node_instances) {
@ -1985,6 +1998,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, "nonce", nonce);
json_set_string(val, "nonce2", nonce2); json_set_string(val, "nonce2", nonce2);
json_set_uint32(val, "ntime32", ntime32); json_set_uint32(val, "ntime32", ntime32);
json_set_uint32(val, "version_mask", version_mask);
json_set_int64(val, "jobid", jobid); json_set_int64(val, "jobid", jobid);
json_set_double(val, "diff", diff); json_set_double(val, "diff", diff);
add_remote_blockdata(ckp, val, cblen, coinbase, data); add_remote_blockdata(ckp, val, cblen, coinbase, data);
@ -2112,11 +2126,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, char *coinbase = NULL, *enonce1 = NULL, *nonce = NULL, *nonce2 = NULL, *gbt_block,
*coinbasehex, *swaphex; *coinbasehex, *swaphex;
uchar *enonce1bin = NULL, hash[32], swap[80], flip32[32]; uchar *enonce1bin = NULL, hash[32], swap[80], flip32[32];
uint32_t ntime32, version_mask = 0;
char blockhash[68], cdfield[64]; char blockhash[68], cdfield[64];
json_t *bval, *bval_copy; json_t *bval, *bval_copy;
int enonce1len, cblen; int enonce1len, cblen;
workbase_t *wb = NULL; workbase_t *wb = NULL;
uint32_t ntime32;
double diff; double diff;
ts_t ts_now; ts_t ts_now;
int64_t id; int64_t id;
@ -2147,6 +2161,11 @@ static void submit_node_block(ckpool_t *ckp, sdata_t *sdata, json_t *val)
goto out; 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); LOGWARNING("Possible upstream block solve diff %lf !", diff);
ts_realtime(&ts_now); ts_realtime(&ts_now);
@ -2178,20 +2197,21 @@ static void submit_node_block(ckpool_t *ckp, sdata_t *sdata, json_t *val)
hex2bin(enonce1bin, enonce1, enonce1len); hex2bin(enonce1bin, enonce1, enonce1len);
coinbase = alloca(wb->coinb1len + wb->enonce1constlen + wb->enonce1varlen + wb->enonce2varlen + wb->coinb2len); coinbase = alloca(wb->coinb1len + wb->enonce1constlen + wb->enonce1varlen + wb->enonce2varlen + wb->coinb2len);
/* Fill in the hashes */ /* 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 */ /* Now we have enough to assemble a block */
gbt_block = process_block(wb, coinbase, cblen, swap, hash, flip32, blockhash); gbt_block = process_block(wb, coinbase, cblen, swap, hash, flip32, blockhash);
ret = local_block_submit(ckp, gbt_block, flip32, wb->height); 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, "height", wb->height,
"blockhash", blockhash, "blockhash", blockhash,
"confirmed", "n", "confirmed", "n",
"workinfoid", wb->id, "workinfoid", wb->id,
"enonce1", enonce1, "enonce1", enonce1,
"nonce2", nonce2, "nonce2", nonce2,
"version_mask", version_mask,
"nonce", nonce, "nonce", nonce,
"reward", wb->coinbasevalue, "reward", wb->coinbasevalue,
"diff", diff, "diff", diff,
@ -2626,6 +2646,11 @@ static void reconnect_global_clients(sdata_t *sdata)
continue; continue;
if (!client->authorised) if (!client->authorised)
continue; continue;
/* Does the client mandate a vmask but the best proxy not
* 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? */ /* Is this client bound to a dead proxy? */
if (!client->reconnect) { if (!client->reconnect) {
/* This client is bound to a user proxy */ /* This client is bound to a user proxy */
@ -2873,31 +2898,6 @@ static void update_subscribe(ckpool_t *ckp, const char *cmd)
check_proxy(sdata, proxy); 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 /* Check how much headroom the userid proxies have and reconnect any clients
* that are not bound to it that should be */ * that are not bound to it that should be */
static void check_userproxies(sdata_t *sdata, proxy_t *proxy, const int userid) static void check_userproxies(sdata_t *sdata, proxy_t *proxy, const int userid)
@ -2919,6 +2919,10 @@ static void check_userproxies(sdata_t *sdata, proxy_t *proxy, const int userid)
if (client->proxy->userid == userid && if (client->proxy->userid == userid &&
client->proxy->parent->priority <= proxy->parent->priority) client->proxy->parent->priority <= proxy->parent->priority)
continue; continue;
/* Tested proxy doesn't have vmask support while client
* mandates it. */
if (client->vmask && !proxy->version_mask)
continue;
if (headroom-- < 1) if (headroom-- < 1)
continue; continue;
reconnects++; reconnects++;
@ -2927,11 +2931,11 @@ static void check_userproxies(sdata_t *sdata, proxy_t *proxy, const int userid)
ck_runlock(&sdata->instance_lock); ck_runlock(&sdata->instance_lock);
if (reconnects) { if (reconnects) {
LOGINFO("%d clients flagged for reconnect to user %d proxies", LOGINFO("%d clients flagged for reconnect to user %d proxy %d",
reconnects, userid); reconnects, userid, proxy->id);
} }
if (headroom < 0) 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) static void update_notify(ckpool_t *ckp, const char *cmd)
@ -3032,6 +3036,15 @@ out:
json_decref(val); 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;
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); static void stratum_send_diff(sdata_t *sdata, const stratum_instance_t *client);
static void update_diff(ckpool_t *ckp, const char *cmd) static void update_diff(ckpool_t *ckp, const char *cmd)
@ -4720,7 +4733,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); static void stratum_send_message(sdata_t *sdata, const stratum_instance_t *client, const char *msg);
/* Need to hold sdata->proxy_lock */ /* 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; proxy_t *subproxy, *best = NULL, *tmp;
int64_t max_headroom; int64_t max_headroom;
@ -4733,6 +4746,8 @@ static proxy_t *__best_subproxy(proxy_t *proxy)
continue; continue;
if (!subproxy->sdata->current_workbase) if (!subproxy->sdata->current_workbase)
continue; continue;
if (vmask && !subproxy->version_mask)
continue;
/* This subproxy data is checked without holding the correct /* This subproxy data is checked without holding the correct
* instance_lock but an incorrect value here is harmless */ * instance_lock but an incorrect value here is harmless */
subproxy_headroom = subproxy->max_clients - subproxy->clients - subproxy->connecting; subproxy_headroom = subproxy->max_clients - subproxy->clients - subproxy->connecting;
@ -4751,8 +4766,9 @@ static proxy_t *__best_subproxy(proxy_t *proxy)
/* Choose the stratifier data for a new client. Use the main ckp_sdata except /* 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 * 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 * for more clients. Signal the generator to recruit more subproxies if we are
* running out of room. */ * 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 int userid) 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; proxy_t *global, *proxy, *tmp, *best = NULL;
@ -4762,14 +4778,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 /* Proxies are ordered by priority so first available will be the best
* priority */ * priority */
mutex_lock(&ckp_sdata->proxy_lock); 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) { HASH_ITER(hh, ckp_sdata->proxies, proxy, tmp) {
if (proxy->userid < userid) if (proxy->userid < userid)
continue; continue;
if (proxy->userid > userid) if (proxy->userid > userid)
break; break;
best = __best_subproxy(proxy); best = __best_subproxy(proxy, vmask);
if (best) if (best)
break; break;
} }
@ -4881,7 +4901,7 @@ static json_t *parse_subscribe(stratum_instance_t *client, const int64_t client_
return json_string("params not an array"); 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))) { if (unlikely(!ckp->node && (!sdata || !sdata->current_workbase))) {
LOGWARNING("Failed to provide subscription due to no %s", sdata ? "current workbase" : "sdata"); LOGWARNING("Failed to provide subscription due to no %s", sdata ? "current workbase" : "sdata");
stratum_send_message(ckp_sdata, client, "Pool Initialising"); stratum_send_message(ckp_sdata, client, "Pool Initialising");
@ -4936,7 +4956,7 @@ static json_t *parse_subscribe(stratum_instance_t *client, const int64_t client_
if (userid == -1) if (userid == -1)
userid = userid_from_sessionip(ckp_sdata, client->address); userid = userid_from_sessionip(ckp_sdata, client->address);
if (userid != -1) { 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) if (user_sdata)
sdata = user_sdata; sdata = user_sdata;
@ -5866,7 +5886,8 @@ downstream_block(ckpool_t *ckp, sdata_t *sdata, const json_t *val, const int cbl
static void static void
test_blocksolve(const stratum_instance_t *client, const workbase_t *wb, const uchar *data, 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 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; char blockhash[68], cdfield[64], *gbt_block;
sdata_t *sdata = client->sdata; sdata_t *sdata = client->sdata;
@ -5889,8 +5910,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); sprintf(cdfield, "%lu,%lu", ts_now.tv_sec, ts_now.tv_nsec);
gbt_block = process_block(wb, coinbase, cblen, data, hash, flip32, blockhash); gbt_block = process_block(wb, coinbase, cblen, data, hash, flip32, blockhash);
send_node_block(ckp, sdata, client->enonce1, nonce, nonce2, ntime32, wb->id, send_node_block(ckp, sdata, client->enonce1, nonce, nonce2, ntime32, version_mask,
diff, client->id, coinbase, cblen, data); wb->id, diff, client->id, coinbase, cblen, data);
val = json_object(); val = json_object();
json_set_int(val, "height", wb->height); json_set_int(val, "height", wb->height);
@ -5907,6 +5928,7 @@ test_blocksolve(const stratum_instance_t *client, const workbase_t *wb, const uc
json_set_string(val, "nonce2", nonce2); json_set_string(val, "nonce2", nonce2);
json_set_string(val, "nonce", nonce); json_set_string(val, "nonce", nonce);
json_set_uint32(val, "ntime32", ntime32); json_set_uint32(val, "ntime32", ntime32);
json_set_uint32(val, "version_mask", version_mask);
json_set_int64(val, "reward", wb->coinbasevalue); json_set_int64(val, "reward", wb->coinbasevalue);
json_set_double(val, "diff", diff); json_set_double(val, "diff", diff);
json_set_string(val, "createdate", cdfield); json_set_string(val, "createdate", cdfield);
@ -5936,7 +5958,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. */ /* 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, 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; char *coinbase;
uchar swap[80]; uchar swap[80];
@ -5946,10 +5969,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); coinbase = ckalloc(wb->coinb1len + wb->enonce1constlen + wb->enonce1varlen + wb->enonce2varlen + wb->coinb2len);
/* Calculate the diff of the share here */ /* 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 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); free(coinbase);
@ -5984,7 +6007,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. /* Submit a share in proxy mode to the parent pool. workbase_lock is held.
* Needs to be entered with client holding a ref count. */ * 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, 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; ckpool_t *ckp = client->ckp;
json_t *json_msg; json_t *json_msg;
@ -5994,6 +6017,8 @@ static void submit_share(stratum_instance_t *client, const int64_t jobid, const
JSON_CPACK(json_msg, "{sIsssssssIsIsi}", "jobid", jobid, "nonce2", enonce2, JSON_CPACK(json_msg, "{sIsssssssIsIsi}", "jobid", jobid, "nonce2", enonce2,
"ntime", ntime, "nonce", nonce, "client_id", client->id, "ntime", ntime, "nonce", nonce, "client_id", client->id,
"proxy", client->proxyid, "subproxy", client->subproxyid); "proxy", client->proxyid, "subproxy", client->subproxyid);
if (version_mask)
json_set_string(json_msg, "vmask", version_mask);
generator_add_send(ckp, json_msg); generator_add_send(ckp, json_msg);
} }
@ -6024,17 +6049,17 @@ static json_t *parse_submit(stratum_instance_t *client, json_t *json_msg,
const json_t *params_val, json_t **err_val) const json_t *params_val, json_t **err_val)
{ {
bool share = false, result = false, invalid = true, submit = false, stale = false; 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; double diff = client->diff, wdiff = 0, sdiff = -1;
char hexhash[68] = {}, sharehash[32], cdfield[64]; char hexhash[68] = {}, sharehash[32], cdfield[64];
const char *workername, *job_id, *ntime, *nonce;
user_instance_t *user = client->user_instance; user_instance_t *user = client->user_instance;
uint32_t ntime32, version_mask32 = 0;
char *fname = NULL, *s, *nonce2; char *fname = NULL, *s, *nonce2;
sdata_t *sdata = client->sdata; sdata_t *sdata = client->sdata;
enum share_err err = SE_NONE; enum share_err err = SE_NONE;
ckpool_t *ckp = client->ckp; ckpool_t *ckp = client->ckp;
char idstring[20] = {}; char idstring[20] = {};
workbase_t *wb = NULL; workbase_t *wb = NULL;
uint32_t ntime32;
uchar hash[32]; uchar hash[32];
int nlen, len; int nlen, len;
time_t now_t; time_t now_t;
@ -6087,6 +6112,18 @@ static json_t *parse_submit(stratum_instance_t *client, json_t *json_msg,
*err_val = JSON_ERR(err); *err_val = JSON_ERR(err);
goto out; 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 && ((~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);
goto out;
}
}
if (safecmp(workername, client->workername)) { if (safecmp(workername, client->workername)) {
err = SE_WORKER_MISMATCH; err = SE_WORKER_MISMATCH;
*err_val = JSON_ERR(err); *err_val = JSON_ERR(err);
@ -6128,7 +6165,7 @@ static json_t *parse_submit(stratum_instance_t *client, json_t *json_msg,
} }
if (id < sdata->blockchange_id) if (id < sdata->blockchange_id)
stale = true; 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) { if (sdiff > client->best_diff) {
worker_instance_t *worker = client->worker_instance; worker_instance_t *worker = client->worker_instance;
@ -6207,7 +6244,7 @@ out_nowb:
* stale shares and filter out the rest. */ * stale shares and filter out the rest. */
if (wb && wb->proxy && submit) { if (wb && wb->proxy && submit) {
LOGINFO("Submitting share upstream: %s", hexhash); 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); add_submit(ckp, client, diff, result, submit);
@ -6431,11 +6468,30 @@ static void suggest_diff(ckpool_t *ckp, stratum_instance_t *client, const char *
stratum_send_diff(ckp->sdata, client); 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 */ /* 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(ckpool_t *ckp, stratum_instance_t *client, const int64_t client_id)
{ {
sdata_t *sdata = client->sdata; sdata_t *sdata = client->sdata;
if (ckp->proxy && client->vmask)
stratum_send_version_mask(client->sdata, client);
stratum_send_diff(sdata, client); stratum_send_diff(sdata, client);
stratum_send_update(sdata, client_id, true); stratum_send_update(sdata, client_id, true);
} }
@ -6561,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()); json_object_set_new_nocheck(val, "error", json_null());
stratum_add_send(sdata, val, client_id, SM_SUBSCRIBERESULT); stratum_add_send(sdata, val, client_id, SM_SUBSCRIBERESULT);
if (likely(client->subscribed)) if (likely(client->subscribed))
init_client(client, client_id); init_client(ckp, client, client_id);
return; return;
} }
@ -6638,6 +6694,27 @@ static void parse_method(ckpool_t *ckp, sdata_t *sdata, stratum_instance_t *clie
return; 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);
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);
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 /* We should only accept requests from subscribed and authed users here
* on */ * on */
if (!client->subscribed) { if (!client->subscribed) {
@ -7682,13 +7759,14 @@ static void sauth_process(ckpool_t *ckp, json_params_t *jp)
mindiff = client->suggest_diff; mindiff = client->suggest_diff;
else else
mindiff = client->worker_instance->mindiff; mindiff = client->worker_instance->mindiff;
if (!mindiff) if (mindiff) {
goto out; mindiff = MAX(ckp->mindiff, mindiff);
mindiff = MAX(ckp->mindiff, mindiff); if (mindiff != client->diff) {
if (mindiff != client->diff) { client->diff = mindiff;
client->diff = mindiff; stratum_send_diff(sdata, client);
stratum_send_diff(sdata, client); }
} }
out: out:
dec_instance_ref(sdata, client); dec_instance_ref(sdata, client);
out_noclient: out_noclient:

3
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 * 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 * 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 */ 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); void parse_remote_txns(ckpool_t *ckp, const json_t *val);
#define parse_upstream_txns(ckp, val) parse_remote_txns(ckp, val) #define parse_upstream_txns(ckp, val) parse_remote_txns(ckp, val)
void parse_upstream_auth(ckpool_t *ckp, json_t *val); void parse_upstream_auth(ckpool_t *ckp, json_t *val);

Loading…
Cancel
Save