diff --git a/configure.ac b/configure.ac index 49d28ee2..d55e77b6 100644 --- a/configure.ac +++ b/configure.ac @@ -38,6 +38,7 @@ AC_CHECK_HEADERS(alloca.h pthread.h stdio.h math.h signal.h sys/prctl.h) AC_CHECK_HEADERS(sys/types.h sys/socket.h sys/stat.h linux/un.h netdb.h) AC_CHECK_HEADERS(stdint.h netinet/in.h netinet/tcp.h sys/ioctl.h getopt.h) AC_CHECK_HEADERS(sys/epoll.h libpq-fe.h postgresql/libpq-fe.h grp.h) +AC_CHECK_HEADERS(gsl/gsl_math.h gsl/gsl_cdf.h) PTHREAD_LIBS="-lpthread" MATH_LIBS="-lm" @@ -59,8 +60,12 @@ AC_ARG_WITH([ckdb], if test "x$ckdb" != "xno"; then AC_CHECK_LIB([pq], [main],[PQ=-lpq],echo "Error: Required library libpq-dev not found. Install it or disable postgresql support with --without-ckdb" && exit 1) + AC_CHECK_LIB([gsl], [main],[GSL=-lgsl],echo "Error: Required library gsl-dev + not found. Install it or disable support with --without-ckdb" && exit 1) + AC_CHECK_LIB([gslcblas], [main],[GSLCBLAS=-lgslcblas],echo "Error: Required library gslcblas + not found. Install it or disable support with --without-ckdb" && exit 1) AC_DEFINE([USE_CKDB], [1], [Defined to 1 if ckdb support required]) - PQ_LIBS="-lpq" + PQ_LIBS="-lpq -lgsl -lgslcblas" else PQ_LIBS="" fi diff --git a/html/BTCSym.png b/html/BTCSym.png new file mode 100644 index 00000000..7e2114c2 Binary files /dev/null and b/html/BTCSym.png differ diff --git a/pool/base.php b/pool/base.php index 1c9824f2..a109a23c 100644 --- a/pool/base.php +++ b/pool/base.php @@ -63,12 +63,38 @@ function howlongago($sec) return $des; } # +function howmanyhrs($tot) +{ + $sec = round($tot); + if ($sec < 60) + $des = $sec.'s'; + else + { + $min = floor($sec / 60); + $sec -= $min * 60; + if ($min < 60) + $des = $min.'m '.$sec.'s'; + else + { + $hr = floor($min / 60); + $min -= $hr * 60; + $des = $hr.'hr '.$min.'m '.$sec.'s'; + } + } + return $des; +} +# function btcfmt($amt) { $amt /= 100000000; return number_format($amt, 8); } # +function utcd($when) +{ + return gmdate('Y-m-d H:i:s+00', round($when)); +} +# global $sipre; # max of uint64 is ~1.845x10^19, 'Z' is above that (10^21) # max of uint256 is ~1.158x10^77, which is well above 'Y' (10^24) @@ -308,7 +334,9 @@ function validUserPass($user, $pass) $key = 'ckp'.rand(1000000,9999999); $_SESSION['ckpkey'] = $key; $_SESSION[$key] = array('who' => $user, 'id' => $user); + return true; } + return false; } # function logout() @@ -338,6 +366,8 @@ function requestRegister() # function tryLogInOut() { + global $loginfailed; + // If already logged in, it will ignore User/Pass if (isset($_SESSION['ckpkey'])) { @@ -347,21 +377,29 @@ function tryLogInOut() } else { + $login = getparam('Login', false); + if (nuem($login)) + return; + $user = getparam('User', false); if ($user !== NULL) $user = loginStr($user); if (nuem($user)) + { + $loginfailed = true; return; + } $pass = getparam('Pass', false); if (nuem($pass)) + { + $loginfailed = true; return; + } - $login = getparam('Login', false); - if (nuem($login)) - return; - - validUserPass($user, $pass); + $valid = validUserPass($user, $pass); + if (!$valid) + $loginfailed = true; } } # diff --git a/pool/db.php b/pool/db.php index 7002d0d5..6c85e9cd 100644 --- a/pool/db.php +++ b/pool/db.php @@ -258,6 +258,30 @@ function getPayments($user) return repDecode($rep); } # +function getMPayouts($user) +{ + if ($user == false) + showIndex(); + $flds = array('username' => $user); + $msg = msgEncode('mpayouts', 'mp', $flds, $user); + $rep = sendsockreply('getMPayments', $msg); + if (!$rep) + dbdown(); + return repDecode($rep); +} +# +function getShifts($user) +{ + if ($user == false) + showIndex(); + $flds = array('username' => $user); + $msg = msgEncode('shifts', 'shift', $flds, $user); + $rep = sendsockreply('getShifts', $msg); + if (!$rep) + dbdown(); + return repDecode($rep); +} +# function getBlocks($user) { if ($user == false) diff --git a/pool/page.php b/pool/page.php index 633ea0d1..4ecccb6c 100644 --- a/pool/page.php +++ b/pool/page.php @@ -110,6 +110,8 @@ div.topd {background-color:#cff; border-color: #cff; border-style: solid; border .topdat {margin-left: 8px; margin-right: 24px; color:green; font-weight: bold;} span.login {float: right; margin-left: 8px; margin-right: 24px;} span.hil {color:blue;} +span.user {color:green;} +span.addr {color:brown;} span.warn {color:orange; font-weight:bold;} span.urg {color:red; font-weight:bold;} span.err {color:red; font-weight:bold; font-size:120%;} @@ -161,7 +163,7 @@ h1 {margin-top: 20px; float:middle; font-size: 20px;} # function pgtop($info, $dotop, $user, $douser) { - global $site_title; + global $site_title, $loginfailed; $phr = '?THs'; $plb = '?'; @@ -286,6 +288,8 @@ function pgtop($info, $dotop, $user, $douser) $top .= " You need to enable javascript to use"; $top .= " the $site_title web site."; + if ($loginfailed === true) + $top .= '
Login Failed
'; if (isset($info['u_nopayaddr'])) $top .= '
Please set a payout address on your account!
'; if (isset($info['u_noemail'])) diff --git a/pool/page_blocks.php b/pool/page_blocks.php index 4040ed5b..1556323c 100644 --- a/pool/page_blocks.php +++ b/pool/page_blocks.php @@ -56,6 +56,43 @@ function doblocks($data, $user) if ($wantcsv === false) { + if ($ans['STATUS'] == 'ok' and isset($ans['s_rows']) and $ans['s_rows'] > 0) + { + $pg .= '

Block Statistics

'; + $pg .= "\n"; + $pg .= ""; + $pg .= ""; + $pg .= ""; + $pg .= ""; + $pg .= ""; + $pg .= ""; + $pg .= "\n"; + + $count = $ans['s_rows']; + for ($i = 0; $i < $count; $i++) + { + if (($i % 2) == 0) + $row = 'even'; + else + $row = 'odd'; + + $desc = $ans['s_desc:'.$i]; + $diff = number_format(100 * $ans['s_diffratio:'.$i], 2); + $mean = number_format(100 * $ans['s_diffmean:'.$i], 2); + $cdferl = number_format($ans['s_cdferl:'.$i], 4); + $luck = number_format(100 * $ans['s_luck:'.$i], 2); + + $pg .= ""; + $pg .= ""; + $pg .= ""; + $pg .= ""; + $pg .= ""; + $pg .= ""; + $pg .= "\n"; + } + $pg .= "
DescriptionDiff%Mean%CDF[Erl]Luck%
$desc Blocks$diff%$mean%$cdferl$luck%
\n"; + } + if ($ans['STATUS'] == 'ok') { $count = $ans['rows']; @@ -70,15 +107,15 @@ function doblocks($data, $user) $s = 's'; } - $pg = "

Last$num Block$s

"; + $pg .= "

Last$num Block$s

"; } else - $pg = '

Blocks

'; + $pg .= '

Blocks

'; list($fg, $bg) = pctcolour(25.0); $pg .= ""; $pg .= " Green  "; - $pg .= 'is good luck. Lower Diff% and bright green is best luck.
'; + $pg .= 'is good luck. Lower Diff% and brighter green is better luck.
'; list($fg, $bg) = pctcolour(100.0); $pg .= ""; $pg .= " 100%  "; @@ -86,7 +123,7 @@ function doblocks($data, $user) list($fg, $bg) = pctcolour(400.0); $pg .= ""; $pg .= " Red  "; - $pg .= 'is bad luck. Higher Diff% and bright red is worse luck.

'; + $pg .= 'is bad luck. Higher Diff% and brighter red is worse luck.

'; $pg .= "\n"; $pg .= ""; @@ -94,7 +131,7 @@ function doblocks($data, $user) $pg .= ""; if ($user !== null) $pg .= ""; - $pg .= ""; + $pg .= ""; $pg .= ""; $pg .= ""; $pg .= ""; @@ -109,7 +146,6 @@ function doblocks($data, $user) $csv = "Sequence,Height,Status,Timestamp,DiffAcc,NetDiff,Hash\n"; if ($ans['STATUS'] == 'ok') { - $tot = $ans['tot']; $count = $ans['rows']; for ($i = 0; $i < $count; $i++) { @@ -130,7 +166,7 @@ function doblocks($data, $user) $seq = ''; } else - $seq = $tot--; + $seq = $ans['seq:'.$i]; if ($stat == '1-Confirm') { if (isset($data['info']['lastheight'])) @@ -143,37 +179,34 @@ function doblocks($data, $user) } $stara = ''; - $starp = ''; - if (isset($ans['status:'.($i+1)])) - if ($ans['status:'.($i+1)] == 'Orphan' - && $stat != 'Orphan') - { - $stara = '*'; - $starp = '*'; - } + if ($stat == 'Orphan') + $stara = '*'; $diffacc = $ans['diffacc:'.$i]; $acc = number_format($diffacc, 0); $netdiff = $ans['netdiff:'.$i]; - if ($netdiff > 0) + $diffratio = $ans['diffratio:'.$i]; + $cdf = $ans['cdf:'.$i]; + $luck = $ans['luck:'.$i]; + + if ($diffratio > 0) { - $pct = 100.0 * $diffacc / $netdiff; + $pct = 100.0 * $diffratio; list($fg, $bg) = pctcolour($pct); - $bpct = "$starp".number_format($pct, 2).'%'; + $bpct = "".number_format($pct, 2).'%'; $bg = " bgcolor=$bg"; $blktot += $diffacc; if ($stat != 'Orphan') $nettot += $netdiff; - $cdfv = 1 - exp(-1 * $diffacc / $netdiff); - $cdf = number_format($cdfv, 2); + $cdfdsp = number_format($cdf, 2); } else { $bg = ''; $bpct = '?'; - $cdf = '?'; + $cdfdsp = '?'; } if ($wantcsv === false) @@ -184,11 +217,11 @@ function doblocks($data, $user) if ($user !== null) $pg .= "'; $pg .= "'; - $pg .= "'; + $pg .= "'; $pg .= "'; $pg .= ""; $pg .= ""; - $pg .= ""; + $pg .= ""; $pg .= "\n"; } else @@ -208,39 +241,16 @@ function doblocks($data, $user) echo $csv; exit(0); } - if ($nettot > 0) + if ($orph === true) { - if (($i % 2) == 0) - $row = 'even'; - else - $row = 'odd'; - - $pct = 100.0 * $blktot / $nettot; - list($fg, $bg) = pctcolour($pct); - $bpct = "".number_format($pct, 2).'%'; - $bg = " bgcolor=$bg"; - - $pg .= ""; - $pg .= ''; - $pg .= '\n"; - if ($orph === true) - { - $pg .= ''; + $diffused = $ans['diffused:'.$i]; + $pg .= ''; + $elapsed = $ans['elapsed:'.$i]; + $pg .= ''; + $phr = $diffused * pow(2,32) / $elapsed; + $pg .= ''; + $diffacc = $ans['diffacc:'.$i]; + $ypct = $diffacc * 100 / $diffused; + $pg .= ''; + $pg .= ''; + $hr = $diffacc * pow(2,32) / $elapsed; + $pg .= ''; + $amount = $ans['amount:'.$i]; + $totamt += $amount; + $pg .= ''; + $pg .= "\n"; + } + if ($count > 1) + { + if (($i % 2) == 0) + $row = 'even'; + else + $row = 'odd'; + + $pg .= ""; + $pg .= ''; + $pg .= ''; + $pg .= ''; + $pg .= "\n"; + } + } + $pg .= "
HeightWhoRewardBlock RewardWhenStatusDiff".htmlspecialchars($ans['workername:'.$i]).'".btcfmt($ans['reward:'.$i]).'".gmdate('Y-m-d H:i:s+00', $ans['firstcreatedate:'.$i]).'".utcd($ans['firstcreatedate:'.$i]).'".$stat.'$stara$acc$bpct$cdf$cdfdsp
Total:'; - $pg .= "
'; + $pg .= ''.btcfmt($ans['minerreward:'.$i]).''.difffmt($diffused).''.howmanyhrs($elapsed).''.siprefmt($phr).'Hs'.number_format($ypct, 2).'%'.difffmt($diffacc).''.dsprate($hr).''.btcfmt($amount).'
Total:'.btcfmt($totamt).'
\n"; + + return $pg; +} +# +function show_mpayouts($info, $page, $menu, $name, $user) +{ + gopage($info, NULL, 'dompayouts', $page, $menu, $name, $user); +} +# +?> diff --git a/pool/page_payments.php b/pool/page_payments.php index 280b0a6c..5bd97ff0 100644 --- a/pool/page_payments.php +++ b/pool/page_payments.php @@ -1,26 +1,45 @@ Payments'; - $pg .= 'The payout transactions on blockchain are here:'; - $pg .= " BTC

"; + $pg .= 'The payment transactions on blockchain are here:'; + $pg .= " BTC
"; + $pg .= "The payments below don't yet show when they have been sent.

"; $ans = getPayments($user); $pg .= "\n"; $pg .= ""; - $pg .= ""; + $pg .= ""; $pg .= ""; + $pg .= ""; $pg .= ""; + $pg .= ""; $pg .= "\n"; if ($ans['STATUS'] == 'ok') { + $all = array(); $count = $ans['rows']; for ($i = 0; $i < $count; $i++) + { + $all[] = array('payoutid' => $ans['payoutid:'.$i], + 'height' => $ans['height:'.$i], + 'payaddress' => $ans['payaddress:'.$i], + 'amount' => $ans['amount:'.$i], + 'paydate' => $ans['paydate:'.$i]); + } + usort($all, 'sortheight'); + $hasdust = false; + for ($i = 0; $i < $count; $i++) { if (($i % 2) == 0) $row = 'even'; @@ -28,11 +47,28 @@ function dopayments($data, $user) $row = 'odd'; $pg .= ""; - $pg .= ''; - $pg .= ''; - $pg .= ''; + $pg .= ''; + $pg .= ''; + $pg .= ''; + $amount = $all[$i]['amount']; + if ($amount < '10000') + { + $dust = '*'; + $hasdust = true; + } + else + $dust = ' '; + $pg .= ''; + $pg .= ""; $pg .= "\n"; } + if ($hasdust === true) + { + $pg .= ''; + } } $pg .= "
DateBlockAddressStatusBTC
'.$ans['paydate:'.$i].''.$ans['payaddress:'.$i].''.btcfmt($ans['amount:'.$i]).''.$all[$i]['height'].''.$all[$i]['payaddress'].' '.btcfmt($amount).'$dust
'; + $pg .= '* '; + $pg .= 'Dust payments are not automatically sent out'; + $pg .= '
\n"; diff --git a/pool/page_shifts.php b/pool/page_shifts.php new file mode 100644 index 00000000..b83d3ec6 --- /dev/null +++ b/pool/page_shifts.php @@ -0,0 +1,76 @@ +\n"; + $pg .= ""; + $pg .= "Shift"; + $pg .= "Start"; + $pg .= "Length"; + $pg .= "Your Diff"; + $pg .= "Avg Hs"; + $pg .= "Shares"; + $pg .= "Avg Share"; + $pg .= "\n"; + if ($ans['STATUS'] != 'ok') + $pg = '

Shifts

'.$pg; + else + { + $count = $ans['rows']; + $pg = '

Last '.($count+1).' Shifts

'.$pg; + for ($i = 0; $i < $count; $i++) + { + if (($i % 2) == 0) + $row = 'even'; + else + $row = 'odd'; + + $pg .= ""; + $shifname = $ans['shift:'.$i]; + $shif = preg_replace(array('/^.* to /','/^.*fin: /'), '', $shifname); + $ablock = false; + if (preg_match('/to.*Block.* fin/', $shifname) == 1) + $ablock = true; + else + { + $shifex = $ans['endmarkextra:'.$i]; + if (preg_match('/Block .* fin/', $shifex) == 1) + $ablock = true; + } + if ($ablock === true) + $btc = ' '; + else + $btc = ''; + $pg .= "$shif$btc"; + $start = $ans['start:'.$i]; + $pg .= ''.utcd($start).''; + $nd = $ans['end:'.$i]; + $elapsed = $nd - $start; + $pg .= ''.howmanyhrs($elapsed).''; + $diffacc = $ans['diffacc:'.$i]; + $pg .= ''.difffmt($diffacc).''; + $hr = $diffacc * pow(2,32) / $elapsed; + $pg .= ''.dsprate($hr).''; + $shareacc = $ans['shareacc:'.$i]; + $pg .= ''.difffmt($shareacc).''; + if ($shareacc > 0) + $avgsh = $diffacc / $shareacc; + else + $avgsh = 0; + $pg .= ''.number_format($avgsh, 2).''; + $pg .= "\n"; + } + } + $pg .= "\n"; + + return $pg; +} +# +function show_shifts($info, $page, $menu, $name, $user) +{ + gopage($info, NULL, 'doshifts', $page, $menu, $name, $user); +} +# +?> diff --git a/pool/prime.php b/pool/prime.php index 0cfe79af..6ee31cfb 100644 --- a/pool/prime.php +++ b/pool/prime.php @@ -3,6 +3,9 @@ global $stt; $stt = microtime(); # +global $loginfailed; +$loginfailed = false; +# include_once('param.php'); include_once('base.php'); # @@ -67,11 +70,13 @@ function check() 'Home' => '' ), 'Account' => array( + 'Rewards' => 'mpayouts', 'Payments' => 'payments', 'Settings' => 'settings', 'User Settings' => 'userset' ), 'Workers' => array( + 'Shifts ' => 'shifts', 'Workers ' => 'workers', 'Management' => 'workmgt', ), diff --git a/src/Makefile.am b/src/Makefile.am index 5e032e62..61d13a6d 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -23,5 +23,5 @@ if WANT_CKDB bin_PROGRAMS += ckdb ckdb_SOURCES = ckdb.c ckdb_cmd.c ckdb_data.c ckdb_dbio.c ckdb_btc.c \ ckdb.h klist.c ktree.c klist.h ktree.h -ckdb_LDADD = libckpool.la @JANSSON_LIBS@ @PQ_LIBS@ +ckdb_LDADD = libckpool.la @JANSSON_LIBS@ @PQ_LIBS@ @MATH_LIBS@ endif diff --git a/src/ckdb.c b/src/ckdb.c index 222682db..ac39b470 100644 --- a/src/ckdb.c +++ b/src/ckdb.c @@ -279,6 +279,7 @@ bool everyone_die = false; tv_t last_heartbeat; tv_t last_workinfo; tv_t last_share; +tv_t last_share_inv; tv_t last_auth; cklock_t last_lock; @@ -398,6 +399,8 @@ const char *blocks_unknown = "?Unknown?"; K_TREE *blocks_root; K_LIST *blocks_free; K_STORE *blocks_store; +tv_t blocks_stats_time; +bool blocks_stats_rebuild = true; // MININGPAYOUTS K_TREE *miningpayouts_root; @@ -2663,6 +2666,8 @@ static void *socketer(__maybe_unused void *arg) case CMD_PPLNS: case CMD_PPLNS2: case CMD_PAYOUTS: + case CMD_MPAYOUTS: + case CMD_SHIFTS: case CMD_DSP: case CMD_BLOCKSTATUS: if (!startup_complete) { @@ -2882,6 +2887,8 @@ static bool reload_line(PGconn *conn, char *filename, uint64_t count, char *buf) case CMD_PPLNS: case CMD_PPLNS2: case CMD_PAYOUTS: + case CMD_MPAYOUTS: + case CMD_SHIFTS: case CMD_USERSTATUS: case CMD_MARKS: LOGERR("%s() Message line %"PRIu64" '%s' - invalid - ignored", @@ -3250,6 +3257,7 @@ static void *listener(void *arg) setnow(&last_heartbeat); copy_tv(&last_workinfo, &last_heartbeat); copy_tv(&last_share, &last_heartbeat); + copy_tv(&last_share_inv, &last_heartbeat); copy_tv(&last_auth, &last_heartbeat); ck_wunlock(&last_lock); diff --git a/src/ckdb.h b/src/ckdb.h index 34c91f7c..ecddeebc 100644 --- a/src/ckdb.h +++ b/src/ckdb.h @@ -37,6 +37,9 @@ #include #endif +#include +#include + #include "ckpool.h" #include "libckpool.h" @@ -52,7 +55,7 @@ #define DB_VLOCK "1" #define DB_VERSION "1.0.0" -#define CKDB_VERSION DB_VERSION"-1.007" +#define CKDB_VERSION DB_VERSION"-1.023" #define WHERE_FFL " - from %s %s() line %d" #define WHERE_FFL_HERE __FILE__, __func__, __LINE__ @@ -192,6 +195,11 @@ extern POOLSTATUS pool; _dstoff += _srclen; \ } while(0) +#define APPEND_REALLOC_RESET(_buf, _off) do { \ + (_buf)[0] = '\0'; \ + _off = 0; \ + } while(0) + enum data_type { TYPE_STR, TYPE_BIGINT, @@ -274,6 +282,7 @@ extern bool everyone_die; extern tv_t last_heartbeat; extern tv_t last_workinfo; extern tv_t last_share; +extern tv_t last_share_inv; extern tv_t last_auth; extern cklock_t last_lock; @@ -341,6 +350,8 @@ enum cmd_values { CMD_PPLNS, CMD_PPLNS2, CMD_PAYOUTS, + CMD_MPAYOUTS, + CMD_SHIFTS, CMD_USERSTATUS, CMD_MARKS, CMD_END @@ -1092,6 +1103,26 @@ typedef struct blocks { char statsconfirmed[TXT_FLAG+1]; HISTORYDATECONTROLFIELDS; bool ignore; // Non DB field + + // Calculated only when = 0 + double netdiff; + + /* Non DB fields for the web page + * Calculate them once off/recalc them when required */ + double blockdiffratio; + double blockcdf; + double blockluck; + + /* diffacc for range calculations - includes orphans before it + * orphans have this set to 0 so they can't be double counted */ + double diffcalc; + + /* From the last found block to this one + * Orphans have these set to zero */ + double diffratio; + double diffmean; + double cdferl; + double luck; } BLOCKS; #define ALLOC_BLOCKS 100 @@ -1131,6 +1162,8 @@ extern const char *blocks_unknown; extern K_TREE *blocks_root; extern K_LIST *blocks_free; extern K_STORE *blocks_store; +extern tv_t blocks_stats_time; +extern bool blocks_stats_rebuild; // MININGPAYOUTS typedef struct miningpayouts { @@ -1190,6 +1223,10 @@ extern cklock_t process_pplns_lock; #define PAYOUTS_PROCESSING 'P' #define PAYOUTS_PROCESSING_STR "P" #define PAYPROCESSING(_status) ((_status)[0] == PAYOUTS_PROCESSING) +// An orphaned payout must be ignored +#define PAYOUTS_ORPHAN 'O' +#define PAYOUTS_ORPHAN_STR "O" +#define PAYORPHAN(_status) ((_status)[0] == PAYOUTS_ORPHAN) /* // EVENTLOG @@ -1717,6 +1754,7 @@ extern int32_t _coinbase1height(char *coinbase1, WHERE_FFL_ARGS); extern cmp_t _cmp_height(char *coinbase1a, char *coinbase1b, WHERE_FFL_ARGS); extern cmp_t cmp_workinfo_height(K_ITEM *a, K_ITEM *b); extern K_ITEM *find_workinfo(int64_t workinfoid, K_TREE_CTX *ctx); +extern K_ITEM *next_workinfo(int64_t workinfoid, K_TREE_CTX *ctx); extern bool workinfo_age(PGconn *conn, 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, @@ -1737,6 +1775,8 @@ void _dbhash2btchash(char *hash, char *buf, size_t siz, WHERE_FFL_ARGS); #define dsp_hash(_hash, _buf, _siz) \ _dsp_hash(_hash, _buf, _siz, WHERE_FFL_HERE) extern void _dsp_hash(char *hash, char *buf, size_t siz, WHERE_FFL_ARGS); +#define blockhash_diff(_hash) _blockhash_diff(_hash, WHERE_FFL_HERE) +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); @@ -1744,6 +1784,7 @@ 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); 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); diff --git a/src/ckdb_cmd.c b/src/ckdb_cmd.c index a9c32c77..80b1f9d1 100644 --- a/src/ckdb_cmd.c +++ b/src/ckdb_cmd.c @@ -811,23 +811,30 @@ static char *cmd_blocklist(__maybe_unused PGconn *conn, char *cmd, char *id, __maybe_unused K_TREE *trf_root) { K_TREE_CTX ctx[1]; - K_ITEM *b_item, *w_item; + K_ITEM *b_item; BLOCKS *blocks; char reply[1024] = ""; char tmp[1024]; - char *buf; + char *buf, *desc, desc_buf[64]; size_t len, off; int32_t height = -1; - tv_t first_cd = {0,0}; - int rows, tot; + tv_t first_cd = {0,0}, stats_tv = {0,0}, stats_tv2 = {0,0}; + int rows, srows, tot, seq; + bool has_stats; LOGDEBUG("%s(): cmd '%s'", __func__, cmd); APPEND_REALLOC_INIT(buf, off, len); APPEND_REALLOC(buf, off, len, "ok."); - rows = 0; + +redo: + K_WLOCK(blocks_free); + has_stats = check_update_blocks_stats(&stats_tv); + K_WUNLOCK(blocks_free); + + srows = rows = 0; K_RLOCK(blocks_free); - b_item = last_in_ktree(blocks_root, ctx); + b_item = first_in_ktree(blocks_root, ctx); tot = 0; while (b_item) { DATA_BLOCKS(blocks, b_item); @@ -835,16 +842,31 @@ static char *cmd_blocklist(__maybe_unused PGconn *conn, char *cmd, char *id, if (blocks->confirmed[0] != BLOCKS_ORPHAN) tot++; } - b_item = prev_in_ktree(ctx); + b_item = next_in_ktree(ctx); } + seq = tot; b_item = last_in_ktree(blocks_root, ctx); while (b_item && rows < 42) { 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) { + snprintf(tmp, sizeof(tmp), + "seq:%d=o%c", + rows, FLDSEP); + APPEND_REALLOC(buf, off, len, tmp); + } else { + snprintf(tmp, sizeof(tmp), + "seq:%d=%d%c", + rows, seq--, FLDSEP); + APPEND_REALLOC(buf, off, len, tmp); + } int_to_buf(blocks->height, reply, sizeof(reply)); snprintf(tmp, sizeof(tmp), "height:%d=%s%c", rows, reply, FLDSEP); APPEND_REALLOC(buf, off, len, tmp); @@ -900,21 +922,21 @@ static char *cmd_blocklist(__maybe_unused PGconn *conn, char *cmd, char *id, snprintf(tmp, sizeof(tmp), "elapsed:%d=%s%c", rows, reply, FLDSEP); APPEND_REALLOC(buf, off, len, tmp); - w_item = find_workinfo(blocks->workinfoid, NULL); - if (w_item) { - char wdiffbin[TXT_SML+1]; - double wdiff; - WORKINFO *workinfo; - DATA_WORKINFO(workinfo, w_item); - hex2bin(wdiffbin, workinfo->bits, 4); - wdiff = diff_from_nbits(wdiffbin); - snprintf(tmp, sizeof(tmp), - "netdiff:%d=%.1f%c", - rows, wdiff, FLDSEP); - APPEND_REALLOC(buf, off, len, tmp); + if (has_stats) { + snprintf(tmp, sizeof(tmp), + "netdiff:%d=%.8f%cdiffratio:%d=%.8f%c" + "cdf:%d=%.8f%cluck:%d=%.8f%c", + rows, blocks->netdiff, FLDSEP, + rows, blocks->blockdiffratio, FLDSEP, + rows, blocks->blockcdf, FLDSEP, + rows, blocks->blockluck, FLDSEP); + APPEND_REALLOC(buf, off, len, tmp); } else { snprintf(tmp, sizeof(tmp), - "netdiff:%d=?%c", rows, FLDSEP); + "netdiff:%d=?%cdiffratio:%d=?%c" + "cdf:%d=?%cluck:%d=?%c", + rows, FLDSEP, rows, FLDSEP, + rows, FLDSEP, rows, FLDSEP); APPEND_REALLOC(buf, off, len, tmp); } @@ -922,17 +944,83 @@ static char *cmd_blocklist(__maybe_unused PGconn *conn, char *cmd, char *id, } b_item = prev_in_ktree(ctx); } + if (has_stats) { + seq = tot; + b_item = last_in_ktree(blocks_root, ctx); + while (b_item) { + DATA_BLOCKS(blocks, b_item); + if (CURRENT(&(blocks->expirydate)) && + blocks->confirmed[0] != BLOCKS_ORPHAN) { + desc = NULL; + if (seq == 1) { + snprintf(desc_buf, sizeof(desc_buf), + "All - Last %d", tot); + desc = desc_buf; + } else if (seq == tot - 4) { + desc = "Last 5"; + } else if (seq == tot - 9) { + desc = "Last 10"; + } else if (seq == tot - 24) { + desc = "Last 25"; + } else if (seq == tot - 49) { + desc = "Last 50"; + } else if (seq == tot - 99) { + desc = "Last 100"; + } + if (desc) { + snprintf(tmp, sizeof(tmp), + "s_seq:%d=%d%c" + "s_desc:%d=%s%c" + "s_diffratio:%d=%.8f%c" + "s_diffmean:%d=%.8f%c" + "s_cdferl:%d=%.8f%c" + "s_luck:%d=%.8f%c", + srows, seq, FLDSEP, + srows, desc, FLDSEP, + srows, blocks->diffratio, FLDSEP, + srows, blocks->diffmean, FLDSEP, + srows, blocks->cdferl, FLDSEP, + srows, blocks->luck, FLDSEP); + APPEND_REALLOC(buf, off, len, tmp); + srows++; + } + seq--; + } + b_item = prev_in_ktree(ctx); + } + copy_tv(&stats_tv2, &blocks_stats_time); + } K_RUNLOCK(blocks_free); + + // Only check for a redo if we used the stats values + if (has_stats) { + /* If the stats changed then redo with the new corrected values + * This isn't likely at all, but it guarantees the blocks + * page shows correct information since any code that wants + * to modify the blocks table must have it under write lock + * then flag the stats as needing to be recalculated */ + if (!tv_equal(&stats_tv, &stats_tv2)) { + APPEND_REALLOC_RESET(buf, off); + goto redo; + } + } + + snprintf(tmp, sizeof(tmp), + "s_rows=%d%cs_flds=%s%c", + srows, FLDSEP, + "s_seq,s_desc,s_diffratio,s_diffmean,s_cdferl,s_luck", + FLDSEP); + APPEND_REALLOC(buf, off, len, tmp); + snprintf(tmp, sizeof(tmp), - "tot=%d%crows=%d%cflds=%s%c", - tot, FLDSEP, + "rows=%d%cflds=%s%c", rows, FLDSEP, - "height,blockhash,nonce,reward,workername,firstcreatedate," + "seq,height,blockhash,nonce,reward,workername,firstcreatedate," "createdate,status,diffacc,diffinv,shareacc,shareinv,elapsed," - "netdiff", FLDSEP); + "netdiff,diffratio,cdf,luck", FLDSEP); APPEND_REALLOC(buf, off, len, tmp); - snprintf(tmp, sizeof(tmp), "arn=%s%carp=%s", "Blocks", FLDSEP, ""); + snprintf(tmp, sizeof(tmp), "arn=%s%carp=%s", "Blocks,BlockStats", FLDSEP, ",s"); APPEND_REALLOC(buf, off, len, tmp); LOGDEBUG("%s.ok.%d_blocks", id, rows); @@ -1092,9 +1180,11 @@ static char *cmd_payments(__maybe_unused PGconn *conn, char *cmd, char *id, __maybe_unused tv_t *notcd, __maybe_unused K_TREE *trf_root) { - K_ITEM *i_username, *u_item, *p_item; + K_ITEM *i_username, *u_item, *p_item, *p2_item, *po_item; K_TREE_CTX ctx[1]; - PAYMENTS *payments, curr; + K_STORE *pay_store; + PAYMENTS *payments, *last_payments = NULL; + PAYOUTS *payouts; USERS *users; char reply[1024] = ""; char tmp[1024]; @@ -1102,6 +1192,7 @@ static char *cmd_payments(__maybe_unused PGconn *conn, char *cmd, char *id, char *buf; size_t len, off; int rows; + bool pok; LOGDEBUG("%s(): cmd '%s'", __func__, cmd); @@ -1116,65 +1207,71 @@ static char *cmd_payments(__maybe_unused PGconn *conn, char *cmd, char *id, return strdup("bad"); DATA_USERS(users, u_item); - bzero(&curr, sizeof(curr)); APPEND_REALLOC_INIT(buf, off, len); APPEND_REALLOC(buf, off, len, "ok."); rows = 0; - - K_RLOCK(payments_free); + pay_store = k_new_store(payments_free); + K_WLOCK(payments_free); p_item = find_first_payments(users->userid, ctx); DATA_PAYMENTS_NULL(payments, p_item); /* TODO: allow to see details of a single payoutid * if it has multiple items (percent payout user) */ while (p_item && payments->userid == users->userid) { if (CURRENT(&(payments->expirydate))) { - if (curr.payoutid && curr.payoutid != payments->payoutid) { - tv_to_buf(&(curr.paydate), reply, sizeof(reply)); - snprintf(tmp, sizeof(tmp), "paydate:%d=%s%c", rows, reply, FLDSEP); - APPEND_REALLOC(buf, off, len, tmp); - - str_to_buf(curr.payaddress, reply, sizeof(reply)); - snprintf(tmp, sizeof(tmp), "payaddress:%d=%s%c", rows, reply, FLDSEP); - APPEND_REALLOC(buf, off, len, tmp); - - bigint_to_buf(curr.amount, reply, sizeof(reply)); - snprintf(tmp, sizeof(tmp), "amount:%d=%s%c", rows, reply, FLDSEP); - APPEND_REALLOC(buf, off, len, tmp); - - rows++; - bzero(&curr, sizeof(curr)); + if (!last_payments || payments->payoutid != last_payments->payoutid) { + p2_item = k_unlink_head(payments_free); + DATA_PAYMENTS_NULL(last_payments, p2_item); + memcpy(last_payments, payments, sizeof(*last_payments)); + k_add_tail(pay_store, p2_item); + } else { + STRNCPY(last_payments->payaddress, "*Multiple"); + last_payments->amount += payments->amount; } - if (!curr.payoutid) { - curr.payoutid = payments->payoutid; - copy_tv(&(curr.paydate), &(payments->paydate)); - STRNCPY(curr.payaddress, payments->payaddress); - } else - STRNCPY(curr.payaddress, "*Multiple"); - curr.amount += payments->amount; } p_item = next_in_ktree(ctx); DATA_PAYMENTS_NULL(payments, p_item); } - K_RUNLOCK(payments_free); - if (curr.payoutid) { - tv_to_buf(&(curr.paydate), reply, sizeof(reply)); - snprintf(tmp, sizeof(tmp), "paydate:%d=%s%c", rows, reply, FLDSEP); - APPEND_REALLOC(buf, off, len, tmp); + K_WUNLOCK(payments_free); + + p_item = pay_store->head; + while (p_item) { + DATA_PAYMENTS(payments, p_item); + pok = false; + K_RLOCK(payouts_free); + po_item = find_payoutid(payments->payoutid); + DATA_PAYOUTS_NULL(payouts, po_item); + if (p_item && PAYGENERATED(payouts->status)) + pok = true; + K_RUNLOCK(payouts_free); + if (pok) { + bigint_to_buf(payouts->payoutid, reply, sizeof(reply)); + snprintf(tmp, sizeof(tmp), "payoutid:%d=%s%c", rows, reply, FLDSEP); + APPEND_REALLOC(buf, off, len, tmp); - str_to_buf(curr.payaddress, reply, sizeof(reply)); - snprintf(tmp, sizeof(tmp), "payaddress:%d=%s%c", rows, reply, FLDSEP); - APPEND_REALLOC(buf, off, len, tmp); + int_to_buf(payouts->height, reply, sizeof(reply)); + snprintf(tmp, sizeof(tmp), "height:%d=%s%c", rows, reply, FLDSEP); + APPEND_REALLOC(buf, off, len, tmp); - bigint_to_buf(curr.amount, reply, sizeof(reply)); - snprintf(tmp, sizeof(tmp), "amount:%d=%s%c", rows, reply, FLDSEP); - APPEND_REALLOC(buf, off, len, tmp); + str_to_buf(payments->payaddress, reply, sizeof(reply)); + snprintf(tmp, sizeof(tmp), "payaddress:%d=%s%c", rows, reply, FLDSEP); + APPEND_REALLOC(buf, off, len, tmp); - rows++; + bigint_to_buf(payments->amount, reply, sizeof(reply)); + snprintf(tmp, sizeof(tmp), "amount:%d=%s%c", rows, reply, FLDSEP); + APPEND_REALLOC(buf, off, len, tmp); + + tv_to_buf(&(payments->paydate), reply, sizeof(reply)); + snprintf(tmp, sizeof(tmp), "paydate:%d=%s%c", rows, reply, FLDSEP); + APPEND_REALLOC(buf, off, len, tmp); + + rows++; + } + p_item = p_item->next; } snprintf(tmp, sizeof(tmp), "rows=%d%cflds=%s%c", rows, FLDSEP, - "paydate,payaddress,amount", FLDSEP); + "payoutid,height,payaddress,amount,paydate", FLDSEP); APPEND_REALLOC(buf, off, len, tmp); snprintf(tmp, sizeof(tmp), "arn=%s%carp=%s", "Payments", FLDSEP, ""); @@ -1945,8 +2042,13 @@ wiconf: return strdup("failed.DATA"); } else { // Only flag a successful share + int32_t errn; + TXT_TO_INT("errn", transfer_data(i_errn), errn); ck_wlock(&last_lock); - setnow(&last_share); + if (errn == SE_NONE) + setnow(&last_share); + else + setnow(&last_share_inv); ck_wunlock(&last_lock); } LOGDEBUG("%s.ok.added %s", id, transfer_data(i_nonce)); @@ -2483,6 +2585,9 @@ static char *cmd_homepage(__maybe_unused PGconn *conn, char *cmd, char *id, ftv_to_buf(&last_share, reply, siz); snprintf(tmp, sizeof(tmp), "lastsh=%s%c", reply, FLDSEP); APPEND_REALLOC(buf, off, len, tmp); + ftv_to_buf(&last_share_inv, reply, siz); + snprintf(tmp, sizeof(tmp), "lastshinv=%s%c", reply, FLDSEP); + APPEND_REALLOC(buf, off, len, tmp); ftv_to_buf(&last_auth, reply, siz); ck_wunlock(&last_lock); snprintf(tmp, sizeof(tmp), "lastau=%s%c", reply, FLDSEP); @@ -4178,7 +4283,71 @@ static char *cmd_payouts(PGconn *conn, char *cmd, char *id, tv_t *now, DATA_PAYOUTS(payouts2, p2_item); DATA_PAYOUTS(old_payouts2, old_p2_item); snprintf(msg, sizeof(msg), - "payout %"PRId64" changed from '%s' to '%s' for" + "payout %"PRId64" changed from '%s' to '%s' for " + "%"PRId32"/%s", + payoutid, old_payouts2->status, payouts2->status, + payouts2->height, payouts2->blockhash); + } else if (strcasecmp(action, "orphan") == 0) { + /* Change the status of a generated payout to orphaned + * Require payoutid + * Use this if the orphan process didn't automatically + * update a generated payout to orphaned + * TODO: get orphaned blocks to automatically do this */ + i_payoutid = require_name(trf_root, "payoutid", 1, + (char *)intpatt, reply, siz); + if (!i_payoutid) + return strdup(reply); + TXT_TO_BIGINT("payoutid", transfer_data(i_payoutid), payoutid); + + K_WLOCK(payouts_free); + p_item = find_payoutid(payoutid); + if (!p_item) { + K_WUNLOCK(payouts_free); + snprintf(reply, siz, + "no payout with id %"PRId64, payoutid); + return strdup(reply); + } + DATA_PAYOUTS(payouts, p_item); + if (!PAYGENERATED(payouts->status)) { + K_WUNLOCK(payouts_free); + snprintf(reply, siz, + "status !generated (%s) for payout %"PRId64, + payouts->status, payoutid); + return strdup(reply); + } + p2_item = k_unlink_head(payouts_free); + K_WUNLOCK(payouts_free); + + /* There is a risk of the p_item changing while it's unlocked, + * but since this is a manual interface it's not really likely + * and there'll be an error if something goes wrong + * It reports the old and new status */ + DATA_PAYOUTS(payouts2, p2_item); + bzero(payouts2, sizeof(*payouts2)); + payouts2->payoutid = payouts->payoutid; + payouts2->height = payouts->height; + STRNCPY(payouts2->blockhash, payouts->blockhash); + payouts2->minerreward = payouts->minerreward; + payouts2->workinfoidstart = payouts->workinfoidstart; + payouts2->workinfoidend = payouts->workinfoidend; + payouts2->elapsed = payouts->elapsed; + STRNCPY(payouts2->status, PAYOUTS_ORPHAN_STR); + payouts2->diffwanted = payouts->diffwanted; + payouts2->diffused = payouts->diffused; + payouts2->shareacc = payouts->shareacc; + copy_tv(&(payouts2->lastshareacc), &(payouts->lastshareacc)); + payouts2->stats = strdup(payouts->stats); + + ok = payouts_add(conn, true, p2_item, &old_p2_item, + by, code, inet, now, NULL, false); + if (!ok) { + snprintf(reply, siz, "failed payout %"PRId64, payoutid); + return strdup(reply); + } + DATA_PAYOUTS(payouts2, p2_item); + DATA_PAYOUTS(old_payouts2, old_p2_item); + snprintf(msg, sizeof(msg), + "payout %"PRId64" changed from '%s' to '%s' for " "%"PRId32"/%s", payoutid, old_payouts2->status, payouts2->status, payouts2->height, payouts2->blockhash); @@ -4254,6 +4423,312 @@ static char *cmd_payouts(PGconn *conn, char *cmd, char *id, tv_t *now, LOGWARNING("%s.%s", id, reply); return strdup(reply); } + +static char *cmd_mpayouts(__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 *i_username, *u_item, *mp_item, *po_item; + K_TREE_CTX ctx[1]; + MININGPAYOUTS *mp; + PAYOUTS *payouts; + USERS *users; + char reply[1024] = ""; + char tmp[1024]; + size_t siz = sizeof(reply); + char *buf; + size_t len, off; + int rows; + + LOGDEBUG("%s(): cmd '%s'", __func__, cmd); + + i_username = require_name(trf_root, "username", 3, (char *)userpatt, reply, siz); + if (!i_username) + return strdup(reply); + + K_RLOCK(users_free); + u_item = find_users(transfer_data(i_username)); + K_RUNLOCK(users_free); + if (!u_item) + return strdup("bad"); + DATA_USERS(users, u_item); + + APPEND_REALLOC_INIT(buf, off, len); + APPEND_REALLOC(buf, off, len, "ok."); + rows = 0; + K_RLOCK(payouts_free); + po_item = last_in_ktree(payouts_root, ctx); + DATA_PAYOUTS_NULL(payouts, po_item); + /* TODO: allow to see details of a single payoutid + * if it has multiple items (percent payout user) */ + while (po_item) { + if (CURRENT(&(payouts->expirydate)) && + PAYGENERATED(payouts->status)) { + // Not locked ... for now + mp_item = find_miningpayouts(payouts->payoutid, + users->userid); + if (mp_item) { + DATA_MININGPAYOUTS(mp, mp_item); + + bigint_to_buf(payouts->payoutid, reply, + sizeof(reply)); + snprintf(tmp, sizeof(tmp), "payoutid:%d=%s%c", + rows, reply, FLDSEP); + APPEND_REALLOC(buf, off, len, tmp); + + int_to_buf(payouts->height, reply, + sizeof(reply)); + snprintf(tmp, sizeof(tmp), "height:%d=%s%c", + rows, reply, FLDSEP); + APPEND_REALLOC(buf, off, len, tmp); + + bigint_to_buf(payouts->elapsed, reply, + sizeof(reply)); + snprintf(tmp, sizeof(tmp), "elapsed:%d=%s%c", + rows, reply, FLDSEP); + APPEND_REALLOC(buf, off, len, tmp); + + bigint_to_buf(mp->amount, reply, sizeof(reply)); + snprintf(tmp, sizeof(tmp), "amount:%d=%s%c", + rows, reply, FLDSEP); + APPEND_REALLOC(buf, off, len, tmp); + + double_to_buf(mp->diffacc, reply, sizeof(reply)); + snprintf(tmp, sizeof(tmp), "diffacc:%d=%s%c", + rows, reply, FLDSEP); + APPEND_REALLOC(buf, off, len, tmp); + + bigint_to_buf(payouts->minerreward, reply, + sizeof(reply)); + snprintf(tmp, sizeof(tmp), "minerreward:%d=%s%c", + rows, reply, FLDSEP); + APPEND_REALLOC(buf, off, len, tmp); + + double_to_buf(payouts->diffused, reply, + sizeof(reply)); + snprintf(tmp, sizeof(tmp), "diffused:%d=%s%c", + rows, reply, FLDSEP); + APPEND_REALLOC(buf, off, len, tmp); + + str_to_buf(payouts->status, reply, + sizeof(reply)); + snprintf(tmp, sizeof(tmp), "status:%d=%s%c", + rows, reply, FLDSEP); + APPEND_REALLOC(buf, off, len, tmp); + + rows++; + } + } + po_item = prev_in_ktree(ctx); + DATA_PAYOUTS_NULL(payouts, po_item); + } + K_RUNLOCK(payouts_free); + + snprintf(tmp, sizeof(tmp), "rows=%d%cflds=%s%c", + rows, FLDSEP, + "payoutid,height,elapsed,amount,diffacc,minerreward,diffused,status", + FLDSEP); + APPEND_REALLOC(buf, off, len, tmp); + + snprintf(tmp, sizeof(tmp), "arn=%s%carp=%s", "MiningPayouts", FLDSEP, ""); + APPEND_REALLOC(buf, off, len, tmp); + + LOGDEBUG("%s.ok.%s", id, transfer_data(i_username)); + return buf; +} + +static char *cmd_shifts(__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 *i_username, *u_item, *m_item, ms_look, *wm_item, *ms_item, *wi_item; + K_TREE_CTX wm_ctx[1], ms_ctx[1]; + WORKMARKERS *wm; + WORKINFO *wi; + MARKERSUMMARY markersummary, *ms, ms_add; + USERS *users; + MARKS *marks = NULL; + char reply[1024] = ""; + char tmp[1024]; + size_t siz = sizeof(reply); + char *buf; + size_t len, off; + tv_t marker_end = { 0L, 0L }; + int rows; + + LOGDEBUG("%s(): cmd '%s'", __func__, cmd); + + i_username = require_name(trf_root, "username", 3, (char *)userpatt, reply, siz); + if (!i_username) + return strdup(reply); + + K_RLOCK(users_free); + u_item = find_users(transfer_data(i_username)); + K_RUNLOCK(users_free); + if (!u_item) + return strdup("bad"); + DATA_USERS(users, u_item); + + APPEND_REALLOC_INIT(buf, off, len); + APPEND_REALLOC(buf, off, len, "ok."); + INIT_MARKERSUMMARY(&ms_look); + ms_look.data = (void *)(&markersummary); + rows = 0; + K_RLOCK(workmarkers_free); + wm_item = last_in_ktree(workmarkers_workinfoid_root, wm_ctx); + DATA_WORKMARKERS_NULL(wm, wm_item); + /* TODO: allow to see details of a single payoutid + * if it has multiple items (percent payout user) */ + while (rows < 98 && wm_item) { + if (CURRENT(&(wm->expirydate)) && WMPROCESSED(wm->status)) { + K_RUNLOCK(workmarkers_free); + + K_RLOCK(marks_free); + m_item = find_marks(wm->workinfoidend); + K_RUNLOCK(marks_free); + DATA_MARKS_NULL(marks, m_item); + if (m_item == NULL) { + // Log it but keep going + LOGERR("%s() missing mark for markerid " + "%"PRId64"/%s widend %"PRId64, + __func__, wm->markerid, + wm->description, + wm->workinfoidend); + } + + bzero(&ms_add, sizeof(ms_add)); + + markersummary.markerid = wm->markerid; + markersummary.userid = users->userid; + markersummary.workername = EMPTY; + K_RLOCK(markersummary_free); + ms_item = find_after_in_ktree(markersummary_root, &ms_look, + cmp_markersummary, ms_ctx); + DATA_MARKERSUMMARY_NULL(ms, ms_item); + while (ms_item && ms->markerid == wm->markerid && + ms->userid == users->userid) { + ms_add.diffacc += ms->diffacc; + ms_add.diffrej += ms->diffrej; + ms_add.shareacc += ms->shareacc; + ms_add.sharerej += ms->sharerej; + + ms_item = next_in_ktree(ms_ctx); + DATA_MARKERSUMMARY_NULL(ms, ms_item); + } + K_RUNLOCK(markersummary_free); + + if (marker_end.tv_sec == 0L) { + wi_item = next_workinfo(wm->workinfoidend, NULL); + if (!wi_item) { + /* There's no workinfo after this shift + * Unexpected ... estimate last wid+30s */ + wi_item = find_workinfo(wm->workinfoidend, NULL); + if (!wi_item) { + // Nothing is currently locked + LOGERR("%s() workmarker %"PRId64"/%s." + " missing widend %"PRId64, + __func__, wm->markerid, + wm->description, + wm->workinfoidend); + snprintf(reply, siz, "data error 1"); + return strdup(reply); + } + DATA_WORKINFO(wi, wi_item); + copy_tv(&marker_end, &(wi->createdate)); + marker_end.tv_sec += 30; + } else { + DATA_WORKINFO(wi, wi_item); + copy_tv(&marker_end, &(wi->createdate)); + } + } + + wi_item = find_workinfo(wm->workinfoidstart, NULL); + if (!wi_item) { + // Nothing is currently locked + LOGERR("%s() workmarker %"PRId64"/%s. missing " + "widstart %"PRId64, + __func__, wm->markerid, wm->description, + wm->workinfoidstart); + snprintf(reply, siz, "data error 2"); + return strdup(reply); + } + DATA_WORKINFO(wi, wi_item); + + bigint_to_buf(wm->markerid, reply, sizeof(reply)); + snprintf(tmp, sizeof(tmp), "markerid:%d=%s%c", + rows, reply, FLDSEP); + APPEND_REALLOC(buf, off, len, tmp); + + str_to_buf(wm->description, reply, sizeof(reply)); + snprintf(tmp, sizeof(tmp), "shift:%d=%s%c", + rows, reply, FLDSEP); + APPEND_REALLOC(buf, off, len, tmp); + + snprintf(tmp, sizeof(tmp), "endmarkextra:%d=%s%c", + rows, + m_item ? marks->extra : EMPTY, + FLDSEP); + APPEND_REALLOC(buf, off, len, tmp); + + ftv_to_buf(&(wi->createdate), reply, sizeof(reply)); + snprintf(tmp, sizeof(tmp), "start:%d=%s%c", + rows, reply, FLDSEP); + APPEND_REALLOC(buf, off, len, tmp); + + ftv_to_buf(&marker_end, reply, sizeof(reply)); + snprintf(tmp, sizeof(tmp), "end:%d=%s%c", + rows, reply, FLDSEP); + APPEND_REALLOC(buf, off, len, tmp); + + double_to_buf(ms_add.diffacc, reply, sizeof(reply)); + snprintf(tmp, sizeof(tmp), "diffacc:%d=%s%c", + rows, reply, FLDSEP); + APPEND_REALLOC(buf, off, len, tmp); + + double_to_buf(ms_add.diffrej, reply, sizeof(reply)); + snprintf(tmp, sizeof(tmp), "diffrej:%d=%s%c", + rows, reply, FLDSEP); + APPEND_REALLOC(buf, off, len, tmp); + + double_to_buf(ms_add.shareacc, reply, sizeof(reply)); + snprintf(tmp, sizeof(tmp), "shareacc:%d=%s%c", + rows, reply, FLDSEP); + APPEND_REALLOC(buf, off, len, tmp); + + double_to_buf(ms_add.sharerej, reply, sizeof(reply)); + snprintf(tmp, sizeof(tmp), "sharerej:%d=%s%c", + rows, reply, FLDSEP); + APPEND_REALLOC(buf, off, len, tmp); + + rows++; + + // Setup for next shift + copy_tv(&marker_end, &(wi->createdate)); + + K_RLOCK(workmarkers_free); + } + wm_item = prev_in_ktree(wm_ctx); + DATA_WORKMARKERS_NULL(wm, wm_item); + } + K_RUNLOCK(workmarkers_free); + + snprintf(tmp, sizeof(tmp), "rows=%d%cflds=%s%c", + rows, FLDSEP, + "markerid,shift,start,end,diffacc,diffrej,shareacc,sharerej", + FLDSEP); + APPEND_REALLOC(buf, off, len, tmp); + + snprintf(tmp, sizeof(tmp), "arn=%s%carp=%s", "Shifts", FLDSEP, ""); + APPEND_REALLOC(buf, off, len, tmp); + + LOGDEBUG("%s.ok.%s", id, transfer_data(i_username)); + return buf; +} + static char *cmd_dsp(__maybe_unused PGconn *conn, __maybe_unused char *cmd, char *id, __maybe_unused tv_t *now, __maybe_unused char *by, __maybe_unused char *code, @@ -4915,6 +5390,8 @@ struct CMDS ckdb_cmds[] = { { CMD_PPLNS, "pplns", false, false, cmd_pplns, ACCESS_SYSTEM ACCESS_WEB }, { CMD_PPLNS2, "pplns2", false, false, cmd_pplns2, ACCESS_SYSTEM ACCESS_WEB }, { CMD_PAYOUTS, "payouts", false, false, cmd_payouts, ACCESS_SYSTEM }, + { CMD_MPAYOUTS, "mpayouts", false, false, cmd_mpayouts, ACCESS_SYSTEM ACCESS_WEB }, + { CMD_SHIFTS, "shifts", false, false, cmd_shifts, ACCESS_SYSTEM ACCESS_WEB }, { CMD_USERSTATUS,"userstatus", false, false, cmd_userstatus, ACCESS_SYSTEM ACCESS_WEB }, { CMD_MARKS, "marks", false, false, cmd_marks, ACCESS_SYSTEM }, { CMD_END, NULL, false, false, NULL, NULL } diff --git a/src/ckdb_data.c b/src/ckdb_data.c index 96649aa8..a4227e7e 100644 --- a/src/ckdb_data.c +++ b/src/ckdb_data.c @@ -1569,6 +1569,34 @@ K_ITEM *find_workinfo(int64_t workinfoid, K_TREE_CTX *ctx) return item; } +K_ITEM *next_workinfo(int64_t workinfoid, K_TREE_CTX *ctx) +{ + WORKINFO workinfo, *wi; + K_TREE_CTX ctx0[1]; + K_ITEM look, *item; + + if (ctx == NULL) + ctx = ctx0; + + workinfo.workinfoid = workinfoid; + workinfo.expirydate.tv_sec = default_expiry.tv_sec; + workinfo.expirydate.tv_usec = default_expiry.tv_usec; + + INIT_WORKINFO(&look); + look.data = (void *)(&workinfo); + K_RLOCK(workinfo_free); + item = find_after_in_ktree(workinfo_root, &look, cmp_workinfo, ctx); + if (item) { + DATA_WORKINFO(wi, item); + while (item && !CURRENT(&(wi->expirydate))) { + item = next_in_ktree(ctx); + DATA_WORKINFO_NULL(wi, item); + } + } + K_RUNLOCK(workinfo_free); + return item; +} + bool workinfo_age(PGconn *conn, 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, @@ -2058,6 +2086,27 @@ void _dsp_hash(char *hash, char *buf, size_t siz, WHERE_FFL_ARGS) STRNCPYSIZ(buf, ptr, siz); } +double _blockhash_diff(char *hash, WHERE_FFL_ARGS) +{ + uchar binhash[SHA256SIZHEX >> 1]; + uchar swap[SHA256SIZHEX >> 1]; + size_t len; + + len = strlen(hash); + // code bug - check this before calling + if (len != SHA256SIZHEX) { + quitfrom(1, file, func, line, + "%s() invalid hash passed - size %d (%d)", + __func__, (int)len, SHA256SIZHEX); + } + + hex2bin(binhash, hash, sizeof(binhash)); + + flip_32(swap, binhash); + + return diff_from_target(swap); +} + void dsp_blocks(K_ITEM *item, FILE *stream) { char createdate_buf[DATE_BUFSIZ], expirydate_buf[DATE_BUFSIZ]; @@ -2350,6 +2399,131 @@ void set_block_share_counters() LOGWARNING("%s(): Update block counters complete", __func__); } +/* Must be under K_WLOCK(blocks_free) when called + * Call this before using the block stats and again check (under lock) + * the blocks_stats_time didn't change after you finish processing + * If it has changed, redo the processing from scratch + * If return is false, then stats aren't available + * TODO: consider storing the partial calculations in the BLOCKS structure + * and only recalc from the last block modified (remembered) + * Will be useful with a large block history */ +bool check_update_blocks_stats(tv_t *stats) +{ + static int64_t last_missing_workinfoid = 0; + static tv_t last_message = { 0L, 0L }; + K_TREE_CTX ctx[1]; + K_ITEM *b_item, *w_item; + WORKINFO *workinfo; + BLOCKS *blocks; + char ndiffbin[TXT_SML+1]; + double ok, diffacc, netsumm, diffmean, pending; + tv_t now; + + /* Wait for startup_complete rather than db_load_complete + * This avoids doing a 'long' lock stats update while reloading */ + if (!startup_complete) + return false; + + if (blocks_stats_rebuild) { + /* Have to first work out the diffcalc for each block + * Orphans count towards the next valid block after the orphan + * so this has to be done in the reverse order of the range + * calculations */ + pending = 0.0; + b_item = first_in_ktree(blocks_root, ctx); + while (b_item) { + DATA_BLOCKS(blocks, b_item); + if (CURRENT(&(blocks->expirydate))) { + pending += blocks->diffacc; + if (blocks->confirmed[0] == BLOCKS_ORPHAN) + blocks->diffcalc = 0.0; + else { + blocks->diffcalc = pending; + pending = 0.0; + } + } + b_item = next_in_ktree(ctx); + } + ok = diffacc = netsumm = diffmean = 0.0; + b_item = last_in_ktree(blocks_root, ctx); + while (b_item) { + DATA_BLOCKS(blocks, b_item); + if (CURRENT(&(blocks->expirydate))) { + if (blocks->netdiff == 0) { + // Deadlock alert + K_RLOCK(workinfo_free); + w_item = find_workinfo(blocks->workinfoid, NULL); + K_RUNLOCK(workinfo_free); + if (!w_item) { + setnow(&now); + if (!blocks->workinfoid != last_missing_workinfoid || + tvdiff(&now, &last_message) >= 5.0) { + LOGEMERG("%s(): missing block workinfoid %" + PRId32"/%"PRId64"/%s", + __func__, blocks->height, + blocks->workinfoid, + blocks->confirmed); + } + last_missing_workinfoid = blocks->workinfoid; + copy_tv(&last_message, &now); + return false; + } + DATA_WORKINFO(workinfo, w_item); + hex2bin(ndiffbin, workinfo->bits, 4); + blocks->netdiff = diff_from_nbits(ndiffbin); + } + /* Stats for each blocks are independent of + * if they are orphans or not */ + if (blocks->netdiff == 0.0) + blocks->blockdiffratio = 0.0; + else + blocks->blockdiffratio = blocks->diffacc / blocks->netdiff; + blocks->blockcdf = 1.0 - exp(-1.0 * blocks->blockdiffratio); + if (blocks->blockdiffratio == 0.0) + blocks->blockluck = 0.0; + else + blocks->blockluck = 1.0 / blocks->blockdiffratio; + + /* Orphans are treated as +diffacc but no block + * i.e. they simply add shares to the later block + * and have running stats set to zero */ + if (blocks->confirmed[0] == BLOCKS_ORPHAN) { + blocks->diffratio = 0.0; + blocks->diffmean = 0.0; + blocks->cdferl = 0.0; + blocks->luck = 0.0; + } else { + ok++; + diffacc += blocks->diffcalc; + netsumm += blocks->netdiff; + + if (netsumm == 0.0) + blocks->diffratio = 0.0; + else + blocks->diffratio = diffacc / netsumm; + + diffmean = ((diffmean * (ok - 1)) + + (blocks->diffcalc / blocks->netdiff)) / ok; + blocks->diffmean = diffmean; + + if (diffmean == 0.0) { + blocks->cdferl = 0.0; + blocks->luck = 0.0; + } else { + blocks->cdferl = gsl_cdf_gamma_P(diffmean, ok, 1.0 / ok); + blocks->luck = 1.0 / diffmean; + } + } + } + b_item = prev_in_ktree(ctx); + } + setnow(&blocks_stats_time); + blocks_stats_rebuild = false; + } + copy_tv(stats, &blocks_stats_time); + return true; +} + /* 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) diff --git a/src/ckdb_dbio.c b/src/ckdb_dbio.c index 73573095..91e43fd8 100644 --- a/src/ckdb_dbio.c +++ b/src/ckdb_dbio.c @@ -3996,9 +3996,13 @@ unparam: blocks_root = remove_from_ktree(blocks_root, old_b_item, cmp_blocks); copy_tv(&(oldblocks->expirydate), cd); blocks_root = add_to_ktree(blocks_root, old_b_item, cmp_blocks); - } + // Copy it over to avoid having to recalculate it + row->netdiff = oldblocks->netdiff; + } else + row->netdiff = 0; blocks_root = add_to_ktree(blocks_root, b_item, cmp_blocks); k_add_head(blocks_store, b_item); + blocks_stats_rebuild = true; } K_WUNLOCK(blocks_free); @@ -4018,6 +4022,7 @@ bool blocks_add(PGconn *conn, char *height, char *blockhash, K_ITEM *b_item, *u_item, *old_b_item; char cd_buf[DATE_BUFSIZ]; char hash_dsp[16+1]; + double hash_diff; BLOCKS *row, *oldblocks; USERS *users; char *upd, *ins; @@ -4038,6 +4043,7 @@ bool blocks_add(PGconn *conn, char *height, char *blockhash, STRNCPY(row->blockhash, blockhash); dsp_hash(blockhash, hash_dsp, sizeof(hash_dsp)); + hash_diff = blockhash_diff(blockhash); K_RLOCK(blocks_free); old_b_item = find_blocks(row->height, blockhash, NULL); @@ -4298,19 +4304,26 @@ flail: blocks_root = remove_from_ktree(blocks_root, old_b_item, cmp_blocks); copy_tv(&(oldblocks->expirydate), cd); blocks_root = add_to_ktree(blocks_root, old_b_item, cmp_blocks); - } + // Copy it over to avoid having to recalculate it + row->netdiff = oldblocks->netdiff; + } else + row->netdiff = 0; blocks_root = add_to_ktree(blocks_root, b_item, cmp_blocks); k_add_head(blocks_store, b_item); + blocks_stats_rebuild = true; } K_WUNLOCK(blocks_free); if (ok) { char pct[16] = "?"; char est[16] = ""; + char diff[16] = ""; K_ITEM *w_item; char tmp[256]; bool blk; + suffix_string(hash_diff, diff, sizeof(diff)-1, 0); + switch (confirmed[0]) { case BLOCKS_NEW: blk = true; @@ -4356,10 +4369,10 @@ flail: break; } - LOGWARNING("%s(): %sStatus: %s, Block: %s/...%s%s", + LOGWARNING("%s(): %sStatus: %s, Block: %s/...%s Diff %s%s", __func__, blk ? "BLOCK! " : "", blocks_confirmed(confirmed), - height, hash_dsp, tmp); + height, hash_dsp, diff, tmp); } return ok; diff --git a/src/connector.c b/src/connector.c index ee6cf809..e4f30d4d 100644 --- a/src/connector.c +++ b/src/connector.c @@ -214,12 +214,11 @@ static int drop_client(cdata_t *cdata, client_instance_t *client) int64_t client_id = 0; int fd; - ck_ilock(&cdata->lock); + ck_wlock(&cdata->lock); fd = client->fd; if (fd != -1) { client_id = client->id; - ck_ulock(&cdata->lock); Close(client->fd); HASH_DEL(cdata->clients, client); DL_APPEND(cdata->dead_clients, client); @@ -227,9 +226,8 @@ static int drop_client(cdata_t *cdata, client_instance_t *client) * epoll list. */ __dec_instance_ref(client); cdata->dead_generated++; - ck_dwilock(&cdata->lock); } - ck_uilock(&cdata->lock); + ck_wunlock(&cdata->lock); if (fd > -1) LOGINFO("Connector dropped client %"PRId64" fd %d", client_id, fd); @@ -561,17 +559,15 @@ static void send_client(cdata_t *cdata, int64_t id, char *buf) return; } - ck_ilock(&cdata->lock); + ck_wlock(&cdata->lock); HASH_FIND_I64(cdata->clients, &id, client); if (likely(client)) { fd = client->fd; /* Grab a reference to this client until the sender_send has * completed processing. */ - ck_ulock(&cdata->lock); __inc_instance_ref(client); - ck_dwilock(&cdata->lock); } - ck_uilock(&cdata->lock); + ck_wunlock(&cdata->lock); if (unlikely(fd == -1)) { ckpool_t *ckp = cdata->ckp; @@ -614,14 +610,11 @@ static client_instance_t *ref_client_by_id(cdata_t *cdata, int64_t id) { client_instance_t *client; - ck_ilock(&cdata->lock); + ck_wlock(&cdata->lock); HASH_FIND_I64(cdata->clients, &id, client); - if (client) { - ck_ulock(&cdata->lock); + if (client) __inc_instance_ref(client); - ck_dwilock(&cdata->lock); - } - ck_uilock(&cdata->lock); + ck_wunlock(&cdata->lock); return client; } diff --git a/src/libckpool.c b/src/libckpool.c index 6b47502e..5bbb14ee 100644 --- a/src/libckpool.c +++ b/src/libckpool.c @@ -337,25 +337,6 @@ void _ck_rlock(cklock_t *lock, const char *file, const char *func, const int lin _mutex_unlock(&lock->mutex, file, func, line); } -/* Intermediate variant of cklock - behaves as a read lock but can be promoted - * to a write lock or demoted to read lock. */ -void _ck_ilock(cklock_t *lock, const char *file, const char *func, const int line) -{ - _mutex_lock(&lock->mutex, file, func, line); -} - -/* Unlock intermediate variant without changing to read or write version */ -void _ck_uilock(cklock_t *lock, const char *file, const char *func, const int line) -{ - _mutex_unlock(&lock->mutex, file, func, line); -} - -/* Upgrade intermediate variant to a write lock */ -void _ck_ulock(cklock_t *lock, const char *file, const char *func, const int line) -{ - _wr_lock(&lock->rwlock, file, func, line); -} - /* Write lock variant of cklock */ void _ck_wlock(cklock_t *lock, const char *file, const char *func, const int line) { @@ -377,13 +358,6 @@ void _ck_dwilock(cklock_t *lock, const char *file, const char *func, const int l _wr_unlock(&lock->rwlock, file, func, line); } -/* Downgrade intermediate variant to a read lock */ -void _ck_dlock(cklock_t *lock, const char *file, const char *func, const int line) -{ - _rd_lock(&lock->rwlock, file, func, line); - _mutex_unlock(&lock->mutex, file, func, line); -} - void _ck_runlock(cklock_t *lock, const char *file, const char *func, const int line) { _rd_unlock(&lock->rwlock, file, func, line); diff --git a/src/libckpool.h b/src/libckpool.h index 79f0f8fd..28ea14c9 100644 --- a/src/libckpool.h +++ b/src/libckpool.h @@ -166,12 +166,8 @@ static inline void flip_80(void *dest_p, const void *src_p) #define cklock_init(_lock) _cklock_init(_lock, __FILE__, __func__, __LINE__) #define ck_rlock(_lock) _ck_rlock(_lock, __FILE__, __func__, __LINE__) -#define ck_ilock(_lock) _ck_ilock(_lock, __FILE__, __func__, __LINE__) -#define ck_uilock(_lock) _ck_uilock(_lock, __FILE__, __func__, __LINE__) -#define ck_ulock(_lock) _ck_ulock(_lock, __FILE__, __func__, __LINE__) #define ck_wlock(_lock) _ck_wlock(_lock, __FILE__, __func__, __LINE__) #define ck_dwlock(_lock) _ck_dwlock(_lock, __FILE__, __func__, __LINE__) -#define ck_dwilock(_lock) _ck_dwilock(_lock, __FILE__, __func__, __LINE__) #define ck_dlock(_lock) _ck_dlock(_lock, __FILE__, __func__, __LINE__) #define ck_runlock(_lock) _ck_runlock(_lock, __FILE__, __func__, __LINE__) #define ck_wunlock(_lock) _ck_wunlock(_lock, __FILE__, __func__, __LINE__) diff --git a/src/stratifier.c b/src/stratifier.c index cf37916c..11fc7778 100644 --- a/src/stratifier.c +++ b/src/stratifier.c @@ -136,7 +136,6 @@ struct json_params { json_t *params; json_t *id_val; int64_t client_id; - char address[INET6_ADDRSTRLEN]; }; typedef struct json_params json_params_t; @@ -145,7 +144,6 @@ typedef struct json_params json_params_t; struct smsg { json_t *json_msg; int64_t client_id; - char address[INET6_ADDRSTRLEN]; }; typedef struct smsg smsg_t; @@ -187,6 +185,7 @@ struct user_instance { time_t auth_time; time_t failed_authtime; /* Last time this username failed to authorise */ int auth_backoff; /* How long to reject any auth attempts since last failure */ + bool throttled; /* Have we begun rejecting auth attempts */ }; /* Combined data from workers with the same workername */ @@ -370,6 +369,7 @@ struct stratifier_data { * is sorted by enonce1_64. */ stratum_instance_t *stratum_instances; stratum_instance_t *disconnected_instances; + stratum_instance_t *recycled_instances; int stratum_generated; int disconnected_generated; @@ -967,7 +967,9 @@ static void update_base(ckpool_t *ckp, const int prio) create_pthread(pth, do_update, ur); } -static void __kill_instance(stratum_instance_t *client) +/* Instead of removing the client instance, we add it to a list of recycled + * clients allowing us to reuse it instead of callocing a new one */ +static void __kill_instance(sdata_t *sdata, stratum_instance_t *client) { if (client->proxy) { client->proxy->bound_clients--; @@ -975,14 +977,31 @@ static void __kill_instance(stratum_instance_t *client) } free(client->workername); free(client->useragent); - free(client); + memset(client, 0, sizeof(stratum_instance_t)); + DL_APPEND(sdata->recycled_instances, client); } static void __del_disconnected(sdata_t *sdata, stratum_instance_t *client) { HASH_DEL(sdata->disconnected_instances, client); sdata->stats.disconnected--; - __kill_instance(client); + __kill_instance(sdata, client); +} + +/* Called with instance_lock held. Note stats.users is protected by + * instance lock to avoid recursive locking. */ +static void __inc_worker(sdata_t *sdata, user_instance_t *instance) +{ + sdata->stats.workers++; + if (!instance->workers++) + sdata->stats.users++; +} + +static void __dec_worker(sdata_t *sdata, user_instance_t *instance) +{ + sdata->stats.workers--; + if (!--instance->workers) + sdata->stats.users--; } /* Removes a client instance we know is on the stratum_instances list and from @@ -990,8 +1009,10 @@ static void __del_disconnected(sdata_t *sdata, stratum_instance_t *client) static void __del_client(sdata_t *sdata, stratum_instance_t *client, user_instance_t *user) { HASH_DEL(sdata->stratum_instances, client); - if (user) + if (user) { DL_DELETE(user->clients, client); + __dec_worker(sdata, user); + } } static void connector_drop_client(ckpool_t *ckp, const int64_t id) @@ -1015,7 +1036,7 @@ static void drop_allclients(ckpool_t *ckp) if (!client->ref) { __del_client(sdata, client, client->user_instance); - __kill_instance(client); + __kill_instance(sdata, client); } else client->dropped = true; kills++; @@ -1625,19 +1646,15 @@ static stratum_instance_t *ref_instance_by_id(sdata_t *sdata, const int64_t id) { stratum_instance_t *client; - ck_ilock(&sdata->instance_lock); + ck_wlock(&sdata->instance_lock); client = __instance_by_id(sdata, id); if (client) { if (unlikely(client->dropped)) client = NULL; - else { - /* Upgrade to write lock to modify client refcount */ - ck_ulock(&sdata->instance_lock); + else __inc_instance_ref(client); - ck_dwilock(&sdata->instance_lock); - } } - ck_uilock(&sdata->instance_lock); + ck_wunlock(&sdata->instance_lock); return client; } @@ -1659,91 +1676,99 @@ out: } /* Ret = 1 is disconnected, 2 is killed, 3 is workerless killed */ -static int __drop_client(sdata_t *sdata, stratum_instance_t *client, user_instance_t *user) +static void __drop_client(sdata_t *sdata, stratum_instance_t *client, user_instance_t *user, + bool lazily, char **msg) { stratum_instance_t *old_client = NULL; - int ret; __del_client(sdata, client, user); HASH_FIND(hh, sdata->disconnected_instances, &client->enonce1_64, sizeof(uint64_t), old_client); /* Only keep around one copy of the old client in server mode */ if (!client->ckp->proxy && !old_client && client->enonce1_64 && client->authorised) { - ret = 1; + ASPRINTF(msg, "Client %"PRId64" %s %suser %s worker %s disconnected %s", + client->id, client->address, user->throttled ? "throttled " : "", + user->username, client->workername, lazily ? "lazily" : ""); HASH_ADD(hh, sdata->disconnected_instances, enonce1_64, sizeof(uint64_t), client); sdata->stats.disconnected++; sdata->disconnected_generated++; client->disconnected_time = time(NULL); } else { - if (client->workername) - ret = 2; - else - ret = 3; - __kill_instance(client); - } - return ret; -} - -static void client_drop_message(const int64_t client_id, const int dropped, const bool lazily) -{ - switch(dropped) { - case 0: - break; - case 1: - LOGNOTICE("Client %"PRId64" disconnected %s", client_id, lazily ? "lazily" : ""); - break; - case 2: - LOGNOTICE("Client %"PRId64" dropped %s", client_id, lazily ? "lazily" : ""); - break; - case 3: - LOGNOTICE("Workerless client %"PRId64" dropped %s", client_id, lazily ? "lazily" : ""); - break; + if (client->workername) { + if (user) { + ASPRINTF(msg, "Client %"PRId64" %s %suser %s worker %s dropped %s", + client->id, client->address, user->throttled ? "throttled " : "", + user->username, client->workername, lazily ? "lazily" : ""); + } else { + ASPRINTF(msg, "Client %"PRId64" %s no user worker %s dropped %s", + client->id, client->address, client->workername, + lazily ? "lazily" : ""); + } + } else { + ASPRINTF(msg, "Workerless client %"PRId64" %s dropped %s", + client->id, client->address, lazily ? "lazily" : ""); + } + __kill_instance(sdata, client); } } -static void dec_worker(ckpool_t *ckp, user_instance_t *instance); - /* Decrease the reference count of instance. */ static void _dec_instance_ref(sdata_t *sdata, stratum_instance_t *client, const char *file, const char *func, const int line) { user_instance_t *user = client->user_instance; - int64_t client_id = client->id; - ckpool_t *ckp = client->ckp; - int dropped = 0, ref; + char_entry_t *entries = NULL; + bool dropped = false; + char *msg; + int ref; ck_wlock(&sdata->instance_lock); ref = --client->ref; /* See if there are any instances that were dropped that could not be * moved due to holding a reference and drop them now. */ - if (unlikely(client->dropped && !ref)) - dropped = __drop_client(sdata, client, user); + if (unlikely(client->dropped && !ref)) { + dropped = true; + __drop_client(sdata, client, user, true, &msg); + add_msg_entry(&entries, &msg); + } ck_wunlock(&sdata->instance_lock); - client_drop_message(client_id, dropped, true); - + notice_msg_entries(&entries); /* This should never happen */ if (unlikely(ref < 0)) LOGERR("Instance ref count dropped below zero from %s %s:%d", file, func, line); - if (dropped) { - if (user) - dec_worker(ckp, user); - reap_proxies(ckp, sdata); - } + if (dropped) + reap_proxies(sdata->ckp, sdata); } #define dec_instance_ref(sdata, instance) _dec_instance_ref(sdata, instance, __FILE__, __func__, __LINE__) +/* If we have a no longer used stratum instance in the recycled linked list, + * use that, otherwise calloc a fresh one. */ +static stratum_instance_t *__recruit_stratum_instance(sdata_t *sdata) +{ + stratum_instance_t *client = sdata->recycled_instances; + + if (client) + DL_DELETE(sdata->recycled_instances, client); + else { + client = ckzalloc(sizeof(stratum_instance_t)); + sdata->stratum_generated++; + } + return client; +} + /* Enter with write instance_lock held */ static stratum_instance_t *__stratum_add_instance(ckpool_t *ckp, const int64_t id, - const int server) + const char *address, const int server) { - stratum_instance_t *client = ckzalloc(sizeof(stratum_instance_t)); + stratum_instance_t *client; sdata_t *sdata = ckp->data; + client = __recruit_stratum_instance(sdata); client->start_time = time(NULL); - sdata->stratum_generated++; client->id = id; + strcpy(client->address, address); client->server = server; client->diff = client->old_diff = ckp->startdiff; client->ckp = ckp; @@ -1774,7 +1799,7 @@ static uint64_t disconnected_sessionid_exists(sdata_t *sdata, const char *sessio /* Number is in BE but we don't swap either of them */ hex2bin(&enonce1_64, sessionid, slen); - ck_ilock(&sdata->instance_lock); + ck_wlock(&sdata->instance_lock); HASH_ITER(hh, sdata->stratum_instances, client, tmp) { if (client->id == id) continue; @@ -1789,16 +1814,12 @@ static uint64_t disconnected_sessionid_exists(sdata_t *sdata, const char *sessio /* Delete the entry once we are going to use it since there * will be a new instance with the enonce1_64 */ old_id = client->id; - - /* Upgrade to write lock to disconnect */ - ck_ulock(&sdata->instance_lock); __del_disconnected(sdata, client); - ck_dwilock(&sdata->instance_lock); ret = enonce1_64; } out_unlock: - ck_uilock(&sdata->instance_lock); + ck_wunlock(&sdata->instance_lock); out: if (ret) LOGNOTICE("Reconnecting old instance %"PRId64" to instance %"PRId64, old_id, id); @@ -1907,47 +1928,26 @@ static void stratum_add_send(sdata_t *sdata, json_t *val, const int64_t client_i ckmsgq_add(sdata->ssends, msg); } -static void inc_worker(ckpool_t *ckp, user_instance_t *instance) -{ - sdata_t *sdata = ckp->data; - - mutex_lock(&sdata->stats_lock); - sdata->stats.workers++; - if (!instance->workers++) - sdata->stats.users++; - mutex_unlock(&sdata->stats_lock); -} - -static void dec_worker(ckpool_t *ckp, user_instance_t *instance) -{ - sdata_t *sdata = ckp->data; - - mutex_lock(&sdata->stats_lock); - sdata->stats.workers--; - if (!--instance->workers) - sdata->stats.users--; - mutex_unlock(&sdata->stats_lock); -} - static void drop_client(ckpool_t *ckp, sdata_t *sdata, const int64_t id) { stratum_instance_t *client, *tmp; + char_entry_t *entries = NULL; user_instance_t *user = NULL; - int dropped = 0, aged = 0; time_t now_t = time(NULL); + int aged = 0; + char *msg; LOGINFO("Stratifier asked to drop client %"PRId64, id); - ck_ilock(&sdata->instance_lock); + ck_wlock(&sdata->instance_lock); client = __instance_by_id(sdata, id); - /* Upgrade to write lock */ - ck_ulock(&sdata->instance_lock); - if (client) { + if (client && !client->dropped) { + user = client->user_instance; /* If the client is still holding a reference, don't drop them * now but wait till the reference is dropped */ if (!client->ref) { - user = client->user_instance; - dropped = __drop_client(sdata, client, user); + __drop_client(sdata, client, user, false, &msg); + add_msg_entry(&entries, &msg); } else client->dropped = true; } @@ -1963,16 +1963,10 @@ static void drop_client(ckpool_t *ckp, sdata_t *sdata, const int64_t id) } ck_wunlock(&sdata->instance_lock); - client_drop_message(id, dropped, false); + notice_msg_entries(&entries); if (aged) LOGINFO("Aged %d disconnected instances to dead", aged); - /* Decrease worker count outside of instance_lock to avoid recursive - * locking */ - if (user) - dec_worker(ckp, user); - - if (aged || dropped) - reap_proxies(ckp, sdata); + reap_proxies(ckp, sdata); } static void stratum_broadcast_message(sdata_t *sdata, const char *msg) @@ -2825,6 +2819,72 @@ static void read_workerstats(ckpool_t *ckp, worker_instance_t *worker) } } +#define DEFAULT_AUTH_BACKOFF (3) /* Set initial backoff to 3 seconds */ + +static user_instance_t *__create_user(sdata_t *sdata, const char *username) +{ + user_instance_t *user = ckzalloc(sizeof(user_instance_t)); + + user->auth_backoff = DEFAULT_AUTH_BACKOFF; + strcpy(user->username, username); + user->id = sdata->user_instance_id++; + HASH_ADD_STR(sdata->user_instances, username, user); + return user; +} + +/* Find user by username or create one if it doesn't already exist */ +static user_instance_t *get_user(sdata_t *sdata, const char *username) +{ + user_instance_t *user; + + ck_wlock(&sdata->instance_lock); + HASH_FIND_STR(sdata->user_instances, username, user); + if (unlikely(!user)) + user = __create_user(sdata, username); + ck_wunlock(&sdata->instance_lock); + + return user; +} + +static worker_instance_t *__create_worker(user_instance_t *user, const char *workername) +{ + worker_instance_t *worker = ckzalloc(sizeof(worker_instance_t)); + + worker->workername = strdup(workername); + worker->user_instance = user; + DL_APPEND(user->worker_instances, worker); + worker->start_time = time(NULL); + return worker; +} + +static worker_instance_t *__get_worker(user_instance_t *user, const char *workername) +{ + worker_instance_t *worker = NULL, *tmp; + + DL_FOREACH(user->worker_instances, tmp) { + if (!safecmp(workername, tmp->workername)) { + worker = tmp; + break; + } + } + return worker; +} + +/* Find worker amongst a user's workers by workername or create one if it + * doesn't yet exist. */ +static worker_instance_t *get_worker(sdata_t *sdata, user_instance_t *user, const char *workername) +{ + worker_instance_t *worker; + + ck_wlock(&sdata->instance_lock); + worker = __get_worker(user, workername); + if (!worker) + worker = __create_worker(user, workername); + ck_wunlock(&sdata->instance_lock); + + return worker; +} + /* This simply strips off the first part of the workername and matches it to a * user or creates a new one. Needs to be entered with client holding a ref * count. */ @@ -2834,7 +2894,6 @@ static user_instance_t *generate_user(ckpool_t *ckp, stratum_instance_t *client, char *base_username = strdupa(workername), *username; bool new_user = false, new_worker = false; sdata_t *sdata = ckp->data; - worker_instance_t *tmp; user_instance_t *user; int len; @@ -2845,39 +2904,23 @@ static user_instance_t *generate_user(ckpool_t *ckp, stratum_instance_t *client, if (unlikely(len > 127)) username[127] = '\0'; - ck_ilock(&sdata->instance_lock); + ck_wlock(&sdata->instance_lock); HASH_FIND_STR(sdata->user_instances, username, user); - /* Upgrade to write lock */ - ck_ulock(&sdata->instance_lock); if (!user) { /* New user instance. Secondary user id will be NULL */ - user = ckzalloc(sizeof(user_instance_t)); - user->auth_backoff = 3; /* Set initial backoff to 3 seconds */ - strcpy(user->username, username); + user = __create_user(sdata, username); new_user = true; - user->id = sdata->user_instance_id++; - HASH_ADD_STR(sdata->user_instances, username, user); } client->user_instance = user; - DL_FOREACH(user->worker_instances, tmp) { - if (!safecmp(workername, tmp->workername)) { - client->worker_instance = tmp; - break; - } - } + client->worker_instance = __get_worker(user, workername); /* Create one worker instance for combined data from workers of the * same name */ if (!client->worker_instance) { - worker_instance_t *worker = ckzalloc(sizeof(worker_instance_t)); - - worker->workername = strdup(workername); - worker->user_instance = user; - DL_APPEND(user->worker_instances, worker); + client->worker_instance = __create_worker(user, workername); new_worker = true; - worker->start_time = time(NULL); - client->worker_instance = worker; } DL_APPEND(user->clients, client); + __inc_worker(sdata,user); ck_wunlock(&sdata->instance_lock); if (CKP_STANDALONE(ckp) && new_user) @@ -2896,6 +2939,22 @@ static user_instance_t *generate_user(ckpool_t *ckp, stratum_instance_t *client, return user; } +static void set_worker_mindiff(ckpool_t *ckp, const char *workername, int mindiff); + +static void parse_worker_diffs(ckpool_t *ckp, json_t *worker_array) +{ + json_t *worker_entry; + char *workername; + size_t index; + int mindiff; + + json_array_foreach(worker_array, index, worker_entry) { + json_get_string(&workername, worker_entry, "workername"); + json_get_int(&mindiff, worker_entry, "difficultydefault"); + set_worker_mindiff(ckp, workername, mindiff); + } +} + /* Send this to the database and parse the response to authorise a user * and get SUID parameters back. We don't add these requests to the sdata->ckdbqueue * since we have to wait for the response but this is done from the authoriser @@ -2970,8 +3029,10 @@ static int send_recv_auth(stratum_instance_t *client) if (unlikely(!val)) LOGWARNING("AUTH JSON decode failed(%d): %s", err_val.line, err_val.text); else { + json_t *worker_array = json_object_get(val, "workers"); + json_get_string(&secondaryuserid, val, "secondaryuserid"); - json_get_int(&worker->mindiff, val, "difficultydefault"); + parse_worker_diffs(ckp, worker_array); client->suggest_diff = worker->mindiff; if (!user->auth_time) user->auth_time = time(NULL); @@ -3032,14 +3093,15 @@ static void queue_delayed_auth(stratum_instance_t *client) } /* Needs to be entered with client holding a ref count. */ -static json_t *parse_authorise(stratum_instance_t *client, const json_t *params_val, json_t **err_val, - const char *address, int *errnum) +static json_t *parse_authorise(stratum_instance_t *client, const json_t *params_val, + json_t **err_val, int *errnum) { user_instance_t *user; ckpool_t *ckp = client->ckp; bool ret = false; const char *buf; int arr_size; + ts_t now; if (unlikely(!json_is_array(params_val))) { *err_val = json_string("params not an array"); @@ -3073,7 +3135,8 @@ static json_t *parse_authorise(stratum_instance_t *client, const json_t *params_ } user = generate_user(ckp, client, buf); client->user_id = user->id; - strcpy(client->address, address); + ts_realtime(&now); + client->start_time = now.tv_sec; /* NOTE workername is NULL prior to this so should not be used in code * till after this point */ client->workername = strdup(buf); @@ -3081,15 +3144,18 @@ static json_t *parse_authorise(stratum_instance_t *client, const json_t *params_ time_t now_t = time(NULL); if (now_t < user->failed_authtime + user->auth_backoff) { - LOGNOTICE("Client %"PRId64" worker %s rate limited due to failed auth attempts", - client->id, buf); + if (!user->throttled) { + user->throttled = true; + LOGNOTICE("Client %"PRId64" %s worker %s rate limited due to failed auth attempts", + client->id, client->address, buf); + } else{ + LOGINFO("Client %"PRId64" %s worker %s rate limited due to failed auth attempts", + client->id, client->address, buf); + } client->dropped = true; goto out; } } - /* NOTE worker count incremented here for any client put onto user's - * list until it's dropped */ - inc_worker(ckp, user); if (CKP_STANDALONE(ckp)) ret = true; else { @@ -3120,10 +3186,10 @@ static json_t *parse_authorise(stratum_instance_t *client, const json_t *params_ LOGNOTICE("Authorised client %"PRId64" worker %s as user %s", client->id, buf, user->username); } - user->auth_backoff = 3; /* Reset auth backoff time */ + user->auth_backoff = DEFAULT_AUTH_BACKOFF; /* Reset auth backoff time */ } else { - LOGNOTICE("Client %"PRId64" worker %s failed to authorise as user %s", client->id, buf, - user->username); + LOGNOTICE("Client %"PRId64" %s worker %s failed to authorise as user %s", + client->id, client->address, buf,user->username); user->failed_authtime = time(NULL); user->auth_backoff <<= 1; /* Cap backoff time to 10 mins */ @@ -3828,7 +3894,7 @@ static void update_client(const stratum_instance_t *client, const int64_t client static json_params_t *create_json_params(const int64_t client_id, const json_t *method, const json_t *params, - const json_t *id_val, const char *address) + const json_t *id_val) { json_params_t *jp = ckalloc(sizeof(json_params_t)); @@ -3836,47 +3902,25 @@ static json_params_t jp->params = json_deep_copy(params); jp->id_val = json_deep_copy(id_val); jp->client_id = client_id; - strcpy(jp->address, address); return jp; } static void set_worker_mindiff(ckpool_t *ckp, const char *workername, int mindiff) { - worker_instance_t *worker = NULL, *tmp; char *username = strdupa(workername), *ignore; - user_instance_t *user = NULL; stratum_instance_t *client; sdata_t *sdata = ckp->data; + worker_instance_t *worker; + user_instance_t *user; ignore = username; strsep(&ignore, "._"); /* Find the user first */ - ck_rlock(&sdata->instance_lock); - HASH_FIND_STR(sdata->user_instances, username, user); - ck_runlock(&sdata->instance_lock); - - /* They may just have not connected yet */ - if (!user) { - LOGINFO("Failed to find user %s in set_worker_mindiff", username); - return; - } + user = get_user(sdata, username); /* Then find the matching worker user */ - ck_rlock(&sdata->instance_lock); - DL_FOREACH(user->worker_instances, tmp) { - if (!safecmp(workername, tmp->workername)) { - worker = tmp; - break; - } - } - ck_runlock(&sdata->instance_lock); - - /* They may just not be connected at the moment */ - if (!worker) { - LOGINFO("Failed to find worker %s in set_worker_mindiff", workername); - return; - } + worker = get_worker(sdata, user, workername); if (mindiff < 1) { LOGINFO("Worker %s requested invalid diff %d", worker->workername, mindiff); @@ -3940,7 +3984,7 @@ static void suggest_diff(stratum_instance_t *client, const char *method, const j /* Enter with client holding ref count */ static void parse_method(sdata_t *sdata, stratum_instance_t *client, const int64_t client_id, - json_t *id_val, json_t *method_val, json_t *params_val, const char *address) + json_t *id_val, json_t *method_val, json_t *params_val) { const char *method; @@ -3949,7 +3993,7 @@ static void parse_method(sdata_t *sdata, stratum_instance_t *client, const int64 * most common messages will be shares so look for those first */ method = json_string_value(method_val); if (likely(cmdmatch(method, "mining.submit") && client->authorised)) { - json_params_t *jp = create_json_params(client_id, method_val, params_val, id_val, address); + json_params_t *jp = create_json_params(client_id, method_val, params_val, id_val); ckmsgq_add(sdata->sshareq, jp); return; @@ -3959,7 +4003,8 @@ static void parse_method(sdata_t *sdata, stratum_instance_t *client, const int64 json_t *val, *result_val; if (unlikely(client->subscribed)) { - LOGNOTICE("Client %"PRId64" trying to subscribe twice", client_id); + LOGNOTICE("Client %"PRId64" %s trying to subscribe twice", + client_id, client->address); return; } result_val = parse_subscribe(client, client_id, params_val); @@ -3981,7 +4026,7 @@ static void parse_method(sdata_t *sdata, stratum_instance_t *client, const int64 if (unlikely(cmdmatch(method, "mining.passthrough"))) { char buf[256]; - LOGNOTICE("Adding passthrough client %"PRId64, client_id); + LOGNOTICE("Adding passthrough client %"PRId64" %s", client_id, client->address); /* We need to inform the connector process that this client * is a passthrough and to manage its messages accordingly. * The client_id stays on the list but we won't send anything @@ -3994,7 +4039,7 @@ static void parse_method(sdata_t *sdata, stratum_instance_t *client, const int64 /* We should only accept subscribed requests from here on */ if (!client->subscribed) { - LOGINFO("Dropping unsubscribed client %"PRId64, client_id); + LOGINFO("Dropping unsubscribed client %"PRId64" %s", client_id, client->address); connector_drop_client(client->ckp, client_id); return; } @@ -4003,10 +4048,11 @@ static void parse_method(sdata_t *sdata, stratum_instance_t *client, const int64 json_params_t *jp; if (unlikely(client->authorised)) { - LOGNOTICE("Client %"PRId64" trying to authorise twice", client_id); + LOGNOTICE("Client %"PRId64" %s trying to authorise twice", + client_id, client->address); return; } - jp = create_json_params(client_id, method_val, params_val, id_val, address); + jp = create_json_params(client_id, method_val, params_val, id_val); ckmsgq_add(sdata->sauthq, jp); return; } @@ -4016,7 +4062,7 @@ static void parse_method(sdata_t *sdata, stratum_instance_t *client, const int64 /* Dropping unauthorised clients here also allows the * stratifier process to restart since it will have lost all * the stratum instance data. Clients will just reconnect. */ - LOGINFO("Dropping unauthorised client %"PRId64, client_id); + LOGINFO("Dropping unauthorised client %"PRId64" %s", client_id, client->address); connector_drop_client(client->ckp, client_id); return; } @@ -4028,13 +4074,13 @@ static void parse_method(sdata_t *sdata, stratum_instance_t *client, const int64 /* Covers both get_transactions and get_txnhashes */ if (cmdmatch(method, "mining.get")) { - json_params_t *jp = create_json_params(client_id, method_val, params_val, id_val, address); + json_params_t *jp = create_json_params(client_id, method_val, params_val, id_val); ckmsgq_add(sdata->stxnq, jp); return; } /* Unhandled message here */ - LOGINFO("Unhandled client %"PRId64" method %s", client_id, method); + LOGINFO("Unhandled client %"PRId64" %s method %s", client_id, client->address, method); return; } @@ -4051,7 +4097,8 @@ static void parse_instance_msg(ckpool_t *ckp, sdata_t *sdata, smsg_t *msg, strat int64_t client_id = msg->client_id; if (client->reject == 2 || (client->reconnect_request && time(NULL) - client->reconnect_request > 60)) { - LOGINFO("Dropping client %"PRId64" tagged for lazy invalidation", client_id); + LOGINFO("Dropping client %"PRId64" %s tagged for lazy invalidation", + client_id, client->address); connector_drop_client(ckp, client_id); goto out; } @@ -4086,7 +4133,7 @@ static void parse_instance_msg(ckpool_t *ckp, sdata_t *sdata, smsg_t *msg, strat send_json_err(sdata, client_id, id_val, "-1:params not found"); goto out; } - parse_method(sdata, client, client_id, id_val, method, params, msg->address); + parse_method(sdata, client, client_id, id_val, method, params); out: free_smsg(msg); } @@ -4094,6 +4141,7 @@ out: static void srecv_process(ckpool_t *ckp, char *buf) { bool noid = false, dropped = false; + char address[INET6_ADDRSTRLEN]; sdata_t *sdata = ckp->data; stratum_instance_t *client; smsg_t *msg; @@ -4125,7 +4173,7 @@ static void srecv_process(ckpool_t *ckp, char *buf) free(msg); goto out; } - strcpy(msg->address, json_string_value(val)); + strcpy(address, json_string_value(val)); json_object_clear(val); val = json_object_get(msg->json_msg, "server"); @@ -4139,15 +4187,13 @@ static void srecv_process(ckpool_t *ckp, char *buf) json_object_clear(val); /* Parse the message here */ - ck_ilock(&sdata->instance_lock); + ck_wlock(&sdata->instance_lock); client = __instance_by_id(sdata, msg->client_id); - /* Upgrade to write lock */ - ck_ulock(&sdata->instance_lock); /* If client_id instance doesn't exist yet, create one */ if (unlikely(!client)) { if (likely(!__dropped_instance(sdata, msg->client_id))) { noid = true; - client = __stratum_add_instance(ckp, msg->client_id, server); + client = __stratum_add_instance(ckp, msg->client_id, address, server); } else dropped = true; } else if (unlikely(client->dropped)) @@ -4240,20 +4286,17 @@ static stratum_instance_t *preauth_ref_instance_by_id(sdata_t *sdata, const int6 { stratum_instance_t *client; - ck_ilock(&sdata->instance_lock); + ck_wlock(&sdata->instance_lock); client = __instance_by_id(sdata, id); if (client) { if (client->dropped || client->authorising || client->authorised) client = NULL; else { - /* Upgrade to write lock to modify client data */ - ck_ulock(&sdata->instance_lock); __inc_instance_ref(client); client->authorising = true; - ck_dwilock(&sdata->instance_lock); } } - ck_uilock(&sdata->instance_lock); + ck_wunlock(&sdata->instance_lock); return client; } @@ -4274,7 +4317,7 @@ static void sauth_process(ckpool_t *ckp, json_params_t *jp) goto out; } - result_val = parse_authorise(client, jp->params, &err_val, jp->address, &errnum); + result_val = parse_authorise(client, jp->params, &err_val, &errnum); if (json_is_true(result_val)) { char *buf;