Skip to main content

fips_core/tree/
declaration.rs

1//! Parent declarations for the spanning tree.
2
3use secp256k1::XOnlyPublicKey;
4use secp256k1::schnorr::Signature;
5use std::fmt;
6
7use super::TreeError;
8use crate::{Identity, NodeAddr};
9
10/// A node's declaration of its parent in the spanning tree.
11///
12/// Each node periodically announces its parent selection. The declaration
13/// includes a monotonic sequence number for freshness and a signature
14/// for authenticity. When `parent_id == node_addr`, the node declares itself
15/// as a root candidate.
16#[derive(Clone)]
17pub struct ParentDeclaration {
18    /// The node making this declaration.
19    node_addr: NodeAddr,
20    /// The selected parent (equals node_addr if self-declaring as root).
21    parent_id: NodeAddr,
22    /// Monotonically increasing sequence number.
23    sequence: u64,
24    /// Timestamp when this declaration was created (Unix seconds).
25    timestamp: u64,
26    /// Schnorr signature over the declaration fields.
27    signature: Option<Signature>,
28}
29
30impl ParentDeclaration {
31    /// Create a new unsigned parent declaration.
32    ///
33    /// The declaration must be signed before transmission using `set_signature()`.
34    pub fn new(node_addr: NodeAddr, parent_id: NodeAddr, sequence: u64, timestamp: u64) -> Self {
35        Self {
36            node_addr,
37            parent_id,
38            sequence,
39            timestamp,
40            signature: None,
41        }
42    }
43
44    /// Create a self-declaration (node is root candidate).
45    pub fn self_root(node_addr: NodeAddr, sequence: u64, timestamp: u64) -> Self {
46        Self::new(node_addr, node_addr, sequence, timestamp)
47    }
48
49    /// Create a declaration with a pre-computed signature.
50    pub fn with_signature(
51        node_addr: NodeAddr,
52        parent_id: NodeAddr,
53        sequence: u64,
54        timestamp: u64,
55        signature: Signature,
56    ) -> Self {
57        Self {
58            node_addr,
59            parent_id,
60            sequence,
61            timestamp,
62            signature: Some(signature),
63        }
64    }
65
66    /// Get the declaring node's ID.
67    pub fn node_addr(&self) -> &NodeAddr {
68        &self.node_addr
69    }
70
71    /// Get the parent node's ID.
72    pub fn parent_id(&self) -> &NodeAddr {
73        &self.parent_id
74    }
75
76    /// Get the sequence number.
77    pub fn sequence(&self) -> u64 {
78        self.sequence
79    }
80
81    /// Get the timestamp.
82    pub fn timestamp(&self) -> u64 {
83        self.timestamp
84    }
85
86    /// Get the signature, if set.
87    pub fn signature(&self) -> Option<&Signature> {
88        self.signature.as_ref()
89    }
90
91    /// Set the signature after signing.
92    pub fn set_signature(&mut self, signature: Signature) {
93        self.signature = Some(signature);
94    }
95
96    /// Sign this declaration with the given identity.
97    ///
98    /// The identity's node_addr must match this declaration's node_addr.
99    /// Returns an error if the node_addrs don't match.
100    pub fn sign(&mut self, identity: &Identity) -> Result<(), TreeError> {
101        if identity.node_addr() != &self.node_addr {
102            return Err(TreeError::InvalidSignature(self.node_addr));
103        }
104        let signature = identity.sign(&self.signing_bytes());
105        self.signature = Some(signature);
106        Ok(())
107    }
108
109    /// Check if this is a root declaration (parent == self).
110    pub fn is_root(&self) -> bool {
111        self.node_addr == self.parent_id
112    }
113
114    /// Check if this declaration is signed.
115    pub fn is_signed(&self) -> bool {
116        self.signature.is_some()
117    }
118
119    /// Get the bytes that should be signed.
120    ///
121    /// Format: node_addr (16) || parent_id (16) || sequence (8) || timestamp (8)
122    pub fn signing_bytes(&self) -> Vec<u8> {
123        let mut bytes = Vec::with_capacity(48);
124        bytes.extend_from_slice(self.node_addr.as_bytes());
125        bytes.extend_from_slice(self.parent_id.as_bytes());
126        bytes.extend_from_slice(&self.sequence.to_le_bytes());
127        bytes.extend_from_slice(&self.timestamp.to_le_bytes());
128        bytes
129    }
130
131    /// Verify the signature on this declaration.
132    ///
133    /// Returns Ok(()) if the signature is valid, or an error otherwise.
134    pub fn verify(&self, pubkey: &XOnlyPublicKey) -> Result<(), TreeError> {
135        let signature = self
136            .signature
137            .as_ref()
138            .ok_or(TreeError::InvalidSignature(self.node_addr))?;
139
140        let secp = secp256k1::Secp256k1::verification_only();
141        let hash = self.signing_hash();
142
143        secp.verify_schnorr(signature, &hash, pubkey)
144            .map_err(|_| TreeError::InvalidSignature(self.node_addr))
145    }
146
147    /// Compute the SHA-256 hash of the signing bytes.
148    fn signing_hash(&self) -> [u8; 32] {
149        use sha2::{Digest, Sha256};
150        let mut hasher = Sha256::new();
151        hasher.update(self.signing_bytes());
152        hasher.finalize().into()
153    }
154
155    /// Check if this declaration is fresher than another.
156    pub fn is_fresher_than(&self, other: &ParentDeclaration) -> bool {
157        self.sequence > other.sequence
158    }
159}
160
161impl fmt::Debug for ParentDeclaration {
162    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
163        f.debug_struct("ParentDeclaration")
164            .field("node_addr", &self.node_addr)
165            .field("parent_id", &self.parent_id)
166            .field("sequence", &self.sequence)
167            .field("is_root", &self.is_root())
168            .field("signed", &self.is_signed())
169            .finish()
170    }
171}
172
173impl PartialEq for ParentDeclaration {
174    fn eq(&self, other: &Self) -> bool {
175        self.node_addr == other.node_addr
176            && self.parent_id == other.parent_id
177            && self.sequence == other.sequence
178            && self.timestamp == other.timestamp
179    }
180}
181
182impl Eq for ParentDeclaration {}