examples: add tcp test net example

This commit is contained in:
Arran Ireland
2025-07-10 14:44:23 +01:00
parent 0b32699ab1
commit 2aa43b186b
14 changed files with 233 additions and 119 deletions

View File

@@ -41,7 +41,7 @@ pub fn init(ally: Allocator, system: *System, identity: ?Identity, options: Opti
var main_endpoint = try endpoint_builder
.setIdentity(identity orelse try Identity.random(&system.rng))
.setDirection(.in)
.setMethod(.single)
.setVariant(.single)
.setName(try Name.init(options.name, &.{}, ally))
.build();
defer main_endpoint.deinit();
@@ -180,7 +180,7 @@ fn packetOut(self: *Self, interface: *Interface, packet: *Packet) !bool {
_ = self;
const purpose = packet.header.purpose;
const method = packet.header.method;
const variant = packet.header.endpoint;
const hops = packet.header.hops;
if (purpose == .announce) {
@@ -188,7 +188,7 @@ fn packetOut(self: *Self, interface: *Interface, packet: *Packet) !bool {
return false;
}
if (method == .plain and hops == 0) {
if (variant == .plain and hops == 0) {
try interface.outgoing.push(.{ .packet = packet.* });
return false;
}

View File

@@ -3,4 +3,4 @@ pub const Fernet = @import("crypto/Fernet.zig");
pub const Hash = @import("crypto/Hash.zig");
pub const Identity = @import("crypto/Identity.zig");
pub const Ed25519 = std.crypto.sign.Ed25519;
pub const X25519 = @import("crypto/X25519.zig");
pub const X25519 = std.crypto.dh.X25519;

View File

@@ -8,7 +8,7 @@ pub const Direction = enum {
out,
};
pub const Method = enum(u2) {
pub const Variant = enum(u2) {
single,
group,
plain,

View File

@@ -6,7 +6,7 @@ const endpoint = @import("../endpoint.zig");
const Allocator = std.mem.Allocator;
const Identity = crypto.Identity;
const Direction = endpoint.Direction;
const Method = endpoint.Method;
const Variant = endpoint.Variant;
const Name = endpoint.Name;
const Hash = crypto.Hash;
const Managed = @import("Managed.zig");
@@ -17,14 +17,14 @@ pub const Error = error{
HasIdentity,
MissingIdentity,
MissingDirection,
MissingMethod,
MissingVariant,
MissingName,
} || Allocator.Error;
ally: Allocator,
identity: ?Identity,
direction: ?Direction,
method: ?Method,
variant: ?Variant,
name: ?Name,
pub fn init(ally: Allocator) Self {
@@ -32,7 +32,7 @@ pub fn init(ally: Allocator) Self {
.ally = ally,
.identity = null,
.direction = null,
.method = null,
.variant = null,
.name = null,
};
}
@@ -47,8 +47,8 @@ pub fn setDirection(self: *Self, direction: Direction) *Self {
return self;
}
pub fn setMethod(self: *Self, method: Method) *Self {
self.method = method;
pub fn setVariant(self: *Self, variant: Variant) *Self {
self.variant = variant;
return self;
}
@@ -59,12 +59,12 @@ pub fn setName(self: *Self, name: Name) *Self {
pub fn build(self: *Self) Error!Managed {
const direction = self.direction orelse return Error.MissingDirection;
const method = self.method orelse return Error.MissingMethod;
const variant = self.variant orelse return Error.MissingVariant;
const name = self.name orelse return Error.MissingName;
if (method == .plain and self.identity != null) {
if (variant == .plain and self.identity != null) {
return Error.HasIdentity;
} else if (method != .plain and self.identity == null) {
} else if (variant != .plain and self.identity == null) {
return Error.MissingIdentity;
}
@@ -87,7 +87,7 @@ pub fn build(self: *Self) Error!Managed {
.ally = self.ally,
.identity = self.identity,
.direction = direction,
.method = method,
.variant = variant,
.name = name,
.hash = hash,
};

View File

@@ -6,7 +6,7 @@ const endpoint = @import("../endpoint.zig");
const Allocator = std.mem.Allocator;
const Identity = crypto.Identity;
const Direction = endpoint.Direction;
const Method = endpoint.Method;
const Variant = endpoint.Variant;
const Name = endpoint.Name;
const Hash = crypto.Hash;
@@ -15,7 +15,7 @@ const Self = @This();
ally: Allocator,
identity: ?Identity,
direction: Direction,
method: Method,
variant: Variant,
name: Name,
hash: Hash,
@@ -24,7 +24,7 @@ pub fn clone(self: *const Self) !Self {
.ally = self.ally,
.identity = self.identity,
.direction = self.direction,
.method = self.method,
.variant = self.variant,
.name = try self.name.clone(),
.hash = self.hash,
};

View File

@@ -88,7 +88,7 @@ test "main" {
.setIdentity(try Identity.random(&rng))
.setName(try Name.init("endpoint", &.{"main"}, ally))
.setDirection(.in)
.setMethod(.single)
.setVariant(.single)
.build();
break :blk endpoint;

View File

@@ -110,7 +110,7 @@ pub const Out = union(enum) {
@tagName(h.format),
@tagName(h.context),
@tagName(h.propagation),
@tagName(h.method),
@tagName(h.endpoint),
@tagName(h.purpose),
h.hops,
});
@@ -146,6 +146,9 @@ pub const Out = union(enum) {
var timestamp_bytes: [5]u8 = undefined;
std.mem.writeInt(u40, &timestamp_bytes, a.timestamp, .big);
try f.entry("timestamp", "{x}", .{hex(&timestamp_bytes)});
if (a.ratchet) |*ratchet| {
try f.entry("ratchet", "{x}", .{hex(ratchet)});
}
try f.entry("signature", "{x}", .{hex(&a.signature.toBytes())});
if (a.application_data.items.len > 0) {

View File

@@ -1,10 +1,10 @@
const std = @import("std");
const crypto = @import("crypto.zig");
const data = @import("data.zig");
const endpoint = @import("endpoint.zig");
const Allocator = std.mem.Allocator;
const Endpoint = @import("endpoint.zig").Managed;
const EndpointMethod = @import("endpoint.zig").Method;
const Endpoint = endpoint.Managed;
const Hash = crypto.Hash;
const Identity = crypto.Identity;
@@ -35,7 +35,7 @@ pub const Payload = union(enum) {
name_hash: Hash.Name,
noise: Noise,
timestamp: Timestamp,
// rachet: ?*const [N]u8,
ratchet: ?crypto.Identity.Ratchet,
signature: crypto.Ed25519.Signature,
application_data: data.Bytes,
};
@@ -58,6 +58,7 @@ pub const Payload = union(enum) {
.name_hash = a.name_hash,
.noise = a.noise,
.timestamp = a.timestamp,
.ratchet = a.ratchet,
.signature = a.signature,
.application_data = try a.application_data.clone(),
},
@@ -78,6 +79,7 @@ pub const Payload = union(enum) {
total += crypto.Hash.name_length;
total += @sizeOf(Announce.Noise);
total += @sizeOf(Announce.Timestamp);
total += if (a.ratchet != null) @sizeOf(crypto.Identity.Ratchet) else 0;
total += crypto.Ed25519.Signature.encoded_length;
total += a.application_data.items.len;
break :blk total;
@@ -148,7 +150,7 @@ pub const Header = packed struct(u16) {
transport,
};
pub const Method = EndpointMethod;
pub const Endpoint = endpoint.Variant;
pub const Purpose = enum(u2) {
data,
@@ -158,12 +160,12 @@ pub const Header = packed struct(u16) {
};
};
interface: Flag.Interface = .open,
format: Flag.Format = .normal,
context: Flag.Context = .none,
propagation: Flag.Propagation = .broadcast,
method: Flag.Method = .single,
purpose: Flag.Purpose = .data,
endpoint: Flag.Endpoint = .single,
propagation: Flag.Propagation = .broadcast,
context: Flag.Context = .none,
format: Flag.Format = .normal,
interface: Flag.Interface = .open,
hops: u8 = 0,
};
@@ -190,3 +192,85 @@ pub const Context = enum(u8) {
link_request_rtt,
link_request_proof,
};
test "validate-raw-announce-roundtrip" {
const t = std.testing;
const ally = t.allocator;
const rng = std.crypto.random;
// Captured from reference implementation - with framing removed.
const raw_announce = "71008133c7ce6d6be9b4070a3b98ee9ecab583dfe79d30200ee5e9f5c5615d45a5b000fb266456840e5f4d010a6fbb4025969f8db5415597e3d7a48431d0534e441d0bdeb78f1064f50b447291dd51617040dc9c40cb5b9adab1314ad270b1297d6fd46ec60bc318e2c0f0d908fc1c2bcdef00686f9b4ef17ec1b73f60b14df6709cb74164bd1890e26ff8a4634bbd855051ef959f413d7f7c8f9ff0f54ee81fb994c4e1975fe6f4b56fb26d2e107bd824d864a6932a2e2c02b1352ad9a31ce1cbeae72902effef1ccdeb7d004fbe527cd39111dc59d0e92c406696f6e323332c0";
var bytes = std.ArrayList(u8).init(ally);
defer bytes.deinit();
var i: usize = 0;
while (i < raw_announce.len) : (i += 2) {
const byte = std.fmt.parseInt(u8, raw_announce[i .. i + 2], 16) catch break;
try bytes.append(byte);
}
var factory = Factory.init(ally, rng, .{});
var p = factory.fromBytes(bytes.items) catch |err| {
std.debug.print("Failed to parse packet: {}\n", .{err});
return;
};
defer p.deinit();
try t.expect(p.header.purpose == .announce);
try t.expect(p.header.context == .some);
try t.expect(p.payload.announce.ratchet != null);
try p.validate();
var buffer: [1024]u8 = undefined;
var q = factory.fromBytes(try p.write(&buffer)) catch |err| {
std.debug.print("Failed to parse packet: {}\n", .{err});
return;
};
defer q.deinit();
try t.expect(q.header.purpose == .announce);
try t.expect(q.header.context == .some);
try t.expect(q.payload.announce.ratchet != null);
try q.validate();
}
test "validate-make-announce" {
const t = std.testing;
const ally = t.allocator;
var rng = std.crypto.random;
var builder = endpoint.Builder.init(ally);
defer builder.deinit();
var announce_endpoint = try builder
.setIdentity(try crypto.Identity.random(&rng))
.setDirection(.in)
.setVariant(.single)
.setName(try endpoint.Name.init("endpoint", &.{"test"}, ally))
.build();
defer announce_endpoint.deinit();
const app_data = "some application data";
const now = 123456789;
var factory = Factory.init(ally, rng, .{});
var announce_packet = try factory.makeAnnounce(&announce_endpoint, app_data, now);
defer announce_packet.deinit();
var raw_bytes = try data.Bytes.initCapacity(ally, announce_packet.size());
raw_bytes.expandToCapacity();
defer raw_bytes.deinit();
const raw_packet = try announce_packet.write(raw_bytes.items);
var p = try factory.fromBytes(raw_packet);
defer p.deinit();
try t.expect(p.header.purpose == .announce);
try p.validate();
const announce = p.payload.announce;
try t.expectEqualStrings(app_data, announce.application_data.items);
}

View File

@@ -72,8 +72,8 @@ pub fn setTransport(self: *Self, endpoint_hash: Hash.Short, transport_id: Hash.S
return self;
}
pub fn setMethod(self: *Self, method: Header.Flag.Method) *Self {
self.header.method = method;
pub fn setVariant(self: *Self, variant: Header.Flag.Endpoint) *Self {
self.header.endpoint = variant;
return self;
}
@@ -89,19 +89,17 @@ pub fn setContext(self: *Self, context: Context) *Self {
}
pub fn setPayload(self: *Self, payload: Payload) *Self {
self.header.purpose = switch (payload) {
.announce => .announce,
else => self.header.purpose,
};
switch (payload) {
.announce => |*a| {
self.header.purpose = .announce;
self.header.context = if (a.ratchet != null) .some else .none;
},
else => {},
}
self.payload = payload;
return self;
}
pub fn appendPayload(self: *Self, payload: []const u8) !*Self {
try self.payload.appendSlice(payload);
return self;
}
pub fn build(self: *Self) !Managed {
const endpoints = self.endpoints orelse return Error.Incomplete;

View File

@@ -45,10 +45,9 @@ pub fn fromBytes(self: *Self, bytes: []const u8) Error!Packet {
index += header_size;
const both_auth = self.config.access_code != null and header.interface == .authenticated;
const both_not_auth = self.config.access_code == null and header.interface == .open;
const non_matching_auth = !(both_auth or both_not_auth);
const both_open = self.config.access_code == null and header.interface == .open;
if (non_matching_auth) {
if (!(both_auth or both_open)) {
return Error.InvalidAuthentication;
}
@@ -81,21 +80,23 @@ pub fn fromBytes(self: *Self, bytes: []const u8) Error!Packet {
@memcpy(&endpoint, bytes[index .. index + endpoint.len]);
index += endpoint.len;
const endpoints = packet.Endpoints{ .normal = .{
.endpoint = endpoint,
} };
const endpoints = packet.Endpoints{
.normal = .{
.endpoint = endpoint,
},
};
break :blk endpoints;
},
.transport => blk: {
var endpoint: crypto.Hash.Short = undefined;
@memcpy(&endpoint, bytes[index .. index + endpoint.len]);
index += endpoint.len;
var transport_id: crypto.Hash.Short = undefined;
@memcpy(&transport_id, bytes[index .. index + transport_id.len]);
index += transport_id.len;
var endpoint: crypto.Hash.Short = undefined;
@memcpy(&endpoint, bytes[index .. index + endpoint.len]);
index += endpoint.len;
const endpoints = packet.Endpoints{ .transport = .{
.transport_id = transport_id,
.endpoint = endpoint,
@@ -135,10 +136,20 @@ pub fn fromBytes(self: *Self, bytes: []const u8) Error!Packet {
index += signature_key_bytes.len;
@memcpy(&announce.name_hash, bytes[index .. index + announce.name_hash.len]);
index += announce.name_hash.len;
@memcpy(&announce.noise, bytes[index .. index + announce.noise.len]);
index += announce.noise.len;
announce.timestamp = std.mem.bytesToValue(u40, bytes[index .. index + @sizeOf(Announce.Timestamp)]);
index += @sizeOf(Announce.Timestamp);
@memcpy(&announce.noise, bytes[index .. index + 5]);
index += 5;
announce.timestamp = std.mem.readInt(u40, bytes[index .. index + 5][0..5], .big);
index += 5;
if (header.context == .some) {
var ratchet: [32]u8 = undefined;
@memcpy(&ratchet, bytes[index .. index + 32]);
announce.ratchet = ratchet;
index += 32;
} else {
announce.ratchet = null;
}
var signature_bytes: [Signature.encoded_length]u8 = undefined;
@memcpy(&signature_bytes, bytes[index .. index + Signature.encoded_length]);
announce.signature = Signature.fromBytes(signature_bytes);
@@ -153,9 +164,7 @@ pub fn fromBytes(self: *Self, bytes: []const u8) Error!Packet {
},
else => .{ .raw = blk: {
var raw = data.Bytes.init(self.ally);
errdefer {
raw.deinit();
}
errdefer raw.deinit();
try raw.appendSlice(bytes[index..]);
break :blk raw;
} },
@@ -171,7 +180,6 @@ pub fn fromBytes(self: *Self, bytes: []const u8) Error!Packet {
};
}
// TODO: Add ratchet.
pub fn makeAnnounce(self: *Self, endpoint: *const Endpoint, application_data: ?[]const u8, now: u64) Error!Packet {
const identity = endpoint.identity orelse return Error.MissingIdentity;
var announce: packet.Payload.Announce = undefined;
@@ -180,6 +188,9 @@ pub fn makeAnnounce(self: *Self, endpoint: *const Endpoint, application_data: ?[
announce.name_hash = endpoint.name.hash.name().*;
self.rng.bytes(&announce.noise);
announce.timestamp = @truncate(now);
var ratchet: crypto.Identity.Ratchet = undefined;
self.rng.bytes(&ratchet);
announce.ratchet = ratchet;
announce.application_data = data.Bytes.init(self.ally);
if (application_data) |app_data| {
@@ -196,7 +207,14 @@ pub fn makeAnnounce(self: *Self, endpoint: *const Endpoint, application_data: ?[
try bytes.appendSlice(announce.public.signature.bytes[0..]);
try bytes.appendSlice(announce.name_hash[0..]);
try bytes.appendSlice(announce.noise[0..]);
try bytes.appendSlice(&std.mem.toBytes(announce.timestamp));
var timestamp_bytes: [5]u8 = undefined;
std.mem.writeInt(u40, &timestamp_bytes, announce.timestamp, .big);
try bytes.appendSlice(&timestamp_bytes);
if (announce.ratchet) |*r| {
try bytes.appendSlice(r[0..]);
}
try bytes.appendSlice(announce.application_data.items);
break :blk try identity.sign(bytes);
@@ -222,51 +240,10 @@ pub fn makePlain(self: *Self, name: Name, payload: packet.Payload) Error!Packet
}
const plain = try builder
.setMethod(.plain)
.setVariant(.plain)
.setEndpoint(name.hash.short().*)
.setPayload(payload)
.build();
return plain;
}
test "make-announce-and-validate" {
const t = std.testing;
const EndpointBuilder = @import("../endpoint.zig").Builder;
const ally = t.allocator;
var rng = std.crypto.random;
const config = Interface.Config{};
var builder = EndpointBuilder.init(ally);
defer builder.deinit();
var endpoint = try builder
.setIdentity(try crypto.Identity.random(&rng))
.setDirection(.in)
.setMethod(.single)
.setName(try Name.init("endpoint", &.{"test"}, ally))
.build();
defer endpoint.deinit();
const app_data = "some application data";
const now = 123456789;
var factory1 = Self.init(ally, rng, config);
var announce_packet = try factory1.makeAnnounce(&endpoint, app_data, now);
defer announce_packet.deinit();
var packet_bytes = try data.Bytes.initCapacity(ally, announce_packet.size());
packet_bytes.expandToCapacity();
defer packet_bytes.deinit();
const raw_packet = try announce_packet.write(packet_bytes.items);
var factory2 = Self.init(ally, rng, config);
var parsed_packet = try factory2.fromBytes(raw_packet);
defer parsed_packet.deinit();
try t.expect(parsed_packet.header.purpose == .announce);
try parsed_packet.validate();
const announce = parsed_packet.payload.announce;
try t.expectEqualStrings(app_data, announce.application_data.items);
}

View File

@@ -43,15 +43,28 @@ pub fn setTransport(self: *Self, transport_id: *const Hash.Short) !void {
pub fn validate(self: *const Self) !void {
switch (self.payload) {
.announce => |a| {
var signed_data = std.ArrayList(u8).init(self.ally);
defer signed_data.deinit();
const endpoint_hash = self.endpoints.endpoint();
try signed_data.appendSlice(endpoint_hash[0..]);
try signed_data.appendSlice(a.public.dh[0..]);
try signed_data.appendSlice(a.public.signature.bytes[0..]);
try signed_data.appendSlice(a.name_hash[0..]);
try signed_data.appendSlice(a.noise[0..]);
var timestamp_bytes: [5]u8 = undefined;
std.mem.writeInt(u40, &timestamp_bytes, a.timestamp, .big);
try signed_data.appendSlice(&timestamp_bytes);
if (a.ratchet) |*ratchet| {
try signed_data.appendSlice(ratchet[0..]);
}
try signed_data.appendSlice(a.application_data.items);
var verifier = try a.signature.verifier(a.public.signature);
verifier.update(&endpoint_hash);
verifier.update(&a.public.dh);
verifier.update(&a.public.signature.bytes);
verifier.update(&a.name_hash);
verifier.update(&a.noise);
verifier.update(&std.mem.toBytes(a.timestamp));
verifier.update(a.application_data.items);
verifier.update(signed_data.items);
try verifier.verify();
const identity = crypto.Identity.fromPublic(a.public);
@@ -114,9 +127,16 @@ pub fn write(self: *const Self, buffer: []u8) ![]u8 {
i += a.name_hash.len;
@memcpy(buffer[i .. i + a.noise.len], &a.noise);
i += a.noise.len;
const timestamp_bytes = std.mem.asBytes(&a.timestamp);
@memcpy(buffer[i .. i + timestamp_bytes.len], timestamp_bytes);
var timestamp_bytes: [5]u8 = undefined;
std.mem.writeInt(u40, &timestamp_bytes, a.timestamp, .big);
@memcpy(buffer[i .. i + timestamp_bytes.len], &timestamp_bytes);
i += timestamp_bytes.len;
if (a.ratchet) |*ratchet| {
@memcpy(buffer[i .. i + ratchet.len], ratchet);
i += ratchet.len;
}
@memcpy(buffer[i .. i + crypto.Ed25519.Signature.encoded_length], &a.signature.toBytes());
i += crypto.Ed25519.Signature.encoded_length;
@memcpy(buffer[i .. i + a.application_data.items.len], a.application_data.items);
@@ -134,17 +154,17 @@ pub fn write(self: *const Self, buffer: []u8) ![]u8 {
pub fn hash(self: *const Self) Hash {
const header = std.mem.asBytes(&self.header);
const method_and_purpose: u8 = std.mem.bytesAsSlice(u4, header)[1];
const variant_and_purpose: u8 = std.mem.bytesAsSlice(u4, header)[1];
const context = @intFromEnum(self.context);
var hasher = switch (self.endpoints) {
.normal => |normal| Hash.incremental(.{
.method_and_purpose = method_and_purpose,
.variant_and_purpose = variant_and_purpose,
.endpoint = normal.endpoint,
.context = context,
}),
.transport => |transport| Hash.incremental(.{
.method_and_purpose = method_and_purpose,
.variant_and_purpose = variant_and_purpose,
.transport_id = transport.transport_id,
.endpoint = transport.endpoint,
.context = context,
@@ -158,7 +178,12 @@ pub fn hash(self: *const Self) Hash {
hasher.update(&announce.public.signature.bytes);
hasher.update(&announce.name_hash);
hasher.update(&announce.noise);
hasher.update(std.mem.asBytes(&announce.timestamp));
var timestamp_bytes: [5]u8 = undefined;
std.mem.writeInt(u40, &timestamp_bytes, announce.timestamp, .big);
hasher.update(&timestamp_bytes);
if (announce.ratchet) |*ratchet| {
hasher.update(ratchet);
}
hasher.update(&announce.signature.r);
hasher.update(&announce.signature.s);
hasher.update(announce.application_data.items);

25
examples/tcp.zig Normal file
View File

@@ -0,0 +1,25 @@
const std = @import("std");
const core = @import("core");
const io = @import("io");
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const ally = gpa.allocator();
var clock = try io.os.Clock.init();
var rng = io.os.Rng.init();
var system = core.System{
.clock = clock.clock(),
.rng = rng.rng(),
};
var node = try core.Node.init(ally, &system, null, .{});
const host = "amsterdam.connect.reticulum.network";
const port = 4965;
var driver = try io.driver.Tcp.init(&node, host, port, ally);
defer driver.deinit();
try driver.run();
}

View File

@@ -44,7 +44,7 @@ test "abc" {
try golden.snap(
@src(),
\\.packet = .{
\\ .header = .{.open, .normal, .none, .broadcast, .single, .announce, hops(0)},
\\ .header = .{.open, .normal, .some, .broadcast, .single, .announce, hops(0)},
\\ .endpoints = .normal{71cb75e4effb51744aa9877da2b9af56},
\\ .context = .none,
\\ .payload = .announce{
@@ -53,7 +53,8 @@ test "abc" {
\\ .name_hash = ca978112ca1bbdcafac2,
\\ .noise = 9325ba36e2,
\\ .timestamp = 000000000a,
\\ .signature = c5fb72c740fd8cc1a1dd844c38039b361f5253060e6a4789d06f925fae58c5907ad29f590c73297ef7f929e4d3a79f1d9a22f6baded58784c3ad743d63c4b205,
\\ .ratchet = 3cd8d216726b89248c7e1ae8d32e176dbb4c25dfbfa4edf2373f70c6c642ff85,
\\ .signature = ca87af796f5ab949ea7e44e0e618a973f558fe81a676d536d022c47e300dba8164dae7a4e51d66b2b5d22ad5ef92b77558cc889710758bac97d1846ce34b6c0b,
\\ .application_data = 6865726520697320736f6d65206170702064617461,
\\ },
\\}
@@ -74,7 +75,7 @@ test "abc" {
try golden.snap(
@src(),
\\.packet = .{
\\ .header = .{.open, .transport, .none, .broadcast, .single, .announce, hops(1)},
\\ .header = .{.open, .transport, .some, .broadcast, .single, .announce, hops(1)},
\\ .endpoints = .transport{71cb75e4effb51744aa9877da2b9af56, f81ae61e99c1e826604cf1e5880c6847},
\\ .context = .none,
\\ .payload = .announce{
@@ -83,7 +84,8 @@ test "abc" {
\\ .name_hash = ca978112ca1bbdcafac2,
\\ .noise = 9325ba36e2,
\\ .timestamp = 000000000a,
\\ .signature = c5fb72c740fd8cc1a1dd844c38039b361f5253060e6a4789d06f925fae58c5907ad29f590c73297ef7f929e4d3a79f1d9a22f6baded58784c3ad743d63c4b205,
\\ .ratchet = 3cd8d216726b89248c7e1ae8d32e176dbb4c25dfbfa4edf2373f70c6c642ff85,
\\ .signature = ca87af796f5ab949ea7e44e0e618a973f558fe81a676d536d022c47e300dba8164dae7a4e51d66b2b5d22ad5ef92b77558cc889710758bac97d1846ce34b6c0b,
\\ .application_data = 6865726520697320736f6d65206170702064617461,
\\ },
\\}

View File

@@ -136,7 +136,7 @@ pub fn addEndpoint(self: *Self, name: []const u8) !core.Endpoint {
_ = try builder
.setIdentity(identity)
.setDirection(.in)
.setMethod(.single)
.setVariant(.single)
.setApplicationName(name);
var endpoint = try builder.build();