tiny.reticulum.destination.registry
Defined in destination.
API (17)
Actions
Public operations.
Types and contracts
Public types and contracts.
Entry: The entry a caller fills in to register a destination the node will answer for: the 16-byte address, the announced name hash, the kind of destination, the proof strategy, which of the node's private identities signs for it, and a group key.ProofStrategy: The choice of what a registered destination does with a packet delivered to it: prove nothing, hand the decision to the application, or prove it at once, so a caller can say what the node does with the packet, following Reticulum@1.5.0 RNS/Destination.py:69-71.Table: The bounded list of destinations one node answers for, for a caller that hands the node one block of storage and gets it back at teardown, following Reticulum@1.5.0 RNS/Transport.py:2834-2845.Table.CapacityTable.ExhaustionTable.InitErrorTable.LimitsTable.Storage
Values and defaults
Public values and defaults.
Source
Source: lib/reticulum/src/destination/registry.zig
zig
const std = @import("std");const alloc_phase = @import("alloc_phase");const destination = @import("root.zig");/// The choice of what a registered destination does with a packet delivered to/// it: prove nothing, hand the decision to the application, or prove it at/// once, so a caller can say what the node does with the packet, following/// Reticulum@1.5.0 RNS/Destination.py:69-71.pub const ProofStrategy = enum(u8) { none = 0x21, app = 0x22, all = 0x23,};/// The entry a caller fills in to register a destination the node will answer/// for: the 16-byte address, the announced name hash, the kind of destination,/// the proof strategy, which of the node's private identities signs for it, and/// a group key. A group destination shares one 64-byte key, which its members/// use as a token key directly, following Reticulum@1.5.0/// RNS/Destination.py:544-558 and Reticulum@1.5.0/// RNS/Cryptography/Token.py:53-55.pub const Entry = struct { hash: [16]u8, /// The ten bytes an announce publishes for this destination's dotted name, /// set so the node can announce the destination under the right published /// name and worked out separately from the address, following /// Reticulum@1.5.0 RNS/Destination.py:188-190. name_hash: [10]u8 = @splat(0), kind: destination.Type, proof_strategy: ProofStrategy, identity_index: ?usize = null, group_key: [64]u8 = @splat(0),};const TableLimits = struct { destinations_max: usize,};const TableCapacity = struct { destinations_max: usize, storage_bytes: usize, pub const DeriveError = error{ InvalidLimit, CapacityOverflow }; pub fn derive(limits: TableLimits) DeriveError!TableCapacity { if (limits.destinations_max == 0) return error.InvalidLimit; const destinations_max = limits.destinations_max; const storage_bytes = alloc_phase.capacity.mul( usize, destinations_max, @sizeOf(Entry), ) catch return error.CapacityOverflow; return .{ .destinations_max = destinations_max, .storage_bytes = storage_bytes }; }};/// The bounded list of destinations one node answers for, for a caller that/// hands the node one block of storage and gets it back at teardown, following/// Reticulum@1.5.0 RNS/Transport.py:2834-2845. The caller fixes how many/// destinations the list holds, and the storage it hands over has to equal the/// byte count derived from that number, or registration returns/// `error.StorageLengthMismatch`. Registering an address the list already holds/// returns `error.Duplicate`, and registering into a full list returns/// `error.Full`, both before the list changes. Looking a destination up scans/// the list, so a lookup costs one pass over it.pub const Table = struct { phase: alloc_phase.capacity.Phase, capacity: Capacity, storage: Storage, entries: []Entry, len: usize = 0, pub const storage_alignment: usize = 8; pub const Storage = []align(storage_alignment) u8; pub const Limits: type = TableLimits; pub const Capacity: type = TableCapacity; pub const Exhaustion = error{ Full, Duplicate }; pub const InitError = Capacity.DeriveError || error{StorageLengthMismatch}; pub const work_limits: alloc_phase.capacity.WorkLimits = .{ .transition_steps_max = 65_536, .cleanup_steps_per_call_max = 0, .cleanup_calls_at_capacity_max = 0, }; pub const claim: alloc_phase.capacity.Declaration = .{ .source = .{ .id = "reticulum.destinations", .kind = .phase_static, .limit_source = .caller, .storage = .{ .covered = &.{.{ .id = "caller_local_destination_table", .lifetime = .transferred, .detail = "caller storage for registered local destinations", }}, .excluded = &.{ "private identity storage selected by identity indexes", "application handlers and persistence effects", }, }, .capacity = .{ .inputs = &.{alloc_phase.capacity.bindInput( Limits, "destinations_max", "destinations_max", )}, .type_selectors = &.{alloc_phase.capacity.bindType(Entry, "destination")}, .nodes = &.{ .{ .input = 0 }, .{ .scale = .{ .node = 0, .coefficient = .{ .size_of_concrete_type = 0 }, } }, }, .assertions = &.{.{ .scope = .closure_total, .measure = .retained, .relation = .exact, .expression = 1, }}, }, .overload = .{ .kind = .reject_before_mutation, .detail = "duplicate and full registration preserve the table", }, .risks = .{ .transitive = .{ .status = .excluded, .detail = "destination registration calls no allocating owner", }, .foreign = .{ .status = .excluded, .detail = "destination registration crosses no foreign boundary", }, }, .work = .{ .equation = "registration scans at most destinations_max entries" }, .obligations = &.{ .{ .key = "reticulum_destinations_capacity", .role = .capacity_model }, .{ .key = "reticulum_destinations_overload", .role = .overload }, .{ .key = "reticulum_destinations_work", .role = .work_bound }, }, }, .bindings = .{ .owner = @This(), .seal = .{ .family = alloc_phase.capacity.selector(@This().activate), .premise = .{ .class = .checked_semantic_fact, .authority = .checker }, }, .teardown = .{ .family = alloc_phase.capacity.selector(@This().deinit), .premise = .{ .class = .checked_semantic_fact, .authority = .checker }, }, }, }; pub fn init(storage: Storage, limits: Limits) InitError!Table { const capacity = try Capacity.derive(limits); if (storage.len != capacity.storage_bytes) return error.StorageLengthMismatch; return .{ .phase = .initialization, .capacity = capacity, .storage = storage, .entries = std.mem.bytesAsSlice(Entry, storage), }; } pub fn activate(self: *Table) void { std.debug.assert(self.phase == .initialization); std.debug.assert(self.len == 0); self.phase = .steady; } pub fn register(self: *Table, entry: Entry) Exhaustion!void { std.debug.assert(self.phase == .steady); if (self.find(entry.hash) != null) return error.Duplicate; if (self.len == self.capacity.destinations_max) return error.Full; self.entries[self.len] = entry; self.len += 1; } pub fn find(self: *const Table, hash: [16]u8) ?*const Entry { std.debug.assert(self.phase == .steady); for (self.entries[0..self.len]) |*entry| { if (std.mem.eql(u8, &entry.hash, &hash)) return entry; } return null; } pub fn count(self: *const Table) usize { std.debug.assert(self.phase == .steady); return self.len; } pub fn deinit(self: *Table) Storage { std.debug.assert(self.phase == .steady); self.phase = .teardown; const storage = self.storage; self.* = undefined; return storage; }};comptime { alloc_phase.capacity.requireProvisionedRejectingOwnerShape(Table);}fn testEntry(value: u8) Entry { return .{ .hash = @splat(value), .kind = .single, .proof_strategy = .all, .identity_index = value, };}test "destinations admit maximum and reject maximum plus one" { comptime { @stardustClaim(alloc_phase.capacity.witness( Table, "reticulum_destinations_capacity", ), null, null, null, null, null, null); @stardustClaim(alloc_phase.capacity.witness( Table, "reticulum_destinations_overload", ), null, null, null, null, null, null); @stardustClaim(alloc_phase.capacity.witness( Table, "reticulum_destinations_work", ), null, null, null, null, null, null); } const capacity = comptime TableCapacity.derive(.{ .destinations_max = 3 }) catch unreachable; var bytes: [capacity.storage_bytes]u8 align(Table.storage_alignment) = undefined; var table = try Table.init(&bytes, .{ .destinations_max = 3 }); table.activate(); defer _ = table.deinit(); for (1..4) |value| try table.register(testEntry(@intCast(value))); try std.testing.expectError(error.Full, table.register(testEntry(4))); try std.testing.expectEqual(@as(usize, 3), table.count());}test "destinations reject duplicate registration without mutation" { const capacity = comptime TableCapacity.derive(.{ .destinations_max = 3 }) catch unreachable; var bytes: [capacity.storage_bytes]u8 align(Table.storage_alignment) = undefined; var table = try Table.init(&bytes, .{ .destinations_max = 3 }); table.activate(); defer _ = table.deinit(); try table.register(testEntry(1)); try std.testing.expectError(error.Duplicate, table.register(testEntry(1))); try std.testing.expectEqual(@as(usize, 1), table.count());}Source: lib/reticulum/src/destination/root.zig:59
zig
pub const registry = @import("registry.zig");Audit
| Definitions | 18 |
|---|---|
| Public names | 18 |
| Members | 16 |
| Version | 26.7.0 |
| Revision | daab053ee433 |