BitLab 0.1.0
BitLab: A Browser for the Bitcoin P2P Network and Blockchain
Loading...
Searching...
No Matches
peer_connection.c File Reference
#include "peer_connection.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <time.h>
#include <errno.h>
#include <stdint.h>
#include <openssl/sha.h>
#include <pthread.h>
#include <sys/time.h>
#include <stdbool.h>
#include <bits/pthreadtypes.h>
#include "peer_queue.h"
#include "utils.h"
#include "log.h"
#include "ip.h"

Go to the source code of this file.

Functions

void parse_inv_message (const unsigned char *payload, size_t payload_len)
 
void handle_inv_message (int idx, const unsigned char *payload, size_t payload_len)
 
size_t build_getblocks_message (unsigned char *buffer, size_t buffer_size, const unsigned char *block_locator, size_t locator_count)
 
size_t write_var_int (unsigned char *buf, uint64_t value)
 
void decode_transactions (const unsigned char *block_data, size_t block_len)
 
static void compute_checksum (const unsigned char *payload, size_t payload_len, unsigned char out[4])
 compute_checksum: Calculate the double-SHA256 of the payload, then copy the first 4 bytes into out[4] as the checksum.
 
static size_t build_version_payload (unsigned char *buf, size_t buf_size)
 build_version_payload: Build a minimal "version" message payload.
 
static size_t build_message (unsigned char *buf, size_t buf_size, const char *command, const unsigned char *payload, size_t payload_len)
 build_message: Creates a Bitcoin P2P message (header + payload) in buf.
 
size_t build_getheaders_message (unsigned char *buffer, size_t buffer_size, const unsigned char *block_locator, size_t locator_count)
 
void compute_block_hash (const unsigned char *block_header, unsigned char *output_hash)
 
void list_connected_nodes ()
 Lists all connected nodes and their details.
 
int get_idx (const char *ip_address)
 Get the index of the node with the given IP address.
 
void send_getaddr_and_wait (int idx)
 Sends a 'getaddr' message to the peer and waits for a response.
 
ssize_t send_addr (int sockfd, const char *ip_addr)
 send_addr: Sends the 'addr' message to the specified socket with the list of peers.
 
static ssize_t send_verack (int sockfd, const char *ip_addr)
 send_verack: Constructs a verack message with an empty payload (length = 0) and a valid 4-byte checksum for an empty payload.
 
static ssize_t send_pong (int sockfd, const unsigned char *nonce8)
 send_pong: Send a "pong" message echoing the same 8-byte nonce from a "ping" payload.
 
ssize_t send_ping (int sockfd, const char *ip_addr)
 
void initialize_node (Node *node, const char *ip, uint16_t port, int socket_fd)
 
void * peer_communication (void *arg)
 
void create_peer_thread (Node *node)
 
int connect_to_peer (const char *ip_addr)
 Connects to a peer using the specified IP address.
 
void disconnect (int node_id)
 Disconnects from the node specified by the node ID.
 
void print_block_header (const unsigned char *header)
 
void parse_headers_message (const unsigned char *payload, size_t payload_len)
 
void load_latest_known_block_hash (unsigned char *block_hash)
 
void send_getheaders_and_wait (int idx)
 Sends a 'getheaders' message to the peer and waits for a response.
 
void send_headers (int idx, const unsigned char *start_hash, const unsigned char *stop_hash)
 Sends a 'headers' message to the peer.
 
void save_blocks_to_file (const unsigned char *payload, size_t payload_len, const char *filename)
 
unsigned char * load_blocks_from_file (const char *filename, size_t *payload_len)
 
void send_getblocks_and_wait (int idx)
 Sends a 'getblocks' message to the peer and waits for a response.
 
size_t build_getdata_message (unsigned char *buffer, size_t buffer_size, const unsigned char *hashes, size_t hash_count)
 
void send_getdata_and_wait (int idx, const unsigned char *hashes, size_t hash_count)
 Sends a 'getdata' message to the peer and waits for a response.
 
size_t build_inv_message (unsigned char *buffer, size_t buffer_size, const unsigned char *inv_data, size_t inv_count)
 Builds an 'inv' message.
 
void send_inv_and_wait (int idx, const unsigned char *inv_data, size_t inv_count)
 Sends an 'inv' message to the peer.
 
void send_tx (int idx, const unsigned char *tx_data, size_t tx_size)
 Sends a 'tx' message to the specified node.
 

Variables

Node nodes [MAX_NODES]
 

Function Documentation

◆ build_getblocks_message()

size_t build_getblocks_message ( unsigned char *  buffer,
size_t  buffer_size,
const unsigned char *  block_locator,
size_t  locator_count 
)

Definition at line 1594 of file peer_connection.c.

1595{
1596 if (buffer_size < sizeof(bitcoin_msg_header) + 37)
1597 return 0;
1598
1599 unsigned char payload[4 + 1 + (locator_count * 32) + 32];
1600
1601 // Set protocol version (4 bytes)
1602 uint32_t version = htonl(70015);
1603 memcpy(payload, &version, 4);
1604
1605 // Set block locator count (var_int encoding)
1606 size_t offset = 4;
1607 offset += write_var_int(payload + offset, locator_count);
1608
1609 // Copy block locator hashes
1610 memcpy(payload + offset, block_locator, locator_count * 32);
1611 offset += locator_count * 32;
1612
1613 // Set hash_stop (32 bytes of zeros for max 500 blocks)
1614 memset(payload + offset, 0, 32);
1615 offset += 32;
1616
1617 // Ensure buffer is large enough
1618 if (offset > buffer_size)
1619 return 0;
1620
1621 // Build final message
1622 return build_message(buffer, buffer_size, "getblocks", payload, offset);
1623}
static size_t build_message(unsigned char *buf, size_t buf_size, const char *command, const unsigned char *payload, size_t payload_len)
build_message: Creates a Bitcoin P2P message (header + payload) in buf.
size_t write_var_int(unsigned char *buf, uint64_t value)

References build_message(), and write_var_int().

Referenced by send_getblocks_and_wait().

◆ build_getdata_message()

size_t build_getdata_message ( unsigned char *  buffer,
size_t  buffer_size,
const unsigned char *  hashes,
size_t  hash_count 
)

Definition at line 1699 of file peer_connection.c.

1700{
1701 if (buffer_size < sizeof(bitcoin_msg_header) + 1 + (hash_count * 36))
1702 return 0;
1703
1704 unsigned char payload[1 + (hash_count * 36)];
1705
1706 // Set inventory count (var_int encoding)
1707 size_t offset = 0;
1708 offset += write_var_int(payload + offset, hash_count);
1709
1710 // Copy inventory vectors (type + hash)
1711 for (size_t i = 0; i < hash_count; i++)
1712 {
1713 uint32_t type = htonl(2); // Assuming type 2 for block (1 for transaction)
1714 memcpy(payload + offset, &type, 4);
1715 offset += 4;
1716 memcpy(payload + offset, hashes + (i * 32), 32);
1717 offset += 32;
1718 }
1719
1720 // Ensure buffer is large enough
1721 if (offset > buffer_size)
1722 return 0;
1723
1724 // Build final message
1725 return build_message(buffer, buffer_size, "getdata", payload, offset);
1726}

References build_message(), and write_var_int().

Referenced by send_getdata_and_wait().

◆ build_getheaders_message()

size_t build_getheaders_message ( unsigned char *  buffer,
size_t  buffer_size,
const unsigned char *  block_locator,
size_t  locator_count 
)

Definition at line 178 of file peer_connection.c.

179{
180 if (buffer_size < sizeof(bitcoin_msg_header) + 37)
181 return 0;
182
183 unsigned char payload[4 + 1 + (locator_count * 32) + 32];
184
185 // Set protocol version (4 bytes)
186 uint32_t version = htonl(70015);
187 memcpy(payload, &version, 4);
188
189 // Set block locator count (var_int encoding)
190 size_t offset = 4;
191 offset += write_var_int(payload + offset, locator_count);
192
193 // Copy block locator hashes
194 memcpy(payload + offset, block_locator, locator_count * 32);
195 offset += locator_count * 32;
196
197 // Set hash_stop (32 bytes of zeros for max 2000 headers)
198 memset(payload + offset, 0, 32);
199 offset += 32;
200
201 // Ensure buffer is large enough
202 if (offset > buffer_size)
203 return 0;
204
205 // Build final message
206 return build_message(buffer, buffer_size, "getheaders", payload, offset);
207}

References build_message(), and write_var_int().

Referenced by send_getheaders_and_wait().

◆ build_inv_message()

size_t build_inv_message ( unsigned char *  buffer,
size_t  buffer_size,
const unsigned char *  inv_data,
size_t  inv_count 
)

Builds an 'inv' message.

This function builds an 'inv' message with the given inventory data.

Parameters
bufferThe buffer to store the built message.
buffer_sizeThe size of the buffer.
inv_dataAn array of inventory vectors (type + hash).
inv_countThe number of inventory vectors in the array.
Returns
The size of the built message, or 0 on failure.

Definition at line 1814 of file peer_connection.c.

1815{
1816 size_t var_int_size = write_var_int(NULL, inv_count); // Get the size of the var_int encoding
1817 size_t payload_size = var_int_size + (inv_count * 36); // var_int size + 36 bytes per inventory vector
1818
1819 printf("inv_count: %zu, payload_size: %zu, buffer_size: %zu\n", inv_count, payload_size, buffer_size);
1820
1821 if (buffer_size < sizeof(bitcoin_msg_header) + payload_size)
1822 {
1823 printf("Buffer size is too small: buffer_size=%zu, required=%zu\n", buffer_size, sizeof(bitcoin_msg_header) + payload_size);
1824 return 0;
1825 }
1826
1827 unsigned char* payload = (unsigned char*)malloc(payload_size);
1828 if (!payload)
1829 {
1830 printf("Failed to allocate memory for payload\n");
1831 return 0;
1832 }
1833
1834 // Set inventory count (var_int encoding)
1835 size_t offset = 0;
1836 offset += write_var_int(payload + offset, inv_count);
1837 log_message(LOG_DEBUG, BITLAB_LOG, __FILE__, "After write_var_int: offset=%zu", offset);
1838
1839 // Copy inventory vectors (type + hash)
1840 for (size_t i = 0; i < inv_count; i++)
1841 {
1842 memcpy(payload + offset, inv_data + (i * 36), 36);
1843 offset += 36;
1844 }
1845 printf("After copying inventory vectors: offset=%zu\n", offset);
1846
1847 // Build final message
1848 size_t message_size = build_message(buffer, buffer_size, "inv", payload, payload_size);
1849 printf("Built message size: %zu\n", message_size);
1850
1851 free(payload);
1852 return message_size;
1853}
void log_message(log_level level, const char *filename, const char *source_file, const char *format,...)
Log a message used to log a message to the console or a file.
Definition log.c:89
#define BITLAB_LOG
Definition log.h:11
@ LOG_DEBUG
Definition log.h:29

References BITLAB_LOG, build_message(), LOG_DEBUG, log_message(), and write_var_int().

Referenced by send_inv_and_wait().

◆ build_message()

static size_t build_message ( unsigned char *  buf,
size_t  buf_size,
const char *  command,
const unsigned char *  payload,
size_t  payload_len 
)
static

build_message: Creates a Bitcoin P2P message (header + payload) in buf.

The 'command' is zero-padded to 12 bytes. The payload is appended. We compute the 4-byte double-SHA256 checksum.

Definition at line 135 of file peer_connection.c.

141{
142 if (buf_size < (sizeof(bitcoin_msg_header) + payload_len))
143 {
144 return 0;
145 }
146
147 // Prepare the header
148 bitcoin_msg_header header;
149 memset(&header, 0, sizeof(header));
150
152
153 // Zero out the command field, then copy up to 12 bytes
154 memset(header.command, 0, sizeof(header.command));
155 {
156 size_t cmd_len = strlen(command);
157 if (cmd_len > sizeof(header.command))
158 {
159 cmd_len = sizeof(header.command);
160 }
161 memcpy(header.command, command, cmd_len);
162 }
163
164 header.length = payload_len;
165
166 // Compute the checksum of the payload
167 unsigned char csum[4];
168 compute_checksum(payload, payload_len, csum);
169 memcpy(&header.checksum, csum, 4);
170
171 // Copy header into buf, then the payload
172 memcpy(buf, &header, sizeof(header));
173 memcpy(buf + sizeof(header), payload, payload_len);
174
175 return sizeof(header) + payload_len;
176}
static void compute_checksum(const unsigned char *payload, size_t payload_len, unsigned char out[4])
compute_checksum: Calculate the double-SHA256 of the payload, then copy the first 4 bytes into out[4]...
#define BITCOIN_MAINNET_MAGIC
unsigned char checksum[4]

References BITCOIN_MAINNET_MAGIC, bitcoin_msg_header::checksum, bitcoin_msg_header::command, compute_checksum(), bitcoin_msg_header::length, and bitcoin_msg_header::magic.

Referenced by build_getblocks_message(), build_getdata_message(), build_getheaders_message(), build_inv_message(), connect_to_peer(), send_addr(), send_getaddr_and_wait(), send_ping(), and send_pong().

◆ build_version_payload()

static size_t build_version_payload ( unsigned char *  buf,
size_t  buf_size 
)
static

build_version_payload: Build a minimal "version" message payload.

This includes: protocol version, services, timestamp, addr_recv, addr_from, nonce, user agent, start_height, and relay.

Definition at line 60 of file peer_connection.c.

61{
62 // We need at least 86 bytes for a minimal version message
63 if (buf_size < 86)
64 {
65 return 0;
66 }
67
68 memset(buf, 0, buf_size);
69 size_t offset = 0;
70
71 // (1) protocol version (int32_t)
72 unsigned int protocol_version = 70015;
73 memcpy(buf + offset, &protocol_version, 4);
74 offset += 4;
75
76 // (2) services (uint64_t) - set to 0
77 unsigned long long services = 0ULL;
78 memcpy(buf + offset, &services, 8);
79 offset += 8;
80
81 // (3) timestamp (int64_t) - current epoch
82 long long timestamp = (long long)time(NULL);
83 memcpy(buf + offset, &timestamp, 8);
84 offset += 8;
85
86 // (4) addr_recv (26 bytes)
87 memset(buf + offset, 0, 8); // services
88 offset += 8;
89 memset(buf + offset, 0, 16); // IP
90 offset += 16;
91 unsigned short port = htons(BITCOIN_MAINNET_PORT);
92 memcpy(buf + offset, &port, 2);
93 offset += 2;
94
95 // (5) addr_from (26 bytes) - same approach
96 memset(buf + offset, 0, 8);
97 offset += 8;
98 memset(buf + offset, 0, 16);
99 offset += 16;
100 memcpy(buf + offset, &port, 2);
101 offset += 2;
102
103 // (6) nonce (8 bytes) - random
104 unsigned long long nonce = (((unsigned long long)rand()) << 32) | rand();
105 memcpy(buf + offset, &nonce, 8);
106 offset += 8;
107
108 // (7) user agent (var_str)
109 const char* user_agent = "/Satoshi:0.1.0/";
110 unsigned char user_agent_len = (unsigned char)strlen(user_agent);
111 buf[offset] = user_agent_len;
112 offset += 1;
113 memcpy(buf + offset, user_agent, user_agent_len);
114 offset += user_agent_len;
115
116 // (8) start_height (int32_t) - let’s set 0 for demo
117 unsigned int start_height = 0;
118 memcpy(buf + offset, &start_height, 4);
119 offset += 4;
120
121 // (9) relay (bool) for version >= 70001; set to 0
122 unsigned char relay = 0;
123 buf[offset] = relay;
124 offset += 1;
125
126 return offset; // total payload size
127}
#define BITCOIN_MAINNET_PORT

References BITCOIN_MAINNET_PORT.

Referenced by connect_to_peer().

◆ compute_block_hash()

void compute_block_hash ( const unsigned char *  block_header,
unsigned char *  output_hash 
)

Definition at line 209 of file peer_connection.c.

210{
211 unsigned char hash1[32];
212 SHA256(block_header, 80, hash1); // First SHA-256
213 SHA256(hash1, 32, output_hash); // Second SHA-256
214}

◆ compute_checksum()

static void compute_checksum ( const unsigned char *  payload,
size_t  payload_len,
unsigned char  out[4] 
)
static

compute_checksum: Calculate the double-SHA256 of the payload, then copy the first 4 bytes into out[4] as the checksum.

Definition at line 39 of file peer_connection.c.

41{
42 unsigned char hash1[SHA256_DIGEST_LENGTH];
43 unsigned char hash2[SHA256_DIGEST_LENGTH];
44
45 // First SHA-256
46 SHA256(payload, payload_len, hash1);
47 // Second SHA-256
48 SHA256(hash1, SHA256_DIGEST_LENGTH, hash2);
49
50 // Copy first 4 bytes to out
51 memcpy(out, hash2, 4);
52}

Referenced by build_message(), send_headers(), send_tx(), and send_verack().

◆ connect_to_peer()

int connect_to_peer ( const char *  ip_addr)

Connects to a peer using the specified IP address.

This function attempts to establish a connection to a peer using the given IP address. It returns a socket file descriptor if the connection is successful, or -1 if it fails.

Parameters
ip_addrThe IP address of the peer to connect to.

Definition at line 1032 of file peer_connection.c.

1033{
1034 // Create the socket
1035 int sockfd;
1036 if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
1037 {
1038 fprintf(stderr, "[Error] socket creation failed: %s\n", strerror(errno));
1039 return -1;
1040 }
1041
1042 struct timeval timeout;
1043 timeout.tv_sec = 3; // seconds
1044 timeout.tv_usec = 0; // microseconds
1045
1046 // Set timeout for recv
1047 if (setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) < 0)
1048 {
1049 perror("setsockopt failed");
1050 close(sockfd);
1051 exit(EXIT_FAILURE);
1052 }
1053 // Set timeout for connect()
1054 if (setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)) < 0)
1055 {
1056 perror("setsockopt failed");
1057 close(sockfd);
1058 return -1;
1059 }
1060
1061 // Prepare the sockaddr_in
1062 struct sockaddr_in servaddr;
1063 memset(&servaddr, 0, sizeof(servaddr));
1064 servaddr.sin_family = AF_INET;
1065 servaddr.sin_port = htons(BITCOIN_MAINNET_PORT);
1066
1067 if (inet_pton(AF_INET, ip_addr, &servaddr.sin_addr) <= 0)
1068 {
1069 fprintf(stderr, "[Error] inet_pton failed (IP '%s'): %s\n",
1070 ip_addr, strerror(errno));
1071 close(sockfd);
1072 return -1;
1073 }
1074
1075 // Connect
1076 if (connect(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)
1077 {
1078 fprintf(stderr, "[Error] connect to %s:%d failed: %s\n",
1079 ip_addr, BITCOIN_MAINNET_PORT, strerror(errno));
1080 close(sockfd);
1081 return -1;
1082 }
1083
1084 printf("[+] Connected to peer %s:%d\n", ip_addr, BITCOIN_MAINNET_PORT);
1085
1086 // Build the 'version' payload
1087 unsigned char version_payload[200];
1088 size_t version_payload_len = build_version_payload(
1089 version_payload, sizeof(version_payload));
1090 if (version_payload_len == 0)
1091 {
1092 fprintf(stderr, "[Error] build_version_payload() failed.\n");
1093 close(sockfd);
1094 return -1;
1095 }
1096
1097 // Create the full 'version' message
1098 unsigned char version_msg[256];
1099 size_t version_msg_len = build_message(
1100 version_msg,
1101 sizeof(version_msg),
1102 "version",
1103 version_payload,
1104 version_payload_len
1105 );
1106 if (version_msg_len == 0)
1107 {
1108 fprintf(stderr, "[Error] build_message() failed for 'version'.\n");
1109 close(sockfd);
1110 return -1;
1111 }
1112
1113 // Send the 'version' message
1114 ssize_t bytes_sent = send(sockfd, version_msg, version_msg_len, 0);
1115 if (bytes_sent < 0)
1116 {
1117 fprintf(stderr, "[Error] send() of 'version' failed: %s\n", strerror(errno));
1118 close(sockfd);
1119 return -1;
1120 }
1121 printf("[+] Sent 'version' message (%zd bytes).\n", bytes_sent);
1122
1123 char log_filename[256];
1124 snprintf(log_filename, sizeof(log_filename), "peer_connection_%s.log",
1125 ip_addr);
1126 init_logging(log_filename);
1127 // Read loop - up to 10 messages
1128 unsigned char recv_buf[2048];
1129 bool connected = false;
1130 for (int i = 0; i < 4; ++i)
1131 {
1132 printf("[d] Executing %dth receive loop iteration\n", i);
1133 ssize_t n = recv(sockfd, recv_buf, sizeof(recv_buf), 0);
1134 if (n < 0)
1135 {
1136 if (errno == EAGAIN || errno == EWOULDBLOCK)
1137 {
1138 log_message(LOG_WARN, log_filename, __FILE__,
1139 "recv() timed out, continuing...");
1140 continue;
1141 }
1142 fprintf(stderr, "[Error] recv() failed: %s\n", strerror(errno));
1143 break;
1144 }
1145 if (n == 0)
1146 {
1147 // Peer closed the connection
1148 printf("[i] Peer closed the connection.\n");
1149 break;
1150 }
1151
1152 // If we have at least a full header, parse it
1153 if (n < (ssize_t)sizeof(bitcoin_msg_header))
1154 {
1155 printf("[!] Received %zd bytes (less than header size 24).\n", n);
1156 continue;
1157 }
1158
1159 printf("[<] Received %zd bytes.\n", n);
1160
1161 // Cast to a header
1162 bitcoin_msg_header* hdr = (bitcoin_msg_header*)recv_buf;
1163
1164 // Check the magic
1165 if (hdr->magic == BITCOIN_MAINNET_MAGIC)
1166 {
1167 char cmd_name[13];
1168 memset(cmd_name, 0, sizeof(cmd_name));
1169 memcpy(cmd_name, hdr->command, 12);
1170
1171 printf("[<] Received command: '%s'\n", cmd_name);
1172
1173 // Determine the payload length & pointer
1174 size_t payload_len = hdr->length;
1175
1176 if (n < (ssize_t)(sizeof(bitcoin_msg_header) + payload_len))
1177 {
1178 printf("[!] Incomplete message; got %zd bytes, expected %zu.\n",
1179 n, sizeof(bitcoin_msg_header) + payload_len);
1180 continue;
1181 }
1182
1183 // Handle known commands
1184 if (strcmp(cmd_name, "version") == 0)
1185 {
1186 // Send verack once we get their version
1187 ssize_t verack_sent = send_verack(sockfd, ip_addr);
1188 if (verack_sent < 0)
1189 {
1190 fprintf(stderr, "[Error] sending verack: %s\n", strerror(errno));
1191 }
1192 else
1193 {
1194 printf("[+] Sent 'verack' message.\n");
1195 }
1196 connected = true;
1197 }
1198 else if (strcmp(cmd_name, "verack") == 0)
1199 {
1200 connected = true;
1201 break;
1202 }
1203 else
1204 {
1205 // Unhandled command
1206 printf("[!] Unhandled command: '%s' (payload size=%u)\n",
1207 cmd_name, hdr->length);
1208 }
1209 }
1210 else
1211 {
1212 printf("[!] Unexpected magic bytes (0x%08X).\n", hdr->magic);
1213 }
1214 }
1215 if (connected == true)
1216 {
1217 for (int j = 0; j < MAX_NODES; ++j)
1218 {
1219 if (nodes[j].is_connected == 0)
1220 {
1221 guarded_print_line("connected to node: %s | %d.", ip_addr, j);
1222 initialize_node(&nodes[j], ip_addr, 8333, sockfd);
1224 break;
1225 }
1226 }
1227 }
1228 else
1229 {
1230 guarded_print_line("Couldn't connect to node\n");
1231 close(sockfd);
1232 }
1233 return 0;
1234}
void init_logging(const char *filename)
Initialize logging used to initialize the logging system, open and preserve the log file.
Definition log.c:34
@ LOG_WARN
Definition log.h:31
Node nodes[MAX_NODES]
void initialize_node(Node *node, const char *ip, uint16_t port, int socket_fd)
static size_t build_version_payload(unsigned char *buf, size_t buf_size)
build_version_payload: Build a minimal "version" message payload.
void create_peer_thread(Node *node)
static ssize_t send_verack(int sockfd, const char *ip_addr)
send_verack: Constructs a verack message with an empty payload (length = 0) and a valid 4-byte checks...
#define MAX_NODES
void guarded_print_line(const char *format,...)
Guarded print line function.
Definition utils.c:65

References BITCOIN_MAINNET_MAGIC, BITCOIN_MAINNET_PORT, build_message(), build_version_payload(), bitcoin_msg_header::command, create_peer_thread(), guarded_print_line(), init_logging(), initialize_node(), bitcoin_msg_header::length, log_message(), LOG_WARN, bitcoin_msg_header::magic, MAX_NODES, nodes, and send_verack().

Referenced by cli_connect().

◆ create_peer_thread()

void create_peer_thread ( Node node)

Definition at line 1017 of file peer_connection.c.

1018{
1019 if (pthread_create(&node->thread, NULL, peer_communication, (void*)node) != 0)
1020 {
1021 perror("Failed to create thread for peer");
1022 exit(1);
1023 }
1024
1025 if (pthread_detach(node->thread) != 0)
1026 {
1027 perror("Failed to detach thread for peer");
1028 exit(1);
1029 }
1030}
void * peer_communication(void *arg)
pthread_t thread

References peer_communication(), and Node::thread.

Referenced by connect_to_peer().

◆ decode_transactions()

void decode_transactions ( const unsigned char *  block_data,
size_t  block_len 
)

Definition at line 1977 of file peer_connection.c.

1978{
1979 (void)block_len; // Mark block_len as unused to avoid the warning
1980
1981 size_t offset = 0;
1982
1983 // Skip the block header (80 bytes)
1984 offset += 80;
1985
1986 // Read the number of transactions (var_int)
1987 uint64_t tx_count = read_var_int(block_data + offset, &offset);
1988
1989 printf("Number of transactions: %lu\n", tx_count);
1990
1991 for (uint64_t i = 0; i < tx_count; i++)
1992 {
1993 // Decode each transaction (simplified for demonstration)
1994 printf("Transaction %lu:\n", i + 1);
1995
1996 // Read transaction version (4 bytes)
1997 uint32_t tx_version;
1998 memcpy(&tx_version, block_data + offset, 4);
1999 offset += 4;
2000 printf(" Version: %u\n", tx_version);
2001
2002 // Read the number of inputs (var_int)
2003 uint64_t input_count = read_var_int(block_data + offset, &offset);
2004 printf(" Number of inputs: %lu\n", input_count);
2005
2006 // Read each input (simplified)
2007 for (uint64_t j = 0; j < input_count; j++)
2008 {
2009 // Skip previous output (32 bytes hash + 4 bytes index)
2010 offset += 36;
2011
2012 // Read script length (var_int)
2013 uint64_t script_len = read_var_int(block_data + offset, &offset);
2014
2015 // Skip script and sequence (script_len + 4 bytes)
2016 offset += script_len + 4;
2017 }
2018
2019 // Read the number of outputs (var_int)
2020 uint64_t output_count = read_var_int(block_data + offset, &offset);
2021 printf(" Number of outputs: %lu\n", output_count);
2022
2023 // Read each output (simplified)
2024 for (uint64_t j = 0; j < output_count; j++)
2025 {
2026 // Read value (8 bytes)
2027 uint64_t value;
2028 memcpy(&value, block_data + offset, 8);
2029 offset += 8;
2030 printf(" Value: %lu\n", value);
2031
2032 // Read script length (var_int)
2033 uint64_t script_len = read_var_int(block_data + offset, &offset);
2034
2035 // Skip script (script_len bytes)
2036 offset += script_len;
2037 }
2038
2039 // Read lock time (4 bytes)
2040 uint32_t lock_time;
2041 memcpy(&lock_time, block_data + offset, 4);
2042 offset += 4;
2043 printf(" Lock time: %u\n", lock_time);
2044 }
2045}
size_t read_var_int(const unsigned char *data, uint64_t *value)
Convert a 64-bit integer from network byte order to host byte order.

References read_var_int().

Referenced by send_getdata_and_wait().

◆ disconnect()

void disconnect ( int  node_id)

Disconnects from the node specified by the node ID.

This function disconnects from the node specified by the given node ID. It closes the socket, terminates the thread, and logs the disconnection.

Parameters
node_idThe ID of the node in the nodes array to disconnect from.

Definition at line 1236 of file peer_connection.c.

1237{
1238 if (node_id < 0 || node_id >= MAX_NODES || !nodes[node_id].is_connected)
1239 {
1240 fprintf(stderr, "[Error] Invalid node ID or node not connected.\n");
1241 return;
1242 }
1243
1244 Node* node = &nodes[node_id];
1245 char log_filename[256];
1246 snprintf(log_filename, sizeof(log_filename), "peer_connection_%s.log",
1247 node->ip_address);
1248
1249 log_message(LOG_INFO, log_filename, __FILE__, "Disconnecting from node %s:%u",
1250 node->ip_address, node->port);
1251
1252 close(node->socket_fd);
1253
1254 node->is_connected = 0;
1255
1256 if (pthread_cancel(node->thread) != 0)
1257 {
1258 perror("Failed to cancel thread for peer");
1259 }
1260
1261 log_message(LOG_INFO, log_filename, __FILE__,
1262 "Successfully disconnected from node %s:%u", node->ip_address,
1263 node->port);
1264}
@ LOG_INFO
Definition log.h:30
The structure to store information about a connected peer.
char ip_address[64]
uint16_t port
int socket_fd
int is_connected

References Node::ip_address, Node::is_connected, LOG_INFO, log_message(), MAX_NODES, nodes, Node::port, Node::socket_fd, and Node::thread.

Referenced by cli_disconnect().

◆ get_idx()

int get_idx ( const char *  ip_address)

Get the index of the node with the given IP address.

Parameters
ip_addressThe IP address of the node.
Returns
The index of the node in the global array.

Definition at line 291 of file peer_connection.c.

292{
293 for (int i = 0; i < MAX_NODES; ++i)
294 {
295 if (nodes[i].is_connected == 1 && strcmp(nodes[i].ip_address, ip_address) == 0)
296 {
297 return i;
298 }
299 }
300 return -1;
301}

References MAX_NODES, and nodes.

Referenced by peer_communication().

◆ handle_inv_message()

void handle_inv_message ( int  idx,
const unsigned char *  payload,
size_t  payload_len 
)

Definition at line 1657 of file peer_connection.c.

1658{
1659 size_t offset = 0;
1660 uint64_t count = read_var_int(payload + offset, &offset);
1661
1662 if (count == 0 || count > 50000)
1663 {
1664 log_message(LOG_WARN, BITLAB_LOG, __FILE__,
1665 "Invalid inventory count in inv message: %llu", count);
1666 return;
1667 }
1668
1669 unsigned char hashes[count * 32];
1670 size_t hash_count = 0;
1671
1672 for (uint64_t i = 0; i < count; i++)
1673 {
1674 if (offset + 36 > payload_len)
1675 {
1676 log_message(LOG_WARN, BITLAB_LOG, __FILE__,
1677 "Insufficient payload length for inventory entry");
1678 return;
1679 }
1680
1681 uint32_t type;
1682 memcpy(&type, payload + offset, 4);
1683 offset += 4;
1684
1685 if (type == 2) // Type 2 for block (1 for transaction)
1686 {
1687 memcpy(hashes + (hash_count * 32), payload + offset, 32);
1688 hash_count++;
1689 }
1690 offset += 32;
1691 }
1692
1693 if (hash_count > 0)
1694 {
1695 send_getdata_and_wait(idx, hashes, hash_count);
1696 }
1697}
void send_getdata_and_wait(int idx, const unsigned char *hashes, size_t hash_count)
Sends a 'getdata' message to the peer and waits for a response.

References BITLAB_LOG, log_message(), LOG_WARN, read_var_int(), and send_getdata_and_wait().

Referenced by peer_communication(), and send_inv_and_wait().

◆ initialize_node()

void initialize_node ( Node node,
const char *  ip,
uint16_t  port,
int  socket_fd 
)

Definition at line 734 of file peer_connection.c.

735{
736 snprintf(node->ip_address, sizeof(node->ip_address), "%s", ip);
737 node->port = port;
738 node->socket_fd = socket_fd;
739 node->is_connected = 1; // Mark as connected
740}

References Node::ip_address, Node::is_connected, Node::port, and Node::socket_fd.

Referenced by connect_to_peer().

◆ list_connected_nodes()

void list_connected_nodes ( )

Lists all connected nodes and their details.

This function iterates through the list of nodes and prints the details of each node that is currently connected.

Definition at line 269 of file peer_connection.c.

270{
271 for (int i = 0; i < MAX_NODES; ++i)
272 {
273 if (nodes[i].is_connected == 1)
274 {
275 guarded_print_line("Node %d:", i);
276 guarded_print_line(" IP Address: %s", nodes[i].ip_address);
277 guarded_print_line(" Port: %u", nodes[i].port);
278 guarded_print_line(" Socket FD: %d", nodes[i].socket_fd);
279 guarded_print_line(" Thread ID: %lu", nodes[i].thread);
280 guarded_print_line(" Is Connected: %d", nodes[i].is_connected);
281 guarded_print_line(" Is operation in progress: %d",
282 nodes[i].operation_in_progress);
283 guarded_print_line(" Compact blocks: %lu",
284 nodes[i].compact_blocks);
285 guarded_print_line(" Fee_rate: %lu",
286 nodes[i].fee_rate);
287 }
288 }
289}

References guarded_print_line(), MAX_NODES, and nodes.

Referenced by cli_list().

◆ load_blocks_from_file()

unsigned char * load_blocks_from_file ( const char *  filename,
size_t *  payload_len 
)

Definition at line 1491 of file peer_connection.c.

1492{
1493 FILE* file = fopen(filename, "rb");
1494 if (!file)
1495 {
1496 perror("Failed to open file for reading");
1497 return NULL;
1498 }
1499
1500 fseek(file, 0, SEEK_END);
1501 *payload_len = ftell(file);
1502 fseek(file, 0, SEEK_SET);
1503
1504 unsigned char* payload = (unsigned char*)malloc(*payload_len);
1505 if (!payload)
1506 {
1507 perror("Failed to allocate memory");
1508 fclose(file);
1509 return NULL;
1510 }
1511
1512 fread(payload, 1, *payload_len, file);
1513 fclose(file);
1514 guarded_print("Blocks loaded from file: %s\n", filename);
1515 return payload;
1516}
void guarded_print(const char *format,...)
Guarded print function.
Definition utils.c:55

References guarded_print().

Referenced by cli_inv(), and peer_communication().

◆ load_latest_known_block_hash()

void load_latest_known_block_hash ( unsigned char *  block_hash)

Definition at line 1315 of file peer_connection.c.

1316{
1317 FILE* file = fopen(HEADERS_FILE, "rb");
1318 if (!file)
1319 {
1320 // No known blocks, use genesis block hash
1321 memset(block_hash, 0, 32);
1322 return;
1323 }
1324
1325 fseek(file, -80, SEEK_END);
1326 fread(block_hash, 32, 1, file);
1327 fclose(file);
1328}
#define HEADERS_FILE

References HEADERS_FILE.

Referenced by send_getblocks_and_wait(), and send_getheaders_and_wait().

◆ parse_headers_message()

void parse_headers_message ( const unsigned char *  payload,
size_t  payload_len 
)

Definition at line 1295 of file peer_connection.c.

1296{
1297 size_t offset = 0;
1298 FILE* file = fopen(HEADERS_FILE, "ab");
1299 if (!file)
1300 {
1301 perror("Failed to open headers file");
1302 return;
1303 }
1304
1305 while (offset + 80 <= payload_len)
1306 {
1307 print_block_header(payload + offset);
1308 fwrite(payload + offset, 80, 1, file);
1309 offset += 80;
1310 }
1311
1312 fclose(file);
1313}
void print_block_header(const unsigned char *header)

References HEADERS_FILE, and print_block_header().

Referenced by send_getheaders_and_wait().

◆ parse_inv_message()

void parse_inv_message ( const unsigned char *  payload,
size_t  payload_len 
)

Definition at line 1625 of file peer_connection.c.

1626{
1627 size_t offset = 0;
1628 uint64_t count = read_var_int(payload + offset, &offset);
1629
1630 guarded_print("Inventory count: %llu\n", count);
1631
1632 for (uint64_t i = 0; i < count; i++)
1633 {
1634 if (offset + 36 > payload_len)
1635 {
1636 guarded_print("Insufficient payload length for inventory entry\n");
1637 return;
1638 }
1639
1640 uint32_t type;
1641 memcpy(&type, payload + offset, 4);
1642 offset += 4;
1643
1644 unsigned char hash[32];
1645 memcpy(hash, payload + offset, 32);
1646 offset += 32;
1647
1648 guarded_print("Inventory item %llu: Type: %u, Hash: ", i + 1, type);
1649 for (int j = 0; j < 32; j++)
1650 {
1651 guarded_print("%02x", hash[j]);
1652 }
1653 guarded_print("\n");
1654 }
1655}

References guarded_print(), and read_var_int().

Referenced by send_getblocks_and_wait().

◆ peer_communication()

void * peer_communication ( void *  arg)

Definition at line 742 of file peer_connection.c.

743{
744 Node* node = (Node*)arg;
745
746 char log_filename[256];
747 snprintf(log_filename, sizeof(log_filename), "peer_connection_%s.log",
748 node->ip_address);
749 log_message(LOG_INFO, log_filename, __FILE__,
750 "started peer communication with node with ip: %s", node->ip_address);
751
752 char buffer[2048];
753 struct timeval tv;
754 tv.tv_sec = 5; // 5 seconds timeout for recv
755 tv.tv_usec = 0;
756 setsockopt(node->socket_fd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof(tv));
757
758 time_t last_ping_time = time(NULL);
759
760 while (node->is_connected)
761 {
762 // Check the magic
763 ssize_t bytes_received = recv(node->socket_fd, buffer, sizeof(buffer) - 1, 0);
764
765 // Wait while other operation is in progress e.g. getaddr
766 while (node->operation_in_progress)
767 {
768 sleep(1);
769 }
770 if (bytes_received < 0)
771 {
772 if (errno == EAGAIN || errno == EWOULDBLOCK)
773 {
774 log_message(LOG_WARN, log_filename, __FILE__,
775 "recv() timed out, continuing...");
776 }
777 else
778 {
779 log_message(LOG_INFO, log_filename, __FILE__,
780 "Recv failed: %s", strerror((errno)));
781 }
782 time_t current_time = time(NULL);
783 if (difftime(current_time, last_ping_time) >= 5)
784 {
785 send_ping(node->socket_fd, node->ip_address);
786 last_ping_time = current_time;
787 }
788
789 continue;
790 }
791 if (bytes_received == 0)
792 {
793 log_message(LOG_INFO, log_filename, __FILE__,
794 "Connection closed by peer %s", node->ip_address);
795 node->is_connected = 0;
796 break;
797 }
798
799 buffer[bytes_received] = '\0'; // Null-terminate the received data
800 log_message(LOG_INFO, log_filename, __FILE__,
801 "Received bytes: %s", buffer);
802 // If we have at least a full header, parse it
803 if (bytes_received < (ssize_t)sizeof(bitcoin_msg_header))
804 {
805 log_message(LOG_INFO, log_filename, __FILE__,
806 "[!] Received %zd bytes (less than header size 24).",
807 bytes_received);
808 continue;
809 }
810
811 // Cast to a header
813 if (hdr->magic == BITCOIN_MAINNET_MAGIC)
814 {
815 char cmd_name[13];
816 memset(cmd_name, 0, sizeof(cmd_name));
817 memcpy(cmd_name, hdr->command, 12);
818
819 log_message(LOG_INFO, log_filename, __FILE__,
820 "[!] Received %s command ",
821 cmd_name);
822
823 // Determine the payload length & pointer
824 size_t payload_len = hdr->length;
825 const unsigned char* payload_data = (const unsigned char*)buffer + sizeof(
827
828 // In real code, you’d handle partial messages if n < header+payload
829 if (bytes_received < (ssize_t)(sizeof(bitcoin_msg_header) + payload_len))
830 {
831 log_message(LOG_INFO, log_filename, __FILE__,
832 "Incomplete message; got %zd bytes, expected %zu.\n",
833 bytes_received, sizeof(bitcoin_msg_header) + payload_len);
834 continue;
835 }
836
837 if (strcmp(cmd_name, "ping") == 0)
838 {
839 // Typically 8-byte payload
840 if (payload_len == 8)
841 {
842 ssize_t s = send_pong(node->socket_fd, payload_data);
843 if (s < 0)
844 {
845 log_message(LOG_ERROR, log_filename, __FILE__,
846 "Sending pong: %s\n", strerror(errno));
847 }
848 else
849 {
850 log_message(LOG_INFO, log_filename, __FILE__,
851 "Successfully sent pong");
852 }
853 }
854 else
855 {
856 log_message(LOG_WARN, log_filename, __FILE__,
857 "Ping payload length is not 8 bytes, not sending pong");
858 }
859 }
860 else if (strcmp(cmd_name, "getaddr") == 0)
861 {
862 if (payload_len == 0)
863 {
864 ssize_t s = send_addr(node->socket_fd, node->ip_address);
865 if (s < 0)
866 {
867 log_message(LOG_ERROR, log_filename, __FILE__,
868 "Sending addr: %s\n", strerror(errno));
869 }
870 else
871 {
872 log_message(LOG_INFO, log_filename, __FILE__,
873 "Successfully sent addresses");
874 }
875 }
876 else
877 {
878 log_message(LOG_WARN, log_filename, __FILE__,
879 "Invalid payload length for 'getaddr' command: %zu",
880 payload_len);
881 }
882 }
883 else if (strcmp(cmd_name, "getheaders") == 0)
884 {
885 // Parse the getheaders message
886 size_t offset = sizeof(bitcoin_msg_header);
887 uint32_t version;
888 memcpy(&version, payload_data + offset, 4);
889 offset += 4;
890
891 unsigned char start_hash[32];
892 memcpy(start_hash, payload_data + offset, 32);
893 offset += 32;
894
895 unsigned char stop_hash[32];
896 memcpy(stop_hash, payload_data + offset, 32);
897
898 // Log the received getheaders message
899 log_message(LOG_INFO, log_filename, __FILE__, "Received 'getheaders' message.");
900
901 // Send the headers in response
902 int idx = get_idx(node->ip_address);
903 send_headers(idx, start_hash, stop_hash);
904 }
905 else if (strcmp(cmd_name, "getblocks") == 0)
906 {
907 // Log the received getblocks message
908 log_message(LOG_INFO, log_filename, __FILE__, "Received 'getblocks' message.");
909
910 // Handle the getblocks request
911 size_t payload_len;
912 unsigned char* payload = load_blocks_from_file("blocks.dat", &payload_len);
913 if (!payload)
914 {
915 log_message(LOG_ERROR, log_filename, __FILE__, "Failed to load blocks from file");
916 }
917 else
918 {
919 ssize_t bytes_sent = send(node->socket_fd, payload, payload_len, 0);
920 if (bytes_sent < 0)
921 {
922 log_message(LOG_ERROR, log_filename, __FILE__, "Failed to send blocks: %s", strerror(errno));
923 }
924 else
925 {
926 log_message(LOG_INFO, log_filename, __FILE__, "Sent blocks to node %s", node->ip_address);
927 }
928 free(payload);
929 }
930 }
931 else if (strcmp(cmd_name, "inv") == 0)
932 {
933 // Handle the inv message
934 log_message(LOG_INFO, log_filename, __FILE__, "Received 'inv' message.");
935 int idx = get_idx(node->ip_address);
936 handle_inv_message(idx, payload_data, payload_len);
937 }
938 else if (strcmp(cmd_name, "getdata") == 0)
939 {
940 // Handle the getdata message
941 log_message(LOG_INFO, log_filename, __FILE__, "Received 'getdata' message.");
942 // Load the requested data from file and send it
943 size_t data_len;
944 unsigned char* data = load_blocks_from_file("data.dat", &data_len);
945 if (!data)
946 {
947 log_message(LOG_ERROR, log_filename, __FILE__, "Failed to load data from file");
948 }
949 else
950 {
951 ssize_t bytes_sent = send(node->socket_fd, data, data_len, 0);
952 if (bytes_sent < 0)
953 {
954 log_message(LOG_ERROR, log_filename, __FILE__, "Failed to send data: %s", strerror(errno));
955 }
956 else
957 {
958 log_message(LOG_INFO, log_filename, __FILE__, "Sent data to node %s", node->ip_address);
959 }
960 free(data);
961 }
962 }
963 // Info about compact blocks is saved but handling of compact blocks is not implemented
964 else if (strcmp(cmd_name, "sendcmpct") == 0)
965 {
966 // usually 9 bytes: fannounce(1 byte) + version(8 bytes)
967 if (payload_len == 9)
968 {
969 unsigned char fannounce = payload_data[0];
970 uint64_t cmpctversion;
971 memcpy(&cmpctversion, payload_data + 1, 8);
972 node->compact_blocks = cmpctversion;
973 log_message(LOG_INFO, log_filename, __FILE__,
974 "compactblocks set to: %lu, fannounce: %u",
975 cmpctversion, fannounce);
976 }
977 else
978 {
979 log_message(LOG_WARN, log_filename, __FILE__,
980 "sendcmpct payload length is not 9 bytes, its: %zu",
981 payload_len);
982 }
983 }
984 // Fee rate is saved, but filtering out transactions is not implemented
985 else if (strcmp(cmd_name, "feefilter") == 0)
986 {
987 // 8 bytes: an uint64_t in little-endian indicating min fee rate in sat/kB
988 if (payload_len == 8)
989 {
990 uint64_t fee_rate;
991 memcpy(&fee_rate, payload_data, 8);
992 node->fee_rate = fee_rate;
993 log_message(LOG_INFO, log_filename, __FILE__,
994 "fee rate set to: %lu", fee_rate);
995 }
996 else
997 {
998 log_message(LOG_WARN, log_filename, __FILE__,
999 "feefilter payload length is not 8 bytes, its: %zu",
1000 payload_len);
1001 }
1002 }
1003 }
1004
1005 time_t current_time = time(NULL);
1006 if (difftime(current_time, last_ping_time) >= 5)
1007 {
1008 send_ping(node->socket_fd, node->ip_address);
1009 last_ping_time = current_time;
1010 }
1011 }
1012
1013 close(node->socket_fd); // Close the socket once done
1014 return NULL;
1015}
@ LOG_ERROR
Definition log.h:32
unsigned char * load_blocks_from_file(const char *filename, size_t *payload_len)
int get_idx(const char *ip_address)
Get the index of the node with the given IP address.
void send_headers(int idx, const unsigned char *start_hash, const unsigned char *stop_hash)
Sends a 'headers' message to the peer.
ssize_t send_ping(int sockfd, const char *ip_addr)
static ssize_t send_pong(int sockfd, const unsigned char *nonce8)
send_pong: Send a "pong" message echoing the same 8-byte nonce from a "ping" payload.
void handle_inv_message(int idx, const unsigned char *payload, size_t payload_len)
ssize_t send_addr(int sockfd, const char *ip_addr)
send_addr: Sends the 'addr' message to the specified socket with the list of peers.
uint64_t fee_rate
uint64_t compact_blocks
int operation_in_progress

References BITCOIN_MAINNET_MAGIC, bitcoin_msg_header::command, Node::compact_blocks, Node::fee_rate, get_idx(), handle_inv_message(), Node::ip_address, Node::is_connected, bitcoin_msg_header::length, load_blocks_from_file(), LOG_ERROR, LOG_INFO, log_message(), LOG_WARN, bitcoin_msg_header::magic, Node::operation_in_progress, send_addr(), send_headers(), send_ping(), send_pong(), and Node::socket_fd.

Referenced by create_peer_thread().

◆ print_block_header()

void print_block_header ( const unsigned char *  header)

Definition at line 1266 of file peer_connection.c.

1267{
1268 uint32_t version;
1269 unsigned char prev_block[32];
1270 unsigned char merkle_root[32];
1271 uint32_t timestamp;
1272 uint32_t bits;
1273 uint32_t nonce;
1274
1275 memcpy(&version, header, 4);
1276 memcpy(prev_block, header + 4, 32);
1277 memcpy(merkle_root, header + 36, 32);
1278 memcpy(&timestamp, header + 68, 4);
1279 memcpy(&bits, header + 72, 4);
1280 memcpy(&nonce, header + 76, 4);
1281
1282 printf("Version: %u\n", version);
1283 printf("Previous Block Hash: ");
1284 for (int i = 0; i < 32; i++) printf("%02x", prev_block[i]);
1285 printf("\n");
1286 printf("Merkle Root: ");
1287 for (int i = 0; i < 32; i++) printf("%02x", merkle_root[i]);
1288 printf("\n");
1289 printf("Timestamp: %u\n", timestamp);
1290 printf("Bits: %u\n", bits);
1291 printf("Nonce: %u\n", nonce);
1292 printf("\n");
1293}

Referenced by parse_headers_message().

◆ save_blocks_to_file()

void save_blocks_to_file ( const unsigned char *  payload,
size_t  payload_len,
const char *  filename 
)

Definition at line 1477 of file peer_connection.c.

1478{
1479 FILE* file = fopen(filename, "wb");
1480 if (!file)
1481 {
1482 perror("Failed to open file for writing");
1483 return;
1484 }
1485
1486 fwrite(payload, 1, payload_len, file);
1487 fclose(file);
1488 guarded_print("Blocks saved to file: %s\n", filename);
1489}

References guarded_print().

Referenced by send_getblocks_and_wait().

◆ send_addr()

ssize_t send_addr ( int  sockfd,
const char *  ip_addr 
)

send_addr: Sends the 'addr' message to the specified socket with the list of peers.

The addresses are converted to IPv6-mapped IPv4 addresses.

Parameters: sockfd - The socket file descriptor to send the message to. ip_addr - The IP address of the peer to log the message.

Returns: The number of bytes sent on success, or -1 on failure.

Definition at line 541 of file peer_connection.c.

542{
543 char log_filename[256];
544 snprintf(log_filename, sizeof(log_filename), "peer_connection_%s.log", ip_addr);
545
546 int peer_count;
547 Peer* peers = get_peer_queue(&peer_count);
548
549 if (peer_count == 0 || peers == NULL)
550 {
551 guarded_print_line("[Error] No peers available to send");
552 return -1;
553 }
554
555 // Allocate memory for address payload (IPv4 to IPv6-mapped)
556 size_t payload_size = peer_count * sizeof(struct sockaddr_in6);
557 unsigned char* addr_payload = malloc(payload_size);
558 if (!addr_payload)
559 {
560 perror("malloc failed");
561 free(peers);
562 return -1;
563 }
564
565 // Populate address payload with peer data (IPv6-mapped addresses for IPv4)
566 for (int i = 0; i < peer_count; ++i)
567 {
568 struct sockaddr_in6* addr = (struct sockaddr_in6*)&addr_payload[i * sizeof(
569 struct sockaddr_in6)];
570 memset(addr, 0, sizeof(struct sockaddr_in6));
571 addr->sin6_family = AF_INET6;
572 addr->sin6_port = htons(peers[i].port);
573 addr->sin6_addr.s6_addr[10] = 0xFF;
574 addr->sin6_addr.s6_addr[11] = 0xFF;
575 inet_pton(AF_INET, peers[i].ip, &addr->sin6_addr.s6_addr[12]);
576
577 char ip_str[INET6_ADDRSTRLEN];
578 inet_ntop(AF_INET6, &addr->sin6_addr, ip_str, INET6_ADDRSTRLEN);
579 if (IN6_IS_ADDR_V4MAPPED(&addr->sin6_addr))
580 {
581 struct in_addr ipv4_addr;
582 memcpy(&ipv4_addr, &addr->sin6_addr.s6_addr[12], 4);
583 inet_ntop(AF_INET, &ipv4_addr, ip_str, INET_ADDRSTRLEN);
584 }
585 log_message(LOG_INFO, log_filename, __FILE__, "Sending address: %s:%d", ip_str,
586 peers[i].port);
587 }
588
589 // Prepare message buffer
590 unsigned char addr_msg[sizeof(bitcoin_msg_header) + payload_size];
591 size_t msg_len = build_message(addr_msg, sizeof(addr_msg), "addr", addr_payload,
592 payload_size);
593
594 if (msg_len == 0)
595 {
596 guarded_print_line("Failed to build 'addr' message.");
597 free(peers);
598 free(addr_payload);
599 return -1;
600 }
601
602 // Send the 'addr' message
603 ssize_t bytes_sent = send(sockfd, addr_msg, msg_len, 0);
604 if (bytes_sent < 0)
605 {
606 perror("Sending addresses failed");
607 }
608 else
609 {
610 log_message(LOG_INFO, log_filename, __FILE__,
611 "Successfully sent addresses");
612 }
613
614 free(peers);
615 free(addr_payload);
616
617 return bytes_sent;
618}
Peer * get_peer_queue(int *count)
Get the peer queue.
Definition peer_queue.c:118
The peer structure used to store the peer information obtained by peer discovery process.
Definition peer_queue.h:16

References build_message(), get_peer_queue(), guarded_print_line(), LOG_INFO, and log_message().

Referenced by peer_communication().

◆ send_getaddr_and_wait()

void send_getaddr_and_wait ( int  idx)

Sends a 'getaddr' message to the peer and waits for a response.

This function sends a 'getaddr' message to the peer identified by the given index and waits for a response. It is used to request a list of known peers from the connected peer.

Parameters
idxThe index of the peer in the nodes array.

Definition at line 303 of file peer_connection.c.

304{
305 if (idx < 0 || idx >= MAX_NODES || !nodes[idx].is_connected)
306 {
307 fprintf(stderr, "[Error] Invalid node index or node not connected.\n");
308 return;
309 }
310
311 Node* node = &nodes[idx];
312 char log_filename[256];
313 snprintf(log_filename, sizeof(log_filename), "peer_connection_%s.log",
314 node->ip_address);
315
316 // Build the 'getaddr' message
317 unsigned char getaddr_msg[sizeof(bitcoin_msg_header)];
318 size_t msg_len = build_message(getaddr_msg, sizeof(getaddr_msg), "getaddr", NULL,
319 0);
320 if (msg_len == 0)
321 {
322 fprintf(stderr, "[Error] Failed to build 'getaddr' message.\n");
323 return;
324 }
325
326 // Send the 'getaddr' message
327 ssize_t bytes_sent = send(node->socket_fd, getaddr_msg, msg_len, 0);
328 if (bytes_sent < 0)
329 {
330 log_message(LOG_INFO, log_filename, __FILE__,
331 "[Error] Failed to send 'getaddr' message: %s", strerror(errno));
332 return;
333 }
334
335 log_message(LOG_INFO, log_filename, __FILE__, "Sent 'getaddr' message.");
336 node->operation_in_progress = 1;
337
338 // Wait for 10 seconds for a response
339 struct timeval tv;
340 tv.tv_sec = 3;
341 tv.tv_usec = 0;
342 setsockopt(node->socket_fd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof(tv));
343
344 char buffer[32768];
345 size_t total_bytes_received = 0;
346 ssize_t bytes_received;
347
348 // Receive the message in parts
349 while (total_bytes_received < sizeof(bitcoin_msg_header))
350 {
351 log_message(LOG_INFO, log_filename, __FILE__, "first while receiving");
352 bytes_received = recv(node->socket_fd, buffer + total_bytes_received,
353 sizeof(buffer) - total_bytes_received - 1, 0);
354 if (bytes_received <= 0)
355 {
356 log_message(LOG_INFO, log_filename, __FILE__, "Recv 1 failed: %s",
357 strerror(errno));
358 node->is_connected = 0;
359 node->operation_in_progress = 0;
360 return;
361 }
362 total_bytes_received += bytes_received;
363 }
364
366 size_t payload_len = hdr->length;
367 size_t message_size = sizeof(bitcoin_msg_header) + payload_len;
368 int retry_count = 0;
369 const int max_retries = 1;
370
371 while (total_bytes_received < message_size)
372 {
373 log_message(LOG_INFO, log_filename, __FILE__, "second while receiving");
374 bytes_received = recv(node->socket_fd, buffer + total_bytes_received,
375 sizeof(buffer) - total_bytes_received - 1, 0);
376 if (bytes_received <= 0)
377 {
378 if (errno == EAGAIN || errno == EWOULDBLOCK)
379 {
380 log_message(LOG_INFO, log_filename, __FILE__,
381 "second while receiving inside if errno");
382 // Resource temporarily unavailable, continue receiving
383
384 if (++retry_count >= max_retries)
385 {
386 log_message(LOG_WARN, log_filename, __FILE__,
387 "Max retries reached, stopping recv.");
388 node->is_connected = 0;
389 node->operation_in_progress = 0;
390 break;
391 }
392 continue;
393 }
394 log_message(LOG_INFO, log_filename, __FILE__, "Recv failed: %s",
395 strerror(errno));
396 node->is_connected = 0;
397 node->operation_in_progress = 0;
398 return;
399 }
400 total_bytes_received += bytes_received;
401 }
402
403 buffer[total_bytes_received] = '\0'; // Null-terminate the received data
404
405 if (hdr->magic == BITCOIN_MAINNET_MAGIC)
406 {
407 char cmd_name[13];
408 memset(cmd_name, 0, sizeof(cmd_name));
409 memcpy(cmd_name, hdr->command, 12);
410
411 log_message(LOG_INFO, log_filename, __FILE__, "[!] Received %s command ",
412 cmd_name);
413
414 const unsigned char* payload_data = (unsigned char*)buffer + sizeof(
416
417
418 if (strcmp(cmd_name, "addr") == 0)
419 {
420 size_t offset = 0;
421
422 // Check if the payload length is sufficient to read the count of address entries
423 if (payload_len < 1)
424 {
425 log_message(LOG_WARN, log_filename, __FILE__,
426 "Insufficient payload length to read address count");
427 return;
428 }
429
430 // Read the count of address entries (var_int)
431 uint64_t count = read_var_int(payload_data + offset, &offset);
432
433 // Log the count of address entries
434 log_message(LOG_INFO, log_filename, __FILE__, "Address count: %llu", count);
435
436 // Ensure count does not exceed the maximum allowed entries
437 if (count > 1000)
438 {
439 log_message(LOG_WARN, log_filename, __FILE__,
440 "Address count exceeds maximum allowed: %llu", count);
441 return;
442 }
443
444 for (uint64_t i = 0; i < count; i++)
445 {
446 if (offset + 30 > payload_len)
447 {
448 log_message(LOG_WARN, log_filename, __FILE__,
449 "Insufficient payload length for address entry");
450 return;
451 }
452
453 // Read timestamp (4 bytes)
454 uint32_t timestamp;
455 memcpy(&timestamp, payload_data + offset, 4);
456 timestamp = ntohl(timestamp);
457 offset += 4;
458
459 // Read services (8 bytes, skip for now)
460 offset += 8;
461
462 // Read IP address (16 bytes)
463 struct in6_addr ip_addr;
464 memcpy(&ip_addr, payload_data + offset, 16);
465 offset += 16;
466
467 // Read port (2 bytes)
468 uint16_t port;
469 memcpy(&port, payload_data + offset, 2);
470 port = ntohs(port);
471 offset += 2;
472
473 // Convert IP to string
474 char ip_str[INET6_ADDRSTRLEN];
475 inet_ntop(AF_INET6, &ip_addr, ip_str, INET6_ADDRSTRLEN);
476
477 // Determine if the address is IPv4-mapped or IPv6
478 const char* ip_type = "IPv6";
479 if (IN6_IS_ADDR_V4MAPPED(&ip_addr))
480 {
481 struct in_addr ipv4_addr;
482 memcpy(&ipv4_addr, &ip_addr.s6_addr[12], 4);
483 inet_ntop(AF_INET, &ipv4_addr, ip_str, INET_ADDRSTRLEN);
484 ip_type = "IPv4";
485 }
486
487 // Check if it's a valid IPv4 address
488 if (strcmp(ip_type, "IPv4") == 0 && is_valid_ipv4(ip_str) && !
489 is_in_private_network(ip_str))
490 {
491 // Guarded print of the IP address and port
492 guarded_print_line("Valid IPv4 Peer: %s:%u (timestamp: %u)", ip_str,
493 port, timestamp);
494
495 // Add to peer queue
496 add_peer_to_queue(ip_str, port);
497
498 // Log the result if valid
499 log_message(LOG_INFO, log_filename, __FILE__,
500 "Received valid IPv4 address: %s:%u (timestamp: %u)",
501 ip_str, port, timestamp);
502 }
503 else if (strcmp(ip_type, "IPv6") == 0)
504 {
505 // Log the IPv6 addresses if needed
506 log_message(LOG_INFO, log_filename, __FILE__,
507 "Received IPv6 address: %s:%u (timestamp: %u)",
508 ip_str, port, timestamp);
509 }
510 }
511
512 if (offset != payload_len)
513 {
514 log_message(LOG_WARN, log_filename, __FILE__,
515 "Remaining bytes after processing: %zu",
516 payload_len - offset);
517 }
518 }
519 }
520 else
521 {
522 log_message(LOG_WARN, log_filename, __FILE__,
523 "else hdr->magic ");
524 }
525
526 node->operation_in_progress = 0;
527}
int is_in_private_network(const char *ip_addr)
Check if the IP address is in the private prefix (e.g.
Definition ip.c:98
void add_peer_to_queue(const char *ip, int port)
Add a peer to the queue.
Definition peer_queue.c:15
int is_valid_ipv4(const char *ip_str)
Check if the IP address is valid.
Definition utils.c:116

References add_peer_to_queue(), BITCOIN_MAINNET_MAGIC, build_message(), bitcoin_msg_header::command, guarded_print_line(), Node::ip_address, Node::is_connected, is_in_private_network(), is_valid_ipv4(), bitcoin_msg_header::length, LOG_INFO, log_message(), LOG_WARN, bitcoin_msg_header::magic, MAX_NODES, nodes, Node::operation_in_progress, read_var_int(), and Node::socket_fd.

Referenced by cli_getaddr().

◆ send_getblocks_and_wait()

void send_getblocks_and_wait ( int  idx)

Sends a 'getblocks' message to the peer and waits for a response.

This function sends a 'getblocks' message to the peer identified by the given index and waits for a response. It is used to request a list of blocks from the connected peer. The response is processed and the blocks are saved to a file.

Parameters
idxThe index of the peer in the nodes array.

Definition at line 1518 of file peer_connection.c.

1519{
1520 if (idx < 0 || idx >= MAX_NODES || !nodes[idx].is_connected)
1521 {
1522 fprintf(stderr, "[Error] Invalid node index or node not connected.\n");
1523 return;
1524 }
1525
1526 Node* node = &nodes[idx];
1527 char log_filename[256];
1528 snprintf(log_filename, sizeof(log_filename), "peer_connection_%s.log", node->ip_address);
1529
1530 // Define block locator and locator count
1531 unsigned char block_locator[MAX_LOCATOR_COUNT * 32];
1532 int locator_count = 1; // Set to 1 to request blocks starting from the latest known block
1533
1534 // Load the latest known block hash
1535 load_latest_known_block_hash(block_locator);
1536
1537 // Build the 'getblocks' message
1538 unsigned char getblocks_msg[sizeof(bitcoin_msg_header) + 4 + 1 + (MAX_LOCATOR_COUNT * 32) + 32];
1539 size_t msg_len = build_getblocks_message(getblocks_msg, sizeof(getblocks_msg), block_locator, locator_count);
1540 if (msg_len == 0)
1541 {
1542 fprintf(stderr, "[Error] Failed to build 'getblocks' message.\n");
1543 return;
1544 }
1545
1546 // Send the 'getblocks' message
1547 ssize_t bytes_sent = send(node->socket_fd, getblocks_msg, msg_len, 0);
1548 if (bytes_sent < 0)
1549 {
1550 log_message(LOG_INFO, log_filename, __FILE__,
1551 "[Error] Failed to send 'getblocks' message: %s", strerror(errno));
1552 return;
1553 }
1554
1555 log_message(LOG_INFO, log_filename, __FILE__, "Sent 'getblocks' message.");
1556 node->operation_in_progress = 1;
1557
1558 // Wait for 10 seconds for a response
1559 struct timeval tv;
1560 tv.tv_sec = 10;
1561 tv.tv_usec = 0;
1562 setsockopt(node->socket_fd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof(tv));
1563
1564 // Increase buffer size to handle larger responses
1565 unsigned char buffer[32768];
1566 size_t total_bytes_received = 0;
1567 ssize_t bytes_received;
1568
1569 // Receive the message in parts
1570 while ((bytes_received = recv(node->socket_fd, buffer + total_bytes_received, sizeof(buffer) - total_bytes_received - 1, 0)) > 0)
1571 {
1572 total_bytes_received += bytes_received;
1573 }
1574
1575 if (bytes_received < 0 && errno != EAGAIN && errno != EWOULDBLOCK)
1576 {
1577 log_message(LOG_INFO, log_filename, __FILE__,
1578 "[Error] Failed to receive response: %s", strerror(errno));
1579 node->operation_in_progress = 0;
1580 return;
1581 }
1582
1583 log_message(LOG_INFO, log_filename, __FILE__, "Received response to 'getblocks' message.");
1584 node->operation_in_progress = 0;
1585
1586 // Process the response and print to CLI
1587 guarded_print("Received response to 'getblocks' message:\n");
1588 parse_inv_message(buffer, total_bytes_received);
1589
1590 // Save the blocks to a file
1591 save_blocks_to_file(buffer, total_bytes_received, "blocks.dat");
1592}
size_t build_getblocks_message(unsigned char *buffer, size_t buffer_size, const unsigned char *block_locator, size_t locator_count)
void parse_inv_message(const unsigned char *payload, size_t payload_len)
void save_blocks_to_file(const unsigned char *payload, size_t payload_len, const char *filename)
void load_latest_known_block_hash(unsigned char *block_hash)
#define MAX_LOCATOR_COUNT

References build_getblocks_message(), guarded_print(), Node::ip_address, load_latest_known_block_hash(), LOG_INFO, log_message(), MAX_LOCATOR_COUNT, MAX_NODES, nodes, Node::operation_in_progress, parse_inv_message(), save_blocks_to_file(), and Node::socket_fd.

Referenced by cli_getblocks().

◆ send_getdata_and_wait()

void send_getdata_and_wait ( int  idx,
const unsigned char *  hashes,
size_t  hash_count 
)

Sends a 'getdata' message to the peer and waits for a response.

This function sends a 'getdata' message to the peer identified by the given index and waits for a response. It is used to request specific blocks or transactions from the connected peer based on the provided hashes. The response is saved to a file and logged to the Bitlab logs.

Parameters
idxThe index of the peer in the nodes array.
hashesAn array of hashes representing the blocks or transactions to request.
hash_countThe number of hashes in the array.

Definition at line 1728 of file peer_connection.c.

1729{
1730 if (idx < 0 || idx >= MAX_NODES || !nodes[idx].is_connected)
1731 {
1732 fprintf(stderr, "[Error] Invalid node index or node not connected.\n");
1733 return;
1734 }
1735
1736 if (hash_count == 0 || hash_count > 50000)
1737 {
1738 fprintf(stderr, "[Error] Invalid hash count. Must be between 1 and 50000.\n");
1739 return;
1740 }
1741
1742 Node* node = &nodes[idx];
1743 char log_filename[256];
1744 snprintf(log_filename, sizeof(log_filename), "peer_connection_%s.log", node->ip_address);
1745
1746 // Build the 'getdata' message
1747 unsigned char getdata_msg[sizeof(bitcoin_msg_header) + 1 + (hash_count * 36)];
1748 size_t msg_len = build_getdata_message(getdata_msg, sizeof(getdata_msg), hashes, hash_count);
1749 if (msg_len == 0)
1750 {
1751 fprintf(stderr, "[Error] Failed to build 'getdata' message.\n");
1752 return;
1753 }
1754
1755 // Send the 'getdata' message
1756 ssize_t bytes_sent = send(node->socket_fd, getdata_msg, msg_len, 0);
1757 if (bytes_sent < 0)
1758 {
1759 log_message(LOG_INFO, log_filename, __FILE__,
1760 "[Error] Failed to send 'getdata' message: %s", strerror(errno));
1761 return;
1762 }
1763
1764 log_message(LOG_INFO, log_filename, __FILE__, "Sent 'getdata' message.");
1765 node->operation_in_progress = 1;
1766
1767 // Wait for 20 seconds for a response (increased timeout)
1768 struct timeval tv;
1769 tv.tv_sec = 20;
1770 tv.tv_usec = 0;
1771 setsockopt(node->socket_fd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof(tv));
1772
1773 // Receive the response
1774 unsigned char buffer[32768];
1775 ssize_t bytes_received;
1776
1777 while ((bytes_received = recv(node->socket_fd, buffer, sizeof(buffer), 0)) > 0)
1778 {
1779 bitcoin_msg_header* hdr = (bitcoin_msg_header*)buffer;
1780 if (hdr->magic == BITCOIN_MAINNET_MAGIC)
1781 {
1782 char cmd_name[13];
1783 memset(cmd_name, 0, sizeof(cmd_name));
1784 memcpy(cmd_name, hdr->command, 12);
1785
1786 if (strcmp(cmd_name, "block") == 0)
1787 {
1788 log_message(LOG_INFO, log_filename, __FILE__, "Received 'block' message.");
1789 decode_transactions(buffer + sizeof(bitcoin_msg_header), hdr->length);
1790 }
1791 }
1792 }
1793
1794 if (bytes_received < 0 && errno != EAGAIN && errno != EWOULDBLOCK)
1795 {
1796 log_message(LOG_INFO, log_filename, __FILE__,
1797 "[Error] Failed to receive block message: %s", strerror(errno));
1798 }
1799
1800 node->operation_in_progress = 0;
1801}
void decode_transactions(const unsigned char *block_data, size_t block_len)
size_t build_getdata_message(unsigned char *buffer, size_t buffer_size, const unsigned char *hashes, size_t hash_count)

References BITCOIN_MAINNET_MAGIC, build_getdata_message(), bitcoin_msg_header::command, decode_transactions(), Node::ip_address, bitcoin_msg_header::length, LOG_INFO, log_message(), bitcoin_msg_header::magic, MAX_NODES, nodes, Node::operation_in_progress, and Node::socket_fd.

Referenced by cli_getdata(), and handle_inv_message().

◆ send_getheaders_and_wait()

void send_getheaders_and_wait ( int  idx)

Sends a 'getheaders' message to the peer and waits for a response.

This function sends a 'getheaders' message to the peer identified by the given index and waits for a response. It is used to request a list of known peers from the connected peer.

Parameters
idxThe index of the peer in the nodes array.

Definition at line 1330 of file peer_connection.c.

1331{
1332 if (idx < 0 || idx >= MAX_NODES || !nodes[idx].is_connected)
1333 {
1334 fprintf(stderr, "[Error] Invalid node index or node not connected.\n");
1335 return;
1336 }
1337
1338 Node* node = &nodes[idx];
1339 char log_filename[256];
1340 snprintf(log_filename, sizeof(log_filename), "peer_connection_%s.log", node->ip_address);
1341
1342 // Define block locator and locator count
1343 unsigned char block_locator[MAX_LOCATOR_COUNT * 32];
1344 int locator_count = 1; // Set to 1 to request headers starting from the latest known block
1345
1346 // Load the latest known block hash
1347 load_latest_known_block_hash(block_locator);
1348
1349 // Build the 'getheaders' message
1350 unsigned char getheaders_msg[sizeof(bitcoin_msg_header) + 4 + 1 + (MAX_LOCATOR_COUNT * 32) + 32];
1351 size_t msg_len = build_getheaders_message(getheaders_msg, sizeof(getheaders_msg), block_locator, locator_count);
1352 if (msg_len == 0)
1353 {
1354 fprintf(stderr, "[Error] Failed to build 'getheaders' message.\n");
1355 return;
1356 }
1357
1358 // Send the 'getheaders' message
1359 ssize_t bytes_sent = send(node->socket_fd, getheaders_msg, msg_len, 0);
1360 if (bytes_sent < 0)
1361 {
1362 log_message(LOG_INFO, log_filename, __FILE__,
1363 "[Error] Failed to send 'getheaders' message: %s", strerror(errno));
1364 return;
1365 }
1366
1367 log_message(LOG_INFO, log_filename, __FILE__, "Sent 'getheaders' message.");
1368 node->operation_in_progress = 1;
1369
1370 // Wait for 10 seconds for a response
1371 struct timeval tv;
1372 tv.tv_sec = 10;
1373 tv.tv_usec = 0;
1374 setsockopt(node->socket_fd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof(tv));
1375
1376 // Receive the response
1377 unsigned char buffer[4096];
1378 ssize_t bytes_received = recv(node->socket_fd, buffer, sizeof(buffer), 0);
1379 if (bytes_received < 0)
1380 {
1381 log_message(LOG_INFO, log_filename, __FILE__,
1382 "[Error] Failed to receive response: %s", strerror(errno));
1383 node->operation_in_progress = 0;
1384 return;
1385 }
1386
1387 log_message(LOG_INFO, log_filename, __FILE__, "Received response to 'getheaders' message.");
1388 node->operation_in_progress = 0;
1389
1390 // Process the response and print to CLI
1391 guarded_print("Received response to 'getheaders' message:\n");
1392 parse_headers_message(buffer, bytes_received);
1393}
size_t build_getheaders_message(unsigned char *buffer, size_t buffer_size, const unsigned char *block_locator, size_t locator_count)
void parse_headers_message(const unsigned char *payload, size_t payload_len)

References build_getheaders_message(), guarded_print(), Node::ip_address, load_latest_known_block_hash(), LOG_INFO, log_message(), MAX_LOCATOR_COUNT, MAX_NODES, nodes, Node::operation_in_progress, parse_headers_message(), and Node::socket_fd.

Referenced by cli_getheaders().

◆ send_headers()

void send_headers ( int  idx,
const unsigned char *  start_hash,
const unsigned char *  stop_hash 
)

Sends a 'headers' message to the peer.

This function sends a 'headers' message to the peer identified by the given index. It retrieves the block headers from the local storage starting from the specified start hash up to the stop hash or the maximum number of headers allowed.

Parameters
idxThe index of the peer in the nodes array.
start_hashThe hash of the first block header to send.
stop_hashThe hash of the last block header to send.

Definition at line 1395 of file peer_connection.c.

1396{
1397 if (idx < 0 || idx >= MAX_NODES || !nodes[idx].is_connected)
1398 {
1399 fprintf(stderr, "[Error] Invalid node index or node not connected.\n");
1400 return;
1401 }
1402
1403 Node* node = &nodes[idx];
1404 char log_filename[256];
1405 snprintf(log_filename, sizeof(log_filename), "peer_connection_%s.log", node->ip_address);
1406
1407 // Open the headers file
1408 FILE* file = fopen(HEADERS_FILE, "rb");
1409 if (!file)
1410 {
1411 perror("Failed to open headers file");
1412 return;
1413 }
1414
1415 // Find the starting block header
1416 unsigned char buffer[81];
1417 size_t headers_count = 0;
1418 int found_start = 0;
1419 while (fread(buffer, 80, 1, file) == 1)
1420 {
1421 if (memcmp(buffer, start_hash, 32) == 0)
1422 {
1423 found_start = 1;
1424 break;
1425 }
1426 }
1427
1428 if (!found_start)
1429 {
1430 fclose(file);
1431 fprintf(stderr, "[Error] Start hash not found in headers file.\n");
1432 return;
1433 }
1434
1435 // Build the 'headers' message
1436 unsigned char headers_msg[sizeof(bitcoin_msg_header) + 1 + (MAX_HEADERS_COUNT * 81)];
1437 size_t offset = sizeof(bitcoin_msg_header);
1438 headers_msg[offset++] = 0; // Placeholder for count
1439
1440 while (headers_count < MAX_HEADERS_COUNT && fread(buffer, 80, 1, file) == 1)
1441 {
1442 memcpy(headers_msg + offset, buffer, 80);
1443 offset += 80;
1444 headers_msg[offset++] = 0; // Transaction count (var_int, 0 for headers only)
1445 headers_count++;
1446
1447 if (memcmp(buffer, stop_hash, 32) == 0)
1448 {
1449 break;
1450 }
1451 }
1452
1453 fclose(file);
1454
1455 // Set the count
1456 headers_msg[sizeof(bitcoin_msg_header)] = headers_count;
1457
1458 // Build the message header
1459 bitcoin_msg_header* header = (bitcoin_msg_header*)headers_msg;
1461 strncpy(header->command, "headers", 12);
1462 header->length = htole32(offset - sizeof(bitcoin_msg_header));
1463 compute_checksum(headers_msg + sizeof(bitcoin_msg_header), offset - sizeof(bitcoin_msg_header), header->checksum);
1464
1465 // Send the 'headers' message
1466 ssize_t bytes_sent = send(node->socket_fd, headers_msg, offset, 0);
1467 if (bytes_sent < 0)
1468 {
1469 log_message(LOG_INFO, log_filename, __FILE__,
1470 "[Error] Failed to send 'headers' message: %s", strerror(errno));
1471 return;
1472 }
1473
1474 log_message(LOG_INFO, log_filename, __FILE__, "Sent 'headers' message.");
1475}
#define MAX_HEADERS_COUNT
#define htole32(x)

References BITCOIN_MAINNET_MAGIC, compute_checksum(), HEADERS_FILE, htole32, Node::ip_address, LOG_INFO, log_message(), bitcoin_msg_header::magic, MAX_HEADERS_COUNT, MAX_NODES, nodes, and Node::socket_fd.

Referenced by peer_communication().

◆ send_inv_and_wait()

void send_inv_and_wait ( int  idx,
const unsigned char *  inv_data,
size_t  inv_count 
)

Sends an 'inv' message to the peer.

Sends an 'inv' message to the peer and waits for a response.

This function sends an 'inv' message to the peer identified by the given index. It is used to advertise the knowledge of one or more objects (blocks or transactions). The inventory data is provided as input to the function.

Parameters
idxThe index of the peer in the nodes array.
inv_dataAn array of inventory vectors (type + hash).
inv_countThe number of inventory vectors in the array.

Definition at line 1866 of file peer_connection.c.

1867{
1868 if (idx < 0 || idx >= MAX_NODES || !nodes[idx].is_connected)
1869 {
1870 fprintf(stderr, "[Error] Invalid node index or node not connected.\n");
1871 return;
1872 }
1873
1874 if (inv_count == 0 || inv_count > 50000)
1875 {
1876 fprintf(stderr, "[Error] Invalid inventory count. Must be between 1 and 50000.\n");
1877 return;
1878 }
1879
1880 Node* node = &nodes[idx];
1881 char log_filename[256];
1882 snprintf(log_filename, sizeof(log_filename), "peer_connection_%s.log", node->ip_address);
1883
1884 size_t var_int_size = write_var_int(NULL, inv_count); // Get the size of the var_int encoding
1885 size_t payload_size = var_int_size + (inv_count * 36); // var_int size + 36 bytes per inventory vector
1886 size_t inv_msg_size = sizeof(bitcoin_msg_header) + payload_size;
1887
1888 printf("inv_count: %zu, inv_msg_size: %zu\n", inv_count, inv_msg_size);
1889
1890 unsigned char* inv_msg = (unsigned char*)malloc(inv_msg_size);
1891 if (!inv_msg)
1892 {
1893 fprintf(stderr, "[Error] Failed to allocate memory for 'inv' message.\n");
1894 return;
1895 }
1896
1897 size_t msg_len = build_inv_message(inv_msg, inv_msg_size, inv_data, inv_count);
1898 printf("msg_len: %zu\n", msg_len);
1899 if (msg_len == 0)
1900 {
1901 fprintf(stderr, "[Error] Failed to build 'inv' message.\n");
1902 free(inv_msg);
1903 return;
1904 }
1905
1906 // Send the 'inv' message
1907 ssize_t bytes_sent = send(node->socket_fd, inv_msg, msg_len, 0);
1908 if (bytes_sent < 0)
1909 {
1910 log_message(LOG_INFO, log_filename, __FILE__,
1911 "[Error] Failed to send 'inv' message: %s", strerror(errno));
1912 free(inv_msg);
1913 return;
1914 }
1915
1916 log_message(LOG_INFO, log_filename, __FILE__, "Sent 'inv' message.");
1917 printf("Sent 'inv' message.\n");
1918 node->operation_in_progress = 1;
1919
1920 // Wait for 10 seconds for a response
1921 struct timeval tv;
1922 tv.tv_sec = 10;
1923 tv.tv_usec = 0;
1924 setsockopt(node->socket_fd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof(tv));
1925
1926 // Receive the response
1927 unsigned char buffer[32768];
1928 ssize_t bytes_received = recv(node->socket_fd, buffer, sizeof(buffer), 0);
1929 if (bytes_received < 0)
1930 {
1931 log_message(LOG_INFO, log_filename, __FILE__,
1932 "[Error] Failed to receive response: %s", strerror(errno));
1933 node->operation_in_progress = 0;
1934 free(inv_msg);
1935 return;
1936 }
1937
1938 log_message(LOG_INFO, log_filename, __FILE__, "Received response to 'inv' message.");
1939 printf("Received response to 'inv' message.\n");
1940 node->operation_in_progress = 0;
1941
1942 // Process the response
1943 if (bytes_received >= (ssize_t)sizeof(bitcoin_msg_header))
1944 {
1945 bitcoin_msg_header* hdr = (bitcoin_msg_header*)buffer;
1946 if (hdr->magic == BITCOIN_MAINNET_MAGIC)
1947 {
1948 char cmd_name[13];
1949 memset(cmd_name, 0, sizeof(cmd_name));
1950 memcpy(cmd_name, hdr->command, 12);
1951
1952 printf("Received command: '%s'\n", cmd_name);
1953
1954 if (strcmp(cmd_name, "inv") == 0)
1955 {
1956 printf("Received 'inv' response:\n");
1957 handle_inv_message(idx, buffer + sizeof(bitcoin_msg_header), bytes_received - sizeof(bitcoin_msg_header));
1958 }
1959 else
1960 {
1961 printf("Unhandled command: '%s'\n", cmd_name);
1962 }
1963 }
1964 else
1965 {
1966 printf("Unexpected magic bytes (0x%08X).\n", hdr->magic);
1967 }
1968 }
1969 else
1970 {
1971 printf("Received incomplete message.\n");
1972 }
1973
1974 free(inv_msg);
1975}
size_t build_inv_message(unsigned char *buffer, size_t buffer_size, const unsigned char *inv_data, size_t inv_count)
Builds an 'inv' message.

References BITCOIN_MAINNET_MAGIC, build_inv_message(), bitcoin_msg_header::command, handle_inv_message(), Node::ip_address, LOG_INFO, log_message(), bitcoin_msg_header::magic, MAX_NODES, nodes, Node::operation_in_progress, Node::socket_fd, and write_var_int().

Referenced by cli_inv().

◆ send_ping()

ssize_t send_ping ( int  sockfd,
const char *  ip_addr 
)

Definition at line 693 of file peer_connection.c.

694{
695 // Generate an 8-byte nonce
696 uint64_t nonce = ((uint64_t)rand() << 32) | rand();
697
698 // Prepare the ping payload (8 bytes)
699 unsigned char ping_payload[8];
700 memcpy(ping_payload, &nonce, sizeof(nonce));
701
702 // Prepare the complete message (header + payload)
703 unsigned char ping_msg[sizeof(bitcoin_msg_header) + sizeof(ping_payload)];
704 size_t msg_len = build_message(
705 ping_msg,
706 sizeof(ping_msg),
707 "ping",
708 ping_payload,
709 sizeof(ping_payload)
710 );
711
712 if (msg_len == 0)
713 {
714 fprintf(stderr, "[Error] Failed to build 'ping' message.\n");
715 return -1;
716 }
717
718 char log_filename[256];
719 snprintf(log_filename, sizeof(log_filename), "peer_connection_%s.log",
720 ip_addr);
721 ssize_t bytes_sent = send(sockfd, ping_msg, msg_len, 0);
722 if (bytes_sent < 0)
723 {
724 log_message(LOG_INFO, log_filename, __FILE__,
725 "[Error] Failed to send 'ping' message: %s", strerror(errno));
726 return -1;
727 }
728
729 log_message(LOG_INFO, log_filename, __FILE__,
730 "Sent 'ping' message with nonce: %llu", (unsigned long long)nonce);
731 return bytes_sent;
732}

References build_message(), LOG_INFO, and log_message().

Referenced by peer_communication().

◆ send_pong()

static ssize_t send_pong ( int  sockfd,
const unsigned char *  nonce8 
)
static

send_pong: Send a "pong" message echoing the same 8-byte nonce from a "ping" payload.

Definition at line 669 of file peer_connection.c.

670{
671 // Build an 8-byte payload containing the same nonce
672 unsigned char pong_payload[8];
673 memcpy(pong_payload, nonce8, 8);
674
675 // Create the message buffer
676 unsigned char pong_msg[sizeof(bitcoin_msg_header) + 8];
677 size_t msg_len = build_message(
678 pong_msg,
679 sizeof(pong_msg),
680 "pong",
681 pong_payload,
682 8
683 );
684 if (msg_len == 0)
685 {
686 fprintf(stderr, "[Error] build_message failed for 'pong'.\n");
687 return -1;
688 }
689
690 return send(sockfd, pong_msg, msg_len, 0);
691}

References build_message().

Referenced by peer_communication().

◆ send_tx()

void send_tx ( int  idx,
const unsigned char *  tx_data,
size_t  tx_size 
)

Sends a 'tx' message to the specified node.

This function sends a 'tx' message to the node identified by the given index with the provided transaction data. It constructs the message with the appropriate Bitcoin protocol header and sends it over the network socket associated with the node.

Parameters
idxThe index of the node in the nodes array.
tx_dataA pointer to the transaction data in hexadecimal format.
tx_sizeThe size of the transaction data in bytes.

Definition at line 2047 of file peer_connection.c.

2048{
2049 if (idx < 0 || idx >= MAX_NODES || !nodes[idx].is_connected)
2050 {
2051 fprintf(stderr, "[Error] Invalid node index or node not connected.\n");
2052 return;
2053 }
2054
2055 Node* node = &nodes[idx];
2056 char log_filename[256];
2057 snprintf(log_filename, sizeof(log_filename), "peer_connection_%s.log", node->ip_address);
2058
2059 if (!tx_data || tx_size == 0)
2060 {
2061 fprintf(stderr, "[Error] Invalid transaction data.\n");
2062 return;
2063 }
2064
2065 // Build the 'tx' message with the appropriate header
2066 unsigned char tx_msg[sizeof(bitcoin_msg_header) + tx_size];
2067
2068 // Build the message header
2069 bitcoin_msg_header* header = (bitcoin_msg_header*)tx_msg;
2071 strncpy(header->command, "tx", 12);
2072 header->length = htole32(tx_size);
2073 compute_checksum(tx_data, tx_size, header->checksum);
2074
2075 // Copy the transaction data
2076 memcpy(tx_msg + sizeof(bitcoin_msg_header), tx_data, tx_size);
2077
2078 // Send the 'tx' message
2079 ssize_t bytes_sent = send(node->socket_fd, tx_msg, sizeof(bitcoin_msg_header) + tx_size, 0);
2080 if (bytes_sent < 0)
2081 {
2082 log_message(LOG_INFO, log_filename, __FILE__,
2083 "[Error] Failed to send 'tx' message: %s", strerror(errno));
2084 return;
2085 }
2086
2087 log_message(LOG_INFO, log_filename, __FILE__, "Sent 'tx' message (%zu bytes).", tx_size);
2088}

References BITCOIN_MAINNET_MAGIC, compute_checksum(), htole32, Node::ip_address, LOG_INFO, log_message(), bitcoin_msg_header::magic, MAX_NODES, nodes, and Node::socket_fd.

Referenced by cli_tx().

◆ send_verack()

static ssize_t send_verack ( int  sockfd,
const char *  ip_addr 
)
static

send_verack: Constructs a verack message with an empty payload (length = 0) and a valid 4-byte checksum for an empty payload.

Definition at line 625 of file peer_connection.c.

626{
627 unsigned char verack_msg[sizeof(bitcoin_msg_header)];
628 memset(verack_msg, 0, sizeof(verack_msg));
629
630 bitcoin_msg_header verack_header;
631 memset(&verack_header, 0, sizeof(verack_header));
632 verack_header.magic = BITCOIN_MAINNET_MAGIC;
633
634 // "verack" as a 12-byte command (zero-padded)
635 {
636 const char* cmd = "verack";
637 memset(verack_header.command, 0, sizeof(verack_header.command));
638 size_t cmd_len = strlen(cmd);
639 if (cmd_len > sizeof(verack_header.command))
640 {
641 cmd_len = sizeof(verack_header.command);
642 }
643 memcpy(verack_header.command, cmd, cmd_len);
644 }
645
646 verack_header.length = 0; // no payload
647
648 // Checksum for empty payload => double-SHA256("") => first 4 bytes
649 unsigned char csum[4];
650 compute_checksum(NULL, 0, csum);
651 memcpy(&verack_header.checksum, csum, 4);
652
653 // Copy to buffer
654 memcpy(verack_msg, &verack_header, sizeof(verack_header));
655
656 char log_filename[256];
657 snprintf(log_filename, sizeof(log_filename), "peer_connection_%s.log", ip_addr);
658 log_message(LOG_INFO, log_filename, __FILE__,
659 "sent verack");
660 // log_to_file('test.log', 'sent verack');
661 // Send to peer
662 return send(sockfd, verack_msg, sizeof(verack_header), 0);
663}

References BITCOIN_MAINNET_MAGIC, bitcoin_msg_header::checksum, bitcoin_msg_header::command, compute_checksum(), bitcoin_msg_header::length, LOG_INFO, log_message(), and bitcoin_msg_header::magic.

Referenced by connect_to_peer().

◆ write_var_int()

size_t write_var_int ( unsigned char *  buf,
uint64_t  value 
)

Definition at line 216 of file peer_connection.c.

217{
218 if (buffer == NULL)
219 {
220 // Calculate the size of the var_int encoding
221 if (value < 0xfd)
222 return 1;
223 else if (value <= 0xffff)
224 return 3;
225 else if (value <= 0xffffffff)
226 return 5;
227 else
228 return 9;
229 }
230
231 log_message(LOG_DEBUG, BITLAB_LOG, __FILE__, "write_var_int: buffer=%p, value=%lu", (void*)buffer, value);
232
233 if (value < 0xfd)
234 {
235 buffer[0] = (unsigned char)value;
236 return 1;
237 }
238 else if (value <= 0xffff)
239 {
240 buffer[0] = 0xfd;
241 buffer[1] = (unsigned char)(value & 0xff);
242 buffer[2] = (unsigned char)((value >> 8) & 0xff);
243 return 3;
244 }
245 else if (value <= 0xffffffff)
246 {
247 buffer[0] = 0xfe;
248 buffer[1] = (unsigned char)(value & 0xff);
249 buffer[2] = (unsigned char)((value >> 8) & 0xff);
250 buffer[3] = (unsigned char)((value >> 16) & 0xff);
251 buffer[4] = (unsigned char)((value >> 24) & 0xff);
252 return 5;
253 }
254 else
255 {
256 buffer[0] = 0xff;
257 buffer[1] = (unsigned char)(value & 0xff);
258 buffer[2] = (unsigned char)((value >> 8) & 0xff);
259 buffer[3] = (unsigned char)((value >> 16) & 0xff);
260 buffer[4] = (unsigned char)((value >> 24) & 0xff);
261 buffer[5] = (unsigned char)((value >> 32) & 0xff);
262 buffer[6] = (unsigned char)((value >> 40) & 0xff);
263 buffer[7] = (unsigned char)((value >> 48) & 0xff);
264 buffer[8] = (unsigned char)((value >> 56) & 0xff);
265 return 9;
266 }
267}

References BITLAB_LOG, LOG_DEBUG, and log_message().

Referenced by build_getblocks_message(), build_getdata_message(), build_getheaders_message(), build_inv_message(), and send_inv_and_wait().

Variable Documentation

◆ nodes