pub struct Mac<P: Platform, const IDENTITIES: usize = DEFAULT_IDENTITIES, const PEERS: usize = DEFAULT_PEERS, const CHANNELS: usize = DEFAULT_CHANNELS, const ACKS: usize = DEFAULT_ACKS, const TX: usize = DEFAULT_TX, const FRAME: usize = MAX_RESEND_FRAME_LEN, const DUP: usize = DEFAULT_DUP, const RN: usize = DEFAULT_CHANNEL_REPLAY, const HN: usize = DEFAULT_CHANNEL_HINT_REPLAY> { /* private fields */ }Implementations§
Source§impl<P: Platform, const IDENTITIES: usize, const PEERS: usize, const CHANNELS: usize, const ACKS: usize, const TX: usize, const FRAME: usize, const DUP: usize, const RN: usize, const HN: usize> Mac<P, IDENTITIES, PEERS, CHANNELS, ACKS, TX, FRAME, DUP, RN, HN>
impl<P: Platform, const IDENTITIES: usize, const PEERS: usize, const CHANNELS: usize, const ACKS: usize, const TX: usize, const FRAME: usize, const DUP: usize, const RN: usize, const HN: usize> Mac<P, IDENTITIES, PEERS, CHANNELS, ACKS, TX, FRAME, DUP, RN, HN>
Sourcepub fn new(
radio: P::Radio,
crypto: CryptoEngine<P::Aes, P::Sha>,
clock: P::Clock,
rng: P::Rng,
counter_store: P::CounterStore,
repeater: RepeaterConfig,
operating_policy: OperatingPolicy,
) -> Self
pub fn new( radio: P::Radio, crypto: CryptoEngine<P::Aes, P::Sha>, clock: P::Clock, rng: P::Rng, counter_store: P::CounterStore, repeater: RepeaterConfig, operating_policy: OperatingPolicy, ) -> Self
Creates a MAC coordinator with the supplied radio, crypto, timing, and policy state.
Sourcepub const fn counters(&self) -> MacCounters
pub const fn counters(&self) -> MacCounters
Cumulative frame tallies since construction.
Sourcepub fn counter_store(&self) -> &P::CounterStore
pub fn counter_store(&self) -> &P::CounterStore
Borrow the counter store.
Sourcepub fn tx_queue_mut(&mut self) -> &mut TxQueue<TX, FRAME>
pub fn tx_queue_mut(&mut self) -> &mut TxQueue<TX, FRAME>
Mutably borrow the transmit queue.
Sourcepub fn dup_cache(&self) -> &DuplicateCache<DUP>
pub fn dup_cache(&self) -> &DuplicateCache<DUP>
Borrow the duplicate cache.
Sourcepub fn peer_registry(&self) -> &PeerRegistry<PEERS>
pub fn peer_registry(&self) -> &PeerRegistry<PEERS>
Borrow the peer registry.
Sourcepub fn peer_registry_mut(&mut self) -> &mut PeerRegistry<PEERS>
pub fn peer_registry_mut(&mut self) -> &mut PeerRegistry<PEERS>
Mutably borrow the peer registry.
Sourcepub fn channels(&self) -> &ChannelTable<CHANNELS, RN, HN>
pub fn channels(&self) -> &ChannelTable<CHANNELS, RN, HN>
Borrow the channel table.
Sourcepub fn channels_mut(&mut self) -> &mut ChannelTable<CHANNELS, RN, HN>
pub fn channels_mut(&mut self) -> &mut ChannelTable<CHANNELS, RN, HN>
Mutably borrow the channel table.
Sourcepub fn repeater_config(&self) -> &RepeaterConfig
pub fn repeater_config(&self) -> &RepeaterConfig
Borrow repeater configuration.
Sourcepub fn repeater_config_mut(&mut self) -> &mut RepeaterConfig
pub fn repeater_config_mut(&mut self) -> &mut RepeaterConfig
Mutably borrow repeater configuration.
Sourcepub fn operating_policy(&self) -> &OperatingPolicy
pub fn operating_policy(&self) -> &OperatingPolicy
Borrow the local operating policy.
Sourcepub fn operating_policy_mut(&mut self) -> &mut OperatingPolicy
pub fn operating_policy_mut(&mut self) -> &mut OperatingPolicy
Mutably borrow the local operating policy.
Sourcepub fn auto_register_full_key_peers(&self) -> bool
pub fn auto_register_full_key_peers(&self) -> bool
Return whether inbound secure packets carrying a full source key may auto-register peers.
Sourcepub fn set_auto_register_full_key_peers(&mut self, enabled: bool)
pub fn set_auto_register_full_key_peers(&mut self, enabled: bool)
Enable or disable inbound full-key peer auto-registration.
Sourcepub fn add_identity(
&mut self,
identity: P::Identity,
) -> Result<LocalIdentityId, CapacityError>
pub fn add_identity( &mut self, identity: P::Identity, ) -> Result<LocalIdentityId, CapacityError>
Register one long-term local identity.
Sourcepub async fn load_persisted_counter(
&mut self,
id: LocalIdentityId,
) -> Result<u32, CounterPersistenceError<<P::CounterStore as CounterStore>::Error>>
pub async fn load_persisted_counter( &mut self, id: LocalIdentityId, ) -> Result<u32, CounterPersistenceError<<P::CounterStore as CounterStore>::Error>>
Load the persisted frame-counter boundary for id from the counter store.
§Flash-wear invariant
This method must remain read-only. In particular, booting or repeatedly rebooting without transmitting must never write a new reservation and wear out embedded flash. The first authenticated send schedules the future reservation; the MAC driver services that pending write because actual transmission has made persistence necessary.
Sourcepub async fn service_counter_persistence(
&mut self,
) -> Result<usize, <P::CounterStore as CounterStore>::Error>
pub async fn service_counter_persistence( &mut self, ) -> Result<usize, <P::CounterStore as CounterStore>::Error>
Persist all currently scheduled frame-counter reservations.
Sourcepub async fn load_all_persisted_rx_counters(
&mut self,
) -> Result<usize, <P::CounterStore as CounterStore>::Error>
pub async fn load_all_persisted_rx_counters( &mut self, ) -> Result<usize, <P::CounterStore as CounterStore>::Error>
Load persisted RX frame-counter boundaries for all registered peers and
store them in [PeerInfo::initial_rx_counter].
Call this once at boot after registering all known peers. When pairwise keys are first derived for each peer, the replay window is initialised to the loaded boundary so frames replayed from before the reboot are rejected.
Sourcepub fn register_ephemeral(
&mut self,
parent: LocalIdentityId,
identity: SoftwareIdentity,
) -> Result<LocalIdentityId, CapacityError>
pub fn register_ephemeral( &mut self, parent: LocalIdentityId, identity: SoftwareIdentity, ) -> Result<LocalIdentityId, CapacityError>
Register an ephemeral software identity linked to parent.
Sourcepub fn remove_ephemeral(&mut self, id: LocalIdentityId) -> bool
pub fn remove_ephemeral(&mut self, id: LocalIdentityId) -> bool
Remove an ephemeral identity slot if one exists at id.
Sourcepub fn identity(
&self,
id: LocalIdentityId,
) -> Option<&IdentitySlot<P::Identity, PEERS, ACKS, FRAME>>
pub fn identity( &self, id: LocalIdentityId, ) -> Option<&IdentitySlot<P::Identity, PEERS, ACKS, FRAME>>
Borrow an identity slot by identifier.
Sourcepub fn identity_mut(
&mut self,
id: LocalIdentityId,
) -> Option<&mut IdentitySlot<P::Identity, PEERS, ACKS, FRAME>>
pub fn identity_mut( &mut self, id: LocalIdentityId, ) -> Option<&mut IdentitySlot<P::Identity, PEERS, ACKS, FRAME>>
Mutably borrow an identity slot by identifier.
Sourcepub fn add_peer(&mut self, key: PublicKey) -> Result<PeerId, AddPeerError>
pub fn add_peer(&mut self, key: PublicKey) -> Result<PeerId, AddPeerError>
Registers or refreshes a known remote peer in the shared registry.
When the software-crypto feature is enabled, the supplied public key
is validated as a well-formed Ed25519 compressed point on the curve.
Malformed keys are rejected with AddPeerError::InvalidPublicKey
before they can pollute the peer registry.
Sourcepub fn remove_peer(&mut self, key: &PublicKey) -> bool
pub fn remove_peer(&mut self, key: &PublicKey) -> bool
Removes a registered peer and every piece of per-peer transport state: pairwise crypto, replay windows, pending counter resyncs, and any deferred inbound frame. Returns whether the peer was registered.
The peer registry is dense, so removal moves the last entry into the freed slot; state keyed by the moved peer’s old identifier is re-keyed here. Persisted RX counter boundaries are deliberately retained: if the peer is re-added later, replay protection resumes from the stored boundary instead of accepting replays from before the removal.
Sourcepub fn add_channel(&mut self, key: ChannelKey) -> Result<(), CapacityError>
pub fn add_channel(&mut self, key: ChannelKey) -> Result<(), CapacityError>
Adds or updates a shared channel and derives its multicast keys.
Sourcepub fn remove_channel(&mut self, key: &ChannelKey) -> bool
pub fn remove_channel(&mut self, key: &ChannelKey) -> bool
Removes a previously added channel by its exact key, discarding the channel’s replay state with it (re-adding the key later starts at first contact). Returns whether a channel was removed.
Sourcepub fn add_named_channel(&mut self, name: &str) -> Result<(), AddChannelError>
pub fn add_named_channel(&mut self, name: &str) -> Result<(), AddChannelError>
Adds or updates a named channel using the coordinator’s channel-key derivation.
The name is canonicalized (ASCII lowercase fold) before derivation, so
Public and public register the same channel.
Sourcepub fn identity_count(&self) -> usize
pub fn identity_count(&self) -> usize
Return the number of occupied identity slots.
Sourcepub fn install_pairwise_keys_advanced(
&mut self,
identity_id: LocalIdentityId,
peer_id: PeerId,
pairwise_keys: PairwiseKeys,
) -> Result<Option<PeerCryptoState>, SendError>
pub fn install_pairwise_keys_advanced( &mut self, identity_id: LocalIdentityId, peer_id: PeerId, pairwise_keys: PairwiseKeys, ) -> Result<Option<PeerCryptoState>, SendError>
Installs pairwise transport keys for one local identity and remote peer.
§Safety (logical)
Installing wrong keys will silently corrupt the session. This method
is deliberately gated behind the unsafe-advanced feature. Prefer
going through the node-layer PFS session manager instead.
Sourcepub fn queue_broadcast(
&mut self,
from: LocalIdentityId,
payload: &[u8],
options: &SendOptions,
) -> Result<SendReceipt, SendError>
pub fn queue_broadcast( &mut self, from: LocalIdentityId, payload: &[u8], options: &SendOptions, ) -> Result<SendReceipt, SendError>
Enqueues a broadcast frame for transmission.
Note: the encrypted, ack_requested, and salt flags on options
are silently forced to false because broadcasts cannot carry any of
them on the wire.
TODO: this sanitization is silent — a caller that explicitly set
ack_requested = true for a broadcast will get a successful
SendReceipt and never learn the flag was dropped. Surface this as a
debug-level event (or split SendOptions into kind-specific
builders) so the loss is observable.
Sourcepub async fn send_broadcast(
&mut self,
from: LocalIdentityId,
payload: &[u8],
options: &SendOptions,
) -> Result<SendReceipt, SendError>
pub async fn send_broadcast( &mut self, from: LocalIdentityId, payload: &[u8], options: &SendOptions, ) -> Result<SendReceipt, SendError>
Enqueue a broadcast frame for transmission.
Sourcepub fn queue_multicast(
&mut self,
from: LocalIdentityId,
channel_id: &ChannelId,
payload: &[u8],
options: &SendOptions,
) -> Result<SendReceipt, SendError>
pub fn queue_multicast( &mut self, from: LocalIdentityId, channel_id: &ChannelId, payload: &[u8], options: &SendOptions, ) -> Result<SendReceipt, SendError>
Enqueues a multicast frame using the configured channel keys.
Sourcepub async fn send_multicast(
&mut self,
from: LocalIdentityId,
channel_id: &ChannelId,
payload: &[u8],
options: &SendOptions,
) -> Result<SendReceipt, SendError>
pub async fn send_multicast( &mut self, from: LocalIdentityId, channel_id: &ChannelId, payload: &[u8], options: &SendOptions, ) -> Result<SendReceipt, SendError>
Enqueue a multicast frame for transmission.
Sourcepub fn queue_mac_ack_for_peer(
&mut self,
peer_id: PeerId,
ack_trailer: [u8; 8],
) -> Result<(), SendError>
pub fn queue_mac_ack_for_peer( &mut self, peer_id: PeerId, ack_trailer: [u8; 8], ) -> Result<(), SendError>
Enqueues a MAC ACK frame, using any cached route to peer_id when available.
Sourcepub fn queue_mac_ack(&mut self, ack_trailer: [u8; 8]) -> Result<(), SendError>
pub fn queue_mac_ack(&mut self, ack_trailer: [u8; 8]) -> Result<(), SendError>
Enqueues an immediate direct MAC ACK frame.
Sourcepub fn queue_unicast(
&mut self,
from: LocalIdentityId,
peer: &PublicKey,
payload: &[u8],
options: &SendOptions,
) -> Result<Option<SendReceipt>, SendError>
pub fn queue_unicast( &mut self, from: LocalIdentityId, peer: &PublicKey, payload: &[u8], options: &SendOptions, ) -> Result<Option<SendReceipt>, SendError>
Enqueues a unicast frame and optional pending-ACK state.
Sourcepub async fn send_unicast(
&mut self,
from: LocalIdentityId,
peer: &PublicKey,
payload: &[u8],
options: &SendOptions,
) -> Result<Option<SendReceipt>, SendError>
pub async fn send_unicast( &mut self, from: LocalIdentityId, peer: &PublicKey, payload: &[u8], options: &SendOptions, ) -> Result<Option<SendReceipt>, SendError>
Enqueue a unicast frame for transmission, deriving secure peer state on first use.
Sourcepub fn queue_blind_unicast(
&mut self,
from: LocalIdentityId,
peer: &PublicKey,
channel_id: &ChannelId,
payload: &[u8],
options: &SendOptions,
) -> Result<Option<SendReceipt>, SendError>
pub fn queue_blind_unicast( &mut self, from: LocalIdentityId, peer: &PublicKey, channel_id: &ChannelId, payload: &[u8], options: &SendOptions, ) -> Result<Option<SendReceipt>, SendError>
Enqueues a blind-unicast frame and optional pending-ACK state.
Sourcepub async fn send_blind_unicast(
&mut self,
from: LocalIdentityId,
peer: &PublicKey,
channel_id: &ChannelId,
payload: &[u8],
options: &SendOptions,
) -> Result<Option<SendReceipt>, SendError>
pub async fn send_blind_unicast( &mut self, from: LocalIdentityId, peer: &PublicKey, channel_id: &ChannelId, payload: &[u8], options: &SendOptions, ) -> Result<Option<SendReceipt>, SendError>
Enqueue a blind-unicast frame for transmission, deriving secure peer state on first use.
Sourcepub async fn transmit_next(
&mut self,
on_event: &mut impl FnMut(LocalIdentityId, MacEventRef<'_>),
) -> Result<Option<SendReceipt>, MacError<<P::Radio as Radio>::Error>>
pub async fn transmit_next( &mut self, on_event: &mut impl FnMut(LocalIdentityId, MacEventRef<'_>), ) -> Result<Option<SendReceipt>, MacError<<P::Radio as Radio>::Error>>
Transmit the next eligible queued frame, if any.
While a post-transmit forwarding listen window is active, only immediate MAC ACK traffic is permitted to bypass the listen state. Forwarded sends arm a new listen window after the radio transmit completes. Non-immediate traffic honors queued CAD backoff state and gives up after the configured maximum number of CAD attempts.
Sourcepub async fn drain_tx_queue(
&mut self,
on_event: &mut impl FnMut(LocalIdentityId, MacEventRef<'_>),
) -> Result<(), MacError<<P::Radio as Radio>::Error>>
pub async fn drain_tx_queue( &mut self, on_event: &mut impl FnMut(LocalIdentityId, MacEventRef<'_>), ) -> Result<(), MacError<<P::Radio as Radio>::Error>>
Keep transmitting until the queue is empty.
Progress stops when CAD keeps reporting busy, when a post-transmit listen window blocks normal traffic, or when the queue is otherwise unable to shrink further in the current cycle.
Sourcepub async fn poll_cycle(
&mut self,
on_event: impl FnMut(LocalIdentityId, MacEventRef<'_>),
) -> Result<(), MacError<<P::Radio as Radio>::Error>>
pub async fn poll_cycle( &mut self, on_event: impl FnMut(LocalIdentityId, MacEventRef<'_>), ) -> Result<(), MacError<<P::Radio as Radio>::Error>>
Runs one coordinator cycle over the current MAC state.
The cycle performs four ordered phases:
- Drain any queued transmit work.
- Receive and process at most one inbound frame.
- Drain any immediate ACK generated during receive handling.
- Service pending ACK timers and emit timeout events.
The callback may be invoked zero or more times depending on what the receive and timeout phases accept or resolve. Service one MAC coordinator cycle.
Sourcepub fn earliest_deadline_ms(&self) -> Option<u64>
pub fn earliest_deadline_ms(&self) -> Option<u64>
Compute the earliest deadline across all coordinator timers.
Returns None when there are no pending timers. The returned value
covers pending ACK deadlines (both ack_deadline_ms and forwarding
confirm_deadline_ms), the post-transmit listen window, and deferred
transmit-queue entries.
Sourcepub async fn next_event(
&mut self,
on_event: impl FnMut(LocalIdentityId, MacEventRef<'_>),
) -> Result<(), MacError<<P::Radio as Radio>::Error>>
pub async fn next_event( &mut self, on_event: impl FnMut(LocalIdentityId, MacEventRef<'_>), ) -> Result<(), MacError<<P::Radio as Radio>::Error>>
Run the coordinator’s event loop until at least one event is delivered or a timer-driven action (retransmit, timeout) is processed.
Unlike poll_cycle, this method properly awaits the
radio and timer deadlines instead of returning immediately when nothing
is ready. Callers can use tokio::select! (or equivalent) to multiplex
user input alongside MAC events:
loop {
tokio::select! {
line = stdin.next_line() => { /* handle input */ }
result = mac.next_event(|id, event| { /* handle event */ }) => {
result?;
}
}
}Sourcepub fn poll_wait_for_wake(
&mut self,
cx: &mut Context<'_>,
buf: &mut [u8; FRAME],
) -> Poll<Result<WakeReason, <P::Radio as Radio>::Error>>
pub fn poll_wait_for_wake( &mut self, cx: &mut Context<'_>, buf: &mut [u8; FRAME], ) -> Poll<Result<WakeReason, <P::Radio as Radio>::Error>>
Register radio/timer wakers and report what has become ready.
This is Phase 2 of next_event exposed as a sync
poll method so that callers sharing the coordinator through an
AsyncRefCell can release the exclusive borrow between polls. The
caller-provided buf is populated with the received frame when the
return value is WakeReason::Received.
Sourcepub async fn process_wake_reason(
&mut self,
reason: WakeReason,
buf: &mut [u8; FRAME],
on_event: impl FnMut(LocalIdentityId, MacEventRef<'_>),
) -> Result<(), MacError<<P::Radio as Radio>::Error>>
pub async fn process_wake_reason( &mut self, reason: WakeReason, buf: &mut [u8; FRAME], on_event: impl FnMut(LocalIdentityId, MacEventRef<'_>), ) -> Result<(), MacError<<P::Radio as Radio>::Error>>
Run Phases 3-5 after poll_wait_for_wake
has reported a wake reason: process the received frame (if any),
drain immediate ACKs, and service pending ACK timeouts.
§Precondition
When reason is WakeReason::Received, buf must be the same
buffer — containing the same bytes — that was passed to the matching
poll_wait_for_wake call. The received
frame bytes live in buf[..rx.len]; reason only carries the
accompanying metadata (RxInfo). Passing a different or reinitialized
buffer will cause the received frame to be silently discarded and the
contents of buf to be misinterpreted as a MAC frame.
This precondition only matters when using the split-phase API for
AsyncRefCell sharing. When driving the
coordinator through next_event the buffer is a
local stack variable and the pairing is guaranteed automatically.
Sourcepub async fn run(
&mut self,
on_event: impl FnMut(LocalIdentityId, MacEventRef<'_>),
) -> Result<(), MacError<<P::Radio as Radio>::Error>>
pub async fn run( &mut self, on_event: impl FnMut(LocalIdentityId, MacEventRef<'_>), ) -> Result<(), MacError<<P::Radio as Radio>::Error>>
Drive the coordinator forever, invoking on_event for each delivered event.
This is the preferred long-lived run loop for standalone MAC-driven tasks such as
repeaters or dedicated radio services. Unlike manually calling
poll_cycle in a loop, run keeps the wake/sleep policy inside
the coordinator by delegating to next_event, which already
waits for radio activity and protocol deadlines.
Sourcepub async fn run_quiet(
&mut self,
) -> Result<(), MacError<<P::Radio as Radio>::Error>>
pub async fn run_quiet( &mut self, ) -> Result<(), MacError<<P::Radio as Radio>::Error>>
Drive the coordinator forever while ignoring emitted events.
Useful for standalone repeaters or bridge tasks that do not need to observe inbound deliveries directly but still need the coordinator to service forwarding, ACKs, and retransmissions without an app-owned polling loop.
Sourcepub async fn process_received_frame(
&mut self,
buf: &mut [u8; FRAME],
frame_len: usize,
rx: &RxInfo,
on_event: impl FnMut(LocalIdentityId, MacEventRef<'_>),
) -> bool
pub async fn process_received_frame( &mut self, buf: &mut [u8; FRAME], frame_len: usize, rx: &RxInfo, on_event: impl FnMut(LocalIdentityId, MacEventRef<'_>), ) -> bool
Process a received frame, dispatching events through on_event.
This is the shared implementation used by both receive_one
and next_event. Returns true when the frame
produced at least one event or side-effect.
Every reception passes through here exactly once, which is what
makes it the place to tally them: the rx_frames/rx_accepted
pair counts one radio reception and whether anything came of it.
Sourcepub async fn receive_one(
&mut self,
on_event: impl FnMut(LocalIdentityId, MacEventRef<'_>),
) -> Result<bool, MacError<<P::Radio as Radio>::Error>>
pub async fn receive_one( &mut self, on_event: impl FnMut(LocalIdentityId, MacEventRef<'_>), ) -> Result<bool, MacError<<P::Radio as Radio>::Error>>
Non-blocking receive: polls the radio once and processes a frame if available.
This is the legacy non-blocking API used by poll_cycle.
For new code, prefer next_event which properly awaits
the radio and timer deadlines.
Sourcepub fn complete_ack(
&mut self,
peer: &PublicKey,
ack_trailer: &[u8; 8],
) -> Option<(LocalIdentityId, SendReceipt)>
pub fn complete_ack( &mut self, peer: &PublicKey, ack_trailer: &[u8; 8], ) -> Option<(LocalIdentityId, SendReceipt)>
Mark a pending receipt as acknowledged and emit an event through on_event.
Sourcepub fn service_pending_ack_timeouts(
&mut self,
on_event: impl FnMut(LocalIdentityId, MacEventRef<'_>),
) -> Result<(), CapacityError>
pub fn service_pending_ack_timeouts( &mut self, on_event: impl FnMut(LocalIdentityId, MacEventRef<'_>), ) -> Result<(), CapacityError>
Expire or retry pending ACK state based on now_ms.
Sourcepub fn cancel_pending_ack(
&mut self,
identity_id: LocalIdentityId,
receipt: SendReceipt,
) -> bool
pub fn cancel_pending_ack( &mut self, identity_id: LocalIdentityId, receipt: SendReceipt, ) -> bool
Cancel a pending ACK-requested send, stopping retransmissions.
Removes the pending ACK entry for the given identity slot and receipt,
and removes any matching entry from the transmit queue. Returns true
if a pending ACK was found and removed.
Sourcepub fn ensure_transient_peer(
&mut self,
key: &PublicKey,
) -> Result<PeerId, AddPeerError>
pub fn ensure_transient_peer( &mut self, key: &PublicKey, ) -> Result<PeerId, AddPeerError>
Ensure key is registered at least transiently, returning its slot.
A known peer is returned as-is; an unknown one is auto-registered
exactly like a full-source sender would be — unpinned and
LRU-evictable, never promoted. This is the node layer’s hook for
answering a stranger (an Identity Request reply, say) whose frame
arrived on a path that does not auto-register its source, such as a
broadcast. Deliberately not gated on
auto_register_full_key_peers:
the caller is making an explicit per-peer decision, not opting into
registering every full-source sender.