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
3 changes: 3 additions & 0 deletions doc/example_datum_gateway_config.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@
"log_level_file": 1
},
"datum": {
"pool_host_secondary": "",
"pool_port_secondary": 28915,
"pool_pubkey_secondary": "",
"migration_max_seconds": 86400,
"pool_pass_workers": true,
"pool_pass_full_users": true,
Expand Down
119 changes: 117 additions & 2 deletions src/datum_api.c
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,8 @@ bool datum_api_var_DATUM_CONNECTION_STATUS(struct buf * const buf, const T_DATUM
const char *colour = "lime";
const char *s, *s2 = "";
const char * const bt_err = datum_blocktemplates_error;
T_DATUM_POOL_ENDPOINT_VIEW pool_ep;
datum_protocol_active_pool(&pool_ep);
if (!datum_protocol_abw_health_ok()) {
colour = "red";
s = "CRITICAL: ";
Expand All @@ -144,6 +146,12 @@ bool datum_api_var_DATUM_CONNECTION_STATUS(struct buf * const buf, const T_DATUM
s = "Initialising...";
} else if (datum_protocol_is_active()) {
s = "Connected and Ready";
if (pool_ep.role == DATUM_POOL_ENDPOINT_SECONDARY) {
colour = "yellow";
s2 = " (secondary pool active)";
} else if (pool_ep.role == DATUM_POOL_ENDPOINT_MIGRATION) {
s2 = " (migrated)";
}
} else if (datum_config.datum_pooled_mining_only && datum_config.datum_pool_host[0]) {
colour = "red";
s = "Not Ready";
Expand All @@ -156,11 +164,18 @@ bool datum_api_var_DATUM_CONNECTION_STATUS(struct buf * const buf, const T_DATUM
return buf_printf(buf, "<svg viewBox='0 0 100 100' role='img' style='width:1em;height:1em'><circle cx='50' cy='60' r='35' style='fill:%s' /></svg> %s%s", colour, s, s2);
}
bool datum_api_var_DATUM_POOL_HOST(struct buf * const buf, const T_DATUM_API_DASH_VARS *vardata) {
(void)vardata;
if (datum_config.datum_pool_host[0]) {
return buf_printf(buf, "%s:%u", datum_config.datum_pool_host, (unsigned)datum_config.datum_pool_port);
} else {
return buf_strcat(buf, "N/A");
}
return buf_strcat(buf, "N/A");
}
bool datum_api_var_DATUM_POOL_SECONDARY(struct buf * const buf, const T_DATUM_API_DASH_VARS *vardata) {
(void)vardata;
if (datum_config.datum_pool_host_secondary[0]) {
return buf_printf(buf, "%s:%u", datum_config.datum_pool_host_secondary, (unsigned)datum_config.datum_pool_port_secondary);
}
return buf_strcat(buf, "Not configured");
}
bool datum_api_var_DATUM_POOL_TAG(struct buf * const buf, const T_DATUM_API_DASH_VARS *vardata) {
if (!buf_strcat(buf, "\"")) return false;
Expand All @@ -179,6 +194,9 @@ bool datum_api_var_DATUM_POOL_DIFF(struct buf * const buf, const T_DATUM_API_DAS
return buf_datum_format_difficulty(buf, datum_pdiff_to_diff(datum_config.override_vardiff_min));
}
bool datum_api_var_DATUM_POOL_PUBKEY(struct buf * const buf, const T_DATUM_API_DASH_VARS *vardata) {
T_DATUM_POOL_ENDPOINT_VIEW pool_ep;
datum_protocol_active_pool(&pool_ep);
if (pool_ep.pubkey[0]) return buf_strcat(buf, pool_ep.pubkey);
return buf_strcat(buf, datum_config.datum_pool_pubkey);
}
bool datum_api_var_STRATUM_ACTIVE_THREADS(struct buf * const buf, const T_DATUM_API_DASH_VARS *vardata) {
Expand Down Expand Up @@ -268,6 +286,7 @@ DATUM_API_VarEntry var_entries[] = {
{"DATUM_SHARES_REJECTED", datum_api_var_DATUM_SHARES_REJECTED},
{"DATUM_CONNECTION_STATUS", datum_api_var_DATUM_CONNECTION_STATUS},
{"DATUM_POOL_HOST", datum_api_var_DATUM_POOL_HOST},
{"DATUM_POOL_SECONDARY", datum_api_var_DATUM_POOL_SECONDARY},
{"DATUM_POOL_TAG", datum_api_var_DATUM_POOL_TAG},
{"DATUM_MINER_TAG", datum_api_var_DATUM_MINER_TAG},
{"DATUM_POOL_DIFF", datum_api_var_DATUM_POOL_DIFF},
Expand Down Expand Up @@ -1342,6 +1361,38 @@ bool datum_api_config_set(const char * const key, const char * const val, struct
datum_api_json_modify_new("datum", "pool_pubkey", json_string(val));
// TODO: apply change without restarting
// TODO: switch pools smoother (keep old connection alive for share submissions until those jobs expire)
} else if (0 == strcmp(key, "datum_pool_host_secondary")) {
if (0 == strcmp(val, datum_config.datum_pool_host_secondary)) return true;
if (strlen(val) > 1023) {
json_array_append_new(errors, json_string_nocheck("Secondary Pool Host is too long"));
return false;
}
if (!datum_api_config_require_restart(status)) return false;
strcpy(datum_config.datum_pool_host_secondary, val);
datum_api_json_modify_new("datum", "pool_host_secondary", json_string(val));
} else if (0 == strcmp(key, "datum_pool_port_secondary")) {
const int val_int = datum_atoi_strict(val, strlen(val));
if (val_int == datum_config.datum_pool_port_secondary) return true;
if (val_int > 65535 || val_int < 1) {
json_array_append_new(errors, json_string_nocheck("Secondary Pool Port must be between 1 and 65535"));
return false;
}
if (!datum_api_config_require_restart(status)) return false;
datum_config.datum_pool_port_secondary = val_int;
datum_api_json_modify_new("datum", "pool_port_secondary", json_integer(val_int));
} else if (0 == strcmp(key, "datum_pool_pubkey_secondary")) {
if (0 == strcmp(val, datum_config.datum_pool_pubkey_secondary)) return true;
if (strlen(val) > 1023) {
json_array_append_new(errors, json_string_nocheck("Secondary Pool Pubkey is too long"));
return false;
}
if (val[0] && !datum_pubkey_hex_is_valid(val)) {
json_array_append_new(errors, json_string_nocheck("Secondary Pool Pubkey must be empty or 128 hexadecimal characters"));
return false;
}
if (!datum_api_config_require_restart(status)) return false;
strcpy(datum_config.datum_pool_pubkey_secondary, val);
datum_api_json_modify_new("datum", "pool_pubkey_secondary", json_string(val));
} else if (0 == strcmp(key, "stratum_fingerprint_miners")) {
bool val_bool;
if (!datum_str_to_bool_strict(val, &val_bool)) {
Expand Down Expand Up @@ -1426,6 +1477,38 @@ static void datum_api_test_config_var(const char * const name, const struct datu
buf_destroy(&output);
}

static bool datum_api_secondary_pool_change_ok(json_t * const posted, struct datum_api_config_set_status * const status) {
const char *host = datum_config.datum_pool_host_secondary;
const char *pubkey = datum_config.datum_pool_pubkey_secondary;
int port = datum_config.datum_pool_port_secondary;
bool ok = true;
json_t *j;

j = json_object_get(posted, "datum_pool_host_secondary");
if (j && json_is_string(j)) host = json_string_value(j);
j = json_object_get(posted, "datum_pool_pubkey_secondary");
if (j && json_is_string(j)) pubkey = json_string_value(j);
j = json_object_get(posted, "datum_pool_port_secondary");
if (j && json_is_string(j)) {
port = datum_atoi_strict(json_string_value(j), json_string_length(j));
}
if (!host || !host[0]) return true;
if (!pubkey || !datum_pubkey_hex_is_valid(pubkey)) {
json_array_append_new(status->errors, json_string_nocheck("Secondary pool pubkey must be 128 hexadecimal characters when a secondary host is set"));
ok = false;
}
if (port < 1 || port > 65535) {
json_array_append_new(status->errors, json_string_nocheck("Secondary pool port must be between 1 and 65535"));
ok = false;
}
if (!ok) {
json_object_del(posted, "datum_pool_host_secondary");
json_object_del(posted, "datum_pool_port_secondary");
json_object_del(posted, "datum_pool_pubkey_secondary");
}
return ok;
}

void datum_api_tests(void) {
char * const executable_path = datum_executable_path;
datum_executable_path = NULL;
Expand All @@ -1450,6 +1533,9 @@ void datum_api_tests(void) {
{"datum_pool_host", "other.example"},
{"datum_pool_port", "4321"},
{"datum_pool_pubkey", "new-key"},
{"datum_pool_host_secondary", "backup.example"},
{"datum_pool_port_secondary", "28916"},
{"datum_pool_pubkey_secondary", "abcd"},
{"bitcoind_work_update_seconds", "120"},
};
for (size_t i = 0; i < sizeof(changes) / sizeof(changes[0]); ++i) {
Expand Down Expand Up @@ -1506,6 +1592,34 @@ void datum_api_tests(void) {
datum_api_test_config_var("disabled:*restart_ro", &context, " disabled");
datum_test(datum_api_config_set("datum_pool_port", "4321", &status));
datum_test(datum_config.datum_pool_port == 4321 && status.need_restart);
char secondary_pk[129];
memset(secondary_pk, 'a', 128);
secondary_pk[128] = '\0';
datum_test(!datum_api_config_set("datum_pool_pubkey_secondary", "abcd", &status));
datum_test(!datum_config.datum_pool_pubkey_secondary[0]);
datum_test(datum_api_config_set("datum_pool_host_secondary", "backup.example", &status));
datum_test(!strcmp(datum_config.datum_pool_host_secondary, "backup.example"));
datum_test(datum_api_config_set("datum_pool_port_secondary", "28916", &status));
datum_test(datum_config.datum_pool_port_secondary == 28916);
datum_test(datum_api_config_set("datum_pool_pubkey_secondary", secondary_pk, &status));
datum_test(!strcmp(datum_config.datum_pool_pubkey_secondary, secondary_pk));
datum_test(status.need_restart);
datum_api_test_config_var("datum_pool_host_secondary", &context, "backup.example");
datum_api_test_config_var("datum_pool_port_secondary", &context, "28916");
datum_api_test_config_var("datum_pool_pubkey_secondary", &context, secondary_pk);
json_t *posted = json_object();
json_object_set_new(posted, "datum_pool_host_secondary", json_string("other.example"));
json_object_set_new(posted, "datum_pool_pubkey_secondary", json_string("zz"));
datum_test(!datum_api_secondary_pool_change_ok(posted, &status));
datum_test(json_object_get(posted, "datum_pool_host_secondary") == NULL);
json_decref(posted);
posted = json_object();
json_object_set_new(posted, "datum_pool_host_secondary", json_string("other.example"));
json_object_set_new(posted, "datum_pool_pubkey_secondary", json_string(secondary_pk));
json_object_set_new(posted, "datum_pool_port_secondary", json_string("28915"));
datum_test(datum_api_secondary_pool_change_ok(posted, &status));
datum_test(json_object_get(posted, "datum_pool_host_secondary") != NULL);
json_decref(posted);
status.cannot_restart = EACCES;
datum_test(!datum_api_config_require_restart(&status));
datum_test(status.need_restart);
Expand Down Expand Up @@ -1602,6 +1716,7 @@ enum MHD_Result datum_api_config_post(struct MHD_Connection * const connection,
.errors = errors,
.cannot_restart = datum_reexec_check(),
};
datum_api_secondary_pool_change_ok(j, &status);
json_object_foreach(j, key, j_it) {
datum_api_config_set(key, json_string_value(j_it), &status);
}
Expand Down
19 changes: 19 additions & 0 deletions src/datum_conf.c
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,15 @@ const T_DATUM_CONFIG_ITEM datum_config_options[] = {
.required = false, .ptr = &datum_config.datum_pool_port, .default_int = 28915 },
{ .var_type = DATUM_CONF_STRING, .category = "datum", .name = "pool_pubkey", .description = "Public key of the DATUM server for initiating encrypted connection. Get from secure location, or set to empty to auto-fetch.",
.required = false, .ptr = datum_config.datum_pool_pubkey, .default_string[0] = "dbb11fa0c2b5403e4f798fa6071bb97e6079d219598366032fdf2ae01962b13c5e66e2be7d6b008f0b2603f3e6f6fc64768fa786c8129c46d3e30a5867734b62", .max_string_len = sizeof(datum_config.datum_pool_pubkey) },
{ .var_type = DATUM_CONF_STRING, .category = "datum", .name = "pool_host_secondary", .description = "Backup DATUM server host/ip. Used when the primary pool cannot be reached. The gateway stays with whichever pool is connected, and tries the other after that connection fails. Empty disables failover.",
.example_default = true,
.required = false, .ptr = datum_config.datum_pool_host_secondary, .default_string[0] = "", .max_string_len = sizeof(datum_config.datum_pool_host_secondary) },
{ .var_type = DATUM_CONF_INT, .category = "datum", .name = "pool_port_secondary", .description = "Backup DATUM server port",
.example_default = true,
.required = false, .ptr = &datum_config.datum_pool_port_secondary, .default_int = 28915 },
{ .var_type = DATUM_CONF_STRING, .category = "datum", .name = "pool_pubkey_secondary", .description = "Public key of the backup DATUM server. Required when pool_host_secondary is set.",
.example_default = true,
.required = false, .ptr = datum_config.datum_pool_pubkey_secondary, .default_string[0] = "", .max_string_len = sizeof(datum_config.datum_pool_pubkey_secondary) },
{ .var_type = DATUM_CONF_INT, .category = "datum", .name = "migration_max_seconds", .description = "Maximum time to remain away from the configured DATUM server after a server-requested migration (0 disables migration)",
.example_default = true,
.required = false, .ptr = &datum_config.datum_pool_migration_max_seconds, .default_int = 86400 },
Expand Down Expand Up @@ -743,6 +752,16 @@ int datum_read_config(const char *conffile) {
DLOG_FATAL("Pooled mining only is set to true, but pool host is not specified.");
return 0;
}
if (datum_config.datum_pool_host_secondary[0] != '\0') {
if (datum_config.datum_pool_port_secondary < 1 || datum_config.datum_pool_port_secondary > 65535) {
DLOG_FATAL("datum.pool_port_secondary must be between 1 and 65535");
return 0;
}
if (!datum_pubkey_hex_is_valid(datum_config.datum_pool_pubkey_secondary)) {
DLOG_FATAL("datum.pool_pubkey_secondary must be 128 hexadecimal characters when datum.pool_host_secondary is set");
return 0;
}
}

// Save some multiplication later
datum_config.datum_protocol_global_timeout_ms = datum_config.datum_protocol_global_timeout * 1000;
Expand Down
5 changes: 4 additions & 1 deletion src/datum_conf.h
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,10 @@ typedef struct {

char datum_pool_host[1024];
int datum_pool_port;
char datum_pool_pubkey[1024];
char datum_pool_host_secondary[1024];
int datum_pool_port_secondary;
char datum_pool_pubkey_secondary[1024];
char datum_pool_migration_host[1024];
int datum_pool_migration_port;
char datum_pool_migration_pubkey[129];
Expand All @@ -170,7 +174,6 @@ typedef struct {
bool datum_pool_pass_workers;
bool datum_pool_pass_full_users;
bool datum_pooled_mining_only;
char datum_pool_pubkey[1024];
int datum_protocol_global_timeout;
uint64_t datum_protocol_global_timeout_ms;

Expand Down
Loading
Loading