Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/datum_conf.c
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ const T_DATUM_CONFIG_ITEM datum_config_options[] = {
{ .var_type = DATUM_CONF_STRING, .category = "bitcoind", .name = "rpcurl", .description = "RPC URL for communication with local bitcoind. (GBT Template Source)",
.example = "\"http://localhost:8332\"",
.required = true, .ptr = datum_config.bitcoind_rpcurl, .max_string_len = sizeof(datum_config.bitcoind_rpcurl) },
{ .var_type = DATUM_CONF_INT, .category = "bitcoind", .name = "parent_fetch_timeout_seconds", .description = "Maximum seconds to wait for a raw parent block RPC response. (1-300)",
.required = false, .ptr = &datum_config.bitcoind_parent_fetch_timeout_seconds, .default_int = 15 },
{ .var_type = DATUM_CONF_INT, .category = "bitcoind", .name = "work_update_seconds", .description = "How many seconds between normal work updates? (5-120, 40 suggested)",
.required = false, .ptr = &datum_config.bitcoind_work_update_seconds, .default_int = 40 },
{ .var_type = DATUM_CONF_BOOL, .category = "bitcoind", .name = "notify_fallback", .description = "Fall back to less efficient methods for new block notifications. Can disable if you use blocknotify.",
Expand Down Expand Up @@ -641,6 +643,10 @@ int datum_read_config(const char *conffile) {
if (datum_config.bitcoind_work_update_seconds > 120) {
datum_config.bitcoind_work_update_seconds = 120;
}
if (datum_config.bitcoind_parent_fetch_timeout_seconds < 1 || datum_config.bitcoind_parent_fetch_timeout_seconds > 300) {
DLOG_FATAL("Configuration option bitcoind.parent_fetch_timeout_seconds must be between 1 and 300");
return -1;
}
if (datum_config.datum_pool_migration_max_seconds < 0) {
DLOG_FATAL("Configuration option datum.migration_max_seconds cannot be negative");
return -1;
Expand Down
1 change: 1 addition & 0 deletions src/datum_conf.h
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ typedef struct {
char bitcoind_rpcuser[128];
char bitcoind_rpcpassword[128];
char bitcoind_rpcurl[256];
int bitcoind_parent_fetch_timeout_seconds;
int bitcoind_work_update_seconds;
bool bitcoind_notify_fallback;

Expand Down
18 changes: 11 additions & 7 deletions src/datum_jsonrpc.c
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ char *basic_http_call(CURL *curl, const char *url) {
return NULL;
}

json_t *json_rpc_call_full(CURL *curl, const char *url, const char *userpass, const char *rpc_req, const char *extra_header, long * const http_resp_code_out) {
static json_t *json_rpc_call_full(CURL *curl, const char *url, const char *userpass, const char *rpc_req, const char *extra_header, long * const http_resp_code_out, const long timeout_seconds) {
json_t *val, *err_val, *res_val;
CURLcode rc;
struct data_buffer all_data = { };
Expand All @@ -158,8 +158,8 @@ json_t *json_rpc_call_full(CURL *curl, const char *url, const char *userpass, co
curl_easy_setopt(curl, CURLOPT_READFUNCTION, upload_data_cb);
curl_easy_setopt(curl, CURLOPT_READDATA, &upload_data);
curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, curl_err_str);
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 5L); // quick timeout!
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 5L); // quick timeout!
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, timeout_seconds);
curl_easy_setopt(curl, CURLOPT_TIMEOUT, timeout_seconds);
curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L);

if (userpass) {
Expand Down Expand Up @@ -240,7 +240,7 @@ json_t *json_rpc_call_full(CURL *curl, const char *url, const char *userpass, co
}

json_t *json_rpc_call(CURL *curl, const char *url, const char *userpass, const char *rpc_req) {
return json_rpc_call_full(curl, url, userpass, rpc_req, NULL, NULL);
return json_rpc_call_full(curl, url, userpass, rpc_req, NULL, NULL, 5L);
}

bool update_rpc_cookie(global_config_t * const cfg) {
Expand All @@ -265,14 +265,18 @@ void update_rpc_auth(global_config_t * const cfg) {
}
}

json_t *bitcoind_json_rpc_call(CURL * const curl, global_config_t * const cfg, const char * const rpc_req) {
json_t *bitcoind_json_rpc_call_with_timeout(CURL * const curl, global_config_t * const cfg, const char * const rpc_req, const long timeout_seconds) {
long http_resp_code = -1;
json_t *j = json_rpc_call_full(curl, cfg->bitcoind_rpcurl, cfg->bitcoind_rpcuserpass, rpc_req, NULL, &http_resp_code);
json_t *j = json_rpc_call_full(curl, cfg->bitcoind_rpcurl, cfg->bitcoind_rpcuserpass, rpc_req, NULL, &http_resp_code, timeout_seconds);
if (j) return j;
if (cfg->bitcoind_rpcuser[0]) return NULL;
if (http_resp_code != 401) return NULL;

// Authentication failure using cookie; reload cookie file and try again
if (!update_rpc_cookie(cfg)) return NULL;
return json_rpc_call(curl, cfg->bitcoind_rpcurl, cfg->bitcoind_rpcuserpass, rpc_req);
return json_rpc_call_full(curl, cfg->bitcoind_rpcurl, cfg->bitcoind_rpcuserpass, rpc_req, NULL, NULL, timeout_seconds);

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this behaviour is acceptable. The parent fetch is just part of a longer process, and this timeout is for the fetch itself.

}

json_t *bitcoind_json_rpc_call(CURL * const curl, global_config_t * const cfg, const char * const rpc_req) {
return bitcoind_json_rpc_call_with_timeout(curl, cfg, rpc_req, 5L);
}
1 change: 1 addition & 0 deletions src/datum_jsonrpc.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,5 +68,6 @@ char *basic_http_call(CURL *curl, const char *url);
bool update_rpc_cookie(global_config_t *cfg);
void update_rpc_auth(global_config_t *cfg);
json_t *bitcoind_json_rpc_call(CURL *curl, global_config_t *cfg, const char *rpc_req);
json_t *bitcoind_json_rpc_call_with_timeout(CURL *curl, global_config_t *cfg, const char *rpc_req, long timeout_seconds);

#endif
2 changes: 1 addition & 1 deletion src/datum_parent_fetch.c
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ static uint8_t parent_fetch_get(
uint8_t ** const block_out, size_t * const block_size_out) {
char *rpc = parent_fetch_rpc_request(request->parent_hash);
if (!rpc) return DATUM_PARENT_FETCH_STATUS_RPC_FAILED;
json_t *response = bitcoind_json_rpc_call(curl, &datum_config, rpc);
json_t *response = bitcoind_json_rpc_call_with_timeout(curl, &datum_config, rpc, datum_config.bitcoind_parent_fetch_timeout_seconds);
free(rpc);
if (!response) return DATUM_PARENT_FETCH_STATUS_UNAVAILABLE;
json_t *result = json_object_get(response, "result");
Expand Down
108 changes: 66 additions & 42 deletions src/datum_parent_fetch_tests.c
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@

typedef struct {
int listen_fd;
bool saw_getblock;
unsigned getblock_requests;
char expected_hash[65];
} T_DATUM_PARENT_FETCH_TEST_SERVER;

Expand All @@ -80,40 +80,59 @@ static bool parent_test_send_all(int fd, const char *data, size_t len) {

static void *parent_test_server(void *arg) {
T_DATUM_PARENT_FETCH_TEST_SERVER *server = arg;
const int client = accept(server->listen_fd, NULL, NULL);
if (client < 0) return NULL;
char request[4096] = {0};
size_t used = 0;
while (used + 1 < sizeof(request)) {
const ssize_t n = recv(client, request + used,
sizeof(request) - used - 1, 0);
if (n <= 0) break;
used += (size_t)n;
request[used] = 0;
char *body = strstr(request, "\r\n\r\n");
char *length = strstr(request, "Content-Length:");
if (!body || !length) continue;
unsigned long body_size = 0;
if (sscanf(length, "Content-Length: %lu", &body_size) == 1 &&
used >= (size_t)(body + 4 - request) + body_size) break;
for (unsigned request_index = 0; request_index < 2; ++request_index) {
const int client = accept(server->listen_fd, NULL, NULL);
if (client < 0) return NULL;
Comment on lines +83 to +85
char request[4096] = {0};
size_t used = 0;
while (used + 1 < sizeof(request)) {
const ssize_t n = recv(client, request + used,
sizeof(request) - used - 1, 0);
if (n <= 0) break;
used += (size_t)n;
request[used] = 0;
char *body = strstr(request, "\r\n\r\n");
char *length = strstr(request, "Content-Length:");
if (!body || !length) continue;
unsigned long body_size = 0;
if (sscanf(length, "Content-Length: %lu", &body_size) == 1 &&
used >= (size_t)(body + 4 - request) + body_size) break;
}
if (strstr(request, "\"method\":\"getblock\"") &&
strstr(request, server->expected_hash) && strstr(request, ",0]")) {
++server->getblock_requests;
}
if (request_index == 1) sleep(2);
const char body[] =
"{\"result\":\"aabbcc\",\"error\":null,\"id\":\"datum-parent-fetch\"}";
char response[512];
const int response_size = snprintf(
response, sizeof(response),
"HTTP/1.1 200 OK\r\nContent-Type: application/json\r\n"
"Content-Length: %zu\r\nConnection: close\r\n\r\n%s",
strlen(body), body);
if (response_size > 0 && (size_t)response_size < sizeof(response)) {
parent_test_send_all(client, response, (size_t)response_size);
}
close(client);
}
server->saw_getblock = strstr(request, "\"method\":\"getblock\"") &&
strstr(request, server->expected_hash) && strstr(request, ",0]");
const char body[] =
"{\"result\":\"aabbcc\",\"error\":null,\"id\":\"datum-parent-fetch\"}";
char response[512];
const int response_size = snprintf(
response, sizeof(response),
"HTTP/1.1 200 OK\r\nContent-Type: application/json\r\n"
"Content-Length: %zu\r\nConnection: close\r\n\r\n%s",
strlen(body), body);
if (response_size > 0 && (size_t)response_size < sizeof(response)) {
parent_test_send_all(client, response, (size_t)response_size);
}
close(client);
return NULL;
}

static bool parent_test_wait_for_reply(unsigned timeout_seconds) {
pthread_mutex_lock(&parent_test_lock);
struct timespec deadline;
clock_gettime(CLOCK_REALTIME, &deadline);
deadline.tv_sec += timeout_seconds;
while (!parent_test_replied) {
if (pthread_cond_timedwait(
&parent_test_cond, &parent_test_lock, &deadline) == ETIMEDOUT) break;
}
const bool replied = parent_test_replied;
pthread_mutex_unlock(&parent_test_lock);
return replied;
}

static void parent_test_reply(
uint8_t job_id, uint64_t session_generation, uint8_t status,
const uint8_t parent_hash[32],
Expand Down Expand Up @@ -166,25 +185,30 @@ void datum_parent_fetch_tests(void) {
"http://127.0.0.1:%u", ntohs(bound.sin_port));
strcpy(datum_config.bitcoind_rpcuser, "test");
strcpy(datum_config.bitcoind_rpcuserpass, "test:test");
datum_config.bitcoind_parent_fetch_timeout_seconds = 15;
datum_test(!datum_parent_fetch_init(parent_test_reply));
datum_test(datum_parent_fetch_enqueue(2, UINT64_C(0x1122334455667788), parent_hash) == DATUM_PARENT_FETCH_STATUS_QUEUED);

pthread_mutex_lock(&parent_test_lock);
struct timespec deadline;
clock_gettime(CLOCK_REALTIME, &deadline);
deadline.tv_sec += 5;
while (!parent_test_replied) {
if (pthread_cond_timedwait(
&parent_test_cond, &parent_test_lock, &deadline) == ETIMEDOUT) break;
}
pthread_mutex_unlock(&parent_test_lock);
datum_test(parent_test_replied);
datum_test(parent_test_wait_for_reply(5));
datum_test(parent_test_status == DATUM_PARENT_FETCH_STATUS_SUCCESS);
datum_test(parent_test_session_generation == UINT64_C(0x1122334455667788));
datum_test(parent_test_block_size == 3);
datum_test(!memcmp(parent_test_block, "\xaa\xbb\xcc", 3));

pthread_mutex_lock(&parent_test_lock);
parent_test_replied = false;
parent_test_status = 0;
parent_test_session_generation = 0;
parent_test_block_size = 0;
pthread_mutex_unlock(&parent_test_lock);
datum_config.bitcoind_parent_fetch_timeout_seconds = 1;
datum_test(datum_parent_fetch_enqueue(3, UINT64_C(0x8877665544332211), parent_hash) == DATUM_PARENT_FETCH_STATUS_QUEUED);
datum_test(parent_test_wait_for_reply(3));
datum_test(parent_test_status == DATUM_PARENT_FETCH_STATUS_UNAVAILABLE);
datum_test(parent_test_session_generation == UINT64_C(0x8877665544332211));
datum_test(parent_test_block_size == 0);
pthread_join(server_thread, NULL);
datum_test(server.saw_getblock);
datum_test(server.getblock_requests == 2);
close(server.listen_fd);
datum_config = saved_config;
}
Loading