From 4c243f24ccdd32f995f82066833862c392b2e6e1 Mon Sep 17 00:00:00 2001 From: kanoi Date: Sat, 5 Sep 2015 01:38:15 +1000 Subject: [PATCH] ckdb/php - store, and use, the block and prevous valid block createtime in BLOCKS --- pool/page_blocks.php | 2 +- src/ckdb.c | 11 +- src/ckdb.h | 23 ++-- src/ckdb_cmd.c | 78 ++++++------ src/ckdb_data.c | 282 +++++++++++++++++++++++++++++++------------ src/ckdb_dbio.c | 106 +++++++++++----- 6 files changed, 340 insertions(+), 162 deletions(-) diff --git a/pool/page_blocks.php b/pool/page_blocks.php index f7cc20ec..73df3c55 100644 --- a/pool/page_blocks.php +++ b/pool/page_blocks.php @@ -111,7 +111,7 @@ function doblocks($data, $user) $row = 'odd'; $desc = $ans['s_desc:'.$i]; - $age = gmdate('j/M H:i',$ans['s_createdate:'.$i]); + $age = daysago($since - $ans['s_prevcreatedate:'.$i]); $diff = number_format(100 * $ans['s_diffratio:'.$i], 2); $mean = number_format(100 * $ans['s_diffmean:'.$i], 2); diff --git a/src/ckdb.c b/src/ckdb.c index 7f219aef..935c8aa6 100644 --- a/src/ckdb.c +++ b/src/ckdb.c @@ -1483,8 +1483,7 @@ static bool setup_data() if (workinfo_current) { DATA_WORKINFO(wic, workinfo_current); STRNCPY(wi.coinbase1, wic->coinbase1); - wi.createdate.tv_sec = 0L; - wi.createdate.tv_usec = 0L; + DATE_ZERO(&(wi.createdate)); INIT_WORKINFO(&look); look.data = (void *)(&wi); // Find the first workinfo for this height @@ -1524,7 +1523,7 @@ static bool setup_data() (_seqset)->seqdata[_i].firsttime.tv_sec = \ (_seqset)->seqdata[_i].firsttime.tv_usec = \ (_seqset)->seqdata[_i].lasttime.tv_sec = \ - (_seqset)->seqdata[_i].lasttime.tv_usec = 0; \ + (_seqset)->seqdata[_i].lasttime.tv_usec = 0L; \ } \ } while (0); @@ -2902,7 +2901,7 @@ static void summarise_blocks() K_RUNLOCK(blocks_free); if (!b_prev) { wi_start = 0; - elapsed_start.tv_sec = elapsed_start.tv_usec = 0L; + DATE_ZERO(&elapsed_start); prev_hi = 0; } else { DATA_BLOCKS(prev_blocks, b_prev); @@ -2921,7 +2920,7 @@ static void summarise_blocks() copy_tv(&elapsed_start, &(prev_workinfo->createdate)); prev_hi = prev_blocks->height; } - elapsed_finish.tv_sec = elapsed_finish.tv_usec = 0L; + DATE_ZERO(&elapsed_finish); // Add up the sharesummaries, abort if any SUMMARY_NEW looksharesummary.workinfoid = wi_finish; @@ -5121,7 +5120,7 @@ static void confirm_reload() } } else { if (confirm_first_workinfoid == 0) { - start.tv_sec = start.tv_usec = 0; + DATE_ZERO(&start); LOGWARNING("%s() no start workinfo found ... " "using time 0", __func__); } else { diff --git a/src/ckdb.h b/src/ckdb.h index 70f9df1f..e98cf5ef 100644 --- a/src/ckdb.h +++ b/src/ckdb.h @@ -55,7 +55,7 @@ #define DB_VLOCK "1" #define DB_VERSION "1.0.2" -#define CKDB_VERSION DB_VERSION"-1.231" +#define CKDB_VERSION DB_VERSION"-1.232" #define WHERE_FFL " - from %s %s() line %d" #define WHERE_FFL_HERE __FILE__, __func__, __LINE__ @@ -270,6 +270,8 @@ extern const tv_t date_eot; #define DATE_BEGIN 1388620800L extern const tv_t date_begin; +#define DATE_ZERO(_tv) (_tv)->tv_sec = (_tv)->tv_usec = 0L + #define BTC_TO_D(_amt) ((double)((_amt) / 100000000.0)) // argv -y - don't run in ckdb mode, just confirm sharesummaries @@ -592,8 +594,7 @@ enum cmd_values { STRNCPY(_row->createby, _by); \ STRNCPY(_row->createcode, _code); \ STRNCPY(_row->createinet, _inet); \ - _row->modifydate.tv_sec = 0; \ - _row->modifydate.tv_usec = 0; \ + DATE_ZERO(&(_row->modifydate)); \ _row->modifyby[0] = '\0'; \ _row->modifycode[0] = '\0'; \ _row->modifyinet[0] = '\0'; \ @@ -615,8 +616,7 @@ enum cmd_values { SET_CREATEBY(_list, _row->createby, _by); \ SET_CREATECODE(_list, _row->createcode, _code); \ SET_CREATEINET(_list, _row->createinet, _inet); \ - _row->modifydate.tv_sec = 0; \ - _row->modifydate.tv_usec = 0; \ + DATE_ZERO(&(_row->modifydate)); \ SET_MODIFYBY(_list, _row->modifyby, EMPTY); \ SET_MODIFYCODE(_list, _row->modifycode, EMPTY); \ SET_MODIFYINET(_list, _row->modifyinet, EMPTY); \ @@ -1496,6 +1496,10 @@ typedef struct blocks { double diffmean; double cdferl; double luck; + + // To save looking them up when needed + tv_t prevcreatedate; // non-DB field + tv_t blockcreatedate; // non-DB field } BLOCKS; #define ALLOC_BLOCKS 100 @@ -2305,12 +2309,15 @@ extern double _blockhash_diff(char *hash, WHERE_FFL_ARGS); extern void dsp_blocks(K_ITEM *item, FILE *stream); extern cmp_t cmp_blocks(K_ITEM *a, K_ITEM *b); extern K_ITEM *find_blocks(int32_t height, char *blockhash, K_TREE_CTX *ctx); -extern K_ITEM *find_blocks_new(K_ITEM *b_item, K_TREE_CTX *ctx); extern K_ITEM *find_prev_blocks(int32_t height); extern const char *blocks_confirmed(char *confirmed); extern void zero_on_new_block(); extern void set_block_share_counters(); extern bool check_update_blocks_stats(tv_t *stats); +#define set_blockcreatedate(_h) _set_blockcreatedate(_h, WHERE_FFL_HERE) +extern bool _set_blockcreatedate(int32_t oldest_height, WHERE_FFL_ARGS); +#define set_prevcreatedate(_h) _set_prevcreatedate(_h, WHERE_FFL_HERE) +extern bool _set_prevcreatedate(int32_t oldest_height, WHERE_FFL_ARGS); extern cmp_t cmp_miningpayouts(K_ITEM *a, K_ITEM *b); extern K_ITEM *find_miningpayouts(int64_t payoutid, int64_t userid); extern K_ITEM *first_miningpayouts(int64_t payoutid, K_TREE_CTX *ctx); @@ -2376,8 +2383,8 @@ 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, enum info_type isnew, bool lock); +#define userinfo_block(_blocks, _isnew, _delta) _userinfo_block(_blocks, _isnew, _delta, true) +extern void _userinfo_block(BLOCKS *blocks, enum info_type isnew, int delta, bool lock); // *** // *** PostgreSQL functions ckdb_dbio.c diff --git a/src/ckdb_cmd.c b/src/ckdb_cmd.c index 11c9bac1..f724b218 100644 --- a/src/ckdb_cmd.c +++ b/src/ckdb_cmd.c @@ -1128,8 +1128,7 @@ static char *cmd_blocklist(__maybe_unused PGconn *conn, char *cmd, char *id, char tmp[1024]; char *buf, *desc, desc_buf[64]; size_t len, off; - int32_t height = -1; - tv_t first_cd = {0,0}, stats_tv = {0,0}, stats_tv2 = {0,0}; + tv_t stats_tv = {0,0}, stats_tv2 = {0,0}; int rows, srows, tot, seq; int64_t maxrows; bool has_stats; @@ -1164,13 +1163,6 @@ redo: b_item = last_in_ktree(blocks_root, ctx); while (b_item && rows < (int)maxrows) { DATA_BLOCKS(blocks, b_item); - /* For each block remember the initial createdate - * Reverse sort order the oldest expirydate is first - * which should be the 'n' record */ - if (height != blocks->height) { - height = blocks->height; - copy_tv(&first_cd, &(blocks->createdate)); - } if (CURRENT(&(blocks->expirydate))) { if (blocks->confirmed[0] == BLOCKS_ORPHAN || blocks->confirmed[0] == BLOCKS_REJECT) { @@ -1207,16 +1199,24 @@ redo: snprintf(tmp, sizeof(tmp), "workername:%d=%s%c", rows, reply, FLDSEP); APPEND_REALLOC(buf, off, len, tmp); + // When block was found snprintf(tmp, sizeof(tmp), "first"CDTRF":%d=%ld%c", rows, - first_cd.tv_sec, FLDSEP); + blocks->blockcreatedate.tv_sec, FLDSEP); APPEND_REALLOC(buf, off, len, tmp); + // Last time block was updated snprintf(tmp, sizeof(tmp), CDTRF":%d=%ld%c", rows, blocks->createdate.tv_sec, FLDSEP); APPEND_REALLOC(buf, off, len, tmp); + // When previous valid block was found + snprintf(tmp, sizeof(tmp), + "prev"CDTRF":%d=%ld%c", rows, + blocks->prevcreatedate.tv_sec, FLDSEP); + APPEND_REALLOC(buf, off, len, tmp); + snprintf(tmp, sizeof(tmp), "confirmed:%d=%s%cstatus:%d=%s%c", rows, blocks->confirmed, FLDSEP, rows, @@ -1305,6 +1305,7 @@ redo: "s_desc:%d=%s%c" "s_height:%d=%d%c" "s_"CDTRF":%d=%ld%c" + "s_prev"CDTRF":%d=%ld%c" "s_diffratio:%d=%.8f%c" "s_diffmean:%d=%.8f%c" "s_cdferl:%d=%.8f%c" @@ -1312,7 +1313,8 @@ redo: srows, seq, FLDSEP, srows, desc, FLDSEP, srows, (int)(blocks->height), FLDSEP, - srows, blocks->createdate.tv_sec, FLDSEP, + srows, blocks->blockcreatedate.tv_sec, FLDSEP, + srows, blocks->prevcreatedate.tv_sec, FLDSEP, srows, blocks->diffratio, FLDSEP, srows, blocks->diffmean, FLDSEP, srows, blocks->cdferl, FLDSEP, @@ -1344,8 +1346,8 @@ redo: snprintf(tmp, sizeof(tmp), "s_rows=%d%cs_flds=%s%c", srows, FLDSEP, - "s_seq,s_desc,s_height,s_"CDTRF",s_diffratio,s_diffmean," - "s_cdferl,s_luck", + "s_seq,s_desc,s_height,s_"CDTRF",s_prev"CDTRF",s_diffratio," + "s_diffmean,s_cdferl,s_luck", FLDSEP); APPEND_REALLOC(buf, off, len, tmp); @@ -1353,8 +1355,8 @@ redo: "rows=%d%cflds=%s%c", rows, FLDSEP, "seq,height,blockhash,nonce,reward,workername,first"CDTRF"," - CDTRF",confirmed,status,info,statsconf,diffacc,diffinv," - "shareacc,shareinv,elapsed,netdiff,diffratio,cdf,luck", + CDTRF",prev"CDTRF",confirmed,status,info,statsconf,diffacc," + "diffinv,shareacc,shareinv,elapsed,netdiff,diffratio,cdf,luck", FLDSEP); APPEND_REALLOC(buf, off, len, tmp); @@ -1706,8 +1708,7 @@ static char *cmd_percent(char *cmd, char *id, tv_t *now, USERS *users) // Add up all user's worker stats to be divided into payout percentages lookworkers.userid = users->userid; lookworkers.workername[0] = '\0'; - lookworkers.expirydate.tv_sec = 0; - lookworkers.expirydate.tv_usec = 0; + DATE_ZERO(&(lookworkers.expirydate)); w_look.data = (void *)(&lookworkers); w_item = find_after_in_ktree(workers_root, &w_look, cmp_workers, w_ctx); DATA_WORKERS_NULL(workers, w_item); @@ -1968,8 +1969,7 @@ static char *cmd_workers(__maybe_unused PGconn *conn, char *cmd, char *id, lookworkers.userid = users->userid; lookworkers.workername[0] = '\0'; - lookworkers.expirydate.tv_sec = 0; - lookworkers.expirydate.tv_usec = 0; + DATE_ZERO(&(lookworkers.expirydate)); w_look.data = (void *)(&lookworkers); w_item = find_after_in_ktree(workers_root, &w_look, cmp_workers, w_ctx); DATA_WORKERS_NULL(workers, w_item); @@ -1983,7 +1983,7 @@ static char *cmd_workers(__maybe_unused PGconn *conn, char *cmd, char *id, copy_tv(&last_share, &(workerstatus->last_share)); K_RUNLOCK(workerstatus_free); } else - last_share.tv_sec = last_share.tv_usec = 0L; + DATE_ZERO(&last_share); if (tvdiff(now, &last_share) < oldworkers) { str_to_buf(workers->workername, reply, sizeof(reply)); @@ -2021,7 +2021,7 @@ static char *cmd_workers(__maybe_unused PGconn *conn, char *cmd, char *id, w_elapsed = -1; if (!ws_item) { - w_lastshare.tv_sec = 0; + w_lastshare.tv_sec = 0L; w_lastdiff = w_diffacc = w_diffinv = w_diffsta = w_diffdup = w_diffhi = @@ -2029,7 +2029,7 @@ static char *cmd_workers(__maybe_unused PGconn *conn, char *cmd, char *id, w_shareinv = w_sharesta = w_sharedup = w_sharehi = w_sharerej = w_active_diffacc = 0; - w_active_start.tv_sec = 0; + w_active_start.tv_sec = 0L; } else { DATA_WORKERSTATUS(workerstatus, ws_item); // It's bad to read possibly changing data @@ -4018,8 +4018,8 @@ static char *cmd_pplns(__maybe_unused PGconn *conn, char *cmd, char *id, LOGDEBUG("%s(): height %"PRId32, __func__, height); - block_tv.tv_sec = block_tv.tv_usec = 0L; - cd.tv_sec = cd.tv_usec = 0L; + DATE_ZERO(&block_tv); + DATE_ZERO(&cd); lookblocks.height = height + 1; lookblocks.blockhash[0] = '\0'; INIT_BLOCKS(&b_look); @@ -4446,7 +4446,6 @@ static char *cmd_pplns2(__maybe_unused PGconn *conn, char *cmd, char *id, PAYMENTS *payments; PAYOUTS *payouts; BLOCKS lookblocks, *blocks; - tv_t block_tv = { 0L, 0L }; WORKINFO *bworkinfo, *workinfo; char ndiffbin[TXT_SML+1]; double ndiff; @@ -4470,36 +4469,26 @@ static char *cmd_pplns2(__maybe_unused PGconn *conn, char *cmd, char *id, LOGDEBUG("%s(): height %"PRId32, __func__, height); - lookblocks.height = height + 1; + lookblocks.height = height; lookblocks.blockhash[0] = '\0'; INIT_BLOCKS(&b_look); b_look.data = (void *)(&lookblocks); K_RLOCK(blocks_free); - b_item = find_before_in_ktree(blocks_root, &b_look, cmp_blocks, b_ctx); + b_item = find_after_in_ktree(blocks_root, &b_look, cmp_blocks, b_ctx); + K_RUNLOCK(blocks_free); if (!b_item) { K_RUNLOCK(blocks_free); - snprintf(reply, siz, "ERR.no block height %"PRId32, height); + snprintf(reply, siz, "ERR.no block height >= %"PRId32, height); return strdup(reply); } DATA_BLOCKS(blocks, b_item); - while (b_item && blocks->height == height) { - if (blocks->confirmed[0] == BLOCKS_NEW) - copy_tv(&block_tv, &(blocks->createdate)); - // Allow any state, but report it - if (CURRENT(&(blocks->expirydate))) - break; - b_item = prev_in_ktree(b_ctx); - DATA_BLOCKS_NULL(blocks, b_item); - } - K_RUNLOCK(blocks_free); if (!b_item || blocks->height != height) { snprintf(reply, siz, "ERR.no block height %"PRId32, height); return strdup(reply); } - if (block_tv.tv_sec == 0) { - snprintf(reply, siz, "ERR.block %"PRId32" missing '%s' record", - height, - blocks_confirmed(BLOCKS_NEW_STR)); + if (blocks->blockcreatedate.tv_sec == 0) { + snprintf(reply, siz, "ERR.block %"PRId32" has 0 blockcreatedate", + height); return strdup(reply); } if (!CURRENT(&(blocks->expirydate))) { @@ -4676,10 +4665,11 @@ static char *cmd_pplns2(__maybe_unused PGconn *conn, char *cmd, char *id, snprintf(tmp, sizeof(tmp), "begin_epoch=%ld%c", workinfo->createdate.tv_sec, FLDSEP); APPEND_REALLOC(buf, off, len, tmp); - tv_to_buf(&block_tv, tv_buf, sizeof(tv_buf)); + tv_to_buf(&(blocks->blockcreatedate), tv_buf, sizeof(tv_buf)); snprintf(tmp, sizeof(tmp), "block_stamp=%s%c", tv_buf, FLDSEP); APPEND_REALLOC(buf, off, len, tmp); - snprintf(tmp, sizeof(tmp), "block_epoch=%ld%c", block_tv.tv_sec, FLDSEP); + snprintf(tmp, sizeof(tmp), "block_epoch=%ld%c", + blocks->blockcreatedate.tv_sec, FLDSEP); APPEND_REALLOC(buf, off, len, tmp); tv_to_buf(&(payouts->lastshareacc), tv_buf, sizeof(tv_buf)); snprintf(tmp, sizeof(tmp), "end_stamp=%s%c", tv_buf, FLDSEP); diff --git a/src/ckdb_data.c b/src/ckdb_data.c index fd8ca978..63a872c0 100644 --- a/src/ckdb_data.c +++ b/src/ckdb_data.c @@ -369,8 +369,7 @@ void _txt_to_data(enum data_type typ, char *nam, char *fld, void *data, size_t s long sec, nsec; int c; // Caller test for tv_sec=0 for failure - ((tv_t *)data)->tv_sec = 0L; - ((tv_t *)data)->tv_usec = 0L; + DATE_ZERO((tv_t *)data); c = sscanf(fld, "%ld,%ld", &sec, &nsec); if (c > 0) { ((tv_t *)data)->tv_sec = (time_t)sec; @@ -1474,8 +1473,7 @@ K_ITEM *first_workers(int64_t userid, K_TREE_CTX *ctx) workers.userid = userid; workers.workername[0] = '\0'; - workers.expirydate.tv_sec = 0L; - workers.expirydate.tv_usec = 0L; + DATE_ZERO(&(workers.expirydate)); INIT_WORKERS(&look); look.data = (void *)(&workers); @@ -1628,8 +1626,7 @@ K_ITEM *find_paymentaddresses_create(int64_t userid, K_TREE_CTX *ctx) K_ITEM look, *item; paymentaddresses.userid = userid; - paymentaddresses.createdate.tv_sec = 0; - paymentaddresses.createdate.tv_usec = 0; + DATE_ZERO(&(paymentaddresses.createdate)); paymentaddresses.payaddress[0] = '\0'; INIT_PAYMENTADDRESSES(&look); @@ -1849,8 +1846,7 @@ K_ITEM *find_optioncontrol(char *optionname, tv_t *now, int32_t height) * activationdate will all be the default value and not * decide the outcome */ STRNCPY(optioncontrol.optionname, optionname); - optioncontrol.activationdate.tv_sec = 0L; - optioncontrol.activationdate.tv_usec = 0L; + DATE_ZERO(&(optioncontrol.activationdate)); optioncontrol.activationheight = OPTIONCONTROL_HEIGHT - 1; optioncontrol.expirydate.tv_sec = default_expiry.tv_sec; optioncontrol.expirydate.tv_usec = default_expiry.tv_usec; @@ -2045,8 +2041,8 @@ bool workinfo_age(int64_t workinfoid, char *poolinstance, char *by, char *code, LOGDEBUG("%s(): age", __func__); - ss_first->tv_sec = ss_first->tv_usec = - ss_last->tv_sec = ss_last->tv_usec = 0; + DATE_ZERO(ss_first); + DATE_ZERO(ss_last); *ss_count = *s_count = *s_diff = 0; wi_item = find_workinfo(workinfoid, NULL); @@ -2142,8 +2138,7 @@ bool workinfo_age(int64_t workinfoid, char *poolinstance, char *by, char *code, lookshares.workinfoid = workinfoid; lookshares.userid = sharesummary->userid; strcpy(lookshares.workername, sharesummary->workername); - lookshares.createdate.tv_sec = 0; - lookshares.createdate.tv_usec = 0; + DATE_ZERO(&(lookshares.createdate)); s_look.data = (void *)(&lookshares); K_WLOCK(shares_free); @@ -2390,8 +2385,8 @@ void auto_age_older(int64_t workinfoid, char *poolinstance, char *by, cmp_sharesummary_workinfoid, ctx); DATA_SHARESUMMARY_NULL(sharesummary, ss_item); - ss_first_min.tv_sec = ss_first_min.tv_usec = - ss_last_max.tv_sec = ss_last_max.tv_usec = 0; + DATE_ZERO(&ss_first_min); + DATE_ZERO(&ss_last_max); ss_count_tot = s_count_tot = s_diff_tot = 0; found = false; @@ -2621,33 +2616,6 @@ K_ITEM *find_blocks(int32_t height, char *blockhash, K_TREE_CTX *ctx) return find_in_ktree(blocks_root, &look, cmp_blocks, ctx); } -/* Find the matching NEW block - requires K_RLOCK() - * This requires calling find_blocks() first to get ctx */ -K_ITEM *find_blocks_new(K_ITEM *b_item, K_TREE_CTX *ctx) -{ - BLOCKS *blocks, *blocks2; - K_ITEM *b2_item; - - // Return what was passed in if it was NULL or was the NEW block - DATA_BLOCKS_NULL(blocks, b_item); - if (!b_item || blocks->confirmed[0] == BLOCKS_NEW) - return b_item; - - // NEW should be after the non-NEW block - b2_item = next_in_ktree(ctx); - DATA_BLOCKS_NULL(blocks2, b2_item); - while (b2_item && blocks2->height == blocks->height && - strcmp(blocks2->blockhash, blocks->blockhash) == 0) { - if (blocks2->confirmed[0] == BLOCKS_NEW) - return b2_item; - - b2_item = next_in_ktree(ctx); - DATA_BLOCKS_NULL(blocks2, b2_item); - } - - return NULL; -} - // Must be R or W locked before call K_ITEM *find_prev_blocks(int32_t height) { @@ -2660,8 +2628,7 @@ K_ITEM *find_prev_blocks(int32_t height) * not NEW, blocks, which might not find the right one */ lookblocks.height = height; lookblocks.blockhash[0] = '\0'; - lookblocks.expirydate.tv_sec = 0L; - lookblocks.expirydate.tv_usec = 0L; + DATE_ZERO(&(lookblocks.expirydate)); INIT_BLOCKS(&look); look.data = (void *)(&lookblocks); @@ -3020,6 +2987,184 @@ bool check_update_blocks_stats(tv_t *stats) return true; } +// Must be under K_WLOCK(blocks_free) when called except during DB load +bool _set_blockcreatedate(int32_t oldest_height, WHERE_FFL_ARGS) +{ + K_TREE_CTX ctx[1]; + BLOCKS *blocks; + K_ITEM *b_item; + int32_t height; + char blockhash[TXT_BIG+1]; + char cd_buf[DATE_BUFSIZ]; + tv_t createdate; + bool ok = true; + + // No blocks? + if (blocks_store->count == 0) + return true; + + height = 0; + blockhash[0] = '\0'; + DATE_ZERO(&createdate); + b_item = last_in_ktree(blocks_root, ctx); + DATA_BLOCKS_NULL(blocks, b_item); + while (b_item && blocks->height >= oldest_height) { + // NEW will be first going back + if (blocks->confirmed[0] == BLOCKS_NEW) { + height = blocks->height; + STRNCPY(blockhash, blocks->blockhash); + copy_tv(&createdate, &(blocks->createdate)); + } + if (blocks->height != height || + strcmp(blocks->blockhash, blockhash) != 0) { + // Missing NEW + tv_to_buf(&(blocks->expirydate), cd_buf, sizeof(cd_buf)); + LOGEMERG("%s() block %"PRId32"/%s/%s/%s has no '" + BLOCKS_NEW_STR "' prev was %"PRId32"/%s." + WHERE_FFL, + __func__, + blocks->height, blocks->blockhash, + blocks->confirmed, cd_buf, + height, blockhash, WHERE_FFL_PASS); + ok = false; + + height = blocks->height; + STRNCPY(blockhash, blocks->blockhash); + // set a useable (incorrect) value + copy_tv(&createdate, &(blocks->createdate)); + } + // Always update it + copy_tv(&(blocks->blockcreatedate), &createdate); + + b_item = prev_in_ktree(ctx); + DATA_BLOCKS_NULL(blocks, b_item); + } + return ok; +} + +// Must be under K_WLOCK(blocks_free) when called except during DB load +bool _set_prevcreatedate(int32_t oldest_height, WHERE_FFL_ARGS) +{ + K_ITEM look, *b_item = NULL, *wi_item; + BLOCKS lookblocks, *blocks = NULL; + K_TREE_CTX b_ctx[1], wi_ctx[1]; + WORKINFO *workinfo; + char curr_blockhash[TXT_BIG+1]; + char cd_buf[DATE_BUFSIZ]; + int32_t curr_height; + tv_t prev_createdate; + tv_t curr_createdate; + bool ok = true, currok = false; + + // No blocks? + if (blocks_store->count == 0) + return true; + + // Find first 'ok' block before oldest_height + lookblocks.height = oldest_height; + lookblocks.blockhash[0] = '\0'; + DATE_ZERO(&(lookblocks.expirydate)); + + INIT_BLOCKS(&look); + look.data = (void *)(&lookblocks); + b_item = find_before_in_ktree(blocks_root, &look, cmp_blocks, b_ctx); + while (b_item) { + DATA_BLOCKS(blocks, b_item); + if (CURRENT(&(blocks->expirydate)) && + blocks->confirmed[0] != BLOCKS_ORPHAN && + blocks->confirmed[0] != BLOCKS_REJECT) + break; + b_item = prev_in_ktree(b_ctx); + } + + // Setup prev_createdate + if (b_item) { + /* prev_createdate is the ok b_item (before oldest_height) + * _set_blockcreatedate() should always be called + * before calling _set_prevcreatedate() */ + copy_tv(&prev_createdate, &(blocks->blockcreatedate)); + + /* Move b_item forward to the next block + * since we don't have the prev value for b_item and + * also don't need to update the b_item block */ + curr_height = blocks->height; + STRNCPY(curr_blockhash, blocks->blockhash); + while (b_item && blocks->height == curr_height && + strcmp(blocks->blockhash, curr_blockhash) == 0) { + b_item = next_in_ktree(b_ctx); + DATA_BLOCKS_NULL(blocks, b_item); + } + } else { + /* There's none before oldest_height, so instead use: + * 'Pool Start' = first workinfo createdate */ + K_RLOCK(workinfo_free); + wi_item = first_in_ktree(workinfo_root, wi_ctx); + K_RUNLOCK(workinfo_free); + if (wi_item) { + DATA_WORKINFO(workinfo, wi_item); + copy_tv(&prev_createdate, &(workinfo->createdate)); + } else { + /* Shouldn't be possible since this function is first + * called after workinfo is loaded and the workinfo + * for each block must exist - thus data corruption */ + DATE_ZERO(&prev_createdate); + LOGEMERG("%s() DB/tree corruption - blocks exist but " + "no workinfo exist!" + WHERE_FFL, + __func__, WHERE_FFL_PASS); + ok = false; + } + b_item = first_in_ktree(blocks_root, b_ctx); + } + + // curr_* is unset and will be set first time in the while loop + curr_height = 0; + curr_blockhash[0] = '\0'; + DATE_ZERO(&curr_createdate); + currok = false; + while (b_item) { + DATA_BLOCKS(blocks, b_item); + // While the same block, keep setting it + if (blocks->height == curr_height && + strcmp(blocks->blockhash, curr_blockhash) == 0) { + copy_tv(&(blocks->prevcreatedate), &prev_createdate); + } else { + // Next block - if currok then 'prev' becomes 'curr' + if (currok) + copy_tv(&prev_createdate, &curr_createdate); + + // New curr - CURRENT will be first + if (!CURRENT(&(blocks->expirydate))) { + tv_to_buf(&(blocks->expirydate), cd_buf, + sizeof(cd_buf)); + LOGEMERG("%s() block %"PRId32"/%s/%s/%s first " + "record is not CURRENT" WHERE_FFL, + __func__, + blocks->height, blocks->blockhash, + blocks->confirmed, cd_buf, + WHERE_FFL_PASS); + ok = false; + } + + curr_height = blocks->height; + STRNCPY(curr_blockhash, blocks->blockhash); + copy_tv(&curr_createdate, &(blocks->blockcreatedate)); + + if (CURRENT(&(blocks->expirydate)) && + blocks->confirmed[0] != BLOCKS_ORPHAN && + blocks->confirmed[0] != BLOCKS_REJECT) + currok = true; + else + currok = false; + + // Set it + copy_tv(&(blocks->prevcreatedate), &prev_createdate); + } + b_item = next_in_ktree(b_ctx); + } + return ok; +} + /* order by payoutid asc,userid asc,expirydate asc * i.e. only one payout amount per block per user */ cmp_t cmp_miningpayouts(K_ITEM *a, K_ITEM *b) @@ -3063,8 +3208,7 @@ K_ITEM *first_miningpayouts(int64_t payoutid, K_TREE_CTX *ctx) miningpayouts.payoutid = payoutid; miningpayouts.userid = 0; - miningpayouts.expirydate.tv_sec = 0; - miningpayouts.expirydate.tv_usec = 0; + DATE_ZERO(&(miningpayouts.expirydate)); INIT_MININGPAYOUTS(&look); look.data = (void *)(&miningpayouts); @@ -3216,8 +3360,7 @@ K_ITEM *find_payouts_wid(int64_t workinfoidend, K_TREE_CTX *ctx) ctx = ctx0; payouts.workinfoidend = workinfoidend+1; - payouts.expirydate.tv_sec = 0; - payouts.expirydate.tv_usec = 0; + DATE_ZERO(&(payouts.expirydate)); INIT_PAYOUTS(&look); look.data = (void *)(&payouts); @@ -3319,9 +3462,9 @@ bool process_pplns(int32_t height, char *blockhash, tv_t *addr_cd) PAYMENTS *payments; WORKINFO *workinfo; PAYOUTS *payouts, *payouts2; - BLOCKS *blocks, *blocks2; + BLOCKS *blocks; USERS *users; - K_ITEM *p_item, *old_p_item, *b_item, *b2_item, *w_item, *wb_item; + K_ITEM *p_item, *old_p_item, *b_item, *w_item, *wb_item; K_ITEM *u_item, *mu_item, *oc_item, *pay_item, *p2_item, *old_p2_item; SHARESUMMARY looksharesummary, *sharesummary; WORKMARKERS lookworkmarkers, *workmarkers; @@ -3369,28 +3512,16 @@ bool process_pplns(int32_t height, char *blockhash, tv_t *addr_cd) // Check the block status K_RLOCK(blocks_free); b_item = find_blocks(height, blockhash, b_ctx); + K_RUNLOCK(blocks_free); if (!b_item) { - K_RUNLOCK(blocks_free); LOGERR("%s(): no block %"PRId32"/%s for payout", __func__, height, blockhash); goto oku; } DATA_BLOCKS(blocks, b_item); - b2_item = find_blocks_new(b_item, b_ctx); - K_RUNLOCK(blocks_free); - if (!b2_item) { - LOGEMERG("%s(): missing %s record for block %"PRId32 - "/%"PRId64"/%s/%s/%"PRId64, - __func__, blocks_confirmed(BLOCKS_NEW_STR), - blocks->height, blocks->workinfoid, - blocks->workername, blocks->confirmed, - blocks->reward); - goto oku; - } - DATA_BLOCKS(blocks2, b2_item); - copy_tv(&end_tv, &(blocks2->createdate)); + copy_tv(&end_tv, &(blocks->blockcreatedate)); if (!addr_cd) - addr_cd = &(blocks2->createdate); + addr_cd = &(blocks->blockcreatedate); LOGDEBUG("%s(): block %"PRId32"/%"PRId64"/%s/%s/%"PRId64, __func__, blocks->height, blocks->workinfoid, @@ -3421,11 +3552,11 @@ bool process_pplns(int32_t height, char *blockhash, tv_t *addr_cd) // Get the PPLNS N values K_RLOCK(optioncontrol_free); - oc_item = find_optioncontrol(PPLNSDIFFTIMES, &(blocks->createdate), + oc_item = find_optioncontrol(PPLNSDIFFTIMES, &(blocks->blockcreatedate), height); K_RUNLOCK(optioncontrol_free); if (!oc_item) { - tv_to_buf(&(blocks->createdate), cd_buf, sizeof(cd_buf)); + tv_to_buf(&(blocks->blockcreatedate), cd_buf, sizeof(cd_buf)); LOGEMERG("%s(): missing optioncontrol %s (%s/%"PRId32")", __func__, PPLNSDIFFTIMES, cd_buf, blocks->height); goto oku; @@ -3434,11 +3565,11 @@ bool process_pplns(int32_t height, char *blockhash, tv_t *addr_cd) diff_times = atof(optioncontrol->optionvalue); K_RLOCK(optioncontrol_free); - oc_item = find_optioncontrol(PPLNSDIFFADD, &(blocks->createdate), + oc_item = find_optioncontrol(PPLNSDIFFADD, &(blocks->blockcreatedate), height); K_RUNLOCK(optioncontrol_free); if (!oc_item) { - tv_to_buf(&(blocks->createdate), cd_buf, sizeof(cd_buf)); + tv_to_buf(&(blocks->blockcreatedate), cd_buf, sizeof(cd_buf)); LOGEMERG("%s(): missing optioncontrol %s (%s/%"PRId32")", __func__, PPLNSDIFFADD, cd_buf, blocks->height); goto oku; @@ -3489,7 +3620,7 @@ bool process_pplns(int32_t height, char *blockhash, tv_t *addr_cd) 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 >= blocks->createdate */ + * which will be >= blocks->blockcreatedate */ while (total_diff < diff_want && ss_item) { switch (sharesummary->complete[0]) { case SUMMARY_CONFIRM: @@ -3692,7 +3823,7 @@ bool process_pplns(int32_t height, char *blockhash, tv_t *addr_cd) bzero(payouts, sizeof(*payouts)); payouts->height = height; STRNCPY(payouts->blockhash, blockhash); - copy_tv(&(payouts->blockcreatedate), &(blocks2->createdate)); + copy_tv(&(payouts->blockcreatedate), &(blocks->blockcreatedate)); d64 = blocks->reward * 9 / 1000; g64 = blocks->reward - d64; payouts->minerreward = g64; @@ -4424,8 +4555,7 @@ static bool gen_workmarkers(PGconn *conn, MARKS *stt, bool after, MARKS *fin, look.data = (void *)(&workinfo); K_RLOCK(workinfo_free); if (before) { - workinfo.expirydate.tv_sec = 0; - workinfo.expirydate.tv_usec = 0; + DATE_ZERO(&(workinfo.expirydate)); wi_fin_item = find_before_in_ktree(workinfo_root, &look, cmp_workinfo, ctx); while (wi_fin_item) { @@ -5021,7 +5151,7 @@ void _userinfo_update(SHARES *shares, SHARESUMMARY *sharesummary, } // N.B. good blocks = blocks - (orphans + rejects) -void _userinfo_block(BLOCKS *blocks, enum info_type isnew, bool lock) +void _userinfo_block(BLOCKS *blocks, enum info_type isnew, int delta, bool lock) { USERINFO *row; K_ITEM *item; @@ -5031,12 +5161,12 @@ void _userinfo_block(BLOCKS *blocks, enum info_type isnew, bool lock) if (lock) K_WLOCK(userinfo_free); if (isnew == INFO_NEW) { - row->blocks++; + row->blocks += delta; copy_tv(&(row->last_block), &(blocks->createdate)); } else if (isnew == INFO_ORPHAN) - row->orphans++; + row->orphans += delta; else if (isnew == INFO_REJECT) - row->rejects++; + row->rejects += delta; if (lock) K_WUNLOCK(userinfo_free); diff --git a/src/ckdb_dbio.c b/src/ckdb_dbio.c index e6552ed2..7c6e4a6b 100644 --- a/src/ckdb_dbio.c +++ b/src/ckdb_dbio.c @@ -1079,11 +1079,11 @@ K_ITEM *useratts_add(PGconn *conn, char *username, char *attname, else TXT_TO_BIGINT("attnum2", attnum2, row->attnum2); if (attdate == NULL || attdate[0] == '\0') - row->attdate.tv_sec = row->attdate.tv_usec = 0L; + DATE_ZERO(&(row->attdate)); else TXT_TO_TV("attdate", attdate, row->attdate); if (attdate2 == NULL || attdate2[0] == '\0') - row->attdate2.tv_sec = row->attdate2.tv_usec = 0L; + DATE_ZERO(&(row->attdate2)); else TXT_TO_TV("attdate2", attdate2, row->attdate2); @@ -2922,7 +2922,8 @@ bool workinfo_fill(PGconn *conn) if (!ok) { free_workinfo_data(item); k_add_head(workinfo_free, item); - } + } else + ok = set_prevcreatedate(0); //K_WUNLOCK(workinfo_free); PQclear(res); @@ -4299,6 +4300,7 @@ unparam: blocks_root = add_to_ktree(blocks_root, b_item, cmp_blocks); k_add_head(blocks_store, b_item); blocks_stats_rebuild = true; + // 'confirmed' is unchanged so no need to recalc *createdate } K_WUNLOCK(blocks_free); @@ -4439,7 +4441,7 @@ bool blocks_add(PGconn *conn, char *height, char *blockhash, } // We didn't use a Begin ok = true; - userinfo_block(row, INFO_NEW); + userinfo_block(row, INFO_NEW, 1); goto unparam; break; case BLOCKS_ORPHAN: @@ -4598,10 +4600,49 @@ bool blocks_add(PGconn *conn, char *height, char *blockhash, } update_old = true; - if (confirmed[0] == BLOCKS_ORPHAN) - userinfo_block(row, INFO_ORPHAN); - else if (confirmed[0] == BLOCKS_REJECT) - userinfo_block(row, INFO_REJECT); + /* handle confirmed state changes for userinfo + * this case statement handles all possible combinations + * even if they can't happen (yet) */ + switch (oldblocks->confirmed[0]) { + case BLOCKS_ORPHAN: + switch (confirmed[0]) { + case BLOCKS_ORPHAN: + break; + case BLOCKS_REJECT: + userinfo_block(row, INFO_ORPHAN, -1); + userinfo_block(row, INFO_REJECT, 1); + break; + default: + userinfo_block(row, INFO_ORPHAN, -1); + break; + } + break; + case BLOCKS_REJECT: + switch (confirmed[0]) { + case BLOCKS_REJECT: + break; + case BLOCKS_ORPHAN: + userinfo_block(row, INFO_REJECT, -1); + userinfo_block(row, INFO_ORPHAN, 1); + break; + default: + userinfo_block(row, INFO_REJECT, -1); + break; + } + break; + default: + switch (confirmed[0]) { + case BLOCKS_ORPHAN: + userinfo_block(row, INFO_ORPHAN, 1); + break; + case BLOCKS_REJECT: + userinfo_block(row, INFO_REJECT, 1); + break; + default: + break; + } + break; + } break; default: LOGERR("%s(): %s.failed.invalid confirm='%s'", @@ -4639,6 +4680,9 @@ flail: blocks_root = add_to_ktree(blocks_root, b_item, cmp_blocks); k_add_head(blocks_store, b_item); blocks_stats_rebuild = true; + // recalc the *createdate fields for possibly affected blocks + set_blockcreatedate(row->height); + set_prevcreatedate(row->height); } K_WUNLOCK(blocks_free); @@ -4712,6 +4756,7 @@ bool blocks_fill(PGconn *conn) { ExecStatusType rescode; PGresult *res; + K_TREE_CTX ctx[1]; K_ITEM *item; int n, i; BLOCKS *row; @@ -4863,16 +4908,30 @@ bool blocks_fill(PGconn *conn) pool.height = row->height; } - if (CURRENT(&(row->expirydate))) { - _userinfo_block(row, INFO_NEW, false); - if (row->confirmed[0] == BLOCKS_ORPHAN) - _userinfo_block(row, INFO_ORPHAN, false); - else if (row->confirmed[0] == BLOCKS_REJECT) - _userinfo_block(row, INFO_REJECT, false); - } + // first add all the NEW blocks + if (row->confirmed[0] == BLOCKS_NEW) + _userinfo_block(row, INFO_NEW, 1, false); } + if (!ok) k_add_head(blocks_free, item); + else + ok = set_blockcreatedate(0); + + // Now update all the CURRENT orphan/reject stats + if (ok) { + item = first_in_ktree(blocks_root, ctx); + while (item) { + DATA_BLOCKS(row, item); + if (CURRENT(&(row->expirydate))) { + if (row->confirmed[0] == BLOCKS_ORPHAN) + _userinfo_block(row, INFO_ORPHAN, 1, false); + else if (row->confirmed[0] == BLOCKS_REJECT) + _userinfo_block(row, INFO_REJECT, 1, false); + } + item = next_in_ktree(ctx); + } + } K_WUNLOCK(blocks_free); PQclear(res); @@ -5506,7 +5565,7 @@ bool payouts_fill(PGconn *conn) { ExecStatusType rescode; PGresult *res; - K_ITEM *item, *b_item, *b2_item; + K_ITEM *item, *b_item; K_TREE_CTX ctx[1]; PAYOUTS *row; BLOCKS *blocks; @@ -5633,17 +5692,10 @@ bool payouts_fill(PGconn *conn) ok = false; break; } else { - b2_item = find_blocks_new(b_item, ctx); - if (!b2_item) { - LOGERR("%s(): payoutid %"PRId64" references " - "block %"PRId32"/%s that has no NEW", - __func__, row->payoutid, row->height, - row->blockhash); - ok = false; - break; - } - DATA_BLOCKS(blocks, b2_item); - copy_tv(&(row->blockcreatedate), &(blocks->createdate)); + // blockcreatedate will already be set + DATA_BLOCKS(blocks, b_item); + copy_tv(&(row->blockcreatedate), + &(blocks->blockcreatedate)); } payouts_root = add_to_ktree(payouts_root, item, cmp_payouts);