Files
fips/src/proto/discovery/wire.rs
T
Johnathan Corgan 5d13090d8f proto: add a bounds-checked byte reader/writer and adopt it across the wire codecs
Introduce a shared proto/codec module with a cursor Reader (short reads fail with
MessageTooShort { expected: position + needed, got: total }) and an append Writer,
and adopt them across the seven subsystem wire codecs, replacing the repetitive
manual slicing, try_into, and from_le_bytes extraction. Each existing length check
maps to the reader (an up-front minimum becomes require at position zero, so the
per-field expected values, including the tree-announce expected 99, are reproduced
exactly); the bloom exact-length check stays explicit since it also rejects
over-long payloads. Encoded bytes and decode decisions are unchanged.
2026-07-08 19:00:25 +00:00

216 lines
6.5 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! Discovery messages: LookupRequest and LookupResponse.
use crate::NodeAddr;
use crate::proto::Error;
use crate::proto::codec::Reader;
use crate::proto::stp::TreeCoordinate;
use crate::proto::stp::{decode_coords, encode_coords};
use secp256k1::schnorr::Signature;
/// Request to discover a node's coordinates.
///
/// Routed through the spanning tree via bloom-filter-guided forwarding.
/// Each transit node forwards only to tree peers whose bloom filter
/// contains the target. TTL limits propagation depth.
#[derive(Clone, Debug)]
pub struct LookupRequest {
/// Unique request identifier.
pub request_id: u64,
/// Node we're looking for.
pub target: NodeAddr,
/// Who's asking (for response routing).
pub origin: NodeAddr,
/// Origin's coordinates (for return path).
pub origin_coords: TreeCoordinate,
/// Remaining propagation hops.
pub ttl: u8,
/// Minimum transport MTU the origin requires for a viable route.
/// 0 means no requirement.
pub min_mtu: u16,
}
impl LookupRequest {
/// Create a new lookup request.
pub fn new(
request_id: u64,
target: NodeAddr,
origin: NodeAddr,
origin_coords: TreeCoordinate,
ttl: u8,
min_mtu: u16,
) -> Self {
Self {
request_id,
target,
origin,
origin_coords,
ttl,
min_mtu,
}
}
/// Decrement TTL for forwarding.
///
/// Returns false if TTL was already 0.
pub fn forward(&mut self) -> bool {
if self.ttl == 0 {
return false;
}
self.ttl -= 1;
true
}
/// Check if this request can still be forwarded.
pub fn can_forward(&self) -> bool {
self.ttl > 0
}
/// Encode as wire format (includes msg_type byte).
///
/// Format: `[0x30][request_id:8][target:16][origin:16][ttl:1][min_mtu:2]`
/// `[origin_coords_cnt:2][origin_coords:16×n]`
pub fn encode(&self) -> Vec<u8> {
let mut buf = Vec::with_capacity(46 + self.origin_coords.depth() * 16);
buf.push(0x30); // msg_type
buf.extend_from_slice(&self.request_id.to_le_bytes());
buf.extend_from_slice(self.target.as_bytes());
buf.extend_from_slice(self.origin.as_bytes());
buf.push(self.ttl);
buf.extend_from_slice(&self.min_mtu.to_le_bytes());
encode_coords(&self.origin_coords, &mut buf);
buf
}
/// Decode from wire format (after msg_type byte has been consumed).
pub fn decode(payload: &[u8]) -> Result<Self, Error> {
// Minimum: request_id(8) + target(16) + origin(16) + ttl(1) + min_mtu(2)
// + coords_count(2) = 45 bytes
let mut reader = Reader::new(payload);
reader.require(45)?;
let request_id = reader.read_u64_le()?;
let target = NodeAddr::from_bytes(reader.read_array::<16>()?);
let origin = NodeAddr::from_bytes(reader.read_array::<16>()?);
let ttl = reader.read_u8()?;
let min_mtu = reader.read_u16_le()?;
let (origin_coords, _consumed) = decode_coords(reader.rest())?;
Ok(Self {
request_id,
target,
origin,
origin_coords,
ttl,
min_mtu,
})
}
}
/// Response to a lookup request with target's coordinates.
///
/// Routed back to the origin using the origin_coords from the request.
#[derive(Clone, Debug)]
pub struct LookupResponse {
/// Echoed request identifier.
pub request_id: u64,
/// The target node.
pub target: NodeAddr,
/// Minimum transport MTU along the response path.
///
/// Initialized to `u16::MAX` by the target. Each transit node applies
/// `path_mtu = path_mtu.min(outgoing_link_mtu)` when forwarding.
/// NOT included in the proof signature (transit annotation).
pub path_mtu: u16,
/// Target's coordinates in the tree.
pub target_coords: TreeCoordinate,
/// Proof that target authorized this response (signature over request).
pub proof: Signature,
}
impl LookupResponse {
/// Create a new lookup response.
///
/// `path_mtu` is initialized to `u16::MAX` by the target; transit
/// nodes reduce it as they forward.
pub fn new(
request_id: u64,
target: NodeAddr,
target_coords: TreeCoordinate,
proof: Signature,
) -> Self {
Self {
request_id,
target,
path_mtu: u16::MAX,
target_coords,
proof,
}
}
/// Get the bytes that should be signed as proof.
///
/// Format: request_id (8) || target (16) || coords_encoding (2 + 16×n)
pub fn proof_bytes(
request_id: u64,
target: &NodeAddr,
target_coords: &TreeCoordinate,
) -> Vec<u8> {
let coord_size = 2 + target_coords.entries().len() * 16;
let mut bytes = Vec::with_capacity(24 + coord_size);
bytes.extend_from_slice(&request_id.to_le_bytes());
bytes.extend_from_slice(target.as_bytes());
encode_coords(target_coords, &mut bytes);
bytes
}
/// Encode as wire format (includes msg_type byte).
///
/// Format: `[0x31][request_id:8][target:16][path_mtu:2][target_coords_cnt:2][target_coords:16×n][proof:64]`
pub fn encode(&self) -> Vec<u8> {
let mut buf = Vec::with_capacity(93 + self.target_coords.depth() * 16);
buf.push(0x31); // msg_type
buf.extend_from_slice(&self.request_id.to_le_bytes());
buf.extend_from_slice(self.target.as_bytes());
buf.extend_from_slice(&self.path_mtu.to_le_bytes());
encode_coords(&self.target_coords, &mut buf);
buf.extend_from_slice(self.proof.as_ref());
buf
}
/// Decode from wire format (after msg_type byte has been consumed).
pub fn decode(payload: &[u8]) -> Result<Self, Error> {
// Minimum: request_id(8) + target(16) + path_mtu(2) + coords_count(2) + proof(64) = 92
let mut reader = Reader::new(payload);
reader.require(92)?;
let request_id = reader.read_u64_le()?;
let target = NodeAddr::from_bytes(reader.read_array::<16>()?);
let path_mtu = reader.read_u16_le()?;
let (target_coords, consumed) = decode_coords(reader.rest())?;
reader.advance(consumed);
let proof = Signature::from_slice(reader.read_bytes(64)?)
.map_err(|_| Error::Malformed("bad proof signature"))?;
Ok(Self {
request_id,
target,
path_mtu,
target_coords,
proof,
})
}
}