Skip to documentation
SLOP

tiny.reticulum.node.transport.discoveries

Reference tiny.reticulum node transport discoveries

Defined in node.transport.

API (22)

Actions

Public operations.

Types and contracts

Public types and contracts.

Values and defaults

Public values and defaults.

No direct callersNo direct callsnode.transportdiscoveries
Static calls · unresolved targets: unknown · external targets: unknown.

Source

Called byCallsNo direct callstest sourcelib.reticulum.src.node.transport.discoveries....py:975-984 keeps a discovery through ...test sourcelib.reticulum.src.node.transport.discoveriestest: discoveries admit maximum and r...test sourcelib.reticulum.src.node.transport.discoveriestest: discoveries engage over batched...node.transport.Stateactivatenode.transport.discoveries.Tableactivate
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallstest sourcelib.reticulum.src.node.transport.discoveries....py:975-984 keeps a discovery through ...test sourcelib.reticulum.src.node.transport.discoveriestest: discoveries admit maximum and r...private sourcelib.reticulum.src.node.transport.discoveries....expiredIndexprivate sourcelib.reticulum.src.node.transport.discoveries....indexOfnode.transport.discoveries.Tableadmits
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallstest sourcelib.reticulum.src.node.transport.discoveries....py:975-984 keeps a discovery through ...test sourcelib.reticulum.src.node.transport.discoveriestest: discoveries admit maximum and r...test sourcelib.reticulum.src.node.transport.discoveriestest: discoveries engage over batched...node.transport.discoveries.Tablefindprivate sourcelib.reticulum.src.node.transport.discoveries....slotnode.transport.discoveries.Tablebatch
Static calls · unresolved targets: 0 · external targets: 2.
Called byCallsNo direct callstest sourcelib.reticulum.src.node.transport.discoveries....py:975-984 keeps a discovery through ...test sourcelib.reticulum.src.node.transport.discoveriestest: discoveries admit maximum and r...test sourcelib.reticulum.src.node.transport.discoveriestest: discoveries engage over batched...node.transport.discoveries.Tablecount
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsNo direct callstest sourcelib.reticulum.src.node.transport.discoveries....py:975-984 keeps a discovery through ...test sourcelib.reticulum.src.node.transport.discoveriestest: discoveries admit maximum and r...test sourcelib.reticulum.src.node.transport.discoveriestest: discoveries engage over batched...node.transport.Statedeinitnode.transport.discoveries.Tabledeinit
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallstest sourcelib.reticulum.src.node.transport.discoveriestest: discoveries engage over batched...node.transport.discoveries.Tablefindprivate sourcelib.reticulum.src.node.transport.discoveries....slotnode.transport.discoveries.Tableengage
Static calls · unresolved targets: 0 · external targets: 1.
Called byCallsnode.transport.discoveries.Tablebatchnode.transport.discoveries.Tableengagetest sourcelib.reticulum.src.node.transport.discoveries....py:975-984 keeps a discovery through ...test sourcelib.reticulum.src.node.transport.discoveriestest: discoveries admit maximum and r...test sourcelib.reticulum.src.node.transport.discoveriestest: discoveries engage over batched...private sourcelib.reticulum.src.node.transport.discoveries....indexOfprivate sourcelib.reticulum.src.node.transport.discoverieslivenode.transport.discoveries.Tablefind
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsNo direct callstest sourcelib.reticulum.src.node.transport.discoveries....py:975-984 keeps a discovery through ...test sourcelib.reticulum.src.node.transport.discoveriestest: discoveries admit maximum and r...test sourcelib.reticulum.src.node.transport.discoveriestest: discoveries engage over batched...node.transport.Stateinitnode.transport.discoveries.Tableinit
Static calls · unresolved targets: 1 · external targets: 0.
Called byCallstest sourcelib.reticulum.src.node.transport.discoveries....py:975-984 keeps a discovery through ...test sourcelib.reticulum.src.node.transport.discoveriestest: discoveries engage over batched...private sourcelib.reticulum.src.node.transport.discoveries....indexOfprivate sourcelib.reticulum.src.node.transport.discoverieslivenode.transport.discoveries.Tabletake
Static calls · unresolved targets: 0 · external targets: 0.

Source: lib/reticulum/src/node/transport/discoveries.zig

zig
const std = @import("std");const alloc_phase = @import("alloc_phase");const carrier = @import("../../carrier/root.zig");pub const Seconds = u64;/// Fifteen seconds, the wait Reticulum@1.5.0 RNS/Transport.py:131,1786,3482/// gives a discovered path, so a caller learns how long a waiting question/// stays worth answering.pub const timeout: Seconds = 15;/// The set of carriers that wait for one discovered path. The set covers every/// carrier index the node can name.pub const Requesters = std.bit_set.ArrayBitSet(u64, 256);/// One destination that requesting carriers wait on.pub const Entry = struct {    destination: [16]u8,    expires: Seconds,    requesters: Requesters,    engaged: bool,};/// Reports whether an entry still counts. This port keeps an entry through the/// second it was created plus fifteen and expires it at plus sixteen.fn live(entry: *const Entry, now: Seconds) bool {    return now <= entry.expires;}comptime {    std.debug.assert(Requesters.bit_length == @as(usize, std.math.maxInt(carrier.Index)) + 1);}const TableLimits = struct {    discoveries_max: usize,};const TableCapacity = struct {    discoveries_max: usize,    storage_bytes: usize,    pub const DeriveError = error{ InvalidLimit, CapacityOverflow };    pub fn derive(limits: TableLimits) DeriveError!TableCapacity {        if (limits.discoveries_max == 0) return error.InvalidLimit;        const storage_bytes = alloc_phase.capacity.mul(            usize,            limits.discoveries_max,            @sizeOf(Entry),        ) catch return error.CapacityOverflow;        return .{ .discoveries_max = limits.discoveries_max, .storage_bytes = storage_bytes };    }};/// Waiting discovery requests, matching Reticulum@1.5.0/// RNS/Transport.py:1777-1792,3478-3497.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 InitError = Capacity.DeriveError || error{StorageLengthMismatch};    pub const Exhaustion = error{Full};    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.discoveries",            .kind = .phase_static,            .limit_source = .caller,            .storage = .{                .covered = &.{.{                    .id = "caller_discovery_table",                    .lifetime = .transferred,                    .detail = "caller storage for waiting discovery requests and requesters",                }},                .excluded = &.{"forwarded request and answer frames in effect storage"},            },            .capacity = .{                .inputs = &.{alloc_phase.capacity.bindInput(                    Limits,                    "discoveries_max",                    "discoveries_max",                )},                .type_selectors = &.{alloc_phase.capacity.bindType(Entry, "discovery")},                .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 = "a new destination replaces an expired entry, else rejects",            },            .risks = .{                .transitive = .{                    .status = .excluded,                    .detail = "discovery operations call no allocating owner",                },                .foreign = .{                    .status = .excluded,                    .detail = "discovery storage crosses no foreign boundary",                },            },            .work = .{ .equation = "operations scan at most discoveries_max entries" },            .obligations = &.{                .{ .key = "reticulum_discoveries_capacity", .role = .capacity_model },                .{ .key = "reticulum_discoveries_overload", .role = .overload },                .{ .key = "reticulum_discoveries_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;    }    /// Returns the live entry for one destination, so a caller checks whether a    /// question for this destination is already outstanding before it sends    /// another.    pub fn find(self: *Table, destination: [16]u8, now: Seconds) ?*Entry {        std.debug.assert(self.phase == .steady);        const index = self.indexOf(destination) orelse return null;        const entry = &self.entries[index];        if (!live(entry, now)) return null;        return entry;    }    /// Answers whether a request about this destination would find room, so a    /// caller asks before it reserves effects because the insert that follows    /// can refuse.    pub fn admits(self: *const Table, destination: [16]u8, now: Seconds) bool {        std.debug.assert(self.phase == .steady);        if (self.indexOf(destination) != null) return true;        if (self.len < self.capacity.discoveries_max) return true;        return self.expiredIndex(now) != null;    }    /// Adds a requesting carrier to the entry for one destination, following    /// Reticulum@1.5.0 RNS/Transport.py:1779-1792, so the answer reaches all of    /// them together. A new entry starts unengaged, which marks a question this    /// node has yet to pass on. A full table with nothing expired to replace    /// returns `error.Full`.    pub fn batch(        self: *Table,        destination: [16]u8,        requester: carrier.Index,        now: Seconds,    ) Exhaustion!void {        std.debug.assert(self.phase == .steady);        if (self.find(destination, now)) |entry| {            entry.requesters.set(requester);            return;        }        const target = try self.slot(destination, now);        target.* = .{            .destination = destination,            .expires = now +| timeout,            .requesters = Requesters.empty,            .engaged = false,        };        target.requesters.set(requester);    }    /// Marks the entry for one destination engaged and keeps the carriers    /// already waiting, following Reticulum@1.5.0 RNS/Transport.py:3478-3497.    /// The entry's wait restarts at the second of the call.    pub fn engage(        self: *Table,        destination: [16]u8,        requester: carrier.Index,        now: Seconds,    ) Exhaustion!void {        std.debug.assert(self.phase == .steady);        var requesters = Requesters.empty;        if (self.find(destination, now)) |entry| requesters = entry.requesters;        requesters.set(requester);        const target = try self.slot(destination, now);        target.* = .{            .destination = destination,            .expires = now +| timeout,            .requesters = requesters,            .engaged = true,        };    }    /// Removes the entry that an announce answers and returns it, following    /// Reticulum@1.5.0 RNS/Transport.py:2350-2352, so a caller sends the answer    /// to each carrier the entry holds. An entry past its wait is removed and    /// reported as null.    pub fn take(self: *Table, destination: [16]u8, now: Seconds) ?Entry {        std.debug.assert(self.phase == .steady);        const index = self.indexOf(destination) orelse return null;        const entry = self.entries[index];        self.len -= 1;        self.entries[index] = self.entries[self.len];        if (!live(&entry, now)) return null;        return entry;    }    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;    }    fn indexOf(self: *const Table, destination: [16]u8) ?usize {        std.debug.assert(self.len <= self.capacity.discoveries_max);        for (self.entries[0..self.len], 0..) |*entry, index| {            if (std.mem.eql(u8, &entry.destination, &destination)) return index;        }        return null;    }    fn expiredIndex(self: *const Table, now: Seconds) ?usize {        for (self.entries[0..self.len], 0..) |*entry, index| {            if (!live(entry, now)) return index;        }        return null;    }    fn slot(self: *Table, destination: [16]u8, now: Seconds) Exhaustion!*Entry {        if (self.indexOf(destination)) |index| return &self.entries[index];        if (self.len < self.capacity.discoveries_max) {            self.len += 1;            return &self.entries[self.len - 1];        }        const index = self.expiredIndex(now) orelse return error.Full;        return &self.entries[index];    }};comptime {    alloc_phase.capacity.requireProvisionedRejectingOwnerShape(Table);}test "discoveries admit maximum and reject a new destination at maximum plus one" {    comptime {        @stardustClaim(alloc_phase.capacity.witness(            Table,            "reticulum_discoveries_capacity",        ), null, null, null, null, null, null);        @stardustClaim(alloc_phase.capacity.witness(            Table,            "reticulum_discoveries_overload",        ), null, null, null, null, null, null);        @stardustClaim(alloc_phase.capacity.witness(            Table,            "reticulum_discoveries_work",        ), null, null, null, null, null, null);    }    const limits = TableLimits{ .discoveries_max = 2 };    const capacity = comptime TableCapacity.derive(limits) catch unreachable;    var bytes: [capacity.storage_bytes]u8 align(Table.storage_alignment) = undefined;    var table = try Table.init(&bytes, limits);    table.activate();    defer _ = table.deinit();    try table.batch(@splat(1), 0, 100);    try table.batch(@splat(2), 1, 100);    try std.testing.expect(!table.admits(@splat(3), 100));    try std.testing.expectError(error.Full, table.batch(@splat(3), 0, 100));    try std.testing.expectEqual(@as(usize, 2), table.count());    try table.batch(@splat(1), 2, 101);    const entry = table.find(@splat(1), 101) orelse return error.TestUnexpectedResult;    try std.testing.expect(entry.requesters.isSet(0) and entry.requesters.isSet(2));    try std.testing.expect(!entry.engaged);}test "Reticulum@1.5.0 RNS/Transport.py:975-984 keeps a discovery through plus 15, not plus 16" {    const limits = TableLimits{ .discoveries_max = 1 };    const capacity = comptime TableCapacity.derive(limits) catch unreachable;    var bytes: [capacity.storage_bytes]u8 align(Table.storage_alignment) = undefined;    var table = try Table.init(&bytes, limits);    table.activate();    defer _ = table.deinit();    try table.batch(@splat(7), 0, 1_000);    try std.testing.expect(table.find(@splat(7), 1_000 + timeout) != null);    try std.testing.expect(table.find(@splat(7), 1_000 + timeout + 1) == null);    try std.testing.expect(table.admits(@splat(8), 1_000 + timeout + 1));    try table.batch(@splat(8), 1, 1_000 + timeout + 1);    try std.testing.expectEqual(@as(usize, 1), table.count());    try std.testing.expect(table.take(@splat(7), 1_000 + timeout + 1) == null);}test "discoveries engage over batched requesters and cover every carrier" {    const limits = TableLimits{ .discoveries_max = 1 };    const capacity = comptime TableCapacity.derive(limits) catch unreachable;    var bytes: [capacity.storage_bytes]u8 align(Table.storage_alignment) = undefined;    var table = try Table.init(&bytes, limits);    table.activate();    defer _ = table.deinit();    try table.batch(@splat(5), 0, 10);    try table.engage(@splat(5), 255, 12);    const engaged = table.find(@splat(5), 12) orelse return error.TestUnexpectedResult;    try std.testing.expect(engaged.engaged);    try std.testing.expectEqual(@as(Seconds, 12 + timeout), engaged.expires);    try std.testing.expect(engaged.requesters.isSet(0) and engaged.requesters.isSet(255));    for (0..256) |index| try table.batch(@splat(5), @intCast(index), 20);    const waiting = table.take(@splat(5), 20) orelse return error.TestUnexpectedResult;    try std.testing.expectEqual(@as(usize, 256), waiting.requesters.count());    try std.testing.expectEqual(@as(usize, 0), table.count());}

Source: lib/reticulum/src/node/transport/root.zig:103

zig
pub const discoveries = @import("discoveries.zig");

Audit

Definitions23
Public names23
Members10
Version26.7.0
Revisiondaab053ee433