Files
fips/src/proto/stp/declaration.rs
T
Johnathan Corgan a2400d823f proto/stp: extract ParentDeclaration and relocate the coordinate type to a shared proto/coord module
Two behavior-neutral relocations, wire bytes unchanged:

- Move the ParentDeclaration type and its impls out of state.rs into a dedicated
  declaration.rs, re-exported at the same path, and relocate the inline wire.rs
  test module into tests/wire.rs alongside the other stp unit tests (reusing the
  shared test helpers). wire.rs drops to non-test code only.

- Move TreeCoordinate, CoordEntry, and the coordinate wire codec out of
  proto/stp into a shared proto/coord module (a peer of proto/link), re-exported
  from proto::stp so every existing import path keeps resolving unchanged.
  Coordinate is a shared addressing primitive with many non-stp consumers, so it
  no longer belongs under the spanning-tree subsystem. The codec carries a
  documented temporary dependency on stp::TreeError until a dedicated CoordError
  is introduced.
2026-07-08 19:00:25 +00:00

145 lines
4.4 KiB
Rust

//! Parent declaration: a node's signed announcement of its spanning-tree parent.
use core::fmt;
use crate::NodeAddr;
/// A node's declaration of its parent in the spanning tree.
///
/// Each node periodically announces its parent selection. The declaration
/// includes a monotonic sequence number for freshness and a signature
/// for authenticity. When `parent_id == node_addr`, the node declares itself
/// as a root candidate.
#[derive(Clone)]
pub struct ParentDeclaration {
/// The node making this declaration.
node_addr: NodeAddr,
/// The selected parent (equals node_addr if self-declaring as root).
parent_id: NodeAddr,
/// Monotonically increasing sequence number.
sequence: u64,
/// Timestamp when this declaration was created (Unix seconds).
timestamp: u64,
/// Raw 64-byte Schnorr signature over the declaration fields. Stored as
/// opaque bytes so the in-core type carries no signature-crypto dependency;
/// the shell computes/verifies it over `signing_bytes()` (§6).
signature: Option<[u8; 64]>,
}
impl ParentDeclaration {
/// Create a new unsigned parent declaration.
///
/// The declaration must be signed before transmission using `set_signature()`.
pub fn new(node_addr: NodeAddr, parent_id: NodeAddr, sequence: u64, timestamp: u64) -> Self {
Self {
node_addr,
parent_id,
sequence,
timestamp,
signature: None,
}
}
/// Create a self-declaration (node is root candidate).
pub fn self_root(node_addr: NodeAddr, sequence: u64, timestamp: u64) -> Self {
Self::new(node_addr, node_addr, sequence, timestamp)
}
/// Create a declaration with a pre-computed signature.
pub fn with_signature(
node_addr: NodeAddr,
parent_id: NodeAddr,
sequence: u64,
timestamp: u64,
signature: [u8; 64],
) -> Self {
Self {
node_addr,
parent_id,
sequence,
timestamp,
signature: Some(signature),
}
}
/// Get the declaring node's ID.
pub fn node_addr(&self) -> &NodeAddr {
&self.node_addr
}
/// Get the parent node's ID.
pub fn parent_id(&self) -> &NodeAddr {
&self.parent_id
}
/// Get the sequence number.
pub fn sequence(&self) -> u64 {
self.sequence
}
/// Get the timestamp.
pub fn timestamp(&self) -> u64 {
self.timestamp
}
/// Get the raw 64-byte signature, if set.
pub fn signature(&self) -> Option<&[u8; 64]> {
self.signature.as_ref()
}
/// Set the raw 64-byte signature after signing.
pub fn set_signature(&mut self, signature: [u8; 64]) {
self.signature = Some(signature);
}
/// Check if this is a root declaration (parent == self).
pub fn is_root(&self) -> bool {
self.node_addr == self.parent_id
}
/// Check if this declaration is signed.
pub fn is_signed(&self) -> bool {
self.signature.is_some()
}
/// Get the bytes that should be signed.
///
/// Format: node_addr (16) || parent_id (16) || sequence (8) || timestamp (8)
pub fn signing_bytes(&self) -> Vec<u8> {
let mut bytes = Vec::with_capacity(48);
bytes.extend_from_slice(self.node_addr.as_bytes());
bytes.extend_from_slice(self.parent_id.as_bytes());
bytes.extend_from_slice(&self.sequence.to_le_bytes());
bytes.extend_from_slice(&self.timestamp.to_le_bytes());
bytes
}
/// Check if this declaration is fresher than another.
pub fn is_fresher_than(&self, other: &ParentDeclaration) -> bool {
self.sequence > other.sequence
}
}
impl fmt::Debug for ParentDeclaration {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ParentDeclaration")
.field("node_addr", &self.node_addr)
.field("parent_id", &self.parent_id)
.field("sequence", &self.sequence)
.field("is_root", &self.is_root())
.field("signed", &self.is_signed())
.finish()
}
}
impl PartialEq for ParentDeclaration {
fn eq(&self, other: &Self) -> bool {
self.node_addr == other.node_addr
&& self.parent_id == other.parent_id
&& self.sequence == other.sequence
&& self.timestamp == other.timestamp
}
}
impl Eq for ParentDeclaration {}