diff --git a/pool/db.php b/pool/db.php index b2cb3b85..39e9f577 100644 --- a/pool/db.php +++ b/pool/db.php @@ -386,6 +386,18 @@ function getBlocks($user) return repDecode($rep); } # +function getUserInfo($user) +{ + if ($user == false) + showIndex(); + $flds = array('username' => $user); + $msg = msgEncode('userinfo', 'usr', $flds, $user); + $rep = sendsockreply('getUserInfo', $msg); + if (!$rep) + dbdown(); + return repDecode($rep); +} +# # e.g. $atts = array('ua_Reset.str' => 'FortyTwo', # 'ua_Reset.date' => 'now+3600') # 'ua_Tanuki.str' => 'Meme', diff --git a/pool/page_userinfo.php b/pool/page_userinfo.php new file mode 100644 index 00000000..1531fa1d --- /dev/null +++ b/pool/page_userinfo.php @@ -0,0 +1,62 @@ +Block Hall of Fame'.$pg; + $pg .= "\n"; + $pg .= ""; + $pg .= ""; + $pg .= ""; + $pg .= ""; + $pg .= "\n"; + + if ($ans['STATUS'] == 'ok') + { + $all = array(); + $count = $ans['rows']; + for ($i = 0; $i < $count; $i++) + { + $all[] = array('blocks' => $ans['blocks:'.$i], + 'username' => $ans['username:'.$i], + 'diffacc' => $ans['diffacc:'.$i]); + } + usort($all, 'blocksorder'); + + for ($i = 0; $i < $count; $i++) + { + if (($i % 2) == 0) + $row = 'even'; + else + $row = 'odd'; + + $pg .= ""; + $un = htmlspecialchars($all[$i]['username']); + $pg .= ""; + $bl = $all[$i]['blocks']; + $pg .= ""; + $diffacc = difffmt($all[$i]['diffacc']); + $pg .= ""; + $pg .= "\n"; + } + } + $pg .= "
UserBlocksDiff
$un$bl$diffacc
\n"; + + return $pg; +} +# +function show_userinfo($info, $page, $menu, $name, $user) +{ + gopage($info, NULL, 'douserinfo', $page, $menu, $name, $user); +} +# +?> diff --git a/src/ckdb.c b/src/ckdb.c index 666f587f..d3ee6728 100644 --- a/src/ckdb.c +++ b/src/ckdb.c @@ -510,6 +510,11 @@ const char *marktype_other_finish_fmt = "fin: %s"; const char *marktype_shift_begin_skip = "Shift stt: "; const char *marktype_shift_end_skip = "Shift fin: "; +// USERINFO from various incoming data +K_TREE *userinfo_root; +K_LIST *userinfo_free; +K_STORE *userinfo_store; + static char logname[512]; static char *dbcode; @@ -1145,6 +1150,11 @@ static void alloc_storage() ALLOC_MARKS, LIMIT_MARKS, true); marks_store = k_new_store(marks_free); marks_root = new_ktree(); + + userinfo_free = k_new_list("UserInfo", sizeof(USERINFO), + ALLOC_USERINFO, LIMIT_USERINFO, true); + userinfo_store = k_new_store(userinfo_free); + userinfo_root = new_ktree(); } #define SEQSETMSG(_set, _seqset, _msgtxt, _endtxt) do { \ @@ -1282,6 +1292,8 @@ static void dealloc_storage() FREE_LISTS(logqueue); + FREE_ALL(userinfo); + FREE_TREE(marks); FREE_STORE_DATA(marks); FREE_LIST_DATA(marks); @@ -3953,6 +3965,7 @@ static void *socketer(__maybe_unused void *arg) case CMD_STATS: case CMD_USERSTATUS: case CMD_SHSTA: + case CMD_USERINFO: ans = ckdb_cmds[msgline->which_cmds].func(NULL, msgline->cmd, msgline->id, @@ -4275,6 +4288,7 @@ static void reload_line(PGconn *conn, char *filename, uint64_t count, char *buf) case CMD_MARKS: case CMD_PSHIFT: case CMD_SHSTA: + case CMD_USERINFO: LOGERR("%s() INVALID message line %"PRIu64 " ignored '%.42s...", __func__, count, diff --git a/src/ckdb.h b/src/ckdb.h index c0e92fe8..4c053ef3 100644 --- a/src/ckdb.h +++ b/src/ckdb.h @@ -55,7 +55,7 @@ #define DB_VLOCK "1" #define DB_VERSION "1.0.0" -#define CKDB_VERSION DB_VERSION"-1.092" +#define CKDB_VERSION DB_VERSION"-1.093" #define WHERE_FFL " - from %s %s() line %d" #define WHERE_FFL_HERE __FILE__, __func__, __LINE__ @@ -403,6 +403,7 @@ enum cmd_values { CMD_MARKS, CMD_PSHIFT, CMD_SHSTA, + CMD_USERINFO, CMD_END }; @@ -1929,6 +1930,35 @@ extern const char *marktype_shift_end_skip; #define MARK_USED_STR "u" #define MUSED(_status) (tolower((_status)[0]) == MARK_USED) +// USERINFO from various incoming data +typedef struct userinfo { + int64_t userid; + char username[TXT_BIG+1]; + int blocks; + int orphans; // How many blocks are orphans + tv_t last_block; + // For all time + double diffacc; + double diffsta; + double diffdup; + double diffhi; + double diffrej; + double shareacc; + double sharesta; + double sharedup; + double sharehi; + double sharerej; +} USERINFO; + +#define ALLOC_USERINFO 1000 +#define LIMIT_USERINFO 0 +#define INIT_USERINFO(_item) INIT_GENERIC(_item, userinfo) +#define DATA_USERINFO(_var, _item) DATA_GENERIC(_var, _item, userinfo, true) + +extern K_TREE *userinfo_root; +extern K_LIST *userinfo_free; +extern K_STORE *userinfo_store; + extern void logmsg(int loglevel, const char *fmt, ...); extern void setnow(tv_t *now); extern void tick(); @@ -2192,6 +2222,17 @@ extern bool _marks_description(char *description, size_t siz, char *marktype, int32_t height, char *shift, char *other, WHERE_FFL_ARGS); extern char *shiftcode(tv_t *createdate); +extern cmp_t cmp_userinfo(K_ITEM *a, K_ITEM *b); +#define get_userinfo(_userid) _get_userinfo(_userid, true) +extern K_ITEM *_get_userinfo(int64_t userid, bool lock); +#define find_userinfo(_userid) _find_create_userinfo(_userid, true, WHERE_FFL_HERE) +#define _find_userinfo(_userid, _lock) _find_create_userinfo(_userid, _lock, WHERE_FFL_HERE) +extern K_ITEM *_find_create_userinfo(int64_t userid, bool lock, WHERE_FFL_ARGS); +#define userinfo_update(_s, _ss, _ms) _userinfo_update(_s, _ss, _ms, true, true) +extern void _userinfo_update(SHARES *shares, SHARESUMMARY *sharesummary, + MARKERSUMMARY *markersummary, bool ss_sub, bool lock); +#define userinfo_block(_blocks, _isnew) _userinfo_block(_blocks, _isnew, true) +extern void _userinfo_block(BLOCKS *blocks, bool isnew, bool lock); // *** // *** PostgreSQL functions ckdb_dbio.c diff --git a/src/ckdb_cmd.c b/src/ckdb_cmd.c index ab0c68a8..0ec64f35 100644 --- a/src/ckdb_cmd.c +++ b/src/ckdb_cmd.c @@ -5194,6 +5194,7 @@ static char *cmd_stats(__maybe_unused PGconn *conn, char *cmd, char *id, USEINFO(poolstats, 1, 1); USEINFO(userstats, 2, 1); USEINFO(workerstatus, 1, 1); + USEINFO(userinfo, 1, 1); USEINFO(msgline, 1, 0); USEINFO(workqueue, 1, 0); USEINFO(transfer, 0, 0); @@ -5863,6 +5864,88 @@ static char *cmd_shsta(__maybe_unused PGconn *conn, char *cmd, char *id, return strdup(buf); } +static char *cmd_userinfo(__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, + __maybe_unused K_TREE *trf_root) +{ + K_ITEM *ui_item; + USERINFO *userinfo; + char reply[1024] = ""; + char tmp[1024]; + size_t len, off; + double d; + char *buf; + int rows; + + LOGDEBUG("%s(): cmd '%s'", __func__, cmd); + + APPEND_REALLOC_INIT(buf, off, len); + APPEND_REALLOC(buf, off, len, "ok."); + + rows = 0; + K_RLOCK(userinfo_free); + ui_item = userinfo_store->head; + while (ui_item) { + DATA_USERINFO(userinfo, ui_item); + + str_to_buf(userinfo->username, reply, sizeof(reply)); + snprintf(tmp, sizeof(tmp), "username:%d=%s%c", + rows, reply, FLDSEP); + APPEND_REALLOC(buf, off, len, tmp); + + snprintf(tmp, sizeof(tmp), "blocks:%d=%d%c", rows, + userinfo->blocks - userinfo->orphans, FLDSEP); + APPEND_REALLOC(buf, off, len, tmp); + + snprintf(tmp, sizeof(tmp), "orphans:%d=%d%c", rows, + userinfo->orphans, FLDSEP); + APPEND_REALLOC(buf, off, len, tmp); + + double_to_buf(userinfo->diffacc, reply, sizeof(reply)); + snprintf(tmp, sizeof(tmp), "diffacc:%d=%s%c", rows, reply, FLDSEP); + APPEND_REALLOC(buf, off, len, tmp); + + d = userinfo->diffsta + userinfo->diffdup + userinfo->diffhi + + userinfo->diffrej; + double_to_buf(d, reply, sizeof(reply)); + snprintf(tmp, sizeof(tmp), "diffinv:%d=%s%c", rows, reply, FLDSEP); + APPEND_REALLOC(buf, off, len, tmp); + + double_to_buf(userinfo->shareacc, reply, sizeof(reply)); + snprintf(tmp, sizeof(tmp), "shareacc:%d=%s%c", rows, reply, FLDSEP); + APPEND_REALLOC(buf, off, len, tmp); + + d = userinfo->sharesta + userinfo->sharedup + userinfo->sharehi + + userinfo->sharerej; + double_to_buf(d, reply, sizeof(reply)); + snprintf(tmp, sizeof(tmp), "shareinv:%d=%s%c", rows, reply, FLDSEP); + APPEND_REALLOC(buf, off, len, tmp); + + snprintf(tmp, sizeof(tmp), "lastblock=%ld%c", + userinfo->last_block.tv_sec, FLDSEP); + APPEND_REALLOC(buf, off, len, tmp); + + rows++; + ui_item = ui_item->next; + } + K_RUNLOCK(userinfo_free); + + snprintf(tmp, sizeof(tmp), + "rows=%d%cflds=%s%c", + rows, FLDSEP, + "username,blocks,orphans,diffacc,diffinv,shareacc,shareinv," + "lastblock", FLDSEP); + APPEND_REALLOC(buf, off, len, tmp); + + snprintf(tmp, sizeof(tmp), "arn=%s%carp=", "UserInfo", FLDSEP); + APPEND_REALLOC(buf, off, len, tmp); + + LOGDEBUG("%s.ok.%d_rows", id, rows); + return buf; +} + // TODO: limit access by having seperate sockets for each #define ACCESS_POOL "p" #define ACCESS_SYSTEM "s" @@ -5975,5 +6058,6 @@ struct CMDS ckdb_cmds[] = { { CMD_MARKS, "marks", false, false, cmd_marks, SEQ_NONE, ACCESS_SYSTEM }, { CMD_PSHIFT, "pshift", false, false, cmd_pshift, SEQ_NONE, ACCESS_SYSTEM ACCESS_WEB }, { CMD_SHSTA, "shsta", true, false, cmd_shsta, SEQ_NONE, ACCESS_SYSTEM }, + { CMD_USERINFO, "userinfo", false, false, cmd_userinfo, SEQ_NONE, ACCESS_WEB }, { CMD_END, NULL, false, false, NULL, SEQ_NONE, NULL } }; diff --git a/src/ckdb_data.c b/src/ckdb_data.c index ac4e0136..214eb81a 100644 --- a/src/ckdb_data.c +++ b/src/ckdb_data.c @@ -936,8 +936,10 @@ void _workerstatus_update(AUTHS *auths, SHARES *shares, file, func, line); if (item) { DATA_WORKERSTATUS(row, item); + K_WLOCK(workerstatus_free); if (tv_newer(&(row->last_auth), &(auths->createdate))) copy_tv(&(row->last_auth), &(auths->createdate)); + K_WUNLOCK(workerstatus_free); } } @@ -953,6 +955,7 @@ void _workerstatus_update(AUTHS *auths, SHARES *shares, file, func, line); if (item) { DATA_WORKERSTATUS(row, item); + K_WLOCK(workerstatus_free); if (tv_newer(&(row->last_share), &(shares->createdate))) { copy_tv(&(row->last_share), &(shares->createdate)); row->last_diff = shares->diff; @@ -987,6 +990,7 @@ void _workerstatus_update(AUTHS *auths, SHARES *shares, row->sharerej++; break; } + K_WLOCK(workerstatus_free); } } @@ -995,6 +999,7 @@ void _workerstatus_update(AUTHS *auths, SHARES *shares, file, func, line); if (item) { DATA_WORKERSTATUS(row, item); + K_WLOCK(workerstatus_free); if (userstats->idle) { if (tv_newer(&(row->last_idle), &(userstats->statsdate))) copy_tv(&(row->last_idle), &(userstats->statsdate)); @@ -1002,6 +1007,7 @@ void _workerstatus_update(AUTHS *auths, SHARES *shares, if (tv_newer(&(row->last_stats), &(userstats->statsdate))) copy_tv(&(row->last_stats), &(userstats->statsdate)); } + K_WUNLOCK(workerstatus_free); } } } @@ -1768,6 +1774,7 @@ bool workinfo_age(PGconn *conn, int64_t workinfoid, char *poolinstance, K_TREE_CTX ss_ctx[1], s_ctx[1]; char cd_buf[DATE_BUFSIZ]; int64_t ss_tot, ss_already, ss_failed, shares_tot, shares_dumped; + int64_t diff_tot; SHARESUMMARY looksharesummary, *sharesummary; WORKINFO *workinfo; SHARES lookshares, *shares; @@ -1822,7 +1829,8 @@ bool workinfo_age(PGconn *conn, int64_t workinfoid, char *poolinstance, looksharesummary.workername = EMPTY; ok = true; - ss_tot = ss_already = ss_failed = shares_tot = shares_dumped = 0; + ss_tot = ss_already = ss_failed = shares_tot = shares_dumped = + diff_tot = 0; ss_look.data = (void *)(&looksharesummary); K_RLOCK(sharesummary_free); ss_item = find_after_in_ktree(sharesummary_workinfoid_root, &ss_look, cmp_sharesummary_workinfoid, ss_ctx); @@ -1891,6 +1899,8 @@ bool workinfo_age(PGconn *conn, int64_t workinfoid, char *poolinstance, break; shares_tot++; + if (shares->errn == SE_NONE) + diff_tot += shares->diff; tmp_item = next_in_ktree(s_ctx); shares_root = remove_from_ktree(shares_root, s_item, cmp_shares); k_unlink_item(shares_store, s_item); @@ -1898,10 +1908,13 @@ bool workinfo_age(PGconn *conn, int64_t workinfoid, char *poolinstance, shares_dumped++; if (reloading && skipupdate && !error[0]) { snprintf(error, sizeof(error), - "reload found aged shares: %"PRId64"/%"PRId64"/%s", + "reload found aged share: %"PRId64 + "/%"PRId64"/%s/%s%.0f", shares->workinfoid, shares->userid, - shares->workername); + shares->workername, + (shares->errn == SE_NONE) ? "" : "*", + shares->diff); } k_add_head(shares_free, s_item); s_item = tmp_item; @@ -1923,12 +1936,13 @@ bool workinfo_age(PGconn *conn, int64_t workinfoid, char *poolinstance, /* If all were already aged, and no shares * then we don't want a message */ if (!(ss_already == ss_tot && shares_tot == 0)) { - LOGERR("%s(): Summary aging of %"PRId64"/%s sstotal=%"PRId64 - " already=%"PRId64" failed=%"PRId64 - ", sharestotal=%"PRId64" dumped=%"PRId64, + LOGERR("%s(): Summary aging of %"PRId64 + "/%s sstotal=%"PRId64" already=%"PRId64 + " failed=%"PRId64", sharestotal=%"PRId64 + " dumped=%"PRId64", diff=%"PRId64, __func__, workinfoid, poolinstance, ss_tot, ss_already, ss_failed, shares_tot, - shares_dumped); + shares_dumped, diff_tot); } } bye: @@ -4435,3 +4449,167 @@ char *shiftcode(tv_t *createdate) LOGDEBUG("%s() code_buf='%s'", __func__, code_buf); return(code_buf); } + +// order by userid asc +cmp_t cmp_userinfo(K_ITEM *a, K_ITEM *b) +{ + USERINFO *ua, *ub; + DATA_USERINFO(ua, a); + DATA_USERINFO(ub, b); + return CMP_BIGINT(ua->userid, ub->userid); +} + +K_ITEM *_get_userinfo(int64_t userid, bool lock) +{ + USERINFO userinfo; + K_TREE_CTX ctx[1]; + K_ITEM look, *find; + + userinfo.userid = userid; + + INIT_USERINFO(&look); + look.data = (void *)(&userinfo); + if (lock) + K_RLOCK(userinfo_free); + find = find_in_ktree(userinfo_root, &look, cmp_userinfo, ctx); + if (lock) + K_RUNLOCK(userinfo_free); + return find; +} + +K_ITEM *_find_create_userinfo(int64_t userid, bool lock, WHERE_FFL_ARGS) +{ + K_ITEM *ui_item, *u_item; + USERS *users = NULL; + USERINFO *row; + + ui_item = _get_userinfo(userid, lock); + if (!ui_item) { + if (lock) + K_RLOCK(users_free); + u_item = find_userid(userid); + if (lock) + K_RUNLOCK(users_free); + DATA_USERS_NULL(users, u_item); + + if (lock) + K_WLOCK(userinfo_free); + ui_item = k_unlink_head(userinfo_free); + DATA_USERINFO(row, ui_item); + + bzero(row, sizeof(*row)); + row->userid = userid; + if (u_item) + STRNCPY(row->username, users->username); + else + bigint_to_buf(userid, row->username, sizeof(row->username)); + + userinfo_root = add_to_ktree(userinfo_root, ui_item, cmp_userinfo); + k_add_head(userinfo_store, ui_item); + if (lock) + K_WUNLOCK(userinfo_free); + } + return ui_item; +} + +void _userinfo_update(SHARES *shares, SHARESUMMARY *sharesummary, + MARKERSUMMARY *markersummary, bool ss_sub, bool lock) +{ + USERINFO *row; + K_ITEM *item; + + if (shares) { + item = _find_userinfo(shares->userid, lock); + DATA_USERINFO(row, item); + if (lock) + K_WLOCK(userinfo_free); + switch (shares->errn) { + case SE_NONE: + row->diffacc += shares->diff; + row->shareacc++; + break; + case SE_STALE: + row->diffsta += shares->diff; + row->sharesta++; + break; + case SE_DUPE: + row->diffdup += shares->diff; + row->sharedup++; + break; + case SE_HIGH_DIFF: + row->diffhi += shares->diff; + row->sharehi++; + break; + default: + row->diffrej += shares->diff; + row->sharerej++; + break; + } + if (lock) + K_WUNLOCK(userinfo_free); + } + + // Only during db load so no locking required + if (sharesummary) { + item = _find_userinfo(sharesummary->userid, false); + DATA_USERINFO(row, item); + if (ss_sub) { + row->diffacc -= sharesummary->diffacc; + row->diffsta -= sharesummary->diffsta; + row->diffdup -= sharesummary->diffdup; + row->diffhi -= sharesummary->diffhi; + row->diffrej -= sharesummary->diffrej; + row->shareacc -= sharesummary->shareacc; + row->sharesta -= sharesummary->sharesta; + row->sharedup -= sharesummary->sharedup; + row->sharehi -= sharesummary->sharehi; + row->sharerej -= sharesummary->sharerej; + } else { + row->diffacc += sharesummary->diffacc; + row->diffsta += sharesummary->diffsta; + row->diffdup += sharesummary->diffdup; + row->diffhi += sharesummary->diffhi; + row->diffrej += sharesummary->diffrej; + row->shareacc += sharesummary->shareacc; + row->sharesta += sharesummary->sharesta; + row->sharedup += sharesummary->sharedup; + row->sharehi += sharesummary->sharehi; + row->sharerej += sharesummary->sharerej; + } + } + + // Only during db load so no locking required + if (markersummary) { + item = _find_userinfo(markersummary->userid, false); + DATA_USERINFO(row, item); + row->diffacc += markersummary->diffacc; + row->diffsta += markersummary->diffsta; + row->diffdup += markersummary->diffdup; + row->diffhi += markersummary->diffhi; + row->diffrej += markersummary->diffrej; + row->shareacc += markersummary->shareacc; + row->sharesta += markersummary->sharesta; + row->sharedup += markersummary->sharedup; + row->sharehi += markersummary->sharehi; + row->sharerej += markersummary->sharerej; + } +} + +// N.B. good blocks = blocks - orphans +void _userinfo_block(BLOCKS *blocks, bool isnew, bool lock) +{ + USERINFO *row; + K_ITEM *item; + + item = find_userinfo(blocks->userid); + DATA_USERINFO(row, item); + if (lock) + K_WLOCK(userinfo_free); + if (isnew) { + row->blocks++; + copy_tv(&(row->last_block), &(blocks->createdate)); + } else + row->orphans++; + if (lock) + K_WLOCK(userinfo_free); +} diff --git a/src/ckdb_dbio.c b/src/ckdb_dbio.c index be709f60..8f119e93 100644 --- a/src/ckdb_dbio.c +++ b/src/ckdb_dbio.c @@ -2836,6 +2836,9 @@ static bool shares_process(PGconn *conn, SHARES *shares, K_TREE *trf_root) } if (!sharesummary->reset) { + _userinfo_update(NULL, sharesummary, NULL, + true, true); + zero_sharesummary(sharesummary, &(shares->createdate), shares->diff); @@ -2844,8 +2847,10 @@ static bool shares_process(PGconn *conn, SHARES *shares, K_TREE *trf_root) } } - if (!confirm_sharesummary) + if (!confirm_sharesummary) { workerstatus_update(NULL, shares, NULL); + userinfo_update(shares, NULL, NULL); + } sharesummary_update(conn, shares, NULL, NULL, shares->createby, shares->createcode, shares->createinet, @@ -3144,6 +3149,9 @@ static bool shareerrors_process(PGconn *conn, SHAREERRORS *shareerrors, } if (!sharesummary->reset) { + _userinfo_update(NULL, sharesummary, NULL, + true, true); + zero_sharesummary(sharesummary, &(shareerrors->createdate), 0.0); @@ -4442,6 +4450,8 @@ bool sharesummary_fill(PGconn *conn) sharesummary_to_pool(p_row, row); + _userinfo_update(NULL, row, NULL, false, false); + tick(); } if (!ok) { @@ -4743,6 +4753,7 @@ bool blocks_add(PGconn *conn, char *height, char *blockhash, } // We didn't use a Begin ok = true; + userinfo_block(row, true); goto unparam; break; case BLOCKS_ORPHAN: @@ -4877,6 +4888,8 @@ bool blocks_add(PGconn *conn, char *height, char *blockhash, } update_old = true; + if (confirmed[0] == BLOCKS_ORPHAN) + userinfo_block(row, false); break; default: LOGERR("%s(): %s.failed.invalid confirm='%s'", @@ -5131,6 +5144,12 @@ bool blocks_fill(PGconn *conn) pool.workinfoid = row->workinfoid; pool.height = row->height; } + + if (CURRENT(&(row->expirydate))) { + _userinfo_block(row, true, false); + if (row->confirmed[0] == BLOCKS_ORPHAN) + _userinfo_block(row, false, false); + } } if (!ok) k_add_head(blocks_free, item); @@ -6461,6 +6480,8 @@ bool markersummary_fill(PGconn *conn) markersummary_to_pool(p_row, row); + _userinfo_update(NULL, NULL, row, false, false); + tick(); } if (!ok) { @@ -6469,6 +6490,7 @@ bool markersummary_fill(PGconn *conn) } p_n = markersummary_pool_store->count; + //K_WUNLOCK(markersummary_free); PQclear(res);