Skip to content

Commit

Permalink
feat: pong (#126)
Browse files Browse the repository at this point in the history
  • Loading branch information
aditya172926 authored Sep 26, 2024
1 parent 4d9a459 commit b2a8ad3
Show file tree
Hide file tree
Showing 3 changed files with 136 additions and 1 deletion.
6 changes: 6 additions & 0 deletions src/network/protocol/messages/lib.zig
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ pub const MempoolMessage = @import("mempool.zig").MempoolMessage;
pub const GetaddrMessage = @import("getaddr.zig").GetaddrMessage;
pub const GetblocksMessage = @import("getblocks.zig").GetblocksMessage;
pub const PingMessage = @import("ping.zig").PingMessage;
pub const PongMessage = @import("pong.zig").PongMessage;

pub const MessageTypes = enum {
Version,
Expand All @@ -13,6 +14,7 @@ pub const MessageTypes = enum {
Getaddr,
Getblocks,
Ping,
Pong,
};

pub const Message = union(MessageTypes) {
Expand All @@ -22,6 +24,7 @@ pub const Message = union(MessageTypes) {
Getaddr: GetaddrMessage,
Getblocks: GetblocksMessage,
Ping: PingMessage,
Pong: PongMessage,

pub fn deinit(self: Message, allocator: std.mem.Allocator) void {
switch (self) {
Expand All @@ -31,6 +34,7 @@ pub const Message = union(MessageTypes) {
.Getaddr => {},
.Getblocks => |m| m.deinit(allocator),
.Ping => {},
.Pong => {},
}
}
pub fn checksum(self: Message) [4]u8 {
Expand All @@ -41,6 +45,7 @@ pub const Message = union(MessageTypes) {
.Getaddr => |m| m.checksum(),
.Getblocks => |m| m.checksum(),
.Ping => |m| m.checksum(),
.Pong => |m| m.checksum(),
};
}

Expand All @@ -52,6 +57,7 @@ pub const Message = union(MessageTypes) {
.Getaddr => |m| m.hintSerializedLen(),
.Getblocks => |m| m.hintSerializedLen(),
.Ping => |m| m.hintSerializedLen(),
.Pong => |m| m.hintSerializedLen(),
};
}
};
104 changes: 104 additions & 0 deletions src/network/protocol/messages/pong.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
const std = @import("std");
const protocol = @import("../lib.zig");
const Sha256 = std.crypto.hash.sha2.Sha256;

/// PongMessage represents the "Pong" message
///
/// https://developer.bitcoin.org/reference/p2p_networking.html#pong
pub const PongMessage = struct {
nonce: u64,

const Self = @This();

pub inline fn name() *const [12]u8 {
return protocol.CommandNames.PONG ++ [_]u8{0} ** 8;
}

/// Returns the message checksum
///
/// Computed as `Sha256(Sha256(self.serialize()))[0..4]`
pub fn checksum(self: *const Self) [4]u8 {
var digest: [32]u8 = undefined;
var hasher = Sha256.init(.{});
const writer = hasher.writer();
self.serializeToWriter(writer) catch unreachable; // Sha256.write is infaible
hasher.final(&digest);

Sha256.hash(&digest, &digest, .{});

return digest[0..4].*;
}

/// Serialize a message as bytes and write them to the buffer.
///
/// buffer.len must be >= than self.hintSerializedLen()
pub fn serializeToSlice(self: *const Self, buffer: []u8) !void {
var fbs = std.io.fixedBufferStream(buffer);
try self.serializeToWriter(fbs.writer());
}

/// Serialize a message as bytes and return them.
pub fn serialize(self: *const Self, allocator: std.mem.Allocator) ![]u8 {
const serialized_len = self.hintSerializedLen();

const ret = try allocator.alloc(u8, serialized_len);
errdefer allocator.free(ret);

try self.serializeToSlice(ret);

return ret;
}

/// Serialize the message as bytes and write them to the Writer.
///
/// `w` should be a valid `Writer`.
pub fn serializeToWriter(self: *const Self, w: anytype) !void {
comptime {
if (!std.meta.hasFn(@TypeOf(w), "writeInt")) @compileError("Expects r to have fn 'writeInt'.");
}

try w.writeInt(u64, self.nonce, .little);
}

/// Returns the hint of the serialized length of the message
pub inline fn hintSerializedLen(_: *const Self) usize {
// 8 bytes for nonce
return 8;
}

pub fn deserializeSlice(allocator: std.mem.Allocator, bytes: []const u8) !Self {
var fbs = std.io.fixedBufferStream(bytes);
return try Self.deserializeReader(allocator, fbs.reader());
}

/// Deserialize a Reader bytes as a `VersionMessage`
pub fn deserializeReader(_: std.mem.Allocator, r: anytype) !Self {
comptime {
if (!std.meta.hasFn(@TypeOf(r), "readInt")) @compileError("Expects r to have fn 'readInt'.");
}

var vm: Self = undefined;

vm.nonce = try r.readInt(u64, .little);
return vm;
}

pub inline fn new(nonce: u64) Self {
return .{
.nonce = nonce,
};
}
};

// TESTS
test "ok_fullflow_pong_message" {
const allocator = std.testing.allocator;

{
const msg = PongMessage.new(0x1234567890abcdef);
const payload = try msg.serialize(allocator);
defer allocator.free(payload);
const deserialized_msg = try PongMessage.deserializeSlice(allocator, payload);
try std.testing.expectEqual(msg.nonce, deserialized_msg.nonce);
}
}
27 changes: 26 additions & 1 deletion src/network/wire/lib.zig
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,8 @@ pub fn receiveMessage(allocator: std.mem.Allocator, r: anytype) !protocol.messag
protocol.messages.Message{ .Getblocks = try protocol.messages.GetblocksMessage.deserializeReader(allocator, r) }
else if (std.mem.eql(u8, &command, protocol.messages.PingMessage.name()))
protocol.messages.Message{ .Ping = try protocol.messages.PingMessage.deserializeReader(allocator, r) }
else if (std.mem.eql(u8, &command, protocol.messages.PongMessage.name()))
protocol.messages.Message{ .Pong = try protocol.messages.PongMessage.deserializeReader(allocator, r) }
else
return error.UnknownMessage;
errdefer message.deinit(allocator);
Expand Down Expand Up @@ -207,7 +209,6 @@ test "ok_send_mempool_message" {
}
}


test "ok_send_getblocks_message" {
const Config = @import("../../config/config.zig").Config;

Expand Down Expand Up @@ -272,6 +273,30 @@ test "ok_send_ping_message" {
}
}

test "ok_send_pong_message" {
const Config = @import("../../config/config.zig").Config;
const ArrayList = std.ArrayList;
const test_allocator = std.testing.allocator;
const PongMessage = protocol.messages.PongMessage;

var list: std.ArrayListAligned(u8, null) = ArrayList(u8).init(test_allocator);
defer list.deinit();

const message = PongMessage.new(21000000);

const writer = list.writer();
try sendMessage(test_allocator, writer, Config.PROTOCOL_VERSION, Config.BitcoinNetworkId.MAINNET, message);
var fbs: std.io.FixedBufferStream([]u8) = std.io.fixedBufferStream(list.items);
const reader = fbs.reader();

const received_message = try receiveMessage(test_allocator, reader);
defer received_message.deinit(test_allocator);

switch (received_message) {
.Pong => |pong_message| try std.testing.expectEqual(message.nonce, pong_message.nonce),
else => unreachable,
}
}

test "ko_receive_invalid_payload_length" {
const Config = @import("../../config/config.zig").Config;
Expand Down

0 comments on commit b2a8ad3

Please sign in to comment.