Skip to documentation
SLOP

tiny.reticulum.node.transport.path

Reference tiny.reticulum node transport path

Defined in node.transport.

API (29)

Actions

Public operations.

Types and contracts

Public types and contracts.

Values and defaults

Public values and defaults.

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

Source

Called byCallsNo direct callersnode.transport.pathemissionnode.transport.path.Entrytimebase
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsNo direct callstest sourcelib.reticulum.src.node.transport.path.test_Re...py:2137-2213 path add rulestest sourcelib.reticulum.src.node.transport.path.test_Re...py:2208-2212 an unresponsive path tak...test sourcelib.reticulum.src.node.transport.path.test_Re...py:2263-2265 evicts the oldest of 65 ...test sourcelib.reticulum.src.node.transport.path.test_Re...py:3146-3154 expires a path so any an...test sourcelib.reticulum.src.node.transport.path.test_Re...py:940-943 culls a path one second af...+4 morenode.transport.path.Tableactivate
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsNo direct callstest sourcelib.reticulum.src.node.transport.path.test_Re...py:3146-3154 expires a path so any an...test sourcelib.reticulum.src.node.transport.path.test_Re...py:940-943 culls a path one second af...test sourcelib.reticulum.src.node.transport.pathtest: paths admit maximum and replace...test sourcelib.reticulum.src.node.transport.pathtest: paths reclaim a culled slot bef...node.transport.path.Tablecount
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsNo direct callstest sourcelib.reticulum.src.node.transport.path.test_Re...py:2137-2213 path add rulestest sourcelib.reticulum.src.node.transport.path.test_Re...py:2208-2212 an unresponsive path tak...test sourcelib.reticulum.src.node.transport.path.test_Re...py:2263-2265 evicts the oldest of 65 ...test sourcelib.reticulum.src.node.transport.path.test_Re...py:3146-3154 expires a path so any an...test sourcelib.reticulum.src.node.transport.path.test_Re...py:940-943 culls a path one second af...+4 morenode.transport.path.Tabledeinit
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallstest sourcelib.reticulum.src.node.transport.path.test_Re...py:3146-3154 expires a path so any an...node.transport.path.Tablefindnode.transport.path.Tableexpire
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsnode.transport.path.Tableexpirenode.transport.path.TablehopsTonode.transport.path.Tablelearnnode.transport.path.TablemarkUnresponsivetest sourcelib.reticulum.src.node.transport.path.test_Re...py:2263-2265 evicts the oldest of 65 ...+4 moreprivate sourcelib.reticulum.src.node.transport.path.Tableslotprivate sourcelib.reticulum.src.node.transport.pathlivenode.transport.path.Tablefind
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallstest sourcelib.reticulum.src.node.transport.path.test_Re...py:3146-3154 expires a path so any an...test sourcelib.reticulum.src.node.transport.path.test_Re...py:940-943 culls a path one second af...node.transport.path.Tablefindnode.transport.path.TablehopsTo
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsNo direct callstest sourcelib.reticulum.src.node.transport.path.test_Re...py:2137-2213 path add rulestest sourcelib.reticulum.src.node.transport.path.test_Re...py:2208-2212 an unresponsive path tak...test sourcelib.reticulum.src.node.transport.path.test_Re...py:2263-2265 evicts the oldest of 65 ...test sourcelib.reticulum.src.node.transport.path.test_Re...py:3146-3154 expires a path so any an...test sourcelib.reticulum.src.node.transport.path.test_Re...py:940-943 culls a path one second af...+4 morenode.transport.path.Tableinit
Static calls · unresolved targets: 1 · external targets: 0.
Called byCallstest sourcelib.reticulum.src.node.transport.path.test_Re...py:2137-2213 path add rulestest sourcelib.reticulum.src.node.transport.path.test_Re...py:2208-2212 an unresponsive path tak...test sourcelib.reticulum.src.node.transport.path.test_Re...py:2263-2265 evicts the oldest of 65 ...test sourcelib.reticulum.src.node.transport.path.test_Re...py:3146-3154 expires a path so any an...test sourcelib.reticulum.src.node.transport.path.test_Re...py:940-943 culls a path one second af...+3 morenode.transport.path.Tablefindprivate sourcelib.reticulum.src.node.transport.path.Tablereclaimnode.transport.pathemissionnode.transport.path.Tablelearn
Static calls · unresolved targets: 0 · external targets: 2.
Called byCallstest sourcelib.reticulum.src.node.transport.path.test_Re...py:2208-2212 an unresponsive path tak...test sourcelib.reticulum.src.node.transport.path.test_Re...py:3146-3154 expires a path so any an...node.transport.path.Tablefindnode.transport.path.TablemarkUnresponsive
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsprivate sourcelib.reticulum.src.node.inboundacceptAnnouncetest sourcelib.reticulum.src.node.transport.path.test_Re...py:2137-2213 path add rulestest sourcelib.reticulum.src.node.transport.path.test_Re...py:2208-2212 an unresponsive path tak...test sourcelib.reticulum.src.node.transport.path.test_Re...py:2263-2265 evicts the oldest of 65 ...test sourcelib.reticulum.src.node.transport.path.test_Re...py:3146-3154 expires a path so any an...test sourcelib.reticulum.src.node.transport.path.test_Re...py:940-943 culls a path one second af...node.transport.pathemissionnode.transport.pathadmits
Static calls · unresolved targets: 0 · external targets: 2.
Called byCallsNo direct callsnode.transport.path.Entrytimebasenode.transport.path.Tablelearnnode.transport.pathadmitsnode.transport.pathemission
Static calls · unresolved targets: 0 · external targets: 0.

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

zig
const std = @import("std");const alloc_phase = @import("alloc_phase");const carrier = @import("../../carrier/root.zig");const destination = @import("../../destination/root.zig");const packet = @import("../../packet/root.zig");const wire = @import("../../wire/root.zig");pub const Seconds = u64;pub const Blob = [10]u8;/// Sixty-four, the number of recent random byte strings Reticulum@1.5.0/// RNS/Transport.py:159 keeps per path.pub const random_blobs_max: u8 = 64;/// One week, the lifetime Reticulum@1.5.0 RNS/Transport.py:123,152 gives a/// path.pub const lifetime: Seconds = 60 * 60 * 24 * 7;/// Whether the last thing sent over a path got through. Reticulum@1.5.0/// RNS/Transport.py:145-147 draws the same three-way distinction.pub const State = enum(u2) {    unknown,    unresponsive,    responsive,};/// One path the node worked out, holding the announces it has already taken and/// the payload of the latest one.pub const Entry = struct {    destination: [16]u8,    next_hop: [16]u8,    announce_hash: packet.Hash,    timestamp: Seconds,    expires: Seconds,    hops: u8,    carrier: carrier.Index,    state: State,    context_flag: u1,    blob_count: u8,    blob_next: u8,    payload_len: u16,    blobs: [random_blobs_max]Blob,    payload_bytes: [destination.announce.payload_bytes_max]u8,    /// Hands back the announce payload the path kept, and null when that    /// payload came in above 465 bytes under a HEADER_1 header and was    /// therefore never copied.    pub fn announcePayload(self: *const Entry) ?[]const u8 {        std.debug.assert(self.payload_len <= self.payload_bytes.len);        if (self.payload_len == 0) return null;        return self.payload_bytes[0..self.payload_len];    }    pub fn holdsBlob(self: *const Entry, blob: Blob) bool {        std.debug.assert(self.blob_count <= random_blobs_max);        for (self.blobs[0..self.blob_count]) |held| {            if (std.mem.eql(u8, &held, &blob)) return true;        }        return false;    }    /// Takes the newest emission time among the announces the path stores,    /// following Reticulum@1.5.0 RNS/Transport.py:3654-3660.    pub fn timebase(self: *const Entry) Seconds {        std.debug.assert(self.blob_count <= random_blobs_max);        var newest: Seconds = 0;        for (self.blobs[0..self.blob_count]) |held| newest = @max(newest, emission(held));        return newest;    }};/// Reads bytes 5 through 10 of the announce's random bytes as an emission time,/// following Reticulum@1.5.0 RNS/Transport.py:3650-3651.pub fn emission(blob: Blob) Seconds {    return std.mem.readInt(u40, blob[5..10], .big);}/// One announce whose signature has checked out, put forward for the path/// store.pub const Candidate = struct {    destination: [16]u8,    next_hop: [16]u8,    announce_hash: packet.Hash,    hops: u8,    carrier: carrier.Index,    context_flag: u1,    blob: Blob,    payload: []const u8,};/// Decides whether an announce replaces a path, following Reticulum@1.5.0/// RNS/Transport.py:2137-2213. A destination with no path admits any announce./// An announce at the path's hop count or nearer is taken when the path has not/// seen it before and it was emitted after the path's timebase. A farther/// announce is taken on a path past its week when the path has not seen it/// before. A farther announce emitted after the timebase is taken when the path/// has not seen it before. A farther announce emitted before the timebase is/// refused. On a path marked unresponsive, an announce it has already taken/// still wins the path back, provided that announce is stamped at the same/// instant as the newest one the path holds and comes from further off./// Reticulum@1.5.0 RNS/Transport.py:2208-2212 is the one place a repeat gets/// through.pub fn admits(known: ?*const Entry, candidate: Candidate, now: Seconds) bool {    std.debug.assert(candidate.hops <= wire.pathfinder_hops);    const entry = known orelse return true;    std.debug.assert(std.mem.eql(u8, &entry.destination, &candidate.destination));    const unseen = !entry.holdsBlob(candidate.blob);    const emitted = emission(candidate.blob);    const base = entry.timebase();    if (candidate.hops <= entry.hops) return unseen and emitted > base;    if (now >= entry.expires) return unseen;    if (emitted > base) return unseen;    if (emitted < base) return false;    return entry.state == .unresponsive;}/// Reports whether a path entry still counts. A path stops counting one week/// past the second it was written, the age Reticulum@1.5.0/// RNS/Transport.py:940-943 sets. A zero expiry marks a path that `expire`/// dropped.fn live(entry: *const Entry, now: Seconds) bool {    if (entry.expires == 0) return false;    return now -| entry.timestamp <= lifetime;}const TableLimits = struct {    paths_max: usize,};const TableCapacity = struct {    paths_max: usize,    storage_bytes: usize,    pub const DeriveError = error{ InvalidLimit, CapacityOverflow };    pub fn derive(limits: TableLimits) DeriveError!TableCapacity {        if (limits.paths_max == 0) return error.InvalidLimit;        const storage_bytes = alloc_phase.capacity.mul(            usize,            limits.paths_max,            @sizeOf(Entry),        ) catch return error.CapacityOverflow;        return .{ .paths_max = limits.paths_max, .storage_bytes = storage_bytes };    }};/// Paths learned from announces, matching Reticulum@1.5.0/// RNS/Transport.py:2374-2375.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 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.paths",            .kind = .phase_static,            .limit_source = .caller,            .storage = .{                .covered = &.{.{                    .id = "caller_path_table",                    .lifetime = .transferred,                    .detail = "caller storage for paths, random blobs, and announce payloads",                }},                .excluded = &.{                    "borrowed announce payload inputs",                    "persistent path records",                },            },            .capacity = .{                .inputs = &.{alloc_phase.capacity.bindInput(                    Limits,                    "paths_max",                    "paths_max",                )},                .type_selectors = &.{alloc_phase.capacity.bindType(Entry, "path")},                .nodes = &.{                    .{ .input = 0 },                    .{ .scale = .{                        .node = 0,                        .coefficient = .{ .size_of_concrete_type = 0 },                    } },                },                .assertions = &.{.{                    .scope = .closure_total,                    .measure = .retained,                    .relation = .exact,                    .expression = 1,                }},            },            .overload = .{                .kind = .not_applicable,                .detail = "a new path replaces a culled path, else the oldest timestamp",            },            .risks = .{                .transitive = .{                    .status = .excluded,                    .detail = "path operations call no allocating owner",                },                .foreign = .{                    .status = .excluded,                    .detail = "path storage crosses no foreign boundary",                },            },            .work = .{ .equation = "operations scan at most paths_max entries and 64 blobs" },            .obligations = &.{                .{ .key = "reticulum_paths_capacity", .role = .capacity_model },                .{ .key = "reticulum_paths_replace", .role = .overload },                .{ .key = "reticulum_paths_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;    }    /// Looks a destination up and hands back a path only while that path sits    /// inside the one-week age Reticulum@1.5.0 RNS/Transport.py:940-943 sets.    pub fn find(self: *Table, hash: [16]u8, now: Seconds) ?*Entry {        std.debug.assert(self.phase == .steady);        const entry = self.slot(hash) orelse return null;        if (!live(entry, now)) return null;        return entry;    }    /// Reports the path hop count, or the pathfinder maximum when the table    /// holds no live path, following Reticulum@1.5.0    /// RNS/Transport.py:3063-3070.    pub fn hopsTo(self: *Table, hash: [16]u8, now: Seconds) u8 {        const entry = self.find(hash, now) orelse return wire.pathfinder_hops;        std.debug.assert(entry.hops <= wire.pathfinder_hops);        return entry.hops;    }    /// Records an accepted announce, following Reticulum@1.5.0    /// RNS/Transport.py:2261-2265,2374-2375. An announce that wins back an    /// unresponsive path at an emission time the path already holds leaves the    /// unresponsive mark standing, so the next announce stamped at that instant    /// wins the path in turn. Every other accepted announce clears the mark,    /// following Reticulum@1.5.0 RNS/Transport.py:2155,2189,2200,2208-2212.    pub fn learn(self: *Table, candidate: Candidate, now: Seconds) *Entry {        std.debug.assert(self.phase == .steady);        std.debug.assert(candidate.hops <= wire.pathfinder_hops);        const previous = self.find(candidate.destination, now);        const target = previous orelse self.reclaim(candidate.destination, now);        if (previous == null) {            target.blob_count = 0;            target.blob_next = 0;        }        const repeats = previous != null and            now < target.expires and            emission(candidate.blob) == target.timebase();        if (!target.holdsBlob(candidate.blob)) {            target.blobs[target.blob_next] = candidate.blob;            target.blob_next = (target.blob_next + 1) % random_blobs_max;            target.blob_count = @min(target.blob_count + 1, random_blobs_max);        }        if (!repeats) target.state = .unknown;        target.destination = candidate.destination;        target.next_hop = candidate.next_hop;        target.announce_hash = candidate.announce_hash;        target.timestamp = now;        target.expires = now +| lifetime;        target.hops = candidate.hops;        target.carrier = candidate.carrier;        target.context_flag = candidate.context_flag;        const retained = candidate.payload.len <= target.payload_bytes.len;        target.payload_len = if (retained) @intCast(candidate.payload.len) else 0;        if (retained) @memcpy(target.payload_bytes[0..candidate.payload.len], candidate.payload);        std.debug.assert(self.find(candidate.destination, now) == target);        return target;    }    /// Notes that a link went unanswered over this path, as Reticulum@1.5.0    /// RNS/Transport.py:3157-3162 does. The answer says whether a path to that    /// destination was still inside its week.    pub fn markUnresponsive(self: *Table, hash: [16]u8, now: Seconds) bool {        std.debug.assert(self.phase == .steady);        const entry = self.find(hash, now) orelse return false;        entry.state = .unresponsive;        return true;    }    /// Throws a path away, leaving its destination open to the next announce    /// from any distance. Reticulum@1.5.0 RNS/Transport.py:3146-3154 reaches    /// the same end by zeroing the written-at second and running its removal    /// pass at once. This port zeroes the expiry because it runs no cull pass.    /// The call reports whether the table held a live path.    pub fn expire(self: *Table, hash: [16]u8, now: Seconds) bool {        std.debug.assert(self.phase == .steady);        const entry = self.find(hash, now) orelse return false;        entry.timestamp = 0;        entry.expires = 0;        return true;    }    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 slot(self: *Table, hash: [16]u8) ?*Entry {        var found: ?*Entry = null;        for (self.entries[0..self.len]) |*entry| {            if (!std.mem.eql(u8, &entry.destination, &hash)) continue;            std.debug.assert(found == null);            found = entry;        }        return found;    }    fn reclaim(self: *Table, hash: [16]u8, now: Seconds) *Entry {        if (self.slot(hash)) |culled| return culled;        for (self.entries[0..self.len]) |*entry| {            if (!live(entry, now)) return entry;        }        if (self.len < self.capacity.paths_max) {            self.len += 1;            return &self.entries[self.len - 1];        }        var oldest = &self.entries[0];        for (self.entries[1..self.len]) |*entry| {            if (entry.timestamp < oldest.timestamp) oldest = entry;        }        return oldest;    }};comptime {    alloc_phase.capacity.requireProvisionedExactOwnerShape(Table);}fn blobAt(salt: u8, emitted: Seconds) Blob {    var blob: Blob = @splat(salt);    std.mem.writeInt(u40, blob[5..10], @intCast(emitted), .big);    return blob;}fn offer(destination_byte: u8, hops: u8, blob: Blob) Candidate {    return .{        .destination = @splat(destination_byte),        .next_hop = @splat(0xb0),        .announce_hash = @splat(destination_byte),        .hops = hops,        .carrier = 1,        .context_flag = 0,        .blob = blob,        .payload = "announce payload",    };}test "paths admit maximum and replace the oldest at maximum plus one" {    comptime {        @stardustClaim(alloc_phase.capacity.witness(            Table,            "reticulum_paths_capacity",        ), null, null, null, null, null, null);        @stardustClaim(alloc_phase.capacity.witness(            Table,            "reticulum_paths_replace",        ), null, null, null, null, null, null);        @stardustClaim(alloc_phase.capacity.witness(            Table,            "reticulum_paths_work",        ), null, null, null, null, null, null);    }    const capacity = comptime TableCapacity.derive(.{ .paths_max = 3 }) catch unreachable;    var bytes: [capacity.storage_bytes]u8 align(Table.storage_alignment) = undefined;    var table = try Table.init(&bytes, .{ .paths_max = 3 });    table.activate();    defer _ = table.deinit();    for (1..4) |value| {        const byte: u8 = @intCast(value);        _ = table.learn(offer(byte, 1, blobAt(byte, 100)), 100 + value);    }    try std.testing.expectEqual(@as(usize, 3), table.count());    _ = table.learn(offer(4, 1, blobAt(4, 200)), 200);    try std.testing.expectEqual(@as(usize, 3), table.count());    try std.testing.expect(table.find(@splat(1), 200) == null);    for (2..5) |value| try std.testing.expect(table.find(@splat(@intCast(value)), 200) != null);}test "paths reclaim a culled slot before the oldest live path" {    const capacity = comptime TableCapacity.derive(.{ .paths_max = 2 }) catch unreachable;    var bytes: [capacity.storage_bytes]u8 align(Table.storage_alignment) = undefined;    var table = try Table.init(&bytes, .{ .paths_max = 2 });    table.activate();    defer _ = table.deinit();    _ = table.learn(offer(1, 1, blobAt(1, 1)), 10);    _ = table.learn(offer(2, 1, blobAt(2, 1)), 5);    const later: Seconds = 10 + lifetime;    try std.testing.expect(table.find(@splat(2), later) == null);    _ = table.learn(offer(3, 1, blobAt(3, 1)), later);    try std.testing.expect(table.find(@splat(1), later) != null);    try std.testing.expect(table.find(@splat(3), later) != null);    try std.testing.expectEqual(@as(usize, 2), table.count());}test "Reticulum@1.5.0 RNS/Transport.py:2137-2213 path add rules" {    const capacity = comptime TableCapacity.derive(.{ .paths_max = 1 }) catch unreachable;    var bytes: [capacity.storage_bytes]u8 align(Table.storage_alignment) = undefined;    var table = try Table.init(&bytes, .{ .paths_max = 1 });    table.activate();    defer _ = table.deinit();    const accepted_at: Seconds = 1_000;    try std.testing.expect(admits(null, offer(7, 3, blobAt(1, 500)), accepted_at));    const entry = table.learn(offer(7, 3, blobAt(1, 500)), accepted_at);    try std.testing.expectEqual(accepted_at + lifetime, entry.expires);    try std.testing.expect(admits(entry, offer(7, 3, blobAt(2, 501)), accepted_at));    try std.testing.expect(admits(entry, offer(7, 2, blobAt(2, 501)), accepted_at));    try std.testing.expect(!admits(entry, offer(7, 3, blobAt(1, 500)), accepted_at));    try std.testing.expect(!admits(entry, offer(7, 3, blobAt(2, 500)), accepted_at));    try std.testing.expect(!admits(entry, offer(7, 2, blobAt(2, 499)), accepted_at));    const before = entry.expires - 1;    try std.testing.expect(admits(entry, offer(7, 4, blobAt(2, 501)), before));    try std.testing.expect(!admits(entry, offer(7, 4, blobAt(2, 500)), before));    try std.testing.expect(!admits(entry, offer(7, 4, blobAt(2, 499)), before));    try std.testing.expect(admits(entry, offer(7, 4, blobAt(2, 499)), entry.expires));    try std.testing.expect(!admits(entry, offer(7, 4, blobAt(1, 500)), entry.expires));}test "Reticulum@1.5.0 RNS/Transport.py:2263-2265 evicts the oldest of 65 blobs" {    const capacity = comptime TableCapacity.derive(.{ .paths_max = 1 }) catch unreachable;    var bytes: [capacity.storage_bytes]u8 align(Table.storage_alignment) = undefined;    var table = try Table.init(&bytes, .{ .paths_max = 1 });    table.activate();    defer _ = table.deinit();    const now: Seconds = 5_000;    for (0..65) |index| {        const offered = offer(9, 1, blobAt(@intCast(index), 1_000 + index));        try std.testing.expect(admits(table.find(offered.destination, now), offered, now));        _ = table.learn(offered, now);    }    const entry = table.find(@splat(9), now).?;    try std.testing.expectEqual(random_blobs_max, entry.blob_count);    try std.testing.expect(!entry.holdsBlob(blobAt(0, 1_000)));    try std.testing.expect(entry.holdsBlob(blobAt(1, 1_001)));    try std.testing.expect(entry.holdsBlob(blobAt(64, 1_064)));    try std.testing.expectEqual(@as(Seconds, 1_064), entry.timebase());}test "Reticulum@1.5.0 RNS/Transport.py:940-943 culls a path one second after a week" {    const capacity = comptime TableCapacity.derive(.{ .paths_max = 1 }) catch unreachable;    var bytes: [capacity.storage_bytes]u8 align(Table.storage_alignment) = undefined;    var table = try Table.init(&bytes, .{ .paths_max = 1 });    table.activate();    defer _ = table.deinit();    _ = table.learn(offer(5, 2, blobAt(5, 10)), 100);    try std.testing.expectEqual(@as(u8, 2), table.hopsTo(@splat(5), 100 + lifetime));    const culled_at: Seconds = 100 + lifetime + 1;    try std.testing.expect(table.find(@splat(5), culled_at) == null);    try std.testing.expectEqual(wire.pathfinder_hops, table.hopsTo(@splat(5), culled_at));    const replay = offer(5, 3, blobAt(5, 10));    try std.testing.expect(admits(table.find(replay.destination, culled_at), replay, culled_at));    const entry = table.learn(replay, culled_at);    try std.testing.expectEqual(@as(u8, 1), entry.blob_count);    try std.testing.expectEqual(@as(usize, 1), table.count());}test "Reticulum@1.5.0 RNS/Transport.py:2208-2212 an unresponsive path takes its announce again" {    const capacity = comptime TableCapacity.derive(.{ .paths_max = 1 }) catch unreachable;    var bytes: [capacity.storage_bytes]u8 align(Table.storage_alignment) = undefined;    var table = try Table.init(&bytes, .{ .paths_max = 1 });    table.activate();    defer _ = table.deinit();    const now: Seconds = 1_000;    const entry = table.learn(offer(7, 2, blobAt(1, 500)), now);    const farther = offer(7, 3, blobAt(1, 500));    try std.testing.expectEqual(State.unknown, entry.state);    try std.testing.expect(!admits(entry, farther, now));    try std.testing.expect(table.markUnresponsive(@splat(7), now));    try std.testing.expectEqual(State.unresponsive, entry.state);    try std.testing.expect(admits(entry, farther, now));    try std.testing.expect(!admits(entry, offer(7, 2, blobAt(1, 500)), now));    try std.testing.expect(!admits(entry, offer(7, 3, blobAt(2, 499)), now));    try std.testing.expect(admits(entry, offer(7, 3, blobAt(2, 500)), now));    _ = table.learn(farther, now + 1);    try std.testing.expectEqual(State.unresponsive, entry.state);    try std.testing.expectEqual(@as(u8, 1), entry.blob_count);    try std.testing.expectEqual(@as(u8, 3), entry.hops);    try std.testing.expect(!admits(entry, farther, now + 1));    try std.testing.expect(admits(entry, offer(7, 4, blobAt(1, 500)), now + 1));    _ = table.learn(offer(7, 3, blobAt(2, 700)), now + 2);    try std.testing.expectEqual(State.unknown, entry.state);    try std.testing.expect(!admits(entry, offer(7, 4, blobAt(1, 500)), now + 2));}test "Reticulum@1.5.0 RNS/Transport.py:3146-3154 expires a path so any announce replaces it" {    const capacity = comptime TableCapacity.derive(.{ .paths_max = 1 }) catch unreachable;    var bytes: [capacity.storage_bytes]u8 align(Table.storage_alignment) = undefined;    var table = try Table.init(&bytes, .{ .paths_max = 1 });    table.activate();    defer _ = table.deinit();    const now: Seconds = 1_000;    _ = table.learn(offer(7, 1, blobAt(1, 500)), now);    try std.testing.expect(table.expire(@splat(7), now));    try std.testing.expect(table.find(@splat(7), now) == null);    try std.testing.expect(table.find(@splat(7), now + lifetime + 1) == null);    try std.testing.expect(!table.expire(@splat(7), now));    try std.testing.expect(!table.markUnresponsive(@splat(7), now));    try std.testing.expectEqual(wire.pathfinder_hops, table.hopsTo(@splat(7), now));    const older = offer(7, 5, blobAt(2, 1));    try std.testing.expect(admits(table.find(older.destination, now), older, now));    const relearned = table.learn(older, now);    try std.testing.expectEqual(@as(u8, 5), relearned.hops);    try std.testing.expectEqual(@as(u8, 1), relearned.blob_count);    try std.testing.expectEqual(@as(usize, 1), table.count());}test "paths copy announce payloads up to the HEADER_2 relay bound" {    const capacity = comptime TableCapacity.derive(.{ .paths_max = 2 }) catch unreachable;    var bytes: [capacity.storage_bytes]u8 align(Table.storage_alignment) = undefined;    var table = try Table.init(&bytes, .{ .paths_max = 2 });    table.activate();    defer _ = table.deinit();    var long: [destination.announce.payload_bytes_max + 1]u8 = @splat(0x5a);    var offered = offer(1, 1, blobAt(1, 1));    offered.payload = long[0..destination.announce.payload_bytes_max];    const kept = table.learn(offered, 1);    long[0] = 0;    try std.testing.expectEqual(@as(usize, long.len - 1), kept.announcePayload().?.len);    try std.testing.expectEqual(@as(u8, 0x5a), kept.announcePayload().?[0]);    offered = offer(2, 1, blobAt(2, 1));    offered.payload = &long;    const skipped = table.learn(offered, 1);    try std.testing.expect(skipped.announcePayload() == null);    try std.testing.expectEqual(@as(u8, 1), skipped.hops);}

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

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

Complete caller list for node.transport.path.Table.activate

9 direct callers.

Complete caller list for node.transport.path.Table.deinit

9 direct callers.

Complete caller list for node.transport.path.Table.find

9 direct callers.

Complete caller list for node.transport.path.Table.init

9 direct callers.

Complete caller list for node.transport.path.Table.learn

8 direct callers.

Audit

Definitions30
Public names30
Members30
Version26.7.0
Revisiondaab053ee433