diff --git a/pool/page.php b/pool/page.php
index d1183b4b..2f335fcf 100644
--- a/pool/page.php
+++ b/pool/page.php
@@ -110,8 +110,8 @@ span.err {color:red; font-weight:bold; font-size:120%;}
span.alert {color:red; font-weight:bold; font-size:250%;}
input.tiny {width: 0px; height: 0px; margin: 0px; padding: 0px; outline: none; border: 0px;}
#n42 {margin:0; position: relative; color:#fff; background:#07e;}
-#n42 a {color:#fff; text-decoration:none; margin: 4px;}
-#n42 td {min-width: 100px; float: left; vertical-align: top; padding: 2px;}
+#n42 a {color:#fff; text-decoration:none; padding: 6px; display:block;}
+#n42 td {min-width: 100px; float: left; vertical-align: top; padding: 0px 2px;}
#n42 td.navboxr {float: right;}
#n42 td.nav {position: relative;}
#n42 div.sub {left: 0px; z-index: 42; position: absolute; visibility: hidden;}
diff --git a/pool/page_pplns.php b/pool/page_pplns.php
index 1b46dd0e..116763aa 100644
--- a/pool/page_pplns.php
+++ b/pool/page_pplns.php
@@ -14,7 +14,7 @@ function stnum($num)
return $b4.$fmt.$af;
}
#
-# ... Of course ... check the output and add the txin
+# ... Of course ... check the output and add the txin ... etc.
function calctx($ans, $count, $miner_sat, $diffacc_total)
{
$pg = '
Name | '; diff --git a/pool/prime.php b/pool/prime.php index b4bd1dee..c50c69f7 100644 --- a/pool/prime.php +++ b/pool/prime.php @@ -69,8 +69,9 @@ function check() 'API' => 'api' ), 'Help' => array( - 'Help' => 'help', - 'Payouts' => 'payout' + 'Payouts' => 'payout', + 'Workers ' => 'workers', + 'Blocks' => 'blocks' ) ); tryLogInOut(); diff --git a/sql/ckdb.sql b/sql/ckdb.sql index f9bf6d73..aaa6eca7 100644 --- a/sql/ckdb.sql +++ b/sql/ckdb.sql @@ -251,6 +251,21 @@ CREATE TABLE sharesummary ( -- per workinfo for each user+worker ); +CREATE TABLE marks ( -- workinfoids to make workmarkers + poolinstance character varying(256) NOT NULL, + workinfoid bigint NOT NULL, + description character varying(256) DEFAULT ''::character varying NOT NULL, + marktype char NOT NULL, -- 'b'lock 'p'plns-begin 's'hift-begin 'e'=shift-end + status char NOT NULL, + createdate timestamp with time zone NOT NULL, + createby character varying(64) DEFAULT ''::character varying NOT NULL, + createcode character varying(128) DEFAULT ''::character varying NOT NULL, + createinet character varying(128) DEFAULT ''::character varying NOT NULL, + expirydate timestamp with time zone DEFAULT '6666-06-06 06:06:06+00', + PRIMARY KEY (poolinstance, workinfoid, expirydate) +); + + CREATE TABLE workmarkers ( -- range of workinfo for share accounting markerid bigint NOT NULL, poolinstance character varying(256) NOT NULL, @@ -263,7 +278,7 @@ CREATE TABLE workmarkers ( -- range of workinfo for share accounting createcode character varying(128) DEFAULT ''::character varying NOT NULL, createinet character varying(128) DEFAULT ''::character varying NOT NULL, expirydate timestamp with time zone DEFAULT '6666-06-06 06:06:06+00', - PRIMARY KEY (markerid) + PRIMARY KEY (markerid, expirydate) ); @@ -417,4 +432,4 @@ CREATE TABLE version ( PRIMARY KEY (vlock) ); -insert into version (vlock,version) values (1,'0.9.4'); +insert into version (vlock,version) values (1,'0.9.5'); diff --git a/sql/v0.9.4-v0.9.5.sql b/sql/v0.9.4-v0.9.5.sql new file mode 100644 index 00000000..c61a4e9c --- /dev/null +++ b/sql/v0.9.4-v0.9.5.sql @@ -0,0 +1,41 @@ +SET SESSION AUTHORIZATION 'postgres'; + +BEGIN transaction; + +DO $$ +DECLARE ver TEXT; +BEGIN + + UPDATE version set version='0.9.5' where vlock=1 and version='0.9.4'; + + IF found THEN + RETURN; + END IF; + + SELECT version into ver from version + WHERE vlock=1; + + RAISE EXCEPTION 'Wrong DB version - expect "0.9.4" - found "%"', ver; + +END $$; + +CREATE TABLE marks ( -- workinfoids to make workmarkers + poolinstance character varying(256) NOT NULL, + workinfoid bigint NOT NULL, + description character varying(256) DEFAULT ''::character varying NOT NULL, + marktype char NOT NULL, -- 'b'lock(end) 'p'plns-begin 's'hift-begin 'e'=shift-end + status char NOT NULL, + createdate timestamp with time zone NOT NULL, + createby character varying(64) DEFAULT ''::character varying NOT NULL, + createcode character varying(128) DEFAULT ''::character varying NOT NULL, + createinet character varying(128) DEFAULT ''::character varying NOT NULL, + expirydate timestamp with time zone DEFAULT '6666-06-06 06:06:06+00', + PRIMARY KEY (poolinstance, workinfoid, expirydate) +); + +ALTER TABLE workmarkers DROP CONSTRAINT workmarkers_pkey; + +ALTER TABLE workmarkers ADD CONSTRAINT workmarkers_pkey + PRIMARY KEY (markerid, expirydate); + +END transaction; diff --git a/src/ckdb.c b/src/ckdb.c index 3554a11e..c0305a38 100644 --- a/src/ckdb.c +++ b/src/ckdb.c @@ -174,8 +174,12 @@ const char *mailpatt = "^[A-Za-z0-9_-][A-Za-z0-9_\\.-]*@[A-Za-z0-9][A-Za-z0-9\\. const char *idpatt = "^[_A-Za-z][_A-Za-z0-9]*$"; const char *intpatt = "^[0-9][0-9]*$"; const char *hashpatt = "^[A-Fa-f0-9]*$"; -// TODO: bitcoind will check it properly -const char *addrpatt = "^[A-Za-z0-9]*$"; +/* BTC addresses start with '1' (one) or '3' (three), + * exclude: capital 'I' (eye), capital 'O' (oh), + * lowercase 'l' (elle) and '0' (zero) + * and with a simple test must be ADDR_MIN_LEN to ADDR_MAX_LEN (ckdb.h) + * bitcoind is used to fully validate them when required */ +const char *addrpatt = "^[13][A-HJ-NP-Za-km-z1-9]*$"; // So the records below have the same 'name' as the klist const char Transfer[] = "Transfer"; @@ -268,6 +272,8 @@ static tv_t reload_timestamp; * workinfo and sharesummary */ int64_t dbload_workinfoid_start = -1; int64_t dbload_workinfoid_finish = MAXID; +// Only restrict sharesummary, not workinfo +bool dbload_only_sharesummary = false; // DB users,workers,auth load is complete bool db_auths_complete = false; @@ -449,6 +455,11 @@ K_TREE *workmarkers_workinfoid_root; K_LIST *workmarkers_free; K_STORE *workmarkers_store; +// MARKS +K_TREE *marks_root; +K_LIST *marks_free; +K_STORE *marks_store; + static char logname[512]; static char *dbcode; @@ -728,6 +739,8 @@ static bool getdata3() } if (!(ok = workinfo_fill(conn)) || everyone_die) goto sukamudai; + if (!(ok = marks_fill(conn)) || everyone_die) + goto sukamudai; if (!(ok = workmarkers_fill(conn)) || everyone_die) goto sukamudai; if (!(ok = markersummary_fill(conn)) || everyone_die) @@ -1030,6 +1043,11 @@ static void alloc_storage() workmarkers_root = new_ktree(); workmarkers_workinfoid_root = new_ktree(); workmarkers_free->dsp_func = dsp_workmarkers; + + marks_free = k_new_list("Marks", sizeof(MARKS), + ALLOC_MARKS, LIMIT_MARKS, true); + marks_store = k_new_store(workmarkers_free); + marks_root = new_ktree(); } static void free_workinfo_data(K_ITEM *item) @@ -1048,6 +1066,11 @@ static void free_sharesummary_data(K_ITEM *item) SHARESUMMARY *sharesummary; DATA_SHARESUMMARY(sharesummary, item); + if (sharesummary->workername) { + LIST_MEM_SUB(sharesummary_free, sharesummary->workername); + free(sharesummary->workername); + sharesummary->workername = NULL; + } SET_CREATEBY(sharesummary_free, sharesummary->createby, EMPTY); SET_CREATECODE(sharesummary_free, sharesummary->createcode, EMPTY); SET_CREATEINET(sharesummary_free, sharesummary->createinet, EMPTY); @@ -1131,6 +1154,8 @@ static void dealloc_storage() { FREE_LISTS(logqueue); + FREE_ALL(marks); + FREE_TREE(workmarkers_workinfoid); FREE_TREE(workmarkers); FREE_STORE_DATA(workmarkers); @@ -1212,6 +1237,8 @@ static bool setup_data() if (dbload_workinfoid_start != -1) { LOGWARNING("WARNING: dbload starting at workinfoid %"PRId64, dbload_workinfoid_start); + if (dbload_only_sharesummary) + LOGWARNING("NOTICE: dbload only restricting sharesummary"); } if (!getdata3() || everyone_die) @@ -1586,7 +1613,7 @@ static void summarise_blocks() // Add up the sharesummaries, abort if any SUMMARY_NEW looksharesummary.workinfoid = wi_finish; looksharesummary.userid = MAXID; - looksharesummary.workername[0] = '\0'; + looksharesummary.workername = EMPTY; INIT_SHARESUMMARY(&ss_look); ss_look.data = (void *)(&looksharesummary); @@ -1652,7 +1679,7 @@ static void summarise_blocks() if (WMREADY(workmarkers->status)) { lookmarkersummary.markerid = workmarkers->markerid; lookmarkersummary.userid = MAXID; - lookmarkersummary.workername[0] = '\0'; + lookmarkersummary.workername = EMPTY; INIT_MARKERSUMMARY(&ms_look); ms_look.data = (void *)(&lookmarkersummary); ms_item = find_before_in_ktree(markersummary_root, &ms_look, @@ -2048,6 +2075,7 @@ static void *socketer(__maybe_unused void *arg) char *last_newid = NULL, *reply_newid = NULL; char *last_setatts = NULL, *reply_setatts = NULL; char *last_setopts = NULL, *reply_setopts = NULL; + char *last_userstatus = NULL, *reply_userstatus = NULL; char *last_web = NULL, *reply_web = NULL; char *reply_last, duptype[CMD_SIZ+1]; enum cmd_values cmdnum; @@ -2151,6 +2179,9 @@ static void *socketer(__maybe_unused void *arg) } else if (last_setopts && strcmp(last_setopts, buf) == 0) { reply_last = reply_setopts; dup = true; + } else if (last_userstatus && strcmp(last_userstatus, buf) == 0) { + reply_last = reply_userstatus; + dup = true; } else if (last_web && strcmp(last_web, buf) == 0) { reply_last = reply_web; dup = true; @@ -2251,6 +2282,7 @@ static void *socketer(__maybe_unused void *arg) case CMD_BLOCKLIST: case CMD_NEWID: case CMD_STATS: + case CMD_USERSTATUS: ans = ckdb_cmds[which_cmds].func(NULL, cmd, id, &now, by_default, (char *)__func__, @@ -2293,6 +2325,9 @@ static void *socketer(__maybe_unused void *arg) case CMD_SETOPTS: STORELASTREPLY(setopts); break; + case CMD_USERSTATUS: + STORELASTREPLY(userstatus); + break; // The rest default: free(rep); @@ -2499,6 +2534,7 @@ static bool reload_line(PGconn *conn, char *filename, uint64_t count, char *buf) case CMD_DSP: case CMD_STATS: case CMD_PPLNS: + case CMD_USERSTATUS: LOGERR("%s() Message line %"PRIu64" '%s' - invalid - ignored", __func__, count, cmd); break; @@ -2854,7 +2890,7 @@ static void compare_summaries(K_TREE *leftsum, char *leftname, looksharesummary.workinfoid = confirm_first_workinfoid; looksharesummary.userid = -1; - looksharesummary.workername[0] = '\0'; + looksharesummary.workername = EMPTY; INIT_SHARESUMMARY(&look); look.data = (void *)(&looksharesummary); @@ -3535,7 +3571,15 @@ int main(int argc, char **argv) case 'w': // Don't use this :) { - int64_t start = atoll(optarg); + char *ptr = optarg; + int64_t start; + + if (*ptr == 's') { + dbload_only_sharesummary = true; + ptr++; + } + + start = atoll(ptr); if (start < 0) { quit(1, "Invalid workinfoid start" " %"PRId64" - must be >= 0", diff --git a/src/ckdb.h b/src/ckdb.h index dd245ef3..47c1b9cc 100644 --- a/src/ckdb.h +++ b/src/ckdb.h @@ -51,8 +51,8 @@ */ #define DB_VLOCK "1" -#define DB_VERSION "0.9.4" -#define CKDB_VERSION DB_VERSION"-0.631" +#define DB_VERSION "0.9.5" +#define CKDB_VERSION DB_VERSION"-0.652" #define WHERE_FFL " - from %s %s() line %d" #define WHERE_FFL_HERE __FILE__, __func__, __LINE__ @@ -80,6 +80,14 @@ extern const char *intpatt; extern const char *hashpatt; extern const char *addrpatt; +/* If a trimmed username is like an address but this many or more characters, + * disallow it */ +#define ADDR_USER_CHECK 16 + +// BTC address size +#define ADDR_MIN_LEN 26 +#define ADDR_MAX_LEN 34 + typedef struct loadstatus { tv_t oldest_sharesummary_firstshare_n; tv_t newest_sharesummary_firstshare_a; @@ -232,6 +240,8 @@ extern int64_t confirm_last_workinfoid; * workinfo and sharesummary */ extern int64_t dbload_workinfoid_start; extern int64_t dbload_workinfoid_finish; +// Only restrict sharesummary, not workinfo +extern bool dbload_only_sharesummary; // DB users,workers,auth load is complete extern bool db_auths_complete; @@ -295,6 +305,7 @@ enum cmd_values { CMD_DSP, CMD_STATS, CMD_PPLNS, + CMD_USERSTATUS, CMD_END }; @@ -338,15 +349,26 @@ enum cmd_values { // The size strdup will allocate multiples of #define MEMBASE 4 +#define LIST_MEM_ADD(_list, _fld) do { \ + size_t __siz; \ + __siz = strlen(_fld) + 1; \ + if (__siz % MEMBASE) \ + __siz += MEMBASE - (__siz % MEMBASE); \ + _list->ram += (int)__siz; \ + } while (0) + +#define LIST_MEM_SUB(_list, _fld) do { \ + size_t __siz; \ + __siz = strlen(_fld) + 1; \ + if (__siz % MEMBASE) \ + __siz += MEMBASE - (__siz % MEMBASE); \ + _list->ram -= (int)__siz; \ + } while (0) + #define SET_POINTER(_list, _fld, _val, _def) do { \ - size_t _siz; \ if ((_fld) && ((_fld) != EMPTY) && ((_fld) != (_def))) { \ - if (_list) { \ - _siz = strlen(_fld) + 1; \ - if (_siz % MEMBASE) \ - _siz += MEMBASE - (_siz % MEMBASE); \ - _list->ram -= (int)_siz; \ - } \ + if (_list) \ + LIST_MEM_SUB(_list, _fld); \ free(_fld); \ } \ if (!(_val) || !(*(_val))) \ @@ -355,12 +377,8 @@ enum cmd_values { if (((_val) == (_def)) || (strcmp(_val, _def) == 0)) \ (_fld) = (_def); \ else { \ - if (_list) { \ - _siz = strlen(_val) + 1; \ - if (_siz % MEMBASE) \ - _siz += MEMBASE - (_siz % MEMBASE); \ - _list->ram += (int)_siz; \ - } \ + if (_list) \ + LIST_MEM_ADD(_list, _val); \ _fld = strdup(_val); \ if (!(_fld)) \ quithere(1, "malloc OOM"); \ @@ -603,9 +621,10 @@ typedef struct transfer { char *mvalue; } TRANSFER; -#define ALLOC_TRANSFER 64 +// Suggest malloc use MMAP - 1913 = largest under 2MB +#define ALLOC_TRANSFER 1913 #define LIMIT_TRANSFER 0 -#define CULL_TRANSFER 1024 +#define CULL_TRANSFER 16 #define INIT_TRANSFER(_item) INIT_GENERIC(_item, transfer) #define DATA_TRANSFER(_var, _item) DATA_GENERIC(_var, _item, transfer, true) @@ -632,7 +651,7 @@ typedef struct users { int64_t userid; char username[TXT_BIG+1]; char usertrim[TXT_BIG+1]; // Non DB field - // TODO: Anything in 'status' disables the account + // Anything in 'status' fails mining authentication char status[TXT_BIG+1]; char emailaddress[TXT_BIG+1]; tv_t joineddate; @@ -926,7 +945,7 @@ extern K_STORE *shareerrors_store; // SHARESUMMARY typedef struct sharesummary { int64_t userid; - char workername[TXT_BIG+1]; + char *workername; int64_t workinfoid; double diffacc; double diffsta; @@ -1323,6 +1342,58 @@ extern K_STORE *workmarkers_store; #define MARKER_COMPLETE 'x' #define WMREADY(_status) (tolower(_status[0]) == MARKER_COMPLETE) +// MARKS +// TODO: implement +typedef struct marks { + char *poolinstance; + int64_t workinfoid; + char *description; + char marktype[TXT_FLAG+1]; + char status[TXT_FLAG+1]; + HISTORYDATECONTROLFIELDS; +} MARKS; + +/* Marks: + * marktype is one of: + * b - block end + * p - pplns begin + * s - shift begin (not yet used) + * e - shift end (not yet used) + * description should one one of + * b - Block NNN stt + * p - Payout NNN fin (where NNN is the block number of the payout) + * s/e - to be decided + * + * WorkMarkers are from a begin workinfoid to an end workinfoid + * the "-1" and "+1" below mean adding to or subtracting from + * the workinfoid number + * + * Until we start using shifts: + * WorkMarkers can be created up to ending in the largest 'p' "-1" + * WorkMarkers will always be the smallest of: + * Block NNN-1 "+1" to Block NNN + * Block NNN "+1" to Payout MMM "-1" + * Payout MMM to Block NNN + * Payout MMM-1 to Payout MMM "-1" + * Thus to generate the WorkMarkers from the Marks: + * Find the last 'p' with no matching workinfoidbegin + * Then determine each previous WorkMarker based on each previous + * mark, using the above rules and stop when we find one that already exists + */ + +#define ALLOC_MARKS 1000 +#define LIMIT_MARKS 0 +#define INIT_MARKS(_item) INIT_GENERIC(_item, marks) +#define DATA_MARKS(_var, _item) DATA_GENERIC(_var, _item, marks, true) +#define DATA_MARKS_NULL(_var, _item) DATA_GENERIC(_var, _item, marks, false) + +extern K_TREE *marks_root; +extern K_LIST *marks_free; +extern K_STORE *marks_store; + +#define MARK_READY 'x' +#define MREADY(_status) (tolower(_status[0]) == MARK_READY) + extern void logmsg(int loglevel, const char *fmt, ...); extern void setnow(tv_t *now); extern void tick(); @@ -1334,6 +1405,7 @@ extern PGconn *dbconnect(); extern char *safe_text(char *txt); extern void username_trim(USERS *users); +extern bool like_address(char *username); extern void _txt_to_data(enum data_type typ, char *nam, char *fld, void *data, size_t siz, WHERE_FFL_ARGS); @@ -1487,6 +1559,7 @@ extern cmp_t cmp_workmarkers(K_ITEM *a, K_ITEM *b); extern cmp_t cmp_workmarkers_workinfoid(K_ITEM *a, K_ITEM *b); extern K_ITEM *find_workmarkers(int64_t workinfoid); extern K_ITEM *find_workmarkerid(int64_t markerid); +extern cmp_t cmp_marks(K_ITEM *a, K_ITEM *b); // *** // *** PostgreSQL functions ckdb_dbio.c @@ -1534,9 +1607,9 @@ extern char *pqerrmsg(PGconn *conn); extern int64_t nextid(PGconn *conn, char *idname, int64_t increment, tv_t *cd, char *by, char *code, char *inet); -extern bool users_pass_email(PGconn *conn, K_ITEM *u_item, char *oldhash, - char *newhash, char *email, char *by, char *code, - char *inet, tv_t *cd, K_TREE *trf_root); +extern bool users_update(PGconn *conn, K_ITEM *u_item, char *oldhash, + char *newhash, char *email, char *by, char *code, + char *inet, tv_t *cd, K_TREE *trf_root, char *status); extern K_ITEM *users_add(PGconn *conn, char *username, char *emailaddress, char *passwordhash, char *by, char *code, char *inet, tv_t *cd, K_TREE *trf_root); @@ -1630,6 +1703,7 @@ extern bool userstats_add(char *poolinstance, char *elapsed, char *username, extern bool userstats_fill(PGconn *conn); extern bool markersummary_fill(PGconn *conn); extern bool workmarkers_fill(PGconn *conn); +extern bool marks_fill(PGconn *conn); extern bool check_db_version(PGconn *conn); // *** diff --git a/src/ckdb_cmd.c b/src/ckdb_cmd.c index c7c580c8..1dcd7b6b 100644 --- a/src/ckdb_cmd.c +++ b/src/ckdb_cmd.c @@ -15,7 +15,7 @@ static char *cmd_adduser(PGconn *conn, char *cmd, char *id, tv_t *now, char *by, { char reply[1024] = ""; size_t siz = sizeof(reply); - K_ITEM *i_username, *i_emailaddress, *i_passwordhash, *u_item; + K_ITEM *i_username, *i_emailaddress, *i_passwordhash, *u_item = NULL; LOGDEBUG("%s(): cmd '%s'", __func__, cmd); @@ -23,18 +23,30 @@ static char *cmd_adduser(PGconn *conn, char *cmd, char *id, tv_t *now, char *by, if (!i_username) return strdup(reply); - i_emailaddress = require_name(trf_root, "emailaddress", 7, (char *)mailpatt, reply, siz); - if (!i_emailaddress) - return strdup(reply); + /* If a username added from the web site looks like an address + * then disallow it - a false positive is not an issue + * Allowing it will create a security issue - someone could create + * an account with someone else's, as yet unused, payout address + * and redirect the payout to another payout address. + * ... and the person who owns the payout address can't check that + * in advance, they'll just find out with their first payout not + * arriving at their payout address */ + if (!like_address(transfer_data(i_username))) { + i_emailaddress = require_name(trf_root, "emailaddress", 7, + (char *)mailpatt, reply, siz); + if (!i_emailaddress) + return strdup(reply); - i_passwordhash = require_name(trf_root, "passwordhash", 64, (char *)hashpatt, reply, siz); - if (!i_passwordhash) - return strdup(reply); + i_passwordhash = require_name(trf_root, "passwordhash", 64, + (char *)hashpatt, reply, siz); + if (!i_passwordhash) + return strdup(reply); - u_item = users_add(conn, transfer_data(i_username), - transfer_data(i_emailaddress), - transfer_data(i_passwordhash), - by, code, inet, now, trf_root); + u_item = users_add(conn, transfer_data(i_username), + transfer_data(i_emailaddress), + transfer_data(i_passwordhash), + by, code, inet, now, trf_root); + } if (!u_item) { LOGERR("%s() %s.failed.DBE", __func__, id); @@ -85,12 +97,13 @@ static char *cmd_newpass(__maybe_unused PGconn *conn, char *cmd, char *id, K_RUNLOCK(users_free); if (u_item) { - ok = users_pass_email(NULL, u_item, - oldhash, - transfer_data(i_newhash), - NULL, - by, code, inet, now, - trf_root); + ok = users_update(NULL, u_item, + oldhash, + transfer_data(i_newhash), + NULL, + by, code, inet, now, + trf_root, + NULL); } else ok = false; } @@ -223,7 +236,8 @@ static char *cmd_userset(PGconn *conn, char *cmd, char *id, email = NULL; } i_address = optional_name(trf_root, "address", - 27, (char *)addrpatt, + ADDR_MIN_LEN, + (char *)addrpatt, reply, siz); if (i_address) address = transfer_data(i_address); @@ -248,10 +262,12 @@ static char *cmd_userset(PGconn *conn, char *cmd, char *id, } if (email && *email) { - ok = users_pass_email(conn, u_item, NULL, - NULL, email, - by, code, inet, - now, trf_root); + ok = users_update(conn, u_item, + NULL, NULL, + email, + by, code, inet, now, + trf_root, + NULL); if (!ok) { reason = "email error"; goto struckout; @@ -2909,6 +2925,9 @@ static K_TREE *upd_add_mu(K_TREE *mu_root, K_STORE *mu_store, int64_t userid, in (also summarising diffacc per user) then keep stepping back until we complete the current begin_workinfoid (also summarising diffacc per user) + While we are still below diff_want + find each workmarker and add on the full set of worksummary + diffacc shares (also summarising diffacc per user) This will give us the total number of diff1 shares (diffacc_total) to use for the payment calculations The value of diff_want defaults to the block's network difficulty @@ -2922,21 +2941,26 @@ static K_TREE *upd_add_mu(K_TREE *mu_root, K_STORE *mu_store, int64_t userid, in diffacc_user * 2^32 / pplns_elapsed PPLNS fraction of the payout would be: diffacc_user / diffacc_total + + N.B. 'begin' means the oldest back in time and 'end' means the newest + 'end' should usually be the info of the found block with the pplns + data going back in time to 'begin' */ -/* TODO: redesign to include workmarkers - * ... before next payout that extends into a markersummary ... */ static char *cmd_pplns(__maybe_unused PGconn *conn, char *cmd, char *id, __maybe_unused tv_t *now, __maybe_unused char *by, __maybe_unused char *code, __maybe_unused char *inet, __maybe_unused tv_t *notcd, K_TREE *trf_root) { - char reply[1024], tmp[1024], *buf; + char reply[1024], tmp[1024], *buf, *block_extra, *share_status = EMPTY; size_t siz = sizeof(reply); K_ITEM *i_height, *i_difftimes, *i_diffadd, *i_allowaged; K_ITEM b_look, ss_look, *b_item, *w_item, *ss_item; + K_ITEM wm_look, *wm_item, ms_look, *ms_item; K_ITEM *mu_item, *wb_item, *u_item; SHARESUMMARY looksharesummary, *sharesummary; + WORKMARKERS lookworkmarkers, *workmarkers; + MARKERSUMMARY lookmarkersummary, *markersummary; MININGPAYOUTS *miningpayouts; WORKINFO *workinfo; TRANSFER *transfer; @@ -2945,13 +2969,14 @@ static char *cmd_pplns(__maybe_unused PGconn *conn, char *cmd, char *id, K_STORE *mu_store; USERS *users; int32_t height; - int64_t workinfoid, end_workinfoid; + int64_t workinfoid, end_workinfoid = 0; int64_t begin_workinfoid; - int64_t share_count; + int64_t total_share_count, acc_share_count; + int64_t ss_count, wm_count, ms_count; char tv_buf[DATE_BUFSIZ]; tv_t cd, begin_tv, block_tv, end_tv; - K_TREE_CTX ctx[1]; - double ndiff, total, elapsed; + K_TREE_CTX ctx[1], wm_ctx[1], ms_ctx[1]; + double ndiff, total_diff, elapsed; double diff_times = 1.0; double diff_add = 0.0; double diff_want; @@ -2982,6 +3007,7 @@ static char *cmd_pplns(__maybe_unused PGconn *conn, char *cmd, char *id, allow_aged = true; } + block_tv.tv_sec = block_tv.tv_usec = 0L; cd.tv_sec = cd.tv_usec = 0L; lookblocks.height = height + 1; lookblocks.blockhash[0] = '\0'; @@ -2996,20 +3022,39 @@ static char *cmd_pplns(__maybe_unused PGconn *conn, char *cmd, char *id, } DATA_BLOCKS_NULL(blocks, b_item); while (b_item && blocks->height == height) { - if (blocks->confirmed[0] == BLOCKS_CONFIRM) + if (blocks->confirmed[0] == BLOCKS_NEW) { + copy_tv(&block_tv, &(blocks->createdate)); + copy_tv(&end_tv, &(blocks->createdate)); + } + // Allow any state, but report it + if (CURRENT(&(blocks->expirydate))) break; b_item = prev_in_ktree(ctx); DATA_BLOCKS_NULL(blocks, b_item); } K_RUNLOCK(blocks_free); if (!b_item || blocks->height != height) { - snprintf(reply, siz, "ERR.unconfirmed block %d", height); + snprintf(reply, siz, "ERR.no CURRENT block %d", height); return strdup(reply); } + if (block_tv.tv_sec == 0) { + snprintf(reply, siz, "ERR.block %d missing '%s' record", + height, + blocks_confirmed(BLOCKS_NEW_STR)); + return strdup(reply); + } + switch (blocks->confirmed[0]) { + case BLOCKS_NEW: + block_extra = "Can't be paid out yet"; + break; + case BLOCKS_ORPHAN: + block_extra = "Can't be paid out"; + break; + default: + block_extra = EMPTY; + break; + } workinfoid = blocks->workinfoid; - copy_tv(&block_tv, &(blocks->createdate)); - copy_tv(&end_tv, &(blocks->createdate)); - w_item = find_workinfo(workinfoid); if (!w_item) { snprintf(reply, siz, "ERR.missing workinfo %"PRId64, workinfoid); @@ -3028,34 +3073,30 @@ static char *cmd_pplns(__maybe_unused PGconn *conn, char *cmd, char *id, } begin_workinfoid = 0; - share_count = 0; - total = 0; + total_share_count = acc_share_count = 0; + total_diff = 0; + ss_count = wm_count = ms_count = 0; + + mu_store = k_new_store(miningpayouts_free); + mu_root = new_ktree(); looksharesummary.workinfoid = workinfoid; looksharesummary.userid = MAXID; - looksharesummary.workername[0] = '\0'; + looksharesummary.workername = EMPTY; INIT_SHARESUMMARY(&ss_look); ss_look.data = (void *)(&looksharesummary); K_RLOCK(sharesummary_free); + K_RLOCK(workmarkers_free); + K_RLOCK(markersummary_free); ss_item = find_before_in_ktree(sharesummary_workinfoid_root, &ss_look, cmp_sharesummary_workinfoid, ctx); - if (!ss_item) { - K_RUNLOCK(sharesummary_free); - snprintf(reply, siz, - "ERR.no shares found with or before " - "workinfo %"PRId64, - workinfoid); - return strdup(reply); - } - DATA_SHARESUMMARY(sharesummary, ss_item); - - mu_store = k_new_store(miningpayouts_free); - mu_root = new_ktree(); - end_workinfoid = sharesummary->workinfoid; + DATA_SHARESUMMARY_NULL(sharesummary, ss_item); + if (ss_item) + end_workinfoid = sharesummary->workinfoid; /* add up all sharesummaries until >= diff_want * also record the latest lastshare - that will be the end pplns time * which will be >= block_tv */ - while (ss_item && total < diff_want) { + while (total_diff < diff_want && ss_item) { switch (sharesummary->complete[0]) { case SUMMARY_CONFIRM: break; @@ -3063,15 +3104,12 @@ static char *cmd_pplns(__maybe_unused PGconn *conn, char *cmd, char *id, if (allow_aged) break; default: - K_RUNLOCK(sharesummary_free); - snprintf(reply, siz, - "ERR.sharesummary1 not ready in " - "workinfo %"PRId64, - sharesummary->workinfoid); - goto shazbot; + share_status = "Not ready1"; } - share_count++; - total += (int64_t)(sharesummary->diffacc); + ss_count++; + total_share_count += sharesummary->sharecount; + acc_share_count += sharesummary->shareacc; + total_diff += (int64_t)(sharesummary->diffacc); begin_workinfoid = sharesummary->workinfoid; if (tv_newer(&end_tv, &(sharesummary->lastshare))) copy_tv(&end_tv, &(sharesummary->lastshare)); @@ -3091,24 +3129,71 @@ static char *cmd_pplns(__maybe_unused PGconn *conn, char *cmd, char *id, if (allow_aged) break; default: - K_RUNLOCK(sharesummary_free); - snprintf(reply, siz, - "ERR.sharesummary2 not ready in " - "workinfo %"PRId64, - sharesummary->workinfoid); - goto shazbot; + if (share_status == EMPTY) + share_status = "Not ready2"; + else + share_status = "Not ready1+2"; } - share_count++; - total += (int64_t)(sharesummary->diffacc); + ss_count++; + total_share_count += sharesummary->sharecount; + acc_share_count += sharesummary->shareacc; + total_diff += (int64_t)(sharesummary->diffacc); mu_root = upd_add_mu(mu_root, mu_store, sharesummary->userid, (int64_t)(sharesummary->diffacc)); ss_item = prev_in_ktree(ctx); DATA_SHARESUMMARY_NULL(sharesummary, ss_item); } + + /* If we haven't met or exceeded the required N, + * move on to the markersummaries */ + if (total_diff < diff_want) { + lookworkmarkers.expirydate.tv_sec = default_expiry.tv_sec; + lookworkmarkers.expirydate.tv_usec = default_expiry.tv_usec; + lookworkmarkers.workinfoidend = begin_workinfoid; + INIT_WORKMARKERS(&wm_look); + wm_look.data = (void *)(&lookworkmarkers); + wm_item = find_before_in_ktree(workmarkers_workinfoid_root, &wm_look, + cmp_workmarkers_workinfoid, wm_ctx); + DATA_WORKMARKERS_NULL(workmarkers, wm_item); + while (total_diff < diff_want && wm_item && CURRENT(&(workmarkers->expirydate))) { + if (WMREADY(workmarkers->status)) { + wm_count++; + lookmarkersummary.markerid = workmarkers->markerid; + lookmarkersummary.userid = MAXID; + lookmarkersummary.workername = EMPTY; + INIT_MARKERSUMMARY(&ms_look); + ms_look.data = (void *)(&lookmarkersummary); + ms_item = find_before_in_ktree(markersummary_root, &ms_look, + cmp_markersummary, ms_ctx); + DATA_MARKERSUMMARY_NULL(markersummary, ms_item); + // add the whole markerid + while (ms_item && markersummary->markerid == workmarkers->markerid) { + if (end_workinfoid == 0) + end_workinfoid = workmarkers->workinfoidend; + ms_count++; + total_share_count += markersummary->sharecount; + acc_share_count += markersummary->shareacc; + total_diff += (int64_t)(markersummary->diffacc); + begin_workinfoid = workmarkers->workinfoidstart; + if (tv_newer(&end_tv, &(markersummary->lastshare))) + copy_tv(&end_tv, &(markersummary->lastshare)); + mu_root = upd_add_mu(mu_root, mu_store, + markersummary->userid, + (int64_t)(markersummary->diffacc)); + ms_item = prev_in_ktree(ms_ctx); + DATA_MARKERSUMMARY_NULL(markersummary, ms_item); + } + } + wm_item = prev_in_ktree(wm_ctx); + DATA_WORKMARKERS_NULL(workmarkers, wm_item); + } + } + K_RUNLOCK(markersummary_free); + K_RUNLOCK(workmarkers_free); K_RUNLOCK(sharesummary_free); - if (total == 0.0) { + if (total_diff == 0.0) { snprintf(reply, siz, "ERR.total share diff 0 before workinfo %"PRId64, workinfoid); @@ -3140,6 +3225,13 @@ static char *cmd_pplns(__maybe_unused PGconn *conn, char *cmd, char *id, APPEND_REALLOC(buf, off, len, tmp); snprintf(tmp, sizeof(tmp), "block_reward=%"PRId64"%c", blocks->reward, FLDSEP); APPEND_REALLOC(buf, off, len, tmp); + snprintf(tmp, sizeof(tmp), "block_status=%s%c", + blocks_confirmed(blocks->confirmed), FLDSEP); + APPEND_REALLOC(buf, off, len, tmp); + snprintf(tmp, sizeof(tmp), "block_extra=%s%c", block_extra, FLDSEP); + APPEND_REALLOC(buf, off, len, tmp); + snprintf(tmp, sizeof(tmp), "share_status=%s%c", share_status, FLDSEP); + APPEND_REALLOC(buf, off, len, tmp); snprintf(tmp, sizeof(tmp), "workername=%s%c", blocks->workername, FLDSEP); APPEND_REALLOC(buf, off, len, tmp); snprintf(tmp, sizeof(tmp), "nonce=%s%c", blocks->nonce, FLDSEP); @@ -3150,7 +3242,7 @@ static char *cmd_pplns(__maybe_unused PGconn *conn, char *cmd, char *id, APPEND_REALLOC(buf, off, len, tmp); snprintf(tmp, sizeof(tmp), "end_workinfoid=%"PRId64"%c", end_workinfoid, FLDSEP); APPEND_REALLOC(buf, off, len, tmp); - snprintf(tmp, sizeof(tmp), "diffacc_total=%.0f%c", total, FLDSEP); + snprintf(tmp, sizeof(tmp), "diffacc_total=%.0f%c", total_diff, FLDSEP); APPEND_REALLOC(buf, off, len, tmp); snprintf(tmp, sizeof(tmp), "pplns_elapsed=%f%c", elapsed, FLDSEP); APPEND_REALLOC(buf, off, len, tmp); @@ -3233,7 +3325,17 @@ static char *cmd_pplns(__maybe_unused PGconn *conn, char *cmd, char *id, APPEND_REALLOC(buf, off, len, tmp); snprintf(tmp, sizeof(tmp), "diff_want=%f%c", diff_want, FLDSEP); APPEND_REALLOC(buf, off, len, tmp); - snprintf(tmp, sizeof(tmp), "share_count=%"PRId64, share_count); + snprintf(tmp, sizeof(tmp), "acc_share_count=%"PRId64"%c", + acc_share_count, FLDSEP); + APPEND_REALLOC(buf, off, len, tmp); + snprintf(tmp, sizeof(tmp), "total_share_count=%"PRId64"%c", + total_share_count, FLDSEP); + APPEND_REALLOC(buf, off, len, tmp); + snprintf(tmp, sizeof(tmp), "ss_count=%"PRId64"%c", ss_count, FLDSEP); + APPEND_REALLOC(buf, off, len, tmp); + snprintf(tmp, sizeof(tmp), "wm_count=%"PRId64"%c", wm_count, FLDSEP); + APPEND_REALLOC(buf, off, len, tmp); + snprintf(tmp, sizeof(tmp), "ms_count=%"PRId64, ms_count); APPEND_REALLOC(buf, off, len, tmp); mu_root = free_ktree(mu_root, NULL); @@ -3379,6 +3481,69 @@ static char *cmd_stats(__maybe_unused PGconn *conn, char *cmd, char *id, return buf; } +// TODO: add to heartbeat to disable the miner if active and status != "" +static char *cmd_userstatus(PGconn *conn, char *cmd, char *id, tv_t *now, char *by, + char *code, char *inet, __maybe_unused tv_t *cd, + K_TREE *trf_root) +{ + char reply[1024] = ""; + size_t siz = sizeof(reply); + K_ITEM *i_username, *i_userid, *i_status, *u_item; + int64_t userid; + char *status; + USERS *users; + bool ok; + + LOGDEBUG("%s(): cmd '%s'", __func__, cmd); + + i_username = optional_name(trf_root, "username", 3, (char *)userpatt, reply, siz); + i_userid = optional_name(trf_root, "userid", 1, (char *)intpatt, reply, siz); + // Either username or userid + if (!i_username && !i_userid) { + snprintf(reply, siz, "failed.invalid/missing userinfo"); + LOGERR("%s.%s", id, reply); + return strdup(reply); + } + + // A zero length status re-enables it + i_status = require_name(trf_root, "status", 0, NULL, reply, siz); + if (!i_status) + return strdup(reply); + status = transfer_data(i_status); + + K_RLOCK(users_free); + if (i_username) + u_item = find_users(transfer_data(i_username)); + else { + TXT_TO_BIGINT("userid", transfer_data(i_userid), userid); + u_item = find_userid(userid); + } + K_RUNLOCK(users_free); + + if (!u_item) + ok = false; + else { + ok = users_update(conn, u_item, + NULL, NULL, + NULL, + by, code, inet, now, + trf_root, + status); + } + + if (!ok) { + LOGERR("%s() %s.failed.DBE", __func__, id); + return strdup("failed.DBE"); + } + DATA_USERS(users, u_item); + snprintf(reply, siz, "ok.updated %"PRId64" %s status %s", + users->userid, + users->username, + status[0] ? "disabled" : "enabled"); + LOGWARNING("%s.%s", id, reply); + return strdup(reply); +} + // TODO: limit access by having seperate sockets for each #define ACCESS_POOL "p" #define ACCESS_SYSTEM "s" @@ -3479,7 +3644,8 @@ struct CMDS ckdb_cmds[] = { { CMD_GETOPTS, "getopts", false, false, cmd_getopts, ACCESS_WEB }, { CMD_SETOPTS, "setopts", false, false, cmd_setopts, ACCESS_WEB }, { CMD_DSP, "dsp", false, false, cmd_dsp, ACCESS_SYSTEM }, - { CMD_STATS, "stats", true, false, cmd_stats, ACCESS_SYSTEM }, - { CMD_PPLNS, "pplns", false, false, cmd_pplns, ACCESS_SYSTEM }, + { CMD_STATS, "stats", true, false, cmd_stats, ACCESS_SYSTEM ACCESS_WEB }, + { CMD_PPLNS, "pplns", false, false, cmd_pplns, ACCESS_SYSTEM ACCESS_WEB }, + { CMD_USERSTATUS,"userstatus", false, false, cmd_userstatus, ACCESS_SYSTEM ACCESS_WEB }, { CMD_END, NULL, false, false, NULL, NULL } }; diff --git a/src/ckdb_data.c b/src/ckdb_data.c index e712f520..241fdac3 100644 --- a/src/ckdb_data.c +++ b/src/ckdb_data.c @@ -67,6 +67,55 @@ void username_trim(USERS *users) } } +/* Is the trimmed username like an address? + * False positive is OK (i.e. 'like') + * Before checking, it is trimmed to avoid web display confusion + * Length check is done before trimming - this may give a false + * positive on any username with lots of trim characters ... which is OK */ +bool like_address(char *username) +{ + char *tmp, *front, *trail; + size_t len; + regex_t re; + int ret; + + len = strlen(username); + if (len < ADDR_USER_CHECK) + return false; + + tmp = strdup(username); + front = tmp; + while (*front && TRIM_IGNORE(*front)) + front++; + + trail = front + strlen(front) - 1; + while (trail >= front) { + if (TRIM_IGNORE(*trail)) + *(trail--) = '\0'; + else + break; + } + + if (regcomp(&re, addrpatt, REG_NOSUB) != 0) { + LOGEMERG("%s(): failed to compile addrpatt '%s'", + __func__, addrpatt); + free(tmp); + // This will disable adding any new usernames ... + return true; + } + + ret = regexec(&re, front, (size_t)0, NULL, 0); + regfree(&re); + + if (ret == 0) { + free(tmp); + return true; + } + + free(tmp); + return false; +} + void _txt_to_data(enum data_type typ, char *nam, char *fld, void *data, size_t siz, WHERE_FFL_ARGS) { char *tmp; @@ -661,7 +710,7 @@ void workerstatus_ready() // The last one looksharesummary.userid = workerstatus->userid; - STRNCPY(looksharesummary.workername, workerstatus->workername); + looksharesummary.workername = workerstatus->workername; looksharesummary.workinfoid = MAXID; ss_look.data = (void *)(&looksharesummary); K_RLOCK(sharesummary_free); @@ -1327,7 +1376,7 @@ bool workinfo_age(PGconn *conn, int64_t workinfoid, char *poolinstance, // Find the first matching sharesummary looksharesummary.workinfoid = workinfoid; looksharesummary.userid = -1; - looksharesummary.workername[0] = '\0'; + looksharesummary.workername = EMPTY; ok = true; ss_tot = ss_already = ss_failed = shares_tot = shares_dumped = 0; @@ -1558,7 +1607,7 @@ K_ITEM *find_sharesummary(int64_t userid, char *workername, int64_t workinfoid) K_ITEM look; sharesummary.userid = userid; - STRNCPY(sharesummary.workername, workername); + sharesummary.workername = workername; sharesummary.workinfoid = workinfoid; INIT_SHARESUMMARY(&look); @@ -1594,7 +1643,7 @@ void auto_age_older(PGconn *conn, int64_t workinfoid, char *poolinstance, // Find the oldest 'unaged' sharesummary < workinfoid and >= prev_found looksharesummary.workinfoid = prev_found; looksharesummary.userid = -1; - looksharesummary.workername[0] = '\0'; + looksharesummary.workername = EMPTY; INIT_SHARESUMMARY(&look); look.data = (void *)(&looksharesummary); @@ -1907,8 +1956,7 @@ void set_block_share_counters() if (sharesummary->workinfoid <= pool.workinfoid) { // Skip back to the next worker looksharesummary.userid = sharesummary->userid; - STRNCPY(looksharesummary.workername, - sharesummary->workername); + looksharesummary.workername = sharesummary->workername; looksharesummary.workinfoid = -1; ss_look.data = (void *)(&looksharesummary); ss_item = find_before_in_ktree(sharesummary_root, &ss_look, @@ -2386,3 +2434,16 @@ K_ITEM *find_workmarkerid(int64_t markerid) return wm_item; } +// order by expirydate asc,workinfoid asc +// TODO: add poolinstance +cmp_t cmp_marks(K_ITEM *a, K_ITEM *b) +{ + MARKS *ma, *mb; + DATA_MARKS(ma, a); + DATA_MARKS(mb, b); + cmp_t c = CMP_TV(ma->expirydate, mb->expirydate); + if (c == 0) + c = CMP_BIGINT(ma->workinfoid, mb->workinfoid); + return c; +} + diff --git a/src/ckdb_dbio.c b/src/ckdb_dbio.c index 8c0b3d52..7d5bddd7 100644 --- a/src/ckdb_dbio.c +++ b/src/ckdb_dbio.c @@ -304,9 +304,10 @@ cleanup: return lastid; } -bool users_pass_email(PGconn *conn, K_ITEM *u_item, char *oldhash, - char *newhash, char *email, char *by, char *code, - char *inet, tv_t *cd, K_TREE *trf_root) +// status was added to the end so type checking intercepts new mistakes +bool users_update(PGconn *conn, K_ITEM *u_item, char *oldhash, + char *newhash, char *email, char *by, char *code, + char *inet, tv_t *cd, K_TREE *trf_root, char *status) { ExecStatusType rescode; bool conned = false; @@ -315,7 +316,7 @@ bool users_pass_email(PGconn *conn, K_ITEM *u_item, char *oldhash, USERS *row, *users; char *upd, *ins; bool ok = false; - char *params[5 + HISTORYDATECOUNT]; + char *params[6 + HISTORYDATECOUNT]; bool hash; int n, par = 0; @@ -337,14 +338,18 @@ bool users_pass_email(PGconn *conn, K_ITEM *u_item, char *oldhash, DATA_USERS(row, item); memcpy(row, users, sizeof(*row)); - // Update one, leave the other + + // Update each one supplied if (hash) { // New salt each password change make_salt(row); password_hash(row->username, newhash, row->salt, row->passwordhash, sizeof(row->passwordhash)); - } else + } + if (email) STRNCPY(row->emailaddress, email); + if (status) + STRNCPY(row->status, status); HISTORYDATEINIT(row, cd, by, code, inet); HISTORYDATETRANSFER(trf_root, row); @@ -384,21 +389,22 @@ bool users_pass_email(PGconn *conn, K_ITEM *u_item, char *oldhash, par = 0; params[par++] = bigint_to_buf(row->userid, NULL, 0); params[par++] = tv_to_buf(cd, NULL, 0); - // Copy them both in - one will be new and one will be old + // Copy them all in - at least one will be new + params[par++] = str_to_buf(row->status, NULL, 0); params[par++] = str_to_buf(row->emailaddress, NULL, 0); params[par++] = str_to_buf(row->passwordhash, NULL, 0); // New salt for each password change (or recopy old) params[par++] = str_to_buf(row->salt, NULL, 0); HISTORYDATEPARAMS(params, par, row); - PARCHKVAL(par, 5 + HISTORYDATECOUNT, params); // 10 as per ins + PARCHKVAL(par, 6 + HISTORYDATECOUNT, params); // 11 as per ins ins = "insert into users " "(userid,username,status,emailaddress,joineddate," "passwordhash,secondaryuserid,salt" HISTORYDATECONTROL ") select " - "userid,username,status,$3,joineddate," - "$4,secondaryuserid,$5," - "$6,$7,$8,$9,$10 from users where " + "userid,username,$3,$4,joineddate," + "$5,secondaryuserid,$6," + "$7,$8,$9,$10,$11 from users where " "userid=$1 and expirydate=$2"; res = PQexecParams(conn, ins, par, NULL, (const char **)params, NULL, NULL, 0, CKPQ_WRITE); @@ -2268,7 +2274,10 @@ bool workinfo_fill(PGconn *conn) " workinfoid in (select workinfoid from blocks) )"; par = 0; params[par++] = tv_to_buf((tv_t *)(&default_expiry), NULL, 0); - params[par++] = bigint_to_buf(dbload_workinfoid_start, NULL, 0); + if (dbload_only_sharesummary) + params[par++] = bigint_to_buf(-1, NULL, 0); + else + params[par++] = bigint_to_buf(dbload_workinfoid_start, NULL, 0); params[par++] = bigint_to_buf(dbload_workinfoid_finish, NULL, 0); PARCHK(par, params); res = PQexecParams(conn, sel, par, NULL, (const char **)params, NULL, NULL, 0, CKPQ_READ); @@ -2713,7 +2722,8 @@ bool _sharesummary_update(PGconn *conn, SHARES *s_row, SHAREERRORS *e_row, K_ITE K_WUNLOCK(sharesummary_free); DATA_SHARESUMMARY(row, item); row->userid = userid; - STRNCPY(row->workername, workername); + row->workername = strdup(workername); + LIST_MEM_ADD(sharesummary_free, row->workername); row->workinfoid = workinfoid; zero_sharesummary(row, sharecreatedate, diff); row->inserted = false; @@ -2942,15 +2952,15 @@ late: PQfinish(conn); // We keep the new item no matter what 'ok' is, since it will be inserted later - K_WLOCK(sharesummary_free); if (new) { + K_WLOCK(sharesummary_free); sharesummary_root = add_to_ktree(sharesummary_root, item, cmp_sharesummary); sharesummary_workinfoid_root = add_to_ktree(sharesummary_workinfoid_root, item, cmp_sharesummary_workinfoid); k_add_head(sharesummary_store, item); + K_WUNLOCK(sharesummary_free); } - K_WUNLOCK(sharesummary_free); return ok; } @@ -3004,6 +3014,7 @@ bool sharesummary_fill(PGconn *conn) for (i = 0; i < n; i++) { item = k_unlink_head(sharesummary_free); DATA_SHARESUMMARY(row, item); + row->workername = NULL; if (everyone_die) { ok = false; @@ -3020,7 +3031,8 @@ bool sharesummary_fill(PGconn *conn) PQ_GET_FLD(res, i, "workername", field, ok); if (!ok) break; - TXT_TO_STR("workername", field, row->workername); + row->workername = strdup(field); + LIST_MEM_ADD(sharesummary_free, row->workername); PQ_GET_FLD(res, i, "workinfoid", field, ok); if (!ok) @@ -3147,8 +3159,15 @@ bool sharesummary_fill(PGconn *conn) tick(); } - if (!ok) + if (!ok) { + DATA_SHARESUMMARY(row, item); + if (row->workername) { + LIST_MEM_SUB(sharesummary_free, row->workername); + free(row->workername); + row->workername = NULL; + } k_add_head(sharesummary_free, item); + } PQclear(res); @@ -4055,6 +4074,10 @@ bool auths_add(PGconn *conn, char *poolinstance, char *username, } DATA_USERS(*users, u_item); + // Any status content means disallow mining + if ((*users)->status[0]) + goto unitem; + STRNCPY(row->poolinstance, poolinstance); row->userid = (*users)->userid; // since update=false, a dup will be ok and do nothing when igndup=true @@ -5088,6 +5111,100 @@ bool workmarkers_fill(PGconn *conn) return ok; } +bool marks_fill(PGconn *conn) +{ + ExecStatusType rescode; + PGresult *res; + K_ITEM *item; + int n, i; + MARKS *row; + char *field; + char *sel; + int fields = 5; + bool ok; + + LOGDEBUG("%s(): select", __func__); + + // TODO: limit how far back + sel = "select " + "poolinstance,workinfoid,description,marktype,status" + HISTORYDATECONTROL + " from marks"; + res = PQexec(conn, sel, CKPQ_READ); + rescode = PQresultStatus(res); + if (!PGOK(rescode)) { + PGLOGERR("Select", rescode, conn); + PQclear(res); + return false; + } + + n = PQnfields(res); + if (n != (fields + HISTORYDATECOUNT)) { + LOGERR("%s(): Invalid field count - should be %d, but is %d", + __func__, fields + HISTORYDATECOUNT, n); + PQclear(res); + return false; + } + + n = PQntuples(res); + LOGDEBUG("%s(): tree build count %d", __func__, n); + ok = true; + for (i = 0; i < n; i++) { + item = k_unlink_head(marks_free); + DATA_MARKS(row, item); + + if (everyone_die) { + ok = false; + break; + } + + PQ_GET_FLD(res, i, "poolinstance", field, ok); + if (!ok) + break; + TXT_TO_PTR("poolinstance", field, row->poolinstance); + + PQ_GET_FLD(res, i, "workinfoid", field, ok); + if (!ok) + break; + TXT_TO_BIGINT("workinfoid", field, row->workinfoid); + + PQ_GET_FLD(res, i, "description", field, ok); + if (!ok) + break; + TXT_TO_PTR("description", field, row->description); + + PQ_GET_FLD(res, i, "marktype", field, ok); + if (!ok) + break; + TXT_TO_STR("marktype", field, row->marktype); + + PQ_GET_FLD(res, i, "status", field, ok); + if (!ok) + break; + TXT_TO_STR("status", field, row->status); + + HISTORYDATEFLDS(res, i, row, ok); + if (!ok) + break; + + marks_root = add_to_ktree(marks_root, item, cmp_marks); + k_add_head(marks_store, item); + + tick(); + } + if (!ok) + k_add_head(marks_free, item); + + PQclear(res); + + if (ok) { + LOGDEBUG("%s(): built", __func__); + LOGWARNING("%s(): loaded %d marks records", __func__, n); + } + + return ok; +} + bool check_db_version(PGconn *conn) { ExecStatusType rescode;