Skip to content

Commit 5afb2ca

Browse files
committed
🚧 init p2p tcp connections
1 parent 8798e20 commit 5afb2ca

File tree

5 files changed

+292
-19
lines changed

5 files changed

+292
-19
lines changed

‎src/config.zig‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ pub const Config = struct {
2020
/// Data directory
2121
datadir: []const u8,
2222

23+
seednode: []const u8,
24+
2325
/// Load the configuration from a file
2426
///
2527
/// # Arguments
@@ -44,6 +46,7 @@ pub const Config = struct {
4446
.p2p_port = 8333,
4547
.testnet = false,
4648
.datadir = try allocator.dupe(u8, ".bitcoin"),
49+
.seednode = "",
4750
};
4851

4952
var buf: [1024]u8 = undefined;

‎src/mempool.zig‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,7 @@ test "Mempool" {
190190
.p2p_port = 8333,
191191
.testnet = false,
192192
.datadir = "/tmp/btczee",
193+
.seednode = "",
193194
};
194195
var mempool = try Mempool.init(allocator, &config);
195196
defer mempool.deinit();

‎src/p2p.zig‎

Lines changed: 78 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,103 @@
11
const std = @import("std");
2+
const net = std.net;
23
const Config = @import("config.zig").Config;
4+
const Peer = @import("peer.zig").Peer;
35

46
/// P2P network handler.
5-
///
6-
/// The P2P network is responsible for handling the peer-to-peer network.
7-
/// It is responsible for handling the network protocol, the block relay, and the node sync.
87
pub const P2P = struct {
98
allocator: std.mem.Allocator,
109
config: *const Config,
10+
peers: std.ArrayList(*Peer),
11+
listener: ?net.Server,
1112

1213
/// Initialize the P2P network
13-
///
14-
/// # Arguments
15-
/// - `allocator`: Memory allocator
16-
/// - `config`: Configuration
17-
///
18-
/// # Returns
19-
/// - `P2P`: P2P network handler
2014
pub fn init(allocator: std.mem.Allocator, config: *const Config) !P2P {
2115
return P2P{
2216
.allocator = allocator,
2317
.config = config,
18+
.peers = std.ArrayList(*Peer).init(allocator),
19+
.listener = null,
2420
};
2521
}
2622

2723
/// Deinitialize the P2P network
28-
///
29-
/// # Arguments
30-
/// - `self`: P2P network handler
3124
pub fn deinit(self: *P2P) void {
32-
// Clean up resources if needed
33-
_ = self;
25+
if (self.listener) |*l| l.deinit();
26+
for (self.peers.items) |peer| {
27+
peer.deinit();
28+
}
29+
self.peers.deinit();
3430
}
3531

3632
/// Start the P2P network
37-
///
38-
/// # Arguments
39-
/// - `self`: P2P network handler
4033
pub fn start(self: *P2P) !void {
4134
std.log.info("Starting P2P network on port {}", .{self.config.p2p_port});
42-
// Implement P2P network initialization
35+
36+
// Initialize the listener
37+
// const address = try net.Address.parseIp4("0.0.0.0", self.config.p2p_port);
38+
// const stream = try net.tcpConnectToAddress(address);
39+
40+
// self.listener = net.Server{
41+
// .listen_address = address,
42+
// .stream = stream,
43+
// };
44+
45+
// // Start accepting connections
46+
// try self.acceptConnections();
47+
48+
// // Connect to seed nodes
49+
// try self.connectToSeedNodes();
50+
}
51+
52+
/// Accept incoming connections
53+
fn acceptConnections(self: *P2P) !void {
54+
while (true) {
55+
const connection = self.listener.?.accept() catch |err| {
56+
std.log.err("Failed to accept connection: {}", .{err});
57+
continue;
58+
};
59+
60+
// Handle the new connection in a separate thread
61+
// TODO: Error handling
62+
_ = try std.Thread.spawn(.{}, handleConnection, .{ self, connection });
63+
}
64+
}
65+
66+
/// Handle a new connection
67+
fn handleConnection(self: *P2P, connection: net.Server.Connection) void {
68+
const peer = Peer.init(self.allocator, connection) catch |err| {
69+
std.log.err("Failed to initialize peer: {}", .{err});
70+
connection.stream.close();
71+
return;
72+
};
73+
74+
self.peers.append(peer) catch |err| {
75+
std.log.err("Failed to add peer: {}", .{err});
76+
peer.deinit();
77+
return;
78+
};
79+
80+
peer.start() catch |err| {
81+
std.log.err("Peer encountered an error: {}", .{err});
82+
_ = self.peers.swapRemove(self.peers.items.len - 1);
83+
peer.deinit();
84+
};
85+
}
86+
87+
/// Connect to seed nodes
88+
fn connectToSeedNodes(self: *P2P) !void {
89+
if (self.config.seednode.len == 0) {
90+
return;
91+
}
92+
93+
const address = try net.Address.parseIp4(self.config.seednode, 8333);
94+
const stream = try net.tcpConnectToAddress(address);
95+
96+
const peer = try Peer.init(self.allocator, .{ .stream = stream, .address = address });
97+
try self.peers.append(peer);
98+
99+
// Start the peer in a new thread
100+
// TODO: Error handling
101+
_ = try std.Thread.spawn(.{}, Peer.start, .{peer});
43102
}
44103
};

‎src/peer.zig‎

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
const std = @import("std");
2+
const net = std.net;
3+
const protocol = @import("protocol.zig");
4+
5+
/// Represents a peer connection in the Bitcoin network
6+
pub const Peer = struct {
7+
allocator: std.mem.Allocator,
8+
stream: net.Stream,
9+
address: net.Address,
10+
version: ?protocol.VersionMessage,
11+
last_seen: i64,
12+
is_outbound: bool,
13+
14+
/// Initialize a new peer
15+
pub fn init(allocator: std.mem.Allocator, connection: net.Server.Connection) !*Peer {
16+
const peer = try allocator.create(Peer);
17+
peer.* = .{
18+
.allocator = allocator,
19+
.stream = connection.stream,
20+
.address = connection.address,
21+
.version = null,
22+
.last_seen = std.time.timestamp(),
23+
.is_outbound = false,
24+
};
25+
return peer;
26+
}
27+
28+
/// Clean up peer resources
29+
pub fn deinit(self: *Peer) void {
30+
self.stream.close();
31+
self.allocator.destroy(self);
32+
}
33+
34+
/// Start peer operations
35+
pub fn start(self: *Peer) !void {
36+
std.log.info("Starting peer connection with {}", .{self.address});
37+
38+
try self.sendVersionMessage();
39+
try self.handleMessages();
40+
}
41+
42+
/// Send version message to peer
43+
fn sendVersionMessage(self: *Peer) !void {
44+
const version_msg = protocol.VersionMessage{
45+
.version = 70015,
46+
.services = 1,
47+
.timestamp = @intCast(std.time.timestamp()),
48+
.addr_recv = protocol.NetworkAddress.init(self.address),
49+
};
50+
51+
try self.sendMessage("version", version_msg);
52+
}
53+
54+
/// Handle incoming messages from peer
55+
fn handleMessages(self: *Peer) !void {
56+
var buffer: [1024]u8 = undefined;
57+
58+
while (true) {
59+
const bytes_read = try self.stream.read(&buffer);
60+
if (bytes_read == 0) break; // Connection closed
61+
62+
// Mock message parsing
63+
const message_type = self.parseMessageType(buffer[0..bytes_read]);
64+
try self.handleMessage(message_type, buffer[0..bytes_read]);
65+
66+
self.last_seen = std.time.timestamp();
67+
}
68+
}
69+
70+
/// Mock function to parse message type
71+
fn parseMessageType(self: *Peer, data: []const u8) []const u8 {
72+
_ = self;
73+
if (std.mem.startsWith(u8, data, "version")) {
74+
return "version";
75+
} else if (std.mem.startsWith(u8, data, "verack")) {
76+
return "verack";
77+
} else {
78+
return "unknown";
79+
}
80+
}
81+
82+
/// Handle a specific message type
83+
fn handleMessage(self: *Peer, message_type: []const u8, data: []const u8) !void {
84+
if (std.mem.eql(u8, message_type, "version")) {
85+
try self.handleVersionMessage(data);
86+
} else if (std.mem.eql(u8, message_type, "verack")) {
87+
try self.handleVerackMessage();
88+
} else {
89+
std.log.warn("Received unknown message type from peer", .{});
90+
}
91+
}
92+
93+
/// Handle version message
94+
fn handleVersionMessage(self: *Peer, data: []const u8) !void {
95+
_ = data; // In a real implementation, parse the version message
96+
97+
// Mock version message handling
98+
self.version = protocol.VersionMessage{
99+
.version = 70015,
100+
.services = 1,
101+
.timestamp = @intCast(std.time.timestamp()),
102+
.addr_recv = protocol.NetworkAddress.init(self.address),
103+
// ... other fields ...
104+
};
105+
106+
try self.sendMessage("verack", {});
107+
}
108+
109+
/// Handle verack message
110+
fn handleVerackMessage(self: *Peer) !void {
111+
std.log.info("Received verack from peer {}", .{self.address});
112+
// In a real implementation, mark the connection as established
113+
}
114+
115+
/// Send a message to the peer
116+
fn sendMessage(self: *Peer, command: []const u8, message: anytype) !void {
117+
_ = message;
118+
// In a real implementation, serialize the message and send it
119+
try self.stream.writer().print("{s}\n", .{command});
120+
}
121+
};

‎src/protocol.zig‎

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
const std = @import("std");
2+
const net = std.net;
3+
4+
/// Protocol version
5+
pub const PROTOCOL_VERSION: u32 = 70015;
6+
7+
/// Network services
8+
pub const ServiceFlags = struct {
9+
pub const NODE_NETWORK: u64 = 1;
10+
pub const NODE_GETUTXO: u64 = 2;
11+
pub const NODE_BLOOM: u64 = 4;
12+
pub const NODE_WITNESS: u64 = 8;
13+
pub const NODE_NETWORK_LIMITED: u64 = 1024;
14+
};
15+
16+
/// Command string length
17+
pub const COMMAND_SIZE: usize = 12;
18+
19+
/// Magic bytes for mainnet
20+
pub const MAGIC_BYTES: [4]u8 = .{ 0xF9, 0xBE, 0xB4, 0xD9 };
21+
22+
/// NetworkAddress represents a network address
23+
pub const NetworkAddress = struct {
24+
services: u64,
25+
ip: [16]u8,
26+
port: u16,
27+
28+
pub fn init(address: net.Address) NetworkAddress {
29+
const result = NetworkAddress{
30+
.services = ServiceFlags.NODE_NETWORK,
31+
.ip = [_]u8{0} ** 16,
32+
.port = address.getPort(),
33+
};
34+
// TODO: Handle untagged union properly (for IPv6)
35+
36+
return result;
37+
}
38+
};
39+
40+
/// VersionMessage represents the "version" message
41+
pub const VersionMessage = struct {
42+
version: i32,
43+
services: u64,
44+
timestamp: i64,
45+
addr_recv: NetworkAddress,
46+
addr_from: NetworkAddress = .{
47+
.services = 0,
48+
.ip = [_]u8{0} ** 16,
49+
.port = 0,
50+
},
51+
nonce: u64 = 0,
52+
user_agent: []const u8 = "",
53+
start_height: i32 = 0,
54+
relay: bool = false,
55+
};
56+
57+
/// Header structure for all messages
58+
pub const MessageHeader = struct {
59+
magic: [4]u8,
60+
command: [COMMAND_SIZE]u8,
61+
length: u32,
62+
checksum: u32,
63+
};
64+
65+
/// Serialize a message to bytes
66+
pub fn serializeMessage(allocator: std.mem.Allocator, command: []const u8, payload: anytype) ![]u8 {
67+
_ = allocator;
68+
_ = command;
69+
_ = payload;
70+
// In a real implementation, this would serialize the message
71+
// For now, we'll just return a mock serialized message
72+
return "serialized message";
73+
}
74+
75+
/// Deserialize bytes to a message
76+
pub fn deserializeMessage(allocator: std.mem.Allocator, bytes: []const u8) !void {
77+
_ = allocator;
78+
_ = bytes;
79+
// In a real implementation, this would deserialize the message
80+
// For now, we'll just do nothing
81+
}
82+
83+
/// Calculate checksum for a message
84+
pub fn calculateChecksum(data: []const u8) u32 {
85+
_ = data;
86+
// In a real implementation, this would calculate the checksum
87+
// For now, we'll just return a mock checksum
88+
return 0x12345678;
89+
}

0 commit comments

Comments
 (0)