rings_measure/event.rs
1use std::num::NonZeroU64;
2
3use serde::Deserialize;
4use serde::Serialize;
5
6/// Why an observation can be attributed to a stable peer identity.
7///
8/// This is an explicit proof token at the pure transition boundary. Inbound
9/// observations require a cryptographically verified peer. An outbound failure
10/// may instead be attributed to the stable DID selected by the local caller;
11/// it makes no remote identity claim. Pre-authentication ingress remains
12/// unattributable and cannot change the ledger.
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14pub enum Authentication {
15 /// The event belongs to a cryptographically authenticated stable peer identity.
16 Authenticated,
17 /// Local code explicitly addressed the event to this stable peer identity.
18 ///
19 /// This variant is valid only for locally originated observations, such as
20 /// [`MeasurementEvent::FailedToSend`] when no connection exists. It must
21 /// never authenticate bytes or claims received from the network. A ledger
22 /// may use it to update reliability for an already-retained peer, but it
23 /// must not create a peer record or refresh authenticated-observation time.
24 LocallyAddressed,
25 /// The transport has not authenticated the stable peer identity.
26 Unauthenticated,
27}
28
29impl Authentication {
30 /// Return whether this proof source permits attribution of `event`.
31 pub const fn permits(self, event: MeasurementEvent) -> bool {
32 match self {
33 Self::Authenticated => true,
34 Self::LocallyAddressed => matches!(event, MeasurementEvent::FailedToSend),
35 Self::Unauthenticated => false,
36 }
37 }
38
39 /// Return whether this proof can establish a new retained peer record.
40 pub const fn establishes_peer(self) -> bool {
41 matches!(self, Self::Authenticated)
42 }
43
44 /// Return whether this proof refreshes authenticated peer-observation time.
45 pub const fn refreshes_peer_observation(self) -> bool {
46 matches!(self, Self::Authenticated)
47 }
48}
49
50/// One logical local observation about a remote peer.
51///
52/// Successful transfer variants carry useful payload bytes. Framing, duplicate
53/// chunks, retransmissions, and unverified bytes are not useful payload.
54#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
55pub enum MeasurementEvent {
56 /// A connection attempt completed successfully.
57 Connected,
58 /// An established or attempted connection ended unsuccessfully.
59 Disconnected,
60 /// One logical message was delivered to the peer.
61 Sent {
62 /// Useful payload bytes delivered to the peer.
63 useful_bytes: u64,
64 },
65 /// One logical message could not be delivered to the peer.
66 ///
67 /// A failure after selecting a destination DID is locally attributable even
68 /// when no authenticated connection exists. A bounded ledger retains that
69 /// evidence only for a peer already established by authenticated observation.
70 /// Remote-originated failures still require authenticated ingress.
71 FailedToSend,
72 /// One logical message from the peer was fully received and verified.
73 Received {
74 /// Useful payload bytes received and verified from the peer.
75 useful_bytes: u64,
76 },
77 /// One logical inbound message failed reassembly, decoding, or verification.
78 FailedToReceive,
79}
80
81/// One or more homogeneous logical observations applied atomically.
82///
83/// For successful transfer events, `useful_bytes` is the aggregate useful
84/// payload across every occurrence. Reliability evidence advances by
85/// [`Self::occurrences`], while byte credit advances once by that aggregate.
86/// This representation lets effect adapters coalesce events in constant space
87/// without inventing zero-byte messages or exposing partial transitions.
88#[derive(Debug, Clone, Copy, PartialEq, Eq)]
89pub struct MeasurementBatch {
90 event: MeasurementEvent,
91 occurrences: NonZeroU64,
92}
93
94impl MeasurementBatch {
95 /// Construct an atomic homogeneous batch.
96 pub const fn new(event: MeasurementEvent, occurrences: NonZeroU64) -> Self {
97 Self { event, occurrences }
98 }
99
100 /// Construct a batch containing exactly one observation.
101 pub const fn single(event: MeasurementEvent) -> Self {
102 Self::new(event, NonZeroU64::MIN)
103 }
104
105 /// Aggregate event, including aggregate useful bytes for transfers.
106 pub const fn event(self) -> MeasurementEvent {
107 self.event
108 }
109
110 /// Number of logical observations represented by this batch.
111 pub const fn occurrences(self) -> NonZeroU64 {
112 self.occurrences
113 }
114}
115
116/// A named counter whose checked update can overflow.
117#[derive(Debug, Clone, Copy, PartialEq, Eq)]
118pub enum Metric {
119 /// Useful payload bytes sent to a peer.
120 BytesSent,
121 /// Useful payload bytes received from a peer.
122 BytesReceived,
123 /// Successful connection observations.
124 Connected,
125 /// Disconnection observations.
126 Disconnected,
127 /// Successful logical sends.
128 Sent,
129 /// Failed logical sends.
130 FailedToSend,
131 /// Successful verified logical receives.
132 Received,
133 /// Failed logical receives.
134 FailedToReceive,
135}
136
137/// Result of applying an observation at the authentication boundary.
138#[derive(Debug, Clone, Copy, PartialEq, Eq)]
139pub enum ApplyOutcome {
140 /// The attributable observation changed the peer record.
141 Applied,
142 /// The proof source cannot attribute this event to the supplied peer.
143 IgnoredUnattributable,
144 /// Local evidence addressed a peer with no retained authenticated record.
145 ///
146 /// Purely local failures cannot establish that the remote peer was ever
147 /// observed, so ignoring them keeps identity rotation from consuming ledger
148 /// capacity or evicting authenticated credit.
149 IgnoredUnknownPeer,
150}