/* * * 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) 2024 Bitcoin Ocean, LLC & Jason Hughes * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "datum_gateway.h" #include "datum_logger.h" #include "datum_utils.h" #include "thirdparty_base58.h" #include "thirdparty_segwit_addr.h" #include "datum_logger.h" volatile int panic_mode = 0; static uint64_t process_start_time = 0; void get_target_from_diff(unsigned char *result, uint64_t diff) { uint64_t dividend_parts[4] = {0, 0, 0, 0x00000000FFFF0000}; uint64_t remainder = 0; uint64_t quotient; memset(result, 0, 32); for (int i = 3; i >= 0; i--) { __uint128_t temp = remainder; temp = (temp << 64) | dividend_parts[i]; quotient = temp / diff; remainder = temp % diff; for (int j = 0; j < 8; j++) { result[(i<<3) + j] = (quotient >> (j<<3)) & 0xFF; } } } uint64_t get_process_uptime_seconds() { struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); return (uint64_t)ts.tv_sec - process_start_time; } void datum_utils_init(void) { build_hex_lookup(); process_start_time = monotonic_time_seconds(); } #ifdef __GNUC__ // faster, less portable uint64_t roundDownToPowerOfTwo_64(uint64_t x) { return 1ULL << (63 - __builtin_clzll(x)); } unsigned char floorPoT(uint64_t x) { if (x == 0) { return 0; } return (63 - __builtin_clzll(x)); } #else // More portable but slower uint64_t roundDownToPowerOfTwo_64(uint64_t x) { x |= x >> 1; x |= x >> 2; x |= x >> 4; x |= x >> 8; x |= x >> 16; x |= x >> 32; return x - (x >> 1); } unsigned char floorPoT(uint64_t x) { if (x == 0) { return 0; } unsigned char pos = 0; while (x >>= 1) { pos++; } return pos; } #endif uint64_t monotonic_time_seconds(void) { struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); // SAFE from system time changes (e.g., NTP adjustments, manual clock changes) return (uint64_t)ts.tv_sec; } uint64_t current_time_millis(void) { struct timeval te; gettimeofday(&te, NULL); // get current time uint64_t milliseconds = te.tv_sec * 1000LL + te.tv_usec / 1000; // calculate milliseconds return milliseconds; } uint64_t current_time_micros(void) { struct timeval te; gettimeofday(&te, NULL); // get current time uint64_t microseconds = te.tv_sec * 1000000LL + te.tv_usec; // calculate microseconds return microseconds; } unsigned char hex_lookup[65536]; unsigned short uchar_hex_lookup[256]; // crappy function written by wizkid057! v2! :D unsigned char hex2bin_uchar(const char *in) { return hex_lookup[*(const unsigned short *)in]; } void uchar_to_hex(char *s, const unsigned char b) { // place the ASCII hexidecimal value for the unsigned char into the string at ptr s // this does NOT null terminate the string! *(unsigned short *)s = uchar_hex_lookup[b]; } void build_hex_lookup(void) { unsigned int i; char a[3]; unsigned short b; for(i = 0; i < 65535; ++i) hex_lookup[i] = 0; hex_lookup[65535] = 0; for(i=0;i<256;i++) { sprintf(a,"%2.2X",i); b = *(unsigned short *)&a; hex_lookup[b] = i; sprintf(a,"%2.2x",i); b = *(unsigned short *)&a; hex_lookup[b] = i; uchar_hex_lookup[i] = b; } } bool my_sha256(void *digest, const void *buffer, size_t length) { crypto_hash_sha256(digest, buffer, length); return 1; } bool double_sha256(void *out, const void *in, size_t length) { unsigned char dg1[32]; my_sha256(dg1, in, length); my_sha256(out, dg1, 32); return 1; } long double get_approx_achieved_diff(const unsigned char *bytes) { if (bytes == NULL) { // Handle null pointer return 0.0L; } // Check if all bytes are zero int allZero = 1; for (int i = 0; i < 32; i++) { if (bytes[i] != 0) { allZero = 0; break; } } if (allZero) { return 0.0L; } // bdiff 1 unsigned char dividendBytes[32] = { // Least significant byte at index 0 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00 // Most significant byte at index 31 }; long double dividend = 0.0L; for (int i = 0; i < 32; i++) { dividend *= 256.0L; // Shift left by 8 bits dividend += dividendBytes[31 - i]; // Add the byte } long double divisor = 0.0L; for (int i = 0; i < 32; i++) { divisor *= 256.0L; // Shift left by 8 bits divisor += bytes[31 - i]; // Add the byte } return dividend / divisor; } void nbits_to_target(uint32_t nbits, uint8_t *target) { uint32_t exponent = (nbits >> 24) & 0xff; uint32_t mantissa = nbits & 0xffffff; int i; memset(target, 0, 32); if (exponent <= 3) { mantissa >>= 8 * (3 - exponent); for (i = 0; i < 3; i++) { target[i] = (mantissa >> (8 * i)) & 0xff; } } else { for (i = 0; i < 3; i++) { target[i + exponent - 3] = (mantissa >> (8 * i)) & 0xff; } } } int compare_hashes(const uint8_t *share_hash, const uint8_t *target) { for (int i = 31; i >= 0; i--) { if (share_hash[i] < target[i]) return -1; // share_hash is smaller, valid block/share if (share_hash[i] > target[i]) return 1; // share_hash is larger, not valid } return 0; // hashes are equal } unsigned long long block_reward(unsigned int block_height) { unsigned long long reward = 5000000000; unsigned int halvings = block_height / 210000; if (halvings >= 64) return 0; reward >>= halvings; return reward; } int get_bitcoin_varint_len_bytes(uint64_t n) { if (n < 0xFD) { return 1; } else if (n <= 0xFFFF) { return 3; } else if (n <= 0xFFFFFFFF) { return 5; } else { return 9; } } int append_bitcoin_varint_hex(uint64_t n, char *s) { if (n < 0xFD) { // Single byte is sufficient uchar_to_hex(s, n); s[2] = 0; return 2; } else if (n <= 0xFFFF) { // Use 0xFD followed by the number, little-endian order sprintf(s, "fd%04" PRIx16, __builtin_bswap16((uint16_t)n)); return 6; } else if (n <= 0xFFFFFFFF) { // Use 0xFE followed by the number, little-endian order sprintf(s, "fe%08" PRIx32, __builtin_bswap32((uint32_t)n)); return 10; } else { // Use 0xFF followed by the number, little-endian order sprintf(s, "ff%016" PRIx64, __builtin_bswap64(n)); return 18; } } int append_UNum_hex(uint64_t n, char *s) { int count = 0; uint64_t temp = n; bool last_msb = false; do { count++; temp >>= 8; } while (temp != 0); int len = 2; uchar_to_hex(s, count); for (int i = 0; i < count; i++) { uchar_to_hex(s+len, (uint8_t)(n & 0xFF)); last_msb = (n >= 0x80); n >>= 8; len += 2; } // if the last byte is >= 0x80, then we need to inject a zero at the end if (last_msb) { count++; uchar_to_hex(s, count); uchar_to_hex(s+len, 0); len+=2; } s[len] = '\0'; return len; } void hex_to_bin_le(const char *hex, unsigned char *bin) { size_t len = strlen(hex); for (size_t i = 0; i < len>>1; i++) { bin[i] = hex2bin_uchar(&hex[len - ((i+1)<<1)]); } } void hex_to_bin(const char *hex, unsigned char *bin) { size_t len = strlen(hex); for (size_t i = 0; i < len>>1; i++) { bin[i] = hex2bin_uchar(&hex[(i<<1)]); } } void panic_from_thread(int a) { // set panic flag panic_mode = 1; printf("PANIC TRIGGERED - %d\n",a); fflush(stdout); DLOG_FATAL("***********************"); DLOG_FATAL("*** PANIC TRIGGERED ***"); DLOG_FATAL("***********************"); // the main thread needs to pickup on this failure and exit as gracefully as possible. while(1) sleep(1); } void hash2hex(unsigned char *bytes, char *hexString) { const char hexDigits[] = "0123456789abcdef"; for (int i = 0; i < 32; ++i) { hexString[i * 2] = hexDigits[(bytes[i] >> 4) & 0x0F]; hexString[i * 2 + 1] = hexDigits[bytes[i] & 0x0F]; } hexString[64] = '\0'; } int addr_2_output_script(const char *addr, unsigned char *script, int max_len) { // takes any valid bitcoin address, and converts it to an output script // returns length of script written, or 0 on failure // NOTE: This is agnostic to testnet vs mainnet addresses! be careful with your networks! int i; size_t al; uint8_t witprog[80]; size_t witprog_len; int witver; const char* hrp = "bc"; al = strlen(addr); if (al < 16) return 0; if (((addr[0] == 'b') && (addr[1] == 'c')) || ((addr[0] == 't') && (addr[1] == 'b'))) { // bitcoin mainnet and testnet BIP 0173 if (addr[0] == 't') { hrp = "tb"; } i = segwit_addr_decode(&witver, witprog, &witprog_len, hrp, addr); if (!i) { return 0; } if (!(((witver == 0) && ((witprog_len == 20) || (witprog_len == 32))) || ((witver == 1) && (witprog_len == 32)))) { // enforcing length restrictions and known witness versions // TODO: Add any new witness version/len combos that are valid return 0; } if (max_len < witprog_len+2) { return 0; } script[0] = (uint8_t)(witver ? (witver + 0x50) : 0); script[1] = (uint8_t)witprog_len; memcpy(script + 2, witprog, witprog_len); return witprog_len + 2; } else { // try P2PKH or P2SH const size_t sz = blkmk_address_to_script(script, max_len, addr); if (sz > INT_MAX) return 0; return sz; } // nothing worked? return 0; } int output_script_2_addr(const unsigned char *script, const int len, char *addr) { int i; int version = 0; int programLen; const unsigned char *ptr; addr[0] = 0; unsigned char temp[32]; unsigned char digest[32]; size_t sz; if (!len) { return 0; } if (script[0] == 0x6A) { i = sprintf(addr, "OP_RETURN"); return i; } if ((script[0] == 0xA9) || (script[0] == 0x76)) { // P2SH / P2PKH if (script[0] == 0xA9) { version = 5; ptr = &script[2]; if (len != 23) return 0; } else { version = 0; ptr = &script[3]; if (len != 25) return 0; } temp[0] = version; memcpy(&temp[1], ptr, 20); double_sha256(digest, temp, 21); memcpy(&temp[21], digest, 4); sz = 255; if (!b58enc(addr, &sz, temp, 25)) { i = sprintf(addr, "UNKNOWN"); return i; } return sz; } if ((script[0] == 0) || (script[0] == 0x51)) { // segwit/taproot if (len < 4 || len > 42) { i = sprintf(addr, "UNKNOWN"); return i; } if (script[0] != 0x00 && (script[0] < 0x51 || script[0] > 0x60)) { i = sprintf(addr, "UNKNOWN"); return i; } if (script[1] < 0x02 || script[1] > 0x28) { i = sprintf(addr, "UNKNOWN"); return i; } version = (script[0] == 0x00) ? 0 : script[0] - 0x50; programLen = script[1]; if (segwit_addr_encode(addr, "bc", version, &script[2], programLen) != 1) { i = sprintf(addr, "UNKNOWN"); return i; } return strlen(addr); } i = sprintf(addr, "UNKNOWN"); return i; } static const unsigned char b64d[] = { 66,66,66,66,66,66,66,66,66,66,64,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,62,66,66,66,63,52,53,54,55,56,57,58,59,60,61,66,66,66,65,66,66,66, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,66,66,66,66,66,66,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66, 66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66, 66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66 }; int base64_decode(const char *in_c, size_t inLen, unsigned char *out, size_t *outLen) { const unsigned char *in = (const unsigned char *)in_c; const unsigned char * const end = in + inLen; char iter = 0; uint32_t buf = 0; size_t len = 0; while (in < end) { unsigned char c = b64d[*in++]; switch (c) { case 64: continue; case 66: return 1; case 65: in = end; continue; default: buf = buf << 6 | c; iter++; if (iter == 4) { if ((len += 3) > *outLen) return 2; *(out++) = (buf >> 16) & 255; *(out++) = (buf >> 8) & 255; *(out++) = buf & 255; buf = 0; iter = 0; } } } if (iter == 3) { if ((len += 2) > *outLen) return 3; *(out++) = (buf >> 10) & 255; *(out++) = (buf >> 2) & 255; } else if (iter == 2) { if (++len > *outLen) return 4; *(out++) = (buf >> 4) & 255; } *outLen = len; return 0; } bool strncpy_workerchars(char *out, const char *in, size_t maxlen) { // copy a string from in to out, stripping out unwanted characters // copy a max of maxlen chars from in to out // in could technically be longer than maxlen if it has a bunch of unwanted chars int i=0; int j=0; char c; if (in == NULL || out == NULL || maxlen == 0) { return false; } out[0] = 0; while((in[i] != 0) && (maxlen > 1)) { c = in[i]; if ((isalnum(c)) || (c == '.') || (c == '-') || (c == '_') || (c == '=') || (c == '@') || (c == ',')) { out[j] = c; j++; maxlen--; } i++; } out[j] = 0; return true; } bool strncpy_uachars(char *out, const char *in, size_t maxlen) { // copy a string from in to out, stripping out unwanted characters // copy a max of maxlen chars from in to out // in could technically be longer than maxlen if it has a bunch of unwanted chars int i=0; int j=0; char c; if (in == NULL || out == NULL || maxlen == 0) { return false; } out[0] = 0; while((in[i] != 0) && (maxlen > 1)) { c = in[i]; if ((isalnum(c)) || (c == '.') || (c == '-') || (c == '_') || (c == '=') || (c == '@') || (c == ',') || (c == ' ') || (c == '|') || (c == '/') || (c == '|') || (c == ':') || (c == '<') || (c == '>') || (c == '\'') || (c == ';')) { out[j] = c; j++; maxlen--; } i++; } out[j] = 0; return true; } long double calc_network_difficulty(const char *bits_hex) { // given a share solution in hex, calculate the network difficulty // Postgres code for this (with hex_to_int added function) // (pow(10, ( (29-tpower_val)*2.4082399653118495617099111577959 ) + log( (65535 / tvalue_val) ) ) ) as network_difficulty char tpower[3]; char tvalue[7]; unsigned char tpower_val; unsigned long tvalue_val; char *ep; int i; long double d; signed short s; tpower[0] = bits_hex[0]; tpower[1] = bits_hex[1]; tpower[2] = 0; for(i=0;i<6;i++) tvalue[i] = bits_hex[2+i]; tvalue[6] = 0; tpower_val = (unsigned char)strtoul(tpower, &ep, 16); tvalue_val = strtoul(tvalue, &ep, 16); s = (signed short)29 - (signed short)tpower_val; d = powl(10.0,(double)s*(long double)2.4082399653118495617099111577959 + log10(65535.0 / (double)tvalue_val)); return d; } #define SIPHASH_ROTATE(a, b) ((uint64_t)(((a)<<(b))|((a)>>(64-(b))))) #define SIPHASH_HALF_ROUND(a,b,c,d,e,f) do { \ a += b; \ c += d; \ b = SIPHASH_ROTATE(b, e) ^ a; \ d = SIPHASH_ROTATE(d, f) ^ c; \ a = SIPHASH_ROTATE(a, 32); \ } while(0) #define SIPHASH_DOUBLE_ROUND(v0,v1,v2,v3) do { \ SIPHASH_HALF_ROUND(v0,v1,v2,v3,13,16); \ SIPHASH_HALF_ROUND(v2,v1,v0,v3,17,21); \ SIPHASH_HALF_ROUND(v0,v1,v2,v3,13,16); \ SIPHASH_HALF_ROUND(v2,v1,v0,v3,17,21); \ } while(0) uint64_t datum_siphash(const void *src, uint64_t sz, const unsigned char key[16]) { uint64_t k0 = (*(const uint64_t *)&key[0]); uint64_t k1 = (*(const uint64_t *)&key[8]); uint64_t b = sz<<56; const uint64_t *in = src; uint64_t v0 = k0 ^ 0x736f6d6570736575ULL; uint64_t v1 = k1 ^ 0x646f72616e646f6dULL; uint64_t v2 = k0 ^ 0x6c7967656e657261ULL; uint64_t v3 = k1 ^ 0x7465646279746573ULL; uint64_t mi,t; uint8_t *pt; const uint8_t *m; while (sz >= 8) { mi = *in; in += 1; sz -= 8; v3 ^= mi; SIPHASH_DOUBLE_ROUND(v0,v1,v2,v3); v0 ^= mi; } t = 0; pt = (uint8_t *)&t; m = (const uint8_t *)in; switch (sz) { case 7: pt[6] = m[6]; [[fallthrough]]; case 6: pt[5] = m[5]; [[fallthrough]]; case 5: pt[4] = m[4]; [[fallthrough]]; case 4: pt[3] = m[3]; [[fallthrough]]; case 3: pt[2] = m[2]; [[fallthrough]]; case 2: pt[1] = m[1]; [[fallthrough]]; case 1: pt[0] = m[0]; [[fallthrough]]; default: break; } b |= t; v3 ^= b; SIPHASH_DOUBLE_ROUND(v0,v1,v2,v3); v0 ^= b; v2 ^= 0xff; SIPHASH_DOUBLE_ROUND(v0,v1,v2,v3); SIPHASH_DOUBLE_ROUND(v0,v1,v2,v3); return (v0 ^ v1) ^ (v2 ^ v3); } uint64_t datum_siphash_mod8(const void *src, uint64_t sz, const unsigned char key[16]) { // use if size is guaranteed to be mod 8 = 0 // should save a few cycles uint64_t k0 = (*(const uint64_t *)&key[0]); uint64_t k1 = (*(const uint64_t *)&key[8]); uint64_t b = sz<<56; const uint64_t *in = src; uint64_t v0 = k0 ^ 0x736f6d6570736575ULL; uint64_t v1 = k1 ^ 0x646f72616e646f6dULL; uint64_t v2 = k0 ^ 0x6c7967656e657261ULL; uint64_t v3 = k1 ^ 0x7465646279746573ULL; uint64_t mi; while (sz >= 8) { mi = *in; in += 1; sz -= 8; v3 ^= mi; SIPHASH_DOUBLE_ROUND(v0,v1,v2,v3); v0 ^= mi; } v3 ^= b; SIPHASH_DOUBLE_ROUND(v0,v1,v2,v3); v0 ^= b; v2 ^= 0xff; SIPHASH_DOUBLE_ROUND(v0,v1,v2,v3); SIPHASH_DOUBLE_ROUND(v0,v1,v2,v3); return (v0 ^ v1) ^ (v2 ^ v3); } // Uses a fixed-size buffer; positive only; digits only // Returns UINT64_MAX on failure uint64_t datum_atoi_strict_u64(const char * const s, const size_t size) { if (!size) return UINT64_MAX; assert(s); uint64_t ret = 0; for (size_t i = 0; i < size; ++i) { if (s[i] < '0' || s[i] > '9') return UINT64_MAX; int digit = s[i] - '0'; if (ret > (UINT64_MAX - digit) / 10) return UINT64_MAX; ret = (ret * 10) + digit; } return ret; } // Uses a fixed-size buffer; positive only; digits only // Returns -1 on failure int datum_atoi_strict(const char * const s, const size_t size) { const uint64_t ret = datum_atoi_strict_u64(s, size); return (ret == UINT64_MAX || ret > INT_MAX) ? -1 : ret; } // Currently accepts 0 and 1 only, but may add more later // Returns true if valid, actual value in *out bool datum_str_to_bool_strict(const char * const s, bool * const out) { if (0 == strcmp(s, "0")) { *out = false; return true; } else if (0 == strcmp(s, "1")) { *out = true; return true; } return false; } char **datum_deepcopy_charpp(const char * const * const p) { size_t sz = sizeof(char*), n = 0; for (const char * const *p2 = p; *p2; ++p2) { sz += sizeof(char*) + strlen(*p2) + 1; ++n; } char **out = malloc(sz); char *p3 = (void*)(&out[n + 1]); out[n] = NULL; for (size_t i = 0; i < n; ++i) { const size_t item_sz = strlen(p[i]) + 1; memcpy(p3, p[i], item_sz); out[i] = p3; p3 += item_sz; } assert(p3 - (char*)out == sz); return out; } void datum_reexec() { // FIXME: kill other threads (except logging?) before closing fds DIR * const D = opendir("/proc/self/fd"); if (D) { for (struct dirent *ent; (ent = readdir(D)) != NULL; ) { const int fd = datum_atoi_strict(ent->d_name, strlen(ent->d_name)); if (fd < 3) continue; fcntl(fd, F_SETFD, FD_CLOEXEC); } closedir(D); } else { DLOG_ERROR("%s: Failed to close files, this could cause issues! (Is /proc mounted?)", __func__); } execv((void*)datum_argv[0], (void*)datum_argv); // execv shouldn't return! DLOG_FATAL("Failed to restart! We're too deep in to recover!"); abort(); } bool datum_secure_strequals(const char *secret, const size_t secret_len, const char *guess) { const size_t guess_len = strlen(guess); size_t acc = secret_len ^ guess_len; for (size_t i = 0; i < guess_len; ++i) { acc |= ((size_t)guess[i]) ^ ((size_t)secret[i % guess_len]); } return !acc; }