Skip to main content

fiber_types/
protocol.rs

1//! Layer 2: Protocol message types and supporting types.
2//!
3//! Contains gossip protocol types (ChannelAnnouncement, ChannelUpdate, NodeAnnouncement,
4//! BroadcastMessage) and their supporting types (EcdsaSignature, AnnouncedNodeName,
5//! FeatureVector, SchnorrSignature).
6
7use crate::channel::{ChannelUpdateChannelFlags, ChannelUpdateMessageFlags};
8use crate::gen::fiber as molecule_fiber;
9use crate::gen::gossip as molecule_gossip;
10use crate::primitives::u8_32_as_byte_32;
11use crate::serde_utils::EntityHex;
12use crate::UdtCfgInfos;
13use crate::{Hash256, Privkey, Pubkey};
14use ckb_types::packed::{BytesVec, OutPoint, Script};
15use ckb_types::prelude::Pack;
16use molecule::prelude::{Builder, Byte, Entity};
17use musig2::LiftedSignature;
18use serde::{Deserialize, Deserializer, Serialize, Serializer};
19use serde_with::serde_as;
20use std::cmp::Ordering;
21use std::time::Duration;
22
23/// The size of a serialized cursor in bytes.
24pub const CURSOR_SIZE: usize = 45;
25pub use feature_bits::*;
26
27type Secp256k1Signature = secp256k1::ecdsa::Signature;
28
29/// A wrapper around secp256k1 ECDSA signature with serde and molecule support.
30#[derive(Clone, PartialOrd, Ord, PartialEq, Eq, Hash, Serialize, Deserialize, Debug)]
31pub struct EcdsaSignature(pub Secp256k1Signature);
32
33impl EcdsaSignature {
34    pub fn verify(&self, pubkey: &Pubkey, message: &[u8; 32]) -> bool {
35        let message = secp256k1::Message::from_digest(*message);
36        let pk = secp256k1::PublicKey::from_slice(&pubkey.0)
37            .expect("Pubkey should always contain valid serialized public key");
38        secp256k1::SECP256K1
39            .verify_ecdsa(&message, &self.0, &pk)
40            .is_ok()
41    }
42}
43
44impl From<EcdsaSignature> for Secp256k1Signature {
45    fn from(sig: EcdsaSignature) -> Self {
46        sig.0
47    }
48}
49
50impl From<Secp256k1Signature> for EcdsaSignature {
51    fn from(sig: Secp256k1Signature) -> Self {
52        Self(sig)
53    }
54}
55
56// EcdsaSignature <-> molecule_fiber::EcdsaSignature
57
58impl From<EcdsaSignature> for molecule_fiber::EcdsaSignature {
59    fn from(signature: EcdsaSignature) -> molecule_fiber::EcdsaSignature {
60        molecule_fiber::EcdsaSignature::new_builder()
61            .set(
62                signature
63                    .0
64                    .serialize_compact()
65                    .into_iter()
66                    .map(Into::into)
67                    .collect::<Vec<Byte>>()
68                    .try_into()
69                    .expect("Signature serialized to correct length"),
70            )
71            .build()
72    }
73}
74
75impl TryFrom<molecule_fiber::EcdsaSignature> for EcdsaSignature {
76    type Error = secp256k1::Error;
77
78    fn try_from(signature: molecule_fiber::EcdsaSignature) -> Result<Self, Self::Error> {
79        let signature = signature.raw_data();
80        Secp256k1Signature::from_compact(&signature).map(Into::into)
81    }
82}
83
84/// A node's announced name (up to 32 bytes, UTF-8 encoded).
85/// If the length is less than 32 bytes, it will be padded with 0.
86/// If the length is more than 32 bytes, it should be truncated.
87#[derive(Eq, PartialEq, Copy, Clone, Default, Hash)]
88pub struct AnnouncedNodeName(pub [u8; 32]);
89
90impl AnnouncedNodeName {
91    pub fn as_bytes(&self) -> &[u8; 32] {
92        &self.0
93    }
94
95    pub fn from_slice(slice: &[u8]) -> Result<Self, String> {
96        if slice.len() > 32 {
97            return Err("Node Alias can not be longer than 32 bytes".to_string());
98        }
99        let mut bytes = [0; 32];
100        bytes[..slice.len()].copy_from_slice(slice);
101        Ok(Self(bytes))
102    }
103
104    pub fn from_string(value: &str) -> Result<Self, String> {
105        let str_bytes = value.as_bytes();
106        Self::from_slice(str_bytes)
107    }
108
109    pub fn as_str(&self) -> &str {
110        let end = self.0.iter().position(|&b| b == 0).unwrap_or(self.0.len());
111        if end == 0 {
112            return "";
113        }
114        std::str::from_utf8(&self.0[..end]).expect("valid utf8 string")
115    }
116}
117
118impl std::fmt::Display for AnnouncedNodeName {
119    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
120        write!(f, "{}", self.as_str())
121    }
122}
123
124impl std::fmt::Debug for AnnouncedNodeName {
125    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
126        write!(f, "AnnouncedNodeName({})", self)
127    }
128}
129
130impl<'s> From<&'s str> for AnnouncedNodeName {
131    fn from(value: &'s str) -> Self {
132        Self::from_string(value).expect("Valid announced node name")
133    }
134}
135
136impl Serialize for AnnouncedNodeName {
137    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
138    where
139        S: Serializer,
140    {
141        serializer.serialize_str(std::str::from_utf8(&self.0).expect("valid utf8 string"))
142    }
143}
144
145impl<'de> Deserialize<'de> for AnnouncedNodeName {
146    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
147    where
148        D: Deserializer<'de>,
149    {
150        let s = String::deserialize(deserializer)?;
151        Self::from_string(&s).map_err(serde::de::Error::custom)
152    }
153}
154
155/// Feature bit type alias.
156pub type FeatureBit = u16;
157
158/// Macro for declaring feature bits and generating methods on FeatureVector.
159///
160/// Each feature is declared with a name and an odd bit number. The macro generates:
161/// - `NAME_REQUIRED` (even bit) and `NAME_OPTIONAL` (odd bit) constants
162/// - `MAX_FEATURE_BIT` constant
163/// - `feature_bit_name()` function
164/// - Setter/unsetter/query methods on `FeatureVector`
165#[macro_export]
166macro_rules! declare_feature_bits_and_methods {
167    (
168        $( $name:ident, $odd:expr; )*
169    ) => {
170        paste::paste! {
171            $(
172                /// Even bit, the bit used to signify that the feature is required.
173                pub const [<$name _REQUIRED>]: u16 = $odd - 1;
174                /// Odd bit, the bit used to signify that the feature is optional.
175                pub const [<$name _OPTIONAL>]: u16 = $odd;
176            )*
177
178            pub const MAX_FEATURE_BIT: u16 = {
179                let mut max = 0;
180                $(
181                    if $odd % 2 == 0 || $odd <= max {
182                        panic!("feature base bit must be defined as increasing odd numbers");
183                    }
184                    max = $odd;
185                )*
186                max
187            };
188
189            pub fn feature_bit_name(bit: FeatureBit) -> &'static str {
190                match bit {
191                    $(
192                        [<$name _REQUIRED>] => stringify!([<$name _REQUIRED>]),
193                        [<$name _OPTIONAL>] => stringify!([<$name _OPTIONAL>]),
194                    )*
195                    _ => "Unknown Feature",
196                }
197            }
198
199            impl FeatureVector {
200                $(
201                    pub fn [<set_ $name:lower _required>](&mut self) {
202                        self.set([<$name _REQUIRED>], true);
203                    }
204                    pub fn [<set_ $name:lower _optional>](&mut self) {
205                        self.set([<$name _OPTIONAL>], true);
206                    }
207                    pub fn [<unset_ $name:lower _required>](&mut self) {
208                        self.set([<$name _REQUIRED>], false);
209                    }
210                    pub fn [<unset_ $name:lower _optional>](&mut self) {
211                        self.set([<$name _OPTIONAL>], false);
212                    }
213                    pub fn [<requires_ $name:lower>](&self) -> bool {
214                        self.requires_feature([<$name _REQUIRED>])
215                    }
216                    pub fn [<supports_ $name:lower>](&self) -> bool {
217                        self.supports_feature([<$name _OPTIONAL>])
218                    }
219                )*
220            }
221        }
222    };
223}
224
225/// Feature bits and methods for the Fiber protocol.
226/// Pair bits: a feature can be introduced as optional (odd bits)
227/// and later upgraded to be compulsory (even bits).
228///   - Even bits are used to signify that the feature is required.
229///   - Odd bits are used to signify that the feature is optional.
230pub mod feature_bits {
231    use super::*;
232    declare_feature_bits_and_methods! {
233        GOSSIP_QUERIES, 1;
234        BASIC_MPP, 3;
235        TRAMPOLINE_ROUTING, 5;
236        // more features, please note that base bit must be defined as increasing odd numbers
237    }
238}
239
240/// A compact bit-vector representing protocol feature flags.
241#[derive(Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
242pub struct FeatureVector {
243    inner: Vec<u8>,
244}
245
246impl Default for FeatureVector {
247    fn default() -> Self {
248        let mut feature = Self::new();
249        feature.set_gossip_queries_required();
250        feature.set_basic_mpp_required();
251        feature.set_trampoline_routing_required();
252
253        // set other default features here
254        // ...
255
256        feature
257    }
258}
259
260impl FeatureVector {
261    pub fn new() -> Self {
262        let len = (feature_bits::MAX_FEATURE_BIT / 8) as usize + 1;
263        Self {
264            inner: vec![0; len],
265        }
266    }
267
268    pub fn from(bytes: Vec<u8>) -> Self {
269        Self { inner: bytes }
270    }
271
272    pub fn bytes(&self) -> Vec<u8> {
273        self.inner.clone()
274    }
275
276    fn is_set(&self, bit: FeatureBit) -> bool {
277        let idx = (bit / 8) as usize;
278        if idx >= self.inner.len() {
279            return false;
280        }
281        self.inner
282            .get(idx)
283            .map(|&byte| (byte >> (bit % 8)) & 1 == 1)
284            .unwrap_or(false)
285    }
286
287    fn set(&mut self, bit: FeatureBit, set: bool) {
288        let idx = (bit / 8) as usize;
289        if self.inner.len() <= idx {
290            self.inner.resize(idx + 1, 0);
291        }
292        let mask = 1 << (bit % 8);
293        if set {
294            self.inner[idx] |= mask;
295        } else {
296            self.inner[idx] &= !mask;
297        }
298    }
299
300    pub fn enabled_features(&self) -> Vec<FeatureBit> {
301        (0..(self.inner.len() * 8) as FeatureBit)
302            .filter(|&bit| self.is_set(bit))
303            .collect()
304    }
305
306    pub fn enabled_features_names(&self) -> Vec<String> {
307        self.enabled_features()
308            .into_iter()
309            .map(feature_bits::feature_bit_name)
310            .map(|name| name.to_string())
311            .collect()
312    }
313
314    pub fn is_empty(&self) -> bool {
315        self.inner.iter().all(|&b| b == 0)
316    }
317
318    pub fn set_feature(&mut self, bit: FeatureBit) {
319        self.set(bit, true);
320    }
321
322    pub fn unset_feature(&mut self, bit: FeatureBit) {
323        self.set(bit, false);
324    }
325
326    pub fn requires_feature(&self, bit: FeatureBit) -> bool {
327        self.is_set(bit) && bit.is_multiple_of(2)
328    }
329
330    pub fn supports_feature(&self, bit: FeatureBit) -> bool {
331        self.is_set(bit) || self.is_set(bit ^ 1)
332    }
333
334    pub fn compatible_with(&self, other: &Self) -> bool {
335        if self
336            .enabled_features()
337            .iter()
338            .any(|&bit| self.requires_feature(bit) && !other.supports_feature(bit))
339        {
340            return false;
341        }
342        true
343    }
344}
345
346impl std::fmt::Debug for FeatureVector {
347    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
348        f.debug_struct("FeatureVector")
349            .field("features", &self.enabled_features_names())
350            .finish()
351    }
352}
353
354/// A wrapper around secp256k1 Schnorr signature with serde and molecule support.
355#[derive(Clone, PartialOrd, Ord, PartialEq, Eq, Hash, Serialize, Deserialize, Debug)]
356pub struct SchnorrSignature(pub secp256k1::schnorr::Signature);
357
358impl SchnorrSignature {
359    /// Returns a reference to the inner secp256k1 Schnorr signature.
360    pub fn inner(&self) -> &secp256k1::schnorr::Signature {
361        &self.0
362    }
363
364    /// Serializes the signature to a 64-byte array.
365    pub fn to_byte_array(&self) -> [u8; 64] {
366        self.0.to_byte_array()
367    }
368
369    /// Creates a SchnorrSignature from a byte slice.
370    pub fn from_slice(data: &[u8]) -> Result<Self, secp256k1::Error> {
371        secp256k1::schnorr::Signature::from_slice(data).map(SchnorrSignature)
372    }
373}
374
375impl std::ops::Deref for SchnorrSignature {
376    type Target = secp256k1::schnorr::Signature;
377
378    fn deref(&self) -> &Self::Target {
379        &self.0
380    }
381}
382
383impl From<SchnorrSignature> for secp256k1::schnorr::Signature {
384    fn from(sig: SchnorrSignature) -> Self {
385        sig.0
386    }
387}
388
389impl From<secp256k1::schnorr::Signature> for SchnorrSignature {
390    fn from(sig: secp256k1::schnorr::Signature) -> Self {
391        Self(sig)
392    }
393}
394
395impl From<LiftedSignature> for SchnorrSignature {
396    fn from(sig: LiftedSignature) -> Self {
397        Self(secp256k1::schnorr::Signature::from(sig))
398    }
399}
400
401// SchnorrSignature <-> molecule_gossip::SchnorrSignature
402
403impl From<SchnorrSignature> for molecule_gossip::SchnorrSignature {
404    fn from(signature: SchnorrSignature) -> molecule_gossip::SchnorrSignature {
405        molecule_gossip::SchnorrSignature::new_builder()
406            .set(
407                signature
408                    .0
409                    .to_byte_array()
410                    .into_iter()
411                    .map(Into::into)
412                    .collect::<Vec<Byte>>()
413                    .try_into()
414                    .expect("Signature serialized to correct length"),
415            )
416            .build()
417    }
418}
419
420impl TryFrom<molecule_gossip::SchnorrSignature> for SchnorrSignature {
421    type Error = secp256k1::Error;
422
423    fn try_from(signature: molecule_gossip::SchnorrSignature) -> Result<Self, Self::Error> {
424        secp256k1::schnorr::Signature::from_slice(signature.as_slice()).map(Into::into)
425    }
426}
427
428/// Announcement of a new channel in the network.
429///
430/// This message is broadcast to all nodes to inform them about a new channel.
431/// It contains the channel's capacity, the nodes involved, and signatures.
432#[serde_as]
433#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize, Hash)]
434pub struct ChannelAnnouncement {
435    /// Signature from node 1
436    pub node1_signature: Option<EcdsaSignature>,
437    /// Signature from node 2
438    pub node2_signature: Option<EcdsaSignature>,
439    /// Signature signed by the funding transaction output public key
440    pub ckb_signature: Option<SchnorrSignature>,
441    /// Feature flags for the channel
442    pub features: u64,
443    /// The chain hash this channel belongs to
444    pub chain_hash: Hash256,
445    /// The outpoint of the funding transaction
446    #[serde_as(as = "EntityHex")]
447    pub channel_outpoint: OutPoint,
448    /// Public key of node 1
449    pub node1_id: Pubkey,
450    /// Public key of node 2
451    pub node2_id: Pubkey,
452    /// The aggregated public key of the funding transaction output
453    pub ckb_key: secp256k1::XOnlyPublicKey,
454    /// The total capacity of the channel
455    pub capacity: u128,
456    /// UDT type script if this is a UDT channel
457    #[serde_as(as = "Option<EntityHex>")]
458    pub udt_type_script: Option<Script>,
459}
460
461impl ChannelAnnouncement {
462    /// Check if the announcement is fully signed.
463    pub fn is_signed(&self) -> bool {
464        self.node1_signature.is_some()
465            && self.node2_signature.is_some()
466            && self.ckb_signature.is_some()
467    }
468
469    /// Get the channel outpoint.
470    pub fn out_point(&self) -> &OutPoint {
471        &self.channel_outpoint
472    }
473
474    /// Create an unsigned channel announcement with the given parameters.
475    pub fn new_unsigned(
476        node1_pubkey: &Pubkey,
477        node2_pubkey: &Pubkey,
478        channel_outpoint: OutPoint,
479        chain_hash: Hash256,
480        ckb_pubkey: &secp256k1::XOnlyPublicKey,
481        capacity: u128,
482        udt_type_script: Option<Script>,
483    ) -> Self {
484        Self {
485            node1_signature: None,
486            node2_signature: None,
487            ckb_signature: None,
488            features: Default::default(),
489            chain_hash,
490            channel_outpoint,
491            node1_id: *node1_pubkey,
492            node2_id: *node2_pubkey,
493            ckb_key: *ckb_pubkey,
494            capacity,
495            udt_type_script,
496        }
497    }
498}
499
500/// Update to an existing channel's routing parameters.
501///
502/// This message is broadcast to update routing information for a channel,
503/// such as fees, timelock requirements, or disabled status.
504#[serde_as]
505#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize, Hash)]
506pub struct ChannelUpdate {
507    /// Signature of the node that wants to update the channel information
508    pub signature: Option<EcdsaSignature>,
509    /// The chain hash this channel belongs to
510    pub chain_hash: Hash256,
511    /// The outpoint of the funding transaction
512    #[serde_as(as = "EntityHex")]
513    pub channel_outpoint: OutPoint,
514    /// Timestamp for this update
515    pub timestamp: u64,
516    /// Message flags (indicates which node this update is from)
517    pub message_flags: ChannelUpdateMessageFlags,
518    /// Channel flags (indicates if channel is disabled)
519    pub channel_flags: ChannelUpdateChannelFlags,
520    /// TLC expiry delta in blocks
521    pub tlc_expiry_delta: u64,
522    /// Minimum TLC value
523    pub tlc_minimum_value: u128,
524    /// Fee proportional millionths
525    pub tlc_fee_proportional_millionths: u128,
526}
527
528impl ChannelUpdate {
529    /// Check if this update is from node 1.
530    pub fn is_update_of_node_1(&self) -> bool {
531        !self.is_update_of_node_2()
532    }
533
534    /// Check if this update is from node 2.
535    pub fn is_update_of_node_2(&self) -> bool {
536        self.message_flags
537            .contains(ChannelUpdateMessageFlags::UPDATE_OF_NODE2)
538    }
539
540    /// Check if the channel is disabled.
541    pub fn is_disabled(&self) -> bool {
542        self.channel_flags
543            .contains(ChannelUpdateChannelFlags::DISABLED)
544    }
545
546    /// Get the message bytes to sign (hash of unsigned molecule serialization).
547    pub fn message_to_sign(&self) -> [u8; 32] {
548        let unsigned_update = ChannelUpdate {
549            signature: None,
550            chain_hash: self.chain_hash,
551            channel_outpoint: self.channel_outpoint.clone(),
552            timestamp: self.timestamp,
553            message_flags: self.message_flags,
554            channel_flags: self.channel_flags,
555            tlc_expiry_delta: self.tlc_expiry_delta,
556            tlc_minimum_value: self.tlc_minimum_value,
557            tlc_fee_proportional_millionths: self.tlc_fee_proportional_millionths,
558        };
559        deterministically_hash(&molecule_fiber::ChannelUpdate::from(unsigned_update))
560    }
561
562    /// Get the cursor for this channel update.
563    pub fn cursor(&self) -> Cursor {
564        Cursor::new(
565            self.timestamp,
566            BroadcastMessageID::ChannelUpdate(self.channel_outpoint.clone()),
567        )
568    }
569}
570
571impl ChannelAnnouncement {
572    /// Get the message bytes to sign (hash of unsigned molecule serialization).
573    pub fn message_to_sign(&self) -> [u8; 32] {
574        let unsigned_announcement = Self {
575            node1_signature: None,
576            node2_signature: None,
577            ckb_signature: None,
578            features: self.features,
579            chain_hash: self.chain_hash,
580            channel_outpoint: self.channel_outpoint.clone(),
581            node1_id: self.node1_id,
582            node2_id: self.node2_id,
583            ckb_key: self.ckb_key,
584            capacity: self.capacity,
585            udt_type_script: self.udt_type_script.clone(),
586        };
587        deterministically_hash(&molecule_gossip::ChannelAnnouncement::from(
588            unsigned_announcement,
589        ))
590    }
591}
592
593impl NodeAnnouncement {
594    /// Create an unsigned node announcement with the given parameters.
595    ///
596    /// The `udt_cfg_infos` and `version` are passed as parameters to avoid
597    /// depending on runtime/global state from the caller's crate.
598    #[allow(clippy::too_many_arguments)]
599    pub fn new_unsigned(
600        node_name: AnnouncedNodeName,
601        features: FeatureVector,
602        addresses: Vec<tentacle_multiaddr::Multiaddr>,
603        node_id: Pubkey,
604        chain_hash: Hash256,
605        timestamp: u64,
606        auto_accept_min_ckb_funding_amount: u64,
607        udt_cfg_infos: UdtCfgInfos,
608        version: String,
609    ) -> Self {
610        Self {
611            signature: None,
612            features,
613            timestamp,
614            node_id,
615            version,
616            node_name,
617            chain_hash,
618            addresses,
619            auto_accept_min_ckb_funding_amount,
620            udt_cfg_infos,
621        }
622    }
623
624    /// Create a signed node announcement.
625    ///
626    /// Builds an unsigned announcement, signs it with the given private key,
627    /// and returns the signed announcement.
628    #[allow(clippy::too_many_arguments)]
629    pub fn new_signed(
630        node_name: AnnouncedNodeName,
631        features: FeatureVector,
632        addresses: Vec<tentacle_multiaddr::Multiaddr>,
633        private_key: &Privkey,
634        chain_hash: Hash256,
635        timestamp: u64,
636        auto_accept_min_ckb_funding_amount: u64,
637        udt_cfg_infos: UdtCfgInfos,
638        version: String,
639    ) -> Self {
640        let mut unsigned = Self::new_unsigned(
641            node_name,
642            features,
643            addresses,
644            private_key.pubkey(),
645            chain_hash,
646            timestamp,
647            auto_accept_min_ckb_funding_amount,
648            udt_cfg_infos,
649            version,
650        );
651        unsigned.signature = Some(private_key.sign(unsigned.message_to_sign()));
652        unsigned
653    }
654
655    /// Get the message bytes to sign (hash of unsigned molecule serialization).
656    pub fn message_to_sign(&self) -> [u8; 32] {
657        let unsigned_announcement = NodeAnnouncement {
658            signature: None,
659            features: self.features.clone(),
660            timestamp: self.timestamp,
661            node_id: self.node_id,
662            version: self.version.clone(),
663            node_name: self.node_name,
664            chain_hash: self.chain_hash,
665            addresses: self.addresses.clone(),
666            auto_accept_min_ckb_funding_amount: self.auto_accept_min_ckb_funding_amount,
667            udt_cfg_infos: self.udt_cfg_infos.clone(),
668        };
669        deterministically_hash(&molecule_gossip::NodeAnnouncement::from(
670            unsigned_announcement,
671        ))
672    }
673
674    /// Get the cursor for this node announcement.
675    pub fn cursor(&self) -> Cursor {
676        Cursor::new(
677            self.timestamp,
678            BroadcastMessageID::NodeAnnouncement(self.node_id),
679        )
680    }
681
682    /// Verify the signature on this announcement.
683    pub fn verify(&self) -> bool {
684        let message = self.message_to_sign();
685        match self.signature {
686            Some(ref signature) => signature.verify(&self.node_id, &message),
687            _ => false,
688        }
689    }
690}
691
692/// Hash a molecule entity deterministically using blake2b-256.
693pub(crate) fn deterministically_hash<T: Entity>(v: &T) -> [u8; 32] {
694    ckb_hash::blake2b_256(v.as_slice())
695}
696
697/// A broadcast message in the gossip protocol.
698///
699/// This enum represents the different types of messages that can be broadcast
700/// to all nodes in the network.
701#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, Hash)]
702pub enum BroadcastMessage {
703    /// Node announcement message
704    NodeAnnouncement(NodeAnnouncement),
705    /// Channel announcement message
706    ChannelAnnouncement(ChannelAnnouncement),
707    /// Channel update message
708    ChannelUpdate(ChannelUpdate),
709}
710
711impl BroadcastMessage {
712    /// Returns the cursor for this broadcast message, if applicable.
713    pub fn cursor(&self) -> Option<Cursor> {
714        match self {
715            BroadcastMessage::ChannelAnnouncement(_) => None,
716            BroadcastMessage::ChannelUpdate(channel_update) => Some(channel_update.cursor()),
717            BroadcastMessage::NodeAnnouncement(node_announcement) => {
718                Some(node_announcement.cursor())
719            }
720        }
721    }
722
723    /// Returns the message ID for this broadcast message.
724    pub fn message_id(&self) -> BroadcastMessageID {
725        match self {
726            BroadcastMessage::NodeAnnouncement(node_announcement) => {
727                BroadcastMessageID::NodeAnnouncement(node_announcement.node_id)
728            }
729            BroadcastMessage::ChannelAnnouncement(channel_announcement) => {
730                BroadcastMessageID::ChannelAnnouncement(
731                    channel_announcement.channel_outpoint.clone(),
732                )
733            }
734            BroadcastMessage::ChannelUpdate(channel_update) => {
735                BroadcastMessageID::ChannelUpdate(channel_update.channel_outpoint.clone())
736            }
737        }
738    }
739
740    /// Returns the timestamp for this broadcast message, if applicable.
741    pub fn timestamp(&self) -> Option<u64> {
742        match self {
743            BroadcastMessage::NodeAnnouncement(node_announcement) => {
744                Some(node_announcement.timestamp)
745            }
746            BroadcastMessage::ChannelAnnouncement(_) => None,
747            BroadcastMessage::ChannelUpdate(channel_update) => Some(channel_update.timestamp),
748        }
749    }
750}
751
752/// Announcement of a node's presence and capabilities.
753///
754/// This message is broadcast to inform other nodes about this node's
755/// addresses, features, and other metadata.
756#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, Hash)]
757pub struct NodeAnnouncement {
758    /// Signature of this message
759    pub signature: Option<EcdsaSignature>,
760    /// Features supported by this node
761    pub features: FeatureVector,
762    /// Timestamp for this announcement
763    pub timestamp: u64,
764    /// The public key of the node
765    pub node_id: Pubkey,
766    /// Software version string
767    pub version: String,
768    /// Human-readable node name (up to 32 bytes)
769    pub node_name: AnnouncedNodeName,
770    /// Reachable addresses for this node
771    pub addresses: Vec<tentacle_multiaddr::Multiaddr>,
772    /// The chain hash this node is on
773    pub chain_hash: Hash256,
774    /// Minimum CKB funding amount for auto-accept
775    pub auto_accept_min_ckb_funding_amount: u64,
776    /// UDT configuration info
777    pub udt_cfg_infos: UdtCfgInfos,
778}
779
780/// The ID of a broadcast message.
781#[derive(Debug, Clone, Eq, PartialEq, Hash)]
782pub enum BroadcastMessageID {
783    ChannelAnnouncement(OutPoint),
784    ChannelUpdate(OutPoint),
785    NodeAnnouncement(Pubkey),
786}
787
788impl Default for BroadcastMessageID {
789    fn default() -> Self {
790        BroadcastMessageID::ChannelAnnouncement(OutPoint::default())
791    }
792}
793
794// We need to implement Ord for BroadcastMessageID to make sure that a ChannelUpdate message is always ordered after ChannelAnnouncement,
795// so that we can use it as the sorting key in fn prune_messages_to_be_saved to simplify the logic.
796// We need to implement Ord for BroadcastMessageID to make sure that a ChannelUpdate message is always ordered after ChannelAnnouncement,
797// so that we can use it as the sorting key in fn prune_messages_to_be_saved to simplify the logic.
798// Ordering: NodeAnnouncement < ChannelAnnouncement < ChannelUpdate
799impl Ord for BroadcastMessageID {
800    fn cmp(&self, other: &Self) -> Ordering {
801        match (self, other) {
802            (
803                BroadcastMessageID::ChannelAnnouncement(outpoint1),
804                BroadcastMessageID::ChannelAnnouncement(outpoint2),
805            ) => outpoint1.cmp(outpoint2),
806            (
807                BroadcastMessageID::ChannelUpdate(outpoint1),
808                BroadcastMessageID::ChannelUpdate(outpoint2),
809            ) => outpoint1.cmp(outpoint2),
810            (
811                BroadcastMessageID::NodeAnnouncement(node1),
812                BroadcastMessageID::NodeAnnouncement(node2),
813            ) => node1.cmp(node2),
814            (BroadcastMessageID::NodeAnnouncement(_), _) => Ordering::Less,
815            (BroadcastMessageID::ChannelUpdate(_), _) => Ordering::Greater,
816            (
817                BroadcastMessageID::ChannelAnnouncement(_),
818                BroadcastMessageID::NodeAnnouncement(_),
819            ) => Ordering::Greater,
820            (BroadcastMessageID::ChannelAnnouncement(_), BroadcastMessageID::ChannelUpdate(_)) => {
821                Ordering::Less
822            }
823        }
824    }
825}
826
827impl PartialOrd for BroadcastMessageID {
828    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
829        Some(self.cmp(other))
830    }
831}
832
833// 1 byte for message type, 36 bytes for message id
834const MESSAGE_ID_SIZE: usize = 1 + 36;
835
836impl BroadcastMessageID {
837    pub fn to_bytes(&self) -> [u8; MESSAGE_ID_SIZE] {
838        let mut result = [0u8; MESSAGE_ID_SIZE];
839        match self {
840            BroadcastMessageID::ChannelAnnouncement(channel_outpoint) => {
841                result[0] = 0;
842                result[1..].copy_from_slice(&channel_outpoint.as_bytes());
843            }
844            BroadcastMessageID::ChannelUpdate(channel_outpoint) => {
845                result[0] = 1;
846                result[1..].copy_from_slice(&channel_outpoint.as_bytes());
847            }
848            BroadcastMessageID::NodeAnnouncement(node_id) => {
849                result[0] = 2;
850                let node_id = node_id.serialize();
851                result[1..1 + node_id.len()].copy_from_slice(&node_id);
852            }
853        };
854        result
855    }
856
857    pub fn from_bytes(bytes: &[u8]) -> Result<Self, anyhow::Error> {
858        use molecule::prelude::Entity;
859        if bytes.len() != MESSAGE_ID_SIZE {
860            anyhow::bail!("Invalid message id size: {}", bytes.len());
861        }
862        match bytes[0] {
863            0 => Ok(BroadcastMessageID::ChannelAnnouncement(
864                OutPoint::from_slice(&bytes[1..])?,
865            )),
866            1 => Ok(BroadcastMessageID::ChannelUpdate(OutPoint::from_slice(
867                &bytes[1..],
868            )?)),
869            2 => Ok(BroadcastMessageID::NodeAnnouncement(Pubkey::from_slice(
870                &bytes[1..1 + Pubkey::serialization_len()],
871            )?)),
872            _ => anyhow::bail!("Invalid message id type: {}", bytes[0]),
873        }
874    }
875}
876
877/// A cursor for paginating broadcast messages.
878#[derive(Debug, Clone, Eq, PartialEq, Hash, Default)]
879pub struct Cursor {
880    pub timestamp: u64,
881    pub message_id: BroadcastMessageID,
882}
883
884impl Cursor {
885    pub fn new(timestamp: u64, message_id: BroadcastMessageID) -> Self {
886        Self {
887            timestamp,
888            message_id,
889        }
890    }
891
892    /// Create a new cursor which is the same as the current cursor but with a smaller timestamp.
893    /// This is useful when we want to query messages back from this cursor for a certain duration.
894    pub fn go_back_for_some_time(&self, duration: Duration) -> Self {
895        let current_timestamp = self.timestamp;
896        let duration_millis = duration.as_millis() as u64;
897        if current_timestamp > duration_millis {
898            Self {
899                timestamp: current_timestamp - duration_millis,
900                message_id: self.message_id.clone(),
901            }
902        } else {
903            Default::default()
904        }
905    }
906
907    pub fn to_bytes(&self) -> [u8; 45] {
908        self.timestamp
909            .to_be_bytes()
910            .into_iter()
911            .chain(self.message_id.to_bytes())
912            .collect::<Vec<_>>()
913            .try_into()
914            .expect("Must serialize cursor to 45 bytes")
915    }
916
917    pub fn from_bytes(bytes: &[u8]) -> Result<Self, anyhow::Error> {
918        if bytes.len() != CURSOR_SIZE {
919            anyhow::bail!("Invalid cursor size: {}, want {}", bytes.len(), CURSOR_SIZE);
920        }
921        let timestamp = u64::from_be_bytes(bytes[..8].try_into().expect("Cursor timestamp to u64"));
922        let message_id = BroadcastMessageID::from_bytes(&bytes[8..])?;
923        Ok(Cursor {
924            timestamp,
925            message_id,
926        })
927    }
928
929    /// A dummy cursor with the maximum timestamp and a dummy message id.
930    /// This is useful when we want to create a cursor after which none of the messages should be included.
931    pub fn max() -> Self {
932        Self {
933            timestamp: u64::MAX,
934            message_id: BroadcastMessageID::ChannelAnnouncement(OutPoint::default()),
935        }
936    }
937
938    pub fn is_max(&self) -> bool {
939        self.timestamp == u64::MAX
940    }
941}
942
943// NodeAnnouncement molecule conversions
944
945impl From<NodeAnnouncement> for molecule_gossip::NodeAnnouncement {
946    fn from(node_announcement: NodeAnnouncement) -> Self {
947        let builder = molecule_gossip::NodeAnnouncement::new_builder()
948            .features(node_announcement.features.bytes().pack())
949            .timestamp(node_announcement.timestamp.pack())
950            .node_id(node_announcement.node_id.into())
951            .version(node_announcement.version.pack())
952            .node_name(u8_32_as_byte_32(&node_announcement.node_name.0))
953            .chain_hash(node_announcement.chain_hash.into())
954            .auto_accept_min_ckb_funding_amount(
955                node_announcement.auto_accept_min_ckb_funding_amount.pack(),
956            )
957            .udt_cfg_infos(node_announcement.udt_cfg_infos.into())
958            .address(
959                BytesVec::new_builder()
960                    .set(
961                        node_announcement
962                            .addresses
963                            .into_iter()
964                            .map(|address| address.to_vec().pack())
965                            .collect(),
966                    )
967                    .build(),
968            );
969
970        let builder = if let Some(signature) = node_announcement.signature {
971            builder.signature(signature.into())
972        } else {
973            builder
974        };
975
976        builder.build()
977    }
978}
979
980impl TryFrom<molecule_gossip::NodeAnnouncement> for NodeAnnouncement {
981    type Error = anyhow::Error;
982
983    fn try_from(node_announcement: molecule_gossip::NodeAnnouncement) -> Result<Self, Self::Error> {
984        use ckb_types::prelude::Unpack;
985        Ok(NodeAnnouncement {
986            signature: Some(
987                node_announcement
988                    .signature()
989                    .try_into()
990                    .map_err(|e: secp256k1::Error| anyhow::anyhow!(e))?,
991            ),
992            features: FeatureVector::from(node_announcement.features().unpack()),
993            timestamp: node_announcement.timestamp().unpack(),
994            node_id: node_announcement
995                .node_id()
996                .try_into()
997                .map_err(|e: secp256k1::Error| anyhow::anyhow!(e))?,
998            version: String::from_utf8(node_announcement.version().unpack()).unwrap_or_default(),
999            chain_hash: node_announcement.chain_hash().into(),
1000            auto_accept_min_ckb_funding_amount: node_announcement
1001                .auto_accept_min_ckb_funding_amount()
1002                .unpack(),
1003            node_name: AnnouncedNodeName::from_slice(node_announcement.node_name().as_slice())
1004                .map_err(|e| anyhow::anyhow!("Invalid node_name: {}", e))?,
1005            udt_cfg_infos: node_announcement.udt_cfg_infos().try_into()?,
1006            addresses: node_announcement
1007                .address()
1008                .into_iter()
1009                .map(|bytes| {
1010                    tentacle_multiaddr::Multiaddr::try_from(bytes.raw_data().to_vec())
1011                        .map_err(Into::into)
1012                })
1013                .collect::<Result<Vec<_>, anyhow::Error>>()?,
1014        })
1015    }
1016}
1017
1018impl From<ChannelAnnouncement> for molecule_gossip::ChannelAnnouncement {
1019    fn from(channel_announcement: ChannelAnnouncement) -> Self {
1020        let builder = molecule_gossip::ChannelAnnouncement::new_builder()
1021            .features(channel_announcement.features.pack())
1022            .chain_hash(channel_announcement.chain_hash.into())
1023            .channel_outpoint(channel_announcement.channel_outpoint)
1024            .node1_id(channel_announcement.node1_id.into())
1025            .node2_id(channel_announcement.node2_id.into())
1026            .capacity(channel_announcement.capacity.pack())
1027            .udt_type_script(channel_announcement.udt_type_script.pack())
1028            .ckb_key(channel_announcement.ckb_key.into());
1029
1030        let builder = if let Some(signature) = channel_announcement.node1_signature {
1031            builder.node1_signature(signature.into())
1032        } else {
1033            builder
1034        };
1035
1036        let builder = if let Some(signature) = channel_announcement.node2_signature {
1037            builder.node2_signature(signature.into())
1038        } else {
1039            builder
1040        };
1041
1042        let builder = if let Some(signature) = channel_announcement.ckb_signature {
1043            builder.ckb_signature(signature.into())
1044        } else {
1045            builder
1046        };
1047
1048        builder.build()
1049    }
1050}
1051
1052impl TryFrom<molecule_gossip::ChannelAnnouncement> for ChannelAnnouncement {
1053    type Error = anyhow::Error;
1054
1055    fn try_from(
1056        channel_announcement: molecule_gossip::ChannelAnnouncement,
1057    ) -> Result<Self, Self::Error> {
1058        use ckb_types::prelude::Unpack;
1059        Ok(ChannelAnnouncement {
1060            node1_signature: Some(channel_announcement.node1_signature().try_into()?),
1061            node2_signature: Some(channel_announcement.node2_signature().try_into()?),
1062            ckb_signature: Some(channel_announcement.ckb_signature().try_into()?),
1063            features: channel_announcement.features().unpack(),
1064            capacity: channel_announcement.capacity().unpack(),
1065            chain_hash: channel_announcement.chain_hash().into(),
1066            channel_outpoint: channel_announcement.channel_outpoint(),
1067            udt_type_script: channel_announcement.udt_type_script().to_opt(),
1068            node1_id: channel_announcement.node1_id().try_into()?,
1069            node2_id: channel_announcement.node2_id().try_into()?,
1070            ckb_key: channel_announcement.ckb_key().try_into()?,
1071        })
1072    }
1073}
1074
1075impl From<ChannelUpdate> for molecule_fiber::ChannelUpdate {
1076    fn from(channel_update: ChannelUpdate) -> Self {
1077        let builder = molecule_fiber::ChannelUpdate::new_builder()
1078            .chain_hash(channel_update.chain_hash.into())
1079            .channel_outpoint(channel_update.channel_outpoint)
1080            .timestamp(channel_update.timestamp.pack())
1081            .message_flags(channel_update.message_flags.bits().pack())
1082            .channel_flags(channel_update.channel_flags.bits().pack())
1083            .tlc_expiry_delta(channel_update.tlc_expiry_delta.pack())
1084            .tlc_minimum_value(channel_update.tlc_minimum_value.pack())
1085            .tlc_fee_proportional_millionths(channel_update.tlc_fee_proportional_millionths.pack());
1086
1087        let builder = if let Some(signature) = channel_update.signature {
1088            builder.signature(signature.into())
1089        } else {
1090            builder
1091        };
1092
1093        builder.build()
1094    }
1095}
1096
1097impl TryFrom<molecule_fiber::ChannelUpdate> for ChannelUpdate {
1098    type Error = anyhow::Error;
1099
1100    fn try_from(channel_update: molecule_fiber::ChannelUpdate) -> Result<Self, Self::Error> {
1101        use ckb_types::prelude::Unpack;
1102        Ok(ChannelUpdate {
1103            signature: Some(channel_update.signature().try_into()?),
1104            chain_hash: channel_update.chain_hash().into(),
1105            channel_outpoint: channel_update.channel_outpoint(),
1106            timestamp: channel_update.timestamp().unpack(),
1107            message_flags: ChannelUpdateMessageFlags::from_bits_truncate(
1108                channel_update.message_flags().unpack(),
1109            ),
1110            channel_flags: ChannelUpdateChannelFlags::from_bits_truncate(
1111                channel_update.channel_flags().unpack(),
1112            ),
1113            tlc_expiry_delta: channel_update.tlc_expiry_delta().unpack(),
1114            tlc_minimum_value: channel_update.tlc_minimum_value().unpack(),
1115            tlc_fee_proportional_millionths: channel_update
1116                .tlc_fee_proportional_millionths()
1117                .unpack(),
1118        })
1119    }
1120}
1121
1122impl Ord for BroadcastMessage {
1123    fn cmp(&self, other: &Self) -> Ordering {
1124        self.message_id()
1125            .cmp(&other.message_id())
1126            .then(self.timestamp().cmp(&other.timestamp()))
1127    }
1128}
1129
1130impl PartialOrd for BroadcastMessage {
1131    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
1132        Some(self.cmp(other))
1133    }
1134}
1135
1136impl From<BroadcastMessage> for molecule_gossip::BroadcastMessageUnion {
1137    fn from(fiber_broadcast_message: BroadcastMessage) -> Self {
1138        match fiber_broadcast_message {
1139            BroadcastMessage::NodeAnnouncement(node_announcement) => {
1140                molecule_gossip::BroadcastMessageUnion::NodeAnnouncement(node_announcement.into())
1141            }
1142            BroadcastMessage::ChannelAnnouncement(channel_announcement) => {
1143                molecule_gossip::BroadcastMessageUnion::ChannelAnnouncement(
1144                    channel_announcement.into(),
1145                )
1146            }
1147            BroadcastMessage::ChannelUpdate(channel_update) => {
1148                molecule_gossip::BroadcastMessageUnion::ChannelUpdate(channel_update.into())
1149            }
1150        }
1151    }
1152}
1153
1154impl TryFrom<molecule_gossip::BroadcastMessageUnion> for BroadcastMessage {
1155    type Error = anyhow::Error;
1156
1157    fn try_from(
1158        fiber_broadcast_message: molecule_gossip::BroadcastMessageUnion,
1159    ) -> Result<Self, Self::Error> {
1160        match fiber_broadcast_message {
1161            molecule_gossip::BroadcastMessageUnion::NodeAnnouncement(node_announcement) => Ok(
1162                BroadcastMessage::NodeAnnouncement(node_announcement.try_into()?),
1163            ),
1164            molecule_gossip::BroadcastMessageUnion::ChannelAnnouncement(channel_announcement) => {
1165                Ok(BroadcastMessage::ChannelAnnouncement(
1166                    channel_announcement.try_into()?,
1167                ))
1168            }
1169            molecule_gossip::BroadcastMessageUnion::ChannelUpdate(channel_update) => {
1170                Ok(BroadcastMessage::ChannelUpdate(channel_update.try_into()?))
1171            }
1172        }
1173    }
1174}
1175
1176impl From<BroadcastMessage> for molecule_gossip::BroadcastMessage {
1177    fn from(fiber_broadcast_message: BroadcastMessage) -> Self {
1178        molecule_gossip::BroadcastMessage::new_builder()
1179            .set(fiber_broadcast_message)
1180            .build()
1181    }
1182}
1183
1184impl TryFrom<molecule_gossip::BroadcastMessage> for BroadcastMessage {
1185    type Error = anyhow::Error;
1186
1187    fn try_from(
1188        fiber_broadcast_message: molecule_gossip::BroadcastMessage,
1189    ) -> Result<Self, Self::Error> {
1190        fiber_broadcast_message.to_enum().try_into()
1191    }
1192}
1193
1194impl Ord for Cursor {
1195    fn cmp(&self, other: &Self) -> Ordering {
1196        self.to_bytes().cmp(&other.to_bytes())
1197    }
1198}
1199
1200impl PartialOrd for Cursor {
1201    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
1202        Some(self.cmp(other))
1203    }
1204}
1205
1206impl From<Cursor> for molecule_gossip::Cursor {
1207    fn from(cursor: Cursor) -> Self {
1208        use molecule::prelude::{Builder, Byte};
1209        let serialized = cursor
1210            .timestamp
1211            .to_be_bytes()
1212            .into_iter()
1213            .chain(cursor.message_id.to_bytes())
1214            .map(Byte::new)
1215            .collect::<Vec<_>>()
1216            .try_into()
1217            .expect("Must serialize cursor to 45 bytes");
1218
1219        molecule_gossip::Cursor::new_builder()
1220            .set(serialized)
1221            .build()
1222    }
1223}
1224
1225impl TryFrom<molecule_gossip::Cursor> for Cursor {
1226    type Error = anyhow::Error;
1227
1228    fn try_from(cursor: molecule_gossip::Cursor) -> Result<Self, Self::Error> {
1229        use molecule::prelude::Entity;
1230        let slice = cursor.as_slice();
1231        if slice.len() != CURSOR_SIZE {
1232            anyhow::bail!("Invalid cursor size: {}, want {}", slice.len(), CURSOR_SIZE);
1233        }
1234        let timestamp = u64::from_be_bytes(slice[..8].try_into().expect("Cursor timestamp to u64"));
1235        let message_id = BroadcastMessageID::from_bytes(&slice[8..])?;
1236        Ok(Cursor {
1237            timestamp,
1238            message_id,
1239        })
1240    }
1241}