diff --git a/pool/base.php b/pool/base.php
index afa6e4cc..4f94e55f 100644
--- a/pool/base.php
+++ b/pool/base.php
@@ -18,6 +18,16 @@ function adddbg($str)
}
}
#
+function sq($str)
+{
+ return str_replace("'", "\\'", $str);
+}
+#
+function dq($str)
+{
+ return str_replace('"', "\\\"", $str);
+}
+#
function howlongago($sec)
{
if ($sec < 60)
diff --git a/pool/db.php b/pool/db.php
index e1939f2f..f8764876 100644
--- a/pool/db.php
+++ b/pool/db.php
@@ -174,15 +174,19 @@ function userReg($user, $email, $pass)
#
function userSettings($user, $email = null, $addr = null, $pass = null)
{
+ $tmo = false;
$flds = array('username' => $user);
if ($email != null)
$flds['email'] = $email;
if ($addr != null)
+ {
$flds['address'] = $addr;
+ $tmo = 3; # 3x the timeout
+ }
if ($pass != null)
$flds['passwordhash'] = myhash($pass);
$msg = msgEncode('usersettings', 'userset', $flds, $user);
- $rep = sendsockreply('userSettings', $msg);
+ $rep = sendsockreply('userSettings', $msg, $tmo);
if (!$rep)
dbdown();
return repDecode($rep);
diff --git a/pool/page.php b/pool/page.php
index db93a9bc..13a2049d 100644
--- a/pool/page.php
+++ b/pool/page.php
@@ -259,8 +259,9 @@ function pgtop($info, $dotop, $user, $douser)
$u1hr = '';
else
{
- $u1hr = dsprate($u1hr);
+ $u1hr = '/'.dsprate($u1hr);
+ // Remove the first XHs if they are the same
if (substr($u1hr, -3) == substr($uhr, -3))
$uhr = substr($uhr, 0, -3);
}
@@ -315,12 +316,17 @@ function pgtop($info, $dotop, $user, $douser)
}
else
{
- if (substr($who, 0, 1) == '1' && strlen($who) > 12)
- $who = substr($who, 0, 11) . '
';
+ $extra = '';
+ $first = substr($who, 0, 1);
+ if (($first == '1' || $first == '3') && strlen($who) > 12)
+ {
+ $who = substr($who, 0, 11);
+ $extra = '
';
+ }
$top .= "
-$who
+".htmlspecialchars($who)."$extra
Hash Rate:
-$uhr/$u1hr";
+$uhr$u1hr";
$top .= makeForm('')."
";
diff --git a/pool/page_blocks.php b/pool/page_blocks.php
index bf97a8e3..9110e5aa 100644
--- a/pool/page_blocks.php
+++ b/pool/page_blocks.php
@@ -111,7 +111,7 @@ function doblocks($data, $user)
$pg .= "
";
- $wn = $ans['workername:'.$i];
+ $wn = htmlspecialchars($ans['workername:'.$i]);
+ $wnv = urlencode($ans['workername:'.$i]);
$pg .= '';
- $pg .= "";
+ $pg .= "";
$pg .= $wn.' | ';
- $md = $ans['difficultydefault:'.$i];
+ $md = intval($ans['difficultydefault:'.$i]);
$pg .= '';
$pg .= "";
$pg .= "";
@@ -66,7 +67,7 @@ function doworkmgt($data, $user)
$settings = array();
for ($i = 0; $i < $count; $i++)
{
- $wn = getparam('workername:'.$i, false);
+ $wn = urldecode(getparam('workername:'.$i, false));
$md = getparam('difficultydefault:'.$i, false);
if (!nuem($wn) && !nuem($md))
{
diff --git a/pool/socket.php b/pool/socket.php
index b8da3e8d..08b683e2 100644
--- a/pool/socket.php
+++ b/pool/socket.php
@@ -1,9 +1,26 @@
$sec, 'usec' => $use);
+ socket_set_option($socket, SOL_SOCKET, SO_SNDTIMEO, $tmo);
+ socket_set_option($socket, SOL_SOCKET, SO_RCVTIMEO, $tmo);
+}
#
# Note that $port in AF_UNIX should be the socket filename
-function _getsock($fun, $port, $unix=true)
+function _getsock($fun, $port, $tmo, $unix=true)
{
$socket = null;
if ($unix === true)
@@ -57,17 +74,15 @@ function _getsock($fun, $port, $unix=true)
}
}
# Avoid getting locked up for long
- $tmo = array('sec' => 2, 'usec' => 0);
- socket_set_option($socket, SOL_SOCKET, SO_SNDTIMEO, $tmo);
- socket_set_option($socket, SOL_SOCKET, SO_RCVTIMEO, $tmo);
+ socktmo($socket, $tmo);
# Enable timeout
socket_set_block($socket);
return $socket;
}
#
-function getsock($fun)
+function getsock($fun, $tmo)
{
- return _getsock($fun, '/opt/ckdb/listener');
+ return _getsock($fun, '/opt/ckdb/listener', $tmo);
}
#
function readsockline($fun, $socket)
@@ -161,10 +176,10 @@ function dosend($fun, $socket, $msg)
return $ret;
}
#
-function sendsock($fun, $msg)
+function sendsock($fun, $msg, $tmo = false)
{
$ret = false;
- $socket = getsock($fun);
+ $socket = getsock($fun, $tmo);
if ($socket !== false)
{
$ret = dosend($fun, $socket, $msg);
@@ -178,10 +193,10 @@ function sendsock($fun, $msg)
# and the data $msg to send to ckdb
# and it returns $ret = false on error or $ret = the string reply
#
-function sendsockreply($fun, $msg)
+function sendsockreply($fun, $msg, $tmo = false)
{
$ret = false;
- $socket = getsock($fun);
+ $socket = getsock($fun, $tmo);
if ($socket !== false)
{
$ret = dosend($fun, $socket, $msg);
diff --git a/src/Makefile.am b/src/Makefile.am
index 1be38c28..5e032e62 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -21,7 +21,7 @@ notifier_LDADD = libckpool.la @JANSSON_LIBS@
if WANT_CKDB
bin_PROGRAMS += ckdb
-ckdb_SOURCES = ckdb.c ckdb_cmd.c ckdb_data.c ckdb_dbio.c ckdb.h \
- klist.c ktree.c klist.h ktree.h
+ckdb_SOURCES = ckdb.c ckdb_cmd.c ckdb_data.c ckdb_dbio.c ckdb_btc.c \
+ ckdb.h klist.c ktree.c klist.h ktree.h
ckdb_LDADD = libckpool.la @JANSSON_LIBS@ @PQ_LIBS@
endif
diff --git a/src/ckdb.c b/src/ckdb.c
index e6ac8f66..8c57682c 100644
--- a/src/ckdb.c
+++ b/src/ckdb.c
@@ -272,8 +272,13 @@ static cklock_t fpm_lock;
static char *first_pool_message;
static sem_t socketer_sem;
+char *btc_server = "http://127.0.0.1:8330";
+char *btc_auth;
+int btc_timeout = 5;
+
char *by_default = "code";
char *inet_default = "127.0.0.1";
+char *id_default = "42";
// LOGQUEUE
K_LIST *logqueue_free;
@@ -373,7 +378,7 @@ K_STORE *sharesummary_store;
// BLOCKS block.id.json={...}
const char *blocks_new = "New";
const char *blocks_confirm = "1-Confirm";
-const char *blocks_42 = "42-Confirm";
+const char *blocks_42 = "Matured";
const char *blocks_orphan = "Orphan";
const char *blocks_unknown = "?Unknown?";
@@ -542,7 +547,7 @@ void logmsg(int loglevel, const char *fmt, ...)
free(buf);
}
-static void setnow(tv_t *now)
+void setnow(tv_t *now)
{
ts_t spec;
spec.tv_sec = 0;
@@ -902,6 +907,7 @@ static void alloc_storage()
LIMIT_PAYMENTADDRESSES, true);
paymentaddresses_store = k_new_store(paymentaddresses_free);
paymentaddresses_root = new_ktree();
+ paymentaddresses_free->dsp_func = dsp_paymentaddresses;
payments_free = k_new_list("Payments", sizeof(PAYMENTS),
ALLOC_PAYMENTS, LIMIT_PAYMENTS, true);
@@ -1257,6 +1263,33 @@ static enum cmd_values breakdown(K_TREE **trf_root, K_STORE **trf_store,
return ckdb_cmds[*which_cmds].cmd_val;
}
+static void check_blocks()
+{
+ K_TREE_CTX ctx[1];
+ K_ITEM *b_item;
+ BLOCKS *blocks;
+
+ K_RLOCK(blocks_free);
+ // Find the oldest block BLOCKS_NEW or BLOCKS_CONFIRM
+ b_item = first_in_ktree(blocks_root, ctx);
+ while (b_item) {
+ DATA_BLOCKS(blocks, b_item);
+ if (!blocks->ignore &&
+ CURRENT(&(blocks->expirydate)) &&
+ (blocks->confirmed[0] == BLOCKS_NEW ||
+ blocks->confirmed[0] == BLOCKS_CONFIRM))
+ break;
+ b_item = next_in_ktree(ctx);
+ }
+ K_RUNLOCK(blocks_free);
+
+ // None
+ if (!b_item)
+ return;
+
+ btc_blockstatus(blocks);
+}
+
static void summarise_blocks()
{
K_ITEM *b_item, *b_prev, *wi_item, ss_look, *ss_item;
@@ -1597,8 +1630,11 @@ static void *summariser(__maybe_unused void *arg)
while (!everyone_die) {
sleep(5);
- if (!everyone_die)
+ if (!everyone_die) {
+ if (startup_complete)
+ check_blocks();
summarise_blocks();
+ }
sleep(4);
if (!everyone_die)
@@ -3019,7 +3055,6 @@ static void check_restore_dir(char *name)
}
static struct option long_options[] = {
- { "dbprefix", required_argument, 0, 'b' },
{ "config", required_argument, 0, 'c' },
{ "dbname", required_argument, 0, 'd' },
{ "help", no_argument, 0, 'h' },
@@ -3027,10 +3062,14 @@ static struct option long_options[] = {
{ "loglevel", required_argument, 0, 'l' },
{ "name", required_argument, 0, 'n' },
{ "dbpass", required_argument, 0, 'p' },
+ { "btc-pass", required_argument, 0, 'P' },
{ "ckpool-logdir", required_argument, 0, 'r' },
{ "logdir", required_argument, 0, 'R' },
{ "sockdir", required_argument, 0, 's' },
+ { "btc-server", required_argument, 0, 'S' },
+ { "btc-timeout", required_argument, 0, 't' },
{ "dbuser", required_argument, 0, 'u' },
+ { "btc-user", required_argument, 0, 'U' },
{ "version", no_argument, 0, 'v' },
{ "confirm", no_argument, 0, 'y' },
{ "confirmrange", required_argument, 0, 'Y' },
@@ -3048,6 +3087,8 @@ static void sighandler(int sig)
int main(int argc, char **argv)
{
struct sigaction handler;
+ char *btc_user = "user";
+ char *btc_pass = "p";
char buf[512];
ckpool_t ckp;
int c, ret, i = 0, j;
@@ -3062,7 +3103,7 @@ int main(int argc, char **argv)
memset(&ckp, 0, sizeof(ckp));
ckp.loglevel = LOG_NOTICE;
- while ((c = getopt_long(argc, argv, "c:d:hkl:n:p:r:R:s:u:vyY:", long_options, &i)) != -1) {
+ while ((c = getopt_long(argc, argv, "c:d:hkl:n:p:P:r:R:s:S:t:u:U:vyY:", long_options, &i)) != -1) {
switch(c) {
case 'c':
ckp.config = strdup(optarg);
@@ -3111,6 +3152,14 @@ int main(int argc, char **argv)
while (*kill)
*(kill++) = '\0';
break;
+ case 'P':
+ btc_pass = strdup(optarg);
+ kill = optarg;
+ if (*kill)
+ *(kill++) = ' ';
+ while (*kill)
+ *(kill++) = '\0';
+ break;
case 'r':
restorefrom = strdup(optarg);
break;
@@ -3120,12 +3169,24 @@ int main(int argc, char **argv)
case 's':
ckp.socket_dir = strdup(optarg);
break;
+ case 'S':
+ btc_server = strdup(optarg);
+ break;
+ case 't':
+ btc_timeout = atoi(optarg);
+ break;
case 'u':
db_user = strdup(optarg);
kill = optarg;
while (*kill)
*(kill++) = ' ';
break;
+ case 'U':
+ btc_user = strdup(optarg);
+ kill = optarg;
+ while (*kill)
+ *(kill++) = ' ';
+ break;
case 'v':
exit(0);
case 'y':
@@ -3139,6 +3200,10 @@ int main(int argc, char **argv)
}
}
+ snprintf(buf, sizeof(buf), "%s:%s", btc_user, btc_pass);
+ btc_auth = http_base64(buf);
+ bzero(buf, sizeof(buf));
+
if (confirm_sharesummary)
dbcode = "y";
else
diff --git a/src/ckdb.h b/src/ckdb.h
index 15386cf7..bc68be96 100644
--- a/src/ckdb.h
+++ b/src/ckdb.h
@@ -52,7 +52,7 @@
#define DB_VLOCK "1"
#define DB_VERSION "0.9.2"
-#define CKDB_VERSION DB_VERSION"-0.504"
+#define CKDB_VERSION DB_VERSION"-0.516"
#define WHERE_FFL " - from %s %s() line %d"
#define WHERE_FFL_HERE __FILE__, __func__, __LINE__
@@ -245,8 +245,13 @@ extern bool everyone_die;
#define STR_SHAREERRORS "shareerror"
#define STR_AGEWORKINFO "ageworkinfo"
+extern char *btc_server;
+extern char *btc_auth;
+extern int btc_timeout;
+
extern char *by_default;
extern char *inet_default;
+extern char *id_default;
enum cmd_values {
CMD_UNSET,
@@ -344,24 +349,26 @@ enum cmd_values {
/* Override _row defaults if transfer fields are present
* We don't care about the reply so it can be small */
#define HISTORYDATETRANSFER(_root, _row) do { \
- char __reply[16]; \
- size_t __siz = sizeof(__reply); \
- K_ITEM *__item; \
- TRANSFER *__transfer; \
- __item = optional_name(_root, "createby", 1, NULL, __reply, __siz); \
- if (__item) { \
- DATA_TRANSFER(__transfer, __item); \
- STRNCPY(_row->createby, __transfer->mvalue); \
- } \
- __item = optional_name(_root, "createcode", 1, NULL, __reply, __siz); \
- if (__item) { \
- DATA_TRANSFER(__transfer, __item); \
- STRNCPY(_row->createcode, __transfer->mvalue); \
- } \
- __item = optional_name(_root, "createinet", 1, NULL, __reply, __siz); \
- if (__item) { \
- DATA_TRANSFER(__transfer, __item); \
- STRNCPY(_row->createinet, __transfer->mvalue); \
+ if (_root) { \
+ char __reply[16]; \
+ size_t __siz = sizeof(__reply); \
+ K_ITEM *__item; \
+ TRANSFER *__transfer; \
+ __item = optional_name(_root, "createby", 1, NULL, __reply, __siz); \
+ if (__item) { \
+ DATA_TRANSFER(__transfer, __item); \
+ STRNCPY(_row->createby, __transfer->mvalue); \
+ } \
+ __item = optional_name(_root, "createcode", 1, NULL, __reply, __siz); \
+ if (__item) { \
+ DATA_TRANSFER(__transfer, __item); \
+ STRNCPY(_row->createcode, __transfer->mvalue); \
+ } \
+ __item = optional_name(_root, "createinet", 1, NULL, __reply, __siz); \
+ if (__item) { \
+ DATA_TRANSFER(__transfer, __item); \
+ STRNCPY(_row->createinet, __transfer->mvalue); \
+ } \
} \
} while (0)
@@ -899,6 +906,7 @@ typedef struct blocks {
int64_t elapsed;
char statsconfirmed[TXT_FLAG+1];
HISTORYDATECONTROLFIELDS;
+ bool ignore; // Non DB field
} BLOCKS;
#define ALLOC_BLOCKS 100
@@ -911,10 +919,16 @@ typedef struct blocks {
#define BLOCKS_NEW_STR "n"
#define BLOCKS_CONFIRM '1'
#define BLOCKS_CONFIRM_STR "1"
+// 42 doesn't actually mean '42' it means matured
#define BLOCKS_42 'F'
#define BLOCKS_42_STR "F"
+// Current block maturity is ... 100
+#define BLOCKS_42_VALUE 100
#define BLOCKS_ORPHAN 'O'
#define BLOCKS_ORPHAN_STR "O"
+/* Block height difference required before checking if it's orphaned
+ * TODO: add a cmd_blockstatus option to un-orphan a block */
+#define BLOCKS_ORPHAN_CHECK 1
#define BLOCKS_STATSPENDING FALSE_CHR
#define BLOCKS_STATSPENDING_STR FALSE_STR
@@ -1167,6 +1181,7 @@ extern K_LIST *workerstatus_free;
extern K_STORE *workerstatus_store;
extern void logmsg(int loglevel, const char *fmt, ...);
+extern void setnow(tv_t *now);
extern void tick();
extern PGconn *dbconnect();
@@ -1268,6 +1283,7 @@ extern K_ITEM *new_worker(PGconn *conn, bool update, int64_t userid, char *worke
char *code, char *inet, tv_t *cd, K_TREE *trf_root);
extern K_ITEM *new_default_worker(PGconn *conn, bool update, int64_t userid, char *workername,
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 K_ITEM *find_paymentaddresses(int64_t userid);
extern cmp_t cmp_payments(K_ITEM *a, K_ITEM *b);
@@ -1290,10 +1306,16 @@ extern void dsp_sharesummary(K_ITEM *item, FILE *stream);
extern cmp_t cmp_sharesummary(K_ITEM *a, K_ITEM *b);
extern cmp_t cmp_sharesummary_workinfoid(K_ITEM *a, K_ITEM *b);
extern void zero_sharesummary(SHARESUMMARY *row, tv_t *cd, double diff);
-extern K_ITEM *find_sharesummary(int64_t userid, char *workername, int64_t workinfoid);
+extern K_ITEM *find_sharesummary(int64_t userid, char *workername,
+ int64_t workinfoid);
extern void auto_age_older(PGconn *conn, int64_t workinfoid, char *poolinstance,
char *by, char *code, char *inet, tv_t *cd);
-extern void dsp_hash(char *hash, char *buf, size_t siz);
+#define dbhash2btchash(_hash, _buf, _siz) \
+ _dbhash2btchash(_hash, _buf, _siz, WHERE_FFL_HERE)
+void _dbhash2btchash(char *hash, char *buf, size_t siz, WHERE_FFL_ARGS);
+#define dsp_hash(_hash, _buf, _siz) \
+ _dsp_hash(_hash, _buf, _siz, WHERE_FFL_HERE)
+extern void _dsp_hash(char *hash, char *buf, size_t siz, WHERE_FFL_ARGS);
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);
@@ -1469,4 +1491,11 @@ struct CMDS {
extern struct CMDS ckdb_cmds[];
+// ***
+// *** ckdb_btc.c
+// ***
+
+extern bool btc_valid_address(char *addr);
+extern void btc_blockstatus(BLOCKS *blocks);
+
#endif
diff --git a/src/ckdb_btc.c b/src/ckdb_btc.c
new file mode 100644
index 00000000..7ede69e5
--- /dev/null
+++ b/src/ckdb_btc.c
@@ -0,0 +1,390 @@
+/*
+ * Copyright 2014 Andrew Smith
+ * Copyright 2014 Con Kolivas
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 3 of the License, or (at your option)
+ * any later version. See COPYING for more details.
+ */
+
+#include "ckdb.h"
+
+#define BTCKEY ((const char *)"result")
+
+#define GETBLOCKHASHCMD "getblockhash"
+#define GETBLOCKHASH "{\"method\":\"" GETBLOCKHASHCMD "\",\"params\":[%d],\"id\":1}"
+#define GETBLOCKHASHKEY NULL
+
+#define GETBLOCKCMD "getblock"
+#define GETBLOCK "{\"method\":\"" GETBLOCKCMD "\",\"params\":[\"%s\"],\"id\":1}"
+#define GETBLOCKCONFKEY ((const char *)"confirmations")
+
+#define VALIDADDRCMD "validateaddress"
+#define VALIDADDR "{\"method\":\"" VALIDADDRCMD "\",\"params\":[\"%s\"],\"id\":1}"
+#define VALIDADDRKEY ((const char *)"isvalid")
+
+static char *btc_data(char *json, size_t *len)
+{
+ size_t off;
+ char tmp[1024];
+ char *buf;
+
+ APPEND_REALLOC_INIT(buf, off, *len);
+ APPEND_REALLOC(buf, off, *len, "POST / HTTP/1.1\n");
+ snprintf(tmp, sizeof(tmp), "Authorization: Basic %s\n", btc_auth);
+ APPEND_REALLOC(buf, off, *len, tmp);
+ snprintf(tmp, sizeof(tmp), "Host: %s/\n", btc_server);
+ APPEND_REALLOC(buf, off, *len, tmp);
+ APPEND_REALLOC(buf, off, *len, "Content-Type: application/json\n");
+ snprintf(tmp, sizeof(tmp), "Content-Length: %d\n\n", (int)strlen(json));
+ APPEND_REALLOC(buf, off, *len, tmp);
+ APPEND_REALLOC(buf, off, *len, json);
+
+ return buf;
+}
+
+#define SOCK_READ 8192
+
+static int read_socket(int fd, char **buf, int timeout)
+{
+ char tmp[SOCK_READ+1];
+ int ret, off, len;
+ tv_t tv_timeout;
+ fd_set readfs;
+
+ len = SOCK_READ;
+ *buf = malloc(len+1);
+ if (!(*buf))
+ quithere(1, "malloc (%d) OOM", len+1);
+ off = 0;
+
+ while (42) {
+ tv_timeout.tv_sec = timeout;
+ tv_timeout.tv_usec = 0;
+ FD_ZERO(&readfs);
+ FD_SET(fd, &readfs);
+ ret = select(fd + 1, &readfs, NULL, NULL, &tv_timeout);
+ if (ret == 0)
+ break;
+
+ if (ret < 0) {
+ LOGERR("%s() btc socket select error %d:%s",
+ __func__, errno, strerror(errno));
+ break;
+ }
+
+ ret = recv(fd, tmp, SOCK_READ, 0);
+ if (ret == 0)
+ break;
+ if (ret < 0) {
+ LOGERR("%s() btc socket recv error %d:%s",
+ __func__, errno, strerror(errno));
+ break;
+ }
+
+ if ((off + ret) > len) {
+ len += SOCK_READ;
+ *buf = realloc(*buf, len + 1);
+ if (!(*buf))
+ quithere(1, "realloc (%d) OOM", len);
+ }
+
+ memcpy(*buf + off, tmp, ret);
+ off += ret;
+ }
+
+ if (close(fd)) {
+ LOGERR("%s() btc socket close error %d:%s",
+ __func__, errno, strerror(errno));
+ }
+
+ return off;
+}
+
+#define btc_io(_cmd, _json) _btc_io(_cmd, _json, WHERE_FFL_HERE)
+
+static char *_btc_io(__maybe_unused const char *cmd, char *json, WHERE_FFL_ARGS)
+{
+ char *ip, *port;
+ char *data, *ans, *res, *ptr;
+ int fd, ret, red;
+ size_t len;
+
+ data = btc_data(json, &len);
+ if (!extract_sockaddr(btc_server, &ip, &port)) {
+ LOGERR("%s() invalid btc server '%s'",
+ __func__, btc_server);
+ return NULL;
+ }
+ fd = connect_socket(ip, port);
+ if (fd < 0) {
+ LOGERR("%s() failed to connect to btc server %s",
+ __func__, btc_server);
+ return NULL;
+ }
+ ret = write_socket(fd, data, len);
+ if (ret != (int)len) {
+ LOGERR("%s() failed to write to btc server %s",
+ __func__, btc_server);
+ return NULL;
+ }
+ red = read_socket(fd, &ans, btc_timeout);
+ ans[red] = '\0';
+ if (strncasecmp(ans, "HTTP/1.1 200 OK", 15)) {
+ char *text = safe_text(ans);
+ LOGERR("%s() btc server response not ok: %s",
+ __func__, text);
+ free(text);
+ free(ans);
+ res = strdup(EMPTY);
+ } else {
+ ptr = strstr(ans, "\n{");
+ if (ptr)
+ res = strdup(ptr+1);
+ else
+ res = strdup(EMPTY);
+ free(ans);
+ }
+ return res;
+}
+
+static json_t *single_decode(char *ans, const char *cmd, const char *key)
+{
+ json_t *json_data, *btc_ob, *json_ob = NULL;
+ json_error_t err_val;
+
+ if (ans && *ans) {
+ json_data = json_loads(ans, JSON_DISABLE_EOF_CHECK, &err_val);
+ if (!json_data) {
+ char *text = safe_text(ans);
+ LOGERR("%s() Json %s decode error "
+ "json_err=(%d:%d:%d)%s:%s ans='%s'",
+ __func__, cmd,
+ err_val.line, err_val.column,
+ err_val.position, err_val.source,
+ err_val.text, text);
+ free(text);
+ } else {
+ btc_ob = json_object_get(json_data, BTCKEY);
+ if (!btc_ob) {
+ char *text = safe_text(ans);
+ LOGERR("%s() Json %s reply missing main key %s "
+ "ans='%s'",
+ __func__, cmd, key, text);
+ free(text);
+ } else {
+ if (key == NULL)
+ json_ob = btc_ob;
+ else {
+ json_ob = json_object_get(btc_ob, key);
+ if (!json_ob) {
+ char *text = safe_text(ans);
+ LOGERR("%s() Json %s reply missing "
+ "sub-key %s ans='%s'",
+ __func__, cmd, key, text);
+ free(text);
+ }
+ }
+ }
+ }
+ }
+ return json_ob;
+}
+
+static char *single_decode_str(char *ans, const char *cmd, const char *key)
+{
+ const char *json_str;
+ char *str = NULL;
+ json_t *json_ob;
+
+ json_ob = single_decode(ans, cmd, key);
+ if (json_ob) {
+ if (!json_is_string(json_ob)) {
+ char *text = safe_text(ans);
+ if (!key)
+ key = BTCKEY;
+ LOGERR("%s() Json %s key %s "
+ "not a string ans='%s'",
+ __func__, cmd, key, text);
+ free(text);
+ } else {
+ json_str = json_string_value(json_ob);
+ if (json_str)
+ str = strdup(json_str);
+ }
+ }
+ return str;
+}
+
+static int64_t single_decode_int(char *ans, const char *cmd, const char *key)
+{
+ json_t *json_ob;
+ int64_t val = 0;
+
+ json_ob = single_decode(ans, cmd, key);
+ if (json_ob) {
+ if (!json_is_integer(json_ob)) {
+ char *text = safe_text(ans);
+ if (!key)
+ key = BTCKEY;
+ LOGERR("%s() Json %s key %s "
+ "not an int ans='%s'",
+ __func__, cmd, key, text);
+ free(text);
+ } else
+ val = (int64_t)json_integer_value(json_ob);
+ }
+ return val;
+}
+
+static bool single_decode_bool(char *ans, const char *cmd, const char *key)
+{
+ json_t *json_ob;
+ int json_typ;
+ bool val = false;
+
+ json_ob = single_decode(ans, cmd, key);
+ if (json_ob) {
+ json_typ = json_typeof(json_ob);
+ if (json_typ != JSON_TRUE && json_typ != JSON_FALSE) {
+ char *text = safe_text(ans);
+ if (!key)
+ key = BTCKEY;
+ LOGERR("%s() Json %s key %s "
+ "not a bool ans='%s'",
+ __func__, cmd, key, text);
+ free(text);
+ } else {
+ if (json_typ == JSON_TRUE)
+ val = true;
+ }
+ }
+ return val;
+}
+
+static char *btc_blockhash(int32_t height)
+{
+ char buf[1024];
+ char *ans;
+ char *hash;
+
+ snprintf(buf, sizeof(buf), GETBLOCKHASH, height);
+ ans = btc_io(GETBLOCKHASHCMD, buf);
+ hash = single_decode_str(ans, GETBLOCKHASHCMD, GETBLOCKHASHKEY);
+ free(ans);
+ return hash;
+}
+
+static int32_t btc_confirms(char *hash)
+{
+ char buf[1024];
+ char *ans;
+ int32_t conf;
+
+ snprintf(buf, sizeof(buf), GETBLOCK, hash);
+ ans = btc_io(GETBLOCKCMD, buf);
+ conf = (int32_t)single_decode_int(ans, GETBLOCKCMD, GETBLOCKCONFKEY);
+ free(ans);
+ return conf;
+}
+
+bool btc_valid_address(char *addr)
+{
+ char buf[1024];
+ char *ans;
+ bool valid;
+
+ snprintf(buf, sizeof(buf), VALIDADDR, addr);
+ ans = btc_io(VALIDADDRCMD, buf);
+ valid = single_decode_bool(ans, VALIDADDRCMD, VALIDADDRKEY);
+ free(ans);
+ return valid;
+}
+
+// Check for orphan or update confirm count
+void btc_blockstatus(BLOCKS *blocks)
+{
+ char hash[TXT_BIG+1];
+ char height_str[32];
+ char *blockhash;
+ int32_t confirms;
+ size_t len;
+ tv_t now;
+ bool ok;
+
+ setnow(&now);
+
+ LOGDEBUG("%s() checking %d %s",
+ __func__,
+ blocks->height, blocks->blockhash);
+
+ // Caller must check this to avoid resending it every time
+ if (blocks->ignore) {
+ LOGERR("%s() ignored block %d passed",
+ __func__, blocks->height);
+ return;
+ }
+
+ len = strlen(blocks->blockhash);
+ if (len != SHA256SIZHEX) {
+ LOGERR("%s() invalid blockhash size %d (%d) for block %d",
+ __func__, len, SHA256SIZHEX, blocks->height);
+
+ /* So we don't keep repeating the message
+ * This should never happen */
+ blocks->ignore = true;
+
+ return;
+ }
+
+ dbhash2btchash(blocks->blockhash, hash, sizeof(hash));
+
+ blockhash = btc_blockhash(blocks->height);
+ // Something's amiss - let it try again later
+ if (!blockhash)
+ return;
+
+ if (strcmp(blockhash, hash) != 0) {
+ snprintf(height_str, sizeof(height_str), "%d", blocks->height);
+ LOGERR("%s() flagging block %d(%s) as %s pool=%s btc=%s",
+ __func__,
+ blocks->height, height_str,
+ blocks_confirmed(BLOCKS_ORPHAN_STR),
+ hash, blockhash);
+
+ ok = blocks_add(NULL, height_str,
+ blocks->blockhash,
+ BLOCKS_ORPHAN_STR,
+ EMPTY, EMPTY, EMPTY, EMPTY,
+ EMPTY, EMPTY, EMPTY, EMPTY,
+ by_default, (char *)__func__, inet_default,
+ &now, false, id_default, NULL);
+
+ if (!ok)
+ blocks->ignore = true;
+
+ return;
+ }
+
+ confirms = btc_confirms(hash);
+ if (confirms >= BLOCKS_42_VALUE) {
+ snprintf(height_str, sizeof(height_str), "%d", blocks->height);
+ LOGERR("%s() flagging block %d(%s) as %s confirms=%d(%d)",
+ __func__,
+ blocks->height, height_str,
+ blocks_confirmed(BLOCKS_42_STR),
+ confirms, BLOCKS_42_VALUE);
+
+ ok = blocks_add(NULL, height_str,
+ blocks->blockhash,
+ BLOCKS_42_STR,
+ EMPTY, EMPTY, EMPTY, EMPTY,
+ EMPTY, EMPTY, EMPTY, EMPTY,
+ by_default, (char *)__func__, inet_default,
+ &now, false, id_default, NULL);
+
+ if (!ok)
+ blocks->ignore = true;
+ }
+}
diff --git a/src/ckdb_cmd.c b/src/ckdb_cmd.c
index 3b99a1b8..ffe6f3e6 100644
--- a/src/ckdb_cmd.c
+++ b/src/ckdb_cmd.c
@@ -240,8 +240,12 @@ static char *cmd_userset(PGconn *conn, char *cmd, char *id,
goto struckout;
}
-// if (address && *address)
-// TODO: validate it
+ if (address && *address) {
+ if (!btc_valid_address(address)) {
+ reason = "Invalid BTC address";
+ goto struckout;
+ }
+ }
if (email && *email) {
ok = users_pass_email(conn, u_item, NULL,
@@ -3271,7 +3275,11 @@ static char *cmd_dsp(__maybe_unused PGconn *conn, __maybe_unused char *cmd,
dsp_ktree(transfer_free, trf_root, transfer_data(i_file), NULL);
- dsp_ktree(sharesummary_free, sharesummary_root, transfer_data(i_file), NULL);
+ dsp_ktree(paymentaddresses_free, paymentaddresses_root,
+ transfer_data(i_file), NULL);
+
+ dsp_ktree(sharesummary_free, sharesummary_root,
+ transfer_data(i_file), NULL);
dsp_ktree(userstats_free, userstats_root, transfer_data(i_file), NULL);
diff --git a/src/ckdb_data.c b/src/ckdb_data.c
index 9dcf4431..30a113f3 100644
--- a/src/ckdb_data.c
+++ b/src/ckdb_data.c
@@ -1006,6 +1006,25 @@ static K_ITEM *new_worker_find_user(PGconn *conn, bool update, char *username,
}
*/
+void dsp_paymentaddresses(K_ITEM *item, FILE *stream)
+{
+ char expirydate_buf[DATE_BUFSIZ], createdate_buf[DATE_BUFSIZ];
+ PAYMENTADDRESSES *pa;
+
+ if (!item)
+ fprintf(stream, "%s() called with (null) item\n", __func__);
+ else {
+ DATA_PAYMENTADDRESSES(pa, item);
+ tv_to_buf(&(pa->expirydate), expirydate_buf, sizeof(expirydate_buf));
+ tv_to_buf(&(pa->createdate), createdate_buf, sizeof(createdate_buf));
+ fprintf(stream, " id=%"PRId64" userid=%"PRId64" addr='%s' "
+ "ratio=%"PRId32" exp=%s cd=%s\n",
+ pa->paymentaddressid, pa->userid,
+ pa->payaddress, pa->payratio,
+ expirydate_buf, createdate_buf);
+ }
+}
+
// order by userid asc,expirydate desc,payaddress asc
cmp_t cmp_paymentaddresses(K_ITEM *a, K_ITEM *b)
{
@@ -1429,7 +1448,6 @@ void dsp_sharesummary(K_ITEM *item, FILE *stream)
fprintf(stream, "%s() called with (null) item\n", __func__);
else {
DATA_SHARESUMMARY(s, item);
-
tv_to_buf(&(s->createdate), createdate_buf, sizeof(createdate_buf));
fprintf(stream, " uid=%"PRId64" wn='%s' wid=%"PRId64" "
"da=%f ds=%f ss=%f c='%s' cd=%s\n",
@@ -1645,14 +1663,45 @@ void auto_age_older(PGconn *conn, int64_t workinfoid, char *poolinstance,
}
}
-// TODO: do this better ... :)
-void dsp_hash(char *hash, char *buf, size_t siz)
+void _dbhash2btchash(char *hash, char *buf, size_t siz, WHERE_FFL_ARGS)
{
+ size_t len;
+ int i, j;
+
+ // code bug
+ if (siz < (SHA256SIZHEX + 1)) {
+ quitfrom(1, file, func, line,
+ "%s() passed buf too small %d (%d)",
+ __func__, (int)siz, SHA256SIZHEX+1);
+ }
+
+ len = strlen(hash);
+ // code bug - check this before calling
+ if (len != SHA256SIZHEX) {
+ quitfrom(1, file, func, line,
+ "%s() invalid hash passed - size %d (%d)",
+ __func__, (int)len, SHA256SIZHEX);
+ }
+
+ for (i = 0; i < SHA256SIZHEX; i++) {
+ j = SHA256SIZHEX - 8 - (i & 0xfff8) + (i % 8);
+ buf[i] = hash[j];
+ }
+ buf[SHA256SIZHEX] = '\0';
+}
+
+void _dsp_hash(char *hash, char *buf, size_t siz, WHERE_FFL_ARGS)
+{
+ char tmp[SHA256SIZHEX+1];
char *ptr;
- ptr = hash + strlen(hash) - (siz - 1) - 8;
- if (ptr < hash)
- ptr = hash;
+ _dbhash2btchash(hash, tmp, sizeof(tmp), file, func, line);
+ ptr = tmp;
+ while (*ptr && *ptr == '0')
+ ptr++;
+ ptr -= 4;
+ if (ptr < tmp)
+ ptr = tmp;
STRNCPYSIZ(buf, ptr, siz);
}
@@ -1666,7 +1715,6 @@ void dsp_blocks(K_ITEM *item, FILE *stream)
fprintf(stream, "%s() called with (null) item\n", __func__);
else {
DATA_BLOCKS(b, item);
-
dsp_hash(b->blockhash, hash_dsp, sizeof(hash_dsp));
tv_to_buf(&(b->createdate), createdate_buf, sizeof(createdate_buf));
tv_to_buf(&(b->expirydate), expirydate_buf, sizeof(expirydate_buf));
diff --git a/src/ckdb_dbio.c b/src/ckdb_dbio.c
index 414c9b4e..97e71b7d 100644
--- a/src/ckdb_dbio.c
+++ b/src/ckdb_dbio.c
@@ -1531,7 +1531,7 @@ unitem:
else {
// Remove from ram, old (unneeded) records
pa.userid = userid;
- pa.expirydate.tv_sec = 0L;
+ pa.expirydate.tv_sec = DATE_S_EOT;
pa.payaddress[0] = '\0';
INIT_PAYMENTADDRESSES(&look);
look.data = (void *)(&pa);
diff --git a/src/ktree.c b/src/ktree.c
index c6e8d3d1..57525c4e 100644
--- a/src/ktree.c
+++ b/src/ktree.c
@@ -133,6 +133,9 @@ void _dsp_ktree(K_LIST *list, K_TREE *root, char *filename, char *msg, KTREE_FFL
time_t now_t;
char stamp[128];
+ if (!list->dsp_func)
+ FAIL("%s", "NULLDSP NULL dsp_func");
+
now_t = time(NULL);
localtime_r(&now_t, &tm);
snprintf(stamp, sizeof(stamp),
|