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
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ add_executable(datum_gateway
src/datum_stratum_dupes.c
src/datum_stratum_tests.c
src/datum_submitblock.c
src/datum_submitblock_tests.c
src/datum_utils.c
src/datum_utils_tests.c
src/thirdparty_base58.c
Expand Down
2 changes: 2 additions & 0 deletions src/datum_gateway.c
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
#include "datum_api.h"
#include "datum_coinbaser.h"
#include "datum_protocol.h"
#include "datum_submitblock.h"

const char *datum_gateway_config_filename = NULL;

Expand Down Expand Up @@ -106,6 +107,7 @@ static error_t parse_opt(int key, char *arg, struct argp_state *state) {
datum_utils_tests();
datum_conf_tests();
datum_stratum_tests();
datum_submitblock_tests();
exit(datum_test_failed);
default:
return ARGP_ERR_UNKNOWN;
Expand Down
17 changes: 2 additions & 15 deletions src/datum_stratum.c
Original file line number Diff line number Diff line change
Expand Up @@ -2238,21 +2238,8 @@ int assembleBlockAndSubmit(uint8_t *block_header, uint8_t *coinbase_txn, size_t
// make the call!
r = bitcoind_json_rpc_call(tcurl, &datum_config, submitblock_req);
curl_easy_cleanup(tcurl);
if (!r) {
// oddly, this means success here.
DLOG_INFO("Block %s submitted to upstream node successfully!",block_hash_hex);
ret = 1;
} else {
s = json_dumps(r, JSON_ENCODE_ANY);
if (!s) {
DLOG_WARN("Upstream node rejected our block! (unknown)");
} else {
DLOG_WARN("Upstream node rejected our block! (%s)",s);
free(s);
}
json_decref(r);
ret = 0;
}
ret = datum_submitblock_log_reply(r, block_hash_hex) ? 1 : 0;
if (r) json_decref(r);

// cleanup
if (free_submitblock_req) {
Expand Down
47 changes: 33 additions & 14 deletions src/datum_submitblock.c
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
#include "datum_utils.h"
#include "datum_conf.h"
#include "datum_jsonrpc.h"
#include "datum_submitblock.h"

pthread_mutex_t submitblock_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t submitblock_cond = PTHREAD_COND_INITIALIZER;
Expand All @@ -61,28 +62,46 @@ void preciousblock(CURL *curl, char *blockhash) {
return;
}

datum_submitblock_status datum_submitblock_reply_status(const json_t *reply) {
if (!reply) return DATUM_SUBMITBLOCK_ACCEPTED;
const json_t * const result = json_object_get(reply, "result");
if (!result || json_is_null(result)) return DATUM_SUBMITBLOCK_ACCEPTED;
if (json_is_string(result) && !strcmp(json_string_value(result), "duplicate")) return DATUM_SUBMITBLOCK_DUPLICATE;
return DATUM_SUBMITBLOCK_REJECTED;
}

// Log what the node said about our block. Returns true when the block is in
// the node's chain, which includes "duplicate": the block is handed in twice,
// once inline from the share that found it and once from the submit thread,
// and the second copy is not a rejection.
bool datum_submitblock_log_reply(const json_t *reply, const char *block_hash_hex) {
switch (datum_submitblock_reply_status(reply)) {
case DATUM_SUBMITBLOCK_ACCEPTED:
DLOG_INFO("Block %s submitted to upstream node successfully!", block_hash_hex);
return true;
case DATUM_SUBMITBLOCK_DUPLICATE:
DLOG_INFO("Block %s was already accepted by the upstream node (duplicate submission)", block_hash_hex);
return true;
case DATUM_SUBMITBLOCK_REJECTED:
default: {
char * const s = json_dumps(reply, JSON_ENCODE_ANY);
DLOG_WARN("Upstream node rejected our block! (%s)", s ? s : "unknown");
free(s);
return false;
}
}
}

void datum_submitblock_doit(CURL *tcurl, char *url, const char *submitblock_req, const char *block_hash_hex) {
json_t *r;
char *s = NULL;
// TODO: Move these types of things to the conf file
if (!url) {
r = bitcoind_json_rpc_call(tcurl, &datum_config, submitblock_req);
} else {
r = json_rpc_call(tcurl, url, NULL, submitblock_req);
}
if (!r) {
// oddly, this means success here.
DLOG_INFO("Block %s submitted to upstream node successfully!",block_hash_hex);
} else {
s = json_dumps(r, JSON_ENCODE_ANY);
if (!s) {
DLOG_WARN("Upstream node rejected our block! (unknown)");
} else {
DLOG_WARN("Upstream node rejected our block! (%s)",s);
free(s);
}
json_decref(r);
}
datum_submitblock_log_reply(r, block_hash_hex);
if (r) json_decref(r);

// precious block!
preciousblock(tcurl, submitblock_hash);
Expand Down
15 changes: 15 additions & 0 deletions src/datum_submitblock.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,21 @@
#ifndef _DATUM_SUBMITBLOCK_H_
#define _DATUM_SUBMITBLOCK_H_

#include <stdbool.h>
#include <jansson.h>

// What a submitblock reply means. The RPC helper returns NULL for the null
// result the node sends on acceptance, so NULL is "accepted" here.
typedef enum {
DATUM_SUBMITBLOCK_ACCEPTED = 0, // null result: the node took the block
DATUM_SUBMITBLOCK_DUPLICATE, // "duplicate": the node already had it, and it is valid
DATUM_SUBMITBLOCK_REJECTED, // anything else, including "duplicate-invalid"
} datum_submitblock_status;

datum_submitblock_status datum_submitblock_reply_status(const json_t *reply);
bool datum_submitblock_log_reply(const json_t *reply, const char *block_hash_hex);
void datum_submitblock_tests(void);

void datum_submitblock_init(void);
void datum_submitblock_trigger(const char *ptr, const char *hash);
void datum_submitblock_waitfree(void);
Expand Down
42 changes: 42 additions & 0 deletions src/datum_submitblock_tests.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
*
* DATUM Gateway
* Decentralized Alternative Templates for Universal Mining
*
* This file is part of the DATUM Gateway and is distributed under the
* same MIT license as the rest of the project. See LICENSE.
*
*/

#include <jansson.h>

#include "datum_submitblock.h"
#include "datum_utils.h"

static datum_submitblock_status status_of(const char * const json_text) {
json_t * const reply = json_loads(json_text, 0, NULL);
datum_test(reply != NULL);
const datum_submitblock_status st = datum_submitblock_reply_status(reply);
json_decref(reply);
return st;
}

void datum_submitblock_tests(void) {
// The RPC helper hands back NULL for the null result the node sends on acceptance
datum_test(datum_submitblock_reply_status(NULL) == DATUM_SUBMITBLOCK_ACCEPTED);
datum_test(status_of("{\"result\":null,\"error\":null,\"id\":1}") == DATUM_SUBMITBLOCK_ACCEPTED);

// Already have it and it is valid: the block is in the chain
datum_test(status_of("{\"result\":\"duplicate\",\"error\":null,\"id\":1}") == DATUM_SUBMITBLOCK_DUPLICATE);

// Every other string is a rejection, including the other duplicate-* forms
datum_test(status_of("{\"result\":\"duplicate-invalid\",\"error\":null,\"id\":1}") == DATUM_SUBMITBLOCK_REJECTED);
datum_test(status_of("{\"result\":\"duplicate-inconclusive\",\"error\":null,\"id\":1}") == DATUM_SUBMITBLOCK_REJECTED);
datum_test(status_of("{\"result\":\"inconclusive\",\"error\":null,\"id\":1}") == DATUM_SUBMITBLOCK_REJECTED);
datum_test(status_of("{\"result\":\"bad-txnmrklroot\",\"error\":null,\"id\":1}") == DATUM_SUBMITBLOCK_REJECTED);
datum_test(status_of("{\"result\":\"prev-blk-not-found\",\"error\":null,\"id\":1}") == DATUM_SUBMITBLOCK_REJECTED);

// A non-string result is not something to call success
datum_test(status_of("{\"result\":true,\"error\":null,\"id\":1}") == DATUM_SUBMITBLOCK_REJECTED);
datum_test(status_of("{\"result\":{\"x\":1},\"error\":null,\"id\":1}") == DATUM_SUBMITBLOCK_REJECTED);
}
Loading