lib/reticulum/src/node/event.zig

daab053ee43316e1809a84551d573ddd1e5bf3d2

  1 const carrier = @import("../carrier/root.zig");
  2 const wire = @import("../wire/root.zig");
  3 const timer = @import("timer.zig");
  4 const ProofStrategy = @import("../destination/root.zig").registry.ProofStrategy;
  5 
  6 pub const PersistToken = u64;
  7 
  8 pub const CarrierFrame = struct {
  9     interface: carrier.Index,
 10     now: timer.Seconds,
 11     bytes: []const u8,
 12     /// Thirty-two bytes of randomness that the caller renews for every frame it gives the node, so
 13     /// a link's keys on the receiving side come out of them. A step that needs a key or an
 14     /// initialization vector runs HKDF-SHA256 over those bytes, taking the link id as the salt and
 15     /// a label that names the use. A responder gets its X25519 private key from this expansion, and
 16     /// Reticulum@1.5.0 RNS/Link.py:276 draws one at random. An initiator's round trip packet and a
 17     /// responder's LINKCLOSE take their initialization vectors this way, and Reticulum@1.5.0
 18     /// RNS/Cryptography/Token.py:89 draws a random one.
 19     entropy: [32]u8,
 20 };
 21 
 22 pub const TimerExpired = struct {
 23     id: timer.TimerId,
 24     now: timer.Seconds,
 25     /// Thirty-two bytes of randomness that the caller renews for every timer it reports, so a timer
 26     /// that closes a link can encrypt. A step driven by a timer derives its keys and initialization
 27     /// vectors the same way, through HKDF-SHA256 with the link id as the salt and a label that
 28     /// names the use.
 29     entropy: [32]u8,
 30 };
 31 
 32 pub const ApplicationSend = struct {
 33     destination: [16]u8,
 34     now: timer.Seconds,
 35     plaintext: []const u8,
 36     context: wire.Context = .none,
 37     hops: u8 = 0,
 38     ephemeral_private: [32]u8,
 39     iv: [16]u8,
 40     create_receipt: bool = true,
 41 };
 42 
 43 pub const ApplicationAnnounce = struct {
 44     destination: [16]u8,
 45     app_data: []const u8,
 46     random: [5]u8,
 47     fresh_rotating_key: ?[32]u8,
 48     now: timer.Seconds,
 49     /// The carrier to answer a path request on, which a caller sets on an announce so
 50     /// Reticulum@1.5.0 RNS/Transport.py:3379-3382 answers on that carrier alone. Left null, the
 51     /// announce goes out on every outgoing carrier.
 52     path_response: ?carrier.Index = null,
 53 };
 54 
 55 pub const ApplicationProve = struct {
 56     destination: [16]u8,
 57     packet_hash: [32]u8,
 58     interface: carrier.Index,
 59     now: timer.Seconds,
 60 };
 61 
 62 /// Asks any node that knows a path to the named destination to answer with one, carrying the
 63 /// caller's own tag, for a caller that has yet to learn a route to that destination, as
 64 /// Reticulum@1.5.0 RNS/Transport.py:3207-3220 requests it. Naming a carrier sends the request on
 65 /// that carrier alone, and leaving it null sends it on every outgoing carrier.
 66 pub const ApplicationPathRequest = struct {
 67     destination: [16]u8,
 68     tag: [16]u8,
 69     interface: ?carrier.Index,
 70     now: timer.Seconds,
 71 };
 72 
 73 /// Asks the node to open a link toward a single destination it holds an identity for, learned from
 74 /// an announce, for an encrypted session that outlives a single packet. `encryption_private` and
 75 /// `signing_private` hold the X25519 and Ed25519 private keys the initiator uses for this session
 76 /// alone, and Reticulum@1.5.0 RNS/Link.py:284-285 draws both at random.
 77 pub const ApplicationLinkOpen = struct {
 78     destination: [16]u8,
 79     encryption_private: [32]u8,
 80     signing_private: [32]u8,
 81     now: timer.Seconds,
 82     /// What the node does with data delivered over this link, chosen here for an initiator link
 83     /// because it has no registered destination to read it from. Under `.none` the node proves
 84     /// nothing, under `.all` it proves every delivered packet at once, and under `.app` it reports
 85     /// each delivery with a proof request that a link proof event answers. By contrast, a responder
 86     /// reads this from its destination, per Reticulum@1.5.0 RNS/Link.py:958-968.
 87     proof_strategy: ProofStrategy = .none,
 88 };
 89 
 90 /// Asks the node to put application plaintext across a link that has activated, so the caller can
 91 /// send application bytes over the open link. Plaintext over 431 bytes returns
 92 /// `error.PacketTooLarge` before the node changes anything, per Reticulum@1.5.0 RNS/Link.py:73.
 93 /// With `create_receipt` set, the node tracks the delivery, fails a receipt that stays in the table
 94 /// max(rtt * 6, 0.005) seconds after the send when no proof has arrived, per Reticulum@1.5.0
 95 /// RNS/Packet.py:419-420 and Reticulum@1.5.0 RNS/Transport.py:1307-1318, and culls the oldest
 96 /// receipt and reports it as a `receipt_update` effect with status `culled` and no round trip time
 97 /// when a later send that asks for a receipt finds the receipt table full.
 98 pub const ApplicationLinkSend = struct {
 99     link_id: [16]u8,
100     plaintext: []const u8,
101     iv: [16]u8,
102     now: timer.Seconds,
103     create_receipt: bool = true,
104 };
105 
106 /// Asks the node to take down one link it holds open, so a caller can close a link once done with
107 /// it. An encrypted LINKCLOSE goes out, the link leaves the pool, and the caller hears that it
108 /// closed. A link that has yet to activate drops at once with no packet sent. The reason is
109 /// `initiator_closed` on an initiator and `destination_closed` on a responder, per Reticulum@1.5.0
110 /// RNS/Link.py:657-672.
111 pub const ApplicationLinkClose = struct {
112     link_id: [16]u8,
113     iv: [16]u8,
114     now: timer.Seconds,
115 };
116 
117 /// Asks the node to answer one packet of link data it has handed up with a proof, so a caller under
118 /// the `.app` strategy can answer a delivery that asked to be proved. The application names the
119 /// packet hash from a delivery whose `proof_requested` was set, and the node sends that hash and a
120 /// signature over it as link plaintext. A packet the node has already forgotten returns
121 /// `error.ProofUnavailable`, per Reticulum@1.5.0 RNS/Link.py:378-389.
122 pub const ApplicationLinkProve = struct {
123     link_id: [16]u8,
124     packet_hash: [32]u8,
125     now: timer.Seconds,
126 };
127 
128 pub const StorageComplete = struct {
129     token: PersistToken,
130     now: timer.Seconds,
131 };
132 
133 pub const Event = union(enum) {
134     carrier_frame: CarrierFrame,
135     timer_expired: TimerExpired,
136     application_send: ApplicationSend,
137     application_announce: ApplicationAnnounce,
138     application_prove: ApplicationProve,
139     application_path_request: ApplicationPathRequest,
140     application_link_open: ApplicationLinkOpen,
141     application_link_send: ApplicationLinkSend,
142     application_link_close: ApplicationLinkClose,
143     application_link_prove: ApplicationLinkProve,
144     storage_complete: StorageComplete,
145 };