diff --git a/CMakeLists.txt b/CMakeLists.txt index 0aa332a7..3141c6ab 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,6 +26,7 @@ add_executable(datum_gateway src/datum_jsonrpc.c src/datum_logger.c src/datum_protocol.c + src/datum_protocol_tests.c src/datum_queue.c src/datum_sockets.c src/datum_stratum.c diff --git a/doc/example_datum_gateway_config.json b/doc/example_datum_gateway_config.json index e46d983b..fa06489d 100644 --- a/doc/example_datum_gateway_config.json +++ b/doc/example_datum_gateway_config.json @@ -27,8 +27,7 @@ "log_level_file": 1 }, "datum": { - "pool_pass_workers": true, - "pool_pass_full_users": true, + "pool_username_behaviour": "passthrough", "pooled_mining_only": true } } diff --git a/doc/usernames.md b/doc/usernames.md index a7883172..b779b50d 100644 --- a/doc/usernames.md +++ b/doc/usernames.md @@ -41,12 +41,16 @@ If the Stratum username *begins* with a period, it is interpreted as a worker na There are three different ways to pass usernames to your pool. By default, the Stratum username is always passed in full, as-is. -You can make this explicit by setting `datum`.`pool_pass_full_users` to `true` in the config file, or "Send Miner Usernames To Pool: Override Bitcoin Address" in the web configurator. +You can make this explicit by setting `datum`.`pool_username_behaviour` to `"passthrough"` in the config file, or "Send Miner Usernames To Pool: Override Bitcoin Address" in the web configurator. -If you change `datum`.`pool_pass_full_users` to `false`, you can then set `datum`.`pool_pass_workers` instead (or "Send Miner Usernames To Pool: Send as worker names" in the web configurator). +You can also set `datum`.`pool_username_behaviour` to `"worker"` (or "Send Miner Usernames To Pool: Send as worker names" in the web configurator). With this setting, the entire Stratum username will be appended after the default username (`mining`.`pool_address`) as a worker. -Finally, if you set both options to `false`, the Stratum username will be ignored entirely. +Another option is to set `datum`.`pool_username_behaviour` to `"strip_worker"` (or "Send Miner Usernames To Pool: Override Bitcoin Address with username prior to period (strip worker)" in the web configurator). +This will strip off everything beginning with the first period (`.`) in the username, and send just the first part. +You can use this if you want to keep the miner worker name private, but still use its username for the Bitcoin address. + +Finally, if you set `datum`.`pool_username_behaviour` to `"ignore"`, the Stratum username will be ignored entirely. Instead, only the configured default username (`mining`.`pool_address`) will be used, without any worker names. ## Username modifiers (advanced) @@ -94,4 +98,4 @@ if you assign *more* than 100%, that portion above will not have any shares subm Do not rely on these behaviours. Always specify the full 100% range explicitly. -NOTE: This feature is handled when shares are received by the Gateway's Stratum server, and will therefore only work if you have `datum`.`pool_pass_full_users` enabled. +NOTE: This feature is handled when shares are received by the Gateway's Stratum server, and will therefore only work if you have `datum`.`pool_username_behaviour` set to `"passthrough"` or `"strip_worker"`. diff --git a/src/datum_api.c b/src/datum_api.c index f4c9a840..56f9fba1 100644 --- a/src/datum_api.c +++ b/src/datum_api.c @@ -964,6 +964,8 @@ size_t datum_api_fill_config_var(const char *var_start, const size_t var_name_le var_start = "readonly:"; colon_pos = &var_start[8]; } + } else if (var_name_len_2 == 27 && 0 == strncmp(var_start_2, "*datum_pool_pass_full_users", 27)) { + val = datum_config.datum_pool_pass_workers && datum_config.datum_pool_pass_full_users; } else if (var_name_len_2 == 24 && 0 == strncmp(var_start_2, "*datum_pool_pass_workers", 24)) { val = datum_config.datum_pool_pass_workers && !datum_config.datum_pool_pass_full_users; } else if (var_name_len_2 == 16 && 0 == strncmp(var_start_2, "*datum_pool_host", 16)) { @@ -986,6 +988,8 @@ size_t datum_api_fill_config_var(const char *var_start, const size_t var_name_le if (copy_sz >= replacement_max_len) copy_sz = replacement_max_len - 1; memcpy(replacement, s, copy_sz); return copy_sz; + } else if (var_name_len_2 == 32 && 0 == strncmp(var_start_2, "*username_behaviour_strip_worker", 32)) { + val = datum_config.datum_pool_pass_full_users && !datum_config.datum_pool_pass_workers; } else if (var_name_len_2 == 27 && 0 == strncmp(var_start_2, "*username_behaviour_private", 27)) { val = !(datum_config.datum_pool_pass_workers || datum_config.datum_pool_pass_full_users); } else if (var_name_len_2 == 22 && 0 == strncmp(var_start_2, "*reward_sharing_prefer", 22)) { @@ -1036,6 +1040,10 @@ 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_FUNC: { + DLOG_ERROR("%s: %s not implemented", __func__, "DATUM_CONF_FUNC"); + break; + } } } else { DLOG_ERROR("%s: '%.*s' not implemented", __func__, (int)(var_end - var_start_2), var_start_2); @@ -1160,25 +1168,52 @@ bool datum_api_config_set(const char * const key, const char * const val, struct strcpy(datum_config.mining_pool_address, val); datum_api_json_modify_new("mining", "pool_address", json_string(val)); } else if (0 == strcmp(key, "username_behaviour")) { + json_t * const config = datum_config.config_json; + assert(config); + + const char *nv; if (0 == strcmp(val, "datum_pool_pass_full_users")) { - if (datum_config.datum_pool_pass_full_users) return true; + if (datum_config.datum_pool_pass_full_users && datum_config.datum_pool_pass_workers) return true; + nv = "passthrough"; datum_config.datum_pool_pass_full_users = true; - // datum_pool_pass_workers doesn't matter with datum_pool_pass_full_users enabled + datum_config.datum_pool_pass_workers = true; } else if (0 == strcmp(val, "datum_pool_pass_workers")) { if (datum_config.datum_pool_pass_workers && !datum_config.datum_pool_pass_full_users) return true; + nv = "worker"; datum_config.datum_pool_pass_full_users = false; datum_config.datum_pool_pass_workers = true; + } else if (0 == strcmp(val, "strip_worker")) { + if (datum_config.datum_pool_pass_full_users && !datum_config.datum_pool_pass_workers) return true; + nv = val; + datum_config.datum_pool_pass_full_users = true; + datum_config.datum_pool_pass_workers = false; } else if (0 == strcmp(val, "private")) { if (!(datum_config.datum_pool_pass_workers || datum_config.datum_pool_pass_full_users)) return true; + nv = "ignore"; datum_config.datum_pool_pass_full_users = false; datum_config.datum_pool_pass_workers = false; } else { json_array_append_new(errors, json_string_nocheck("Invalid option for \"Send Miner Usernames To Pool\"")); return false; } - datum_api_json_modify_new("datum", "pool_pass_full_users", json_boolean(datum_config.datum_pool_pass_full_users)); - if (!datum_config.datum_pool_pass_full_users) { - datum_api_json_modify_new("datum", "pool_pass_workers", json_boolean(datum_config.datum_pool_pass_workers)); + datum_api_json_modify_new("datum", "pool_username_behaviour", json_string_nocheck(nv)); + + json_t *j = json_object_get(config, "datum"); + if (j && (json_object_get(j, "pool_pass_full_users") + || json_object_get(j, "pool_pass_workers") + || json_object_get(j, "_legacy_username_behaviour"))) { + if (nv[0] == 's') { // strip_worker, not supported by legacy config + // Must delete legacy keys or we will trigger a warning at startup + json_object_del(j, "pool_pass_full_users"); + json_object_del(j, "pool_pass_workers"); + datum_api_json_modify_new("datum", "_legacy_username_behaviour", json_true()); + } else { + json_object_del(j, "_legacy_username_behaviour"); + datum_api_json_modify_new("datum", "pool_pass_full_users", json_boolean(datum_config.datum_pool_pass_full_users)); + if (!datum_config.datum_pool_pass_full_users) { + datum_api_json_modify_new("datum", "pool_pass_workers", json_boolean(datum_config.datum_pool_pass_workers)); + } + } } } else if (0 == strcmp(key, "mining_coinbase_tag_secondary")) { if (0 == strcmp(val, datum_config.mining_coinbase_tag_secondary)) return true; diff --git a/src/datum_conf.c b/src/datum_conf.c index 43dc4dc8..73101fb9 100644 --- a/src/datum_conf.c +++ b/src/datum_conf.c @@ -57,8 +57,58 @@ const char *datum_conf_var_type_text[] = { "string", "string_array", "{\"modname\":{\"address\":proportion,...},...}", + NULL, // func }; +static int datum_conf_username_behaviour(const T_DATUM_CONFIG_ITEM * const c, const json_t * const j, const char ** const out_type) { + if (out_type) { + *out_type = "string"; + } + if (json_is_string(j)) { + const char * const s = json_string_value(j); + switch (json_string_length(j)) { + case 4: + if (0 == strcasecmp("pass", s)) break; + return -1; + case 6: + if (0 == strcasecmp("worker", s)) { + datum_config.datum_pool_pass_full_users = false; + datum_config.datum_pool_pass_workers = true; + return 0; + } + if (0 == strcasecmp("ignore", s)) { + datum_config.datum_pool_pass_full_users = false; + datum_config.datum_pool_pass_workers = false; + return 0; + } + return -1; + case 8: + if (0 == strcasecmp("passthru", s)) break; + return -1; + case 11: + if (0 == strcasecmp("passthrough", s)) break; + return -1; + case 12: + if (0 == strcasecmp("strip_worker", s)) { + datum_config.datum_pool_pass_full_users = true; + datum_config.datum_pool_pass_workers = false; + return 0; + } + return -1; + default: + return -1; + } + } else if (json_is_null(j) || !j) { + // fallthrough + } else { + return -1; + } + // Only "passthrough" and variants reach here + datum_config.datum_pool_pass_full_users = true; + datum_config.datum_pool_pass_workers = true; + return 0; +} + const T_DATUM_CONFIG_ITEM datum_config_options[] = { // Bitcoind configs { .var_type = DATUM_CONF_STRING, .category = "bitcoind", .name = "rpccookiefile", .description = "Path to file to read RPC cookie from, for communication with local bitcoind.", @@ -177,12 +227,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] = "f21f2f0ef0aa1970468f22bad9bb7f4535146f8e4a8f646bebc93da3d89b1406f40d032f09a417d94dc068055df654937922d2c89522e3e8f6f0e649de473003", .max_string_len = sizeof(datum_config.datum_pool_pubkey) }, - { .var_type = DATUM_CONF_BOOL, .category = "datum", .name = "pool_pass_workers", .description = "Pass stratum miner usernames as sub-worker names to the pool (pool_username.miner's username)", - .example_default = true, + { .var_type = DATUM_CONF_BOOL, .category = "datum", .name = "pool_pass_workers", + // DEPRECATED (but CAUTION if removing - this currently is responsible for initialising the default value!) .required = false, .ptr = &datum_config.datum_pool_pass_workers, .default_bool = true }, - { .var_type = DATUM_CONF_BOOL, .category = "datum", .name = "pool_pass_full_users", .description = "Pass stratum miner usernames as raw usernames to the pool (use if putting multiple payout addresses on miners behind this gateway)", - .example_default = true, + { .var_type = DATUM_CONF_BOOL, .category = "datum", .name = "pool_pass_full_users", + // DEPRECATED (but CAUTION if removing - this currently is responsible for initialising the default value!) .required = false, .ptr = &datum_config.datum_pool_pass_full_users, .default_bool = true }, + { .var_type = DATUM_CONF_FUNC, .category = "datum", .name = "pool_username_behaviour", .description = "Whether and how to Pass stratum miner usernames to the pool: \"passthrough\" sends it as-is (overriding mining.pool_address), \"worker\" appends it after mining.pool_address, \"ignore\" disregards it entirely, and \"strip_worker\" passes only the username up until the first period character", + .example_default = true, + .required = false, .ptr_func = &datum_conf_username_behaviour, .default_string[0] = "\"passthrough\"" }, { .var_type = DATUM_CONF_BOOL, .category = "datum", .name = "always_pay_self", .description = "Always include my datum.pool_username payout in my blocks if possible", .required = false, .ptr = &datum_config.datum_always_pay_self, .default_bool = true }, { .var_type = DATUM_CONF_BOOL, .category = "datum", .name = "pooled_mining_only", .description = "If the DATUM pool server becomes unavailable, terminate miner connections (otherwise, 100% of any blocks you find pay mining.pool_address)", @@ -221,6 +274,15 @@ json_t *load_json_from_file(const char *file_path) { return root; } +const char *datum_config_get_type_string(const T_DATUM_CONFIG_ITEM * const c) { + if (c->var_type == DATUM_CONF_FUNC) { + const char *expected_type; + c->ptr_func(c, NULL, &expected_type); + return expected_type; + } + return datum_conf_var_type_text[c->var_type]; +} + void datum_config_set_default(const T_DATUM_CONFIG_ITEM *c) { // set the default switch(c->var_type) { @@ -252,6 +314,13 @@ void datum_config_set_default(const T_DATUM_CONFIG_ITEM *c) { *umods_p = NULL; break; } + + case DATUM_CONF_FUNC: { + json_t * const j_null = json_null(); + c->ptr_func(c, j_null, NULL); + json_decref(j_null); + break; + } } } @@ -410,6 +479,10 @@ 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_FUNC: { + return c->ptr_func(c, item, NULL); + } } return -1; @@ -452,10 +525,13 @@ int datum_read_config(const char *conffile) { continue; } - // item might be valid + // item might be valid* j = datum_config_parse_value(&datum_config_options[i], item); if (j == -1) { - DLOG_ERROR("Could not parse configuration option %s.%s. Type should be %s", datum_config_options[i].category, datum_config_options[i].name, datum_conf_var_type_text[datum_config_options[i].var_type]); + const T_DATUM_CONFIG_ITEM * const c = &datum_config_options[i]; + const char * const expected_type = datum_config_get_type_string(c); + DLOG_ERROR("Could not parse configuration option %s.%s. Type should be %s", + c->category, c->name, expected_type); return -1; } else if (j == -2) { DLOG_ERROR("Configuration option %s.%s exceeds maximum length of %d", datum_config_options[i].category, datum_config_options[i].name, datum_config_options[i].max_string_len - 1); @@ -463,6 +539,31 @@ int datum_read_config(const char *conffile) { } } + cat = json_object_get(config, "datum"); + if (json_is_object(cat)) { + item = json_object_get(cat, "pool_pass_full_users"); + if (item && (!json_is_false(item)) ? !(datum_config.datum_pool_pass_full_users && datum_config.datum_pool_pass_workers) : datum_config.datum_pool_pass_full_users) { + DLOG_WARN("Deprecated configuration option %s.%s ignored (%s.%s overrides)", + "datum", "pool_pass_full_users", + "datum", "pool_username_behaviour"); + } else { + item = json_object_get(cat, "pool_pass_workers"); + if (item && (!json_is_false(item)) != datum_config.datum_pool_pass_workers) { + DLOG_WARN("Deprecated configuration option %s.%s ignored (%s.%s overrides)", + "datum", "pool_pass_workers", + "datum", "pool_username_behaviour"); + } + } + item = json_object_get(cat, "pool_username_behaviour"); + if (((!item) || json_is_null(item)) && datum_config.datum_pool_pass_full_users && !datum_config.datum_pool_pass_workers) { + datum_config.datum_pool_pass_workers = true; + DLOG_WARN("Deprecated configuration option %s.%s ignored (also-deprecated %s.%s overrides; consider migrating to %s.%s)", + "datum", "pool_pass_workers", + "datum", "pool_pass_full_users", + "datum", "pool_username_behaviour"); + } + } + #ifdef ENABLE_API if (datum_config.api_modify_conf) { datum_config.config_json = config; @@ -625,6 +726,7 @@ void datum_gateway_help(const char * const argv0) { puts("Configuration file options:\n\n{"); for (unsigned int i = 0; i < NUM_CONFIG_ITEMS; ++i) { const T_DATUM_CONFIG_ITEM * const opt = &datum_config_options[i]; + if (!opt->description[0]) continue; // deprecated/hidden options if (strcmp(opt->category, lastcat)) { if (i) { puts(" },"); } printf(" \"%s\": {\n", opt->category); @@ -632,7 +734,8 @@ void datum_gateway_help(const char * const argv0) { } p = 30 - strlen(opt->name); if (p < 0) p = 0; - printf(" \"%s\": %.*s %s (%s", opt->name, p, paddots, opt->description, datum_conf_var_type_text[opt->var_type]); + const char * const expected_type = datum_config_get_type_string(opt); + printf(" \"%s\": %.*s %s (%s", opt->name, p, paddots, opt->description, expected_type); if (opt->required) { puts(", REQUIRED)"); } else { @@ -652,6 +755,13 @@ void datum_gateway_help(const char * const argv0) { break; } + case DATUM_CONF_FUNC: { + if (opt->default_string[0]) { + printf(", default: %s)\n", opt->default_string[0]); + } + break; + } + default: { puts(")"); break; @@ -712,6 +822,15 @@ void datum_gateway_example_conf(void) { puts("{}"); break; } + + case DATUM_CONF_FUNC: { + if (opt->default_string[0]) { + printf("%s", opt->default_string[0]); + } else { + printf("null"); + } + break; + } } } } diff --git a/src/datum_conf.h b/src/datum_conf.h index 98632037..eeb6d3b9 100644 --- a/src/datum_conf.h +++ b/src/datum_conf.h @@ -52,9 +52,16 @@ enum datum_conf_vartype { DATUM_CONF_STRING, DATUM_CONF_STRING_ARRAY, DATUM_CONF_USERNAME_MODS, + DATUM_CONF_FUNC, }; -typedef struct { +typedef struct T_DATUM_CONFIG_ITEM T_DATUM_CONFIG_ITEM; + +// Usage: func(item, config json item, NULL) - assign item +// Usage: func(item, NULL, pointer to const char*) - store type string pointer +typedef int (* const DATUM_Conf_VarFunc)(const T_DATUM_CONFIG_ITEM *, const json_t *, const char **out_type); + +struct T_DATUM_CONFIG_ITEM { char category[32]; char name[64]; char description[512]; @@ -70,10 +77,13 @@ typedef struct { }; }; - void *ptr; + union { + void *ptr; + DATUM_Conf_VarFunc ptr_func; + }; bool required; -} T_DATUM_CONFIG_ITEM; +}; 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); diff --git a/src/datum_gateway.c b/src/datum_gateway.c index 3a32d0f7..f7ae99a6 100644 --- a/src/datum_gateway.c +++ b/src/datum_gateway.c @@ -81,6 +81,7 @@ struct arguments { char *config_file; }; +void datum_protocol_tests(void); void datum_stratum_tests(void); void datum_conf_tests(void); void datum_utils_tests(void); @@ -105,6 +106,7 @@ static error_t parse_opt(int key, char *arg, struct argp_state *state) { case 0x101: // test datum_utils_tests(); datum_conf_tests(); + datum_protocol_tests(); datum_stratum_tests(); exit(datum_test_failed); default: diff --git a/src/datum_protocol.c b/src/datum_protocol.c index 76dbc425..a0d96f9f 100644 --- a/src/datum_protocol.c +++ b/src/datum_protocol.c @@ -1310,6 +1310,23 @@ int datum_protocol_pow_submit( return datum_queue_add_item(&pow_queue, &pow); } +int datum_protocol_submit_username(char * const username, const size_t username_sz, const global_config_t * const cfg, const char * const input_username) { + if (((!cfg->datum_pool_pass_workers) && ((!cfg->datum_pool_pass_full_users) || input_username[0] == '.')) || input_username[0] == '\0') { + return snprintf(username, username_sz, "%s", cfg->mining_pool_address); + } else if (cfg->datum_pool_pass_full_users && input_username[0] != '.') { + const char * const endpos = cfg->datum_pool_pass_workers ? NULL : strchr(input_username, '.'); + // TODO: Make sure the usernames are addresses, and if not use one of the configured addresses + if (endpos) { + return snprintf(username, username_sz, "%.*s", (int)(endpos - input_username), input_username); + } else { + return snprintf(username, username_sz, "%s", input_username); + } + } else { + // append the miner's username to the configured address as .workername + return snprintf(username, username_sz, "%s%s%s", cfg->mining_pool_address, (input_username[0] == '.') ? "" : ".", input_username); + } +} + // {"params": ["mzjP9Hn7aqaCLM5pSgMSQzgs3gnxSFv91B", "662599770700", "f40c000000000000", "66259976", "48220d13", "00d30000"], "id": 182, "method": "mining.submit"} int datum_protocol_pow(void *arg) { T_DATUM_PROTOCOL_POW *pow = arg; @@ -1339,16 +1356,15 @@ int datum_protocol_pow(void *arg) { memcpy(&msg[i], pow->extranonce, 12); i+=12; // extranonce1+2 17 char * const username = (char *)&msg[i]; - if (((!datum_config.datum_pool_pass_full_users) && (!datum_config.datum_pool_pass_workers)) || pow->username[0] == '\0') { - i+=snprintf(username, 385, "%s", datum_config.mining_pool_address); - } else if (datum_config.datum_pool_pass_full_users && pow->username[0] != '.') { - // TODO: Make sure the usernames are addresses, and if not use one of the configured addresses - i+=snprintf(username, 385, "%s", pow->username); - } else if (datum_config.datum_pool_pass_full_users || datum_config.datum_pool_pass_workers) { - // append the miner's username to the configured address as .workername - i+=snprintf(username, 385, "%s%s%s", datum_config.mining_pool_address, (pow->username[0] == '.') ? "" : ".", pow->username); - } - i++; // already 0 from snprintf + j = datum_protocol_submit_username(username, DATUM_PROTOCOL_MAX_USERNAME_LEN + 1, &datum_config, pow->username); + if (j < 0) { + DLOG_ERROR("Unexpected error copying username to POW!"); + // Still submit it without a username in case it's a block + username[0] = '\0'; + j = 0; + } + if (j > DATUM_PROTOCOL_MAX_USERNAME_LEN) j = DATUM_PROTOCOL_MAX_USERNAME_LEN; + i += j + 1; // including final null byte // reserve 4 bytes for future use memset(&msg[i], 0, 4); i+=4; diff --git a/src/datum_protocol.h b/src/datum_protocol.h index 60b0d40b..3f15d01a 100644 --- a/src/datum_protocol.h +++ b/src/datum_protocol.h @@ -50,6 +50,7 @@ #define DATUM_PROTOCOL_MAX_CMD_DATA_SIZE 4194304 // 2^22 - protocol limit! #define DATUM_PROTOCOL_BUFFER_SIZE (DATUM_PROTOCOL_MAX_CMD_DATA_SIZE*3) +#define DATUM_PROTOCOL_MAX_USERNAME_LEN 384 #define MAX_DATUM_CLIENT_EVENTS 32 diff --git a/src/datum_protocol_tests.c b/src/datum_protocol_tests.c new file mode 100644 index 00000000..6559eaba --- /dev/null +++ b/src/datum_protocol_tests.c @@ -0,0 +1,113 @@ +/* + * + * DATUM Gateway + * Decentralized Alternative Templates for Universal Mining + * + * This file is part of OCEAN's Bitcoin mining decentralization + * project, DATUM. + * + * https://ocean.xyz + * + * --- + * + * Copyright (c) 2026 Bitcoin Ocean, LLC & Luke Dashjr + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#include +#include + +#include "datum_conf.h" +#include "datum_protocol.h" +#include "datum_utils.h" + +int datum_protocol_submit_username(char * const username, const size_t username_sz, const global_config_t * const cfg, const char * const input_username); + +void datum_protocol_submit_username_tests() { + const char addr[] = "1someaddress"; + const size_t addr_len = sizeof(addr) - 1; + const char addr_2[] = "2anotheraddress"; + const size_t addr_2_len = sizeof(addr_2) - 1; + const char addr_2w[] = "2anotheraddress.worker"; + const size_t addr_2w_len = sizeof(addr_2w) - 1; + const char noaddr_w[] = ".Worker"; + const size_t noaddr_w_len = sizeof(noaddr_w) - 1; + global_config_t cfg = { + .datum_pool_pass_full_users = true, + .datum_pool_pass_workers = true, + }; + strcpy(cfg.mining_pool_address, addr); + char buf[386]; + + // "passthrough" tests: + datum_test(datum_protocol_submit_username(buf, sizeof(buf), &cfg, "") == addr_len); + datum_test(0 == strcmp(buf, addr)); + datum_test(datum_protocol_submit_username(buf, sizeof(buf), &cfg, addr_2) == addr_2_len); + datum_test(0 == strcmp(buf, addr_2)); + datum_test(datum_protocol_submit_username(buf, sizeof(buf), &cfg, addr_2w) == addr_2w_len); + datum_test(0 == strcmp(buf, addr_2w)); + datum_test(datum_protocol_submit_username(buf, sizeof(buf), &cfg, noaddr_w) == addr_len + noaddr_w_len); + datum_test(0 == memcmp(buf, addr, addr_len)); + datum_test(0 == strcmp(&buf[addr_len], noaddr_w)); + + // "worker" tests: + cfg.datum_pool_pass_full_users = false; + datum_test(datum_protocol_submit_username(buf, sizeof(buf), &cfg, "") == addr_len); + datum_test(0 == strcmp(buf, addr)); + datum_test(datum_protocol_submit_username(buf, sizeof(buf), &cfg, addr_2) == addr_len + 1 + addr_2_len); + datum_test(0 == memcmp(buf, addr, addr_len)); + datum_test('.' == buf[addr_len]); + datum_test(0 == strcmp(&buf[addr_len + 1], addr_2)); + datum_test(datum_protocol_submit_username(buf, sizeof(buf), &cfg, addr_2w) == addr_len + 1 + addr_2w_len); + datum_test(0 == memcmp(buf, addr, addr_len)); + datum_test('.' == buf[addr_len]); + datum_test(0 == strcmp(&buf[addr_len + 1], addr_2w)); + datum_test(datum_protocol_submit_username(buf, sizeof(buf), &cfg, noaddr_w) == addr_len + noaddr_w_len); + datum_test(0 == memcmp(buf, addr, addr_len)); + datum_test(0 == strcmp(&buf[addr_len], noaddr_w)); + + // "ignore" tests: + cfg.datum_pool_pass_workers = false; + datum_test(datum_protocol_submit_username(buf, sizeof(buf), &cfg, "") == addr_len); + datum_test(0 == strcmp(buf, addr)); + datum_test(datum_protocol_submit_username(buf, sizeof(buf), &cfg, addr_2) == addr_len); + datum_test(0 == strcmp(buf, addr)); + datum_test(datum_protocol_submit_username(buf, sizeof(buf), &cfg, addr_2w) == addr_len); + datum_test(0 == strcmp(buf, addr)); + datum_test(datum_protocol_submit_username(buf, sizeof(buf), &cfg, noaddr_w) == addr_len); + datum_test(0 == strcmp(buf, addr)); + + // "strip_worker" tests: + cfg.datum_pool_pass_full_users = true; + datum_test(datum_protocol_submit_username(buf, sizeof(buf), &cfg, "") == addr_len); + datum_test(0 == strcmp(buf, addr)); + datum_test(datum_protocol_submit_username(buf, sizeof(buf), &cfg, addr_2) == addr_2_len); + datum_test(0 == strcmp(buf, addr_2)); + datum_test(datum_protocol_submit_username(buf, sizeof(buf), &cfg, addr_2w) == addr_2_len); + datum_test(0 == strcmp(buf, addr_2)); + datum_test(datum_protocol_submit_username(buf, sizeof(buf), &cfg, noaddr_w) == addr_len); + datum_test(0 == strcmp(buf, addr)); +} + +void datum_protocol_tests(void) { + datum_protocol_submit_username_tests(); +} diff --git a/www/config.html b/www/config.html index 20ebc8c5..be2f08a6 100644 --- a/www/config.html +++ b/www/config.html @@ -114,6 +114,7 @@

Basic