Files
fips/src/tree/mod.rs
T
Sats And SportsandJohnathan Corgan b36966be3a Tighten TreeAnnounce validation to match spanning tree specification
Adds TreeAnnounce::validate_semantics() called from handle_tree_announce
before any tree-state mutation. Enforces that the ancestry accompanying
a parent declaration conforms to the spanning tree rules:

- first ancestry entry matches the signed sender
- is_root declarations carry a single-entry ancestry
- non-root declarations include the signed parent as the second entry
- the advertised root is the minimum node_addr in the ancestry

Non-conforming announcements are rejected with a warn log and no state
change. Adds unit tests for each rejected shape plus an integration
test covering the full receive path in a two-node tree.

Co-authored-by: Johnathan Corgan <johnathan@corganlabs.com>
2026-04-15 16:55:48 +00:00

71 lines
1.9 KiB
Rust

//! Spanning Tree Protocol Entities
//!
//! Tree coordinates and parent declarations for the FIPS spanning tree.
//! The spanning tree provides a routing topology where each node maintains
//! a path to a common root, enabling greedy distance-based routing.
mod coordinate;
mod declaration;
mod state;
use thiserror::Error;
use crate::{IdentityError, NodeAddr};
pub use coordinate::{CoordEntry, TreeCoordinate};
pub use declaration::ParentDeclaration;
pub use state::TreeState;
/// Errors related to spanning tree operations.
#[derive(Debug, Error)]
pub enum TreeError {
#[error("invalid tree coordinate: empty path")]
EmptyCoordinate,
#[error("invalid ancestry: does not reach claimed root")]
AncestryNotToRoot,
#[error("invalid ancestry: root declaration must contain only the sender")]
RootDeclarationMismatch,
#[error("invalid ancestry: non-root declaration must include a parent hop")]
AncestryTooShort,
#[error("invalid ancestry: sender {declared} does not match first path entry {ancestry}")]
AncestryNodeMismatch {
declared: NodeAddr,
ancestry: NodeAddr,
},
#[error(
"invalid ancestry: signed parent {declared} does not match first ancestry hop {ancestry}"
)]
AncestryParentMismatch {
declared: NodeAddr,
ancestry: NodeAddr,
},
#[error(
"invalid ancestry: advertised root {advertised} is not the minimum path entry {minimum}"
)]
AncestryRootNotMinimum {
advertised: NodeAddr,
minimum: NodeAddr,
},
#[error("signature verification failed for node {0:?}")]
InvalidSignature(NodeAddr),
#[error("sequence number regression: got {got}, expected > {expected}")]
SequenceRegression { got: u64, expected: u64 },
#[error("parent not in peers: {0:?}")]
ParentNotPeer(NodeAddr),
#[error("identity error: {0}")]
Identity(#[from] IdentityError),
}
#[cfg(test)]
mod tests;