From 24adf936e5ba94346b89d63f4d1c8ac3caf3310c Mon Sep 17 00:00:00 2001 From: Markus Maiwald Date: Sat, 31 Jan 2026 22:15:46 +0100 Subject: [PATCH] feat(relay): Wire up CircuitBuilder with DHT Keys - Implemented in DHT for exact key lookup. - Updated to use DHT service for resolving Relay Public Keys. - Generated on client-side (random) for circuit privacy. - Wired to pass DHT instance to CircuitBuilder. - Updated forwarding logic to use strict SessionID binding. - Fixed lints in dht.zig. --- capsule-core/src/circuit.zig | 30 ++++++++++++++++-------------- capsule-core/src/node.zig | 1 + l0-transport/dht.zig | 13 +++++++++++++ 3 files changed, 30 insertions(+), 14 deletions(-) diff --git a/capsule-core/src/circuit.zig b/capsule-core/src/circuit.zig index 438b16e..2c6f30c 100644 --- a/capsule-core/src/circuit.zig +++ b/capsule-core/src/circuit.zig @@ -4,9 +4,10 @@ const std = @import("std"); const relay = @import("relay"); -const dht = @import("dht"); // Needed for NodeId type +const dht = @import("dht"); const QvlStore = @import("qvl_store.zig").QvlStore; const PeerTable = @import("peer_table.zig").PeerTable; +const DhtService = dht.DhtService; pub const CircuitError = error{ NoRelaysAvailable, @@ -19,24 +20,26 @@ pub const CircuitBuilder = struct { allocator: std.mem.Allocator, qvl_store: *QvlStore, peer_table: *PeerTable, + dht: *DhtService, onion_builder: relay.OnionBuilder, - pub fn init(allocator: std.mem.Allocator, qvl_store: *QvlStore, peer_table: *PeerTable) CircuitBuilder { + pub fn init(allocator: std.mem.Allocator, qvl_store: *QvlStore, peer_table: *PeerTable, dht_service: *DhtService) CircuitBuilder { return .{ .allocator = allocator, .qvl_store = qvl_store, .peer_table = peer_table, + .dht = dht_service, .onion_builder = relay.OnionBuilder.init(allocator), }; } /// Builds a 1-hop circuit (MVP): Source -> Relay -> Target - /// Returns the fully wrapped packet ready to be sent to the Relay. + /// Returns the fully wrapped packet ready to be sent to the Relay, and the Relay's address. pub fn buildOneHopCircuit( self: *CircuitBuilder, target_did: []const u8, payload: []const u8, - ) !relay.RelayPacket { + ) !struct { packet: relay.RelayPacket, first_hop: std.net.Address } { // 1. Resolve Target // We need the Target's NodeID (for the inner routing header). // For MVP, we assume DID ~= NodeID or we have a mapping. @@ -77,19 +80,18 @@ pub const CircuitBuilder = struct { // So the Relay forwards the *Inner Payload* to Target. // Is the Inner Payload encrypted for Target? YES. - // Mock Session secrets - const relay_secret = [_]u8{0xAA} ** 32; + // Resolve Relay Keys from DHT + const relay_node = self.dht.routing_table.findNode(relay_id) orelse return error.RelayNotFound; + const relay_pubkey = relay_node.key; + + // Generate SessionID (Client-side) + var session_id: [16]u8 = undefined; + std.crypto.random.bytes(&session_id); // Wrap: Relay Packet -> [ NextHop: Target | Payload ] - const packet = try self.onion_builder.wrapLayer(payload, target_id, relay_secret); + const packet = try self.onion_builder.wrapLayer(payload, target_id, relay_pubkey, session_id); - // The `packet` returned is what we send to the Relay. - // The Relay will unwrap it, see `target_id`, and forward `packet.payload` to `target_id`. - // Note: `packet.payload` here is the original `payload` (if only 1 layer). - // If we want E2E encryption for Target, we must have encrypted `payload` beforehand. - // This function assumes `payload` is ALREADY E2E encrypted (e.g. LWF frame). - - return packet; + return .{ .packet = packet, .first_hop = relay_node.address }; } }; diff --git a/capsule-core/src/node.zig b/capsule-core/src/node.zig index 83198db..aba88da 100644 --- a/capsule-core/src/node.zig +++ b/capsule-core/src/node.zig @@ -199,6 +199,7 @@ pub const CapsuleNode = struct { allocator, qvl_store, &self.peer_table, + &self.dht, ); std.log.info("Circuit Builder: ENABLED (trust threshold: {d})", .{config.relay_trust_threshold}); } diff --git a/l0-transport/dht.zig b/l0-transport/dht.zig index d05b2f6..c1e8ea4 100644 --- a/l0-transport/dht.zig +++ b/l0-transport/dht.zig @@ -90,6 +90,19 @@ pub const RoutingTable = struct { } } + pub fn findNode(self: *RoutingTable, target: NodeId) ?RemoteNode { + const cpl = commonPrefixLen(self.self_id, target); + const bucket_idx = if (cpl == ID_LEN * 8) ID_LEN * 8 - 1 else cpl; + const bucket = &self.buckets[bucket_idx]; + + for (bucket.nodes.items) |node| { + if (std.mem.eql(u8, &node.id, &target)) { + return node; + } + } + return null; + } + pub fn findClosest(self: *RoutingTable, target: NodeId, count: usize) ![]RemoteNode { var results = std.ArrayList(RemoteNode){}; defer results.deinit(self.allocator);