Skip to documentation
SLOP

tiny.reticulum.node.transport.retransmit

Reference tiny.reticulum node transport retransmit

Defined in node.transport.

API (7)

Actions

Public operations.

Types and contracts

Public types and contracts.

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

Source

Called byCallsNo direct callsprivate sourcelib.reticulum.src.node.inboundacceptAnnouncenode.transport.requestsreceivenode.transport.retransmitcanSchedule
Static calls · unresolved targets: 0 · external targets: 1.
Called byCallsNo direct callsprivate sourcelib.reticulum.src.node.inboundacceptAnnouncenode.transport.retransmitdetect
Static calls · unresolved targets: 0 · external targets: 2.
Called byCallsprivate sourcelib.reticulum.src.node.inboundacceptAnnouncenode.inbounddiagnosticnode.transport.announces.Recordinitnode.transport.retransmitschedulenode.transport.retransmitenter
Static calls · unresolved targets: 0 · external targets: 1.
Called byCallsNo direct callsprivate sourcelib.reticulum.src.node.inboundacceptAnnouncenode.transport.retransmitplan
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsNo direct callsprivate sourcelib.reticulum.src.node.transport.requestsanswerKnownnode.transport.retransmitenternode.transport.retransmitsweepnode.transport.retransmitschedule
Static calls · unresolved targets: 0 · external targets: 3.
Called byCallsnode.inboundtimerExpirednode.outboundframeCountnode.outboundreservenode.transport.retransmitscheduleprivate sourcelib.reticulum.src.node.transport.retransmitvisitnode.transport.retransmitsweep
Static calls · unresolved targets: 0 · external targets: 4.

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

zig
const std = @import("std");const reticulum = @import("../../root.zig");const announces = @import("announces.zig");const destination = reticulum.destination;const node = reticulum.node;const packet = reticulum.packet;const wire = reticulum.wire;/// Describes what becomes of an accepted announce at the rebroadcast queue, so/// the receive path knows whether accepting an announce also queues one to/// send.pub const Plan = enum {    none,    write,    too_large,};const Visit = enum {    kept,    completed,    stopped,};/// Decides whether a transport node rebroadcasts, following Reticulum@1.5.0/// RNS/Transport.py:2267-2289, so a caller can reserve effects for the/// rebroadcast in the same step before it accepts an announce. An announce the/// path table refused leaves the queue untouched, as does one that reached a/// node carrying no traffic for others. An announce marked a path response/// leaves the queue untouched, because it answers the one carrier that asked./// An announce at or past the pathfinder maximum leaves the queue untouched. A/// HEADER_1 payload above 465 bytes has no room left under a HEADER_2/// rebroadcast, so the step reports it and queues nothing.pub fn plan(node_owner: *const node.Node, value: wire.Packet, admitted: bool) Plan {    std.debug.assert(value.packet_type == .announce);    if (!admitted) return .none;    if (!node_owner.transport.enabled) return .none;    if (value.context == .path_response) return .none;    if (value.hops >= wire.pathfinder_hops) return .none;    if (value.payload.len > destination.announce.payload_bytes_max) return .too_large;    return .write;}/// Answers whether a slot stands free for the timer that drives the rebroadcast/// queue, so a caller checks before changing any table because a sweep with/// nowhere to put its timer has to fail the whole step.pub fn canSchedule(node_owner: *const node.Node) bool {    return node_owner.timers.canScheduleAfterCancel(.announces, null);}/// Completes entries that neighbors rebroadcast, following Reticulum@1.5.0/// RNS/Transport.py:2093-2116, so the node learns its own copy is no longer/// needed when a neighbor carries the same announce. An announce heard at this/// entry's own hop count counts as a neighbor rebroadcast, and two of them/// drop the entry once the node has sent it at least once. An announce heard/// one hop further on means a neighbor already passed this entry along, so the/// step drops the entry when the node has sent it and its next send is still/// ahead.pub fn detect(node_owner: *node.Node, value: wire.Packet, now: node.Seconds) void {    if (!node_owner.transport.enabled) return;    if (value.transport_id == null) return;    const entry = node_owner.transport.announces.find(value.destination) orelse return;    std.debug.assert(value.hops >= 1);    std.debug.assert(entry.record.hops <= wire.pathfinder_hops);    const heard: u16 = value.hops - 1;    const record = &entry.record;    if (heard == record.hops) {        record.local_rebroadcasts +|= 1;        if (record.retries > 0 and record.local_rebroadcasts >= announces.local_rebroadcasts_max) {            _ = node_owner.transport.announces.remove(value.destination);            return;        }    }    const passed_on = heard == @as(u16, record.hops) + 1;    if (passed_on and record.retries > 0 and now < record.due) {        _ = node_owner.transport.announces.remove(value.destination);    }}/// Queues the announce with its turn set one second ahead, as Reticulum@1.5.0/// RNS/Transport.py:2248-2289 does, so the step turns the decision into a/// queued rebroadcast and arms the sweep that will send it. A payload too large/// to relay reports `announce_relay_too_large`. A full announce table reports/// `announce_table_full`.pub fn enter(    node_owner: *node.Node,    value: wire.Packet,    decision: Plan,    hash: packet.Hash,    now: node.Seconds,) void {    switch (decision) {        .none => {},        .too_large => node.inbound.diagnostic(node_owner, .announce_relay_too_large, hash),        .write => {            const due = now +| announces.first_delay;            _ = node_owner.transport.announces.insert(announces.Record.init(.{                .destination = value.destination,                .due = due,                .timestamp = now,                .retries = 0,                .hops = value.hops,                .block_rebroadcasts = false,                .attached = null,                .context_flag = value.context_flag,                .payload = value.payload,            })) catch {                node.inbound.diagnostic(node_owner, .announce_table_full, hash);                return;            };            schedule(node_owner, due);        },    }}/// Moves the timer only when the second given falls sooner than the one already/// set, because entries share one timer.pub fn schedule(node_owner: *node.Node, at: node.Seconds) void {    if (node_owner.timers.scheduledAt(.announces)) |scheduled| {        if (scheduled <= at) return;    }    node_owner.timers.schedule(.announces, at) catch unreachable;    node_owner.effects.push(.{ .schedule_timer = .{        .id = .announces,        .at = at,    } }) catch unreachable;}/// Sends due rebroadcasts by ascending hops when the timer fires, following/// Reticulum@1.5.0 RNS/Transport.py:737-799,1239-1240. A pass that would/// overrun the room for effects breaks off and sets the timer at the second it/// broke off on. An empty table cancels the timer and returns. A destination/// whose identity the node no longer recalls is dropped from the queue.pub fn sweep(node_owner: *node.Node, now: node.Seconds) node.StepError!void {    if (node_owner.transport.announces.count() == 0) {        _ = node_owner.timers.cancel(.announces);        return;    }    const widest = node.outbound.frameCount(node_owner, .all);    try node.outbound.reserve(node_owner, widest + 1, widest);    _ = node_owner.timers.cancel(.announces);    var hops: u16 = 0;    while (hops <= wire.pathfinder_hops) : (hops += 1) {        var index: usize = 0;        while (index < node_owner.transport.announces.len) {            const entry = &node_owner.transport.announces.entries[index];            if (entry.record.hops != hops or entry.record.due > now) {                index += 1;                continue;            }            switch (try visit(node_owner, entry, now)) {                .kept => index += 1,                .completed => node_owner.transport.announces.removeAt(index),                .stopped => {                    schedule(node_owner, now);                    return;                },            }        }    }    if (node_owner.transport.announces.earliestDue()) |due| schedule(node_owner, due);}/// Answers whether an entry has reached the rebroadcast or retry limit,/// following Reticulum@1.5.0 RNS/Transport.py:743-748, so an announce stops/// circulating.fn completed(record: *const announces.Record) bool {    if (record.retries > 0 and record.retries >= announces.local_rebroadcasts_max) return true;    return record.retries > announces.retries_max;}fn visit(node_owner: *node.Node, entry: *announces.Entry, now: node.Seconds) node.StepError!Visit {    const record = &entry.record;    std.debug.assert(record.retries <= announces.local_rebroadcasts_max);    std.debug.assert(record.due <= now);    if (completed(record)) return .completed;    if (node_owner.known_identities.recall(record.destination) == null) return .completed;    const fanout: node.outbound.Fanout = if (record.attached) |index| .{ .one = index } else .all;    const routed = record.attached != null or        node_owner.transport.paths.find(record.destination, now) != null;    const carriers = if (routed) node.outbound.frameCount(node_owner, fanout) else 0;    node.outbound.reserve(node_owner, carriers + 1, carriers) catch return .stopped;    if (carriers > 0) {        const frame = encode(node_owner, record);        _ = node_owner.duplicate_hashes.insert(wire.hash.full(frame) catch unreachable);        try node.outbound.transmit(node_owner, fanout, frame);    }    record.due = now +| announces.retry_delay;    record.retries += 1;    _ = entry.release(now);    return .kept;}/// Assembles the outgoing packet under a HEADER_2 header from the payload the/// queue kept, as Reticulum@1.5.0 RNS/Transport.py:756-779 does, carrying this/// node's own transport identity so the next node learns who passed it on. A/// record that blocks further rebroadcasts is marked a path response.fn encode(node_owner: *node.Node, record: *const announces.Record) []const u8 {    const transport_hash = node_owner.transport.identity_hash orelse unreachable;    std.debug.assert(record.hops < wire.pathfinder_hops);    return wire.encode(.{        .ifac = 0,        .header = .two,        .context_flag = record.context_flag,        .transport = .transport,        .destination_type = .single,        .packet_type = .announce,        .hops = record.hops,        .transport_id = transport_hash,        .destination = record.destination,        .context = if (record.block_rebroadcasts) .path_response else .none,        .payload = record.payload(),    }, node_owner.scratch) catch unreachable;}

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

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

Audit

Definitions8
Public names8
Members3
Version26.7.0
Revisiondaab053ee433