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
26 changes: 20 additions & 6 deletions src/datum_api.c
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,9 @@ static void html_leading_zeros(char * const buffer, const size_t buffer_size, co
}

static void datum_api_format_share_counts(char *buffer, size_t buffer_size, uint64_t count, uint64_t diff) {
snprintf(buffer, buffer_size, "%llu (%llu diff)", (unsigned long long)count, (unsigned long long)diff);
char diffstr[DATUM_FORMAT_DIFFICULTY_OUT_SZ];
datum_format_difficulty(diffstr, sizeof(diffstr), datum_pdiff_to_diff(diff));
snprintf(buffer, buffer_size, "%llu (%s diff)", (unsigned long long)count, diffstr);
}

void datum_api_var_STRATUM_SHARES_ACCEPTED(char *buffer, size_t buffer_size, const T_DATUM_API_DASH_VARS *vardata) {
Expand Down Expand Up @@ -171,7 +173,7 @@ void datum_api_var_DATUM_MINER_TAG(char *buffer, size_t buffer_size, const T_DAT
buffer[i+2] = 0;
}
void datum_api_var_DATUM_POOL_DIFF(char *buffer, size_t buffer_size, const T_DATUM_API_DASH_VARS *vardata) {
snprintf(buffer, buffer_size, "%llu", (unsigned long long)datum_config.override_vardiff_min);
datum_format_difficulty(buffer, buffer_size, datum_pdiff_to_diff(datum_config.override_vardiff_min));
}
void datum_api_var_DATUM_POOL_PUBKEY(char *buffer, size_t buffer_size, const T_DATUM_API_DASH_VARS *vardata) {
snprintf(buffer, buffer_size, "%s", datum_config.datum_pool_pubkey);
Expand Down Expand Up @@ -228,7 +230,8 @@ void datum_api_var_STRATUM_JOB_WITNESS(char *buffer, size_t buffer_size, const T
snprintf(buffer, buffer_size, "%s", vardata->sjob->block_template->default_witness_commitment);
}
void datum_api_var_STRATUM_JOB_DIFF(char *buffer, size_t buffer_size, const T_DATUM_API_DASH_VARS *vardata) {
snprintf(buffer, buffer_size, "%.3Lf", calc_network_difficulty(vardata->sjob->nbits));
datum_format_difficulty(buffer, buffer_size,
calc_network_difficulty_blake2b(vardata->sjob->nbits_uint));
}
void datum_api_var_STRATUM_JOB_VERSION(char *buffer, size_t buffer_size, const T_DATUM_API_DASH_VARS *vardata) {
snprintf(buffer, buffer_size, "%s (%u)", vardata->sjob->version, (unsigned)vardata->sjob->version_uint);
Expand Down Expand Up @@ -902,6 +905,7 @@ int datum_api_client_dashboard(struct MHD_Connection *connection) {
double hr;
unsigned char astat;
double thr = 0.0;
char difficulty[DATUM_FORMAT_DIFFICULTY_OUT_SZ];

const int max_threads = global_stratum_app ? global_stratum_app->max_threads : 0;

Expand Down Expand Up @@ -946,14 +950,17 @@ int datum_api_client_dashboard(struct MHD_Connection *connection) {
sz += snprintf(&output[sz], max_sz-1-sz, "<TD>N/A</TD>");
}

sz += snprintf(&output[sz], max_sz-1-sz, "<TD>%"PRIu64"</TD>", m->current_diff);
sz += snprintf(&output[sz], max_sz-1-sz, "<TD>%"PRIu64" (%"PRIu64")</TD>", m->share_diff_accepted, m->share_count_accepted);
datum_format_difficulty(difficulty, sizeof(difficulty), datum_pdiff_to_diff(m->current_diff));
sz += snprintf(&output[sz], max_sz-1-sz, "<TD>%s</TD>", difficulty);
datum_format_difficulty(difficulty, sizeof(difficulty), datum_pdiff_to_diff(m->share_diff_accepted));
sz += snprintf(&output[sz], max_sz-1-sz, "<TD>%s (%"PRIu64")</TD>", difficulty, m->share_count_accepted);

hr = 0.0;
if (m->share_diff_accepted > 0) {
hr = ((double)m->share_diff_rejected / (double)(m->share_diff_accepted + m->share_diff_rejected))*100.0;
}
sz += snprintf(&output[sz], max_sz-1-sz, "<TD>%"PRIu64" (%"PRIu64") %.2f%%</TD>", m->share_diff_rejected, m->share_count_rejected, hr);
datum_format_difficulty(difficulty, sizeof(difficulty), datum_pdiff_to_diff(m->share_diff_rejected));
sz += snprintf(&output[sz], max_sz-1-sz, "<TD>%s (%"PRIu64") %.2f%%</TD>", difficulty, m->share_count_rejected, hr);

astat = m->stats.active_index?0:1; // inverted
hr = 0.0;
Expand Down Expand Up @@ -1082,6 +1089,13 @@ size_t datum_api_fill_config_var(const char *var_start, const size_t var_name_le
DLOG_ERROR("%s: %s not implemented", __func__, "DATUM_CONF_USERNAME_MODS");
break;
}
case DATUM_CONF_DIFFICULTY: {
val = *((int *)item->ptr);
if (!colon_pos) {
return datum_format_difficulty(replacement, replacement_max_len, datum_pdiff_to_diff(val));
}
break;
}
}
} else {
DLOG_ERROR("%s: '%.*s' not implemented", __func__, (int)(var_end - var_start_2), var_start_2);
Expand Down
157 changes: 151 additions & 6 deletions src/datum_conf.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@
// Custom configurator and help output generator

#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <math.h>
#include <stdint.h>
Expand All @@ -58,6 +60,7 @@ const char *datum_conf_var_type_text[] = {
"string",
"string_array",
"{\"modname\":{\"address\":proportion,...},...}",
"difficulty",
};

const T_DATUM_CONFIG_ITEM datum_config_options[] = {
Expand Down Expand Up @@ -93,7 +96,7 @@ const T_DATUM_CONFIG_ITEM datum_config_options[] = {
.required = false, .ptr = &datum_config.stratum_v1_max_clients, .default_int = 1024 },
{ .var_type = DATUM_CONF_INT, .category = "stratum", .name = "trust_proxy", .description = "Enable support for the PROXY protocol, trusting up to the specified number of levels deep of proxies (-1 to disable entirely)",
.required = false, .ptr = &datum_config.stratum_v1_trust_proxy, .default_int = -1 },
{ .var_type = DATUM_CONF_INT, .category = "stratum", .name = "vardiff_min", .description = "Work difficulty floor",
{ .var_type = DATUM_CONF_DIFFICULTY, .category = "stratum", .name = "vardiff_min", .description = "Work difficulty floor (integer or quoted decimal with SI suffix)",
.required = false, .ptr = &datum_config.stratum_v1_vardiff_min, .default_int = 1024 },
{ .var_type = DATUM_CONF_INT, .category = "stratum", .name = "vardiff_target_shares_min",.description = "Adjust work difficulty to target this many shares per minute",
.required = false, .ptr = &datum_config.stratum_v1_vardiff_target_shares_min, .default_int = 8 },
Expand Down Expand Up @@ -261,6 +264,11 @@ void datum_config_set_default(const T_DATUM_CONFIG_ITEM *c) {
*umods_p = NULL;
break;
}

case DATUM_CONF_DIFFICULTY: {
*((int *)c->ptr) = c->default_int;
break;
}
}
}

Expand Down Expand Up @@ -363,6 +371,110 @@ struct datum_username_mod *datum_username_mods_find(struct datum_username_mod *u
return NULL;
}

static int datum_config_compare_difficulty(const char *value, const char *end, int exponent, uint64_t boundary) {
// Removing the decimal point gives an integer coefficient in units of 10^exponent.
// Compare it with the hash boundary rounded to those units (the input's half-unit tolerance).
if (exponent > 0) {
for (int i = 1; i < exponent && boundary; ++i) boundary /= 10;
boundary = boundary / 10 + (boundary % 10 >= 5);
if (!boundary) return 1; // The validated input is positive.
exponent = 0;
}
int comparison = 0;
while (end > value) {
const char digit = *--end;
if (digit == '.') continue;
const int value_digit = digit - '0';
int boundary_digit;
if (exponent < 0) {
boundary_digit = 0;
++exponent; // Consume a virtual trailing zero without multiplying the boundary.
} else {
boundary_digit = boundary % 10;
boundary /= 10;
}
if (value_digit != boundary_digit) {
comparison = value_digit > boundary_digit ? 1 : -1;
}
}
return boundary ? -1 : comparison;
}

int datum_config_parse_difficulty(int * const out, json_t * const item) {
switch (json_typeof(item)) {
case JSON_INTEGER: {
const json_int_t value = json_integer_value(item);
if (value < 1) return -1;
if (value <= INT_MAX) {
*out = roundDownToPowerOfTwo_64(value);
return (*out == value) ? 3 : -1;
}
const uint64_t hashes = value;
const uint64_t minimum = (hashes >> 32) + !!(hashes & UINT32_MAX);
const uint64_t max = roundDownToPowerOfTwo_64(INT_MAX);
if (minimum > max) return -1;
*out = minimum <= 1 ? 1 : roundDownToPowerOfTwo_64(minimum - 1) << 1;
return (((uint64_t)*out << 32) > hashes) ? 2 : 1;
}
case JSON_REAL:
case JSON_STRING:
break;
default:
return -1;
}

const char * const value_str = json_string_value(item);
char *end = NULL;
int exponent = 0;
bitcoin_difficulty_typ requested;
if (value_str) {
errno = 0;
requested = strtold(value_str, &end);
if (errno || end == value_str) return -1;

int decimal_places = 0;
for (const char *p = value_str; p < end; ++p) {
if (*p == '.') {
if (end - p - 1 > INT_MAX) return -1;
decimal_places = (end - p) - 1;
} else if (!isdigit((unsigned char)*p)) {
// Don't allow explicit exponents, whitespace, sign, etc
return -1;
}
}

int decimal_exponent = 0;
if (end[0]) {
static const char suffixes[] = "KMGTPEZYRQ";
const char * const suffix = strchr(suffixes, toupper((unsigned char)end[0]));
if (end[1] || !suffix) return -1;
decimal_exponent = ((suffix - suffixes) + 1) * 3;
requested *= powl(10.0L, decimal_exponent);
}
exponent = decimal_exponent - decimal_places;
} else {
requested = json_real_value(item);
}

if (!isfinite(requested) || requested <= 0) return -1;
const uint64_t max = roundDownToPowerOfTwo_64(INT_MAX);
// Estimate the next power of two; only the original digits decide which boundary is admissible.
const uint64_t minimum = ceill(fminl(requested / datum_pdiff_to_diff(1), max));
*out = minimum <= 1 ? 1 : roundDownToPowerOfTwo_64(minimum - 1) << 1;
if (!value_str) {
if (requested > datum_pdiff_to_diff(max)) return -1;
return datum_pdiff_to_diff(*out) > requested ? 2 : 1;
}
// A positive decimal's half-unit tolerance cannot extend below half its value.
if (*out > 1 && datum_config_compare_difficulty(value_str, end, exponent, (uint64_t)(*out / 2) << 32) <= 0) {
*out /= 2;
} else if (datum_config_compare_difficulty(value_str, end, exponent, (uint64_t)*out << 32) > 0) {
if ((uint64_t)*out >= max) return -1;
*out *= 2;
}
return datum_config_compare_difficulty(value_str, end, exponent, (uint64_t)*out << 32) < 0 ? 2 : 1;
}

int datum_config_parse_value(const T_DATUM_CONFIG_ITEM *c, json_t *item) {
switch(c->var_type) {
case DATUM_CONF_INT: {
Expand Down Expand Up @@ -426,6 +538,25 @@ int datum_config_parse_value(const T_DATUM_CONFIG_ITEM *c, json_t *item) {
case DATUM_CONF_USERNAME_MODS: {
return datum_config_parse_username_mods(c->ptr, item, true);
}

case DATUM_CONF_DIFFICULTY: {
int * const out = c->ptr;
const int result = datum_config_parse_difficulty(out, item);
if (result < 0) return result;
if (result > 1) {
char diffstr[DATUM_FORMAT_DIFFICULTY_OUT_SZ];
datum_format_difficulty(diffstr, sizeof(diffstr), datum_pdiff_to_diff(*out));
switch (result) {
case 2:
DLOG_WARN("%s.%s rounded up to %s", c->category, c->name, diffstr);
break;
case 3:
DLOG_WARN("%s.%s uses legacy integer syntax; use \"%s\" instead", c->category, c->name, diffstr);
break;
}
}
return 1;
}
}

return -1;
Expand Down Expand Up @@ -589,17 +720,17 @@ int datum_read_config(const char *conffile) {
return 0;
}

if (datum_config.stratum_v1_vardiff_min < 1) {
DLOG_FATAL("Stratum server stratum.vardiff_min must be at least 1 (suggest at least 1024, but more likely 32768)");
return 0;
}

if (roundDownToPowerOfTwo_64(datum_config.stratum_v1_vardiff_min) != datum_config.stratum_v1_vardiff_min) {
const int nv = roundDownToPowerOfTwo_64(datum_config.stratum_v1_vardiff_min);
DLOG_WARN("stratum.vardiff_min MUST be a power of two. adjusting from %d to %d", datum_config.stratum_v1_vardiff_min, nv);
datum_config.stratum_v1_vardiff_min = nv;
}

if (datum_config.stratum_v1_vardiff_min < 1) {
DLOG_FATAL("Stratum server stratum.vardiff_min must be at least 1 (suggest at least 1024, but more likely 32768)");
return 0;
}

if (datum_config.stratum_v1_max_clients > (datum_config.stratum_v1_max_clients_per_thread*datum_config.stratum_v1_max_threads)) {
DLOG_FATAL("Stratum server configuration error. Max clients too high for thread settings");
return 0;
Expand Down Expand Up @@ -677,6 +808,13 @@ void datum_gateway_help(const char * const argv0) {
break;
}

case DATUM_CONF_DIFFICULTY: {
char difficulty[DATUM_FORMAT_DIFFICULTY_OUT_SZ];
datum_format_difficulty(difficulty, sizeof(difficulty), datum_pdiff_to_diff(opt->default_int));
printf(", default: \"%s\")\n", difficulty);
break;
}

default: {
puts(")");
break;
Expand Down Expand Up @@ -737,6 +875,13 @@ void datum_gateway_example_conf(void) {
puts("{}");
break;
}

case DATUM_CONF_DIFFICULTY: {
char difficulty[DATUM_FORMAT_DIFFICULTY_OUT_SZ];
datum_format_difficulty(difficulty, sizeof(difficulty), datum_pdiff_to_diff(opt->default_int));
printf("\"%s\"", difficulty);
break;
}
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions src/datum_conf.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ enum datum_conf_vartype {
DATUM_CONF_STRING,
DATUM_CONF_STRING_ARRAY,
DATUM_CONF_USERNAME_MODS,
DATUM_CONF_DIFFICULTY,
};

typedef struct {
Expand All @@ -80,6 +81,7 @@ typedef struct {

const T_DATUM_CONFIG_ITEM *datum_config_get_option_info(const char *category, size_t category_len, const char *name, size_t name_len);
const T_DATUM_CONFIG_ITEM *datum_config_get_option_info2(const char *category, const char *name);
int datum_config_parse_difficulty(int *out, json_t *item);

struct datum_addr_range {
char *addr;
Expand Down
Loading
Loading