|
35 | 35 |
|
36 | 36 | // Custom configurator and help output generator |
37 | 37 |
|
| 38 | +#include <assert.h> |
38 | 39 | #include <limits.h> |
| 40 | +#include <math.h> |
39 | 41 | #include <stdio.h> |
40 | 42 | #include <stdlib.h> |
41 | 43 | #include <string.h> |
|
49 | 51 |
|
50 | 52 | global_config_t datum_config; |
51 | 53 |
|
52 | | -const char *datum_conf_var_type_text[] = { "boolean", "integer", "string", "string_array" }; |
| 54 | +const char *datum_conf_var_type_text[] = { |
| 55 | + "boolean", |
| 56 | + "integer", |
| 57 | + "string", |
| 58 | + "string_array", |
| 59 | + "{\"modname\":{\"address\":proportion,...},...}", |
| 60 | +}; |
53 | 61 |
|
54 | 62 | const T_DATUM_CONFIG_ITEM datum_config_options[] = { |
55 | 63 | // Bitcoind configs |
@@ -102,6 +110,7 @@ const T_DATUM_CONFIG_ITEM datum_config_options[] = { |
102 | 110 | .required = false, .ptr = &datum_config.stratum_v1_idle_timeout_no_share, .default_int = 7200 }, |
103 | 111 | { .var_type = DATUM_CONF_INT, .category = "stratum", .name = "idle_timeout_max_last_work", .description = "Seconds we allow a subscribed connection to be idle since its last accepted share? (0 disables)", |
104 | 112 | .required = false, .ptr = &datum_config.stratum_v1_idle_timeout_max_last_work, .default_int = 0 }, |
| 113 | + { .var_type = DATUM_CONF_USERNAME_MODS, .category = "stratum", .name = "username_modifiers", .description = "Modifiers to redirect some portion of shares to alternate usernames", .required = false, .ptr = &datum_config.stratum_username_mod, }, |
105 | 114 |
|
106 | 115 | // mining settings |
107 | 116 | { .var_type = DATUM_CONF_STRING, .category = "mining", .name = "pool_address", .description = "Bitcoin address used for mining rewards.", |
@@ -234,7 +243,109 @@ void datum_config_set_default(const T_DATUM_CONFIG_ITEM *c) { |
234 | 243 | ((char *)c->ptr)[0] = 0; |
235 | 244 | break; |
236 | 245 | } |
| 246 | + |
| 247 | + case DATUM_CONF_USERNAME_MODS: { |
| 248 | + struct datum_username_mod ** const umods_p = c->ptr; |
| 249 | + free(*umods_p); |
| 250 | + *umods_p = NULL; |
| 251 | + break; |
| 252 | + } |
| 253 | + } |
| 254 | +} |
| 255 | + |
| 256 | +int datum_config_parse_username_mods(struct datum_username_mod ** const umods_p, json_t * const item, const bool log_errors) { |
| 257 | + if (!json_object_size(item)) { |
| 258 | + if (json_is_null(item) || json_is_object(item)) { |
| 259 | + free(*umods_p); |
| 260 | + *umods_p = NULL; |
| 261 | + return 1; |
| 262 | + } |
| 263 | + return -1; |
| 264 | + } |
| 265 | + |
| 266 | + size_t sz = (sizeof(**umods_p) + _Alignof(struct datum_username_mod) - 1) * (json_object_size(item) + 1); |
| 267 | + const char *modname, *addr; |
| 268 | + json_t *moddefn, *proportion_j; |
| 269 | + bool at_least_one_mod = false; |
| 270 | + json_object_foreach(item, modname, moddefn) { |
| 271 | + if (json_is_null(moddefn)) continue; |
| 272 | + if (!json_is_object(moddefn)) return -1; |
| 273 | + |
| 274 | + at_least_one_mod = true; |
| 275 | + sz += strlen(modname) + 1 + (sizeof(*((*umods_p)->ranges)) * (json_object_size(moddefn) + 1)); |
| 276 | + |
| 277 | + json_object_foreach(moddefn, addr, proportion_j) { |
| 278 | + if (json_is_null(proportion_j)) continue; |
| 279 | + if (!json_is_number(proportion_j)) return -1; |
| 280 | + if (json_number_value(proportion_j) < 0) return -1; |
| 281 | + sz += strlen(addr) + 1; |
| 282 | + } |
| 283 | + } |
| 284 | + |
| 285 | + free(*umods_p); |
| 286 | + |
| 287 | + if (!at_least_one_mod) { |
| 288 | + *umods_p = NULL; |
| 289 | + return 1; |
| 290 | + } |
| 291 | + |
| 292 | + uint8_t *p = malloc(sz); |
| 293 | + assert(p); |
| 294 | + *umods_p = (struct datum_username_mod*)p; |
| 295 | + json_object_foreach(item, modname, moddefn) { |
| 296 | + if (json_is_null(moddefn)) continue; |
| 297 | + |
| 298 | + struct datum_username_mod * const umod = (struct datum_username_mod*)p; |
| 299 | + umod->sz = sizeof(*umod) + (sizeof(*umod->ranges) * (json_object_size(moddefn) + 1)); |
| 300 | + p += umod->sz; |
| 301 | + umod->modname_len = strlen(modname); |
| 302 | + umod->modname = memcpy(p, modname, umod->modname_len); |
| 303 | + p += umod->modname_len; |
| 304 | + |
| 305 | + double sum = 0; |
| 306 | + struct datum_addr_range *addr_range = umod->ranges; |
| 307 | + json_object_foreach(moddefn, addr, proportion_j) { |
| 308 | + if (json_is_null(proportion_j)) continue; |
| 309 | + |
| 310 | + sum += json_number_value(proportion_j); |
| 311 | + const double nonce_max_d = ceil(sum * 0x10000) - 1; |
| 312 | + if (nonce_max_d < 0) continue; |
| 313 | + const uint16_t nonce_max = (nonce_max_d > 0xffff) ? (uint16_t)0xffff : (uint16_t)nonce_max_d; |
| 314 | + addr_range->addr_len = strlen(addr); |
| 315 | + addr_range->addr = memcpy(p, addr, addr_range->addr_len + 1); |
| 316 | + addr_range->max = nonce_max; |
| 317 | + p = &p[addr_range->addr_len + 1]; |
| 318 | + ++addr_range; |
| 319 | + if (nonce_max == 0xffff) break; |
| 320 | + } |
| 321 | + if (log_errors && (addr_range == umod->ranges || addr_range[-1].max != 0xffff)) { |
| 322 | + double missing_percent = 100 * (1 - sum); |
| 323 | + const unsigned int missing_percent_precision = datum_double_precision(&missing_percent); |
| 324 | + DLOG_ERROR("Username modifier '%s' is configured to not distribute %.*f%% of shares!", modname, missing_percent_precision, missing_percent); |
| 325 | + } |
| 326 | + addr_range[0].addr = NULL; |
| 327 | + assert((uint8_t*)addr_range <= &((uint8_t*)umod)[umod->sz]); // otherwise we overwrote strings! |
| 328 | + assert(p <= &((uint8_t*)*umods_p)[sz]); // otherwise we overran the buffer! |
| 329 | + umod->sz = datum_align_sz(p - (uint8_t*)umod, _Alignof(struct datum_username_mod)); |
| 330 | + p = &((uint8_t*)umod)[umod->sz]; |
237 | 331 | } |
| 332 | + ((struct datum_username_mod*)p)->sz = 0; |
| 333 | + |
| 334 | + return 1; |
| 335 | +} |
| 336 | + |
| 337 | +struct datum_username_mod *datum_username_mods_next(struct datum_username_mod * const prev_umod) { |
| 338 | + struct datum_username_mod * const next_umod = (struct datum_username_mod *)&(((uint8_t*)prev_umod)[prev_umod->sz]); |
| 339 | + return next_umod->sz ? next_umod : NULL; |
| 340 | +} |
| 341 | + |
| 342 | +struct datum_username_mod *datum_username_mods_find(struct datum_username_mod *umod, const char * const modname, const size_t modname_len) { |
| 343 | + for ( ; umod; umod = datum_username_mods_next(umod)) { |
| 344 | + if (modname_len != umod->modname_len) continue; |
| 345 | + if (strncmp(modname, umod->modname, modname_len)) continue; |
| 346 | + return umod; |
| 347 | + } |
| 348 | + return NULL; |
238 | 349 | } |
239 | 350 |
|
240 | 351 | int datum_config_parse_value(const T_DATUM_CONFIG_ITEM *c, json_t *item) { |
@@ -293,6 +404,10 @@ int datum_config_parse_value(const T_DATUM_CONFIG_ITEM *c, json_t *item) { |
293 | 404 | ((char (*)[1024])c->ptr)[i][0] = 0; |
294 | 405 | return 1; |
295 | 406 | } |
| 407 | + |
| 408 | + case DATUM_CONF_USERNAME_MODS: { |
| 409 | + return datum_config_parse_username_mods(c->ptr, item, true); |
| 410 | + } |
296 | 411 | } |
297 | 412 |
|
298 | 413 | return -1; |
@@ -590,6 +705,11 @@ void datum_gateway_example_conf(void) { |
590 | 705 | puts("[]"); |
591 | 706 | break; |
592 | 707 | } |
| 708 | + |
| 709 | + case DATUM_CONF_USERNAME_MODS: { |
| 710 | + puts("{}"); |
| 711 | + break; |
| 712 | + } |
593 | 713 | } |
594 | 714 | } |
595 | 715 | } |
|
0 commit comments