From bd972bfab60780e6068ac2f96acc3c9ad4f83d37 Mon Sep 17 00:00:00 2001 From: DcruBro Date: Sat, 25 Apr 2026 20:28:38 +0200 Subject: [PATCH] hello and ack exchange --- include/packettype.h | 16 +++--- include/txmempool.h | 1 + include/utils.h | 132 +++++++++++++++++++++++++++++++++++++++++++ src/main.c | 107 ++++++++--------------------------- src/nets/net_node.c | 70 ++++++++++++++++++++++- src/txmempool.c | 2 +- 6 files changed, 236 insertions(+), 92 deletions(-) diff --git a/include/packettype.h b/include/packettype.h index fab9e21..44e0215 100644 --- a/include/packettype.h +++ b/include/packettype.h @@ -6,13 +6,15 @@ typedef enum { PACKET_TYPE_NONE = 0, PACKET_TYPE_HELLO = 1, // Hello, let's connect! Here's who I am, and what I have! - PACKET_TYPE_FETCH_BLOCK = 2, // I want a Block - PACKET_TYPE_BLOCK_DATA = 3, // Here's a Block (response to fetch) - I don't care what you do with it, but you wanted it, so here it is! - PACKET_TYPE_BROADCAST_BLOCK = 4, // Here's a new Block I want to share with the network (unsolicited - e.g. just mined it) - PACKET_TYPE_ACK_BLOCK = 5, // I have received your block, here's what I did with it (response to broadcast) - PACKET_TYPE_BROADCAST_TX = 6, // Here's a new transaction I want to share with the network - PACKET_TYPE_ACK_TX = 7, // I have received your transaction, here's what I did with it (response to broadcast) - PACKET_TYPE_MAX = 9 + PACKET_TYPE_ACK_HELLO = 2, // I received your hello, here's who I am and what I have (response to hello) + PACKET_TYPE_FETCH_BLOCK = 3, // I want a Block + PACKET_TYPE_BLOCK_DATA = 4, // Here's a Block (response to fetch) - I don't care what you do with it, but you wanted it, so here it is! + PACKET_TYPE_BROADCAST_BLOCK = 5, // Here's a new Block I want to share with the network (unsolicited - e.g. just mined it) + PACKET_TYPE_ACK_BLOCK = 6, // I have received your block, here's what I did with it (response to broadcast) + PACKET_TYPE_BROADCAST_TX = 7, // Here's a new transaction I want to share with the network + PACKET_TYPE_ACK_TX = 8, // I have received your transaction, here's what I did with it (response to broadcast) + PACKET_TYPE_ERROR = 9, // Something went wrong with the packet you sent me, here's an error message (can be response to any packet) + PACKET_TYPE_MAX = 10 } packet_type_t; static inline int PacketType_IsValid(uint8_t packetType) { diff --git a/include/txmempool.h b/include/txmempool.h index f92c5ec..01e137a 100644 --- a/include/txmempool.h +++ b/include/txmempool.h @@ -10,6 +10,7 @@ KHASH_INIT(tx_mempool_map_m, key32_t, signed_transaction_t, 1, hash_key32, eq_ke extern khash_t(tx_mempool_map_m)* txMempool; void TxMempool_Init(); +// Assumed that the transation was confirmed to be valid int TxMempool_Insert(signed_transaction_t tx); bool TxMempool_Lookup(uint8_t* txHash, signed_transaction_t* out); void TxMempool_Print(); diff --git a/include/utils.h b/include/utils.h index 9a0cadf..901b12f 100644 --- a/include/utils.h +++ b/include/utils.h @@ -5,6 +5,7 @@ #include #include #include +#include typedef struct { uint8_t bytes[32]; @@ -30,4 +31,135 @@ static inline void AddressToHexString(const uint8_t address[32], char out[65]) { to_hex(address, out); } +static inline bool ParseHexAddress32(const char* in, uint8_t outAddress[32]) { + if (!in || !outAddress) { + return false; + } + + const char* p = in; + if (p[0] == '0' && (p[1] == 'x' || p[1] == 'X')) { + p += 2; + } + + if (strlen(p) != 64) { + return false; + } + + for (size_t i = 0; i < 32; ++i) { + char hi = p[i * 2]; + char lo = p[i * 2 + 1]; + int hiVal = (hi >= '0' && hi <= '9') ? (hi - '0') : + (hi >= 'a' && hi <= 'f') ? (10 + hi - 'a') : + (hi >= 'A' && hi <= 'F') ? (10 + hi - 'A') : -1; + int loVal = (lo >= '0' && lo <= '9') ? (lo - '0') : + (lo >= 'a' && lo <= 'f') ? (10 + lo - 'a') : + (lo >= 'A' && lo <= 'F') ? (10 + lo - 'A') : -1; + + if (hiVal < 0 || loVal < 0) { + return false; + } + + outAddress[i] = (uint8_t)((hiVal << 4) | loVal); + } + + return true; +} + +static inline bool IsValidIPv4(const char* ip) { + if (!ip || *ip == '\0') { + return false; + } + + int octetCount = 0; + const char* p = ip; + + while (*p != '\0') { + if (octetCount >= 4) { + return false; + } + + if (*p < '0' || *p > '9') { + return false; + } + + unsigned int value = 0; + int digits = 0; + while (*p >= '0' && *p <= '9') { + value = (value * 10u) + (unsigned int)(*p - '0'); + if (value > 255u) { + return false; + } + ++digits; + if (digits > 3) { + return false; + } + ++p; + } + + if (digits == 0) { + return false; + } + + ++octetCount; + if (octetCount < 4) { + if (*p != '.') { + return false; + } + ++p; + if (*p == '\0') { + return false; + } + } + } + + return octetCount == 4; +} + +static inline void Uint256ToDecimal(const uint256_t* value, char* out, size_t outSize) { + if (!value || !out || outSize == 0) { + return; + } + + uint64_t tmp[4] = { + value->limbs[0], + value->limbs[1], + value->limbs[2], + value->limbs[3] + }; + + if (tmp[0] == 0 && tmp[1] == 0 && tmp[2] == 0 && tmp[3] == 0) { + if (outSize >= 2) { + out[0] = '0'; + out[1] = '\0'; + } else { + out[0] = '\0'; + } + return; + } + + char digits[80]; + size_t digitCount = 0; + + while (tmp[0] != 0 || tmp[1] != 0 || tmp[2] != 0 || tmp[3] != 0) { + uint64_t remainder = 0; + for (int i = 3; i >= 0; --i) { + __uint128_t cur = ((__uint128_t)remainder << 64) | tmp[i]; + tmp[i] = (uint64_t)(cur / 10u); + remainder = (uint64_t)(cur % 10u); + } + + if (digitCount < sizeof(digits) - 1) { + digits[digitCount++] = (char)('0' + remainder); + } else { + break; + } + } + + size_t writeLen = (digitCount < (outSize - 1)) ? digitCount : (outSize - 1); + for (size_t i = 0; i < writeLen; ++i) { + out[i] = digits[digitCount - 1 - i]; + } + out[writeLen] = '\0'; +} + #endif diff --git a/src/main.c b/src/main.c index 891295f..6312632 100644 --- a/src/main.c +++ b/src/main.c @@ -84,53 +84,6 @@ static bool GenerateTestMinerIdentity(uint8_t privateKey[32], uint8_t compressed return false; } -static void Uint256ToDecimal(const uint256_t* value, char* out, size_t outSize) { - if (!value || !out || outSize == 0) { - return; - } - - uint64_t tmp[4] = { - value->limbs[0], - value->limbs[1], - value->limbs[2], - value->limbs[3] - }; - - if (tmp[0] == 0 && tmp[1] == 0 && tmp[2] == 0 && tmp[3] == 0) { - if (outSize >= 2) { - out[0] = '0'; - out[1] = '\0'; - } else { - out[0] = '\0'; - } - return; - } - - char digits[80]; - size_t digitCount = 0; - - while (tmp[0] != 0 || tmp[1] != 0 || tmp[2] != 0 || tmp[3] != 0) { - uint64_t remainder = 0; - for (int i = 3; i >= 0; --i) { - __uint128_t cur = ((__uint128_t)remainder << 64) | tmp[i]; - tmp[i] = (uint64_t)(cur / 10u); - remainder = (uint64_t)(cur % 10u); - } - - if (digitCount < sizeof(digits) - 1) { - digits[digitCount++] = (char)('0' + remainder); - } else { - break; - } - } - - size_t writeLen = (digitCount < (outSize - 1)) ? digitCount : (outSize - 1); - for (size_t i = 0; i < writeLen; ++i) { - out[i] = digits[digitCount - 1 - i]; - } - out[writeLen] = '\0'; -} - static bool MineBlock(block_t* block) { if (!block) { return false; @@ -148,40 +101,6 @@ static bool MineBlock(block_t* block) { } } -static bool ParseHexAddress32(const char* in, uint8_t outAddress[32]) { - if (!in || !outAddress) { - return false; - } - - const char* p = in; - if (p[0] == '0' && (p[1] == 'x' || p[1] == 'X')) { - p += 2; - } - - if (strlen(p) != 64) { - return false; - } - - for (size_t i = 0; i < 32; ++i) { - char hi = p[i * 2]; - char lo = p[i * 2 + 1]; - int hiVal = (hi >= '0' && hi <= '9') ? (hi - '0') : - (hi >= 'a' && hi <= 'f') ? (10 + hi - 'a') : - (hi >= 'A' && hi <= 'F') ? (10 + hi - 'A') : -1; - int loVal = (lo >= '0' && lo <= '9') ? (lo - '0') : - (lo >= 'a' && lo <= 'f') ? (10 + lo - 'a') : - (lo >= 'A' && lo <= 'F') ? (10 + lo - 'A') : -1; - - if (hiVal < 0 || loVal < 0) { - return false; - } - - outAddress[i] = (uint8_t)((hiVal << 4) | loVal); - } - - return true; -} - static bool FlushChainAndSheet(blockchain_t* chain, const char* chainDataDir, uint256_t currentSupply, @@ -507,7 +426,7 @@ int main(int argc, char* argv[]) { char supplyStr[80]; Uint256ToDecimal(¤tSupply, supplyStr, sizeof(supplyStr)); printf("Current chain has %zu blocks, total supply %s\n", Chain_Size(chain), supplyStr); - printf("Commands: mine , send
, balance [address], flushchain, fullverify, wipechain, genaddr, exit\n"); + printf("Commands: mine , send
, balance [address], connect , flushchain, fullverify, wipechain, genaddr, exit\n"); char line[1024]; while (true) { @@ -682,6 +601,28 @@ int main(int argc, char* argv[]) { continue; } + if (strcmp(cmd, "connect") == 0) { + char* ipStr = strtok(NULL, " \t"); + char* extra = strtok(NULL, " \t"); + if (!ipStr || extra) { + printf("usage: connect \n"); + continue; + } + + if (!IsValidIPv4(ipStr)) { + printf("invalid IPv4 address\n"); + continue; + } + + if (Node_ConnectPeer(node, ipStr, LISTEN_PORT) != 0) { + printf("failed to connect to %s:%u\n", ipStr, (unsigned int)LISTEN_PORT); + continue; + } + + printf("connect requested to %s:%u\n", ipStr, (unsigned int)LISTEN_PORT); + continue; + } + if (strcmp(cmd, "flushchain") == 0) { if (FlushChainAndSheet(chain, chainDataDir, currentSupply, currentReward)) { printf("chain flushed\n"); @@ -758,7 +699,7 @@ int main(int argc, char* argv[]) { break; } - printf("Unknown command. Available: mine, send, balance, flushchain, fullverify, wipechain, genaddr, exit\n"); + printf("Unknown command. Available: mine, send, balance, connect, flushchain, fullverify, wipechain, genaddr, exit\n"); } (void)FlushChainAndSheet(chain, chainDataDir, currentSupply, currentReward); diff --git a/src/nets/net_node.c b/src/nets/net_node.c index 212fc22..a15d7d0 100644 --- a/src/nets/net_node.c +++ b/src/nets/net_node.c @@ -218,15 +218,47 @@ void Node_Server_OnData(tcp_connection_t* client) { printf("Received HELLO from node %u: protoVersion=%u, blockHeight=%" PRIu64 "\n", client ? client->connectionId : 0U, protoVersion, blockHeight); + // Craft and send ACK_HELLO + uint8_t ackBuf[100]; + uint8_t* ackData = ackBuf; + size_t ackOffset = 0; + memcpy(ackData + ackOffset, &protoVersion, sizeof(protoVersion)); + ackOffset += sizeof(protoVersion); + memcpy(ackData + ackOffset, ¤tBlockHeight, sizeof(currentBlockHeight)); + ackOffset += sizeof(currentBlockHeight); + break; } + case PACKET_TYPE_ACK_HELLO: { + // This is illegal + printf("Received unexpected ACK_HELLO packet from node %u\n", client ? client->connectionId : 0U); + + // Send the error and kill the connection + const char* msg = "You can't ACK_HELLO me! I'm a server!"; + Node_SendPacket(Node_FromConnection(client), client, PACKET_TYPE_ERROR, msg, strlen(msg)); + TcpConnection_RequestClose(client); + return; + } case PACKET_TYPE_FETCH_BLOCK: case PACKET_TYPE_BLOCK_DATA: case PACKET_TYPE_BROADCAST_BLOCK: case PACKET_TYPE_ACK_BLOCK: case PACKET_TYPE_BROADCAST_TX: case PACKET_TYPE_ACK_TX: + case PACKET_TYPE_ERROR: { + // Decode the message inside as text + char* text = (char*)malloc(payloadLen + 1); + if (!text) { + return; + } + memcpy(text, payload, payloadLen); + text[payloadLen] = '\0'; + printf("Received packet type %u from node %u with message: %s\n", + (unsigned int)packetType, client ? client->connectionId : 0U, text); + free(text); + break; + } default: return; } @@ -273,14 +305,50 @@ void Node_Client_OnData(tcp_connection_t* client) { } switch (packetType) { - case PACKET_TYPE_HELLO: + case PACKET_TYPE_HELLO: { + // This is illegal + printf("Received unexpected HELLO packet from node %u\n", client ? client->connectionId : 0U); + + // Send the error and kill the connection + const char* msg = "You can't HELLO me! I'm a client!"; + Node_SendPacket(Node_FromConnection(client), client, PACKET_TYPE_ERROR, msg, strlen(msg)); + TcpConnection_RequestClose(client); + return; + } + case PACKET_TYPE_ACK_HELLO: { + // Decode ACK_HELLO + if (payloadLen < sizeof(uint32_t) + sizeof(uint64_t)) { + return; + } + + uint32_t protoVersion; + uint64_t blockHeight; + memcpy(&protoVersion, payload, sizeof(protoVersion)); + memcpy(&blockHeight, payload + sizeof(protoVersion), sizeof(blockHeight)); + + printf("Received ACK_HELLO from node %u with protoVersion %u and blockHeight %lu\n", client ? client->connectionId : 0U, protoVersion, (unsigned long)blockHeight); + break; + } case PACKET_TYPE_FETCH_BLOCK: case PACKET_TYPE_BLOCK_DATA: case PACKET_TYPE_BROADCAST_BLOCK: case PACKET_TYPE_ACK_BLOCK: case PACKET_TYPE_BROADCAST_TX: case PACKET_TYPE_ACK_TX: + case PACKET_TYPE_ERROR: { + // Decode the message inside as text + char* text = (char*)malloc(payloadLen + 1); + if (!text) { + return; + } + memcpy(text, payload, payloadLen); + text[payloadLen] = '\0'; + printf("Received packet type %u from node %u with message: %s\n", + (unsigned int)packetType, client ? client->connectionId : 0U, text); + free(text); + break; + } default: return; } diff --git a/src/txmempool.c b/src/txmempool.c index a3adc46..84e9651 100644 --- a/src/txmempool.c +++ b/src/txmempool.c @@ -10,7 +10,7 @@ int TxMempool_Insert(signed_transaction_t tx) { if (!txMempool) { return -1; } uint8_t txHash[32]; - Transaction_CalculateHash(&tx.transaction, txHash); + Transaction_CalculateHash(&tx, txHash); key32_t key; memcpy(key.bytes, txHash, 32);