tiny.reticulum.node.announce
Defined in node.
API (1)
Actions
Public operations.
run: Announces one of the node's own destinations, so every node that hears it learns the destination's public keys and how far away it sits, as Reticulum@1.5.0 RNS/Destination.py:228-315 builds it.
Source
Source: lib/reticulum/src/node/announce.zig
zig
const std = @import("std");const reticulum = @import("../root.zig");const destination = reticulum.destination;const identity = reticulum.identity;const node = reticulum.node;const wire = reticulum.wire;const timestamp_max: node.Seconds = std.math.maxInt(u40);fn needsRotation(ring: *const identity.Ring, now: node.Seconds) bool { if (ring.count == 0) return true; const latest = ring.latest_time orelse return true; const deadline = std.math.add(node.Seconds, latest, identity.ratchet.reference_interval) catch std.math.maxInt(node.Seconds); return now > deadline;}fn randomHash(random: [5]u8, now: node.Seconds) [10]u8 { std.debug.assert(now <= timestamp_max); var result: [10]u8 = undefined; @memcpy(result[0..5], &random); std.mem.writeInt(u40, result[5..10], @intCast(now), .big); return result;}fn rotatingKey( node_owner: *node.Node, identity_index: usize, announce: node.ApplicationAnnounce,) node.StepError!?[32]u8 { const binding = node_owner.ratchet_bindings[identity_index]; const ring = binding.ring orelse return null; if (needsRotation(ring, announce.now)) { const fresh_bytes = announce.fresh_rotating_key orelse return error.MissingEntropy; var fresh = identity.Ratchet.fromBytes(fresh_bytes); defer fresh.zero(); _ = ring.rotate(announce.now, identity.ratchet.reference_interval, fresh); } const latest = ring.latest() orelse return error.MissingEntropy; return latest.publicBytes();}fn buildFrame( node_owner: *node.Node, value: node.ApplicationAnnounce, private: *const identity.Private, name_hash: [10]u8, rotating_public: ?[32]u8,) node.StepError![]const u8 { const payload_start: usize = wire.header_one_bytes; const payload = destination.announce.build(.{ .destination_hash = value.destination, .name_hash = name_hash, .random_hash = randomHash(value.random, value.now), .rotating_public_key = rotating_public, .app_data = value.app_data, }, private, node_owner.scratch[payload_start..]) catch |err| switch (err) { error.AppDataTooLong => return error.AppDataTooLong, error.OutputTooSmall => return error.PacketTooLarge, }; return wire.encode(.{ .ifac = 0, .header = .one, .context_flag = @intFromBool(rotating_public != null), .transport = .broadcast, .destination_type = .single, .packet_type = .announce, .hops = 0, .transport_id = null, .destination = value.destination, .context = if (value.path_response == null) .none else .path_response, .payload = payload, }, node_owner.scratch) catch return error.PacketTooLarge;}fn fanoutFor(value: node.ApplicationAnnounce) node.outbound.Fanout { const index = value.path_response orelse return .all; return .{ .one = index };}/// Announces one of the node's own destinations, so every node that hears it learns the/// destination's public keys and how far away it sits, as Reticulum@1.5.0/// RNS/Destination.py:228-315 builds it. The same call answers a path request. With a carrier named/// to answer on, the announce goes out on that carrier alone, following Reticulum@1.5.0/// RNS/Transport.py:3379-3382, and otherwise it goes out on every outgoing carrier. The announce/// stamps the caller's instant into five of its ten random bytes, so an instant past 2^40 - 1/// seconds returns `error.TimestampOutOfRange`. A destination hash with no registration behind it/// returns `error.UnknownDestination`, and a registration of another kind, or one that names no/// local identity, returns `error.InvalidDestination`. A fanout that reaches no outgoing carrier/// returns `error.NoOutgoingCarrier`. With a rotating-key ring bound to the destination's identity,/// the announce carries a rotating public key, and the node rotates a fresh one in when the ring is/// empty or its newest key is more than thirty minutes old. A rotation with no fresh key from the/// caller returns `error.MissingEntropy`. The node remembers the rotating key it published and/// stores the announce's packet hash, so the same announce coming back is a duplicate.pub fn run(node_owner: *node.Node, value: node.ApplicationAnnounce) node.StepError!void { if (value.now > timestamp_max) return error.TimestampOutOfRange; const entry = node_owner.destinations.find(value.destination) orelse return error.UnknownDestination; if (entry.kind != .single) return error.InvalidDestination; const identity_index = entry.identity_index orelse return error.InvalidDestination; const private = node_owner.identityAt(identity_index) orelse return error.InvalidDestination; const fanout = fanoutFor(value); const carriers = node.outbound.frameCount(node_owner, fanout); if (carriers == 0) return error.NoOutgoingCarrier; try node.outbound.reserve(node_owner, carriers, carriers); const rotating_public = try rotatingKey(node_owner, identity_index, value); const frame = try buildFrame(node_owner, value, private, entry.name_hash, rotating_public); if (rotating_public) |public_key| node_owner.known_ratchets.remember(.{ .destination_hash = value.destination, .public_key = public_key, .received = value.now, }); const packet_hash = wire.hash.full(frame) catch unreachable; _ = node_owner.duplicate_hashes.insert(packet_hash); try node.outbound.transmit(node_owner, fanout, frame);}Source: lib/reticulum/src/node/root.zig:74
zig
pub const announce = @import("announce.zig");Complete call list for node.announce.run
7 direct calls.
lib.reticulum.src.node.announce.buildFrame[function] — private source atlib/reticulum/src/node/announce.zig:44in nearest public ownertiny.reticulum.node.announcelib.reticulum.src.node.announce.fanoutFor[function] — private source atlib/reticulum/src/node/announce.zig:77in nearest public ownertiny.reticulum.node.announcelib.reticulum.src.node.announce.rotatingKey[function] — private source atlib/reticulum/src/node/announce.zig:27in nearest public ownertiny.reticulum.node.announcetiny.reticulum.node.outbound.frameCount[function] atlib/reticulum/src/node/outbound.zig:175tiny.reticulum.node.outbound.reserve[function] atlib/reticulum/src/node/outbound.zig:184tiny.reticulum.node.outbound.transmit[function] atlib/reticulum/src/node/outbound.zig:213tiny.reticulum.wire.hash.full[function] atlib/reticulum/src/wire/hash.zig:40
Audit
| Definitions | 2 |
|---|---|
| Public names | 2 |
| Members | 0 |
| Version | 26.7.0 |
| Revision | daab053ee433 |