diff --git a/pool/base.php b/pool/base.php index 828454a3..3e4c7c02 100644 --- a/pool/base.php +++ b/pool/base.php @@ -126,9 +126,12 @@ function btcfmt($amt) return number_format($amt, 8); } # -function utcd($when) +function utcd($when, $brief = false) { - return gmdate('Y-m-d H:i:s+00', round($when)); + if ($brief) + return gmdate('M-d H:i:s', round($when)); + else + return gmdate('Y-m-d H:i:s+00', round($when)); } # global $sipre; diff --git a/pool/page_shifts.php b/pool/page_shifts.php index 1be0d587..63ae6087 100644 --- a/pool/page_shifts.php +++ b/pool/page_shifts.php @@ -4,10 +4,11 @@ function doshifts($data, $user) { $ans = getShifts($user); - $pg = "\n"; + $pg = "Click here to jump to the start of the last payout

"; + $pg .= "
\n"; $pg .= ""; $pg .= ""; - $pg .= ""; + $pg .= ""; $pg .= ""; $pg .= ""; $pg .= ""; @@ -15,6 +16,8 @@ function doshifts($data, $user) $pg .= ""; $pg .= ""; $pg .= ""; + $pg .= ""; + $pg .= ""; $pg .= "\n"; if (($ans['STATUS'] != 'ok') || !isset($ans['prefix_all'])) @@ -28,9 +31,13 @@ function doshifts($data, $user) for ($i = 0; $i < $count; $i++) { $u = ''; + $mark = ''; if (isset($ans['lastpayoutstart:'.$i]) && $ans['lastpayoutstart:'.$i] != '') + { $u = 'u'; + $mark = ''; + } if (($i % 2) == 0) $row = "even$u"; else @@ -52,9 +59,9 @@ function doshifts($data, $user) $btc = ' '; else $btc = ''; - $pg .= ""; + $pg .= ""; $start = $ans['start:'.$i]; - $pg .= ''; + $pg .= ''; $nd = $ans['end:'.$i]; $elapsed = $nd - $start; $pg .= ''; @@ -72,10 +79,23 @@ function doshifts($data, $user) $avgsh = 0; $pg .= ''; $pg .= ''; + $ppsr = (float)$ans['ppsrewarded:'.$i]; + if ($ppsr > 0) + $ppsd = sprintf('%.3e', $ppsr); + else + $ppsd = '0'; + $pg .= ""; + $ppsv = (float)$ans['ppsvalue:'.$i]; + if ($ppsv > 0) + $pgot = number_format(100.0 * $ppsr / $ppsv, 2).'%'; + else + $pgot = '?'; + $pg .= ""; $pg .= "\n"; } } $pg .= "
ShiftStartStart UTCLengthYour DiffInv DiffSharesAvg ShareRewardsPPS*PPS%
$shif$btc$shif$btc$mark'.utcd($start).''.utcd($start, true).''.howmanyhrs($elapsed).''.number_format($avgsh, 2).''.$ans['rewards:'.$i].'$ppsd$pgot
\n"; + $pg .= "* The PPS value unit is satoshis
"; return $pg; } diff --git a/src/ckdb.h b/src/ckdb.h index cefe3dbe..4bb19b5b 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.300" +#define CKDB_VERSION DB_VERSION"-1.301" #define WHERE_FFL " - from %s %s() line %d" #define WHERE_FFL_HERE __FILE__, __func__, __LINE__ @@ -1294,6 +1294,7 @@ typedef struct optioncontrol { // Value it must default to (to work properly) #define OPTIONCONTROL_HEIGHT 1 +#define MAX_HEIGHT 999999999 // Test it here rather than obscuring the #define elsewhere #if ((OPTIONCONTROL_HEIGHT+1) != START_POOL_HEIGHT) @@ -1346,6 +1347,13 @@ extern tv_t last_bc; // current network diff extern double current_ndiff; +// Offset in binary coinbase1 of the block number +#define BLOCKNUM_OFFSET 42 +// Initial block reward (satoshi) +#define REWARD_BASE 5000000000.0 +// How many blocks per halving +#define REWARD_HALVE 210000.0 + // SHARES shares.id.json={...} typedef struct shares { int64_t workinfoid; @@ -2090,6 +2098,8 @@ extern void sequence_report(bool lock); #define REWARDOVERRIDE "MinerReward" +#define PPSOVERRIDE "PPSValue" + // Data free functions (first) #define FREE_ITEM(item) do { } while(0) // TODO: make a macro for all other to use above macro @@ -2264,7 +2274,7 @@ extern K_ITEM *find_first_paypayid(int64_t userid, int64_t payoutid, K_TREE_CTX extern cmp_t cmp_accountbalance(K_ITEM *a, K_ITEM *b); extern K_ITEM *find_accountbalance(int64_t userid); extern cmp_t cmp_optioncontrol(K_ITEM *a, K_ITEM *b); -extern K_ITEM *find_optioncontrol(char *optionname, tv_t *now, int32_t height); +extern K_ITEM *find_optioncontrol(char *optionname, const tv_t *now, int32_t height); extern int64_t user_sys_setting(int64_t userid, char *setting_name, int64_t setting_default, tv_t *now); extern cmp_t cmp_workinfo(K_ITEM *a, K_ITEM *b); @@ -2279,6 +2289,7 @@ extern bool workinfo_age(int64_t workinfoid, char *poolinstance, char *by, char *code, char *inet, tv_t *cd, tv_t *ss_first, tv_t *ss_last, int64_t *ss_count, int64_t *s_count, int64_t *s_diff); +extern double workinfo_pps(K_ITEM *w_item, int64_t workinfoid, bool lock); extern cmp_t cmp_shares(K_ITEM *a, K_ITEM *b); extern cmp_t cmp_shareerrors(K_ITEM *a, K_ITEM *b); extern void dsp_sharesummary(K_ITEM *item, FILE *stream); diff --git a/src/ckdb_cmd.c b/src/ckdb_cmd.c index 0e1c23e7..935adca5 100644 --- a/src/ckdb_cmd.c +++ b/src/ckdb_cmd.c @@ -5383,6 +5383,16 @@ static char *cmd_shifts(__maybe_unused PGconn *conn, char *cmd, char *id, rows, wm->rewards, FLDSEP); APPEND_REALLOC(buf, off, len, tmp); + // Use %.15e -> 16 non-leading-zero decimal places + snprintf(tmp, sizeof(tmp), "ppsvalue:%d=%.15f%c", + rows, wm->pps_value, FLDSEP); + APPEND_REALLOC(buf, off, len, tmp); + + // Use %.15e -> 16 non-leading-zero decimal places + snprintf(tmp, sizeof(tmp), "ppsrewarded:%d=%.15e%c", + rows, wm->rewarded, FLDSEP); + APPEND_REALLOC(buf, off, len, tmp); + snprintf(tmp, sizeof(tmp), "lastpayoutstart:%d=%s%c", rows, (wm->workinfoidstart == @@ -5431,7 +5441,8 @@ static char *cmd_shifts(__maybe_unused PGconn *conn, char *cmd, char *id, snprintf(tmp, sizeof(tmp), "rows=%d%cflds=%s%c", rows, FLDSEP, "markerid,shift,start,end,rewards," - "lastpayoutstart", FLDSEP); + "ppsvalue,ppsrewarded,lastpayoutstart", + FLDSEP); APPEND_REALLOC(buf, off, len, tmp); snprintf(tmp, sizeof(tmp), "arn=%s", "Shifts"); diff --git a/src/ckdb_data.c b/src/ckdb_data.c index a7cc7eb3..720f3a8e 100644 --- a/src/ckdb_data.c +++ b/src/ckdb_data.c @@ -1822,7 +1822,7 @@ static bool _reward_override_name(int32_t height, char *buf, size_t siz, } // Must be R or W locked before call -K_ITEM *find_optioncontrol(char *optionname, tv_t *now, int32_t height) +K_ITEM *find_optioncontrol(char *optionname, const tv_t *now, int32_t height) { OPTIONCONTROL optioncontrol, *oc, *ocbest; K_TREE_CTX ctx[1]; @@ -1832,6 +1832,9 @@ K_ITEM *find_optioncontrol(char *optionname, tv_t *now, int32_t height) * 1) activationdate is <= now * and * 2) height <= specified height (pool.height = current) + * The logic being: if 'now' is after the record activation date + * and 'height' is after the record activation height then + * the record is active * Remember the active record with the newest activationdate * If two records have the same activation date, then * remember the active record with the highest height @@ -2197,6 +2200,61 @@ bye: return ok; } + +// The PPS value of a 1diff share for the given workinfoid +double workinfo_pps(K_ITEM *w_item, int64_t workinfoid, bool lock) +{ + OPTIONCONTROL *optioncontrol; + K_ITEM *oc_item; + char oc_name[TXT_SML+1]; + char coinbase1bin[TXT_SML+1]; + char ndiffbin[TXT_SML+1]; + WORKINFO *workinfo; + double w_diff; + int w_blocknum; + size_t len; + + // Allow optioncontrol override for a given workinfoid + snprintf(oc_name, sizeof(oc_name), PPSOVERRIDE"_%"PRId64, workinfoid); + if (lock) + K_RLOCK(optioncontrol_free); + // No time/height control is used, just find the latest record + oc_item = find_optioncontrol(oc_name, &date_eot, MAX_HEIGHT); + if (lock) + K_RUNLOCK(optioncontrol_free); + + // Value is a floating point double of satoshi + if (oc_item) { + DATA_OPTIONCONTROL(optioncontrol, oc_item); + return atof(optioncontrol->optionvalue); + } + + if (!w_item) { + LOGERR("%s(): missing workinfo %"PRId64, + __func__, workinfoid); + return 0.0; + } + + DATA_WORKINFO(workinfo, w_item); + len = strlen(workinfo->coinbase1); + if (len < (BLOCKNUM_OFFSET * 2 + 4) || (len & 1)) { + LOGERR("%s(): Invalid coinbase1 len %d - " + "should be >= %d and even - for wid %"PRId64, + __func__, (int)len, (BLOCKNUM_OFFSET * 2 + 4), + workinfoid); + return 0.0; + } + + hex2bin(ndiffbin, workinfo->bits, 4); + w_diff = diff_from_nbits(ndiffbin); + hex2bin(coinbase1bin, workinfo->coinbase1 + (BLOCKNUM_OFFSET * 2), + (len - (BLOCKNUM_OFFSET * 2)) >> 1); + w_blocknum = get_sernumber((uchar *)coinbase1bin); + + // BASE halving to determine coinbase reward then divided by difficulty + return(REWARD_BASE * pow(0.5, floor((double)w_blocknum / REWARD_HALVE)) / w_diff); +} + // order by workinfoid asc,userid asc,workername asc,createdate asc,nonce asc,expirydate desc cmp_t cmp_shares(K_ITEM *a, K_ITEM *b) { @@ -4767,6 +4825,10 @@ bool reward_shifts(PAYOUTS *payouts, bool lock, int delta) K_ITEM *wm_item; WORKMARKERS *wm; bool did_one = false; + double payout_pps; + + payout_pps = (double)delta * (double)(payouts->minerreward) / + payouts->diffused; if (lock) K_WLOCK(workmarkers_free); @@ -4781,6 +4843,7 @@ bool reward_shifts(PAYOUTS *payouts, bool lock, int delta) * onto the PROCESSED status if it isn't already processed */ if (CURRENT(&(wm->expirydate))) { wm->rewards += delta; + wm->rewarded += payout_pps; did_one = true; } wm_item = next_in_ktree(ctx); @@ -4792,7 +4855,13 @@ bool reward_shifts(PAYOUTS *payouts, bool lock, int delta) return did_one; } -// (re)calculate rewards for a shift +/* (re)calculate rewards for a shift + * N.B. we don't need to zero/undo a workmarkers rewards directly + * since this is just a counter of how many times it's been rewarded + * and thus if the shift is expired the counter is ignored + * We only need to (re)calculate it when the workmarker is created + * Payouts code processing will increment/decrement all current rewards as + * needed with reward_shifts() when payouts are added/changed/removed */ bool shift_rewards(K_ITEM *wm_item) { PAYOUTS *payouts = NULL; diff --git a/src/ckdb_dbio.c b/src/ckdb_dbio.c index 881f9b3f..db2ed64f 100644 --- a/src/ckdb_dbio.c +++ b/src/ckdb_dbio.c @@ -6628,12 +6628,13 @@ bool _workmarkers_process(PGconn *conn, bool already, bool add, WHERE_FFL_PASS); goto rollback; } - w_item = find_workinfo(workinfoidend, NULL); + w_item = find_workinfo(workinfoidstart, NULL); if (!w_item) goto rollback; - w_item = find_workinfo(workinfoidstart, NULL); + w_item = find_workinfo(workinfoidend, NULL); if (!w_item) goto rollback; + K_WLOCK(workmarkers_free); wm_item = k_unlink_head(workmarkers_free); K_WUNLOCK(workmarkers_free); @@ -6698,6 +6699,7 @@ bool _workmarkers_process(PGconn *conn, bool already, bool add, PGLOGERR("Insert", rescode, conn); goto rollback; } + row->pps_value = workinfo_pps(w_item, workinfoidend, true); } ok = true; @@ -6837,9 +6839,18 @@ bool workmarkers_fill(PGconn *conn) add_to_ktree(workmarkers_workinfoid_root, item); k_add_head(workmarkers_store, item); + wi_item = find_workinfo(row->workinfoidend, NULL); + if (!wi_item) { + LOGERR("%s(): ERROR workmarkerid %"PRId64 + " wid end %"PRId64" doesn't exist! " + "PPS value will be zero", + __func__, row->markerid, + row->workinfoidend); + } + row->pps_value = workinfo_pps(wi_item, row->workinfoidend, false); + if (dbstatus.newest_workmarker_workinfoid < row->workinfoidend) { dbstatus.newest_workmarker_workinfoid = row->workinfoidend; - wi_item = find_workinfo(row->workinfoidend, NULL); if (!wi_item) { LOGEMERG("%s(): FAILURE workmarkerid %"PRId64 " wid end %"PRId64" doesn't exist! "