use std::collections::HashMap;
use std::net::SocketAddr;
use std::sync::atomic::{AtomicBool, AtomicU64, AtomicUsize, Ordering};
use arc_swap::ArcSwapOption;
use std::sync::Arc;
use std::time::{Duration, Instant};
use async_trait::async_trait;
use bytes::Bytes;
use crossbeam_queue::SegQueue;
use dashmap::DashMap;
use tokio::sync::Notify;
use tokio::task::JoinHandle;
use super::crypto::{handshake_prologue, CryptoError, NoiseHandshake, SessionKeys, StaticKeypair};
use super::failure::{FailureDetector, FailureDetectorConfig, NodeStatus};
use super::identity::{
EntityId, EntityKeypair, PermissionToken, RevocationRegistry, TokenCache, TokenChain,
TokenScope,
};
use super::pool::PacketBuilder;
use super::behavior::broadcast::{
RouteWithdrawal, WithdrawalSeqGate, SUBPROTOCOL_CAPABILITY_ANN, SUBPROTOCOL_ROUTE_WITHDRAW,
};
use super::behavior::capability::{
CapabilityAnnouncement, CapabilityFilter, CapabilitySet, ScopeFilter, MAX_CAPABILITY_HOPS,
};
use super::behavior::loadbalance::HealthStatus;
use super::behavior::proximity::{
EnhancedPingwave, PingwaveAdmission, ProximityConfig, ProximityGraph,
};
use super::behavior::sensing;
use super::behavior::tag::Tag;
use super::channel::membership::{self, MembershipMsg, SUBPROTOCOL_CHANNEL_MEMBERSHIP};
use super::channel::{
AckReason, AuthGuard, AuthVerdict, ChannelConfigRegistry, ChannelHash, ChannelId, ChannelName,
ChannelPublisher, OnFailure, PublishConfig, PublishReport, SubscriberRoster,
};
use super::compute::SUBPROTOCOL_MIGRATION;
use super::protocol::{self, EventFrame, PacketFlags, HEADER_SIZE, MAGIC, TAG_SIZE};
const PACKET_WIRE_OVERHEAD: usize = HEADER_SIZE + TAG_SIZE;
const STREAM_GRANT_DRAIN_INTERVAL: Duration = Duration::from_millis(1);
const RETRANSMIT_TICK: Duration = Duration::from_millis(25);
const GRANT_EVENTS_PER_PACKET: usize =
protocol::MAX_PAYLOAD_SIZE / (EventFrame::LEN_SIZE + STREAM_WINDOW_SIZE);
pub const ACK_RANGES_CAPABILITY_TAG: &str = "net.reliable.stream_ack_ranges@1";
const ACK_RANGES_CAP_CACHE_TTL: Duration = Duration::from_secs(5);
const ACK_RANGES_CAP_CACHE_MAX_AGE: Duration = Duration::from_secs(40);
#[derive(Debug, Default)]
pub struct ControlPlaneStats {
pub grant_packets_sent: AtomicU64,
pub grant_events_sent: AtomicU64,
pub nack_packets_sent: AtomicU64,
pub nack_events_sent: AtomicU64,
pub reset_packets_sent: AtomicU64,
pub reset_events_sent: AtomicU64,
pub retransmit_packets_sent: AtomicU64,
pub ack_range_packets_sent: AtomicU64,
pub ack_range_events_sent: AtomicU64,
}
impl ControlPlaneStats {
#[inline]
fn record_packet(packets: &AtomicU64, events_ctr: &AtomicU64, events: usize) {
packets.fetch_add(1, Ordering::Relaxed);
events_ctr.fetch_add(events as u64, Ordering::Relaxed);
}
}
const COMMITTED_FLUSH_STALL_BUDGET: Duration = Duration::from_secs(30);
async fn await_credit_or_stall(
delay: &mut Duration,
cap: Duration,
deadline: tokio::time::Instant,
) -> Result<(), StreamError> {
if tokio::time::Instant::now() >= deadline {
return Err(StreamError::Transport(
"stream credit stalled: receiver granted no credit within the send budget".to_string(),
));
}
tokio::time::sleep(*delay).await;
*delay = (*delay * 2).min(cap);
Ok(())
}
#[derive(Clone)]
struct PendingStreamGrant {
session: Arc<NetSession>,
peer_addr: SocketAddr,
total_consumed: u64,
}
#[allow(clippy::type_complexity)]
fn group_grants_by_session(
drained: HashMap<(u64, u64), PendingStreamGrant>,
) -> HashMap<u64, (Arc<NetSession>, SocketAddr, Vec<(u64, u64)>)> {
let mut by_session: HashMap<u64, (Arc<NetSession>, SocketAddr, Vec<(u64, u64)>)> =
HashMap::new();
for ((session_id, stream_id), grant) in drained {
let PendingStreamGrant {
session,
peer_addr,
total_consumed,
} = grant;
by_session
.entry(session_id)
.or_insert_with(|| (session.clone(), peer_addr, Vec::new()))
.2
.push((stream_id, total_consumed));
}
by_session
}
fn peer_supports_ack_ranges(
cache: &DashMap<u64, (bool, Instant)>,
session_id_to_node: &DashMap<u64, u64>,
capability_fold: &super::behavior::fold::Fold<super::behavior::fold::CapabilityFold>,
session_id: u64,
) -> bool {
let Some(node_id) = session_id_to_node.get(&session_id).map(|e| *e.value()) else {
return false;
};
if let Some(hit) = cache.get(&node_id) {
if hit.value().1.elapsed() < ACK_RANGES_CAP_CACHE_TTL {
return hit.value().0;
}
}
let supports = super::behavior::fold::capability::capability_tags_for(capability_fold, node_id)
.iter()
.any(|t| t == ACK_RANGES_CAPABILITY_TAG);
cache.insert(node_id, (supports, Instant::now()));
supports
}
fn sweep_ack_ranges_cache(cache: &DashMap<u64, (bool, Instant)>, max_age: Duration) {
cache.retain(|_, (_, cached_at)| cached_at.elapsed() < max_age);
}
#[allow(clippy::type_complexity)]
fn build_session_control_events(
session: &Arc<NetSession>,
grants: &[(u64, u64)],
emit_ack_ranges: bool,
) -> (Vec<(u64, Bytes)>, Vec<Bytes>, Vec<Bytes>) {
let mut grant_entries: Vec<(u64, Bytes)> = Vec::with_capacity(grants.len());
let mut nack_events: Vec<Bytes> = Vec::new();
let mut ack_events: Vec<Bytes> = Vec::new();
for &(stream_id, total_consumed) in grants {
let (ack_seq, nack, ranges) = session
.try_stream(stream_id)
.map(|s| {
s.with_reliability(|r| {
let ranges = if emit_ack_ranges {
r.build_ack_ranges(MAX_ACK_RANGES)
} else {
Vec::new()
};
(r.rx_ack_seq(), r.build_nack(), ranges)
})
})
.unwrap_or((0, None, Vec::new()));
let payload = StreamWindow {
stream_id,
total_consumed,
ack_seq,
}
.encode();
grant_entries.push((stream_id, Bytes::copy_from_slice(&payload)));
if let Some(nack) = nack {
let payload = StreamNack {
stream_id,
next_expected: nack.next_expected,
missing_bitmap: nack.missing_bitmap,
}
.encode();
nack_events.push(Bytes::copy_from_slice(&payload));
}
if !ranges.is_empty() {
let msg = StreamAckRanges {
stream_id,
ack_seq,
ranges,
};
ack_events.push(Bytes::from(msg.encode()));
}
}
(grant_entries, nack_events, ack_events)
}
fn pack_control_events(events: &[Bytes]) -> Vec<std::ops::Range<usize>> {
let mut chunks: Vec<std::ops::Range<usize>> = Vec::new();
let mut start = 0usize;
let mut acc = 0usize;
for (i, e) in events.iter().enumerate() {
let framed = EventFrame::LEN_SIZE + e.len();
if i > start && acc + framed > protocol::MAX_PAYLOAD_SIZE {
chunks.push(start..i);
start = i;
acc = 0;
}
acc += framed;
}
if start < events.len() {
chunks.push(start..events.len());
}
chunks
}
#[allow(clippy::too_many_arguments)]
async fn emit_control_chunks(
socket: &NetSocket,
builder: &mut super::pool::ThreadLocalPooledBuilder<'_>,
session: &NetSession,
addr: SocketAddr,
events: &[Bytes],
subprotocol_id: u16,
packets_ctr: &AtomicU64,
events_ctr: &AtomicU64,
) {
for range in pack_control_events(events) {
let chunk = &events[range];
let seq = session.next_control_tx_seq();
let packet = builder.build_subprotocol(
CONTROL_STREAM_ID,
seq,
chunk,
PacketFlags::NONE,
subprotocol_id,
);
if socket.send_to(&packet, addr).await.is_ok() {
ControlPlaneStats::record_packet(packets_ctr, events_ctr, chunk.len());
}
}
}
#[inline]
fn wire_bytes_for_payload(payload_bytes: usize) -> u32 {
payload_bytes
.saturating_add(PACKET_WIRE_OVERHEAD)
.min(u32::MAX as usize) as u32
}
use super::reroute::ReroutePolicy;
use super::route::{RoutingHeader, ROUTING_HEADER_SIZE, ROUTING_MAGIC};
use super::router::{NetRouter, RouterConfig};
use super::session::{NetSession, TxAdmit, CONTROL_STREAM_ID};
use super::stream::{Stream, StreamConfig, StreamError, StreamStats};
use super::subnet::{DropReason, SubnetGateway, SubnetId, SubnetPolicy};
use super::subprotocol::stream_window::{
StreamAckRanges, StreamNack, StreamReset, StreamWindow, MAX_ACK_RANGES, STREAM_WINDOW_SIZE,
SUBPROTOCOL_STREAM_ACK, SUBPROTOCOL_STREAM_NACK, SUBPROTOCOL_STREAM_RESET,
SUBPROTOCOL_STREAM_WINDOW,
};
use super::subprotocol::MigrationSubprotocolHandler;
use super::transport::{NetSocket, PacketReceiver, ParsedPacket, SocketBufferConfig};
use super::Visibility;
use tokio::sync::oneshot;
use crate::adapter::{Adapter, ShardPollResult};
use crate::error::AdapterError;
use crate::event::{Batch, StoredEvent};
type InboundQueues = Arc<DashMap<u16, SegQueue<StoredEvent>>>;
struct FoldGenerationEntry {
counter: AtomicU64,
last_touched_us: AtomicU64,
}
impl FoldGenerationEntry {
fn new() -> Self {
Self {
counter: AtomicU64::new(0),
last_touched_us: AtomicU64::new(super::current_timestamp_micros()),
}
}
}
const FOLD_GENERATION_GC_INTERVAL: Duration = Duration::from_secs(300);
const FOLD_GENERATION_GC_MAX_AGE: Duration = Duration::from_secs(3600);
fn node_id_to_graph_id(node_id: u64) -> [u8; 32] {
let mut id = [0u8; 32];
id[0..8].copy_from_slice(&node_id.to_le_bytes());
id
}
#[expect(
clippy::unwrap_used,
reason = "input is &[u8; 32]; slicing [0..8] then .try_into::<[u8; 8]>() is statically infallible"
)]
fn graph_id_to_node_id(graph_id: &[u8; 32]) -> u64 {
u64::from_le_bytes(graph_id[0..8].try_into().unwrap())
}
pub type PartitionFilter = Arc<dashmap::DashSet<SocketAddr>>;
#[cfg(feature = "nat-traversal")]
type PendingPunchIntroduces =
Arc<DashMap<u64, (u64, u64, u32, oneshot::Sender<PunchIntroduceOutcome>)>>;
#[cfg(feature = "nat-traversal")]
type PendingPunchAcks = Arc<
DashMap<
u64,
(
u64,
u64,
oneshot::Sender<super::traversal::rendezvous::PunchAck>,
),
>,
>;
#[cfg(feature = "nat-traversal")]
#[derive(Debug, Clone, Copy)]
enum PunchIntroduceOutcome {
Introduce(super::traversal::rendezvous::PunchIntroduce),
Rejected(super::traversal::rendezvous::RejectReason),
}
#[cfg(feature = "nat-traversal")]
#[derive(Debug, Default)]
struct RendezvousBudgets {
requests: DashMap<u64, (u32, Instant)>,
trains: DashMap<u64, (u32, Instant)>,
concurrent_trains: std::sync::atomic::AtomicUsize,
}
#[cfg(feature = "nat-traversal")]
impl RendezvousBudgets {
fn charge(map: &DashMap<u64, (u32, Instant)>, key: u64, window: Duration, max: u32) -> bool {
if max == 0 {
return true;
}
let now = Instant::now();
let mut entry = map.entry(key).or_insert((0, now));
let (count, window_start) = entry.value_mut();
if now.duration_since(*window_start) >= window {
*count = 0;
*window_start = now;
}
if *count >= max {
return false;
}
*count += 1;
true
}
fn charge_request(&self, requester: u64, window: Duration, max: u32) -> bool {
Self::charge(&self.requests, requester, window, max)
}
fn charge_train(&self, source: u64, window: Duration, max: u32) -> bool {
Self::charge(&self.trains, source, window, max)
}
fn try_train_slot(self: &Arc<Self>, max: usize) -> Option<TrainSlot> {
use std::sync::atomic::Ordering;
if max == 0 {
return Some(TrainSlot { budgets: None });
}
let mut cur = self.concurrent_trains.load(Ordering::Relaxed);
loop {
if cur >= max {
return None;
}
match self.concurrent_trains.compare_exchange_weak(
cur,
cur + 1,
Ordering::AcqRel,
Ordering::Relaxed,
) {
Ok(_) => {
return Some(TrainSlot {
budgets: Some(self.clone()),
})
}
Err(observed) => cur = observed,
}
}
}
}
#[cfg(feature = "nat-traversal")]
#[derive(Debug, Clone, Copy)]
struct UpgradeCacheEntry {
next_eligible: Instant,
failures: u32,
done: bool,
}
#[cfg(feature = "nat-traversal")]
struct TrainSlot {
budgets: Option<Arc<RendezvousBudgets>>,
}
#[cfg(feature = "nat-traversal")]
impl Drop for TrainSlot {
fn drop(&mut self) {
if let Some(b) = &self.budgets {
b.concurrent_trains
.fetch_sub(1, std::sync::atomic::Ordering::AcqRel);
}
}
}
struct PeerRegistrationGuard {
peer_node_id: u64,
registered_session_id: u64,
registered_next_hop: SocketAddr,
peers: Arc<DashMap<u64, PeerInfo>>,
peer_addrs: Arc<DashMap<u64, SocketAddr>>,
session_id_to_node: Arc<DashMap<u64, u64>>,
router: Arc<NetRouter>,
}
impl PeerRegistrationGuard {
fn commit(self) {
let me = std::mem::ManuallyDrop::new(self);
#[expect(
clippy::multiple_unsafe_ops_per_block,
reason = "three ptr::read calls form a single semantic op (consume ManuallyDrop fields together so they drop normally)"
)]
unsafe {
let _peers = std::ptr::read(&me.peers);
let _peer_addrs = std::ptr::read(&me.peer_addrs);
let _session_id_to_node = std::ptr::read(&me.session_id_to_node);
let _router = std::ptr::read(&me.router);
}
}
}
impl Drop for PeerRegistrationGuard {
fn drop(&mut self) {
self.peers.remove_if(&self.peer_node_id, |_, pi| {
pi.addr == self.registered_next_hop
});
self.peer_addrs.remove_if(&self.peer_node_id, |_, addr| {
*addr == self.registered_next_hop
});
self.session_id_to_node
.remove_if(&self.registered_session_id, |_, n| *n == self.peer_node_id);
self.router
.routing_table()
.remove_route_if_next_hop_is(self.peer_node_id, self.registered_next_hop);
}
}
#[derive(Debug, PartialEq, Eq)]
enum RoutedRotationOutcome {
DropReplay,
RefuseFresh,
DeferBusy,
AcceptRotation,
}
fn routed_rotation_outcome(
existing: &PeerInfo,
new_static: &[u8; 32],
new_ephemeral: &[u8; 32],
session_timeout: Duration,
) -> RoutedRotationOutcome {
if existing.remote_static_pub == *new_static {
if existing.last_initiator_ephemeral.as_ref() == Some(new_ephemeral) {
return RoutedRotationOutcome::DropReplay;
}
let live = !existing.session.is_timed_out(session_timeout);
let busy = existing.session.has_open_streams() || existing.session.has_unacked();
if live && busy {
return RoutedRotationOutcome::DeferBusy;
}
return RoutedRotationOutcome::AcceptRotation;
}
if existing.session.is_timed_out(session_timeout) {
RoutedRotationOutcome::AcceptRotation
} else {
RoutedRotationOutcome::RefuseFresh
}
}
struct RetainedChain {
chain: TokenChain,
signatures_verified: AtomicBool,
}
impl RetainedChain {
fn new(chain: TokenChain) -> Self {
Self {
chain,
signatures_verified: AtomicBool::new(false),
}
}
}
#[derive(Clone)]
struct DispatchCtx {
local_node_id: u64,
peers: Arc<DashMap<u64, PeerInfo>>,
addr_to_node: Arc<DashMap<SocketAddr, u64>>,
peer_addrs: Arc<DashMap<u64, SocketAddr>>,
router: Arc<NetRouter>,
failure_detector: Arc<FailureDetector>,
inbound: InboundQueues,
#[cfg(feature = "cortex")]
rpc_inbound_dispatchers: Arc<
DashMap<
u16,
Vec<(
ChannelHash,
crate::adapter::net::cortex::RpcInboundDispatcher,
)>,
>,
>,
num_shards: u16,
migration_handler: Arc<ArcSwapOption<MigrationSubprotocolHandler>>,
#[cfg(feature = "redex")]
replication_inbound_router:
Arc<parking_lot::RwLock<Option<Arc<dyn super::redex::ReplicationInboundRouter>>>>,
#[cfg(feature = "meshdb")]
meshdb_inbound_router:
Arc<parking_lot::RwLock<Option<Arc<dyn super::behavior::meshdb::MeshDbInboundRouter>>>>,
fold_router:
Arc<parking_lot::RwLock<Option<Arc<dyn super::behavior::fold::FoldChannelRouter>>>>,
#[cfg(feature = "dataforts")]
greedy_observer: Arc<parking_lot::RwLock<Option<Arc<dyn super::dataforts::GreedyObserver>>>>,
#[cfg(feature = "dataforts")]
blob_transfer_engine:
Arc<parking_lot::RwLock<Option<Arc<super::dataforts::blob::transfer::BlobTransferEngine>>>>,
pending_handshakes: Arc<DashMap<u64, PendingHandshake>>,
pending_direct_initiators: Arc<DashMap<SocketAddr, oneshot::Sender<Bytes>>>,
static_keypair: StaticKeypair,
psk: [u8; 32],
socket: Arc<NetSocket>,
proximity_graph: Arc<ProximityGraph>,
partition_filter: PartitionFilter,
enable_route_withdraw: bool,
route_withdraw_seq: Arc<AtomicU64>,
route_withdraw_damper: Arc<DashMap<(u64, Option<u64>), std::time::Instant>>,
route_withdraw_gate: Arc<WithdrawalSeqGate>,
route_withdraw_cascades_inflight: Arc<AtomicUsize>,
enable_sensing_coalescing: bool,
sensing_interest_ttl: Duration,
sensing_interest_table: Arc<parking_lot::Mutex<sensing::InterestTable>>,
sensing_counters: Arc<sensing::SensingCounters>,
sensing_over_cap: Arc<AtomicU64>,
sensing_local_root: sensing::AudienceScopeCommitment,
sensing_fleet_scope: bool,
sensing_upstream_damper: Arc<DashMap<(u64, [u8; 32]), std::time::Instant>>,
#[cfg(feature = "redex")]
sensing_leader: Arc<parking_lot::Mutex<Option<sensing::SensingLeader>>>,
#[cfg(feature = "redex")]
sensing_fold_coalescer: Arc<DashMap<[u8; 32], SensingFoldGate>>,
#[cfg(feature = "redex")]
sensing_local_entity_root: sensing::AudienceScopeCommitment,
sensing_emitter: Arc<parking_lot::Mutex<Option<sensing::OriginEmitter>>>,
sensing_emitter_notify: Arc<tokio::sync::Notify>,
signing_identity: Arc<EntityKeypair>,
capability_version: Arc<AtomicU64>,
sensing_observer_gate: Arc<parking_lot::Mutex<sensing::IncarnationSeqGate>>,
sensing_continuity_factor: u32,
sensing_overlay_changed: Arc<tokio::sync::watch::Sender<u64>>,
sensing_capability_interests: CapabilityInterestExpectations,
sensing_observations: Arc<parking_lot::Mutex<SensingObservations>>,
pending_stream_grants: Arc<parking_lot::Mutex<HashMap<(u64, u64), PendingStreamGrant>>>,
pending_stream_grants_notify: Arc<Notify>,
control_stats: Arc<ControlPlaneStats>,
packet_pool_size: usize,
default_reliable: bool,
session_timeout: Duration,
roster: Arc<SubscriberRoster>,
channel_configs: Option<Arc<ChannelConfigRegistry>>,
pending_membership_acks: Arc<DashMap<u64, (u64, oneshot::Sender<MembershipAck>)>>,
#[cfg(feature = "nat-traversal")]
pending_reflex_probes:
Arc<DashMap<u64, (u64, tokio::sync::oneshot::Sender<std::net::SocketAddr>)>>,
#[cfg(feature = "nat-traversal")]
pending_punch_introduces: PendingPunchIntroduces,
#[cfg(feature = "nat-traversal")]
pending_punch_acks: PendingPunchAcks,
#[cfg(feature = "nat-traversal")]
punch_observers: Arc<
DashMap<
SocketAddr,
(
u64,
tokio::sync::oneshot::Sender<super::traversal::rendezvous::Keepalive>,
),
>,
>,
#[cfg(feature = "nat-traversal")]
rendezvous_budgets: Arc<RendezvousBudgets>,
#[cfg(feature = "nat-traversal")]
traversal_config: super::traversal::TraversalConfig,
max_channels_per_peer: usize,
capability_fold: Arc<super::behavior::fold::Fold<super::behavior::fold::CapabilityFold>>,
ack_ranges_peer_cache: Arc<DashMap<u64, (bool, Instant)>>,
#[cfg(feature = "dataforts")]
capability_set_cache: Arc<super::behavior::fold::capability_bridge::CapabilitySetCache>,
seen_announcements: Arc<DashMap<(u64, u64, bool), std::time::Instant>>,
require_signed_capabilities: bool,
local_subnet: SubnetId,
local_subnet_policy: Option<Arc<SubnetPolicy>>,
peer_subnets: Arc<DashMap<u64, SubnetId>>,
subnet_gateway: Option<Arc<SubnetGateway>>,
peer_entity_ids: Arc<DashMap<u64, EntityId>>,
origin_hash_to_node: Arc<DashMap<u64, u64>>,
session_id_to_node: Arc<DashMap<u64, u64>>,
token_cache: Option<Arc<TokenCache>>,
subscriber_chains: Arc<DashMap<(u64, ChannelHash), RetainedChain>>,
auth_guard: Arc<AuthGuard>,
auth_failures: Arc<DashMap<u64, AuthFailureState>>,
max_auth_failures_per_window: u16,
auth_failure_window: Duration,
auth_throttle_duration: Duration,
}
#[derive(Debug, Clone)]
pub(crate) struct MembershipAck {
pub accepted: bool,
pub reason: Option<AckReason>,
}
#[derive(Debug, Clone)]
pub struct MeshNodeConfig {
pub bind_addr: SocketAddr,
pub psk: [u8; 32],
pub heartbeat_interval: Duration,
pub session_timeout: Duration,
pub num_shards: u16,
pub packet_pool_size: usize,
pub default_reliable: bool,
#[cfg(feature = "batched-ingress")]
pub batched_ingress: bool,
pub handshake_timeout: Duration,
pub handshake_retries: usize,
pub socket_buffers: SocketBufferConfig,
pub max_queue_depth: usize,
pub fair_quantum: usize,
pub stream_idle_timeout: Duration,
pub max_streams: usize,
pub max_channels_per_peer: usize,
pub membership_ack_timeout: Duration,
pub require_signed_capabilities: bool,
pub capability_gc_interval: Duration,
pub capability_reannounce_interval: Duration,
pub enable_stream_ack_ranges: bool,
pub subnet: SubnetId,
pub subnet_policy: Option<Arc<SubnetPolicy>>,
pub default_visibility: Visibility,
pub min_announce_interval: Duration,
pub announce_debounce: Duration,
pub event_pingwave_min_gap: Duration,
pub enable_route_withdraw: bool,
pub enable_sensing_coalescing: bool,
pub sensing_interest_ttl: Duration,
pub max_interests_per_peer: usize,
pub attestation_cadence_floor: Duration,
pub continuity_factor: u32,
pub sensing_owner_root: Option<sensing::AudienceScopeCommitment>,
pub sensing_incarnation: Option<sensing::Incarnation>,
pub token_sweep_interval: Duration,
pub max_auth_failures_per_window: u16,
pub auth_failure_window: Duration,
pub auth_throttle_duration: Duration,
#[cfg(feature = "nat-traversal")]
pub reflex_override: Option<SocketAddr>,
#[cfg(feature = "port-mapping")]
pub try_port_mapping: bool,
#[cfg(feature = "nat-traversal")]
pub auto_direct_upgrade: bool,
}
impl MeshNodeConfig {
pub fn new(bind_addr: SocketAddr, psk: [u8; 32]) -> Self {
Self {
bind_addr,
psk,
heartbeat_interval: Duration::from_secs(5),
session_timeout: Duration::from_secs(30),
num_shards: 4,
packet_pool_size: 64,
default_reliable: false,
#[cfg(feature = "batched-ingress")]
batched_ingress: false,
handshake_timeout: Duration::from_secs(5),
handshake_retries: 3,
socket_buffers: SocketBufferConfig::for_testing(),
max_queue_depth: 1024,
fair_quantum: 16,
stream_idle_timeout: Duration::from_secs(300),
max_streams: 4096,
max_channels_per_peer: 1024,
membership_ack_timeout: Duration::from_secs(5),
require_signed_capabilities: true,
capability_gc_interval: Duration::from_secs(60),
capability_reannounce_interval: Duration::from_secs(150),
enable_stream_ack_ranges: true,
subnet: SubnetId::GLOBAL,
subnet_policy: None,
default_visibility: Visibility::Global,
min_announce_interval: Duration::from_secs(10),
announce_debounce: Duration::from_millis(100),
event_pingwave_min_gap: Duration::from_millis(250),
enable_route_withdraw: true,
enable_sensing_coalescing: false,
sensing_interest_ttl: Duration::from_secs(30),
max_interests_per_peer: 512,
attestation_cadence_floor: sensing::DEFAULT_ATTESTATION_CADENCE_FLOOR,
continuity_factor: 3,
sensing_owner_root: None,
sensing_incarnation: None,
token_sweep_interval: Duration::from_secs(30),
max_auth_failures_per_window: 16,
auth_failure_window: Duration::from_secs(60),
auth_throttle_duration: Duration::from_secs(30),
#[cfg(feature = "nat-traversal")]
reflex_override: None,
#[cfg(feature = "port-mapping")]
try_port_mapping: false,
#[cfg(feature = "nat-traversal")]
auto_direct_upgrade: false,
}
}
#[cfg(feature = "nat-traversal")]
pub fn with_auto_direct_upgrade(mut self, enabled: bool) -> Self {
self.auto_direct_upgrade = enabled;
self
}
#[cfg(feature = "nat-traversal")]
pub fn with_reflex_override(mut self, external: SocketAddr) -> Self {
self.reflex_override = Some(external);
self
}
#[cfg(feature = "port-mapping")]
pub fn with_try_port_mapping(mut self, enabled: bool) -> Self {
self.try_port_mapping = enabled;
self
}
pub fn with_heartbeat_interval(mut self, interval: Duration) -> Self {
self.heartbeat_interval = interval;
self
}
pub fn with_session_timeout(mut self, timeout: Duration) -> Self {
self.session_timeout = timeout;
self
}
pub fn with_num_shards(mut self, n: u16) -> Self {
self.num_shards = n;
self
}
#[cfg(feature = "batched-ingress")]
pub fn with_batched_ingress(mut self, enabled: bool) -> Self {
self.batched_ingress = enabled;
self
}
pub fn with_handshake(mut self, retries: usize, timeout: Duration) -> Self {
self.handshake_retries = retries;
self.handshake_timeout = timeout;
self
}
pub fn with_require_signed_capabilities(mut self, require: bool) -> Self {
self.require_signed_capabilities = require;
self
}
pub fn with_capability_gc_interval(mut self, interval: Duration) -> Self {
self.capability_gc_interval = interval;
self
}
pub fn with_capability_reannounce_interval(mut self, interval: Duration) -> Self {
self.capability_reannounce_interval = interval;
self
}
pub fn with_stream_ack_ranges(mut self, enable: bool) -> Self {
self.enable_stream_ack_ranges = enable;
self
}
pub fn with_min_announce_interval(mut self, interval: Duration) -> Self {
self.min_announce_interval = interval;
self
}
pub fn with_announce_debounce(mut self, debounce: Duration) -> Self {
self.announce_debounce = debounce;
self
}
pub fn with_event_pingwave_min_gap(mut self, gap: Duration) -> Self {
self.event_pingwave_min_gap = gap;
self
}
pub fn with_route_withdraw(mut self, enable: bool) -> Self {
self.enable_route_withdraw = enable;
self
}
pub fn with_sensing_coalescing(mut self, enable: bool) -> Self {
self.enable_sensing_coalescing = enable;
self
}
pub fn with_sensing_interest_ttl(mut self, ttl: Duration) -> Self {
self.sensing_interest_ttl = ttl;
self
}
pub fn with_max_interests_per_peer(mut self, cap: usize) -> Self {
self.max_interests_per_peer = cap;
self
}
pub fn with_attestation_cadence_floor(mut self, floor: Duration) -> Self {
self.attestation_cadence_floor = floor;
self
}
pub fn with_continuity_factor(mut self, k: u32) -> Self {
self.continuity_factor = k;
self
}
pub fn with_sensing_owner_root(mut self, root: sensing::AudienceScopeCommitment) -> Self {
self.sensing_owner_root = Some(root);
self
}
pub fn with_sensing_incarnation(mut self, incarnation: sensing::Incarnation) -> Self {
self.sensing_incarnation = Some(incarnation);
self
}
pub fn with_token_sweep_interval(mut self, interval: Duration) -> Self {
self.token_sweep_interval = interval;
self
}
pub fn with_auth_failure_limit(
mut self,
max_per_window: u16,
window: Duration,
throttle: Duration,
) -> Self {
self.max_auth_failures_per_window = max_per_window;
self.auth_failure_window = window;
self.auth_throttle_duration = throttle;
self
}
pub fn with_subnet(mut self, subnet: SubnetId) -> Self {
self.subnet = subnet;
self
}
pub fn with_subnet_policy(mut self, policy: Arc<SubnetPolicy>) -> Self {
self.subnet_policy = Some(policy);
self
}
pub fn with_default_visibility(mut self, visibility: Visibility) -> Self {
self.default_visibility = visibility;
self
}
}
struct PeerInfo {
node_id: u64,
addr: SocketAddr,
session: Arc<NetSession>,
remote_static_pub: [u8; 32],
last_initiator_ephemeral: Option<[u8; 32]>,
}
struct PendingHandshake {
noise: NoiseHandshake,
tx: oneshot::Sender<Result<SessionKeys, CryptoError>>,
}
#[inline]
fn routing_id(node_id: u64) -> u64 {
(node_id as u32) as u64
}
#[inline]
fn subscriber_origin_hash(node_id: u64) -> u64 {
node_id
}
#[cfg(feature = "cortex")]
pub(super) const RPC_ROUTE_CACHE_SOFT_CAP: usize = 256;
#[cfg(feature = "cortex")]
pub(super) struct RpcRoute {
pub request_channel: ChannelName,
pub request_channel_hash: ChannelHash,
pub request_stream_id: u64,
pub reply_channel: ChannelName,
pub reply_hash: ChannelHash,
}
fn synthesize_compute_not_supported_reply(payload: &[u8]) -> Option<Bytes> {
use crate::adapter::net::compute::orchestrator::wire as mig_wire;
use crate::adapter::net::compute::{MigrationFailureReason, MigrationMessage};
let msg = mig_wire::decode(payload).ok()?;
let origin = match msg {
MigrationMessage::TakeSnapshot { daemon_origin, .. }
| MigrationMessage::SnapshotReady { daemon_origin, .. } => daemon_origin,
_ => return None,
};
let reply = MigrationMessage::MigrationFailed {
daemon_origin: origin,
reason: MigrationFailureReason::ComputeNotSupported,
};
mig_wire::encode(&reply).ok().map(Bytes::from)
}
#[inline]
fn nonzero_interval(d: Duration) -> Duration {
if d.is_zero() {
Duration::from_secs(1)
} else {
d
}
}
#[inline]
fn capability_reannounce_ttl(
reannounce_interval: Duration,
min_announce_interval: Duration,
) -> Duration {
reannounce_interval
.max(min_announce_interval)
.saturating_mul(2)
.max(Duration::from_secs(1))
}
#[cfg(feature = "nat-traversal")]
async fn await_punch_observer_outcome(
obs_rx: tokio::sync::oneshot::Receiver<super::traversal::rendezvous::Keepalive>,
deadline: Duration,
punch_observers: &DashMap<
SocketAddr,
(
u64,
tokio::sync::oneshot::Sender<super::traversal::rendezvous::Keepalive>,
),
>,
peer_reflex: SocketAddr,
) -> bool {
match tokio::time::timeout(deadline, obs_rx).await {
Ok(Ok(_ka)) => true,
Ok(Err(_)) => false,
Err(_) => {
punch_observers.remove(&peer_reflex);
false
}
}
}
#[cfg(feature = "nat-traversal")]
struct SweepGuard<'a>(&'a std::sync::atomic::AtomicBool);
#[cfg(feature = "nat-traversal")]
impl<'a> SweepGuard<'a> {
fn try_enter(flag: &'a std::sync::atomic::AtomicBool) -> Option<Self> {
if flag.swap(true, std::sync::atomic::Ordering::Acquire) {
None
} else {
Some(Self(flag))
}
}
}
#[cfg(feature = "nat-traversal")]
impl Drop for SweepGuard<'_> {
fn drop(&mut self) {
self.0.store(false, std::sync::atomic::Ordering::Release);
}
}
#[cfg(feature = "nat-traversal")]
fn keepalive_send_offsets(fire_at_ms: u64, now_ms: u64, deadline: Duration) -> [Duration; 3] {
let base_lead = Duration::from_millis(fire_at_ms.saturating_sub(now_ms)).min(deadline);
[
base_lead,
base_lead.saturating_add(Duration::from_millis(100)),
base_lead.saturating_add(Duration::from_millis(250)),
]
}
#[derive(Debug, Default)]
struct AuthFailureState {
failures: u16,
window_start: Option<std::time::Instant>,
throttled_until: Option<std::time::Instant>,
}
fn sweep_expired_subscribers(
roster: &SubscriberRoster,
guard: &AuthGuard,
token_cache: Option<&Arc<TokenCache>>,
peer_entity_ids: &DashMap<u64, EntityId>,
channel_configs: Option<&Arc<ChannelConfigRegistry>>,
subscriber_chains: &DashMap<(u64, ChannelHash), RetainedChain>,
) {
let (Some(cache), Some(configs)) = (token_cache, channel_configs) else {
return;
};
let revocation = cache.revocation().as_ref();
let skew = cache.clock_skew_secs();
let peers: Vec<(u64, EntityId)> = peer_entity_ids
.iter()
.map(|e| (*e.key(), e.value().clone()))
.collect();
for (node_id, entity_id) in peers {
for channel_id in roster.channels_for(node_id) {
let name = channel_id.name();
let Some(cfg) = configs.get_by_name(name.as_str()) else {
continue;
};
if !cfg.token_required() {
continue;
}
let authorized = subscriber_chains
.get(&(node_id, name.hash()))
.is_some_and(|r| cfg.reverify_subscribe(&r.chain, &entity_id, revocation, skew));
if !authorized {
guard.revoke_channel(subscriber_origin_hash(node_id), name);
roster.remove(&channel_id, node_id);
subscriber_chains.remove(&(node_id, name.hash()));
tracing::debug!(
node_id = format!("{:#x}", node_id),
channel = name.as_str(),
"auth: evicted subscriber with expired/invalid/revoked token chain",
);
}
}
}
}
const DEFAULT_HANDSHAKE_TTL: u8 = 16;
const MAX_HOPS: u8 = 16;
const MAX_BLOB_HEAT_TAGS_PER_ANNOUNCE: usize = 256;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum AddrInstallMode {
DirectOverwrite,
RoutedPreserve,
}
struct AnnounceGate {
last_broadcast_at: Option<std::time::Instant>,
deferred_scheduled: bool,
deferral_generation: u64,
}
#[cfg(feature = "cortex")]
pub(super) struct LocalServiceRegistry {
set: dashmap::DashSet<String>,
change_signal: Arc<tokio::sync::watch::Sender<u64>>,
}
#[cfg(feature = "cortex")]
impl LocalServiceRegistry {
fn new(change_signal: Arc<tokio::sync::watch::Sender<u64>>) -> Self {
Self {
set: dashmap::DashSet::new(),
change_signal,
}
}
pub(super) fn insert(&self, service: String) -> bool {
let inserted = self.set.insert(service);
if inserted {
self.change_signal.send_modify(|g| *g = g.wrapping_add(1));
}
inserted
}
pub(super) fn remove(&self, service: &str) -> Option<String> {
let removed = self.set.remove(service);
if removed.is_some() {
self.change_signal.send_modify(|g| *g = g.wrapping_add(1));
}
removed
}
pub(super) fn is_empty(&self) -> bool {
self.set.is_empty()
}
pub(super) fn snapshot(&self) -> Vec<String> {
self.set.iter().map(|s| s.clone()).collect()
}
}
fn spawn_event_pingwave(
gate: &Arc<parking_lot::Mutex<EventPingwaveGate>>,
min_gap: Duration,
proximity_graph: &Arc<ProximityGraph>,
socket: &Arc<NetSocket>,
peers: &Arc<DashMap<u64, PeerInfo>>,
partition_filter: &PartitionFilter,
resend: bool,
) {
if min_gap == Duration::MAX {
return;
}
enum Action {
EmitNow,
Defer(Duration),
Coalesced,
}
let now = std::time::Instant::now();
let action = {
let mut g = gate.lock();
match g.last_emit {
Some(t) if now.saturating_duration_since(t) < min_gap => {
if g.deferred_scheduled {
Action::Coalesced
} else {
g.deferred_scheduled = true;
Action::Defer(min_gap - now.saturating_duration_since(t))
}
}
_ => {
g.last_emit = Some(now);
Action::EmitNow
}
}
};
match action {
Action::Coalesced => {}
Action::EmitNow => {
let rounds = if resend { 2 } else { 1 };
tokio::spawn(flood_event_pingwave_rounds(
rounds,
proximity_graph.clone(),
socket.clone(),
peers.clone(),
partition_filter.clone(),
));
}
Action::Defer(delay) => {
let gate = gate.clone();
let proximity_graph = proximity_graph.clone();
let socket = socket.clone();
let peers = peers.clone();
let filter = partition_filter.clone();
tokio::spawn(async move {
tokio::time::sleep(delay).await;
{
let mut g = gate.lock();
g.deferred_scheduled = false;
g.last_emit = Some(std::time::Instant::now());
}
flood_event_pingwave_rounds(1, proximity_graph, socket, peers, filter).await;
});
}
}
}
async fn flood_event_pingwave_rounds(
rounds: u8,
proximity_graph: Arc<ProximityGraph>,
socket: Arc<NetSocket>,
peers: Arc<DashMap<u64, PeerInfo>>,
filter: PartitionFilter,
) {
for round in 0..rounds {
if round > 0 {
tokio::time::sleep(EVENT_PINGWAVE_RESEND_DELAY).await;
}
let pw_bytes = proximity_graph
.create_pingwave(HealthStatus::Healthy)
.to_bytes();
let targets: Vec<SocketAddr> = peers
.iter()
.filter_map(|e| {
let addr = e.value().addr;
if filter.contains(&addr) {
None
} else {
Some(addr)
}
})
.collect();
for addr in targets {
let _ = socket.send_to(&pw_bytes, addr).await;
}
}
}
#[derive(Default)]
struct EventPingwaveGate {
last_emit: Option<std::time::Instant>,
deferred_scheduled: bool,
}
const EVENT_PINGWAVE_RESEND_DELAY: Duration = Duration::from_millis(50);
const ROUTE_WITHDRAW_DAMP_WINDOW: Duration = Duration::from_secs(1);
const MAX_INFLIGHT_ROUTE_WITHDRAW_CASCADES: usize = 64;
fn route_withdraw_damp_admit(
damper: &DashMap<(u64, Option<u64>), std::time::Instant>,
key: (u64, Option<u64>),
now: std::time::Instant,
) -> bool {
let mut fresh = false;
damper
.entry(key)
.and_modify(|t| {
if now.saturating_duration_since(*t) >= ROUTE_WITHDRAW_DAMP_WINDOW {
*t = now;
fresh = true;
}
})
.or_insert_with(|| {
fresh = true;
now
});
fresh
}
async fn run_route_withdrawal_flood(
seq_counter: Arc<AtomicU64>,
damper: Arc<DashMap<(u64, Option<u64>), std::time::Instant>>,
socket: Arc<NetSocket>,
peers: Arc<DashMap<u64, PeerInfo>>,
partition_filter: PartitionFilter,
dest: u64,
exclude: Option<u64>,
) {
let now = std::time::Instant::now();
if !route_withdraw_damp_admit(&damper, (dest, exclude), now) {
return;
}
if damper.len() > 1024 {
damper.retain(|_, t| now.saturating_duration_since(*t) < ROUTE_WITHDRAW_DAMP_WINDOW);
}
let mut targets: Vec<(SocketAddr, Arc<NetSession>)> = Vec::new();
for entry in peers.iter() {
let peer_id = *entry.key();
if peer_id == dest || Some(peer_id) == exclude {
continue;
}
let addr = entry.value().addr;
if partition_filter.contains(&addr) {
continue;
}
targets.push((addr, entry.value().session.clone()));
}
if targets.is_empty() {
return;
}
let payload = RouteWithdrawal {
dest,
seq: seq_counter.fetch_add(1, Ordering::Relaxed),
}
.to_bytes();
let stream_id = SUBPROTOCOL_ROUTE_WITHDRAW as u64;
let events = [Bytes::copy_from_slice(&payload)];
for (addr, session) in targets {
let seq = session.get_or_create_stream(stream_id).next_tx_seq();
let packet = {
let mut builder = session.thread_local_pool().get();
builder.build_subprotocol(
stream_id,
seq,
&events,
PacketFlags::NONE,
SUBPROTOCOL_ROUTE_WITHDRAW,
)
};
let _ = socket.send_to(&packet, addr).await;
}
}
fn spawn_route_withdrawal_flood(
seq_counter: &Arc<AtomicU64>,
damper: &Arc<DashMap<(u64, Option<u64>), std::time::Instant>>,
socket: &Arc<NetSocket>,
peers: &Arc<DashMap<u64, PeerInfo>>,
partition_filter: &PartitionFilter,
dest: u64,
exclude: Option<u64>,
) {
tokio::spawn(run_route_withdrawal_flood(
seq_counter.clone(),
damper.clone(),
socket.clone(),
peers.clone(),
partition_filter.clone(),
dest,
exclude,
));
}
const SENSING_UPSTREAM_MIN_GAP: Duration = Duration::from_millis(100);
#[cfg(feature = "redex")]
#[allow(clippy::too_many_arguments)]
fn dispatch_sensing_leader_deliveries(
socket: &Arc<NetSocket>,
peers: &Arc<DashMap<u64, PeerInfo>>,
addr_to_node: &Arc<DashMap<SocketAddr, u64>>,
router: &Arc<NetRouter>,
partition_filter: &PartitionFilter,
local_node_id: u64,
observations: &Arc<parking_lot::Mutex<SensingObservations>>,
overlay: &Arc<tokio::sync::watch::Sender<u64>>,
factor: u32,
deliveries: Vec<sensing::Delivery>,
now: Instant,
) {
let mut overlay_moved = false;
for delivery in deliveries {
let branch = delivery.attestation.branch();
let wire = {
let observations = observations.lock();
observations.latest.get(&branch).and_then(|cached| {
((cached.origin_incarnation, cached.seq)
== (
delivery.attestation.origin_incarnation,
delivery.attestation.seq,
))
.then(|| cached.clone())
})
};
let Some(wire) = wire else {
continue;
};
match delivery.to {
sensing::DownstreamId::Peer(node) => {
let stream_id = if delivery.continuity_bearing {
sensing::SUBPROTOCOL_READINESS_ATTESTATION as u64
} else {
sensing::SENSING_PROVISIONAL_STREAM
};
if let Ok(bytes) = sensing::encode_attestation(&wire) {
spawn_sensing_frame_send(
socket,
peers,
addr_to_node,
router,
partition_filter,
local_node_id,
node,
stream_id,
sensing::SUBPROTOCOL_READINESS_ATTESTATION,
bytes,
);
}
}
sensing::DownstreamId::Local => {
let interval = wire.promised_cadence;
overlay_moved |= observations.lock().feed_consumer_cell(
&branch,
&wire,
delivery.continuity_bearing,
interval,
factor,
now,
);
}
sensing::DownstreamId::Leader => {}
}
}
if overlay_moved {
overlay.send_modify(|generation| {
*generation = generation.wrapping_add(1);
});
}
}
fn sensing_interval_in_bounds(interval: Duration, ttl: Duration) -> bool {
!interval.is_zero() && interval <= ttl
}
const MAX_SENSING_OBSERVATIONS: usize = 4096;
#[derive(Clone, Copy)]
struct SensingDeliverySlot {
last_status: Option<sensing::AttestedStatus>,
last_delivered: Option<(sensing::Incarnation, u64)>,
next_due: Instant,
pending: bool,
}
#[derive(Default)]
struct SensingObservations {
latest: HashMap<sensing::ProviderInterestKey, sensing::ReadinessAttestation>,
refusals: HashMap<sensing::ProviderInterestKey, (sensing::ReadinessAttestation, Instant)>,
provider_epochs: HashMap<u64, (sensing::Incarnation, u64)>,
upstream: HashMap<sensing::ProviderInterestKey, sensing::ObservationCell>,
slots: HashMap<(sensing::ProviderInterestKey, sensing::DownstreamId), SensingDeliverySlot>,
consumer_cells: HashMap<sensing::ProviderInterestKey, sensing::ObservationCell>,
}
impl SensingObservations {
fn reclaim_branch(&mut self, key: &sensing::ProviderInterestKey) {
self.refusals.remove(key);
self.reclaim_status(key);
}
fn reclaim_status(&mut self, key: &sensing::ProviderInterestKey) {
self.latest.remove(key);
self.upstream.remove(key);
self.slots.retain(|(branch, _), _| branch != key);
self.consumer_cells.remove(key);
self.reclaim_orphan_epochs([key.provider]);
}
fn feed_consumer_cell(
&mut self,
branch: &sensing::ProviderInterestKey,
attestation: &sensing::ReadinessAttestation,
bearing: bool,
own_interval: Duration,
factor: u32,
now: Instant,
) -> bool {
let cell = self
.consumer_cells
.entry(branch.clone())
.or_insert_with(|| sensing::ObservationCell::register(now, own_interval, factor));
cell.update_interval(own_interval);
let before = sensing_scheduler_view(cell);
cell.on_admitted_beat(
now,
sensing::DeliveredBeat {
attested_status: attestation.status,
estimated_start: attestation.estimated_start,
source_incarnation: attestation.origin_incarnation,
capability_generation: attestation.capability_generation,
seq: attestation.seq,
promised_cadence: attestation.promised_cadence,
continuity_bearing: bearing,
},
);
sensing_scheduler_view(cell) != before
}
fn reclaim_orphan_epochs(&mut self, providers: impl IntoIterator<Item = u64>) {
for provider in providers {
if !self.latest.keys().any(|k| k.provider == provider)
&& !self.refusals.keys().any(|k| k.provider == provider)
{
self.provider_epochs.remove(&provider);
}
}
}
fn disrupt_provider(
&mut self,
provider: u64,
reason: sensing::DisruptReason,
) -> (Vec<sensing::ProviderInterestKey>, bool) {
let mut branches = Vec::new();
for (key, cell) in self.upstream.iter_mut() {
if key.provider == provider && cell.continuity() != sensing::Continuity::Expired {
cell.disrupt(reason);
branches.push(key.clone());
}
}
let mut overlay_moved = false;
for (key, cell) in self.consumer_cells.iter_mut() {
if key.provider == provider {
let before = cell.projected();
cell.disrupt(reason);
overlay_moved |= cell.projected() != before;
}
}
(branches, overlay_moved)
}
fn update_upstream_interval(
&mut self,
branch: &sensing::ProviderInterestKey,
aggregate: Option<Duration>,
) {
if let (Some(cell), Some(interval)) = (self.upstream.get_mut(branch), aggregate) {
cell.update_interval(interval);
}
}
fn update_consumer_interval(
&mut self,
branch: &sensing::ProviderInterestKey,
interval: Duration,
) {
if let Some(cell) = self.consumer_cells.get_mut(branch) {
cell.update_interval(interval);
}
}
fn update_consumer_intervals(
&mut self,
interest: &sensing::CapabilityInterestKey,
interval: Duration,
) {
for (key, cell) in self.consumer_cells.iter_mut() {
if &key.interest == interest {
cell.update_interval(interval);
}
}
}
}
fn sensing_scheduler_view(
cell: &sensing::ObservationCell,
) -> (sensing::ProjectedReadiness, Option<Duration>, u64) {
let observation = cell.observation();
(
cell.projected(),
observation.and_then(|obs| obs.estimated_start),
observation
.map(|obs| obs.capability_generation)
.unwrap_or(0),
)
}
fn sensing_live_direct_session(
peers: &DashMap<u64, PeerInfo>,
addr_to_node: &DashMap<SocketAddr, u64>,
failure_detector: Option<&FailureDetector>,
node: u64,
) -> bool {
let Some(addr) = peers.get(&node).map(|p| p.value().addr) else {
return false;
};
sensing_addr_is_live_direct(addr_to_node, failure_detector, node, addr)
}
fn sensing_addr_is_live_direct(
addr_to_node: &DashMap<SocketAddr, u64>,
failure_detector: Option<&FailureDetector>,
node: u64,
addr: SocketAddr,
) -> bool {
if addr_to_node.get(&addr).map(|e| *e.value()) != Some(node) {
return false;
}
match failure_detector {
Some(detector) => !matches!(
detector.status(node),
NodeStatus::Failed | NodeStatus::Suspected
),
None => true,
}
}
fn disrupt_sensing_provider(
table: &parking_lot::Mutex<sensing::InterestTable>,
observations: &parking_lot::Mutex<SensingObservations>,
overlay: &tokio::sync::watch::Sender<u64>,
provider: u64,
reason: sensing::DisruptReason,
) {
let (branches, overlay_moved) = observations.lock().disrupt_provider(provider, reason);
if !branches.is_empty() {
let mut table = table.lock();
for branch in &branches {
table.set_upstream_continuity(branch, sensing::Continuity::Expired);
}
}
if overlay_moved {
overlay.send_modify(|generation| {
*generation = generation.wrapping_add(1);
});
}
}
#[allow(clippy::too_many_arguments)]
fn apply_sensing_removal_action(
observations: &parking_lot::Mutex<SensingObservations>,
emitter: &parking_lot::Mutex<Option<sensing::OriginEmitter>>,
emitter_stamp: Option<u64>,
socket: &Arc<NetSocket>,
peers: &Arc<DashMap<u64, PeerInfo>>,
addr_to_node: &Arc<DashMap<SocketAddr, u64>>,
router: &Arc<NetRouter>,
partition_filter: &PartitionFilter,
local_node_id: u64,
key: &sensing::ProviderInterestKey,
action: sensing::UpstreamAction,
) {
match action {
sensing::UpstreamAction::Deregister => {
observations.lock().reclaim_branch(key);
if key.provider == local_node_id {
if let (Some(emitter), Some(stamp)) = (emitter.lock().as_mut(), emitter_stamp) {
emitter.retire_if_stale(&key.interest.interest_digest, stamp);
}
} else {
let frame = sensing::SensingInterestFrame::Deregister {
interest_digest: key.interest.interest_digest,
target: Some(key.provider),
};
if let Ok(bytes) = sensing::encode_interest_frame(&frame) {
spawn_sensing_frame_send(
socket,
peers,
addr_to_node,
router,
partition_filter,
local_node_id,
key.provider,
sensing::SUBPROTOCOL_SENSING_INTEREST as u64,
sensing::SUBPROTOCOL_SENSING_INTEREST,
bytes,
);
}
}
}
sensing::UpstreamAction::Register { strictest } => {
observations
.lock()
.update_upstream_interval(key, Some(strictest));
}
sensing::UpstreamAction::None => {}
}
}
#[allow(clippy::too_many_arguments)]
fn remove_sensing_downstream(
table: &parking_lot::Mutex<sensing::InterestTable>,
observations: &parking_lot::Mutex<SensingObservations>,
emitter: &parking_lot::Mutex<Option<sensing::OriginEmitter>>,
socket: &Arc<NetSocket>,
peers: &Arc<DashMap<u64, PeerInfo>>,
addr_to_node: &Arc<DashMap<SocketAddr, u64>>,
router: &Arc<NetRouter>,
partition_filter: &PartitionFilter,
local_node_id: u64,
failed: u64,
now: Instant,
) {
let emitter_stamp = emitter.lock().as_ref().map(|e| e.stamp());
let actions = table
.lock()
.remove_downstream(sensing::DownstreamId::Peer(failed), now);
for (key, action) in actions {
apply_sensing_removal_action(
observations,
emitter,
emitter_stamp,
socket,
peers,
addr_to_node,
router,
partition_filter,
local_node_id,
&key,
action,
);
}
}
#[cfg(feature = "redex")]
#[allow(clippy::too_many_arguments)]
fn remove_sensing_leader_consumer(
leader: &parking_lot::Mutex<Option<sensing::SensingLeader>>,
table: &parking_lot::Mutex<sensing::InterestTable>,
observations: &parking_lot::Mutex<SensingObservations>,
emitter: &parking_lot::Mutex<Option<sensing::OriginEmitter>>,
socket: &Arc<NetSocket>,
peers: &Arc<DashMap<u64, PeerInfo>>,
addr_to_node: &Arc<DashMap<SocketAddr, u64>>,
router: &Arc<NetRouter>,
partition_filter: &PartitionFilter,
local_node_id: u64,
local_root: sensing::AudienceScopeCommitment,
ttl: Duration,
failed: u64,
now: Instant,
) {
let actions = {
let mut slot = leader.lock();
match slot.as_mut() {
Some(leader) => leader.remove_downstream(sensing::DownstreamId::Peer(failed), now),
None => return,
}
};
for (branch, action) in actions {
match action {
sensing::UpstreamAction::Deregister => {
let emitter_stamp = emitter.lock().as_ref().map(|e| e.stamp());
let mesh_actions = table.lock().deregister(
&branch.interest.interest_digest,
Some(branch.provider),
sensing::DownstreamId::Leader,
now,
);
for (key, mesh_action) in mesh_actions {
apply_sensing_removal_action(
observations,
emitter,
emitter_stamp,
socket,
peers,
addr_to_node,
router,
partition_filter,
local_node_id,
&key,
mesh_action,
);
}
}
sensing::UpstreamAction::Register { strictest } => {
let (outcome, aggregate) = {
let mut table = table.lock();
let outcome = table.register(
&branch,
sensing::DownstreamId::Leader,
strictest,
ttl,
local_root,
now,
);
(outcome, table.aggregate(&branch, now))
};
if matches!(outcome, sensing::RegisterOutcome::Registered(_)) {
observations
.lock()
.update_upstream_interval(&branch, aggregate);
}
}
sensing::UpstreamAction::None => {}
}
}
}
#[derive(Debug, Clone)]
pub struct SensingReadinessOverlay {
pub aggregate: sensing::AggregateView,
pub candidates: Vec<((u64, u64), sensing::ReadinessObservation)>,
}
#[derive(Clone, Copy)]
struct CapabilityInterestExpectation {
requested_sample_interval: Duration,
expires_at: Instant,
audience: sensing::AudienceScopeCommitment,
}
type CapabilityInterestExpectations =
Arc<parking_lot::Mutex<HashMap<sensing::CapabilityInterestKey, CapabilityInterestExpectation>>>;
fn sensing_upstream_damper_admits(
damper: &DashMap<(u64, [u8; 32]), std::time::Instant>,
provider: u64,
interest_digest: [u8; 32],
min_gap: Duration,
) -> bool {
let now = std::time::Instant::now();
let mut fresh = false;
damper
.entry((provider, interest_digest))
.and_modify(|t| {
if now.saturating_duration_since(*t) >= min_gap {
*t = now;
fresh = true;
}
})
.or_insert_with(|| {
fresh = true;
now
});
if damper.len() > 4096 {
damper.retain(|_, t| now.saturating_duration_since(*t) < SENSING_UPSTREAM_MIN_GAP);
}
fresh
}
fn sensing_effective_min_gap(soft_state_ttl: Duration) -> Duration {
SENSING_UPSTREAM_MIN_GAP.min(soft_state_ttl / 2)
}
#[cfg(feature = "redex")]
struct SensingFoldGate {
last_run: std::time::Instant,
generation: u64,
pending: Option<u64>,
}
#[cfg(feature = "redex")]
enum SensingFoldGateDecision {
RunNow,
Defer { remaining: Duration, token: u64 },
Coalesced,
}
#[cfg(feature = "redex")]
fn sensing_fold_gate_admit(
coalescer: &DashMap<[u8; 32], SensingFoldGate>,
digest: [u8; 32],
min_gap: Duration,
) -> SensingFoldGateDecision {
let now = std::time::Instant::now();
let mut decision = SensingFoldGateDecision::RunNow;
coalescer
.entry(digest)
.and_modify(|gate| {
let elapsed = now.saturating_duration_since(gate.last_run);
if elapsed >= min_gap {
gate.last_run = now;
gate.pending = None;
decision = SensingFoldGateDecision::RunNow;
} else if gate.pending.is_some() {
decision = SensingFoldGateDecision::Coalesced;
} else {
gate.generation = gate.generation.wrapping_add(1);
let token = gate.generation;
gate.pending = Some(token);
decision = SensingFoldGateDecision::Defer {
remaining: min_gap - elapsed,
token,
};
}
})
.or_insert_with(|| SensingFoldGate {
last_run: now,
generation: 0,
pending: None,
});
if coalescer.len() > 4096 {
coalescer.retain(|_, gate| {
gate.pending.is_some() || now.saturating_duration_since(gate.last_run) < min_gap
});
}
decision
}
#[cfg(feature = "redex")]
fn sensing_fold_gate_reclaim(
coalescer: &DashMap<[u8; 32], SensingFoldGate>,
digest: &[u8; 32],
token: u64,
) -> bool {
coalescer
.get_mut(digest)
.map(|mut gate| {
if gate.pending == Some(token) {
gate.pending = None;
gate.last_run = std::time::Instant::now();
true
} else {
false
}
})
.unwrap_or(false)
}
#[allow(clippy::too_many_arguments)]
fn spawn_sensing_frame_send(
socket: &Arc<NetSocket>,
peers: &Arc<DashMap<u64, PeerInfo>>,
addr_to_node: &Arc<DashMap<SocketAddr, u64>>,
router: &Arc<NetRouter>,
partition_filter: &PartitionFilter,
local_node_id: u64,
target: u64,
stream_id: u64,
subprotocol: u16,
payload: Vec<u8>,
) {
let next_addr = peers
.get(&target)
.map(|p| p.value().addr)
.or_else(|| router.routing_table().lookup(target));
let Some(addr) = next_addr else {
return;
};
if partition_filter.contains(&addr) {
return;
}
let Some(hop_node) = addr_to_node.get(&addr).map(|e| *e.value()) else {
return;
};
if hop_node == local_node_id {
return;
}
let Some(session) = peers.get(&hop_node).map(|e| e.value().session.clone()) else {
return;
};
let socket = socket.clone();
tokio::spawn(async move {
let events = [Bytes::from(payload)];
let seq = session.get_or_create_stream(stream_id).next_tx_seq();
let packet = {
let mut builder = session.thread_local_pool().get();
builder.build_subprotocol(stream_id, seq, &events, PacketFlags::NONE, subprotocol)
};
let _ = socket.send_to(&packet, addr).await;
});
}
#[cfg(feature = "redex")]
#[allow(clippy::too_many_arguments)]
fn sensing_candidate_snapshot_from_parts(
capability_fold: &super::behavior::fold::Fold<super::behavior::fold::CapabilityFold>,
proximity_graph: &ProximityGraph,
router: &NetRouter,
peers: &DashMap<u64, PeerInfo>,
peer_entity_ids: &DashMap<u64, EntityId>,
local_node_id: u64,
local_entity_root: sensing::AudienceScopeCommitment,
local_owner_root: &sensing::AudienceScopeCommitment,
capability_id: &sensing::CapabilityId,
) -> Vec<sensing::CandidateProvider> {
let declarers = sensing::extract_declarers(capability_fold, capability_id, |node_id| {
if node_id == local_node_id {
Some(local_entity_root)
} else {
peer_entity_ids
.get(&node_id)
.map(|entry| sensing::AudienceScopeCommitment::owner_root(entry.value()))
}
});
sensing::build_candidate_snapshot(
&declarers,
local_owner_root,
|node_id| sensing::proximity_route_estimate(proximity_graph, node_id),
|node_id| {
node_id == local_node_id
|| peers.contains_key(&node_id)
|| router.routing_table().lookup(node_id).is_some()
},
)
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum SensingRegistrationError {
Disabled,
Scope(sensing::ScopeError),
Interval {
requested: Duration,
max: Duration,
},
ZeroTtl,
AtCapacity,
}
impl std::fmt::Display for SensingRegistrationError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Disabled => f.write_str("sensing coalescing is disabled on this node"),
Self::Scope(error) => write!(f, "sensing scope validation refused: {error}"),
Self::Interval { requested, max } => write!(
f,
"sample interval {requested:?} out of bounds (0 < D <= {max:?})"
),
Self::ZeroTtl => f.write_str("zero soft-state ttl — the row would be dead on arrival"),
Self::AtCapacity => {
f.write_str("origin at live-stream capacity — registration rolled back")
}
}
}
}
impl std::error::Error for SensingRegistrationError {}
pub struct MeshNode {
identity: Arc<EntityKeypair>,
static_keypair: StaticKeypair,
node_id: u64,
config: MeshNodeConfig,
socket: Arc<NetSocket>,
peers: Arc<DashMap<u64, PeerInfo>>,
addr_to_node: Arc<DashMap<SocketAddr, u64>>,
router: Arc<NetRouter>,
failure_detector: Arc<FailureDetector>,
inbound: InboundQueues,
#[cfg(feature = "cortex")]
rpc_inbound_dispatchers: Arc<
DashMap<
u16,
Vec<(
ChannelHash,
crate::adapter::net::cortex::RpcInboundDispatcher,
)>,
>,
>,
#[cfg(feature = "cortex")]
rpc_client_pending: Arc<crate::adapter::net::cortex::RpcClientPending>,
#[cfg(feature = "cortex")]
rpc_round_robin_cursor: Arc<std::sync::atomic::AtomicU64>,
#[cfg(feature = "cortex")]
rpc_reply_subscriptions: Arc<dashmap::DashMap<(u64, u64), Arc<str>>>,
#[cfg(feature = "cortex")]
rpc_local_services: Arc<LocalServiceRegistry>,
#[cfg(feature = "tool")]
tool_registry: Arc<crate::adapter::net::cortex::tool::ToolMetadataRegistry>,
#[cfg(feature = "cortex")]
rpc_metrics: Arc<crate::adapter::net::mesh_rpc_metrics::RpcMetricsRegistry>,
#[cfg(feature = "cortex")]
rpc_route_cache: Arc<DashMap<String, Arc<RpcRoute>>>,
#[cfg(feature = "cortex")]
rpc_observer: Arc<ArcSwapOption<crate::adapter::net::cortex::rpc_observer::RpcObserverHandle>>,
#[cfg(feature = "cortex")]
cancel_registry: Arc<crate::adapter::net::cancel_registry::CancelRegistry>,
migration_handler: Arc<ArcSwapOption<MigrationSubprotocolHandler>>,
pending_handshakes: Arc<DashMap<u64, PendingHandshake>>,
pending_direct_initiators: Arc<DashMap<SocketAddr, oneshot::Sender<Bytes>>>,
proximity_graph: Arc<ProximityGraph>,
reroute_policy: Arc<ReroutePolicy>,
peer_addrs: Arc<DashMap<u64, SocketAddr>>,
partition_filter: PartitionFilter,
roster: Arc<SubscriberRoster>,
channel_configs: Option<Arc<ChannelConfigRegistry>>,
pending_membership_acks: Arc<DashMap<u64, (u64, oneshot::Sender<MembershipAck>)>>,
#[cfg(feature = "nat-traversal")]
pending_reflex_probes: Arc<DashMap<u64, (u64, oneshot::Sender<std::net::SocketAddr>)>>,
#[cfg(feature = "nat-traversal")]
pending_punch_introduces: PendingPunchIntroduces,
#[cfg(feature = "nat-traversal")]
pending_punch_acks: PendingPunchAcks,
#[cfg(feature = "nat-traversal")]
next_waiter_gen: Arc<std::sync::atomic::AtomicU64>,
#[cfg(feature = "nat-traversal")]
next_punch_id: Arc<std::sync::atomic::AtomicU32>,
#[cfg(feature = "nat-traversal")]
punch_observers: Arc<
DashMap<
SocketAddr,
(
u64,
oneshot::Sender<super::traversal::rendezvous::Keepalive>,
),
>,
>,
#[cfg(feature = "nat-traversal")]
rendezvous_budgets: Arc<RendezvousBudgets>,
#[cfg(feature = "nat-traversal")]
upgrade_cache: Arc<DashMap<u64, UpgradeCacheEntry>>,
#[cfg(feature = "nat-traversal")]
nat_classifying: std::sync::atomic::AtomicBool,
#[cfg(feature = "nat-traversal")]
nat_class: Arc<std::sync::atomic::AtomicU8>,
#[cfg(feature = "nat-traversal")]
reflex_addr: Arc<ArcSwapOption<SocketAddr>>,
#[cfg(feature = "nat-traversal")]
reflex_override_active: Arc<std::sync::atomic::AtomicBool>,
#[cfg(feature = "nat-traversal")]
traversal_publish_mu: Arc<parking_lot::Mutex<()>>,
#[cfg(feature = "nat-traversal")]
traversal_config: super::traversal::TraversalConfig,
#[cfg(feature = "nat-traversal")]
traversal_stats: Arc<super::traversal::TraversalStats>,
capability_fold: Arc<super::behavior::fold::Fold<super::behavior::fold::CapabilityFold>>,
#[cfg(feature = "dataforts")]
capability_set_cache: Arc<super::behavior::fold::capability_bridge::CapabilitySetCache>,
reservation_fold: Arc<super::behavior::fold::Fold<super::behavior::fold::ReservationFold>>,
island_fold: Arc<super::behavior::fold::Fold<super::behavior::fold::IslandTopologyFold>>,
liveness_down: Arc<arc_swap::ArcSwap<std::collections::HashSet<super::behavior::fold::NodeId>>>,
seen_announcements: Arc<DashMap<(u64, u64, bool), std::time::Instant>>,
announce_gate: Arc<parking_lot::Mutex<AnnounceGate>>,
announce_mu: parking_lot::Mutex<()>,
local_caps_changed: Arc<tokio::sync::watch::Sender<u64>>,
event_pingwave_gate: Arc<parking_lot::Mutex<EventPingwaveGate>>,
route_withdraw_seq: Arc<AtomicU64>,
route_withdraw_damper: Arc<DashMap<(u64, Option<u64>), std::time::Instant>>,
route_withdraw_gate: Arc<WithdrawalSeqGate>,
route_withdraw_cascades_inflight: Arc<AtomicUsize>,
sensing_interest_table: Arc<parking_lot::Mutex<sensing::InterestTable>>,
sensing_counters: Arc<sensing::SensingCounters>,
sensing_over_cap: Arc<AtomicU64>,
sensing_local_root: sensing::AudienceScopeCommitment,
sensing_upstream_damper: Arc<DashMap<(u64, [u8; 32]), std::time::Instant>>,
#[cfg(feature = "redex")]
sensing_leader: Arc<parking_lot::Mutex<Option<sensing::SensingLeader>>>,
#[cfg(feature = "redex")]
sensing_fold_coalescer: Arc<DashMap<[u8; 32], SensingFoldGate>>,
sensing_emitter: Arc<parking_lot::Mutex<Option<sensing::OriginEmitter>>>,
sensing_emitter_notify: Arc<tokio::sync::Notify>,
sensing_evaluators:
Arc<DashMap<sensing::CapabilityId, Arc<dyn sensing::ReadinessEvaluator + Send + Sync>>>,
sensing_observer_gate: Arc<parking_lot::Mutex<sensing::IncarnationSeqGate>>,
sensing_overlay_changed: Arc<tokio::sync::watch::Sender<u64>>,
sensing_capability_interests: CapabilityInterestExpectations,
sensing_observations: Arc<parking_lot::Mutex<SensingObservations>>,
local_announcement: Arc<ArcSwapOption<CapabilityAnnouncement>>,
user_caps: Arc<parking_lot::RwLock<Option<CapabilitySet>>>,
#[cfg(feature = "redex")]
replication_inbound_router:
Arc<parking_lot::RwLock<Option<Arc<dyn super::redex::ReplicationInboundRouter>>>>,
#[cfg(feature = "meshdb")]
meshdb_inbound_router:
Arc<parking_lot::RwLock<Option<Arc<dyn super::behavior::meshdb::MeshDbInboundRouter>>>>,
fold_router:
Arc<parking_lot::RwLock<Option<Arc<dyn super::behavior::fold::FoldChannelRouter>>>>,
fold_generations: Arc<DashMap<(u16, u64), FoldGenerationEntry>>,
#[cfg(feature = "dataforts")]
greedy_observer: Arc<parking_lot::RwLock<Option<Arc<dyn super::dataforts::GreedyObserver>>>>,
#[cfg(feature = "dataforts")]
blob_transfer_engine:
Arc<parking_lot::RwLock<Option<Arc<super::dataforts::blob::transfer::BlobTransferEngine>>>>,
capability_version: Arc<AtomicU64>,
local_subnet: SubnetId,
local_subnet_policy: Option<Arc<SubnetPolicy>>,
peer_subnets: Arc<DashMap<u64, SubnetId>>,
subnet_gateway: Option<Arc<SubnetGateway>>,
#[cfg(feature = "cortex")]
aggregator_registry: Option<Arc<super::behavior::aggregator::AggregatorRegistry>>,
peer_entity_ids: Arc<DashMap<u64, EntityId>>,
origin_hash_to_node: Arc<DashMap<u64, u64>>,
session_id_to_node: Arc<DashMap<u64, u64>>,
token_cache: Option<Arc<TokenCache>>,
subscriber_chains: Arc<DashMap<(u64, ChannelHash), RetainedChain>>,
published_chains: Arc<DashMap<ChannelHash, TokenChain>>,
auth_guard: Arc<AuthGuard>,
auth_failures: Arc<DashMap<u64, AuthFailureState>>,
tasks: Arc<tokio::sync::Mutex<Vec<JoinHandle<()>>>>,
shutdown: Arc<AtomicBool>,
shutdown_notify: Arc<Notify>,
pending_stream_grants: Arc<parking_lot::Mutex<HashMap<(u64, u64), PendingStreamGrant>>>,
pending_stream_grants_notify: Arc<Notify>,
control_stats: Arc<ControlPlaneStats>,
ack_ranges_peer_cache: Arc<DashMap<u64, (bool, Instant)>>,
started: AtomicBool,
self_weak: Arc<std::sync::OnceLock<std::sync::Weak<MeshNode>>>,
accept_in_flight: std::sync::atomic::AtomicUsize,
}
impl MeshNode {
pub fn public_key(&self) -> &[u8; 32] {
&self.static_keypair.public
}
pub fn is_shutdown(&self) -> bool {
self.shutdown.load(Ordering::Acquire)
}
pub async fn new(
identity: EntityKeypair,
config: MeshNodeConfig,
) -> Result<Self, AdapterError> {
let node_id = identity.node_id();
let static_keypair = StaticKeypair::generate();
let socket = NetSocket::with_config(config.bind_addr, config.socket_buffers)
.await
.map_err(|e| AdapterError::Connection(format!("bind failed: {}", e)))?;
let socket = Arc::new(socket);
let router_config = RouterConfig {
local_id: node_id,
bind_addr: SocketAddr::from(([127, 0, 0, 1], 0)),
max_queue_depth: config.max_queue_depth,
fair_quantum: config.fair_quantum,
..Default::default()
};
let router = NetRouter::new(router_config)
.await
.map_err(|e| AdapterError::Connection(format!("router bind failed: {}", e)))?;
let router = Arc::new(router);
router
.routing_table()
.set_max_route_age(config.session_timeout.saturating_mul(3));
let peer_addrs: Arc<DashMap<u64, SocketAddr>> = Arc::new(DashMap::new());
let peers: Arc<DashMap<u64, PeerInfo>> = Arc::new(DashMap::new());
let addr_to_node: Arc<DashMap<SocketAddr, u64>> = Arc::new(DashMap::new());
let graph_node_id = node_id_to_graph_id(node_id);
let proximity_graph = Arc::new(ProximityGraph::new(
graph_node_id,
ProximityConfig::default(),
));
let reroute_policy = Arc::new(
ReroutePolicy::new(router.routing_table().clone(), peer_addrs.clone())
.with_proximity_graph(proximity_graph.clone()),
);
let roster: Arc<SubscriberRoster> = Arc::new(SubscriberRoster::new());
let peer_subnets: Arc<DashMap<u64, SubnetId>> = Arc::new(DashMap::new());
let peer_entity_ids: Arc<DashMap<u64, EntityId>> = Arc::new(DashMap::new());
let origin_hash_to_node: Arc<DashMap<u64, u64>> = Arc::new(DashMap::new());
let session_id_to_node: Arc<DashMap<u64, u64>> = Arc::new(DashMap::new());
let capability_fold: Arc<
super::behavior::fold::Fold<super::behavior::fold::CapabilityFold>,
> = Arc::new(super::behavior::fold::Fold::new());
#[cfg(feature = "dataforts")]
let capability_set_cache =
Arc::new(super::behavior::fold::capability_bridge::CapabilitySetCache::new());
let reservation_fold: Arc<
super::behavior::fold::Fold<super::behavior::fold::ReservationFold>,
> = Arc::new(super::behavior::fold::Fold::new());
let island_fold: Arc<
super::behavior::fold::Fold<super::behavior::fold::IslandTopologyFold>,
> = Arc::new(super::behavior::fold::Fold::new());
let fold_registry = Arc::new(super::behavior::fold::FoldRegistry::new());
fold_registry.register(capability_fold.clone());
fold_registry.register(reservation_fold.clone());
fold_registry.register(island_fold.clone());
let fold_router: Arc<
parking_lot::RwLock<Option<Arc<dyn super::behavior::fold::FoldChannelRouter>>>,
> = Arc::new(parking_lot::RwLock::new(Some(
fold_registry.clone() as Arc<dyn super::behavior::fold::FoldChannelRouter>
)));
let rp_failure = reroute_policy.clone();
let rp_recovery = reroute_policy.clone();
let roster_failure = roster.clone();
let peer_subnets_failure = peer_subnets.clone();
let peer_entity_ids_failure = peer_entity_ids.clone();
let origin_hash_to_node_failure = origin_hash_to_node.clone();
let capability_fold_failure = capability_fold.clone();
let subscriber_chains: Arc<DashMap<(u64, ChannelHash), RetainedChain>> =
Arc::new(DashMap::new());
let subscriber_chains_failure = subscriber_chains.clone();
let partition_filter: PartitionFilter = Arc::new(dashmap::DashSet::new());
let event_pingwave_gate: Arc<parking_lot::Mutex<EventPingwaveGate>> =
Arc::new(parking_lot::Mutex::new(EventPingwaveGate::default()));
let event_pingwave_min_gap = config.event_pingwave_min_gap;
let event_pingwave_gate_recovery = event_pingwave_gate.clone();
let proximity_graph_recovery = proximity_graph.clone();
let socket_recovery = socket.clone();
let peers_recovery = peers.clone();
let partition_filter_recovery = partition_filter.clone();
let route_withdraw_seq: Arc<AtomicU64> = Arc::new(AtomicU64::new(0));
let route_withdraw_damper: Arc<DashMap<(u64, Option<u64>), std::time::Instant>> =
Arc::new(DashMap::new());
let enable_route_withdraw = config.enable_route_withdraw;
let route_withdraw_seq_failure = route_withdraw_seq.clone();
let route_withdraw_damper_failure = route_withdraw_damper.clone();
let socket_failure = socket.clone();
let peers_failure = peers.clone();
let partition_filter_failure = partition_filter.clone();
let proximity_graph_failure = proximity_graph.clone();
let sensing_local_root = config
.sensing_owner_root
.unwrap_or_else(|| sensing::AudienceScopeCommitment::owner_root(identity.entity_id()));
let sensing_interest_table = Arc::new(parking_lot::Mutex::new(
sensing::InterestTable::new(config.max_interests_per_peer),
));
let sensing_cadence_floor = {
let floor = if config.attestation_cadence_floor.is_zero() {
sensing::DEFAULT_ATTESTATION_CADENCE_FLOOR
} else {
config.attestation_cadence_floor
};
if config.sensing_interest_ttl.is_zero() {
floor
} else {
floor.min(config.sensing_interest_ttl)
}
};
let sensing_emitter = Arc::new(parking_lot::Mutex::new(
match (config.enable_sensing_coalescing, config.sensing_incarnation) {
(true, Some(incarnation)) => Some(sensing::OriginEmitter::new(
node_id,
incarnation,
sensing_cadence_floor,
)),
_ => None,
},
));
let sensing_observations: Arc<parking_lot::Mutex<SensingObservations>> =
Arc::new(parking_lot::Mutex::new(SensingObservations::default()));
let sensing_overlay_changed = Arc::new(tokio::sync::watch::channel(0u64).0);
#[cfg(feature = "redex")]
let sensing_leader: Arc<parking_lot::Mutex<Option<sensing::SensingLeader>>> =
Arc::new(parking_lot::Mutex::new(None));
let sensing_table_failure = sensing_interest_table.clone();
let sensing_observations_failure = sensing_observations.clone();
let sensing_overlay_failure = sensing_overlay_changed.clone();
let sensing_emitter_failure = sensing_emitter.clone();
#[cfg(feature = "redex")]
let sensing_leader_failure = sensing_leader.clone();
let sensing_router_failure = router.clone();
let sensing_addr_to_node_failure = addr_to_node.clone();
let enable_sensing_failure = config.enable_sensing_coalescing;
let sensing_overlay_recovery = sensing_overlay_changed.clone();
let enable_sensing_recovery = config.enable_sensing_coalescing;
#[cfg(feature = "redex")]
let sensing_local_root_failure = sensing_local_root;
#[cfg(feature = "redex")]
let sensing_interest_ttl_failure = config.sensing_interest_ttl;
let local_node_id_failure = node_id;
#[cfg(feature = "nat-traversal")]
let upgrade_cache: Arc<DashMap<u64, UpgradeCacheEntry>> = Arc::new(DashMap::new());
#[cfg(feature = "nat-traversal")]
let upgrade_cache_failure = upgrade_cache.clone();
let failure_detector = FailureDetector::with_config(FailureDetectorConfig {
timeout: config.session_timeout,
miss_threshold: 3,
suspicion_threshold: 2,
cleanup_interval: Duration::from_secs(60),
})
.on_failure(move |node_id| {
if enable_sensing_failure {
let now = Instant::now();
let failed_addr = peers_failure.get(&node_id).map(|p| p.value().addr);
let providers: std::collections::HashSet<u64> = {
let observations = sensing_observations_failure.lock();
observations
.upstream
.keys()
.chain(observations.consumer_cells.keys())
.map(|key| key.provider)
.collect()
};
#[cfg(feature = "redex")]
let providers = {
let mut providers = providers;
if let Some(leader) = sensing_leader_failure.lock().as_ref() {
providers.extend(leader.relay.branch_providers());
}
providers
};
for provider in providers {
let through_failed = provider == node_id
|| match sensing_router_failure.routing_table().lookup(provider) {
Some(next_hop) => failed_addr == Some(next_hop),
None => !sensing_live_direct_session(
&peers_failure,
&sensing_addr_to_node_failure,
None,
provider,
),
};
if !through_failed {
continue;
}
disrupt_sensing_provider(
&sensing_table_failure,
&sensing_observations_failure,
&sensing_overlay_failure,
provider,
sensing::DisruptReason::PathFailed,
);
#[cfg(feature = "redex")]
if let Some(leader) = sensing_leader_failure.lock().as_mut() {
leader
.relay
.disrupt_provider(provider, sensing::DisruptReason::PathFailed);
}
}
remove_sensing_downstream(
&sensing_table_failure,
&sensing_observations_failure,
&sensing_emitter_failure,
&socket_failure,
&peers_failure,
&sensing_addr_to_node_failure,
&sensing_router_failure,
&partition_filter_failure,
local_node_id_failure,
node_id,
now,
);
#[cfg(feature = "redex")]
remove_sensing_leader_consumer(
&sensing_leader_failure,
&sensing_table_failure,
&sensing_observations_failure,
&sensing_emitter_failure,
&socket_failure,
&peers_failure,
&sensing_addr_to_node_failure,
&sensing_router_failure,
&partition_filter_failure,
local_node_id_failure,
sensing_local_root_failure,
sensing_interest_ttl_failure,
node_id,
now,
);
sensing_overlay_failure.send_modify(|generation| {
*generation = generation.wrapping_add(1);
});
}
rp_failure.on_failure(node_id);
let removed = roster_failure.remove_peer(node_id);
if !removed.is_empty() {
tracing::debug!(
node_id = format!("{:#x}", node_id),
channels = removed.len(),
"roster: evicted failed peer from channels"
);
}
peer_subnets_failure.remove(&node_id);
let removed_entity_id = peer_entity_ids_failure.remove(&node_id).map(|(_, eid)| eid);
if let Some(eid) = removed_entity_id {
let origin_hash = eid.origin_hash();
origin_hash_to_node_failure
.remove_if(&origin_hash, |_, claimant| *claimant == node_id);
}
capability_fold_failure.evict_node(node_id, "failure-detector");
subscriber_chains_failure.retain(|(nid, _), _| *nid != node_id);
if enable_route_withdraw {
spawn_route_withdrawal_flood(
&route_withdraw_seq_failure,
&route_withdraw_damper_failure,
&socket_failure,
&peers_failure,
&partition_filter_failure,
node_id,
None,
);
}
proximity_graph_failure.remove_edge(
node_id_to_graph_id(local_node_id_failure),
node_id_to_graph_id(node_id),
);
#[cfg(feature = "nat-traversal")]
upgrade_cache_failure.remove(&node_id);
})
.on_recovery(move |node_id| {
rp_recovery.on_recovery(node_id);
spawn_event_pingwave(
&event_pingwave_gate_recovery,
event_pingwave_min_gap,
&proximity_graph_recovery,
&socket_recovery,
&peers_recovery,
&partition_filter_recovery,
false,
);
if enable_sensing_recovery {
sensing_overlay_recovery.send_modify(|generation| {
*generation = generation.wrapping_add(1);
});
}
});
let pending_handshakes: Arc<DashMap<u64, PendingHandshake>> = Arc::new(DashMap::new());
let pending_direct_initiators: Arc<DashMap<SocketAddr, oneshot::Sender<Bytes>>> =
Arc::new(DashMap::new());
let local_subnet = config.subnet;
let local_subnet_policy = config.subnet_policy.clone();
#[cfg(feature = "nat-traversal")]
let initial_reflex_override = config.reflex_override;
let local_caps_changed = Arc::new(tokio::sync::watch::channel(0u64).0);
Ok(Self {
identity: Arc::new(identity),
static_keypair,
node_id,
config,
socket,
peers,
addr_to_node,
router,
failure_detector: Arc::new(failure_detector),
inbound: Arc::new(DashMap::new()),
#[cfg(feature = "cortex")]
rpc_inbound_dispatchers: Arc::new(DashMap::new()),
#[cfg(feature = "cortex")]
rpc_client_pending: Arc::new(crate::adapter::net::cortex::RpcClientPending::new()),
#[cfg(feature = "cortex")]
rpc_round_robin_cursor: Arc::new(std::sync::atomic::AtomicU64::new(0)),
#[cfg(feature = "cortex")]
rpc_reply_subscriptions: Arc::new(dashmap::DashMap::new()),
#[cfg(feature = "cortex")]
rpc_local_services: Arc::new(LocalServiceRegistry::new(local_caps_changed.clone())),
#[cfg(feature = "tool")]
tool_registry: Arc::new(
crate::adapter::net::cortex::tool::ToolMetadataRegistry::with_change_signal(
local_caps_changed.clone(),
),
),
local_caps_changed,
#[cfg(feature = "cortex")]
rpc_metrics: Arc::new(crate::adapter::net::mesh_rpc_metrics::RpcMetricsRegistry::new()),
#[cfg(feature = "cortex")]
rpc_route_cache: Arc::new(DashMap::new()),
#[cfg(feature = "cortex")]
rpc_observer: Arc::new(ArcSwapOption::empty()),
#[cfg(feature = "cortex")]
cancel_registry: Arc::new(crate::adapter::net::cancel_registry::CancelRegistry::new()),
migration_handler: Arc::new(ArcSwapOption::empty()),
pending_handshakes,
pending_direct_initiators,
proximity_graph,
reroute_policy,
peer_addrs,
partition_filter,
event_pingwave_gate,
route_withdraw_seq,
route_withdraw_damper,
route_withdraw_gate: Arc::new(WithdrawalSeqGate::new()),
route_withdraw_cascades_inflight: Arc::new(AtomicUsize::new(0)),
sensing_interest_table,
sensing_counters: Arc::new(sensing::SensingCounters::default()),
sensing_over_cap: Arc::new(AtomicU64::new(0)),
sensing_local_root,
sensing_upstream_damper: Arc::new(DashMap::new()),
#[cfg(feature = "redex")]
sensing_leader,
#[cfg(feature = "redex")]
sensing_fold_coalescer: Arc::new(DashMap::new()),
sensing_emitter,
sensing_emitter_notify: Arc::new(tokio::sync::Notify::new()),
sensing_evaluators: Arc::new(DashMap::new()),
sensing_observer_gate: Arc::new(parking_lot::Mutex::new(
sensing::IncarnationSeqGate::new(),
)),
sensing_overlay_changed,
sensing_capability_interests: Arc::new(parking_lot::Mutex::new(HashMap::new())),
sensing_observations,
roster,
channel_configs: None,
pending_membership_acks: Arc::new(DashMap::new()),
#[cfg(feature = "nat-traversal")]
pending_reflex_probes: Arc::new(DashMap::new()),
#[cfg(feature = "nat-traversal")]
pending_punch_introduces: Arc::new(DashMap::new()),
#[cfg(feature = "nat-traversal")]
pending_punch_acks: Arc::new(DashMap::new()),
#[cfg(feature = "nat-traversal")]
next_waiter_gen: Arc::new(std::sync::atomic::AtomicU64::new(1)),
#[cfg(feature = "nat-traversal")]
next_punch_id: Arc::new(std::sync::atomic::AtomicU32::new(1)),
#[cfg(feature = "nat-traversal")]
nat_classifying: std::sync::atomic::AtomicBool::new(false),
#[cfg(feature = "nat-traversal")]
punch_observers: Arc::new(DashMap::new()),
#[cfg(feature = "nat-traversal")]
rendezvous_budgets: Arc::new(RendezvousBudgets::default()),
#[cfg(feature = "nat-traversal")]
upgrade_cache,
#[cfg(feature = "nat-traversal")]
nat_class: Arc::new(std::sync::atomic::AtomicU8::new(
if initial_reflex_override.is_some() {
super::traversal::classify::NatClass::Open.as_u8()
} else {
super::traversal::classify::NatClass::Unknown.as_u8()
},
)),
#[cfg(feature = "nat-traversal")]
reflex_addr: Arc::new(match initial_reflex_override {
Some(addr) => ArcSwapOption::from_pointee(addr),
None => ArcSwapOption::empty(),
}),
#[cfg(feature = "nat-traversal")]
reflex_override_active: Arc::new(std::sync::atomic::AtomicBool::new(
initial_reflex_override.is_some(),
)),
#[cfg(feature = "nat-traversal")]
traversal_publish_mu: Arc::new(parking_lot::Mutex::new(())),
#[cfg(feature = "nat-traversal")]
traversal_config: super::traversal::TraversalConfig::default(),
#[cfg(feature = "nat-traversal")]
traversal_stats: Arc::new(super::traversal::TraversalStats::new()),
capability_fold,
#[cfg(feature = "dataforts")]
capability_set_cache,
reservation_fold,
island_fold,
liveness_down: Arc::new(arc_swap::ArcSwap::from_pointee(
std::collections::HashSet::new(),
)),
seen_announcements: Arc::new(DashMap::new()),
announce_mu: parking_lot::Mutex::new(()),
announce_gate: Arc::new(parking_lot::Mutex::new(AnnounceGate {
last_broadcast_at: None,
deferred_scheduled: false,
deferral_generation: 0,
})),
local_announcement: Arc::new(ArcSwapOption::empty()),
user_caps: Arc::new(parking_lot::RwLock::new(None)),
#[cfg(feature = "redex")]
replication_inbound_router: Arc::new(parking_lot::RwLock::new(None)),
#[cfg(feature = "meshdb")]
meshdb_inbound_router: Arc::new(parking_lot::RwLock::new(None)),
fold_router,
fold_generations: Arc::new(DashMap::new()),
#[cfg(feature = "dataforts")]
greedy_observer: Arc::new(parking_lot::RwLock::new(None)),
#[cfg(feature = "dataforts")]
blob_transfer_engine: Arc::new(parking_lot::RwLock::new(None)),
capability_version: Arc::new(AtomicU64::new(0)),
local_subnet,
local_subnet_policy,
peer_subnets,
subnet_gateway: None,
#[cfg(feature = "cortex")]
aggregator_registry: None,
peer_entity_ids,
origin_hash_to_node,
session_id_to_node,
token_cache: None,
subscriber_chains,
published_chains: Arc::new(DashMap::new()),
auth_guard: Arc::new(AuthGuard::new()),
auth_failures: Arc::new(DashMap::new()),
tasks: Arc::new(tokio::sync::Mutex::new(Vec::new())),
shutdown: Arc::new(AtomicBool::new(false)),
shutdown_notify: Arc::new(Notify::new()),
pending_stream_grants: Arc::new(parking_lot::Mutex::new(HashMap::new())),
pending_stream_grants_notify: Arc::new(Notify::new()),
control_stats: Arc::new(ControlPlaneStats::default()),
ack_ranges_peer_cache: Arc::new(DashMap::new()),
started: AtomicBool::new(false),
self_weak: Arc::new(std::sync::OnceLock::new()),
accept_in_flight: std::sync::atomic::AtomicUsize::new(0),
})
}
pub fn node_id(&self) -> u64 {
self.node_id
}
pub fn origin_hash(&self) -> u64 {
self.identity.entity_id().origin_hash()
}
pub fn auth_guard(&self) -> &Arc<AuthGuard> {
&self.auth_guard
}
pub fn token_cache(&self) -> Option<&Arc<TokenCache>> {
self.token_cache.as_ref()
}
pub fn subscriber_chain_count(&self) -> usize {
self.subscriber_chains.len()
}
pub fn entity_id(&self) -> &EntityId {
self.identity.entity_id()
}
pub fn entity_keypair(&self) -> &EntityKeypair {
&self.identity
}
pub fn peer_entity_id(&self, node_id: u64) -> Option<EntityId> {
self.peer_entity_ids
.get(&node_id)
.map(|e| e.value().clone())
}
pub fn peer_addr(&self, node_id: u64) -> Option<SocketAddr> {
self.peers.get(&node_id).map(|e| e.value().addr)
}
pub fn register_sensing_interest(
&self,
spec: &sensing::InterestSpec,
provider: u64,
requested_sample_interval: Duration,
soft_state_ttl: Duration,
) -> Result<sensing::RegisterOutcome, SensingRegistrationError> {
if !self.config.enable_sensing_coalescing {
return Err(SensingRegistrationError::Disabled);
}
if !sensing_interval_in_bounds(requested_sample_interval, self.config.sensing_interest_ttl)
{
return Err(SensingRegistrationError::Interval {
requested: requested_sample_interval,
max: self.config.sensing_interest_ttl,
});
}
if soft_state_ttl.is_zero() {
return Err(SensingRegistrationError::ZeroTtl);
}
let proven_root = sensing::validate_subscriber_scope(
&self.sensing_local_root,
&self.sensing_local_root,
&self.sensing_local_root,
&spec.audience,
&self.sensing_counters,
)
.map_err(SensingRegistrationError::Scope)?;
let key = sensing::ProviderInterestKey::new(spec.key(), provider);
let ttl = soft_state_ttl.min(self.config.sensing_interest_ttl);
let now = Instant::now();
let (outcome, aggregate) = {
let mut table = self.sensing_interest_table.lock();
let outcome = table.register(
&key,
sensing::DownstreamId::Local,
requested_sample_interval,
ttl,
proven_root,
now,
);
(outcome, table.aggregate(&key, now))
};
if matches!(outcome, sensing::RegisterOutcome::Registered(_)) {
let mut observations = self.sensing_observations.lock();
observations.update_upstream_interval(&key, aggregate);
observations.update_consumer_interval(&key, requested_sample_interval);
}
if matches!(outcome, sensing::RegisterOutcome::Registered(_)) && provider == self.node_id {
if let Some(strictest) = aggregate {
let refusal = {
let mut slot = self.sensing_emitter.lock();
match slot.as_mut() {
None => None,
Some(emitter) => emitter.register(spec, strictest, now).err(),
}
};
match refusal {
None => self.sensing_emitter_notify.notify_one(),
Some(sensing::StreamRefusal::AtCapacity) => {
let _ = self.sensing_interest_table.lock().deregister(
&key.interest.interest_digest,
Some(provider),
sensing::DownstreamId::Local,
now,
);
return Err(SensingRegistrationError::AtCapacity);
}
Some(sensing::StreamRefusal::Cadence(refusal)) => {
self.sensing_counters
.cadence_refusals
.fetch_add(1, Ordering::Relaxed);
let stamp = self
.sensing_emitter
.lock()
.as_ref()
.map(|emitter| emitter.stamp());
let partition = self.sensing_interest_table.lock().on_refusal(
&key,
refusal.minimum_supported,
now,
);
{
let mut slot = self.sensing_emitter.lock();
if let Some(emitter) = slot.as_mut() {
match partition.upstream {
sensing::UpstreamAction::Register { strictest } => {
let _ = emitter.register(spec, strictest, now);
}
sensing::UpstreamAction::Deregister => {
if let Some(stamp) = stamp {
emitter.retire_if_stale(
&key.interest.interest_digest,
stamp,
);
}
}
sensing::UpstreamAction::None => {}
}
}
}
self.sensing_emitter_notify.notify_one();
return Ok(sensing::RegisterOutcome::RefusedByCachedFloor {
minimum_supported: refusal.minimum_supported,
});
}
}
}
} else if matches!(outcome, sensing::RegisterOutcome::Registered(_)) {
{
let mut observations = self.sensing_observations.lock();
let slot_key = (key.clone(), sensing::DownstreamId::Local);
if !observations.slots.contains_key(&slot_key) {
if let Some(cached) = observations.latest.get(&key).cloned() {
observations.slots.insert(
slot_key,
SensingDeliverySlot {
last_status: Some(cached.status),
last_delivered: Some((cached.origin_incarnation, cached.seq)),
next_due: now + requested_sample_interval,
pending: false,
},
);
let moved = observations.feed_consumer_cell(
&key,
&cached,
false,
requested_sample_interval,
self.config.continuity_factor,
now,
);
drop(observations);
if moved {
self.sensing_overlay_changed.send_modify(|generation| {
*generation = generation.wrapping_add(1);
});
}
}
}
}
if let Some(strictest) = aggregate {
if sensing_upstream_damper_admits(
&self.sensing_upstream_damper,
provider,
*key.interest.interest_digest.as_bytes(),
sensing_effective_min_gap(ttl),
) {
let frame = sensing::SensingInterestFrame::provider_registration(
spec, provider, strictest, ttl,
);
if let Ok(bytes) = sensing::encode_interest_frame(&frame) {
spawn_sensing_frame_send(
&self.socket,
&self.peers,
&self.addr_to_node,
&self.router,
&self.partition_filter,
self.node_id,
provider,
sensing::SUBPROTOCOL_SENSING_INTEREST as u64,
sensing::SUBPROTOCOL_SENSING_INTEREST,
bytes,
);
}
}
}
}
Ok(outcome)
}
pub fn register_capability_interest(
&self,
spec: &sensing::InterestSpec,
leader: u64,
requested_sample_interval: Duration,
soft_state_ttl: Duration,
) -> Result<(), SensingRegistrationError> {
if !self.config.enable_sensing_coalescing {
return Err(SensingRegistrationError::Disabled);
}
if !sensing_interval_in_bounds(requested_sample_interval, self.config.sensing_interest_ttl)
{
return Err(SensingRegistrationError::Interval {
requested: requested_sample_interval,
max: self.config.sensing_interest_ttl,
});
}
if soft_state_ttl.is_zero() {
return Err(SensingRegistrationError::ZeroTtl);
}
sensing::validate_subscriber_scope(
&self.sensing_local_root,
&self.sensing_local_root,
&self.sensing_local_root,
&spec.audience,
&self.sensing_counters,
)
.map_err(SensingRegistrationError::Scope)?;
let ttl = soft_state_ttl.min(self.config.sensing_interest_ttl);
let key = spec.key();
{
let mut interests = self.sensing_capability_interests.lock();
if !interests.contains_key(&key)
&& interests.len() >= self.config.max_interests_per_peer
{
return Err(SensingRegistrationError::AtCapacity);
}
interests.insert(
key.clone(),
CapabilityInterestExpectation {
requested_sample_interval,
expires_at: Instant::now() + ttl,
audience: spec.audience,
},
);
}
self.sensing_observations
.lock()
.update_consumer_intervals(&key, requested_sample_interval);
if sensing_upstream_damper_admits(
&self.sensing_upstream_damper,
leader,
*key.interest_digest.as_bytes(),
sensing_effective_min_gap(ttl),
) {
let frame = sensing::SensingInterestFrame::capability_registration(
spec,
requested_sample_interval,
ttl,
self.node_id,
);
if let Ok(bytes) = sensing::encode_interest_frame(&frame) {
spawn_sensing_frame_send(
&self.socket,
&self.peers,
&self.addr_to_node,
&self.router,
&self.partition_filter,
self.node_id,
leader,
sensing::SUBPROTOCOL_SENSING_INTEREST as u64,
sensing::SUBPROTOCOL_SENSING_INTEREST,
bytes,
);
}
}
Ok(())
}
pub fn register_readiness_evaluator(
&self,
capability_id: sensing::CapabilityId,
evaluator: Arc<dyn sensing::ReadinessEvaluator + Send + Sync>,
) {
self.sensing_evaluators.insert(capability_id, evaluator);
}
pub fn unregister_readiness_evaluator(&self, capability_id: &sensing::CapabilityId) -> bool {
self.sensing_evaluators.remove(capability_id).is_some()
}
pub fn notify_sensing_state_changed(&self, capability_id: &sensing::CapabilityId) {
let moved = {
let mut slot = self.sensing_emitter.lock();
match slot.as_mut() {
Some(emitter) => emitter.poke(capability_id, Instant::now()),
None => false,
}
};
if moved {
self.sensing_emitter_notify.notify_one();
}
}
pub fn sensing_origin_active(&self) -> bool {
self.sensing_emitter.lock().is_some()
}
pub fn sensing_live_streams(&self) -> usize {
self.sensing_emitter
.lock()
.as_ref()
.map(|emitter| emitter.live_streams())
.unwrap_or(0)
}
pub fn sensing_latest_attestation(
&self,
key: &sensing::ProviderInterestKey,
) -> Option<sensing::ReadinessAttestation> {
self.sensing_observations.lock().latest.get(key).cloned()
}
pub fn sensing_latest_refusal(
&self,
key: &sensing::ProviderInterestKey,
) -> Option<sensing::ReadinessAttestation> {
self.sensing_observations
.lock()
.refusals
.get(key)
.map(|(attestation, _)| attestation.clone())
}
pub fn sensing_observation_count(&self) -> usize {
self.sensing_observations.lock().latest.len()
}
pub fn sensing_provider_epoch_count(&self) -> usize {
self.sensing_observations.lock().provider_epochs.len()
}
pub fn sensing_upstream_continuity(
&self,
key: &sensing::ProviderInterestKey,
) -> Option<sensing::Continuity> {
self.sensing_observations
.lock()
.upstream
.get(key)
.map(sensing::ObservationCell::continuity)
}
pub fn sensing_projected(
&self,
key: &sensing::ProviderInterestKey,
) -> sensing::ProjectedReadiness {
self.sensing_observations
.lock()
.consumer_cells
.get(key)
.map(sensing::ObservationCell::projected)
.unwrap_or(sensing::ProjectedReadiness::Unknown)
}
pub fn sensing_branch_projections(
&self,
interest: &sensing::CapabilityInterestKey,
) -> Vec<(u64, sensing::ProjectedReadiness, Option<Duration>)> {
self.sensing_observations
.lock()
.consumer_cells
.iter()
.filter(|(key, _)| &key.interest == interest)
.map(|(key, cell)| {
(
key.provider,
cell.projected(),
cell.observation().and_then(|obs| obs.estimated_start),
)
})
.collect()
}
pub fn sensing_aggregate_view(
&self,
spec: &sensing::InterestSpec,
budget: &sensing::ConsumerLatencyBudget,
resolved_population: Option<&[u64]>,
) -> sensing::AggregateView {
let branches = self.sensing_branch_views(&spec.key(), resolved_population);
sensing::project_aggregate(
&spec.providers,
spec.result_mode,
budget,
&branches,
resolved_population.is_some(),
)
}
fn sensing_branch_views(
&self,
interest: &sensing::CapabilityInterestKey,
resolved_population: Option<&[u64]>,
) -> Vec<sensing::BranchView> {
let mut branches: Vec<sensing::BranchView> = self
.sensing_branch_projections(interest)
.into_iter()
.map(
|(provider, projection, estimated_start)| sensing::BranchView {
provider,
projection,
estimated_start,
route_estimate: sensing::proximity_route_estimate(
&self.proximity_graph,
provider,
),
},
)
.collect();
if let Some(expected) = resolved_population {
branches.retain(|branch| expected.contains(&branch.provider));
for provider in expected {
if !branches.iter().any(|branch| branch.provider == *provider) {
branches.push(sensing::BranchView {
provider: *provider,
projection: sensing::ProjectedReadiness::Unknown,
estimated_start: None,
route_estimate: sensing::proximity_route_estimate(
&self.proximity_graph,
*provider,
),
});
}
}
}
branches
}
pub fn sensing_readiness_overlay(
&self,
spec: &sensing::InterestSpec,
budget: &sensing::ConsumerLatencyBudget,
resolved_population: Option<&[u64]>,
) -> SensingReadinessOverlay {
let interest = spec.key();
let candidates: Vec<((u64, u64), sensing::ReadinessObservation)> = {
let observations = self.sensing_observations.lock();
let mut candidates: Vec<((u64, u64), sensing::ReadinessObservation)> = observations
.consumer_cells
.iter()
.filter(|(key, _)| {
key.interest == interest
&& resolved_population
.is_none_or(|expected| expected.contains(&key.provider))
})
.filter_map(|(key, cell)| {
cell.observation().map(|observation| {
(
(key.provider, observation.capability_generation),
*observation,
)
})
})
.collect();
candidates.sort_by_key(|(key, _)| *key);
candidates
};
SensingReadinessOverlay {
aggregate: self.sensing_aggregate_view(spec, budget, resolved_population),
candidates,
}
}
pub fn sensed_candidates(
&self,
spec: &sensing::InterestSpec,
budget: &sensing::ConsumerLatencyBudget,
resolved_population: Option<&[u64]>,
) -> super::behavior::scheduler_bridge::SensedCandidates {
let branches = self.sensing_branch_views(&spec.key(), resolved_population);
super::behavior::scheduler_bridge::project_sensed_candidates(&branches, budget)
}
pub fn subscribe_sensing_overlay_changes(&self) -> tokio::sync::watch::Receiver<u64> {
self.sensing_overlay_changed.subscribe()
}
pub fn subscribe_sensing_scheduler_inputs(&self) -> tokio::sync::watch::Receiver<u64> {
self.sensing_overlay_changed.subscribe()
}
pub fn sensing_observer_poisoned(
&self,
origin: u64,
digest: sensing::Digest256,
) -> Option<sensing::Incarnation> {
self.sensing_observer_gate.lock().poisoned(origin, digest)
}
pub fn sensing_counters(&self) -> Arc<sensing::SensingCounters> {
self.sensing_counters.clone()
}
pub fn sensing_over_cap_refusals(&self) -> u64 {
self.sensing_over_cap.load(Ordering::Relaxed)
}
pub fn sensing_interest_count(&self) -> usize {
self.sensing_interest_table.lock().len()
}
pub fn sensing_table_is_empty(&self) -> bool {
self.sensing_interest_table.lock().is_empty()
}
pub fn sensing_downstreams(
&self,
key: &sensing::ProviderInterestKey,
) -> Vec<sensing::DownstreamId> {
self.sensing_interest_table
.lock()
.downstreams(key, Instant::now())
}
pub fn sensing_downstream_entry(
&self,
key: &sensing::ProviderInterestKey,
downstream: sensing::DownstreamId,
) -> Option<sensing::DownstreamEntry> {
self.sensing_interest_table
.lock()
.downstream_entry(key, downstream)
.copied()
}
pub fn sensing_local_root(&self) -> sensing::AudienceScopeCommitment {
self.sensing_local_root
}
#[cfg(feature = "redex")]
pub fn assume_sensing_leader(&self) -> bool {
if !self.config.enable_sensing_coalescing {
return false;
}
let leader = sensing::SensingLeader::new(
self.sensing_local_root,
sensing::CandidatePolicy::default(),
self.config.continuity_factor,
self.config.max_interests_per_peer,
self.config.sensing_interest_ttl,
);
*self.sensing_leader.lock() = Some(leader);
true
}
#[cfg(feature = "redex")]
pub fn sensing_leader_interest_count(&self) -> Option<usize> {
self.sensing_leader
.lock()
.as_ref()
.map(|leader| leader.interest_count())
}
#[cfg(feature = "redex")]
pub fn sensing_leader_load(&self) -> Option<sensing::SensingLeaderLoad> {
self.sensing_leader
.lock()
.as_ref()
.map(|leader| leader.load(Instant::now()))
}
#[cfg(feature = "redex")]
pub fn sensing_leader_branches(
&self,
key: &sensing::CapabilityInterestKey,
) -> Option<Vec<u64>> {
self.sensing_leader
.lock()
.as_ref()
.map(|leader| leader.branches(key))
}
#[cfg(feature = "redex")]
pub fn sensing_leader_branch_downstreams(
&self,
key: &sensing::ProviderInterestKey,
) -> Option<Vec<sensing::DownstreamId>> {
self.sensing_leader
.lock()
.as_ref()
.map(|leader| leader.relay.table.downstreams(key, Instant::now()))
}
#[cfg(feature = "redex")]
pub fn sensing_candidate_snapshot(
&self,
capability_id: &sensing::CapabilityId,
) -> Vec<sensing::CandidateProvider> {
sensing_candidate_snapshot_from_parts(
&self.capability_fold,
&self.proximity_graph,
&self.router,
&self.peers,
&self.peer_entity_ids,
self.node_id,
sensing::AudienceScopeCommitment::owner_root(self.identity.entity_id()),
&self.sensing_local_root,
capability_id,
)
}
#[doc(hidden)]
pub fn test_pin_peer_entity(&self, node_id: u64, entity_id: EntityId) {
self.peer_entity_ids.entry(node_id).or_insert(entity_id);
}
#[doc(hidden)]
#[cfg(feature = "nat-traversal")]
pub fn peer_session_for_test(&self, node_id: u64) -> Option<Arc<NetSession>> {
self.peers.get(&node_id).map(|e| e.value().session.clone())
}
pub fn migration_identity_context(
&self,
) -> crate::adapter::net::subprotocol::MigrationIdentityContext {
use crate::adapter::net::state::snapshot::StateSnapshot;
use crate::adapter::net::subprotocol::MigrationIdentityContext;
let priv_secret = x25519_dalek::StaticSecret::from(self.static_keypair.private);
let unseal_snapshot = Arc::new(
move |snapshot: &StateSnapshot|
-> Result<Option<_>, crate::adapter::net::identity::EnvelopeError> {
snapshot.open_identity_envelope(&priv_secret)
},
);
let peers = self.peers.clone();
let peer_static_lookup = Arc::new(move |node_id: u64| {
peers.get(&node_id).and_then(|e| {
let pk = e.value().remote_static_pub;
if pk == [0u8; 32] {
None
} else {
Some(pk)
}
})
});
MigrationIdentityContext {
unseal_snapshot,
peer_static_lookup,
}
}
pub fn peer_static_x25519(&self, node_id: u64) -> Option<[u8; 32]> {
let entry = self.peers.get(&node_id)?;
let pk = entry.value().remote_static_pub;
if pk == [0u8; 32] {
None
} else {
Some(pk)
}
}
pub fn peer_subnet(&self, node_id: u64) -> Option<SubnetId> {
self.peer_subnets.get(&node_id).map(|e| *e.value())
}
pub fn local_subnet(&self) -> SubnetId {
self.local_subnet
}
pub fn local_subnet_policy(&self) -> Option<&Arc<SubnetPolicy>> {
self.local_subnet_policy.as_ref()
}
pub fn known_subnets(&self) -> Vec<(u64, SubnetId)> {
let mut out: Vec<(u64, SubnetId)> = self
.peer_subnets
.iter()
.map(|e| (*e.key(), *e.value()))
.collect();
out.sort_by_key(|(node_id, _)| *node_id);
out
}
pub fn local_addr(&self) -> SocketAddr {
self.socket.local_addr()
}
pub fn router(&self) -> &Arc<NetRouter> {
&self.router
}
pub fn failure_detector(&self) -> &Arc<FailureDetector> {
&self.failure_detector
}
pub fn set_migration_handler(&self, handler: Arc<MigrationSubprotocolHandler>) {
self.migration_handler.store(Some(handler));
}
pub fn clear_migration_handler(&self) {
self.migration_handler.store(None);
}
pub fn has_migration_handler(&self) -> bool {
self.migration_handler.load().is_some()
}
pub fn block_peer(&self, addr: SocketAddr) {
self.partition_filter.insert(addr);
}
pub fn unblock_peer(&self, addr: &SocketAddr) {
self.partition_filter.remove(addr);
}
pub fn is_blocked(&self, addr: &SocketAddr) -> bool {
self.partition_filter.contains(addr)
}
pub fn proximity_graph(&self) -> &Arc<ProximityGraph> {
&self.proximity_graph
}
pub fn reroute_policy(&self) -> &Arc<ReroutePolicy> {
&self.reroute_policy
}
pub fn peer_count(&self) -> usize {
self.peers.len()
}
pub async fn connect(
&self,
peer_addr: SocketAddr,
peer_pubkey: &[u8; 32],
peer_node_id: u64,
) -> Result<u64, AdapterError> {
let keys = self
.handshake_initiator(peer_addr, peer_pubkey, peer_node_id)
.await?;
self.install_peer(
peer_node_id,
peer_addr,
keys,
AddrInstallMode::DirectOverwrite,
);
let peer_graph_id = node_id_to_graph_id(peer_node_id);
let pw = EnhancedPingwave::new(peer_graph_id, 0, 1).with_load(0, HealthStatus::Healthy);
self.proximity_graph.on_pingwave(pw, peer_addr);
self.failure_detector.heartbeat(peer_node_id, peer_addr);
self.push_local_announcement(peer_addr).await;
self.emit_event_pingwave(true);
Ok(peer_node_id)
}
fn install_peer(
&self,
peer_node_id: u64,
peer_addr: SocketAddr,
keys: SessionKeys,
addr_mode: AddrInstallMode,
) {
self.install_peer_cas(peer_node_id, peer_addr, keys, addr_mode, None);
}
fn install_peer_cas(
&self,
peer_node_id: u64,
peer_addr: SocketAddr,
keys: SessionKeys,
addr_mode: AddrInstallMode,
expected_prior_session_id: Option<u64>,
) -> bool {
use dashmap::mapref::entry::Entry;
let remote_static_pub = keys.remote_static_pub;
let session = Arc::new(NetSession::new(
keys,
peer_addr,
self.config.packet_pool_size,
self.config.default_reliable,
));
let session_id = session.session_id();
let new_entry = PeerInfo {
node_id: peer_node_id,
addr: peer_addr,
session,
remote_static_pub,
last_initiator_ephemeral: None,
};
let displaced: Option<PeerInfo> = match self.peers.entry(peer_node_id) {
Entry::Occupied(mut occ) => {
if let Some(expected) = expected_prior_session_id {
if occ.get().session.session_id() != expected {
return false;
}
}
Some(occ.insert(new_entry))
}
Entry::Vacant(vac) => {
if expected_prior_session_id.is_some() {
return false;
}
vac.insert(new_entry);
None
}
};
self.router.add_route(peer_node_id, peer_addr);
self.peer_addrs.insert(peer_node_id, peer_addr);
let displaced_addr = displaced.as_ref().map(|d| d.addr);
if let Some(old) = &displaced {
self.session_id_to_node
.remove_if(&old.session.session_id(), |_, n| *n == peer_node_id);
if old.addr != peer_addr {
self.addr_to_node
.remove_if(&old.addr, |_, n| *n == peer_node_id);
}
}
self.session_id_to_node.insert(session_id, peer_node_id);
self.route_withdraw_gate.forget_sender(peer_node_id);
match addr_mode {
AddrInstallMode::DirectOverwrite => {
if let Some(old_addr) = displaced_addr {
if old_addr != peer_addr {
self.router
.routing_table()
.migrate_next_hop(old_addr, peer_addr);
self.addr_to_node
.remove_if(&old_addr, |_, n| *n == peer_node_id);
}
}
self.addr_to_node.insert(peer_addr, peer_node_id);
}
AddrInstallMode::RoutedPreserve => {
self.addr_to_node.entry(peer_addr).or_insert(peer_node_id);
}
}
true
}
pub async fn accept(&self, peer_node_id: u64) -> Result<(SocketAddr, u64), AdapterError> {
use std::sync::atomic::Ordering as AtOrd;
struct AcceptGuard<'a>(&'a std::sync::atomic::AtomicUsize);
impl Drop for AcceptGuard<'_> {
fn drop(&mut self) {
self.0.fetch_sub(1, AtOrd::SeqCst);
}
}
self.accept_in_flight.fetch_add(1, AtOrd::SeqCst);
let _guard = AcceptGuard(&self.accept_in_flight);
if self.started.load(AtOrd::SeqCst) {
return Err(AdapterError::Fatal(
"Mesh::accept called after start() — the dispatch loop is already \
consuming inbound packets and would race the responder handshake. \
Call accept() for every peer BEFORE invoking start()."
.into(),
));
}
let (keys, peer_addr) = self.handshake_responder(peer_node_id).await?;
let remote_static_pub = keys.remote_static_pub;
let session = Arc::new(NetSession::new(
keys,
peer_addr,
self.config.packet_pool_size,
self.config.default_reliable,
));
let session_id = session.session_id();
self.router.add_route(peer_node_id, peer_addr);
let displaced = self.peers.insert(
peer_node_id,
PeerInfo {
node_id: peer_node_id,
addr: peer_addr,
session,
remote_static_pub,
last_initiator_ephemeral: None,
},
);
self.addr_to_node.insert(peer_addr, peer_node_id);
self.peer_addrs.insert(peer_node_id, peer_addr);
if let Some(old_addr) = displaced.as_ref().map(|d| d.addr) {
if old_addr != peer_addr {
self.router
.routing_table()
.migrate_next_hop(old_addr, peer_addr);
self.addr_to_node
.remove_if(&old_addr, |_, n| *n == peer_node_id);
}
}
if let Some(old) = displaced {
self.session_id_to_node
.remove_if(&old.session.session_id(), |_, n| *n == peer_node_id);
}
self.session_id_to_node.insert(session_id, peer_node_id);
self.route_withdraw_gate.forget_sender(peer_node_id);
let peer_graph_id = node_id_to_graph_id(peer_node_id);
let pw = EnhancedPingwave::new(peer_graph_id, 0, 1).with_load(0, HealthStatus::Healthy);
self.proximity_graph.on_pingwave(pw, peer_addr);
self.failure_detector.heartbeat(peer_node_id, peer_addr);
self.push_local_announcement(peer_addr).await;
self.emit_event_pingwave(true);
Ok((peer_addr, peer_node_id))
}
pub fn start(&self) {
use std::sync::atomic::Ordering as AtOrd;
if self.started.swap(true, AtOrd::SeqCst) {
return; }
if self.accept_in_flight.load(AtOrd::SeqCst) > 0 {
self.started.store(false, AtOrd::SeqCst);
tracing::warn!(
"MeshNode::start() called while an accept() is in flight — \
refusing to start the dispatch loop to avoid racing the \
responder handshake. Retry start() after accept() returns."
);
return;
}
let recv_handle = self.spawn_receive_loop();
let heartbeat_handle = self.spawn_heartbeat_loop();
let stream_grant_drainer_handle = self.spawn_stream_grant_drainer_loop();
let retransmit_handle = self.spawn_retransmit_loop();
let router_handle = match self.router.start() {
Some(h) => h,
None => {
tracing::warn!(
"MeshNode::start called while the router dispatch loop \
was already running; ignoring the duplicate start. \
This usually indicates start() was invoked twice."
);
return;
}
};
let capability_gc_handle = self.spawn_capability_gc_loop();
let capability_reannounce_handle = self.spawn_capability_reannounce_loop();
let capability_announce_on_change_handle = self.spawn_capability_announce_on_change_loop();
let fold_generation_gc_handle = self.spawn_fold_generation_gc_loop();
let sensing_emitter_handle = self.spawn_sensing_emitter_loop();
let token_sweep_handle = self.spawn_token_sweep_loop();
#[cfg(feature = "port-mapping")]
let port_mapping_handle = if self.config.try_port_mapping {
use super::traversal::portmap::{
sequential_mapper_from_os, MappingSink, NullPortMapper, PortMapperClient,
PortMapperTask,
};
let traversal_stats = self.traversal_stats.clone();
let reflex_addr = self.reflex_addr.clone();
let nat_class = self.nat_class.clone();
let reflex_override_active = self.reflex_override_active.clone();
let publish_mu = self.traversal_publish_mu.clone();
let shutdown = self.shutdown.clone();
let shutdown_notify = self.shutdown_notify.clone();
let internal_port = self.config.bind_addr.port();
let renewal = self.traversal_config.port_mapping_renewal;
Some(tokio::spawn(async move {
let client: Box<dyn PortMapperClient> = match sequential_mapper_from_os().await {
Some(seq) => Box::new(seq),
None => {
tracing::debug!(
"port-mapping: OS gateway + LAN IP resolution failed; \
falling back to NullPortMapper",
);
Box::new(NullPortMapper::new())
}
};
let sink = MappingSink::new(
traversal_stats,
reflex_addr,
nat_class,
reflex_override_active,
publish_mu,
);
let task = PortMapperTask::new(
client,
sink,
internal_port,
renewal,
shutdown,
shutdown_notify,
);
task.run().await;
}))
} else {
None
};
let tasks = self.tasks.clone();
tokio::spawn(async move {
let mut tasks = tasks.lock().await;
tasks.push(recv_handle);
tasks.push(heartbeat_handle);
tasks.push(stream_grant_drainer_handle);
tasks.push(retransmit_handle);
tasks.push(router_handle);
tasks.push(capability_gc_handle);
tasks.push(capability_reannounce_handle);
tasks.push(capability_announce_on_change_handle);
tasks.push(fold_generation_gc_handle);
if let Some(h) = sensing_emitter_handle {
tasks.push(h);
}
tasks.push(token_sweep_handle);
#[cfg(feature = "port-mapping")]
if let Some(h) = port_mapping_handle {
tasks.push(h);
}
});
}
pub fn start_arc(self: &Arc<Self>) {
let _ = self.self_weak.set(Arc::downgrade(self));
self.start();
#[cfg(feature = "nat-traversal")]
let _upgrade_loop_handle = self.spawn_direct_upgrade_loop();
}
#[cfg(feature = "nat-traversal")]
pub fn spawn_nat_classify_loop(self: &Arc<Self>) -> JoinHandle<()> {
let node = Arc::clone(self);
let shutdown = self.shutdown.clone();
let shutdown_notify = self.shutdown_notify.clone();
let reclassify_interval = self.traversal_config.classify_deadline.saturating_mul(12);
tokio::spawn(async move {
let mut poll = tokio::time::interval(Duration::from_millis(200));
poll.tick().await; loop {
if shutdown.load(Ordering::Acquire) {
return;
}
tokio::select! {
_ = shutdown_notify.notified() => return,
_ = poll.tick() => {
if node.peers.len() >= 2 {
break;
}
}
}
}
node.reclassify_nat().await;
let mut tick = tokio::time::interval(nonzero_interval(reclassify_interval));
tick.tick().await; while !shutdown.load(Ordering::Acquire) {
tokio::select! {
_ = tick.tick() => {
node.reclassify_nat().await;
}
_ = shutdown_notify.notified() => break,
}
}
})
}
#[cfg(feature = "port-mapping")]
pub fn spawn_port_mapping_loop(
&self,
client: Box<dyn super::traversal::portmap::PortMapperClient>,
) -> JoinHandle<()> {
use super::traversal::portmap::{MappingSink, PortMapperTask};
let sink = MappingSink::new(
self.traversal_stats.clone(),
self.reflex_addr.clone(),
self.nat_class.clone(),
self.reflex_override_active.clone(),
self.traversal_publish_mu.clone(),
);
let internal_port = self.config.bind_addr.port();
let renewal = self.traversal_config.port_mapping_renewal;
let task = PortMapperTask::new(
client,
sink,
internal_port,
renewal,
self.shutdown.clone(),
self.shutdown_notify.clone(),
);
tokio::spawn(task.run())
}
fn spawn_capability_gc_loop(&self) -> JoinHandle<()> {
let seen = self.seen_announcements.clone();
let interval = self.config.capability_gc_interval;
let dedup_retention =
std::time::Duration::from_secs(2 * u64::from(CapabilityAnnouncement::DEFAULT_TTL_SECS));
let shutdown = self.shutdown.clone();
let shutdown_notify = self.shutdown_notify.clone();
tokio::spawn(async move {
if interval == Duration::MAX {
let _ = shutdown_notify.notified().await;
return;
}
let mut tick = tokio::time::interval(nonzero_interval(interval));
tick.tick().await;
while !shutdown.load(Ordering::Acquire) {
tokio::select! {
_ = tick.tick() => {
seen.retain(|_, instant| instant.elapsed() < dedup_retention);
}
_ = shutdown_notify.notified() => break,
}
}
})
}
fn spawn_capability_reannounce_loop(&self) -> JoinHandle<()> {
let interval = self.config.capability_reannounce_interval;
let min_announce = self.config.min_announce_interval;
let self_weak = self.self_weak.clone();
let shutdown = self.shutdown.clone();
let shutdown_notify = self.shutdown_notify.clone();
tokio::spawn(async move {
if interval == Duration::MAX {
let _ = shutdown_notify.notified().await;
return;
}
let ttl = capability_reannounce_ttl(interval, min_announce);
let mut tick = tokio::time::interval(nonzero_interval(interval));
tick.tick().await;
while !shutdown.load(Ordering::Acquire) {
tokio::select! {
_ = tick.tick() => {
let Some(weak) = self_weak.get() else { continue };
let Some(node) = weak.upgrade() else { break };
#[cfg(feature = "nat-traversal")]
node.reclassify_if_reflex_drifted().await;
if let Err(e) = node.announce_from_baseline(None, ttl, true).await {
tracing::debug!(error = %e, "capability re-announce failed");
}
}
_ = shutdown_notify.notified() => break,
}
}
})
}
const CHANGE_ANNOUNCE_START_ARC_POLL: Duration = Duration::from_millis(200);
fn spawn_capability_announce_on_change_loop(&self) -> JoinHandle<()> {
let debounce = self.config.announce_debounce;
let reannounce_interval = self.config.capability_reannounce_interval;
let min_announce = self.config.min_announce_interval;
let self_weak = self.self_weak.clone();
let mut change_rx = self.local_caps_changed.subscribe();
let shutdown = self.shutdown.clone();
let shutdown_notify = self.shutdown_notify.clone();
tokio::spawn(async move {
if debounce == Duration::MAX {
let _ = shutdown_notify.notified().await;
return;
}
let ttl = if reannounce_interval == Duration::MAX {
Duration::from_secs(300)
} else {
capability_reannounce_ttl(reannounce_interval, min_announce)
};
while !shutdown.load(Ordering::Acquire) {
tokio::select! {
changed = change_rx.changed() => {
if changed.is_err() {
return;
}
let node = loop {
match self_weak.get() {
Some(weak) => match weak.upgrade() {
Some(node) => break node,
None => return, },
None => {
tokio::select! {
_ = tokio::time::sleep(
Self::CHANGE_ANNOUNCE_START_ARC_POLL) => {}
_ = shutdown_notify.notified() => return,
}
}
}
};
tokio::select! {
_ = tokio::time::sleep(debounce) => {}
_ = shutdown_notify.notified() => return,
}
let _ = change_rx.borrow_and_update();
if let Err(e) = node.announce_from_baseline(None, ttl, true).await {
tracing::debug!(
error = %e,
"change-driven capability announce failed"
);
}
node.emit_event_pingwave(false);
}
_ = shutdown_notify.notified() => return,
}
}
})
}
fn spawn_fold_generation_gc_loop(&self) -> JoinHandle<()> {
let generations = self.fold_generations.clone();
let shutdown = self.shutdown.clone();
let shutdown_notify = self.shutdown_notify.clone();
tokio::spawn(async move {
let mut tick = tokio::time::interval(FOLD_GENERATION_GC_INTERVAL);
tick.tick().await;
while !shutdown.load(Ordering::Acquire) {
tokio::select! {
_ = tick.tick() => {
let cutoff_us = super::current_timestamp_micros()
.saturating_sub(FOLD_GENERATION_GC_MAX_AGE.as_micros() as u64);
generations.retain(|_, e| {
e.last_touched_us.load(Ordering::Relaxed) >= cutoff_us
});
}
_ = shutdown_notify.notified() => break,
}
}
})
}
fn spawn_sensing_emitter_loop(&self) -> Option<JoinHandle<()>> {
if self.sensing_emitter.lock().is_none() {
return None;
}
let emitter = self.sensing_emitter.clone();
let notify = self.sensing_emitter_notify.clone();
let evaluators = self.sensing_evaluators.clone();
let table = self.sensing_interest_table.clone();
#[cfg(feature = "redex")]
let sensing_leader = self.sensing_leader.clone();
let identity = self.identity.clone();
let capability_version = self.capability_version.clone();
let socket = self.socket.clone();
let peers = self.peers.clone();
let addr_to_node = self.addr_to_node.clone();
let router = self.router.clone();
let partition_filter = self.partition_filter.clone();
let local_node_id = self.node_id;
let observations = self.sensing_observations.clone();
let overlay = self.sensing_overlay_changed.clone();
let factor = self.config.continuity_factor;
let counters = self.sensing_counters.clone();
let shutdown = self.shutdown.clone();
let shutdown_notify = self.shutdown_notify.clone();
Some(tokio::spawn(async move {
while !shutdown.load(Ordering::Acquire) {
let next = { emitter.lock().as_ref().and_then(|e| e.next_due()) };
let now = Instant::now();
match next {
None => {
tokio::select! {
_ = notify.notified() => continue,
_ = shutdown_notify.notified() => break,
}
}
Some(due) if due > now => {
tokio::select! {
_ = tokio::time::sleep(due - now) => {}
_ = notify.notified() => {}
_ = shutdown_notify.notified() => break,
}
continue;
}
Some(_) => {}
}
let generation = capability_version.load(Ordering::Relaxed);
let beats = {
let mut slot = emitter.lock();
let Some(origin) = slot.as_mut() else { break };
origin.collect_due(now, generation)
};
for beat in beats {
let digest = beat.key().interest_digest;
let stamp = beat.stamp();
let branch =
sensing::ProviderInterestKey::new(beat.key().clone(), local_node_id);
let downstreams = { table.lock().downstreams(&branch, now) };
if downstreams.is_empty() {
if let Some(origin) = emitter.lock().as_mut() {
origin.retire_if_stale(&digest, stamp);
}
continue;
}
let mut local_interval: Option<Duration> = None;
let mut leader_row = false;
let peer_downstreams: Vec<u64> = downstreams
.into_iter()
.filter_map(|downstream| match downstream {
sensing::DownstreamId::Peer(node) => Some(node),
sensing::DownstreamId::Local => {
local_interval = Some(
table
.lock()
.downstream_entry(&branch, sensing::DownstreamId::Local)
.map(|row| row.requested_sample_interval)
.unwrap_or(Duration::from_millis(50)),
);
None
}
sensing::DownstreamId::Leader => {
leader_row = true;
None
}
})
.collect();
let evaluation = evaluators
.get(&beat.key().capability_id)
.map(|entry| entry.value().clone())
.map(|evaluator| evaluator.evaluate(&beat.request()));
let unsigned = beat.into_unsigned(evaluation);
let Ok(signed) = sensing::sign_attestation(&identity, unsigned) else {
continue;
};
counters
.attestations_emitted
.fetch_add(1, Ordering::Relaxed);
if local_interval.is_some() || leader_row {
observations
.lock()
.latest
.insert(branch.clone(), signed.clone());
}
if let Some(interval) = local_interval {
let moved = {
let mut observations = observations.lock();
observations
.feed_consumer_cell(&branch, &signed, true, interval, factor, now)
};
if moved {
overlay.send_modify(|generation| {
*generation = generation.wrapping_add(1);
});
}
}
#[cfg(feature = "redex")]
if leader_row {
if let Ok(semantic) =
sensing::semantic_attestation(&branch.interest, &signed)
{
let deliveries = {
let mut slot = sensing_leader.lock();
match slot.as_mut() {
Some(leader) => leader.on_attestation(now, &semantic, true),
None => Vec::new(),
}
};
dispatch_sensing_leader_deliveries(
&socket,
&peers,
&addr_to_node,
&router,
&partition_filter,
local_node_id,
&observations,
&overlay,
factor,
deliveries,
now,
);
}
}
#[cfg(not(feature = "redex"))]
let _ = leader_row;
if peer_downstreams.is_empty() {
continue;
}
let Ok(bytes) = sensing::encode_attestation(&signed) else {
continue;
};
for node in peer_downstreams {
spawn_sensing_frame_send(
&socket,
&peers,
&addr_to_node,
&router,
&partition_filter,
local_node_id,
node,
sensing::SUBPROTOCOL_READINESS_ATTESTATION as u64,
sensing::SUBPROTOCOL_READINESS_ATTESTATION,
bytes.clone(),
);
}
}
}
}))
}
fn spawn_token_sweep_loop(&self) -> JoinHandle<()> {
let roster = self.roster.clone();
let guard = self.auth_guard.clone();
let cache = self.token_cache.clone();
let peer_entity_ids = self.peer_entity_ids.clone();
let channel_configs = self.channel_configs.clone();
let subscriber_chains = self.subscriber_chains.clone();
let interval = self.config.token_sweep_interval;
let shutdown = self.shutdown.clone();
let shutdown_notify = self.shutdown_notify.clone();
tokio::spawn(async move {
if interval == Duration::MAX {
let _ = shutdown_notify.notified().await;
return;
}
let mut tick = tokio::time::interval(nonzero_interval(interval));
tick.tick().await;
while !shutdown.load(Ordering::Acquire) {
tokio::select! {
_ = tick.tick() => {
sweep_expired_subscribers(
&roster,
&guard,
cache.as_ref(),
&peer_entity_ids,
channel_configs.as_ref(),
&subscriber_chains,
);
}
_ = shutdown_notify.notified() => break,
}
}
})
}
fn spawn_receive_loop(&self) -> JoinHandle<()> {
let socket = self.socket.socket_arc();
let shutdown = self.shutdown.clone();
let shutdown_notify = self.shutdown_notify.clone();
#[cfg(all(target_os = "linux", feature = "batched-ingress"))]
let batched_ingress = self.config.batched_ingress;
let ctx = DispatchCtx {
local_node_id: self.node_id,
peers: self.peers.clone(),
addr_to_node: self.addr_to_node.clone(),
peer_addrs: self.peer_addrs.clone(),
router: self.router.clone(),
failure_detector: self.failure_detector.clone(),
inbound: self.inbound.clone(),
#[cfg(feature = "cortex")]
rpc_inbound_dispatchers: self.rpc_inbound_dispatchers.clone(),
num_shards: self.config.num_shards,
migration_handler: self.migration_handler.clone(),
#[cfg(feature = "redex")]
replication_inbound_router: self.replication_inbound_router.clone(),
#[cfg(feature = "meshdb")]
meshdb_inbound_router: self.meshdb_inbound_router.clone(),
fold_router: self.fold_router.clone(),
#[cfg(feature = "dataforts")]
greedy_observer: self.greedy_observer.clone(),
#[cfg(feature = "dataforts")]
blob_transfer_engine: self.blob_transfer_engine.clone(),
pending_handshakes: self.pending_handshakes.clone(),
pending_direct_initiators: self.pending_direct_initiators.clone(),
static_keypair: self.static_keypair.clone(),
psk: self.config.psk,
socket: self.socket.clone(),
proximity_graph: self.proximity_graph.clone(),
partition_filter: self.partition_filter.clone(),
enable_route_withdraw: self.config.enable_route_withdraw,
route_withdraw_seq: self.route_withdraw_seq.clone(),
route_withdraw_damper: self.route_withdraw_damper.clone(),
route_withdraw_gate: self.route_withdraw_gate.clone(),
route_withdraw_cascades_inflight: self.route_withdraw_cascades_inflight.clone(),
enable_sensing_coalescing: self.config.enable_sensing_coalescing,
sensing_interest_ttl: self.config.sensing_interest_ttl,
sensing_interest_table: self.sensing_interest_table.clone(),
sensing_counters: self.sensing_counters.clone(),
sensing_over_cap: self.sensing_over_cap.clone(),
sensing_local_root: self.sensing_local_root,
sensing_fleet_scope: self.config.sensing_owner_root.is_some(),
sensing_upstream_damper: self.sensing_upstream_damper.clone(),
#[cfg(feature = "redex")]
sensing_leader: self.sensing_leader.clone(),
#[cfg(feature = "redex")]
sensing_fold_coalescer: self.sensing_fold_coalescer.clone(),
#[cfg(feature = "redex")]
sensing_local_entity_root: sensing::AudienceScopeCommitment::owner_root(
self.identity.entity_id(),
),
sensing_emitter: self.sensing_emitter.clone(),
sensing_emitter_notify: self.sensing_emitter_notify.clone(),
signing_identity: self.identity.clone(),
capability_version: self.capability_version.clone(),
sensing_observer_gate: self.sensing_observer_gate.clone(),
sensing_continuity_factor: self.config.continuity_factor,
sensing_overlay_changed: self.sensing_overlay_changed.clone(),
sensing_capability_interests: self.sensing_capability_interests.clone(),
sensing_observations: self.sensing_observations.clone(),
pending_stream_grants: self.pending_stream_grants.clone(),
pending_stream_grants_notify: self.pending_stream_grants_notify.clone(),
control_stats: self.control_stats.clone(),
packet_pool_size: self.config.packet_pool_size,
default_reliable: self.config.default_reliable,
session_timeout: self.config.session_timeout,
roster: self.roster.clone(),
channel_configs: self.channel_configs.clone(),
pending_membership_acks: self.pending_membership_acks.clone(),
#[cfg(feature = "nat-traversal")]
pending_reflex_probes: self.pending_reflex_probes.clone(),
#[cfg(feature = "nat-traversal")]
pending_punch_introduces: self.pending_punch_introduces.clone(),
#[cfg(feature = "nat-traversal")]
pending_punch_acks: self.pending_punch_acks.clone(),
#[cfg(feature = "nat-traversal")]
punch_observers: self.punch_observers.clone(),
#[cfg(feature = "nat-traversal")]
rendezvous_budgets: self.rendezvous_budgets.clone(),
#[cfg(feature = "nat-traversal")]
traversal_config: self.traversal_config.clone(),
max_channels_per_peer: self.config.max_channels_per_peer,
capability_fold: self.capability_fold.clone(),
ack_ranges_peer_cache: self.ack_ranges_peer_cache.clone(),
#[cfg(feature = "dataforts")]
capability_set_cache: self.capability_set_cache.clone(),
seen_announcements: self.seen_announcements.clone(),
require_signed_capabilities: self.config.require_signed_capabilities,
local_subnet: self.local_subnet,
local_subnet_policy: self.local_subnet_policy.clone(),
peer_subnets: self.peer_subnets.clone(),
subnet_gateway: self.subnet_gateway.clone(),
peer_entity_ids: self.peer_entity_ids.clone(),
origin_hash_to_node: self.origin_hash_to_node.clone(),
session_id_to_node: self.session_id_to_node.clone(),
token_cache: self.token_cache.clone(),
subscriber_chains: self.subscriber_chains.clone(),
auth_guard: self.auth_guard.clone(),
auth_failures: self.auth_failures.clone(),
max_auth_failures_per_window: self.config.max_auth_failures_per_window,
auth_failure_window: self.config.auth_failure_window,
auth_throttle_duration: self.config.auth_throttle_duration,
};
enum IngressReceiver {
Single(PacketReceiver),
#[cfg(all(target_os = "linux", feature = "batched-ingress"))]
Batched(super::transport::BatchedPacketReceiver),
}
impl IngressReceiver {
#[inline]
async fn recv(&mut self) -> std::io::Result<(Bytes, SocketAddr)> {
match self {
IngressReceiver::Single(r) => r.recv().await,
#[cfg(all(target_os = "linux", feature = "batched-ingress"))]
IngressReceiver::Batched(r) => r.recv().await,
}
}
#[inline]
fn reset_is_fatal(&self) -> bool {
match self {
IngressReceiver::Single(_) => false,
#[cfg(all(target_os = "linux", feature = "batched-ingress"))]
IngressReceiver::Batched(_) => true,
}
}
}
tokio::spawn(async move {
let mut receiver = {
#[cfg(all(target_os = "linux", feature = "batched-ingress"))]
{
if batched_ingress {
IngressReceiver::Batched(super::transport::BatchedPacketReceiver::new(
socket,
))
} else {
IngressReceiver::Single(PacketReceiver::new(socket))
}
}
#[cfg(not(all(target_os = "linux", feature = "batched-ingress")))]
{
IngressReceiver::Single(PacketReceiver::new(socket))
}
};
let reset_is_fatal = receiver.reset_is_fatal();
while !shutdown.load(Ordering::Acquire) {
tokio::select! {
result = receiver.recv() => {
match result {
Ok((data, source)) => {
Self::dispatch_packet(data, source, &ctx);
}
Err(e)
if reset_is_fatal
&& e.kind() == std::io::ErrorKind::ConnectionReset =>
{
if !shutdown.load(Ordering::Acquire) {
tracing::warn!(
"mesh batch receiver thread exited, stopping receiver"
);
}
break;
}
Err(e) => {
if !shutdown.load(Ordering::Acquire) {
tracing::warn!(error = %e, "mesh receive error");
}
}
}
}
_ = shutdown_notify.notified() => {
break;
}
}
}
})
}
fn dispatch_packet(data: Bytes, source: SocketAddr, ctx: &DispatchCtx) {
if ctx.partition_filter.contains(&source) {
return;
}
#[cfg(feature = "nat-traversal")]
if data.len() == super::traversal::rendezvous::KEEPALIVE_LEN {
if let Some(ka) = super::traversal::rendezvous::decode_keepalive(&data) {
if let Some((_, (_expected, tx))) = ctx
.punch_observers
.remove_if(&source, |_, (expected, _)| ka.sender_node_id == *expected)
{
let _ = tx.send(ka);
}
return;
}
}
if data.len() == EnhancedPingwave::SIZE && u16::from_le_bytes([data[0], data[1]]) != MAGIC {
if let Some(pw) = EnhancedPingwave::from_bytes(&data) {
let origin_nid = graph_id_to_node_id(&pw.origin_id);
if origin_nid == ctx.local_node_id {
return;
}
if pw.hop_count >= MAX_HOPS {
return;
}
let from_node_id = match ctx.addr_to_node.get(&source) {
Some(e) => *e.value(),
None => return,
};
let metric = (pw.hop_count as u16).saturating_add(2);
let from_graph_id = node_id_to_graph_id(from_node_id);
let admission = ctx
.proximity_graph
.admit_pingwave_from(pw, from_graph_id, source);
let fwd_pw = match admission {
PingwaveAdmission::RejectedDuplicate => return,
PingwaveAdmission::AcceptedNoForward => {
ctx.router
.routing_table()
.add_route_with_metric(origin_nid, source, metric);
return;
}
PingwaveAdmission::AcceptedAndForward(fwd_pw) => {
ctx.router
.routing_table()
.add_route_with_metric(origin_nid, source, metric);
fwd_pw
}
};
{
let fwd_bytes = fwd_pw.to_bytes();
let socket = ctx.socket.clone();
let peers = ctx.peers.clone();
let filter = ctx.partition_filter.clone();
let router = ctx.router.clone();
tokio::spawn(async move {
let next_hop = router.routing_table().lookup(origin_nid);
for entry in peers.iter() {
let addr = entry.value().addr;
if addr == source {
continue; }
if Some(addr) == next_hop {
continue; }
if filter.contains(&addr) {
continue;
}
let _ = socket.send_to(&fwd_bytes, addr).await;
}
});
}
return;
}
}
let local_node_id = ctx.local_node_id;
let peers = &ctx.peers;
let router = &ctx.router;
let failure_detector = &ctx.failure_detector;
let first2 = if data.len() >= 2 {
u16::from_le_bytes([data[0], data[1]])
} else {
0
};
let is_routed =
first2 == ROUTING_MAGIC && data.len() >= ROUTING_HEADER_SIZE + protocol::HEADER_SIZE;
let is_direct = first2 == MAGIC;
if !is_routed && !is_direct {
return;
}
if is_routed {
if let Some(routing_header) = RoutingHeader::from_bytes(&data[..ROUTING_HEADER_SIZE]) {
if routing_header.dest_id == local_node_id {
let inner = data.slice(ROUTING_HEADER_SIZE..);
let parsed = match ParsedPacket::parse(inner, source) {
Some(p) => p,
None => return,
};
if parsed.header.flags.is_heartbeat() {
return;
}
if parsed.header.flags.is_handshake() {
Self::handle_routed_handshake(&parsed, &routing_header, source, ctx);
return;
}
let session_id = parsed.header.session_id;
let matched = ctx
.session_id_to_node
.get(&session_id)
.map(|e| *e.value())
.and_then(|node_id| {
peers
.get(&node_id)
.map(|e| (node_id, e.value().session.clone()))
})
.filter(|(_, session)| session.session_id() == session_id);
if let Some((peer_node_id, session)) = matched {
Self::process_local_packet(parsed, peer_node_id, &session, ctx);
session.touch();
}
} else {
if routing_header.is_expired() {
return;
}
let next_hop = match router.routing_table().lookup(routing_header.dest_id) {
Some(addr) => addr,
None => return,
};
if ctx.partition_filter.contains(&next_hop) {
return;
}
let mut fwd_header = routing_header;
fwd_header.forward();
let forwarded = match data.try_into_mut() {
Ok(mut mut_data) => {
fwd_header.write_at(&mut mut_data[..ROUTING_HEADER_SIZE]);
mut_data.freeze()
}
Err(orig_data) => {
let mut new_data = bytes::BytesMut::with_capacity(orig_data.len());
new_data.extend_from_slice(&fwd_header.to_bytes());
new_data.extend_from_slice(&orig_data[ROUTING_HEADER_SIZE..]);
new_data.freeze()
}
};
let socket = ctx.socket.clone();
tokio::spawn(async move {
let _ = socket.send_to(&forwarded, next_hop).await;
});
}
}
return;
}
let parsed = match ParsedPacket::parse(data, source) {
Some(p) => p,
None => return,
};
if parsed.header.flags.is_handshake() {
if let Some((_, tx)) = ctx.pending_direct_initiators.remove(&source) {
let _ = tx.send(parsed.payload);
}
return;
}
let session_id = parsed.header.session_id;
let matched = ctx
.addr_to_node
.get(&source)
.map(|e| *e.value())
.and_then(|nid| peers.get(&nid))
.filter(|p| p.session.session_id() == session_id)
.map(|p| (p.value().node_id, p.value().session.clone()))
.or_else(|| {
peers
.iter()
.find(|e| e.value().session.session_id() == session_id)
.map(|e| (e.value().node_id, e.value().session.clone()))
});
let (peer_node_id, session) = match matched {
Some(x) => x,
None => return,
};
if parsed.header.flags.is_heartbeat() {
if !session.verify_and_touch_heartbeat(&parsed) {
return;
}
failure_detector.heartbeat(peer_node_id, source);
return;
}
Self::process_local_packet(parsed, peer_node_id, &session, ctx);
session.touch();
}
fn handle_routed_handshake(
parsed: &ParsedPacket,
routing_header: &RoutingHeader,
source: SocketAddr,
ctx: &DispatchCtx,
) {
let peer_routing_id = routing_header.src_id as u64;
if let Some((_, pending)) = ctx.pending_handshakes.remove(&peer_routing_id) {
let PendingHandshake { mut noise, tx } = pending;
let result = (|| -> Result<SessionKeys, CryptoError> {
noise.read_message(&parsed.payload)?;
noise.into_session_keys()
})();
let _ = tx.send(result);
return;
}
let self_routing_id = routing_id(ctx.local_node_id);
let prologue = handshake_prologue(peer_routing_id, self_routing_id);
let mut noise =
match NoiseHandshake::responder_with_prologue(&ctx.psk, &ctx.static_keypair, &prologue)
{
Ok(n) => n,
Err(e) => {
tracing::warn!(error = %e, "routed handshake: responder build failed");
return;
}
};
if parsed.payload.len() < 32 {
tracing::warn!(
"routed handshake: msg1 too short ({}); NKpsk0 msg1 must carry a 32-byte ephemeral prefix",
parsed.payload.len()
);
return;
}
let mut initiator_ephemeral = [0u8; 32];
initiator_ephemeral.copy_from_slice(&parsed.payload[..32]);
let msg1_payload = match noise.read_message(&parsed.payload) {
Ok(p) => p,
Err(e) => {
tracing::warn!(error = %e, "routed handshake: read_message failed (msg1 tampered or wrong PSK)");
return;
}
};
if msg1_payload.len() < 8 {
tracing::warn!(
"routed handshake: msg1 payload too short ({}); need 8 bytes of src node_id",
msg1_payload.len()
);
return;
}
#[expect(
clippy::unwrap_used,
reason = "msg1_payload.len() >= 8 guard above; [..8].try_into::<[u8; 8]>() is infallible"
)]
let peer_node_id = u64::from_le_bytes(msg1_payload[..8].try_into().unwrap());
if routing_id(peer_node_id) != peer_routing_id {
tracing::warn!(
payload = format!("{:#x}", peer_node_id),
routing = format!("{:#x}", peer_routing_id),
"routed handshake: src_node_id in payload does not match routing header"
);
return;
}
let msg2 = match noise.write_message(&[]) {
Ok(m) => m,
Err(e) => {
tracing::warn!(error = %e, "routed handshake: write_message failed");
return;
}
};
let keys = match noise.into_session_keys() {
Ok(k) => k,
Err(e) => {
tracing::warn!(error = %e, "routed handshake: key extraction failed");
return;
}
};
let mut builder = PacketBuilder::new(&[0u8; 32], 0);
let inner = builder.build_handshake(&msg2);
let reply_routing = RoutingHeader::new(
peer_node_id,
ctx.local_node_id as u32,
DEFAULT_HANDSHAKE_TTL,
);
let mut routed = bytes::BytesMut::with_capacity(ROUTING_HEADER_SIZE + inner.len());
routed.extend_from_slice(&reply_routing.to_bytes());
routed.extend_from_slice(&inner);
let next_hop = ctx
.router
.routing_table()
.lookup(peer_node_id)
.unwrap_or(source);
let remote_static_pub = keys.remote_static_pub;
let registered_session_id: u64 = match ctx.peers.entry(peer_node_id) {
dashmap::mapref::entry::Entry::Occupied(mut occ) => {
match routed_rotation_outcome(
occ.get(),
&remote_static_pub,
&initiator_ephemeral,
ctx.session_timeout,
) {
RoutedRotationOutcome::DropReplay => {
tracing::warn!(
peer_node_id,
"routed handshake: dropping msg1 — live session already \
established for this peer with matching remote_static_pub \
AND identical initiator ephemeral (replay guard)"
);
return;
}
RoutedRotationOutcome::RefuseFresh => {
tracing::warn!(
peer_node_id,
"routed handshake: refusing key rotation — existing \
session is still within session_timeout. New keys \
can be installed once the live session has gone \
silent for at least session_timeout (rotation gate)"
);
return;
}
RoutedRotationOutcome::DeferBusy => {
tracing::debug!(
peer_node_id,
"routed handshake: deferring key rotation — existing \
session is live and busy (open streams / unacked \
in-flight data). Swapping now would drop that state; \
the initiator retries once the session is quiescent \
(direct-path upgrade C3 busy gate)"
);
return;
}
RoutedRotationOutcome::AcceptRotation => {
let displaced_session_id = occ.get().session.session_id();
ctx.session_id_to_node
.remove_if(&displaced_session_id, |_, n| *n == peer_node_id);
let session = Arc::new(NetSession::new(
keys,
source,
ctx.packet_pool_size,
ctx.default_reliable,
));
let session_id = session.session_id();
occ.insert(PeerInfo {
node_id: peer_node_id,
addr: source,
session,
remote_static_pub,
last_initiator_ephemeral: Some(initiator_ephemeral),
});
session_id
}
}
}
dashmap::mapref::entry::Entry::Vacant(vac) => {
let session = Arc::new(NetSession::new(
keys,
source,
ctx.packet_pool_size,
ctx.default_reliable,
));
let session_id = session.session_id();
vac.insert(PeerInfo {
node_id: peer_node_id,
addr: source,
session,
remote_static_pub,
last_initiator_ephemeral: Some(initiator_ephemeral),
});
session_id
}
};
ctx.peer_addrs.insert(peer_node_id, source);
ctx.router.add_route(peer_node_id, source);
ctx.session_id_to_node
.insert(registered_session_id, peer_node_id);
ctx.route_withdraw_gate.forget_sender(peer_node_id);
let socket = ctx.socket.clone();
let payload = routed.freeze();
let guard = PeerRegistrationGuard {
peer_node_id,
registered_session_id,
registered_next_hop: source,
peers: ctx.peers.clone(),
peer_addrs: ctx.peer_addrs.clone(),
session_id_to_node: ctx.session_id_to_node.clone(),
router: ctx.router.clone(),
};
tokio::spawn(async move {
match socket.send_to(&payload, next_hop).await {
Ok(_) => {
guard.commit();
}
Err(e) => {
tracing::warn!(
peer = format!("{:#x}", peer_node_id),
error = %e,
"routed handshake: msg2 send failed; unregistering peer"
);
}
}
});
}
#[inline]
fn resolve_grant_peer(
peers: &DashMap<u64, PeerInfo>,
addr_to_node: &DashMap<SocketAddr, u64>,
session: &NetSession,
) -> Option<(SocketAddr, Arc<NetSession>)> {
session
.cached_node_id()
.and_then(|nid| {
peers.get(&nid).and_then(|p| {
(p.value().session.session_id() == session.session_id())
.then(|| (p.value().addr, p.value().session.clone()))
})
})
.or_else(|| {
let peer_addr = session.peer_addr();
let resolved = addr_to_node
.get(&peer_addr)
.and_then(|node_id| {
peers.get(&*node_id).and_then(|p| {
(p.value().session.session_id() == session.session_id()).then(|| {
(p.value().node_id, p.value().addr, p.value().session.clone())
})
})
})
.or_else(|| {
peers
.iter()
.find(|e| e.value().session.session_id() == session.session_id())
.map(|e| (e.value().node_id, e.value().addr, e.value().session.clone()))
});
resolved.map(|(nid, addr, sess)| {
session.cache_node_id(nid);
(addr, sess)
})
})
}
fn process_local_packet(
mut parsed: ParsedPacket,
from_node: u64,
session: &NetSession,
ctx: &DispatchCtx,
) {
let inbound = &ctx.inbound;
let num_shards = ctx.num_shards;
if !parsed.header.flags.is_handshake()
&& !parsed.header.flags.is_heartbeat()
&& !parsed.is_valid_length()
{
return;
}
let aad = parsed.header.aad();
let counter = u64::from_le_bytes(parsed.header.nonce[4..12].try_into().unwrap_or([0u8; 8]));
let rx_cipher = session.rx_cipher();
let payload = std::mem::take(&mut parsed.payload);
let decrypted = match rx_cipher.decrypt_to_bytes(counter, &aad, payload) {
Ok(d) => {
if !rx_cipher.try_admit_rx_counter(counter) {
return;
}
d
}
Err(_) => return,
};
if parsed.header.subprotocol_id == SUBPROTOCOL_MIGRATION {
let handler_guard = ctx.migration_handler.load();
if let Some(handler) = handler_guard.as_ref() {
let events = EventFrame::read_events(decrypted, parsed.header.event_count);
if events.is_empty() {
return;
}
if events.len() > 1 {
tracing::warn!(
n = events.len(),
from_node = from_node,
"migration subprotocol received multi-event frame \
(protocol design is single-event per frame); \
processing each message in order"
);
}
for payload in events {
match handler.handle_message(&payload, from_node) {
Ok(outbound) => {
const MAX_MIGRATION_LOOPBACK_DEPTH: usize = 32;
let mut pending: std::collections::VecDeque<_> = outbound.into();
let mut loopback_count: usize = 0;
while let Some(msg) = pending.pop_front() {
if msg.dest_node == ctx.local_node_id {
loopback_count += 1;
if loopback_count > MAX_MIGRATION_LOOPBACK_DEPTH {
tracing::warn!(
depth = loopback_count,
from_node = from_node,
cap = MAX_MIGRATION_LOOPBACK_DEPTH,
"migration handler loopback exceeded \
MAX_MIGRATION_LOOPBACK_DEPTH; dropping \
remaining queue to keep the dispatch \
task responsive to other peers. A \
correct handler should converge in a \
small bounded number of self-bounces.",
);
break;
}
match handler.handle_message(&msg.payload, ctx.local_node_id) {
Ok(more) => pending.extend(more),
Err(e) => {
tracing::warn!(
error = %e,
"migration handler loopback error",
);
}
}
continue;
}
let dest_session = ctx
.peers
.get(&msg.dest_node)
.map(|e| (e.value().addr, e.value().session.clone()));
if let Some((dest_addr, dest_sess)) = dest_session {
if ctx.partition_filter.contains(&dest_addr) {
continue;
}
let socket = ctx.socket.clone();
let payload = Bytes::from(msg.payload);
tokio::spawn(async move {
let pool = dest_sess.thread_local_pool();
let mut builder = pool.get();
let seq = {
let stream = dest_sess
.get_or_create_stream(SUBPROTOCOL_MIGRATION as u64);
stream.next_tx_seq()
};
let events = vec![payload];
let packet = builder.build_subprotocol(
SUBPROTOCOL_MIGRATION as u64,
seq,
&events,
PacketFlags::NONE,
SUBPROTOCOL_MIGRATION,
);
let _ = socket.send_to(&packet, dest_addr).await;
});
}
}
}
Err(e) => {
tracing::warn!(error = %e, "migration handler error");
}
}
} return; }
let events = EventFrame::read_events(decrypted, parsed.header.event_count);
for payload in events {
if let Some(reply) = synthesize_compute_not_supported_reply(&payload) {
let dest_session = ctx
.peers
.get(&from_node)
.map(|e| (e.value().addr, e.value().session.clone()));
if let Some((dest_addr, dest_sess)) = dest_session {
if !ctx.partition_filter.contains(&dest_addr) {
let socket = ctx.socket.clone();
tokio::spawn(async move {
let pool = dest_sess.thread_local_pool();
let mut builder = pool.get();
let seq = {
let stream = dest_sess
.get_or_create_stream(SUBPROTOCOL_MIGRATION as u64);
stream.next_tx_seq()
};
let events = vec![reply];
let packet = builder.build_subprotocol(
SUBPROTOCOL_MIGRATION as u64,
seq,
&events,
PacketFlags::NONE,
SUBPROTOCOL_MIGRATION,
);
let _ = socket.send_to(&packet, dest_addr).await;
});
}
}
}
}
return;
}
if parsed.header.subprotocol_id == SUBPROTOCOL_STREAM_WINDOW {
let events = EventFrame::read_events(decrypted, parsed.header.event_count);
for payload in events {
match StreamWindow::decode(&payload) {
Ok(grant) => {
if session.is_grant_quarantined(grant.stream_id) {
tracing::debug!(
stream_id = format!("{:#x}", grant.stream_id),
"dropping StreamWindow grant for recently-closed stream"
);
} else if let Some(state) = session.try_stream(grant.stream_id) {
state.apply_authoritative_grant(grant.total_consumed);
state.with_reliability(|r| r.on_ack(grant.ack_seq));
}
}
Err(e) => {
tracing::debug!(error = %e, "malformed StreamWindow grant");
}
}
}
return;
}
if parsed.header.subprotocol_id == SUBPROTOCOL_STREAM_NACK {
let events = EventFrame::read_events(decrypted, parsed.header.event_count);
let mut packets: Vec<Bytes> = Vec::new();
for payload in events {
let sn = match StreamNack::decode(&payload) {
Ok(sn) => sn,
Err(e) => {
tracing::debug!(error = %e, "malformed StreamNack");
continue;
}
};
if session.is_grant_quarantined(sn.stream_id) {
continue;
}
let Some(state) = session.try_stream(sn.stream_id) else {
continue;
};
let nack = super::protocol::NackPayload {
next_expected: sn.next_expected,
missing_bitmap: sn.missing_bitmap,
};
let descriptors = state.with_reliability(|r| r.on_nack(&nack));
if descriptors.is_empty() {
continue;
}
let pool = session.thread_local_pool();
let mut builder = pool.get();
for d in &descriptors {
let p = builder.build(d.stream_id, d.seq, &d.events, d.flags);
packets.push(p);
}
}
if !packets.is_empty() {
let socket = ctx.socket.clone();
let dest = parsed.source;
let control_stats = ctx.control_stats.clone();
tokio::spawn(async move {
for p in packets {
if socket.send_to(&p, dest).await.is_ok() {
control_stats
.retransmit_packets_sent
.fetch_add(1, Ordering::Relaxed);
}
}
});
}
return;
}
if parsed.header.subprotocol_id == SUBPROTOCOL_STREAM_ACK {
let events = EventFrame::read_events(decrypted, parsed.header.event_count);
for payload in events {
let ack = match StreamAckRanges::decode(&payload) {
Ok(a) => a,
Err(e) => {
tracing::debug!(error = %e, "malformed StreamAckRanges");
continue;
}
};
if session.is_grant_quarantined(ack.stream_id) {
continue;
}
let Some(state) = session.try_stream(ack.stream_id) else {
continue;
};
state.with_reliability(|r| r.on_ack_ranges(ack.ack_seq, &ack.ranges));
}
return;
}
if parsed.header.subprotocol_id == SUBPROTOCOL_STREAM_RESET {
let events = EventFrame::read_events(decrypted, parsed.header.event_count);
for payload in events {
let Ok(reset) = StreamReset::decode(&payload) else {
continue;
};
#[cfg(feature = "dataforts")]
if super::dataforts::blob::is_transfer_stream_id(reset.stream_id) {
if let Some(engine) = ctx.blob_transfer_engine.read().as_ref() {
engine.on_reset(reset.stream_id);
}
}
session.close_stream(reset.stream_id);
}
return;
}
if parsed.header.subprotocol_id == SUBPROTOCOL_CHANNEL_MEMBERSHIP {
let events = EventFrame::read_events(decrypted, parsed.header.event_count);
if events.is_empty() {
return;
}
for payload in events {
Self::handle_membership_message(&payload, from_node, ctx);
}
return;
}
if parsed.header.subprotocol_id == SUBPROTOCOL_CAPABILITY_ANN {
let events = EventFrame::read_events(decrypted, parsed.header.event_count);
if events.is_empty() {
return;
}
for payload in events {
Self::handle_capability_announcement(&payload, from_node, ctx);
}
return;
}
if parsed.header.subprotocol_id == SUBPROTOCOL_ROUTE_WITHDRAW {
let events = EventFrame::read_events(decrypted, parsed.header.event_count);
for payload in events {
Self::handle_route_withdrawal(&payload, from_node, ctx);
}
return;
}
if parsed.header.subprotocol_id == sensing::SUBPROTOCOL_SENSING_INTEREST {
if !ctx.enable_sensing_coalescing {
return;
}
if from_node == 0 {
return;
}
let events = EventFrame::read_events(decrypted, parsed.header.event_count);
for payload in events {
Self::handle_sensing_interest_frame(&payload, from_node, ctx);
}
return;
}
if parsed.header.subprotocol_id == sensing::SUBPROTOCOL_READINESS_ATTESTATION {
if !ctx.enable_sensing_coalescing {
return;
}
if from_node == 0 {
return;
}
let stream_id = parsed.header.stream_id;
let provisional = if stream_id == sensing::SUBPROTOCOL_READINESS_ATTESTATION as u64 {
false
} else if stream_id == sensing::SENSING_PROVISIONAL_STREAM {
true
} else {
ctx.sensing_counters
.protocol_invalid
.fetch_add(1, Ordering::Relaxed);
tracing::debug!(
from_node = format!("{:#x}", from_node),
stream_id,
"sensing: unknown 0x0C03 envelope stream dropped"
);
return;
};
let events = EventFrame::read_events(decrypted, parsed.header.event_count);
for payload in events {
Self::handle_sensing_attestation_frame(&payload, from_node, provisional, ctx);
}
return;
}
#[cfg(feature = "redex")]
if parsed.header.subprotocol_id == super::redex::SUBPROTOCOL_REDEX {
let events = EventFrame::read_events(decrypted, parsed.header.event_count);
if events.is_empty() {
return;
}
let router_guard = ctx.replication_inbound_router.read();
let Some(router) = router_guard.as_ref() else {
return;
};
if from_node == 0 {
return;
}
for payload in events {
Self::dispatch_replication_payload(&payload, from_node, router.as_ref());
}
return;
}
#[cfg(feature = "dataforts")]
if parsed.header.subprotocol_id == super::dataforts::blob::SUBPROTOCOL_BLOB_TRANSFER {
let events = EventFrame::read_events(decrypted, parsed.header.event_count);
if events.is_empty() {
return;
}
let engine_guard = ctx.blob_transfer_engine.read();
let Some(engine) = engine_guard.as_ref() else {
return;
};
if from_node == 0 {
return;
}
let stream_id = parsed.header.stream_id;
for payload in events {
engine.on_request(from_node, stream_id, &payload);
}
return;
}
#[cfg(feature = "meshdb")]
if parsed.header.subprotocol_id == super::behavior::meshdb::SUBPROTOCOL_MESHDB {
let events = EventFrame::read_events(decrypted, parsed.header.event_count);
if events.is_empty() {
return;
}
let router_guard = ctx.meshdb_inbound_router.read();
let Some(router) = router_guard.as_ref() else {
return;
};
if from_node == 0 {
return;
}
for payload in events {
if let Err(e) = router.try_route(from_node, &payload) {
tracing::debug!(error = %e, from_node, "meshdb: drop frame");
}
}
return;
}
if parsed.header.subprotocol_id == super::behavior::fold::SUBPROTOCOL_FOLD {
let events = EventFrame::read_events(decrypted, parsed.header.event_count);
if events.is_empty() {
return;
}
let router_guard = ctx.fold_router.read();
let Some(router) = router_guard.as_ref() else {
return;
};
if from_node == 0 {
return;
}
let Some(publisher) = ctx
.peer_entity_ids
.get(&from_node)
.map(|e| e.value().clone())
else {
tracing::debug!(from_node, "fold: missing peer EntityId, drop frame");
return;
};
for payload in events {
if let Err(e) = router.try_route(&publisher, &payload) {
tracing::debug!(error = %e, from_node, "fold: drop frame");
}
}
return;
}
#[cfg(feature = "nat-traversal")]
if parsed.header.subprotocol_id == super::traversal::SUBPROTOCOL_REFLEX {
use super::traversal::reflex;
let events = EventFrame::read_events(decrypted, parsed.header.event_count);
if events.is_empty() {
return;
}
if from_node == 0 {
return;
}
for payload in events {
let Some(msg) = reflex::decode(&payload) else {
continue;
};
match msg {
reflex::ReflexMsg::Request => {
let Some((dest_addr, dest_sess)) = ctx
.peers
.get(&from_node)
.map(|e| (e.value().addr, e.value().session.clone()))
else {
continue;
};
if ctx.partition_filter.contains(&dest_addr) {
continue;
}
let response = reflex::encode_response(dest_addr);
let socket = ctx.socket.clone();
tokio::spawn(async move {
let pool = dest_sess.thread_local_pool();
let mut builder = pool.get();
let seq = {
let stream = dest_sess.get_or_create_stream(
super::traversal::SUBPROTOCOL_REFLEX as u64,
);
stream.next_tx_seq()
};
let events = vec![response];
let packet = builder.build_subprotocol(
super::traversal::SUBPROTOCOL_REFLEX as u64,
seq,
&events,
PacketFlags::NONE,
super::traversal::SUBPROTOCOL_REFLEX,
);
let _ = socket.send_to(&packet, dest_addr).await;
});
}
reflex::ReflexMsg::Response(observed) => {
if let Some((_, (_gen, tx))) = ctx.pending_reflex_probes.remove(&from_node)
{
let _ = tx.send(observed);
}
}
}
}
return;
}
#[cfg(feature = "nat-traversal")]
if parsed.header.subprotocol_id == super::traversal::SUBPROTOCOL_RENDEZVOUS {
use super::traversal::rendezvous;
let events = EventFrame::read_events(decrypted, parsed.header.event_count);
if events.is_empty() {
return;
}
if from_node == 0 {
return;
}
for payload in events {
let Some(msg) = rendezvous::decode(&payload) else {
continue;
};
match msg {
rendezvous::RendezvousMsg::PunchRequest(req) => {
Self::handle_punch_request(from_node, req, ctx);
}
rendezvous::RendezvousMsg::PunchIntroduce(intro) => {
let took = ctx
.pending_punch_introduces
.remove_if(&intro.peer, |_, (_gen, expected_coord, _pid, _)| {
*expected_coord == from_node
});
if let Some((_, (_gen, _expected, _pid, tx))) = took {
let _ = tx.send(PunchIntroduceOutcome::Introduce(intro));
Self::schedule_punch(from_node, intro, ctx, None);
continue;
}
if ctx.pending_punch_introduces.contains_key(&intro.peer) {
tracing::trace!(
from = from_node,
target = intro.peer,
"rendezvous: PunchIntroduce from non-coordinator session peer; dropping"
);
continue;
}
let Some(slot) =
Self::unsolicited_introduce_permitted(&intro, from_node, ctx)
else {
continue;
};
Self::schedule_punch(from_node, intro, ctx, Some(slot));
}
rendezvous::RendezvousMsg::PunchAck(ack) => {
if ack.to_peer == ctx.local_node_id {
let took = ctx
.pending_punch_acks
.remove_if(&ack.from_peer, |_, (_gen, expected_coord, _)| {
*expected_coord == from_node
});
if let Some((_, (_gen, _expected, tx))) = took {
let _ = tx.send(ack);
} else if ctx.pending_punch_acks.contains_key(&ack.from_peer) {
tracing::trace!(
from = from_node,
claimed = ack.from_peer,
"rendezvous: PunchAck not forwarded by recorded coordinator; dropping"
);
continue;
}
} else {
Self::forward_punch_ack(ack, ctx);
}
}
rendezvous::RendezvousMsg::PunchReject(rej) => {
let took = ctx.pending_punch_introduces.remove_if(
&rej.target,
|_, (_gen, expected_coord, pid, _)| {
*expected_coord == from_node && *pid == rej.punch_id
},
);
if let Some((_, (_gen, _expected, _pid, tx))) = took {
let _ = tx.send(PunchIntroduceOutcome::Rejected(rej.reason));
} else {
tracing::trace!(
from = from_node,
target = rej.target,
punch_id = rej.punch_id,
reason = rej.reason.kind(),
"rendezvous: PunchReject with no matching waiter / \
non-coordinator sender / stale punch_id; dropping"
);
}
}
}
}
return;
}
if parsed.header.subprotocol_id != 0 {
tracing::trace!(
from_node = format!("{:#x}", from_node),
subprotocol_id = format!("{:#06x}", parsed.header.subprotocol_id),
"dispatch: unknown subprotocol id dropped (forward-compat)"
);
return;
}
let payload_bytes = (decrypted.len() + PACKET_WIRE_OVERHEAD) as u64;
let events = EventFrame::read_events(decrypted, parsed.header.event_count);
let stream_id = parsed.header.stream_id;
let shard_id = if num_shards > 0 {
(stream_id % num_shards as u64) as u16
} else {
0
};
let grant_bytes = {
let reliable_pkt = parsed.header.flags.contains(PacketFlags::RELIABLE);
let stream = session
.get_or_create_stream_for_packet(stream_id, ctx.default_reliable || reliable_pkt);
let accepted = stream.with_reliability(|r| r.on_receive(parsed.header.sequence));
if accepted {
stream.update_rx_seq(parsed.header.sequence);
stream.on_bytes_consumed(payload_bytes)
} else {
None
}
};
if let Some(total_consumed) = grant_bytes {
let peer = Self::resolve_grant_peer(&ctx.peers, &ctx.addr_to_node, session);
if let Some((peer_addr, peer_session)) = peer {
if !ctx.partition_filter.contains(&peer_addr) {
{
let mut guard = ctx.pending_stream_grants.lock();
guard.insert(
(peer_session.session_id(), stream_id),
PendingStreamGrant {
session: peer_session,
peer_addr,
total_consumed,
},
);
}
ctx.pending_stream_grants_notify.notify_one();
}
}
}
#[cfg(feature = "cortex")]
if let Some(entry) = ctx.rpc_inbound_dispatchers.get(&parsed.header.channel_hash) {
enum Snapshot {
Single(
ChannelHash,
crate::adapter::net::cortex::RpcInboundDispatcher,
),
Many(
Vec<(
ChannelHash,
crate::adapter::net::cortex::RpcInboundDispatcher,
)>,
),
}
let snapshot = match entry.as_slice() {
[] => {
drop(entry);
return;
}
[only] => {
let (c, d) = only.clone();
Snapshot::Single(c, d)
}
many => Snapshot::Many(many.to_vec()),
};
drop(entry);
let origin_hash = parsed.header.origin_hash;
let session_id = session.session_id();
let from_node = session.cached_node_id().or_else(|| {
let resolved = ctx
.addr_to_node
.get(&session.peer_addr())
.and_then(|nid| {
ctx.peers.get(&*nid).and_then(|p| {
(p.value().session.session_id() == session_id).then_some(*nid)
})
})
.or_else(|| {
ctx.peers
.iter()
.find(|e| e.value().session.session_id() == session_id)
.map(|e| e.value().node_id)
});
if let Some(nid) = resolved {
session.cache_node_id(nid);
}
resolved
});
let Some(from_node) = from_node else {
tracing::warn!(
target: "mesh.rpc",
session_id = session_id,
peer_addr = ?session.peer_addr(),
"dropping cortex-RPC event: wire session has no resolvable NodeId; \
refusing to deliver under sentinel binding"
);
return;
};
match snapshot {
Snapshot::Single(canonical, disp) => {
for event_data in events.into_iter() {
disp(crate::adapter::net::cortex::RpcInboundEvent {
channel_hash: canonical,
origin_hash,
from_node,
payload: event_data,
});
}
}
Snapshot::Many(pairs) => {
for event_data in events.into_iter() {
for (canonical, disp) in &pairs {
disp(crate::adapter::net::cortex::RpcInboundEvent {
channel_hash: *canonical,
origin_hash,
from_node,
payload: event_data.clone(),
});
}
}
}
}
return;
}
#[cfg(feature = "dataforts")]
if super::dataforts::blob::is_transfer_stream_id(stream_id) {
let engine_guard = ctx.blob_transfer_engine.read();
if let Some(engine) = engine_guard.as_ref() {
engine.on_data(stream_id, parsed.header.sequence, events);
}
return;
}
#[cfg(feature = "dataforts")]
let greedy = ctx.greedy_observer.read().clone();
#[cfg(feature = "dataforts")]
let chain_caps: Option<
std::sync::Arc<crate::adapter::net::behavior::capability::CapabilitySet>,
> = if greedy.is_some() {
let origin_hash: u64 = parsed.header.origin_hash;
let publisher_node = ctx.origin_hash_to_node.get(&origin_hash).map(|v| *v);
Some(match publisher_node {
Some(nid) => ctx
.capability_set_cache
.get_or_synthesize(&ctx.capability_fold, nid),
None => std::sync::Arc::new(
crate::adapter::net::behavior::capability::CapabilitySet::new(),
),
})
} else {
None
};
let queue = inbound.entry(shard_id).or_default();
let seq = parsed.header.sequence;
for (i, event_data) in events.into_iter().enumerate() {
#[cfg(feature = "dataforts")]
if let (Some(observer), Some(caps)) = (&greedy, &chain_caps) {
observer.observe_event(
parsed.header.channel_hash,
parsed.header.origin_hash,
caps.clone(),
event_data.clone(),
);
}
use std::fmt::Write;
let mut event_id = String::with_capacity(24);
let _ = write!(event_id, "{}:{}", seq, i);
queue.push(StoredEvent::new(event_id, event_data, seq, shard_id));
}
}
pub fn control_plane_stats(&self) -> Arc<ControlPlaneStats> {
self.control_stats.clone()
}
fn spawn_stream_grant_drainer_loop(&self) -> JoinHandle<()> {
let socket = self.socket.clone();
let partition_filter = self.partition_filter.clone();
let pending = self.pending_stream_grants.clone();
let notify = self.pending_stream_grants_notify.clone();
let shutdown = self.shutdown.clone();
let shutdown_notify = self.shutdown_notify.clone();
let control_stats = self.control_stats.clone();
let ack_ranges_enabled = self.config.enable_stream_ack_ranges;
let ack_cache = self.ack_ranges_peer_cache.clone();
let session_id_to_node = self.session_id_to_node.clone();
let capability_fold = self.capability_fold.clone();
tokio::spawn(async move {
while !shutdown.load(Ordering::Acquire) {
tokio::select! {
_ = notify.notified() => {}
_ = tokio::time::sleep(STREAM_GRANT_DRAIN_INTERVAL) => {}
_ = shutdown_notify.notified() => {
if shutdown.load(Ordering::Acquire) {
break;
}
}
}
let drained = std::mem::take(&mut *pending.lock());
if drained.is_empty() {
continue;
}
for (session_id, (session, peer_addr, grants)) in group_grants_by_session(drained) {
if partition_filter.contains(&peer_addr) {
continue;
}
let emit_ack_ranges = ack_ranges_enabled
&& peer_supports_ack_ranges(
&ack_cache,
&session_id_to_node,
&capability_fold,
session_id,
);
let (grant_entries, nack_events, ack_events) =
build_session_control_events(&session, &grants, emit_ack_ranges);
let pool = session.thread_local_pool();
let mut builder = pool.get();
for chunk in grant_entries.chunks(GRANT_EVENTS_PER_PACKET) {
let events: Vec<Bytes> = chunk.iter().map(|(_, e)| e.clone()).collect();
let seq = session.next_control_tx_seq();
let packet = builder.build_subprotocol(
CONTROL_STREAM_ID,
seq,
&events,
PacketFlags::NONE,
SUBPROTOCOL_STREAM_WINDOW,
);
if let Err(e) = socket.send_to(&packet, peer_addr).await {
tracing::debug!(error = %e, "StreamWindow grant send failed");
continue;
}
for (stream_id, _) in chunk {
if let Some(state) = session.try_stream(*stream_id) {
state.note_grant_sent();
}
}
ControlPlaneStats::record_packet(
&control_stats.grant_packets_sent,
&control_stats.grant_events_sent,
chunk.len(),
);
}
emit_control_chunks(
&socket,
&mut builder,
&session,
peer_addr,
&nack_events,
SUBPROTOCOL_STREAM_NACK,
&control_stats.nack_packets_sent,
&control_stats.nack_events_sent,
)
.await;
emit_control_chunks(
&socket,
&mut builder,
&session,
peer_addr,
&ack_events,
SUBPROTOCOL_STREAM_ACK,
&control_stats.ack_range_packets_sent,
&control_stats.ack_range_events_sent,
)
.await;
}
}
})
}
fn spawn_retransmit_loop(&self) -> JoinHandle<()> {
let peers = self.peers.clone();
let socket = self.socket.clone();
let shutdown = self.shutdown.clone();
let shutdown_notify = self.shutdown_notify.clone();
let control_stats = self.control_stats.clone();
let ack_ranges_enabled = self.config.enable_stream_ack_ranges;
let ack_cache = self.ack_ranges_peer_cache.clone();
let session_id_to_node = self.session_id_to_node.clone();
let capability_fold = self.capability_fold.clone();
tokio::spawn(async move {
while !shutdown.load(Ordering::Acquire) {
tokio::select! {
_ = tokio::time::sleep(RETRANSMIT_TICK) => {}
_ = shutdown_notify.notified() => {
if shutdown.load(Ordering::Acquire) {
break;
}
}
}
let mut work: Vec<(
SocketAddr,
Arc<NetSession>,
Vec<Arc<super::RetransmitDescriptor>>,
)> = Vec::new();
for peer in peers.iter() {
let due = peer.value().session.collect_timed_out_retransmits();
if !due.is_empty() {
work.push((peer.value().addr, peer.value().session.clone(), due));
}
}
for (addr, session, due) in work {
let pool = session.thread_local_pool();
let mut builder = pool.get();
for d in due {
let packet = builder.build(d.stream_id, d.seq, &d.events, d.flags);
if socket.send_to(&packet, addr).await.is_ok() {
control_stats
.retransmit_packets_sent
.fetch_add(1, Ordering::Relaxed);
}
}
}
let mut resets: Vec<(SocketAddr, Arc<NetSession>, Vec<u64>)> = Vec::new();
for peer in peers.iter() {
let failed = peer.value().session.take_failed_stream_ids();
if !failed.is_empty() {
resets.push((peer.value().addr, peer.value().session.clone(), failed));
}
}
for (addr, session, failed) in resets {
let pool = session.thread_local_pool();
let mut builder = pool.get();
let reset_events: Vec<Bytes> = failed
.into_iter()
.map(|stream_id| {
Bytes::copy_from_slice(&StreamReset { stream_id }.encode())
})
.collect();
emit_control_chunks(
&socket,
&mut builder,
&session,
addr,
&reset_events,
SUBPROTOCOL_STREAM_RESET,
&control_stats.reset_packets_sent,
&control_stats.reset_events_sent,
)
.await;
}
struct TickGaps {
addr: SocketAddr,
session: Arc<NetSession>,
reports: Vec<super::session::GapReport>,
}
let mut work: Vec<TickGaps> = Vec::new();
for peer in peers.iter() {
let session = &peer.value().session;
let want_ranges = ack_ranges_enabled
&& peer_supports_ack_ranges(
&ack_cache,
&session_id_to_node,
&capability_fold,
session.session_id(),
);
let reports = session.collect_gap_reports(want_ranges, MAX_ACK_RANGES);
if !reports.is_empty() {
work.push(TickGaps {
addr: peer.value().addr,
session: session.clone(),
reports,
});
}
}
for TickGaps {
addr,
session,
reports,
} in work
{
let pool = session.thread_local_pool();
let mut builder = pool.get();
let nack_events: Vec<Bytes> = reports
.iter()
.map(|r| {
Bytes::copy_from_slice(
&StreamNack {
stream_id: r.stream_id,
next_expected: r.nack.next_expected,
missing_bitmap: r.nack.missing_bitmap,
}
.encode(),
)
})
.collect();
emit_control_chunks(
&socket,
&mut builder,
&session,
addr,
&nack_events,
SUBPROTOCOL_STREAM_NACK,
&control_stats.nack_packets_sent,
&control_stats.nack_events_sent,
)
.await;
let ack_events: Vec<Bytes> = reports
.into_iter()
.filter(|r| !r.ranges.is_empty())
.map(|r| {
Bytes::from(
StreamAckRanges {
stream_id: r.stream_id,
ack_seq: r.ack_seq,
ranges: r.ranges,
}
.encode(),
)
})
.collect();
emit_control_chunks(
&socket,
&mut builder,
&session,
addr,
&ack_events,
SUBPROTOCOL_STREAM_ACK,
&control_stats.ack_range_packets_sent,
&control_stats.ack_range_events_sent,
)
.await;
}
}
})
}
fn spawn_heartbeat_loop(&self) -> JoinHandle<()> {
let socket = self.socket.clone();
let peers = self.peers.clone();
let addr_to_node = self.addr_to_node.clone();
let peer_addrs = self.peer_addrs.clone();
let session_id_to_node = self.session_id_to_node.clone();
let ack_ranges_peer_cache = self.ack_ranges_peer_cache.clone();
let route_withdraw_gate = self.route_withdraw_gate.clone();
let failure_detector = self.failure_detector.clone();
let enable_sensing_coalescing = self.config.enable_sensing_coalescing;
let sensing_interest_table = self.sensing_interest_table.clone();
let sensing_emitter = self.sensing_emitter.clone();
let sensing_observations = self.sensing_observations.clone();
let sensing_interest_ttl = self.config.sensing_interest_ttl;
let sensing_overlay_changed = self.sensing_overlay_changed.clone();
let continuity_factor = self.config.continuity_factor;
let sensing_capability_interests = self.sensing_capability_interests.clone();
#[cfg(feature = "redex")]
let sensing_leader = self.sensing_leader.clone();
let local_node_id = self.node_id;
let interval = self.config.heartbeat_interval;
let shutdown = self.shutdown.clone();
let shutdown_notify = self.shutdown_notify.clone();
let partition_filter = self.partition_filter.clone();
let proximity_graph = self.proximity_graph.clone();
let router = self.router.clone();
let max_route_age = self.config.session_timeout.saturating_mul(3);
let dead_peer_timeout = self.config.session_timeout.saturating_mul(30);
let stream_idle_timeout = self.config.stream_idle_timeout;
let max_streams = self.config.max_streams;
tokio::spawn(async move {
while !shutdown.load(Ordering::Acquire) {
tokio::select! {
_ = tokio::time::sleep(interval) => {
let pw = proximity_graph.create_pingwave(HealthStatus::Healthy);
let pw_bytes = pw.to_bytes();
let snapshot: Vec<(SocketAddr, Arc<NetSession>)> = peers
.iter()
.filter_map(|entry| {
let peer_addr = entry.value().addr;
if partition_filter.contains(&peer_addr) {
None
} else {
Some((peer_addr, entry.value().session.clone()))
}
})
.collect();
for (peer_addr, session) in snapshot {
let packet = session.build_heartbeat();
let _ = socket.send_to(&packet, peer_addr).await;
let _ = socket.send_to(&pw_bytes, peer_addr).await;
}
router.routing_table().sweep_stale(max_route_age);
proximity_graph.sweep_stale_edges(max_route_age);
if enable_sensing_coalescing {
#[cfg(feature = "redex")]
{
let deliveries = {
let mut slot = sensing_leader.lock();
match slot.as_mut() {
Some(leader) => {
leader.sweep(Instant::now());
leader.poll(Instant::now())
}
None => Vec::new(),
}
};
if !deliveries.is_empty() {
dispatch_sensing_leader_deliveries(
&socket,
&peers,
&addr_to_node,
&router,
&partition_filter,
local_node_id,
&sensing_observations,
&sensing_overlay_changed,
continuity_factor,
deliveries,
Instant::now(),
);
}
}
let emitter_stamp =
sensing_emitter.lock().as_ref().map(|e| e.stamp());
let actions =
sensing_interest_table.lock().expire(Instant::now());
for (key, action) in actions {
if action == sensing::UpstreamAction::Deregister {
sensing_observations.lock().reclaim_branch(&key);
}
if let sensing::UpstreamAction::Register { strictest } = action {
sensing_observations
.lock()
.update_upstream_interval(&key, Some(strictest));
}
if key.provider == local_node_id {
if action == sensing::UpstreamAction::Deregister {
if let (Some(emitter), Some(stamp)) =
(sensing_emitter.lock().as_mut(), emitter_stamp)
{
emitter.retire_if_stale(
&key.interest.interest_digest,
stamp,
);
}
}
continue;
}
if action == sensing::UpstreamAction::Deregister {
let frame = sensing::SensingInterestFrame::Deregister {
interest_digest: key.interest.interest_digest,
target: Some(key.provider),
};
if let Ok(bytes) = sensing::encode_interest_frame(&frame) {
spawn_sensing_frame_send(
&socket,
&peers,
&addr_to_node,
&router,
&partition_filter,
local_node_id,
key.provider,
sensing::SUBPROTOCOL_SENSING_INTEREST as u64,
sensing::SUBPROTOCOL_SENSING_INTEREST,
bytes,
);
}
}
}
let sweep_now = Instant::now();
let aged: Vec<sensing::ProviderInterestKey> = {
let observations = sensing_observations.lock();
observations
.refusals
.iter()
.filter(|(_, (_, stored_at))| {
sweep_now.duration_since(*stored_at)
>= sensing_interest_ttl
})
.map(|(key, _)| key.clone())
.collect()
};
if !aged.is_empty() {
let dead: Vec<sensing::ProviderInterestKey> = {
let table = sensing_interest_table.lock();
aged.into_iter()
.filter(|key| !table.has_entry(key))
.collect()
};
let mut observations = sensing_observations.lock();
let providers: Vec<u64> =
dead.iter().map(|key| key.provider).collect();
for key in dead {
observations.refusals.remove(&key);
}
observations.reclaim_orphan_epochs(providers);
}
let poll_now = Instant::now();
let mut overlay_moved = false;
let (continuities, pending) = {
let mut observations = sensing_observations.lock();
let mut continuities = Vec::new();
for (branch, cell) in observations.upstream.iter_mut() {
cell.expire_if_due(poll_now);
continuities.push((branch.clone(), cell.continuity()));
}
for cell in observations.consumer_cells.values_mut() {
let before = cell.projected();
cell.expire_if_due(poll_now);
overlay_moved |= cell.projected() != before;
}
let slot_keys: Vec<(
(sensing::ProviderInterestKey, sensing::DownstreamId),
bool,
)> = observations
.slots
.iter()
.map(|(key, slot)| (key.clone(), slot.pending))
.collect();
(continuities, slot_keys)
};
let mut live_pending = Vec::new();
let mut dead_slots = Vec::new();
{
let mut table = sensing_interest_table.lock();
for (branch, continuity) in continuities {
table.set_upstream_continuity(&branch, continuity);
}
for ((branch, downstream), pending) in pending {
match table.downstream_entry(&branch, downstream) {
Some(row) if row.expires_at > poll_now => {
if pending {
live_pending.push((
branch,
downstream,
row.requested_sample_interval,
));
}
}
_ => dead_slots.push((branch, downstream)),
}
}
}
let mut flushes: Vec<(u64, Vec<u8>, bool)> = Vec::new();
#[cfg(feature = "redex")]
let mut leader_feeds: Vec<(
sensing::ProviderInterestKey,
sensing::ReadinessAttestation,
bool,
)> = Vec::new();
{
let mut observations = sensing_observations.lock();
for key in dead_slots {
observations.slots.remove(&key);
}
for (branch, downstream, interval) in live_pending {
let bearing = observations
.upstream
.get(&branch)
.map(|cell| {
cell.continuity()
== sensing::Continuity::Established
})
.unwrap_or(false);
let Some(cached) =
observations.latest.get(&branch).cloned()
else {
continue;
};
let Some(slot) = observations
.slots
.get_mut(&(branch.clone(), downstream))
else {
continue;
};
let newer = slot.last_delivered.is_none_or(|prev| {
(cached.origin_incarnation, cached.seq) > prev
});
if slot.pending && newer && poll_now >= slot.next_due {
slot.last_status = Some(cached.status);
slot.last_delivered =
Some((cached.origin_incarnation, cached.seq));
slot.next_due = poll_now + interval;
slot.pending = false;
match downstream {
sensing::DownstreamId::Peer(node) => {
if let Ok(bytes) =
sensing::encode_attestation(&cached)
{
flushes.push((node, bytes, bearing));
}
}
sensing::DownstreamId::Local => {
overlay_moved |= observations
.feed_consumer_cell(
&branch,
&cached,
bearing,
interval,
continuity_factor,
poll_now,
);
}
#[cfg(feature = "redex")]
sensing::DownstreamId::Leader => {
leader_feeds.push((
branch.clone(),
cached.clone(),
bearing,
));
}
#[cfg(not(feature = "redex"))]
sensing::DownstreamId::Leader => {}
}
}
}
}
#[cfg(feature = "redex")]
for (branch, cached, bearing) in leader_feeds {
let Ok(semantic) = sensing::semantic_attestation(
&branch.interest,
&cached,
) else {
continue;
};
let deliveries = {
let mut slot = sensing_leader.lock();
match slot.as_mut() {
Some(leader) => leader.on_attestation(
poll_now, &semantic, bearing,
),
None => Vec::new(),
}
};
dispatch_sensing_leader_deliveries(
&socket,
&peers,
&addr_to_node,
&router,
&partition_filter,
local_node_id,
&sensing_observations,
&sensing_overlay_changed,
continuity_factor,
deliveries,
poll_now,
);
}
if overlay_moved {
sensing_overlay_changed.send_modify(|generation| {
*generation = generation.wrapping_add(1);
});
}
for (node, bytes, bearing) in flushes {
let stream_id = if bearing {
sensing::SUBPROTOCOL_READINESS_ATTESTATION as u64
} else {
sensing::SENSING_PROVISIONAL_STREAM
};
spawn_sensing_frame_send(
&socket,
&peers,
&addr_to_node,
&router,
&partition_filter,
local_node_id,
node,
stream_id,
sensing::SUBPROTOCOL_READINESS_ATTESTATION,
bytes,
);
}
let live_interests: std::collections::HashSet<
sensing::CapabilityInterestKey,
> = {
let mut interests =
sensing_capability_interests.lock();
interests
.retain(|_, expectation| {
expectation.expires_at > poll_now
});
interests.keys().cloned().collect()
};
let branch_keys: std::collections::HashSet<
sensing::ProviderInterestKey,
> = {
let observations = sensing_observations.lock();
observations
.latest
.keys()
.chain(observations.upstream.keys())
.chain(observations.consumer_cells.keys())
.chain(
observations
.slots
.keys()
.map(|(branch, _)| branch),
)
.cloned()
.collect()
};
let mut dead_branches = Vec::new();
let mut dead_cells = Vec::new();
{
let table = sensing_interest_table.lock();
for key in branch_keys {
if live_interests.contains(&key.interest) {
continue;
}
if table.downstreams(&key, poll_now).is_empty() {
dead_branches.push(key);
continue;
}
let local_live = table
.downstream_entry(
&key,
sensing::DownstreamId::Local,
)
.is_some_and(|row| {
row.expires_at > poll_now
});
if !local_live {
dead_cells.push(key);
}
}
}
if !dead_branches.is_empty() || !dead_cells.is_empty() {
let mut projection_dropped = false;
let mut observations = sensing_observations.lock();
for key in dead_branches {
projection_dropped |= observations
.consumer_cells
.contains_key(&key);
observations.reclaim_branch(&key);
}
for key in dead_cells {
projection_dropped |= observations
.consumer_cells
.remove(&key)
.is_some();
}
drop(observations);
if projection_dropped {
sensing_overlay_changed.send_modify(|generation| {
*generation = generation.wrapping_add(1);
});
}
}
}
sweep_ack_ranges_cache(
&ack_ranges_peer_cache,
ACK_RANGES_CAP_CACHE_MAX_AGE,
);
for entry in peers.iter() {
entry.value().session.evict_idle_streams(
stream_idle_timeout,
max_streams,
"idle_timeout",
);
}
let _ = failure_detector.check_all();
let failed = failure_detector.failed_nodes();
for node_id in failed {
let still_silent = match peers.get(&node_id) {
Some(e) => e.value().session.is_timed_out(dead_peer_timeout),
None => false,
};
if !still_silent {
continue;
}
if let Some((_, old_info)) = peers.remove(&node_id) {
let old_addr = old_info.addr;
let old_session_id = old_info.session.session_id();
addr_to_node
.remove_if(&old_addr, |_, n| *n == node_id);
peer_addrs
.remove_if(&node_id, |_, addr| *addr == old_addr);
session_id_to_node
.remove_if(&old_session_id, |_, n| *n == node_id);
ack_ranges_peer_cache.remove(&node_id);
tracing::info!(
node_id = format!("{:#x}", node_id),
"evicted permanently-dead peer from peer map",
);
}
failure_detector.remove(node_id);
route_withdraw_gate.forget_sender(node_id);
}
}
_ = shutdown_notify.notified() => {
break;
}
}
}
})
}
pub async fn send_to_peer(
&self,
peer_addr: SocketAddr,
batch: &Batch,
) -> Result<(), AdapterError> {
if self.partition_filter.contains(&peer_addr) {
return Ok(());
}
let node_id = self
.addr_to_node
.get(&peer_addr)
.map(|e| *e.value())
.ok_or_else(|| AdapterError::Connection("unknown peer".into()))?;
let peer = self
.peers
.get(&node_id)
.ok_or_else(|| AdapterError::Connection("unknown peer".into()))?;
let session = &peer.session;
let stream_id = batch.shard_id as u64;
let reliable = {
let stream = session.get_or_create_stream(stream_id);
stream.with_reliability(|r| r.needs_ack())
};
let pool = session.thread_local_pool();
let mut builder = pool.get();
let mut current_batch: Vec<Bytes> = Vec::with_capacity(64);
let mut current_size = 0usize;
for event in &batch.events {
let event_bytes = event.raw.clone();
let frame_size = EventFrame::LEN_SIZE + event_bytes.len();
if current_size + frame_size > protocol::MAX_PAYLOAD_SIZE && !current_batch.is_empty() {
let seq = {
let stream = session.get_or_create_stream(stream_id);
stream.next_tx_seq()
};
let flags = if reliable {
PacketFlags::RELIABLE
} else {
PacketFlags::NONE
};
let packet = builder.build(stream_id, seq, ¤t_batch, flags);
self.socket
.send_to(&packet, peer_addr)
.await
.map_err(|e| AdapterError::Connection(format!("send failed: {}", e)))?;
current_batch.clear();
current_size = 0;
}
current_batch.push(event_bytes);
current_size += frame_size;
}
if !current_batch.is_empty() {
let seq = {
let stream = session.get_or_create_stream(stream_id);
stream.next_tx_seq()
};
let flags = if reliable {
PacketFlags::RELIABLE
} else {
PacketFlags::NONE
};
let packet = builder.build(stream_id, seq, ¤t_batch, flags);
self.socket
.send_to(&packet, peer_addr)
.await
.map_err(|e| AdapterError::Connection(format!("send failed: {}", e)))?;
}
drop(builder);
session.touch();
Ok(())
}
pub async fn send_routed(&self, dest_node_id: u64, batch: &Batch) -> Result<(), AdapterError> {
let (dest_addr, session) = self
.peers
.get(&dest_node_id)
.map(|e| (e.value().addr, e.value().session.clone()))
.ok_or_else(|| {
AdapterError::Connection(format!("no session for node {:#x}", dest_node_id))
})?;
let next_hop = self
.router
.routing_table()
.lookup(dest_node_id)
.unwrap_or(dest_addr);
let stream_id = batch.shard_id as u64;
let reliable = {
let stream = session.get_or_create_stream(stream_id);
stream.with_reliability(|r| r.needs_ack())
};
let pool = session.thread_local_pool();
let mut builder = pool.get();
let routing_header = RoutingHeader::new(dest_node_id, self.node_id as u32, 8);
let routing_bytes = routing_header.to_bytes();
let mut current_batch: Vec<Bytes> = Vec::with_capacity(64);
let mut current_size = 0usize;
for event in &batch.events {
let event_bytes = event.raw.clone();
let frame_size = EventFrame::LEN_SIZE + event_bytes.len();
if current_size + frame_size > protocol::MAX_PAYLOAD_SIZE && !current_batch.is_empty() {
let seq = {
let stream = session.get_or_create_stream(stream_id);
stream.next_tx_seq()
};
let flags = if reliable {
PacketFlags::RELIABLE
} else {
PacketFlags::NONE
};
let net_packet = builder.build(stream_id, seq, ¤t_batch, flags);
let mut routed =
bytes::BytesMut::with_capacity(ROUTING_HEADER_SIZE + net_packet.len());
routed.extend_from_slice(&routing_bytes);
routed.extend_from_slice(&net_packet);
self.socket
.send_to(&routed, next_hop)
.await
.map_err(|e| AdapterError::Connection(format!("send failed: {}", e)))?;
current_batch.clear();
current_size = 0;
}
current_batch.push(event_bytes);
current_size += frame_size;
}
if !current_batch.is_empty() {
let seq = {
let stream = session.get_or_create_stream(stream_id);
stream.next_tx_seq()
};
let flags = if reliable {
PacketFlags::RELIABLE
} else {
PacketFlags::NONE
};
let net_packet = builder.build(stream_id, seq, ¤t_batch, flags);
let mut routed = bytes::BytesMut::with_capacity(ROUTING_HEADER_SIZE + net_packet.len());
routed.extend_from_slice(&routing_bytes);
routed.extend_from_slice(&net_packet);
self.socket
.send_to(&routed, next_hop)
.await
.map_err(|e| AdapterError::Connection(format!("send failed: {}", e)))?;
}
drop(builder);
session.touch();
Ok(())
}
pub fn roster(&self) -> &Arc<SubscriberRoster> {
&self.roster
}
#[cfg(feature = "cortex")]
pub fn register_rpc_inbound(
&self,
channel_hash: ChannelHash,
dispatcher: crate::adapter::net::cortex::RpcInboundDispatcher,
) -> Option<crate::adapter::net::cortex::RpcInboundDispatcher> {
let wire = channel_hash as u16;
let mut entry = self.rpc_inbound_dispatchers.entry(wire).or_default();
for (existing_canonical, existing_disp) in entry.iter_mut() {
if *existing_canonical == channel_hash {
return Some(std::mem::replace(existing_disp, dispatcher));
}
}
entry.push((channel_hash, dispatcher));
None
}
#[cfg(feature = "cortex")]
pub fn unregister_rpc_inbound(
&self,
channel_hash: ChannelHash,
) -> Option<crate::adapter::net::cortex::RpcInboundDispatcher> {
let wire = channel_hash as u16;
let removed = {
let mut entry = self.rpc_inbound_dispatchers.get_mut(&wire)?;
let pos = entry.iter().position(|(c, _)| *c == channel_hash)?;
let (_, removed) = entry.remove(pos);
removed
};
self.rpc_inbound_dispatchers
.remove_if(&wire, |_, v| v.is_empty());
Some(removed)
}
#[cfg(feature = "cortex")]
pub fn rpc_inbound_dispatcher_registered(&self, channel_hash: ChannelHash) -> bool {
self.rpc_inbound_dispatchers
.get(&(channel_hash as u16))
.map(|entry| entry.iter().any(|(c, _)| *c == channel_hash))
.unwrap_or(false)
}
#[cfg(feature = "cortex")]
pub(super) fn rpc_client_pending_arc(
&self,
) -> Arc<crate::adapter::net::cortex::RpcClientPending> {
self.rpc_client_pending.clone()
}
#[cfg(feature = "cortex")]
pub(super) fn rpc_round_robin_cursor_arc(&self) -> Arc<std::sync::atomic::AtomicU64> {
self.rpc_round_robin_cursor.clone()
}
#[cfg(feature = "cortex")]
pub(super) fn rpc_reply_subscriptions_arc(
&self,
) -> Arc<dashmap::DashMap<(u64, u64), Arc<str>>> {
self.rpc_reply_subscriptions.clone()
}
#[cfg(feature = "cortex")]
pub(super) fn public_key_origin_hash(&self) -> u64 {
self.identity.entity_id().origin_hash()
}
#[cfg(feature = "cortex")]
pub(super) fn rpc_route_for_service(&self, service: &str) -> Result<Arc<RpcRoute>, String> {
if let Some(r) = self.rpc_route_cache.get(service) {
return Ok(Arc::clone(r.value()));
}
let route = Arc::new(self.build_rpc_route(service)?);
if self.rpc_route_cache.len() < RPC_ROUTE_CACHE_SOFT_CAP {
let entry = self
.rpc_route_cache
.entry(service.to_string())
.or_insert_with(|| Arc::clone(&route));
Ok(Arc::clone(entry.value()))
} else {
Ok(route)
}
}
#[cfg(feature = "cortex")]
fn build_rpc_route(&self, service: &str) -> Result<RpcRoute, String> {
let request_channel = ChannelName::new(&format!("{service}.requests"))
.map_err(|e| format!("invalid service name: {e}"))?;
let request_channel_id = ChannelId::new(request_channel.clone());
let request_channel_hash = request_channel_id.hash();
let request_stream_id = Self::publish_stream_id(&request_channel_id);
let self_origin = self.public_key_origin_hash();
let reply_channel = ChannelName::new(&format!("{service}.replies.{self_origin:016x}"))
.map_err(|e| format!("invalid reply channel name: {e}"))?;
let reply_hash = reply_channel.hash();
Ok(RpcRoute {
request_channel,
request_channel_hash,
request_stream_id,
reply_channel,
reply_hash,
})
}
#[cfg(feature = "cortex")]
pub(super) fn rpc_local_services_arc(&self) -> Arc<LocalServiceRegistry> {
self.rpc_local_services.clone()
}
pub fn subscribe_local_caps_changes(&self) -> tokio::sync::watch::Receiver<u64> {
self.local_caps_changed.subscribe()
}
pub fn local_caps_generation(&self) -> u64 {
*self.local_caps_changed.borrow()
}
#[cfg(test)]
pub(crate) fn test_bump_local_caps_changed(&self) {
self.local_caps_changed
.send_modify(|g| *g = g.wrapping_add(1));
}
pub fn capability_announce_version(&self) -> u64 {
self.capability_version.load(Ordering::Relaxed)
}
fn emit_event_pingwave(&self, resend: bool) {
spawn_event_pingwave(
&self.event_pingwave_gate,
self.config.event_pingwave_min_gap,
&self.proximity_graph,
&self.socket,
&self.peers,
&self.partition_filter,
resend,
);
if self.config.enable_sensing_coalescing {
self.sensing_overlay_changed.send_modify(|generation| {
*generation = generation.wrapping_add(1);
});
}
}
#[cfg(feature = "tool")]
pub fn tool_registry(&self) -> &Arc<crate::adapter::net::cortex::tool::ToolMetadataRegistry> {
&self.tool_registry
}
#[cfg(feature = "tool")]
pub fn list_tools(
&self,
matcher: Option<&super::behavior::fold::capability_aggregation::TagMatcher>,
) -> Vec<crate::adapter::net::cortex::tool::ToolDescriptor> {
use std::collections::{HashMap, HashSet};
use super::behavior::tag::Tag;
use super::behavior::tag_codec::tools_from_tags;
use super::behavior::ToolCapability;
use crate::adapter::net::cortex::tool::ToolDescriptor;
if let Some(m) = matcher {
if let Err(e) = m.validate() {
panic!(
"MeshNode::list_tools given a matcher this binary can't \
evaluate: {e}",
);
}
}
type Bucket = (ToolDescriptor, HashSet<u64>);
let mut buckets: HashMap<(String, String), Bucket> = HashMap::new();
self.capability_fold.with_state(|state| {
for ((_class, node_id), entry) in state.entries.iter() {
let membership = &entry.payload;
if let Some(matcher) = matcher {
if !matcher.matches_any(&membership.tags) {
continue;
}
}
let parsed_tags: Vec<Tag> = membership
.tags
.iter()
.filter_map(|s| Tag::parse(s).ok())
.collect();
let tools = tools_from_tags(&parsed_tags);
if tools.is_empty() {
continue;
}
let metadata = &membership.metadata;
for mut cap in tools {
if cap.input_schema.is_none() {
if let Some(s) =
metadata.get(&ToolCapability::input_schema_metadata_key(&cap.tool_id))
{
cap.input_schema = Some(s.clone());
}
}
if cap.output_schema.is_none() {
if let Some(s) =
metadata.get(&ToolCapability::output_schema_metadata_key(&cap.tool_id))
{
cap.output_schema = Some(s.clone());
}
}
let descriptor = ToolDescriptor::from_capability(&cap, metadata);
let key = (descriptor.tool_id.clone(), descriptor.version.clone());
use std::collections::hash_map::Entry;
let bucket = match buckets.entry(key) {
Entry::Occupied(e) => {
let bucket = e.into_mut();
bucket.0 = descriptor;
bucket
}
Entry::Vacant(e) => e.insert((descriptor, HashSet::new())),
};
bucket.1.insert(*node_id);
}
}
});
let mut out: Vec<ToolDescriptor> = buckets
.into_iter()
.map(|(_, (mut desc, nodes))| {
desc.node_count = nodes.len() as u32;
desc
})
.collect();
out.sort_by(|a, b| a.tool_id.cmp(&b.tool_id).then(a.version.cmp(&b.version)));
out
}
#[cfg(feature = "tool")]
pub fn watch_tools(
self: &Arc<Self>,
matcher: Option<super::behavior::fold::capability_aggregation::TagMatcher>,
interval: Option<Duration>,
) -> crate::adapter::net::cortex::tool::ToolListWatch {
use crate::adapter::net::cortex::tool::{ToolDescriptor, ToolListChange, ToolListWatch};
use std::collections::HashMap;
let (tx, rx) = tokio::sync::mpsc::channel::<ToolListChange>(256);
let cancel = std::sync::Arc::new(Notify::new());
let cancel_task = cancel.clone();
let mut change_rx = self.capability_fold.subscribe_changes();
let initial_snapshot: HashMap<(String, String), ToolDescriptor> = self
.list_tools(matcher.as_ref())
.into_iter()
.map(|d| ((d.tool_id.clone(), d.version.clone()), d))
.collect();
let node = self.clone();
let matcher_for_task = matcher;
let ceiling = interval;
tokio::spawn(async move {
let mut prev = initial_snapshot;
let mut ticker = ceiling.map(|d| {
let mut t = tokio::time::interval(d);
t.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Delay);
t
});
if let Some(t) = ticker.as_mut() {
t.tick().await;
}
loop {
tokio::select! {
r = change_rx.changed() => {
if r.is_err() {
return;
}
}
_ = async {
match ticker.as_mut() {
Some(t) => {
t.tick().await;
}
None => std::future::pending::<()>().await,
}
} => {}
_ = cancel_task.notified() => {
return;
}
_ = tx.closed() => {
return;
}
}
if tx.is_closed() {
return;
}
let next: HashMap<(String, String), ToolDescriptor> = node
.list_tools(matcher_for_task.as_ref())
.into_iter()
.map(|d| ((d.tool_id.clone(), d.version.clone()), d))
.collect();
for (key, desc) in next.iter() {
if !prev.contains_key(key)
&& tx.send(ToolListChange::Added(desc.clone())).await.is_err()
{
return;
}
}
for (key, desc) in prev.iter() {
if !next.contains_key(key)
&& tx
.send(ToolListChange::Removed(desc.clone()))
.await
.is_err()
{
return;
}
}
for (key, new_desc) in next.iter() {
if let Some(old_desc) = prev.get(key) {
if new_desc.node_count != old_desc.node_count
&& tx
.send(ToolListChange::NodeCountChanged {
descriptor: new_desc.clone(),
prev_node_count: old_desc.node_count,
})
.await
.is_err()
{
return;
}
}
}
prev = next;
}
});
ToolListWatch {
receiver: rx,
cancel,
}
}
#[cfg(feature = "cortex")]
pub(super) fn rpc_metrics_arc(
&self,
) -> Arc<crate::adapter::net::mesh_rpc_metrics::RpcMetricsRegistry> {
self.rpc_metrics.clone()
}
#[cfg(feature = "cortex")]
pub fn rpc_metrics_snapshot(
&self,
) -> crate::adapter::net::mesh_rpc_metrics::RpcMetricsSnapshot {
self.rpc_metrics.snapshot()
}
#[cfg(feature = "cortex")]
pub fn set_rpc_observer(
&self,
observer: Option<crate::adapter::net::cortex::rpc_observer::RpcObserverHandle>,
) {
self.rpc_observer.store(observer.map(Arc::new));
}
#[cfg(feature = "cortex")]
pub fn rpc_observer(
&self,
) -> Option<crate::adapter::net::cortex::rpc_observer::RpcObserverHandle> {
self.rpc_observer.load_full().map(|arc| (*arc).clone())
}
#[cfg(feature = "cortex")]
pub fn reserve_cancel_token(&self) -> u64 {
self.cancel_registry.reserve_token()
}
#[cfg(feature = "cortex")]
pub fn cancel(&self, token: u64) {
self.cancel_registry.cancel(token);
}
#[cfg(feature = "cortex")]
pub(crate) fn cancel_registry(
&self,
) -> &Arc<crate::adapter::net::cancel_registry::CancelRegistry> {
&self.cancel_registry
}
#[cfg(feature = "cortex")]
pub fn cancel_registry_len(&self) -> usize {
self.cancel_registry.len()
}
#[cfg(feature = "cortex")]
pub(crate) fn fire_rpc_observer_outbound(
&self,
callee: u64,
method: &str,
latency_ms: u32,
status: crate::adapter::net::cortex::rpc_observer::RpcCallStatus,
request_bytes: u32,
response_bytes: u32,
) {
if let Some(obs) = self.rpc_observer() {
let evt = crate::adapter::net::cortex::rpc_observer::RpcCallEvent {
caller: self.node_id(),
callee,
method: method.to_string(),
latency_ms,
status,
request_bytes,
response_bytes,
direction: crate::adapter::net::cortex::rpc_observer::RpcDirection::Outbound,
ts_unix_ms: crate::adapter::net::cortex::rpc_observer::unix_now_ms(),
};
obs.on_call(evt);
}
}
#[cfg(feature = "cortex")]
pub(super) fn entity_id_for_node(&self, node_id: u64) -> Option<[u8; 32]> {
self.peer_entity_ids
.get(&node_id)
.map(|e| *e.value().as_bytes())
}
pub fn set_channel_configs(&mut self, configs: Arc<ChannelConfigRegistry>) {
let gateway = Arc::new(SubnetGateway::new(self.local_subnet, configs.clone()));
self.channel_configs = Some(configs);
self.subnet_gateway = Some(gateway);
}
pub fn gateway(&self) -> Option<&Arc<SubnetGateway>> {
self.subnet_gateway.as_ref()
}
pub fn channel_configs(&self) -> Option<&Arc<ChannelConfigRegistry>> {
self.channel_configs.as_ref()
}
#[cfg(feature = "cortex")]
pub fn set_aggregator_registry(
&mut self,
registry: Arc<super::behavior::aggregator::AggregatorRegistry>,
) {
debug_assert!(
!self.started.load(std::sync::atomic::Ordering::SeqCst),
"set_aggregator_registry must be called before MeshNode::start; \
installing after the receive loop is live races channel-publish init",
);
self.aggregator_registry = Some(registry);
}
#[cfg(feature = "cortex")]
pub fn aggregator_registry(
&self,
) -> Option<&Arc<super::behavior::aggregator::AggregatorRegistry>> {
self.aggregator_registry.as_ref()
}
pub fn set_token_cache(&mut self, cache: Arc<TokenCache>) {
self.token_cache = Some(cache);
}
pub async fn subscribe_channel(
&self,
publisher_node_id: u64,
channel: ChannelName,
) -> Result<(), AdapterError> {
self.send_membership_request(publisher_node_id, channel, true, None, None)
.await
}
pub async fn subscribe_channel_with_token(
&self,
publisher_node_id: u64,
channel: ChannelName,
token: PermissionToken,
) -> Result<(), AdapterError> {
self.subscribe_channel_with_chain(publisher_node_id, channel, TokenChain::single(token))
.await
}
pub async fn subscribe_channel_with_chain(
&self,
publisher_node_id: u64,
channel: ChannelName,
chain: TokenChain,
) -> Result<(), AdapterError> {
self.send_membership_request(
publisher_node_id,
channel,
true,
Some(chain.to_bytes()),
None,
)
.await
}
pub fn set_publish_chain(&self, channel: &ChannelName, chain: TokenChain) {
self.published_chains.insert(channel.hash(), chain);
}
pub async fn subscribe_channel_in_queue_group(
&self,
publisher_node_id: u64,
channel: ChannelName,
queue_group: String,
) -> Result<(), AdapterError> {
self.send_membership_request(publisher_node_id, channel, true, None, Some(queue_group))
.await
}
pub async fn subscribe_channel_in_queue_group_with_token(
&self,
publisher_node_id: u64,
channel: ChannelName,
queue_group: String,
token: PermissionToken,
) -> Result<(), AdapterError> {
self.send_membership_request(
publisher_node_id,
channel,
true,
Some(TokenChain::single(token).to_bytes()),
Some(queue_group),
)
.await
}
pub async fn unsubscribe_channel(
&self,
publisher_node_id: u64,
channel: ChannelName,
) -> Result<(), AdapterError> {
self.send_membership_request(publisher_node_id, channel, false, None, None)
.await
}
async fn send_membership_request(
&self,
publisher_node_id: u64,
channel: ChannelName,
subscribe: bool,
token: Option<Vec<u8>>,
queue_group: Option<String>,
) -> Result<(), AdapterError> {
let peer_addr = {
let peer = self.peers.get(&publisher_node_id).ok_or_else(|| {
AdapterError::Connection(format!(
"no session to publisher {:#x}",
publisher_node_id
))
})?;
peer.addr
};
let mut nonce_bytes = [0u8; 8];
if let Err(e) = getrandom::fill(&mut nonce_bytes) {
return Err(AdapterError::Connection(format!(
"membership nonce generation failed: {e}"
)));
}
let nonce = u64::from_le_bytes(nonce_bytes);
let msg = if subscribe {
MembershipMsg::Subscribe {
channel: channel.clone(),
nonce,
token,
queue_group,
}
} else {
MembershipMsg::Unsubscribe {
channel: channel.clone(),
nonce,
}
};
let bytes = membership::encode(&msg);
let (tx, rx) = oneshot::channel::<MembershipAck>();
self.pending_membership_acks
.insert(nonce, (publisher_node_id, tx));
if let Err(e) = self
.send_subprotocol(peer_addr, SUBPROTOCOL_CHANNEL_MEMBERSHIP, &bytes)
.await
{
self.pending_membership_acks.remove(&nonce);
return Err(e);
}
let ack = match tokio::time::timeout(self.config.membership_ack_timeout, rx).await {
Ok(Ok(ack)) => ack,
Ok(Err(_)) => {
self.pending_membership_acks.remove(&nonce);
return Err(AdapterError::Connection(
"membership ack channel closed".into(),
));
}
Err(_) => {
self.pending_membership_acks.remove(&nonce);
return Err(AdapterError::Connection(format!(
"membership ack timeout ({:?}) for channel {}",
self.config.membership_ack_timeout, channel
)));
}
};
if !ack.accepted {
return Err(AdapterError::Connection(format!(
"membership request rejected: {:?}",
ack.reason
)));
}
Ok(())
}
fn handle_membership_message(payload: &[u8], from_node: u64, ctx: &DispatchCtx) {
let msg = match membership::decode(payload) {
Ok(m) => m,
Err(e) => {
tracing::warn!(error = %e, "membership decode failed");
return;
}
};
match msg {
MembershipMsg::Subscribe {
channel,
nonce,
token,
queue_group,
} => {
let (accepted, reason) =
Self::authorize_subscribe(&channel, from_node, token.as_deref(), ctx);
if accepted {
ctx.auth_guard
.allow_channel(subscriber_origin_hash(from_node), &channel);
let id = ChannelId::new(channel);
let mode = match queue_group {
None => crate::adapter::net::channel::SubscriptionMode::Broadcast,
Some(name) => crate::adapter::net::channel::SubscriptionMode::QueueGroup(
crate::adapter::net::channel::QueueGroupName::new(name),
),
};
ctx.roster.add_with_mode(id, from_node, mode);
Self::clear_auth_failures(from_node, ctx);
} else if !matches!(
reason,
Some(AckReason::TooManyChannels) | Some(AckReason::RateLimited)
) {
Self::record_auth_failure(from_node, ctx);
}
Self::send_membership_ack(from_node, nonce, accepted, reason, ctx);
}
MembershipMsg::Unsubscribe { channel, nonce } => {
ctx.auth_guard
.revoke_channel(subscriber_origin_hash(from_node), &channel);
let id = ChannelId::new(channel);
ctx.subscriber_chains.remove(&(from_node, id.hash()));
ctx.roster.remove(&id, from_node);
Self::send_membership_ack(from_node, nonce, true, None, ctx);
}
MembershipMsg::Ack {
nonce,
accepted,
reason,
} => {
let took = ctx
.pending_membership_acks
.remove_if(&nonce, |_, (expected, _)| *expected == from_node);
if let Some((_, (_expected, tx))) = took {
let _ = tx.send(MembershipAck { accepted, reason });
} else if ctx.pending_membership_acks.contains_key(&nonce) {
tracing::trace!(
nonce,
from = from_node,
"membership ack from non-publisher session peer; dropping"
);
} else {
tracing::debug!(
nonce,
"membership ack with no pending request (duplicate or timed out)"
);
}
}
}
}
#[cfg(feature = "redex")]
fn dispatch_replication_payload(
payload: &[u8],
from_node: u64,
router: &dyn super::redex::ReplicationInboundRouter,
) {
use super::redex::{
Inbound, SyncHeartbeat, SyncNack, SyncRequest, SyncResponse, DISPATCH_SYNC_HEARTBEAT,
DISPATCH_SYNC_NACK, DISPATCH_SYNC_REQUEST, DISPATCH_SYNC_RESPONSE,
};
if payload.len() < 3 {
return;
}
let dispatch_code = payload[2];
let (channel_id, event) = match dispatch_code {
DISPATCH_SYNC_HEARTBEAT => match SyncHeartbeat::from_bytes(payload) {
Ok(msg) => (
msg.channel_id,
Inbound::Heartbeat {
from: from_node,
msg,
},
),
Err(_) => return,
},
DISPATCH_SYNC_REQUEST => match SyncRequest::from_bytes(payload) {
Ok(msg) => (
msg.channel_id,
Inbound::SyncRequest {
from: from_node,
msg,
},
),
Err(_) => return,
},
DISPATCH_SYNC_RESPONSE => match SyncResponse::from_bytes(payload) {
Ok(msg) => (
msg.channel_id,
Inbound::SyncResponse {
from: from_node,
msg,
},
),
Err(_) => return,
},
DISPATCH_SYNC_NACK => match SyncNack::from_bytes(payload) {
Ok(msg) => (
msg.channel_id,
Inbound::SyncNack {
from: from_node,
msg,
},
),
Err(_) => return,
},
_ => return,
};
let _ = router.try_route(channel_id, event);
}
fn promotable_direct_hop(
addr_to_node: &DashMap<SocketAddr, u64>,
failure_detector: &FailureDetector,
hop: u64,
addr: SocketAddr,
via_addr: SocketAddr,
) -> bool {
if addr == via_addr {
return false;
}
if addr_to_node.get(&addr).map(|e| *e.value()) != Some(hop) {
return false;
}
!matches!(
failure_detector.status(hop),
NodeStatus::Failed | NodeStatus::Suspected
)
}
#[allow(clippy::too_many_arguments)]
fn try_promote_graph_alternate(
proximity_graph: &ProximityGraph,
router: &NetRouter,
peer_addrs: &DashMap<u64, SocketAddr>,
addr_to_node: &DashMap<SocketAddr, u64>,
failure_detector: &FailureDetector,
dest: u64,
from_node: u64,
via_addr: SocketAddr,
) -> bool {
let Some(path) = proximity_graph.path_to_excluding_first_hop(
&node_id_to_graph_id(dest),
&node_id_to_graph_id(from_node),
) else {
return false;
};
let Some(first_hop) = path.get(1).map(graph_id_to_node_id) else {
return false;
};
if first_hop == from_node {
return false;
}
let Some(addr) = peer_addrs.get(&first_hop).map(|a| *a.value()) else {
return false;
};
if !Self::promotable_direct_hop(addr_to_node, failure_detector, first_hop, addr, via_addr) {
return false;
}
let metric = (path.len() as u16).saturating_sub(2).saturating_add(2);
router
.routing_table()
.add_route_with_metric(dest, addr, metric);
true
}
fn handle_route_withdrawal(payload: &[u8], from_node: u64, ctx: &DispatchCtx) {
if !ctx.enable_route_withdraw {
return;
}
let Some(w) = RouteWithdrawal::from_bytes(payload) else {
tracing::trace!(
from_node = format!("{:#x}", from_node),
"route-withdraw: malformed payload dropped"
);
return;
};
let dest = w.dest;
if dest == ctx.local_node_id || dest == from_node {
return;
}
let Some(via_addr) = ctx.peers.get(&from_node).map(|p| p.value().addr) else {
return;
};
if !ctx.route_withdraw_gate.admit(from_node, dest, w.seq) {
return;
}
ctx.proximity_graph
.remove_edge(node_id_to_graph_id(from_node), node_id_to_graph_id(dest));
let route_dropped = ctx
.router
.routing_table()
.remove_route_if_next_hop_is(dest, via_addr);
if ctx.enable_sensing_coalescing
&& (route_dropped
|| (ctx.router.routing_table().lookup(dest).is_none()
&& !sensing_live_direct_session(
&ctx.peers,
&ctx.addr_to_node,
Some(&ctx.failure_detector),
dest,
)))
{
disrupt_sensing_provider(
&ctx.sensing_interest_table,
&ctx.sensing_observations,
&ctx.sensing_overlay_changed,
dest,
sensing::DisruptReason::PathFailed,
);
#[cfg(feature = "redex")]
if let Some(leader) = ctx.sensing_leader.lock().as_mut() {
leader
.relay
.disrupt_provider(dest, sensing::DisruptReason::PathFailed);
}
ctx.sensing_overlay_changed.send_modify(|generation| {
*generation = generation.wrapping_add(1);
});
}
if !route_dropped {
return;
}
tracing::debug!(
dest = format!("{:#x}", dest),
via = format!("{:#x}", from_node),
"route-withdraw: dropped route"
);
if let Some(peer) = ctx.peers.get(&dest) {
let addr = peer.value().addr;
drop(peer);
if Self::promotable_direct_hop(
&ctx.addr_to_node,
&ctx.failure_detector,
dest,
addr,
via_addr,
) {
ctx.router.routing_table().add_route(dest, addr);
return;
}
}
let inflight = ctx.route_withdraw_cascades_inflight.clone();
if inflight.fetch_add(1, Ordering::AcqRel) >= MAX_INFLIGHT_ROUTE_WITHDRAW_CASCADES {
inflight.fetch_sub(1, Ordering::AcqRel);
tracing::debug!(
dest = format!("{:#x}", dest),
"route-withdraw: cascade shed (over in-flight cap); anti-entropy repairs"
);
return;
}
let proximity_graph = ctx.proximity_graph.clone();
let router = ctx.router.clone();
let peer_addrs = ctx.peer_addrs.clone();
let addr_to_node = ctx.addr_to_node.clone();
let failure_detector = ctx.failure_detector.clone();
let route_withdraw_seq = ctx.route_withdraw_seq.clone();
let route_withdraw_damper = ctx.route_withdraw_damper.clone();
let socket = ctx.socket.clone();
let peers = ctx.peers.clone();
let partition_filter = ctx.partition_filter.clone();
tokio::spawn(async move {
let promoted = Self::try_promote_graph_alternate(
&proximity_graph,
&router,
&peer_addrs,
&addr_to_node,
&failure_detector,
dest,
from_node,
via_addr,
);
if !promoted {
run_route_withdrawal_flood(
route_withdraw_seq,
route_withdraw_damper,
socket,
peers,
partition_filter,
dest,
Some(from_node),
)
.await;
}
inflight.fetch_sub(1, Ordering::AcqRel);
});
}
fn handle_sensing_interest_frame(payload: &[u8], from_node: u64, ctx: &DispatchCtx) {
let Ok(frame) = sensing::decode_interest_frame(payload) else {
ctx.sensing_counters
.protocol_invalid
.fetch_add(1, Ordering::Relaxed);
tracing::trace!(
from_node = format!("{:#x}", from_node),
len = payload.len(),
"sensing: undecodable 0x0C02 payload dropped"
);
return;
};
let Some(sender_entity) = ctx
.peer_entity_ids
.get(&from_node)
.map(|e| e.value().clone())
else {
tracing::debug!(
from_node = format!("{:#x}", from_node),
"sensing: no pinned EntityId for sender, drop frame"
);
return;
};
let entity_root = sensing::AudienceScopeCommitment::owner_root(&sender_entity);
let claimed_scope = match &frame {
sensing::SensingInterestFrame::CapabilityRegistration { audience_scope, .. }
| sensing::SensingInterestFrame::ProviderRegistration { audience_scope, .. } => {
Some(*audience_scope)
}
sensing::SensingInterestFrame::Deregister { .. } => None,
};
let session_root =
if ctx.sensing_fleet_scope && claimed_scope == Some(ctx.sensing_local_root) {
ctx.sensing_local_root
} else {
entity_root
};
let now = Instant::now();
match &frame {
sensing::SensingInterestFrame::CapabilityRegistration {
capability_id,
requested_sample_interval,
soft_state_ttl,
..
} => {
if !sensing_interval_in_bounds(*requested_sample_interval, ctx.sensing_interest_ttl)
|| soft_state_ttl.is_zero()
{
tracing::trace!(
from_node = format!("{:#x}", from_node),
interval_ms = requested_sample_interval.as_millis() as u64,
ttl_ms = soft_state_ttl.as_millis() as u64,
"sensing: out-of-bounds interval/ttl dropped"
);
return;
}
#[cfg(not(feature = "redex"))]
let _ = (capability_id, requested_sample_interval, soft_state_ttl);
#[cfg(feature = "redex")]
{
if ctx.sensing_leader.lock().is_none() {
return;
}
let snapshot = sensing_candidate_snapshot_from_parts(
&ctx.capability_fold,
&ctx.proximity_graph,
&ctx.router,
&ctx.peers,
&ctx.peer_entity_ids,
ctx.local_node_id,
ctx.sensing_local_entity_root,
&ctx.sensing_local_root,
capability_id,
);
let mut slot = ctx.sensing_leader.lock();
let Some(leader) = slot.as_mut() else {
return;
};
let registration = match leader.register_from_frame(
&frame,
from_node,
&session_root,
&ctx.sensing_local_root,
&ctx.sensing_counters,
&snapshot,
now,
) {
Ok(registration) => registration,
Err(rejection) => {
if matches!(
rejection,
sensing::FrameRejection::Resolution(
sensing::ResolutionRefusal::SelectorTooBroad { .. }
)
) {
ctx.sensing_counters
.broad_selector_refusals
.fetch_add(1, Ordering::Relaxed);
}
tracing::debug!(
from_node = format!("{:#x}", from_node),
rejection = %rejection,
"sensing: leader intake refused registration"
);
return;
}
};
ctx.sensing_counters
.interests_registered
.fetch_add(1, Ordering::Relaxed);
if registration.newly_resolved {
ctx.sensing_counters
.candidate_fanout_total
.fetch_add(registration.branches.len() as u64, Ordering::Relaxed);
} else {
ctx.sensing_counters
.interests_coalesced
.fetch_add(1, Ordering::Relaxed);
}
if !registration.warm_starts.is_empty() {
dispatch_sensing_leader_deliveries(
&ctx.socket,
&ctx.peers,
&ctx.addr_to_node,
&ctx.router,
&ctx.partition_filter,
ctx.local_node_id,
&ctx.sensing_observations,
&ctx.sensing_overlay_changed,
ctx.sensing_continuity_factor,
registration.warm_starts.clone(),
now,
);
}
let branch_demands: Vec<(u64, Duration)> = registration
.admitted_branches
.iter()
.filter_map(|provider| {
let branch = sensing::ProviderInterestKey::new(
registration.interest.clone(),
*provider,
);
leader
.relay
.table
.aggregate(&branch, now)
.map(|strictest| (*provider, strictest))
})
.collect();
drop(slot);
if branch_demands.is_empty() {
return;
}
let Ok(spec) = frame.validated_spec(&ctx.sensing_counters) else {
return;
};
let ttl = (*soft_state_ttl).min(ctx.sensing_interest_ttl);
for (provider, strictest_demand) in branch_demands {
let key = sensing::ProviderInterestKey::new(
registration.interest.clone(),
provider,
);
let (outcome, aggregate) = {
let mut table = ctx.sensing_interest_table.lock();
let outcome = table.register(
&key,
sensing::DownstreamId::Leader,
strictest_demand,
ttl,
ctx.sensing_local_root,
now,
);
(outcome, table.aggregate(&key, now))
};
if !matches!(outcome, sensing::RegisterOutcome::Registered(_)) {
continue;
}
ctx.sensing_observations
.lock()
.update_upstream_interval(&key, aggregate);
if provider == ctx.local_node_id {
Self::feed_sensing_origin(
ctx,
&key,
&spec,
sensing::DownstreamId::Leader,
now,
);
continue;
}
let Some(strictest) = aggregate else {
continue;
};
if !sensing_upstream_damper_admits(
&ctx.sensing_upstream_damper,
provider,
*key.interest.interest_digest.as_bytes(),
sensing_effective_min_gap(ttl),
) {
continue;
}
let upstream = sensing::SensingInterestFrame::provider_registration(
&spec, provider, strictest, ttl,
);
if let Ok(bytes) = sensing::encode_interest_frame(&upstream) {
spawn_sensing_frame_send(
&ctx.socket,
&ctx.peers,
&ctx.addr_to_node,
&ctx.router,
&ctx.partition_filter,
ctx.local_node_id,
provider,
sensing::SUBPROTOCOL_SENSING_INTEREST as u64,
sensing::SUBPROTOCOL_SENSING_INTEREST,
bytes,
);
}
}
}
}
sensing::SensingInterestFrame::ProviderRegistration { audience_scope, .. } => {
let Ok(validated) = frame.validate_provider_registration(&ctx.sensing_counters)
else {
return;
};
if !sensing_interval_in_bounds(
validated.requested_sample_interval,
ctx.sensing_interest_ttl,
) || validated.soft_state_ttl.is_zero()
{
tracing::trace!(
from_node = format!("{:#x}", from_node),
interval_ms = validated.requested_sample_interval.as_millis() as u64,
ttl_ms = validated.soft_state_ttl.as_millis() as u64,
"sensing: out-of-bounds interval/ttl dropped"
);
return;
}
let Ok(proven_root) = sensing::validate_subscriber_scope(
&session_root,
audience_scope,
&ctx.sensing_local_root,
&validated.spec.audience,
&ctx.sensing_counters,
) else {
return;
};
let key = sensing::ProviderInterestKey::new(validated.spec.key(), validated.target);
let ttl = validated.soft_state_ttl.min(ctx.sensing_interest_ttl);
let outcome = ctx.sensing_interest_table.lock().register(
&key,
sensing::DownstreamId::Peer(from_node),
validated.requested_sample_interval,
ttl,
proven_root,
now,
);
match outcome {
sensing::RegisterOutcome::Registered(action) => {
if validated.target == ctx.local_node_id {
if validated.spec.providers.is_provider_free() {
ctx.sensing_counters
.provider_free_registrations
.fetch_add(1, Ordering::Relaxed);
let distinct_upstreams = ctx
.sensing_interest_table
.lock()
.downstreams(&key, now)
.into_iter()
.filter(|downstream| {
matches!(downstream, sensing::DownstreamId::Peer(_))
})
.count();
if distinct_upstreams >= 2 {
ctx.sensing_counters
.divergent_resolution_merge_miss
.fetch_add(1, Ordering::Relaxed);
}
}
Self::feed_sensing_origin(
ctx,
&key,
&validated.spec,
sensing::DownstreamId::Peer(from_node),
now,
);
return;
}
if action == sensing::UpstreamAction::Deregister {
Self::send_sensing_deregister_upstream(ctx, &key);
return;
}
let cached = {
let mut observations = ctx.sensing_observations.lock();
let slot_key = (key.clone(), sensing::DownstreamId::Peer(from_node));
if observations.slots.contains_key(&slot_key) {
None
} else {
let cached = observations.latest.get(&key).cloned();
if let Some(cached) = &cached {
observations.slots.insert(
slot_key,
SensingDeliverySlot {
last_status: Some(cached.status),
last_delivered: Some((
cached.origin_incarnation,
cached.seq,
)),
next_due: now + validated.requested_sample_interval,
pending: false,
},
);
}
cached
}
};
if let Some(cached) = cached {
if let Ok(bytes) = sensing::encode_attestation(&cached) {
spawn_sensing_frame_send(
&ctx.socket,
&ctx.peers,
&ctx.addr_to_node,
&ctx.router,
&ctx.partition_filter,
ctx.local_node_id,
from_node,
sensing::SENSING_PROVISIONAL_STREAM,
sensing::SUBPROTOCOL_READINESS_ATTESTATION,
bytes,
);
}
}
let Some(strictest) =
ctx.sensing_interest_table.lock().aggregate(&key, now)
else {
return;
};
ctx.sensing_observations
.lock()
.update_upstream_interval(&key, Some(strictest));
if !sensing_upstream_damper_admits(
&ctx.sensing_upstream_damper,
validated.target,
*key.interest.interest_digest.as_bytes(),
sensing_effective_min_gap(ttl),
) {
return;
}
let upstream = sensing::SensingInterestFrame::provider_registration(
&validated.spec,
validated.target,
strictest,
ttl,
);
if let Ok(bytes) = sensing::encode_interest_frame(&upstream) {
spawn_sensing_frame_send(
&ctx.socket,
&ctx.peers,
&ctx.addr_to_node,
&ctx.router,
&ctx.partition_filter,
ctx.local_node_id,
validated.target,
sensing::SUBPROTOCOL_SENSING_INTEREST as u64,
sensing::SUBPROTOCOL_SENSING_INTEREST,
bytes,
);
}
}
sensing::RegisterOutcome::OverCap => {
ctx.sensing_over_cap.fetch_add(1, Ordering::Relaxed);
tracing::debug!(
from_node = format!("{:#x}", from_node),
"sensing: registration refused over per-peer cap"
);
}
sensing::RegisterOutcome::RefusedByCachedFloor { minimum_supported } => {
tracing::debug!(
from_node = format!("{:#x}", from_node),
floor_ms = minimum_supported.as_millis() as u64,
"sensing: registration refused by cached provider floor"
);
if validated.target == ctx.local_node_id {
ctx.sensing_counters
.cadence_refusals
.fetch_add(1, Ordering::Relaxed);
let generation = ctx.capability_version.load(Ordering::Relaxed);
let beat = {
let mut slot = ctx.sensing_emitter.lock();
slot.as_mut().map(|emitter| {
emitter.refusal_beat(
&validated.spec,
sensing::CadenceRefusal { minimum_supported },
generation,
)
})
};
if let Some(beat) = beat {
Self::send_sensing_refusal_beat(ctx, beat, [from_node]);
}
}
}
}
}
sensing::SensingInterestFrame::Deregister {
interest_digest,
target,
} => {
let emitter_stamp = ctx.sensing_emitter.lock().as_ref().map(|e| e.stamp());
let actions = ctx.sensing_interest_table.lock().deregister(
interest_digest,
*target,
sensing::DownstreamId::Peer(from_node),
now,
);
for (key, action) in actions {
if action == sensing::UpstreamAction::Deregister {
ctx.sensing_observations.lock().reclaim_branch(&key);
}
if let sensing::UpstreamAction::Register { strictest } = action {
ctx.sensing_observations
.lock()
.update_upstream_interval(&key, Some(strictest));
}
if key.provider == ctx.local_node_id {
if action == sensing::UpstreamAction::Deregister {
if let (Some(emitter), Some(stamp)) =
(ctx.sensing_emitter.lock().as_mut(), emitter_stamp)
{
emitter.retire_if_stale(&key.interest.interest_digest, stamp);
}
}
continue;
}
if action == sensing::UpstreamAction::Deregister {
Self::send_sensing_deregister_upstream(ctx, &key);
}
}
}
}
}
fn feed_sensing_origin(
ctx: &DispatchCtx,
key: &sensing::ProviderInterestKey,
spec: &sensing::InterestSpec,
downstream: sensing::DownstreamId,
now: Instant,
) {
let Some(strictest) = ctx.sensing_interest_table.lock().aggregate(key, now) else {
return;
};
let outcome = {
let mut slot = ctx.sensing_emitter.lock();
let Some(emitter) = slot.as_mut() else {
return;
};
emitter
.register(spec, strictest, now)
.map_err(|refusal| (refusal, emitter.stamp()))
};
let (refusal, stamp) = match outcome {
Ok(()) => {
ctx.sensing_emitter_notify.notify_one();
return;
}
Err((sensing::StreamRefusal::AtCapacity, _)) => {
let _ = ctx.sensing_interest_table.lock().deregister(
&key.interest.interest_digest,
Some(key.provider),
downstream,
now,
);
tracing::debug!(
digest = ?key.interest.interest_digest,
"sensing: origin at live-stream capacity, registration rolled back"
);
return;
}
Err((sensing::StreamRefusal::Cadence(refusal), stamp)) => (refusal, stamp),
};
ctx.sensing_counters
.cadence_refusals
.fetch_add(1, Ordering::Relaxed);
let partition =
ctx.sensing_interest_table
.lock()
.on_refusal(key, refusal.minimum_supported, now);
let generation = ctx.capability_version.load(Ordering::Relaxed);
let beat = {
let mut slot = ctx.sensing_emitter.lock();
let Some(emitter) = slot.as_mut() else {
return;
};
let beat = emitter.refusal_beat(spec, refusal, generation);
match partition.upstream {
sensing::UpstreamAction::Register { strictest } => {
let _ = emitter.register(spec, strictest, now);
}
sensing::UpstreamAction::Deregister => {
emitter.retire_if_stale(&key.interest.interest_digest, stamp);
}
sensing::UpstreamAction::None => {}
}
beat
};
ctx.sensing_emitter_notify.notify_one();
#[cfg(feature = "redex")]
let leader_refused = partition.refused.contains(&sensing::DownstreamId::Leader);
let signed_refusal = Self::send_sensing_refusal_beat(ctx, beat, {
partition.refused.into_iter().filter_map(|d| match d {
sensing::DownstreamId::Peer(node) => Some(node),
sensing::DownstreamId::Local | sensing::DownstreamId::Leader => None,
})
});
#[cfg(feature = "redex")]
if leader_refused {
if let Some(bytes) = signed_refusal {
Self::apply_sensing_leader_refusal(
ctx,
key,
refusal.minimum_supported,
&bytes,
now,
);
}
}
#[cfg(not(feature = "redex"))]
let _ = signed_refusal;
}
fn handle_sensing_attestation_frame(
payload: &[u8],
from_node: u64,
provisional: bool,
ctx: &DispatchCtx,
) {
let Ok(attestation) = sensing::decode_attestation(payload) else {
ctx.sensing_counters
.protocol_invalid
.fetch_add(1, Ordering::Relaxed);
tracing::trace!(
from_node = format!("{:#x}", from_node),
len = payload.len(),
"sensing: undecodable 0x0C03 payload dropped"
);
return;
};
let interest = sensing::CapabilityInterestKey {
capability_id: attestation.capability_id.clone(),
interest_digest: attestation.interest_digest,
};
let branch = sensing::ProviderInterestKey::new(interest, attestation.origin);
let now = Instant::now();
let watch_candidate: Option<(Duration, sensing::AudienceScopeCommitment)> = {
let interests = ctx.sensing_capability_interests.lock();
interests.get(&branch.interest).and_then(|expectation| {
(expectation.expires_at > now)
.then_some((expectation.requested_sample_interval, expectation.audience))
})
};
let has_rows = !ctx
.sensing_interest_table
.lock()
.downstreams(&branch, now)
.is_empty();
if watch_candidate.is_none() && !has_rows {
tracing::trace!(
origin = format!("{:#x}", attestation.origin),
"sensing: unsolicited attestation dropped"
);
return;
}
let Some(origin_entity) = ctx
.peer_entity_ids
.get(&attestation.origin)
.map(|e| e.value().clone())
else {
tracing::debug!(
origin = format!("{:#x}", attestation.origin),
"sensing: no pinned EntityId for attestation origin, drop"
);
return;
};
if let Err(error) = sensing::verify_attestation(&attestation, &origin_entity) {
ctx.sensing_counters
.protocol_invalid
.fetch_add(1, Ordering::Relaxed);
tracing::debug!(
origin = format!("{:#x}", attestation.origin),
error = %error,
"sensing: attestation signature verification failed"
);
return;
}
let digest_watch: Option<Duration> = match watch_candidate {
Some((interval, audience)) => {
if sensing::AudienceScopeCommitment::owner_root(&origin_entity) == audience {
Some(interval)
} else {
ctx.sensing_counters
.scope_refusals
.fetch_add(1, Ordering::Relaxed);
tracing::debug!(
origin = format!("{:#x}", attestation.origin),
"sensing: signer does not derive the watch's owner root, \
watch justification refused"
);
None
}
}
None => None,
};
if digest_watch.is_none() && !has_rows {
return;
}
let is_refusal =
attestation.status_reason == sensing::StatusReason::SamplingIntervalUnsupported;
if is_refusal
&& !sensing_interval_in_bounds(attestation.promised_cadence, ctx.sensing_interest_ttl)
{
ctx.sensing_counters
.protocol_invalid
.fetch_add(1, Ordering::Relaxed);
tracing::debug!(
origin = format!("{:#x}", attestation.origin),
floor_ms = attestation.promised_cadence.as_millis() as u64,
"sensing: refusal beat with out-of-bounds floor dropped"
);
return;
}
let fingerprint = sensing::Digest256::from_bytes(attestation.transcript_digest());
let admission = ctx.sensing_observer_gate.lock().admit(
attestation.origin,
attestation.interest_digest,
attestation.origin_incarnation,
attestation.seq,
fingerprint,
);
if admission == sensing::Admission::Equivocation {
ctx.sensing_counters
.protocol_invalid
.fetch_add(1, Ordering::Relaxed);
tracing::warn!(
origin = format!("{:#x}", attestation.origin),
incarnation = attestation.origin_incarnation.get(),
seq = attestation.seq,
"sensing: equivocating attestation — incarnation poisoned"
);
return;
}
if !admission.is_admitted() {
ctx.sensing_counters
.attestations_gated
.fetch_add(1, Ordering::Relaxed);
tracing::trace!(
origin = format!("{:#x}", attestation.origin),
admission = ?admission,
"sensing: stale attestation dropped at observer gate"
);
return;
}
let epoch = (
attestation.origin_incarnation,
attestation.capability_generation,
);
enum EpochStanding {
Fresh,
Advanced((sensing::Incarnation, u64)),
Stale,
}
let standing = {
let mut observations = ctx.sensing_observations.lock();
match observations
.provider_epochs
.get(&attestation.origin)
.copied()
{
None => {
observations
.provider_epochs
.insert(attestation.origin, epoch);
EpochStanding::Fresh
}
Some(current) if epoch > current => {
observations
.provider_epochs
.insert(attestation.origin, epoch);
EpochStanding::Advanced(current)
}
Some(current) if epoch == current => EpochStanding::Fresh,
Some(_) => EpochStanding::Stale,
}
};
if matches!(standing, EpochStanding::Stale) {
ctx.sensing_counters
.attestations_superseded
.fetch_add(1, Ordering::Relaxed);
tracing::trace!(
origin = format!("{:#x}", attestation.origin),
incarnation = attestation.origin_incarnation.get(),
generation = attestation.capability_generation,
"sensing: globally stale epoch dropped"
);
return;
}
if let EpochStanding::Advanced(previous) = standing {
ctx.sensing_interest_table
.lock()
.invalidate_provider_floors(attestation.origin);
#[cfg(feature = "redex")]
if let Some(leader) = ctx.sensing_leader.lock().as_mut() {
leader
.relay
.table
.invalidate_provider_floors(attestation.origin);
}
let reason = if attestation.origin_incarnation > previous.0 {
sensing::DisruptReason::IncarnationSuperseded
} else {
sensing::DisruptReason::GenerationChanged
};
disrupt_sensing_provider(
&ctx.sensing_interest_table,
&ctx.sensing_observations,
&ctx.sensing_overlay_changed,
attestation.origin,
reason,
);
#[cfg(feature = "redex")]
if let Some(leader) = ctx.sensing_leader.lock().as_mut() {
leader.relay.disrupt_provider(attestation.origin, reason);
}
}
if is_refusal {
{
let mut observations = ctx.sensing_observations.lock();
if observations.refusals.len() < MAX_SENSING_OBSERVATIONS
|| observations.refusals.contains_key(&branch)
{
observations
.refusals
.insert(branch.clone(), (attestation.clone(), now));
} else {
tracing::debug!(
origin = format!("{:#x}", attestation.origin),
"sensing: refusal store at cap, new branch dropped"
);
}
}
let partition = ctx.sensing_interest_table.lock().on_refusal(
&branch,
attestation.promised_cadence,
now,
);
#[cfg(feature = "redex")]
if partition.refused.contains(&sensing::DownstreamId::Leader) {
Self::apply_sensing_leader_refusal(
ctx,
&branch,
attestation.promised_cadence,
payload,
now,
);
}
for downstream in &partition.refused {
let sensing::DownstreamId::Peer(node) = downstream else {
continue;
};
spawn_sensing_frame_send(
&ctx.socket,
&ctx.peers,
&ctx.addr_to_node,
&ctx.router,
&ctx.partition_filter,
ctx.local_node_id,
*node,
sensing::SUBPROTOCOL_READINESS_ATTESTATION as u64,
sensing::SUBPROTOCOL_READINESS_ATTESTATION,
payload.to_vec(),
);
}
if partition.upstream == sensing::UpstreamAction::Deregister {
let still_dead = ctx
.sensing_interest_table
.lock()
.downstreams(&branch, now)
.is_empty();
if still_dead {
if branch.provider != ctx.local_node_id {
Self::send_sensing_deregister_upstream(ctx, &branch);
}
ctx.sensing_observations.lock().reclaim_status(&branch);
return;
}
}
let aggregate = ctx.sensing_interest_table.lock().aggregate(&branch, now);
ctx.sensing_observations
.lock()
.update_upstream_interval(&branch, aggregate);
return;
}
let beat = sensing::DeliveredBeat {
attested_status: attestation.status,
estimated_start: attestation.estimated_start,
source_incarnation: attestation.origin_incarnation,
capability_generation: attestation.capability_generation,
seq: attestation.seq,
promised_cadence: attestation.promised_cadence,
continuity_bearing: !provisional,
};
let own_interval = ctx
.sensing_interest_table
.lock()
.aggregate(&branch, now)
.unwrap_or(attestation.promised_cadence);
let continuity = {
let mut observations = ctx.sensing_observations.lock();
if observations.latest.len() >= MAX_SENSING_OBSERVATIONS
&& !observations.latest.contains_key(&branch)
{
tracing::debug!(
origin = format!("{:#x}", attestation.origin),
"sensing: observation store at cap, new branch dropped"
);
return;
}
observations
.latest
.insert(branch.clone(), attestation.clone());
let factor = ctx.sensing_continuity_factor;
let cell = observations
.upstream
.entry(branch.clone())
.or_insert_with(|| sensing::ObservationCell::register(now, own_interval, factor));
cell.update_interval(own_interval);
cell.on_admitted_beat(now, beat);
cell.continuity()
};
let still_solicited = digest_watch.is_some_and(|_| {
ctx.sensing_capability_interests
.lock()
.get(&branch.interest)
.is_some_and(|expectation| expectation.expires_at > Instant::now())
}) || !ctx
.sensing_interest_table
.lock()
.downstreams(&branch, Instant::now())
.is_empty();
if !still_solicited {
ctx.sensing_observations.lock().reclaim_branch(&branch);
return;
}
ctx.sensing_interest_table
.lock()
.set_upstream_continuity(&branch, continuity);
let bearing = continuity == sensing::Continuity::Established;
Self::schedule_sensing_forwards(ctx, &branch, &attestation, payload, bearing, now);
if let Some(interval) = digest_watch {
let moved = ctx.sensing_observations.lock().feed_consumer_cell(
&branch,
&attestation,
bearing,
interval,
ctx.sensing_continuity_factor,
now,
);
if moved {
ctx.sensing_overlay_changed.send_modify(|generation| {
*generation = generation.wrapping_add(1);
});
}
}
}
fn schedule_sensing_forwards(
ctx: &DispatchCtx,
branch: &sensing::ProviderInterestKey,
attestation: &sensing::ReadinessAttestation,
payload: &[u8],
bearing: bool,
now: Instant,
) {
let rows: Vec<(sensing::DownstreamId, Duration)> = {
let table = ctx.sensing_interest_table.lock();
table
.downstreams(branch, now)
.into_iter()
.filter_map(|downstream| {
table
.downstream_entry(branch, downstream)
.map(|row| (downstream, row.requested_sample_interval))
})
.collect()
};
let mut forwards: Vec<u64> = Vec::new();
let mut overlay_moved = false;
let mut feed_leader = false;
{
let mut observations = ctx.sensing_observations.lock();
for (downstream, interval) in rows {
let slot = observations
.slots
.entry((branch.clone(), downstream))
.or_insert(SensingDeliverySlot {
last_status: None,
last_delivered: None,
next_due: now,
pending: false,
});
let edge = slot.last_status != Some(attestation.status);
let due = now >= slot.next_due;
if edge || due {
slot.last_status = Some(attestation.status);
slot.last_delivered = Some((attestation.origin_incarnation, attestation.seq));
slot.next_due = now + interval;
slot.pending = false;
match downstream {
sensing::DownstreamId::Peer(node) => forwards.push(node),
sensing::DownstreamId::Local => {
overlay_moved |= observations.feed_consumer_cell(
branch,
attestation,
bearing,
interval,
ctx.sensing_continuity_factor,
now,
);
}
sensing::DownstreamId::Leader => feed_leader = true,
}
} else {
slot.pending = true;
}
}
}
if overlay_moved {
ctx.sensing_overlay_changed.send_modify(|generation| {
*generation = generation.wrapping_add(1);
});
}
#[cfg(feature = "redex")]
if feed_leader {
if let Ok(semantic) = sensing::semantic_attestation(&branch.interest, attestation) {
let deliveries = {
let mut slot = ctx.sensing_leader.lock();
match slot.as_mut() {
Some(leader) => leader.on_attestation(now, &semantic, bearing),
None => Vec::new(),
}
};
dispatch_sensing_leader_deliveries(
&ctx.socket,
&ctx.peers,
&ctx.addr_to_node,
&ctx.router,
&ctx.partition_filter,
ctx.local_node_id,
&ctx.sensing_observations,
&ctx.sensing_overlay_changed,
ctx.sensing_continuity_factor,
deliveries,
now,
);
}
}
#[cfg(not(feature = "redex"))]
let _ = feed_leader;
let stream_id = if bearing {
sensing::SUBPROTOCOL_READINESS_ATTESTATION as u64
} else {
sensing::SENSING_PROVISIONAL_STREAM
};
if !forwards.is_empty() {
ctx.sensing_counters
.attestations_forwarded
.fetch_add(forwards.len() as u64, Ordering::Relaxed);
}
for node in forwards {
spawn_sensing_frame_send(
&ctx.socket,
&ctx.peers,
&ctx.addr_to_node,
&ctx.router,
&ctx.partition_filter,
ctx.local_node_id,
node,
stream_id,
sensing::SUBPROTOCOL_READINESS_ATTESTATION,
payload.to_vec(),
);
}
}
fn send_sensing_refusal_beat(
ctx: &DispatchCtx,
beat: sensing::UnsignedAttestation,
peers: impl IntoIterator<Item = u64>,
) -> Option<Vec<u8>> {
let Ok(signed) = sensing::sign_attestation(&ctx.signing_identity, beat) else {
return None;
};
let Ok(bytes) = sensing::encode_attestation(&signed) else {
return None;
};
for node in peers {
spawn_sensing_frame_send(
&ctx.socket,
&ctx.peers,
&ctx.addr_to_node,
&ctx.router,
&ctx.partition_filter,
ctx.local_node_id,
node,
sensing::SUBPROTOCOL_READINESS_ATTESTATION as u64,
sensing::SUBPROTOCOL_READINESS_ATTESTATION,
bytes.clone(),
);
}
Some(bytes)
}
#[cfg(feature = "redex")]
fn reconcile_sensing_leader_fold(ctx: &DispatchCtx, now: Instant) {
let capability_ids: Vec<sensing::CapabilityId> = {
let slot = ctx.sensing_leader.lock();
match slot.as_ref() {
Some(leader) => leader.interest_capability_ids(),
None => return,
}
};
let mut moved = false;
for capability_id in capability_ids {
let key_digest = *blake3::hash(capability_id.as_str().as_bytes()).as_bytes();
match sensing_fold_gate_admit(
&ctx.sensing_fold_coalescer,
key_digest,
SENSING_UPSTREAM_MIN_GAP,
) {
SensingFoldGateDecision::RunNow => {
moved |= Self::reconcile_sensing_leader_fold_one(ctx, &capability_id, now);
}
SensingFoldGateDecision::Defer { remaining, token } => {
let ctx = ctx.clone();
tokio::spawn(async move {
tokio::time::sleep(remaining).await;
if !sensing_fold_gate_reclaim(
&ctx.sensing_fold_coalescer,
&key_digest,
token,
) {
return;
}
if Self::reconcile_sensing_leader_fold_one(
&ctx,
&capability_id,
Instant::now(),
) {
ctx.sensing_overlay_changed.send_modify(|generation| {
*generation = generation.wrapping_add(1);
});
}
});
}
SensingFoldGateDecision::Coalesced => {}
}
}
if moved {
ctx.sensing_overlay_changed.send_modify(|generation| {
*generation = generation.wrapping_add(1);
});
}
}
#[cfg(feature = "redex")]
fn reconcile_sensing_leader_fold_one(
ctx: &DispatchCtx,
capability_id: &sensing::CapabilityId,
now: Instant,
) -> bool {
let snapshot = sensing_candidate_snapshot_from_parts(
&ctx.capability_fold,
&ctx.proximity_graph,
&ctx.router,
&ctx.peers,
&ctx.peer_entity_ids,
ctx.local_node_id,
ctx.sensing_local_entity_root,
&ctx.sensing_local_root,
capability_id,
);
let reconciliation = {
let mut slot = ctx.sensing_leader.lock();
match slot.as_mut() {
Some(leader) => leader.reconcile_with_snapshot(capability_id, &snapshot, now),
None => return false,
}
};
let emitter_stamp = ctx.sensing_emitter.lock().as_ref().map(|e| e.stamp());
for branch in reconciliation.torn_down {
let mesh_actions = ctx.sensing_interest_table.lock().deregister(
&branch.interest.interest_digest,
Some(branch.provider),
sensing::DownstreamId::Leader,
now,
);
for (key, action) in mesh_actions {
apply_sensing_removal_action(
&ctx.sensing_observations,
&ctx.sensing_emitter,
emitter_stamp,
&ctx.socket,
&ctx.peers,
&ctx.addr_to_node,
&ctx.router,
&ctx.partition_filter,
ctx.local_node_id,
&key,
action,
);
}
}
for (branch, spec) in reconciliation.added {
let strictest = {
let slot = ctx.sensing_leader.lock();
slot.as_ref()
.and_then(|leader| leader.relay.table.aggregate(&branch, now))
};
let Some(strictest) = strictest else {
continue;
};
let ttl = ctx.sensing_interest_ttl;
let outcome = ctx.sensing_interest_table.lock().register(
&branch,
sensing::DownstreamId::Leader,
strictest,
ttl,
ctx.sensing_local_root,
now,
);
if !matches!(outcome, sensing::RegisterOutcome::Registered(_)) {
continue;
}
ctx.sensing_observations
.lock()
.update_upstream_interval(&branch, Some(strictest));
if branch.provider == ctx.local_node_id {
Self::feed_sensing_origin(ctx, &branch, &spec, sensing::DownstreamId::Leader, now);
} else {
let upstream = sensing::SensingInterestFrame::provider_registration(
&spec,
branch.provider,
strictest,
ttl,
);
if let Ok(bytes) = sensing::encode_interest_frame(&upstream) {
spawn_sensing_frame_send(
&ctx.socket,
&ctx.peers,
&ctx.addr_to_node,
&ctx.router,
&ctx.partition_filter,
ctx.local_node_id,
branch.provider,
sensing::SUBPROTOCOL_SENSING_INTEREST as u64,
sensing::SUBPROTOCOL_SENSING_INTEREST,
bytes,
);
}
}
}
reconciliation.changed
}
#[cfg(feature = "redex")]
fn apply_sensing_leader_refusal(
ctx: &DispatchCtx,
branch: &sensing::ProviderInterestKey,
minimum_supported: Duration,
signed_refusal: &[u8],
now: Instant,
) {
let partition = {
let mut slot = ctx.sensing_leader.lock();
match slot.as_mut() {
Some(leader) => leader.on_refusal(branch, minimum_supported, now),
None => return,
}
};
for downstream in &partition.refused {
let sensing::DownstreamId::Peer(node) = downstream else {
continue;
};
spawn_sensing_frame_send(
&ctx.socket,
&ctx.peers,
&ctx.addr_to_node,
&ctx.router,
&ctx.partition_filter,
ctx.local_node_id,
*node,
sensing::SUBPROTOCOL_READINESS_ATTESTATION as u64,
sensing::SUBPROTOCOL_READINESS_ATTESTATION,
signed_refusal.to_vec(),
);
}
let sensing::UpstreamAction::Register { strictest } = partition.upstream else {
return;
};
let Some(spec) = partition.spec else {
return;
};
let ttl = ctx.sensing_interest_ttl;
let (outcome, mesh_aggregate) = {
let mut table = ctx.sensing_interest_table.lock();
let outcome = table.register(
branch,
sensing::DownstreamId::Leader,
strictest,
ttl,
ctx.sensing_local_root,
now,
);
(outcome, table.aggregate(branch, now))
};
if !matches!(outcome, sensing::RegisterOutcome::Registered(_)) {
return;
}
if branch.provider == ctx.local_node_id {
Self::feed_sensing_origin(ctx, branch, &spec, sensing::DownstreamId::Leader, now);
return;
}
let Some(current) = mesh_aggregate else {
return;
};
let upstream = sensing::SensingInterestFrame::provider_registration(
&spec,
branch.provider,
current,
ttl,
);
if let Ok(bytes) = sensing::encode_interest_frame(&upstream) {
spawn_sensing_frame_send(
&ctx.socket,
&ctx.peers,
&ctx.addr_to_node,
&ctx.router,
&ctx.partition_filter,
ctx.local_node_id,
branch.provider,
sensing::SUBPROTOCOL_SENSING_INTEREST as u64,
sensing::SUBPROTOCOL_SENSING_INTEREST,
bytes,
);
}
}
fn send_sensing_deregister_upstream(ctx: &DispatchCtx, key: &sensing::ProviderInterestKey) {
let frame = sensing::SensingInterestFrame::Deregister {
interest_digest: key.interest.interest_digest,
target: Some(key.provider),
};
if let Ok(bytes) = sensing::encode_interest_frame(&frame) {
spawn_sensing_frame_send(
&ctx.socket,
&ctx.peers,
&ctx.addr_to_node,
&ctx.router,
&ctx.partition_filter,
ctx.local_node_id,
key.provider,
sensing::SUBPROTOCOL_SENSING_INTEREST as u64,
sensing::SUBPROTOCOL_SENSING_INTEREST,
bytes,
);
}
}
fn handle_capability_announcement(payload: &[u8], from_node: u64, ctx: &DispatchCtx) {
let Some(mut ann) = CapabilityAnnouncement::from_bytes(payload) else {
tracing::trace!(
from_node = format!("{:#x}", from_node),
len = payload.len(),
"capability: decode failed"
);
return;
};
if ann.hop_count == 0 && ann.node_id != from_node {
tracing::trace!(
from_node = format!("{:#x}", from_node),
ann_node = format!("{:#x}", ann.node_id),
"capability: node_id mismatch (peer can only announce for itself)"
);
return;
}
if ann.node_id == ctx.local_node_id {
return;
}
let is_direct = ann.hop_count == 0;
let dedup_key = (ann.node_id, ann.version, is_direct);
if ctx.seen_announcements.contains_key(&dedup_key) {
return;
}
if ctx.require_signed_capabilities && ann.signature.is_none() {
tracing::trace!(
from_node = format!("{:#x}", from_node),
"capability: unsigned announcement rejected"
);
return;
}
let signature_verified = if ann.signature.is_some() {
if ann.verify().is_err() {
tracing::trace!(
from_node = format!("{:#x}", from_node),
"capability: signature verification failed"
);
return;
}
true
} else {
false
};
if ann.entity_id.node_id() != ann.node_id {
tracing::trace!(
from_node = format!("{:#x}", from_node),
claimed_node = format!("{:#x}", ann.node_id),
derived_node = format!("{:#x}", ann.entity_id.node_id()),
"capability: node_id does not match entity_id derivation"
);
return;
}
if signature_verified && ann.hop_count == 0 {
if let Some(existing) = ctx.peer_entity_ids.get(&from_node) {
if *existing.value() != ann.entity_id {
tracing::trace!(
from_node = format!("{:#x}", from_node),
"capability: entity_id rebind rejected (TOFU)"
);
return;
}
} else {
ctx.peer_entity_ids.insert(from_node, ann.entity_id.clone());
let origin_hash = ann.entity_id.origin_hash();
let _ = ctx
.origin_hash_to_node
.entry(origin_hash)
.or_insert(from_node);
}
}
if signature_verified && ann.hop_count == 0 {
if let Some(policy) = ctx.local_subnet_policy.as_ref() {
let subnet = policy.assign(&ann.capabilities);
ctx.peer_subnets.insert(from_node, subnet);
}
}
ctx.seen_announcements
.insert(dedup_key, std::time::Instant::now());
if ann.hop_count > 0 {
if let Some(entry) = ctx.peer_addrs.get(&from_node) {
let sender_addr = *entry.value();
let metric = u16::from(ann.hop_count) + 2;
ctx.router
.routing_table()
.add_route_with_metric(ann.node_id, sender_addr, metric);
}
}
if ann.hop_count < MAX_CAPABILITY_HOPS - 1 {
let mut forwarded = ann.clone();
forwarded.hop_count = forwarded.hop_count.saturating_add(1);
let fwd_bytes = forwarded.to_bytes();
Self::forward_capability_announcement(fwd_bytes, ann.node_id, from_node, ctx);
}
ann.strip_reserved_metadata();
if from_node != ctx.local_node_id {
Self::filter_unauthorized_heat_tags(&mut ann.capabilities);
}
let fold_ann = super::behavior::fold::capability_bridge::translate_announcement(&ann);
let _ = ctx.capability_fold.apply(fold_ann);
if ctx.enable_sensing_coalescing {
ctx.sensing_overlay_changed.send_modify(|generation| {
*generation = generation.wrapping_add(1);
});
#[cfg(feature = "redex")]
Self::reconcile_sensing_leader_fold(ctx, Instant::now());
}
ctx.ack_ranges_peer_cache.remove(&ann.node_id);
}
fn filter_unauthorized_heat_tags(
caps: &mut crate::adapter::net::behavior::capability::CapabilitySet,
) {
let mut claimed: std::collections::HashSet<String> = std::collections::HashSet::new();
for tag in &caps.tags {
if let Tag::Reserved { prefix, body } = tag {
if prefix == "causal:" {
let hex_end = body
.bytes()
.position(|b| b == b':' || b == b'[')
.unwrap_or(body.len());
claimed.insert(body[..hex_end].to_string());
}
}
}
let mut blob_heat_budget: usize = MAX_BLOB_HEAT_TAGS_PER_ANNOUNCE;
caps.tags.retain(|tag| match tag {
Tag::Reserved { prefix, body } if prefix == "heat:" => {
if body.starts_with("blob:") {
if blob_heat_budget == 0 {
return false;
}
blob_heat_budget -= 1;
return true;
}
let hex_end = body.bytes().position(|b| b == b'=').unwrap_or(body.len());
claimed.contains(&body[..hex_end])
}
_ => true,
});
}
fn forward_capability_announcement(
payload: Vec<u8>,
origin_node_id: u64,
sender_node_id: u64,
ctx: &DispatchCtx,
) {
let peers = ctx.peers.clone();
let socket = ctx.socket.clone();
let partition_filter = ctx.partition_filter.clone();
let router = ctx.router.clone();
tokio::spawn(async move {
let next_hop_addr = router.routing_table().lookup(origin_node_id);
for entry in peers.iter() {
let peer = entry.value();
if peer.node_id == sender_node_id {
continue; }
if Some(peer.addr) == next_hop_addr {
continue; }
if partition_filter.contains(&peer.addr) {
continue;
}
let session = &peer.session;
let stream_id = SUBPROTOCOL_CAPABILITY_ANN as u64;
let pool = session.thread_local_pool();
let mut builder = pool.get();
let seq = {
let stream = session.get_or_create_stream(stream_id);
stream.next_tx_seq()
};
let events = vec![Bytes::copy_from_slice(&payload)];
let packet = builder.build_subprotocol(
stream_id,
seq,
&events,
PacketFlags::NONE,
SUBPROTOCOL_CAPABILITY_ANN,
);
let _ = socket.send_to(&packet, peer.addr).await;
drop(builder);
session.touch();
}
});
}
#[cfg(feature = "nat-traversal")]
fn handle_punch_request(
from_node: u64,
req: super::traversal::rendezvous::PunchRequest,
ctx: &DispatchCtx,
) {
use super::traversal::rendezvous::{
PunchIntroduce, PunchReject, RejectReason, RendezvousMsg,
};
let Some((a_addr, a_session)) = ctx
.peers
.get(&from_node)
.map(|e| (e.value().addr, e.value().session.clone()))
else {
return;
};
let reject_target = req.target;
let reject_punch_id = req.punch_id;
let send_reject = |reason: RejectReason| {
let session = a_session.clone();
let socket = ctx.socket.clone();
let body = RendezvousMsg::PunchReject(PunchReject {
target: reject_target,
punch_id: reject_punch_id,
reason,
})
.encode();
tokio::spawn(async move {
let pool = session.thread_local_pool();
let mut builder = pool.get();
let seq = {
let stream = session
.get_or_create_stream(super::traversal::SUBPROTOCOL_RENDEZVOUS as u64);
stream.next_tx_seq()
};
let events = vec![body];
let packet = builder.build_subprotocol(
super::traversal::SUBPROTOCOL_RENDEZVOUS as u64,
seq,
&events,
PacketFlags::NONE,
super::traversal::SUBPROTOCOL_RENDEZVOUS,
);
let _ = socket.send_to(&packet, a_addr).await;
});
};
if !ctx.rendezvous_budgets.charge_request(
from_node,
ctx.traversal_config.punch_budget_window,
ctx.traversal_config.punch_requests_per_window,
) {
tracing::trace!(
from_node = format!("{:#x}", from_node),
"rendezvous: PunchRequest over per-requester budget; rejecting",
);
send_reject(RejectReason::RateLimited);
return;
}
let Some(b_reflex) =
super::behavior::fold::reflex_addr_for(&ctx.capability_fold, req.target)
else {
tracing::trace!(
from_node = format!("{:#x}", from_node),
target = format!("{:#x}", req.target),
"rendezvous: no cached reflex for target; rejecting PunchRequest",
);
send_reject(RejectReason::UnknownTargetReflex);
return;
};
if req.self_reflex.ip() != a_addr.ip() {
tracing::trace!(
from_node = format!("{:#x}", from_node),
claimed = %req.self_reflex,
session_src = %a_addr,
"rendezvous: PunchRequest self_reflex IP != session source; \
rejecting (anti-reflection)",
);
send_reject(RejectReason::ReflexMismatch);
return;
}
let a_reflex = req.self_reflex;
let fire_lead = ctx.traversal_config.punch_fire_lead;
let fire_at_ms = match std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH) {
Ok(d) => d
.saturating_add(fire_lead)
.as_millis()
.min(u64::MAX as u128) as u64,
Err(_) => {
return;
}
};
let intro_to_a = RendezvousMsg::PunchIntroduce(PunchIntroduce {
peer: req.target,
peer_reflex: b_reflex,
fire_at_ms,
})
.encode();
let intro_to_b = RendezvousMsg::PunchIntroduce(PunchIntroduce {
peer: from_node,
peer_reflex: a_reflex,
fire_at_ms,
})
.encode();
let Some((b_addr, b_session)) = ctx
.peers
.get(&req.target)
.map(|e| (e.value().addr, e.value().session.clone()))
else {
tracing::trace!(
from_node = format!("{:#x}", from_node),
target = format!("{:#x}", req.target),
"rendezvous: target peer not directly connected; rejecting",
);
send_reject(RejectReason::NoSessionWithTarget);
return;
};
if ctx.partition_filter.contains(&a_addr) || ctx.partition_filter.contains(&b_addr) {
return;
}
let socket_a = ctx.socket.clone();
let socket_b = ctx.socket.clone();
tokio::spawn(async move {
let pool = a_session.thread_local_pool();
let mut builder = pool.get();
let seq = {
let stream =
a_session.get_or_create_stream(super::traversal::SUBPROTOCOL_RENDEZVOUS as u64);
stream.next_tx_seq()
};
let events = vec![intro_to_a];
let packet = builder.build_subprotocol(
super::traversal::SUBPROTOCOL_RENDEZVOUS as u64,
seq,
&events,
PacketFlags::NONE,
super::traversal::SUBPROTOCOL_RENDEZVOUS,
);
let _ = socket_a.send_to(&packet, a_addr).await;
});
tokio::spawn(async move {
let pool = b_session.thread_local_pool();
let mut builder = pool.get();
let seq = {
let stream =
b_session.get_or_create_stream(super::traversal::SUBPROTOCOL_RENDEZVOUS as u64);
stream.next_tx_seq()
};
let events = vec![intro_to_b];
let packet = builder.build_subprotocol(
super::traversal::SUBPROTOCOL_RENDEZVOUS as u64,
seq,
&events,
PacketFlags::NONE,
super::traversal::SUBPROTOCOL_RENDEZVOUS,
);
let _ = socket_b.send_to(&packet, b_addr).await;
});
}
#[cfg(feature = "nat-traversal")]
fn unsolicited_introduce_permitted(
intro: &super::traversal::rendezvous::PunchIntroduce,
from_node: u64,
ctx: &DispatchCtx,
) -> Option<TrainSlot> {
if let Some(cached) =
super::behavior::fold::reflex_addr_for(&ctx.capability_fold, intro.peer)
{
if intro.peer_reflex.ip() != cached.ip() {
tracing::trace!(
from = from_node,
counterpart = intro.peer,
claimed = %intro.peer_reflex,
announced = %cached,
"rendezvous: unsolicited PunchIntroduce peer_reflex IP != \
announced reflex; dropping (anti-reflection, Finding 4)"
);
return None;
}
}
let window = ctx.traversal_config.punch_budget_window;
if !ctx.rendezvous_budgets.charge_train(
from_node,
window,
ctx.traversal_config.punch_trains_per_window,
) {
tracing::trace!(
from = from_node,
counterpart = intro.peer,
"rendezvous: unsolicited PunchIntroduce over per-source train \
budget; dropping"
);
return None;
}
match ctx
.rendezvous_budgets
.try_train_slot(ctx.traversal_config.punch_trains_concurrent_max)
{
Some(slot) => Some(slot),
None => {
tracing::trace!(
from = from_node,
counterpart = intro.peer,
"rendezvous: unsolicited PunchIntroduce over global concurrent-train \
ceiling; dropping"
);
None
}
}
}
#[cfg(feature = "nat-traversal")]
fn schedule_punch(
coordinator_node_id: u64,
intro: super::traversal::rendezvous::PunchIntroduce,
ctx: &DispatchCtx,
train_slot: Option<TrainSlot>,
) {
use super::traversal::rendezvous::{encode_keepalive, Keepalive, PunchAck, RendezvousMsg};
let Some((coord_addr, coord_session)) = ctx
.peers
.get(&coordinator_node_id)
.map(|e| (e.value().addr, e.value().session.clone()))
else {
return;
};
if ctx.partition_filter.contains(&coord_addr) {
return;
}
if ctx.partition_filter.contains(&intro.peer_reflex) {
return;
}
let (obs_tx, obs_rx) = oneshot::channel();
ctx.punch_observers
.insert(intro.peer_reflex, (intro.peer, obs_tx));
let now_ms = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map(|d| d.as_millis() as u64)
.unwrap_or(0);
let offsets = keepalive_send_offsets(
intro.fire_at_ms,
now_ms,
ctx.traversal_config.punch_deadline,
);
let local_node_id = ctx.local_node_id;
let peer_reflex = intro.peer_reflex;
let peer = intro.peer;
let socket_send = ctx.socket.clone();
let socket_ack = ctx.socket.clone();
let deadline = ctx.traversal_config.punch_deadline;
let punch_observers = ctx.punch_observers.clone();
let keepalive_payload = encode_keepalive(&Keepalive {
sender_node_id: local_node_id,
punch_id: 0, });
tokio::spawn(async move {
let start = tokio::time::Instant::now();
for offset in offsets {
tokio::time::sleep_until(start + offset).await;
let _ = socket_send
.send_to(&keepalive_payload[..], peer_reflex)
.await;
}
let mut resend = tokio::time::interval(Duration::from_millis(250));
resend.tick().await; while start.elapsed() < deadline {
resend.tick().await;
let _ = socket_send
.send_to(&keepalive_payload[..], peer_reflex)
.await;
}
});
tokio::spawn(async move {
let _train_slot = train_slot;
if !await_punch_observer_outcome(obs_rx, deadline, &punch_observers, peer_reflex).await
{
return;
}
let ack_body = RendezvousMsg::PunchAck(PunchAck {
from_peer: local_node_id,
to_peer: peer,
punch_id: 0,
})
.encode();
let pool = coord_session.thread_local_pool();
let mut builder = pool.get();
let seq = {
let stream = coord_session
.get_or_create_stream(super::traversal::SUBPROTOCOL_RENDEZVOUS as u64);
stream.next_tx_seq()
};
let events = vec![ack_body];
let packet = builder.build_subprotocol(
super::traversal::SUBPROTOCOL_RENDEZVOUS as u64,
seq,
&events,
PacketFlags::NONE,
super::traversal::SUBPROTOCOL_RENDEZVOUS,
);
let _ = socket_ack.send_to(&packet, coord_addr).await;
});
}
#[cfg(feature = "nat-traversal")]
fn forward_punch_ack(ack: super::traversal::rendezvous::PunchAck, ctx: &DispatchCtx) {
use super::traversal::rendezvous::RendezvousMsg;
let Some((dest_addr, dest_session)) = ctx
.peers
.get(&ack.to_peer)
.map(|e| (e.value().addr, e.value().session.clone()))
else {
tracing::trace!(
to_peer = format!("{:#x}", ack.to_peer),
"rendezvous: no session with PunchAck.to_peer; dropping",
);
return;
};
if ctx.partition_filter.contains(&dest_addr) {
return;
}
let body = RendezvousMsg::PunchAck(ack).encode();
let socket = ctx.socket.clone();
tokio::spawn(async move {
let pool = dest_session.thread_local_pool();
let mut builder = pool.get();
let seq = {
let stream = dest_session
.get_or_create_stream(super::traversal::SUBPROTOCOL_RENDEZVOUS as u64);
stream.next_tx_seq()
};
let events = vec![body];
let packet = builder.build_subprotocol(
super::traversal::SUBPROTOCOL_RENDEZVOUS as u64,
seq,
&events,
PacketFlags::NONE,
super::traversal::SUBPROTOCOL_RENDEZVOUS,
);
let _ = socket.send_to(&packet, dest_addr).await;
});
}
fn authorize_subscribe(
channel: &ChannelName,
from_node: u64,
token_bytes: Option<&[u8]>,
ctx: &DispatchCtx,
) -> (bool, Option<AckReason>) {
if Self::is_auth_throttled(from_node, ctx) {
return (false, Some(AckReason::RateLimited));
}
let channel_id = ChannelId::new(channel.clone());
let already_subscribed = ctx.roster.is_subscribed(from_node, &channel_id);
if !already_subscribed
&& ctx.roster.channels_for_peer_count(from_node) >= ctx.max_channels_per_peer
{
return (false, Some(AckReason::TooManyChannels));
}
let Some(ref configs) = ctx.channel_configs else {
return (true, None);
};
let Some(cfg_ref) = configs.get_by_name(channel.as_str()) else {
return (false, Some(AckReason::UnknownChannel));
};
let cfg = cfg_ref.clone();
drop(cfg_ref);
let peer_subnet = ctx
.peer_subnets
.get(&from_node)
.map(|e| *e.value())
.unwrap_or(SubnetId::GLOBAL);
let visible = Self::subnet_visible(ctx.local_subnet, peer_subnet, cfg.visibility);
if let Some(gw) = ctx.subnet_gateway.as_ref() {
if visible {
gw.record_forward();
} else {
gw.record_drop(Self::visibility_drop_reason(cfg.visibility));
}
}
if !visible {
return (false, Some(AckReason::Unauthorized));
}
let presented_chain = token_bytes.and_then(|bytes| TokenChain::from_bytes(bytes).ok());
let has_auth_gates =
cfg.publish_caps.is_some() || cfg.subscribe_caps.is_some() || cfg.token_required();
if !has_auth_gates {
return (true, None);
}
let peer_caps = super::behavior::fold::capability_bridge::synthesize_capability_set(
&ctx.capability_fold,
from_node,
);
let transient_revocation;
let (revocation, skew_secs): (&RevocationRegistry, u64) = match ctx.token_cache.as_ref() {
Some(cache) => (cache.revocation().as_ref(), cache.clock_skew_secs()),
None => {
transient_revocation = RevocationRegistry::new();
(&transient_revocation, 0)
}
};
let Some(peer_entity) = ctx
.peer_entity_ids
.get(&from_node)
.map(|e| e.value().clone())
else {
if cfg.token_required() {
return (false, Some(AckReason::Unauthorized));
}
let dummy = EntityId::from_bytes([0u8; 32]);
return if cfg.can_subscribe(&peer_caps, &dummy, None, revocation, skew_secs) {
(true, None)
} else {
(false, Some(AckReason::Unauthorized))
};
};
if !cfg.can_subscribe(
&peer_caps,
&peer_entity,
presented_chain.as_ref(),
revocation,
skew_secs,
) {
return (false, Some(AckReason::Unauthorized));
}
if let Some(chain) = presented_chain {
ctx.subscriber_chains.insert(
(from_node, cfg.channel_id.hash()),
RetainedChain::new(chain),
);
}
(true, None)
}
fn is_auth_throttled(from_node: u64, ctx: &DispatchCtx) -> bool {
if ctx.max_auth_failures_per_window == u16::MAX {
return false; }
let Some(mut entry) = ctx.auth_failures.get_mut(&from_node) else {
return false;
};
match entry.throttled_until {
Some(until) if std::time::Instant::now() < until => true,
Some(_) => {
entry.throttled_until = None;
entry.failures = 0;
entry.window_start = None;
false
}
None => false,
}
}
fn record_auth_failure(from_node: u64, ctx: &DispatchCtx) {
if ctx.max_auth_failures_per_window == u16::MAX {
return;
}
let now = std::time::Instant::now();
let mut entry = ctx.auth_failures.entry(from_node).or_default();
let reset_window = match entry.window_start {
Some(start) => now.duration_since(start) >= ctx.auth_failure_window,
None => true,
};
if reset_window {
entry.window_start = Some(now);
entry.failures = 0;
}
entry.failures = entry.failures.saturating_add(1);
if entry.failures >= ctx.max_auth_failures_per_window {
entry.throttled_until = Some(now + ctx.auth_throttle_duration);
}
}
fn clear_auth_failures(from_node: u64, ctx: &DispatchCtx) {
ctx.auth_failures.remove(&from_node);
}
fn visibility_drop_reason(visibility: Visibility) -> DropReason {
match visibility {
Visibility::Global => DropReason::SubnetLocal, Visibility::SubnetLocal => DropReason::SubnetLocal,
Visibility::ParentVisible => DropReason::NotAncestor,
Visibility::Exported => DropReason::NotExported,
}
}
fn subnet_visible(source: SubnetId, dest: SubnetId, visibility: Visibility) -> bool {
match visibility {
Visibility::Global => true,
Visibility::SubnetLocal => source.is_same_subnet(dest),
Visibility::ParentVisible => {
dest.is_ancestor_of(source)
}
Visibility::Exported => false,
}
}
fn send_membership_ack(
to_node: u64,
nonce: u64,
accepted: bool,
reason: Option<AckReason>,
ctx: &DispatchCtx,
) {
let Some(peer_entry) = ctx.peers.get(&to_node) else {
return;
};
let dest_addr = peer_entry.value().addr;
if ctx.partition_filter.contains(&dest_addr) {
return;
}
let dest_sess = peer_entry.value().session.clone();
let socket = ctx.socket.clone();
let ack = MembershipMsg::Ack {
nonce,
accepted,
reason,
};
let bytes = Bytes::from(membership::encode(&ack));
drop(peer_entry);
tokio::spawn(async move {
let pool = dest_sess.thread_local_pool();
let mut builder = pool.get();
let stream_id = SUBPROTOCOL_CHANNEL_MEMBERSHIP as u64;
let seq = {
let stream = dest_sess.get_or_create_stream(stream_id);
stream.next_tx_seq()
};
let events = vec![bytes];
let packet = builder.build_subprotocol(
stream_id,
seq,
&events,
PacketFlags::NONE,
SUBPROTOCOL_CHANNEL_MEMBERSHIP,
);
let _ = socket.send_to(&packet, dest_addr).await;
});
}
pub fn channel_publisher(
&self,
channel: ChannelName,
config: PublishConfig,
) -> ChannelPublisher {
ChannelPublisher::new(channel, config)
}
pub async fn publish(
&self,
publisher: &ChannelPublisher,
payload: Bytes,
) -> Result<PublishReport, AdapterError> {
self.publish_many(publisher, &[payload]).await
}
pub async fn publish_many(
&self,
publisher: &ChannelPublisher,
events: &[Bytes],
) -> Result<PublishReport, AdapterError> {
let cfg_snapshot = self.channel_configs.as_ref().and_then(|cr| {
cr.get_by_name(publisher.channel().name().as_str())
.map(|c| c.clone())
});
if let Some(cfg) = cfg_snapshot.as_ref() {
if cfg.publish_caps.is_some() || cfg.token_required() {
let self_caps = self
.local_announcement
.load()
.as_deref()
.map(|ann| ann.capabilities.clone())
.unwrap_or_default();
let self_entity = self.identity.entity_id().clone();
let held = self
.published_chains
.get(&cfg.channel_id.hash())
.map(|c| c.value().clone());
let transient_revocation;
let (revocation, skew, chain) = match self.token_cache.as_ref() {
Some(cache) => {
let chain = held.or_else(|| {
cache
.get_for_action(
&self_entity,
TokenScope::PUBLISH,
cfg.channel_id.hash(),
)
.map(TokenChain::single)
});
(cache.revocation().as_ref(), cache.clock_skew_secs(), chain)
}
None => {
transient_revocation = RevocationRegistry::new();
(&transient_revocation, 0u64, held)
}
};
if !cfg.can_publish(&self_caps, &self_entity, chain.as_ref(), revocation, skew) {
return Err(AdapterError::Connection(
"channel: publish denied by channel ACL".into(),
));
}
}
}
let mut subscribers = self.roster.dispatch_recipients(publisher.channel());
let visibility = cfg_snapshot
.as_ref()
.map(|c| c.visibility)
.unwrap_or(self.config.default_visibility);
let channel_name = publisher.channel().name().clone();
let channel_hash = channel_name.hash();
let auth_guard = self.auth_guard.clone();
let require_token = cfg_snapshot
.as_ref()
.map(|c| c.token_required())
.unwrap_or(false);
subscribers.retain(|peer_id| {
let peer_subnet = self
.peer_subnets
.get(peer_id)
.map(|e| *e.value())
.unwrap_or(SubnetId::GLOBAL);
let visible = Self::subnet_visible(self.local_subnet, peer_subnet, visibility);
if let Some(gw) = self.subnet_gateway.as_ref() {
if visible {
gw.record_forward();
} else {
gw.record_drop(Self::visibility_drop_reason(visibility));
}
}
if !visible {
return false;
}
let origin = subscriber_origin_hash(*peer_id);
let admitted = match auth_guard.check_fast(origin, channel_hash) {
AuthVerdict::Allowed => auth_guard.is_authorized_full(origin, &channel_name),
AuthVerdict::Denied => false,
AuthVerdict::NeedsFullCheck => {
if auth_guard.is_authorized_full(origin, &channel_name) {
auth_guard.allow_channel(origin, &channel_name);
true
} else {
false
}
}
};
if !admitted {
return false;
}
if !require_token {
return true;
}
let entity = self.peer_entity_ids.get(peer_id).map(|e| e.value().clone());
let chain_ok = match (entity, self.token_cache.as_ref(), cfg_snapshot.as_ref()) {
(Some(entity), Some(cache), Some(cfg)) => self
.subscriber_chains
.get(&(*peer_id, channel_hash))
.is_some_and(|r| {
let revocation = cache.revocation().as_ref();
let skew = cache.clock_skew_secs();
if r.signatures_verified.load(Ordering::Relaxed) {
cfg.reverify_subscribe_presigned(&r.chain, &entity, revocation, skew)
} else if cfg.reverify_subscribe(&r.chain, &entity, revocation, skew) {
r.signatures_verified.store(true, Ordering::Relaxed);
true
} else {
false
}
}),
_ => false,
};
if !chain_ok {
auth_guard.revoke_channel(origin, &channel_name);
return false;
}
true
});
let mut report = PublishReport {
attempted: subscribers.len(),
delivered: 0,
errors: Vec::new(),
};
if subscribers.is_empty() {
return Ok(report);
}
let reliable = publisher.config().reliability.is_reliable();
let stream_id = Self::publish_stream_id(publisher.channel());
let max_inflight = publisher.config().max_inflight;
let on_failure = publisher.config().on_failure;
use tokio::sync::Semaphore;
let sem = Arc::new(Semaphore::new(max_inflight.max(1)));
match on_failure {
OnFailure::FailFast => {
for peer_id in &subscribers {
match self
.publish_to_peer(*peer_id, channel_hash, stream_id, reliable, events)
.await
{
Ok(()) => report.delivered += 1,
Err(e) => {
report.errors.push((*peer_id, e));
return Ok(report);
}
}
}
Ok(report)
}
OnFailure::BestEffort | OnFailure::Collect => {
let mut handles = Vec::with_capacity(subscribers.len());
let events_shared: Arc<[Bytes]> = events.to_vec().into();
for peer_id in subscribers {
let permit = Arc::clone(&sem);
let events_for_task: Arc<[Bytes]> = Arc::clone(&events_shared);
let fut = async move {
let _permit = permit.acquire_owned().await.ok();
(
peer_id,
self.publish_to_peer(
peer_id,
channel_hash,
stream_id,
reliable,
&events_for_task,
)
.await,
)
};
handles.push(fut);
}
let results = futures::future::join_all(handles).await;
for (peer_id, res) in results {
match res {
Ok(()) => report.delivered += 1,
Err(e) => report.errors.push((peer_id, e)),
}
}
if matches!(on_failure, OnFailure::BestEffort)
&& report.delivered == 0
&& !report.errors.is_empty()
{
let first = report
.errors
.first()
.map(|(id, e)| {
format!(
"all {} peers failed (first: {:#x}: {})",
report.attempted, id, e
)
})
.unwrap_or_else(|| "all peers failed".into());
return Err(AdapterError::Connection(first));
}
Ok(report)
}
}
}
pub(super) fn publish_stream_id(channel: &ChannelId) -> u64 {
0x0001_0000_0000_0000 | channel.hash()
}
pub(super) async fn publish_to_peer(
&self,
peer_node_id: u64,
channel_hash: ChannelHash,
stream_id: u64,
reliable: bool,
events: &[Bytes],
) -> Result<(), AdapterError> {
let (dest_addr, session) = match self.peers.get(&peer_node_id) {
Some(p) => (p.value().addr, p.value().session.clone()),
None => {
return Err(AdapterError::Connection(format!(
"publish: no session for subscriber {:#x}",
peer_node_id
)));
}
};
if self.partition_filter.contains(&dest_addr) {
return Err(AdapterError::Connection(format!(
"publish: peer {:#x} is partitioned",
peer_node_id
)));
}
session.open_stream_with(stream_id, reliable, 1);
let payload_bytes: usize = events.iter().map(|e| EventFrame::LEN_SIZE + e.len()).sum();
let needed = wire_bytes_for_payload(payload_bytes);
let (guard, seq) = match session.try_acquire_tx_credit_guard(stream_id, needed) {
TxAdmit::Acquired { guard, seq } => (guard, seq),
TxAdmit::WindowFull => {
return Err(AdapterError::Connection(format!(
"publish: stream {:#x} backpressured",
stream_id
)));
}
TxAdmit::StreamClosed => {
return Err(AdapterError::Connection(format!(
"publish: stream {:#x} closed",
stream_id
)));
}
};
let pool = session.thread_local_pool();
let mut builder = pool.get();
builder.set_channel_hash(channel_hash as u16);
builder.set_origin_hash(self.identity.entity_id().origin_hash());
let flags = if reliable {
PacketFlags::RELIABLE
} else {
PacketFlags::NONE
};
let packet = builder.build_subprotocol(
stream_id, seq, events, flags, 0,
);
let next_hop = self
.router
.routing_table()
.lookup(peer_node_id)
.unwrap_or(dest_addr);
self.socket
.send_to(&packet, next_hop)
.await
.map_err(|e| AdapterError::Connection(format!("publish send failed: {}", e)))?;
guard.commit();
drop(builder);
session.touch();
Ok(())
}
pub async fn publish_fold_to_peer<P>(
&self,
peer_addr: SocketAddr,
ann: &super::behavior::fold::SignedAnnouncement<P>,
) -> Result<usize, AdapterError>
where
P: serde::Serialize + serde::de::DeserializeOwned,
{
let bytes = ann
.encode()
.map_err(|e| AdapterError::Connection(format!("fold: encode failed: {e}")))?;
let n = bytes.len();
self.send_subprotocol(peer_addr, super::behavior::fold::SUBPROTOCOL_FOLD, &bytes)
.await?;
Ok(n)
}
pub async fn publish_fold_broadcast<P>(
&self,
ann: &super::behavior::fold::SignedAnnouncement<P>,
) -> Result<usize, AdapterError>
where
P: serde::Serialize + serde::de::DeserializeOwned,
{
let bytes = ann
.encode()
.map_err(|e| AdapterError::Connection(format!("fold: encode failed: {e}")))?;
let peer_addrs: Vec<SocketAddr> = self.peers.iter().map(|e| e.value().addr).collect();
let bytes_ref = &bytes;
let sends = peer_addrs.into_iter().map(|addr| async move {
let result = self
.send_subprotocol(addr, super::behavior::fold::SUBPROTOCOL_FOLD, bytes_ref)
.await;
(addr, result)
});
let results = futures::future::join_all(sends).await;
let mut sent = 0usize;
for (addr, result) in results {
match result {
Ok(()) => sent += 1,
Err(e) => {
tracing::trace!(peer = %addr, error = %e, "fold: broadcast send failed");
}
}
}
Ok(sent)
}
pub(crate) fn next_fold_generation(&self, kind: u16, class: u64) -> u64 {
let entry = self
.fold_generations
.entry((kind, class))
.or_insert_with(FoldGenerationEntry::new);
entry
.last_touched_us
.store(super::current_timestamp_micros(), Ordering::Relaxed);
entry.counter.fetch_add(1, Ordering::Relaxed) + 1
}
async fn publish_fold<K>(
&self,
counter_class: u64,
envelope_class: u64,
payload: K::Payload,
) -> Result<usize, AdapterError>
where
K: super::behavior::fold::FoldKind,
{
let gen = self.next_fold_generation(K::KIND_ID, counter_class);
let meta = super::behavior::fold::EnvelopeMeta {
announced_at: super::current_timestamp_micros(),
..Default::default()
};
let ann = super::behavior::fold::SignedAnnouncement::sign(
&self.identity,
K::KIND_ID,
envelope_class,
self.node_id,
gen,
meta,
payload,
)
.map_err(|e| AdapterError::Connection(format!("fold: sign failed: {e}")))?;
self.publish_fold_broadcast(&ann).await
}
pub async fn publish_capability_membership(
&self,
membership: super::behavior::fold::CapabilityMembership,
) -> Result<usize, AdapterError> {
let class = membership.class_hash;
self.publish_fold::<super::behavior::fold::CapabilityFold>(class, class, membership)
.await
}
pub async fn publish_route(
&self,
destination: super::behavior::fold::NodeId,
next_hop: SocketAddr,
metric: u32,
) -> Result<usize, AdapterError> {
self.publish_fold::<super::behavior::fold::RoutingFold>(
0,
0,
super::behavior::fold::RouteAnnouncement {
destination,
next_hop,
metric,
via: self.node_id,
},
)
.await
}
pub async fn publish_reservation(
&self,
resource_id: super::behavior::fold::ResourceId,
state: super::behavior::fold::ReservationState,
) -> Result<usize, AdapterError> {
self.publish_fold::<super::behavior::fold::ReservationFold>(
resource_id,
0,
super::behavior::fold::ReservationAnnouncement { resource_id, state },
)
.await
}
pub async fn publish_island_topology(
&self,
mut record: super::behavior::fold::IslandRecord,
) -> Result<usize, AdapterError> {
use super::behavior::fold::{FoldKind, IslandTopologyFold};
record.host = self.node_id;
let island_id = record.id;
let gen = self.next_fold_generation(IslandTopologyFold::KIND_ID, island_id);
let meta = super::behavior::fold::EnvelopeMeta {
announced_at: super::current_timestamp_micros(),
..Default::default()
};
let ann = super::behavior::fold::SignedAnnouncement::sign(
&self.identity,
IslandTopologyFold::KIND_ID,
0,
self.node_id,
gen,
meta,
record,
)
.map_err(|e| AdapterError::Connection(format!("island: sign failed: {e}")))?;
let _ = self.island_fold.apply(ann.clone());
self.publish_fold_broadcast(&ann).await
}
pub async fn send_subprotocol(
&self,
peer_addr: SocketAddr,
subprotocol_id: u16,
payload: &[u8],
) -> Result<(), AdapterError> {
if self.partition_filter.contains(&peer_addr) {
return Ok(());
}
let node_id = self
.addr_to_node
.get(&peer_addr)
.map(|e| *e.value())
.ok_or_else(|| AdapterError::Connection("unknown peer".into()))?;
let peer = self
.peers
.get(&node_id)
.ok_or_else(|| AdapterError::Connection("unknown peer".into()))?;
let session = &peer.session;
let stream_id = subprotocol_id as u64;
let pool = session.thread_local_pool();
let mut builder = pool.get();
let seq = {
let stream = session.get_or_create_stream(stream_id);
stream.next_tx_seq()
};
let events = vec![Bytes::copy_from_slice(payload)];
let packet =
builder.build_subprotocol(stream_id, seq, &events, PacketFlags::NONE, subprotocol_id);
self.socket
.send_to(&packet, peer_addr)
.await
.map_err(|e| AdapterError::Connection(format!("send failed: {}", e)))?;
drop(builder);
session.touch();
Ok(())
}
#[cfg(feature = "cortex")]
pub(crate) fn index_self_with_local_services(&self) {
let baseline = self.user_caps_snapshot();
let merged = {
let mut m = baseline;
for svc in self.rpc_local_services.snapshot() {
m = m.add_tag(format!("nrpc:{}", svc.as_str()));
}
m
};
let version = self.capability_version.fetch_add(1, Ordering::Relaxed) + 1;
let mut ann = CapabilityAnnouncement::new(
self.node_id,
self.identity.entity_id().clone(),
version,
merged,
)
.with_ttl(300);
ann.sign(&self.identity);
let fold_ann = super::behavior::fold::capability_bridge::translate_announcement(&ann);
let _ = self.capability_fold.apply(fold_ann);
}
pub async fn announce_capabilities(&self, caps: CapabilitySet) -> Result<(), AdapterError> {
self.announce_capabilities_with(caps, Duration::from_secs(300), true)
.await
}
pub async fn announce_capabilities_with(
&self,
caps: CapabilitySet,
ttl: Duration,
sign: bool,
) -> Result<(), AdapterError> {
self.announce_from_baseline(Some(caps), ttl, sign).await
}
async fn announce_from_baseline(
&self,
new_baseline: Option<CapabilitySet>,
ttl: Duration,
sign: bool,
) -> Result<(), AdapterError> {
let to_broadcast = {
let _announce_guard = self.announce_mu.lock();
let caps = match new_baseline {
Some(caps) => {
*self.user_caps.write() = Some(caps.clone());
caps
}
None => self.user_caps.read().clone().unwrap_or_default(),
};
#[cfg(feature = "cortex")]
let caps = if self.rpc_local_services.is_empty() {
caps
} else {
let mut merged = caps;
for svc in self.rpc_local_services.snapshot() {
let tag = format!("nrpc:{}", svc.as_str());
merged = merged.add_tag(tag);
}
merged
};
#[cfg(feature = "tool")]
let caps = if self.tool_registry.is_empty() {
caps
} else {
use crate::adapter::net::behavior::ToolCapability;
use crate::adapter::net::cortex::tool::{
description_metadata_key, pricing_terms_metadata_key, streaming_metadata_key,
tags_metadata_key,
};
let snapshot = self.tool_registry.snapshot();
let tools_to_add: Vec<ToolCapability> = snapshot
.iter()
.map(|descriptor| {
let mut cap = ToolCapability::new(&descriptor.tool_id, &descriptor.name)
.with_version(&descriptor.version)
.with_estimated_time(descriptor.estimated_time_ms)
.with_stateless(descriptor.stateless);
if let Some(ref schema) = descriptor.input_schema {
cap = cap.with_input_schema(schema.clone());
}
if let Some(ref schema) = descriptor.output_schema {
cap = cap.with_output_schema(schema.clone());
}
for req in &descriptor.requires {
cap = cap.requires(req.clone());
}
cap
})
.collect();
let mut merged = caps.add_tools(tools_to_add);
for descriptor in snapshot.iter() {
merged = merged.add_tag(format!("ai-tool:{}", descriptor.tool_id));
if let Some(ref desc) = descriptor.description {
merged = merged.with_metadata(
description_metadata_key(&descriptor.tool_id),
desc.clone(),
);
}
if descriptor.streaming {
merged = merged.with_metadata(
streaming_metadata_key(&descriptor.tool_id),
"1".to_string(),
);
}
if !descriptor.tags.is_empty() {
merged = merged.with_metadata(
tags_metadata_key(&descriptor.tool_id),
descriptor.tags.join(","),
);
}
if let Some(ref terms) = descriptor.pricing_terms {
merged = merged.with_metadata(
pricing_terms_metadata_key(&descriptor.tool_id),
terms.clone(),
);
}
}
merged
};
let caps = if self.config.enable_stream_ack_ranges {
caps.add_tag(ACK_RANGES_CAPABILITY_TAG.to_string())
} else {
caps
};
let version = self.capability_version.fetch_add(1, Ordering::Relaxed) + 1;
#[cfg(feature = "nat-traversal")]
let (caps, reflex_snapshot) = {
use super::traversal::classify::NatClass;
let _g = self.traversal_publish_mu.lock();
let class =
NatClass::from_u8(self.nat_class.load(std::sync::atomic::Ordering::Acquire));
let reflex = self.reflex_addr.load_full().map(|arc| *arc);
let mut next = caps;
next.tags.retain(|t| !t.to_string().starts_with("nat:"));
let next = next.add_tag(class.tag().to_string());
(next, reflex)
};
self.proximity_graph.set_local_capabilities(caps.clone());
let mut ann = CapabilityAnnouncement::new(
self.node_id,
self.identity.entity_id().clone(),
version,
caps,
)
.with_ttl(ttl.as_secs().min(u32::MAX as u64) as u32);
#[cfg(feature = "nat-traversal")]
{
ann = ann.with_reflex_addr(reflex_snapshot);
}
if sign {
ann.sign(&self.identity);
}
let fold_ann = super::behavior::fold::capability_bridge::translate_announcement(&ann);
let _ = self.capability_fold.apply(fold_ann);
self.local_announcement.store(Some(Arc::new(ann.clone())));
let now = std::time::Instant::now();
let min_interval = self.config.min_announce_interval;
let defer_for = {
let mut gate = self.announce_gate.lock();
let elapsed = gate
.last_broadcast_at
.map(|t| now.saturating_duration_since(t));
match elapsed {
Some(e) if e < min_interval => {
if gate.deferred_scheduled {
return Ok(());
}
if self.self_weak.get().is_none() {
tracing::debug!(
"capability: in-window announce not deferred \
(node not started via start_arc)"
);
return Ok(());
}
gate.deferred_scheduled = true;
gate.deferral_generation = gate.deferral_generation.wrapping_add(1);
Some((min_interval - e, gate.deferral_generation))
}
_ => {
gate.last_broadcast_at = Some(now);
None
}
}
};
if let Some((delay, generation)) = defer_for {
self.spawn_deferred_announce(delay, generation);
None
} else {
Some(ann.to_bytes())
}
};
let Some(bytes) = to_broadcast else {
return Ok(());
};
self.broadcast_announcement_bytes(&bytes).await;
Ok(())
}
async fn broadcast_announcement_bytes(&self, bytes: &[u8]) {
let peer_addrs: Vec<SocketAddr> = self.peers.iter().map(|e| e.value().addr).collect();
for addr in peer_addrs {
if let Err(e) = self
.send_subprotocol(addr, SUBPROTOCOL_CAPABILITY_ANN, bytes)
.await
{
tracing::trace!(peer = %addr, error = %e, "capability: announce send failed");
}
}
}
fn spawn_deferred_announce(&self, delay: Duration, generation: u64) {
let Some(weak) = self.self_weak.get().cloned() else {
self.announce_gate.lock().deferred_scheduled = false;
return;
};
let shutdown = self.shutdown.clone();
let shutdown_notify = self.shutdown_notify.clone();
tokio::spawn(async move {
tokio::select! {
_ = tokio::time::sleep(delay) => {}
_ = shutdown_notify.notified() => return,
}
if shutdown.load(Ordering::Acquire) {
return;
}
let Some(node) = weak.upgrade() else { return };
node.flush_deferred_announce(generation).await;
});
}
async fn flush_deferred_announce(&self, generation: u64) {
{
let mut gate = self.announce_gate.lock();
if !gate.deferred_scheduled || gate.deferral_generation != generation {
return;
}
gate.deferred_scheduled = false;
gate.last_broadcast_at = Some(std::time::Instant::now());
}
let Some(ann) = self.local_announcement.load_full() else {
return;
};
self.broadcast_announcement_bytes(&ann.to_bytes()).await;
}
fn chain_hex(origin_hash: u64) -> String {
format!("{origin_hash:016x}")
}
fn is_causal_for(tag: &Tag, hex: &str) -> bool {
match tag {
Tag::Reserved { prefix, body } if prefix.as_str() == "causal:" => {
Self::is_causal_body_for(body, hex)
}
_ => false,
}
}
fn is_causal_for_str(tag: &str, hex: &str) -> bool {
let Some(body) = tag.strip_prefix("causal:") else {
return false;
};
Self::is_causal_body_for(body, hex)
}
fn is_causal_body_for(body: &str, hex: &str) -> bool {
if !body.starts_with(hex) {
return false;
}
matches!(
body.as_bytes().get(hex.len()),
None | Some(b':') | Some(b'[')
)
}
fn replace_causal_tags(caps: &mut CapabilitySet, origin_hash: u64, replacement: Option<Tag>) {
let hex = Self::chain_hex(origin_hash);
caps.tags.retain(|t| !Self::is_causal_for(t, &hex));
if let Some(t) = replacement {
caps.tags.insert(t);
}
}
#[cfg(feature = "dataforts")]
fn is_heat_for(tag: &Tag, hex: &str) -> bool {
match tag {
Tag::Reserved { prefix, body } if prefix == "heat:" => {
if !body.starts_with(hex) {
return false;
}
matches!(body.as_bytes().get(hex.len()), Some(b'='))
}
_ => false,
}
}
#[cfg(feature = "dataforts")]
fn replace_heat_tags(caps: &mut CapabilitySet, origin_hash: u64, replacement: Option<Tag>) {
let hex = Self::chain_hex(origin_hash);
caps.tags.retain(|t| !Self::is_heat_for(t, &hex));
if let Some(t) = replacement {
caps.tags.insert(t);
}
}
#[cfg(feature = "dataforts")]
fn blob_hex(hash: &[u8; 32]) -> String {
let mut s = String::with_capacity(64);
for b in hash {
use std::fmt::Write;
let _ = write!(s, "{:02x}", b);
}
s
}
#[cfg(feature = "dataforts")]
fn is_blob_heat_for(tag: &Tag, hex64: &str) -> bool {
match tag {
Tag::Reserved { prefix, body } if prefix == "heat:" => {
if !body.starts_with("blob:") {
return false;
}
let rest = &body[5..];
if !rest.starts_with(hex64) {
return false;
}
matches!(rest.as_bytes().get(hex64.len()), Some(b'='))
}
_ => false,
}
}
#[cfg(feature = "dataforts")]
fn replace_blob_heat_tags(caps: &mut CapabilitySet, hash: &[u8; 32], replacement: Option<Tag>) {
let hex = Self::blob_hex(hash);
caps.tags.retain(|t| !Self::is_blob_heat_for(t, &hex));
if let Some(t) = replacement {
caps.tags.insert(t);
}
}
pub(crate) fn user_caps_snapshot(&self) -> CapabilitySet {
self.user_caps.read().clone().unwrap_or_default()
}
pub async fn announce_chain(&self, origin_hash: u64, tip_seq: u64) -> Result<(), AdapterError> {
let hex = Self::chain_hex(origin_hash);
let replacement = Tag::parse(&format!("causal:{hex}:{tip_seq}")).ok();
let mut snapshot = self.user_caps_snapshot();
Self::replace_causal_tags(&mut snapshot, origin_hash, replacement);
self.announce_capabilities(snapshot).await
}
pub async fn announce_chain_range(
&self,
origin_hash: u64,
start_seq: u64,
end_seq: u64,
) -> Result<(), AdapterError> {
if start_seq >= end_seq {
return Ok(());
}
let hex = Self::chain_hex(origin_hash);
let replacement = Tag::parse(&format!("causal:{hex}[{start_seq}..{end_seq}]")).ok();
let mut snapshot = self.user_caps_snapshot();
Self::replace_causal_tags(&mut snapshot, origin_hash, replacement);
self.announce_capabilities(snapshot).await
}
pub async fn withdraw_chain(&self, origin_hash: u64) -> Result<(), AdapterError> {
let mut snapshot = self.user_caps_snapshot();
Self::replace_causal_tags(&mut snapshot, origin_hash, None);
self.announce_capabilities(snapshot).await
}
#[cfg(feature = "dataforts")]
pub async fn announce_heat(&self, origin_hash: u64, rate: f64) -> Result<(), AdapterError> {
let hex = Self::chain_hex(origin_hash);
let clamped = if rate.is_finite() {
rate.clamp(0.0, 1.0)
} else {
return Err(AdapterError::Fatal("heat rate must be finite".to_string()));
};
let replacement = Tag::parse(&format!("heat:{hex}={clamped:.2}")).ok();
let mut snapshot = self.user_caps_snapshot();
Self::replace_heat_tags(&mut snapshot, origin_hash, replacement);
self.announce_capabilities(snapshot).await
}
#[cfg(feature = "dataforts")]
pub async fn withdraw_heat(&self, origin_hash: u64) -> Result<(), AdapterError> {
let mut snapshot = self.user_caps_snapshot();
Self::replace_heat_tags(&mut snapshot, origin_hash, None);
self.announce_capabilities(snapshot).await
}
#[cfg(feature = "dataforts")]
pub async fn announce_heat_batch(
&self,
updates: &[(u64, Option<f64>)],
) -> Result<(), AdapterError> {
if updates.is_empty() {
return Ok(());
}
let mut snapshot = self.user_caps_snapshot();
for &(origin_hash, rate_opt) in updates {
let replacement = match rate_opt {
Some(rate) if rate.is_finite() => {
let hex = Self::chain_hex(origin_hash);
let clamped = rate.clamp(0.0, 1.0);
Tag::parse(&format!("heat:{hex}={clamped:.2}")).ok()
}
Some(_) => {
tracing::trace!(
origin_hash = origin_hash,
"heat: non-finite rate skipped in batch"
);
continue;
}
None => None,
};
Self::replace_heat_tags(&mut snapshot, origin_hash, replacement);
}
self.announce_capabilities(snapshot).await
}
}
#[cfg(feature = "dataforts")]
#[async_trait::async_trait]
impl super::dataforts::HeatSink for MeshNode {
async fn announce_heat(&self, origin_hash: u64, rate: f64) -> Result<(), AdapterError> {
MeshNode::announce_heat(self, origin_hash, rate).await
}
async fn withdraw_heat(&self, origin_hash: u64) -> Result<(), AdapterError> {
MeshNode::withdraw_heat(self, origin_hash).await
}
async fn announce_heat_batch(
&self,
updates: &[(u64, Option<f64>)],
) -> Result<(), AdapterError> {
MeshNode::announce_heat_batch(self, updates).await
}
}
#[cfg(feature = "dataforts")]
impl MeshNode {
pub async fn announce_blob_heat(&self, hash: [u8; 32], rate: f64) -> Result<(), AdapterError> {
let clamped = if rate.is_finite() {
rate.clamp(0.0, 1.0)
} else {
return Err(AdapterError::Fatal(
"blob heat rate must be finite".to_string(),
));
};
let hex = Self::blob_hex(&hash);
let replacement = Tag::parse(&format!("heat:blob:{hex}={clamped:.2}")).ok();
let mut snapshot = self.user_caps_snapshot();
Self::replace_blob_heat_tags(&mut snapshot, &hash, replacement);
self.announce_capabilities(snapshot).await
}
pub async fn withdraw_blob_heat(&self, hash: [u8; 32]) -> Result<(), AdapterError> {
let mut snapshot = self.user_caps_snapshot();
Self::replace_blob_heat_tags(&mut snapshot, &hash, None);
self.announce_capabilities(snapshot).await
}
pub async fn announce_blob_heat_batch(
&self,
updates: &[([u8; 32], Option<f64>)],
) -> Result<(), AdapterError> {
if updates.is_empty() {
return Ok(());
}
let mut snapshot = self.user_caps_snapshot();
for (hash, rate_opt) in updates {
let replacement = match rate_opt {
Some(rate) if rate.is_finite() => {
let clamped = rate.clamp(0.0, 1.0);
let hex = Self::blob_hex(hash);
Tag::parse(&format!("heat:blob:{hex}={clamped:.2}")).ok()
}
Some(_) => {
tracing::trace!(
hash = ?hash,
"blob heat: non-finite rate skipped in batch"
);
continue;
}
None => None,
};
Self::replace_blob_heat_tags(&mut snapshot, hash, replacement);
}
self.announce_capabilities(snapshot).await
}
}
#[cfg(feature = "dataforts")]
#[async_trait::async_trait]
impl super::dataforts::BlobHeatSink for MeshNode {
async fn announce_blob_heat(&self, hash: [u8; 32], rate: f64) -> Result<(), AdapterError> {
MeshNode::announce_blob_heat(self, hash, rate).await
}
async fn withdraw_blob_heat(&self, hash: [u8; 32]) -> Result<(), AdapterError> {
MeshNode::withdraw_blob_heat(self, hash).await
}
async fn announce_blob_heat_batch(
&self,
updates: &[([u8; 32], Option<f64>)],
) -> Result<(), AdapterError> {
MeshNode::announce_blob_heat_batch(self, updates).await
}
}
#[cfg(all(feature = "dataforts", feature = "cortex"))]
impl MeshNode {
pub async fn send_overflow_push(
self: &Arc<Self>,
target_node_id: u64,
blob_hash: [u8; 32],
size_bytes: u64,
) -> Result<super::dataforts::blob::overflow::OverflowPushAck, super::dataforts::blob::BlobError>
{
use super::dataforts::blob::overflow::{
OverflowPush, OverflowPushAck, OVERFLOW_PUSH_SERVICE,
};
use super::dataforts::blob::BlobError;
let request = OverflowPush {
blob_hash,
size_bytes,
sender_node_id: self.node_id(),
};
let body = postcard::to_allocvec(&request)
.map_err(|e| BlobError::Backend(format!("overflow push: encode failed: {e}")))?;
let reply = self
.call(
target_node_id,
OVERFLOW_PUSH_SERVICE,
bytes::Bytes::from(body),
super::mesh_rpc::CallOptions::default(),
)
.await
.map_err(|e| BlobError::Backend(format!("overflow push: RPC failed: {e}")))?;
let ack: OverflowPushAck = postcard::from_bytes(&reply.body)
.map_err(|e| BlobError::Backend(format!("overflow push: decode ack failed: {e}")))?;
Ok(ack)
}
pub fn serve_overflow_push(
self: &Arc<Self>,
adapter: Arc<super::dataforts::blob::MeshBlobAdapter>,
) -> Result<super::mesh_rpc::ServeHandle, super::mesh_rpc::ServeError> {
use super::dataforts::blob::overflow::{OverflowPushHandler, OVERFLOW_PUSH_SERVICE};
let handler = Arc::new(OverflowPushHandler::new(Arc::clone(self), adapter));
self.serve_rpc(OVERFLOW_PUSH_SERVICE, handler)
}
pub async fn announce_blob_overflow_state(
&self,
adapter: &super::dataforts::blob::MeshBlobAdapter,
) -> Result<(), super::AdapterError> {
use super::behavior::{BlobCapability, Tag, TaxonomyAxis};
let mut caps = self.user_caps_snapshot();
let enabled = adapter.overflow_enabled();
let present = BlobCapability::from_capability_set(&caps).overflow_enabled;
if enabled == present {
return self.announce_capabilities(caps).await;
}
let target = Tag::AxisPresent {
axis: TaxonomyAxis::Dataforts,
key: "blob.overflow".to_string(),
};
if enabled {
caps.tags.insert(target);
} else {
caps.tags.remove(&target);
}
self.announce_capabilities(caps).await
}
}
#[cfg(feature = "dataforts")]
impl MeshNode {
pub fn serve_blob_transfer(
self: &Arc<Self>,
adapter: Arc<super::dataforts::blob::MeshBlobAdapter>,
) -> Arc<super::dataforts::blob::transfer::BlobTransferEngine> {
let engine = Arc::new(super::dataforts::blob::transfer::BlobTransferEngine::new(
self, adapter,
));
*self.blob_transfer_engine.write() = Some(engine.clone());
engine
}
pub async fn transfer_fetch_chunk(
self: &Arc<Self>,
holder: u64,
hash: [u8; 32],
) -> Result<bytes::Bytes, super::dataforts::blob::BlobError> {
use super::dataforts::blob::transfer::{next_transfer_stream_id, TransferControl};
use super::dataforts::blob::BlobError;
let engine = self.blob_transfer_engine.read().clone().ok_or_else(|| {
BlobError::Backend("blob transfer: engine not installed (serve_blob_transfer?)".into())
})?;
let stream_id = next_transfer_stream_id();
let (tx, rx) = tokio::sync::oneshot::channel();
engine.register_pending(stream_id, holder, hash, tx);
let _ = self.open_stream(
holder,
stream_id,
StreamConfig::new().with_reliability(super::Reliability::Reliable),
);
if let Err(e) = self
.send_transfer_control(holder, stream_id, &TransferControl::Request { hash })
.await
{
engine.cancel_pending(stream_id);
return Err(BlobError::Backend(format!(
"blob transfer: send request failed: {e}"
)));
}
match tokio::time::timeout(std::time::Duration::from_secs(30), rx).await {
Ok(Ok(result)) => result,
Ok(Err(_canceled)) => {
engine.cancel_pending(stream_id);
Err(BlobError::Backend(
"blob transfer: engine dropped the reply".into(),
))
}
Err(_elapsed) => {
engine.cancel_pending(stream_id);
Err(BlobError::NotFound(format!(
"blob transfer: timed out fetching mesh://{}",
Self::blob_hex(&hash)
)))
}
}
}
pub async fn transfer_fetch_chunk_discovered(
self: &Arc<Self>,
hash: [u8; 32],
) -> Result<bytes::Bytes, super::dataforts::blob::BlobError> {
use super::dataforts::blob::BlobError;
const DISCOVERY_PROBE_TIMEOUT: Duration = Duration::from_secs(10);
let candidates: Vec<u64> = self.peers.iter().map(|e| *e.key()).collect();
if candidates.is_empty() {
return Err(BlobError::NotFound(format!(
"blob transfer: no connected peers to discover mesh://{}",
Self::blob_hex(&hash)
)));
}
for peer in candidates {
match tokio::time::timeout(
DISCOVERY_PROBE_TIMEOUT,
self.transfer_fetch_chunk(peer, hash),
)
.await
{
Ok(Ok(bytes)) => return Ok(bytes),
Ok(Err(_)) | Err(_) => continue,
}
}
Err(BlobError::NotFound(format!(
"blob transfer: no connected peer served mesh://{}",
Self::blob_hex(&hash)
)))
}
async fn send_transfer_control(
&self,
peer: u64,
stream_id: u64,
control: &super::dataforts::blob::transfer::TransferControl,
) -> Result<(), super::AdapterError> {
let (dest_addr, session) = match self.peers.get(&peer) {
Some(p) => (p.value().addr, p.value().session.clone()),
None => {
return Err(super::AdapterError::Connection(format!(
"transfer control: no session for {peer:#x}"
)))
}
};
let bytes = bytes::Bytes::from(postcard::to_allocvec(control).map_err(|e| {
super::AdapterError::Connection(format!("transfer control: encode failed: {e}"))
})?);
let pool = session.thread_local_pool();
let mut builder = pool.get();
let seq = session.get_or_create_stream(stream_id).next_tx_seq();
let events = [bytes];
let packet = builder.build_subprotocol(
stream_id,
seq,
&events,
PacketFlags::RELIABLE,
super::dataforts::blob::SUBPROTOCOL_BLOB_TRANSFER,
);
self.socket.send_to(&packet, dest_addr).await.map_err(|e| {
super::AdapterError::Connection(format!("transfer control: send failed: {e}"))
})?;
Ok(())
}
}
#[cfg(feature = "net")]
impl MeshNode {
#[cfg(feature = "redex")]
pub fn set_replication_inbound_router(
&self,
router: Option<Arc<dyn super::redex::ReplicationInboundRouter>>,
) {
*self.replication_inbound_router.write() = router;
}
#[cfg(feature = "meshdb")]
pub fn set_meshdb_inbound_router(
&self,
router: Option<Arc<dyn super::behavior::meshdb::MeshDbInboundRouter>>,
) {
*self.meshdb_inbound_router.write() = router;
}
#[cfg(feature = "meshdb")]
pub fn has_meshdb_inbound_router(&self) -> bool {
self.meshdb_inbound_router.read().is_some()
}
pub fn set_fold_router(
&self,
router: Option<Arc<dyn super::behavior::fold::FoldChannelRouter>>,
) {
*self.fold_router.write() = router;
}
pub fn has_fold_router(&self) -> bool {
self.fold_router.read().is_some()
}
pub fn fold_stats(&self) -> Vec<super::behavior::fold::FoldStats> {
let guard = self.fold_router.read();
let Some(router) = guard.as_ref() else {
return Vec::new();
};
router.stats()
}
#[cfg(feature = "dataforts")]
pub fn set_greedy_observer(&self, observer: Option<Arc<dyn super::dataforts::GreedyObserver>>) {
*self.greedy_observer.write() = observer;
}
#[cfg(feature = "dataforts")]
pub fn has_greedy_observer(&self) -> bool {
self.greedy_observer.read().is_some()
}
pub fn find_chain_holders(&self, origin_hash: u64) -> Vec<u64> {
let hex = Self::chain_hex(origin_hash);
let mut holders: Vec<u64> = self.capability_fold.with_state(|state| {
let mut seen: std::collections::HashSet<u64> = std::collections::HashSet::new();
for entry in state.entries.values() {
if seen.contains(&entry.node_id) {
continue;
}
if entry
.payload
.tags
.iter()
.any(|t| Self::is_causal_for_str(t, &hex))
{
seen.insert(entry.node_id);
}
}
seen.into_iter().collect::<Vec<u64>>()
});
let self_id = self.node_id;
let proximity = self.proximity_graph.clone();
let rtt_of = |node: u64| -> Option<std::time::Duration> {
if node == self_id {
Some(std::time::Duration::ZERO)
} else {
let graph_id = node_id_to_graph_id(node);
proximity.nearest_rtt(|n| n.node_id == graph_id)
}
};
holders.sort_by(|&a, &b| {
let rtt_a = rtt_of(a);
let rtt_b = rtt_of(b);
match (rtt_a, rtt_b) {
(Some(da), Some(db)) => match da.cmp(&db) {
std::cmp::Ordering::Equal => a.cmp(&b),
other => other,
},
(Some(_), None) => std::cmp::Ordering::Less,
(None, Some(_)) => std::cmp::Ordering::Greater,
(None, None) => a.cmp(&b),
}
});
holders
}
pub fn find_nodes_by_filter(&self, filter: &CapabilityFilter) -> Vec<u64> {
super::behavior::fold::capability_bridge::find_nodes_matching(&self.capability_fold, filter)
}
pub fn find_nodes_by_filter_scoped(
&self,
filter: &CapabilityFilter,
scope: &ScopeFilter<'_>,
) -> Vec<u64> {
let my_subnet = self.local_subnet;
let peer_subnets = self.peer_subnets.clone();
let local_node_id = self.node_id;
let policy_installed = self.local_subnet_policy.is_some();
super::behavior::fold::capability_bridge::find_nodes_matching_scoped(
&self.capability_fold,
filter,
scope,
|nid| {
if nid == local_node_id {
return true;
}
match peer_subnets.get(&nid).map(|e| *e.value()) {
Some(s) => s == my_subnet,
None => policy_installed,
}
},
)
}
#[cfg(feature = "nat-traversal")]
pub fn peer_reflex_addr(&self, peer_node_id: u64) -> Option<std::net::SocketAddr> {
super::behavior::fold::reflex_addr_for(&self.capability_fold, peer_node_id)
}
#[cfg(feature = "nat-traversal")]
pub fn peer_nat_class(&self, peer_node_id: u64) -> super::traversal::classify::NatClass {
use super::traversal::classify::NatClass;
self.capability_fold.with_state(|state| {
let Some(keys) = state.by_node.get(&peer_node_id) else {
return NatClass::Unknown;
};
for key in keys {
let Some(entry) = state.entries.get(key) else {
continue;
};
for tag in entry.payload.tags.iter() {
if let Some(class) = NatClass::from_tag(tag) {
return class;
}
}
}
NatClass::Unknown
})
}
#[cfg(feature = "nat-traversal")]
pub fn traversal_stats(&self) -> super::traversal::TraversalStatsSnapshot {
self.traversal_stats.snapshot()
}
#[cfg(feature = "nat-traversal")]
fn is_relayed_peer(&self, peer_id: u64, addr: &SocketAddr) -> bool {
self.addr_to_node
.get(addr)
.map(|owner| *owner != peer_id)
.unwrap_or(true)
}
#[cfg(feature = "nat-traversal")]
pub fn select_punch_coordinator(&self, target: u64) -> Option<u64> {
if let Some(next_hop) = self.router.routing_table().lookup(target) {
if let Some(nid) = self.addr_to_node.get(&next_hop).map(|e| *e.value()) {
if nid != target && nid != self.node_id && self.peers.contains_key(&nid) {
return Some(nid);
}
}
}
let mut any: Vec<u64> = Vec::new();
for entry in self.peers.iter() {
let nid = *entry.key();
if nid == target || nid == self.node_id {
continue;
}
if self.is_relayed_peer(nid, &entry.value().addr) {
continue;
}
any.push(nid);
}
let relay_capable_set = super::behavior::fold::nodes_with_capability_tag(
&self.capability_fold,
&any,
super::behavior::capability::RELAY_CAPABLE_TAG,
);
if !relay_capable_set.is_empty() {
let relay_capable: Vec<u64> = any
.iter()
.copied()
.filter(|nid| relay_capable_set.contains(nid))
.collect();
Self::spread_pick(&relay_capable)
} else {
Self::spread_pick(&any)
}
}
#[cfg(feature = "nat-traversal")]
fn spread_pick(pool: &[u64]) -> Option<u64> {
use std::hash::BuildHasher;
if pool.is_empty() {
return None;
}
let salt = std::collections::hash_map::RandomState::new().hash_one(pool.len() as u64);
pool.get((salt % pool.len() as u64) as usize).copied()
}
#[cfg(feature = "nat-traversal")]
pub async fn connect_direct_auto(
&self,
peer_node_id: u64,
peer_pubkey: &[u8; 32],
) -> Result<u64, super::traversal::TraversalError> {
use super::traversal::classify::{pair_action, PairAction};
use super::traversal::TraversalError;
let action = pair_action(self.nat_class(), self.peer_nat_class(peer_node_id));
match action {
PairAction::Direct => self.connect_direct(peer_node_id, peer_pubkey, 0).await,
PairAction::SinglePunch | PairAction::SkipPunch => {
match self.select_punch_coordinator(peer_node_id) {
Some(coord) => self.connect_direct(peer_node_id, peer_pubkey, coord).await,
None => {
self.traversal_stats.record_rendezvous_no_relay();
Err(TraversalError::RendezvousNoRelay)
}
}
}
}
}
#[cfg(feature = "nat-traversal")]
pub async fn connect_direct(
&self,
peer_node_id: u64,
peer_pubkey: &[u8; 32],
coordinator: u64,
) -> Result<u64, super::traversal::TraversalError> {
use super::traversal::classify::{pair_action, PairAction};
use super::traversal::TraversalError;
let local_class = self.nat_class();
let remote_class = self.peer_nat_class(peer_node_id);
let action = pair_action(local_class, remote_class);
let coordinator_addr = || {
self.peer_addrs
.get(&coordinator)
.map(|e| *e.value())
.ok_or(TraversalError::PeerNotReachable)
};
let session_matches = |want_addr: std::net::SocketAddr| {
self.peers
.get(&peer_node_id)
.map(|e| e.value().addr == want_addr)
.unwrap_or(false)
};
let connect_on_direct_path = |target_addr: std::net::SocketAddr| async move {
let id = if session_matches(target_addr) {
peer_node_id
} else {
self.connect_via(target_addr, peer_pubkey, peer_node_id)
.await
.map_err(|e| TraversalError::Transport(e.to_string()))?
};
self.addr_to_node.insert(target_addr, peer_node_id);
Ok::<u64, TraversalError>(id)
};
let connect_via_coordinator = |coord_addr: std::net::SocketAddr| async move {
if session_matches(coord_addr) {
return Ok(peer_node_id);
}
self.connect_via(coord_addr, peer_pubkey, peer_node_id)
.await
.map_err(|e| TraversalError::Transport(e.to_string()))
};
match action {
PairAction::Direct => {
let peer_reflex = self
.peer_reflex_addr(peer_node_id)
.ok_or(TraversalError::PeerNotReachable)?;
match connect_on_direct_path(peer_reflex).await {
Ok(id) => Ok(id),
Err(_) => {
let id = self
.connect_routed(peer_pubkey, peer_node_id)
.await
.map_err(|e| TraversalError::Transport(e.to_string()))?;
self.traversal_stats.record_relay_fallback();
Ok(id)
}
}
}
PairAction::SkipPunch => {
let coord = coordinator_addr()?;
let id = connect_via_coordinator(coord).await?;
self.traversal_stats.record_relay_fallback();
Ok(id)
}
PairAction::SinglePunch => {
let coord = coordinator_addr()?;
let self_reflex = self.reflex_addr().unwrap_or_else(|| self.local_addr());
let (ack_tx, ack_rx) = oneshot::channel();
let ack_gen = self
.next_waiter_gen
.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
self.pending_punch_acks
.insert(peer_node_id, (ack_gen, coordinator, ack_tx));
let punch_outcome = self
.request_punch(coordinator, peer_node_id, self_reflex)
.await;
let intro = match punch_outcome {
Ok(intro) => {
self.traversal_stats.record_punch_attempt();
intro
}
Err(_) => {
self.pending_punch_acks
.remove_if(&peer_node_id, |_, (g, _, _)| *g == ack_gen);
let id = connect_via_coordinator(coord).await?;
self.traversal_stats.record_relay_fallback();
return Ok(id);
}
};
let deadline = self.traversal_config.punch_deadline;
match tokio::time::timeout(deadline, ack_rx).await {
Ok(Ok(_ack)) => {
match connect_on_direct_path(intro.peer_reflex).await {
Ok(id) => {
self.traversal_stats.record_punch_success();
Ok(id)
}
Err(_) => {
let id = connect_via_coordinator(coord).await?;
self.traversal_stats.record_relay_fallback();
Ok(id)
}
}
}
Ok(Err(_)) => {
let id = connect_via_coordinator(coord).await?;
self.traversal_stats.record_relay_fallback();
Ok(id)
}
Err(_) => {
self.pending_punch_acks
.remove_if(&peer_node_id, |_, (g, _, _)| *g == ack_gen);
self.traversal_stats.record_punch_timeout();
let id = connect_via_coordinator(coord).await?;
self.traversal_stats.record_relay_fallback();
Ok(id)
}
}
}
}
}
pub fn find_best_node(
&self,
req: &super::behavior::capability::CapabilityRequirement,
) -> Option<u64> {
let candidates = super::behavior::fold::capability_bridge::find_nodes_matching(
&self.capability_fold,
&req.filter,
);
Self::best_by_score(&self.capability_fold, candidates, req)
}
pub fn find_best_node_scoped(
&self,
req: &super::behavior::capability::CapabilityRequirement,
scope: &ScopeFilter<'_>,
) -> Option<u64> {
let candidates = self.find_nodes_by_filter_scoped(&req.filter, scope);
Self::best_by_score(&self.capability_fold, candidates, req)
}
fn best_by_score(
fold: &Arc<super::behavior::fold::Fold<super::behavior::fold::CapabilityFold>>,
candidates: Vec<u64>,
req: &super::behavior::capability::CapabilityRequirement,
) -> Option<u64> {
let mut scored: Vec<(u64, f32)> = candidates
.into_iter()
.map(|node_id| {
let caps = super::behavior::fold::capability_bridge::synthesize_capability_set(
fold, node_id,
);
(node_id, req.score(&caps))
})
.collect();
scored.sort_by(|(na, sa), (nb, sb)| {
sb.partial_cmp(sa)
.unwrap_or(std::cmp::Ordering::Equal)
.then_with(|| na.cmp(nb))
});
scored.into_iter().next().map(|(node_id, _)| node_id)
}
pub fn capability_fold(
&self,
) -> &Arc<super::behavior::fold::Fold<super::behavior::fold::CapabilityFold>> {
&self.capability_fold
}
pub fn reservation_fold(
&self,
) -> &Arc<super::behavior::fold::Fold<super::behavior::fold::ReservationFold>> {
&self.reservation_fold
}
pub fn island_fold(
&self,
) -> &Arc<super::behavior::fold::Fold<super::behavior::fold::IslandTopologyFold>> {
&self.island_fold
}
pub fn match_islands(
&self,
criteria: &super::behavior::gang::MatchCriteria,
) -> Vec<super::behavior::fold::IslandId> {
let down = self.liveness_down.load();
super::behavior::gang::match_islands(
&self.capability_fold,
&self.island_fold,
criteria,
&down,
)
}
pub fn set_liveness_down(
&self,
down: std::collections::HashSet<super::behavior::fold::NodeId>,
) {
self.liveness_down.store(Arc::new(down));
}
pub fn match_islands_sensed(
&self,
criteria: &super::behavior::gang::MatchCriteria,
spec: &sensing::InterestSpec,
budget: &sensing::ConsumerLatencyBudget,
resolved_population: Option<&[u64]>,
) -> Vec<super::behavior::fold::IslandId> {
let sensed = self.sensed_candidates(spec, budget, resolved_population);
let down = self.liveness_down.load();
let non_viable: std::collections::HashSet<super::behavior::fold::NodeId> =
sensed.non_viable.iter().copied().collect();
super::behavior::gang::match_islands_sensed(
&self.capability_fold,
&self.island_fold,
criteria,
&down,
&non_viable,
&sensed.viable,
)
}
pub async fn claim_island_sensed(
&self,
criteria: &super::behavior::gang::MatchCriteria,
spec: &sensing::InterestSpec,
budget: &sensing::ConsumerLatencyBudget,
resolved_population: Option<&[u64]>,
until_unix_us: u64,
) -> Result<Option<super::behavior::fold::IslandId>, AdapterError> {
for island in self.match_islands_sensed(criteria, spec, budget, resolved_population) {
if matches!(
self.reserve_island(island, until_unix_us).await?,
super::behavior::gang::ClaimOutcome::Won
) {
return Ok(Some(island));
}
}
Ok(None)
}
pub async fn reserve_island(
&self,
island: super::behavior::fold::IslandId,
until_unix_us: u64,
) -> Result<super::behavior::gang::ClaimOutcome, AdapterError> {
self.apply_and_broadcast_reservation(
island,
super::behavior::fold::ReservationState::Reserved {
holder: self.node_id,
until_unix_us,
},
)
.await
}
pub async fn release_island(
&self,
island: super::behavior::fold::IslandId,
) -> Result<super::behavior::gang::ClaimOutcome, AdapterError> {
use super::behavior::fold::ReservationQuery;
let held_by_us = self
.reservation_fold
.query(ReservationQuery::State(island))
.first()
.and_then(|(_, state)| state.holder())
== Some(self.node_id);
if !held_by_us {
return Ok(super::behavior::gang::ClaimOutcome::Lost);
}
self.apply_and_broadcast_reservation(island, super::behavior::fold::ReservationState::Free)
.await
}
pub async fn claim_island(
&self,
criteria: &super::behavior::gang::MatchCriteria,
until_unix_us: u64,
) -> Result<Option<super::behavior::fold::IslandId>, AdapterError> {
for island in self.match_islands(criteria) {
if matches!(
self.reserve_island(island, until_unix_us).await?,
super::behavior::gang::ClaimOutcome::Won
) {
return Ok(Some(island));
}
}
Ok(None)
}
async fn apply_and_broadcast_reservation(
&self,
island: super::behavior::fold::IslandId,
state: super::behavior::fold::ReservationState,
) -> Result<super::behavior::gang::ClaimOutcome, AdapterError> {
use super::behavior::fold::{ApplyOutcome, FoldKind, ReservationFold};
let gen = self.next_fold_generation(ReservationFold::KIND_ID, island);
let meta = super::behavior::fold::EnvelopeMeta {
announced_at: super::current_timestamp_micros(),
..Default::default()
};
let ann = super::behavior::fold::SignedAnnouncement::sign(
&self.identity,
ReservationFold::KIND_ID,
0,
self.node_id,
gen,
meta,
super::behavior::fold::ReservationAnnouncement {
resource_id: island,
state,
},
)
.map_err(|e| AdapterError::Connection(format!("reservation: sign failed: {e}")))?;
let outcome = self
.reservation_fold
.apply(ann.clone())
.map_err(|e| AdapterError::Connection(format!("reservation: apply failed: {e}")))?;
if let Err(e) = self.publish_fold_broadcast(&ann).await {
tracing::warn!(
island,
error = %e,
"reservation broadcast failed; local CAS applied but peers not notified",
);
}
Ok(match outcome {
ApplyOutcome::Inserted | ApplyOutcome::Replaced => {
super::behavior::gang::ClaimOutcome::Won
}
ApplyOutcome::Rejected => super::behavior::gang::ClaimOutcome::Lost,
})
}
#[doc(hidden)]
pub fn test_inject_capability_announcement(
&self,
ann: super::behavior::capability::CapabilityAnnouncement,
) {
let fold_ann = super::behavior::fold::capability_bridge::translate_announcement(&ann);
let _ = self.capability_fold.apply(fold_ann);
}
#[doc(hidden)]
pub fn test_capability_fold_has(&self, node_id: u64) -> bool {
self.capability_fold
.with_state(|state| state.by_node.contains_key(&node_id))
}
#[doc(hidden)]
pub fn test_capability_fold_get(
&self,
node_id: u64,
) -> super::behavior::capability::CapabilitySet {
super::behavior::fold::capability_bridge::synthesize_capability_set(
&self.capability_fold,
node_id,
)
}
pub fn get_node_by_origin_hash(&self, origin_hash: u64) -> Option<u64> {
self.origin_hash_to_node.get(&origin_hash).map(|v| *v)
}
async fn push_local_announcement(&self, peer_addr: SocketAddr) {
let Some(ann) = self.local_announcement.load_full() else {
return;
};
let bytes = ann.to_bytes();
if let Err(e) = self
.send_subprotocol(peer_addr, SUBPROTOCOL_CAPABILITY_ANN, &bytes)
.await
{
tracing::trace!(
peer = %peer_addr,
error = %e,
"capability: session-open push failed"
);
}
}
pub fn open_stream(
&self,
peer_node_id: u64,
stream_id: u64,
config: StreamConfig,
) -> Result<Stream, AdapterError> {
let peer = self.peers.get(&peer_node_id).ok_or_else(|| {
AdapterError::Connection(format!(
"open_stream: no session for peer {:#x}",
peer_node_id
))
})?;
let reliable = config.reliability.is_reliable();
let epoch = peer.session.open_stream_full(
stream_id,
reliable,
config.fairness_weight,
config.window_bytes,
);
self.router
.scheduler()
.set_stream_weight(stream_id, config.fairness_weight);
if peer.session.stream_count() > self.config.max_streams {
peer.session.evict_idle_streams(
Duration::from_nanos(u64::MAX),
self.config.max_streams,
"cap_exceeded",
);
}
Ok(Stream {
peer_node_id,
stream_id,
epoch,
config,
})
}
pub fn close_stream(&self, peer_node_id: u64, stream_id: u64) {
if let Some(peer) = self.peers.get(&peer_node_id) {
peer.session.close_stream(stream_id);
}
}
pub async fn close_stream_graceful(
&self,
peer_node_id: u64,
stream_id: u64,
timeout: Duration,
) {
let deadline = std::time::Instant::now() + timeout;
loop {
let drained = match self.peers.get(&peer_node_id) {
Some(p) => p
.session
.try_stream(stream_id)
.map(|s| !s.with_reliability(|r| r.has_pending()))
.unwrap_or(true), None => true, };
if drained || std::time::Instant::now() >= deadline {
break;
}
tokio::time::sleep(Duration::from_millis(2)).await;
}
self.close_stream(peer_node_id, stream_id);
}
pub async fn send_on_stream(
&self,
stream: &Stream,
events: &[Bytes],
) -> Result<(), StreamError> {
let peer = self
.peers
.get(&stream.peer_node_id)
.ok_or(StreamError::NotConnected)?;
let peer_addr = peer.addr;
let session = peer.session.clone();
drop(peer);
if self.partition_filter.contains(&peer_addr) {
return Ok(()); }
let stream_id = stream.stream_id;
let reliable = stream.config.reliability.is_reliable();
let scheduled = stream.config.scheduled;
match session.try_stream(stream_id) {
None => return Err(StreamError::NotConnected),
Some(state) if state.epoch() != stream.epoch => {
return Err(StreamError::NotConnected);
}
Some(state) if reliable && !state.with_reliability(|r| r.can_send()) => {
return Err(StreamError::Backpressure);
}
Some(_) => {}
}
let pool = session.thread_local_pool();
let mut builder = pool.get();
let mut current_batch: Vec<Bytes> = Vec::with_capacity(64);
let mut current_size = 0usize;
let flags = if reliable {
PacketFlags::RELIABLE
} else {
PacketFlags::NONE
};
let mut committed_any = false;
for event in events {
let frame_size = EventFrame::LEN_SIZE + event.len();
if current_size + frame_size > protocol::MAX_PAYLOAD_SIZE && !current_batch.is_empty() {
self.flush_stream_batch(
&session,
&mut builder,
stream,
stream_id,
peer_addr,
scheduled,
flags,
¤t_batch,
current_size,
&mut committed_any,
)
.await?;
current_batch.clear();
current_size = 0;
}
current_batch.push(event.clone());
current_size += frame_size;
}
if !current_batch.is_empty() {
self.flush_stream_batch(
&session,
&mut builder,
stream,
stream_id,
peer_addr,
scheduled,
flags,
¤t_batch,
current_size,
&mut committed_any,
)
.await?;
}
drop(builder);
session.touch();
Ok(())
}
#[allow(clippy::too_many_arguments)]
async fn flush_stream_batch(
&self,
session: &Arc<NetSession>,
builder: &mut super::pool::ThreadLocalPooledBuilder<'_>,
stream: &Stream,
stream_id: u64,
peer_addr: SocketAddr,
scheduled: bool,
flags: PacketFlags,
batch: &[Bytes],
batch_size: usize,
committed_any: &mut bool,
) -> Result<(), StreamError> {
let needed = wire_bytes_for_payload(batch_size);
let mut delay = Duration::from_millis(5);
let cap = Duration::from_millis(200);
let stall_deadline = tokio::time::Instant::now() + COMMITTED_FLUSH_STALL_BUDGET;
loop {
let (guard, seq) =
match session.try_acquire_tx_credit_matching_epoch(stream_id, stream.epoch, needed)
{
TxAdmit::Acquired { guard, seq } => (guard, seq),
TxAdmit::WindowFull => {
if *committed_any {
await_credit_or_stall(&mut delay, cap, stall_deadline).await?;
continue;
}
return Err(StreamError::Backpressure);
}
TxAdmit::StreamClosed => return Err(StreamError::NotConnected),
};
let packet = builder.build(stream_id, seq, batch, flags);
match self
.deliver_stream_packet(scheduled, &packet, peer_addr, stream_id)
.await
{
Ok(()) => {
guard.commit(); Self::register_retransmit(session, stream_id, stream.epoch, seq, batch, flags);
*committed_any = true;
return Ok(());
}
Err(StreamError::Backpressure) => {
drop(guard);
session.try_rollback_tx_seq(stream_id, stream.epoch, seq);
if *committed_any {
await_credit_or_stall(&mut delay, cap, stall_deadline).await?;
continue;
}
return Err(StreamError::Backpressure);
}
Err(e) => {
drop(guard);
session.try_rollback_tx_seq(stream_id, stream.epoch, seq);
return Err(e);
}
}
}
}
fn register_retransmit(
session: &Arc<NetSession>,
stream_id: u64,
epoch: u64,
seq: u64,
events: &[Bytes],
flags: PacketFlags,
) {
if !flags.contains(PacketFlags::RELIABLE) {
return;
}
let descriptor = Arc::new(super::RetransmitDescriptor {
seq,
stream_id,
events: events.to_vec(),
flags,
});
if let Some(state) = session.try_stream(stream_id) {
if state.epoch() == epoch {
state.with_reliability(|r| r.on_send(descriptor));
}
}
}
async fn deliver_stream_packet(
&self,
scheduled: bool,
packet: &[u8],
peer_addr: SocketAddr,
stream_id: u64,
) -> Result<(), StreamError> {
if scheduled {
let queued = super::router::QueuedPacket {
data: Bytes::copy_from_slice(packet),
dest: peer_addr,
stream_id,
priority: false,
queued_at: std::time::Instant::now(),
};
if self.router.scheduler().enqueue(queued) {
Ok(())
} else {
Err(StreamError::Backpressure)
}
} else {
self.socket
.send_to(packet, peer_addr)
.await
.map(|_| ())
.map_err(|e| StreamError::Transport(format!("send failed: {}", e)))
}
}
pub async fn send_with_retry(
&self,
stream: &Stream,
events: &[Bytes],
max_retries: usize,
) -> Result<(), StreamError> {
let mut delay = Duration::from_millis(5);
let cap = Duration::from_millis(200);
let mut last_backpressure: Option<StreamError> = None;
for _ in 0..max_retries.saturating_add(1) {
match self.send_on_stream(stream, events).await {
Ok(()) => return Ok(()),
Err(StreamError::Backpressure) => {
last_backpressure = Some(StreamError::Backpressure);
tokio::time::sleep(delay).await;
delay = (delay * 2).min(cap);
}
Err(e) => return Err(e),
}
}
Err(last_backpressure.unwrap_or(StreamError::Backpressure))
}
pub async fn send_blocking(
&self,
stream: &Stream,
events: &[Bytes],
) -> Result<(), StreamError> {
self.send_with_retry(stream, events, 4096).await
}
pub fn stream_stats(&self, peer_node_id: u64, stream_id: u64) -> Option<StreamStats> {
let peer = self.peers.get(&peer_node_id)?;
let state = peer.session.get_stream(stream_id)?;
Some(StreamStats {
tx_seq: state.current_tx_seq(),
rx_seq: state.current_rx_seq(),
inbound_pending: state.inbound_len() as u64,
last_activity_ns: state.last_activity_ns(),
active: state.is_active(),
backpressure_events: state.backpressure_events(),
tx_credit_remaining: state.tx_credit_remaining(),
tx_window: state.tx_window(),
credit_grants_received: state.credit_grants_received(),
credit_grants_sent: state.credit_grants_sent(),
})
}
pub fn all_stream_stats(&self, peer_node_id: u64) -> Vec<(u64, StreamStats)> {
let peer = match self.peers.get(&peer_node_id) {
Some(p) => p,
None => return Vec::new(),
};
let session = peer.session.clone();
drop(peer);
session
.stream_ids()
.into_iter()
.filter_map(|sid| {
let state = session.get_stream(sid)?;
Some((
sid,
StreamStats {
tx_seq: state.current_tx_seq(),
rx_seq: state.current_rx_seq(),
inbound_pending: state.inbound_len() as u64,
last_activity_ns: state.last_activity_ns(),
active: state.is_active(),
backpressure_events: state.backpressure_events(),
tx_credit_remaining: state.tx_credit_remaining(),
tx_window: state.tx_window(),
credit_grants_received: state.credit_grants_received(),
credit_grants_sent: state.credit_grants_sent(),
},
))
})
.collect()
}
async fn try_connect_via_once(
&self,
relay_addr: SocketAddr,
dest_pubkey: &[u8; 32],
dest_node_id: u64,
) -> Result<SessionKeys, AdapterError> {
let pending_key = routing_id(dest_node_id);
let prologue = handshake_prologue(routing_id(self.node_id), pending_key);
let mut noise =
NoiseHandshake::initiator_with_prologue(&self.config.psk, dest_pubkey, &prologue)
.map_err(|e| AdapterError::Fatal(format!("handshake init failed: {}", e)))?;
let msg1 = noise
.write_message(&self.node_id.to_le_bytes())
.map_err(|e| AdapterError::Connection(format!("write_message failed: {}", e)))?;
let (tx, rx) = oneshot::channel();
match self.pending_handshakes.entry(pending_key) {
dashmap::mapref::entry::Entry::Occupied(_) => {
return Err(AdapterError::Connection(format!(
"connect_via: handshake already in flight for peer {:#x}",
dest_node_id
)));
}
dashmap::mapref::entry::Entry::Vacant(v) => {
v.insert(PendingHandshake { noise, tx });
}
}
let inner = {
let mut builder = PacketBuilder::new(&[0u8; 32], 0);
builder.build_handshake(&msg1)
};
let routing = RoutingHeader::new(dest_node_id, self.node_id as u32, DEFAULT_HANDSHAKE_TTL);
let mut routed = bytes::BytesMut::with_capacity(ROUTING_HEADER_SIZE + inner.len());
routed.extend_from_slice(&routing.to_bytes());
routed.extend_from_slice(&inner);
if let Err(e) = self.socket.send_to(&routed, relay_addr).await {
self.pending_handshakes.remove(&pending_key);
return Err(AdapterError::Connection(format!("send failed: {}", e)));
}
let keys = match tokio::time::timeout(self.config.handshake_timeout, rx).await {
Ok(Ok(Ok(k))) => k,
Ok(Ok(Err(e))) => {
self.pending_handshakes.remove(&pending_key);
return Err(AdapterError::Fatal(format!("handshake failed: {}", e)));
}
Ok(Err(_)) => {
self.pending_handshakes.remove(&pending_key);
return Err(AdapterError::Connection("handshake channel dropped".into()));
}
Err(_) => {
self.pending_handshakes.remove(&pending_key);
return Err(AdapterError::Connection("handshake timeout".into()));
}
};
Ok(keys)
}
pub async fn connect_via(
&self,
relay_addr: SocketAddr,
dest_pubkey: &[u8; 32],
dest_node_id: u64,
) -> Result<u64, AdapterError> {
let mut attempt = 0;
let keys = loop {
attempt += 1;
match self
.try_connect_via_once(relay_addr, dest_pubkey, dest_node_id)
.await
{
Ok(keys) => break keys,
Err(e) if attempt < self.config.handshake_retries => {
tracing::warn!(
attempt,
error = %e,
"mesh routed handshake failed, retrying"
);
tokio::time::sleep(Duration::from_millis(100 * attempt as u64)).await;
}
Err(e) => return Err(e),
}
};
self.install_peer(
dest_node_id,
relay_addr,
keys,
AddrInstallMode::RoutedPreserve,
);
Ok(dest_node_id)
}
#[cfg(feature = "nat-traversal")]
async fn connect_via_cas(
&self,
target_addr: SocketAddr,
dest_pubkey: &[u8; 32],
dest_node_id: u64,
expected_prior_session_id: u64,
addr_mode: AddrInstallMode,
) -> Result<bool, AdapterError> {
let mut attempt = 0;
let keys = loop {
attempt += 1;
match self
.try_connect_via_once(target_addr, dest_pubkey, dest_node_id)
.await
{
Ok(keys) => break keys,
Err(e) if attempt < self.config.handshake_retries => {
tracing::debug!(attempt, error = %e, "upgrade handshake retry");
tokio::time::sleep(Duration::from_millis(100 * attempt as u64)).await;
}
Err(e) => return Err(e),
}
};
Ok(self.install_peer_cas(
dest_node_id,
target_addr,
keys,
addr_mode,
Some(expected_prior_session_id),
))
}
#[cfg(feature = "nat-traversal")]
fn upgrade_should_attempt(&self, peer_id: u64) -> bool {
match self.upgrade_cache.get(&peer_id) {
None => true,
Some(e) => !e.done && Instant::now() >= e.next_eligible,
}
}
#[cfg(feature = "nat-traversal")]
fn upgrade_lease(&self, peer_id: u64, lease: Duration) {
let mut e = self
.upgrade_cache
.entry(peer_id)
.or_insert(UpgradeCacheEntry {
next_eligible: Instant::now(),
failures: 0,
done: false,
});
e.next_eligible = Instant::now() + lease;
}
#[cfg(feature = "nat-traversal")]
fn upgrade_record_done(&self, peer_id: u64) {
let mut e = self
.upgrade_cache
.entry(peer_id)
.or_insert(UpgradeCacheEntry {
next_eligible: Instant::now(),
failures: 0,
done: false,
});
e.done = true;
}
#[cfg(feature = "nat-traversal")]
fn upgrade_record_defer(&self, peer_id: u64, delay: Duration) {
let mut e = self
.upgrade_cache
.entry(peer_id)
.or_insert(UpgradeCacheEntry {
next_eligible: Instant::now(),
failures: 0,
done: false,
});
e.next_eligible = Instant::now() + delay;
}
#[cfg(feature = "nat-traversal")]
fn upgrade_record_failure(&self, peer_id: u64) {
const BASE: Duration = Duration::from_secs(2);
const MAX_SHIFT: u32 = 5; let mut e = self
.upgrade_cache
.entry(peer_id)
.or_insert(UpgradeCacheEntry {
next_eligible: Instant::now(),
failures: 0,
done: false,
});
e.failures = e.failures.saturating_add(1);
let backoff = BASE.saturating_mul(1u32 << e.failures.min(MAX_SHIFT));
e.next_eligible = Instant::now() + backoff;
}
#[cfg(feature = "nat-traversal")]
async fn attempt_direct_upgrade(&self, peer_id: u64) {
use super::traversal::classify::{pair_action, PairAction};
const SINGLEPUNCH_RECHECK: Duration = Duration::from_secs(30);
let Some((relay_addr, prior_sid, pubkey, busy)) = self.peers.get(&peer_id).map(|e| {
let v = e.value();
(
v.addr,
v.session.session_id(),
v.remote_static_pub,
v.session.has_open_streams() || v.session.has_unacked(),
)
}) else {
return;
};
if !self.is_relayed_peer(peer_id, &relay_addr) {
self.upgrade_record_done(peer_id);
return;
}
if busy {
self.traversal_stats.record_upgrade_deferred_busy();
self.upgrade_record_defer(peer_id, Duration::from_secs(1));
return;
}
let action = pair_action(self.nat_class(), self.peer_nat_class(peer_id));
let target_addr = match action {
PairAction::Direct => match self.peer_reflex_addr(peer_id) {
Some(addr) => addr,
None => {
self.upgrade_record_failure(peer_id);
return;
}
},
PairAction::SkipPunch => {
self.upgrade_record_done(peer_id);
return;
}
PairAction::SinglePunch => {
self.upgrade_record_defer(peer_id, SINGLEPUNCH_RECHECK);
return;
}
};
if target_addr == relay_addr {
self.upgrade_record_done(peer_id);
return;
}
self.traversal_stats.record_upgrade_attempt();
match self
.connect_via_cas(
target_addr,
&pubkey,
peer_id,
prior_sid,
AddrInstallMode::DirectOverwrite,
)
.await
{
Ok(true) => {
self.traversal_stats.record_upgrade_success();
self.upgrade_record_done(peer_id);
}
Ok(false) | Err(_) => {
self.upgrade_record_failure(peer_id);
}
}
}
#[doc(hidden)]
#[cfg(feature = "nat-traversal")]
pub async fn attempt_direct_upgrade_for_test(&self, peer_id: u64) {
self.attempt_direct_upgrade(peer_id).await;
}
#[cfg(feature = "nat-traversal")]
fn upgrade_is_loop_candidate(&self, peer_id: u64) -> bool {
let Some(addr) = self.peers.get(&peer_id).map(|e| e.value().addr) else {
return false;
};
self.upgrade_is_loop_candidate_at(peer_id, addr)
}
#[cfg(feature = "nat-traversal")]
fn upgrade_is_loop_candidate_at(&self, peer_id: u64, addr: SocketAddr) -> bool {
if self.node_id >= peer_id {
return false;
}
if !self.is_relayed_peer(peer_id, &addr) {
return false;
}
self.upgrade_should_attempt(peer_id)
}
#[doc(hidden)]
#[cfg(feature = "nat-traversal")]
pub fn upgrade_is_loop_candidate_for_test(&self, peer_id: u64) -> bool {
self.upgrade_is_loop_candidate(peer_id)
}
#[doc(hidden)]
#[cfg(feature = "nat-traversal")]
pub fn upgrade_cache_contains_for_test(&self, peer_id: u64) -> bool {
self.upgrade_cache.contains_key(&peer_id)
}
#[doc(hidden)]
#[cfg(feature = "nat-traversal")]
pub fn upgrade_entry_is_done_for_test(&self, peer_id: u64) -> Option<bool> {
self.upgrade_cache.get(&peer_id).map(|e| e.done)
}
#[cfg(feature = "nat-traversal")]
pub fn spawn_direct_upgrade_loop(self: &Arc<Self>) -> JoinHandle<()> {
const SCAN_INTERVAL: Duration = Duration::from_secs(1);
const ATTEMPT_LEASE: Duration = Duration::from_secs(10);
let weak = Arc::downgrade(self);
let shutdown = self.shutdown.clone();
let shutdown_notify = self.shutdown_notify.clone();
tokio::spawn(async move {
match weak.upgrade() {
Some(node) if node.config.auto_direct_upgrade => {}
_ => return,
}
let mut tick = tokio::time::interval(SCAN_INTERVAL);
tick.tick().await; while !shutdown.load(Ordering::Acquire) {
tokio::select! {
_ = shutdown_notify.notified() => {
if shutdown.load(Ordering::Acquire) {
return;
}
}
_ = tick.tick() => {}
}
let Some(node) = weak.upgrade() else { break };
let candidates: Vec<u64> = node
.peers
.iter()
.filter(|entry| {
node.upgrade_is_loop_candidate_at(*entry.key(), entry.value().addr)
})
.map(|entry| *entry.key())
.collect();
for peer_id in candidates {
node.upgrade_lease(peer_id, ATTEMPT_LEASE);
let n = node.clone();
tokio::spawn(async move {
n.attempt_direct_upgrade(peer_id).await;
});
}
}
})
}
pub async fn connect_routed(
&self,
dest_pubkey: &[u8; 32],
dest_node_id: u64,
) -> Result<u64, AdapterError> {
let first_hop = self
.router
.routing_table()
.lookup(dest_node_id)
.ok_or_else(|| {
AdapterError::Connection(format!(
"connect_routed: no route to peer {:#x}",
dest_node_id
))
})?;
self.connect_via(first_hop, dest_pubkey, dest_node_id).await
}
async fn handshake_initiator(
&self,
peer_addr: SocketAddr,
peer_pubkey: &[u8; 32],
peer_node_id: u64,
) -> Result<SessionKeys, AdapterError> {
let mut attempt = 0;
loop {
attempt += 1;
match self
.try_handshake_initiator(peer_addr, peer_pubkey, peer_node_id)
.await
{
Ok(keys) => return Ok(keys),
Err(e) if attempt < self.config.handshake_retries => {
tracing::warn!(attempt, error = %e, "mesh handshake failed, retrying");
tokio::time::sleep(Duration::from_millis(100 * attempt as u64)).await;
}
Err(e) => return Err(e),
}
}
}
async fn try_handshake_initiator(
&self,
peer_addr: SocketAddr,
peer_pubkey: &[u8; 32],
peer_node_id: u64,
) -> Result<SessionKeys, AdapterError> {
let timeout = self.config.handshake_timeout;
let prologue = handshake_prologue(routing_id(self.node_id), routing_id(peer_node_id));
let mut handshake =
NoiseHandshake::initiator_with_prologue(&self.config.psk, peer_pubkey, &prologue)
.map_err(|e| AdapterError::Fatal(format!("handshake init failed: {}", e)))?;
let msg1 = handshake
.write_message(&[])
.map_err(|e| AdapterError::Connection(format!("write_message failed: {}", e)))?;
let mut builder = PacketBuilder::new(&[0u8; 32], 0);
let packet = builder.build_handshake(&msg1);
let payload_bytes = if self.started.load(Ordering::Acquire) {
let (tx, rx) = oneshot::channel::<Bytes>();
self.pending_direct_initiators.insert(peer_addr, tx);
if let Err(e) = self.socket.send_to(&packet, peer_addr).await {
self.pending_direct_initiators.remove(&peer_addr);
return Err(AdapterError::Connection(format!("send failed: {}", e)));
}
match tokio::time::timeout(timeout, rx).await {
Ok(Ok(payload)) => payload,
Ok(Err(_)) => {
self.pending_direct_initiators.remove(&peer_addr);
return Err(AdapterError::Connection("handshake channel dropped".into()));
}
Err(_) => {
self.pending_direct_initiators.remove(&peer_addr);
return Err(AdapterError::Connection("handshake timeout".into()));
}
}
} else {
let socket_arc = self.socket.socket_arc();
self.socket
.send_to(&packet, peer_addr)
.await
.map_err(|e| AdapterError::Connection(format!("send failed: {}", e)))?;
let parsed = tokio::time::timeout(timeout, async {
loop {
let mut recv_buf = bytes::BytesMut::with_capacity(protocol::MAX_PACKET_SIZE);
recv_buf.resize(protocol::MAX_PACKET_SIZE, 0);
let (n, source) = socket_arc
.recv_from(&mut recv_buf)
.await
.map_err(|e| AdapterError::Connection(format!("recv failed: {}", e)))?;
if source != peer_addr {
continue;
}
recv_buf.truncate(n);
let data = recv_buf.freeze();
if let Some(p) = ParsedPacket::parse(data, source) {
if p.header.flags.is_handshake() {
return Ok::<_, AdapterError>(p);
}
}
}
})
.await
.map_err(|_| AdapterError::Connection("handshake timeout".into()))??;
parsed.payload
};
handshake
.read_message(&payload_bytes)
.map_err(|e| AdapterError::Connection(format!("read_message failed: {}", e)))?;
handshake
.into_session_keys()
.map_err(|e| AdapterError::Fatal(format!("key extraction failed: {}", e)))
}
async fn handshake_responder(
&self,
peer_node_id: u64,
) -> Result<(SessionKeys, SocketAddr), AdapterError> {
let mut attempt = 0;
loop {
attempt += 1;
match self.try_handshake_responder(peer_node_id).await {
Ok(result) => return Ok(result),
Err(e) if attempt < self.config.handshake_retries => {
tracing::warn!(attempt, error = %e, "mesh accept failed, retrying");
tokio::time::sleep(Duration::from_millis(100 * attempt as u64)).await;
}
Err(e) => return Err(e),
}
}
}
async fn try_handshake_responder(
&self,
peer_node_id: u64,
) -> Result<(SessionKeys, SocketAddr), AdapterError> {
let timeout = self.config.handshake_timeout;
let socket_arc = self.socket.socket_arc();
let (parsed, source) = tokio::time::timeout(timeout, async {
loop {
let mut recv_buf = bytes::BytesMut::with_capacity(protocol::MAX_PACKET_SIZE);
recv_buf.resize(protocol::MAX_PACKET_SIZE, 0);
let (n, source) = socket_arc
.recv_from(&mut recv_buf)
.await
.map_err(|e| AdapterError::Connection(format!("recv failed: {}", e)))?;
recv_buf.truncate(n);
let data = recv_buf.freeze();
if let Some(p) = ParsedPacket::parse(data, source) {
if p.header.flags.is_handshake() {
return Ok::<_, AdapterError>((p, source));
}
}
}
})
.await
.map_err(|_| AdapterError::Connection("handshake timeout".into()))??;
let prologue = handshake_prologue(routing_id(peer_node_id), routing_id(self.node_id));
let mut handshake = NoiseHandshake::responder_with_prologue(
&self.config.psk,
&self.static_keypair,
&prologue,
)
.map_err(|e| AdapterError::Fatal(format!("handshake init failed: {}", e)))?;
handshake
.read_message(&parsed.payload)
.map_err(|e| AdapterError::Connection(format!("read_message failed: {}", e)))?;
let msg2 = handshake
.write_message(&[])
.map_err(|e| AdapterError::Connection(format!("write_message failed: {}", e)))?;
let mut builder = PacketBuilder::new(&[0u8; 32], 0);
let packet = builder.build_handshake(&msg2);
self.socket
.send_to(&packet, source)
.await
.map_err(|e| AdapterError::Connection(format!("send failed: {}", e)))?;
let keys = handshake
.into_session_keys()
.map_err(|e| AdapterError::Fatal(format!("key extraction failed: {}", e)))?;
Ok((keys, source))
}
#[cfg(feature = "nat-traversal")]
pub async fn probe_reflex(
&self,
peer_node_id: u64,
) -> Result<std::net::SocketAddr, super::traversal::TraversalError> {
use super::traversal::{reflex, TraversalError};
let peer_addr = self
.peer_addrs
.get(&peer_node_id)
.map(|e| *e.value())
.ok_or(TraversalError::PeerNotReachable)?;
let (tx, rx) = oneshot::channel();
let gen = self
.next_waiter_gen
.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
self.pending_reflex_probes.insert(peer_node_id, (gen, tx));
let body = reflex::encode_request();
if let Err(e) = self
.send_subprotocol(peer_addr, super::traversal::SUBPROTOCOL_REFLEX, &body)
.await
{
self.pending_reflex_probes
.remove_if(&peer_node_id, |_, (g, _)| *g == gen);
return Err(TraversalError::Transport(e.to_string()));
}
let timeout = self.traversal_config.reflex_timeout;
match tokio::time::timeout(timeout, rx).await {
Ok(Ok(addr)) => Ok(addr),
Ok(Err(_recv_err)) => {
Err(TraversalError::ReflexTimeout)
}
Err(_elapsed) => {
self.pending_reflex_probes
.remove_if(&peer_node_id, |_, (g, _)| *g == gen);
Err(TraversalError::ReflexTimeout)
}
}
}
#[cfg(feature = "nat-traversal")]
pub fn nat_class(&self) -> super::traversal::classify::NatClass {
super::traversal::classify::NatClass::from_u8(
self.nat_class.load(std::sync::atomic::Ordering::Acquire),
)
}
#[cfg(feature = "nat-traversal")]
pub fn reflex_addr(&self) -> Option<std::net::SocketAddr> {
self.reflex_addr.load_full().map(|arc| *arc)
}
#[cfg(feature = "nat-traversal")]
pub fn set_reflex_override(&self, external: SocketAddr) {
use std::sync::atomic::Ordering;
let _g = self.traversal_publish_mu.lock();
self.reflex_addr.store(Some(Arc::new(external)));
self.nat_class.store(
super::traversal::classify::NatClass::Open.as_u8(),
Ordering::Release,
);
self.reflex_override_active.store(true, Ordering::Release);
{
let mut gate = self.announce_gate.lock();
gate.last_broadcast_at = None;
gate.deferred_scheduled = false;
}
}
#[cfg(feature = "nat-traversal")]
pub fn clear_reflex_override(&self) {
use std::sync::atomic::Ordering;
let _g = self.traversal_publish_mu.lock();
if !self.reflex_override_active.swap(false, Ordering::AcqRel) {
return;
}
self.reflex_addr.store(None);
self.nat_class.store(
super::traversal::classify::NatClass::Unknown.as_u8(),
Ordering::Release,
);
{
let mut gate = self.announce_gate.lock();
gate.last_broadcast_at = None;
gate.deferred_scheduled = false;
}
}
#[cfg(feature = "nat-traversal")]
pub async fn reclassify_if_reflex_drifted(&self) -> bool {
use std::sync::atomic::Ordering as AtOrd;
if self.reflex_override_active.load(AtOrd::Acquire) {
return false;
}
let Some(published) = self.local_announcement.load_full() else {
return false;
};
let Some(observed) = self.reflex_addr() else {
return false;
};
if published.reflex_addr == Some(observed) {
return false;
}
self.reclassify_nat().await;
true
}
#[cfg(feature = "nat-traversal")]
#[doc(hidden)]
pub fn set_reflex_for_test(&self, addr: SocketAddr) {
let _g = self.traversal_publish_mu.lock();
self.reflex_addr.store(Some(Arc::new(addr)));
}
#[cfg(feature = "nat-traversal")]
#[doc(hidden)]
pub fn force_nat_class_for_test(&self, class: super::traversal::classify::NatClass) {
self.nat_class.store(class.as_u8(), Ordering::Release);
}
#[doc(hidden)]
pub fn local_announcement_for_test(&self) -> Option<Arc<CapabilityAnnouncement>> {
self.local_announcement.load_full()
}
#[cfg(feature = "nat-traversal")]
pub async fn request_punch(
&self,
relay: u64,
target: u64,
self_reflex: std::net::SocketAddr,
) -> Result<super::traversal::rendezvous::PunchIntroduce, super::traversal::TraversalError>
{
use super::traversal::rendezvous::{PunchRequest, RendezvousMsg};
use super::traversal::TraversalError;
let relay_addr = self
.peer_addrs
.get(&relay)
.map(|e| *e.value())
.ok_or(TraversalError::PeerNotReachable)?;
let (tx, rx) = oneshot::channel();
let gen = self
.next_waiter_gen
.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
let punch_id = {
let mut id = self
.next_punch_id
.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
if id == 0 {
id = self
.next_punch_id
.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
}
id
};
self.pending_punch_introduces
.insert(target, (gen, relay, punch_id, tx));
let body = RendezvousMsg::PunchRequest(PunchRequest {
target,
punch_id,
self_reflex,
})
.encode();
if let Err(e) = self
.send_subprotocol(relay_addr, super::traversal::SUBPROTOCOL_RENDEZVOUS, &body)
.await
{
self.pending_punch_introduces
.remove_if(&target, |_, (g, _, _, _)| *g == gen);
return Err(TraversalError::Transport(e.to_string()));
}
let deadline = self.traversal_config.punch_deadline;
match tokio::time::timeout(deadline, rx).await {
Ok(Ok(PunchIntroduceOutcome::Introduce(intro))) => Ok(intro),
Ok(Ok(PunchIntroduceOutcome::Rejected(reason))) => {
self.traversal_stats.record_punch_rejection();
Err(TraversalError::RendezvousRejected(
reason.kind().to_string(),
))
}
Ok(Err(_recv_err)) => {
Err(TraversalError::PunchFailed)
}
Err(_elapsed) => {
self.pending_punch_introduces
.remove_if(&target, |_, (g, _, _, _)| *g == gen);
self.traversal_stats.record_punch_timeout();
Err(TraversalError::PunchFailed)
}
}
}
#[cfg(feature = "nat-traversal")]
pub async fn await_punch_introduce(
&self,
counterpart: u64,
coordinator: u64,
) -> Result<super::traversal::rendezvous::PunchIntroduce, super::traversal::TraversalError>
{
use super::traversal::TraversalError;
let (tx, rx) = oneshot::channel();
let gen = self
.next_waiter_gen
.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
self.pending_punch_introduces
.insert(counterpart, (gen, coordinator, 0, tx));
let deadline = self.traversal_config.punch_deadline;
match tokio::time::timeout(deadline, rx).await {
Ok(Ok(PunchIntroduceOutcome::Introduce(intro))) => Ok(intro),
Ok(Ok(PunchIntroduceOutcome::Rejected(reason))) => {
self.traversal_stats.record_punch_rejection();
Err(TraversalError::RendezvousRejected(
reason.kind().to_string(),
))
}
Ok(Err(_)) => Err(TraversalError::PunchFailed),
Err(_) => {
self.pending_punch_introduces
.remove_if(&counterpart, |_, (g, _, _, _)| *g == gen);
self.traversal_stats.record_punch_timeout();
Err(TraversalError::PunchFailed)
}
}
}
#[cfg(feature = "nat-traversal")]
pub async fn await_punch_ack(
&self,
counterpart: u64,
coordinator: u64,
) -> Result<super::traversal::rendezvous::PunchAck, super::traversal::TraversalError> {
use super::traversal::TraversalError;
let (tx, rx) = oneshot::channel();
let gen = self
.next_waiter_gen
.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
self.pending_punch_acks
.insert(counterpart, (gen, coordinator, tx));
let deadline = self.traversal_config.punch_deadline;
match tokio::time::timeout(deadline, rx).await {
Ok(Ok(ack)) => Ok(ack),
Ok(Err(_)) => Err(TraversalError::PunchFailed),
Err(_) => {
self.pending_punch_acks
.remove_if(&counterpart, |_, (g, _, _)| *g == gen);
Err(TraversalError::PunchFailed)
}
}
}
#[cfg(feature = "nat-traversal")]
pub async fn reclassify_nat(&self) {
use super::traversal::classify::ClassifyFsm;
let Some(_sweep) = SweepGuard::try_enter(&self.nat_classifying) else {
return;
};
if self
.reflex_override_active
.load(std::sync::atomic::Ordering::Acquire)
{
return;
}
let peers: Vec<u64> = self.peers.iter().map(|e| *e.key()).take(2).collect();
if peers.len() < 2 {
return;
}
let bind = self.local_addr();
let futures = peers.iter().copied().map(|peer| async move {
let res = self.probe_reflex(peer).await;
(peer, res)
});
let deadline = self.traversal_config.classify_deadline;
let results = match tokio::time::timeout(deadline, futures::future::join_all(futures)).await
{
Ok(results) => results,
Err(_elapsed) => {
tracing::debug!("nat-traversal: classify_deadline elapsed, keeping prior state");
return;
}
};
let mut fsm = ClassifyFsm::new();
let mut latest_reflex: Option<std::net::SocketAddr> = None;
for (peer, res) in results {
if let Ok(addr) = res {
fsm.observe(peer, addr);
latest_reflex = Some(addr);
}
}
let class = fsm.classify(bind);
self.commit_reclassify_observations(class, latest_reflex, fsm.observation_count());
}
#[cfg(feature = "nat-traversal")]
fn commit_reclassify_observations(
&self,
class: super::traversal::classify::NatClass,
latest_reflex: Option<std::net::SocketAddr>,
observation_count: usize,
) {
use std::sync::atomic::Ordering;
let _g = self.traversal_publish_mu.lock();
if self.reflex_override_active.load(Ordering::Acquire) {
tracing::debug!("nat-traversal: reflex override installed mid-sweep, skipping commit");
return;
}
if observation_count < 2 {
tracing::debug!(
observation_count,
"nat-traversal: fewer than 2 probe observations this sweep, keeping prior pair"
);
return;
}
let Some(addr) = latest_reflex else {
tracing::debug!(
"nat-traversal: no probe observations this sweep, keeping prior (class, reflex) pair"
);
return;
};
self.nat_class.store(class.as_u8(), Ordering::Release);
self.reflex_addr.store(Some(Arc::new(addr)));
tracing::debug!(
nat_class = ?class,
reflex = ?addr,
"nat-traversal: reclassified",
);
}
}
#[async_trait]
impl Adapter for MeshNode {
async fn init(&mut self) -> Result<(), AdapterError> {
Ok(())
}
async fn on_batch(&self, batch: std::sync::Arc<Batch>) -> Result<(), AdapterError> {
let peer_addr = self
.peers
.iter()
.next()
.map(|e| e.value().addr)
.ok_or_else(|| AdapterError::Connection("no peers connected".into()))?;
self.send_to_peer(peer_addr, &batch).await
}
async fn flush(&self) -> Result<(), AdapterError> {
Ok(())
}
async fn shutdown(&self) -> Result<(), AdapterError> {
self.shutdown.store(true, Ordering::Release);
self.shutdown_notify.notify_waiters();
self.router.stop();
for entry in self.peers.iter() {
entry.value().session.deactivate();
}
let tasks = std::mem::take(&mut *self.tasks.lock().await);
for handle in tasks {
let _ = handle.await;
}
Ok(())
}
async fn poll_shard(
&self,
shard_id: u16,
from_id: Option<&str>,
limit: usize,
) -> Result<ShardPollResult, AdapterError> {
let queue = match self.inbound.get(&shard_id) {
Some(q) => q,
None => return Ok(ShardPollResult::empty()),
};
let mut events = Vec::with_capacity(limit.min(1000));
let mut last_id = None;
let _ = from_id;
for _ in 0..limit {
match queue.pop() {
Some(event) => {
last_id = Some(event.id.clone());
events.push(event);
}
None => break,
}
}
let has_more = !queue.is_empty();
Ok(ShardPollResult {
events,
next_id: last_id,
has_more,
})
}
fn name(&self) -> &'static str {
"mesh"
}
async fn is_healthy(&self) -> bool {
self.started.load(Ordering::Acquire) && !self.peers.is_empty()
}
}
impl Drop for MeshNode {
fn drop(&mut self) {
self.shutdown.store(true, Ordering::Release);
self.shutdown_notify.notify_waiters();
self.router.stop();
}
}
#[cfg(all(test, feature = "nat-traversal"))]
mod punch_observer_tests {
use super::*;
use crate::adapter::net::traversal::rendezvous::Keepalive;
use tokio::sync::oneshot;
fn sample_ka() -> Keepalive {
Keepalive {
sender_node_id: 0x1234,
punch_id: 0,
}
}
fn sample_peer() -> SocketAddr {
"198.51.100.5:9001".parse().unwrap()
}
const EXPECTED_PEER: u64 = 0x1234;
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn fires_true_when_keepalive_arrives() {
let observers: DashMap<SocketAddr, (u64, oneshot::Sender<Keepalive>)> = DashMap::new();
let peer = sample_peer();
let (tx, rx) = oneshot::channel();
observers.insert(peer, (EXPECTED_PEER, tx));
let (_, (_id, fired_tx)) = observers.remove(&peer).unwrap();
fired_tx.send(sample_ka()).expect("send");
let result =
await_punch_observer_outcome(rx, Duration::from_secs(1), &observers, peer).await;
assert!(result, "keepalive arrival should return true");
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn timeout_evicts_own_stale_entry() {
let observers: DashMap<SocketAddr, (u64, oneshot::Sender<Keepalive>)> = DashMap::new();
let peer = sample_peer();
let (tx, rx) = oneshot::channel();
observers.insert(peer, (EXPECTED_PEER, tx));
let result =
await_punch_observer_outcome(rx, Duration::from_millis(50), &observers, peer).await;
assert!(!result, "timeout should return false");
assert!(
!observers.contains_key(&peer),
"timeout should evict our own stale entry",
);
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn sender_dropped_leaves_replacement_observer_intact() {
let observers: DashMap<SocketAddr, (u64, oneshot::Sender<Keepalive>)> = DashMap::new();
let peer = sample_peer();
let (tx_a, rx_a) = oneshot::channel::<Keepalive>();
observers.insert(peer, (EXPECTED_PEER, tx_a));
let (tx_b, _rx_b) = oneshot::channel::<Keepalive>();
observers.insert(peer, (EXPECTED_PEER, tx_b));
assert!(observers.contains_key(&peer), "B's sender in map");
let result =
await_punch_observer_outcome(rx_a, Duration::from_secs(5), &observers, peer).await;
assert!(!result, "sender-dropped path returns false");
assert!(
observers.contains_key(&peer),
"B's sender must still be in the map — A's cleanup must not evict",
);
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn timeout_then_sender_drop_does_not_double_evict() {
let observers: DashMap<SocketAddr, (u64, oneshot::Sender<Keepalive>)> = DashMap::new();
let peer = sample_peer();
let (tx_a, rx_a) = oneshot::channel::<Keepalive>();
observers.insert(peer, (EXPECTED_PEER, tx_a));
let r1 =
await_punch_observer_outcome(rx_a, Duration::from_millis(20), &observers, peer).await;
assert!(!r1);
assert!(!observers.contains_key(&peer));
let (tx_b, rx_b) = oneshot::channel::<Keepalive>();
observers.insert(peer, (EXPECTED_PEER, tx_b));
let (tx_c, _rx_c) = oneshot::channel::<Keepalive>();
observers.insert(peer, (EXPECTED_PEER, tx_c));
let r2 = await_punch_observer_outcome(rx_b, Duration::from_secs(5), &observers, peer).await;
assert!(!r2);
assert!(
observers.contains_key(&peer),
"C's sender must remain after B's sender-dropped cleanup",
);
}
}
#[cfg(all(test, feature = "nat-traversal"))]
mod keepalive_offset_tests {
use super::keepalive_send_offsets;
use std::time::Duration;
const DEADLINE: Duration = Duration::from_secs(5);
#[test]
fn normal_lead_is_preserved_with_documented_spacing() {
let offsets = keepalive_send_offsets(1_000_500, 1_000_000, DEADLINE);
assert_eq!(offsets[0], Duration::from_millis(500));
assert_eq!(offsets[1], Duration::from_millis(600));
assert_eq!(offsets[2], Duration::from_millis(750));
}
#[test]
fn past_fire_at_collapses_to_immediate() {
let offsets = keepalive_send_offsets(900_000, 1_000_000, DEADLINE);
assert_eq!(offsets[0], Duration::ZERO);
assert_eq!(offsets[1], Duration::from_millis(100));
assert_eq!(offsets[2], Duration::from_millis(250));
}
#[test]
fn far_future_fire_at_is_clamped_to_deadline() {
let offsets = keepalive_send_offsets(1_000_000 + 1_000_000_000_000, 1_000_000, DEADLINE);
assert_eq!(offsets[0], DEADLINE, "base lead must clamp to deadline");
assert_eq!(offsets[1], DEADLINE + Duration::from_millis(100));
assert_eq!(offsets[2], DEADLINE + Duration::from_millis(250));
}
#[test]
fn u64_max_fire_at_does_not_panic_and_clamps() {
let offsets = keepalive_send_offsets(u64::MAX, 0, DEADLINE);
assert_eq!(offsets[0], DEADLINE);
assert!(offsets[2] <= DEADLINE + Duration::from_millis(250));
}
}
#[cfg(all(test, feature = "nat-traversal"))]
mod sweep_guard_tests {
use super::SweepGuard;
use std::sync::atomic::AtomicBool;
#[test]
fn try_enter_is_single_flight_and_releases_on_drop() {
let flag = AtomicBool::new(false);
let g1 = SweepGuard::try_enter(&flag);
assert!(g1.is_some(), "first entry must acquire the gate");
assert!(
SweepGuard::try_enter(&flag).is_none(),
"a second concurrent entry must be refused while the first is held",
);
drop(g1);
assert!(
SweepGuard::try_enter(&flag).is_some(),
"after the guard drops, the gate must be re-enterable",
);
}
}
#[cfg(all(test, feature = "nat-traversal"))]
mod reclassify_override_race_tests {
use super::*;
use crate::adapter::net::traversal::classify::NatClass;
use std::net::SocketAddr;
async fn build_node_for_test() -> Arc<MeshNode> {
let addr: SocketAddr = "127.0.0.1:0".parse().unwrap();
let cfg = MeshNodeConfig::new(addr, [0x17u8; 32]);
Arc::new(
MeshNode::new(EntityKeypair::generate(), cfg)
.await
.expect("MeshNode::new"),
)
}
#[tokio::test]
async fn announced_capabilities_reach_the_origin_pingwave() {
let node = build_node_for_test().await;
let before = node
.proximity_graph()
.create_pingwave(HealthStatus::Healthy);
assert_eq!(before.capability_hash, 0, "no caps announced yet");
assert_eq!(before.capability_version, 0);
node.announce_capabilities(CapabilitySet::new().add_tag("svc:demo".to_string()))
.await
.expect("announce");
let after = node
.proximity_graph()
.create_pingwave(HealthStatus::Healthy);
assert_ne!(
after.capability_hash, 0,
"pingwave must carry the announced capability hash, not the default"
);
assert!(
after.capability_version > before.capability_version,
"announcing capabilities must bump the pingwave capability version",
);
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn bare_start_then_mutate_then_start_arc_still_announces() {
let addr: SocketAddr = "127.0.0.1:0".parse().unwrap();
let cfg = MeshNodeConfig::new(addr, [0x23u8; 32])
.with_announce_debounce(Duration::from_millis(20))
.with_capability_reannounce_interval(Duration::MAX);
let node = Arc::new(
MeshNode::new(EntityKeypair::generate(), cfg)
.await
.expect("MeshNode::new"),
);
node.start();
tokio::time::sleep(Duration::from_millis(50)).await;
node.test_bump_local_caps_changed();
tokio::time::sleep(Duration::from_millis(150)).await;
let v_before = node.capability_version.load(Ordering::Relaxed);
node.start_arc();
let deadline = tokio::time::Instant::now() + Duration::from_secs(3);
let mut announced = false;
while tokio::time::Instant::now() < deadline {
if node.capability_version.load(Ordering::Relaxed) > v_before {
announced = true;
break;
}
tokio::time::sleep(Duration::from_millis(25)).await;
}
assert!(
announced,
"the mutation made between bare start() and start_arc() was dropped — \
capability_version never advanced after start_arc"
);
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn capability_reannounce_keeps_self_entry_alive_past_ttl() {
let addr: SocketAddr = "127.0.0.1:0".parse().unwrap();
let cfg = MeshNodeConfig::new(addr, [0x21u8; 32])
.with_capability_reannounce_interval(Duration::from_millis(40));
let node = Arc::new(
MeshNode::new(EntityKeypair::generate(), cfg)
.await
.expect("MeshNode::new"),
);
node.announce_capabilities_with(CapabilitySet::new(), Duration::from_millis(120), true)
.await
.expect("announce");
node.start_arc();
let v0 = node.capability_version.load(Ordering::Relaxed);
tokio::time::sleep(Duration::from_millis(750)).await;
let present = node
.capability_fold
.with_state(|s| s.by_node.contains_key(&node.node_id));
let v1 = node.capability_version.load(Ordering::Relaxed);
assert!(
present,
"self-entry must survive past its TTL while the re-announce loop runs"
);
assert!(
v1 >= v0 + 3,
"the loop must re-announce (broadcast) periodically: version {v0} -> {v1}"
);
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn capability_self_entry_expires_without_reannounce() {
let addr: SocketAddr = "127.0.0.1:0".parse().unwrap();
let cfg = MeshNodeConfig::new(addr, [0x22u8; 32])
.with_capability_reannounce_interval(Duration::MAX);
let node = Arc::new(
MeshNode::new(EntityKeypair::generate(), cfg)
.await
.expect("MeshNode::new"),
);
node.announce_capabilities_with(CapabilitySet::new(), Duration::from_millis(120), true)
.await
.expect("announce");
node.start_arc();
tokio::time::sleep(Duration::from_millis(750)).await;
let present = node
.capability_fold
.with_state(|s| s.by_node.contains_key(&node.node_id));
assert!(
!present,
"without the re-announce loop the self-entry must expire and be swept"
);
}
#[test]
fn reannounce_ttl_covers_throttled_broadcast_cadence() {
let ttl = capability_reannounce_ttl(Duration::from_millis(40), Duration::from_secs(10));
assert_eq!(
ttl,
Duration::from_secs(20),
"TTL must be 2× the effective (throttled) cadence, not 2× the 40 ms tick"
);
}
#[test]
fn reannounce_ttl_uses_reannounce_when_it_dominates() {
let ttl = capability_reannounce_ttl(Duration::from_secs(150), Duration::from_secs(10));
assert_eq!(ttl, Duration::from_secs(300));
}
#[test]
fn reannounce_ttl_floors_at_one_second() {
let ttl = capability_reannounce_ttl(Duration::from_millis(40), Duration::from_millis(50));
assert_eq!(ttl, Duration::from_secs(1));
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn commit_applies_when_override_inactive() {
let node = build_node_for_test().await;
let probed: SocketAddr = "198.51.100.9:4242".parse().unwrap();
node.commit_reclassify_observations(NatClass::Cone, Some(probed), 2);
assert_eq!(node.nat_class(), NatClass::Cone);
assert_eq!(node.reflex_addr(), Some(probed));
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn commit_skips_when_override_installed_mid_sweep() {
let node = build_node_for_test().await;
let override_addr: SocketAddr = "203.0.113.77:9999".parse().unwrap();
let probed: SocketAddr = "198.51.100.9:4242".parse().unwrap();
node.set_reflex_override(override_addr);
assert_eq!(node.nat_class(), NatClass::Open);
assert_eq!(node.reflex_addr(), Some(override_addr));
node.commit_reclassify_observations(NatClass::Symmetric, Some(probed), 2);
assert_eq!(
node.nat_class(),
NatClass::Open,
"mid-sweep commit stomped the override's NAT class — \
the node would be demoted from Open back to Symmetric",
);
assert_eq!(
node.reflex_addr(),
Some(override_addr),
"mid-sweep commit stomped the override reflex — the \
node would advertise the classifier's observation \
instead of the known-public mapping",
);
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn commit_skips_nat_class_store_even_when_reflex_absent() {
let node = build_node_for_test().await;
let override_addr: SocketAddr = "203.0.113.77:9999".parse().unwrap();
node.set_reflex_override(override_addr);
node.commit_reclassify_observations(NatClass::Unknown, None, 0);
assert_eq!(
node.nat_class(),
NatClass::Open,
"`nat_class` was overwritten with Unknown even though \
`latest_reflex` was None — demonstrates the second \
half of the bug that the bare `if let Some` would've \
missed",
);
assert_eq!(node.reflex_addr(), Some(override_addr));
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn commit_skips_both_stores_when_no_observation_and_no_override() {
let node = build_node_for_test().await;
let probed: SocketAddr = "198.51.100.9:4242".parse().unwrap();
node.commit_reclassify_observations(NatClass::Cone, Some(probed), 2);
assert_eq!(node.nat_class(), NatClass::Cone);
assert_eq!(node.reflex_addr(), Some(probed));
node.commit_reclassify_observations(NatClass::Unknown, None, 0);
assert_eq!(
node.nat_class(),
NatClass::Cone,
"no-observation sweep flapped nat_class from Cone to \
Unknown; pre-fix the store landed unconditionally even \
though the paired reflex store was gated by Some",
);
assert_eq!(
node.reflex_addr(),
Some(probed),
"reflex_addr was not torn (good) but nat_class was \
rewritten — the pair an `announce_capabilities_with` \
reader sees under traversal_publish_mu becomes \
(fresh class, stale reflex) which violates the mutex's \
coherent-snapshot invariant",
);
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn commit_keeps_prior_class_on_single_observation_sweep() {
let node = build_node_for_test().await;
let probed: SocketAddr = "198.51.100.9:4242".parse().unwrap();
node.commit_reclassify_observations(NatClass::Cone, Some(probed), 2);
assert_eq!(node.nat_class(), NatClass::Cone);
assert_eq!(node.reflex_addr(), Some(probed));
let single: SocketAddr = "198.51.100.10:4243".parse().unwrap();
node.commit_reclassify_observations(NatClass::Unknown, Some(single), 1);
assert_eq!(
node.nat_class(),
NatClass::Cone,
"a single-observation sweep must not downgrade a good class to Unknown",
);
assert_eq!(
node.reflex_addr(),
Some(probed),
"a single-observation sweep must not overwrite the published reflex",
);
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn commit_keeps_prior_on_torn_class_without_reflex() {
let node = build_node_for_test().await;
let probed: SocketAddr = "198.51.100.9:4242".parse().unwrap();
node.commit_reclassify_observations(NatClass::Cone, Some(probed), 2);
assert_eq!(node.nat_class(), NatClass::Cone);
assert_eq!(node.reflex_addr(), Some(probed));
node.commit_reclassify_observations(NatClass::Open, None, 2);
assert_eq!(
node.nat_class(),
NatClass::Cone,
"a torn (class, None) commit must not publish a class without a reflex",
);
assert_eq!(
node.reflex_addr(),
Some(probed),
"a torn (class, None) commit must leave the published reflex intact",
);
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn commit_resumes_after_override_cleared() {
let node = build_node_for_test().await;
let override_addr: SocketAddr = "203.0.113.77:9999".parse().unwrap();
let probed: SocketAddr = "198.51.100.9:4242".parse().unwrap();
node.set_reflex_override(override_addr);
node.clear_reflex_override();
node.commit_reclassify_observations(NatClass::Cone, Some(probed), 2);
assert_eq!(node.nat_class(), NatClass::Cone);
assert_eq!(node.reflex_addr(), Some(probed));
}
}
#[cfg(all(test, feature = "redex"))]
mod sensing_fold_gate_tests {
use super::*;
const GAP: Duration = Duration::from_secs(3600);
#[test]
fn leading_edge_runs_then_defers_then_coalesces() {
let coalescer = DashMap::new();
assert!(matches!(
sensing_fold_gate_admit(&coalescer, [1; 32], GAP),
SensingFoldGateDecision::RunNow
));
assert!(matches!(
sensing_fold_gate_admit(&coalescer, [1; 32], GAP),
SensingFoldGateDecision::Defer { .. }
));
assert!(
matches!(
sensing_fold_gate_admit(&coalescer, [1; 32], GAP),
SensingFoldGateDecision::Coalesced
),
"further in-window changes coalesce into the ONE scheduled boundary run",
);
assert!(matches!(
sensing_fold_gate_admit(&coalescer, [2; 32], GAP),
SensingFoldGateDecision::RunNow
));
}
fn defer_token(decision: SensingFoldGateDecision) -> u64 {
match decision {
SensingFoldGateDecision::Defer { token, .. } => token,
_ => panic!("expected Defer"),
}
}
#[test]
fn boundary_reclaim_is_exactly_once_and_a_fresh_run_subsumes_the_sleeper() {
let coalescer = DashMap::new();
assert!(matches!(
sensing_fold_gate_admit(&coalescer, [1; 32], GAP),
SensingFoldGateDecision::RunNow
));
let token = defer_token(sensing_fold_gate_admit(&coalescer, [1; 32], GAP));
assert!(
sensing_fold_gate_reclaim(&coalescer, &[1; 32], token),
"the sleeper owns its scheduled boundary run",
);
assert!(
!sensing_fold_gate_reclaim(&coalescer, &[1; 32], token),
"exactly one trailing run per window",
);
assert!(matches!(
sensing_fold_gate_admit(&coalescer, [2; 32], GAP),
SensingFoldGateDecision::RunNow
));
let subsumed = defer_token(sensing_fold_gate_admit(&coalescer, [2; 32], GAP));
assert!(matches!(
sensing_fold_gate_admit(&coalescer, [2; 32], Duration::ZERO),
SensingFoldGateDecision::RunNow
));
assert!(
!sensing_fold_gate_reclaim(&coalescer, &[2; 32], subsumed),
"a fresh out-of-window run subsumes the sleeper's pending reconciliation",
);
}
#[test]
fn a_stale_sleeper_cannot_steal_a_newer_windows_pending_reconciliation() {
let coalescer = DashMap::new();
assert!(matches!(
sensing_fold_gate_admit(&coalescer, [1; 32], GAP),
SensingFoldGateDecision::RunNow
));
let s1 = defer_token(sensing_fold_gate_admit(&coalescer, [1; 32], GAP));
assert!(matches!(
sensing_fold_gate_admit(&coalescer, [1; 32], Duration::ZERO),
SensingFoldGateDecision::RunNow
));
let s2 = defer_token(sensing_fold_gate_admit(&coalescer, [1; 32], GAP));
assert_ne!(s1, s2, "each window mints a distinct ownership token");
assert!(
!sensing_fold_gate_reclaim(&coalescer, &[1; 32], s1),
"a stale sleeper must not steal a newer window's pending reconciliation",
);
assert!(
sensing_fold_gate_reclaim(&coalescer, &[1; 32], s2),
"the current window's sleeper still owns its run",
);
}
}
#[cfg(test)]
mod sensing_live_direct_session_tests {
use super::*;
#[test]
fn relayed_session_address_is_not_a_live_direct_session() {
let relay_addr: SocketAddr = "127.0.0.1:9001".parse().unwrap();
let addr_to_node: DashMap<SocketAddr, u64> = DashMap::new();
addr_to_node.insert(relay_addr, 0xE0); assert!(
!sensing_addr_is_live_direct(&addr_to_node, None, 0xF0, relay_addr),
"a relayed PeerInfo must never read as a live direct session",
);
}
#[test]
fn direct_session_reverse_maps_to_the_node_itself() {
let addr: SocketAddr = "127.0.0.1:9002".parse().unwrap();
let addr_to_node: DashMap<SocketAddr, u64> = DashMap::new();
addr_to_node.insert(addr, 0xD1);
assert!(sensing_addr_is_live_direct(&addr_to_node, None, 0xD1, addr));
let stale: SocketAddr = "127.0.0.1:9003".parse().unwrap();
assert!(!sensing_addr_is_live_direct(
&addr_to_node,
None,
0xD1,
stale
));
}
}
#[cfg(test)]
mod fold_publisher_helpers_tests {
use super::*;
use std::net::SocketAddr;
async fn build_node_for_test() -> Arc<MeshNode> {
let addr: SocketAddr = "127.0.0.1:0".parse().unwrap();
let cfg = MeshNodeConfig::new(addr, [0x42u8; 32]);
Arc::new(
MeshNode::new(EntityKeypair::generate(), cfg)
.await
.expect("MeshNode::new"),
)
}
#[tokio::test]
async fn next_fold_generation_first_call_returns_one() {
let node = build_node_for_test().await;
assert_eq!(node.next_fold_generation(0x0F00, 0xAA), 1);
}
#[tokio::test]
async fn next_fold_generation_advances_monotonically_per_class() {
let node = build_node_for_test().await;
let class = 0xBEEF;
let kind = 0x0F00;
let mut last = 0;
for _ in 0..8 {
let g = node.next_fold_generation(kind, class);
assert!(
g > last,
"generation must be strictly monotonic per (kind, class)"
);
last = g;
}
assert_eq!(last, 8, "8 calls → gens 1..=8");
}
#[tokio::test]
async fn next_fold_generation_shards_per_kind_and_class() {
let node = build_node_for_test().await;
let g_a_x = node.next_fold_generation(0x0F00, 0xAA);
let g_a_y = node.next_fold_generation(0x0F00, 0xBB);
let g_b_x = node.next_fold_generation(0x0F01, 0xAA);
assert_eq!(g_a_x, 1);
assert_eq!(g_a_y, 1);
assert_eq!(g_b_x, 1);
let g_a_x_2 = node.next_fold_generation(0x0F00, 0xAA);
assert_eq!(g_a_x_2, 2);
assert_eq!(node.next_fold_generation(0x0F00, 0xBB), 2);
assert_eq!(node.next_fold_generation(0x0F01, 0xAA), 2);
}
#[tokio::test]
async fn publish_capability_membership_signs_with_node_identity() {
let node = build_node_for_test().await;
let result = node
.publish_capability_membership(super::super::behavior::fold::CapabilityMembership {
class_hash: 0x1000,
tags: vec!["gpu".into()],
hardware: None,
state: super::super::behavior::fold::NodeState::Idle,
region: Some("us-east".into()),
price_quote: None,
reflex_addr: None,
allowed_nodes: Vec::new(),
allowed_subnets: Vec::new(),
allowed_groups: Vec::new(),
metadata: std::collections::BTreeMap::new(),
})
.await;
assert!(result.is_ok());
assert_eq!(result.unwrap(), 0, "no peers → 0 sent");
let next = node.next_fold_generation(
<super::super::behavior::fold::CapabilityFold as super::super::behavior::fold::FoldKind>::KIND_ID,
0x1000,
);
assert_eq!(
next, 2,
"first publish used gen=1, next_fold_generation returns gen=2"
);
}
#[tokio::test]
async fn fold_generation_gc_evicts_stale_slots_keeps_recent() {
let node = build_node_for_test().await;
let kind = 0x0F00u16;
let stale_class = 0xAAAA_u64;
let fresh_class = 0xBBBB_u64;
node.next_fold_generation(kind, stale_class);
node.fold_generations
.get(&(kind, stale_class))
.expect("stale entry exists")
.last_touched_us
.store(0, Ordering::Relaxed);
node.next_fold_generation(kind, fresh_class);
let cutoff_us = crate::adapter::net::current_timestamp_micros()
.saturating_sub(FOLD_GENERATION_GC_MAX_AGE.as_micros() as u64);
node.fold_generations
.retain(|_, e| e.last_touched_us.load(Ordering::Relaxed) >= cutoff_us);
assert!(
!node.fold_generations.contains_key(&(kind, stale_class)),
"stale slot evicted"
);
assert!(
node.fold_generations.contains_key(&(kind, fresh_class)),
"fresh slot survives"
);
}
#[tokio::test]
async fn origin_hash_index_first_write_wins_on_adversarial_collision() {
let node = build_node_for_test().await;
let hash = 0xCAFE_BABE_DEAD_BEEF_u64;
let first = 0xAAAA_u64;
let second = 0xBBBB_u64;
assert_eq!(node.get_node_by_origin_hash(hash), None);
node.origin_hash_to_node.entry(hash).or_insert(first);
assert_eq!(node.get_node_by_origin_hash(hash), Some(first));
node.origin_hash_to_node.entry(hash).or_insert(second);
assert_eq!(
node.get_node_by_origin_hash(hash),
Some(first),
"first-write-wins must not be displaced by an adversarial grind"
);
node.origin_hash_to_node
.remove_if(&hash, |_, claimant| *claimant == second);
assert_eq!(
node.get_node_by_origin_hash(hash),
Some(first),
"remove_if on non-claimant is a no-op"
);
node.origin_hash_to_node
.remove_if(&hash, |_, claimant| *claimant == first);
assert_eq!(
node.get_node_by_origin_hash(hash),
None,
"remove_if on the current claimant clears the slot"
);
}
#[tokio::test]
async fn origin_hash_index_distinguishes_low32_collisions() {
let node = build_node_for_test().await;
let low_common: u32 = 0xDEAD_BEEF;
let hash_a: u64 = low_common as u64;
let hash_b: u64 = (0x4242_4242_u64 << 32) | (low_common as u64);
assert_eq!(hash_a as u32, hash_b as u32);
assert_ne!(hash_a, hash_b);
node.origin_hash_to_node.insert(hash_a, 0xAAAA);
node.origin_hash_to_node.insert(hash_b, 0xBBBB);
assert_eq!(node.get_node_by_origin_hash(hash_a), Some(0xAAAA));
assert_eq!(node.get_node_by_origin_hash(hash_b), Some(0xBBBB));
}
#[tokio::test]
async fn local_subnet_defaults_to_global_without_config_override() {
let node = build_node_for_test().await;
assert_eq!(node.local_subnet(), SubnetId::GLOBAL);
assert!(node.local_subnet_policy().is_none());
}
#[tokio::test]
async fn gateway_handle_is_none_until_channel_configs_installed() {
let node = build_node_for_test().await;
assert!(node.gateway().is_none());
let addr: SocketAddr = "127.0.0.1:0".parse().unwrap();
let cfg = MeshNodeConfig::new(addr, [0x17u8; 32]);
let mut owned = MeshNode::new(EntityKeypair::generate(), cfg)
.await
.expect("MeshNode::new");
assert!(owned.gateway().is_none());
owned.set_channel_configs(std::sync::Arc::new(
crate::adapter::net::ChannelConfigRegistry::new(),
));
let gw = owned.gateway().expect("gateway installed");
assert_eq!(gw.local_subnet(), SubnetId::GLOBAL);
assert_eq!(gw.forwarded_count(), 0);
assert_eq!(gw.dropped_count(), 0);
}
#[tokio::test]
async fn failed_peer_eviction_drops_retained_subscribe_chains() {
let addr: SocketAddr = "127.0.0.1:0".parse().unwrap();
let cfg =
MeshNodeConfig::new(addr, [0x17u8; 32]).with_session_timeout(Duration::from_millis(1));
let node = MeshNode::new(EntityKeypair::generate(), cfg)
.await
.expect("MeshNode::new");
let dead: u64 = 0xDEAD;
let live: u64 = 0xBEEF;
let channel_hash: ChannelHash = 0x1234;
let issuer = EntityKeypair::generate();
let subject = EntityKeypair::generate();
let chain = TokenChain::single(PermissionToken::issue(
&issuer,
subject.entity_id().clone(),
TokenScope::SUBSCRIBE,
channel_hash,
3600,
0,
));
node.subscriber_chains
.insert((dead, channel_hash), RetainedChain::new(chain.clone()));
node.subscriber_chains
.insert((live, channel_hash), RetainedChain::new(chain));
assert_eq!(node.subscriber_chains.len(), 2);
node.failure_detector.heartbeat(dead, addr);
node.failure_detector.heartbeat(live, addr);
tokio::time::sleep(Duration::from_millis(20)).await;
node.failure_detector.heartbeat(live, addr);
let failed = node.failure_detector.check_all();
assert!(failed.contains(&dead), "dead peer must be detected failed");
assert!(
node.subscriber_chains.get(&(live, channel_hash)).is_some(),
"live peer's retained chain must survive"
);
assert!(
node.subscriber_chains.get(&(dead, channel_hash)).is_none(),
"failed peer's retained chain must be evicted"
);
}
#[tokio::test]
async fn delegated_publish_chain_authorizes_publish() {
use crate::adapter::net::{ChannelConfig, ChannelConfigRegistry, ChannelId, ChannelName};
let addr: SocketAddr = "127.0.0.1:0".parse().unwrap();
let cfg = MeshNodeConfig::new(addr, [0x17u8; 32]);
let mut node = MeshNode::new(EntityKeypair::generate(), cfg)
.await
.expect("MeshNode::new");
let owner = EntityKeypair::generate();
let mid = EntityKeypair::generate();
let channel = ChannelName::new("fleet/telemetry").unwrap();
let registry = ChannelConfigRegistry::new();
registry.insert(
ChannelConfig::new(ChannelId::new(channel.clone()))
.with_token_roots(vec![owner.entity_id().clone()]),
);
node.set_channel_configs(Arc::new(registry));
let root = PermissionToken::issue(
&owner,
mid.entity_id().clone(),
TokenScope::PUBLISH.union(TokenScope::DELEGATE),
channel.hash(),
3600,
2,
);
let leaf = root
.delegate(&mid, node.entity_id().clone(), TokenScope::PUBLISH)
.expect("delegation should succeed");
let chain = TokenChain {
tokens: vec![root, leaf],
};
let publisher = node.channel_publisher(channel.clone(), PublishConfig::default());
assert!(
node.publish_many(&publisher, &[Bytes::from_static(b"x")])
.await
.is_err(),
"delegated publisher must be denied until it presents its chain"
);
node.set_publish_chain(&channel, chain);
let report = node
.publish_many(&publisher, &[Bytes::from_static(b"x")])
.await
.expect("held delegated chain must authorize publish");
assert_eq!(report.attempted, 0);
}
#[cfg(feature = "cortex")]
#[tokio::test]
async fn aggregator_registry_is_none_until_installed() {
use crate::adapter::net::behavior::aggregator::AggregatorRegistry;
let addr: SocketAddr = "127.0.0.1:0".parse().unwrap();
let cfg = MeshNodeConfig::new(addr, [0x17u8; 32]);
let mut owned = MeshNode::new(EntityKeypair::generate(), cfg)
.await
.expect("MeshNode::new");
assert!(owned.aggregator_registry().is_none());
let registry = std::sync::Arc::new(AggregatorRegistry::new());
owned.set_aggregator_registry(registry.clone());
let installed = owned.aggregator_registry().expect("registry installed");
assert!(std::sync::Arc::ptr_eq(installed, ®istry));
assert!(installed.is_empty());
}
#[tokio::test]
async fn known_subnets_sorted_by_node_id() {
let node = build_node_for_test().await;
assert!(node.known_subnets().is_empty());
node.peer_subnets
.insert(0xC0FFEE, SubnetId::new(&[3, 7, 2]));
node.peer_subnets.insert(0xAAAA, SubnetId::new(&[3, 7, 1]));
node.peer_subnets.insert(0xB0B0, SubnetId::new(&[3, 8]));
let snapshot = node.known_subnets();
let ids: Vec<u64> = snapshot.iter().map(|(id, _)| *id).collect();
assert_eq!(ids, vec![0xAAAA, 0xB0B0, 0xC0FFEE]);
assert_eq!(snapshot[0].1, SubnetId::new(&[3, 7, 1]));
assert_eq!(snapshot[1].1, SubnetId::new(&[3, 8]));
assert_eq!(snapshot[2].1, SubnetId::new(&[3, 7, 2]));
}
#[tokio::test]
async fn capability_fold_is_wired_at_construction() {
let node = build_node_for_test().await;
assert_eq!(node.capability_fold().stats().entries, 0);
assert!(node.has_fold_router());
let stats = node.fold_stats();
assert!(
stats.iter().any(|s| {
s.kind == <super::super::behavior::fold::CapabilityFold
as super::super::behavior::fold::FoldKind>::KIND_ID
}),
"capability fold registered in default router"
);
}
}
#[cfg(test)]
mod route_withdrawal_promotion_tests {
use super::*;
use crate::adapter::net::failure::{FailureDetector, FailureDetectorConfig, NodeStatus};
use std::net::SocketAddr;
use std::time::Duration;
fn addr(port: u16) -> SocketAddr {
format!("127.0.0.1:{port}").parse().unwrap()
}
fn detector() -> FailureDetector {
FailureDetector::with_config(FailureDetectorConfig {
timeout: Duration::from_millis(1),
miss_threshold: 1,
suspicion_threshold: 1,
cleanup_interval: Duration::from_secs(60),
})
}
#[test]
fn healthy_direct_hop_is_promotable() {
let hop = 0xA1;
let hop_addr = addr(4001);
let via_addr = addr(4099);
let a2n = DashMap::new();
a2n.insert(hop_addr, hop);
let fd = detector();
fd.heartbeat(hop, hop_addr); assert!(MeshNode::promotable_direct_hop(
&a2n, &fd, hop, hop_addr, via_addr
));
}
#[test]
fn failed_hop_is_not_promotable() {
let hop = 0xA2;
let hop_addr = addr(4002);
let via_addr = addr(4099);
let a2n = DashMap::new();
a2n.insert(hop_addr, hop);
let fd = detector();
fd.heartbeat(hop, hop_addr);
std::thread::sleep(Duration::from_millis(5));
assert_eq!(fd.check_all(), vec![hop], "precondition: hop is Failed");
assert_eq!(fd.status(hop), NodeStatus::Failed);
assert!(
!MeshNode::promotable_direct_hop(&a2n, &fd, hop, hop_addr, via_addr),
"a Failed hop must never be promoted (un-displaceable metric-1 black hole)"
);
}
#[test]
fn relayed_hop_is_not_promotable() {
let hop = 0xA3;
let relay_addr = addr(4003);
let via_addr = addr(4099);
let a2n = DashMap::new();
a2n.insert(relay_addr, 0xBEEF);
let fd = detector();
fd.heartbeat(hop, relay_addr); assert!(
!MeshNode::promotable_direct_hop(&a2n, &fd, hop, relay_addr, via_addr),
"a relayed hop (addr maps to the relay, not the hop) must not be promoted"
);
}
#[test]
fn hop_at_the_withdrawing_addr_is_not_promotable() {
let hop = 0xA4;
let via_addr = addr(4004);
let a2n = DashMap::new();
a2n.insert(via_addr, hop);
let fd = detector();
fd.heartbeat(hop, via_addr);
assert!(
!MeshNode::promotable_direct_hop(&a2n, &fd, hop, via_addr, via_addr),
"promoting the withdrawing sender's own address reinstalls the dropped route"
);
}
#[test]
fn withdraw_damper_keys_on_dest_and_exclude() {
let damper: DashMap<(u64, Option<u64>), std::time::Instant> = DashMap::new();
let dest = 0xD;
let t0 = std::time::Instant::now();
assert!(route_withdraw_damp_admit(&damper, (dest, Some(0xB)), t0));
assert!(!route_withdraw_damp_admit(&damper, (dest, Some(0xB)), t0));
assert!(route_withdraw_damp_admit(&damper, (dest, Some(0xC)), t0));
assert!(route_withdraw_damp_admit(&damper, (dest, None), t0));
let t1 = t0 + ROUTE_WITHDRAW_DAMP_WINDOW;
assert!(route_withdraw_damp_admit(&damper, (dest, Some(0xB)), t1));
}
}
#[cfg(test)]
mod heartbeat_aead_tests {
use super::*;
use crate::adapter::net::crypto::{NoiseHandshake, StaticKeypair};
use crate::adapter::net::pool::PacketBuilder;
use crate::adapter::net::protocol::{NetHeader, PacketFlags};
fn counter_of(packet: &[u8]) -> u64 {
u64::from_le_bytes(
packet[16..24]
.try_into()
.expect("packet header is at least 24 bytes"),
)
}
fn make_session_keys() -> (
crate::adapter::net::crypto::SessionKeys,
crate::adapter::net::crypto::SessionKeys,
) {
let psk = [0x42u8; 32];
let responder_kp = StaticKeypair::generate();
let mut initiator = NoiseHandshake::initiator(&psk, &responder_kp.public).unwrap();
let mut responder = NoiseHandshake::responder(&psk, &responder_kp).unwrap();
let msg1 = initiator.write_message(&[]).unwrap();
responder.read_message(&msg1).unwrap();
let msg2 = responder.write_message(&[]).unwrap();
initiator.read_message(&msg2).unwrap();
(
initiator.into_session_keys().unwrap(),
responder.into_session_keys().unwrap(),
)
}
#[test]
fn aead_authenticated_heartbeat_passes_verification_and_touches_session() {
let (init_keys, resp_keys) = make_session_keys();
let resp_session = NetSession::new(resp_keys, "127.0.0.1:5000".parse().unwrap(), 4, false);
let mut builder = PacketBuilder::new(&init_keys.tx_key, init_keys.session_id);
let bytes = builder.build_heartbeat();
let parsed = ParsedPacket::parse(bytes, "127.0.0.1:5000".parse().unwrap())
.expect("legitimate heartbeat must parse");
assert!(parsed.header.flags.is_heartbeat());
let last_before = resp_session.last_activity_ns();
std::thread::sleep(std::time::Duration::from_millis(2));
assert!(
resp_session.verify_and_touch_heartbeat(&parsed),
"AEAD-authenticated heartbeat must verify against the matched session"
);
assert!(
resp_session.last_activity_ns() > last_before,
"successful verify must touch the session — verify+touch are fused"
);
}
#[test]
fn unauthenticated_heartbeat_fails_verification_and_does_not_touch() {
let (_init_keys, resp_keys) = make_session_keys();
let resp_session = NetSession::new(resp_keys, "127.0.0.1:5000".parse().unwrap(), 4, false);
let mut forged = bytes::BytesMut::new();
let mut header_bytes = NetHeader::heartbeat(resp_session.session_id()).to_bytes();
header_bytes[12..16].copy_from_slice(&[0u8; 4]);
header_bytes[16..24].copy_from_slice(&1u64.to_le_bytes());
forged.extend_from_slice(&header_bytes);
forged.extend_from_slice(&[0xAAu8; 16]); let parsed = ParsedPacket::parse(forged.freeze(), "127.0.0.1:5000".parse().unwrap())
.expect("forged heartbeat must still parse — verification is downstream");
assert!(parsed.header.flags.is_heartbeat());
let last_before = resp_session.last_activity_ns();
std::thread::sleep(std::time::Duration::from_millis(2));
assert!(
!resp_session.verify_and_touch_heartbeat(&parsed),
"heartbeat with garbage AEAD tag must NOT verify — pre-fix the \
mesh dispatcher would have called session.touch() / \
failure_detector.heartbeat() unconditionally"
);
assert_eq!(
resp_session.last_activity_ns(),
last_before,
"failed verify must NOT touch the session — verify+touch are fused"
);
}
#[tokio::test]
async fn peer_registration_guard_rolls_back_on_drop_when_not_completed() {
let peer_id = 0xDEAD_BEEFu64;
let next_hop: SocketAddr = "10.0.0.1:9000".parse().unwrap();
let peers: Arc<DashMap<u64, PeerInfo>> = Arc::new(DashMap::new());
let peer_addrs: Arc<DashMap<u64, SocketAddr>> = Arc::new(DashMap::new());
let router = Arc::new(
NetRouter::new(crate::adapter::net::router::RouterConfig::new(
0xCAFE_BABE,
"127.0.0.1:0".parse().unwrap(),
))
.await
.unwrap(),
);
let (init_keys, _resp_keys) = make_session_keys();
let session = Arc::new(NetSession::new(init_keys, next_hop, 4, false));
let registered_session_id = session.session_id();
let session_id_to_node: Arc<DashMap<u64, u64>> = Arc::new(DashMap::new());
session_id_to_node.insert(registered_session_id, peer_id);
peers.insert(
peer_id,
PeerInfo {
node_id: peer_id,
addr: next_hop,
session,
remote_static_pub: [0u8; 32],
last_initiator_ephemeral: None,
},
);
peer_addrs.insert(peer_id, next_hop);
router.add_route(peer_id, next_hop);
{
let _guard = PeerRegistrationGuard {
peer_node_id: peer_id,
registered_session_id,
registered_next_hop: next_hop,
peers: peers.clone(),
peer_addrs: peer_addrs.clone(),
session_id_to_node: session_id_to_node.clone(),
router: router.clone(),
};
}
assert!(
!peers.contains_key(&peer_id),
"peers entry must be removed by Drop rollback"
);
assert!(
!peer_addrs.contains_key(&peer_id),
"peer_addrs entry must be removed by Drop rollback"
);
assert!(
!session_id_to_node.contains_key(®istered_session_id),
"session_id_to_node entry must be removed by Drop rollback (PERF_AUDIT §2.4)"
);
assert!(
router.routing_table().lookup(peer_id).is_none(),
"route must be removed by Drop rollback"
);
}
#[tokio::test]
async fn peer_registration_guard_is_no_op_on_drop_when_completed() {
let peer_id = 0xCAFE_F00Du64;
let next_hop: SocketAddr = "10.0.0.2:9000".parse().unwrap();
let peers: Arc<DashMap<u64, PeerInfo>> = Arc::new(DashMap::new());
let peer_addrs: Arc<DashMap<u64, SocketAddr>> = Arc::new(DashMap::new());
let router = Arc::new(
NetRouter::new(crate::adapter::net::router::RouterConfig::new(
0xCAFE_BABE,
"127.0.0.1:0".parse().unwrap(),
))
.await
.unwrap(),
);
let (init_keys, _resp_keys) = make_session_keys();
let session = Arc::new(NetSession::new(init_keys, next_hop, 4, false));
let registered_session_id = session.session_id();
let session_id_to_node: Arc<DashMap<u64, u64>> = Arc::new(DashMap::new());
session_id_to_node.insert(registered_session_id, peer_id);
peers.insert(
peer_id,
PeerInfo {
node_id: peer_id,
addr: next_hop,
session,
remote_static_pub: [0u8; 32],
last_initiator_ephemeral: None,
},
);
peer_addrs.insert(peer_id, next_hop);
router.add_route(peer_id, next_hop);
{
let guard = PeerRegistrationGuard {
peer_node_id: peer_id,
registered_session_id,
registered_next_hop: next_hop,
peers: peers.clone(),
peer_addrs: peer_addrs.clone(),
session_id_to_node: session_id_to_node.clone(),
router: router.clone(),
};
guard.commit();
}
assert!(peers.contains_key(&peer_id));
assert!(peer_addrs.contains_key(&peer_id));
assert!(
session_id_to_node.contains_key(®istered_session_id),
"commit() must preserve session_id_to_node alongside the other maps"
);
assert!(router.routing_table().lookup(peer_id).is_some());
}
#[tokio::test]
async fn peer_registration_guard_preserves_concurrent_overwrite() {
let peer_id = 0xFACE_F00Du64;
let stale: SocketAddr = "10.0.0.3:9000".parse().unwrap();
let fresh: SocketAddr = "10.0.0.4:9000".parse().unwrap();
let peers: Arc<DashMap<u64, PeerInfo>> = Arc::new(DashMap::new());
let peer_addrs: Arc<DashMap<u64, SocketAddr>> = Arc::new(DashMap::new());
let router = Arc::new(
NetRouter::new(crate::adapter::net::router::RouterConfig::new(
0xCAFE_BABE,
"127.0.0.1:0".parse().unwrap(),
))
.await
.unwrap(),
);
let (init_keys, _resp_keys) = make_session_keys();
let session = Arc::new(NetSession::new(init_keys, fresh, 4, false));
let fresh_session_id = session.session_id();
let stale_session_id = fresh_session_id.wrapping_add(0xDEAD_BEEF);
let session_id_to_node: Arc<DashMap<u64, u64>> = Arc::new(DashMap::new());
session_id_to_node.insert(fresh_session_id, peer_id);
peers.insert(
peer_id,
PeerInfo {
node_id: peer_id,
addr: fresh,
session,
remote_static_pub: [0u8; 32],
last_initiator_ephemeral: None,
},
);
peer_addrs.insert(peer_id, fresh);
router.add_route(peer_id, fresh);
{
let _guard = PeerRegistrationGuard {
peer_node_id: peer_id,
registered_session_id: stale_session_id, registered_next_hop: stale, peers: peers.clone(),
peer_addrs: peer_addrs.clone(),
session_id_to_node: session_id_to_node.clone(),
router: router.clone(),
};
}
assert!(
peers.contains_key(&peer_id),
"peers must keep the fresh (concurrent-retry) entry"
);
assert_eq!(*peer_addrs.get(&peer_id).unwrap(), fresh);
assert!(
session_id_to_node.contains_key(&fresh_session_id),
"session_id_to_node must keep the fresh entry — the stale guard's session_id \
differs, so remove_if leaves the live entry alone"
);
assert_eq!(router.routing_table().lookup(peer_id), Some(fresh));
}
#[tokio::test]
async fn routed_dispatch_lookup_filters_session_id_mismatch() {
let peer_id = 0xBEEF_CAFEu64;
let peer_addr: SocketAddr = "10.1.1.1:9000".parse().unwrap();
let peers: Arc<DashMap<u64, PeerInfo>> = Arc::new(DashMap::new());
let session_id_to_node: Arc<DashMap<u64, u64>> = Arc::new(DashMap::new());
let (init_keys, _resp_keys) = make_session_keys();
let session = Arc::new(NetSession::new(init_keys, peer_addr, 4, false));
let live_session_id = session.session_id();
peers.insert(
peer_id,
PeerInfo {
node_id: peer_id,
addr: peer_addr,
session,
remote_static_pub: [0u8; 32],
last_initiator_ephemeral: None,
},
);
session_id_to_node.insert(live_session_id, peer_id);
let resolved = session_id_to_node
.get(&live_session_id)
.map(|e| *e.value())
.and_then(|nid| peers.get(&nid).map(|e| (nid, e.value().session.clone())))
.filter(|(_, s)| s.session_id() == live_session_id);
assert!(
resolved.is_some(),
"fresh inbound for the live session_id must resolve via the reverse index"
);
let (resolved_node, resolved_session) = resolved.unwrap();
assert_eq!(resolved_node, peer_id);
assert_eq!(resolved_session.session_id(), live_session_id);
let old_session_id = live_session_id.wrapping_add(1);
session_id_to_node.insert(old_session_id, peer_id);
let stale = session_id_to_node
.get(&old_session_id)
.map(|e| *e.value())
.and_then(|nid| peers.get(&nid).map(|e| (nid, e.value().session.clone())))
.filter(|(_, s)| s.session_id() == old_session_id);
assert!(
stale.is_none(),
"stale reverse-index entry must NOT resolve to the live session — \
the .filter() guard preserves the pre-fix `peers.iter().find(matching session_id)` \
semantic that returned None in the same scenario"
);
}
#[tokio::test]
async fn install_peer_replacement_evicts_displaced_reverse_index_entry() {
let addr: SocketAddr = "127.0.0.1:0".parse().unwrap();
let cfg = MeshNodeConfig::new(addr, [0x2Au8; 32]);
let node = MeshNode::new(EntityKeypair::generate(), cfg)
.await
.expect("MeshNode::new");
let peer_id = 0xFEED_F00Du64;
let peer_addr: SocketAddr = "10.2.2.2:9100".parse().unwrap();
let (first_keys, _) = make_session_keys();
let first_session_id = first_keys.session_id;
node.install_peer(
peer_id,
peer_addr,
first_keys,
AddrInstallMode::DirectOverwrite,
);
assert_eq!(
node.session_id_to_node.get(&first_session_id).map(|e| *e),
Some(peer_id),
"first install must populate the reverse index"
);
let (second_keys, _) = make_session_keys();
let second_session_id = second_keys.session_id;
assert_ne!(
first_session_id, second_session_id,
"fresh handshake must derive a distinct session_id"
);
node.install_peer(
peer_id,
peer_addr,
second_keys,
AddrInstallMode::DirectOverwrite,
);
assert_eq!(
node.session_id_to_node.get(&second_session_id).map(|e| *e),
Some(peer_id),
"replacement install must index the fresh session_id"
);
assert!(
!node.session_id_to_node.contains_key(&first_session_id),
"displaced session_id must be evicted from the reverse index — \
leaving it would leak one entry per re-handshake"
);
assert_eq!(
node.session_id_to_node.len(),
1,
"exactly one reverse-index entry per live peer session"
);
}
#[tokio::test]
async fn install_peer_cas_refuses_on_session_id_mismatch() {
let addr: SocketAddr = "127.0.0.1:0".parse().unwrap();
let cfg = MeshNodeConfig::new(addr, [0x2Cu8; 32]);
let node = MeshNode::new(EntityKeypair::generate(), cfg)
.await
.expect("MeshNode::new");
let peer_id = 0xAB_CD_EF_01u64;
let relay_addr: SocketAddr = "10.9.9.9:9100".parse().unwrap();
let (relay_keys, _) = make_session_keys();
let relay_session_id = relay_keys.session_id;
node.install_peer(
peer_id,
relay_addr,
relay_keys,
AddrInstallMode::RoutedPreserve,
);
let (raced_keys, _) = make_session_keys();
let raced_session_id = raced_keys.session_id;
node.install_peer(
peer_id,
relay_addr,
raced_keys,
AddrInstallMode::RoutedPreserve,
);
let punched_addr: SocketAddr = "10.1.1.1:7000".parse().unwrap();
let (punch_keys, _) = make_session_keys();
let installed = node.install_peer_cas(
peer_id,
punched_addr,
punch_keys,
AddrInstallMode::DirectOverwrite,
Some(relay_session_id),
);
assert!(!installed, "CAS must refuse when the session_id changed");
assert_eq!(
node.peers
.get(&peer_id)
.map(|p| p.value().session.session_id()),
Some(raced_session_id),
"the racing session must be left intact after a refused CAS",
);
assert!(
!node.addr_to_node.contains_key(&punched_addr),
"a refused CAS must not touch addr_to_node",
);
}
#[tokio::test]
async fn install_peer_cas_installs_and_cleans_stale_addr_on_match() {
let addr: SocketAddr = "127.0.0.1:0".parse().unwrap();
let cfg = MeshNodeConfig::new(addr, [0x2Du8; 32]);
let node = MeshNode::new(EntityKeypair::generate(), cfg)
.await
.expect("MeshNode::new");
let peer_id = 0x11_22_33_44u64;
let old_addr: SocketAddr = "10.5.5.5:9100".parse().unwrap();
let (first_keys, _) = make_session_keys();
let first_session_id = first_keys.session_id;
node.install_peer(
peer_id,
old_addr,
first_keys,
AddrInstallMode::DirectOverwrite,
);
assert_eq!(
node.addr_to_node.get(&old_addr).map(|e| *e),
Some(peer_id),
"precondition: old addr maps to the peer",
);
let new_addr: SocketAddr = "10.1.1.1:7000".parse().unwrap();
let (punch_keys, _) = make_session_keys();
let punch_session_id = punch_keys.session_id;
let installed = node.install_peer_cas(
peer_id,
new_addr,
punch_keys,
AddrInstallMode::DirectOverwrite,
Some(first_session_id),
);
assert!(installed, "CAS must install when the session_id matches");
assert_eq!(
node.peers
.get(&peer_id)
.map(|p| p.value().session.session_id()),
Some(punch_session_id),
"the punched session must replace the old one",
);
assert_eq!(
node.addr_to_node.get(&new_addr).map(|e| *e),
Some(peer_id),
"the new addr must map to the peer",
);
assert!(
!node.addr_to_node.contains_key(&old_addr),
"C4: the displaced addr's stale reverse mapping must be removed",
);
}
#[tokio::test]
async fn grant_peer_resolution_self_primes_node_id_cache() {
let addr: SocketAddr = "127.0.0.1:0".parse().unwrap();
let cfg = MeshNodeConfig::new(addr, [0x2Bu8; 32]);
let node = MeshNode::new(EntityKeypair::generate(), cfg)
.await
.expect("MeshNode::new");
let peer_id = 0xCAFE_D00Du64;
let peer_addr: SocketAddr = "10.3.3.3:9100".parse().unwrap();
let (keys, _) = make_session_keys();
node.install_peer(peer_id, peer_addr, keys, AddrInstallMode::DirectOverwrite);
let session = node
.peers
.get(&peer_id)
.map(|p| p.value().session.clone())
.expect("peer installed");
assert_eq!(
session.cached_node_id(),
None,
"install_peer must not pre-prime the cache (lazy by design)"
);
let (resolved_addr, resolved_session) =
MeshNode::resolve_grant_peer(&node.peers, &node.addr_to_node, &session)
.expect("fallback chain resolves the installed peer");
assert_eq!(resolved_addr, peer_addr);
assert_eq!(resolved_session.session_id(), session.session_id());
assert_eq!(
session.cached_node_id(),
Some(peer_id),
"fallback resolution must publish the cache (self-priming)"
);
node.addr_to_node.remove(&peer_addr);
let (addr2, _) = MeshNode::resolve_grant_peer(&node.peers, &node.addr_to_node, &session)
.expect("tier-1 cached resolution");
assert_eq!(addr2, peer_addr);
let (new_keys, _) = make_session_keys();
node.install_peer(
peer_id,
peer_addr,
new_keys,
AddrInstallMode::DirectOverwrite,
);
assert!(
MeshNode::resolve_grant_peer(&node.peers, &node.addr_to_node, &session).is_none(),
"stale session must not resolve to the replacement peer entry"
);
}
#[test]
fn pooled_heartbeat_builds_succeed_in_sequence_and_verify() {
let (init_keys, resp_keys) = make_session_keys();
let init_session = NetSession::new(
init_keys.clone(),
"127.0.0.1:5001".parse().unwrap(),
4,
false,
);
let resp_session = NetSession::new(resp_keys, "127.0.0.1:5000".parse().unwrap(), 4, false);
let h1_bytes = init_session.build_heartbeat();
let h2_bytes = init_session.build_heartbeat();
let p1 = ParsedPacket::parse(h1_bytes, "127.0.0.1:5001".parse().unwrap())
.expect("first heartbeat must parse");
let p2 = ParsedPacket::parse(h2_bytes, "127.0.0.1:5001".parse().unwrap())
.expect("second heartbeat must parse");
assert!(
resp_session.verify_and_touch_heartbeat(&p1),
"first pooled heartbeat must verify — pre-fix the \
all-zero key would have produced an AEAD tag the \
receiver couldn't decrypt"
);
assert!(
resp_session.verify_and_touch_heartbeat(&p2),
"second pooled heartbeat must also verify — pre-fix, \
a per-builder fresh counter would reuse counter=0 \
and the receiver's replay window would reject this \
as a duplicate"
);
}
#[test]
fn replay_of_authenticated_heartbeat_fails_verification_on_second_try() {
let (init_keys, resp_keys) = make_session_keys();
let resp_session = NetSession::new(resp_keys, "127.0.0.1:5000".parse().unwrap(), 4, false);
let mut builder = PacketBuilder::new(&init_keys.tx_key, init_keys.session_id);
let bytes = builder.build_heartbeat();
let parsed = ParsedPacket::parse(bytes, "127.0.0.1:5000".parse().unwrap()).unwrap();
assert!(resp_session.verify_and_touch_heartbeat(&parsed));
assert!(
!resp_session.verify_and_touch_heartbeat(&parsed),
"replay of an already-accepted heartbeat must fail"
);
}
#[test]
fn heartbeat_and_data_share_tx_counter_strictly_monotonic() {
let (init_keys, _resp_keys) = make_session_keys();
let init_session = NetSession::new(
init_keys.clone(),
"127.0.0.1:5001".parse().unwrap(),
4,
false,
);
let h1 = init_session.build_heartbeat();
let d1 = {
let mut pooled = init_session.thread_local_pool().get();
pooled.build(
0xCAFE_F00D,
0,
&[bytes::Bytes::from_static(b"event-a")],
PacketFlags::NONE,
)
};
let h2 = init_session.build_heartbeat();
let d2 = {
let mut pooled = init_session.thread_local_pool().get();
pooled.build(
0xCAFE_F00D,
1,
&[bytes::Bytes::from_static(b"event-b")],
PacketFlags::NONE,
)
};
let h3 = init_session.build_heartbeat();
let counters = [
counter_of(&h1),
counter_of(&d1),
counter_of(&h2),
counter_of(&d2),
counter_of(&h3),
];
for window in counters.windows(2) {
assert!(
window[0] < window[1],
"tx counters must be strictly increasing across heartbeat/data \
interleave; got {:?} (regression: heartbeats and data \
are drawing from independent counters)",
counters
);
}
}
#[test]
fn cr8_dispatch_must_not_use_single_event_pattern() {
let needle = format!("events.into_iter().{}()", "next");
let src = include_str!("mesh.rs");
for (lineno, line) in src.lines().enumerate() {
let trimmed = line.trim_start();
if trimmed.starts_with("//") {
continue;
}
assert!(
!trimmed.contains(&needle),
"CR-8 regression: single-event dispatch pattern reintroduced \
at mesh.rs:{} — multi-event frames will silently drop \
every payload past the first.\n line: {}",
lineno + 1,
line
);
}
}
#[test]
fn routed_rotation_outcome_drops_replay_for_matching_static_and_ephemeral() {
let addr: SocketAddr = "10.0.0.1:9000".parse().unwrap();
let (init_keys, _) = make_session_keys();
let session = Arc::new(NetSession::new(init_keys, addr, 4, false));
let static_a = [0xAAu8; 32];
let ephemeral_a = [0xCCu8; 32];
let info = PeerInfo {
node_id: 0xBEEF_BEEFu64,
addr,
session,
remote_static_pub: static_a,
last_initiator_ephemeral: Some(ephemeral_a),
};
assert_eq!(
routed_rotation_outcome(&info, &static_a, &ephemeral_a, Duration::from_secs(30)),
RoutedRotationOutcome::DropReplay,
);
}
#[test]
fn routed_rotation_outcome_accepts_reinit_with_fresh_ephemeral() {
let addr: SocketAddr = "10.0.0.1:9000".parse().unwrap();
let (init_keys, _) = make_session_keys();
let session = Arc::new(NetSession::new(init_keys, addr, 4, false));
let static_a = [0xAAu8; 32];
let ephemeral_old = [0xCCu8; 32];
let ephemeral_new = [0xDDu8; 32];
let info = PeerInfo {
node_id: 0xBEEF_BEEFu64,
addr,
session,
remote_static_pub: static_a,
last_initiator_ephemeral: Some(ephemeral_old),
};
assert_eq!(
routed_rotation_outcome(&info, &static_a, &ephemeral_new, Duration::from_secs(30)),
RoutedRotationOutcome::AcceptRotation,
);
}
#[test]
fn routed_rotation_outcome_refuses_rotation_while_session_is_fresh() {
let addr: SocketAddr = "10.0.0.1:9000".parse().unwrap();
let (init_keys, _) = make_session_keys();
let session = Arc::new(NetSession::new(init_keys, addr, 4, false));
let info = PeerInfo {
node_id: 0xBEEF_BEEFu64,
addr,
session,
remote_static_pub: [0xAAu8; 32],
last_initiator_ephemeral: Some([0xCCu8; 32]),
};
let new_static = [0xBBu8; 32];
let new_ephemeral = [0xDDu8; 32];
assert_eq!(
routed_rotation_outcome(&info, &new_static, &new_ephemeral, Duration::from_secs(30),),
RoutedRotationOutcome::RefuseFresh,
);
}
#[test]
fn routed_rotation_outcome_accepts_rotation_after_session_timeout() {
let addr: SocketAddr = "10.0.0.1:9000".parse().unwrap();
let (init_keys, _) = make_session_keys();
let session = Arc::new(NetSession::new(init_keys, addr, 4, false));
let info = PeerInfo {
node_id: 0xBEEF_BEEFu64,
addr,
session,
remote_static_pub: [0xAAu8; 32],
last_initiator_ephemeral: Some([0xCCu8; 32]),
};
std::thread::sleep(Duration::from_millis(5));
let new_static = [0xBBu8; 32];
let new_ephemeral = [0xDDu8; 32];
assert_eq!(
routed_rotation_outcome(&info, &new_static, &new_ephemeral, Duration::from_millis(1),),
RoutedRotationOutcome::AcceptRotation,
);
}
#[test]
fn routed_rotation_outcome_defers_while_session_busy() {
let addr: SocketAddr = "10.0.0.1:9000".parse().unwrap();
let (init_keys, _) = make_session_keys();
let session = Arc::new(NetSession::new(init_keys, addr, 4, false));
session.get_or_create_stream(1);
assert!(session.has_open_streams(), "precondition: session is busy");
let static_a = [0xAAu8; 32];
let info = PeerInfo {
node_id: 0xBEEF_BEEFu64,
addr,
session,
remote_static_pub: static_a,
last_initiator_ephemeral: Some([0xCCu8; 32]),
};
assert_eq!(
routed_rotation_outcome(&info, &static_a, &[0xDDu8; 32], Duration::from_secs(30)),
RoutedRotationOutcome::DeferBusy,
);
}
#[test]
fn routed_rotation_outcome_accepts_busy_session_past_timeout() {
let addr: SocketAddr = "10.0.0.1:9000".parse().unwrap();
let (init_keys, _) = make_session_keys();
let session = Arc::new(NetSession::new(init_keys, addr, 4, false));
session.get_or_create_stream(1);
assert!(session.has_open_streams(), "precondition: session is busy");
let static_a = [0xAAu8; 32];
let info = PeerInfo {
node_id: 0xBEEF_BEEFu64,
addr,
session,
remote_static_pub: static_a,
last_initiator_ephemeral: Some([0xCCu8; 32]),
};
std::thread::sleep(Duration::from_millis(5));
assert_eq!(
routed_rotation_outcome(&info, &static_a, &[0xDDu8; 32], Duration::from_millis(1)),
RoutedRotationOutcome::AcceptRotation,
);
}
#[test]
fn connect_direct_upgrade_refreshes_addr_to_node() {
let src = include_str!("mesh.rs");
let start = src
.find("let connect_on_direct_path =")
.expect("connect_on_direct_path closure must exist");
let scan_end = (start + 4000).min(src.len());
let body = &src[start..scan_end];
assert!(
body.contains("self.addr_to_node.insert(target_addr, peer_node_id)"),
"regression: connect_on_direct_path must refresh addr_to_node \
on success — pre-fix the dispatch fast path missed on the \
upgraded session's reflex addr and fell back to a linear \
peers.iter().find per packet for exactly the sessions that \
benefit most from the index."
);
}
#[test]
fn migration_loopback_drain_caps_self_bounce_depth() {
let src = include_str!("mesh.rs");
assert!(
src.contains("const MAX_MIGRATION_LOOPBACK_DEPTH: usize"),
"regression: process_local_packet's migration loopback drain \
must declare a `MAX_MIGRATION_LOOPBACK_DEPTH` cap. Pre-fix \
the loop ran unbounded so a handler stuck in a self-bounce \
state would starve the dispatch task."
);
assert!(
src.contains("loopback_count > MAX_MIGRATION_LOOPBACK_DEPTH"),
"regression: the migration loopback drain must short-circuit \
past the depth cap with a warn — the bare `loopback_count += 1; \
match handler.handle_message(...)` shape without the threshold \
check leaves the unbounded-spin hazard in place."
);
}
#[test]
fn publish_to_peer_propagates_reliable_to_packet_flags() {
let src = include_str!("mesh.rs");
let start = src
.find("async fn publish_to_peer(")
.expect("publish_to_peer must exist");
let mut scan_end = (start + 6000).min(src.len());
while scan_end < src.len() && !src.is_char_boundary(scan_end) {
scan_end += 1;
}
let body = &src[start..scan_end];
assert!(
body.contains("if reliable") && body.contains("PacketFlags::RELIABLE"),
"regression: publish_to_peer must thread `reliable` into the packet \
header — pre-fix it hard-coded PacketFlags::NONE while only \
feeding `reliable` into open_stream_with, leaving every other \
sender call site (send_to_peer, send_routed, send_on_stream) \
inconsistent."
);
}
#[test]
fn routed_handshake_uses_entry_api_for_atomic_insert() {
let src = include_str!("mesh.rs");
let start = src
.find("fn handle_routed_handshake")
.expect("handle_routed_handshake must exist");
let scan_end = (start + 16_000).min(src.len());
let body = &src[start..scan_end];
assert!(
body.contains("ctx.peers.entry(peer_node_id)"),
"regression: handle_routed_handshake must use peers.entry() so the \
replay/rotation decision and the insert are atomic. Pre-fix the \
function used `peers.get` followed by `peers.insert`, which let \
two concurrent handshakes race the insert and wedge the loser's \
pending_handshakes state until handshake_timeout."
);
assert!(
!body.contains("ctx.peers.insert(\n peer_node_id,"),
"regression: bare ctx.peers.insert(peer_node_id, ...) reintroduced \
outside the peers.entry() block — the insert must be gated by the \
same entry guard as the existing-static check."
);
}
#[test]
fn authorize_subscribe_only_suppresses_cap_for_already_subscribed() {
let src = include_str!("mesh.rs");
let start = src
.find("fn authorize_subscribe(")
.expect("authorize_subscribe must exist");
let scan_end = (start + 4_000).min(src.len());
let body = &src[start..scan_end];
assert!(
body.contains("let already_subscribed = ctx.roster.is_subscribed("),
"regression: authorize_subscribe must capture an \
`already_subscribed` boolean and use it to gate ONLY the \
cap rejection."
);
assert!(
body.contains("!already_subscribed") && body.contains(">= ctx.max_channels_per_peer"),
"regression: the cap rejection must be guarded by \
`!already_subscribed && ... >= max_channels_per_peer` so \
an under-cap or already-subscribed peer falls through to \
the visibility / registry / token gates rather than being \
admitted with `(true, None)` ahead of them."
);
assert!(
!body.contains("if ctx.roster.is_subscribed(from_node, &channel_id) {\n return (true, None);"),
"regression: the broad `if is_subscribed -> return (true, None)` \
short-circuit reintroduced — this bypasses visibility, \
registry, and token validation for any peer already in the \
roster. Use the narrower `!already_subscribed && >= cap` \
form so the cap rejection is the ONLY thing suppressed."
);
}
#[test]
fn hop_count_increments_must_be_saturating() {
let bare_bump = format!("hop_count {} 1", "+=");
let src = include_str!("mesh.rs");
for (lineno, line) in src.lines().enumerate() {
let trimmed = line.trim_start();
if trimmed.starts_with("//") {
continue;
}
assert!(
!trimmed.contains(&bare_bump),
"hop_count regression: bare `+= 1` reintroduced at \
mesh.rs:{} — use `saturating_add(1)` so an attacker-\
controlled `hop_count == u8::MAX` cannot wrap.\n line: {}",
lineno + 1,
line,
);
}
}
}
#[cfg(test)]
#[cfg(feature = "redex")]
mod replication_dispatch_tests {
use super::*;
use crate::adapter::net::redex::{ChannelId, ReplicaRole};
use crate::adapter::net::redex::{
Inbound, ReplicationInboundRouter, SyncHeartbeat, SyncNack, SyncNackError, SyncRequest,
SyncResponse, DISPATCH_SYNC_NACK,
};
use parking_lot::Mutex as ParkingMutex;
#[derive(Default)]
struct RecorderRouter {
events: ParkingMutex<Vec<(ChannelId, Inbound)>>,
always_reject: ParkingMutex<bool>,
}
impl ReplicationInboundRouter for RecorderRouter {
fn try_route(&self, channel_id: ChannelId, inbound: Inbound) -> Result<(), Inbound> {
if *self.always_reject.lock() {
return Err(inbound);
}
self.events.lock().push((channel_id, inbound));
Ok(())
}
}
fn cid_for(name: &str) -> ChannelId {
let cn = ChannelName::new(name).unwrap();
ChannelId::from_name(&cn)
}
#[test]
fn heartbeat_dispatches_to_router() {
let cid = cid_for("test/heartbeat");
let hb = SyncHeartbeat {
channel_id: cid,
tail_seq: 42,
role: ReplicaRole::Leader,
wall_clock_ms: 0,
};
let payload = hb.to_bytes();
let router = RecorderRouter::default();
MeshNode::dispatch_replication_payload(&payload, 0xDEAD_BEEF, &router);
let events = router.events.lock();
assert_eq!(events.len(), 1);
let (got_cid, ref got_inbound) = events[0];
assert_eq!(got_cid, cid);
assert!(matches!(
got_inbound,
Inbound::Heartbeat {
from: 0xDEAD_BEEF,
..
}
));
}
#[test]
fn sync_request_dispatches_to_router() {
let cid = cid_for("test/sync_request");
let req = SyncRequest {
channel_id: cid,
since_seq: 100,
chunk_max: 4096,
request_id: 0,
class: Default::default(),
};
let payload = req.to_bytes();
let router = RecorderRouter::default();
MeshNode::dispatch_replication_payload(&payload, 0x12, &router);
let events = router.events.lock();
assert!(matches!(
events[0].1,
Inbound::SyncRequest { from: 0x12, .. }
));
}
#[test]
fn sync_response_dispatches_to_router() {
let cid = cid_for("test/sync_response");
let resp = SyncResponse {
channel_id: cid,
first_seq: 0,
leader_first_retained_seq: 0,
events: vec![],
request_id: 0,
};
let payload = resp.to_bytes();
let router = RecorderRouter::default();
MeshNode::dispatch_replication_payload(&payload, 0x34, &router);
let events = router.events.lock();
assert!(matches!(
events[0].1,
Inbound::SyncResponse { from: 0x34, .. }
));
}
#[test]
fn sync_nack_dispatches_to_router() {
let cid = cid_for("test/sync_nack");
let nack = SyncNack {
channel_id: cid,
since_seq: 50,
error_code: SyncNackError::NotLeader,
leader_first_retained_seq: 0,
detail: "re-resolve leader".to_string(),
request_id: 0,
};
let payload = nack.to_bytes();
let router = RecorderRouter::default();
MeshNode::dispatch_replication_payload(&payload, 0x56, &router);
let events = router.events.lock();
assert!(matches!(events[0].1, Inbound::SyncNack { from: 0x56, .. }));
}
#[test]
fn truncated_payload_dropped_silently() {
let router = RecorderRouter::default();
let payload: Vec<u8> = vec![0x00, 0x0E, DISPATCH_SYNC_NACK];
MeshNode::dispatch_replication_payload(&payload, 0, &router);
assert!(router.events.lock().is_empty());
}
#[test]
fn payload_shorter_than_header_dropped() {
let router = RecorderRouter::default();
let payload: Vec<u8> = vec![0x00, 0x0E]; MeshNode::dispatch_replication_payload(&payload, 0, &router);
assert!(router.events.lock().is_empty());
}
#[test]
fn unknown_dispatch_code_dropped() {
let router = RecorderRouter::default();
let payload: Vec<u8> = vec![0x00, 0x0E, 0x2F, 0xAA, 0xBB];
MeshNode::dispatch_replication_payload(&payload, 0, &router);
assert!(router.events.lock().is_empty());
}
#[test]
fn wrong_subprotocol_id_in_payload_dropped() {
let cid = cid_for("test/wrong_subprotocol");
let hb = SyncHeartbeat {
channel_id: cid,
tail_seq: 0,
role: ReplicaRole::Leader,
wall_clock_ms: 0,
};
let mut payload = hb.to_bytes();
payload[0] = 0x00;
payload[1] = 0x05; let router = RecorderRouter::default();
MeshNode::dispatch_replication_payload(&payload, 0, &router);
assert!(router.events.lock().is_empty());
}
#[test]
fn router_rejection_swallowed_silently() {
let cid = cid_for("test/rejection");
let hb = SyncHeartbeat {
channel_id: cid,
tail_seq: 0,
role: ReplicaRole::Leader,
wall_clock_ms: 0,
};
let payload = hb.to_bytes();
let router = RecorderRouter::default();
*router.always_reject.lock() = true;
MeshNode::dispatch_replication_payload(&payload, 0, &router);
assert!(router.events.lock().is_empty());
}
}
#[cfg(test)]
mod chain_helper_tests {
use super::*;
use crate::adapter::net::behavior::capability::CapabilitySet;
use crate::adapter::net::behavior::tag::Tag;
#[test]
fn chain_hex_is_lowercase_16_chars() {
assert_eq!(MeshNode::chain_hex(0), "0000000000000000");
assert_eq!(
MeshNode::chain_hex(0xDEAD_BEEF_CAFE_BABE),
"deadbeefcafebabe"
);
assert_eq!(MeshNode::chain_hex(u64::MAX), "ffffffffffffffff");
for h in [0u64, 1, 0x42, u64::MAX] {
assert_eq!(MeshNode::chain_hex(h).len(), 16);
}
}
#[cfg(feature = "dataforts")]
#[test]
fn blob_hex_is_lowercase_64_chars() {
let zero = [0u8; 32];
assert_eq!(MeshNode::blob_hex(&zero).len(), 64);
assert!(MeshNode::blob_hex(&zero).chars().all(|c| c == '0'));
let mut h = [0u8; 32];
h[0] = 0xDE;
h[1] = 0xAD;
h[31] = 0xFF;
let hex = MeshNode::blob_hex(&h);
assert!(hex.starts_with("dead"));
assert!(hex.ends_with("ff"));
assert_eq!(hex.len(), 64);
}
#[cfg(feature = "dataforts")]
#[test]
fn is_blob_heat_for_matches_blob_body_only() {
let mut h = [0u8; 32];
h[0] = 0x42;
let hex = MeshNode::blob_hex(&h);
let blob_tag = Tag::Reserved {
prefix: "heat:".to_string(),
body: format!("blob:{}=0.5", hex),
};
assert!(MeshNode::is_blob_heat_for(&blob_tag, &hex));
let chain_tag = Tag::Reserved {
prefix: "heat:".to_string(),
body: format!("{}=0.5", MeshNode::chain_hex(0x42)),
};
assert!(!MeshNode::is_blob_heat_for(&chain_tag, &hex));
}
#[cfg(feature = "dataforts")]
#[test]
fn replace_blob_heat_tags_round_trip() {
let mut h = [0u8; 32];
h[0] = 0x77;
let hex = MeshNode::blob_hex(&h);
let mut caps = CapabilitySet::default();
let initial = Tag::Reserved {
prefix: "heat:".to_string(),
body: format!("blob:{}=0.10", hex),
};
caps.tags.insert(initial.clone());
let replacement = Tag::Reserved {
prefix: "heat:".to_string(),
body: format!("blob:{}=0.80", hex),
};
MeshNode::replace_blob_heat_tags(&mut caps, &h, Some(replacement.clone()));
assert!(caps.tags.contains(&replacement));
assert!(!caps.tags.contains(&initial));
MeshNode::replace_blob_heat_tags(&mut caps, &h, None);
assert!(!caps
.tags
.iter()
.any(|t| MeshNode::is_blob_heat_for(t, &hex)));
}
fn causal_tag(body: impl Into<String>) -> Tag {
Tag::Reserved {
prefix: "causal:".to_string(),
body: body.into(),
}
}
#[test]
fn is_causal_for_presence_form() {
let hex = MeshNode::chain_hex(0x42);
assert!(MeshNode::is_causal_for(&causal_tag(&hex), &hex));
}
#[test]
fn is_causal_for_tip_form() {
let hex = MeshNode::chain_hex(0x42);
assert!(MeshNode::is_causal_for(
&causal_tag(format!("{hex}:100")),
&hex
));
}
#[test]
fn is_causal_for_range_form() {
let hex = MeshNode::chain_hex(0x42);
assert!(MeshNode::is_causal_for(
&causal_tag(format!("{hex}[50..100]")),
&hex
));
}
#[test]
fn is_causal_for_rejects_different_hash() {
let our = MeshNode::chain_hex(0x42);
let theirs = MeshNode::chain_hex(0x43);
assert!(!MeshNode::is_causal_for(&causal_tag(&theirs), &our));
assert!(!MeshNode::is_causal_for(
&causal_tag(format!("{theirs}:100")),
&our,
));
}
#[test]
fn is_causal_for_rejects_non_causal_reserved() {
let hex = MeshNode::chain_hex(0x42);
let t = Tag::Reserved {
prefix: "fork-of:".to_string(),
body: hex.clone(),
};
assert!(!MeshNode::is_causal_for(&t, &hex));
}
#[test]
fn is_causal_for_rejects_axis_value_tag() {
let hex = MeshNode::chain_hex(0x42);
let t = Tag::parse("hardware.gpu=nvidia").unwrap();
assert!(!MeshNode::is_causal_for(&t, &hex));
}
#[test]
fn is_causal_for_no_false_match_on_hex_prefix() {
let our = MeshNode::chain_hex(0x42);
let mut bogus = our.clone();
bogus.push('x'); bogus.push_str(":99");
let t = causal_tag(bogus);
assert!(!MeshNode::is_causal_for(&t, &our));
}
#[test]
fn replace_causal_tags_strips_every_variant() {
let mut caps = CapabilitySet::default();
let our = 0x42u64;
let our_hex = MeshNode::chain_hex(our);
let other_hex = MeshNode::chain_hex(0x43);
caps.tags.insert(causal_tag(&our_hex));
caps.tags.insert(causal_tag(format!("{our_hex}:50")));
caps.tags.insert(causal_tag(format!("{our_hex}[10..20]")));
caps.tags.insert(causal_tag(&other_hex));
caps.tags.insert(Tag::parse("hardware.gpu").unwrap());
MeshNode::replace_causal_tags(&mut caps, our, None);
let our_count = caps
.tags
.iter()
.filter(|t| MeshNode::is_causal_for(t, &our_hex))
.count();
let other_count = caps
.tags
.iter()
.filter(|t| MeshNode::is_causal_for(t, &other_hex))
.count();
assert_eq!(our_count, 0, "every variant for our hash must be stripped");
assert_eq!(other_count, 1, "other chain's tag must survive");
assert!(
caps.tags.iter().any(|t| matches!(t,
Tag::AxisPresent { axis, key }
if axis == &crate::adapter::net::behavior::tag::TaxonomyAxis::Hardware
&& key == "gpu"
)),
"non-causal tag must survive"
);
}
#[test]
fn replace_causal_tags_inserts_replacement() {
let mut caps = CapabilitySet::default();
let our = 0x42u64;
let our_hex = MeshNode::chain_hex(our);
caps.tags.insert(causal_tag(format!("{our_hex}:50")));
let replacement = causal_tag(format!("{our_hex}:100"));
MeshNode::replace_causal_tags(&mut caps, our, Some(replacement.clone()));
let variants: Vec<_> = caps
.tags
.iter()
.filter(|t| MeshNode::is_causal_for(t, &our_hex))
.collect();
assert_eq!(variants.len(), 1, "exactly one causal: tag for our hash");
assert_eq!(variants[0], &replacement);
}
#[test]
fn filter_unauthorized_heat_tags_strips_unclaimed_origins() {
let mut caps = CapabilitySet::default();
let owned_hex = MeshNode::chain_hex(0xCAFE);
let forged_hex = MeshNode::chain_hex(0xDEAD);
caps.tags.insert(causal_tag(&owned_hex));
caps.tags.insert(Tag::Reserved {
prefix: "heat:".to_string(),
body: format!("{owned_hex}=0.50"),
});
caps.tags.insert(Tag::Reserved {
prefix: "heat:".to_string(),
body: format!("{forged_hex}=0.99"),
});
caps.tags.insert(Tag::Reserved {
prefix: "scope:".to_string(),
body: "industrial".to_string(),
});
MeshNode::filter_unauthorized_heat_tags(&mut caps);
let surviving_heat: Vec<_> = caps
.tags
.iter()
.filter_map(|t| match t {
Tag::Reserved { prefix, body } if prefix == "heat:" => Some(body.clone()),
_ => None,
})
.collect();
assert_eq!(surviving_heat.len(), 1);
assert!(surviving_heat[0].starts_with(&owned_hex));
assert!(caps.tags.iter().any(|t| matches!(
t,
Tag::Reserved { prefix, .. } if prefix == "scope:"
)));
assert!(caps
.tags
.iter()
.any(|t| MeshNode::is_causal_for(t, &owned_hex)));
}
#[cfg(feature = "dataforts")]
#[test]
fn filter_unauthorized_heat_tags_caps_blob_heat_flood_per_announce() {
let mut caps = CapabilitySet::default();
let flood = MAX_BLOB_HEAT_TAGS_PER_ANNOUNCE * 2;
for i in 0..flood {
let mut hash = [0u8; 32];
hash[..8].copy_from_slice(&(i as u64).to_le_bytes());
let hex = MeshNode::blob_hex(&hash);
caps.tags.insert(Tag::Reserved {
prefix: "heat:".to_string(),
body: format!("blob:{hex}=1.00"),
});
}
let pre_filter = caps
.tags
.iter()
.filter(|t| {
matches!(t, Tag::Reserved { prefix, body }
if prefix == "heat:" && body.starts_with("blob:"))
})
.count();
assert_eq!(pre_filter, flood);
MeshNode::filter_unauthorized_heat_tags(&mut caps);
let post_filter = caps
.tags
.iter()
.filter(|t| {
matches!(t, Tag::Reserved { prefix, body }
if prefix == "heat:" && body.starts_with("blob:"))
})
.count();
assert_eq!(
post_filter, MAX_BLOB_HEAT_TAGS_PER_ANNOUNCE,
"filter must drop blob-heat tags past the per-announce cap"
);
}
}
#[cfg(test)]
#[cfg(feature = "cortex")]
mod route_cache_tests {
use super::*;
use std::net::SocketAddr;
async fn build_node_for_test() -> Arc<MeshNode> {
let addr: SocketAddr = "127.0.0.1:0".parse().unwrap();
let cfg = MeshNodeConfig::new(addr, [0x17u8; 32]);
Arc::new(
MeshNode::new(EntityKeypair::generate(), cfg)
.await
.expect("MeshNode::new"),
)
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn cached_lookup_returns_same_arc() {
let node = build_node_for_test().await;
let a = node.rpc_route_for_service("svc.alpha").expect("first");
let b = node.rpc_route_for_service("svc.alpha").expect("second");
assert!(
Arc::ptr_eq(&a, &b),
"repeat lookup must hit the cache (same Arc), not rebuild"
);
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn cached_route_matches_freshly_computed() {
let node = build_node_for_test().await;
let service = "svc.beta";
let cached = node.rpc_route_for_service(service).expect("cached");
let expected_request =
ChannelName::new(&format!("{service}.requests")).expect("request name");
let expected_request_id = ChannelId::new(expected_request.clone());
let expected_request_hash = expected_request_id.hash();
let expected_stream_id = MeshNode::publish_stream_id(&expected_request_id);
let self_origin = node.public_key_origin_hash();
let expected_reply =
ChannelName::new(&format!("{service}.replies.{self_origin:016x}")).expect("reply name");
let expected_reply_hash = expected_reply.hash();
assert_eq!(cached.request_channel.as_str(), expected_request.as_str());
assert_eq!(cached.request_channel_hash, expected_request_hash);
assert_eq!(cached.request_stream_id, expected_stream_id);
assert_eq!(cached.reply_channel.as_str(), expected_reply.as_str());
assert_eq!(cached.reply_hash, expected_reply_hash);
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn distinct_services_get_distinct_routes() {
let node = build_node_for_test().await;
let a = node.rpc_route_for_service("svc.one").expect("one");
let b = node.rpc_route_for_service("svc.two").expect("two");
assert_ne!(a.request_channel.as_str(), b.request_channel.as_str());
assert_ne!(a.request_channel_hash, b.request_channel_hash);
assert_ne!(a.reply_channel.as_str(), b.reply_channel.as_str());
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn invalid_service_name_returns_err() {
let node = build_node_for_test().await;
assert!(node.rpc_route_for_service("SVC.Bad").is_err());
assert!(node.rpc_route_cache.get("SVC.Bad").is_none());
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn cache_respects_soft_cap() {
let node = build_node_for_test().await;
let overflow = 64usize;
for i in 0..(RPC_ROUTE_CACHE_SOFT_CAP + overflow) {
let svc = format!("svc.cap.{i:04}");
let route = node
.rpc_route_for_service(&svc)
.expect("build must succeed regardless of cap");
assert_eq!(
route.request_channel.as_str(),
format!("{svc}.requests").as_str()
);
}
assert!(
node.rpc_route_cache.len() <= RPC_ROUTE_CACHE_SOFT_CAP,
"cache size {} exceeds soft cap {}",
node.rpc_route_cache.len(),
RPC_ROUTE_CACHE_SOFT_CAP,
);
let len_before = node.rpc_route_cache.len();
let _ = node.rpc_route_for_service("svc.overflow.new").unwrap();
assert_eq!(
node.rpc_route_cache.len(),
len_before,
"post-cap insert must be a no-op for new services"
);
let first = node.rpc_route_for_service("svc.cap.0000").unwrap();
let second = node.rpc_route_for_service("svc.cap.0000").unwrap();
assert!(Arc::ptr_eq(&first, &second));
}
}
#[cfg(test)]
mod routed_forward_tests {
use super::*;
use bytes::Bytes;
#[test]
fn forward_fast_and_slow_paths_produce_identical_wire_bytes() {
let header = RoutingHeader::new(0xDDDD_EEEE_FFFF_0001, 0xABCD_1234, 7);
let body: &[u8] = b"opaque-encrypted-inner-packet-bytes-not-touched-by-forwarding";
let mut packet = bytes::BytesMut::with_capacity(ROUTING_HEADER_SIZE + body.len());
packet.extend_from_slice(&header.to_bytes());
packet.extend_from_slice(body);
let original = packet.freeze();
let routing_header =
RoutingHeader::from_bytes(&original[..ROUTING_HEADER_SIZE]).expect("header parses");
let mut fwd_header = routing_header;
assert!(fwd_header.forward(), "ttl=7 must be forwardable");
let data_shared = original.clone();
let outstanding_clone = original.clone();
let slow = match data_shared.try_into_mut() {
Ok(_) => panic!("refcount > 1 must take the slow path"),
Err(orig_data) => {
let mut new_data = bytes::BytesMut::with_capacity(orig_data.len());
new_data.extend_from_slice(&fwd_header.to_bytes());
new_data.extend_from_slice(&orig_data[ROUTING_HEADER_SIZE..]);
new_data.freeze()
}
};
drop(outstanding_clone);
let fast = match original.try_into_mut() {
Ok(mut mut_data) => {
fwd_header.write_at(&mut mut_data[..ROUTING_HEADER_SIZE]);
mut_data.freeze()
}
Err(_) => panic!("sole-owned Bytes must take the fast path"),
};
assert_eq!(
fast, slow,
"fast (in-place write_at) and slow (rebuild) forward paths must be wire-identical"
);
let reparsed = RoutingHeader::from_bytes(&fast[..ROUTING_HEADER_SIZE]).unwrap();
assert_eq!(reparsed.ttl, 6, "forward() must decrement ttl");
assert_eq!(reparsed.hop_count, 1, "forward() must increment hop_count");
assert_eq!(reparsed.dest_id, routing_header.dest_id);
assert_eq!(reparsed.src_id, routing_header.src_id);
assert_eq!(
&fast[ROUTING_HEADER_SIZE..],
body,
"forwarding must never touch the encrypted body"
);
}
#[test]
fn split_with_live_parent_takes_slow_path() {
let mut parent = bytes::BytesMut::with_capacity(256);
parent.extend_from_slice(&RoutingHeader::new(1, 2, 3).to_bytes());
parent.extend_from_slice(b"body");
let child: Bytes = parent.split().freeze();
assert!(
child.try_into_mut().is_err(),
"split().freeze() with the parent BytesMut alive must take the slow path — \
if this ever starts succeeding, re-evaluate PERF_AUDIT §2.2/§2.5: the \
in-place forward fast path would then fire on the default ingress too"
);
drop(parent);
}
}
#[cfg(test)]
mod committed_flush_stall_tests {
use super::*;
#[tokio::test(start_paused = true)]
async fn committed_flush_retry_is_bounded_by_stall_budget() {
let cap = Duration::from_millis(200);
let deadline = tokio::time::Instant::now() + COMMITTED_FLUSH_STALL_BUDGET;
let mut delay = Duration::from_millis(5);
let mut iters = 0u32;
loop {
match await_credit_or_stall(&mut delay, cap, deadline).await {
Ok(()) => {
iters += 1;
assert!(iters < 100_000, "retry must be bounded, not spin forever");
}
Err(StreamError::Transport(msg)) => {
assert!(
msg.contains("credit stalled"),
"terminal stall error: {msg}"
);
break;
}
Err(other) => panic!("unexpected error variant: {other}"),
}
}
assert!(iters > 0, "should back off at least once before giving up");
assert!(
iters < 10_000,
"should reach the budget via capped backoff, got {iters}"
);
}
#[tokio::test(start_paused = true)]
async fn await_credit_backs_off_while_under_deadline() {
let cap = Duration::from_millis(200);
let deadline = tokio::time::Instant::now() + Duration::from_secs(60);
let mut delay = Duration::from_millis(5);
match await_credit_or_stall(&mut delay, cap, deadline).await {
Ok(()) => {}
Err(e) => panic!("under deadline must continue (Ok), got {e}"),
}
assert_eq!(delay, Duration::from_millis(10), "backoff must double");
}
}
#[cfg(test)]
mod stream_ack_batching_tests {
use super::*;
use crate::adapter::net::crypto::{NoiseHandshake, PacketCipher, StaticKeypair};
use crate::adapter::net::protocol::NetHeader;
use crate::adapter::net::subprotocol::stream_window::{STREAM_NACK_SIZE, STREAM_RESET_SIZE};
const NACK_EVENTS_PER_PACKET: usize =
protocol::MAX_PAYLOAD_SIZE / (EventFrame::LEN_SIZE + STREAM_NACK_SIZE);
const RESET_EVENTS_PER_PACKET: usize =
protocol::MAX_PAYLOAD_SIZE / (EventFrame::LEN_SIZE + STREAM_RESET_SIZE);
fn make_session_keys() -> (
crate::adapter::net::crypto::SessionKeys,
crate::adapter::net::crypto::SessionKeys,
) {
let psk = [0x42u8; 32];
let responder_kp = StaticKeypair::generate();
let mut initiator = NoiseHandshake::initiator(&psk, &responder_kp.public).unwrap();
let mut responder = NoiseHandshake::responder(&psk, &responder_kp).unwrap();
let msg1 = initiator.write_message(&[]).unwrap();
responder.read_message(&msg1).unwrap();
let msg2 = responder.write_message(&[]).unwrap();
initiator.read_message(&msg2).unwrap();
(
initiator.into_session_keys().unwrap(),
responder.into_session_keys().unwrap(),
)
}
fn session_at(addr: &str) -> Arc<NetSession> {
let (_init, resp) = make_session_keys();
Arc::new(NetSession::new(resp, addr.parse().unwrap(), 4, false))
}
fn pending(session: &Arc<NetSession>, addr: &str, consumed: u64) -> PendingStreamGrant {
PendingStreamGrant {
session: session.clone(),
peer_addr: addr.parse().unwrap(),
total_consumed: consumed,
}
}
#[test]
fn chunk_constants_fit_payload_and_event_caps() {
for (per_packet, event_size) in [
(GRANT_EVENTS_PER_PACKET, STREAM_WINDOW_SIZE),
(NACK_EVENTS_PER_PACKET, STREAM_NACK_SIZE),
(RESET_EVENTS_PER_PACKET, STREAM_RESET_SIZE),
] {
assert!(per_packet > 1, "batching must batch");
let frame = EventFrame::LEN_SIZE + event_size;
assert!(
per_packet * frame <= protocol::MAX_PAYLOAD_SIZE,
"a full chunk must fit the payload budget"
);
assert!(
(per_packet + 1) * frame > protocol::MAX_PAYLOAD_SIZE,
"chunk must fill the packet (one more event would overflow)"
);
assert!(
per_packet <= NetHeader::MAX_EVENTS_PER_PACKET as usize,
"payload budget, not the event-count cap, must bind"
);
}
}
#[test]
fn grouping_is_by_session_not_peer_addr() {
let addr = "127.0.0.1:7001";
let s1 = session_at(addr);
let s2 = session_at(addr);
let mut drained = HashMap::new();
drained.insert((s1.session_id(), 10u64), pending(&s1, addr, 100));
drained.insert((s2.session_id(), 10u64), pending(&s2, addr, 200));
let grouped = group_grants_by_session(drained);
assert_eq!(grouped.len(), 2, "distinct sessions must stay separate");
}
#[test]
fn grouping_collects_all_streams_of_one_session() {
let addr = "127.0.0.1:7002";
let s = session_at(addr);
let mut drained = HashMap::new();
for stream_id in 0..100u64 {
drained.insert(
(s.session_id(), stream_id),
pending(&s, addr, stream_id * 10),
);
}
let grouped = group_grants_by_session(drained);
assert_eq!(grouped.len(), 1, "one session ⇒ one batch");
let (_, peer_addr, grants) = &grouped[&s.session_id()];
assert_eq!(*peer_addr, addr.parse().unwrap());
let mut seen: Vec<(u64, u64)> = grants.clone();
seen.sort_unstable();
assert_eq!(
seen,
(0..100u64).map(|i| (i, i * 10)).collect::<Vec<_>>(),
"every stream survives grouping exactly once with its value"
);
}
#[test]
fn build_session_control_events_does_not_bump_grants_sent() {
let addr = "127.0.0.1:7005";
let s = session_at(addr);
let sid = 42u64;
s.get_or_create_stream_for_packet(sid, true);
let grants = vec![(sid, 1000u64)];
let (entries, _nacks, _acks) = build_session_control_events(&s, &grants, false);
assert_eq!(entries.len(), 1);
let state = s.try_stream(sid).expect("stream exists");
assert_eq!(
state.credit_grants_sent(),
0,
"building the grant event must not count it as sent"
);
state.note_grant_sent();
assert_eq!(state.credit_grants_sent(), 1);
}
#[test]
fn build_session_control_events_pairs_ids_and_gates_gap_events() {
use crate::adapter::net::subprotocol::stream_window::StreamAckRanges;
let addr = "127.0.0.1:7006";
let s = session_at(addr);
s.get_or_create_stream_for_packet(1, true)
.with_reliability(|r| {
assert!(r.on_receive(0));
});
s.get_or_create_stream_for_packet(2, true)
.with_reliability(|r| {
assert!(r.on_receive(0));
assert!(r.on_receive(5));
});
let grants = vec![(1u64, 10u64), (2u64, 20u64)];
let (entries, nacks, acks) = build_session_control_events(&s, &grants, true);
assert_eq!(entries.len(), 2);
for (sid, event) in &entries {
let g = StreamWindow::decode(event).expect("grant event decodes");
assert_eq!(g.stream_id, *sid, "entry id must match its encoded event");
}
assert_eq!(nacks.len(), 1, "only the gapped stream NACKs");
let n = StreamNack::decode(&nacks[0]).expect("nack decodes");
assert_eq!(n.stream_id, 2);
assert_eq!(n.next_expected, 1);
assert_eq!(acks.len(), 1, "only the gapped stream has SACK ranges");
let a = StreamAckRanges::decode(&acks[0]).expect("ack ranges decode");
assert_eq!(a.stream_id, 2);
assert_eq!(a.ack_seq, 1);
assert_eq!(a.ranges, vec![(5, 6)]);
let (_, _, acks_off) = build_session_control_events(&s, &grants, false);
assert!(acks_off.is_empty(), "no ranges to a non-advertising peer");
}
#[test]
fn ack_ranges_cache_sweep_drops_only_stale_entries() {
let cache: DashMap<u64, (bool, Instant)> = DashMap::new();
let stale_at = Instant::now() - ACK_RANGES_CAP_CACHE_MAX_AGE - Duration::from_secs(1);
cache.insert(1, (true, stale_at));
cache.insert(2, (false, stale_at));
cache.insert(3, (true, Instant::now()));
sweep_ack_ranges_cache(&cache, ACK_RANGES_CAP_CACHE_MAX_AGE);
assert!(!cache.contains_key(&1), "stale positive entry swept");
assert!(!cache.contains_key(&2), "stale negative entry swept");
assert!(
cache.contains_key(&3),
"fresh entry survives — active peers keep their cached verdict"
);
}
#[test]
fn ack_ranges_gate_reresolves_after_announcement_invalidation() {
use crate::adapter::net::behavior::capability::{CapabilityAnnouncement, CapabilitySet};
use crate::adapter::net::behavior::fold::capability::CapabilityFold;
use crate::adapter::net::behavior::fold::capability_bridge::translate_announcement;
use crate::adapter::net::behavior::fold::Fold;
let cache: DashMap<u64, (bool, Instant)> = DashMap::new();
let session_id_to_node: DashMap<u64, u64> = DashMap::new();
let fold: Fold<CapabilityFold> = Fold::new();
let kp = crate::adapter::net::EntityKeypair::generate();
let node_id = kp.node_id();
let session_id = 0x5E5510_u64; session_id_to_node.insert(session_id, node_id);
assert!(!peer_supports_ack_ranges(
&cache,
&session_id_to_node,
&fold,
session_id
));
assert_eq!(cache.get(&node_id).map(|e| e.value().0), Some(false));
let caps = CapabilitySet::new().add_tag(ACK_RANGES_CAPABILITY_TAG.to_string());
let ann = CapabilityAnnouncement::new(node_id, kp.entity_id().clone(), 1, caps);
fold.apply(translate_announcement(&ann))
.expect("fold apply");
assert!(
!peer_supports_ack_ranges(&cache, &session_id_to_node, &fold, session_id),
"cached false shadows the freshly-folded capability"
);
cache.remove(&node_id);
assert!(
peer_supports_ack_ranges(&cache, &session_id_to_node, &fold, session_id),
"after invalidation the gate must see the announced capability"
);
}
#[test]
fn no_advertised_capability_keeps_sender_on_legacy_path() {
use crate::adapter::net::behavior::fold::capability::CapabilityFold;
use crate::adapter::net::behavior::fold::Fold;
let cache: DashMap<u64, (bool, Instant)> = DashMap::new();
let session_id_to_node: DashMap<u64, u64> = DashMap::new();
let fold: Fold<CapabilityFold> = Fold::new();
let session = session_at("127.0.0.1:7011");
session_id_to_node.insert(session.session_id(), 0xBEEF_u64);
let supported =
peer_supports_ack_ranges(&cache, &session_id_to_node, &fold, session.session_id());
assert!(
!supported,
"no advertisement ⇒ legacy path even with local support enabled"
);
session
.get_or_create_stream_for_packet(3, true)
.with_reliability(|r| {
assert!(r.on_receive(0));
assert!(r.on_receive(5));
});
let (grants, nacks, acks) =
build_session_control_events(&session, &[(3u64, 10u64)], supported);
assert_eq!(grants.len(), 1, "grant still emitted");
assert_eq!(nacks.len(), 1, "legacy NACK still emitted for the gap");
assert!(
acks.is_empty(),
"no StreamAckRanges to a peer that has not advertised support"
);
}
#[test]
fn grouping_keeps_peers_separate() {
let a1 = "127.0.0.1:7003";
let a2 = "127.0.0.1:7004";
let s1 = session_at(a1);
let s2 = session_at(a2);
let mut drained = HashMap::new();
drained.insert((s1.session_id(), 1u64), pending(&s1, a1, 11));
drained.insert((s2.session_id(), 1u64), pending(&s2, a2, 22));
let grouped = group_grants_by_session(drained);
assert_eq!(grouped.len(), 2);
assert_eq!(grouped[&s1.session_id()].1, a1.parse().unwrap());
assert_eq!(grouped[&s2.session_id()].1, a2.parse().unwrap());
}
fn roundtrip_full_chunk(payloads: Vec<Vec<u8>>, subprotocol_id: u16) -> Vec<Bytes> {
let key = [0x5Au8; 32];
let session_id = 0xACE0_FACEu64;
let mut builder = PacketBuilder::new(&key, session_id);
let events: Vec<Bytes> = payloads.iter().map(|p| Bytes::copy_from_slice(p)).collect();
let pkt = builder.build_subprotocol(
CONTROL_STREAM_ID,
1,
&events,
PacketFlags::NONE,
subprotocol_id,
);
assert!(
pkt.len() <= protocol::MAX_PACKET_SIZE,
"full chunk must fit one wire packet ({} > {})",
pkt.len(),
protocol::MAX_PACKET_SIZE
);
let header = NetHeader::from_bytes(&pkt[..HEADER_SIZE]).expect("header parses");
assert_eq!(header.event_count as usize, events.len());
assert_eq!(header.subprotocol_id, subprotocol_id);
let rx = PacketCipher::new(&key, session_id);
let nonce = u64::from_le_bytes(pkt[16..24].try_into().unwrap());
let aad = header.aad();
let mut buf = bytes::BytesMut::from(&pkt[HEADER_SIZE..]);
let n = rx
.decrypt_in_place(nonce, &aad, &mut buf[..])
.expect("decrypt succeeds");
EventFrame::read_events(buf.split_to(n).freeze(), header.event_count)
}
#[test]
fn full_grant_chunk_roundtrips_as_one_packet() {
let grants: Vec<StreamWindow> = (0..GRANT_EVENTS_PER_PACKET as u64)
.map(|i| StreamWindow {
stream_id: i,
total_consumed: i * 7,
ack_seq: i * 3,
})
.collect();
let recovered = roundtrip_full_chunk(
grants.iter().map(|g| g.encode().to_vec()).collect(),
SUBPROTOCOL_STREAM_WINDOW,
);
assert_eq!(
recovered.len(),
grants.len(),
"every grant survives framing"
);
for (event, g) in recovered.iter().zip(&grants) {
assert_eq!(StreamWindow::decode(event).unwrap(), *g);
}
}
#[test]
fn full_nack_chunk_roundtrips_as_one_packet() {
let nacks: Vec<StreamNack> = (0..NACK_EVENTS_PER_PACKET as u64)
.map(|i| StreamNack {
stream_id: i,
next_expected: i + 1,
missing_bitmap: i | 1,
})
.collect();
let recovered = roundtrip_full_chunk(
nacks.iter().map(|n| n.encode().to_vec()).collect(),
SUBPROTOCOL_STREAM_NACK,
);
assert_eq!(recovered.len(), nacks.len());
for (event, n) in recovered.iter().zip(&nacks) {
assert_eq!(StreamNack::decode(event).unwrap(), *n);
}
}
#[test]
fn full_reset_chunk_roundtrips_as_one_packet() {
let resets: Vec<StreamReset> = (0..RESET_EVENTS_PER_PACKET as u64)
.map(|i| StreamReset { stream_id: i })
.collect();
let recovered = roundtrip_full_chunk(
resets.iter().map(|r| r.encode().to_vec()).collect(),
SUBPROTOCOL_STREAM_RESET,
);
assert_eq!(recovered.len(), resets.len());
for (event, r) in recovered.iter().zip(&resets) {
assert_eq!(StreamReset::decode(event).unwrap(), *r);
}
}
#[test]
fn oversized_grant_batch_spills_without_loss() {
let total = GRANT_EVENTS_PER_PACKET * 2 + 122;
let events: Vec<Bytes> = (0..total as u64)
.map(|i| {
Bytes::copy_from_slice(
&StreamWindow {
stream_id: i,
total_consumed: i,
ack_seq: 0,
}
.encode(),
)
})
.collect();
let chunks: Vec<&[Bytes]> = events.chunks(GRANT_EVENTS_PER_PACKET).collect();
assert_eq!(chunks.len(), 3, "ceil(2N+122 / N) = 3 packets");
assert_eq!(
chunks.iter().map(|c| c.len()).sum::<usize>(),
total,
"no grant dropped by the spill"
);
for c in &chunks {
assert!(EventFrame::calculate_size(c) <= protocol::MAX_PAYLOAD_SIZE);
}
}
#[test]
fn pack_control_events_fills_by_size_not_worst_case() {
let events: Vec<Bytes> = (0..200u64)
.map(|i| {
Bytes::from(
StreamAckRanges {
stream_id: i,
ack_seq: 0,
ranges: vec![(10 * i + 1, 10 * i + 2)],
}
.encode(),
)
})
.collect();
let ranges = pack_control_events(&events);
let covered: usize = ranges.iter().map(|r| r.len()).sum();
assert_eq!(covered, events.len(), "no event dropped by packing");
let mut next = 0usize;
for r in &ranges {
assert_eq!(r.start, next, "chunks are contiguous and in order");
next = r.end;
}
for r in &ranges {
assert!(
EventFrame::calculate_size(&events[r.clone()]) <= protocol::MAX_PAYLOAD_SIZE,
"a packed chunk must fit the payload budget"
);
}
let worst_case_packets = events.len().div_ceil(
protocol::MAX_PAYLOAD_SIZE / (EventFrame::LEN_SIZE + StreamAckRanges::MAX_SIZE),
);
assert!(
ranges.len() < worst_case_packets,
"size packing ({} packets) must beat worst-case count ({})",
ranges.len(),
worst_case_packets
);
}
#[test]
fn pack_control_events_lone_oversized_event_ships_alone() {
let big = Bytes::from(vec![0u8; protocol::MAX_PAYLOAD_SIZE + 10]);
let small = Bytes::from_static(b"x");
let events = vec![small.clone(), big, small];
let ranges = pack_control_events(&events);
assert_eq!(ranges.len(), 3);
assert!(ranges.iter().all(|r| !r.is_empty()));
}
}