Con Kolivas
10 years ago
17 changed files with 645 additions and 64 deletions
@ -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; |
||||
} |
||||
} |
Loading…
Reference in new issue