Browse Source

Merge branch 'master' of bitbucket.org:ckolivas/ckpool

master
Con Kolivas 9 years ago
parent
commit
79758b2582
  1. 7
      README
  2. 3
      pool/address.php
  3. 36
      pool/base.php
  4. 42
      pool/email.php
  5. 9
      pool/inc.php
  6. 2
      pool/page.php
  7. 72
      pool/page_2fa.php
  8. 5
      pool/page_blocks.php
  9. 4
      pool/page_mpayouts.php
  10. 1
      pool/page_psperf.php
  11. 3
      pool/page_reg.php
  12. 10
      pool/page_shifts.php
  13. 7
      pool/page_usperf.php
  14. 3
      pool/worker.php
  15. 51
      src/ckdb.c
  16. 44
      src/ckdb.h
  17. 2
      src/ckdb_btc.c
  18. 404
      src/ckdb_cmd.c
  19. 7
      src/ckdb_crypt.c
  20. 412
      src/ckdb_data.c
  21. 131
      src/ckdb_dbio.c
  22. 10
      src/ktree.c

7
README

@ -80,12 +80,15 @@ sudo apt-get install build-essential
make
Building with ckdb requires installation of the postgresql development library.
Building with ckdb requires installation of the postgresql, gsl and ssl
development libraries.
sudo apt-get install build-essential libpq-dev
sudo apt-get install build-essential libpq-dev libgsl0ldbl libgsl0-dev
./configure
make
N.B. ckdb also requires libssl-dev but libpq-dev depends on it and installs it
Building from git also requires autoconf and automake

3
pool/address.php

@ -8,7 +8,8 @@ include_once('param.php');
function go()
{
$a = getparam('a', true);
if (substr($a, 0, 1) != '1')
$f = substr($a, 0, 1);
if ($f != '1' and $f != '3')
return;
if (strlen($a) < 24)
return;

36
pool/base.php

@ -28,6 +28,42 @@ function dq($str)
return str_replace('"', "\\\"", $str);
}
#
function daysago($val)
{
if ($val < -13)
return '';
if ($val < 60)
$des = number_format($val,0).'s';
else
{
$val = $val/60;
if ($val < 60)
$des = number_format($val,1).'min';
else
{
$val = $val/60;
if ($val < 24)
$des = number_format($val,1).'hrs';
else
{
$val = $val/24;
if ($val < 43)
$des = number_format($val,1).'days';
else
{
$val = $val/7;
if ($val < 10000)
$des = number_format($val,1).'wks';
else
$des = '';
}
}
}
}
return $des;
}
#
function howlongago($sec)
{
if ($sec < 60)

42
pool/email.php

@ -186,7 +186,7 @@ function twofaSetup($to, $whoip, $emailinfo)
return false;
$message = "2FA is ready to be tested.$eol";
$message = "It will be enabled once you test it.$eol$eol";
$message .= "It will be enabled once you test it.$eol$eol";
$message .= $ret;
return sendnoheader($to, "2FA is Ready to be Enabled", $message, $emailinfo);
@ -211,6 +211,46 @@ function twofaEnabled($to, $whoip, $emailinfo)
return sendnoheader($to, "2FA is Enabled", $message, $emailinfo);
}
#
function twofaCancel($to, $whoip, $emailinfo)
{
global $eol;
if (!isset($emailinfo['KWebURL']))
return false;
$web = $emailinfo['KWebURL'];
$ret = emailEnd('2fa change', $whoip, $emailinfo);
if ($ret === false)
return false;
$message = "2FA setup was cancelled on your account.$eol";
$message .= "You can set it up later if you want.$eol$eol";
$message .= $ret;
return sendnoheader($to, "2FA was Cancelled", $message, $emailinfo);
}
#
function twofaRemove($to, $whoip, $emailinfo)
{
global $eol;
if (!isset($emailinfo['KWebURL']))
return false;
$web = $emailinfo['KWebURL'];
$ret = emailEnd('2fa change', $whoip, $emailinfo);
if ($ret === false)
return false;
$message = "2FA was removed from your account.$eol";
$message .= "You can set it up again later if you want.$eol$eol";
$message .= $ret;
return sendnoheader($to, "2FA was Removed", $message, $emailinfo);
}
#
# getOpts required for email
# If they aren't all setup in the DB then email functions will return false
function emailOptList()

9
pool/inc.php

@ -6,6 +6,7 @@ function GBaseJS()
function sep(d){ans={};var ar=d.split('\\t');var l=ar.length;for(var i=0;i<l;i++){var e=ar[i].indexOf('=');ans[ar[i].substr(0,e)]=ar[i].substr(e+1)}return ans}
function dfmt(c,e){var d=new Date(e*1000);var DD,HH,MM;if(c['utc']){DD=d.getUTCDate();HH=d.getUTCHours();MM=d.getUTCMinutes()}else{DD=d.getDate();HH=d.getHours();MM=d.getMinutes()}var ans=''+DD+'/';if(HH<10){ans += '0'}ans += ''+HH+':';if(MM<10){ans += '0'}ans += ''+MM;return ans}
function ccb(c,n){var e=document.getElementById(n);c[n]=(e&&e.checked)}
function ccbd(c,n,d){var e=document.getElementById(n);if(e){c[n]=e.checked}else{c[n]=d}}
function gch(z,zm){if(z<0.5){return 0.5}if(z>(zm-0.5)){return(zm-0.5)}return z}
function gchx(c,x){return gch(x*c['xm']+c['xo'],c['ctx'].canvas.width)}
function gchy(c,y){return gch((1-y)*c['ym']+c['yo'],c['ctx'].canvas.height)}
@ -27,7 +28,7 @@ function gfl(c){c['ctx'].fill()}
function gst(c){c['ctx'].stroke()}
function gfi(c){gle(c);gst(c)}
function gbd(c){gbe(c,0,0);gln(c,1,0);gln(c,1,1);gln(c,0,1);gle(c);gfl(c);gst(c)}
function ggr(c,xs,ys,yt,xn,x0,x1,y0,y1,ar,nx,vx,vy,av,w,cols){gtso(c,xs,ys);gss(c,'black');glw(c,1.5);gbe(c,0,1);gln(c,0,0);gln(c,1,0);gst(c);glw(c,0.2);var hi=c['ctx'].measureText('M').width, wi=c['ctx'].measureText('1').width;for(var i=0;i<11;i++){var y=i/10.0;gbe(c,-0.01,y);gln(c,1,y);gst(c);var t=''+(((y1-y0)*i/10+y0).toFixed(2));gfz(c,0,y,-wi,0,t,'black','end')}gfz(c,gx0(c),0.55,wi,0,yt,'#0080ff','left');var m=Math.round(0.5+xn/20.0);for(var i=0;i<xn;i++){var n=ar[nx+i];var x=ar[vx+i];var xo=(x-x0)/(x1-x0);if(c['skey'] && (i<(xn-1)) && (i%m) == 0){gbe(c,xo,0);gln(c,xo,-0.01);gst(c);gfz(c,xo,0,0,-hi*1.5,n,'#00a050','center')}if(c['slines']){gbe(c,xo,0);gln(c,xo,1);gst(c)}}var xhr=3600+x1-(x1%3600);gss(c,'brown');if(c['tkey'] || c['tlines']){var hlv=c['hln'][c['hl']];hrs=c['hrs'][c['hr']]*3600/hlv;var l=0;tpos=2.7;if(c['over']){tpos=1.5}for(var i=xhr;i>=x0;i-=hrs){var n=dfmt(c,i);var xo=(i-x0)/(x1-x0);if(xo<=1 && c['tkey'] && ((l%hlv)==0)){gbe(c,xo,0);gln(c,xo,-0.02);gst(c);gfz(c,xo,0,0,-hi*tpos,n,'brown','center')}if(xo<=1 && c['tlines']){gbe(c,xo,0);gln(c,xo,1);gst(c)}l++}}glw(c,1);if(c['smooth']){for(var j=1;j<w.length;j++){var f=1;gss(c,cols[j-1]);var xa=0,ya=0,xb=0,yb=0;for(var i=0;i<xn;i++){var x=ar[vx+i];var y=ar[w[j]+vy+i];var xo=(x-x0)/(x1-x0);var yo=(y-y0)/(y1-y0);if(f==1){gbe(c,xo,yo);f=0;xb=xo;yb=yo}else{gct(c,(xa+xb)/2,(ya+yb)/2,xb,yb,(xb+xo)/2,(yb+yo)/2)}xa=xb;ya=yb;xb=xo;yb=yo}gct(c,(xa+xb)/2,(ya+yb)/2,xo,yo,xo,yo);gst(c)}}else{for(var j=1;j<w.length;j++){var f=1;gss(c,cols[j-1]);for(var i=0;i<xn;i++){var x=ar[vx+i];var y=ar[w[j]+vy+i];var xo=(x-x0)/(x1-x0);var yo=(y-y0)/(y1-y0);if(f==1){gbe(c,xo,yo);f=0}else{gln(c,xo,yo)}}gst(c)}}glw(c,1);for(var j=1;j<w.length;j++){if(av[j-1]>0){gss(c,'red');var y=(av[j-1]-y0)/(y1-y0);gbe(c,0,y);gln(c,1,y);gst(c);var t=''+av[j-1].toFixed(2)+'av';gfz(c,1,y,1,0,t,cols[j-1],'left')}}if(c['tkey']){var col,hrl=c['hrs'].length;for(var i=0;i<hrl;i++){if(c['hr']==i){col='red'}else{col='black'}gfz(c,1,0,c['xo']-c['pxe'],hi*(i+1)*2,''+c['hrs'][i],col,'end')}for(var i=0;i<c['hln'].length;i++){if(c['hl']==i){col='red'}else{col='black'}gfz(c,1,0,c['xo']-c['pxe'],hi*(i+2+hrl)*2,''+c['hrs'][i],col,'end')}}}
function ggr(c,xs,ys,yt,xn,x0,x1,y0,y1,ar,nx,vx,vy,av,w,cols){gtso(c,xs,ys);gss(c,'black');glw(c,1.5);gbe(c,0,1);gln(c,0,0);gln(c,1,0);gst(c);glw(c,0.2);var hi=c['ctx'].measureText('M').width, wi=c['ctx'].measureText('0').width;for(var i=0;i<11;i++){var y=i/10.0;gbe(c,-0.01,y);gln(c,1,y);gst(c);var t=''+(((y1-y0)*i/10+y0).toFixed(2));gfz(c,0,y,-wi,0,t,'black','end')}gfz(c,gx0(c),0.55,wi,0,yt,'#0080ff','left');var m=Math.round(0.5+xn/20.0);for(var i=0;i<xn;i++){var n=ar[nx+i];var x=ar[vx+i];var xo=(x-x0)/(x1-x0);if(c['skey'] && (i<(xn-1)) && (i%m) == 0){gbe(c,xo,0);gln(c,xo,-0.01);gst(c);gfz(c,xo,0,0,-hi*1.5,n,'#00a050','center')}if(c['slines']){gbe(c,xo,0);gln(c,xo,1);gst(c)}}var xhr=3600+x1-(x1%3600);gss(c,'brown');if(c['tkey'] || c['tlines']){var hlv=c['hln'][c['hl']];hrs=c['hrs'][c['hr']]*3600/hlv;var l=0;tpos=2.7;if(c['over']){tpos=1.5}for(var i=xhr;i>=x0;i-=hrs){var n=dfmt(c,i);var xo=(i-x0)/(x1-x0);if(xo<=1 && c['tkey'] && ((l%hlv)==0)){gbe(c,xo,0);gln(c,xo,-0.02);gst(c);gfz(c,xo,0,0,-hi*tpos,n,'brown','center')}if(xo<=1 && c['tlines']){gbe(c,xo,0);gln(c,xo,1);gst(c)}l++}}glw(c,1);if(c['smooth']){for(var j=1;j<w.length;j++){if(c['lin'+j]){var f=1;gss(c,cols[j-1]);var xa=0,ya=0,xb=0,yb=0;for(var i=0;i<xn;i++){var x=ar[vx+i];var y=ar[w[j]+vy+i];var xo=(x-x0)/(x1-x0);var yo=(y-y0)/(y1-y0);if(f==1){gbe(c,xo,yo);f=0;xb=xo;yb=yo}else{gct(c,(xa+xb)/2,(ya+yb)/2,xb,yb,(xb+xo)/2,(yb+yo)/2)}xa=xb;ya=yb;xb=xo;yb=yo}gct(c,(xa+xb)/2,(ya+yb)/2,xo,yo,xo,yo);gst(c)}}}else{for(var j=1;j<w.length;j++){if(c['lin'+j]){var f=1;gss(c,cols[j-1]);for(var i=0;i<xn;i++){var x=ar[vx+i];var y=ar[w[j]+vy+i];var xo=(x-x0)/(x1-x0);var yo=(y-y0)/(y1-y0);if(f==1){gbe(c,xo,yo);f=0}else{gln(c,xo,yo)}}gst(c)}}}glw(c,1);for(var j=1;j<w.length;j++){if(av[j-1]>0 && c['lin'+j]){gss(c,'red');var y=(av[j-1]-y0)/(y1-y0);gbe(c,0,y);gln(c,1,y);gst(c);var t=''+av[j-1].toFixed(2)+'av';gfz(c,1,y,1,0,t,cols[j-1],'left')}}if(c['tkey']){var col,hrl=c['hrs'].length;for(var i=0;i<hrl;i++){if(c['hr']==i){col='red'}else{col='black'}gfz(c,1,0,c['xo']-c['pxe'],hi*(i+1)*2,''+c['hrs'][i],col,'end')}for(var i=0;i<c['hln'].length;i++){if(c['hl']==i){col='red'}else{col='black'}gfz(c,1,0,c['xo']-c['pxe'],hi*(i+2+hrl)*2,''+c['hrs'][i],col,'end')}}}
function sn(i,shi){if(shi.indexOf(' Shift ')<0){return ''+(i%10)}else{return shi.replace(/.* ([a-z])[a-z]*$/,'$1')}}
function gc2(c){var div=document.getElementById('can0');while (div.firstChild){div.removeChild(div.firstChild)}c['can']=document.createElement('canvas');c['can'].id='can';c['xo']=0.0;c['yo']=0.0;c['ctx']=c['can'].getContext('2d');c['ctx'].canvas.width=c['xm']+1;c['ctx'].canvas.height=c['ym']+1;div.appendChild(c['can']);c['pxe']=Math.max(Math.round(c['xm']/250),1)}
function gc(c){c['wx']=window.innerWidth;c['wy']=window.innerHeight;c['xm']=Math.max(Math.round(c['wx']*0.9+0.5),400);c['ym']=Math.max(Math.round(c['wy']*0.8+0.5),400);if(c['ym']>c['xm']){c['ym']=c['xm']}gc2(c)}
@ -36,6 +37,7 @@ function opts(t,i){var e=document.getElementById(i);if(t.checked){e.style.visibi
function ghrs(c){c['hrs']=[1,2,3,4,6,8,12,24,48];c['hln']=[1,2,3,4,6]}
function ghg(c,dx){var tl=dx/(gchx(c,1)/50)/3600;for(var j=c['hrs'].length-1;j>=0;j--){if(tl<c['hrs'][j]){c['hr']=j}else{break}}if(tl<0.5){var tb=1/tl;for(var k=0;k<c['hln'].length;k++){if(c['hln'][k]<tb){c['hl']=k}else{break}}}else{c['hl']=1}}
function gopt(c,cbx){for(var i=0;i<cbx.length;i++){ccb(c,cbx[i])}}
function gsh(c,w){for(var i=1;i<w.length;i++){ccbd(c,'lin'+i,true)}}
function doinit(cbx,xon){for(var i=0;i<cbx.length;i++){var e=document.getElementById(cbx[i]);if(e){var n=gcn(cbx[i]);if(n==''){if(xon[cbx[i]]){e.checked=true}else{e.checked=false}}else{if(n=='1'){e.checked=true}else{e.checked=false}}}}}
";
return $g;
@ -97,6 +99,7 @@ span.hdr {font-weight:bold;text-decoration:underline;}
span.nn {font-weight:bold;color:red;}
span.warn {color:orange;font-weight:bold;}
span.urg {color:red;font-weight:bold;}
span.notice {color:blue;font-weight:bold;font-size:120%;}
span.err {color:red;font-weight:bold;font-size:120%;}
span.alert {color:red;font-weight:bold;font-size:250%;}
input.tiny {width:0px;height:0px;margin:0px;padding:0px;outline:none;border:0px;}
@ -104,16 +107,20 @@ input.tiny {width:0px;height:0px;margin:0px;padding:0px;outline:none;border:0px;
#n42 a {color:#fff;text-decoration:none;padding:6px;display:block;}
#n42 td {min-width:100px;float:left;vertical-align:top;padding:0px 2px;}
#n42 td.navboxr {float:right;}
#n42 td.nav0 {position:relative;}
#n42 td.nav {position:relative;}
#n42 td.ts {border-width:1px;border-color:#0022ee;border-style:solid none none none;}
#n42 div.sub {left:0px;z-index:42;position:absolute;visibility:hidden;}
#n42 td.nav0:hover {background:#0099ee;}
#n42 td.nav:hover {background:#0099ee;}
#n42 td.nav:hover div.sub {background:#0077ee;visibility:visible;}
h1 {margin-top:20px;float:middle;font-size:20px;}
.foot, .push {height:50px;}
.title {background-color:#909090;}
.even {background-color:#cccccc;}
.evenu td {background-color:#cccccc;border-bottom:2px solid red;}
.odd {background-color:#a8a8a8;}
.oddu td {background-color:#a8a8a8;border-bottom:2px solid red;}
.hid {display:none;}
.dl {text-align:left;padding:2px 8px;}
.dr {text-align:right;padding:2px 8px;}

2
pool/page.php

@ -490,7 +490,7 @@ function pgmenu($menus)
$first = false;
if ($submenu == $menu)
{
$ret .= "<tr><td class=nav onclick=''>".makeLink($item)."$menu</a>";
$ret .= "<tr><td class=nav0>".makeLink($item)."$menu</a>";
$ret .= '<div class=sub><table cellpadding=0 cellspacing=0 border=0 width=100%>';
continue;
}

72
pool/page_2fa.php

@ -11,7 +11,7 @@ function app_txt($ones)
return $app;
}
#
function set_2fa($data, $user, $tfa, $ans, $err)
function set_2fa($data, $user, $tfa, $ans, $err, $msg)
{
$draw = false;
@ -20,10 +20,12 @@ function set_2fa($data, $user, $tfa, $ans, $err)
if ($err !== null and $err != '')
$pg .= "<span class=err>$err<br><br></span>";
if ($msg !== null and $msg != '')
$pg .= "<span class=notice>$msg<br><br></span>";
$pg .= '<table cellpadding=20 cellspacing=0 border=1>';
$pg .= '<tr class=dc><td><center>';
$pg .= makeForm('2fa');
$pg .= '<table cellpadding=5 cellspacing=0 border=0>';
$pg .= '<tr class=dc><td>';
switch ($tfa)
@ -33,9 +35,10 @@ function set_2fa($data, $user, $tfa, $ans, $err)
$pg .= "You don't have Two Factor Authentication (2FA) setup yet<br><br>";
$pg .= 'To use 2FA you need an App on your phone/tablet<br>';
$pg .= app_txt('ones');
$pg .= makeForm('2fa');
$pg .= 'Click here to begin the setup process for 2FA: ';
$pg .= '<input type=submit name=Setup value=Setup>';
$pg .= '</td></tr>';
$pg .= '</form></td></tr>';
break;
case 'test':
$pg .= '<tr class=dc><td>';
@ -66,8 +69,9 @@ function set_2fa($data, $user, $tfa, $ans, $err)
$pg .= '<div id=can0><canvas id=can width=1 height=1>';
$pg .= 'A qrcode will show here if your browser supports html5/canvas';
$pg .= "</canvas></div><br>";
$pg .= makeForm('2fa');
$pg .= 'Then enter your App 2FA Value: <input name=Value value="" size=10> ';
$pg .= '<input type=submit name=Test value=Test></td></tr>';
$pg .= '<input type=submit name=Test value=Test></form></td></tr>';
$pg .= '<tr class=dl><td>';
$pg .= app_txt('2FA apps');
$pg .= '<span class=urg>N.B.</span> if you wish to setup 2FA on more than one device,<br>';
@ -79,20 +83,32 @@ function set_2fa($data, $user, $tfa, $ans, $err)
$pg .= 'so your should copy it and store it somewhere securely.<br>';
$pg .= 'For security reasons, the site will not show you an active <span class=urg>2FA Secret Key</span>.<br>';
$pg .= '</td></tr>';
$pg .= '<tr class=dl><td>';
$pg .= makeForm('2fa');
$pg .= '<br>If you wish to cancel setting up 2FA, click here: ';
$pg .= '<input type=submit name=Cancel value=Cancel></form></td></tr>';
break;
case 'ok':
$pg .= '<tr class=dc><td>';
$pg .= '2FA is enabled on your account.<br><br>';
$pg .= 'If you wish to replace your Secret Key with a new one:<br><br>';
$pg .= makeForm('2fa');
$pg .= 'Current 2FA Value: <input name=Value value="" size=10> ';
$pg .= '<input type=submit name=New value=New><span class=st1>*</span><br><br>';
$pg .= '<input type=submit name=New value=New><span class=st1>*</span>';
$pg .= '</form><br><br>';
$pg .= '<span class=st1>*</span>WARNING: replacing the Secret Key will disable 2FA<br>';
$pg .= 'until you successfully test the new key,<br>';
$pg .= 'thus getting a new key is effectively the same as disabling 2FA.<br><br>';
$pg .= '</td></tr>';
$pg .= '<tr class=dc><td>';
$pg .= makeForm('2fa');
$pg .= 'If you wish to remove 2FA from your account,<br>';
$pg .= 'enter your App 2FA Value: <input name=Value value="" size=10><br>';
$pg .= 'then click remove: <input type=submit name=Remove value=Remove>';
$pg .= '</form></td></tr>';
break;
}
$pg .= '</table></form>';
$pg .= '</table>';
$pg .= '</center></td></tr>';
$pg .= '<tr class=dl><td>';
@ -147,14 +163,23 @@ function set_2fa($data, $user, $tfa, $ans, $err)
#
function do2fa($data, $user)
{
$mailmode = '';
$err = '';
$msg = '';
$setup = getparam('Setup', false);
$testemail = false;
if ($setup === 'Setup')
{
// rand() included as part of the entropy
$ans = get2fa($user, 'setup', rand(1073741824,2147483647), 0);
$testemail = true;
$mailmode = 'Setup';
}
else
{
$can = getparam('Cancel', false);
if ($can === 'Cancel')
{
$ans = get2fa($user, 'untest', 0, 0);
$mailmode = 'Cancel';
}
else
{
@ -163,7 +188,7 @@ function do2fa($data, $user)
if ($test === 'Test' and $value !== null)
{
$ans = get2fa($user, 'test', 0, $value);
$testemail = true;
$mailmode = 'Test';
}
else
{
@ -171,12 +196,22 @@ function do2fa($data, $user)
if ($nw === 'New' and $value !== null)
{
$ans = get2fa($user, 'new', rand(1073741824,2147483647), $value);
$testemail = true;
$mailmode = 'New';
}
else
{
$rem = getparam('Remove', false);
if ($rem === 'Remove' and $value !== null)
{
$ans = get2fa($user, 'remove', 0, $value);
$mailmode = 'Remove';
}
else
$ans = get2fa($user, '', 0, 0);
}
}
}
}
if ($ans['STATUS'] != 'ok')
$err = 'DBERR';
else
@ -184,7 +219,7 @@ function do2fa($data, $user)
if (isset($ans['2fa_error']))
$err = $ans['2fa_error'];
if ($testemail and $err == '')
if ($mailmode != '' and $err == '')
{
$ans2 = userSettings($user);
if ($ans2['STATUS'] != 'ok')
@ -199,12 +234,16 @@ function do2fa($data, $user)
$err = 'An error occurred, check your details below';
else
{
if ($setup === 'Setup')
if ($mailmode === 'Setup')
twofaSetup($email, zeip(), $emailinfo);
else if ($test === 'Test')
else if ($mailmode === 'Test')
twofaEnabled($email, zeip(), $emailinfo);
else if ($nw === 'New')
else if ($mailmode === 'New')
twofaSetup($email, zeip(), $emailinfo);
else if ($mailmode === 'Cancel')
twofaCancel($email, zeip(), $emailinfo);
else if ($mailmode === 'Remove')
twofaRemove($email, zeip(), $emailinfo);
}
}
}
@ -213,7 +252,10 @@ function do2fa($data, $user)
$tfa = null;
else
$tfa = $ans['2fa_status'];
$pg = set_2fa($data, $user, $tfa, $ans, $err);
if (isset($ans['2fa_msg']))
$msg = $ans['2fa_msg'];
$pg = set_2fa($data, $user, $tfa, $ans, $err, $msg);
return $pg;
}
#

5
pool/page_blocks.php

@ -93,12 +93,15 @@ function doblocks($data, $user)
$pg .= "<table callpadding=0 cellspacing=0 border=0>\n";
$pg .= "<tr class=title>";
$pg .= "<td class=dl>Description</td>";
$pg .= "<td class=dr>Time</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";
$since = $data['info']['lastblock'];
$count = $ans['s_rows'];
for ($i = 0; $i < $count; $i++)
{
@ -108,6 +111,7 @@ function doblocks($data, $user)
$row = 'odd';
$desc = $ans['s_desc:'.$i];
$age = daysago($since - $ans['s_prevcreatedate:'.$i]);
$diff = number_format(100 * $ans['s_diffratio:'.$i], 2);
$mean = number_format(100 * $ans['s_diffmean:'.$i], 2);
@ -120,6 +124,7 @@ function doblocks($data, $user)
$pg .= "<tr class=$row>";
$pg .= "<td class=dl>$desc Blocks</td>";
$pg .= "<td class=dr>$age</td>";
$pg .= "<td class=dr>$diff%</td>";
$pg .= "<td class=dr>$mean%</td>";
$pg .= "<td class=dr$bg>$cdferldsp</td>";

4
pool/page_mpayouts.php

@ -14,6 +14,7 @@ function dompayouts($data, $user)
$pg .= "<table callpadding=0 cellspacing=0 border=0>\n";
$pg .= "<tr class=title>";
$pg .= "<td class=dr>Block</td>";
$pg .= "<td class=dr>Block UTC</td>";
$pg .= "<td class=dr>Miner Reward</td>";
$pg .= "<td class=dr>N Diff</td>";
$pg .= "<td class=dr>N Range</td>";
@ -36,6 +37,7 @@ function dompayouts($data, $user)
$pg .= "<tr class=$row>";
$pg .= '<td class=dr>'.$ans['height:'.$i].'</td>';
$pg .= '<td class=dr>'.gmdate('j/M H:i',$ans['blockcreatedate:'.$i]).'</td>';
$pg .= '<td class=dr>'.btcfmt($ans['minerreward:'.$i]).'</td>';
$diffused = $ans['diffused:'.$i];
$pg .= '<td class=dr>'.difffmt($diffused).'</td>';
@ -63,7 +65,7 @@ function dompayouts($data, $user)
$pg .= "<tr class=$row>";
$pg .= '<td class=dr>Total:</td>';
$pg .= '<td class=dl colspan=7></td>';
$pg .= '<td class=dl colspan=8></td>';
$pg .= '<td class=dr>'.btcfmt($totamt).'</td>';
$pg .= "</tr>\n";
}

1
pool/page_psperf.php

@ -6,6 +6,7 @@ $g = "function gdrw(c,d,cbx){gc(c);ghrs(c);gopt(c,cbx);
gfs(c,'white');gss(c,'#0000c0');glw(c,2);gbd(c);
var rows=d['rows'],ymin=-1,ymax=0,xmin=-1,xmax=0,tda=[];
var w=d['arp'].split(',');var cols=d['cols'].split(',');
gsh(c,w);
for(var j=1;j<w.length;j++){tda[j-1]=0}
for(var i=0;i<rows;i++){var s=parseFloat(d['start:'+i]);var e=parseFloat(d['end:'+i]);d['nx:'+i]=sn(i,d['shift:'+i]);if(xmin==-1||xmin>s){xmin=s}if(xmax<e){xmax=e}d['vx:'+i]=(s+e)/2.0;
for(var j=1;j<w.length;j++){var pre=w[j];var ths=0,nam=pre+'diffacc:'+i;if(d[nam]){var da=parseFloat(d[nam]);ths=(da/(e-s))*Math.pow(2,32)/Math.pow(10,12);tda[j-1]+=da}d[pre+'ths:'+i]=ths;if(ymin==-1||ymin>ths){ymin=ths}if(ths>ymax)ymax=ths}

3
pool/page_reg.php

@ -65,7 +65,8 @@ function doregres($data, $u)
<td class=dl><input type=submit name=Register value=Register></td></tr>
<tr><td colspan=2 class=dc><br><font size=-1><span class=st1>*</span>
All fields are required<br>Your Username can't be a BTC address</font></td></tr>
<tr><td colspan=2 class=dc><br>". passrequires() . "</td></tr>
<tr><td colspan=2 class=dc><font size=-1><br>Note: your username is upper/lowercase sensitive,<br>
and you must also have upper/lowercase correct on all your miners<br><br>" . passrequires() . "</font></td></tr>
</table>
</form>";

10
pool/page_shifts.php

@ -14,6 +14,7 @@ function doshifts($data, $user)
$pg .= "<td class=dr>Avg Hs</td>";
$pg .= "<td class=dr>Shares</td>";
$pg .= "<td class=dr>Avg Share</td>";
$pg .= "<td class=dr>Rewards</td>";
$pg .= "</tr>\n";
if (($ans['STATUS'] != 'ok') || !isset($ans['prefix_all']))
@ -26,10 +27,14 @@ function doshifts($data, $user)
$pg = '<h1>Last '.($count+1).' Shifts</h1>'.$pg;
for ($i = 0; $i < $count; $i++)
{
$u = '';
if (isset($ans['lastpayoutstart:'.$i])
&& $ans['lastpayoutstart:'.$i] != '')
$u = 'u';
if (($i % 2) == 0)
$row = 'even';
$row = "even$u";
else
$row = 'odd';
$row = "odd$u";
$pg .= "<tr class=$row>";
$shifname = $ans['shift:'.$i];
@ -66,6 +71,7 @@ function doshifts($data, $user)
else
$avgsh = 0;
$pg .= '<td class=dr>'.number_format($avgsh, 2).'</td>';
$pg .= '<td class=dr>'.$ans['rewards:'.$i].'</td>';
$pg .= "</tr>\n";
}
}

7
pool/page_usperf.php

@ -6,6 +6,7 @@ $g = "function gdrw(c,d,cbx){gc(c);ghrs(c);gopt(c,cbx);
gfs(c,'white');gss(c,'#0000c0');glw(c,2);gbd(c);
var rows=d['rows'],ymin=-1,ymax=0,xmin=-1,xmax=0,tda=[];
var w=d['arp'].split(',');var cols=d['cols'].split(',');
gsh(c,w);
for(var j=1;j<w.length;j++){tda[j-1]=0}
for(var i=0;i<rows;i++){var s=parseFloat(d['start:'+i]);var e=parseFloat(d['end:'+i]);d['nx:'+i]=sn(i,d['shift:'+i]);if(xmin==-1||xmin>s){xmin=s}if(xmax<e){xmax=e}d['vx:'+i]=(s+e)/2.0;
for(var j=1;j<w.length;j++){var pre=w[j];var ths=0,nam=pre+'diffacc:'+i;if(d[nam]){var da=parseFloat(d[nam]);ths=(da/(e-s))*Math.pow(2,32)/Math.pow(10,12);tda[j-1]+=da}d[pre+'ths:'+i]=ths;if(ymin==-1||ymin>ths){ymin=ths}if(ths>ymax)ymax=ths;document.getElementById('worker'+j).value=d[pre+'worker']}
@ -72,7 +73,8 @@ function dousperf($data, $user)
$pg .= '<form>';
$tt = "<ul class=tip><li>all = all workers</li><li>noname = worker with no workername</li>";
$tt .= "<li>or full workername without the username i.e. .worker or _worker</li></ul>";
$tt .= "<li>or full workername without the username i.e. .worker or _worker</li>";
$tt .= "<li>add a '*' on the end to match multiple workers e.g. .S3*</li></ul>";
$pg .= "<span class=q onclick='tip(\"wtip\",6000)'>?</span>";
$pg .= "<span class=tip0><span class=notip id=wtip>$tt</span></span>";
@ -82,7 +84,8 @@ function dousperf($data, $user)
foreach ($cols as $col)
{
$i++;
$pg .= " <span class=nb><font color=$col>Worker$i:</font>";
$pg .= " <span class=nb><font color=$col>Worker$i";
$pg .= "<input type=checkbox id=lin$i checked onclick='godrw(0)'>:</font>";
$pg .= "<input type=text size=10 id=worker$i$onch> </span>";
if ($i > 1)

3
pool/worker.php

@ -8,7 +8,8 @@ include_once('param.php');
function go()
{
$a = getparam('a', true);
if (substr($a, 0, 1) != '1')
$f = substr($a, 0, 1);
if ($f != '1' and $f != '3')
return;
if (preg_match('/^[a-zA-Z0-9]{24,}[\._]?[a-zA-Z0-9\._]*$/', $a) === false)
return;

51
src/ckdb.c

@ -36,30 +36,7 @@
* much larger DB tables so that ckdb is effectively ready for messages
* almost immediately
* The first ckpool message allows us to know where ckpool is up to
* in the CCLs and thus where to stop processing the CCLs to stay in
* sync with ckpool
* If ckpool isn't running, then the reload will complete at the end of
* the last CCL file, however if the 1st message arrives from ckpool while
* processing the CCLs, that will mark the point where to stop processing
* but can also produce a fatal error at the end of processing, reporting
* the ckpool message, if the message was not found in the CCL processing
* after the message was received
* This can be caused by two circumstances:
* 1) the disk had not yet written it to the CCL when ckdb read EOF and
* ckpool was started at about the same time as the reload completed.
* This can be seen if the message displayed in the fatal error IS NOT
* in ckdb's message logfile.
* A ckdb restart will resolve this
* 2) ckpool was started at the time of the end of the reload, but the
* message was written to disk and found in the CCL before it was
* processed in the message queue.
* This can be seen if the message displayed in the fatal error IS in
* ckdb's message logfile and means the messages after it in ckdb's
* message logfile have already been processed.
* Again, a ckdb restart will resolve this
* In both the above (very rare) cases, if ckdb was to continue running,
* it would break the synchronisation and could cause DB problems, so
* ckdb aborting and needing a complete restart resolves it
* in the CCLs - see reload_from() for how this is handled
* The users table, required for the authorise messages, is always updated
* immediately
*/
@ -301,6 +278,7 @@ static sem_t socketer_sem;
char *btc_server = "http://127.0.0.1:8330";
char *btc_auth;
int btc_timeout = 5;
cklock_t btc_lock;
char *by_default = "code";
char *inet_default = "127.0.0.1";
@ -442,6 +420,7 @@ K_STORE *miningpayouts_store;
// PAYOUTS
K_TREE *payouts_root;
K_TREE *payouts_id_root;
K_TREE *payouts_wid_root;
K_LIST *payouts_free;
K_STORE *payouts_store;
cklock_t process_pplns_lock;
@ -794,12 +773,11 @@ static bool getdata3()
if (!confirm_sharesummary) {
if (!(ok = paymentaddresses_fill(conn)) || everyone_die)
goto sukamudai;
/* FYI must be after blocks */
if (!(ok = payments_fill(conn)) || everyone_die)
goto sukamudai;
if (!(ok = miningpayouts_fill(conn)) || everyone_die)
goto sukamudai;
if (!(ok = payouts_fill(conn)) || everyone_die)
goto sukamudai;
}
if (!(ok = workinfo_fill(conn)) || everyone_die)
goto sukamudai;
@ -808,6 +786,11 @@ static bool getdata3()
/* must be after workinfo */
if (!(ok = workmarkers_fill(conn)) || everyone_die)
goto sukamudai;
if (!confirm_sharesummary) {
/* must be after workmarkers */
if (!(ok = payouts_fill(conn)) || everyone_die)
goto sukamudai;
}
if (!(ok = markersummary_fill(conn)) || everyone_die)
goto sukamudai;
if (!confirm_sharesummary && !everyone_die)
@ -1104,6 +1087,7 @@ static void alloc_storage()
payouts_store = k_new_store(payouts_free);
payouts_root = new_ktree();
payouts_id_root = new_ktree();
payouts_wid_root = new_ktree();
auths_free = k_new_list("Auths", sizeof(AUTHS),
ALLOC_AUTHS, LIMIT_AUTHS, true);
@ -1322,6 +1306,7 @@ static void dealloc_storage()
FREE_ALL(poolstats);
FREE_ALL(auths);
FREE_TREE(payouts_wid);
FREE_TREE(payouts_id);
FREE_TREE(payouts);
FREE_STORE_DATA(payouts);
@ -1498,8 +1483,7 @@ static bool setup_data()
if (workinfo_current) {
DATA_WORKINFO(wic, workinfo_current);
STRNCPY(wi.coinbase1, wic->coinbase1);
wi.createdate.tv_sec = 0L;
wi.createdate.tv_usec = 0L;
DATE_ZERO(&(wi.createdate));
INIT_WORKINFO(&look);
look.data = (void *)(&wi);
// Find the first workinfo for this height
@ -1539,7 +1523,7 @@ static bool setup_data()
(_seqset)->seqdata[_i].firsttime.tv_sec = \
(_seqset)->seqdata[_i].firsttime.tv_usec = \
(_seqset)->seqdata[_i].lasttime.tv_sec = \
(_seqset)->seqdata[_i].lasttime.tv_usec = 0; \
(_seqset)->seqdata[_i].lasttime.tv_usec = 0L; \
} \
} while (0);
@ -2917,7 +2901,7 @@ static void summarise_blocks()
K_RUNLOCK(blocks_free);
if (!b_prev) {
wi_start = 0;
elapsed_start.tv_sec = elapsed_start.tv_usec = 0L;
DATE_ZERO(&elapsed_start);
prev_hi = 0;
} else {
DATA_BLOCKS(prev_blocks, b_prev);
@ -2936,7 +2920,7 @@ static void summarise_blocks()
copy_tv(&elapsed_start, &(prev_workinfo->createdate));
prev_hi = prev_blocks->height;
}
elapsed_finish.tv_sec = elapsed_finish.tv_usec = 0L;
DATE_ZERO(&elapsed_finish);
// Add up the sharesummaries, abort if any SUMMARY_NEW
looksharesummary.workinfoid = wi_finish;
@ -3958,6 +3942,7 @@ static void *socketer(__maybe_unused void *arg)
case CMD_USERSTATUS:
case CMD_SHSTA:
case CMD_USERINFO:
case CMD_BTCSET:
ans = ckdb_cmds[msgline->which_cmds].func(NULL,
msgline->cmd,
msgline->id,
@ -4297,6 +4282,7 @@ static void reload_line(PGconn *conn, char *filename, uint64_t count, char *buf)
case CMD_PSHIFT:
case CMD_SHSTA:
case CMD_USERINFO:
case CMD_BTCSET:
LOGERR("%s() INVALID message line %"PRIu64
" ignored '%.42s...",
__func__, count,
@ -5134,7 +5120,7 @@ static void confirm_reload()
}
} else {
if (confirm_first_workinfoid == 0) {
start.tv_sec = start.tv_usec = 0;
DATE_ZERO(&start);
LOGWARNING("%s() no start workinfo found ... "
"using time 0", __func__);
} else {
@ -5610,6 +5596,7 @@ int main(int argc, char **argv)
ckp.main.processname = strdup("main");
cklock_init(&last_lock);
cklock_init(&btc_lock);
cklock_init(&seq_lock);
cklock_init(&process_pplns_lock);

44
src/ckdb.h

@ -55,7 +55,7 @@
#define DB_VLOCK "1"
#define DB_VERSION "1.0.2"
#define CKDB_VERSION DB_VERSION"-1.222"
#define CKDB_VERSION DB_VERSION"-1.241"
#define WHERE_FFL " - from %s %s() line %d"
#define WHERE_FFL_HERE __FILE__, __func__, __LINE__
@ -270,6 +270,8 @@ extern const tv_t date_eot;
#define DATE_BEGIN 1388620800L
extern const tv_t date_begin;
#define DATE_ZERO(_tv) (_tv)->tv_sec = (_tv)->tv_usec = 0L
#define BTC_TO_D(_amt) ((double)((_amt) / 100000000.0))
// argv -y - don't run in ckdb mode, just confirm sharesummaries
@ -335,6 +337,8 @@ extern cklock_t last_lock;
extern char *btc_server;
extern char *btc_auth;
extern int btc_timeout;
// Lock access to the above variables so they can be changed
extern cklock_t btc_lock;
#define EDDB "expirydate"
#define CDDB "createdate"
@ -401,6 +405,7 @@ enum cmd_values {
CMD_PSHIFT,
CMD_SHSTA,
CMD_USERINFO,
CMD_BTCSET,
CMD_END
};
@ -589,8 +594,7 @@ enum cmd_values {
STRNCPY(_row->createby, _by); \
STRNCPY(_row->createcode, _code); \
STRNCPY(_row->createinet, _inet); \
_row->modifydate.tv_sec = 0; \
_row->modifydate.tv_usec = 0; \
DATE_ZERO(&(_row->modifydate)); \
_row->modifyby[0] = '\0'; \
_row->modifycode[0] = '\0'; \
_row->modifyinet[0] = '\0'; \
@ -612,8 +616,7 @@ enum cmd_values {
SET_CREATEBY(_list, _row->createby, _by); \
SET_CREATECODE(_list, _row->createcode, _code); \
SET_CREATEINET(_list, _row->createinet, _inet); \
_row->modifydate.tv_sec = 0; \
_row->modifydate.tv_usec = 0; \
DATE_ZERO(&(_row->modifydate)); \
SET_MODIFYBY(_list, _row->modifyby, EMPTY); \
SET_MODIFYCODE(_list, _row->modifycode, EMPTY); \
SET_MODIFYINET(_list, _row->modifyinet, EMPTY); \
@ -1493,6 +1496,10 @@ typedef struct blocks {
double diffmean;
double cdferl;
double luck;
// To save looking them up when needed
tv_t prevcreatedate; // non-DB field
tv_t blockcreatedate; // non-DB field
} BLOCKS;
#define ALLOC_BLOCKS 100
@ -1545,6 +1552,11 @@ extern K_STORE *blocks_store;
extern tv_t blocks_stats_time;
extern bool blocks_stats_rebuild;
// Default number of blocks to display on web
#define BLOCKS_DEFAULT 42
// OptionControl can override it
#define BLOCKS_SETTING_NAME "BlocksPageSize"
// MININGPAYOUTS
typedef struct miningpayouts {
int64_t payoutid;
@ -1570,6 +1582,7 @@ typedef struct payouts {
int64_t payoutid;
int32_t height;
char blockhash[TXT_BIG+1];
tv_t blockcreatedate; // non-DB field
int64_t minerreward;
int64_t workinfoidstart;
int64_t workinfoidend;
@ -1591,6 +1604,7 @@ typedef struct payouts {
extern K_TREE *payouts_root;
extern K_TREE *payouts_id_root;
extern K_TREE *payouts_wid_root;
extern K_LIST *payouts_free;
extern K_STORE *payouts_store;
extern cklock_t process_pplns_lock;
@ -1893,6 +1907,9 @@ typedef struct workmarkers {
int64_t workinfoidstart;
char *description;
char status[TXT_FLAG+1];
int rewards; // non-DB field
double pps_value; // non-DB field
double rewarded; // non-DB field
HISTORYDATECONTROLFIELDS;
} WORKMARKERS;
@ -2297,6 +2314,10 @@ extern const char *blocks_confirmed(char *confirmed);
extern void zero_on_new_block();
extern void set_block_share_counters();
extern bool check_update_blocks_stats(tv_t *stats);
#define set_blockcreatedate(_h) _set_blockcreatedate(_h, WHERE_FFL_HERE)
extern bool _set_blockcreatedate(int32_t oldest_height, WHERE_FFL_ARGS);
#define set_prevcreatedate(_h) _set_prevcreatedate(_h, WHERE_FFL_HERE)
extern bool _set_prevcreatedate(int32_t oldest_height, WHERE_FFL_ARGS);
extern cmp_t cmp_miningpayouts(K_ITEM *a, K_ITEM *b);
extern K_ITEM *find_miningpayouts(int64_t payoutid, int64_t userid);
extern K_ITEM *first_miningpayouts(int64_t payoutid, K_TREE_CTX *ctx);
@ -2305,9 +2326,11 @@ extern K_TREE *upd_add_mu(K_TREE *mu_root, K_STORE *mu_store, int64_t userid,
double diffacc);
extern cmp_t cmp_payouts(K_ITEM *a, K_ITEM *b);
extern cmp_t cmp_payouts_id(K_ITEM *a, K_ITEM *b);
extern cmp_t cmp_payouts_wid(K_ITEM *a, K_ITEM *b);
extern K_ITEM *find_payouts(int32_t height, char *blockhash);
extern K_ITEM *find_last_payouts();
extern K_ITEM *find_payoutid(int64_t payoutid);
extern K_ITEM *find_payouts_wid(int64_t workinfoidend, K_TREE_CTX *ctx);
extern double payout_stats(PAYOUTS *payouts, char *statname);
extern bool process_pplns(int32_t height, char *blockhash, tv_t *now);
extern cmp_t cmp_auths(K_ITEM *a, K_ITEM *b);
@ -2335,11 +2358,13 @@ extern bool make_markersummaries(bool msg, char *by, char *code, char *inet,
extern void dsp_workmarkers(K_ITEM *item, FILE *stream);
extern cmp_t cmp_workmarkers(K_ITEM *a, K_ITEM *b);
extern cmp_t cmp_workmarkers_workinfoid(K_ITEM *a, K_ITEM *b);
extern K_ITEM *find_workmarkers(int64_t workinfoid, bool anystatus, char status);
extern K_ITEM *find_workmarkers(int64_t workinfoid, bool anystatus, char status, K_TREE_CTX *ctx);
extern K_ITEM *find_workmarkerid(int64_t markerid, bool anystatus, char status);
extern bool workmarkers_generate(PGconn *conn, char *err, size_t siz,
char *by, char *code, char *inet, tv_t *cd,
K_TREE *trf_root, bool none_error);
extern bool reward_shifts(PAYOUTS *payouts, bool lock, int delta);
extern bool shift_rewards(K_ITEM *wm_item);
extern cmp_t cmp_marks(K_ITEM *a, K_ITEM *b);
extern K_ITEM *find_marks(int64_t workinfoid);
extern const char *marks_marktype(char *marktype);
@ -2358,8 +2383,8 @@ extern K_ITEM *_find_create_userinfo(int64_t userid, bool lock, WHERE_FFL_ARGS);
#define userinfo_update(_s, _ss, _ms) _userinfo_update(_s, _ss, _ms, true, true)
extern void _userinfo_update(SHARES *shares, SHARESUMMARY *sharesummary,
MARKERSUMMARY *markersummary, bool ss_sub, bool lock);
#define userinfo_block(_blocks, _isnew) _userinfo_block(_blocks, _isnew, true)
extern void _userinfo_block(BLOCKS *blocks, enum info_type isnew, bool lock);
#define userinfo_block(_blocks, _isnew, _delta) _userinfo_block(_blocks, _isnew, _delta, true)
extern void _userinfo_block(BLOCKS *blocks, enum info_type isnew, int delta, bool lock);
// ***
// *** PostgreSQL functions ckdb_dbio.c
@ -2608,6 +2633,7 @@ extern bool check_2fa(USERS *users, int32_t value);
extern bool tst_2fa(K_ITEM *old_u_item, int32_t value, char *by, char *code,
char *inet, tv_t *cd, K_TREE *trf_root);
extern K_ITEM *remove_2fa(K_ITEM *old_u_item, int32_t value, char *by,
char *code, char *inet, tv_t *cd, K_TREE *trf_root);
char *code, char *inet, tv_t *cd, K_TREE *trf_root,
bool check);
#endif

2
src/ckdb_btc.c

@ -32,9 +32,11 @@ static char *btc_data(char *json, size_t *len)
APPEND_REALLOC_INIT(buf, off, *len);
APPEND_REALLOC(buf, off, *len, "POST / HTTP/1.1\n");
ck_wlock(&btc_lock);
snprintf(tmp, sizeof(tmp), "Authorization: Basic %s\n", btc_auth);
APPEND_REALLOC(buf, off, *len, tmp);
snprintf(tmp, sizeof(tmp), "Host: %s/\n", btc_server);
ck_wunlock(&btc_lock);
APPEND_REALLOC(buf, off, *len, tmp);
APPEND_REALLOC(buf, off, *len, "Content-Type: application/json\n");
snprintf(tmp, sizeof(tmp), "Content-Length: %d\n\n", (int)strlen(json));

404
src/ckdb_cmd.c

@ -213,7 +213,7 @@ static char *cmd_2fa(__maybe_unused PGconn *conn, char *cmd, char *id,
int32_t entropy, value;
USERS *users;
char *action, *buf = NULL, *st = NULL;
char *sfa_status = EMPTY, *sfa_error = EMPTY;
char *sfa_status = EMPTY, *sfa_error = EMPTY, *sfa_msg = EMPTY;
bool ok = false, key = false;
LOGDEBUG("%s(): cmd '%s'", __func__, cmd);
@ -301,9 +301,24 @@ static char *cmd_2fa(__maybe_unused PGconn *conn, char *cmd, char *id,
else {
key = false;
sfa_status = "ok";
sfa_msg = "2FA Enabled";
}
// Report sfa_error to web
ok = true;
} else if (strcmp(action, "untest") == 0) {
// Can't untest if it's not ready to test
if ((users->databits & (USER_TOTPAUTH | USER_TEST2FA))
!= (USER_TOTPAUTH | USER_TEST2FA))
goto dame;
// since it's currently test, the value isn't required
u_new = remove_2fa(u_item, 0, by, code, inet, now,
trf_root, false);
if (u_new) {
ok = true;
sfa_status = EMPTY;
key = false;
sfa_msg = "2FA Cancelled";
}
} else if (strcmp(action, "new") == 0) {
// Can't new if 2FA isn't already present -> setup
if ((users->databits & USER_TOTPAUTH) == 0)
@ -329,23 +344,27 @@ static char *cmd_2fa(__maybe_unused PGconn *conn, char *cmd, char *id,
// Can't remove if 2FA isn't already present
if (!(users->databits & (USER_TOTPAUTH | USER_TEST2FA)))
goto dame;
// remove requires value
value = (int32_t)atoi(transfer_data(i_value));
if (!check_2fa(users, value)) {
sfa_error = "Invalid code";
// Report sfa_error to web
ok = true;
} else {
/* already tested 2fa so don't retest, also,
* a retest will fail using the same value */
u_new = remove_2fa(u_item, value, by, code,
inet, now, trf_root);
inet, now, trf_root, false);
if (u_new) {
ok = true;
sfa_status = EMPTY;
key = false;
sfa_msg = "2FA Removed";
}
}
}
if (key) {
char *keystr, *issuer = "Kano";
char *keystr, *issuer = "KanoCKDB";
char cd_buf[DATE_BUFSIZ];
unsigned char *bin;
OPTIONCONTROL *oc;
@ -411,8 +430,9 @@ dame:
return strdup("failed.");
}
snprintf(tmp, sizeof(tmp), "2fa_status=%s%c2fa_error=%s",
sfa_status, FLDSEP, sfa_error);
snprintf(tmp, sizeof(tmp), "2fa_status=%s%c2fa_error=%s%c2fa_msg=%s",
sfa_status, FLDSEP, sfa_error, FLDSEP,
sfa_msg);
APPEND_REALLOC(buf, off, len, tmp);
LOGDEBUG("%s.%s-%s.%s", id, transfer_data(i_username), action, buf);
return buf;
@ -1116,7 +1136,7 @@ static char *cmd_workerstats(__maybe_unused PGconn *conn, char *cmd, char *id,
}
static char *cmd_blocklist(__maybe_unused PGconn *conn, char *cmd, char *id,
__maybe_unused tv_t *now, __maybe_unused char *by,
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)
@ -1128,13 +1148,16 @@ static char *cmd_blocklist(__maybe_unused PGconn *conn, char *cmd, char *id,
char tmp[1024];
char *buf, *desc, desc_buf[64];
size_t len, off;
int32_t height = -1;
tv_t first_cd = {0,0}, stats_tv = {0,0}, stats_tv2 = {0,0};
tv_t stats_tv = {0,0}, stats_tv2 = {0,0};
int rows, srows, tot, seq;
int64_t maxrows;
bool has_stats;
LOGDEBUG("%s(): cmd '%s'", __func__, cmd);
// 0 means just the system setting
maxrows = user_sys_setting(0, BLOCKS_SETTING_NAME, BLOCKS_DEFAULT, now);
APPEND_REALLOC_INIT(buf, off, len);
APPEND_REALLOC(buf, off, len, "ok.");
@ -1158,15 +1181,8 @@ redo:
}
seq = tot;
b_item = last_in_ktree(blocks_root, ctx);
while (b_item && rows < 42) {
while (b_item && rows < (int)maxrows) {
DATA_BLOCKS(blocks, b_item);
/* For each block remember the initial createdate
* Reverse sort order the oldest expirydate is first
* which should be the 'n' record */
if (height != blocks->height) {
height = blocks->height;
copy_tv(&first_cd, &(blocks->createdate));
}
if (CURRENT(&(blocks->expirydate))) {
if (blocks->confirmed[0] == BLOCKS_ORPHAN ||
blocks->confirmed[0] == BLOCKS_REJECT) {
@ -1203,16 +1219,24 @@ redo:
snprintf(tmp, sizeof(tmp), "workername:%d=%s%c", rows, reply, FLDSEP);
APPEND_REALLOC(buf, off, len, tmp);
// When block was found
snprintf(tmp, sizeof(tmp),
"first"CDTRF":%d=%ld%c", rows,
first_cd.tv_sec, FLDSEP);
blocks->blockcreatedate.tv_sec, FLDSEP);
APPEND_REALLOC(buf, off, len, tmp);
// Last time block was updated
snprintf(tmp, sizeof(tmp),
CDTRF":%d=%ld%c", rows,
blocks->createdate.tv_sec, FLDSEP);
APPEND_REALLOC(buf, off, len, tmp);
// When previous valid block was found
snprintf(tmp, sizeof(tmp),
"prev"CDTRF":%d=%ld%c", rows,
blocks->prevcreatedate.tv_sec, FLDSEP);
APPEND_REALLOC(buf, off, len, tmp);
snprintf(tmp, sizeof(tmp),
"confirmed:%d=%s%cstatus:%d=%s%c", rows,
blocks->confirmed, FLDSEP, rows,
@ -1299,12 +1323,18 @@ redo:
snprintf(tmp, sizeof(tmp),
"s_seq:%d=%d%c"
"s_desc:%d=%s%c"
"s_height:%d=%d%c"
"s_"CDTRF":%d=%ld%c"
"s_prev"CDTRF":%d=%ld%c"
"s_diffratio:%d=%.8f%c"
"s_diffmean:%d=%.8f%c"
"s_cdferl:%d=%.8f%c"
"s_luck:%d=%.8f%c",
srows, seq, FLDSEP,
srows, desc, FLDSEP,
srows, (int)(blocks->height), FLDSEP,
srows, blocks->blockcreatedate.tv_sec, FLDSEP,
srows, blocks->prevcreatedate.tv_sec, FLDSEP,
srows, blocks->diffratio, FLDSEP,
srows, blocks->diffmean, FLDSEP,
srows, blocks->cdferl, FLDSEP,
@ -1336,7 +1366,8 @@ 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",
"s_seq,s_desc,s_height,s_"CDTRF",s_prev"CDTRF",s_diffratio,"
"s_diffmean,s_cdferl,s_luck",
FLDSEP);
APPEND_REALLOC(buf, off, len, tmp);
@ -1344,8 +1375,8 @@ redo:
"rows=%d%cflds=%s%c",
rows, FLDSEP,
"seq,height,blockhash,nonce,reward,workername,first"CDTRF","
CDTRF",confirmed,status,info,statsconf,diffacc,diffinv,"
"shareacc,shareinv,elapsed,netdiff,diffratio,cdf,luck",
CDTRF",prev"CDTRF",confirmed,status,info,statsconf,diffacc,"
"diffinv,shareacc,shareinv,elapsed,netdiff,diffratio,cdf,luck",
FLDSEP);
APPEND_REALLOC(buf, off, len, tmp);
@ -1697,8 +1728,7 @@ static char *cmd_percent(char *cmd, char *id, tv_t *now, USERS *users)
// Add up all user's worker stats to be divided into payout percentages
lookworkers.userid = users->userid;
lookworkers.workername[0] = '\0';
lookworkers.expirydate.tv_sec = 0;
lookworkers.expirydate.tv_usec = 0;
DATE_ZERO(&(lookworkers.expirydate));
w_look.data = (void *)(&lookworkers);
w_item = find_after_in_ktree(workers_root, &w_look, cmp_workers, w_ctx);
DATA_WORKERS_NULL(workers, w_item);
@ -1959,8 +1989,7 @@ static char *cmd_workers(__maybe_unused PGconn *conn, char *cmd, char *id,
lookworkers.userid = users->userid;
lookworkers.workername[0] = '\0';
lookworkers.expirydate.tv_sec = 0;
lookworkers.expirydate.tv_usec = 0;
DATE_ZERO(&(lookworkers.expirydate));
w_look.data = (void *)(&lookworkers);
w_item = find_after_in_ktree(workers_root, &w_look, cmp_workers, w_ctx);
DATA_WORKERS_NULL(workers, w_item);
@ -1974,7 +2003,7 @@ static char *cmd_workers(__maybe_unused PGconn *conn, char *cmd, char *id,
copy_tv(&last_share, &(workerstatus->last_share));
K_RUNLOCK(workerstatus_free);
} else
last_share.tv_sec = last_share.tv_usec = 0L;
DATE_ZERO(&last_share);
if (tvdiff(now, &last_share) < oldworkers) {
str_to_buf(workers->workername, reply, sizeof(reply));
@ -2012,7 +2041,7 @@ static char *cmd_workers(__maybe_unused PGconn *conn, char *cmd, char *id,
w_elapsed = -1;
if (!ws_item) {
w_lastshare.tv_sec = 0;
w_lastshare.tv_sec = 0L;
w_lastdiff = w_diffacc =
w_diffinv = w_diffsta =
w_diffdup = w_diffhi =
@ -2020,7 +2049,7 @@ static char *cmd_workers(__maybe_unused PGconn *conn, char *cmd, char *id,
w_shareinv = w_sharesta =
w_sharedup = w_sharehi =
w_sharerej = w_active_diffacc = 0;
w_active_start.tv_sec = 0;
w_active_start.tv_sec = 0L;
} else {
DATA_WORKERSTATUS(workerstatus, ws_item);
// It's bad to read possibly changing data
@ -4009,8 +4038,8 @@ static char *cmd_pplns(__maybe_unused PGconn *conn, char *cmd, char *id,
LOGDEBUG("%s(): height %"PRId32, __func__, height);
block_tv.tv_sec = block_tv.tv_usec = 0L;
cd.tv_sec = cd.tv_usec = 0L;
DATE_ZERO(&block_tv);
DATE_ZERO(&cd);
lookblocks.height = height + 1;
lookblocks.blockhash[0] = '\0';
INIT_BLOCKS(&b_look);
@ -4437,7 +4466,6 @@ static char *cmd_pplns2(__maybe_unused PGconn *conn, char *cmd, char *id,
PAYMENTS *payments;
PAYOUTS *payouts;
BLOCKS lookblocks, *blocks;
tv_t block_tv = { 0L, 0L };
WORKINFO *bworkinfo, *workinfo;
char ndiffbin[TXT_SML+1];
double ndiff;
@ -4461,36 +4489,26 @@ static char *cmd_pplns2(__maybe_unused PGconn *conn, char *cmd, char *id,
LOGDEBUG("%s(): height %"PRId32, __func__, height);
lookblocks.height = height + 1;
lookblocks.height = height;
lookblocks.blockhash[0] = '\0';
INIT_BLOCKS(&b_look);
b_look.data = (void *)(&lookblocks);
K_RLOCK(blocks_free);
b_item = find_before_in_ktree(blocks_root, &b_look, cmp_blocks, b_ctx);
b_item = find_after_in_ktree(blocks_root, &b_look, cmp_blocks, b_ctx);
K_RUNLOCK(blocks_free);
if (!b_item) {
K_RUNLOCK(blocks_free);
snprintf(reply, siz, "ERR.no block height %"PRId32, height);
snprintf(reply, siz, "ERR.no block height >= %"PRId32, height);
return strdup(reply);
}
DATA_BLOCKS(blocks, b_item);
while (b_item && blocks->height == height) {
if (blocks->confirmed[0] == BLOCKS_NEW)
copy_tv(&block_tv, &(blocks->createdate));
// Allow any state, but report it
if (CURRENT(&(blocks->expirydate)))
break;
b_item = prev_in_ktree(b_ctx);
DATA_BLOCKS_NULL(blocks, b_item);
}
K_RUNLOCK(blocks_free);
if (!b_item || blocks->height != height) {
snprintf(reply, siz, "ERR.no block height %"PRId32, height);
return strdup(reply);
}
if (block_tv.tv_sec == 0) {
snprintf(reply, siz, "ERR.block %"PRId32" missing '%s' record",
height,
blocks_confirmed(BLOCKS_NEW_STR));
if (blocks->blockcreatedate.tv_sec == 0) {
snprintf(reply, siz, "ERR.block %"PRId32" has 0 blockcreatedate",
height);
return strdup(reply);
}
if (!CURRENT(&(blocks->expirydate))) {
@ -4667,10 +4685,11 @@ static char *cmd_pplns2(__maybe_unused PGconn *conn, char *cmd, char *id,
snprintf(tmp, sizeof(tmp), "begin_epoch=%ld%c",
workinfo->createdate.tv_sec, FLDSEP);
APPEND_REALLOC(buf, off, len, tmp);
tv_to_buf(&block_tv, tv_buf, sizeof(tv_buf));
tv_to_buf(&(blocks->blockcreatedate), tv_buf, sizeof(tv_buf));
snprintf(tmp, sizeof(tmp), "block_stamp=%s%c", tv_buf, FLDSEP);
APPEND_REALLOC(buf, off, len, tmp);
snprintf(tmp, sizeof(tmp), "block_epoch=%ld%c", block_tv.tv_sec, FLDSEP);
snprintf(tmp, sizeof(tmp), "block_epoch=%ld%c",
blocks->blockcreatedate.tv_sec, FLDSEP);
APPEND_REALLOC(buf, off, len, tmp);
tv_to_buf(&(payouts->lastshareacc), tv_buf, sizeof(tv_buf));
snprintf(tmp, sizeof(tmp), "end_stamp=%s%c", tv_buf, FLDSEP);
@ -4780,6 +4799,8 @@ static char *cmd_payouts(PGconn *conn, char *cmd, char *id, tv_t *now,
snprintf(reply, siz, "failed payout %"PRId64, payoutid);
return strdup(reply);
}
// Original wasn't generated, so reward it
reward_shifts(payouts2, true, 1);
DATA_PAYOUTS(payouts2, p2_item);
DATA_PAYOUTS(old_payouts2, old_p2_item);
snprintf(msg, sizeof(msg),
@ -4849,6 +4870,8 @@ static char *cmd_payouts(PGconn *conn, char *cmd, char *id, tv_t *now,
snprintf(reply, siz, "failed payout %"PRId64, payoutid);
return strdup(reply);
}
// Original was generated, so undo the reward
reward_shifts(payouts2, true, -1);
DATA_PAYOUTS(payouts2, p2_item);
DATA_PAYOUTS(old_payouts2, old_p2_item);
snprintf(msg, sizeof(msg),
@ -4866,6 +4889,7 @@ static char *cmd_payouts(PGconn *conn, char *cmd, char *id, tv_t *now,
return strdup(reply);
TXT_TO_BIGINT("payoutid", transfer_data(i_payoutid), payoutid);
// payouts_full_expire updates the shift rewards
p_item = payouts_full_expire(conn, payoutid, now, true);
if (!p_item) {
snprintf(reply, siz, "failed payout %"PRId64, payoutid);
@ -4902,6 +4926,7 @@ static char *cmd_payouts(PGconn *conn, char *cmd, char *id, tv_t *now,
return strdup(reply);
TXT_TO_CTV("addrdate", transfer_data(i_addrdate), addrdate);
// process_pplns updates the shift rewards
if (addrdate.tv_sec == 0)
ok = process_pplns(height, blockhash, NULL);
else
@ -4982,6 +5007,11 @@ static char *cmd_mpayouts(__maybe_unused PGconn *conn, char *cmd, char *id,
rows, reply, FLDSEP);
APPEND_REALLOC(buf, off, len, tmp);
snprintf(tmp, sizeof(tmp),
"block"CDTRF":%d=%ld%c", rows,
payouts->blockcreatedate.tv_sec, FLDSEP);
APPEND_REALLOC(buf, off, len, tmp);
bigint_to_buf(payouts->elapsed, reply,
sizeof(reply));
snprintf(tmp, sizeof(tmp), "elapsed:%d=%s%c",
@ -5026,7 +5056,7 @@ static char *cmd_mpayouts(__maybe_unused PGconn *conn, char *cmd, char *id,
snprintf(tmp, sizeof(tmp), "rows=%d%cflds=%s%c",
rows, FLDSEP,
"payoutid,height,elapsed,amount,diffacc,minerreward,diffused,status",
"payoutid,height,block"CDTRF",elapsed,amount,diffacc,minerreward,diffused,status",
FLDSEP);
APPEND_REALLOC(buf, off, len, tmp);
@ -5037,15 +5067,16 @@ static char *cmd_mpayouts(__maybe_unused PGconn *conn, char *cmd, char *id,
return buf;
}
/* Find the offset, in list, of the workername
* -1 means NULL list, empty list or not found */
static int worker_offset(char **list, char *workername)
typedef struct worker_match {
char *worker;
bool match;
size_t len;
bool used;
} WM;
static char *worker_offset(char *workername)
{
char *c1, *c2;
int i;
if (!list || !(*list))
return -1;
/* Find the start of the workername including the SEP */
c1 = strchr(workername, WORKSEP1);
@ -5057,11 +5088,8 @@ static int worker_offset(char **list, char *workername)
// No workername after the username
if (!c1)
c1 = WORKERS_EMPTY;
for (i = 0; list[i]; i++) {
if (strcmp(c1, list[i]) == 0)
return i;
}
return -1;
return c1;
}
/* Some arbitrarily large limit, increase it if needed
@ -5069,28 +5097,19 @@ static int worker_offset(char **list, char *workername)
#define SELECT_LIMIT 63
/* select is a string of workernames separated by WORKERS_SEL_SEP
* Return an array of strings of select broken up
* The array is terminated by NULL
* Setup the wm array of workers with select broken up
* The wm array is terminated by workers = NULL
* and will have 0 elements if select is NULL/empty
* The count of the first occurrence of WORKERS_ALL is returned in *all_count,
* The count of the first occurrence of WORKERS_ALL is returned,
* or -1 if WORKERS_ALL isn't found */
static char **select_list(char *select, int *all_count)
static int select_list(WM *wm, char *select)
{
size_t len, offset, siz;
char **list = NULL;
int count;
int count, all_count = -1;
size_t len, offset;
char *end;
*all_count = -1;
siz = sizeof(char *) * (SELECT_LIMIT + 1);
list = malloc(siz);
if (!list)
quithere(1, "malloc (%d) OOM", (int)siz);
list[0] = NULL;
if (select == NULL || *select == '\0')
return list;
return all_count;
len = strlen(select);
count = 0;
@ -5099,24 +5118,24 @@ static char **select_list(char *select, int *all_count)
if (select[offset] == WORKERS_SEL_SEP)
offset++;
else {
list[count] = select + offset;
list[count+1] = NULL;
end = strchr(list[count], WORKERS_SEL_SEP);
wm[count].worker = select + offset;
wm[count+1].worker = NULL;
end = strchr(wm[count].worker, WORKERS_SEL_SEP);
if (end != NULL) {
offset = 1 + end - select;
*end = '\0';
}
if (*all_count == -1 &&
strcasecmp(list[count], WORKERS_ALL) == 0) {
*all_count = count;
if (all_count == -1 &&
strcasecmp(wm[count].worker, WORKERS_ALL) == 0) {
all_count = count;
}
if (end == NULL || ++count > SELECT_LIMIT)
break;
}
}
return list;
return all_count;
}
static char *cmd_shifts(__maybe_unused PGconn *conn, char *cmd, char *id,
@ -5129,7 +5148,7 @@ static char *cmd_shifts(__maybe_unused PGconn *conn, char *cmd, char *id,
K_TREE_CTX wm_ctx[1], ms_ctx[1];
WORKMARKERS *wm;
WORKINFO *wi;
MARKERSUMMARY markersummary, *ms, ms_add;
MARKERSUMMARY markersummary, *ms, ms_add[SELECT_LIMIT+1];
PAYOUTS *payouts;
USERS *users;
MARKS *marks = NULL;
@ -5137,14 +5156,14 @@ static char *cmd_shifts(__maybe_unused PGconn *conn, char *cmd, char *id,
char tmp[1024];
size_t siz = sizeof(reply);
char *select = NULL;
char **selects = NULL;
bool used[SELECT_LIMIT];
char *buf;
WM workm[SELECT_LIMIT+1];
char *buf, *work;
size_t len, off;
tv_t marker_end = { 0L, 0L };
int rows, want, i, where_all;
int64_t maxrows;
double wm_count, d;
int64_t last_payout_start = 0;
LOGDEBUG("%s(): cmd '%s'", __func__, cmd);
@ -5171,23 +5190,34 @@ static char *cmd_shifts(__maybe_unused PGconn *conn, char *cmd, char *id,
wm_count *= 1.42;
if (maxrows < wm_count)
maxrows = wm_count;
last_payout_start = payouts->workinfoidstart;
}
i_select = optional_name(trf_root, "select", 1, NULL, reply, siz);
if (i_select)
select = strdup(transfer_data(i_select));
selects = select_list(select, &where_all);
bzero(workm, sizeof(workm));
where_all = select_list(&(workm[0]), select);
// Nothing selected = all
if (*selects == NULL) {
if (workm[0].worker == NULL) {
where_all = 0;
selects[0] = WORKERS_ALL;
selects[1] = NULL;
workm[0].worker = WORKERS_ALL;
} else {
for (i = 0; workm[i].worker; i++) {
// N.B. len is only used if match is true
len = workm[i].len = strlen(workm[i].worker);
// If at least 3 characters and last is '*'
if (len > 2 && workm[i].worker[len-1] == '*') {
workm[i].worker[len-1] = '\0';
workm[i].match = true;
workm[i].len--;
}
}
}
bzero(used, sizeof(used));
if (where_all >= 0)
used[where_all] = true;
workm[where_all].used = true;
APPEND_REALLOC_INIT(buf, off, len);
APPEND_REALLOC(buf, off, len, "ok.");
@ -5216,7 +5246,12 @@ static char *cmd_shifts(__maybe_unused PGconn *conn, char *cmd, char *id,
wm->workinfoidend);
}
bzero(&ms_add, sizeof(ms_add));
// Zero everything for this shift
bzero(ms_add, sizeof(ms_add));
for (i = 0; workm[i].worker; i++) {
if (i != where_all)
workm[i].used = false;
}
markersummary.markerid = wm->markerid;
markersummary.userid = users->userid;
@ -5227,49 +5262,56 @@ static char *cmd_shifts(__maybe_unused PGconn *conn, char *cmd, char *id,
DATA_MARKERSUMMARY_NULL(ms, ms_item);
while (ms_item && ms->markerid == wm->markerid &&
ms->userid == users->userid) {
ms_add.diffacc += ms->diffacc;
ms_add.diffsta += ms->diffsta;
ms_add.diffdup += ms->diffdup;
ms_add.diffhi += ms->diffhi;
ms_add.diffrej += ms->diffrej;
ms_add.shareacc += ms->shareacc;
ms_add.sharesta += ms->sharesta;
ms_add.sharedup += ms->sharedup;
ms_add.sharehi += ms->sharehi;
ms_add.sharerej += ms->sharerej;
want = worker_offset(selects, ms->workername);
if (want >= 0) {
used[want] = true;
double_to_buf(ms->diffacc, reply, sizeof(reply));
work = worker_offset(ms->workername);
for (want = 0; workm[want].worker; want++) {
if ((want == where_all) ||
(workm[want].match && strncmp(work, workm[want].worker, workm[want].len) == 0) ||
(!(workm[want].match) && strcmp(workm[want].worker, work) == 0)) {
workm[want].used = true;
ms_add[want].diffacc += ms->diffacc;
ms_add[want].diffsta += ms->diffsta;
ms_add[want].diffdup += ms->diffdup;
ms_add[want].diffhi += ms->diffhi;
ms_add[want].diffrej += ms->diffrej;
ms_add[want].shareacc += ms->shareacc;
ms_add[want].sharesta += ms->sharesta;
ms_add[want].sharedup += ms->sharedup;
ms_add[want].sharehi += ms->sharehi;
ms_add[want].sharerej += ms->sharerej;
}
}
ms_item = next_in_ktree(ms_ctx);
DATA_MARKERSUMMARY_NULL(ms, ms_item);
}
K_RUNLOCK(markersummary_free);
for (i = 0; i <= SELECT_LIMIT; i++) {
if (workm[i].used) {
double_to_buf(ms_add[i].diffacc, reply, sizeof(reply));
snprintf(tmp, sizeof(tmp), "%d_diffacc:%d=%s%c",
want, rows, reply, FLDSEP);
i, rows, reply, FLDSEP);
APPEND_REALLOC(buf, off, len, tmp);
d = ms->diffsta + ms->diffdup +
ms->diffhi + ms->diffrej;
d = ms_add[i].diffsta + ms_add[i].diffdup +
ms_add[i].diffhi + ms_add[i].diffrej;
double_to_buf(d, reply, sizeof(reply));
snprintf(tmp, sizeof(tmp), "%d_diffinv:%d=%s%c",
want, rows, reply, FLDSEP);
i, rows, reply, FLDSEP);
APPEND_REALLOC(buf, off, len, tmp);
double_to_buf(ms->shareacc, reply, sizeof(reply));
double_to_buf(ms_add[i].shareacc, reply, sizeof(reply));
snprintf(tmp, sizeof(tmp), "%d_shareacc:%d=%s%c",
want, rows, reply, FLDSEP);
i, rows, reply, FLDSEP);
APPEND_REALLOC(buf, off, len, tmp);
d = ms->sharesta + ms->sharedup +
ms->sharehi + ms->sharerej;
d = ms_add[i].sharesta + ms_add[i].sharedup +
ms_add[i].sharehi + ms_add[i].sharerej;
double_to_buf(d, reply, sizeof(reply));
snprintf(tmp, sizeof(tmp), "%d_shareinv:%d=%s%c",
want, rows, reply, FLDSEP);
i, rows, reply, FLDSEP);
APPEND_REALLOC(buf, off, len, tmp);
}
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);
@ -5286,7 +5328,6 @@ static char *cmd_shifts(__maybe_unused PGconn *conn, char *cmd, char *id,
wm->workinfoidend);
snprintf(reply, siz, "data error 1");
free(buf);
free(selects);
return(strdup(reply));
}
DATA_WORKINFO(wi, wi_item);
@ -5307,7 +5348,6 @@ static char *cmd_shifts(__maybe_unused PGconn *conn, char *cmd, char *id,
wm->workinfoidstart);
snprintf(reply, siz, "data error 2");
free(buf);
free(selects);
return(strdup(reply));
}
DATA_WORKINFO(wi, wi_item);
@ -5338,35 +5378,18 @@ static char *cmd_shifts(__maybe_unused PGconn *conn, char *cmd, char *id,
rows, reply, FLDSEP);
APPEND_REALLOC(buf, off, len, tmp);
if (where_all >= 0) {
double_to_buf(ms_add.diffacc, reply, sizeof(reply));
snprintf(tmp, sizeof(tmp), "%d_diffacc:%d=%s%c",
where_all, rows,
reply, FLDSEP);
APPEND_REALLOC(buf, off, len, tmp);
d = ms_add.diffsta + ms_add.diffdup +
ms_add.diffhi + ms_add.diffrej;
double_to_buf(d, reply, sizeof(reply));
snprintf(tmp, sizeof(tmp), "%d_diffinv:%d=%s%c",
where_all, rows,
reply, FLDSEP);
snprintf(tmp, sizeof(tmp), "rewards:%d=%d%c",
rows, wm->rewards, FLDSEP);
APPEND_REALLOC(buf, off, len, tmp);
double_to_buf(ms_add.shareacc, reply, sizeof(reply));
snprintf(tmp, sizeof(tmp), "%d_shareacc:%d=%s%c",
where_all, rows,
reply, FLDSEP);
snprintf(tmp, sizeof(tmp), "lastpayoutstart:%d=%s%c",
rows,
(wm->workinfoidstart ==
last_payout_start) ?
"Y" : EMPTY,
FLDSEP);
APPEND_REALLOC(buf, off, len, tmp);
d = ms_add.sharesta + ms_add.sharedup +
ms_add.sharehi + ms_add.sharerej;
double_to_buf(d, reply, sizeof(reply));
snprintf(tmp, sizeof(tmp), "%d_shareinv:%d=%s%c",
where_all, rows,
reply, FLDSEP);
APPEND_REALLOC(buf, off, len, tmp);
}
rows++;
// Setup for next shift
@ -5379,11 +5402,13 @@ static char *cmd_shifts(__maybe_unused PGconn *conn, char *cmd, char *id,
}
K_RUNLOCK(workmarkers_free);
for (i = 0; selects[i]; i++) {
if (used[i]) {
for (i = 0; workm[i].worker; i++) {
if (workm[i].used) {
snprintf(tmp, sizeof(tmp),
"%d_worker=%s%c",
i, selects[i], FLDSEP);
"%d_worker=%s%s%c",
i, workm[i].worker,
workm[i].match ? "*" : EMPTY,
FLDSEP);
APPEND_REALLOC(buf, off, len, tmp);
snprintf(tmp, sizeof(tmp),
"%d_flds=%s%c", i,
@ -5404,13 +5429,14 @@ static char *cmd_shifts(__maybe_unused PGconn *conn, char *cmd, char *id,
* other workers start >= 0 and finish <= rows-1 */
snprintf(tmp, sizeof(tmp), "rows=%d%cflds=%s%c",
rows, FLDSEP,
"markerid,shift,start,end", FLDSEP);
"markerid,shift,start,end,rewards,"
"lastpayoutstart", FLDSEP);
APPEND_REALLOC(buf, off, len, tmp);
snprintf(tmp, sizeof(tmp), "arn=%s", "Shifts");
APPEND_REALLOC(buf, off, len, tmp);
for (i = 0; selects[i]; i++) {
if (used[i]) {
for (i = 0; workm[i].worker; i++) {
if (workm[i].used) {
snprintf(tmp, sizeof(tmp), ",Worker_%d", i);
APPEND_REALLOC(buf, off, len, tmp);
}
@ -5418,15 +5444,14 @@ static char *cmd_shifts(__maybe_unused PGconn *conn, char *cmd, char *id,
snprintf(tmp, sizeof(tmp), "%carp=", FLDSEP);
APPEND_REALLOC(buf, off, len, tmp);
for (i = 0; selects[i]; i++) {
if (used[i]) {
for (i = 0; workm[i].worker; i++) {
if (workm[i].used) {
snprintf(tmp, sizeof(tmp), ",%d_", i);
APPEND_REALLOC(buf, off, len, tmp);
}
}
LOGDEBUG("%s.ok.%s", id, transfer_data(i_username));
free(selects);
return(buf);
}
@ -5537,7 +5562,7 @@ static char *cmd_stats(__maybe_unused PGconn *conn, char *cmd, char *id,
USEINFO(marks, 1, 1);
USEINFO(blocks, 1, 1);
USEINFO(miningpayouts, 1, 1);
USEINFO(payouts, 1, 2);
USEINFO(payouts, 1, 3);
USEINFO(auths, 1, 1);
USEINFO(poolstats, 1, 1);
USEINFO(userstats, 2, 1);
@ -6300,6 +6325,60 @@ static char *cmd_userinfo(__maybe_unused PGconn *conn, char *cmd, char *id,
return buf;
}
/* Set/show the BTC server settings
* You must supply the btcserver to change anything
* The format for userpass is username:password
* If you don't supply the btcserver it will simply report the current server
* If supply btcserver but not the userpass it will use the current userpass
* The reply will ONLY contain the URL, not the user/pass */
static char *cmd_btcset(__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, K_TREE *trf_root)
{
K_ITEM *i_btcserver, *i_userpass;
char *btcserver = NULL, *userpass = NULL, *tmp;
char reply[1024] = "";
size_t siz = sizeof(reply);
char buf[256];
i_btcserver = optional_name(trf_root, "btcserver", 1, NULL, reply, siz);
if (i_btcserver) {
btcserver = strdup(transfer_data(i_btcserver));
i_userpass = optional_name(trf_root, "userpass", 0, NULL, reply, siz);
if (i_userpass)
userpass = transfer_data(i_userpass);
ck_wlock(&btc_lock);
btc_server = btcserver;
btcserver = NULL;
if (userpass) {
if (btc_auth) {
tmp = btc_auth;
while (*tmp)
*(tmp++) = '\0';
}
FREENULL(btc_auth);
btc_auth = http_base64(userpass);
}
ck_wunlock(&btc_lock);
if (userpass) {
tmp = userpass;
while (*tmp)
*(tmp++) = '\0';
}
}
FREENULL(btcserver);
ck_wlock(&btc_lock);
snprintf(buf, sizeof(buf), "ok.btcserver=%s", btc_server);
ck_wunlock(&btc_lock);
LOGDEBUG("%s.%s.%s", id, cmd, buf);
return strdup(buf);
}
// TODO: limit access by having seperate sockets for each
#define ACCESS_POOL "p"
#define ACCESS_SYSTEM "s"
@ -6414,5 +6493,6 @@ struct CMDS ckdb_cmds[] = {
{ CMD_PSHIFT, "pshift", false, false, cmd_pshift, SEQ_NONE, ACCESS_SYSTEM ACCESS_WEB },
{ CMD_SHSTA, "shsta", true, false, cmd_shsta, SEQ_NONE, ACCESS_SYSTEM },
{ CMD_USERINFO, "userinfo", false, false, cmd_userinfo, SEQ_NONE, ACCESS_WEB },
{ CMD_BTCSET, "btcset", false, false, cmd_btcset, SEQ_NONE, ACCESS_SYSTEM },
{ CMD_END, NULL, false, false, NULL, SEQ_NONE, NULL }
};

7
src/ckdb_crypt.c

@ -237,13 +237,16 @@ bool tst_2fa(K_ITEM *old_u_item, int32_t value, char *by, char *code,
}
K_ITEM *remove_2fa(K_ITEM *old_u_item, int32_t value, char *by, char *code,
char *inet, tv_t *cd, K_TREE *trf_root)
char *inet, tv_t *cd, K_TREE *trf_root, bool check)
{
K_ITEM *u_item = NULL;
USERS *old_users, *users;
bool ok, did = false;
bool ok = true, did = false;
DATA_USERS(old_users, old_u_item);
/* N.B. check_2fa will fail if it is called a second time
* with the same value */
if (check)
ok = check_2fa(old_users, value);
if (ok) {
K_WLOCK(users_free);

412
src/ckdb_data.c

@ -369,8 +369,7 @@ void _txt_to_data(enum data_type typ, char *nam, char *fld, void *data, size_t s
long sec, nsec;
int c;
// Caller test for tv_sec=0 for failure
((tv_t *)data)->tv_sec = 0L;
((tv_t *)data)->tv_usec = 0L;
DATE_ZERO((tv_t *)data);
c = sscanf(fld, "%ld,%ld", &sec, &nsec);
if (c > 0) {
((tv_t *)data)->tv_sec = (time_t)sec;
@ -882,12 +881,17 @@ K_ITEM *_find_create_workerstatus(int64_t userid, char *workername,
K_WUNLOCK(workerstatus_free);
if (ws_err) {
LOGERR("%s(): CREATED Missing workerstatus %"PRId64"/%s"
LOGNOTICE("%s(): CREATED Missing workerstatus"
" %"PRId64"/%s"
WHERE_FFL WHERE_FFL,
__func__, userid, workername,
file2, func2, line2, WHERE_FFL_PASS);
if (w_err) {
LOGERR("%s(): %s Missing worker %"PRId64"/%s",
int sta = LOG_ERR;
if (w_item)
sta = LOG_NOTICE;
LOGMSG(sta,
"%s(): %s Missing worker %"PRId64"/%s",
__func__,
w_item ? "CREATED" : "FAILED TO CREATE",
userid, workername);
@ -1469,8 +1473,7 @@ K_ITEM *first_workers(int64_t userid, K_TREE_CTX *ctx)
workers.userid = userid;
workers.workername[0] = '\0';
workers.expirydate.tv_sec = 0L;
workers.expirydate.tv_usec = 0L;
DATE_ZERO(&(workers.expirydate));
INIT_WORKERS(&look);
look.data = (void *)(&workers);
@ -1623,8 +1626,7 @@ K_ITEM *find_paymentaddresses_create(int64_t userid, K_TREE_CTX *ctx)
K_ITEM look, *item;
paymentaddresses.userid = userid;
paymentaddresses.createdate.tv_sec = 0;
paymentaddresses.createdate.tv_usec = 0;
DATE_ZERO(&(paymentaddresses.createdate));
paymentaddresses.payaddress[0] = '\0';
INIT_PAYMENTADDRESSES(&look);
@ -1844,8 +1846,7 @@ K_ITEM *find_optioncontrol(char *optionname, tv_t *now, int32_t height)
* activationdate will all be the default value and not
* decide the outcome */
STRNCPY(optioncontrol.optionname, optionname);
optioncontrol.activationdate.tv_sec = 0L;
optioncontrol.activationdate.tv_usec = 0L;
DATE_ZERO(&(optioncontrol.activationdate));
optioncontrol.activationheight = OPTIONCONTROL_HEIGHT - 1;
optioncontrol.expirydate.tv_sec = default_expiry.tv_sec;
optioncontrol.expirydate.tv_usec = default_expiry.tv_usec;
@ -2040,8 +2041,8 @@ bool workinfo_age(int64_t workinfoid, char *poolinstance, char *by, char *code,
LOGDEBUG("%s(): age", __func__);
ss_first->tv_sec = ss_first->tv_usec =
ss_last->tv_sec = ss_last->tv_usec = 0;
DATE_ZERO(ss_first);
DATE_ZERO(ss_last);
*ss_count = *s_count = *s_diff = 0;
wi_item = find_workinfo(workinfoid, NULL);
@ -2065,7 +2066,7 @@ bool workinfo_age(int64_t workinfoid, char *poolinstance, char *by, char *code,
}
K_RLOCK(workmarkers_free);
wm_item = find_workmarkers(workinfoid, false, MARKER_PROCESSED);
wm_item = find_workmarkers(workinfoid, false, MARKER_PROCESSED, NULL);
K_RUNLOCK(workmarkers_free);
// Should never happen?
if (wm_item && !reloading) {
@ -2137,8 +2138,7 @@ bool workinfo_age(int64_t workinfoid, char *poolinstance, char *by, char *code,
lookshares.workinfoid = workinfoid;
lookshares.userid = sharesummary->userid;
strcpy(lookshares.workername, sharesummary->workername);
lookshares.createdate.tv_sec = 0;
lookshares.createdate.tv_usec = 0;
DATE_ZERO(&(lookshares.createdate));
s_look.data = (void *)(&lookshares);
K_WLOCK(shares_free);
@ -2385,8 +2385,8 @@ void auto_age_older(int64_t workinfoid, char *poolinstance, char *by,
cmp_sharesummary_workinfoid, ctx);
DATA_SHARESUMMARY_NULL(sharesummary, ss_item);
ss_first_min.tv_sec = ss_first_min.tv_usec =
ss_last_max.tv_sec = ss_last_max.tv_usec = 0;
DATE_ZERO(&ss_first_min);
DATE_ZERO(&ss_last_max);
ss_count_tot = s_count_tot = s_diff_tot = 0;
found = false;
@ -2628,8 +2628,7 @@ K_ITEM *find_prev_blocks(int32_t height)
* not NEW, blocks, which might not find the right one */
lookblocks.height = height;
lookblocks.blockhash[0] = '\0';
lookblocks.expirydate.tv_sec = 0L;
lookblocks.expirydate.tv_usec = 0L;
DATE_ZERO(&(lookblocks.expirydate));
INIT_BLOCKS(&look);
look.data = (void *)(&lookblocks);
@ -2919,8 +2918,8 @@ bool check_update_blocks_stats(tv_t *stats)
K_RUNLOCK(workinfo_free);
if (!w_item) {
setnow(&now);
if (!blocks->workinfoid != last_missing_workinfoid ||
tvdiff(&now, &last_message) >= 5.0) {
if (blocks->workinfoid != last_missing_workinfoid ||
tvdiff(&now, &last_message) >= 15.0) {
LOGEMERG("%s(): missing block workinfoid %"
PRId32"/%"PRId64"/%s",
__func__, blocks->height,
@ -2988,6 +2987,184 @@ bool check_update_blocks_stats(tv_t *stats)
return true;
}
// Must be under K_WLOCK(blocks_free) when called except during DB load
bool _set_blockcreatedate(int32_t oldest_height, WHERE_FFL_ARGS)
{
K_TREE_CTX ctx[1];
BLOCKS *blocks;
K_ITEM *b_item;
int32_t height;
char blockhash[TXT_BIG+1];
char cd_buf[DATE_BUFSIZ];
tv_t createdate;
bool ok = true;
// No blocks?
if (blocks_store->count == 0)
return true;
height = 0;
blockhash[0] = '\0';
DATE_ZERO(&createdate);
b_item = last_in_ktree(blocks_root, ctx);
DATA_BLOCKS_NULL(blocks, b_item);
while (b_item && blocks->height >= oldest_height) {
// NEW will be first going back
if (blocks->confirmed[0] == BLOCKS_NEW) {
height = blocks->height;
STRNCPY(blockhash, blocks->blockhash);
copy_tv(&createdate, &(blocks->createdate));
}
if (blocks->height != height ||
strcmp(blocks->blockhash, blockhash) != 0) {
// Missing NEW
tv_to_buf(&(blocks->expirydate), cd_buf, sizeof(cd_buf));
LOGEMERG("%s() block %"PRId32"/%s/%s/%s has no '"
BLOCKS_NEW_STR "' prev was %"PRId32"/%s."
WHERE_FFL,
__func__,
blocks->height, blocks->blockhash,
blocks->confirmed, cd_buf,
height, blockhash, WHERE_FFL_PASS);
ok = false;
height = blocks->height;
STRNCPY(blockhash, blocks->blockhash);
// set a useable (incorrect) value
copy_tv(&createdate, &(blocks->createdate));
}
// Always update it
copy_tv(&(blocks->blockcreatedate), &createdate);
b_item = prev_in_ktree(ctx);
DATA_BLOCKS_NULL(blocks, b_item);
}
return ok;
}
// Must be under K_WLOCK(blocks_free) when called except during DB load
bool _set_prevcreatedate(int32_t oldest_height, WHERE_FFL_ARGS)
{
K_ITEM look, *b_item = NULL, *wi_item;
BLOCKS lookblocks, *blocks = NULL;
K_TREE_CTX b_ctx[1], wi_ctx[1];
WORKINFO *workinfo;
char curr_blockhash[TXT_BIG+1];
char cd_buf[DATE_BUFSIZ];
int32_t curr_height;
tv_t prev_createdate;
tv_t curr_createdate;
bool ok = true, currok = false;
// No blocks?
if (blocks_store->count == 0)
return true;
// Find first 'ok' block before oldest_height
lookblocks.height = oldest_height;
lookblocks.blockhash[0] = '\0';
DATE_ZERO(&(lookblocks.expirydate));
INIT_BLOCKS(&look);
look.data = (void *)(&lookblocks);
b_item = find_before_in_ktree(blocks_root, &look, cmp_blocks, b_ctx);
while (b_item) {
DATA_BLOCKS(blocks, b_item);
if (CURRENT(&(blocks->expirydate)) &&
blocks->confirmed[0] != BLOCKS_ORPHAN &&
blocks->confirmed[0] != BLOCKS_REJECT)
break;
b_item = prev_in_ktree(b_ctx);
}
// Setup prev_createdate
if (b_item) {
/* prev_createdate is the ok b_item (before oldest_height)
* _set_blockcreatedate() should always be called
* before calling _set_prevcreatedate() */
copy_tv(&prev_createdate, &(blocks->blockcreatedate));
/* Move b_item forward to the next block
* since we don't have the prev value for b_item and
* also don't need to update the b_item block */
curr_height = blocks->height;
STRNCPY(curr_blockhash, blocks->blockhash);
while (b_item && blocks->height == curr_height &&
strcmp(blocks->blockhash, curr_blockhash) == 0) {
b_item = next_in_ktree(b_ctx);
DATA_BLOCKS_NULL(blocks, b_item);
}
} else {
/* There's none before oldest_height, so instead use:
* 'Pool Start' = first workinfo createdate */
K_RLOCK(workinfo_free);
wi_item = first_in_ktree(workinfo_root, wi_ctx);
K_RUNLOCK(workinfo_free);
if (wi_item) {
DATA_WORKINFO(workinfo, wi_item);
copy_tv(&prev_createdate, &(workinfo->createdate));
} else {
/* Shouldn't be possible since this function is first
* called after workinfo is loaded and the workinfo
* for each block must exist - thus data corruption */
DATE_ZERO(&prev_createdate);
LOGEMERG("%s() DB/tree corruption - blocks exist but "
"no workinfo exist!"
WHERE_FFL,
__func__, WHERE_FFL_PASS);
ok = false;
}
b_item = first_in_ktree(blocks_root, b_ctx);
}
// curr_* is unset and will be set first time in the while loop
curr_height = 0;
curr_blockhash[0] = '\0';
DATE_ZERO(&curr_createdate);
currok = false;
while (b_item) {
DATA_BLOCKS(blocks, b_item);
// While the same block, keep setting it
if (blocks->height == curr_height &&
strcmp(blocks->blockhash, curr_blockhash) == 0) {
copy_tv(&(blocks->prevcreatedate), &prev_createdate);
} else {
// Next block - if currok then 'prev' becomes 'curr'
if (currok)
copy_tv(&prev_createdate, &curr_createdate);
// New curr - CURRENT will be first
if (!CURRENT(&(blocks->expirydate))) {
tv_to_buf(&(blocks->expirydate), cd_buf,
sizeof(cd_buf));
LOGEMERG("%s() block %"PRId32"/%s/%s/%s first "
"record is not CURRENT" WHERE_FFL,
__func__,
blocks->height, blocks->blockhash,
blocks->confirmed, cd_buf,
WHERE_FFL_PASS);
ok = false;
}
curr_height = blocks->height;
STRNCPY(curr_blockhash, blocks->blockhash);
copy_tv(&curr_createdate, &(blocks->blockcreatedate));
if (CURRENT(&(blocks->expirydate)) &&
blocks->confirmed[0] != BLOCKS_ORPHAN &&
blocks->confirmed[0] != BLOCKS_REJECT)
currok = true;
else
currok = false;
// Set it
copy_tv(&(blocks->prevcreatedate), &prev_createdate);
}
b_item = next_in_ktree(b_ctx);
}
return ok;
}
/* order by payoutid asc,userid asc,expirydate asc
* i.e. only one payout amount per block per user */
cmp_t cmp_miningpayouts(K_ITEM *a, K_ITEM *b)
@ -3031,8 +3208,7 @@ K_ITEM *first_miningpayouts(int64_t payoutid, K_TREE_CTX *ctx)
miningpayouts.payoutid = payoutid;
miningpayouts.userid = 0;
miningpayouts.expirydate.tv_sec = 0;
miningpayouts.expirydate.tv_usec = 0;
DATE_ZERO(&(miningpayouts.expirydate));
INIT_MININGPAYOUTS(&look);
look.data = (void *)(&miningpayouts);
@ -3109,6 +3285,22 @@ cmp_t cmp_payouts_id(K_ITEM *a, K_ITEM *b)
return c;
}
/* order by workinfoidend asc,expirydate asc
* This must use workinfoidend, not workinfoidstart, since a change
* in the payout PPLNS N could have 2 payouts with the same start,
* but currently there is only one payout per block
* i.e. workinfoidend can only have one payout */
cmp_t cmp_payouts_wid(K_ITEM *a, K_ITEM *b)
{
PAYOUTS *pa, *pb;
DATA_PAYOUTS(pa, a);
DATA_PAYOUTS(pb, b);
cmp_t c = CMP_BIGINT(pa->workinfoidend, pb->workinfoidend);
if (c == 0)
c = CMP_TV(pa->expirydate, pb->expirydate);
return c;
}
K_ITEM *find_payouts(int32_t height, char *blockhash)
{
PAYOUTS payouts;
@ -3157,6 +3349,24 @@ K_ITEM *find_payoutid(int64_t payoutid)
return find_in_ktree(payouts_id_root, &look, cmp_payouts_id, ctx);
}
// First payouts workinfoidend equal or before workinfoidend
K_ITEM *find_payouts_wid(int64_t workinfoidend, K_TREE_CTX *ctx)
{
PAYOUTS payouts;
K_TREE_CTX ctx0[1];
K_ITEM look;
if (ctx == NULL)
ctx = ctx0;
payouts.workinfoidend = workinfoidend+1;
DATE_ZERO(&(payouts.expirydate));
INIT_PAYOUTS(&look);
look.data = (void *)(&payouts);
return find_before_in_ktree(payouts_wid_root, &look, cmp_payouts_wid, ctx);
}
/* Values from payout stats, returns -1 if statname isn't found
* If code needs a value then it probably really should be a new payouts field
* rather than stored in the stats passed to the pplns2 web page
@ -3252,9 +3462,9 @@ bool process_pplns(int32_t height, char *blockhash, tv_t *addr_cd)
PAYMENTS *payments;
WORKINFO *workinfo;
PAYOUTS *payouts, *payouts2;
BLOCKS *blocks, *blocks2;
BLOCKS *blocks;
USERS *users;
K_ITEM *p_item, *old_p_item, *b_item, *b2_item, *w_item, *wb_item;
K_ITEM *p_item, *old_p_item, *b_item, *w_item, *wb_item;
K_ITEM *u_item, *mu_item, *oc_item, *pay_item, *p2_item, *old_p2_item;
SHARESUMMARY looksharesummary, *sharesummary;
WORKMARKERS lookworkmarkers, *workmarkers;
@ -3302,37 +3512,16 @@ bool process_pplns(int32_t height, char *blockhash, tv_t *addr_cd)
// Check the block status
K_RLOCK(blocks_free);
b_item = find_blocks(height, blockhash, b_ctx);
if (!b_item) {
K_RUNLOCK(blocks_free);
if (!b_item) {
LOGERR("%s(): no block %"PRId32"/%s for payout",
__func__, height, blockhash);
goto oku;
}
DATA_BLOCKS(blocks, b_item);
b2_item = b_item;
DATA_BLOCKS(blocks2, b2_item);
while (b2_item && blocks2->height == height &&
strcmp(blocks2->blockhash, blockhash) == 0) {
if (blocks2->confirmed[0] == BLOCKS_NEW) {
copy_tv(&end_tv, &(blocks2->createdate));
copy_tv(&end_tv, &(blocks->blockcreatedate));
if (!addr_cd)
addr_cd = &(blocks2->createdate);
break;
}
b2_item = next_in_ktree(b_ctx);
DATA_BLOCKS_NULL(blocks2, b2_item);
}
K_RUNLOCK(blocks_free);
// If addr_cd was null it should've been set to the block NEW createdate
if (!addr_cd || end_tv.tv_sec == 0) {
LOGEMERG("%s(): missing %s record for block %"PRId32
"/%"PRId64"/%s/%s/%"PRId64,
__func__, blocks_confirmed(BLOCKS_NEW_STR),
blocks->height, blocks->workinfoid,
blocks->workername, blocks->confirmed,
blocks->reward);
goto oku;
}
addr_cd = &(blocks->blockcreatedate);
LOGDEBUG("%s(): block %"PRId32"/%"PRId64"/%s/%s/%"PRId64,
__func__, blocks->height, blocks->workinfoid,
@ -3363,11 +3552,11 @@ bool process_pplns(int32_t height, char *blockhash, tv_t *addr_cd)
// Get the PPLNS N values
K_RLOCK(optioncontrol_free);
oc_item = find_optioncontrol(PPLNSDIFFTIMES, &(blocks->createdate),
oc_item = find_optioncontrol(PPLNSDIFFTIMES, &(blocks->blockcreatedate),
height);
K_RUNLOCK(optioncontrol_free);
if (!oc_item) {
tv_to_buf(&(blocks->createdate), cd_buf, sizeof(cd_buf));
tv_to_buf(&(blocks->blockcreatedate), cd_buf, sizeof(cd_buf));
LOGEMERG("%s(): missing optioncontrol %s (%s/%"PRId32")",
__func__, PPLNSDIFFTIMES, cd_buf, blocks->height);
goto oku;
@ -3376,11 +3565,11 @@ bool process_pplns(int32_t height, char *blockhash, tv_t *addr_cd)
diff_times = atof(optioncontrol->optionvalue);
K_RLOCK(optioncontrol_free);
oc_item = find_optioncontrol(PPLNSDIFFADD, &(blocks->createdate),
oc_item = find_optioncontrol(PPLNSDIFFADD, &(blocks->blockcreatedate),
height);
K_RUNLOCK(optioncontrol_free);
if (!oc_item) {
tv_to_buf(&(blocks->createdate), cd_buf, sizeof(cd_buf));
tv_to_buf(&(blocks->blockcreatedate), cd_buf, sizeof(cd_buf));
LOGEMERG("%s(): missing optioncontrol %s (%s/%"PRId32")",
__func__, PPLNSDIFFADD, cd_buf, blocks->height);
goto oku;
@ -3431,7 +3620,7 @@ bool process_pplns(int32_t height, char *blockhash, tv_t *addr_cd)
end_workinfoid = sharesummary->workinfoid;
/* Add up all sharesummaries until >= diff_want
* also record the latest lastshare - that will be the end pplns time
* which will be >= blocks->createdate */
* which will be >= blocks->blockcreatedate */
while (total_diff < diff_want && ss_item) {
switch (sharesummary->complete[0]) {
case SUMMARY_CONFIRM:
@ -3584,7 +3773,7 @@ bool process_pplns(int32_t height, char *blockhash, tv_t *addr_cd)
* block - so abort
* The fix is to create the marks and summaries needed via
* cmd_marks() then manually trigger the payout generation
* TODO: via cmd_payouts() ... which isn't available yet */
* via cmd_payouts() */
LOGEMERG("%s(): payout had < 1 (%"PRId64") workmarkers for "
"block %"PRId32"/%"PRId64"/%s/%s/%"PRId64
" beginwi=%"PRId64" ss=%"PRId64" diff=%.1f",
@ -3634,6 +3823,7 @@ bool process_pplns(int32_t height, char *blockhash, tv_t *addr_cd)
bzero(payouts, sizeof(*payouts));
payouts->height = height;
STRNCPY(payouts->blockhash, blockhash);
copy_tv(&(payouts->blockcreatedate), &(blocks->blockcreatedate));
d64 = blocks->reward * 9 / 1000;
g64 = blocks->reward - d64;
payouts->minerreward = g64;
@ -3805,20 +3995,8 @@ bool process_pplns(int32_t height, char *blockhash, tv_t *addr_cd)
pa_item = pa_item->next;
}
} else {
/* Address user or normal user without a paymentaddress
* TODO: user table needs a flag to say which it is ...
* for now use a simple test */
bool gotaddr = false;
size_t len;
switch (users->username[0]) {
case '1':
case '3':
len = strlen(users->username);
if (len >= ADDR_MIN_LEN && len <= ADDR_MAX_LEN)
gotaddr = true;
}
if (gotaddr) {
/* Address user or normal user without a paymentaddress */
if (users->userbits & USER_ADDRESS) {
K_WLOCK(payments_free);
pay_item = k_unlink_head(payments_free);
K_WUNLOCK(payments_free);
@ -3901,6 +4079,9 @@ bool process_pplns(int32_t height, char *blockhash, tv_t *addr_cd)
ss_count, wm_count, ms_count, usercount, diff_times,
diff_add, cd_buf);
/* At this point the payout is complete, but it just hasn't been
* flagged complete yet in the DB */
K_WLOCK(payouts_free);
p2_item = k_unlink_head(payouts_free);
K_WUNLOCK(payouts_free);
@ -3909,6 +4090,7 @@ bool process_pplns(int32_t height, char *blockhash, tv_t *addr_cd)
payouts2->payoutid = payouts->payoutid;
payouts2->height = payouts->height;
STRNCPY(payouts2->blockhash, payouts->blockhash);
copy_tv(&(payouts2->blockcreatedate), &(payouts->blockcreatedate));
payouts2->minerreward = payouts->minerreward;
payouts2->workinfoidstart = payouts->workinfoidstart;
payouts2->workinfoidend = payouts->workinfoidend;
@ -3926,13 +4108,20 @@ bool process_pplns(int32_t height, char *blockhash, tv_t *addr_cd)
ok = payouts_add(conn, true, p2_item, &old_p2_item, (char *)by_default,
(char *)__func__, (char *)inet_default, &now, NULL,
false);
if (!ok) {
/* All that's required is to mark the payout GENERATED
* since it already exists in the DB and in RAM, thus a manual
* cmd_payouts 'generated' is all that's needed to fix it */
LOGEMERG("%s(): payout %"PRId64" for block %"PRId32"/%s "
"NOT set generated - it needs to be set manually",
__func__, payouts->payoutid, blocks->height,
blocks->blockhash);
}
// Flag each shift as rewarded
reward_shifts(payouts2, true, 1);
CKPQDisco(&conn, conned);
goto oku;
@ -4141,7 +4330,7 @@ K_ITEM *_find_markersummary(int64_t markerid, int64_t workinfoid,
K_TREE_CTX ctx[1];
if (markerid == 0) {
wm_item = find_workmarkers(workinfoid, false, MARKER_PROCESSED);
wm_item = find_workmarkers(workinfoid, false, MARKER_PROCESSED, NULL);
if (wm_item) {
DATA_WORKMARKERS(wm, wm_item);
markerid = wm->markerid;
@ -4272,12 +4461,15 @@ cmp_t cmp_workmarkers_workinfoid(K_ITEM *a, K_ITEM *b)
return c;
}
K_ITEM *find_workmarkers(int64_t workinfoid, bool anystatus, char status)
K_ITEM *find_workmarkers(int64_t workinfoid, bool anystatus, char status, K_TREE_CTX *ctx)
{
WORKMARKERS workmarkers, *wm;
K_TREE_CTX ctx[1];
K_TREE_CTX ctx0[1];
K_ITEM look, *wm_item;
if (ctx == NULL)
ctx = ctx0;
workmarkers.expirydate.tv_sec = default_expiry.tv_sec;
workmarkers.expirydate.tv_usec = default_expiry.tv_usec;
workmarkers.workinfoidend = workinfoid-1;
@ -4363,8 +4555,7 @@ static bool gen_workmarkers(PGconn *conn, MARKS *stt, bool after, MARKS *fin,
look.data = (void *)(&workinfo);
K_RLOCK(workinfo_free);
if (before) {
workinfo.expirydate.tv_sec = 0;
workinfo.expirydate.tv_usec = 0;
DATE_ZERO(&(workinfo.expirydate));
wi_fin_item = find_before_in_ktree(workinfo_root, &look,
cmp_workinfo, ctx);
while (wi_fin_item) {
@ -4396,7 +4587,8 @@ static bool gen_workmarkers(PGconn *conn, MARKS *stt, bool after, MARKS *fin,
* sort order and matching errors */
if (wi_fin->workinfoid >= wi_stt->workinfoid) {
K_RLOCK(workmarkers_free);
old_wm_item = find_workmarkers(wi_fin->workinfoid, true, '\0');
old_wm_item = find_workmarkers(wi_fin->workinfoid, true, '\0',
NULL);
K_RUNLOCK(workmarkers_free);
DATA_WORKMARKERS_NULL(old_wm, old_wm_item);
if (old_wm_item && (WMREADY(old_wm->status) ||
@ -4575,6 +4767,68 @@ bool workmarkers_generate(PGconn *conn, char *err, size_t siz, char *by,
return true;
}
// delta = 1 or -1 i.e. reward or undo reward
bool reward_shifts(PAYOUTS *payouts, bool lock, int delta)
{
// TODO: PPS calculations
K_TREE_CTX ctx[1];
K_ITEM *wm_item;
WORKMARKERS *wm;
bool did_one = false;
if (lock)
K_WLOCK(workmarkers_free);
wm_item = find_workmarkers(payouts->workinfoidstart, false,
MARKER_PROCESSED, ctx);
while (wm_item) {
DATA_WORKMARKERS(wm, wm_item);
if (wm->workinfoidstart > payouts->workinfoidend)
break;
/* The status doesn't matter since we want the rewards passed
* onto the PROCESSED status if it isn't already processed */
if (CURRENT(&(wm->expirydate))) {
wm->rewards += delta;
did_one = true;
}
wm_item = next_in_ktree(ctx);
}
if (lock)
K_WUNLOCK(workmarkers_free);
return did_one;
}
// (re)calculate rewards for a shift
bool shift_rewards(K_ITEM *wm_item)
{
PAYOUTS *payouts = NULL;
K_TREE_CTX ctx[1];
WORKMARKERS *wm;
K_ITEM *p_item;
int rewards = 0;
DATA_WORKMARKERS(wm, wm_item);
// Deadlock risk since calling code should have workmarkers locked
K_RLOCK(payouts_free);
p_item = find_payouts_wid(wm->workinfoidend, ctx);
DATA_PAYOUTS_NULL(payouts, p_item);
// a workmarker should not cross a payout boundary
while (p_item && payouts->workinfoidstart <= wm->workinfoidstart &&
wm->workinfoidend <= payouts->workinfoidend) {
if (CURRENT(&(payouts->expirydate)))
rewards++;
p_item = prev_in_ktree(ctx);
DATA_PAYOUTS_NULL(payouts, p_item);
}
K_RUNLOCK(payouts_free);
wm->rewards = rewards;
return (rewards > 0);
}
// order by expirydate asc,workinfoid asc
// TODO: add poolinstance
cmp_t cmp_marks(K_ITEM *a, K_ITEM *b)
@ -4897,7 +5151,7 @@ void _userinfo_update(SHARES *shares, SHARESUMMARY *sharesummary,
}
// N.B. good blocks = blocks - (orphans + rejects)
void _userinfo_block(BLOCKS *blocks, enum info_type isnew, bool lock)
void _userinfo_block(BLOCKS *blocks, enum info_type isnew, int delta, bool lock)
{
USERINFO *row;
K_ITEM *item;
@ -4907,12 +5161,12 @@ void _userinfo_block(BLOCKS *blocks, enum info_type isnew, bool lock)
if (lock)
K_WLOCK(userinfo_free);
if (isnew == INFO_NEW) {
row->blocks++;
row->blocks += delta;
copy_tv(&(row->last_block), &(blocks->createdate));
} else if (isnew == INFO_ORPHAN)
row->orphans++;
row->orphans += delta;
else if (isnew == INFO_REJECT)
row->rejects++;
row->rejects += delta;
if (lock)
K_WUNLOCK(userinfo_free);

131
src/ckdb_dbio.c

@ -1079,11 +1079,11 @@ K_ITEM *useratts_add(PGconn *conn, char *username, char *attname,
else
TXT_TO_BIGINT("attnum2", attnum2, row->attnum2);
if (attdate == NULL || attdate[0] == '\0')
row->attdate.tv_sec = row->attdate.tv_usec = 0L;
DATE_ZERO(&(row->attdate));
else
TXT_TO_TV("attdate", attdate, row->attdate);
if (attdate2 == NULL || attdate2[0] == '\0')
row->attdate2.tv_sec = row->attdate2.tv_usec = 0L;
DATE_ZERO(&(row->attdate2));
else
TXT_TO_TV("attdate2", attdate2, row->attdate2);
@ -2922,7 +2922,8 @@ bool workinfo_fill(PGconn *conn)
if (!ok) {
free_workinfo_data(item);
k_add_head(workinfo_free, item);
}
} else
ok = set_prevcreatedate(0);
//K_WUNLOCK(workinfo_free);
PQclear(res);
@ -2959,7 +2960,7 @@ static bool shares_process(PGconn *conn, SHARES *shares, K_TREE *trf_root)
if (reloading && !confirm_sharesummary) {
// We only need to know if the workmarker is processed
wm_item = find_workmarkers(shares->workinfoid, false,
MARKER_PROCESSED);
MARKER_PROCESSED, NULL);
if (wm_item) {
LOGDEBUG("%s(): workmarker exists for wid %"PRId64
" %"PRId64"/%s/%ld,%ld",
@ -3260,7 +3261,7 @@ static bool shareerrors_process(PGconn *conn, SHAREERRORS *shareerrors,
if (reloading && !confirm_sharesummary) {
// We only need to know if the workmarker is processed
wm_item = find_workmarkers(shareerrors->workinfoid, false,
MARKER_PROCESSED);
MARKER_PROCESSED, NULL);
if (wm_item) {
LOGDEBUG("%s(): workmarker exists for wid %"PRId64
" %"PRId64"/%s/%ld,%ld",
@ -4024,7 +4025,8 @@ bool _sharesummary_update(SHARES *s_row, SHAREERRORS *e_row, K_ITEM *ss_item,
}
K_RLOCK(workmarkers_free);
wm_item = find_workmarkers(workinfoid, false, MARKER_PROCESSED);
wm_item = find_workmarkers(workinfoid, false, MARKER_PROCESSED,
NULL);
K_RUNLOCK(workmarkers_free);
if (wm_item) {
DATA_WORKMARKERS(wm, wm_item);
@ -4298,6 +4300,7 @@ unparam:
blocks_root = add_to_ktree(blocks_root, b_item, cmp_blocks);
k_add_head(blocks_store, b_item);
blocks_stats_rebuild = true;
// 'confirmed' is unchanged so no need to recalc *createdate
}
K_WUNLOCK(blocks_free);
@ -4438,7 +4441,7 @@ bool blocks_add(PGconn *conn, char *height, char *blockhash,
}
// We didn't use a Begin
ok = true;
userinfo_block(row, INFO_NEW);
userinfo_block(row, INFO_NEW, 1);
goto unparam;
break;
case BLOCKS_ORPHAN:
@ -4597,10 +4600,49 @@ bool blocks_add(PGconn *conn, char *height, char *blockhash,
}
update_old = true;
if (confirmed[0] == BLOCKS_ORPHAN)
userinfo_block(row, INFO_ORPHAN);
else if (confirmed[0] == BLOCKS_REJECT)
userinfo_block(row, INFO_REJECT);
/* handle confirmed state changes for userinfo
* this case statement handles all possible combinations
* even if they can't happen (yet) */
switch (oldblocks->confirmed[0]) {
case BLOCKS_ORPHAN:
switch (confirmed[0]) {
case BLOCKS_ORPHAN:
break;
case BLOCKS_REJECT:
userinfo_block(row, INFO_ORPHAN, -1);
userinfo_block(row, INFO_REJECT, 1);
break;
default:
userinfo_block(row, INFO_ORPHAN, -1);
break;
}
break;
case BLOCKS_REJECT:
switch (confirmed[0]) {
case BLOCKS_REJECT:
break;
case BLOCKS_ORPHAN:
userinfo_block(row, INFO_REJECT, -1);
userinfo_block(row, INFO_ORPHAN, 1);
break;
default:
userinfo_block(row, INFO_REJECT, -1);
break;
}
break;
default:
switch (confirmed[0]) {
case BLOCKS_ORPHAN:
userinfo_block(row, INFO_ORPHAN, 1);
break;
case BLOCKS_REJECT:
userinfo_block(row, INFO_REJECT, 1);
break;
default:
break;
}
break;
}
break;
default:
LOGERR("%s(): %s.failed.invalid confirm='%s'",
@ -4638,6 +4680,9 @@ flail:
blocks_root = add_to_ktree(blocks_root, b_item, cmp_blocks);
k_add_head(blocks_store, b_item);
blocks_stats_rebuild = true;
// recalc the *createdate fields for possibly affected blocks
set_blockcreatedate(row->height);
set_prevcreatedate(row->height);
}
K_WUNLOCK(blocks_free);
@ -4711,6 +4756,7 @@ bool blocks_fill(PGconn *conn)
{
ExecStatusType rescode;
PGresult *res;
K_TREE_CTX ctx[1];
K_ITEM *item;
int n, i;
BLOCKS *row;
@ -4862,16 +4908,30 @@ bool blocks_fill(PGconn *conn)
pool.height = row->height;
}
// first add all the NEW blocks
if (row->confirmed[0] == BLOCKS_NEW)
_userinfo_block(row, INFO_NEW, 1, false);
}
if (!ok)
k_add_head(blocks_free, item);
else
ok = set_blockcreatedate(0);
// Now update all the CURRENT orphan/reject stats
if (ok) {
item = first_in_ktree(blocks_root, ctx);
while (item) {
DATA_BLOCKS(row, item);
if (CURRENT(&(row->expirydate))) {
_userinfo_block(row, INFO_NEW, false);
if (row->confirmed[0] == BLOCKS_ORPHAN)
_userinfo_block(row, INFO_ORPHAN, false);
_userinfo_block(row, INFO_ORPHAN, 1, false);
else if (row->confirmed[0] == BLOCKS_REJECT)
_userinfo_block(row, INFO_REJECT, false);
_userinfo_block(row, INFO_REJECT, 1, false);
}
item = next_in_ktree(ctx);
}
}
if (!ok)
k_add_head(blocks_free, item);
K_WUNLOCK(blocks_free);
PQclear(res);
@ -5118,12 +5178,15 @@ void payouts_add_ram(bool ok, K_ITEM *p_item, K_ITEM *old_p_item, tv_t *cd)
DATA_PAYOUTS(oldp, old_p_item);
payouts_root = remove_from_ktree(payouts_root, old_p_item, cmp_payouts);
payouts_id_root = remove_from_ktree(payouts_id_root, old_p_item, cmp_payouts_id);
payouts_wid_root = remove_from_ktree(payouts_wid_root, old_p_item, cmp_payouts_wid);
copy_tv(&(oldp->expirydate), cd);
payouts_root = add_to_ktree(payouts_root, old_p_item, cmp_payouts);
payouts_id_root = add_to_ktree(payouts_id_root, old_p_item, cmp_payouts_id);
payouts_wid_root = add_to_ktree(payouts_wid_root, old_p_item, cmp_payouts_wid);
}
payouts_root = add_to_ktree(payouts_root, p_item, cmp_payouts);
payouts_id_root = add_to_ktree(payouts_id_root, p_item, cmp_payouts_id);
payouts_wid_root = add_to_ktree(payouts_wid_root, p_item, cmp_payouts_wid);
k_add_head(payouts_store, p_item);
}
K_WUNLOCK(payouts_free);
@ -5430,9 +5493,11 @@ K_ITEM *payouts_full_expire(PGconn *conn, int64_t payoutid, tv_t *now, bool lock
DATA_PAYOUTS(payouts, po_item);
payouts_root = remove_from_ktree(payouts_root, po_item, cmp_payouts);
payouts_id_root = remove_from_ktree(payouts_id_root, po_item, cmp_payouts_id);
payouts_wid_root = remove_from_ktree(payouts_wid_root, po_item, cmp_payouts_wid);
copy_tv(&(payouts->expirydate), now);
payouts_root = add_to_ktree(payouts_root, po_item, cmp_payouts);
payouts_id_root = add_to_ktree(payouts_id_root, po_item, cmp_payouts_id);
payouts_wid_root = add_to_ktree(payouts_wid_root, po_item, cmp_payouts_wid);
mp_item = first_miningpayouts(payoutid, mp_ctx);
DATA_MININGPAYOUTS_NULL(mp, mp_item);
@ -5465,6 +5530,12 @@ K_ITEM *payouts_full_expire(PGconn *conn, int64_t payoutid, tv_t *now, bool lock
DATA_PAYMENTS_NULL(payments, pm_item);
}
if (PAYGENERATED(payouts->status)) {
// Original was generated, so undo the reward
reward_shifts(payouts, true, -1);
}
ok = true;
matane:
if (begun)
@ -5494,8 +5565,10 @@ bool payouts_fill(PGconn *conn)
{
ExecStatusType rescode;
PGresult *res;
K_ITEM *item;
K_ITEM *item, *b_item;
K_TREE_CTX ctx[1];
PAYOUTS *row;
BLOCKS *blocks;
int n, i;
char *field;
char *sel;
@ -5609,10 +5682,30 @@ bool payouts_fill(PGconn *conn)
if (!ok)
break;
// This also of course, verifies the payouts -> blocks reference
b_item = find_blocks(row->height, row->blockhash, ctx);
if (!b_item) {
LOGERR("%s(): payoutid %"PRId64" references unknown "
"block %"PRId32"/%s",
__func__, row->payoutid, row->height,
row->blockhash);
ok = false;
break;
} else {
// blockcreatedate will already be set
DATA_BLOCKS(blocks, b_item);
copy_tv(&(row->blockcreatedate),
&(blocks->blockcreatedate));
}
payouts_root = add_to_ktree(payouts_root, item, cmp_payouts);
payouts_id_root = add_to_ktree(payouts_id_root, item, cmp_payouts_id);
payouts_wid_root = add_to_ktree(payouts_wid_root, item, cmp_payouts_wid);
k_add_head(payouts_store, item);
if (CURRENT(&(row->expirydate)) && PAYGENERATED(row->status))
reward_shifts(row, false, 1);
tick();
}
if (!ok) {
@ -6517,7 +6610,7 @@ bool _workmarkers_process(PGconn *conn, bool already, bool add,
if (markerid == 0) {
K_RLOCK(workmarkers_free);
old_wm_item = find_workmarkers(workinfoidend, true, '\0');
old_wm_item = find_workmarkers(workinfoidend, true, '\0', NULL);
K_RUNLOCK(workmarkers_free);
} else {
K_RLOCK(workmarkers_free);
@ -6691,6 +6784,8 @@ unparam:
cmp_workmarkers_workinfoid);
}
if (wm_item) {
shift_rewards(wm_item);
workmarkers_root = add_to_ktree(workmarkers_root,
wm_item,
cmp_workmarkers);

10
src/ktree.c

@ -89,15 +89,11 @@ static void show_ktree(K_TREE *root, char *path, int pos, char *(*dsp_funct)(K_I
path[pos] = '\0';
switch(root->red)
{
case RED_RED:
if (root->red == RED_RED)
col = 'R';
break;
case RED_BLACK:
else
// if (root->red == RED_BLACK)
col = 'B';
break;
}
printf(" %c %s=%s\n", col, path, dsp_funct(root->data));

Loading…
Cancel
Save