Browse Source

Merge branch 'master' into proxydev

Conflicts:
	src/ckpool.c
	src/stratifier.c
master
Con Kolivas 10 years ago
parent
commit
167b2dce2d
  1. 327
      pool/page_pplns2.php
  2. 1
      pool/prime.php
  3. 51
      sql/ckdb.sql
  4. 1
      sql/initid.sh
  5. 101
      sql/v0.9.6-v1.0.0.sql
  6. 80
      src/ckdb.c
  7. 126
      src/ckdb.h
  8. 590
      src/ckdb_cmd.c
  9. 1063
      src/ckdb_data.c
  10. 757
      src/ckdb_dbio.c
  11. 11
      src/ckpool.c
  12. 9
      src/ckpool.h
  13. 65
      src/stratifier.c

327
pool/page_pplns2.php

@ -0,0 +1,327 @@
<?php
#
function stnum($num)
{
$b4 = '';
$af = '';
$fmt = number_format($num, 0);
if ($num > 99999999)
$b4 = '<span class=urg>';
else if ($num > 9999999)
$b4 = '<span class=warn>';
if ($b4 != '')
$af = '</span>';
return $b4.$fmt.$af;
}
#
# ... Of course ... check the output and add the txin ... etc.
function calctx($ans, $count, $miner_sat, $diffacc_total)
{
$pg = '<br><table cellpadding=0 cellspacing=0 border=0>';
$pg .= '<tr><td>';
$dust = getparam('dust', true);
if (nuem($dust) || $dust <= 0)
$dust = 10000;
$fee = getparam('fee', true);
if (nuem($fee) || $fee < 0)
$fee = 0;
$fee *= 100000000;
$adr = array();
$ers = '';
$unpaid = 0;
$change = $miner_sat;
$dust_amt = 0; # not included in $change
for ($i = 0; $i < $count; $i++)
{
$username = $ans['user:'.$i];
$diffacc_user = $ans['diffacc:'.$i];
$pay_sat = $ans['amount:'.$i];
$payaddress = $ans['payaddress:'.$i];
if ($payaddress == 'none')
{
$c0 = substr($username, 0, 1);
$parts = explode('.', $username);
$len = strlen($parts[0]);
if (($c0 == '1' || $c0 == '3') && $len > 26 && $len < 37)
$payaddress = $parts[0];
else
{
if ($pay_sat > 0)
{
$dd = '';
if ($pay_sat < $dust)
$dd = ' (dust)';
$ers .= "No address for '$username'$dd<br>";
}
$unpaid += $pay_sat;
continue;
}
}
if (isset($adr[$payaddress]))
$adr[$payaddress] += $pay_sat;
else
$adr[$payaddress] = $pay_sat;
$change -= $pay_sat;
}
$txout = '';
$comma = '';
foreach ($adr as $payaddress => $pay_sat)
{
if ($pay_sat < $dust)
$dust_amt += $pay_sat;
else
{
$txout .= "$comma\"$payaddress\":".btcfmt($pay_sat);
$comma = ', ';
}
}
if ($change > 0 || $dust_amt > 0 || $change < $fee)
{
$pg .= "<span class=err>Dust limit = $dust = ".btcfmt($dust);
$pg .= ", Dust amount = $dust_amt = ".btcfmt($dust_amt);
$pg .= ",<br>Upaid = $unpaid = ".btcfmt($unpaid);
$pg .= ", Change = $change = ".btcfmt($change);
$pg .= ",<br>Fee = $fee = ".btcfmt($fee)."</span><br>";
if ($change < $fee)
$ers .= "Change ($change) is less than Fee ($fee)<br>";
if (($dust_amt + $change - $fee) > 0)
{
$txout .= "$comma\"&lt;changeaddress&gt;\":";
$txout .= btcfmt($dust_amt + $change - $fee);
$comma = ', ';
}
}
if (strlen($ers) > 0)
$pg .= "<span class=err>$ers</span><br>";
$txn = '[{"txid":"&lt;txid1&gt;","vout":&lt;n&gt;},';
$txn .= '{"txid":"&lt;txid2&gt;","vout":&lt;n&gt;}] ';
$txn .= '{'.$txout.'}<br>';
$pg .= $txn.'</td></tr></table>';
return $pg;
}
#
function fmtdata($code, $val)
{
switch ($code)
{
case ',':
$ret = number_format($val);
break;
case '.':
$ret = number_format($val, 1);
break;
default:
$ret = $val;
}
return $ret;
}
#
function dopplns2($data, $user)
{
global $send_sep;
$pg = '<h1>CKPool</h1>';
$blk = getparam('blk', true);
if (nuem($blk))
{
$tx = '';
# so can make a link
$blkuse = getparam('blkuse', true);
if (nuem($blkuse))
$blkuse = '';
else
$tx = 'y';
$pg = '<br>'.makeForm('pplns2')."
Block: <input type=text name=blk size=10 value='$blkuse'>
&nbsp; Tx: <input type=text name=tx size=1 value='$tx'>
&nbsp; Dust (Satoshi): <input type=text name=dust size=5 value='10000'>
&nbsp; Fee (BTC): <input type=text name=fee size=5 value='0.0'>
&nbsp;<input type=submit name=Calc value=Calc>
</form>";
}
else
{
$tx = getparam('tx', true);
if (nuem($tx) || substr($tx, 0, 1) != 'y')
$dotx = false;
else
$dotx = true;
$flds = array('height' => $blk);
$msg = msgEncode('pplns2', 'pplns2', $flds, $user);
$rep = sendsockreply('pplns2', $msg, 4);
if ($rep == false)
$ans = array();
else
$ans = repDecode($rep);
if ($ans['ERROR'] != null)
return '<font color=red size=+1><br>'.$ans['STATUS'].': '.$ans['ERROR'].'</font>';
if (!isset($ans['pplns_last']))
return '<font color=red size=+1><br>Partial data returned</font>';
$reward_sat = $ans['block_reward'];
$miner_sat = $ans['miner_reward'];
$ans['miner_sat'] = $miner_sat;
$data = array( 'Block' => 'block',
'Block Status' => 'block_status',
'Block Hash' => 'block_hash',
'Block Reward (Satoshis)' => 'block_reward',
'Miner Reward (Satoshis)' => 'miner_sat',
'PPLNS Wanted' => '.diff_want',
'PPLNS Used' => '.diffacc_total',
'Elapsed Seconds' => ',pplns_elapsed',
'Users' => 'rows',
'Oldest Workinfoid' => 'begin_workinfoid',
'Oldest Time' => 'begin_stamp',
'Oldest Epoch' => 'begin_epoch',
'Block Workinfoid' => 'block_workinfoid',
'Block Time' => 'block_stamp',
'Block Epoch' => 'block_epoch',
'Newest Workinfoid' => 'end_workinfoid',
'Newest Share Time' => 'end_stamp',
'Newest Share Epoch' => 'end_epoch',
'Network Difficulty' => 'block_ndiff',
'PPLNS Factor' => 'diff_times',
'PPLNS Added' => 'diff_add',
'Accepted Share Count' => ',acc_share_count',
'Total Share Count' => ',total_share_count',
'ShareSummary Count' => ',ss_count',
'WorkMarkers Count' => ',wm_count',
'MarkerSummary Count' => ',ms_count');
$pg = '<br><a href=https://blockchain.info/block-height/';
$pg .= $ans['block'].'>Blockchain '.$ans['block']."</a><br>\n";
if (strlen($ans['marks_status']) > 0)
{
$pg .= '<br><span class=err>';
$msg = $ans['marks_status'];
$pg .= str_replace(' ', '&nbsp;', $msg)."</span><br>\n";
}
if (strlen($ans['block_extra']) > 0)
{
$pg .= '<br><span class=err>';
$msg = $ans['block_status'].' - '.$ans['block_extra'];
$pg .= str_replace(' ', '&nbsp;', $msg)."</span><br>\n";
}
if (strlen($ans['share_status']) > 0)
{
$pg .= '<br><span class=err>';
$msg = $ans['share_status']." - Can't be paid out yet";
$pg .= str_replace(' ', '&nbsp;', $msg)."</span><br>\n";
}
$pg .= "<br><table callpadding=0 cellspacing=0 border=0>\n";
$pg .= '<tr class=title>';
$pg .= '<td class=dl>Name</td>';
$pg .= '<td class=dr>Value</td>';
$pg .= "</tr>\n";
$i = 0;
foreach ($data as $dsp => $name)
{
if (($i++ % 2) == 0)
$row = 'even';
else
$row = 'odd';
$pg .= "<tr class=$row>";
$pg .= "<td class=dl>$dsp</td>";
switch ($name[0])
{
case ',':
case '.':
$nm = substr($name, 1);
$fmt = fmtdata($name[0], $ans[$nm]);
break;
default:
$fmt = $ans[$name];
break;
}
$pg .= "<td class=dr>$fmt</td>";
$pg .= "</tr>\n";
}
$pg .= "</table><br><table cellpadding=0 cellspacing=0 border=0>\n";
$pg .= '<tr class=title>';
$pg .= '<td class=dl>User</td>';
$pg .= '<td class=dr>Diff Accepted</td>';
$pg .= '<td class=dr>%</td>';
$pg .= '<td class=dr>Avg Hashrate</td>';
$pg .= '<td class=dr>BTC -0.9%</td>';
$pg .= '<td class=dr>Address</td>';
$pg .= "</tr>\n";
$diffacc_total = $ans['diffacc_total'];
if ($diffacc_total == 0)
$diffacc_total = pow(10,15);
$elapsed = $ans['pplns_elapsed'];
$count = $ans['rows'];
$tot_pay = 0;
for ($i = 0; $i < $count; $i++)
{
$diffacc_user = $ans['diffacc:'.$i];
$diffacc_percent = number_format(100.0 * $diffacc_user / $diffacc_total, 3).'%';
$avg_hash = number_format($diffacc_user / $elapsed * pow(2,32), 0);
$pay_sat = $ans['amount:'.$i];
$payaddress = $ans['payaddress:'.$i];
if (($i % 2) == 0)
$row = 'even';
else
$row = 'odd';
$pg .= "<tr class=$row>";
$pg .= '<td class=dl>'.$ans['user:'.$i].'</td>';
$pg .= "<td class=dr>$diffacc_user</td>";
$pg .= "<td class=dr>$diffacc_percent</td>";
$pg .= "<td class=dr>$avg_hash</td>";
$pg .= '<td class=dr>'.btcfmt($pay_sat).'</td>';
$pg .= "<td class=dr>$payaddress</td>";
$pg .= "</tr>\n";
$tot_pay += $pay_sat;
}
if (($i % 2) == 0)
$row = 'even';
else
$row = 'odd';
$pg .= "<tr class=$row>";
$pg .= '<td class=dl colspan=3></td>';
$pg .= '<td class=dr></td>';
$pg .= '<td class=dr>'.btcfmt($tot_pay).'</td>';
$pg .= '<td class=dr></td>';
$pg .= "</tr>\n";
$pg .= "</table>\n";
if ($dotx === true)
$pg .= calctx($ans, $count, $miner_sat, $diffacc_total);
}
return $pg;
}
#
function show_pplns2($info, $page, $menu, $name, $user)
{
gopage($info, NULL, 'dopplns2', $page, $menu, $name, $user);
}
#
?>

1
pool/prime.php

@ -19,6 +19,7 @@ function process($p, $user, $menu)
if ($user == 'Kano' || $user == 'ckolivas')
{
$menu['Admin']['ckp'] = 'ckp';
$menu['Admin']['PPLNS2'] = 'pplns2';
$menu['Admin']['PPLNS'] = 'pplns';
$menu['Admin']['AllWork'] = 'allwork';
}

51
sql/ckdb.sql

@ -84,11 +84,14 @@ CREATE UNIQUE INDEX payadduserid ON paymentaddresses USING btree (userid, payadd
CREATE TABLE payments (
paymentid bigint NOT NULL, -- unique per record
payoutid bigint NOT NULL,
userid bigint NOT NULL,
subname character varying(256) NOT NULL,
paydate timestamp with time zone NOT NULL,
payaddress character varying(256) DEFAULT ''::character varying NOT NULL,
originaltxn character varying(256) DEFAULT ''::character varying NOT NULL,
amount bigint NOT NULL, -- satoshis
diffacc float DEFAULT 0 NOT NULL,
committxn character varying(256) DEFAULT ''::character varying NOT NULL,
commitblockhash character varying(256) DEFAULT ''::character varying NOT NULL,
createdate timestamp with time zone NOT NULL,
@ -98,10 +101,10 @@ CREATE TABLE payments (
expirydate timestamp with time zone DEFAULT '6666-06-06 06:06:06+00',
PRIMARY KEY (paymentid, expirydate)
);
CREATE UNIQUE INDEX payuserid ON payments USING btree (userid, payaddress, originaltxn, expirydate);
CREATE UNIQUE INDEX payuserid ON payments USING btree (payoutid, userid, subname, payaddress, originaltxn, expirydate);
CREATE TABLE accountbalance ( -- summarised from miningpayouts and payments
CREATE TABLE accountbalance ( -- summarised from miningpayouts and payments - RAM only
userid bigint NOT NULL,
confirmedpaid bigint DEFAULT 0 NOT NULL, -- satoshis
confirmedunpaid bigint DEFAULT 0 NOT NULL, -- satoshis
@ -111,8 +114,11 @@ CREATE TABLE accountbalance ( -- summarised from miningpayouts and payments
createby character varying(64) DEFAULT ''::character varying NOT NULL,
createcode character varying(128) DEFAULT ''::character varying NOT NULL,
createinet character varying(128) DEFAULT ''::character varying NOT NULL,
expirydate timestamp with time zone DEFAULT '6666-06-06 06:06:06+00',
PRIMARY KEY (userid, expirydate)
modifydate timestamp with time zone DEFAULT '6666-06-06 06:06:06+00',
modifyby character varying(64) DEFAULT ''::character varying NOT NULL,
modifycode character varying(128) DEFAULT ''::character varying NOT NULL,
modifyinet character varying(128) DEFAULT ''::character varying NOT NULL,
PRIMARY KEY (userid)
);
@ -120,6 +126,7 @@ CREATE TABLE accountadjustment ( -- manual corrections
userid bigint NOT NULL,
authority character varying(256) NOT NULL,
reason text NOT NULL,
message character varying(256) NOT NULL,
amount bigint DEFAULT 0 NOT NULL, -- satoshis
createdate timestamp with time zone NOT NULL,
createby character varying(64) DEFAULT ''::character varying NOT NULL,
@ -341,22 +348,42 @@ CREATE TABLE blocks (
);
-- calculation for the given block - orphans will be here also (not deleted later)
-- rules for orphans/next block will be pool dependent
CREATE TABLE miningpayouts (
miningpayoutid bigint NOT NULL, -- unique per record
payoutid bigint NOT NULL,
userid bigint NOT NULL,
diffacc float DEFAULT 0 NOT NULL,
amount bigint DEFAULT 0 NOT NULL, -- satoshis
createdate timestamp with time zone NOT NULL,
createby character varying(64) DEFAULT ''::character varying NOT NULL,
createcode character varying(128) DEFAULT ''::character varying NOT NULL,
createinet character varying(128) DEFAULT ''::character varying NOT NULL,
expirydate timestamp with time zone DEFAULT '6666-06-06 06:06:06+00',
PRIMARY KEY (payoutid, userid, expirydate)
);
CREATE TABLE payouts (
payoutid bigint NOT NULL, -- unique per record
height integer not NULL,
blockhash character varying(256) DEFAULT ''::character varying NOT NULL,
amount bigint DEFAULT 0 NOT NULL,
blockhash character varying(256) NOT NULL,
minerreward bigint NOT NULL, -- satoshis
workinfoidstart bigint NOT NULL,
workinfoidend bigint NOT NULL, -- should be block workinfoid
elapsed bigint NOT NULL,
status char DEFAULT ' ' NOT NULL,
diffwanted float DEFAULT 0 NOT NULL,
diffused float DEFAULT 0 NOT NULL,
shareacc float DEFAULT 0 NOT NULL,
lastshareacc timestamp with time zone NOT NULL,
stats text DEFAULT ''::text NOT NULL,
createdate timestamp with time zone NOT NULL,
createby character varying(64) DEFAULT ''::character varying NOT NULL,
createcode character varying(128) DEFAULT ''::character varying NOT NULL,
createinet character varying(128) DEFAULT ''::character varying NOT NULL,
expirydate timestamp with time zone DEFAULT '6666-06-06 06:06:06+00',
PRIMARY KEY (miningpayoutid, expirydate)
PRIMARY KEY (payoutid, expirydate)
);
CREATE UNIQUE INDEX minpayuserid ON miningpayouts USING btree (userid, blockhash, expirydate);
CREATE UNIQUE INDEX payoutsblock ON payouts USING btree (height, blockhash, expirydate);
CREATE TABLE eventlog (
@ -433,4 +460,4 @@ CREATE TABLE version (
PRIMARY KEY (vlock)
);
insert into version (vlock,version) values (1,'0.9.6');
insert into version (vlock,version) values (1,'1.0.0');

1
sql/initid.sh

@ -20,3 +20,4 @@ addid authid ${now}300000
addid userid ${now}400000
addid markerid ${now}500000
addid paymentaddressid ${now}600000
addid payoutid ${now}700000

101
sql/v0.9.6-v1.0.0.sql

@ -0,0 +1,101 @@
SET SESSION AUTHORIZATION 'postgres';
BEGIN transaction;
DO $$
DECLARE ver TEXT;
BEGIN
UPDATE version set version='1.0.0' where vlock=1 and version='0.9.6';
IF found THEN
RETURN;
END IF;
SELECT version into ver from version
WHERE vlock=1;
RAISE EXCEPTION 'Wrong DB version - expect "0.9.6" - found "%"', ver;
END $$;
DROP TABLE payments;
CREATE TABLE payments (
paymentid bigint NOT NULL, -- unique per record
payoutid bigint NOT NULL,
userid bigint NOT NULL,
subname character varying(256) NOT NULL,
paydate timestamp with time zone NOT NULL,
payaddress character varying(256) DEFAULT ''::character varying NOT NULL,
originaltxn character varying(256) DEFAULT ''::character varying NOT NULL,
amount bigint NOT NULL, -- satoshis
diffacc float DEFAULT 0 NOT NULL,
committxn character varying(256) DEFAULT ''::character varying NOT NULL,
commitblockhash character varying(256) DEFAULT ''::character varying NOT NULL,
createdate timestamp with time zone NOT NULL,
createby character varying(64) DEFAULT ''::character varying NOT NULL,
createcode character varying(128) DEFAULT ''::character varying NOT NULL,
createinet character varying(128) DEFAULT ''::character varying NOT NULL,
expirydate timestamp with time zone DEFAULT '6666-06-06 06:06:06+00',
PRIMARY KEY (paymentid, expirydate)
);
CREATE UNIQUE INDEX payuserid ON payments USING btree (payoutid, userid, subname, payaddress, originaltxn, expirydate);
DROP TABLE accountbalance;
CREATE TABLE accountbalance ( -- summarised from miningpayouts and payments - RAM only
userid bigint NOT NULL,
confirmedpaid bigint DEFAULT 0 NOT NULL, -- satoshis
confirmedunpaid bigint DEFAULT 0 NOT NULL, -- satoshis
pendingconfirm bigint DEFAULT 0 NOT NULL, -- satoshis
heightupdate integer not NULL,
createdate timestamp with time zone NOT NULL,
createby character varying(64) DEFAULT ''::character varying NOT NULL,
createcode character varying(128) DEFAULT ''::character varying NOT NULL,
createinet character varying(128) DEFAULT ''::character varying NOT NULL,
modifydate timestamp with time zone DEFAULT '6666-06-06 06:06:06+00',
modifyby character varying(64) DEFAULT ''::character varying NOT NULL,
modifycode character varying(128) DEFAULT ''::character varying NOT NULL,
modifyinet character varying(128) DEFAULT ''::character varying NOT NULL,
PRIMARY KEY (userid)
);
DROP TABLE miningpayouts;
CREATE TABLE miningpayouts (
payoutid bigint NOT NULL,
userid bigint NOT NULL,
diffacc float DEFAULT 0 NOT NULL,
amount bigint DEFAULT 0 NOT NULL, -- satoshis
createdate timestamp with time zone NOT NULL,
createby character varying(64) DEFAULT ''::character varying NOT NULL,
createcode character varying(128) DEFAULT ''::character varying NOT NULL,
createinet character varying(128) DEFAULT ''::character varying NOT NULL,
expirydate timestamp with time zone DEFAULT '6666-06-06 06:06:06+00',
PRIMARY KEY (payoutid, userid, expirydate)
);
CREATE TABLE payouts (
payoutid bigint NOT NULL, -- unique per record
height integer not NULL,
blockhash character varying(256) NOT NULL,
minerreward bigint NOT NULL, -- satoshis
workinfoidstart bigint NOT NULL,
workinfoidend bigint NOT NULL, -- should be block workinfoid
elapsed bigint NOT NULL,
status char DEFAULT ' ' NOT NULL,
diffwanted float DEFAULT 0 NOT NULL,
diffused float DEFAULT 0 NOT NULL,
shareacc float DEFAULT 0 NOT NULL,
lastshareacc timestamp with time zone NOT NULL,
stats text DEFAULT ''::text NOT NULL,
createdate timestamp with time zone NOT NULL,
createby character varying(64) DEFAULT ''::character varying NOT NULL,
createcode character varying(128) DEFAULT ''::character varying NOT NULL,
createinet character varying(128) DEFAULT ''::character varying NOT NULL,
expirydate timestamp with time zone DEFAULT '6666-06-06 06:06:06+00',
PRIMARY KEY (payoutid, expirydate)
);
CREATE UNIQUE INDEX payoutsblock ON payouts USING btree (height, blockhash, expirydate);
insert into idcontrol (idname,lastid,createdate,createby) values ('payoutid',999,now(),'1.0.0update');
END transaction;

80
src/ckdb.c

@ -27,7 +27,7 @@
* and ckpool only verifies authorise responses
* Thus we can queue all messages:
* workinfo, shares, shareerror, ageworkinfo, poolstats, userstats
* and block
* and blocks
* with an ok.queued reply to ckpool, to be processed after the reload
* completes and just process authorise messages immediately while the
* reload runs
@ -61,7 +61,7 @@
* it would break the synchronisation and could cause DB problems, so
* ckdb aborting and needing a complete restart resolves it
* The users table, required for the authorise messages, is always updated
* immediately and is not affected by ckpool messages until we
* immediately
*/
/* Reload data needed
@ -92,17 +92,17 @@
* already exist
* DB+RAM blocks: resolved by workinfo - any unsaved blocks (if any)
* will be after the last DB workinfo
* DB+RAM accountbalance (TODO): resolved by shares/workinfo/blocks
* RAM workerstatus: all except last_idle are set at the end of the
* CCL reload
* Code currently doesn't use last_idle
* RAM accountbalance: TODO: created as data is loaded
*
* idcontrol: only userid reuse is critical and the user is added
* immeditately to the DB before replying to the add message
*
* Tables that are/will be written straight to the DB, so are OK:
* users, useraccounts, paymentaddresses, payments,
* accountadjustment, optioncontrol, miningpayouts,
* accountadjustment, optioncontrol, miningpayouts, payouts,
* eventlog, workmarkers, markersummary
*
* The code deals with the issue of 'now' when reloading by:
@ -329,6 +329,7 @@ K_STORE *workers_store;
// PAYMENTADDRESSES
K_TREE *paymentaddresses_root;
K_TREE *paymentaddresses_create_root;
K_LIST *paymentaddresses_free;
K_STORE *paymentaddresses_store;
@ -337,7 +338,6 @@ K_TREE *payments_root;
K_LIST *payments_free;
K_STORE *payments_store;
/* unused yet
// ACCOUNTBALANCE
K_TREE *accountbalance_root;
K_LIST *accountbalance_free;
@ -347,7 +347,6 @@ K_STORE *accountbalance_store;
K_TREE *accountadjustment_root;
K_LIST *accountadjustment_free;
K_STORE *accountadjustment_store;
*/
// IDCONTROL
// These are only used for db access - not stored in memory
@ -405,6 +404,13 @@ K_TREE *miningpayouts_root;
K_LIST *miningpayouts_free;
K_STORE *miningpayouts_store;
// PAYOUTS
K_TREE *payouts_root;
K_TREE *payouts_id_root;
K_LIST *payouts_free;
K_STORE *payouts_store;
cklock_t process_pplns_lock;
/*
// EVENTLOG
K_TREE *eventlog_root;
@ -742,6 +748,10 @@ static bool getdata3()
goto sukamudai;
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;
@ -953,6 +963,7 @@ static void alloc_storage()
LIMIT_PAYMENTADDRESSES, true);
paymentaddresses_store = k_new_store(paymentaddresses_free);
paymentaddresses_root = new_ktree();
paymentaddresses_create_root = new_ktree();
paymentaddresses_free->dsp_func = dsp_paymentaddresses;
payments_free = k_new_list("Payments", sizeof(PAYMENTS),
@ -960,6 +971,11 @@ static void alloc_storage()
payments_store = k_new_store(payments_free);
payments_root = new_ktree();
accountbalance_free = k_new_list("AccountBalance", sizeof(ACCOUNTBALANCE),
ALLOC_ACCOUNTBALANCE, LIMIT_ACCOUNTBALANCE, true);
accountbalance_store = k_new_store(accountbalance_free);
accountbalance_root = new_ktree();
idcontrol_free = k_new_list("IDControl", sizeof(IDCONTROL),
ALLOC_IDCONTROL, LIMIT_IDCONTROL, true);
idcontrol_store = k_new_store(idcontrol_free);
@ -999,6 +1015,12 @@ static void alloc_storage()
miningpayouts_store = k_new_store(miningpayouts_free);
miningpayouts_root = new_ktree();
payouts_free = k_new_list("Payouts", sizeof(PAYOUTS),
ALLOC_PAYOUTS, LIMIT_PAYOUTS, true);
payouts_store = k_new_store(payouts_free);
payouts_root = new_ktree();
payouts_id_root = new_ktree();
auths_free = k_new_list("Auths", sizeof(AUTHS),
ALLOC_AUTHS, LIMIT_AUTHS, true);
auths_store = k_new_store(auths_free);
@ -1110,6 +1132,10 @@ static void dealloc_storage()
FREE_ALL(poolstats);
FREE_ALL(auths);
FREE_TREE(payouts_id);
FREE_ALL(payouts);
FREE_ALL(miningpayouts);
FREE_ALL(blocks);
@ -1131,7 +1157,10 @@ static void dealloc_storage()
FREE_LIST_DATA(workinfo);
FREE_LISTS(idcontrol);
FREE_ALL(accountbalance);
FREE_ALL(payments);
FREE_TREE(paymentaddresses_create);
FREE_ALL(paymentaddresses);
FREE_ALL(workers);
@ -1534,6 +1563,20 @@ static void check_blocks()
btc_blockstatus(blocks);
}
static void pplns_block(BLOCKS *blocks)
{
if (sharesummary_marks_limit) {
LOGEMERG("%s() sharesummary marks limit, block %"PRId32" payout skipped",
__func__, blocks->height);
return;
}
// Give it a sec after the block summarisation
sleep(1);
process_pplns(blocks->height, blocks->blockhash, NULL);
}
static void summarise_blocks()
{
K_ITEM *b_item, *b_prev, *wi_item, ss_look, *ss_item;
@ -1733,17 +1776,15 @@ static void summarise_blocks()
"%0.f/%.0f/%.0f/%.0f/%"PRId64,
__func__, blocks->height,
diffacc, diffinv, shareacc, shareinv, elapsed);
// Now the summarisation is confirmed, generate the payout data
pplns_block(blocks);
} else {
LOGERR("%s() block %d, failed to confirm stats",
__func__, blocks->height);
}
}
static void summarise_poolstats()
{
// TODO
}
static void *summariser(__maybe_unused void *arg)
{
int i;
@ -1752,7 +1793,7 @@ static void *summariser(__maybe_unused void *arg)
rename_proc("db_summariser");
while (!everyone_die && !db_load_complete)
while (!everyone_die && !startup_complete)
cksleep_ms(42);
summariser_using_data = true;
@ -1764,12 +1805,8 @@ static void *summariser(__maybe_unused void *arg)
}
if (everyone_die)
break;
else {
if (startup_complete)
check_blocks();
if (!everyone_die)
summarise_blocks();
}
else
check_blocks();
for (i = 0; i < 4; i++) {
if (!everyone_die)
@ -1778,7 +1815,7 @@ static void *summariser(__maybe_unused void *arg)
if (everyone_die)
break;
else
summarise_poolstats();
summarise_blocks();
for (i = 0; i < 4; i++) {
if (!everyone_die)
@ -2624,6 +2661,8 @@ static void *socketer(__maybe_unused void *arg)
case CMD_WORKERS:
case CMD_PAYMENTS:
case CMD_PPLNS:
case CMD_PPLNS2:
case CMD_PAYOUTS:
case CMD_DSP:
case CMD_BLOCKSTATUS:
if (!startup_complete) {
@ -2841,6 +2880,8 @@ static bool reload_line(PGconn *conn, char *filename, uint64_t count, char *buf)
case CMD_DSP:
case CMD_STATS:
case CMD_PPLNS:
case CMD_PPLNS2:
case CMD_PAYOUTS:
case CMD_USERSTATUS:
case CMD_MARKS:
LOGERR("%s() Message line %"PRIu64" '%s' - invalid - ignored",
@ -4062,6 +4103,7 @@ int main(int argc, char **argv)
ckp.main.processname = strdup("main");
cklock_init(&last_lock);
cklock_init(&process_pplns_lock);
if (confirm_sharesummary) {
// TODO: add a system lock to stop running 2 at once?

126
src/ckdb.h

@ -51,8 +51,8 @@
*/
#define DB_VLOCK "1"
#define DB_VERSION "0.9.6"
#define CKDB_VERSION DB_VERSION"-0.920"
#define DB_VERSION "1.0.0"
#define CKDB_VERSION DB_VERSION"-1.007"
#define WHERE_FFL " - from %s %s() line %d"
#define WHERE_FFL_HERE __FILE__, __func__, __LINE__
@ -94,6 +94,9 @@ extern const char *addrpatt;
// BTC address size
#define ADDR_MIN_LEN 26
#define ADDR_MAX_LEN 34
/* All characters in a payaddress are less than this
* thus setting the 1st char to this will be greater than any payaddress */
#define MAX_PAYADDR '~'
typedef struct loadstatus {
tv_t oldest_sharesummary_firstshare_n;
@ -336,6 +339,8 @@ enum cmd_values {
CMD_DSP,
CMD_STATS,
CMD_PPLNS,
CMD_PPLNS2,
CMD_PAYOUTS,
CMD_USERSTATUS,
CMD_MARKS,
CMD_END
@ -816,6 +821,7 @@ typedef struct paymentaddresses {
#define DATA_PAYMENTADDRESSES_NULL(_var, _item) DATA_GENERIC(_var, _item, paymentaddresses, false)
extern K_TREE *paymentaddresses_root;
extern K_TREE *paymentaddresses_create_root;
extern K_LIST *paymentaddresses_free;
extern K_STORE *paymentaddresses_store;
@ -824,14 +830,18 @@ extern K_STORE *paymentaddresses_store;
// PAYMENTS
typedef struct payments {
int64_t paymentid;
int64_t payoutid;
int64_t userid;
char subname[TXT_BIG+1];
tv_t paydate;
char payaddress[TXT_BIG+1];
char originaltxn[TXT_BIG+1];
int64_t amount;
double diffacc;
char committxn[TXT_BIG+1];
char commitblockhash[TXT_BIG+1];
HISTORYDATECONTROLFIELDS;
K_ITEM *old_item; // Non-db field
} PAYMENTS;
#define ALLOC_PAYMENTS 1024
@ -844,7 +854,6 @@ extern K_TREE *payments_root;
extern K_LIST *payments_free;
extern K_STORE *payments_store;
/* unused yet
// ACCOUNTBALANCE
typedef struct accountbalance {
int64_t userid;
@ -852,7 +861,7 @@ typedef struct accountbalance {
int64_t confirmedunpaid;
int64_t pendingconfirm;
int32_t heightupdate;
HISTORYDATECONTROLFIELDS;
MODIFYDATECONTROLFIELDS;
} ACCOUNTBALANCE;
#define ALLOC_ACCOUNTBALANCE 1024
@ -881,7 +890,6 @@ typedef struct accountadjustment {
extern K_TREE *accountadjustment_root;
extern K_LIST *accountadjustment_free;
extern K_STORE *accountadjustment_store;
*/
// IDCONTROL
typedef struct idcontrol {
@ -1126,23 +1134,63 @@ extern K_STORE *blocks_store;
// MININGPAYOUTS
typedef struct miningpayouts {
int64_t miningpayoutid;
int64_t payoutid;
int64_t userid;
int32_t height;
char blockhash[TXT_BIG+1];
double diffacc;
int64_t amount;
HISTORYDATECONTROLFIELDS;
K_ITEM *old_item; // Non-db field
} MININGPAYOUTS;
#define ALLOC_MININGPAYOUTS 1000
#define LIMIT_MININGPAYOUTS 0
#define INIT_MININGPAYOUTS(_item) INIT_GENERIC(_item, miningpayouts)
#define DATA_MININGPAYOUTS(_var, _item) DATA_GENERIC(_var, _item, miningpayouts, true)
#define DATA_MININGPAYOUTS_NULL(_var, _item) DATA_GENERIC(_var, _item, miningpayouts, false)
extern K_TREE *miningpayouts_root;
extern K_LIST *miningpayouts_free;
extern K_STORE *miningpayouts_store;
// PAYOUTS
typedef struct payouts {
int64_t payoutid;
int32_t height;
char blockhash[TXT_BIG+1];
int64_t minerreward;
int64_t workinfoidstart;
int64_t workinfoidend;
int64_t elapsed;
char status[TXT_FLAG+1];
double diffwanted;
double diffused;
double shareacc;
tv_t lastshareacc;
char *stats;
HISTORYDATECONTROLFIELDS;
} PAYOUTS;
#define ALLOC_PAYOUTS 1000
#define LIMIT_PAYOUTS 0
#define INIT_PAYOUTS(_item) INIT_GENERIC(_item, payouts)
#define DATA_PAYOUTS(_var, _item) DATA_GENERIC(_var, _item, payouts, true)
#define DATA_PAYOUTS_NULL(_var, _item) DATA_GENERIC(_var, _item, payouts, false)
extern K_TREE *payouts_root;
extern K_TREE *payouts_id_root;
extern K_LIST *payouts_free;
extern K_STORE *payouts_store;
extern cklock_t process_pplns_lock;
// N.B. status should be checked under r/w lock
#define PAYOUTS_GENERATED 'G'
#define PAYOUTS_GENERATED_STR "G"
#define PAYGENERATED(_status) ((_status)[0] == PAYOUTS_GENERATED)
// A processing payout must be ignored
#define PAYOUTS_PROCESSING 'P'
#define PAYOUTS_PROCESSING_STR "P"
#define PAYPROCESSING(_status) ((_status)[0] == PAYOUTS_PROCESSING)
/*
// EVENTLOG
typedef struct eventlog {
@ -1527,6 +1575,20 @@ extern PGconn *dbconnect();
// *** ckdb_data.c ***
// ***
/* Blocks after 334106 were set to 5xN
* however, they cannot count back to include the workinfoid of 333809
* due to the markersummaries that were created.
* Code checks that if the block is after FIVExSTT then it must stop
* counting back shares at - and not include - FIVExWID */
#define FIVExSTT 334106
#define FIVExLIM 333809
// 333809 workinfoid
#define FIVExWID 6085620100361140756
// optioncontrol names for PPLNS N diff calculation
#define PPLNSDIFFTIMES "pplns_diff_times"
#define PPLNSDIFFADD "pplns_diff_add"
// Data free functions (first)
extern void free_workinfo_data(K_ITEM *item);
extern void free_sharesummary_data(K_ITEM *item);
@ -1635,12 +1697,19 @@ extern K_ITEM *new_default_worker(PGconn *conn, bool update, int64_t userid, cha
char *by, char *code, char *inet, tv_t *cd, K_TREE *trf_root);
extern void dsp_paymentaddresses(K_ITEM *item, FILE *stream);
extern cmp_t cmp_paymentaddresses(K_ITEM *a, K_ITEM *b);
extern cmp_t cmp_payaddr_create(K_ITEM *a, K_ITEM *b);
extern K_ITEM *find_paymentaddresses(int64_t userid, K_TREE_CTX *ctx);
extern K_ITEM *find_paymentaddresses_create(int64_t userid, K_TREE_CTX *ctx);
extern K_ITEM *find_one_payaddress(int64_t userid, char *payaddress, K_TREE_CTX *ctx);
extern K_ITEM *find_any_payaddress(char *payaddress);
extern cmp_t cmp_payments(K_ITEM *a, K_ITEM *b);
extern K_ITEM *find_payments(int64_t payoutid, int64_t userid, char *subname);
extern K_ITEM *find_first_payments(int64_t userid, K_TREE_CTX *ctx);
extern K_ITEM *find_first_paypayid(int64_t userid, int64_t payoutid, K_TREE_CTX *ctx);
extern cmp_t cmp_accountbalance(K_ITEM *a, K_ITEM *b);
extern K_ITEM *find_accountbalance(int64_t userid);
extern cmp_t cmp_optioncontrol(K_ITEM *a, K_ITEM *b);
extern K_ITEM *find_optioncontrol(char *optionname, tv_t *now);
extern K_ITEM *find_optioncontrol(char *optionname, tv_t *now, int32_t height);
extern cmp_t cmp_workinfo(K_ITEM *a, K_ITEM *b);
#define coinbase1height(_cb1) _coinbase1height(_cb1, WHERE_FFL_HERE)
extern int32_t _coinbase1height(char *coinbase1, WHERE_FFL_ARGS);
@ -1670,12 +1739,23 @@ void _dbhash2btchash(char *hash, char *buf, size_t siz, WHERE_FFL_ARGS);
extern void _dsp_hash(char *hash, char *buf, size_t siz, WHERE_FFL_ARGS);
extern void dsp_blocks(K_ITEM *item, FILE *stream);
extern cmp_t cmp_blocks(K_ITEM *a, K_ITEM *b);
extern K_ITEM *find_blocks(int32_t height, char *blockhash);
extern K_ITEM *find_blocks(int32_t height, char *blockhash, K_TREE_CTX *ctx);
extern K_ITEM *find_prev_blocks(int32_t height);
extern const char *blocks_confirmed(char *confirmed);
extern void zero_on_new_block();
extern void set_block_share_counters();
extern 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);
extern cmp_t cmp_mu(K_ITEM *a, K_ITEM *b);
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 K_ITEM *find_payouts(int32_t height, char *blockhash);
extern K_ITEM *find_last_payouts();
extern K_ITEM *find_payoutid(int64_t payoutid);
extern bool process_pplns(int32_t height, char *blockhash, tv_t *now);
extern cmp_t cmp_auths(K_ITEM *a, K_ITEM *b);
extern cmp_t cmp_poolstats(K_ITEM *a, K_ITEM *b);
extern void dsp_userstats(K_ITEM *item, FILE *stream);
@ -1751,6 +1831,12 @@ extern PGresult *_CKPQexecParams(PGconn *conn, const char *qry,
#define PGLOGEMERG(_str, _rescode, _conn) PGLOG(LOGEMERG, _str, _rescode, _conn)
extern char *pqerrmsg(PGconn *conn);
extern bool CKPQConn(PGconn **conn);
extern void CKPQDisco(PGconn **conn, bool conned);
extern bool _CKPQBegin(PGconn *conn, WHERE_FFL_ARGS);
#define CKPQBegin(_conn) _CKPQBegin(conn, WHERE_FFL_HERE)
extern void _CKPQEnd(PGconn *conn, bool commit, WHERE_FFL_ARGS);
#define CKPQEnd(_conn, _commit) _CKPQEnd(_conn, _commit, WHERE_FFL_HERE)
extern int64_t nextid(PGconn *conn, char *idname, int64_t increment,
tv_t *cd, char *by, char *code, char *inet);
@ -1783,6 +1869,11 @@ extern bool paymentaddresses_set(PGconn *conn, int64_t userid, K_LIST *pa_store,
char *by, char *code, char *inet, tv_t *cd,
K_TREE *trf_root);
extern bool paymentaddresses_fill(PGconn *conn);
extern void payments_add_ram(bool ok, K_ITEM *mp_item, K_ITEM *old_mp_item,
tv_t *cd);
extern bool payments_add(PGconn *conn, bool add, K_ITEM *p_item,
K_ITEM **old_p_item, char *by, char *code, char *inet,
tv_t *cd, K_TREE *trf_root, bool already);
extern bool payments_fill(PGconn *conn);
extern bool idcontrol_add(PGconn *conn, char *idname, char *idvalue, char *by,
char *code, char *inet, tv_t *cd, K_TREE *trf_root);
@ -1827,10 +1918,19 @@ extern bool blocks_add(PGconn *conn, char *height, char *blockhash,
char *by, char *code, char *inet, tv_t *cd,
bool igndup, char *id, K_TREE *trf_root);
extern bool blocks_fill(PGconn *conn);
extern bool miningpayouts_add(PGconn *conn, char *username, char *height,
char *blockhash, char *amount, char *by,
char *code, char *inet, tv_t *cd, K_TREE *trf_root);
extern void miningpayouts_add_ram(bool ok, K_ITEM *mp_item, K_ITEM *old_mp_item,
tv_t *cd);
extern bool miningpayouts_add(PGconn *conn, bool add, K_ITEM *mp_item,
K_ITEM **old_mp_item, char *by, char *code,
char *inet, tv_t *cd, K_TREE *trf_root,
bool already);
extern bool miningpayouts_fill(PGconn *conn);
extern void payouts_add_ram(bool ok, K_ITEM *p_item, K_ITEM *old_p_item,
tv_t *cd);
extern bool payouts_add(PGconn *conn, bool add, K_ITEM *p_item,
K_ITEM **old_p_item, char *by, char *code, char *inet,
tv_t *cd, K_TREE *trf_root, bool already);
extern bool payouts_fill(PGconn *conn);
extern bool auths_add(PGconn *conn, char *poolinstance, char *username,
char *workername, char *clientid, char *enonce1,
char *useragent, char *preauth, char *by, char *code,

590
src/ckdb_cmd.c

@ -971,7 +971,7 @@ static char *cmd_blockstatus(__maybe_unused PGconn *conn, char *cmd, char *id,
action = transfer_data(i_action);
K_RLOCK(blocks_free);
b_item = find_blocks(height, transfer_data(i_blockhash));
b_item = find_blocks(height, transfer_data(i_blockhash), NULL);
K_RUNLOCK(blocks_free);
if (!b_item) {
@ -1092,9 +1092,9 @@ static char *cmd_payments(__maybe_unused PGconn *conn, char *cmd, char *id,
__maybe_unused tv_t *notcd,
__maybe_unused K_TREE *trf_root)
{
K_ITEM *i_username, look, *u_item, *p_item;
K_ITEM *i_username, *u_item, *p_item;
K_TREE_CTX ctx[1];
PAYMENTS lookpayments, *payments;
PAYMENTS *payments, curr;
USERS *users;
char reply[1024] = "";
char tmp[1024];
@ -1116,33 +1116,62 @@ static char *cmd_payments(__maybe_unused PGconn *conn, char *cmd, char *id,
return strdup("bad");
DATA_USERS(users, u_item);
lookpayments.userid = users->userid;
lookpayments.paydate.tv_sec = 0;
lookpayments.paydate.tv_usec = 0;
INIT_PAYMENTS(&look);
look.data = (void *)(&lookpayments);
p_item = find_after_in_ktree(payments_root, &look, cmp_payments, ctx);
DATA_PAYMENTS_NULL(payments, p_item);
bzero(&curr, sizeof(curr));
APPEND_REALLOC_INIT(buf, off, len);
APPEND_REALLOC(buf, off, len, "ok.");
rows = 0;
K_RLOCK(payments_free);
p_item = find_first_payments(users->userid, ctx);
DATA_PAYMENTS_NULL(payments, p_item);
/* TODO: allow to see details of a single payoutid
* if it has multiple items (percent payout user) */
while (p_item && payments->userid == users->userid) {
tv_to_buf(&(payments->paydate), reply, sizeof(reply));
if (CURRENT(&(payments->expirydate))) {
if (curr.payoutid && curr.payoutid != payments->payoutid) {
tv_to_buf(&(curr.paydate), reply, sizeof(reply));
snprintf(tmp, sizeof(tmp), "paydate:%d=%s%c", rows, reply, FLDSEP);
APPEND_REALLOC(buf, off, len, tmp);
str_to_buf(curr.payaddress, reply, sizeof(reply));
snprintf(tmp, sizeof(tmp), "payaddress:%d=%s%c", rows, reply, FLDSEP);
APPEND_REALLOC(buf, off, len, tmp);
bigint_to_buf(curr.amount, reply, sizeof(reply));
snprintf(tmp, sizeof(tmp), "amount:%d=%s%c", rows, reply, FLDSEP);
APPEND_REALLOC(buf, off, len, tmp);
rows++;
bzero(&curr, sizeof(curr));
}
if (!curr.payoutid) {
curr.payoutid = payments->payoutid;
copy_tv(&(curr.paydate), &(payments->paydate));
STRNCPY(curr.payaddress, payments->payaddress);
} else
STRNCPY(curr.payaddress, "*Multiple");
curr.amount += payments->amount;
}
p_item = next_in_ktree(ctx);
DATA_PAYMENTS_NULL(payments, p_item);
}
K_RUNLOCK(payments_free);
if (curr.payoutid) {
tv_to_buf(&(curr.paydate), reply, sizeof(reply));
snprintf(tmp, sizeof(tmp), "paydate:%d=%s%c", rows, reply, FLDSEP);
APPEND_REALLOC(buf, off, len, tmp);
str_to_buf(payments->payaddress, reply, sizeof(reply));
str_to_buf(curr.payaddress, reply, sizeof(reply));
snprintf(tmp, sizeof(tmp), "payaddress:%d=%s%c", rows, reply, FLDSEP);
APPEND_REALLOC(buf, off, len, tmp);
bigint_to_buf(payments->amount, reply, sizeof(reply));
bigint_to_buf(curr.amount, reply, sizeof(reply));
snprintf(tmp, sizeof(tmp), "amount:%d=%s%c", rows, reply, FLDSEP);
APPEND_REALLOC(buf, off, len, tmp);
rows++;
p_item = next_in_ktree(ctx);
DATA_PAYMENTS_NULL(payments, p_item);
}
snprintf(tmp, sizeof(tmp), "rows=%d%cflds=%s%c",
rows, FLDSEP,
"paydate,payaddress,amount", FLDSEP);
@ -2220,7 +2249,7 @@ static char *cmd_auth_do(PGconn *conn, char *cmd, char *id, char *by,
i_preauth = &auth_preauth;
K_RLOCK(optioncontrol_free);
oc_item = find_optioncontrol(OPTIONCONTROL_AUTOADDUSER, cd);
oc_item = find_optioncontrol(OPTIONCONTROL_AUTOADDUSER, cd, pool.height);
K_RUNLOCK(optioncontrol_free);
if (oc_item) {
K_RLOCK(users_free);
@ -3112,7 +3141,7 @@ static char *cmd_getopts(__maybe_unused PGconn *conn, char *cmd, char *id,
if (comma)
*(comma++) = '\0';
K_RLOCK(optioncontrol_free);
oc_item = find_optioncontrol(ptr, now);
oc_item = find_optioncontrol(ptr, now, pool.height);
K_RUNLOCK(optioncontrol_free);
/* web code must check the existance of the optionname
* in the reply since it will be missing if it doesn't
@ -3305,81 +3334,12 @@ rollback:
return strdup(reply);
}
// order by userid asc
static cmp_t cmp_mu(K_ITEM *a, K_ITEM *b)
{
MININGPAYOUTS *ma, *mb;
DATA_MININGPAYOUTS(ma, a);
DATA_MININGPAYOUTS(mb, b);
return CMP_BIGINT(ma->userid, mb->userid);
}
static K_TREE *upd_add_mu(K_TREE *mu_root, K_STORE *mu_store, int64_t userid, int64_t diffacc)
{
MININGPAYOUTS lookminingpayouts, *miningpayouts;
K_ITEM look, *mu_item;
K_TREE_CTX ctx[1];
lookminingpayouts.userid = userid;
INIT_MININGPAYOUTS(&look);
look.data = (void *)(&lookminingpayouts);
mu_item = find_in_ktree(mu_root, &look, cmp_mu, ctx);
if (mu_item) {
DATA_MININGPAYOUTS(miningpayouts, mu_item);
miningpayouts->amount += diffacc;
} else {
K_WLOCK(mu_store);
mu_item = k_unlink_head(miningpayouts_free);
DATA_MININGPAYOUTS(miningpayouts, mu_item);
miningpayouts->userid = userid;
miningpayouts->amount = diffacc;
mu_root = add_to_ktree(mu_root, mu_item, cmp_mu);
k_add_head(mu_store, mu_item);
K_WUNLOCK(mu_store);
}
return mu_root;
}
/* Find the block_workinfoid of the block requested
then add all it's diffacc shares
then keep stepping back shares until diffacc_total matches or exceeds
the number required (diff_want) - this is begin_workinfoid
(also summarising diffacc per user)
then keep stepping back until we complete the current begin_workinfoid
(also summarising diffacc per user)
While we are still below diff_want
find each workmarker and add on the full set of worksummary
diffacc shares (also summarising diffacc per user)
This will give us the total number of diff1 shares (diffacc_total)
to use for the payment calculations
The value of diff_want defaults to the block's network difficulty
(block_ndiff) but can be changed with diff_times and diff_add to:
block_ndiff * diff_times + diff_add
N.B. diff_times and diff_add can be zero, positive or negative
The pplns_elapsed time of the shares is from the createdate of the
begin_workinfoid that has shares accounted to the total,
up to the createdate of the last share
The user average hashrate would be:
diffacc_user * 2^32 / pplns_elapsed
PPLNS fraction of the payout would be:
diffacc_user / diffacc_total
N.B. 'begin' means the oldest back in time and 'end' means the newest
'end' should usually be the info of the found block with the pplns
data going back in time to 'begin'
*/
/* Blocks after 334106 were set to 5xN
* however, they cannot count back to include the workinfoid of 333809
* due to the markersummaries that were created.
* Code checks that if the block is after FIVExSTT then it must stop
* counting back shares at - and not include - FIVExWID */
#define FIVExSTT 334106
#define FIVExLIM 333809
// 333809 workinfoid
#define FIVExWID 6085620100361140756
/* Kept for reference/comparison to cmd_pplns2()
* This will get different results due to the fact that it uses the current
* contents of the payoutaddresses table
* However, the only differences should be the addresses,
* and the breakdown for percent address users,
* the totals per user and per payout should still be the same */
static char *cmd_pplns(__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,
@ -3857,6 +3817,443 @@ shazbot:
return strdup(reply);
}
// Generated from the payouts, miningpayouts and payments data
static char *cmd_pplns2(__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)
{
char reply[1024], tmp[1024], *buf;
char *block_extra, *marks_status = EMPTY;
size_t siz = sizeof(reply);
K_ITEM *i_height;
K_ITEM b_look, *b_item, *p_item, *mp_item, *pay_item, *u_item;
K_ITEM *w_item;
MININGPAYOUTS *miningpayouts;
PAYMENTS *payments;
PAYOUTS *payouts;
BLOCKS lookblocks, *blocks;
tv_t block_tv = { 0L, 0L };
WORKINFO *bworkinfo, *workinfo;
char ndiffbin[TXT_SML+1];
double ndiff;
USERS *users;
int32_t height;
K_TREE_CTX b_ctx[1], mp_ctx[1], pay_ctx[1];
char tv_buf[DATE_BUFSIZ];
size_t len, off;
int rows;
bool pok;
LOGDEBUG("%s(): cmd '%s'", __func__, cmd);
if (sharesummary_marks_limit)
marks_status = "ckdb -w load value means pplns may be incorrect";
i_height = require_name(trf_root, "height", 1, NULL, reply, siz);
if (!i_height)
return strdup(reply);
TXT_TO_INT("height", transfer_data(i_height), height);
LOGDEBUG("%s(): height %"PRId32, __func__, height);
lookblocks.height = height + 1;
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);
if (!b_item) {
K_RUNLOCK(blocks_free);
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));
return strdup(reply);
}
if (!CURRENT(&(blocks->expirydate))) {
snprintf(reply, siz, "ERR.no CURRENT block %d"PRId32, height);
return strdup(reply);
}
LOGDEBUG("%s(): block %"PRId32"/%"PRId64"/%s/%s/%"PRId64,
__func__, blocks->height, blocks->workinfoid,
blocks->workername, blocks->confirmed, blocks->reward);
switch (blocks->confirmed[0]) {
case BLOCKS_NEW:
block_extra = "Can't be paid out yet";
break;
case BLOCKS_ORPHAN:
block_extra = "Can't be paid out";
break;
default:
block_extra = EMPTY;
break;
}
w_item = find_workinfo(blocks->workinfoid, NULL);
if (!w_item) {
snprintf(reply, siz, "ERR.missing block workinfo record!"
" %"PRId64,
blocks->workinfoid);
return strdup(reply);
}
DATA_WORKINFO(bworkinfo, w_item);
pok = false;
K_RLOCK(payouts_free);
p_item = find_payouts(height, blocks->blockhash);
DATA_PAYOUTS_NULL(payouts, p_item);
if (p_item && PAYGENERATED(payouts->status))
pok = true;
K_RUNLOCK(payouts_free);
if (!p_item) {
snprintf(reply, siz, "ERR.no payout for %"PRId32"/%s",
height, blocks->blockhash);
return strdup(reply);
}
if (!pok) {
snprintf(reply, siz, "ERR.payout %"PRId64" status=%s "
"for %"PRId32"/%s",
payouts->payoutid, payouts->status, height,
blocks->blockhash);
return strdup(reply);
}
LOGDEBUG("%s(): total %.1f want %.1f",
__func__, payouts->diffused, payouts->diffwanted);
w_item = find_workinfo(payouts->workinfoidstart, NULL);
if (!w_item) {
snprintf(reply, siz, "ERR.missing begin workinfo record!"
" %"PRId64,
payouts->workinfoidstart);
return strdup(reply);
}
DATA_WORKINFO(workinfo, w_item);
APPEND_REALLOC_INIT(buf, off, len);
APPEND_REALLOC(buf, off, len, "ok.");
snprintf(tmp, sizeof(tmp), "block=%d%c", height, FLDSEP);
APPEND_REALLOC(buf, off, len, tmp);
snprintf(tmp, sizeof(tmp), "block_hash=%s%c", blocks->blockhash, FLDSEP);
APPEND_REALLOC(buf, off, len, tmp);
snprintf(tmp, sizeof(tmp), "block_reward=%"PRId64"%c", blocks->reward, FLDSEP);
APPEND_REALLOC(buf, off, len, tmp);
snprintf(tmp, sizeof(tmp), "miner_reward=%"PRId64"%c", payouts->minerreward, FLDSEP);
APPEND_REALLOC(buf, off, len, tmp);
snprintf(tmp, sizeof(tmp), "block_status=%s%c",
blocks_confirmed(blocks->confirmed), FLDSEP);
APPEND_REALLOC(buf, off, len, tmp);
snprintf(tmp, sizeof(tmp), "block_extra=%s%c", block_extra, FLDSEP);
APPEND_REALLOC(buf, off, len, tmp);
snprintf(tmp, sizeof(tmp), "marks_status=%s%c", marks_status, FLDSEP);
APPEND_REALLOC(buf, off, len, tmp);
snprintf(tmp, sizeof(tmp), "workername=%s%c", blocks->workername, FLDSEP);
APPEND_REALLOC(buf, off, len, tmp);
snprintf(tmp, sizeof(tmp), "nonce=%s%c", blocks->nonce, FLDSEP);
APPEND_REALLOC(buf, off, len, tmp);
snprintf(tmp, sizeof(tmp), "begin_workinfoid=%"PRId64"%c", payouts->workinfoidstart, FLDSEP);
APPEND_REALLOC(buf, off, len, tmp);
snprintf(tmp, sizeof(tmp), "block_workinfoid=%"PRId64"%c", blocks->workinfoid, FLDSEP);
APPEND_REALLOC(buf, off, len, tmp);
snprintf(tmp, sizeof(tmp), "end_workinfoid=%"PRId64"%c", payouts->workinfoidend, FLDSEP);
APPEND_REALLOC(buf, off, len, tmp);
snprintf(tmp, sizeof(tmp), "diffacc_total=%.1f%c", payouts->diffused, FLDSEP);
APPEND_REALLOC(buf, off, len, tmp);
snprintf(tmp, sizeof(tmp), "pplns_elapsed=%"PRId64"%c", payouts->elapsed, FLDSEP);
APPEND_REALLOC(buf, off, len, tmp);
rows = 0;
K_RLOCK(miningpayouts_free);
mp_item = first_miningpayouts(payouts->payoutid, mp_ctx);
K_RUNLOCK(miningpayouts_free);
DATA_MININGPAYOUTS_NULL(miningpayouts, mp_item);
while (mp_item && miningpayouts->payoutid == payouts->payoutid) {
if (CURRENT(&(miningpayouts->expirydate))) {
int out = 0;
K_RLOCK(users_free);
u_item = find_userid(miningpayouts->userid);
K_RUNLOCK(users_free);
if (!u_item) {
snprintf(reply, siz,
"ERR.unknown userid %"PRId64,
miningpayouts->userid);
goto shazbot;
}
DATA_USERS(users, u_item);
K_RLOCK(payments_free);
pay_item = find_first_paypayid(miningpayouts->userid,
payouts->payoutid,
pay_ctx);
DATA_PAYMENTS_NULL(payments, pay_item);
while (pay_item &&
payments->userid == miningpayouts->userid &&
payments->payoutid == payouts->payoutid) {
if (CURRENT(&(payments->expirydate))) {
snprintf(tmp, sizeof(tmp),
"user:%d=%s%c"
"payaddress:%d=%s%c"
"amount:%d=%"PRId64"%c"
"diffacc:%d=%.1f%c",
rows, payments->subname, FLDSEP,
rows, payments->payaddress, FLDSEP,
rows, payments->amount, FLDSEP,
rows, payments->diffacc, FLDSEP);
APPEND_REALLOC(buf, off, len, tmp);
rows++;
out++;
}
pay_item = next_in_ktree(pay_ctx);
DATA_PAYMENTS_NULL(payments, pay_item);
}
K_RUNLOCK(payments_free);
if (out == 0) {
snprintf(tmp, sizeof(tmp),
"user:%d=%s.0%c"
"payaddress:%d=%s%c"
"amount:%d=%"PRId64"%c"
"diffacc:%d=%.1f%c",
rows, users->username, FLDSEP,
rows, "none", FLDSEP,
rows, miningpayouts->amount, FLDSEP,
rows, miningpayouts->diffacc, FLDSEP);
APPEND_REALLOC(buf, off, len, tmp);
rows++;
}
}
K_RLOCK(miningpayouts_free);
mp_item = next_in_ktree(mp_ctx);
K_RUNLOCK(miningpayouts_free);
DATA_MININGPAYOUTS_NULL(miningpayouts, mp_item);
}
snprintf(tmp, sizeof(tmp),
"rows=%d%cflds=%s%c",
rows, FLDSEP,
"user,payaddress,amount,diffacc", FLDSEP);
APPEND_REALLOC(buf, off, len, tmp);
snprintf(tmp, sizeof(tmp), "arn=%s%carp=%s%c",
"Users", FLDSEP, "", FLDSEP);
APPEND_REALLOC(buf, off, len, tmp);
tv_to_buf(&(workinfo->createdate), tv_buf, sizeof(tv_buf));
snprintf(tmp, sizeof(tmp), "begin_stamp=%s%c", tv_buf, FLDSEP);
APPEND_REALLOC(buf, off, len, tmp);
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));
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);
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);
APPEND_REALLOC(buf, off, len, tmp);
snprintf(tmp, sizeof(tmp), "end_epoch=%ld%c",
payouts->lastshareacc.tv_sec, FLDSEP);
APPEND_REALLOC(buf, off, len, tmp);
snprintf(tmp, sizeof(tmp), "%s%c", payouts->stats, FLDSEP);
APPEND_REALLOC(buf, off, len, tmp);
hex2bin(ndiffbin, bworkinfo->bits, 4);
ndiff = diff_from_nbits(ndiffbin);
snprintf(tmp, sizeof(tmp), "block_ndiff=%f%c", ndiff, FLDSEP);
APPEND_REALLOC(buf, off, len, tmp);
snprintf(tmp, sizeof(tmp), "diff_want=%.1f%c",
payouts->diffwanted, FLDSEP);
APPEND_REALLOC(buf, off, len, tmp);
snprintf(tmp, sizeof(tmp), "acc_share_count=%.0f%c",
payouts->shareacc, FLDSEP);
APPEND_REALLOC(buf, off, len, tmp);
// So web can always verify it received all data
APPEND_REALLOC(buf, off, len, "pplns_last=1");
LOGDEBUG("%s.ok.pplns.%s", id, buf);
return buf;
shazbot:
return strdup(reply);
}
static char *cmd_payouts(PGconn *conn, char *cmd, char *id, tv_t *now,
char *by, char *code, char *inet,
__maybe_unused tv_t *cd, K_TREE *trf_root)
{
char reply[1024] = "";
size_t siz = sizeof(reply);
char msg[1024] = "";
K_ITEM *i_action, *i_payoutid, *i_height, *i_blockhash, *i_addrdate;
K_ITEM *p_item, *p2_item, *old_p2_item;
PAYOUTS *payouts, *payouts2, *old_payouts2;
char *action;
int64_t payoutid = -1;
int32_t height = 0;
char blockhash[TXT_BIG+1];
tv_t addrdate;
bool ok = true;
LOGDEBUG("%s(): cmd '%s'", __func__, cmd);
i_action = require_name(trf_root, "action", 1, NULL, reply, siz);
if (!i_action)
return strdup(reply);
action = transfer_data(i_action);
if (strcasecmp(action, "generated") == 0) {
/* Change the status of a processing payout to generated
* Require payoutid
* Use this if the payout process completed but the end txn,
* that only updates the payout to generated, failed */
i_payoutid = require_name(trf_root, "payoutid", 1,
(char *)intpatt, reply, siz);
if (!i_payoutid)
return strdup(reply);
TXT_TO_BIGINT("payoutid", transfer_data(i_payoutid), payoutid);
K_WLOCK(payouts_free);
p_item = find_payoutid(payoutid);
if (!p_item) {
K_WUNLOCK(payouts_free);
snprintf(reply, siz,
"no payout with id %"PRId64, payoutid);
return strdup(reply);
}
DATA_PAYOUTS(payouts, p_item);
if (!PAYPROCESSING(payouts->status)) {
K_WUNLOCK(payouts_free);
snprintf(reply, siz,
"status !processing (%s) for payout %"PRId64,
payouts->status, payoutid);
return strdup(reply);
}
p2_item = k_unlink_head(payouts_free);
K_WUNLOCK(payouts_free);
/* There is a risk of the p_item changing while it's unlocked,
* but since this is a manual interface it's not really likely
* and there'll be an error if something goes wrong
* It reports the old and new status */
DATA_PAYOUTS(payouts2, p2_item);
bzero(payouts2, sizeof(*payouts2));
payouts2->payoutid = payouts->payoutid;
payouts2->height = payouts->height;
STRNCPY(payouts2->blockhash, payouts->blockhash);
payouts2->minerreward = payouts->minerreward;
payouts2->workinfoidstart = payouts->workinfoidstart;
payouts2->workinfoidend = payouts->workinfoidend;
payouts2->elapsed = payouts->elapsed;
STRNCPY(payouts2->status, PAYOUTS_GENERATED_STR);
payouts2->diffwanted = payouts->diffwanted;
payouts2->diffused = payouts->diffused;
payouts2->shareacc = payouts->shareacc;
copy_tv(&(payouts2->lastshareacc), &(payouts->lastshareacc));
payouts2->stats = strdup(payouts->stats);
ok = payouts_add(conn, true, p2_item, &old_p2_item,
by, code, inet, now, NULL, false);
if (!ok) {
snprintf(reply, siz, "failed payout %"PRId64, payoutid);
return strdup(reply);
}
DATA_PAYOUTS(payouts2, p2_item);
DATA_PAYOUTS(old_payouts2, old_p2_item);
snprintf(msg, sizeof(msg),
"payout %"PRId64" changed from '%s' to '%s' for"
"%"PRId32"/%s",
payoutid, old_payouts2->status, payouts2->status,
payouts2->height, payouts2->blockhash);
/*
} else if (strcasecmp(action, "expire") == 0) {
/ TODO: Expire the payout - effectively deletes it
* Require payoutid
* If any payments are paid then don't allow it /
i_payoutid = require_name(trf_root, "payoutid", 1,
(char *)intpatt, reply, siz);
if (!i_payoutid)
return strdup(reply);
TXT_TO_BIGINT("payoutid", transfer_data(i_payoutid), payoutid);
K_WLOCK(payouts_free);
p_item = find_payoutid(payoutid);
if (!p_item) {
K_WUNLOCK(payouts_free);
snprintf(reply, siz,
"no payout with id %"PRId64, payoutid);
return strdup(reply);
}
p2_item = k_unlink_head(payouts_free);
K_WUNLOCK(payouts_free);
DATA_PAYOUTS(payouts2, p2_item);
bzero(payouts2, sizeof(*payouts2));
payouts2->payoutid = payouts->payoutid;
...
*/
} else if (strcasecmp(action, "process") == 0) {
/* Generate a payout
* Require height, blockhash and addrdate
* addrdate is an epoch integer
* and 0 means uses the default = block NEW createdate
* this is the date to use to determine payoutaddresses
* Check the console for processing messages */
i_height = require_name(trf_root, "height", 6,
(char *)intpatt, reply, siz);
if (!i_height)
return strdup(reply);
TXT_TO_INT("height", transfer_data(i_height), height);
i_blockhash = require_name(trf_root, "blockhash", 64,
(char *)hashpatt, reply, siz);
if (!i_blockhash)
return strdup(reply);
TXT_TO_STR("blockhash", transfer_data(i_blockhash), blockhash);
i_addrdate = require_name(trf_root, "addrdate", 1,
(char *)intpatt, reply, siz);
if (!i_addrdate)
return strdup(reply);
TXT_TO_CTV("addrdate", transfer_data(i_addrdate), addrdate);
if (addrdate.tv_sec == 0)
ok = process_pplns(height, blockhash, NULL);
else
ok = process_pplns(height, blockhash, &addrdate);
} else {
snprintf(reply, siz, "unknown action '%s'", action);
LOGERR("%s.%s", id, reply);
return strdup(reply);
}
snprintf(reply, siz, "%s.%s%s%s",
ok ? "ok" : "ERR",
action,
msg[0] ? " " : EMPTY,
msg[0] ? msg : EMPTY);
LOGWARNING("%s.%s", id, reply);
return strdup(reply);
}
static char *cmd_dsp(__maybe_unused PGconn *conn, __maybe_unused char *cmd,
char *id, __maybe_unused tv_t *now,
__maybe_unused char *by, __maybe_unused char *code,
@ -3884,6 +4281,9 @@ static char *cmd_dsp(__maybe_unused PGconn *conn, __maybe_unused char *cmd,
dsp_ktree(paymentaddresses_free, paymentaddresses_root,
transfer_data(i_file), NULL);
dsp_ktree(paymentaddresses_create_free, paymentaddresses_root,
transfer_data(i_file), NULL);
dsp_ktree(sharesummary_free, sharesummary_root,
transfer_data(i_file), NULL);
@ -3944,8 +4344,9 @@ static char *cmd_stats(__maybe_unused PGconn *conn, char *cmd, char *id,
USEINFO(users, 1, 2);
USEINFO(useratts, 1, 1);
USEINFO(workers, 1, 1);
USEINFO(paymentaddresses, 1, 1);
USEINFO(paymentaddresses, 1, 2);
USEINFO(payments, 1, 1);
USEINFO(accountbalance, 1, 1);
USEINFO(idcontrol, 1, 0);
USEINFO(optioncontrol, 1, 1);
USEINFO(workinfo, 1, 1);
@ -3957,6 +4358,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(auths, 1, 1);
USEINFO(poolstats, 1, 1);
USEINFO(userstats, 2, 1);
@ -4511,6 +4913,8 @@ struct CMDS ckdb_cmds[] = {
{ CMD_DSP, "dsp", false, false, cmd_dsp, ACCESS_SYSTEM },
{ CMD_STATS, "stats", true, false, cmd_stats, ACCESS_SYSTEM ACCESS_WEB },
{ CMD_PPLNS, "pplns", false, false, cmd_pplns, ACCESS_SYSTEM ACCESS_WEB },
{ CMD_PPLNS2, "pplns2", false, false, cmd_pplns2, ACCESS_SYSTEM ACCESS_WEB },
{ CMD_PAYOUTS, "payouts", false, false, cmd_payouts, ACCESS_SYSTEM },
{ CMD_USERSTATUS,"userstatus", false, false, cmd_userstatus, ACCESS_SYSTEM ACCESS_WEB },
{ CMD_MARKS, "marks", false, false, cmd_marks, ACCESS_SYSTEM },
{ CMD_END, NULL, false, false, NULL, NULL }

1063
src/ckdb_data.c

File diff suppressed because it is too large Load Diff

757
src/ckdb_dbio.c

@ -167,6 +167,8 @@ char *pqerrmsg(PGconn *conn)
#define PQPARAM14 PQPARAM8 ",$9,$10,$11,$12,$13,$14"
#define PQPARAM15 PQPARAM8 ",$9,$10,$11,$12,$13,$14,$15"
#define PQPARAM16 PQPARAM8 ",$9,$10,$11,$12,$13,$14,$15,$16"
#define PQPARAM17 PQPARAM16 ",$17"
#define PQPARAM18 PQPARAM16 ",$17,$18"
#define PQPARAM22 PQPARAM16 ",$17,$18,$19,$20,$21,$22"
#define PQPARAM26 PQPARAM22 ",$23,$24,$25,$26"
#define PQPARAM27 PQPARAM26 ",$27"
@ -222,6 +224,70 @@ PGresult *_CKPQexecParams(PGconn *conn, const char *qry,
#define PQexec CKPQexec
#define PQexecParams CKPQexecParams
// TODO: switch all to use this
bool CKPQConn(PGconn **conn)
{
if (*conn == NULL) {
LOGDEBUG("%s(): connecting", __func__);
*conn = dbconnect();
return true;
}
return false;
}
// TODO: switch all to use this
void CKPQDisco(PGconn **conn, bool conned)
{
if (conned) {
LOGDEBUG("%s(): disco", __func__);
PQfinish(*conn);
}
}
// TODO: switch all to use this
bool _CKPQBegin(PGconn *conn, WHERE_FFL_ARGS)
{
ExecStatusType rescode;
PGresult *res;
res = PQexec(conn, "Begin", CKPQ_WRITE);
rescode = PQresultStatus(res);
PQclear(res);
if (!PGOK(rescode)) {
char *buf = pqerrmsg(conn);
LOGEMERG("%s(): Begin failed (%d) '%s'" WHERE_FFL,
__func__, (int)rescode, buf, WHERE_FFL_PASS);
free(buf);
return false;
}
LOGDEBUG("%s(): begin", __func__);
return true;
}
// TODO: switch all to use this
void _CKPQEnd(PGconn *conn, bool commit, WHERE_FFL_ARGS)
{
ExecStatusType rescode;
PGresult *res;
if (commit) {
LOGDEBUG("%s(): commit", __func__);
res = PQexec(conn, "Commit", CKPQ_WRITE);
} else {
LOGDEBUG("%s(): rollback", __func__);
res = PQexec(conn, "Rollback", CKPQ_WRITE);
}
rescode = PQresultStatus(res);
PQclear(res);
if (!PGOK(rescode)) {
char *buf = pqerrmsg(conn);
LOGEMERG("%s(): %s failed (%d) '%s'" WHERE_FFL,
__func__, commit ? "commit" : "rollback",
(int)rescode, buf, WHERE_FFL_PASS);
free(buf);
}
}
int64_t nextid(PGconn *conn, char *idname, int64_t increment,
tv_t *cd, char *by, char *code, char *inet)
{
@ -1642,9 +1708,13 @@ unparam:
n++;
paymentaddresses_root = remove_from_ktree(paymentaddresses_root, item,
cmp_paymentaddresses);
paymentaddresses_create_root = remove_from_ktree(paymentaddresses_create_root,
item, cmp_payaddr_create);
copy_tv(&(row->expirydate), cd);
paymentaddresses_root = add_to_ktree(paymentaddresses_root, item,
cmp_paymentaddresses);
paymentaddresses_create_root = add_to_ktree(paymentaddresses_create_root,
item, cmp_payaddr_create);
}
item = prev;
DATA_PAYMENTADDRESSES_NULL(row, item);
@ -1658,6 +1728,8 @@ unparam:
if (!pa->match) {
paymentaddresses_root = add_to_ktree(paymentaddresses_root, match,
cmp_paymentaddresses);
paymentaddresses_create_root = add_to_ktree(paymentaddresses_create_root,
match, cmp_payaddr_create);
k_unlink_item(pa_store, match);
k_add_head(paymentaddresses_store, match);
count++;
@ -1745,7 +1817,10 @@ bool paymentaddresses_fill(PGconn *conn)
if (!ok)
break;
paymentaddresses_root = add_to_ktree(paymentaddresses_root, item, cmp_paymentaddresses);
paymentaddresses_root = add_to_ktree(paymentaddresses_root, item,
cmp_paymentaddresses);
paymentaddresses_create_root = add_to_ktree(paymentaddresses_create_root,
item, cmp_payaddr_create);
k_add_head(paymentaddresses_store, item);
}
if (!ok)
@ -1762,30 +1837,169 @@ bool paymentaddresses_fill(PGconn *conn)
return ok;
}
// The timing of the memory table updates depends on 'already'
void payments_add_ram(bool ok, K_ITEM *p_item, K_ITEM *old_p_item, tv_t *cd)
{
PAYMENTS *oldp;
LOGDEBUG("%s(): ok %c", __func__, ok ? 'Y' : 'N');
K_WLOCK(payments_free);
if (!ok) {
// Cleanup for the calling function
k_add_head(payments_free, p_item);
} else {
if (old_p_item) {
DATA_PAYMENTS(oldp, old_p_item);
payments_root = remove_from_ktree(payments_root, old_p_item, cmp_payments);
copy_tv(&(oldp->expirydate), cd);
payments_root = add_to_ktree(payments_root, old_p_item, cmp_payments);
}
payments_root = add_to_ktree(payments_root, p_item, cmp_payments);
k_add_head(payments_store, p_item);
}
K_WUNLOCK(payments_free);
}
/* Add means create a new one and expire the old one if it exists,
* otherwise we only expire the old one if it exists
* It's the calling functions job to determine if a new one is required
* - i.e. if there is a difference between the old and new
* already = already begun a transaction - and don't update the ram table */
bool payments_add(PGconn *conn, bool add, K_ITEM *p_item, K_ITEM **old_p_item,
char *by, char *code, char *inet, tv_t *cd, K_TREE *trf_root,
bool already)
{
ExecStatusType rescode;
bool conned = false;
PGresult *res;
bool ok = false, begun = false;
PAYMENTS *row, *oldp = NULL;
char *upd, *ins;
char *params[11 + HISTORYDATECOUNT];
int n, par = 0;
LOGDEBUG("%s(): add %c already %c", __func__,
add ? 'Y' : 'N', already ? 'Y' : 'N');
DATA_PAYMENTS(row, p_item);
K_RLOCK(payments_free);
*old_p_item = find_payments(row->payoutid, row->userid, row->subname);
K_RUNLOCK(payments_free);
conned = CKPQConn(&conn);
if (!already) {
begun = CKPQBegin(conn);
if (!begun)
goto unparam;
}
if (*old_p_item) {
LOGDEBUG("%s(): updating old", __func__);
DATA_PAYMENTS(oldp, *old_p_item);
upd = "update payments set expirydate=$1 where paymentid=$2"
" and expirydate=$3";
par = 0;
params[par++] = tv_to_buf(cd, NULL, 0);
params[par++] = bigint_to_buf(oldp->paymentid, NULL, 0);
params[par++] = tv_to_buf((tv_t *)&default_expiry, NULL, 0);
PARCHKVAL(par, 3, params);
res = PQexecParams(conn, upd, par, NULL, (const char **)params, NULL, NULL, 0, CKPQ_WRITE);
rescode = PQresultStatus(res);
PQclear(res);
if (!PGOK(rescode)) {
PGLOGERR("Update", rescode, conn);
goto rollback;
}
for (n = 0; n < par; n++)
free(params[n]);
par = 0;
// Expiring an old record
row->paymentid = oldp->paymentid;
} else {
if (add) {
// Creating a new record
row->paymentid = nextid(conn, "paymentid", (int64_t)1, cd, by, code, inet);
if (row->paymentid == 0)
goto rollback;
}
}
if (add) {
LOGDEBUG("%s(): adding new", __func__);
HISTORYDATEINIT(row, cd, by, code, inet);
HISTORYDATETRANSFER(trf_root, row);
par = 0;
params[par++] = bigint_to_buf(row->paymentid, NULL, 0);
params[par++] = bigint_to_buf(row->payoutid, NULL, 0);
params[par++] = bigint_to_buf(row->userid, NULL, 0);
params[par++] = str_to_buf(row->subname, NULL, 0);
params[par++] = tv_to_buf(&(row->paydate), NULL, 0);
params[par++] = str_to_buf(row->payaddress, NULL, 0);
params[par++] = str_to_buf(row->originaltxn, NULL, 0);
params[par++] = bigint_to_buf(row->amount, NULL, 0);
params[par++] = double_to_buf(row->diffacc, NULL, 0);
params[par++] = str_to_buf(row->committxn, NULL, 0);
params[par++] = str_to_buf(row->commitblockhash, NULL, 0);
HISTORYDATEPARAMS(params, par, row);
PARCHK(par, params);
ins = "insert into payments "
"(paymentid,payoutid,userid,subname,paydate,payaddress,"
"originaltxn,amount,diffacc,committxn,commitblockhash"
HISTORYDATECONTROL ") values (" PQPARAM16 ")";
res = PQexecParams(conn, ins, par, NULL, (const char **)params, NULL, NULL, 0, CKPQ_WRITE);
rescode = PQresultStatus(res);
if (!PGOK(rescode)) {
PGLOGERR("Insert", rescode, conn);
goto unparam;
}
}
ok = true;
rollback:
if (begun)
CKPQEnd(conn, ok);
unparam:
for (n = 0; n < par; n++)
free(params[n]);
CKPQDisco(&conn, conned);
if (!already)
payments_add_ram(ok, p_item, *old_p_item, cd);
return ok;
}
bool payments_fill(PGconn *conn)
{
ExecStatusType rescode;
PGresult *res;
K_ITEM *item;
PAYMENTS *row;
char *params[1];
int n, i, par = 0;
int n, i;
char *field;
char *sel;
int fields = 8;
int fields = 11;
bool ok;
LOGDEBUG("%s(): select", __func__);
// TODO: handle selecting a subset, eg 20 per web page (in blocklist also)
sel = "select "
"userid,paydate,payaddress,originaltxn,amount,committxn,commitblockhash"
"paymentid,payoutid,userid,subname,paydate,payaddress,"
"originaltxn,amount,diffacc,committxn,commitblockhash"
HISTORYDATECONTROL
",paymentid from payments where expirydate=$1";
par = 0;
params[par++] = tv_to_buf((tv_t *)(&default_expiry), NULL, 0);
PARCHK(par, params);
res = PQexecParams(conn, sel, par, NULL, (const char **)params, NULL, NULL, 0, CKPQ_READ);
" from payments";
res = PQexec(conn, sel, CKPQ_READ);
rescode = PQresultStatus(res);
if (!PGOK(rescode)) {
PGLOGERR("Select", rescode, conn);
@ -1814,11 +2028,26 @@ bool payments_fill(PGconn *conn)
break;
}
PQ_GET_FLD(res, i, "paymentid", field, ok);
if (!ok)
break;
TXT_TO_BIGINT("paymentid", field, row->paymentid);
PQ_GET_FLD(res, i, "payoutid", field, ok);
if (!ok)
break;
TXT_TO_BIGINT("payoutid", field, row->payoutid);
PQ_GET_FLD(res, i, "userid", field, ok);
if (!ok)
break;
TXT_TO_BIGINT("userid", field, row->userid);
PQ_GET_FLD(res, i, "subname", field, ok);
if (!ok)
break;
TXT_TO_STR("subname", field, row->subname);
PQ_GET_FLD(res, i, "paydate", field, ok);
if (!ok)
break;
@ -1839,6 +2068,11 @@ bool payments_fill(PGconn *conn)
break;
TXT_TO_BIGINT("amount", field, row->amount);
PQ_GET_FLD(res, i, "diffacc", field, ok);
if (!ok)
break;
TXT_TO_DOUBLE("diffacc", field, row->diffacc);
PQ_GET_FLD(res, i, "committxn", field, ok);
if (!ok)
break;
@ -1853,11 +2087,6 @@ bool payments_fill(PGconn *conn)
if (!ok)
break;
PQ_GET_FLD(res, i, "paymentid", field, ok);
if (!ok)
break;
TXT_TO_BIGINT("paymentid", field, row->paymentid);
payments_root = add_to_ktree(payments_root, item, cmp_payments);
k_add_head(payments_store, item);
}
@ -3653,7 +3882,7 @@ bool blocks_stats(PGconn *conn, int32_t height, char *blockhash,
dsp_hash(blockhash, hash_dsp, sizeof(hash_dsp));
K_RLOCK(blocks_free);
old_b_item = find_blocks(height, blockhash);
old_b_item = find_blocks(height, blockhash, NULL);
K_RUNLOCK(blocks_free);
if (!old_b_item) {
@ -3811,7 +4040,7 @@ bool blocks_add(PGconn *conn, char *height, char *blockhash,
dsp_hash(blockhash, hash_dsp, sizeof(hash_dsp));
K_RLOCK(blocks_free);
old_b_item = find_blocks(row->height, blockhash);
old_b_item = find_blocks(row->height, blockhash, NULL);
K_RUNLOCK(blocks_free);
DATA_BLOCKS_NULL(oldblocks, old_b_item);
@ -4297,96 +4526,128 @@ bool blocks_fill(PGconn *conn)
return ok;
}
bool miningpayouts_add(PGconn *conn, char *username, char *height,
char *blockhash, char *amount, char *by,
char *code, char *inet, tv_t *cd, K_TREE *trf_root)
// The timing of the memory table updates depends on 'already'
void miningpayouts_add_ram(bool ok, K_ITEM *mp_item, K_ITEM *old_mp_item, tv_t *cd)
{
MININGPAYOUTS *oldmp;
LOGDEBUG("%s(): ok %c", __func__, ok ? 'Y' : 'N');
K_WLOCK(miningpayouts_free);
if (!ok) {
// Cleanup for the calling function
k_add_head(miningpayouts_free, mp_item);
} else {
if (old_mp_item) {
DATA_MININGPAYOUTS(oldmp, old_mp_item);
miningpayouts_root = remove_from_ktree(miningpayouts_root, old_mp_item, cmp_miningpayouts);
copy_tv(&(oldmp->expirydate), cd);
miningpayouts_root = add_to_ktree(miningpayouts_root, old_mp_item, cmp_miningpayouts);
}
miningpayouts_root = add_to_ktree(miningpayouts_root, mp_item, cmp_miningpayouts);
k_add_head(miningpayouts_store, mp_item);
}
K_WUNLOCK(miningpayouts_free);
}
/* Add means create a new one and expire the old one if it exists,
* otherwise we only expire the old one if it exists
* It's the calling functions job to determine if a new one is required
* - i.e. if there is a difference between the old and new
* already = already begun a transaction - and don't update the ram table */
bool miningpayouts_add(PGconn *conn, bool add, K_ITEM *mp_item,
K_ITEM **old_mp_item, char *by, char *code, char *inet,
tv_t *cd, K_TREE *trf_root, bool already)
{
ExecStatusType rescode;
bool conned = false;
PGresult *res;
K_ITEM *m_item, *u_item;
bool ok = false;
MININGPAYOUTS *row;
USERS *users;
char *ins;
char *params[5 + HISTORYDATECOUNT];
bool ok = false, begun = false;
MININGPAYOUTS *row, *oldmp = NULL;
char *upd, *ins;
char *params[4 + HISTORYDATECOUNT];
int n, par = 0;
LOGDEBUG("%s(): add", __func__);
LOGDEBUG("%s(): add %c already %c", __func__,
add ? 'Y' : 'N', already ? 'Y' : 'N');
K_WLOCK(miningpayouts_free);
m_item = k_unlink_head(miningpayouts_free);
K_WUNLOCK(miningpayouts_free);
DATA_MININGPAYOUTS(row, m_item);
DATA_MININGPAYOUTS(row, mp_item);
K_RLOCK(miningpayouts_free);
*old_mp_item = find_miningpayouts(row->payoutid, row->userid);
K_RUNLOCK(miningpayouts_free);
if (conn == NULL) {
conn = dbconnect();
conned = true;
conned = CKPQConn(&conn);
if (!already) {
begun = CKPQBegin(conn);
if (!begun)
goto unparam;
}
if (*old_mp_item) {
LOGDEBUG("%s(): updating old", __func__);
row->miningpayoutid = nextid(conn, "miningpayoutid", (int64_t)1, cd, by, code, inet);
if (row->miningpayoutid == 0)
goto unitem;
DATA_MININGPAYOUTS(oldmp, *old_mp_item);
K_RLOCK(users_free);
u_item = find_users(username);
K_RUNLOCK(users_free);
if (!u_item) {
char *txt;
LOGERR("%s(): unknown user '%s'",
__func__,
txt = safe_text(username));
free(txt);
goto unitem;
upd = "update miningpayouts set expirydate=$1 where payoutid=$2"
" and userid=$3 and expirydate=$4";
par = 0;
params[par++] = tv_to_buf(cd, NULL, 0);
params[par++] = bigint_to_buf(oldmp->payoutid, NULL, 0);
params[par++] = bigint_to_buf(oldmp->userid, NULL, 0);
params[par++] = tv_to_buf((tv_t *)&default_expiry, NULL, 0);
PARCHKVAL(par, 4, params);
res = PQexecParams(conn, upd, par, NULL, (const char **)params, NULL, NULL, 0, CKPQ_WRITE);
rescode = PQresultStatus(res);
PQclear(res);
if (!PGOK(rescode)) {
PGLOGERR("Update", rescode, conn);
goto rollback;
}
for (n = 0; n < par; n++)
free(params[n]);
par = 0;
}
DATA_USERS(users, u_item);
row->userid = users->userid;
TXT_TO_INT("height", height, row->height);
STRNCPY(row->blockhash, blockhash);
TXT_TO_BIGINT("amount", amount, row->amount);
if (add) {
LOGDEBUG("%s(): adding new", __func__);
HISTORYDATEINIT(row, cd, by, code, inet);
HISTORYDATETRANSFER(trf_root, row);
HISTORYDATEINIT(row, cd, by, code, inet);
HISTORYDATETRANSFER(trf_root, row);
par = 0;
params[par++] = bigint_to_buf(row->miningpayoutid, NULL, 0);
params[par++] = bigint_to_buf(row->userid, NULL, 0);
params[par++] = int_to_buf(row->height, NULL, 0);
params[par++] = str_to_buf(row->blockhash, NULL, 0);
params[par++] = bigint_to_buf(row->amount, NULL, 0);
HISTORYDATEPARAMS(params, par, row);
PARCHK(par, params);
par = 0;
params[par++] = bigint_to_buf(row->payoutid, NULL, 0);
params[par++] = bigint_to_buf(row->userid, NULL, 0);
params[par++] = double_to_buf(row->diffacc, NULL, 0);
params[par++] = bigint_to_buf(row->amount, NULL, 0);
HISTORYDATEPARAMS(params, par, row);
PARCHK(par, params);
ins = "insert into miningpayouts "
"(miningpayoutid,userid,height,blockhash,amount"
HISTORYDATECONTROL ") values (" PQPARAM10 ")";
ins = "insert into miningpayouts "
"(payoutid,userid,diffacc,amount"
HISTORYDATECONTROL ") values (" PQPARAM9 ")";
res = PQexecParams(conn, ins, par, NULL, (const char **)params, NULL, NULL, 0, CKPQ_WRITE);
rescode = PQresultStatus(res);
if (!PGOK(rescode)) {
PGLOGERR("Insert", rescode, conn);
goto unparam;
res = PQexecParams(conn, ins, par, NULL, (const char **)params, NULL, NULL, 0, CKPQ_WRITE);
rescode = PQresultStatus(res);
if (!PGOK(rescode)) {
PGLOGERR("Insert", rescode, conn);
goto unparam;
}
}
ok = true;
rollback:
if (begun)
CKPQEnd(conn, ok);
unparam:
PQclear(res);
for (n = 0; n < par; n++)
free(params[n]);
unitem:
if (conned)
PQfinish(conn);
K_WLOCK(miningpayouts_free);
if (!ok)
k_add_head(miningpayouts_free, m_item);
else {
miningpayouts_root = add_to_ktree(miningpayouts_root, m_item, cmp_miningpayouts);
k_add_head(miningpayouts_store, m_item);
}
K_WUNLOCK(miningpayouts_free);
CKPQDisco(&conn, conned);
if (!already)
miningpayouts_add_ram(ok, mp_item, *old_mp_item, cd);
return ok;
}
@ -4397,23 +4658,19 @@ bool miningpayouts_fill(PGconn *conn)
PGresult *res;
K_ITEM *item;
MININGPAYOUTS *row;
char *params[1];
int n, i, par = 0;
int n, i;
char *field;
char *sel;
int fields = 5;
int fields = 4;
bool ok;
LOGDEBUG("%s(): select", __func__);
sel = "select "
"miningpayoutid,userid,height,blockhash,amount"
"payoutid,userid,diffacc,amount"
HISTORYDATECONTROL
" from miningpayouts where expirydate=$1";
par = 0;
params[par++] = tv_to_buf((tv_t *)(&default_expiry), NULL, 0);
PARCHK(par, params);
res = PQexecParams(conn, sel, par, NULL, (const char **)params, NULL, NULL, 0, CKPQ_READ);
" from miningpayouts";
res = PQexec(conn, sel, CKPQ_READ);
rescode = PQresultStatus(res);
if (!PGOK(rescode)) {
PGLOGERR("Select", rescode, conn);
@ -4442,25 +4699,20 @@ bool miningpayouts_fill(PGconn *conn)
break;
}
PQ_GET_FLD(res, i, "miningpayoutid", field, ok);
PQ_GET_FLD(res, i, "payoutid", field, ok);
if (!ok)
break;
TXT_TO_BIGINT("miningpayoutid", field, row->miningpayoutid);
TXT_TO_BIGINT("payoutid", field, row->payoutid);
PQ_GET_FLD(res, i, "userid", field, ok);
if (!ok)
break;
TXT_TO_BIGINT("userid", field, row->userid);
PQ_GET_FLD(res, i, "height", field, ok);
if (!ok)
break;
TXT_TO_INT("height", field, row->height);
PQ_GET_FLD(res, i, "blockhash", field, ok);
PQ_GET_FLD(res, i, "diffacc", field, ok);
if (!ok)
break;
TXT_TO_STR("blockhash", field, row->blockhash);
TXT_TO_DOUBLE("diffacc", field, row->diffacc);
PQ_GET_FLD(res, i, "amount", field, ok);
if (!ok)
@ -4490,6 +4742,292 @@ bool miningpayouts_fill(PGconn *conn)
return ok;
}
// The timing of the memory table updates depends on 'already'
void payouts_add_ram(bool ok, K_ITEM *p_item, K_ITEM *old_p_item, tv_t *cd)
{
PAYOUTS *oldp;
LOGDEBUG("%s(): ok %c", __func__, ok ? 'Y' : 'N');
K_WLOCK(payouts_free);
if (!ok) {
// Cleanup for the calling function
k_add_head(payouts_free, p_item);
} else {
if (old_p_item) {
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);
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_root = add_to_ktree(payouts_root, p_item, cmp_payouts);
payouts_id_root = add_to_ktree(payouts_id_root, p_item, cmp_payouts_id);
k_add_head(payouts_store, p_item);
}
K_WUNLOCK(payouts_free);
}
/* Add means create a new one and expire the old one if it exists,
* otherwise we only expire the old one if it exists
* It's the calling functions job to determine if a new one is required
* - i.e. if there is a difference between the old and new
* already = already begun a transaction - and don't update the ram table */
bool payouts_add(PGconn *conn, bool add, K_ITEM *p_item, K_ITEM **old_p_item,
char *by, char *code, char *inet, tv_t *cd, K_TREE *trf_root,
bool already)
{
ExecStatusType rescode;
bool conned = false;
PGresult *res;
bool ok = false, begun = false;
PAYOUTS *row, *oldpayouts = NULL;
char *upd, *ins;
char *params[13 + HISTORYDATECOUNT];
int n, par = 0;
LOGDEBUG("%s(): add %c already %c", __func__,
add ? 'Y' : 'N', already ? 'Y' : 'N');
DATA_PAYOUTS(row, p_item);
K_RLOCK(payouts_free);
*old_p_item = find_payouts(row->height, row->blockhash);
K_RUNLOCK(payouts_free);
conned = CKPQConn(&conn);
if (!already) {
begun = CKPQBegin(conn);
if (!begun)
goto unparam;
}
if (*old_p_item) {
LOGDEBUG("%s(): updating old", __func__);
DATA_PAYOUTS(oldpayouts, *old_p_item);
upd = "update payouts set expirydate=$1 where payoutid=$2"
" and expirydate=$3";
par = 0;
params[par++] = tv_to_buf(cd, NULL, 0);
params[par++] = bigint_to_buf(oldpayouts->payoutid, NULL, 0);
params[par++] = tv_to_buf((tv_t *)&default_expiry, NULL, 0);
PARCHKVAL(par, 3, params);
res = PQexecParams(conn, upd, par, NULL, (const char **)params, NULL, NULL, 0, CKPQ_WRITE);
rescode = PQresultStatus(res);
PQclear(res);
if (!PGOK(rescode)) {
PGLOGERR("Update", rescode, conn);
goto rollback;
}
for (n = 0; n < par; n++)
free(params[n]);
par = 0;
// Expiring an old record
row->payoutid = oldpayouts->payoutid;
} else {
if (add) {
// Creating a new record
row->payoutid = nextid(conn, "payoutid", (int64_t)1, cd, by, code, inet);
if (row->payoutid == 0)
goto rollback;
}
}
if (add) {
LOGDEBUG("%s(): adding new", __func__);
HISTORYDATEINIT(row, cd, by, code, inet);
HISTORYDATETRANSFER(trf_root, row);
par = 0;
params[par++] = bigint_to_buf(row->payoutid, NULL, 0);
params[par++] = int_to_buf(row->height, NULL, 0);
params[par++] = str_to_buf(row->blockhash, NULL, 0);
params[par++] = bigint_to_buf(row->minerreward, NULL, 0);
params[par++] = bigint_to_buf(row->workinfoidstart, NULL, 0);
params[par++] = bigint_to_buf(row->workinfoidend, NULL, 0);
params[par++] = bigint_to_buf(row->elapsed, NULL, 0);
params[par++] = str_to_buf(row->status, NULL, 0);
params[par++] = double_to_buf(row->diffwanted, NULL, 0);
params[par++] = double_to_buf(row->diffused, NULL, 0);
params[par++] = double_to_buf(row->shareacc, NULL, 0);
params[par++] = tv_to_buf(&(row->lastshareacc), NULL, 0);
params[par++] = str_to_buf(row->stats, NULL, 0);
HISTORYDATEPARAMS(params, par, row);
PARCHK(par, params);
ins = "insert into payouts "
"(payoutid,height,blockhash,minerreward,workinfoidstart,"
"workinfoidend,elapsed,status,diffwanted,diffused,shareacc,"
"lastshareacc,stats"
HISTORYDATECONTROL ") values (" PQPARAM18 ")";
res = PQexecParams(conn, ins, par, NULL, (const char **)params, NULL, NULL, 0, CKPQ_WRITE);
rescode = PQresultStatus(res);
if (!PGOK(rescode)) {
PGLOGERR("Insert", rescode, conn);
goto unparam;
}
}
ok = true;
rollback:
if (begun)
CKPQEnd(conn, ok);
unparam:
for (n = 0; n < par; n++)
free(params[n]);
CKPQDisco(&conn, conned);
if (!already)
payouts_add_ram(ok, p_item, *old_p_item, cd);
return ok;
}
bool payouts_fill(PGconn *conn)
{
ExecStatusType rescode;
PGresult *res;
K_ITEM *item;
PAYOUTS *row;
int n, i;
char *field;
char *sel;
int fields = 13;
bool ok;
LOGDEBUG("%s(): select", __func__);
sel = "select "
"payoutid,height,blockhash,minerreward,workinfoidstart,workinfoidend,"
"elapsed,status,diffwanted,diffused,shareacc,lastshareacc,stats"
HISTORYDATECONTROL
" from payouts";
res = PQexec(conn, sel, CKPQ_READ);
rescode = PQresultStatus(res);
if (!PGOK(rescode)) {
PGLOGERR("Select", rescode, conn);
PQclear(res);
return false;
}
n = PQnfields(res);
if (n != (fields + HISTORYDATECOUNT)) {
LOGERR("%s(): Invalid field count - should be %d, but is %d",
__func__, fields + HISTORYDATECOUNT, n);
PQclear(res);
return false;
}
n = PQntuples(res);
LOGDEBUG("%s(): tree build count %d", __func__, n);
ok = true;
K_WLOCK(payouts_free);
for (i = 0; i < n; i++) {
item = k_unlink_head(payouts_free);
DATA_PAYOUTS(row, item);
if (everyone_die) {
ok = false;
break;
}
PQ_GET_FLD(res, i, "payoutid", field, ok);
if (!ok)
break;
TXT_TO_BIGINT("payoutid", field, row->payoutid);
PQ_GET_FLD(res, i, "height", field, ok);
if (!ok)
break;
TXT_TO_INT("height", field, row->height);
PQ_GET_FLD(res, i, "blockhash", field, ok);
if (!ok)
break;
TXT_TO_STR("blockhash", field, row->blockhash);
PQ_GET_FLD(res, i, "minerreward", field, ok);
if (!ok)
break;
TXT_TO_BIGINT("minerreward", field, row->minerreward);
PQ_GET_FLD(res, i, "workinfoidstart", field, ok);
if (!ok)
break;
TXT_TO_BIGINT("workinfoidstart", field, row->workinfoidstart);
PQ_GET_FLD(res, i, "workinfoidend", field, ok);
if (!ok)
break;
TXT_TO_BIGINT("workinfoidend", field, row->workinfoidend);
PQ_GET_FLD(res, i, "elapsed", field, ok);
if (!ok)
break;
TXT_TO_BIGINT("elapsed", field, row->elapsed);
PQ_GET_FLD(res, i, "status", field, ok);
if (!ok)
break;
TXT_TO_STR("status", field, row->status);
PQ_GET_FLD(res, i, "diffwanted", field, ok);
if (!ok)
break;
TXT_TO_DOUBLE("diffwanted", field, row->diffwanted);
PQ_GET_FLD(res, i, "diffused", field, ok);
if (!ok)
break;
TXT_TO_DOUBLE("diffused", field, row->diffused);
PQ_GET_FLD(res, i, "shareacc", field, ok);
if (!ok)
break;
TXT_TO_DOUBLE("shareacc", field, row->shareacc);
PQ_GET_FLD(res, i, "lastshareacc", field, ok);
if (!ok)
break;
TXT_TO_TV("lastshareacc", field, row->lastshareacc);
PQ_GET_FLD(res, i, "stats", field, ok);
if (!ok)
break;
TXT_TO_BLOB("stats", field, row->stats);
HISTORYDATEFLDS(res, i, row, ok);
if (!ok)
break;
payouts_root = add_to_ktree(payouts_root, item, cmp_payouts);
payouts_id_root = add_to_ktree(payouts_id_root, item, cmp_payouts_id);
k_add_head(payouts_store, item);
tick();
}
if (!ok)
k_add_head(payouts_free, item);
K_WUNLOCK(payouts_free);
PQclear(res);
if (ok) {
LOGDEBUG("%s(): built", __func__);
LOGWARNING("%s(): loaded %d payout records", __func__, n);
}
return ok;
}
// TODO: discard them from RAM
bool auths_add(PGconn *conn, char *poolinstance, char *username,
char *workername, char *clientid, char *enonce1,
@ -5312,7 +5850,8 @@ bool _workmarkers_process(PGconn *conn, bool already, bool add,
bool ok = false, begun = false;
int n, par = 0;
LOGDEBUG("%s(): add", __func__);
LOGDEBUG("%s(): add %c already %c", __func__,
add ? 'Y' : 'N', already ? 'Y' : 'N');
if (markerid == 0) {
K_RLOCK(workmarkers_free);
@ -5324,6 +5863,8 @@ bool _workmarkers_process(PGconn *conn, bool already, bool add,
K_RUNLOCK(workmarkers_free);
}
if (old_wm_item) {
LOGDEBUG("%s(): updating old", __func__);
DATA_WORKMARKERS(oldworkmarkers, old_wm_item);
if (!conn) {
conn = dbconnect();
@ -5363,6 +5904,8 @@ bool _workmarkers_process(PGconn *conn, bool already, bool add,
}
if (add) {
LOGDEBUG("%s(): adding new", __func__);
if (poolinstance == NULL || description == NULL ||
status == NULL) {
LOGEMERG("%s(): NULL field(s) passed:%s%s%s"
@ -5639,12 +6182,14 @@ bool _marks_process(PGconn *conn, bool add, char *poolinstance,
bool ok = false, begun = false;
int n, par = 0;
LOGDEBUG("%s(): add", __func__);
LOGDEBUG("%s(): add %c", __func__, add ? 'Y' : 'N');
K_RLOCK(marks_free);
old_m_item = find_marks(workinfoid);
K_RUNLOCK(marks_free);
if (old_m_item) {
LOGDEBUG("%s(): updating old", __func__);
DATA_MARKS(oldmarks, old_m_item);
if (!conn) {
conn = dbconnect();
@ -5682,6 +6227,8 @@ bool _marks_process(PGconn *conn, bool add, char *poolinstance,
}
if (add) {
LOGDEBUG("%s(): adding new", __func__);
if (poolinstance == NULL || description == NULL ||
extra == NULL || marktype == NULL || status == NULL) {
LOGEMERG("%s(): NULL field(s) passed:%s%s%s%s%s"

11
src/ckpool.c

@ -535,10 +535,6 @@ void *async_send_proc(void *arg)
LOGERR("Attempted to send message %s to null path in send_proc", msg ? msg : "");
goto out;
}
if (unlikely(!msg || !strlen(msg))) {
LOGERR("Attempted to send null message to socket %s in send_proc", path);
goto out;
}
/* At startup the pid fields are not set up before some processes are
* forked so they never inherit them. */
if (unlikely(!pi->pid)) {
@ -580,9 +576,14 @@ out_nofail:
* closing the socket immediately. */
void _send_proc(proc_instance_t *pi, const char *msg, const char *file, const char *func, const int line)
{
struct proc_message *pm = ckalloc(sizeof(struct proc_message));
struct proc_message *pm;
pthread_t pth;
if (unlikely(!msg || !strlen(msg))) {
LOGERR("Attempted to send null message to %s in send_proc", pi->processname);
return;
}
pm = ckalloc(sizeof(struct proc_message));
pm->pi = pi;
pm->msg = strdup(msg);
pm->file = file;

9
src/ckpool.h

@ -73,6 +73,15 @@ struct char_entry {
char *buf;
};
typedef struct log_entry log_entry_t;
struct log_entry {
log_entry_t *next;
log_entry_t *prev;
char *fname;
char *buf;
};
struct server_instance {
/* Hash table data */
UT_hash_handle hh;

65
src/stratifier.c

@ -4088,6 +4088,36 @@ static void update_workerstats(ckpool_t *ckp, sdata_t *sdata)
}
}
static void add_log_entry(log_entry_t **entries, char **fname, char **buf)
{
log_entry_t *entry = ckalloc(sizeof(log_entry_t));
entry->fname = *fname;
*fname = NULL;
entry->buf = *buf;
*buf = NULL;
DL_APPEND(*entries, entry);
}
static void dump_log_entries(log_entry_t **entries)
{
log_entry_t *entry, *tmpentry;
FILE *fp;
DL_FOREACH_SAFE(*entries, entry, tmpentry) {
DL_DELETE(*entries, entry);
fp = fopen(entry->fname, "we");
if (likely(fp)) {
fprintf(fp, "%s", entry->buf);
fclose(fp);
} else
LOGERR("Failed to fopen %s in dump_log_entries", entry->fname);
free(entry->fname);
free(entry->buf);
free(entry);
}
}
static void *statsupdate(void *arg)
{
ckpool_t *ckp = (ckpool_t *)arg;
@ -4107,14 +4137,14 @@ static void *statsupdate(void *arg)
char suffix360[16], suffix1440[16], suffix10080[16];
char_entry_t *char_list = NULL, *char_t, *chartmp_t;
stratum_instance_t *client, *tmp;
log_entry_t *log_entries = NULL;
user_instance_t *user, *tmpuser;
int idle_workers = 0;
char fname[512] = {};
char *fname, *s;
tv_t now, diff;
ts_t ts_now;
json_t *val;
FILE *fp;
char *s;
int i;
tv_time(&now);
@ -4180,17 +4210,10 @@ static void *statsupdate(void *arg)
"lastupdate", now.tv_sec,
"bestshare", worker->best_diff);
snprintf(fname, 511, "%s/workers/%s", ckp->logdir, worker->workername);
fp = fopen(fname, "we");
if (unlikely(!fp)) {
LOGERR("Failed to fopen %s", fname);
continue;
}
ASPRINTF(&fname, "%s/workers/%s", ckp->logdir, worker->workername);
s = json_dumps(val, JSON_NO_UTF8 | JSON_PRESERVE_ORDER | JSON_EOL);
fprintf(fp, "%s", s);
dealloc(s);
add_log_entry(&log_entries, &fname, &s);
json_decref(val);
fclose(fp);
}
/* Decay times per user */
@ -4230,25 +4253,22 @@ static void *statsupdate(void *arg)
"workers", user->workers,
"bestshare", user->best_diff);
snprintf(fname, 511, "%s/users/%s", ckp->logdir, user->username);
fp = fopen(fname, "we");
if (unlikely(!fp)) {
LOGERR("Failed to fopen %s", fname);
continue;
}
s = json_dumps(val, JSON_NO_UTF8 | JSON_PRESERVE_ORDER);
fprintf(fp, "%s\n", s);
ASPRINTF(&fname, "%s/users/%s", ckp->logdir, user->username);
s = json_dumps(val, JSON_NO_UTF8 | JSON_PRESERVE_ORDER | JSON_EOL);
add_log_entry(&log_entries, &fname, &s);
if (!idle) {
char_t = ckalloc(sizeof(char_entry_t));
s = json_dumps(val, JSON_NO_UTF8 | JSON_PRESERVE_ORDER);
ASPRINTF(&char_t->buf, "User %s:%s", user->username, s);
DL_APPEND(char_list, char_t);
}
dealloc(s);
json_decref(val);
fclose(fp);
}
ck_runlock(&sdata->instance_lock);
/* Dump log entries out of instance_lock */
dump_log_entries(&log_entries);
DL_FOREACH_SAFE(char_list, char_t, chartmp_t) {
LOGNOTICE("%s", char_t->buf);
DL_DELETE(char_list, char_t);
@ -4277,10 +4297,11 @@ static void *statsupdate(void *arg)
ghs10080 = stats->dsps10080 * nonces;
suffix_string(ghs10080, suffix10080, 16, 0);
snprintf(fname, 511, "%s/pool/pool.status", ckp->logdir);
ASPRINTF(&fname, "%s/pool/pool.status", ckp->logdir);
fp = fopen(fname, "we");
if (unlikely(!fp))
LOGERR("Failed to fopen %s", fname);
dealloc(fname);
JSON_CPACK(val, "{si,si,si,si,si,si}",
"runtime", diff.tv_sec,

Loading…
Cancel
Save