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> { /* private fields */ }Expand description
Central MAC coordinator that owns and drives the full UMSH radio-facing state machine.
Mac is the top-level entry point for UMSH protocol operation. It combines a radio driver,
cryptographic engine, clock, RNG, counter store, and all protocol state into a single
fully-typed, allocation-free structure. All const-generic capacity parameters are enforced
at compile time via heapless collections — there are no heap allocations inside Mac.
§Generic parameters
P: Platform— a trait bundle supplying the concrete driver types forRadio,Aes/Sha(crypto),Clock,Rng, andCounterStore. ImplementPlatformonce per deployment target to swap in real hardware drivers, software stubs, or test doubles.IDENTITIES— maximum simultaneously active local identities (defaultDEFAULT_IDENTITIES).PEERS— maximum known remote peers and their per-identity pairwise key entries (defaultDEFAULT_PEERS).CHANNELS— maximum registered multicast channel keys (defaultDEFAULT_CHANNELS).ACKS— maximum simultaneously in-flight ACK-requested sends per identity (defaultDEFAULT_ACKS).TX— depth of the transmit queue (defaultDEFAULT_TX). Must be large enough to absorb a burst of control frames (MAC ACKs + forwarded frames) alongside any backlogged application sends.FRAME— maximum byte length of a stored frame buffer for retransmission (default [MAX_RESEND_FRAME_LEN]).DUP— capacity of the duplicate-detection cache (defaultDEFAULT_DUP).
§Lifecycle
- Construct with
Mac::new, supplying concrete driver instances and policy. - Register identities via
Mac::add_identity; callMac::load_persisted_counteron each long-term identity to restore the safe frame-counter start point from non-volatile storage. - Register peers via
Mac::add_peer. Secure unicast and blind-unicast state is derived lazily from the local private key and peer public key on first use. - Register channels via
Mac::add_channelorMac::add_named_channel. - Drive the event loop via
Mac::run/Mac::run_quietfor long-lived tasks, or by awaitingMac::next_eventwhen you need to multiplex MAC progress with other async work. The coordinator handles incoming frames, outgoing transmits, forwarding, ACK matching, retransmission scheduling, and timer deadlines — no external polling required. - Send traffic by calling
queue_broadcast,queue_unicast,queue_multicast, etc. from application code between (or concurrent with) event-loop iterations. - Persist counters by calling
Mac::service_counter_persistencewhenevernext_eventsignals that pending persistence work is ready to flush.
§Example (pseudo-code)
let mut mac = Mac::<MyPlatform>::new(
radio, crypto, clock, rng, counter_store,
RepeaterConfig::default(), OperatingPolicy::default(),
);
let id = mac.add_identity(my_identity)?;
mac.load_persisted_counter(id).await?;
mac.run(|id, event| {
let _ = (id, event);
// handle deliveries / ACKs here and schedule persistence work as needed
}).await?;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> Mac<P, IDENTITIES, PEERS, CHANNELS, ACKS, TX, FRAME, DUP>
impl<P: Platform, const IDENTITIES: usize, const PEERS: usize, const CHANNELS: usize, const ACKS: usize, const TX: usize, const FRAME: usize, const DUP: usize> Mac<P, IDENTITIES, PEERS, CHANNELS, ACKS, TX, FRAME, DUP>
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 fn crypto(&self) -> &CryptoEngine<P::Aes, P::Sha>
pub fn crypto(&self) -> &CryptoEngine<P::Aes, P::Sha>
Borrow the crypto engine.
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>
pub fn channels(&self) -> &ChannelTable<CHANNELS>
Borrow the channel table.
Sourcepub fn channels_mut(&mut self) -> &mut ChannelTable<CHANNELS>
pub fn channels_mut(&mut self) -> &mut ChannelTable<CHANNELS>
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.
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 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, CapacityError>
pub fn add_peer(&mut self, key: PublicKey) -> Result<PeerId, CapacityError>
Registers or refreshes a known remote peer in the shared registry.
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 add_named_channel(&mut self, name: &str) -> Result<(), CapacityError>
pub fn add_named_channel(&mut self, name: &str) -> Result<(), CapacityError>
Adds or updates a named channel using the coordinator’s channel-key derivation.
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.
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,
dst: NodeHint,
ack_tag: [u8; 8],
) -> Result<(), SendError>
pub fn queue_mac_ack_for_peer( &mut self, peer_id: PeerId, dst: NodeHint, ack_tag: [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,
dst: NodeHint,
ack_tag: [u8; 8],
) -> Result<(), SendError>
pub fn queue_mac_ack( &mut self, dst: NodeHint, ack_tag: [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 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 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_tag: &[u8; 8],
) -> Option<(LocalIdentityId, SendReceipt)>
pub fn complete_ack( &mut self, peer: &PublicKey, ack_tag: &[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.