forked from Ocean/datum_gateway
812 lines
20 KiB
C
812 lines
20 KiB
C
/*
|
|
*
|
|
* 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 <assert.h>
|
|
#include <sys/types.h>
|
|
#include <dirent.h>
|
|
#include <fcntl.h>
|
|
#include <sodium.h>
|
|
#include <stdio.h>
|
|
#include <stdarg.h>
|
|
#include <stdlib.h>
|
|
#include <time.h>
|
|
#include <unistd.h>
|
|
#include <math.h>
|
|
#include <stdbool.h>
|
|
#include <ctype.h>
|
|
#include <string.h>
|
|
#include <stddef.h>
|
|
#include <stdint.h>
|
|
#include <sys/time.h>
|
|
#include <inttypes.h>
|
|
#include <unistd.h>
|
|
|
|
#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;
|
|
}
|