Copied TCP impl from other project, basic Block implementation, randomx pow, signing via secp256k1

This commit is contained in:
2026-03-29 17:18:23 +02:00
commit 57bfe61c13
26 changed files with 1775 additions and 0 deletions

163
src/block/block.c Normal file
View File

@@ -0,0 +1,163 @@
#include <block/block.h>
#include <stdlib.h>
block_t* Block_Create() {
block_t* block = (block_t*)malloc(sizeof(block_t));
if (!block) {
return NULL;
}
memset(&block->header, 0, sizeof(block_header_t));
block->transactions = DYNARR_CREATE(signed_transaction_t, 1);
if (!block->transactions) {
free(block);
return NULL;
}
return block;
}
void Block_CalculateHash(const block_t* block, uint8_t* outHash) {
if (!block || !outHash || !block->transactions || DynArr_size(block->transactions) <= 0) {
return;
}
// Merkle root TODO
// Flatten the block header and transactions into a single buffer for hashing (assume that txs are verified - usually on receive)
uint8_t buffer[sizeof(block_header_t) + (DynArr_size(block->transactions) * DynArr_elemSize(block->transactions))];
memcpy(buffer, &block->header, sizeof(block_header_t));
for (size_t i = 0; i < DynArr_size(block->transactions); i++) {
void* txPtr = (char*)DynArr_at(block->transactions, i);
memcpy(buffer + sizeof(block_header_t) + (i * DynArr_elemSize(block->transactions)), txPtr, DynArr_elemSize(block->transactions));
}
SHA256((const unsigned char*)buffer, sizeof(buffer), outHash);
SHA256(outHash, 32, outHash); // Double-Hash
}
void Block_CalculateRandomXHash(const block_t* block, uint8_t* outHash) {
if (!block || !outHash || !block->transactions || DynArr_size(block->transactions) <= 0) {
return;
}
// Merkle root TODO
// Flatten the block header and transactions into a single buffer for hashing (assume that txs are verified - usually on receive)
uint8_t buffer[sizeof(block_header_t) + (DynArr_size(block->transactions) * DynArr_elemSize(block->transactions))];
memcpy(buffer, &block->header, sizeof(block_header_t));
for (size_t i = 0; i < DynArr_size(block->transactions); i++) {
void* txPtr = (char*)DynArr_at(block->transactions, i);
memcpy(buffer + sizeof(block_header_t) + (i * DynArr_elemSize(block->transactions)), txPtr, DynArr_elemSize(block->transactions));
}
RandomX_CalculateHash(buffer, sizeof(buffer), outHash);
}
void Block_AddTransaction(block_t* block, signed_transaction_t* tx) {
if (!block || !tx || !block->transactions) {
return;
}
DynArr_push_back(block->transactions, tx);
}
void Block_RemoveTransaction(block_t* block, uint8_t* txHash) {
if (!block || !txHash || !block->transactions) {
return;
}
for (size_t i = 0; i < DynArr_size(block->transactions); i++) {
signed_transaction_t* currentTx = (signed_transaction_t*)DynArr_at(block->transactions, i);
if (memcmp(currentTx->signature.txHash, txHash, 32) == 0) {
DynArr_remove(block->transactions, i);
return;
}
}
}
static int Uint256_CompareBE(const uint8_t a[32], const uint8_t b[32]) {
for (int i = 0; i < 32; ++i) {
if (a[i] < b[i]) return -1;
if (a[i] > b[i]) return 1;
}
return 0;
}
static bool DecodeCompactTarget(uint32_t nBits, uint8_t target[32]) {
memset(target, 0, 32);
uint32_t exponent = nBits >> 24;
uint32_t mantissa = nBits & 0x007fffff; // ignore sign bit for now
bool negative = (nBits & 0x00800000) != 0;
if (negative || mantissa == 0) {
return false;
}
// Compute: target = mantissa * 256^(exponent - 3)
if (exponent <= 3) {
mantissa >>= 8 * (3 - exponent);
target[29] = (mantissa >> 16) & 0xff;
target[30] = (mantissa >> 8) & 0xff;
target[31] = mantissa & 0xff;
} else {
uint32_t byte_index = exponent - 3; // number of zero-bytes appended on right
if (byte_index > 29) {
return false; // overflow 256 bits
}
target[32 - byte_index - 3] = (mantissa >> 16) & 0xff;
target[32 - byte_index - 2] = (mantissa >> 8) & 0xff;
target[32 - byte_index - 1] = mantissa & 0xff;
}
return true;
}
bool Block_HasValidProofOfWork(const block_t* block) {
if (!block) {
return false;
}
uint8_t target[32];
if (!DecodeCompactTarget(block->header.difficultyTarget, target)) {
return false;
}
uint8_t hash[32];
Block_CalculateRandomXHash(block, hash);
return Uint256_CompareBE(hash, target) <= 0;
}
bool Block_AllTransactionsValid(const block_t* block) {
if (!block || !block->transactions) {
return false;
}
bool hasCoinbase = false;
for (size_t i = 0; i < DynArr_size(block->transactions); i++) {
signed_transaction_t* tx = (signed_transaction_t*)DynArr_at(block->transactions, i);
if (!Transaction_Verify(tx)) {
return false;
}
if (Address_IsCoinbase(tx->transaction.senderAddress)) {
if (hasCoinbase) {
return false; // More than one coinbase transaction
}
hasCoinbase = true;
}
}
return true && hasCoinbase && DynArr_size(block->transactions) > 0; // Every block must have at least one transaction (the coinbase)
}
void Block_Destroy(block_t* block) {
if (!block) return;
DynArr_destroy(block->transactions);
free(block);
}

53
src/block/chain.c Normal file
View File

@@ -0,0 +1,53 @@
#include <block/chain.h>
blockchain_t* Chain_Create() {
blockchain_t* ptr = (blockchain_t*)malloc(sizeof(blockchain_t));
if (!ptr) {
return NULL;
}
ptr->blocks = DYNARR_CREATE(block_t, 1);
ptr->size = 0;
return ptr;
}
void Chain_Destroy(blockchain_t* chain) {
if (chain) {
if (chain->blocks) {
DynArr_destroy(chain->blocks);
}
free(chain);
}
}
bool Chain_AddBlock(blockchain_t* chain, block_t* block) {
if (chain && block && chain->blocks) {
DynArr_push_back(chain->blocks, block);
return true;
}
return false;
}
block_t* Chain_GetBlock(blockchain_t* chain, size_t index) {
if (chain) {
return DynArr_at(chain->blocks, index);
}
return NULL;
}
size_t Chain_Size(blockchain_t* chain) {
if (chain) {
return DynArr_size(chain->blocks);
}
return 0;
}
bool Chain_IsValid(blockchain_t* chain) {
if (!chain || !chain->blocks) {
return false;
}
// Add validation logic here
return true;
}

76
src/block/transaction.c Normal file
View File

@@ -0,0 +1,76 @@
#include <block/transaction.h>
#include <string.h>
void Transaction_CalculateHash(const signed_transaction_t* tx, uint8_t* outHash) {
if (!tx || !outHash) {
return;
}
uint8_t buffer[sizeof(transaction_t)];
memcpy(buffer, &tx->transaction, sizeof(transaction_t));
SHA256(buffer, sizeof(buffer), outHash);
SHA256(outHash, 32, outHash); // Double-Hash
}
void Transaction_Sign(signed_transaction_t* tx, const uint8_t* privateKey) {
if (!tx || !privateKey) {
return;
}
Transaction_CalculateHash(tx, tx->signature.txHash);
Crypto_SignData(
(const uint8_t*)&tx->transaction,
sizeof(transaction_t),
privateKey,
tx->signature.signature
);
}
bool Transaction_Verify(const signed_transaction_t* tx) {
if (!tx) {
return false;
}
if (Address_IsCoinbase(tx->transaction.senderAddress)) {
// Coinbase transactions are valid if the signature is correct for the block (handled in Block_Verify)
return true;
}
uint8_t computeAddress[32];
SHA256(tx->transaction.compressedPublicKey, 33, computeAddress); // Address is hash of public key
if (memcmp(computeAddress, tx->transaction.senderAddress, 32) != 0) {
return false; // Sender address does not match public key
}
if (tx->transaction.amount == 0) {
return false; // Zero-amount transactions are not valid
}
if (tx->transaction.fee > tx->transaction.amount) {
return false; // Fee cannot exceed amount
}
if (tx->transaction.version != 1) {
return false; // Unsupported version
}
if (Address_IsCoinbase(tx->transaction.recipientAddress)) {
return false; // Cannot send to coinbase address
}
uint8_t txHash[32];
Transaction_CalculateHash(tx, txHash);
if (memcmp(txHash, tx->signature.txHash, 32) != 0) {
return false; // Hash does not match signature hash
}
// If all checks pass, verify the signature
return Crypto_VerifySignature(
(const uint8_t*)&tx->transaction,
sizeof(transaction_t),
tx->signature.signature,
tx->transaction.compressedPublicKey
);
}

77
src/crypto/crypto.c Normal file
View File

@@ -0,0 +1,77 @@
#include <crypto/crypto.h>
#include <string.h>
static bool crypto_hash_to_32(const uint8_t* data, size_t len, uint8_t out32[32]) {
if (!data || !out32) {
return false;
}
return SHA256(data, len, out32) != NULL;
}
bool Crypto_VerifySignature(const uint8_t* data, size_t len, const uint8_t* signature, const uint8_t* publicKey) {
if (!data || !signature || !publicKey) {
return false;
}
uint8_t digest[32];
if (!crypto_hash_to_32(data, len, digest)) {
return false;
}
secp256k1_context* ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY);
if (!ctx) {
return false;
}
secp256k1_pubkey pubkey;
secp256k1_ecdsa_signature sig;
const int pub_ok = secp256k1_ec_pubkey_parse(ctx, &pubkey, publicKey, 33);
const int sig_ok = secp256k1_ecdsa_signature_parse_compact(ctx, &sig, signature);
const int verified = (pub_ok && sig_ok) ? secp256k1_ecdsa_verify(ctx, &sig, digest, &pubkey) : 0;
secp256k1_context_destroy(ctx);
return verified == 1;
}
void Crypto_SignData(const uint8_t* data, size_t len, const uint8_t* privateKey, uint8_t* outSignature) {
if (!data || !privateKey || !outSignature) {
return;
}
uint8_t digest[32];
if (!crypto_hash_to_32(data, len, digest)) {
memset(outSignature, 0, 64);
return;
}
secp256k1_context* ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN);
if (!ctx) {
memset(outSignature, 0, 64);
return;
}
if (!secp256k1_ec_seckey_verify(ctx, privateKey)) {
memset(outSignature, 0, 64);
secp256k1_context_destroy(ctx);
return;
}
secp256k1_ecdsa_signature sig;
const int sign_ok = secp256k1_ecdsa_sign(
ctx,
&sig,
digest,
privateKey,
secp256k1_nonce_function_default,
NULL
);
if (!sign_ok) {
memset(outSignature, 0, 64);
secp256k1_context_destroy(ctx);
return;
}
secp256k1_ecdsa_signature_serialize_compact(ctx, outSignature, &sig);
secp256k1_context_destroy(ctx);
}

175
src/dynarr.c Normal file
View File

@@ -0,0 +1,175 @@
#include <dynarr.h>
DynArr* DynArr_create(size_t elemSize, size_t capacity) {
DynArr* p = (DynArr*)malloc(sizeof(DynArr));
if (!p) return NULL;
p->elemSize = elemSize;
p->capacity = capacity;
p->data = malloc(elemSize * capacity);
if (!p->data) {
free(p);
return NULL;
}
p->size = 0;
return p;
}
// Reserve n blocks in arary; New size will be n, NOT size + n; Reserving less memory that current will fail, use prune instead.
void DynArr_reserve(DynArr* p, size_t n) {
if (n <= p->capacity) {
printf("reserve ignored; attempted to reserve less or equal to current capacity\n");
return;
}
if (n > DYNARR_MAX_CAPACITY) {
printf("reserve ignored; attempted to reserve over 32 bits\n");
return;
}
void* new_data = realloc(p->data, n * p->elemSize);
if (!new_data) {
printf("reserve failed\n");
exit(1);
}
p->data = new_data;
p->capacity = n;
}
// Push data into a new block at the end of the array; If value is NULL, the new block will be zeroed.
void* DynArr_push_back(DynArr* p, void* value) {
//if (value == NULL) {
// printf("push_back ignored; value is null");
// return NULL;
//}
if (p->size >= p->capacity) {
size_t new_cap = (p->capacity == 0) ? 1 : p->capacity * 2;
if (new_cap < p->capacity || new_cap > DYNARR_MAX_CAPACITY) {
printf("push_back ignored; capacity overflow\n");
return NULL;
}
void* new_data = realloc(p->data, new_cap * p->elemSize);
if (!new_data) {
printf("push failed\n");
exit(1);
}
p->capacity = new_cap;
p->data = new_data;
}
void* dst = (void*)((char*)p->data + (p->size * p->elemSize));
if (value == NULL) {
memset(dst, 0, p->elemSize); // Handle NULL value.
} else {
memcpy((char*)dst, value, p->elemSize);
}
p->size++;
return dst;
}
// Remove the last block in the array.
void DynArr_pop_back(DynArr* p) {
if (p->size == 0) {
printf("pop_back ignored; size is 0\n");
return;
}
p->size--; // Will automatically overwrite that memory naturally
}
// Remove first block from array.
void DynArr_pop_front(DynArr* p) {
if (p->size == 0) {
printf("pop_front ignored; size is 0\n");
return;
}
memmove(
(char*)p->data,
(char*)p->data + p->elemSize,
(p->size - 1) * p->elemSize
);
p->size--;
}
// Remove index from array. This moves all blocks after the index block.
void DynArr_remove(DynArr* p, size_t index) {
if (index >= p->size) return;
memmove(
(char*)p->data + (index * p->elemSize),
(char*)p->data + (index + 1) * p->elemSize,
(p->size - index - 1) * p->elemSize
);
p->size--;
}
// Erase the array. This will not free unused blocks.
void DynArr_erase(DynArr* p) {
p->size = 0;
}
// Prune and free unused blocks. If pruning to zero, ensure to reserve after.
void DynArr_prune(DynArr* p) {
void* new_data = realloc(p->data, (p->size == 0 ? 1 : p->size) * p->elemSize);
if (!new_data) {
printf("pruning failed\n");
exit(1);
}
p->data = new_data;
p->capacity = p->size;
}
// Get a pointer to a block by index
void* DynArr_at(DynArr* p, size_t index) {
if (index >= p->size) return NULL;
return (char*)p->data + (index * p->elemSize);
}
// Get the index by block pointer
size_t DynArr_at_ptr(DynArr* p, void* ptr) {
if (!p || !ptr) {
printf("invalid pointer\n");
exit(1);
}
for (size_t i = 0; i < p->size; i++) {
if ((void*)(((char*)p->data) + (i * p->elemSize)) == ptr) {
return i;
}
}
// If for some reason the array has 2^64 elements in it, then fuck it, I guess we'll just crash, I don't care.
return -1;
}
// Get size
size_t DynArr_size(DynArr* p) {
return p->size;
}
// Get element size
size_t DynArr_elemSize(DynArr* p) {
return p->elemSize;
}
// Get capacity
size_t DynArr_capacity(DynArr* p) {
return p->capacity;
}
void DynArr_destroy(DynArr* p) {
if (!p) return;
free(p->data);
free(p);
}

58
src/dynset.c Normal file
View File

@@ -0,0 +1,58 @@
#include <dynset.h>
DynSet* DynSet_Create(size_t elemSize) {
DynSet* set = (DynSet*)malloc(sizeof(DynSet));
if (!set) {
return NULL;
}
set->arr = DynArr_create(elemSize, 1);
if (!set->arr) {
free(set);
return NULL;
}
return set;
}
void DynSet_Destroy(DynSet* set) {
if (set) {
DynArr_destroy(set->arr);
free(set);
}
}
int DynSet_Insert(DynSet* set, const void* element) {
if (DynSet_Contains(set, element)) {
return 0; // Element already exists
}
return DynArr_push_back(set->arr, element) != NULL;
}
int DynSet_Contains(DynSet* set, const void* element) {
size_t size = DynArr_size(set->arr);
for (size_t i = 0; i < size; i++) {
void* current = DynArr_at(set->arr, i);
if (memcmp(current, element, set->arr->elemSize) == 0) {
return 1; // Found
}
}
return 0; // Not found
}
size_t DynSet_Size(DynSet* set) {
return DynArr_size(set->arr);
}
void* DynSet_Get(DynSet* set, size_t index) {
return DynArr_at(set->arr, index);
}
void DynSet_Remove(DynSet* set, const void* element) {
size_t size = DynArr_size(set->arr);
for (size_t i = 0; i < size; i++) {
void* current = DynArr_at(set->arr, i);
if (memcmp(current, element, set->arr->elemSize) == 0) {
DynArr_remove(set->arr, i);
return;
}
}
}

250
src/main.c Normal file
View File

@@ -0,0 +1,250 @@
#include <block/chain.h>
#include <block/transaction.h>
#include <openssl/sha.h>
#include <secp256k1.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <randomx/librx_wrapper.h>
#include <signal.h>
void handle_sigint(int sig) {
printf("Caught signal %d, exiting...\n", sig);
RandomX_Destroy();
exit(0);
}
static double MonotonicSeconds(void) {
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return (double)ts.tv_sec + ((double)ts.tv_nsec / 1000000000.0);
}
static double MeasureRandomXHashrate(void) {
uint8_t input[80] = {0};
uint8_t outHash[32];
uint64_t counter = 0;
const double start = MonotonicSeconds();
double now = start;
do {
memcpy(input, &counter, sizeof(counter));
RandomX_CalculateHash(input, sizeof(input), outHash);
counter++;
now = MonotonicSeconds();
} while ((now - start) < 0.75); // short local benchmark window
const double elapsed = now - start;
if (elapsed <= 0.0 || counter == 0) {
return 0.0;
}
return (double)counter / elapsed;
}
static uint32_t CompactTargetForExpectedHashes(double expectedHashes) {
if (expectedHashes < 1.0) {
expectedHashes = 1.0;
}
// For exponent 0x1f: target = mantissa * 2^(8*(0x1f-3)) = mantissa * 2^224
// So expected hashes ~= 2^256 / target = 2^32 / mantissa.
double mantissaF = 4294967296.0 / expectedHashes;
if (mantissaF < 1.0) {
mantissaF = 1.0;
}
if (mantissaF > 8388607.0) {
mantissaF = 8388607.0; // 0x007fffff
}
const uint32_t mantissa = (uint32_t)mantissaF;
return (0x1fU << 24) | (mantissa & 0x007fffffU);
}
static bool GenerateKeypair(
const secp256k1_context* ctx,
uint8_t outPrivateKey[32],
uint8_t outCompressedPublicKey[33]
) {
if (!ctx || !outPrivateKey || !outCompressedPublicKey) {
return false;
}
secp256k1_pubkey pubkey;
for (size_t i = 0; i < 1024; ++i) {
arc4random_buf(outPrivateKey, 32);
if (!secp256k1_ec_seckey_verify(ctx, outPrivateKey)) {
continue;
}
if (!secp256k1_ec_pubkey_create(ctx, &pubkey, outPrivateKey)) {
continue;
}
size_t serializedLen = 33;
if (!secp256k1_ec_pubkey_serialize(
ctx,
outCompressedPublicKey,
&serializedLen,
&pubkey,
SECP256K1_EC_COMPRESSED
)) {
continue;
}
return serializedLen == 33;
}
return false;
}
static bool MineBlock(block_t* block) {
if (!block) {
return false;
}
for (uint64_t nonce = 0;; ++nonce) {
block->header.nonce = nonce;
if (Block_HasValidProofOfWork(block)) {
return true;
}
if (nonce == UINT64_MAX) {
return false;
}
}
}
int main(void) {
signal(SIGINT, handle_sigint);
// Init RandomX
if (!RandomX_Init("minicoin")) { // TODO: Use a key that is not hardcoded; E.g. hash of the last block, every thousand blocks, etc.
fprintf(stderr, "failed to initialize RandomX\n");
return 1;
}
blockchain_t* chain = Chain_Create();
if (!chain) {
fprintf(stderr, "failed to create chain\n");
return 1;
}
secp256k1_context* secpCtx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);
if (!secpCtx) {
fprintf(stderr, "failed to create secp256k1 context\n");
Chain_Destroy(chain);
return 1;
}
uint8_t senderPrivateKey[32];
uint8_t receiverPrivateKey[32];
uint8_t senderCompressedPublicKey[33];
uint8_t receiverCompressedPublicKey[33];
if (!GenerateKeypair(secpCtx, senderPrivateKey, senderCompressedPublicKey) ||
!GenerateKeypair(secpCtx, receiverPrivateKey, receiverCompressedPublicKey)) {
fprintf(stderr, "failed to generate keypairs\n");
secp256k1_context_destroy(secpCtx);
Chain_Destroy(chain);
return 1;
}
signed_transaction_t tx;
memset(&tx, 0, sizeof(tx));
tx.transaction.version = 1;
tx.transaction.amount = 100;
tx.transaction.fee = 1;
SHA256(senderCompressedPublicKey, 33, tx.transaction.senderAddress);
SHA256(receiverCompressedPublicKey, 33, tx.transaction.recipientAddress);
memcpy(tx.transaction.compressedPublicKey, senderCompressedPublicKey, 33);
Transaction_Sign(&tx, senderPrivateKey);
if (!Transaction_Verify(&tx)) {
fprintf(stderr, "signed transaction did not verify\n");
secp256k1_context_destroy(secpCtx);
Chain_Destroy(chain);
RandomX_Destroy();
return 1;
}
block_t* block = Block_Create();
if (!block) {
fprintf(stderr, "failed to create block\n");
secp256k1_context_destroy(secpCtx);
Chain_Destroy(chain);
RandomX_Destroy();
return 1;
}
block->header.version = 1;
block->header.blockNumber = (uint32_t)Chain_Size(chain);
memset(block->header.prevHash, 0, sizeof(block->header.prevHash));
memset(block->header.merkleRoot, 0, sizeof(block->header.merkleRoot));
block->header.timestamp = (uint64_t)time(NULL);
const double hps = MeasureRandomXHashrate();
const double targetSeconds = 60.0;
const double expectedHashes = (hps > 0.0) ? (hps * targetSeconds) : 65536.0;
block->header.difficultyTarget = CompactTargetForExpectedHashes(expectedHashes);
block->header.nonce = 0;
printf("RandomX benchmark: %.2f H/s, target %.0fs, nBits=0x%08x\n",
hps,
targetSeconds,
block->header.difficultyTarget);
Block_AddTransaction(block, &tx);
printf("Added transaction to block: sender %02x... -> recipient %02x..., amount %lu, fee %lu\n",
tx.transaction.senderAddress[0], tx.transaction.senderAddress[31],
tx.transaction.recipientAddress[0], tx.transaction.recipientAddress[31],
tx.transaction.amount, tx.transaction.fee);
if (!MineBlock(block)) {
fprintf(stderr, "failed to mine block within nonce range\n");
Block_Destroy(block);
secp256k1_context_destroy(secpCtx);
Chain_Destroy(chain);
RandomX_Destroy();
return 1;
}
if (!Chain_AddBlock(chain, block)) {
fprintf(stderr, "failed to append block to chain\n");
Block_Destroy(block);
secp256k1_context_destroy(secpCtx);
Chain_Destroy(chain);
RandomX_Destroy();
return 1;
}
printf("Mined block %u with nonce %llu and chain size %zu\n",
block->header.blockNumber,
(unsigned long long)block->header.nonce,
Chain_Size(chain));
printf("Block hash (SHA256): ");
uint8_t blockHash[32];
Block_CalculateHash(block, blockHash);
for (size_t i = 0; i < 32; ++i) {
printf("%02x", blockHash[i]);
}
printf("\nBlock hash (RandomX): ");
uint8_t randomXHash[32];
Block_CalculateRandomXHash(block, randomXHash);
for (size_t i = 0; i < 32; ++i) {
printf("%02x", randomXHash[i]);
}
printf("\n");
// Chain currently stores a copy of block_t that references the same tx array pointer,
// so we do not destroy `block` here to avoid invalidating chain data.
secp256k1_context_destroy(secpCtx);
Chain_Destroy(chain);
return 0;
}

41
src/numgen.c Normal file
View File

@@ -0,0 +1,41 @@
#include <numgen.h>
unsigned char random_byte(void) {
return (unsigned char)(rand() % 256);
}
uint16_t random_two_byte(void) {
uint16_t x;
unsigned char bytes[2];
for (unsigned char i = 0; i < 2; i++) {
bytes[i] = random_byte();
}
memcpy(&x, bytes, sizeof(x));
return x;
}
uint32_t random_four_byte(void) {
uint32_t x;
unsigned char bytes[4];
for (unsigned char i = 0; i < 4; i++) {
bytes[i] = random_byte();
}
memcpy(&x, bytes, sizeof(x));
return x;
}
uint64_t random_eight_byte(void) {
uint64_t x;
unsigned char bytes[8];
for (unsigned char i = 0; i < 8; i++) {
bytes[i] = random_byte();
}
memcpy(&x, bytes, sizeof(x));
return x;
}

View File

@@ -0,0 +1,73 @@
#include <randomx/librx_wrapper.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
static randomx_cache* rxCache = NULL;
static randomx_dataset* rxDataset = NULL;
static randomx_vm* rxVm = NULL;
bool RandomX_Init(const char* key) {
if (!key || rxCache || rxVm) {
return false;
}
const randomx_flags baseFlags = randomx_get_flags() | RANDOMX_FLAG_JIT;
randomx_flags vmFlags = baseFlags | RANDOMX_FLAG_FULL_MEM;
rxCache = randomx_alloc_cache(baseFlags);
if (!rxCache) {
return false;
}
randomx_init_cache(rxCache, key, strlen(key));
// Prefer full-memory mode. If dataset allocation fails, fall back to light mode.
rxDataset = randomx_alloc_dataset(vmFlags);
if (rxDataset) {
const unsigned long datasetItems = randomx_dataset_item_count();
randomx_init_dataset(rxDataset, rxCache, 0, datasetItems);
rxVm = randomx_create_vm(vmFlags, NULL, rxDataset);
if (rxVm) {
printf("RandomX initialized in full-memory mode\n");
return true;
}
randomx_release_dataset(rxDataset);
rxDataset = NULL;
}
vmFlags = baseFlags;
rxVm = randomx_create_vm(vmFlags, rxCache, NULL);
if (!rxVm) {
randomx_release_cache(rxCache);
rxCache = NULL;
return false;
}
printf("RandomX initialized in light mode\n");
return true;
}
void RandomX_Destroy() {
if (rxVm) {
randomx_destroy_vm(rxVm);
rxVm = NULL;
}
if (rxDataset) {
randomx_release_dataset(rxDataset);
rxDataset = NULL;
}
if (rxCache) {
randomx_release_cache(rxCache);
rxCache = NULL;
}
}
void RandomX_CalculateHash(const uint8_t* input, size_t inputLen, uint8_t* output) {
if (!rxVm || !input || !output) {
return;
}
randomx_calculate_hash(rxVm, input, inputLen, output);
}

317
src/tcpd/tcpserver.c Normal file
View File

@@ -0,0 +1,317 @@
#include <tcpd/tcpserver.h>
TcpServer* TcpServer_Create() {
TcpServer* svr = (TcpServer*)malloc(sizeof(TcpServer));
if (!svr) {
perror("tcpserver - creation failure");
exit(1);
}
svr->sockFd = -1;
svr->svrThread = 0;
svr->on_connect = NULL;
svr->on_data = NULL;
svr->on_disconnect = NULL;
svr->clients = 0;
svr->clientsArrPtr = NULL;
return svr;
}
void TcpServer_Destroy(TcpServer* ptr) {
if (ptr) {
if (ptr->clientsArrPtr) {
for (size_t i = 0; i < ptr->clients; i++) {
if (ptr->clientsArrPtr[i]) {
free(ptr->clientsArrPtr[i]);
}
}
free(ptr->clientsArrPtr);
}
close(ptr->sockFd);
free(ptr);
}
}
void TcpServer_Init(TcpServer* ptr, unsigned short port, const char* addr) {
if (ptr) {
// Create socket
ptr->sockFd = socket(AF_INET, SOCK_STREAM, 0);
if (ptr->sockFd < 0) {
perror("tcpserver - socket");
exit(EXIT_FAILURE);
}
// Allow quick port resue
ptr->opt = 1;
setsockopt(ptr->sockFd, SOL_SOCKET, SO_REUSEADDR, &ptr->opt, sizeof(int));
// Fill address structure
memset(&ptr->addr, 0, sizeof(ptr->addr));
ptr->addr.sin_family = AF_INET;
ptr->addr.sin_port = htons(port);
inet_pton(AF_INET, addr, &ptr->addr.sin_addr);
// Bind
if (bind(ptr->sockFd, (struct sockaddr*)&ptr->addr, sizeof(ptr->addr)) < 0) {
perror("tcpserver - bind");
close(ptr->sockFd);
exit(EXIT_FAILURE);
}
}
}
// Do not call outside of func.
void* TcpServer_clientthreadprocess(void* ptr) {
if (!ptr) {
perror("Client ptr is null!\n");
return NULL;
}
tcpclient_thread_args* args = (tcpclient_thread_args*)ptr;
TcpClient* cli = args->clientPtr;
TcpServer* svr = args->serverPtr;
if (args) {
free(args);
}
while (1) {
memset(cli->dataBuf, 0, MTU); // Reset buffer
ssize_t n = recv(cli->clientFd, cli->dataBuf, MTU, 0);
cli->dataBufLen = n;
if (n == 0) {
break; // Client disconnected
} else if (n > 0) {
if (cli->on_data) {
cli->on_data(cli);
}
}
pthread_testcancel(); // Check for thread death
}
cli->on_disconnect(cli);
// Close on exit
close(cli->clientFd);
// Destroy
TcpClient** arr = svr->clientsArrPtr;
size_t idx = Generic_FindClientInArrayByPtr(arr, cli, svr->clients);
if (idx != SIZE_MAX) {
if (arr[idx]) {
free(arr[idx]);
arr[idx] = NULL;
}
} else {
perror("tcpserver (client thread) - something already freed the client!");
}
//free(ptr);
return NULL;
}
// Do not call outside of func.
void* TcpServer_threadprocess(void* ptr) {
if (!ptr) {
perror("Client ptr is null!\n");
return NULL;
}
TcpServer* svr = (TcpServer*)ptr;
while (1) {
TcpClient tempclient;
socklen_t clientsize = sizeof(tempclient.clientAddr);
int client = accept(svr->sockFd, (struct sockaddr*)&tempclient.clientAddr, &clientsize);
if (client >= 0) {
tempclient.clientFd = client;
tempclient.on_data = svr->on_data;
tempclient.on_disconnect = svr->on_disconnect;
// I'm lazy, so I'm just copying the data for now (I should probably make this a better way)
TcpClient* heapCli = (TcpClient*)malloc(sizeof(TcpClient));
if (!heapCli) {
perror("tcpserver - client failed to allocate");
exit(EXIT_FAILURE); // Wtf just happened???
}
heapCli->clientAddr = tempclient.clientAddr;
heapCli->clientFd = tempclient.clientFd;
heapCli->on_data = tempclient.on_data;
heapCli->on_disconnect = tempclient.on_disconnect;
heapCli->clientId = random_four_byte();
heapCli->dataBufLen = 0;
size_t i;
for (i = 0; i < svr->clients; i++) {
if (svr->clientsArrPtr[i] == NULL) {
// Make use of that space
svr->clientsArrPtr[i] = heapCli; // We have now transfered the ownership :)
break;
}
}
if (i == svr->clients) {
// Not found
// RST; Thread doesn't exist yet
struct linger so_linger;
so_linger.l_onoff = 1;
so_linger.l_linger = 0;
setsockopt(heapCli->clientFd, SOL_SOCKET, SO_LINGER, &so_linger, sizeof(so_linger));
close(heapCli->clientFd);
free(heapCli);
heapCli = NULL;
//svr->clientsArrPtr[i] = NULL;
continue;
}
tcpclient_thread_args* arg = (tcpclient_thread_args*)malloc(sizeof(tcpclient_thread_args));
arg->clientPtr = heapCli;
arg->serverPtr = svr;
if (svr->on_connect) {
svr->on_connect(heapCli);
}
pthread_create(&heapCli->clientThread, NULL, TcpServer_clientthreadprocess, arg);
pthread_detach(heapCli->clientThread); // May not work :(
}
pthread_testcancel(); // Check for thread death
}
return NULL;
}
void TcpServer_Start(TcpServer* ptr, int maxcons) {
if (ptr) {
if (listen(ptr->sockFd, maxcons) < 0) {
perror("tcpserver - listen");
close(ptr->sockFd);
exit(EXIT_FAILURE);
}
ptr->clients = maxcons;
ptr->clientsArrPtr = (TcpClient**)malloc(sizeof(TcpClient*) * maxcons);
if (!ptr->clientsArrPtr) {
perror("tcpserver - allocation of client space fatally errored");
exit(EXIT_FAILURE);
}
// Fucking null out everything
for (int i = 0; i < maxcons; i++) {
ptr->clientsArrPtr[i] = NULL;
}
}
// Spawn server thread
pthread_create(&ptr->svrThread, NULL, TcpServer_threadprocess, ptr);
}
void TcpServer_Stop(TcpServer* ptr) {
if (ptr && ptr->svrThread != 0) {
// Stop server
pthread_cancel(ptr->svrThread);
pthread_join(ptr->svrThread, NULL);
// Disconnect clients
for (size_t i = 0; i < ptr->clients; i++) {
TcpClient* cliPtr = ptr->clientsArrPtr[i];
if (cliPtr) {
close(cliPtr->clientFd);
pthread_cancel(cliPtr->clientThread);
}
}
ptr->svrThread = 0;
}
}
void TcpServer_Send(TcpServer* ptr, TcpClient* cli, void* data, size_t len) {
if (ptr && cli && data && len > 0) {
size_t sent = 0;
while (sent < len) {
// Ensure that all data is sent. TCP can split sends.
ssize_t n = send(cli->clientFd, (unsigned char*)data + sent, len - sent, 0);
if (n < 0) {
perror("tcpserver - send error");
break;
}
sent += n;
}
}
}
void Generic_SendSocket(int sock, void* data, size_t len) {
if (sock > 0 && data && len > 0) {
size_t sent = 0;
while (sent < len) {
ssize_t n = send(sock, (unsigned char*)data + sent, len - sent, 0);
if (n < 0) {
perror("generic - send socket error");
break;
}
sent += n;
}
}
}
void TcpServer_Disconnect(TcpServer* ptr, TcpClient* cli) {
if (ptr && cli) {
close(cli->clientFd);
pthread_cancel(cli->clientThread);
size_t idx = Generic_FindClientInArrayByPtr(ptr->clientsArrPtr, cli, ptr->clients);
if (idx != SIZE_MAX) {
if (ptr->clientsArrPtr[idx]) {
free(ptr->clientsArrPtr[idx]);
}
ptr->clientsArrPtr[idx] = NULL;
} else {
perror("tcpserver - didn't find client to disconnect in array!");
}
}
}
void TcpServer_KillClient(TcpServer* ptr, TcpClient* cli) {
if (ptr && cli) {
// RST the connection
struct linger so_linger;
so_linger.l_onoff = 1;
so_linger.l_linger = 0;
setsockopt(cli->clientFd, SOL_SOCKET, SO_LINGER, &so_linger, sizeof(so_linger));
close(cli->clientFd);
pthread_cancel(cli->clientThread);
size_t idx = Generic_FindClientInArrayByPtr(ptr->clientsArrPtr, cli, ptr->clients);
if (idx != SIZE_MAX) {
if (ptr->clientsArrPtr[idx]) {
free(ptr->clientsArrPtr[idx]);
}
ptr->clientsArrPtr[idx] = NULL;
} else {
perror("tcpserver - didn't find client to kill in array!");
}
}
}
size_t Generic_FindClientInArrayByPtr(TcpClient** arr, TcpClient* ptr, size_t len) {
for (size_t i = 0; i < len; i++) {
if (arr[i] == ptr) {
return i;
}
}
return SIZE_MAX; // Returns max unsigned, likely improbable to be correct
}