use std::net::SocketAddr;
use std::time::Instant;
use crate::constants;
use crate::core::endpoint::handshake as framing;
use crate::core::{
ConnectionId, EndpointOutput, EstablishedSession, Install, Role, Timestamp, Transmit,
};
use crate::error::ConnectError;
use crate::identity::{Identity, PublicKeyOf};
use crate::packet::{Handshake, Mac1Key};
use super::Endpoint;
use super::guard::{ChainPin, PinKind};
use super::intro_queue::{Arrival, IntroEntry};
use super::staged::{ChainState, IntroId, MidState};
use super::tables::StaticState;
enum EagerRead<I: Identity> {
Read {
claimed: PublicKeyOf<I>,
mid: Box<MidState<I>>,
},
Local,
Malformed,
}
impl<I: Identity> Endpoint<I> {
pub(super) fn route_initiation(
&mut self,
now: Instant,
src: SocketAddr,
sender_index: u32,
msg1: &[u8],
) {
if !self.is_hinted(src) {
self.park_initiation(now, src, sender_index, msg1);
return;
}
match self.eager_read(msg1) {
EagerRead::Local => self.park_initiation(now, src, sender_index, msg1),
EagerRead::Malformed => {}
EagerRead::Read { claimed, mid } => {
match self.pending_outbound_remote(claimed.as_ref()) {
Some(conn) => {
self.internal_tiebreak(now, src, sender_index, conn, claimed, *mid);
}
None => self.demote(now, src, sender_index, msg1, claimed, mid),
}
}
}
}
fn is_hinted(&self, src: SocketAddr) -> bool {
self.statics.hints().any(|hint| hint == src)
}
pub(super) fn pending_outbound_remote(&self, peer_static: &[u8]) -> Option<ConnectionId> {
self.statics
.get(peer_static)
.filter(|entry| entry.state == StaticState::Pending)
.map(|entry| entry.conn)
}
fn eager_read(&mut self, msg1: &[u8]) -> EagerRead<I> {
let (provider, our_key) = match self.identity.open() {
Ok(opened) => opened,
Err(error) => {
tracing::warn!(
target: "slither::io",
verb = "handle_datagram",
stage = "Identity::open",
%error,
"the identity provider failed to open"
);
return EagerRead::Local;
}
};
let responder =
match <I::Suite as Handshake>::responder(provider, constants::PROLOGUE, our_key) {
Ok(responder) => responder,
Err(error) => {
tracing::warn!(
target: "slither::io",
verb = "handle_datagram",
stage = "Handshake::responder",
%error,
"the responder machine would not build on our static"
);
return EagerRead::Local;
}
};
match <I::Suite as Handshake>::read_msg1_intro(responder, msg1) {
Ok((claimed, mid)) => EagerRead::Read {
claimed,
mid: Box::new(mid),
},
Err(_) => EagerRead::Malformed,
}
}
fn demote(
&mut self,
now: Instant,
src: SocketAddr,
sender_index: u32,
msg1: &[u8],
claimed: PublicKeyOf<I>,
mid: Box<MidState<I>>,
) {
let outcome = self.intros.arrive(now, src, sender_index, msg1);
if let Some(evicted) = outcome.evicted {
self.release_evicted_chain(now, src, evicted);
}
let id = match outcome.arrival {
Arrival::Parked(id) => {
self.emit(EndpointOutput::IntroReady(id, src));
id
}
Arrival::Refreshed(id) => id,
Arrival::Dropped => return,
};
let key = claimed.as_ref().to_vec();
let pinned = self.guard.pin(&key, PinKind::Claimed);
self.intros.consume(id);
let entry = self
.intros
.get_mut(id)
.expect("the entry was parked or refreshed immediately above");
entry.state = ChainState::Claimed { mid, claimed };
if pinned {
entry.guard_pin = Some(ChainPin {
key,
kind: PinKind::Claimed,
});
}
}
pub(super) fn intercept_parked_intro(&mut self, now: Instant, id: IntroId, dial: ConnectionId) {
let Some(entry) = self.intros.remove(id) else {
debug_assert!(false, "the chain was present when `read_identity` drove it");
return;
};
let IntroEntry {
src,
sender_index,
state,
guard_undo,
guard_pin,
..
} = entry;
let ChainState::Claimed { mid, claimed } = state else {
debug_assert!(
false,
"§6.5 step 4 runs on the chain `read_identity` has just driven to `Claimed`"
);
return;
};
debug_assert!(
guard_undo.is_none(),
"§17.1: a chain intercepted at stage 1 has no provisional record"
);
self.internal_tiebreak(now, src, sender_index, dial, claimed, *mid);
self.release_chain_guard_state(now, guard_undo, guard_pin);
}
fn internal_tiebreak(
&mut self,
now: Instant,
src: SocketAddr,
peer_index: u32,
conn: ConnectionId,
claimed: PublicKeyOf<I>,
mid: MidState<I>,
) {
let Ok((payload, read)) = <I::Suite as Handshake>::complete(mid) else {
tracing::debug!(
target: "slither::policy",
event = "tiebreak_tag_death",
%src,
"an initiation claiming a pending static failed msg1's tail tag"
);
return;
};
let timestamp = Timestamp::decode(&payload);
let peer_static = claimed.as_ref().to_vec();
if !self.guard.admits(&peer_static, timestamp) {
tracing::debug!(
target: "slither::policy",
event = "tiebreak_replay",
%src,
"an authenticated initiation for a pending static failed the timestamp guard"
);
return;
}
if self.wins_tiebreak(&peer_static) {
self.record_tiebreak_timestamp(now, conn, &peer_static, timestamp);
tracing::debug!(
target: "slither::policy",
event = "tiebreak_won",
%src,
"our static is the smaller: the crossing initiation is dropped and recorded"
);
return;
}
let Ok((msg2, transport)) = <I::Suite as Handshake>::write_msg2(read) else {
tracing::debug!(
target: "slither::policy",
event = "tiebreak_msg2_failed",
%src,
"msg2 would not write on a lost tie-break; the pending is untouched"
);
return;
};
self.record_tiebreak_timestamp(now, conn, &peer_static, timestamp);
let Some(mut pending) = self.pendings.remove(&conn) else {
debug_assert!(false, "the pending was read out of the static map above");
return;
};
if let Some(index) = pending.sender_index.take() {
self.indices.remove_pending(index);
}
pending.state = None;
drop(pending);
let our_index = self.indices.mint(&mut self.rng);
let (seal, open) = <I::Suite as Handshake>::into_datagram(transport, self.epoch_size());
let peer_mac1 = Mac1Key::derive(&peer_static);
let data = framing::frame_resp(our_index, peer_index, &msg2, &peer_mac1);
self.indices.insert_session(our_index, conn);
self.statics.promote(&peer_static, Some(timestamp));
tracing::debug!(
target: "slither::policy",
event = "tiebreak_admitted",
%src,
"the peer's static is the smaller: our pending is cancelled and we install as responder"
);
self.emit(EndpointOutput::Transmit(Transmit { to: src, data }));
self.emit(EndpointOutput::ToConnection(
conn,
Install {
session: EstablishedSession {
seal,
open,
our_index,
peer_index,
anchor: src,
},
role: Role::Responder,
anchor_from_msg1: true,
},
));
}
pub(super) fn wins_tiebreak(&self, peer_static: &[u8]) -> bool {
self.our_static() < peer_static
}
pub(super) fn record_tiebreak_timestamp(
&mut self,
now: Instant,
conn: ConnectionId,
peer_static: &[u8],
timestamp: Timestamp,
) {
let _ = self.guard.record(peer_static, timestamp, now);
self.statics.arm_guard_exemption(peer_static);
if let Some(pending) = self.pendings.get_mut(&conn)
&& !pending.guard_pinned
{
pending.guard_pinned = self.guard.pin(peer_static, PinKind::KeyHolder);
}
}
pub(super) fn extend_guard_exemption(&mut self, now: Instant, peer_static: &[u8]) {
self.guard
.extend_exemption(peer_static, now + constants::HANDSHAKE_GIVEUP);
}
pub(super) fn cancel_pending_losing_tiebreak(&mut self, now: Instant, dial: ConnectionId) {
self.drop_pending(now, dial);
self.emit(EndpointOutput::HandshakeFailed(
dial,
ConnectError::AlreadyConnected,
));
}
}
#[cfg(test)]
mod tests {
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use std::time::Duration;
use super::*;
use crate::config::Config;
use crate::core::endpoint::staged::IntroId;
use crate::core::{Connection, Disposition};
use crate::error::{AcceptError, AuthError, IntroError};
use crate::identity::Identity;
use crate::packet::ReferenceSuite;
use crate::testutil::{CountingIdentity, DhCounter};
type Suite = ReferenceSuite;
type Id = CountingIdentity<Suite>;
type Pk = PublicKeyOf<Id>;
fn addr(last: u8, port: u16) -> SocketAddr {
SocketAddr::new(IpAddr::V4(Ipv4Addr::new(10, 0, 0, last)), port)
}
fn t0() -> Instant {
Instant::now()
}
fn init_index(datagram: &[u8]) -> u32 {
match crate::packet::classify::<Suite>(datagram) {
Some(crate::packet::Inbound::Init { header, .. }) => header.sender_index,
_ => panic!("not a framed initiation"),
}
}
fn init_msg1(datagram: &[u8]) -> Vec<u8> {
match crate::packet::classify::<Suite>(datagram) {
Some(crate::packet::Inbound::Init { msg1, .. }) => msg1.to_vec(),
_ => panic!("not a framed initiation"),
}
}
#[derive(Default)]
struct Drained {
transmits: Vec<Transmit>,
intros: Vec<(IntroId, SocketAddr)>,
installs: Vec<ConnectionId>,
failed: Vec<(ConnectionId, ConnectError)>,
replaced: Vec<ConnectionId>,
contested: Vec<ConnectionId>,
deadline: Option<Instant>,
}
struct Node {
ep: Endpoint<Id>,
dhs: DhCounter,
pk: Pk,
addr: SocketAddr,
}
impl Node {
fn new(now: Instant, key_seed: u8, rng_seed: u8, at: SocketAddr) -> Self {
let identity: Id = CountingIdentity::seeded([key_seed; 32]);
let dhs = identity.counter();
let pk = *identity.public_static();
let ep = Endpoint::new(now, Config::default(), identity, [rng_seed; 32]);
Node {
ep,
dhs,
pk,
addr: at,
}
}
fn canonical(&self) -> &[u8] {
self.pk.as_ref()
}
fn drain(&mut self) -> Drained {
let mut d = Drained::default();
for _ in 0..10_000 {
match self.ep.poll_output() {
EndpointOutput::Timeout(deadline) => {
d.deadline = deadline;
return d;
}
EndpointOutput::Transmit(t) => d.transmits.push(t),
EndpointOutput::IntroReady(id, src) => d.intros.push((id, src)),
EndpointOutput::ToConnection(id, _) => d.installs.push(id),
EndpointOutput::HandshakeFailed(id, why) => d.failed.push((id, why)),
EndpointOutput::Replaced(id) => d.replaced.push(id),
EndpointOutput::Contested(id) => d.contested.push(id),
}
}
panic!("poll_output() never reached Timeout (§16.4)");
}
fn feed(&mut self, now: Instant, src: SocketAddr, datagram: &[u8]) -> Drained {
let disposition = self.ep.handle_datagram(now, src, datagram);
assert!(
matches!(disposition, Disposition::Done),
"an initiation is always the endpoint's own"
);
self.drain()
}
fn timeout(&mut self, now: Instant) -> Drained {
self.ep.handle_timeout(now);
self.drain()
}
fn dial(&mut self, now: Instant, to: SocketAddr, peer: &Pk) -> (ConnectionId, Drained) {
let (id, _connection) = self
.ep
.mint_pending(now, to, *peer)
.expect("the static is NONE");
self.ep.start_attempt(now, id);
(id, self.drain())
}
}
fn lone_msg1(from: &mut Node, now: Instant, to: &Node) -> Vec<u8> {
let (conn, drained) = from.dial(now, to.addr, &to.pk);
let datagram = drained.transmits[0].data.clone();
let our_index = init_index(&datagram);
from.ep
.handle_connection_event(now, conn, crate::core::ToEndpoint::Retired { our_index });
let _ = from.drain();
datagram
}
fn ordered_pair(now: Instant) -> (Node, Node) {
let x = Node::new(now, 7, 0x11, addr(1, 4001));
let y = Node::new(now, 9, 0x22, addr(2, 4002));
if x.canonical() < y.canonical() {
(x, y)
} else {
(y, x)
}
}
#[test]
fn an_unhinted_initiation_parks_at_zero_dh() {
let t = t0();
let (mut a, mut b) = ordered_pair(t);
let msg1 = lone_msg1(&mut b, t, &a);
a.dhs.reset();
let drained = a.feed(t, b.addr, &msg1);
assert_eq!(
drained.intros.len(),
1,
"§6.5 step 2: it parks and surfaces"
);
assert_eq!(a.dhs.get(), 0, "§6.1: a parked introduction costs 0 DH");
}
#[test]
fn an_established_connection_contributes_no_hint() {
let t = t0();
let (mut a, mut b) = ordered_pair(t);
let (_dial, out) = a.dial(t, b.addr, &b.pk);
let msg1 = out.transmits[0].data.clone();
let resp = {
let drained = b.feed(t, a.addr, &msg1);
let (id, _src) = drained.intros[0];
b.ep.authenticate(t, id).expect("a real msg1 authenticates");
let (_conn, _c) = b.ep.accept(t, id).expect("B has no row for A");
b.drain().transmits[0].data.clone()
};
let _ = a.feed(t, b.addr, &resp);
assert!(a.ep.hints().is_empty(), "§17.4: a LIVE row hints nothing");
let mut c = Node::new(t, 13, 0x33, addr(3, 4003));
let msg1_again = lone_msg1(&mut c, t, &a);
a.dhs.reset();
let drained = a.feed(t, b.addr, &msg1_again);
assert_eq!(
drained.intros.len(),
1,
"§6.5: LIVE goes to the staged path"
);
assert_eq!(
a.dhs.get(),
0,
"no eager read fired for an established peer"
);
}
#[test]
fn a_demoted_initiation_carries_its_paid_mid_state() {
let t = t0();
let (mut a, b) = ordered_pair(t);
let mut c = Node::new(t, 13, 0x33, addr(3, 4003));
let _ = a.dial(t, b.addr, &b.pk);
let from_c = lone_msg1(&mut c, t, &a);
a.dhs.reset();
let drained = a.feed(t, b.addr, &from_c);
assert_eq!(drained.intros.len(), 1, "it still surfaces as an `Intro`");
assert_eq!(a.dhs.get(), 1, "§6.5 step 3: the eager read is one `es`");
let (id, _src) = drained.intros[0];
let claimed = a.ep.read_identity(t, id).expect("the claim is cached");
assert_eq!(
claimed.as_ref(),
c.canonical(),
"the demoted entry is tagged with the claim the eager read produced"
);
assert_eq!(
a.dhs.get(),
1,
"§6.5: `read_identity()` on a demoted entry is **0 incremental DH**"
);
a.ep.authenticate(t, id).expect("a real msg1 authenticates");
assert_eq!(a.dhs.get(), 2, "§6.1: `authenticate()` is 2 DH cumulative");
a.ep.accept(t, id).expect("C's static is NONE");
assert_eq!(a.dhs.get(), 4, "§6.1: `accept()` is 4 DH cumulative");
}
#[test]
fn a_forgery_cannot_cancel_a_pending() {
let t = t0();
let (mut small, mut large) = ordered_pair(t);
let (dial, out) = large.dial(t, small.addr, &small.pk);
assert_eq!(out.transmits.len(), 1, "the dial put one msg1 on the wire");
let mut forged = lone_msg1(&mut small, t, &large);
let index = init_index(&forged);
let tail = forged.len() - crate::constants::MAC1_LEN - 1;
forged[tail] ^= 0xff;
let framed = framing::frame_init(
index,
&init_msg1(&forged),
&Mac1Key::derive(large.canonical()),
);
large.dhs.reset();
let drained = large.feed(t, small.addr, &framed);
assert_eq!(
large.dhs.get(),
2,
"§6.6: the forgery reached step 1 and died there — `es` then `ss`, no msg2"
);
assert!(
drained.transmits.is_empty() && drained.installs.is_empty(),
"§6.6 step 1: a tag death writes no msg2 and installs nothing"
);
assert!(
drained.intros.is_empty(),
"§6.5: the packet never touches the accept queue"
);
assert_eq!(
large.ep.greatest(small.canonical()),
None,
"§6.6: a step-1 failure records nothing"
);
let later = t
+ crate::constants::RETRANSMIT_BASE
+ crate::constants::RETRANSMIT_JITTER_MAX
+ Duration::from_millis(1);
let after = large.timeout(later);
assert_eq!(
after.transmits.len(),
1,
"§6.7: a forgery cannot cancel a pending — the dial is still retransmitting"
);
assert!(
after.failed.is_empty(),
"and it was not failed either: {:?}",
after.failed
);
let _ = dial;
}
#[test]
fn the_tiebreak_winner_drops_the_inbound_and_records_it() {
let t = t0();
let (mut small, mut large) = ordered_pair(t);
let (dial, _) = small.dial(t, large.addr, &large.pk);
let crossing = lone_msg1(&mut large, t, &small);
small.dhs.reset();
let drained = small.feed(t, large.addr, &crossing);
assert!(
drained.intros.is_empty() && drained.transmits.is_empty(),
"§6.5: the application never sees it, and §6.6 step 3 writes no msg2"
);
assert!(
drained.installs.is_empty() && drained.failed.is_empty(),
"the winner's own outbound is untouched"
);
assert_eq!(
small.dhs.get(),
2,
"§6.6: `es` then `ss`, and no `ee`/`se` on the winner side"
);
assert!(
small.ep.greatest(large.canonical()).is_some(),
"§6.7: the winner **records** the loser's timestamp"
);
assert_eq!(
small.ep.replacement_basis(large.canonical()),
Some(None),
"§17.4: the winner is the initiator, so its basis stays `None`"
);
assert!(
small.ep.guard_pins(large.canonical()) > 0,
"§17.1: the in-flight outbound pending pins the entry the record just created"
);
let _ = dial;
}
#[test]
fn a_replayed_initiation_dies_at_the_guard() {
let t = t0();
let (mut small, mut large) = ordered_pair(t);
let (_dial, _) = large.dial(t, small.addr, &small.pk);
let crossing = lone_msg1(&mut small, t, &large);
let first = large.feed(t, small.addr, &crossing);
assert_eq!(first.installs.len(), 1, "§6.6 step 4 admitted the first");
let recorded = large
.ep
.greatest(small.canonical())
.expect("the admission recorded");
let replay = large.feed(t, small.addr, &crossing);
if let Some((id, _)) = replay.intros.first() {
assert!(
matches!(large.ep.authenticate(t, *id), Err(AuthError::Replay)),
"§17.1: the same initiation is no longer strictly greater"
);
}
assert_eq!(
large.ep.greatest(small.canonical()),
Some(recorded),
"a failed check writes nothing (§17.1 mitigation (iii))"
);
}
#[test]
fn the_tiebreak_loser_cancels_its_pending_and_installs_as_responder() {
let t = t0();
let (mut small, mut large) = ordered_pair(t);
let (dial, _) = large.dial(t, small.addr, &small.pk);
let crossing = lone_msg1(&mut small, t, &large);
large.dhs.reset();
let drained = large.feed(t, small.addr, &crossing);
assert_eq!(large.dhs.get(), 4, "§6.6: `es`, `ss`, then `ee` and `se`");
assert_eq!(drained.transmits.len(), 1, "§6.6 step 4 writes msg2");
assert_eq!(
drained.transmits[0].to, small.addr,
"§5.6: the responder anchors at the msg1 source"
);
assert_eq!(
drained.installs,
vec![dial],
"§6.7: the **dial's own** connection is completed by the tie-break's Install"
);
assert!(
drained.failed.is_empty(),
"no give-up, no error (§6.7): {:?}",
drained.failed
);
assert!(
drained.intros.is_empty(),
"§6.5: the application never sees the packet"
);
assert!(
matches!(large.ep.replacement_basis(small.canonical()), Some(Some(_))),
"§17.4: the tie-break loser's admit step sets the basis to `Some(t)`"
);
assert!(
large.ep.hints().is_empty(),
"§17.4: the row is LIVE now, and a LIVE row hints nothing"
);
assert!(
matches!(
large.ep.mint_pending(t, small.addr, small.pk),
Err(ConnectError::AlreadyConnected)
),
"§16.1: one session per static, and the row was promoted rather than doubled"
);
}
#[test]
fn an_intercepted_intro_leaves_no_stage_zero_entry() {
for local_wins in [true, false] {
let t = t0();
let (small, large) = ordered_pair(t);
let (mut local, mut peer) = if local_wins {
(small, large)
} else {
(large, small)
};
let msg1 = lone_msg1(&mut peer, t, &local);
let elsewhere = addr(9, 4009);
let drained = local.feed(t, elsewhere, &msg1);
let (id, _src) = drained.intros[0];
assert_eq!(
local.ep.intro_source(id),
Some(elsewhere),
"precondition: the entry is parked and observable"
);
let (_dial, _) = local.dial(t, peer.addr, &peer.pk);
assert!(
matches!(local.ep.read_identity(t, id), Err(IntroError::Internal)),
"§6.5 step 4 intercepts (local_wins = {local_wins})"
);
let _ = local.drain();
assert_eq!(
local.ep.intro_source(id),
None,
"§6.5 step 4: the entry is removed, so the source's stage-0 slot \
is returned exactly as the eager route never took one \
(local_wins = {local_wins})"
);
assert!(
matches!(local.ep.authenticate(t, id), Err(AuthError::Expired)),
"and the staged verbs say so"
);
}
}
#[test]
fn the_pending_branch_winner_refuses_and_keeps_its_record() {
let t = t0();
let (mut small, mut large) = ordered_pair(t);
let from_large = lone_msg1(&mut large, t, &small);
let elsewhere = addr(9, 4009);
let drained = small.feed(t, elsewhere, &from_large);
let (id, _src) = drained.intros[0];
let claimed = small.ep.read_identity(t, id).expect("readable");
assert_eq!(claimed.as_ref(), large.canonical());
let (_peer, timestamp) = small.ep.authenticate(t, id).expect("authenticates");
let (dial, _) = small.dial(t, large.addr, &large.pk);
let refused = small.ep.accept(t, id);
let after = small.drain();
assert!(
matches!(refused, Err(AcceptError::Stale)),
"§6.4: our static is smaller, so we are the tie-break winner: {:?}",
refused.map(|_| "Ok")
);
assert!(
after.failed.is_empty(),
"§6.4: the pending is **left in place** — {:?}",
after.failed
);
assert_eq!(
small.ep.greatest(large.canonical()),
Some(timestamp),
"§17.1's one exception: this `Stale` KEEPS the candidate's timestamp"
);
assert!(
small.ep.guard_pins(large.canonical()) > 0,
"§17.1: the chain's pin went with the chain, but the dial still pins the entry"
);
let _ = dial;
}
#[test]
fn a_dialled_live_rows_stale_reverts_its_record_and_marks_contested() {
let t = t0();
let (small, mut large) = ordered_pair(t);
let mut third = Node::new(t, 21, 0x44, addr(4, 4004));
let captured = lone_msg1(&mut third, t, &large);
let (dialled, out) = large.dial(t, third.addr, &third.pk);
let msg1 = out.transmits[0].data.clone();
let resp = {
let drained = third.feed(t, large.addr, &msg1);
let (id, _src) = drained.intros[0];
third
.ep
.authenticate(t, id)
.expect("a real msg1 authenticates");
let (_conn, _c) = third.ep.accept(t, id).expect("third has no row for large");
third.drain().transmits[0].data.clone()
};
let _ = large.feed(t, third.addr, &resp);
assert!(
large.ep.greatest(third.canonical()).is_none(),
"§17.1: a dial writes no record at all — the guard bars nothing here"
);
let drained = large.feed(t, addr(8, 4010), &captured);
let (id2, _) = drained.intros[0];
let (_pk, captured_ts) = large.ep.authenticate(t, id2).expect("authenticates");
assert_eq!(
large.ep.greatest(third.canonical()),
Some(captured_ts),
"`authenticate()` writes provisionally, and that is what must be reverted"
);
assert!(
matches!(large.ep.accept(t, id2), Err(AcceptError::Stale)),
"§6.4: a `None` basis refuses every candidate, however new"
);
assert_eq!(
large.ep.greatest(third.canonical()),
None,
"§17.1 mitigation (i): a non-winner `Stale` REVERTS its provisional record"
);
let after = large.drain();
assert_eq!(
after.contested,
vec![dialled],
"§7.5's probe is asked of the connection the refusal was about"
);
assert!(
after.replaced.is_empty(),
"a refusal replaces nothing: {:?}",
after.replaced
);
let again = large.feed(t, addr(8, 4011), &captured);
let (id3, _) = again.intros[0];
large
.ep
.authenticate(t, id3)
.expect("the same bytes authenticate a second time");
assert_eq!(
large.ep.greatest(third.canonical()),
Some(captured_ts),
"the second surfacing wrote the same provisional record as the first"
);
assert!(
matches!(large.ep.accept(t, id3), Err(AcceptError::Stale)),
"§6.4: the `None` basis is not spent by having refused once"
);
assert_eq!(
large.ep.greatest(third.canonical()),
None,
"§17.1 mitigation (i) applies to the second refusal exactly as to the first"
);
let after2 = large.drain();
assert_eq!(
after2.contested,
vec![dialled],
"ruling 36: the endpoint signals every refusal of an admitted \
candidate; ruling 41's *second refusal is a no-op* is the \
connection core's, and this is the signal it no-ops on"
);
assert!(
after2.failed.is_empty() && after2.replaced.is_empty(),
"the live connection must be untouched every time, because the \
basis is `None`: {:?} / {:?}",
after2.failed,
after2.replaced
);
let _ = small;
}
#[test]
fn the_pending_branch_loser_cancels_its_dial_and_installs() {
let t = t0();
let (mut small, mut large) = ordered_pair(t);
let from_small = lone_msg1(&mut small, t, &large);
let drained = large.feed(t, addr(9, 4009), &from_small);
let (id, _src) = drained.intros[0];
large.ep.authenticate(t, id).expect("authenticates");
let (dial, _) = large.dial(t, small.addr, &small.pk);
let accepted = large.ep.accept(t, id);
let after = large.drain();
let (conn, _connection): (ConnectionId, Connection<Suite>) = accepted
.expect("§6.4: the peer's static is smaller, so we lose and install as responder");
assert_ne!(
conn, dial,
"§6.4: this route installs a **fresh** connection; the dial is cancelled"
);
assert_eq!(
after.failed,
vec![(dial, ConnectError::AlreadyConnected)],
"§6.4: the cancelled pending's `Connecting` resolves `AlreadyConnected`"
);
assert_eq!(after.transmits.len(), 1, "the accept wrote msg2");
assert!(
matches!(large.ep.replacement_basis(small.canonical()), Some(Some(_))),
"§17.4: we responded, so the basis is `Some(t)`"
);
assert!(
large.ep.hints().is_empty(),
"the dial is gone: §16.1 leaves exactly one connection for this static"
);
}
#[test]
fn the_ordinary_api_ordering_completes_in_both_key_orders() {
for chain_holder_is_smaller in [true, false] {
let t = t0();
let (small, large) = ordered_pair(t);
let (mut a, mut b) = if chain_holder_is_smaller {
(small, large)
} else {
(large, small)
};
let order = if chain_holder_is_smaller {
"chain holder is the tie-break WINNER"
} else {
"chain holder is the tie-break LOSER"
};
let (b_dial, out) = b.dial(t, a.addr, &a.pk);
let msg1 = out.transmits[0].data.clone();
let drained = a.feed(t, b.addr, &msg1);
let (id, _src) = drained.intros[0];
a.ep.read_identity(t, id).expect("readable");
a.ep.authenticate(t, id).expect("authenticates");
let (a_dial, dial_out) = a.dial(t, b.addr, &b.pk);
let accepted = a.ep.accept(t, id);
let after_accept = a.drain();
let mut a_resolved = accepted.is_ok();
let mut b_resolved = false;
let mut timed_out: Vec<(&str, ConnectionId, ConnectError)> = Vec::new();
let mut to_b: Vec<Vec<u8>> = dial_out
.transmits
.iter()
.chain(after_accept.transmits.iter())
.map(|t| t.data.clone())
.collect();
for (conn, why) in after_accept.failed {
match why {
ConnectError::AlreadyConnected => {
assert_eq!(conn, a_dial, "{order}: only the dial is cancelled");
assert!(accepted.is_ok(), "{order}: §6.4's loser side installs");
}
other => timed_out.push(("A", conn, other)),
}
}
let mut to_a: Vec<Vec<u8>> = Vec::new();
for _ in 0..4 {
for datagram in std::mem::take(&mut to_b) {
let d = b.feed(t, a.addr, &datagram);
b_resolved |= !d.installs.is_empty();
for (conn, why) in d.failed {
timed_out.push(("B", conn, why));
}
to_a.extend(d.transmits.iter().map(|t| t.data.clone()));
}
for datagram in std::mem::take(&mut to_a) {
let d = a.feed(t, b.addr, &datagram);
a_resolved |= !d.installs.is_empty();
for (conn, why) in d.failed {
timed_out.push(("A", conn, why));
}
to_b.extend(d.transmits.iter().map(|t| t.data.clone()));
}
}
assert!(
a_resolved,
"{order}: A's `read_identity() → connect() → accept()` never resolved, \
with no clock advanced — this is ruling 91's regression"
);
assert!(b_resolved, "{order}: B's dial never resolved");
let past = t + crate::constants::HANDSHAKE_GIVEUP + Duration::from_secs(1);
for (side, node) in [("A", &mut a), ("B", &mut b)] {
for (conn, why) in node.timeout(past).failed {
timed_out.push((side, conn, why));
}
}
assert!(
timed_out.is_empty(),
"{order}: a dial reached HANDSHAKE_GIVEUP — {timed_out:?}"
);
let _ = (a_dial, b_dial);
}
}
#[test]
fn a_minted_pending_is_already_a_pending_outbound_remote() {
let t = t0();
let (mut small, mut large) = ordered_pair(t);
let (dial, _connection) = large
.ep
.mint_pending(t, small.addr, small.pk)
.expect("the static is NONE");
let quiet = large.drain();
assert!(
quiet.transmits.is_empty(),
"ruling 90: `mint_pending` puts nothing on the wire"
);
assert_eq!(
large.ep.hints(),
vec![small.addr],
"§17.4: the hint set is the pending tables' dialled addresses"
);
let crossing = lone_msg1(&mut small, t, &large);
let drained = large.feed(t, small.addr, &crossing);
assert!(
drained.intros.is_empty(),
"§6.5: a PENDING static's initiation **must** enter the internal path"
);
assert_eq!(
drained.installs,
vec![dial],
"§6.6 step 4 completes the dial that had not yet transmitted"
);
}
#[test]
fn exactly_one_side_responds() {
let t = t0();
let (mut small, mut large) = ordered_pair(t);
let (_b_dial, out) = large.dial(t, small.addr, &small.pk);
let msg1 = out.transmits[0].data.clone();
let drained = small.feed(t, large.addr, &msg1);
let (id, _src) = drained.intros[0];
small.ep.read_identity(t, id).expect("readable");
small.ep.authenticate(t, id).expect("authenticates");
let (_a_dial, dial_out) = small.dial(t, large.addr, &large.pk);
let refused = small.ep.accept(t, id);
assert!(
matches!(refused, Err(AcceptError::Stale)),
"the smaller static wins §6.7's comparison"
);
let mut to_large = dial_out.transmits;
to_large.extend(small.drain().transmits);
for transmit in to_large {
let d = large.feed(t, small.addr, &transmit.data);
for reply in d.transmits {
let _ = small.feed(t, large.addr, &reply.data);
}
}
assert_eq!(
small.ep.replacement_basis(large.canonical()),
Some(None),
"§17.4/§6.7: the winner dialled, so its basis is `None`"
);
assert!(
matches!(large.ep.replacement_basis(small.canonical()), Some(Some(_))),
"§17.4/§6.7: the loser responded, so its basis is `Some(t)`"
);
}
#[test]
fn a_winner_side_record_outlives_its_dial_by_the_giveup() {
let t = t0();
let (mut small, mut large) = ordered_pair(t);
let (dial, _) = small.dial(t, large.addr, &large.pk);
let crossing = lone_msg1(&mut large, t, &small);
let _ = small.feed(t, large.addr, &crossing);
let recorded = small
.ep
.greatest(large.canonical())
.expect("§6.7: the winner records");
let death = t + crate::constants::HANDSHAKE_GIVEUP;
let d = small.timeout(death);
assert_eq!(
d.failed,
vec![(dial, ConnectError::TimedOut)],
"the outbound never completed"
);
let ordinary_orphan_would_be_gone =
death + crate::constants::TS_GUARD_ORPHAN_TTL + Duration::from_secs(1);
let _ = small.timeout(ordinary_orphan_would_be_gone);
assert_eq!(
small.ep.greatest(large.canonical()),
Some(recorded),
"§17.1: the winner-side record is exempt from orphan aging for HANDSHAKE_GIVEUP"
);
let past_the_extension =
death + crate::constants::HANDSHAKE_GIVEUP + Duration::from_secs(1);
let _ = small.timeout(past_the_extension);
assert_eq!(
small.ep.greatest(large.canonical()),
None,
"§17.1: and only for HANDSHAKE_GIVEUP — the extension lapses rather than pinning forever"
);
}
}