diff --git a/src/ckdb.c b/src/ckdb.c index 4dca76ad..222682db 100644 --- a/src/ckdb.c +++ b/src/ckdb.c @@ -2662,6 +2662,7 @@ static void *socketer(__maybe_unused void *arg) case CMD_PAYMENTS: case CMD_PPLNS: case CMD_PPLNS2: + case CMD_PAYOUTS: case CMD_DSP: case CMD_BLOCKSTATUS: if (!startup_complete) { @@ -2880,6 +2881,7 @@ static bool reload_line(PGconn *conn, char *filename, uint64_t count, char *buf) 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", diff --git a/src/ckdb.h b/src/ckdb.h index 7f65e817..3a529a49 100644 --- a/src/ckdb.h +++ b/src/ckdb.h @@ -52,7 +52,7 @@ #define DB_VLOCK "1" #define DB_VERSION "1.0.0" -#define CKDB_VERSION DB_VERSION"-1.000" +#define CKDB_VERSION DB_VERSION"-1.001" #define WHERE_FFL " - from %s %s() line %d" #define WHERE_FFL_HERE __FILE__, __func__, __LINE__ @@ -340,7 +340,7 @@ enum cmd_values { CMD_STATS, CMD_PPLNS, CMD_PPLNS2, - CMD_PAYOUT, + CMD_PAYOUTS, CMD_USERSTATUS, CMD_MARKS, CMD_END @@ -1182,6 +1182,7 @@ 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) @@ -1754,7 +1755,7 @@ 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 void process_pplns(int32_t height, char *blockhash, tv_t *now); +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); diff --git a/src/ckdb_cmd.c b/src/ckdb_cmd.c index 5d1bcba0..c7f89e79 100644 --- a/src/ckdb_cmd.c +++ b/src/ckdb_cmd.c @@ -4030,6 +4030,165 @@ 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, payouts2->status, old_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 for 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, @@ -4690,7 +4849,7 @@ struct CMDS ckdb_cmds[] = { { 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_PAYOUT, "payout", false, false, cmd_payout, ACCESS_SYSTEM }, + { 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 } diff --git a/src/ckdb_data.c b/src/ckdb_data.c index 3d227af9..ce38a11a 100644 --- a/src/ckdb_data.c +++ b/src/ckdb_data.c @@ -2557,11 +2557,11 @@ K_ITEM *find_payoutid(int64_t payoutid) N.B. process_pplns() is only automatically triggered once after the block summarisation is verified, so it can always report all errors */ -void process_pplns(int32_t height, char *blockhash, tv_t *addr_cd) +bool process_pplns(int32_t height, char *blockhash, tv_t *addr_cd) { K_TREE_CTX b_ctx[1], ss_ctx[1], wm_ctx[1], ms_ctx[1], pay_ctx[1], mu_ctx[1]; bool allow_aged = true, conned = false, begun = false; - bool countbacklimit, ok; + bool countbacklimit, ok = false; PGconn *conn = NULL; MININGPAYOUTS *miningpayouts; OPTIONCONTROL *optioncontrol; @@ -2586,7 +2586,8 @@ void process_pplns(int32_t height, char *blockhash, tv_t *addr_cd) char ndiffbin[TXT_SML+1]; double diff_times, diff_add; char cd_buf[CDATE_BUFSIZ]; - tv_t begin_tv, end_tv, now; + tv_t end_tv = { 0L, 0L }; + tv_t begin_tv, now; char buf[1024]; /* @@ -2624,30 +2625,30 @@ void process_pplns(int32_t height, char *blockhash, tv_t *addr_cd) goto oku; } DATA_BLOCKS(blocks, b_item); - // If addr_cd is null, use the block NEW createdate - if (!addr_cd) { - 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) { + 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, &(blocks->createdate)); + if (!addr_cd) addr_cd = &(blocks2->createdate); - break; - } - b2_item = next_in_ktree(b_ctx); - DATA_BLOCKS_NULL(blocks2, b2_item); - } - if (!addr_cd) { - K_RUNLOCK(blocks_free); - LOGEMERG("%s(): missing NEW record for block %"PRId32 - "/%"PRId64"/%s/%s/%"PRId64, - __func__, blocks->height, blocks->workinfoid, - blocks->workername, blocks->confirmed, - blocks->reward); - goto oku; + 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; + } LOGDEBUG("%s(): block %"PRId32"/%"PRId64"/%s/%s/%"PRId64, __func__, blocks->height, blocks->workinfoid, @@ -3211,6 +3212,8 @@ void process_pplns(int32_t height, char *blockhash, tv_t *addr_cd) goto oku; shazbot: + ok = false; + if (begun) CKPQEnd(conn, false); CKPQDisco(&conn, conned); @@ -3250,7 +3253,7 @@ oku: } addr_store = k_free_store(addr_store); } - return; + return ok; } // order by userid asc,createdate asc,authid asc,expirydate desc