Browse Source

Merge branch 'master' into multiproxy

Conflicts:
	src/stratifier.c
master
Con Kolivas 10 years ago
parent
commit
afc2b8f802
  1. 7
      configure.ac
  2. BIN
      html/BTCSym.png
  3. 48
      pool/base.php
  4. 24
      pool/db.php
  5. 6
      pool/page.php
  6. 116
      pool/page_blocks.php
  7. 81
      pool/page_mpayouts.php
  8. 48
      pool/page_payments.php
  9. 76
      pool/page_shifts.php
  10. 5
      pool/prime.php
  11. 2
      src/Makefile.am
  12. 8
      src/ckdb.c
  13. 43
      src/ckdb.h
  14. 611
      src/ckdb_cmd.c
  15. 174
      src/ckdb_data.c
  16. 21
      src/ckdb_dbio.c
  17. 21
      src/connector.c
  18. 26
      src/libckpool.c
  19. 4
      src/libckpool.h
  20. 411
      src/stratifier.c

7
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(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(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(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" PTHREAD_LIBS="-lpthread"
MATH_LIBS="-lm" MATH_LIBS="-lm"
@ -59,8 +60,12 @@ AC_ARG_WITH([ckdb],
if test "x$ckdb" != "xno"; then if test "x$ckdb" != "xno"; then
AC_CHECK_LIB([pq], [main],[PQ=-lpq],echo "Error: Required library libpq-dev 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) 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]) AC_DEFINE([USE_CKDB], [1], [Defined to 1 if ckdb support required])
PQ_LIBS="-lpq" PQ_LIBS="-lpq -lgsl -lgslcblas"
else else
PQ_LIBS="" PQ_LIBS=""
fi fi

BIN
html/BTCSym.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 387 B

48
pool/base.php

@ -63,12 +63,38 @@ function howlongago($sec)
return $des; 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) function btcfmt($amt)
{ {
$amt /= 100000000; $amt /= 100000000;
return number_format($amt, 8); return number_format($amt, 8);
} }
# #
function utcd($when)
{
return gmdate('Y-m-d H:i:s+00', round($when));
}
#
global $sipre; global $sipre;
# max of uint64 is ~1.845x10^19, 'Z' is above that (10^21) # 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) # 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); $key = 'ckp'.rand(1000000,9999999);
$_SESSION['ckpkey'] = $key; $_SESSION['ckpkey'] = $key;
$_SESSION[$key] = array('who' => $user, 'id' => $user); $_SESSION[$key] = array('who' => $user, 'id' => $user);
return true;
} }
return false;
} }
# #
function logout() function logout()
@ -338,6 +366,8 @@ function requestRegister()
# #
function tryLogInOut() function tryLogInOut()
{ {
global $loginfailed;
// If already logged in, it will ignore User/Pass // If already logged in, it will ignore User/Pass
if (isset($_SESSION['ckpkey'])) if (isset($_SESSION['ckpkey']))
{ {
@ -347,21 +377,29 @@ function tryLogInOut()
} }
else else
{ {
$login = getparam('Login', false);
if (nuem($login))
return;
$user = getparam('User', false); $user = getparam('User', false);
if ($user !== NULL) if ($user !== NULL)
$user = loginStr($user); $user = loginStr($user);
if (nuem($user)) if (nuem($user))
{
$loginfailed = true;
return; return;
}
$pass = getparam('Pass', false); $pass = getparam('Pass', false);
if (nuem($pass)) if (nuem($pass))
{
$loginfailed = true;
return; return;
}
$login = getparam('Login', false); $valid = validUserPass($user, $pass);
if (nuem($login)) if (!$valid)
return; $loginfailed = true;
validUserPass($user, $pass);
} }
} }
# #

24
pool/db.php

@ -258,6 +258,30 @@ function getPayments($user)
return repDecode($rep); 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) function getBlocks($user)
{ {
if ($user == false) if ($user == false)

6
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;} .topdat {margin-left: 8px; margin-right: 24px; color:green; font-weight: bold;}
span.login {float: right; margin-left: 8px; margin-right: 24px;} span.login {float: right; margin-left: 8px; margin-right: 24px;}
span.hil {color:blue;} span.hil {color:blue;}
span.user {color:green;}
span.addr {color:brown;}
span.warn {color:orange; font-weight:bold;} span.warn {color:orange; font-weight:bold;}
span.urg {color:red; font-weight:bold;} span.urg {color:red; font-weight:bold;}
span.err {color:red; font-weight:bold; font-size:120%;} 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) function pgtop($info, $dotop, $user, $douser)
{ {
global $site_title; global $site_title, $loginfailed;
$phr = '?THs'; $phr = '?THs';
$plb = '?'; $plb = '?';
@ -286,6 +288,8 @@ function pgtop($info, $dotop, $user, $douser)
$top .= " You need to enable javascript to use"; $top .= " You need to enable javascript to use";
$top .= " the $site_title web site.</div>"; $top .= " the $site_title web site.</div>";
if ($loginfailed === true)
$top .= '<div class=accwarn>Login Failed</div>';
if (isset($info['u_nopayaddr'])) if (isset($info['u_nopayaddr']))
$top .= '<div class=accwarn>Please set a payout address on your account!</div>'; $top .= '<div class=accwarn>Please set a payout address on your account!</div>';
if (isset($info['u_noemail'])) if (isset($info['u_noemail']))

116
pool/page_blocks.php

@ -56,6 +56,43 @@ function doblocks($data, $user)
if ($wantcsv === false) if ($wantcsv === false)
{ {
if ($ans['STATUS'] == 'ok' and isset($ans['s_rows']) and $ans['s_rows'] > 0)
{
$pg .= '<h1>Block Statistics</h1>';
$pg .= "<table callpadding=0 cellspacing=0 border=0>\n";
$pg .= "<tr class=title>";
$pg .= "<td class=dl>Description</td>";
$pg .= "<td class=dr>Diff%</td>";
$pg .= "<td class=dr>Mean%</td>";
$pg .= "<td class=dr>CDF[Erl]</td>";
$pg .= "<td class=dr>Luck%</td>";
$pg .= "</tr>\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 .= "<tr class=$row>";
$pg .= "<td class=dl>$desc Blocks</td>";
$pg .= "<td class=dr>$diff%</td>";
$pg .= "<td class=dr>$mean%</td>";
$pg .= "<td class=dr>$cdferl</td>";
$pg .= "<td class=dr>$luck%</td>";
$pg .= "</tr>\n";
}
$pg .= "</table>\n";
}
if ($ans['STATUS'] == 'ok') if ($ans['STATUS'] == 'ok')
{ {
$count = $ans['rows']; $count = $ans['rows'];
@ -70,15 +107,15 @@ function doblocks($data, $user)
$s = 's'; $s = 's';
} }
$pg = "<h1>Last$num Block$s</h1>"; $pg .= "<h1>Last$num Block$s</h1>";
} }
else else
$pg = '<h1>Blocks</h1>'; $pg .= '<h1>Blocks</h1>';
list($fg, $bg) = pctcolour(25.0); list($fg, $bg) = pctcolour(25.0);
$pg .= "<span style='background:$bg; color:$fg;'>"; $pg .= "<span style='background:$bg; color:$fg;'>";
$pg .= "&nbsp;Green&nbsp;</span>&nbsp;"; $pg .= "&nbsp;Green&nbsp;</span>&nbsp;";
$pg .= 'is good luck. Lower Diff% and bright green is best luck.<br>'; $pg .= 'is good luck. Lower Diff% and brighter green is better luck.<br>';
list($fg, $bg) = pctcolour(100.0); list($fg, $bg) = pctcolour(100.0);
$pg .= "<span style='background:$bg; color:$fg;'>"; $pg .= "<span style='background:$bg; color:$fg;'>";
$pg .= "&nbsp;100%&nbsp;</span>&nbsp;"; $pg .= "&nbsp;100%&nbsp;</span>&nbsp;";
@ -86,7 +123,7 @@ function doblocks($data, $user)
list($fg, $bg) = pctcolour(400.0); list($fg, $bg) = pctcolour(400.0);
$pg .= "<span style='background:$bg; color:$fg;'>"; $pg .= "<span style='background:$bg; color:$fg;'>";
$pg .= "&nbsp;Red&nbsp;</span>&nbsp;"; $pg .= "&nbsp;Red&nbsp;</span>&nbsp;";
$pg .= 'is bad luck. Higher Diff% and bright red is worse luck.<br><br>'; $pg .= 'is bad luck. Higher Diff% and brighter red is worse luck.<br><br>';
$pg .= "<table callpadding=0 cellspacing=0 border=0>\n"; $pg .= "<table callpadding=0 cellspacing=0 border=0>\n";
$pg .= "<tr class=title>"; $pg .= "<tr class=title>";
@ -94,7 +131,7 @@ function doblocks($data, $user)
$pg .= "<td class=dl>Height</td>"; $pg .= "<td class=dl>Height</td>";
if ($user !== null) if ($user !== null)
$pg .= "<td class=dl>Who</td>"; $pg .= "<td class=dl>Who</td>";
$pg .= "<td class=dr>Reward</td>"; $pg .= "<td class=dr>Block Reward</td>";
$pg .= "<td class=dc>When</td>"; $pg .= "<td class=dc>When</td>";
$pg .= "<td class=dr>Status</td>"; $pg .= "<td class=dr>Status</td>";
$pg .= "<td class=dr>Diff</td>"; $pg .= "<td class=dr>Diff</td>";
@ -109,7 +146,6 @@ function doblocks($data, $user)
$csv = "Sequence,Height,Status,Timestamp,DiffAcc,NetDiff,Hash\n"; $csv = "Sequence,Height,Status,Timestamp,DiffAcc,NetDiff,Hash\n";
if ($ans['STATUS'] == 'ok') if ($ans['STATUS'] == 'ok')
{ {
$tot = $ans['tot'];
$count = $ans['rows']; $count = $ans['rows'];
for ($i = 0; $i < $count; $i++) for ($i = 0; $i < $count; $i++)
{ {
@ -130,7 +166,7 @@ function doblocks($data, $user)
$seq = ''; $seq = '';
} }
else else
$seq = $tot--; $seq = $ans['seq:'.$i];
if ($stat == '1-Confirm') if ($stat == '1-Confirm')
{ {
if (isset($data['info']['lastheight'])) if (isset($data['info']['lastheight']))
@ -143,37 +179,34 @@ function doblocks($data, $user)
} }
$stara = ''; $stara = '';
$starp = ''; if ($stat == 'Orphan')
if (isset($ans['status:'.($i+1)])) $stara = '<span class=st1>*</span>';
if ($ans['status:'.($i+1)] == 'Orphan'
&& $stat != 'Orphan')
{
$stara = '<span class=st1>*</span>';
$starp = '<span class=st0>*</span>';
}
$diffacc = $ans['diffacc:'.$i]; $diffacc = $ans['diffacc:'.$i];
$acc = number_format($diffacc, 0); $acc = number_format($diffacc, 0);
$netdiff = $ans['netdiff:'.$i]; $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); list($fg, $bg) = pctcolour($pct);
$bpct = "<font color=$fg>$starp".number_format($pct, 2).'%</font>'; $bpct = "<font color=$fg>".number_format($pct, 2).'%</font>';
$bg = " bgcolor=$bg"; $bg = " bgcolor=$bg";
$blktot += $diffacc; $blktot += $diffacc;
if ($stat != 'Orphan') if ($stat != 'Orphan')
$nettot += $netdiff; $nettot += $netdiff;
$cdfv = 1 - exp(-1 * $diffacc / $netdiff); $cdfdsp = number_format($cdf, 2);
$cdf = number_format($cdfv, 2);
} }
else else
{ {
$bg = ''; $bg = '';
$bpct = '?'; $bpct = '?';
$cdf = '?'; $cdfdsp = '?';
} }
if ($wantcsv === false) if ($wantcsv === false)
@ -184,11 +217,11 @@ function doblocks($data, $user)
if ($user !== null) if ($user !== null)
$pg .= "<td class=dl$ex>".htmlspecialchars($ans['workername:'.$i]).'</td>'; $pg .= "<td class=dl$ex>".htmlspecialchars($ans['workername:'.$i]).'</td>';
$pg .= "<td class=dr$ex>".btcfmt($ans['reward:'.$i]).'</td>'; $pg .= "<td class=dr$ex>".btcfmt($ans['reward:'.$i]).'</td>';
$pg .= "<td class=dl$ex>".gmdate('Y-m-d H:i:s+00', $ans['firstcreatedate:'.$i]).'</td>'; $pg .= "<td class=dl$ex>".utcd($ans['firstcreatedate:'.$i]).'</td>';
$pg .= "<td class=dr$ex>".$stat.'</td>'; $pg .= "<td class=dr$ex>".$stat.'</td>';
$pg .= "<td class=dr>$stara$acc</td>"; $pg .= "<td class=dr>$stara$acc</td>";
$pg .= "<td class=dr$bg>$bpct</td>"; $pg .= "<td class=dr$bg>$bpct</td>";
$pg .= "<td class=dr>$cdf</td>"; $pg .= "<td class=dr>$cdfdsp</td>";
$pg .= "</tr>\n"; $pg .= "</tr>\n";
} }
else else
@ -208,39 +241,16 @@ function doblocks($data, $user)
echo $csv; echo $csv;
exit(0); exit(0);
} }
if ($nettot > 0) if ($orph === true)
{ {
if (($i % 2) == 0) $pg .= '<tr><td colspan=';
$row = 'even';
else
$row = 'odd';
$pct = 100.0 * $blktot / $nettot;
list($fg, $bg) = pctcolour($pct);
$bpct = "<font color=$fg>".number_format($pct, 2).'%</font>';
$bg = " bgcolor=$bg";
$pg .= "<tr class=$row>";
$pg .= '<td colspan=2 class=dr>Total:</td>';
$pg .= '<td class=dl colspan=';
if ($user === null) if ($user === null)
$pg .= '4'; $pg .= '7';
else else
$pg .= '5'; $pg .= '8';
$pg .= '></td>'; $pg .= ' class=dc><font size=-1><span class=st1>*</span>';
$pg .= "<td class=dr$bg>".$bpct.'</td>'; $pg .= "Orphans count as shares but not as a block in calculations";
$pg .= "<td></td></tr>\n"; $pg .= '</font></td></tr>';
if ($orph === true)
{
$pg .= '<tr><td colspan=';
if ($user === null)
$pg .= '7';
else
$pg .= '8';
$pg .= ' class=dc><font size=-1><span class=st1>*</span>';
$pg .= '% total is adjusted to include orphans correctly';
$pg .= '</font></td></tr>';
}
} }
$pg .= "</table>\n"; $pg .= "</table>\n";

81
pool/page_mpayouts.php

@ -0,0 +1,81 @@
<?php
#
function dompayouts($data, $user)
{
$pg = '<h1>Mining Rewards</h1>';
$ans = getMPayouts($user);
$pg .= "The rewards you've earned for each block the pool has found.<br>";
$pg .= 'See the ';
$pg .= makeLink('payments');
$pg .= "Payments</a> page for the payments you've been sent.<br><br>";
$pg .= "<table callpadding=0 cellspacing=0 border=0>\n";
$pg .= "<tr class=title>";
$pg .= "<td class=dr>Block</td>";
$pg .= "<td class=dr>Miner Reward</td>";
$pg .= "<td class=dr>N Diff</td>";
$pg .= "<td class=dr>N Range</td>";
$pg .= "<td class=dr>Pool N Avg</td>";
$pg .= "<td class=dr>Your %</td>";
$pg .= "<td class=dr>Your N Diff</td>";
$pg .= "<td class=dr>Your N Avg</td>";
$pg .= "<td class=dr>Your BTC</td>";
$pg .= "</tr>\n";
if ($ans['STATUS'] == 'ok')
{
$totamt = 0;
$count = $ans['rows'];
for ($i = 0; $i < $count; $i++)
{
if (($i % 2) == 0)
$row = 'even';
else
$row = 'odd';
$pg .= "<tr class=$row>";
$pg .= '<td class=dr>'.$ans['height:'.$i].'</td>';
$pg .= '<td class=dr>'.btcfmt($ans['minerreward:'.$i]).'</td>';
$diffused = $ans['diffused:'.$i];
$pg .= '<td class=dr>'.difffmt($diffused).'</td>';
$elapsed = $ans['elapsed:'.$i];
$pg .= '<td class=dr>'.howmanyhrs($elapsed).'</td>';
$phr = $diffused * pow(2,32) / $elapsed;
$pg .= '<td class=dr>'.siprefmt($phr).'Hs</td>';
$diffacc = $ans['diffacc:'.$i];
$ypct = $diffacc * 100 / $diffused;
$pg .= '<td class=dr>'.number_format($ypct, 2).'%</td>';
$pg .= '<td class=dr>'.difffmt($diffacc).'</td>';
$hr = $diffacc * pow(2,32) / $elapsed;
$pg .= '<td class=dr>'.dsprate($hr).'</td>';
$amount = $ans['amount:'.$i];
$totamt += $amount;
$pg .= '<td class=dr>'.btcfmt($amount).'</td>';
$pg .= "</tr>\n";
}
if ($count > 1)
{
if (($i % 2) == 0)
$row = 'even';
else
$row = 'odd';
$pg .= "<tr class=$row>";
$pg .= '<td class=dr>Total:</td>';
$pg .= '<td class=dl colspan=7></td>';
$pg .= '<td class=dr>'.btcfmt($totamt).'</td>';
$pg .= "</tr>\n";
}
}
$pg .= "</table>\n";
return $pg;
}
#
function show_mpayouts($info, $page, $menu, $name, $user)
{
gopage($info, NULL, 'dompayouts', $page, $menu, $name, $user);
}
#
?>

48
pool/page_payments.php

@ -1,26 +1,45 @@
<?php <?php
# #
function sortheight($a, $b)
{
return $b['height'] - $a['height'];
}
#
function dopayments($data, $user) function dopayments($data, $user)
{ {
$bc = 'https://blockchain.info/address/'; $bc = 'https://blockchain.info/address/';
$addr1 = '1KzFJddTvK9TQWsmWFKYJ9fRx9QeSATyrT'; $addr1 = '1KzFJddTvK9TQWsmWFKYJ9fRx9QeSATyrT';
$pg = '<h1>Payments</h1>'; $pg = '<h1>Payments</h1>';
$pg .= 'The payout transactions on blockchain are here:'; $pg .= 'The payment transactions on blockchain are here:';
$pg .= " <a href='$bc$addr1' target=_blank>BTC</a><br><br>"; $pg .= " <a href='$bc$addr1' target=_blank>BTC</a><br>";
$pg .= "The payments below don't yet show when they have been sent.<br><br>";
$ans = getPayments($user); $ans = getPayments($user);
$pg .= "<table callpadding=0 cellspacing=0 border=0>\n"; $pg .= "<table callpadding=0 cellspacing=0 border=0>\n";
$pg .= "<tr class=title>"; $pg .= "<tr class=title>";
$pg .= "<td class=dl>Date</td>"; $pg .= "<td class=dl>Block</td>";
$pg .= "<td class=dl>Address</td>"; $pg .= "<td class=dl>Address</td>";
$pg .= "<td class=dl>Status</td>";
$pg .= "<td class=dr>BTC</td>"; $pg .= "<td class=dr>BTC</td>";
$pg .= "<td class=dl></td>";
$pg .= "</tr>\n"; $pg .= "</tr>\n";
if ($ans['STATUS'] == 'ok') if ($ans['STATUS'] == 'ok')
{ {
$all = array();
$count = $ans['rows']; $count = $ans['rows'];
for ($i = 0; $i < $count; $i++) 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) if (($i % 2) == 0)
$row = 'even'; $row = 'even';
@ -28,11 +47,28 @@ function dopayments($data, $user)
$row = 'odd'; $row = 'odd';
$pg .= "<tr class=$row>"; $pg .= "<tr class=$row>";
$pg .= '<td class=dl>'.$ans['paydate:'.$i].'</td>'; $pg .= '<td class=dl>'.$all[$i]['height'].'</td>';
$pg .= '<td class=dl>'.$ans['payaddress:'.$i].'</td>'; $pg .= '<td class=dl>'.$all[$i]['payaddress'].'</td>';
$pg .= '<td class=dr>'.btcfmt($ans['amount:'.$i]).'</td>'; $pg .= '<td class=dl>&nbsp;</td>';
$amount = $all[$i]['amount'];
if ($amount < '10000')
{
$dust = '<span class=st1>*</span>';
$hasdust = true;
}
else
$dust = '&nbsp;';
$pg .= '<td class=dr>'.btcfmt($amount).'</td>';
$pg .= "<td class=dl>$dust</td>";
$pg .= "</tr>\n"; $pg .= "</tr>\n";
} }
if ($hasdust === true)
{
$pg .= '<tr><td colspan=5 class=dc>';
$pg .= '<font size=-1><span class=st1>*</span> ';
$pg .= 'Dust payments are not automatically sent out';
$pg .= '</font></td></tr>';
}
} }
$pg .= "</table>\n"; $pg .= "</table>\n";

76
pool/page_shifts.php

@ -0,0 +1,76 @@
<?php
#
function doshifts($data, $user)
{
$ans = getShifts($user);
$pg = "<table callpadding=0 cellspacing=0 border=0>\n";
$pg .= "<tr class=title>";
$pg .= "<td class=dl>Shift</td>";
$pg .= "<td class=dl>Start</td>";
$pg .= "<td class=dr>Length</td>";
$pg .= "<td class=dr>Your Diff</td>";
$pg .= "<td class=dr>Avg Hs</td>";
$pg .= "<td class=dr>Shares</td>";
$pg .= "<td class=dr>Avg Share</td>";
$pg .= "</tr>\n";
if ($ans['STATUS'] != 'ok')
$pg = '<h1>Shifts</h1>'.$pg;
else
{
$count = $ans['rows'];
$pg = '<h1>Last '.($count+1).' Shifts</h1>'.$pg;
for ($i = 0; $i < $count; $i++)
{
if (($i % 2) == 0)
$row = 'even';
else
$row = 'odd';
$pg .= "<tr class=$row>";
$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 = ' <img src=/BTCSym.png border=0>';
else
$btc = '';
$pg .= "<td class=dl>$shif$btc</td>";
$start = $ans['start:'.$i];
$pg .= '<td class=dl>'.utcd($start).'</td>';
$nd = $ans['end:'.$i];
$elapsed = $nd - $start;
$pg .= '<td class=dr>'.howmanyhrs($elapsed).'</td>';
$diffacc = $ans['diffacc:'.$i];
$pg .= '<td class=dr>'.difffmt($diffacc).'</td>';
$hr = $diffacc * pow(2,32) / $elapsed;
$pg .= '<td class=dr>'.dsprate($hr).'</td>';
$shareacc = $ans['shareacc:'.$i];
$pg .= '<td class=dr>'.difffmt($shareacc).'</td>';
if ($shareacc > 0)
$avgsh = $diffacc / $shareacc;
else
$avgsh = 0;
$pg .= '<td class=dr>'.number_format($avgsh, 2).'</td>';
$pg .= "</tr>\n";
}
}
$pg .= "</table>\n";
return $pg;
}
#
function show_shifts($info, $page, $menu, $name, $user)
{
gopage($info, NULL, 'doshifts', $page, $menu, $name, $user);
}
#
?>

5
pool/prime.php

@ -3,6 +3,9 @@
global $stt; global $stt;
$stt = microtime(); $stt = microtime();
# #
global $loginfailed;
$loginfailed = false;
#
include_once('param.php'); include_once('param.php');
include_once('base.php'); include_once('base.php');
# #
@ -67,11 +70,13 @@ function check()
'Home' => '' 'Home' => ''
), ),
'Account' => array( 'Account' => array(
'Rewards' => 'mpayouts',
'Payments' => 'payments', 'Payments' => 'payments',
'Settings' => 'settings', 'Settings' => 'settings',
'User Settings' => 'userset' 'User Settings' => 'userset'
), ),
'Workers' => array( 'Workers' => array(
'Shifts ' => 'shifts',
'Workers ' => 'workers', 'Workers ' => 'workers',
'Management' => 'workmgt', 'Management' => 'workmgt',
), ),

2
src/Makefile.am

@ -23,5 +23,5 @@ if WANT_CKDB
bin_PROGRAMS += ckdb bin_PROGRAMS += ckdb
ckdb_SOURCES = ckdb.c ckdb_cmd.c ckdb_data.c ckdb_dbio.c ckdb_btc.c \ 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.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 endif

8
src/ckdb.c

@ -279,6 +279,7 @@ bool everyone_die = false;
tv_t last_heartbeat; tv_t last_heartbeat;
tv_t last_workinfo; tv_t last_workinfo;
tv_t last_share; tv_t last_share;
tv_t last_share_inv;
tv_t last_auth; tv_t last_auth;
cklock_t last_lock; cklock_t last_lock;
@ -398,6 +399,8 @@ const char *blocks_unknown = "?Unknown?";
K_TREE *blocks_root; K_TREE *blocks_root;
K_LIST *blocks_free; K_LIST *blocks_free;
K_STORE *blocks_store; K_STORE *blocks_store;
tv_t blocks_stats_time;
bool blocks_stats_rebuild = true;
// MININGPAYOUTS // MININGPAYOUTS
K_TREE *miningpayouts_root; K_TREE *miningpayouts_root;
@ -2663,6 +2666,8 @@ static void *socketer(__maybe_unused void *arg)
case CMD_PPLNS: case CMD_PPLNS:
case CMD_PPLNS2: case CMD_PPLNS2:
case CMD_PAYOUTS: case CMD_PAYOUTS:
case CMD_MPAYOUTS:
case CMD_SHIFTS:
case CMD_DSP: case CMD_DSP:
case CMD_BLOCKSTATUS: case CMD_BLOCKSTATUS:
if (!startup_complete) { 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_PPLNS:
case CMD_PPLNS2: case CMD_PPLNS2:
case CMD_PAYOUTS: case CMD_PAYOUTS:
case CMD_MPAYOUTS:
case CMD_SHIFTS:
case CMD_USERSTATUS: case CMD_USERSTATUS:
case CMD_MARKS: case CMD_MARKS:
LOGERR("%s() Message line %"PRIu64" '%s' - invalid - ignored", LOGERR("%s() Message line %"PRIu64" '%s' - invalid - ignored",
@ -3250,6 +3257,7 @@ static void *listener(void *arg)
setnow(&last_heartbeat); setnow(&last_heartbeat);
copy_tv(&last_workinfo, &last_heartbeat); copy_tv(&last_workinfo, &last_heartbeat);
copy_tv(&last_share, &last_heartbeat); copy_tv(&last_share, &last_heartbeat);
copy_tv(&last_share_inv, &last_heartbeat);
copy_tv(&last_auth, &last_heartbeat); copy_tv(&last_auth, &last_heartbeat);
ck_wunlock(&last_lock); ck_wunlock(&last_lock);

43
src/ckdb.h

@ -37,6 +37,9 @@
#include <postgresql/libpq-fe.h> #include <postgresql/libpq-fe.h>
#endif #endif
#include <gsl/gsl_math.h>
#include <gsl/gsl_cdf.h>
#include "ckpool.h" #include "ckpool.h"
#include "libckpool.h" #include "libckpool.h"
@ -52,7 +55,7 @@
#define DB_VLOCK "1" #define DB_VLOCK "1"
#define DB_VERSION "1.0.0" #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 " - from %s %s() line %d"
#define WHERE_FFL_HERE __FILE__, __func__, __LINE__ #define WHERE_FFL_HERE __FILE__, __func__, __LINE__
@ -192,6 +195,11 @@ extern POOLSTATUS pool;
_dstoff += _srclen; \ _dstoff += _srclen; \
} while(0) } while(0)
#define APPEND_REALLOC_RESET(_buf, _off) do { \
(_buf)[0] = '\0'; \
_off = 0; \
} while(0)
enum data_type { enum data_type {
TYPE_STR, TYPE_STR,
TYPE_BIGINT, TYPE_BIGINT,
@ -274,6 +282,7 @@ extern bool everyone_die;
extern tv_t last_heartbeat; extern tv_t last_heartbeat;
extern tv_t last_workinfo; extern tv_t last_workinfo;
extern tv_t last_share; extern tv_t last_share;
extern tv_t last_share_inv;
extern tv_t last_auth; extern tv_t last_auth;
extern cklock_t last_lock; extern cklock_t last_lock;
@ -341,6 +350,8 @@ enum cmd_values {
CMD_PPLNS, CMD_PPLNS,
CMD_PPLNS2, CMD_PPLNS2,
CMD_PAYOUTS, CMD_PAYOUTS,
CMD_MPAYOUTS,
CMD_SHIFTS,
CMD_USERSTATUS, CMD_USERSTATUS,
CMD_MARKS, CMD_MARKS,
CMD_END CMD_END
@ -1092,6 +1103,26 @@ typedef struct blocks {
char statsconfirmed[TXT_FLAG+1]; char statsconfirmed[TXT_FLAG+1];
HISTORYDATECONTROLFIELDS; HISTORYDATECONTROLFIELDS;
bool ignore; // Non DB field 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; } BLOCKS;
#define ALLOC_BLOCKS 100 #define ALLOC_BLOCKS 100
@ -1131,6 +1162,8 @@ extern const char *blocks_unknown;
extern K_TREE *blocks_root; extern K_TREE *blocks_root;
extern K_LIST *blocks_free; extern K_LIST *blocks_free;
extern K_STORE *blocks_store; extern K_STORE *blocks_store;
extern tv_t blocks_stats_time;
extern bool blocks_stats_rebuild;
// MININGPAYOUTS // MININGPAYOUTS
typedef struct miningpayouts { typedef struct miningpayouts {
@ -1190,6 +1223,10 @@ extern cklock_t process_pplns_lock;
#define PAYOUTS_PROCESSING 'P' #define PAYOUTS_PROCESSING 'P'
#define PAYOUTS_PROCESSING_STR "P" #define PAYOUTS_PROCESSING_STR "P"
#define PAYPROCESSING(_status) ((_status)[0] == PAYOUTS_PROCESSING) #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 // 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_height(char *coinbase1a, char *coinbase1b, WHERE_FFL_ARGS);
extern cmp_t cmp_workinfo_height(K_ITEM *a, K_ITEM *b); 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 *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, extern bool workinfo_age(PGconn *conn, int64_t workinfoid, char *poolinstance,
char *by, char *code, char *inet, tv_t *cd, char *by, char *code, char *inet, tv_t *cd,
tv_t *ss_first, tv_t *ss_last, int64_t *ss_count, 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) \ #define dsp_hash(_hash, _buf, _siz) \
_dsp_hash(_hash, _buf, _siz, WHERE_FFL_HERE) _dsp_hash(_hash, _buf, _siz, WHERE_FFL_HERE)
extern void _dsp_hash(char *hash, char *buf, size_t siz, WHERE_FFL_ARGS); 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 void dsp_blocks(K_ITEM *item, FILE *stream);
extern cmp_t cmp_blocks(K_ITEM *a, K_ITEM *b); extern cmp_t cmp_blocks(K_ITEM *a, K_ITEM *b);
extern K_ITEM *find_blocks(int32_t height, char *blockhash, K_TREE_CTX *ctx); extern K_ITEM *find_blocks(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 const char *blocks_confirmed(char *confirmed);
extern void zero_on_new_block(); extern void zero_on_new_block();
extern void set_block_share_counters(); 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 cmp_t cmp_miningpayouts(K_ITEM *a, K_ITEM *b);
extern K_ITEM *find_miningpayouts(int64_t payoutid, int64_t userid); extern K_ITEM *find_miningpayouts(int64_t payoutid, int64_t userid);
extern K_ITEM *first_miningpayouts(int64_t payoutid, K_TREE_CTX *ctx); extern K_ITEM *first_miningpayouts(int64_t payoutid, K_TREE_CTX *ctx);

611
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) __maybe_unused K_TREE *trf_root)
{ {
K_TREE_CTX ctx[1]; K_TREE_CTX ctx[1];
K_ITEM *b_item, *w_item; K_ITEM *b_item;
BLOCKS *blocks; BLOCKS *blocks;
char reply[1024] = ""; char reply[1024] = "";
char tmp[1024]; char tmp[1024];
char *buf; char *buf, *desc, desc_buf[64];
size_t len, off; size_t len, off;
int32_t height = -1; int32_t height = -1;
tv_t first_cd = {0,0}; tv_t first_cd = {0,0}, stats_tv = {0,0}, stats_tv2 = {0,0};
int rows, tot; int rows, srows, tot, seq;
bool has_stats;
LOGDEBUG("%s(): cmd '%s'", __func__, cmd); LOGDEBUG("%s(): cmd '%s'", __func__, cmd);
APPEND_REALLOC_INIT(buf, off, len); APPEND_REALLOC_INIT(buf, off, len);
APPEND_REALLOC(buf, off, len, "ok."); 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); K_RLOCK(blocks_free);
b_item = last_in_ktree(blocks_root, ctx); b_item = first_in_ktree(blocks_root, ctx);
tot = 0; tot = 0;
while (b_item) { while (b_item) {
DATA_BLOCKS(blocks, 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) if (blocks->confirmed[0] != BLOCKS_ORPHAN)
tot++; tot++;
} }
b_item = prev_in_ktree(ctx); b_item = next_in_ktree(ctx);
} }
seq = tot;
b_item = last_in_ktree(blocks_root, ctx); b_item = last_in_ktree(blocks_root, ctx);
while (b_item && rows < 42) { while (b_item && rows < 42) {
DATA_BLOCKS(blocks, b_item); 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) { if (height != blocks->height) {
height = blocks->height; height = blocks->height;
copy_tv(&first_cd, &(blocks->createdate)); copy_tv(&first_cd, &(blocks->createdate));
} }
if (CURRENT(&(blocks->expirydate))) { 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)); int_to_buf(blocks->height, reply, sizeof(reply));
snprintf(tmp, sizeof(tmp), "height:%d=%s%c", rows, reply, FLDSEP); snprintf(tmp, sizeof(tmp), "height:%d=%s%c", rows, reply, FLDSEP);
APPEND_REALLOC(buf, off, len, tmp); 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); snprintf(tmp, sizeof(tmp), "elapsed:%d=%s%c", rows, reply, FLDSEP);
APPEND_REALLOC(buf, off, len, tmp); APPEND_REALLOC(buf, off, len, tmp);
w_item = find_workinfo(blocks->workinfoid, NULL); if (has_stats) {
if (w_item) { snprintf(tmp, sizeof(tmp),
char wdiffbin[TXT_SML+1]; "netdiff:%d=%.8f%cdiffratio:%d=%.8f%c"
double wdiff; "cdf:%d=%.8f%cluck:%d=%.8f%c",
WORKINFO *workinfo; rows, blocks->netdiff, FLDSEP,
DATA_WORKINFO(workinfo, w_item); rows, blocks->blockdiffratio, FLDSEP,
hex2bin(wdiffbin, workinfo->bits, 4); rows, blocks->blockcdf, FLDSEP,
wdiff = diff_from_nbits(wdiffbin); rows, blocks->blockluck, FLDSEP);
snprintf(tmp, sizeof(tmp), APPEND_REALLOC(buf, off, len, tmp);
"netdiff:%d=%.1f%c",
rows, wdiff, FLDSEP);
APPEND_REALLOC(buf, off, len, tmp);
} else { } else {
snprintf(tmp, sizeof(tmp), 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); 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); 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); 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), snprintf(tmp, sizeof(tmp),
"tot=%d%crows=%d%cflds=%s%c", "rows=%d%cflds=%s%c",
tot, FLDSEP,
rows, FLDSEP, rows, FLDSEP,
"height,blockhash,nonce,reward,workername,firstcreatedate," "seq,height,blockhash,nonce,reward,workername,firstcreatedate,"
"createdate,status,diffacc,diffinv,shareacc,shareinv,elapsed," "createdate,status,diffacc,diffinv,shareacc,shareinv,elapsed,"
"netdiff", FLDSEP); "netdiff,diffratio,cdf,luck", FLDSEP);
APPEND_REALLOC(buf, off, len, tmp); 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); APPEND_REALLOC(buf, off, len, tmp);
LOGDEBUG("%s.ok.%d_blocks", id, rows); 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 tv_t *notcd,
__maybe_unused K_TREE *trf_root) __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]; K_TREE_CTX ctx[1];
PAYMENTS *payments, curr; K_STORE *pay_store;
PAYMENTS *payments, *last_payments = NULL;
PAYOUTS *payouts;
USERS *users; USERS *users;
char reply[1024] = ""; char reply[1024] = "";
char tmp[1024]; char tmp[1024];
@ -1102,6 +1192,7 @@ static char *cmd_payments(__maybe_unused PGconn *conn, char *cmd, char *id,
char *buf; char *buf;
size_t len, off; size_t len, off;
int rows; int rows;
bool pok;
LOGDEBUG("%s(): cmd '%s'", __func__, cmd); 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"); return strdup("bad");
DATA_USERS(users, u_item); DATA_USERS(users, u_item);
bzero(&curr, sizeof(curr));
APPEND_REALLOC_INIT(buf, off, len); APPEND_REALLOC_INIT(buf, off, len);
APPEND_REALLOC(buf, off, len, "ok."); APPEND_REALLOC(buf, off, len, "ok.");
rows = 0; rows = 0;
pay_store = k_new_store(payments_free);
K_RLOCK(payments_free); K_WLOCK(payments_free);
p_item = find_first_payments(users->userid, ctx); p_item = find_first_payments(users->userid, ctx);
DATA_PAYMENTS_NULL(payments, p_item); DATA_PAYMENTS_NULL(payments, p_item);
/* TODO: allow to see details of a single payoutid /* TODO: allow to see details of a single payoutid
* if it has multiple items (percent payout user) */ * if it has multiple items (percent payout user) */
while (p_item && payments->userid == users->userid) { while (p_item && payments->userid == users->userid) {
if (CURRENT(&(payments->expirydate))) { if (CURRENT(&(payments->expirydate))) {
if (curr.payoutid && curr.payoutid != payments->payoutid) { if (!last_payments || payments->payoutid != last_payments->payoutid) {
tv_to_buf(&(curr.paydate), reply, sizeof(reply)); p2_item = k_unlink_head(payments_free);
snprintf(tmp, sizeof(tmp), "paydate:%d=%s%c", rows, reply, FLDSEP); DATA_PAYMENTS_NULL(last_payments, p2_item);
APPEND_REALLOC(buf, off, len, tmp); memcpy(last_payments, payments, sizeof(*last_payments));
k_add_tail(pay_store, p2_item);
str_to_buf(curr.payaddress, reply, sizeof(reply)); } else {
snprintf(tmp, sizeof(tmp), "payaddress:%d=%s%c", rows, reply, FLDSEP); STRNCPY(last_payments->payaddress, "*Multiple");
APPEND_REALLOC(buf, off, len, tmp); last_payments->amount += payments->amount;
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 (!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); p_item = next_in_ktree(ctx);
DATA_PAYMENTS_NULL(payments, p_item); DATA_PAYMENTS_NULL(payments, p_item);
} }
K_RUNLOCK(payments_free); K_WUNLOCK(payments_free);
if (curr.payoutid) {
tv_to_buf(&(curr.paydate), reply, sizeof(reply)); p_item = pay_store->head;
snprintf(tmp, sizeof(tmp), "paydate:%d=%s%c", rows, reply, FLDSEP); while (p_item) {
APPEND_REALLOC(buf, off, len, tmp); 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)); int_to_buf(payouts->height, reply, sizeof(reply));
snprintf(tmp, sizeof(tmp), "payaddress:%d=%s%c", rows, reply, FLDSEP); snprintf(tmp, sizeof(tmp), "height:%d=%s%c", rows, reply, FLDSEP);
APPEND_REALLOC(buf, off, len, tmp); APPEND_REALLOC(buf, off, len, tmp);
bigint_to_buf(curr.amount, reply, sizeof(reply)); str_to_buf(payments->payaddress, reply, sizeof(reply));
snprintf(tmp, sizeof(tmp), "amount:%d=%s%c", rows, reply, FLDSEP); snprintf(tmp, sizeof(tmp), "payaddress:%d=%s%c", rows, reply, FLDSEP);
APPEND_REALLOC(buf, off, len, tmp); 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", snprintf(tmp, sizeof(tmp), "rows=%d%cflds=%s%c",
rows, FLDSEP, rows, FLDSEP,
"paydate,payaddress,amount", FLDSEP); "payoutid,height,payaddress,amount,paydate", FLDSEP);
APPEND_REALLOC(buf, off, len, tmp); APPEND_REALLOC(buf, off, len, tmp);
snprintf(tmp, sizeof(tmp), "arn=%s%carp=%s", "Payments", FLDSEP, ""); snprintf(tmp, sizeof(tmp), "arn=%s%carp=%s", "Payments", FLDSEP, "");
@ -1945,8 +2042,13 @@ wiconf:
return strdup("failed.DATA"); return strdup("failed.DATA");
} else { } else {
// Only flag a successful share // Only flag a successful share
int32_t errn;
TXT_TO_INT("errn", transfer_data(i_errn), errn);
ck_wlock(&last_lock); ck_wlock(&last_lock);
setnow(&last_share); if (errn == SE_NONE)
setnow(&last_share);
else
setnow(&last_share_inv);
ck_wunlock(&last_lock); ck_wunlock(&last_lock);
} }
LOGDEBUG("%s.ok.added %s", id, transfer_data(i_nonce)); 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); ftv_to_buf(&last_share, reply, siz);
snprintf(tmp, sizeof(tmp), "lastsh=%s%c", reply, FLDSEP); snprintf(tmp, sizeof(tmp), "lastsh=%s%c", reply, FLDSEP);
APPEND_REALLOC(buf, off, len, tmp); 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); ftv_to_buf(&last_auth, reply, siz);
ck_wunlock(&last_lock); ck_wunlock(&last_lock);
snprintf(tmp, sizeof(tmp), "lastau=%s%c", reply, FLDSEP); 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(payouts2, p2_item);
DATA_PAYOUTS(old_payouts2, old_p2_item); DATA_PAYOUTS(old_payouts2, old_p2_item);
snprintf(msg, sizeof(msg), 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", "%"PRId32"/%s",
payoutid, old_payouts2->status, payouts2->status, payoutid, old_payouts2->status, payouts2->status,
payouts2->height, payouts2->blockhash); 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); LOGWARNING("%s.%s", id, reply);
return strdup(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, static char *cmd_dsp(__maybe_unused PGconn *conn, __maybe_unused char *cmd,
char *id, __maybe_unused tv_t *now, char *id, __maybe_unused tv_t *now,
__maybe_unused char *by, __maybe_unused char *code, __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_PPLNS, "pplns", false, false, cmd_pplns, ACCESS_SYSTEM ACCESS_WEB },
{ CMD_PPLNS2, "pplns2", false, false, cmd_pplns2, 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_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_USERSTATUS,"userstatus", false, false, cmd_userstatus, ACCESS_SYSTEM ACCESS_WEB },
{ CMD_MARKS, "marks", false, false, cmd_marks, ACCESS_SYSTEM }, { CMD_MARKS, "marks", false, false, cmd_marks, ACCESS_SYSTEM },
{ CMD_END, NULL, false, false, NULL, NULL } { CMD_END, NULL, false, false, NULL, NULL }

174
src/ckdb_data.c

@ -1569,6 +1569,34 @@ K_ITEM *find_workinfo(int64_t workinfoid, K_TREE_CTX *ctx)
return item; 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, bool workinfo_age(PGconn *conn, int64_t workinfoid, char *poolinstance,
char *by, char *code, char *inet, tv_t *cd, char *by, char *code, char *inet, tv_t *cd,
tv_t *ss_first, tv_t *ss_last, int64_t *ss_count, 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); 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) void dsp_blocks(K_ITEM *item, FILE *stream)
{ {
char createdate_buf[DATE_BUFSIZ], expirydate_buf[DATE_BUFSIZ]; 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__); 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 /* order by payoutid asc,userid asc,expirydate asc
* i.e. only one payout amount per block per user */ * i.e. only one payout amount per block per user */
cmp_t cmp_miningpayouts(K_ITEM *a, K_ITEM *b) cmp_t cmp_miningpayouts(K_ITEM *a, K_ITEM *b)

21
src/ckdb_dbio.c

@ -3996,9 +3996,13 @@ unparam:
blocks_root = remove_from_ktree(blocks_root, old_b_item, cmp_blocks); blocks_root = remove_from_ktree(blocks_root, old_b_item, cmp_blocks);
copy_tv(&(oldblocks->expirydate), cd); copy_tv(&(oldblocks->expirydate), cd);
blocks_root = add_to_ktree(blocks_root, old_b_item, cmp_blocks); 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); blocks_root = add_to_ktree(blocks_root, b_item, cmp_blocks);
k_add_head(blocks_store, b_item); k_add_head(blocks_store, b_item);
blocks_stats_rebuild = true;
} }
K_WUNLOCK(blocks_free); 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; K_ITEM *b_item, *u_item, *old_b_item;
char cd_buf[DATE_BUFSIZ]; char cd_buf[DATE_BUFSIZ];
char hash_dsp[16+1]; char hash_dsp[16+1];
double hash_diff;
BLOCKS *row, *oldblocks; BLOCKS *row, *oldblocks;
USERS *users; USERS *users;
char *upd, *ins; char *upd, *ins;
@ -4038,6 +4043,7 @@ bool blocks_add(PGconn *conn, char *height, char *blockhash,
STRNCPY(row->blockhash, blockhash); STRNCPY(row->blockhash, blockhash);
dsp_hash(blockhash, hash_dsp, sizeof(hash_dsp)); dsp_hash(blockhash, hash_dsp, sizeof(hash_dsp));
hash_diff = blockhash_diff(blockhash);
K_RLOCK(blocks_free); K_RLOCK(blocks_free);
old_b_item = find_blocks(row->height, blockhash, NULL); 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); blocks_root = remove_from_ktree(blocks_root, old_b_item, cmp_blocks);
copy_tv(&(oldblocks->expirydate), cd); copy_tv(&(oldblocks->expirydate), cd);
blocks_root = add_to_ktree(blocks_root, old_b_item, cmp_blocks); 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); blocks_root = add_to_ktree(blocks_root, b_item, cmp_blocks);
k_add_head(blocks_store, b_item); k_add_head(blocks_store, b_item);
blocks_stats_rebuild = true;
} }
K_WUNLOCK(blocks_free); K_WUNLOCK(blocks_free);
if (ok) { if (ok) {
char pct[16] = "?"; char pct[16] = "?";
char est[16] = ""; char est[16] = "";
char diff[16] = "";
K_ITEM *w_item; K_ITEM *w_item;
char tmp[256]; char tmp[256];
bool blk; bool blk;
suffix_string(hash_diff, diff, sizeof(diff)-1, 0);
switch (confirmed[0]) { switch (confirmed[0]) {
case BLOCKS_NEW: case BLOCKS_NEW:
blk = true; blk = true;
@ -4356,10 +4369,10 @@ flail:
break; break;
} }
LOGWARNING("%s(): %sStatus: %s, Block: %s/...%s%s", LOGWARNING("%s(): %sStatus: %s, Block: %s/...%s Diff %s%s",
__func__, blk ? "BLOCK! " : "", __func__, blk ? "BLOCK! " : "",
blocks_confirmed(confirmed), blocks_confirmed(confirmed),
height, hash_dsp, tmp); height, hash_dsp, diff, tmp);
} }
return ok; return ok;

21
src/connector.c

@ -214,12 +214,11 @@ static int drop_client(cdata_t *cdata, client_instance_t *client)
int64_t client_id = 0; int64_t client_id = 0;
int fd; int fd;
ck_ilock(&cdata->lock); ck_wlock(&cdata->lock);
fd = client->fd; fd = client->fd;
if (fd != -1) { if (fd != -1) {
client_id = client->id; client_id = client->id;
ck_ulock(&cdata->lock);
Close(client->fd); Close(client->fd);
HASH_DEL(cdata->clients, client); HASH_DEL(cdata->clients, client);
DL_APPEND(cdata->dead_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. */ * epoll list. */
__dec_instance_ref(client); __dec_instance_ref(client);
cdata->dead_generated++; cdata->dead_generated++;
ck_dwilock(&cdata->lock);
} }
ck_uilock(&cdata->lock); ck_wunlock(&cdata->lock);
if (fd > -1) if (fd > -1)
LOGINFO("Connector dropped client %"PRId64" fd %d", client_id, fd); 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; return;
} }
ck_ilock(&cdata->lock); ck_wlock(&cdata->lock);
HASH_FIND_I64(cdata->clients, &id, client); HASH_FIND_I64(cdata->clients, &id, client);
if (likely(client)) { if (likely(client)) {
fd = client->fd; fd = client->fd;
/* Grab a reference to this client until the sender_send has /* Grab a reference to this client until the sender_send has
* completed processing. */ * completed processing. */
ck_ulock(&cdata->lock);
__inc_instance_ref(client); __inc_instance_ref(client);
ck_dwilock(&cdata->lock);
} }
ck_uilock(&cdata->lock); ck_wunlock(&cdata->lock);
if (unlikely(fd == -1)) { if (unlikely(fd == -1)) {
ckpool_t *ckp = cdata->ckp; 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; client_instance_t *client;
ck_ilock(&cdata->lock); ck_wlock(&cdata->lock);
HASH_FIND_I64(cdata->clients, &id, client); HASH_FIND_I64(cdata->clients, &id, client);
if (client) { if (client)
ck_ulock(&cdata->lock);
__inc_instance_ref(client); __inc_instance_ref(client);
ck_dwilock(&cdata->lock); ck_wunlock(&cdata->lock);
}
ck_uilock(&cdata->lock);
return client; return client;
} }

26
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); _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 */ /* Write lock variant of cklock */
void _ck_wlock(cklock_t *lock, const char *file, const char *func, const int line) 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); _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) void _ck_runlock(cklock_t *lock, const char *file, const char *func, const int line)
{ {
_rd_unlock(&lock->rwlock, file, func, line); _rd_unlock(&lock->rwlock, file, func, line);

4
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 cklock_init(_lock) _cklock_init(_lock, __FILE__, __func__, __LINE__)
#define ck_rlock(_lock) _ck_rlock(_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_wlock(_lock) _ck_wlock(_lock, __FILE__, __func__, __LINE__)
#define ck_dwlock(_lock) _ck_dwlock(_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_dlock(_lock) _ck_dlock(_lock, __FILE__, __func__, __LINE__)
#define ck_runlock(_lock) _ck_runlock(_lock, __FILE__, __func__, __LINE__) #define ck_runlock(_lock) _ck_runlock(_lock, __FILE__, __func__, __LINE__)
#define ck_wunlock(_lock) _ck_wunlock(_lock, __FILE__, __func__, __LINE__) #define ck_wunlock(_lock) _ck_wunlock(_lock, __FILE__, __func__, __LINE__)

411
src/stratifier.c

@ -136,7 +136,6 @@ struct json_params {
json_t *params; json_t *params;
json_t *id_val; json_t *id_val;
int64_t client_id; int64_t client_id;
char address[INET6_ADDRSTRLEN];
}; };
typedef struct json_params json_params_t; typedef struct json_params json_params_t;
@ -145,7 +144,6 @@ typedef struct json_params json_params_t;
struct smsg { struct smsg {
json_t *json_msg; json_t *json_msg;
int64_t client_id; int64_t client_id;
char address[INET6_ADDRSTRLEN];
}; };
typedef struct smsg smsg_t; typedef struct smsg smsg_t;
@ -187,6 +185,7 @@ struct user_instance {
time_t auth_time; time_t auth_time;
time_t failed_authtime; /* Last time this username failed to authorise */ time_t failed_authtime; /* Last time this username failed to authorise */
int auth_backoff; /* How long to reject any auth attempts since last failure */ 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 */ /* Combined data from workers with the same workername */
@ -370,6 +369,7 @@ struct stratifier_data {
* is sorted by enonce1_64. */ * is sorted by enonce1_64. */
stratum_instance_t *stratum_instances; stratum_instance_t *stratum_instances;
stratum_instance_t *disconnected_instances; stratum_instance_t *disconnected_instances;
stratum_instance_t *recycled_instances;
int stratum_generated; int stratum_generated;
int disconnected_generated; int disconnected_generated;
@ -967,7 +967,9 @@ static void update_base(ckpool_t *ckp, const int prio)
create_pthread(pth, do_update, ur); 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) { if (client->proxy) {
client->proxy->bound_clients--; client->proxy->bound_clients--;
@ -975,14 +977,31 @@ static void __kill_instance(stratum_instance_t *client)
} }
free(client->workername); free(client->workername);
free(client->useragent); 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) static void __del_disconnected(sdata_t *sdata, stratum_instance_t *client)
{ {
HASH_DEL(sdata->disconnected_instances, client); HASH_DEL(sdata->disconnected_instances, client);
sdata->stats.disconnected--; 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 /* 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) static void __del_client(sdata_t *sdata, stratum_instance_t *client, user_instance_t *user)
{ {
HASH_DEL(sdata->stratum_instances, client); HASH_DEL(sdata->stratum_instances, client);
if (user) if (user) {
DL_DELETE(user->clients, client); DL_DELETE(user->clients, client);
__dec_worker(sdata, user);
}
} }
static void connector_drop_client(ckpool_t *ckp, const int64_t id) 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) { if (!client->ref) {
__del_client(sdata, client, client->user_instance); __del_client(sdata, client, client->user_instance);
__kill_instance(client); __kill_instance(sdata, client);
} else } else
client->dropped = true; client->dropped = true;
kills++; kills++;
@ -1625,19 +1646,15 @@ static stratum_instance_t *ref_instance_by_id(sdata_t *sdata, const int64_t id)
{ {
stratum_instance_t *client; stratum_instance_t *client;
ck_ilock(&sdata->instance_lock); ck_wlock(&sdata->instance_lock);
client = __instance_by_id(sdata, id); client = __instance_by_id(sdata, id);
if (client) { if (client) {
if (unlikely(client->dropped)) if (unlikely(client->dropped))
client = NULL; client = NULL;
else { else
/* Upgrade to write lock to modify client refcount */
ck_ulock(&sdata->instance_lock);
__inc_instance_ref(client); __inc_instance_ref(client);
ck_dwilock(&sdata->instance_lock);
}
} }
ck_uilock(&sdata->instance_lock); ck_wunlock(&sdata->instance_lock);
return client; return client;
} }
@ -1659,91 +1676,99 @@ out:
} }
/* Ret = 1 is disconnected, 2 is killed, 3 is workerless killed */ /* 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; stratum_instance_t *old_client = NULL;
int ret;
__del_client(sdata, client, user); __del_client(sdata, client, user);
HASH_FIND(hh, sdata->disconnected_instances, &client->enonce1_64, sizeof(uint64_t), old_client); 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 */ /* Only keep around one copy of the old client in server mode */
if (!client->ckp->proxy && !old_client && client->enonce1_64 && client->authorised) { 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); HASH_ADD(hh, sdata->disconnected_instances, enonce1_64, sizeof(uint64_t), client);
sdata->stats.disconnected++; sdata->stats.disconnected++;
sdata->disconnected_generated++; sdata->disconnected_generated++;
client->disconnected_time = time(NULL); client->disconnected_time = time(NULL);
} else { } else {
if (client->workername) if (client->workername) {
ret = 2; if (user) {
else ASPRINTF(msg, "Client %"PRId64" %s %suser %s worker %s dropped %s",
ret = 3; client->id, client->address, user->throttled ? "throttled " : "",
__kill_instance(client); user->username, client->workername, lazily ? "lazily" : "");
} } else {
return ret; ASPRINTF(msg, "Client %"PRId64" %s no user worker %s dropped %s",
} client->id, client->address, client->workername,
lazily ? "lazily" : "");
static void client_drop_message(const int64_t client_id, const int dropped, const bool lazily) }
{ } else {
switch(dropped) { ASPRINTF(msg, "Workerless client %"PRId64" %s dropped %s",
case 0: client->id, client->address, lazily ? "lazily" : "");
break; }
case 1: __kill_instance(sdata, client);
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;
} }
} }
static void dec_worker(ckpool_t *ckp, user_instance_t *instance);
/* Decrease the reference count of instance. */ /* Decrease the reference count of instance. */
static void _dec_instance_ref(sdata_t *sdata, stratum_instance_t *client, const char *file, static void _dec_instance_ref(sdata_t *sdata, stratum_instance_t *client, const char *file,
const char *func, const int line) const char *func, const int line)
{ {
user_instance_t *user = client->user_instance; user_instance_t *user = client->user_instance;
int64_t client_id = client->id; char_entry_t *entries = NULL;
ckpool_t *ckp = client->ckp; bool dropped = false;
int dropped = 0, ref; char *msg;
int ref;
ck_wlock(&sdata->instance_lock); ck_wlock(&sdata->instance_lock);
ref = --client->ref; ref = --client->ref;
/* See if there are any instances that were dropped that could not be /* See if there are any instances that were dropped that could not be
* moved due to holding a reference and drop them now. */ * moved due to holding a reference and drop them now. */
if (unlikely(client->dropped && !ref)) if (unlikely(client->dropped && !ref)) {
dropped = __drop_client(sdata, client, user); dropped = true;
__drop_client(sdata, client, user, true, &msg);
add_msg_entry(&entries, &msg);
}
ck_wunlock(&sdata->instance_lock); ck_wunlock(&sdata->instance_lock);
client_drop_message(client_id, dropped, true); notice_msg_entries(&entries);
/* This should never happen */ /* This should never happen */
if (unlikely(ref < 0)) if (unlikely(ref < 0))
LOGERR("Instance ref count dropped below zero from %s %s:%d", file, func, line); LOGERR("Instance ref count dropped below zero from %s %s:%d", file, func, line);
if (dropped) { if (dropped)
if (user) reap_proxies(sdata->ckp, sdata);
dec_worker(ckp, user);
reap_proxies(ckp, sdata);
}
} }
#define dec_instance_ref(sdata, instance) _dec_instance_ref(sdata, instance, __FILE__, __func__, __LINE__) #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 */ /* Enter with write instance_lock held */
static stratum_instance_t *__stratum_add_instance(ckpool_t *ckp, const int64_t id, 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; sdata_t *sdata = ckp->data;
client = __recruit_stratum_instance(sdata);
client->start_time = time(NULL); client->start_time = time(NULL);
sdata->stratum_generated++;
client->id = id; client->id = id;
strcpy(client->address, address);
client->server = server; client->server = server;
client->diff = client->old_diff = ckp->startdiff; client->diff = client->old_diff = ckp->startdiff;
client->ckp = ckp; 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 */ /* Number is in BE but we don't swap either of them */
hex2bin(&enonce1_64, sessionid, slen); hex2bin(&enonce1_64, sessionid, slen);
ck_ilock(&sdata->instance_lock); ck_wlock(&sdata->instance_lock);
HASH_ITER(hh, sdata->stratum_instances, client, tmp) { HASH_ITER(hh, sdata->stratum_instances, client, tmp) {
if (client->id == id) if (client->id == id)
continue; 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 /* Delete the entry once we are going to use it since there
* will be a new instance with the enonce1_64 */ * will be a new instance with the enonce1_64 */
old_id = client->id; old_id = client->id;
/* Upgrade to write lock to disconnect */
ck_ulock(&sdata->instance_lock);
__del_disconnected(sdata, client); __del_disconnected(sdata, client);
ck_dwilock(&sdata->instance_lock);
ret = enonce1_64; ret = enonce1_64;
} }
out_unlock: out_unlock:
ck_uilock(&sdata->instance_lock); ck_wunlock(&sdata->instance_lock);
out: out:
if (ret) if (ret)
LOGNOTICE("Reconnecting old instance %"PRId64" to instance %"PRId64, old_id, id); 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); 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) static void drop_client(ckpool_t *ckp, sdata_t *sdata, const int64_t id)
{ {
stratum_instance_t *client, *tmp; stratum_instance_t *client, *tmp;
char_entry_t *entries = NULL;
user_instance_t *user = NULL; user_instance_t *user = NULL;
int dropped = 0, aged = 0;
time_t now_t = time(NULL); time_t now_t = time(NULL);
int aged = 0;
char *msg;
LOGINFO("Stratifier asked to drop client %"PRId64, id); LOGINFO("Stratifier asked to drop client %"PRId64, id);
ck_ilock(&sdata->instance_lock); ck_wlock(&sdata->instance_lock);
client = __instance_by_id(sdata, id); client = __instance_by_id(sdata, id);
/* Upgrade to write lock */ if (client && !client->dropped) {
ck_ulock(&sdata->instance_lock); user = client->user_instance;
if (client) {
/* If the client is still holding a reference, don't drop them /* If the client is still holding a reference, don't drop them
* now but wait till the reference is dropped */ * now but wait till the reference is dropped */
if (!client->ref) { if (!client->ref) {
user = client->user_instance; __drop_client(sdata, client, user, false, &msg);
dropped = __drop_client(sdata, client, user); add_msg_entry(&entries, &msg);
} else } else
client->dropped = true; 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); ck_wunlock(&sdata->instance_lock);
client_drop_message(id, dropped, false); notice_msg_entries(&entries);
if (aged) if (aged)
LOGINFO("Aged %d disconnected instances to dead", aged); LOGINFO("Aged %d disconnected instances to dead", aged);
/* Decrease worker count outside of instance_lock to avoid recursive reap_proxies(ckp, sdata);
* locking */
if (user)
dec_worker(ckp, user);
if (aged || dropped)
reap_proxies(ckp, sdata);
} }
static void stratum_broadcast_message(sdata_t *sdata, const char *msg) 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 /* 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 * user or creates a new one. Needs to be entered with client holding a ref
* count. */ * count. */
@ -2834,7 +2894,6 @@ static user_instance_t *generate_user(ckpool_t *ckp, stratum_instance_t *client,
char *base_username = strdupa(workername), *username; char *base_username = strdupa(workername), *username;
bool new_user = false, new_worker = false; bool new_user = false, new_worker = false;
sdata_t *sdata = ckp->data; sdata_t *sdata = ckp->data;
worker_instance_t *tmp;
user_instance_t *user; user_instance_t *user;
int len; int len;
@ -2845,39 +2904,23 @@ static user_instance_t *generate_user(ckpool_t *ckp, stratum_instance_t *client,
if (unlikely(len > 127)) if (unlikely(len > 127))
username[127] = '\0'; username[127] = '\0';
ck_ilock(&sdata->instance_lock); ck_wlock(&sdata->instance_lock);
HASH_FIND_STR(sdata->user_instances, username, user); HASH_FIND_STR(sdata->user_instances, username, user);
/* Upgrade to write lock */
ck_ulock(&sdata->instance_lock);
if (!user) { if (!user) {
/* New user instance. Secondary user id will be NULL */ /* New user instance. Secondary user id will be NULL */
user = ckzalloc(sizeof(user_instance_t)); user = __create_user(sdata, username);
user->auth_backoff = 3; /* Set initial backoff to 3 seconds */
strcpy(user->username, username);
new_user = true; new_user = true;
user->id = sdata->user_instance_id++;
HASH_ADD_STR(sdata->user_instances, username, user);
} }
client->user_instance = user; client->user_instance = user;
DL_FOREACH(user->worker_instances, tmp) { client->worker_instance = __get_worker(user, workername);
if (!safecmp(workername, tmp->workername)) {
client->worker_instance = tmp;
break;
}
}
/* Create one worker instance for combined data from workers of the /* Create one worker instance for combined data from workers of the
* same name */ * same name */
if (!client->worker_instance) { if (!client->worker_instance) {
worker_instance_t *worker = ckzalloc(sizeof(worker_instance_t)); client->worker_instance = __create_worker(user, workername);
worker->workername = strdup(workername);
worker->user_instance = user;
DL_APPEND(user->worker_instances, worker);
new_worker = true; new_worker = true;
worker->start_time = time(NULL);
client->worker_instance = worker;
} }
DL_APPEND(user->clients, client); DL_APPEND(user->clients, client);
__inc_worker(sdata,user);
ck_wunlock(&sdata->instance_lock); ck_wunlock(&sdata->instance_lock);
if (CKP_STANDALONE(ckp) && new_user) 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; 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 /* 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 * 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 * 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)) if (unlikely(!val))
LOGWARNING("AUTH JSON decode failed(%d): %s", err_val.line, err_val.text); LOGWARNING("AUTH JSON decode failed(%d): %s", err_val.line, err_val.text);
else { else {
json_t *worker_array = json_object_get(val, "workers");
json_get_string(&secondaryuserid, val, "secondaryuserid"); json_get_string(&secondaryuserid, val, "secondaryuserid");
json_get_int(&worker->mindiff, val, "difficultydefault"); parse_worker_diffs(ckp, worker_array);
client->suggest_diff = worker->mindiff; client->suggest_diff = worker->mindiff;
if (!user->auth_time) if (!user->auth_time)
user->auth_time = time(NULL); 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. */ /* 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, static json_t *parse_authorise(stratum_instance_t *client, const json_t *params_val,
const char *address, int *errnum) json_t **err_val, int *errnum)
{ {
user_instance_t *user; user_instance_t *user;
ckpool_t *ckp = client->ckp; ckpool_t *ckp = client->ckp;
bool ret = false; bool ret = false;
const char *buf; const char *buf;
int arr_size; int arr_size;
ts_t now;
if (unlikely(!json_is_array(params_val))) { if (unlikely(!json_is_array(params_val))) {
*err_val = json_string("params not an array"); *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); user = generate_user(ckp, client, buf);
client->user_id = user->id; 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 /* NOTE workername is NULL prior to this so should not be used in code
* till after this point */ * till after this point */
client->workername = strdup(buf); 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); time_t now_t = time(NULL);
if (now_t < user->failed_authtime + user->auth_backoff) { if (now_t < user->failed_authtime + user->auth_backoff) {
LOGNOTICE("Client %"PRId64" worker %s rate limited due to failed auth attempts", if (!user->throttled) {
client->id, buf); 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; client->dropped = true;
goto out; 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)) if (CKP_STANDALONE(ckp))
ret = true; ret = true;
else { 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", LOGNOTICE("Authorised client %"PRId64" worker %s as user %s",
client->id, buf, user->username); client->id, buf, user->username);
} }
user->auth_backoff = 3; /* Reset auth backoff time */ user->auth_backoff = DEFAULT_AUTH_BACKOFF; /* Reset auth backoff time */
} else { } else {
LOGNOTICE("Client %"PRId64" worker %s failed to authorise as user %s", client->id, buf, LOGNOTICE("Client %"PRId64" %s worker %s failed to authorise as user %s",
user->username); client->id, client->address, buf,user->username);
user->failed_authtime = time(NULL); user->failed_authtime = time(NULL);
user->auth_backoff <<= 1; user->auth_backoff <<= 1;
/* Cap backoff time to 10 mins */ /* 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 static json_params_t
*create_json_params(const int64_t client_id, const json_t *method, const json_t *params, *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)); json_params_t *jp = ckalloc(sizeof(json_params_t));
@ -3836,47 +3902,25 @@ static json_params_t
jp->params = json_deep_copy(params); jp->params = json_deep_copy(params);
jp->id_val = json_deep_copy(id_val); jp->id_val = json_deep_copy(id_val);
jp->client_id = client_id; jp->client_id = client_id;
strcpy(jp->address, address);
return jp; return jp;
} }
static void set_worker_mindiff(ckpool_t *ckp, const char *workername, int mindiff) static void set_worker_mindiff(ckpool_t *ckp, const char *workername, int mindiff)
{ {
worker_instance_t *worker = NULL, *tmp;
char *username = strdupa(workername), *ignore; char *username = strdupa(workername), *ignore;
user_instance_t *user = NULL;
stratum_instance_t *client; stratum_instance_t *client;
sdata_t *sdata = ckp->data; sdata_t *sdata = ckp->data;
worker_instance_t *worker;
user_instance_t *user;
ignore = username; ignore = username;
strsep(&ignore, "._"); strsep(&ignore, "._");
/* Find the user first */ /* Find the user first */
ck_rlock(&sdata->instance_lock); user = get_user(sdata, username);
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;
}
/* Then find the matching worker user */ /* Then find the matching worker user */
ck_rlock(&sdata->instance_lock); worker = get_worker(sdata, user, workername);
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;
}
if (mindiff < 1) { if (mindiff < 1) {
LOGINFO("Worker %s requested invalid diff %d", worker->workername, mindiff); 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 */ /* Enter with client holding ref count */
static void parse_method(sdata_t *sdata, stratum_instance_t *client, const int64_t client_id, 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; 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 */ * most common messages will be shares so look for those first */
method = json_string_value(method_val); method = json_string_value(method_val);
if (likely(cmdmatch(method, "mining.submit") && client->authorised)) { 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); ckmsgq_add(sdata->sshareq, jp);
return; return;
@ -3959,7 +4003,8 @@ static void parse_method(sdata_t *sdata, stratum_instance_t *client, const int64
json_t *val, *result_val; json_t *val, *result_val;
if (unlikely(client->subscribed)) { 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; return;
} }
result_val = parse_subscribe(client, client_id, params_val); 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"))) { if (unlikely(cmdmatch(method, "mining.passthrough"))) {
char buf[256]; 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 /* We need to inform the connector process that this client
* is a passthrough and to manage its messages accordingly. * is a passthrough and to manage its messages accordingly.
* The client_id stays on the list but we won't send anything * 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 */ /* We should only accept subscribed requests from here on */
if (!client->subscribed) { 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); connector_drop_client(client->ckp, client_id);
return; return;
} }
@ -4003,10 +4048,11 @@ static void parse_method(sdata_t *sdata, stratum_instance_t *client, const int64
json_params_t *jp; json_params_t *jp;
if (unlikely(client->authorised)) { 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; 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); ckmsgq_add(sdata->sauthq, jp);
return; 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 /* Dropping unauthorised clients here also allows the
* stratifier process to restart since it will have lost all * stratifier process to restart since it will have lost all
* the stratum instance data. Clients will just reconnect. */ * 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); connector_drop_client(client->ckp, client_id);
return; 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 */ /* Covers both get_transactions and get_txnhashes */
if (cmdmatch(method, "mining.get")) { 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); ckmsgq_add(sdata->stxnq, jp);
return; return;
} }
/* Unhandled message here */ /* 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; 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; int64_t client_id = msg->client_id;
if (client->reject == 2 || (client->reconnect_request && time(NULL) - client->reconnect_request > 60)) { 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); connector_drop_client(ckp, client_id);
goto out; 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"); send_json_err(sdata, client_id, id_val, "-1:params not found");
goto out; 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: out:
free_smsg(msg); free_smsg(msg);
} }
@ -4094,6 +4141,7 @@ out:
static void srecv_process(ckpool_t *ckp, char *buf) static void srecv_process(ckpool_t *ckp, char *buf)
{ {
bool noid = false, dropped = false; bool noid = false, dropped = false;
char address[INET6_ADDRSTRLEN];
sdata_t *sdata = ckp->data; sdata_t *sdata = ckp->data;
stratum_instance_t *client; stratum_instance_t *client;
smsg_t *msg; smsg_t *msg;
@ -4125,7 +4173,7 @@ static void srecv_process(ckpool_t *ckp, char *buf)
free(msg); free(msg);
goto out; goto out;
} }
strcpy(msg->address, json_string_value(val)); strcpy(address, json_string_value(val));
json_object_clear(val); json_object_clear(val);
val = json_object_get(msg->json_msg, "server"); 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); json_object_clear(val);
/* Parse the message here */ /* Parse the message here */
ck_ilock(&sdata->instance_lock); ck_wlock(&sdata->instance_lock);
client = __instance_by_id(sdata, msg->client_id); 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 client_id instance doesn't exist yet, create one */
if (unlikely(!client)) { if (unlikely(!client)) {
if (likely(!__dropped_instance(sdata, msg->client_id))) { if (likely(!__dropped_instance(sdata, msg->client_id))) {
noid = true; noid = true;
client = __stratum_add_instance(ckp, msg->client_id, server); client = __stratum_add_instance(ckp, msg->client_id, address, server);
} else } else
dropped = true; dropped = true;
} else if (unlikely(client->dropped)) } 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; stratum_instance_t *client;
ck_ilock(&sdata->instance_lock); ck_wlock(&sdata->instance_lock);
client = __instance_by_id(sdata, id); client = __instance_by_id(sdata, id);
if (client) { if (client) {
if (client->dropped || client->authorising || client->authorised) if (client->dropped || client->authorising || client->authorised)
client = NULL; client = NULL;
else { else {
/* Upgrade to write lock to modify client data */
ck_ulock(&sdata->instance_lock);
__inc_instance_ref(client); __inc_instance_ref(client);
client->authorising = true; client->authorising = true;
ck_dwilock(&sdata->instance_lock);
} }
} }
ck_uilock(&sdata->instance_lock); ck_wunlock(&sdata->instance_lock);
return client; return client;
} }
@ -4274,7 +4317,7 @@ static void sauth_process(ckpool_t *ckp, json_params_t *jp)
goto out; 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)) { if (json_is_true(result_val)) {
char *buf; char *buf;

Loading…
Cancel
Save