Start doing TCP networking

This commit is contained in:
2026-04-15 21:38:30 +02:00
parent 258ca9474f
commit d631eb190d
10 changed files with 190 additions and 54 deletions

View File

@@ -1,11 +1,15 @@
Move to a GPU algo. RandomX is a good candidate, but CPU mining is not that attractive to anyone but people who actually want to support the project.
It won't incentivize people who want to profit, which let's be fair, is the majority of miners.
TODO:
Implement Horizen's "Reorg Penalty" system to make it harder for the young chain to be attacked by a powerful miner.
Make transactions private. A bit more work, but it's a challenge worth taking on.
I want to make an "optional privacy" system, where the TX can be public or private. Of course private TXs need more bytes, so the fees (although low) will be higher for them.
I need to figure out a way to make the privacy work without a UTXO system, and instead, with a "Balance Sheet" approach.
Move the Networking Code to support win32 as well, as I'm just doing POSIX right now
DONE:
I want to move away from the Monero emission. I want to do something a bit radical for cryptocurrency, but I feel like it's necessary to make it more like money:
a constant inflation rate of 1.5% per year. It's lower than fiat (USD is ~2.8% per year), and it additionally doesn't fluctuate during crisis. It's constant.
Move to a GPU algo. RandomX is a good candidate, but CPU mining is not that attractive to anyone but people who actually want to support the project.
Sadly, CPUs won't incentivize people who want to profit, which let's be fair, is the majority of miners.

View File

@@ -7,6 +7,11 @@
#include <block/chain.h>
#include <block/block.h>
// Nets
#define MAX_CONS 32 // Some baseline for now
#define LISTEN_PORT 9393
// Economics
#define DECIMALS 1000000000000ULL
#define DIFFICULTY_ADJUSTMENT_INTERVAL 3840 // Every 3840 blocks (roughly every 4 days with a 90 second block time)
// Max adjustment per is x2. So if blocks are coming in too fast, the difficulty will at most double every 24 hours, and vice versa if they're coming in too slow.

29
include/nets/net_node.h Normal file
View File

@@ -0,0 +1,29 @@
#ifndef NET_NODE_H
#define NET_NODE_H
#ifndef _WIN32
// POSIX
#include <tcpd/tcpconnection.h>
#include <tcpd/tcpserver.h>
#endif
#include <dynarr.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
#include <constants.h>
typedef struct {
tcp_server_t* server;
// TODO: Add the list of clients as well
} net_node_t;
net_node_t* Node_Create();
void Node_Destroy(net_node_t* node);
// Callback logic
void Node_Server_OnConnect(tcp_connection_t* client);
void Node_Server_OnData(tcp_connection_t* client);
void Node_Server_OnDisconnect(tcp_connection_t* client);
#endif

View File

@@ -11,19 +11,19 @@
#define MTU 1500
struct TcpClient {
struct tcp_connection_t {
int clientFd;
struct sockaddr_in clientAddr;
uint32_t clientId;
unsigned char dataBuf[MTU];
ssize_t dataBufLen;
void (*on_data)(struct TcpClient* client);
void (*on_disconnect)(struct TcpClient* client);
void (*on_data)(struct tcp_connection_t* client);
void (*on_disconnect)(struct tcp_connection_t* client);
pthread_t clientThread;
};
typedef struct TcpClient TcpClient;
typedef struct tcp_connection_t tcp_connection_t;
#endif

View File

@@ -10,7 +10,7 @@
#include <pthread.h>
#include <stdint.h>
#include <tcpd/tcpclient.h>
#include <tcpd/tcpconnection.h>
#include <numgen.h>
#include <dynarr.h>
@@ -20,37 +20,37 @@ typedef struct {
int opt;
// Called before the client thread runs
void (*on_connect)(TcpClient* client);
void (*on_connect)(tcp_connection_t* client);
// Called when data is received
void (*on_data)(TcpClient* client);
void (*on_data)(tcp_connection_t* client);
// Called before the socket and client thread are killed; Do NOT free client manually
void (*on_disconnect)(TcpClient* client);
void (*on_disconnect)(tcp_connection_t* client);
// max clients
size_t clients;
TcpClient** clientsArrPtr;
tcp_connection_t** clientsArrPtr;
pthread_t svrThread;
} TcpServer;
} tcp_server_t;
struct tcpclient_thread_args {
TcpClient* clientPtr;
TcpServer* serverPtr;
tcp_connection_t* clientPtr;
tcp_server_t* serverPtr;
};
typedef struct tcpclient_thread_args tcpclient_thread_args;
TcpServer* TcpServer_Create();
void TcpServer_Destroy(TcpServer* ptr);
tcp_server_t* TcpServer_Create();
void TcpServer_Destroy(tcp_server_t* ptr);
void TcpServer_Init(TcpServer* ptr, unsigned short port, const char* addr);
void TcpServer_Start(TcpServer* ptr, int maxcons);
void TcpServer_Stop(TcpServer* ptr);
void TcpServer_Send(TcpServer* ptr, TcpClient* cli, void* data, size_t len);
void TcpServer_Init(tcp_server_t* ptr, unsigned short port, const char* addr);
void TcpServer_Start(tcp_server_t* ptr, int maxcons);
void TcpServer_Stop(tcp_server_t* ptr);
void TcpServer_Send(tcp_server_t* ptr, tcp_connection_t* cli, void* data, size_t len);
void Generic_SendSocket(int sock, void* data, size_t len);
void TcpServer_Disconnect(TcpServer* ptr, TcpClient* cli);
void TcpServer_KillClient(TcpServer* ptr, TcpClient* cli);
void TcpServer_Disconnect(tcp_server_t* ptr, tcp_connection_t* cli);
void TcpServer_KillClient(tcp_server_t* ptr, tcp_connection_t* cli);
size_t Generic_FindClientInArrayByPtr(TcpClient** arr, TcpClient* ptr, size_t len);
size_t Generic_FindClientInArrayByPtr(tcp_connection_t** arr, tcp_connection_t* ptr, size_t len);
#endif

View File

@@ -455,15 +455,43 @@ bool Chain_LoadFromFile(blockchain_t* chain, const char* dirpath, uint256_t* out
FILE* chainFile = fopen(chainPath, "rb+");
FILE* tableFile = fopen(tablePath, "rb+");
if (!metaFile || !chainFile || !tableFile) {
if (metaFile) fclose(metaFile);
if (chainFile) fclose(chainFile);
if (tableFile) fclose(tableFile);
return false;
}
size_t savedSize = 0;
if (fread(&savedSize, sizeof(size_t), 1, metaFile) != 1) return false;
if (fread(outLastSavedHash, sizeof(uint8_t), 32, metaFile) != 32) return false;
if (fread(outCurrentSupply, sizeof(uint256_t), 1, metaFile) != 1) return false;
if (fread(outDifficultyTarget, sizeof(uint32_t), 1, metaFile) != 1) return false;
if (fread(outCurrentReward, sizeof(uint64_t), 1, metaFile) != 1) return false;
if (fread(&savedSize, sizeof(size_t), 1, metaFile) != 1) {
fclose(metaFile);
fclose(chainFile);
fclose(tableFile);
return false;
}
if (fread(outLastSavedHash, sizeof(uint8_t), 32, metaFile) != 32) {
fclose(metaFile);
fclose(chainFile);
fclose(tableFile);
return false;
}
if (fread(outCurrentSupply, sizeof(uint256_t), 1, metaFile) != 1) {
fclose(metaFile);
fclose(chainFile);
fclose(tableFile);
return false;
}
if (fread(outDifficultyTarget, sizeof(uint32_t), 1, metaFile) != 1) {
fclose(metaFile);
fclose(chainFile);
fclose(tableFile);
return false;
}
if (fread(outCurrentReward, sizeof(uint64_t), 1, metaFile) != 1) {
fclose(metaFile);
fclose(chainFile);
fclose(tableFile);
return false;
}
fclose(metaFile);
// TODO: Might add a flag to allow reading from a point onward, but just rewrite for now
@@ -481,6 +509,8 @@ bool Chain_LoadFromFile(blockchain_t* chain, const char* dirpath, uint256_t* out
}
if (loc.blockNumber != i) {
fclose(chainFile);
fclose(tableFile);
return false; // Mismatch
}
@@ -491,24 +521,30 @@ bool Chain_LoadFromFile(blockchain_t* chain, const char* dirpath, uint256_t* out
return false;
}
block_t* blk = Block_Create();
// Header-only load path: do not allocate per-block transaction arrays.
block_t* blk = (block_t*)calloc(1, sizeof(block_t));
if (!blk) {
fclose(chainFile);
fclose(tableFile);
return false;
}
// Read block header and transactions
if (fread(&blk->header, sizeof(block_header_t), 1, chainFile) != 1) {
fclose(chainFile);
Block_Destroy(blk);
fclose(tableFile);
free(blk);
return false;
}
size_t txSize = 0;
if (fread(&txSize, sizeof(size_t), 1, chainFile) != 1) {
fclose(chainFile);
Block_Destroy(blk);
fclose(tableFile);
free(blk);
return false;
}
(void)txSize;
/*for (size_t j = 0; j < txSize; j++) {
signed_transaction_t tx;
@@ -526,7 +562,7 @@ bool Chain_LoadFromFile(blockchain_t* chain, const char* dirpath, uint256_t* out
if (!DynArr_push_back(chain->blocks, blk)) {
fclose(chainFile);
fclose(tableFile);
Block_Destroy(blk);
free(blk);
return false;
}
chain->size++;
@@ -537,6 +573,8 @@ bool Chain_LoadFromFile(blockchain_t* chain, const char* dirpath, uint256_t* out
}
chain->size = savedSize;
fclose(chainFile);
fclose(tableFile);
// After read, you SHOULD verify chain validity. We're not doing it here since returning false is a bit unclear if the read failed or if the chain is invalid.
return true;

View File

@@ -30,7 +30,7 @@ void Transaction_Sign(signed_transaction_t* tx, const uint8_t* privateKey) {
Transaction_CalculateHash(tx, txHash);
Crypto_SignData(
txHash,
sizeof(transaction_t),
32,
privateKey,
tx->signature.signature
);

View File

@@ -15,6 +15,8 @@
#include <autolykos2/autolykos2.h>
#include <time.h>
#include <nets/net_node.h>
#ifndef CHAIN_DATA_DIR
#define CHAIN_DATA_DIR "chain_data"
#endif
@@ -169,6 +171,12 @@ int main(int argc, char* argv[]) {
uint256_t currentSupply = uint256_from_u64(0);
net_node_t* node = Node_Create();
if (!node) {
BalanceSheet_Destroy();
return 1;
}
blockchain_t* chain = Chain_Create();
if (!chain) {
fprintf(stderr, "failed to create chain\n");
@@ -479,5 +487,8 @@ int main(int argc, char* argv[]) {
Chain_Destroy(chain);
Block_ShutdownPowContext();
Node_Destroy(node);
return 0;
}

43
src/nets/net_node.c Normal file
View File

@@ -0,0 +1,43 @@
#include <nets/net_node.h>
net_node_t* Node_Create() {
net_node_t* node = (net_node_t*)malloc(sizeof(net_node_t));
if (!node) { return NULL; }
node->server = TcpServer_Create();
if (!node->server) {
free(node);
return NULL;
}
TcpServer_Init(node->server, LISTEN_PORT, "0.0.0.0"); // All interfaces
// Register callbacks before starting the server
node->server->on_connect = Node_Server_OnConnect;
node->server->on_data = Node_Server_OnData;
node->server->on_disconnect = Node_Server_OnDisconnect;
TcpServer_Start(node->server, MAX_CONS);
return node;
}
void Node_Destroy(net_node_t* node) {
if (!node || !node->server) { return; }
TcpServer_Stop(node->server);
TcpServer_Destroy(node->server);
free(node);
}
void Node_Server_OnConnect(tcp_connection_t* client) {
printf("A node connected!\n");
}
void Node_Server_OnData(tcp_connection_t* client) {
printf("A node sent data!\n");
}
void Node_Server_OnDisconnect(tcp_connection_t* client) {
printf("A node disconnected!\n");
}

View File

@@ -1,7 +1,9 @@
#ifndef _WIN32
#include <tcpd/tcpserver.h>
TcpServer* TcpServer_Create() {
TcpServer* svr = (TcpServer*)malloc(sizeof(TcpServer));
tcp_server_t* TcpServer_Create() {
tcp_server_t* svr = (tcp_server_t*)malloc(sizeof(tcp_server_t));
if (!svr) {
perror("tcpserver - creation failure");
@@ -20,7 +22,7 @@ TcpServer* TcpServer_Create() {
return svr;
}
void TcpServer_Destroy(TcpServer* ptr) {
void TcpServer_Destroy(tcp_server_t* ptr) {
if (ptr) {
if (ptr->clientsArrPtr) {
for (size_t i = 0; i < ptr->clients; i++) {
@@ -37,7 +39,7 @@ void TcpServer_Destroy(TcpServer* ptr) {
}
}
void TcpServer_Init(TcpServer* ptr, unsigned short port, const char* addr) {
void TcpServer_Init(tcp_server_t* ptr, unsigned short port, const char* addr) {
if (ptr) {
// Create socket
ptr->sockFd = socket(AF_INET, SOCK_STREAM, 0);
@@ -74,8 +76,8 @@ void* TcpServer_clientthreadprocess(void* ptr) {
tcpclient_thread_args* args = (tcpclient_thread_args*)ptr;
TcpClient* cli = args->clientPtr;
TcpServer* svr = args->serverPtr;
tcp_connection_t* cli = args->clientPtr;
tcp_server_t* svr = args->serverPtr;
if (args) {
free(args);
@@ -97,13 +99,15 @@ void* TcpServer_clientthreadprocess(void* ptr) {
pthread_testcancel(); // Check for thread death
}
if (cli->on_disconnect) {
cli->on_disconnect(cli);
}
// Close on exit
close(cli->clientFd);
// Destroy
TcpClient** arr = svr->clientsArrPtr;
tcp_connection_t** arr = svr->clientsArrPtr;
size_t idx = Generic_FindClientInArrayByPtr(arr, cli, svr->clients);
if (idx != SIZE_MAX) {
if (arr[idx]) {
@@ -126,9 +130,9 @@ void* TcpServer_threadprocess(void* ptr) {
return NULL;
}
TcpServer* svr = (TcpServer*)ptr;
tcp_server_t* svr = (tcp_server_t*)ptr;
while (1) {
TcpClient tempclient;
tcp_connection_t tempclient;
socklen_t clientsize = sizeof(tempclient.clientAddr);
int client = accept(svr->sockFd, (struct sockaddr*)&tempclient.clientAddr, &clientsize);
if (client >= 0) {
@@ -137,7 +141,7 @@ void* TcpServer_threadprocess(void* ptr) {
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));
tcp_connection_t* heapCli = (tcp_connection_t*)malloc(sizeof(tcp_connection_t));
if (!heapCli) {
perror("tcpserver - client failed to allocate");
exit(EXIT_FAILURE); // Wtf just happened???
@@ -193,7 +197,7 @@ void* TcpServer_threadprocess(void* ptr) {
return NULL;
}
void TcpServer_Start(TcpServer* ptr, int maxcons) {
void TcpServer_Start(tcp_server_t* ptr, int maxcons) {
if (ptr) {
if (listen(ptr->sockFd, maxcons) < 0) {
perror("tcpserver - listen");
@@ -202,7 +206,7 @@ void TcpServer_Start(TcpServer* ptr, int maxcons) {
}
ptr->clients = maxcons;
ptr->clientsArrPtr = (TcpClient**)malloc(sizeof(TcpClient*) * maxcons);
ptr->clientsArrPtr = (tcp_connection_t**)malloc(sizeof(tcp_connection_t*) * maxcons);
if (!ptr->clientsArrPtr) {
perror("tcpserver - allocation of client space fatally errored");
@@ -219,7 +223,7 @@ void TcpServer_Start(TcpServer* ptr, int maxcons) {
pthread_create(&ptr->svrThread, NULL, TcpServer_threadprocess, ptr);
}
void TcpServer_Stop(TcpServer* ptr) {
void TcpServer_Stop(tcp_server_t* ptr) {
if (ptr && ptr->svrThread != 0) {
// Stop server
pthread_cancel(ptr->svrThread);
@@ -227,7 +231,7 @@ void TcpServer_Stop(TcpServer* ptr) {
// Disconnect clients
for (size_t i = 0; i < ptr->clients; i++) {
TcpClient* cliPtr = ptr->clientsArrPtr[i];
tcp_connection_t* cliPtr = ptr->clientsArrPtr[i];
if (cliPtr) {
close(cliPtr->clientFd);
pthread_cancel(cliPtr->clientThread);
@@ -238,7 +242,7 @@ void TcpServer_Stop(TcpServer* ptr) {
}
}
void TcpServer_Send(TcpServer* ptr, TcpClient* cli, void* data, size_t len) {
void TcpServer_Send(tcp_server_t* ptr, tcp_connection_t* cli, void* data, size_t len) {
if (ptr && cli && data && len > 0) {
size_t sent = 0;
while (sent < len) {
@@ -267,7 +271,7 @@ void Generic_SendSocket(int sock, void* data, size_t len) {
}
}
void TcpServer_Disconnect(TcpServer* ptr, TcpClient* cli) {
void TcpServer_Disconnect(tcp_server_t* ptr, tcp_connection_t* cli) {
if (ptr && cli) {
close(cli->clientFd);
pthread_cancel(cli->clientThread);
@@ -284,7 +288,7 @@ void TcpServer_Disconnect(TcpServer* ptr, TcpClient* cli) {
}
}
void TcpServer_KillClient(TcpServer* ptr, TcpClient* cli) {
void TcpServer_KillClient(tcp_server_t* ptr, tcp_connection_t* cli) {
if (ptr && cli) {
// RST the connection
struct linger so_linger;
@@ -306,7 +310,7 @@ void TcpServer_KillClient(TcpServer* ptr, TcpClient* cli) {
}
}
size_t Generic_FindClientInArrayByPtr(TcpClient** arr, TcpClient* ptr, size_t len) {
size_t Generic_FindClientInArrayByPtr(tcp_connection_t** arr, tcp_connection_t* ptr, size_t len) {
for (size_t i = 0; i < len; i++) {
if (arr[i] == ptr) {
return i;
@@ -315,3 +319,5 @@ size_t Generic_FindClientInArrayByPtr(TcpClient** arr, TcpClient* ptr, size_t le
return SIZE_MAX; // Returns max unsigned, likely improbable to be correct
}
#endif