Skip to main content

fiber_types/
channel.rs

1//! Channel-related types: state flags, TLC status, channel state enum.
2
3use crate::crate_time::SystemTime;
4use crate::gen::fiber as molecule_fiber;
5use crate::invoice::HashAlgorithm;
6use crate::onion::PaymentOnionPacket;
7use crate::onion::TlcErrPacket;
8use crate::protocol::{ChannelAnnouncement, ChannelUpdate, EcdsaSignature};
9use crate::serde_utils::PartialSignatureAsBytes;
10use crate::serde_utils::PubNonceAsBytes;
11use crate::EntityHex;
12use crate::Hash256;
13use crate::Privkey;
14use crate::Pubkey;
15use bitflags::bitflags;
16use ckb_types::packed::Byte32 as MByte32;
17use ckb_types::packed::Script;
18use ckb_types::packed::Transaction;
19use ckb_types::prelude::{Pack, Unpack};
20use ckb_types::H256;
21use molecule::prelude::{Builder, Entity};
22use musig2::BinaryEncoding;
23use musig2::PartialSignature;
24use musig2::PubNonce;
25use musig2::{SecNonce, SecNonceBuilder};
26use serde::{Deserialize, Serialize};
27use serde_with::serde_as;
28use std::collections::{HashMap, VecDeque};
29use std::fmt::{Debug, Formatter};
30
31bitflags! {
32    #[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
33    #[serde(transparent)]
34    pub struct ChannelFlags: u8 {
35        const PUBLIC = 1;
36        const ONE_WAY = 1 << 1;
37        const EXTERNAL_FUNDING = 1 << 2;
38    }
39
40    #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
41    #[serde(transparent)]
42    pub struct ChannelUpdateChannelFlags: u32 {
43        const DISABLED = 1;
44    }
45
46    #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
47    #[serde(transparent)]
48    pub struct ChannelUpdateMessageFlags: u32 {
49        const UPDATE_OF_NODE1 = 0;
50        const UPDATE_OF_NODE2 = 1;
51    }
52
53    #[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
54    #[serde(transparent)]
55    pub struct NegotiatingFundingFlags: u32 {
56        const OUR_INIT_SENT = 1;
57        const THEIR_INIT_SENT = 1 << 1;
58        const INIT_SENT = NegotiatingFundingFlags::OUR_INIT_SENT.bits() | NegotiatingFundingFlags::THEIR_INIT_SENT.bits();
59        const AWAITING_EXTERNAL_FUNDING = 1 << 2;
60    }
61
62    #[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
63    #[serde(transparent)]
64    pub struct CollaboratingFundingTxFlags: u32 {
65        const AWAITING_REMOTE_TX_COLLABORATION_MSG = 1;
66        const PREPARING_LOCAL_TX_COLLABORATION_MSG = 1 << 1;
67        const OUR_TX_COMPLETE_SENT = 1 << 2;
68        const THEIR_TX_COMPLETE_SENT = 1 << 3;
69        const COLLABORATION_COMPLETED = CollaboratingFundingTxFlags::OUR_TX_COMPLETE_SENT.bits() | CollaboratingFundingTxFlags::THEIR_TX_COMPLETE_SENT.bits();
70    }
71
72    #[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
73    #[serde(transparent)]
74    pub struct SigningCommitmentFlags: u32 {
75        const OUR_COMMITMENT_SIGNED_SENT = 1;
76        const THEIR_COMMITMENT_SIGNED_SENT = 1 << 1;
77        const COMMITMENT_SIGNED_SENT = SigningCommitmentFlags::OUR_COMMITMENT_SIGNED_SENT.bits() | SigningCommitmentFlags::THEIR_COMMITMENT_SIGNED_SENT.bits();
78    }
79
80    #[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
81    #[serde(transparent)]
82    pub struct AwaitingTxSignaturesFlags: u32 {
83        const OUR_TX_SIGNATURES_SENT = 1;
84        const THEIR_TX_SIGNATURES_SENT = 1 << 1;
85        const TX_SIGNATURES_SENT = AwaitingTxSignaturesFlags::OUR_TX_SIGNATURES_SENT.bits() | AwaitingTxSignaturesFlags::THEIR_TX_SIGNATURES_SENT.bits();
86    }
87
88    #[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
89    #[serde(transparent)]
90    pub struct AwaitingChannelReadyFlags: u32 {
91        const OUR_CHANNEL_READY = 1;
92        const THEIR_CHANNEL_READY = 1 << 1;
93        const CHANNEL_READY = AwaitingChannelReadyFlags::OUR_CHANNEL_READY.bits() | AwaitingChannelReadyFlags::THEIR_CHANNEL_READY.bits();
94    }
95
96    #[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
97    #[serde(transparent)]
98    pub struct ShuttingDownFlags: u32 {
99        const OUR_SHUTDOWN_SENT = 1;
100        const THEIR_SHUTDOWN_SENT = 1 << 1;
101        const AWAITING_PENDING_TLCS = ShuttingDownFlags::OUR_SHUTDOWN_SENT.bits() | ShuttingDownFlags::THEIR_SHUTDOWN_SENT.bits();
102        const DROPPING_PENDING = 1 << 2;
103        const WAITING_COMMITMENT_CONFIRMATION = 1 << 3;
104    }
105
106    #[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
107    #[serde(transparent)]
108    pub struct CloseFlags: u32 {
109        const COOPERATIVE = 1;
110        const UNCOOPERATIVE_LOCAL = 1 << 1;
111        const ABANDONED = 1 << 2;
112        const FUNDING_ABORTED = 1 << 3;
113        const UNCOOPERATIVE_REMOTE = 1 << 4;
114        const WAITING_ONCHAIN_SETTLEMENT = 1 << 5;
115    }
116
117    #[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
118    #[serde(transparent)]
119    pub struct AppliedFlags: u8 {
120        const ADD = 1;
121        const REMOVE = 1 << 1;
122    }
123}
124
125/// The id of a tlc, it can be either offered or received.
126#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize, PartialOrd, Ord, Hash)]
127pub enum TLCId {
128    /// Offered tlc id
129    Offered(u64),
130    /// Received tlc id
131    Received(u64),
132}
133
134impl From<TLCId> for u64 {
135    fn from(id: TLCId) -> u64 {
136        match id {
137            TLCId::Offered(id) => id,
138            TLCId::Received(id) => id,
139        }
140    }
141}
142
143impl TLCId {
144    pub fn is_offered(&self) -> bool {
145        matches!(self, TLCId::Offered(_))
146    }
147
148    pub fn is_received(&self) -> bool {
149        !self.is_offered()
150    }
151
152    pub fn flip(&self) -> Self {
153        match self {
154            TLCId::Offered(id) => TLCId::Received(*id),
155            TLCId::Received(id) => TLCId::Offered(*id),
156        }
157    }
158
159    pub fn flip_mut(&mut self) {
160        *self = self.flip();
161    }
162}
163
164/// The status of an outbound tlc
165#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
166pub enum OutboundTlcStatus {
167    // Offered tlc created and sent to remote party
168    LocalAnnounced,
169    // Received ACK from remote party for this offered tlc
170    Committed,
171    // Remote party removed this tlc
172    RemoteRemoved,
173    // We received another RemoveTlc message from peer when we are waiting for the ack of the last one.
174    // So we need another ACK to confirm the removal.
175    RemoveWaitPrevAck,
176    // We have sent commitment signed to peer and waiting ACK for confirming this RemoveTlc
177    RemoveWaitAck,
178    // We have received the ACK for the RemoveTlc, it's safe to remove this tlc
179    RemoveAckConfirmed,
180}
181
182/// The status of an inbound tlc
183#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
184pub enum InboundTlcStatus {
185    // Received tlc from remote party, but not committed yet
186    RemoteAnnounced,
187    // We received another AddTlc peer message when we are waiting for the ack of the last one.
188    // So we need another ACK to confirm the addition.
189    AnnounceWaitPrevAck,
190    // We have sent commitment signed to peer and waiting ACK for confirming this AddTlc
191    AnnounceWaitAck,
192    // We have received ACK from peer and Committed this tlc
193    Committed,
194    // We have removed this tlc, but haven't received ACK from peer
195    LocalRemoved,
196    // We have received the ACK for the RemoveTlc, it's safe to remove this tlc
197    RemoveAckConfirmed,
198}
199
200/// The status of a tlc
201#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
202pub enum TlcStatus {
203    /// Outbound tlc
204    Outbound(OutboundTlcStatus),
205    /// Inbound tlc
206    Inbound(InboundTlcStatus),
207}
208
209impl TlcStatus {
210    pub fn as_outbound_status(&self) -> OutboundTlcStatus {
211        match self {
212            TlcStatus::Outbound(status) => status.clone(),
213            _ => {
214                unreachable!("unexpected status")
215            }
216        }
217    }
218
219    pub fn as_inbound_status(&self) -> InboundTlcStatus {
220        match self {
221            TlcStatus::Inbound(status) => status.clone(),
222            _ => {
223                unreachable!("unexpected status ")
224            }
225        }
226    }
227}
228
229/// The state of a channel.
230///
231/// Note: fiber-lib uses default serde (bincode-compatible), while fiber-json-types
232/// uses `#[serde(tag = "state_name", content = "state_flags")]` for JSON.
233/// This definition uses the default (bincode-compatible) representation.
234/// The JSON-specific tagged version is defined in fiber-json-types.
235#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
236pub enum ChannelState {
237    /// We are negotiating the parameters required for the channel prior to funding it.
238    /// For channels opened with external funding, this state is also used together with
239    /// `NegotiatingFundingFlags::AWAITING_EXTERNAL_FUNDING` to indicate that we are waiting
240    /// for the user to sign and submit the funding transaction externally.
241    NegotiatingFunding(NegotiatingFundingFlags),
242    /// We're collaborating with the other party on the funding transaction.
243    CollaboratingFundingTx(CollaboratingFundingTxFlags),
244    /// We have collaborated over the funding and are now waiting for CommitmentSigned messages.
245    SigningCommitment(SigningCommitmentFlags),
246    /// We've received and sent `commitment_signed` and are now waiting for both
247    /// party to collaborate on creating a valid funding transaction.
248    AwaitingTxSignatures(AwaitingTxSignaturesFlags),
249    /// We've received/sent `funding_created` and `funding_signed` and are thus now waiting on the
250    /// funding transaction to confirm.
251    AwaitingChannelReady(AwaitingChannelReadyFlags),
252    /// Both we and our counterparty consider the funding transaction confirmed and the channel is
253    /// now operational.
254    ChannelReady,
255    /// We've successfully negotiated a `closing_signed` dance.
256    ShuttingDown(ShuttingDownFlags),
257    /// This channel is closed.
258    Closed(CloseFlags),
259}
260
261#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
262pub enum ChannelConnectivityState {
263    Online,
264    Offline,
265    Syncing,
266}
267
268#[serde_as]
269#[derive(Clone, Debug, Serialize, Deserialize)]
270pub struct ExternalFundingPersistState {
271    #[serde_as(as = "EntityHex")]
272    pub funding_lock_script: Script,
273    #[serde_as(as = "Vec<EntityHex>")]
274    pub funding_lock_script_cell_deps: Vec<ckb_types::packed::CellDep>,
275    #[serde_as(as = "EntityHex")]
276    pub unsigned_funding_tx: Transaction,
277    pub started_at_ms: u64,
278    pub signed_submitted: bool,
279    pub peer_commitment_signed_received: bool,
280}
281
282impl ChannelState {
283    pub fn is_awaiting_external_funding(&self) -> bool {
284        matches!(
285            self,
286            ChannelState::NegotiatingFunding(flags)
287                if flags.contains(NegotiatingFundingFlags::AWAITING_EXTERNAL_FUNDING)
288        )
289    }
290
291    pub fn is_closed(&self) -> bool {
292        matches!(
293            self,
294            ChannelState::Closed(_)
295                | ChannelState::ShuttingDown(ShuttingDownFlags::WAITING_COMMITMENT_CONFIRMATION)
296        )
297    }
298
299    pub fn can_abort_funding(&self) -> bool {
300        match self {
301            ChannelState::NegotiatingFunding(_)
302            | ChannelState::CollaboratingFundingTx(_)
303            | ChannelState::SigningCommitment(_) => true,
304            ChannelState::AwaitingTxSignatures(flags)
305                if !flags.contains(AwaitingTxSignaturesFlags::OUR_TX_SIGNATURES_SENT) =>
306            {
307                true
308            }
309            _ => false,
310        }
311    }
312}
313
314impl ShuttingDownFlags {
315    pub fn is_ok_for_commitment_operation(&self) -> bool {
316        !self.contains(ShuttingDownFlags::DROPPING_PENDING)
317            && !self.contains(ShuttingDownFlags::WAITING_COMMITMENT_CONFIRMATION)
318    }
319}
320
321/// The initial commitment number for a channel.
322pub const INITIAL_COMMITMENT_NUMBER: u64 = 0;
323
324/// Tracks the local and remote commitment numbers.
325#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
326pub struct CommitmentNumbers {
327    pub local: u64,
328    pub remote: u64,
329}
330
331impl Default for CommitmentNumbers {
332    fn default() -> Self {
333        Self::new()
334    }
335}
336
337impl CommitmentNumbers {
338    pub fn new() -> Self {
339        Self {
340            local: INITIAL_COMMITMENT_NUMBER,
341            remote: INITIAL_COMMITMENT_NUMBER,
342        }
343    }
344
345    pub fn get_local(&self) -> u64 {
346        self.local
347    }
348
349    pub fn get_remote(&self) -> u64 {
350        self.remote
351    }
352
353    pub fn increment_local(&mut self) {
354        self.local += 1;
355    }
356
357    pub fn increment_remote(&mut self) {
358        self.remote += 1;
359    }
360}
361
362/// Channel constraints for TLC value and number limits.
363#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, Default)]
364pub struct ChannelConstraints {
365    /// The maximum value that can be in pending TLCs.
366    pub max_tlc_value_in_flight: u128,
367    /// The maximum number of TLCs that can be accepted.
368    pub max_tlc_number_in_flight: u64,
369}
370
371impl ChannelConstraints {
372    pub fn new(max_tlc_value_in_flight: u128, max_tlc_number_in_flight: u64) -> Self {
373        Self {
374            max_tlc_value_in_flight,
375            max_tlc_number_in_flight,
376        }
377    }
378}
379
380/// TLC-related information for a channel.
381/// We can update this information through the channel update message.
382#[derive(Default, Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
383pub struct ChannelTlcInfo {
384    /// The timestamp when the following information is updated.
385    pub timestamp: u64,
386
387    /// Whether this channel is enabled for TLC forwarding or not.
388    pub enabled: bool,
389
390    /// The fee rate for TLC transfers. We only have these values set when
391    /// this is a public channel. Both sides may set this value differently.
392    /// This is a fee that is paid by the sender of the TLC.
393    /// The detailed calculation for the fee of forwarding TLCs is
394    /// `fee = round_above(tlc_fee_proportional_millionths * tlc_value / 1,000,000)`.
395    pub tlc_fee_proportional_millionths: u128,
396
397    /// The expiry delta timestamp, in milliseconds, for the TLC.
398    pub tlc_expiry_delta: u64,
399
400    /// The minimal TLC value we can receive in relay TLC.
401    pub tlc_minimum_value: u128,
402}
403
404impl ChannelTlcInfo {
405    /// Create a new `ChannelTlcInfo` with the given parameters.
406    pub fn new(
407        tlc_minimum_value: u128,
408        tlc_expiry_delta: u64,
409        tlc_fee_proportional_millionths: u128,
410        timestamp: u64,
411    ) -> Self {
412        Self {
413            tlc_minimum_value,
414            tlc_expiry_delta,
415            tlc_fee_proportional_millionths,
416            enabled: true,
417            timestamp,
418        }
419    }
420}
421
422/// One counterparty's public keys which do not change over the life of a channel.
423#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
424pub struct ChannelBasePublicKeys {
425    /// The public key which is used to sign all commitment transactions, as it appears in the
426    /// on-chain channel lock-in 2-of-2 multisig output.
427    pub funding_pubkey: Pubkey,
428    /// The base point which is used (with derive_public_key) to derive a per-commitment public key
429    /// which is used to encumber HTLC-in-flight outputs.
430    pub tlc_base_key: Pubkey,
431}
432
433/// When we are forwarding a TLC, we need to know the previous TLC information.
434/// This struct keeps the information of the previous TLC.
435#[derive(Debug, Copy, Clone, Serialize, Deserialize, Eq, PartialEq, Hash)]
436pub struct PrevTlcInfo {
437    pub prev_channel_id: Hash256,
438    /// The TLC is always a received TLC because we are forwarding it.
439    pub prev_tlc_id: u64,
440    pub forwarding_fee: u128,
441    pub shared_secret: Option<[u8; 32]>,
442}
443
444impl PrevTlcInfo {
445    pub fn new(prev_channel_id: Hash256, prev_tlc_id: u64, forwarding_fee: u128) -> Self {
446        Self {
447            prev_channel_id,
448            prev_tlc_id,
449            forwarding_fee,
450            shared_secret: None,
451        }
452    }
453
454    pub fn new_with_shared_secret(
455        prev_channel_id: Hash256,
456        prev_tlc_id: u64,
457        forwarding_fee: u128,
458        shared_secret: [u8; 32],
459    ) -> Self {
460        Self {
461            prev_channel_id,
462            prev_tlc_id,
463            forwarding_fee,
464            shared_secret: Some(shared_secret),
465        }
466    }
467}
468
469#[derive(Clone, Serialize, Deserialize, Eq, PartialEq)]
470pub struct TlcInfo {
471    pub status: TlcStatus,
472    pub tlc_id: TLCId,
473    pub amount: u128,
474    pub payment_hash: Hash256,
475    /// bolt04 total amount of the payment, must exist if payment secret is set
476    pub total_amount: Option<u128>,
477    /// bolt04 payment secret, only exists for last hop in multi-path payment
478    pub payment_secret: Option<Hash256>,
479    /// The attempt id associate with the tlc, only on outbound tlc
480    /// only exists for first hop in multi-path payment
481    pub attempt_id: Option<u64>,
482    pub expiry: u64,
483    pub hash_algorithm: HashAlgorithm,
484    // the onion packet for multi-hop payment
485    pub onion_packet: Option<PaymentOnionPacket>,
486    /// Shared secret used in forwarding.
487    ///
488    /// Save it to backward errors. Use all zeros when no shared secrets are available.
489    pub shared_secret: [u8; 32],
490    #[serde(default)]
491    pub is_trampoline_hop: bool,
492    pub created_at: CommitmentNumbers,
493    pub removed_reason: Option<RemoveTlcReason>,
494
495    /// Note: `forwarding_tlc` is used to track the tlc chain for a multi-tlc payment.
496    ///
497    /// For an outbound tlc, this field records the previous (upstream) tlc,
498    /// so we can walk backward when removing tlcs.
499    ///
500    /// For an inbound tlc, this field records the next (downstream) tlc,
501    /// so we can continue tracking the forwarding path.
502    ///
503    /// Example:
504    ///
505    ///   Node A ---------> Node B ------------> Node C ------------> Node D
506    ///   tlc_1  ---------> tlc_1(in) ---------> tlc_2(in) ---------> tlc_3
507    ///                     tlc_2(out)           tlc_3(out)
508    ///                forwarding_tlc        forwarding_tlc
509    ///
510    ///   forwarding_tlc relations:
511    ///
512    ///   - Node B:
513    ///     - inbound: tlc_1.forwarding_tlc = Some((channel_BC, tlc2_id))
514    ///     - outbound: tlc_2.forwarding_tlc = Some((channel_AB, tlc1_id))
515    ///
516    ///   - Node C:
517    ///     - inbound: tlc_2.forwarding_tlc = Some((channel_CD, tlc3_id))
518    ///     - outbound: tlc_3.forwarding_tlc = Some((channel_BC, tlc2_id))
519    ///
520    pub forwarding_tlc: Option<(Hash256, u64)>,
521    pub removed_confirmed_at: Option<u64>,
522    pub applied_flags: AppliedFlags,
523}
524
525use std::fmt;
526use std::time::Duration;
527
528impl fmt::Debug for TlcInfo {
529    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
530        f.debug_struct("TlcInfo")
531            .field("status", &self.status)
532            .field("tlc_id", &self.tlc_id)
533            .field("amount", &self.amount)
534            .field("payment_hash", &self.payment_hash)
535            .field("expiry", &self.expiry)
536            .field("created_at", &self.created_at)
537            .field("removed_reason", &self.removed_reason)
538            .field("applied_flags", &self.applied_flags)
539            .finish()
540    }
541}
542
543impl TlcInfo {
544    pub fn log(&self) -> String {
545        format!(
546            "id: {:?} status: {:?} amount: {:?} removed: {:?} hash: {:?} ",
547            &self.tlc_id, self.status, self.amount, self.removed_reason, self.payment_hash,
548        )
549    }
550
551    pub fn id(&self) -> u64 {
552        self.tlc_id.into()
553    }
554
555    pub fn is_offered(&self) -> bool {
556        self.tlc_id.is_offered()
557    }
558
559    pub fn is_received(&self) -> bool {
560        !self.is_offered()
561    }
562
563    pub fn get_commitment_numbers(&self) -> CommitmentNumbers {
564        self.created_at
565    }
566
567    pub fn flip_mut(&mut self) {
568        self.tlc_id.flip_mut();
569    }
570
571    pub fn outbound_status(&self) -> OutboundTlcStatus {
572        self.status.as_outbound_status()
573    }
574
575    pub fn inbound_status(&self) -> InboundTlcStatus {
576        self.status.as_inbound_status()
577    }
578
579    pub fn is_fail_remove_confirmed(&self) -> bool {
580        matches!(self.removed_reason, Some(RemoveTlcReason::RemoveTlcFail(_)))
581            && matches!(
582                self.status,
583                TlcStatus::Outbound(OutboundTlcStatus::RemoveAckConfirmed)
584                    | TlcStatus::Outbound(OutboundTlcStatus::RemoveWaitAck)
585                    | TlcStatus::Inbound(InboundTlcStatus::RemoveAckConfirmed)
586            )
587    }
588
589    /// Get the value for the field `htlc_type` in commitment lock witness.
590    /// - Lowest 1 bit: 0 if the tlc is offered by the remote party, 1 otherwise.
591    /// - High 7 bits:
592    ///     - 0: ckb hash
593    ///     - 1: sha256
594    pub fn get_htlc_type(&self) -> u8 {
595        let offered_flag = if self.is_offered() { 0u8 } else { 1u8 };
596        ((self.hash_algorithm as u8) << 1) + offered_flag
597    }
598}
599
600/// A collection of pending TLCs.
601#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, Default)]
602pub struct PendingTlcs {
603    pub tlcs: Vec<TlcInfo>,
604    pub next_tlc_id: u64,
605}
606
607impl PendingTlcs {
608    pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut TlcInfo> {
609        self.tlcs.iter_mut()
610    }
611
612    pub fn get_next_id(&self) -> u64 {
613        self.next_tlc_id
614    }
615
616    pub fn increment_next_id(&mut self) {
617        self.next_tlc_id += 1;
618    }
619
620    pub fn add_tlc(&mut self, tlc: TlcInfo) {
621        self.tlcs.push(tlc);
622    }
623}
624
625/// The state of all TLCs for a channel.
626#[derive(Default, Clone, Debug, Serialize, Deserialize)]
627pub struct TlcState {
628    pub offered_tlcs: PendingTlcs,
629    pub received_tlcs: PendingTlcs,
630    pub waiting_ack: bool,
631}
632
633impl TlcState {
634    pub fn info(&self) -> String {
635        format!(
636            "offer_tlcs: {:?} received_tlcs: {:?}",
637            self.offered_tlcs.tlcs.len(),
638            self.received_tlcs.tlcs.len(),
639        )
640    }
641
642    #[cfg(debug_assertions)]
643    pub fn debug(&self) {
644        let format_tlc_list = |tlcs: &[TlcInfo]| -> String {
645            if tlcs.is_empty() {
646                "    <none>".to_string()
647            } else {
648                tlcs.iter()
649                    .map(|tlc| format!("    {}", tlc.log()))
650                    .collect::<Vec<_>>()
651                    .join("\n")
652            }
653        };
654
655        let offered_str = format_tlc_list(&self.offered_tlcs.tlcs);
656        let received_str = format_tlc_list(&self.received_tlcs.tlcs);
657
658        if offered_str.contains("<none>") && received_str.contains("<none>") {
659            tracing::info!("TlcState: <none>");
660        } else {
661            tracing::info!(
662                "TlcState:\n  Offered:\n{}\n  Received:\n{}",
663                offered_str,
664                received_str
665            );
666        }
667    }
668
669    pub fn get_mut(&mut self, tlc_id: &TLCId) -> Option<&mut TlcInfo> {
670        self.offered_tlcs
671            .tlcs
672            .iter_mut()
673            .find(|tlc| tlc.tlc_id == *tlc_id)
674            .or_else(|| {
675                self.received_tlcs
676                    .tlcs
677                    .iter_mut()
678                    .find(|tlc| tlc.tlc_id == *tlc_id)
679            })
680    }
681
682    pub fn get(&self, tlc_id: &TLCId) -> Option<&TlcInfo> {
683        if tlc_id.is_offered() {
684            self.offered_tlcs
685                .tlcs
686                .iter()
687                .find(|tlc| tlc.tlc_id == *tlc_id)
688        } else {
689            self.received_tlcs
690                .tlcs
691                .iter()
692                .find(|tlc| tlc.tlc_id == *tlc_id)
693        }
694    }
695
696    pub fn get_committed_received_tlcs(&self) -> impl Iterator<Item = &TlcInfo> + '_ {
697        self.received_tlcs.tlcs.iter().filter(|tlc| {
698            debug_assert!(tlc.is_received());
699            matches!(tlc.inbound_status(), InboundTlcStatus::Committed)
700        })
701    }
702
703    pub fn get_expired_offered_tlcs(
704        &self,
705        expect_expiry: u64,
706    ) -> impl Iterator<Item = &TlcInfo> + '_ {
707        self.offered_tlcs.tlcs.iter().filter(move |tlc| {
708            tlc.outbound_status() != OutboundTlcStatus::LocalAnnounced
709                && tlc.removed_confirmed_at.is_none()
710                && tlc.expiry < expect_expiry
711        })
712    }
713
714    pub fn get_next_offering(&self) -> u64 {
715        self.offered_tlcs.get_next_id()
716    }
717
718    pub fn get_next_received(&self) -> u64 {
719        self.received_tlcs.get_next_id()
720    }
721
722    pub fn increment_offering(&mut self) {
723        self.offered_tlcs.increment_next_id();
724    }
725
726    pub fn increment_received(&mut self) {
727        self.received_tlcs.increment_next_id();
728    }
729
730    pub fn set_waiting_ack(&mut self, waiting_ack: bool) {
731        self.waiting_ack = waiting_ack;
732    }
733
734    pub fn all_tlcs(&self) -> impl Iterator<Item = &TlcInfo> + '_ {
735        self.offered_tlcs
736            .tlcs
737            .iter()
738            .chain(self.received_tlcs.tlcs.iter())
739    }
740
741    pub fn apply_remove_tlc(&mut self, tlc_id: TLCId) {
742        if tlc_id.is_offered() {
743            self.offered_tlcs.tlcs.retain(|tlc| tlc.tlc_id != tlc_id);
744        } else {
745            self.received_tlcs.tlcs.retain(|tlc| tlc.tlc_id != tlc_id);
746        }
747    }
748
749    pub fn add_offered_tlc(&mut self, tlc: TlcInfo) {
750        self.offered_tlcs.add_tlc(tlc);
751    }
752
753    pub fn add_received_tlc(&mut self, tlc: TlcInfo) {
754        self.received_tlcs.add_tlc(tlc);
755    }
756
757    pub fn set_received_tlc_removed(&mut self, tlc_id: u64, reason: RemoveTlcReason) -> Hash256 {
758        let tlc = self.get_mut(&TLCId::Received(tlc_id)).expect("get tlc");
759        assert_eq!(tlc.inbound_status(), InboundTlcStatus::Committed);
760        tlc.removed_reason = Some(reason);
761        tlc.status = TlcStatus::Inbound(InboundTlcStatus::LocalRemoved);
762        tlc.payment_hash
763    }
764
765    pub fn set_offered_tlc_removed(&mut self, tlc_id: u64, reason: RemoveTlcReason) -> Hash256 {
766        let tlc = self.get_mut(&TLCId::Offered(tlc_id)).expect("get tlc");
767        assert_eq!(tlc.outbound_status(), OutboundTlcStatus::Committed);
768        tlc.removed_reason = Some(reason);
769        tlc.status = TlcStatus::Outbound(OutboundTlcStatus::RemoteRemoved);
770        tlc.payment_hash
771    }
772
773    pub fn commitment_signed_tlcs(&self, for_remote: bool) -> impl Iterator<Item = &TlcInfo> + '_ {
774        self.offered_tlcs
775            .tlcs
776            .iter()
777            .filter(move |tlc| match tlc.outbound_status() {
778                OutboundTlcStatus::LocalAnnounced => for_remote,
779                OutboundTlcStatus::Committed => true,
780                OutboundTlcStatus::RemoteRemoved => for_remote,
781                OutboundTlcStatus::RemoveWaitPrevAck => for_remote,
782                OutboundTlcStatus::RemoveWaitAck => false,
783                OutboundTlcStatus::RemoveAckConfirmed => false,
784            })
785            .chain(
786                self.received_tlcs
787                    .tlcs
788                    .iter()
789                    .filter(move |tlc| match tlc.inbound_status() {
790                        InboundTlcStatus::RemoteAnnounced => !for_remote,
791                        InboundTlcStatus::AnnounceWaitPrevAck => !for_remote,
792                        InboundTlcStatus::AnnounceWaitAck => true,
793                        InboundTlcStatus::Committed => true,
794                        InboundTlcStatus::LocalRemoved => !for_remote,
795                        InboundTlcStatus::RemoveAckConfirmed => false,
796                    }),
797            )
798    }
799
800    pub fn update_for_commitment_signed(&mut self) -> bool {
801        for tlc in self.offered_tlcs.tlcs.iter_mut() {
802            if tlc.outbound_status() == OutboundTlcStatus::RemoteRemoved {
803                let status = if self.waiting_ack {
804                    OutboundTlcStatus::RemoveWaitPrevAck
805                } else {
806                    OutboundTlcStatus::RemoveWaitAck
807                };
808                tlc.status = TlcStatus::Outbound(status);
809            }
810        }
811        for tlc in self.received_tlcs.tlcs.iter_mut() {
812            if tlc.inbound_status() == InboundTlcStatus::RemoteAnnounced {
813                let status = if self.waiting_ack {
814                    InboundTlcStatus::AnnounceWaitPrevAck
815                } else {
816                    InboundTlcStatus::AnnounceWaitAck
817                };
818                tlc.status = TlcStatus::Inbound(status)
819            }
820        }
821        self.need_another_commitment_signed()
822    }
823
824    pub fn update_for_revoke_and_ack(&mut self, commitment_number: CommitmentNumbers) {
825        for tlc in self.offered_tlcs.tlcs.iter_mut() {
826            match tlc.outbound_status() {
827                OutboundTlcStatus::LocalAnnounced => {
828                    tlc.status = TlcStatus::Outbound(OutboundTlcStatus::Committed);
829                }
830                OutboundTlcStatus::RemoveWaitPrevAck => {
831                    tlc.status = TlcStatus::Outbound(OutboundTlcStatus::RemoveWaitAck);
832                }
833                OutboundTlcStatus::RemoveWaitAck => {
834                    tlc.status = TlcStatus::Outbound(OutboundTlcStatus::RemoveAckConfirmed);
835                    tlc.removed_confirmed_at = Some(commitment_number.get_local());
836                }
837                _ => {}
838            }
839        }
840
841        for tlc in self.received_tlcs.tlcs.iter_mut() {
842            match tlc.inbound_status() {
843                InboundTlcStatus::AnnounceWaitPrevAck => {
844                    tlc.status = TlcStatus::Inbound(InboundTlcStatus::AnnounceWaitAck);
845                }
846                InboundTlcStatus::AnnounceWaitAck => {
847                    tlc.status = TlcStatus::Inbound(InboundTlcStatus::Committed);
848                }
849                InboundTlcStatus::LocalRemoved => {
850                    tlc.status = TlcStatus::Inbound(InboundTlcStatus::RemoveAckConfirmed);
851                    tlc.removed_confirmed_at = Some(commitment_number.get_remote());
852                }
853                _ => {}
854            }
855        }
856    }
857
858    pub fn need_another_commitment_signed(&self) -> bool {
859        self.offered_tlcs.tlcs.iter().any(|tlc| {
860            let status = tlc.outbound_status();
861            matches!(
862                status,
863                OutboundTlcStatus::LocalAnnounced
864                    | OutboundTlcStatus::RemoteRemoved
865                    | OutboundTlcStatus::RemoveWaitPrevAck
866                    | OutboundTlcStatus::RemoveWaitAck
867            )
868        }) || self.received_tlcs.tlcs.iter().any(|tlc| {
869            let status = tlc.inbound_status();
870            matches!(
871                status,
872                InboundTlcStatus::RemoteAnnounced
873                    | InboundTlcStatus::AnnounceWaitPrevAck
874                    | InboundTlcStatus::AnnounceWaitAck
875            )
876        })
877    }
878}
879
880/// Command to add a new TLC to a channel.
881#[derive(Clone, Serialize, Deserialize, Eq, PartialEq, Hash)]
882pub struct AddTlcCommand {
883    pub amount: u128,
884    pub payment_hash: Hash256,
885    /// The attempt id associated with the TLC.
886    pub attempt_id: Option<u64>,
887    pub expiry: u64,
888    pub hash_algorithm: HashAlgorithm,
889    /// Onion packet for the next node.
890    pub onion_packet: Option<PaymentOnionPacket>,
891    /// Shared secret used in forwarding.
892    /// Save it for outbound (offered) TLC to backward errors.
893    /// Use all zeros when no shared secrets are available.
894    pub shared_secret: [u8; 32],
895    /// Whether this outbound TLC is the trampoline-boundary hop.
896    pub is_trampoline_hop: bool,
897    pub previous_tlc: Option<PrevTlcInfo>,
898}
899
900impl fmt::Debug for AddTlcCommand {
901    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
902        f.debug_struct("AddTlcCommand")
903            .field("amount", &self.amount)
904            .field("payment_hash", &self.payment_hash)
905            .field("attempt_id", &self.attempt_id)
906            .field("expiry", &self.expiry)
907            .field("hash_algorithm", &self.hash_algorithm)
908            .field("is_trampoline_hop", &self.is_trampoline_hop)
909            .field("previous_tlc", &self.previous_tlc)
910            .finish()
911    }
912}
913
914/// A retryable TLC operation that may need to be replayed after reconnection.
915#[derive(Clone, Serialize, Deserialize, Eq, PartialEq, Debug, Hash)]
916pub enum RetryableTlcOperation {
917    RemoveTlc(TLCId, RemoveTlcReason),
918    AddTlc(AddTlcCommand),
919}
920
921/// Message to add a TLC to the channel.
922#[derive(Clone, Serialize, Deserialize, Eq, PartialEq, Hash)]
923pub struct AddTlc {
924    pub channel_id: Hash256,
925    pub tlc_id: u64,
926    pub amount: u128,
927    pub payment_hash: Hash256,
928    pub expiry: u64,
929    pub hash_algorithm: HashAlgorithm,
930    pub onion_packet: Option<PaymentOnionPacket>,
931}
932
933impl fmt::Debug for AddTlc {
934    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
935        f.debug_struct("AddTlc")
936            .field("channel_id", &self.channel_id)
937            .field("tlc_id", &self.tlc_id)
938            .field("amount", &self.amount)
939            .field("payment_hash", &self.payment_hash)
940            .field("expiry", &self.expiry)
941            .field("hash_algorithm", &self.hash_algorithm)
942            .finish()
943    }
944}
945
946impl From<AddTlc> for molecule_fiber::AddTlc {
947    fn from(add_tlc: AddTlc) -> Self {
948        molecule_fiber::AddTlc::new_builder()
949            .channel_id(add_tlc.channel_id.into())
950            .tlc_id(add_tlc.tlc_id.pack())
951            .amount(add_tlc.amount.pack())
952            .payment_hash(add_tlc.payment_hash.into())
953            .expiry(add_tlc.expiry.pack())
954            .hash_algorithm(molecule::prelude::Byte::new(add_tlc.hash_algorithm as u8))
955            .onion_packet(
956                add_tlc
957                    .onion_packet
958                    .map(|p| p.into_bytes())
959                    .unwrap_or_default()
960                    .pack(),
961            )
962            .build()
963    }
964}
965
966impl TryFrom<molecule_fiber::AddTlc> for AddTlc {
967    type Error = anyhow::Error;
968
969    fn try_from(add_tlc: molecule_fiber::AddTlc) -> Result<Self, Self::Error> {
970        let onion_packet_bytes: Vec<u8> = add_tlc.onion_packet().unpack();
971        let onion_packet =
972            (!onion_packet_bytes.is_empty()).then(|| PaymentOnionPacket::new(onion_packet_bytes));
973        Ok(AddTlc {
974            onion_packet,
975            channel_id: add_tlc.channel_id().into(),
976            tlc_id: add_tlc.tlc_id().unpack(),
977            amount: add_tlc.amount().unpack(),
978            payment_hash: add_tlc.payment_hash().into(),
979            expiry: add_tlc.expiry().unpack(),
980            hash_algorithm: add_tlc
981                .hash_algorithm()
982                .try_into()
983                .map_err(|e: crate::invoice::UnknownHashAlgorithmError| anyhow::anyhow!(e))?,
984        })
985    }
986}
987
988/// Message to remove a TLC from the channel.
989#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, Hash)]
990pub struct RemoveTlc {
991    pub channel_id: Hash256,
992    pub tlc_id: u64,
993    pub reason: RemoveTlcReason,
994}
995
996impl From<RemoveTlc> for molecule_fiber::RemoveTlc {
997    fn from(remove_tlc: RemoveTlc) -> Self {
998        molecule_fiber::RemoveTlc::new_builder()
999            .channel_id(remove_tlc.channel_id.into())
1000            .tlc_id(remove_tlc.tlc_id.pack())
1001            .reason(
1002                molecule_fiber::RemoveTlcReason::new_builder()
1003                    .set(remove_tlc.reason)
1004                    .build(),
1005            )
1006            .build()
1007    }
1008}
1009
1010impl TryFrom<molecule_fiber::RemoveTlc> for RemoveTlc {
1011    type Error = anyhow::Error;
1012
1013    fn try_from(remove_tlc: molecule_fiber::RemoveTlc) -> Result<Self, Self::Error> {
1014        Ok(RemoveTlc {
1015            channel_id: remove_tlc.channel_id().into(),
1016            tlc_id: remove_tlc.tlc_id().unpack(),
1017            reason: remove_tlc.reason().into(),
1018        })
1019    }
1020}
1021
1022/// TLC update message to resend during channel reestablishment.
1023#[derive(Clone, Serialize, Deserialize, Eq, PartialEq, Debug, Hash)]
1024pub enum TlcReplayUpdate {
1025    Add(AddTlc),
1026    Remove(RemoveTlc),
1027}
1028
1029/// Version for `CommitDiff` serialization compatibility.
1030pub const CURRENT_COMMIT_DIFF_VERSION: u8 = 2;
1031
1032fn default_commit_diff_version() -> u8 {
1033    CURRENT_COMMIT_DIFF_VERSION
1034}
1035
1036/// Optional template fields for `CommitmentSigned` replay.
1037#[serde_as]
1038#[derive(Clone, Debug, Serialize, Deserialize)]
1039pub struct CommitmentSignedTemplate {
1040    #[serde_as(as = "PubNonceAsBytes")]
1041    pub next_commitment_nonce: PubNonce,
1042    #[serde(default)]
1043    #[serde_as(as = "Option<PartialSignatureAsBytes>")]
1044    pub funding_tx_partial_signature: Option<PartialSignature>,
1045}
1046
1047/// Replay ordering hint when both revoke+commit are owed.
1048#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
1049pub enum ReplayOrderHint {
1050    RevokeThenCommit,
1051    CommitThenRevoke,
1052}
1053
1054/// Everything needed to resend a pending `CommitmentSigned` after reconnect.
1055#[serde_as]
1056#[derive(Clone, Debug, Serialize, Deserialize)]
1057pub struct CommitDiff {
1058    /// Structure version for backward/forward compatibility.
1059    #[serde(default = "default_commit_diff_version")]
1060    pub version: u8,
1061    /// Channel that owns this diff.
1062    #[serde(default)]
1063    pub channel_id: Hash256,
1064    /// Local/remote commitment numbers when this commitment was sent.
1065    #[serde(default)]
1066    pub local_commitment_number_at_send: u64,
1067    #[serde(default)]
1068    pub remote_commitment_number_at_send: u64,
1069    /// The commitment transaction (used for resign, not rebuilt).
1070    #[serde_as(as = "EntityHex")]
1071    pub commit_tx: Transaction,
1072    /// TLC updates included in this commitment (for resending).
1073    #[serde(default, alias = "tlc_updates")]
1074    pub replay_updates: Vec<TlcReplayUpdate>,
1075    /// Optional template fields for `CommitmentSigned` replay.
1076    #[serde(default)]
1077    pub commitment_signed_template: Option<CommitmentSignedTemplate>,
1078    /// Optional replay ordering hint when both revoke+commit are owed.
1079    #[serde(default)]
1080    pub replay_order_hint: Option<ReplayOrderHint>,
1081    /// Creation timestamp.
1082    #[serde(default, alias = "created_at")]
1083    pub created_at_ms: u64,
1084}
1085
1086/// Information about a channel shutdown.
1087#[serde_as]
1088#[derive(Clone, Serialize, Deserialize, Eq, PartialEq, Debug)]
1089pub struct ShutdownInfo {
1090    #[serde_as(as = "EntityHex")]
1091    pub close_script: Script,
1092    pub fee_rate: u64,
1093    #[serde_as(as = "Option<PartialSignatureAsBytes>")]
1094    pub signature: Option<PartialSignature>,
1095}
1096
1097/// Message to revoke the previous commitment and acknowledge the new one.
1098#[serde_as]
1099#[derive(Debug, Clone, Serialize, Deserialize)]
1100pub struct RevokeAndAck {
1101    pub channel_id: Hash256,
1102    #[serde_as(as = "PartialSignatureAsBytes")]
1103    pub revocation_partial_signature: PartialSignature,
1104    pub next_per_commitment_point: Pubkey,
1105    #[serde_as(as = "PubNonceAsBytes")]
1106    pub next_revocation_nonce: PubNonce,
1107}
1108// This struct holds the channel information that are only relevant when the channel
1109// is public. The information includes signatures to the channel announcement message,
1110// our config for the channel that will be published to the network (via ChannelUpdate).
1111// For ChannelUpdate config, only information on our side are saved here because we have no
1112// control to the config on the counterparty side. And they will publish
1113// the config to the network via another ChannelUpdate message.
1114#[serde_as]
1115#[derive(Default, Clone, Debug, Serialize, Deserialize)]
1116pub struct PublicChannelInfo {
1117    /// Channel announcement signatures, may be empty for private channel.
1118    #[serde_as(as = "Option<(_, PartialSignatureAsBytes)>")]
1119    pub local_channel_announcement_signature: Option<(EcdsaSignature, PartialSignature)>,
1120    #[serde_as(as = "Option<(_, PartialSignatureAsBytes)>")]
1121    pub remote_channel_announcement_signature: Option<(EcdsaSignature, PartialSignature)>,
1122    #[serde_as(as = "Option<PubNonceAsBytes>")]
1123    pub remote_channel_announcement_nonce: Option<PubNonce>,
1124    pub channel_announcement: Option<ChannelAnnouncement>,
1125    pub channel_update: Option<ChannelUpdate>,
1126}
1127
1128impl PublicChannelInfo {
1129    pub fn new() -> Self {
1130        Default::default()
1131    }
1132}
1133
1134/// A simple implementation of a channel signer that keeps the private keys in memory.
1135///
1136/// This implementation performs no policy checks and is insufficient by itself as
1137/// a secure external signer.
1138#[derive(Clone, Eq, PartialEq, Serialize, Deserialize)]
1139pub struct InMemorySigner {
1140    /// Holder secret key in the 2-of-2 multisig script of a channel.
1141    pub funding_key: Privkey,
1142    /// Holder HTLC secret key used in commitment transaction HTLC outputs.
1143    pub tlc_base_key: Privkey,
1144    /// SecNonce used to generate valid signature in musig.
1145    pub musig2_base_nonce: Privkey,
1146    /// Seed to derive above keys (per commitment).
1147    pub commitment_seed: [u8; 32],
1148}
1149
1150impl fmt::Debug for InMemorySigner {
1151    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1152        f.debug_struct("InMemorySigner")
1153            .field("funding_key", &"[REDACTED]")
1154            .field("tlc_base_key", &"[REDACTED]")
1155            .field("musig2_base_nonce", &"[REDACTED]")
1156            .field("commitment_seed", &"[REDACTED]")
1157            .finish()
1158    }
1159}
1160
1161/// Hash data with a salt using blake2b.
1162pub fn blake2b_hash_with_salt(data: &[u8], salt: &[u8]) -> [u8; 32] {
1163    let mut hasher = ckb_hash::new_blake2b();
1164    hasher.update(salt);
1165    hasher.update(data);
1166    let mut result = [0u8; 32];
1167    hasher.finalize(&mut result);
1168    result
1169}
1170
1171/// Compute a tweak value from a commitment point using blake2b.
1172pub fn get_tweak_by_commitment_point(commitment_point: &Pubkey) -> [u8; 32] {
1173    let mut hasher = ckb_hash::new_blake2b();
1174    hasher.update(&commitment_point.serialize());
1175    let mut result = [0u8; 32];
1176    hasher.finalize(&mut result);
1177    result
1178}
1179
1180/// Derive a private key by tweaking a secret with a commitment point.
1181pub fn derive_private_key(secret: &Privkey, commitment_point: &Pubkey) -> Privkey {
1182    secret.tweak(get_tweak_by_commitment_point(commitment_point))
1183}
1184
1185/// Derive a public key by tweaking a base key with a commitment point.
1186pub fn derive_public_key(base_key: &Pubkey, commitment_point: &Pubkey) -> Pubkey {
1187    base_key.tweak(get_tweak_by_commitment_point(commitment_point))
1188}
1189
1190/// Derive the TLC public key from a base key and commitment point.
1191pub fn derive_tlc_pubkey(base_key: &Pubkey, commitment_point: &Pubkey) -> Pubkey {
1192    derive_public_key(base_key, commitment_point)
1193}
1194
1195/// Derive the commitment secret for a given commitment number from a seed.
1196///
1197/// The commitment number should be in the range \[0, 2^48).
1198pub fn get_commitment_secret(commitment_seed: &[u8; 32], commitment_number: u64) -> [u8; 32] {
1199    let mut res: [u8; 32] = *commitment_seed;
1200    for i in 0..48 {
1201        let bitpos = 47 - i;
1202        if commitment_number & (1 << bitpos) == (1 << bitpos) {
1203            res[bitpos / 8] ^= 1 << (bitpos & 7);
1204            res = ckb_hash::blake2b_256(res);
1205        }
1206    }
1207    res
1208}
1209
1210/// Derive the commitment point (public key) for a given commitment number from a seed.
1211pub fn get_commitment_point(commitment_seed: &[u8; 32], commitment_number: u64) -> Pubkey {
1212    Privkey::from(&get_commitment_secret(commitment_seed, commitment_number)).pubkey()
1213}
1214
1215/// Context for musig2 nonce derivation.
1216pub enum Musig2Context {
1217    /// Commitment transaction context.
1218    Commitment,
1219    /// Revocation context.
1220    Revoke,
1221}
1222
1223impl std::fmt::Display for Musig2Context {
1224    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1225        let context_str = match self {
1226            Musig2Context::Commitment => "COMMITMENT",
1227            Musig2Context::Revoke => "REVOKE",
1228        };
1229        write!(f, "{}", context_str)
1230    }
1231}
1232
1233impl InMemorySigner {
1234    /// Generate an `InMemorySigner` from a seed.
1235    pub fn generate_from_seed(params: &[u8]) -> InMemorySigner {
1236        let seed = ckb_hash::blake2b_256(params);
1237
1238        let commitment_seed = {
1239            let mut hasher = ckb_hash::new_blake2b();
1240            hasher.update(&seed);
1241            hasher.update(&b"commitment seed"[..]);
1242            let mut result = [0u8; 32];
1243            hasher.finalize(&mut result);
1244            result
1245        };
1246
1247        let key_derive = |seed: &[u8], info: &[u8]| {
1248            let result = blake2b_hash_with_salt(seed, info);
1249            Privkey::from_slice(&result)
1250        };
1251
1252        let funding_key = key_derive(&seed, b"funding key");
1253        let tlc_base_key = key_derive(funding_key.as_ref(), b"HTLC base key");
1254        let musig2_base_nonce = key_derive(tlc_base_key.as_ref(), b"musig nocne");
1255
1256        InMemorySigner {
1257            funding_key,
1258            tlc_base_key,
1259            musig2_base_nonce,
1260            commitment_seed,
1261        }
1262    }
1263
1264    /// Get the base public keys for this signer.
1265    pub fn get_base_public_keys(&self) -> ChannelBasePublicKeys {
1266        ChannelBasePublicKeys {
1267            funding_pubkey: self.funding_key.pubkey(),
1268            tlc_base_key: self.tlc_base_key.pubkey(),
1269        }
1270    }
1271
1272    /// Returns the commitment point for the given commitment number.
1273    ///
1274    /// The commitment point is the public key derived from the commitment seed
1275    /// and the commitment number. It is used to derive the pubkeys used in
1276    /// TLC (htlc and revocation outputs).
1277    pub fn get_commitment_point(&self, commitment_number: u64) -> Pubkey {
1278        get_commitment_point(&self.commitment_seed, commitment_number)
1279    }
1280
1281    /// Returns the commitment secret for the given commitment number.
1282    pub fn get_commitment_secret(&self, commitment_number: u64) -> [u8; 32] {
1283        get_commitment_secret(&self.commitment_seed, commitment_number)
1284    }
1285
1286    /// Derive the TLC key for the given commitment number.
1287    pub fn derive_tlc_key(&self, new_commitment_number: u64) -> Privkey {
1288        let per_commitment_point = self.get_commitment_point(new_commitment_number);
1289        derive_private_key(&self.tlc_base_key, &per_commitment_point)
1290    }
1291
1292    /// Derive a musig2 nonce for the given commitment number and context.
1293    pub fn derive_musig2_nonce(&self, commitment_number: u64, context: Musig2Context) -> SecNonce {
1294        let commitment_point = self.get_commitment_point(commitment_number);
1295        let seckey = derive_private_key(&self.musig2_base_nonce, &commitment_point);
1296
1297        SecNonceBuilder::new(seckey.as_ref())
1298            .with_extra_input(&context.to_string())
1299            .build()
1300    }
1301}
1302
1303/// The status of a channel opening operation initiated by the local node.
1304#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
1305pub enum ChannelOpeningStatus {
1306    /// The `open_channel` RPC has been submitted and the `OpenChannel` message has been sent
1307    /// to the peer. We are waiting for the peer to respond with an `AcceptChannel` message.
1308    WaitingForPeer,
1309    /// The peer accepted the channel. We are now collaborating on the funding transaction.
1310    FundingTxBuilding,
1311    /// The funding transaction has been submitted to the chain and is awaiting confirmation.
1312    FundingTxBroadcasted,
1313    /// The funding transaction has been confirmed and the channel is fully open.
1314    ChannelReady,
1315    /// The channel opening failed. The `failure_detail` field contains the reason.
1316    Failed,
1317}
1318
1319/// A record that tracks a channel-opening attempt — either outbound (initiated by us)
1320/// or inbound (initiated by a remote peer and pending local acceptance).
1321///
1322/// Outbound records are created when `open_channel` is called.
1323/// Inbound records are created when an `OpenChannel` message is received from a peer.
1324#[serde_as]
1325#[derive(Clone, Debug, Serialize, Deserialize)]
1326pub struct ChannelOpenRecord {
1327    /// The channel ID. For outbound channels this is initially the temporary ID; it is
1328    /// updated to the final channel ID once the peer sends `AcceptChannel`. For inbound
1329    /// channels, the temp ID is replaced by the computed new ID when `accept_channel` is
1330    /// called.
1331    pub channel_id: Hash256,
1332    /// The remote peer public key.
1333    pub pubkey: Pubkey,
1334    /// Whether the local node is the accepting side (received the `OpenChannel` request).
1335    pub is_acceptor: bool,
1336    /// Current status of the opening process.
1337    pub status: ChannelOpeningStatus,
1338    /// The local node's funding amount for the channel.
1339    /// For outbound channels this is what the initiator contributes.
1340    /// For inbound channels this is set to the remote peer's funding amount.
1341    pub funding_amount: u128,
1342    /// Human-readable description of why the opening failed, set only when `status == Failed`.
1343    pub failure_detail: Option<String>,
1344    /// Timestamp (milliseconds since UNIX epoch) when the record was created.
1345    pub created_at: u64,
1346    /// Timestamp (milliseconds since UNIX epoch) of the last status update.
1347    pub last_updated_at: u64,
1348}
1349
1350impl ChannelOpenRecord {
1351    /// Create a new outbound record in the `WaitingForPeer` state.
1352    pub fn new(channel_id: Hash256, pubkey: Pubkey, funding_amount: u128) -> Self {
1353        let now = crate::now_timestamp_as_millis_u64();
1354        Self {
1355            channel_id,
1356            pubkey,
1357            is_acceptor: false,
1358            status: ChannelOpeningStatus::WaitingForPeer,
1359            funding_amount,
1360            failure_detail: None,
1361            created_at: now,
1362            last_updated_at: now,
1363        }
1364    }
1365
1366    /// Create a new inbound record in the `WaitingForPeer` state.
1367    /// Used when a remote peer's `OpenChannel` request is queued for local acceptance.
1368    pub fn new_inbound(channel_id: Hash256, pubkey: Pubkey, remote_funding_amount: u128) -> Self {
1369        let mut record = Self::new(channel_id, pubkey, remote_funding_amount);
1370        record.is_acceptor = true;
1371        record
1372    }
1373
1374    /// Transition to a new status.
1375    pub fn update_status(&mut self, status: ChannelOpeningStatus) {
1376        self.status = status;
1377        self.last_updated_at = crate::now_timestamp_as_millis_u64();
1378    }
1379
1380    /// Transition to `Failed` and record the reason.
1381    pub fn fail(&mut self, reason: String) {
1382        self.status = ChannelOpeningStatus::Failed;
1383        self.failure_detail = Some(reason);
1384        self.last_updated_at = crate::now_timestamp_as_millis_u64();
1385    }
1386}
1387
1388/// Store trait for persisting and querying outbound channel-opening records.
1389pub trait ChannelOpenRecordStore {
1390    /// Return all stored channel-opening records.
1391    fn get_channel_open_records(&self) -> Vec<ChannelOpenRecord>;
1392    /// Return the record for the given channel ID, if any.
1393    fn get_channel_open_record(&self, channel_id: &Hash256) -> Option<ChannelOpenRecord>;
1394    /// Persist (insert or overwrite) a channel-opening record.
1395    fn insert_channel_open_record(&self, record: ChannelOpenRecord);
1396    /// Delete the record for the given channel ID.
1397    fn delete_channel_open_record(&self, channel_id: &Hash256);
1398}
1399
1400/// A TLC that is pending notification for settlement.
1401#[derive(Clone, Serialize, Deserialize, Debug)]
1402pub struct PendingNotifySettleTlc {
1403    pub payment_hash: Hash256,
1404    pub tlc_id: u64,
1405    /// The expire time if the TLC should be held.
1406    pub hold_expire_at: Option<u64>,
1407}
1408
1409impl PendingNotifySettleTlc {
1410    /// Check if a PendingNotifySettleTlc should be held.
1411    pub fn pending_notify_should_hold(&self) -> bool {
1412        self.hold_expire_at.is_some()
1413    }
1414
1415    /// Get the remaining hold expiry duration for a PendingNotifySettleTlc.
1416    pub fn pending_notify_hold_expiry_duration(
1417        &self,
1418        now_millis_since_unix_epoch: u64,
1419    ) -> Duration {
1420        Duration::from_millis(
1421            self.hold_expire_at
1422                .unwrap_or_default()
1423                .saturating_sub(now_millis_since_unix_epoch),
1424        )
1425    }
1426}
1427
1428/// The core serializable state of a channel actor.
1429///
1430/// This struct contains all the persistable fields of a channel.
1431/// Runtime-only fields (like actor references) are managed separately in fiber-lib.
1432#[serde_as]
1433#[derive(Clone, Serialize, Deserialize)]
1434pub struct ChannelActorData {
1435    pub state: ChannelState,
1436    /// The data below are only relevant if the channel is public.
1437    pub public_channel_info: Option<PublicChannelInfo>,
1438
1439    pub local_tlc_info: ChannelTlcInfo,
1440    pub remote_tlc_info: Option<ChannelTlcInfo>,
1441
1442    /// The local public key used to establish p2p network connection.
1443    pub local_pubkey: Pubkey,
1444    /// The remote public key used to establish p2p network connection.
1445    pub remote_pubkey: Pubkey,
1446
1447    pub id: Hash256,
1448    #[serde_as(as = "Option<EntityHex>")]
1449    pub funding_tx: Option<Transaction>,
1450
1451    pub funding_tx_confirmed_at: Option<(H256, u32, u64)>,
1452
1453    #[serde_as(as = "Option<EntityHex>")]
1454    pub funding_udt_type_script: Option<Script>,
1455
1456    /// Is this channel initially inbound?
1457    /// An inbound channel is one where the counterparty is the funder of the channel.
1458    pub is_acceptor: bool,
1459
1460    /// Is this channel one-way?
1461    /// Combines with is_acceptor to determine if the channel able to send payment to the counterparty or not.
1462    pub is_one_way: bool,
1463
1464    /// The amount of CKB/UDT that we own in the channel.
1465    /// This value will only change after we have resolved a tlc.
1466    pub to_local_amount: u128,
1467    /// The amount of CKB/UDT that the remote owns in the channel.
1468    /// This value will only change after we have resolved a tlc.
1469    pub to_remote_amount: u128,
1470
1471    /// These two amounts used to keep the minimal ckb amount for the two parties.
1472    /// TLC operations will not affect these two amounts, only used to keep the commitment transactions
1473    /// to be valid, so that any party can close the channel at any time.
1474    pub local_reserved_ckb_amount: u64,
1475    pub remote_reserved_ckb_amount: u64,
1476
1477    /// The commitment fee rate is used to calculate the fee for the commitment transactions.
1478    /// The side who want to submit the commitment transaction will pay fee.
1479    pub commitment_fee_rate: u64,
1480
1481    /// The delay time for the commitment transaction, this value is set by the initiator of the channel.
1482    /// It must be a relative EpochNumberWithFraction in u64 format.
1483    pub commitment_delay_epoch: u64,
1484
1485    /// The fee rate used for funding transaction, the initiator may set it as `funding_fee_rate` option,
1486    /// if it's not set, DEFAULT_FEE_RATE will be used as default value, two sides will use the same fee rate.
1487    pub funding_fee_rate: u64,
1488
1489    /// Signer is used to sign the commitment transactions.
1490    pub signer: InMemorySigner,
1491
1492    /// Cached channel public keys for easier of access.
1493    pub local_channel_public_keys: ChannelBasePublicKeys,
1494
1495    /// Commitment numbers that are used to derive keys.
1496    /// This value is guaranteed to be 0 when channel is just created.
1497    pub commitment_numbers: CommitmentNumbers,
1498
1499    pub local_constraints: ChannelConstraints,
1500    pub remote_constraints: ChannelConstraints,
1501
1502    /// All the TLC related information.
1503    pub tlc_state: TlcState,
1504
1505    /// The retryable tlc operations that are waiting to be processed.
1506    pub retryable_tlc_operations: VecDeque<RetryableTlcOperation>,
1507    pub waiting_forward_tlc_tasks: HashMap<TLCId, [u8; 32]>,
1508
1509    /// The remote lock script for close channel, setup during the channel establishment.
1510    #[serde_as(as = "Option<EntityHex>")]
1511    pub remote_shutdown_script: Option<Script>,
1512    /// The local lock script for close channel.
1513    #[serde_as(as = "EntityHex")]
1514    pub local_shutdown_script: Script,
1515
1516    /// Basically the latest remote nonce sent by the peer with the CommitmentSigned message,
1517    /// but we will only update this field after we have sent a RevokeAndAck to the peer.
1518    #[serde_as(as = "Option<PubNonceAsBytes>")]
1519    pub last_committed_remote_nonce: Option<PubNonce>,
1520
1521    #[serde_as(as = "Option<PubNonceAsBytes>")]
1522    pub remote_revocation_nonce_for_verify: Option<PubNonce>,
1523    #[serde_as(as = "Option<PubNonceAsBytes>")]
1524    pub remote_revocation_nonce_for_send: Option<PubNonce>,
1525    #[serde_as(as = "Option<PubNonceAsBytes>")]
1526    pub remote_revocation_nonce_for_next: Option<PubNonce>,
1527
1528    /// The latest commitment transaction we're holding,
1529    /// it can be broadcasted to blockchain by us to force close the channel.
1530    #[serde_as(as = "Option<EntityHex>")]
1531    pub latest_commitment_transaction: Option<Transaction>,
1532
1533    /// All the commitment point that are sent from the counterparty.
1534    /// We need to save all these points to derive the keys for the commitment transactions.
1535    pub remote_commitment_points: Vec<(u64, Pubkey)>,
1536    pub remote_channel_public_keys: Option<ChannelBasePublicKeys>,
1537
1538    /// The shutdown info for both local and remote, setup by the shutdown command or message.
1539    pub local_shutdown_info: Option<ShutdownInfo>,
1540    pub remote_shutdown_info: Option<ShutdownInfo>,
1541
1542    /// Transaction hash of the shutdown transaction.
1543    /// The shutdown transaction can be COOPERATIVE or UNCOOPERATIVE.
1544    pub shutdown_transaction_hash: Option<H256>,
1545
1546    /// A flag to indicate whether the channel is reestablishing,
1547    /// we won't process any messages until the channel is reestablished.
1548    pub reestablishing: bool,
1549    pub last_revoke_ack_msg: Option<RevokeAndAck>,
1550
1551    pub created_at: SystemTime,
1552
1553    /// TLC updates sent to peer since the last local CommitmentSigned.
1554    /// This preserves send order for reestablish replay.
1555    #[serde(default)]
1556    pub pending_replay_updates: Vec<TlcReplayUpdate>,
1557
1558    /// Tracks whether the last outbound sync message was RevokeAndAck.
1559    #[serde(default)]
1560    pub last_was_revoke: bool,
1561
1562    /// Runtime connectivity state persisted for restart recovery.
1563    pub connectivity_state: ChannelConnectivityState,
1564
1565    /// Persisted state for an in-progress external funding flow.
1566    #[serde(default)]
1567    pub external_funding: Option<ExternalFundingPersistState>,
1568}
1569
1570fn partial_signature_to_molecule(partial_signature: PartialSignature) -> MByte32 {
1571    MByte32::from_slice(partial_signature.serialize().as_ref()).expect("[Byte; 32] from [u8; 32]")
1572}
1573
1574fn pub_nonce_to_molecule(pub_nonce: PubNonce) -> molecule_fiber::PubNonce {
1575    molecule_fiber::PubNonce::from_slice(pub_nonce.to_bytes().as_ref())
1576        .expect("PubNonce from 66 bytes")
1577}
1578
1579impl From<PubNonce> for molecule_fiber::PubNonce {
1580    fn from(value: PubNonce) -> Self {
1581        molecule_fiber::PubNonce::from_slice(value.to_bytes().as_ref())
1582            .expect("valid pubnonce serialized to 66 bytes")
1583    }
1584}
1585
1586impl TryFrom<molecule_fiber::PubNonce> for PubNonce {
1587    type Error = musig2::errors::DecodeError<PubNonce>;
1588
1589    fn try_from(value: molecule_fiber::PubNonce) -> Result<Self, Self::Error> {
1590        PubNonce::from_bytes(value.as_slice())
1591    }
1592}
1593
1594impl From<RevokeAndAck> for molecule_fiber::RevokeAndAck {
1595    fn from(revoke_and_ack: RevokeAndAck) -> Self {
1596        molecule_fiber::RevokeAndAck::new_builder()
1597            .channel_id(revoke_and_ack.channel_id.into())
1598            .revocation_partial_signature(partial_signature_to_molecule(
1599                revoke_and_ack.revocation_partial_signature,
1600            ))
1601            .next_per_commitment_point(revoke_and_ack.next_per_commitment_point.into())
1602            .next_revocation_nonce(pub_nonce_to_molecule(revoke_and_ack.next_revocation_nonce))
1603            .build()
1604    }
1605}
1606
1607impl TryFrom<molecule_fiber::RevokeAndAck> for RevokeAndAck {
1608    type Error = anyhow::Error;
1609
1610    fn try_from(revoke_and_ack: molecule_fiber::RevokeAndAck) -> Result<Self, Self::Error> {
1611        Ok(RevokeAndAck {
1612            channel_id: revoke_and_ack.channel_id().into(),
1613            revocation_partial_signature: PartialSignature::from_slice(
1614                revoke_and_ack.revocation_partial_signature().as_slice(),
1615            )
1616            .map_err(|e| anyhow::anyhow!(e))?,
1617            next_per_commitment_point: revoke_and_ack
1618                .next_per_commitment_point()
1619                .try_into()
1620                .map_err(|e: secp256k1::Error| anyhow::anyhow!(e))?,
1621            next_revocation_nonce: PubNonce::from_bytes(
1622                revoke_and_ack.next_revocation_nonce().as_slice(),
1623            )
1624            .map_err(|e| anyhow::anyhow!("{}", e))?,
1625        })
1626    }
1627}
1628
1629/// The fulfillment of a TLC removal.
1630#[derive(Debug, Copy, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
1631pub struct RemoveTlcFulfill {
1632    pub payment_preimage: Hash256,
1633}
1634
1635/// The reason for removing a TLC.
1636#[derive(Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
1637pub enum RemoveTlcReason {
1638    RemoveTlcFulfill(RemoveTlcFulfill),
1639    RemoveTlcFail(TlcErrPacket),
1640}
1641
1642impl Debug for RemoveTlcReason {
1643    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1644        match self {
1645            RemoveTlcReason::RemoveTlcFulfill(_fulfill) => {
1646                write!(f, "RemoveTlcFulfill")
1647            }
1648            RemoveTlcReason::RemoveTlcFail(_fail) => {
1649                write!(f, "RemoveTlcFail")
1650            }
1651        }
1652    }
1653}
1654
1655impl RemoveTlcReason {
1656    /// Intermediate node backwards the error to the previous hop using the shared secret
1657    /// used in forwarding the onion packet.
1658    pub fn backward(self, shared_secret: &[u8; 32]) -> Self {
1659        match self {
1660            RemoveTlcReason::RemoveTlcFulfill(remove_tlc_fulfill) => {
1661                RemoveTlcReason::RemoveTlcFulfill(remove_tlc_fulfill)
1662            }
1663            RemoveTlcReason::RemoveTlcFail(remove_tlc_fail) => {
1664                RemoveTlcReason::RemoveTlcFail(remove_tlc_fail.backward(shared_secret))
1665            }
1666        }
1667    }
1668}
1669
1670impl From<RemoveTlcReason> for molecule_fiber::RemoveTlcReasonUnion {
1671    fn from(remove_tlc_reason: RemoveTlcReason) -> Self {
1672        match remove_tlc_reason {
1673            RemoveTlcReason::RemoveTlcFulfill(remove_tlc_fulfill) => {
1674                molecule_fiber::RemoveTlcReasonUnion::RemoveTlcFulfill(remove_tlc_fulfill.into())
1675            }
1676            RemoveTlcReason::RemoveTlcFail(remove_tlc_fail) => {
1677                molecule_fiber::RemoveTlcReasonUnion::TlcErrPacket(remove_tlc_fail.into())
1678            }
1679        }
1680    }
1681}
1682
1683impl From<RemoveTlcReason> for molecule_fiber::RemoveTlcReason {
1684    fn from(remove_tlc_reason: RemoveTlcReason) -> Self {
1685        molecule_fiber::RemoveTlcReason::new_builder()
1686            .set(remove_tlc_reason)
1687            .build()
1688    }
1689}
1690
1691impl From<molecule_fiber::RemoveTlcReason> for RemoveTlcReason {
1692    fn from(remove_tlc_reason: molecule_fiber::RemoveTlcReason) -> Self {
1693        match remove_tlc_reason.to_enum() {
1694            molecule_fiber::RemoveTlcReasonUnion::RemoveTlcFulfill(remove_tlc_fulfill) => {
1695                RemoveTlcReason::RemoveTlcFulfill(remove_tlc_fulfill.into())
1696            }
1697            molecule_fiber::RemoveTlcReasonUnion::TlcErrPacket(remove_tlc_fail) => {
1698                RemoveTlcReason::RemoveTlcFail(remove_tlc_fail.into())
1699            }
1700        }
1701    }
1702}
1703
1704impl From<RemoveTlcFulfill> for molecule_fiber::RemoveTlcFulfill {
1705    fn from(remove_tlc_fulfill: RemoveTlcFulfill) -> Self {
1706        molecule_fiber::RemoveTlcFulfill::new_builder()
1707            .payment_preimage(remove_tlc_fulfill.payment_preimage.into())
1708            .build()
1709    }
1710}
1711
1712impl From<molecule_fiber::RemoveTlcFulfill> for RemoveTlcFulfill {
1713    fn from(remove_tlc_fulfill: molecule_fiber::RemoveTlcFulfill) -> Self {
1714        RemoveTlcFulfill {
1715            payment_preimage: remove_tlc_fulfill.payment_preimage().into(),
1716        }
1717    }
1718}
1719
1720/// The channel update info with a single direction of channel.
1721///
1722/// This is a pure data struct used by both the internal graph representation
1723/// and the RPC JSON response types.
1724#[serde_as]
1725#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
1726pub struct ChannelUpdateInfo {
1727    /// The timestamp is the time when the channel update was received by the node.
1728    #[serde_as(as = "crate::U64Hex")]
1729    pub timestamp: u64,
1730    /// Whether the channel can be currently used for payments (in this one direction).
1731    pub enabled: bool,
1732    /// The exact amount of balance that we can send to the other party via the channel.
1733    #[serde_as(as = "Option<crate::U128Hex>")]
1734    pub outbound_liquidity: Option<u128>,
1735    /// The difference in htlc expiry values that you must have when routing through this channel (in milliseconds).
1736    #[serde_as(as = "crate::U64Hex")]
1737    pub tlc_expiry_delta: u64,
1738    /// The minimum value, which must be relayed to the next hop via the channel
1739    #[serde_as(as = "crate::U128Hex")]
1740    pub tlc_minimum_value: u128,
1741    /// The forwarding fee rate for the channel.
1742    #[serde_as(as = "crate::U64Hex")]
1743    pub fee_rate: u64,
1744}
1745
1746impl From<&ChannelTlcInfo> for ChannelUpdateInfo {
1747    fn from(info: &ChannelTlcInfo) -> Self {
1748        Self {
1749            timestamp: info.timestamp,
1750            enabled: info.enabled,
1751            outbound_liquidity: None,
1752            tlc_expiry_delta: info.tlc_expiry_delta,
1753            tlc_minimum_value: info.tlc_minimum_value,
1754            fee_rate: info.tlc_fee_proportional_millionths as u64,
1755        }
1756    }
1757}
1758
1759impl From<ChannelTlcInfo> for ChannelUpdateInfo {
1760    fn from(info: ChannelTlcInfo) -> Self {
1761        Self::from(&info)
1762    }
1763}
1764
1765impl From<crate::protocol::ChannelUpdate> for ChannelUpdateInfo {
1766    fn from(update: crate::protocol::ChannelUpdate) -> Self {
1767        Self::from(&update)
1768    }
1769}
1770
1771impl From<&crate::protocol::ChannelUpdate> for ChannelUpdateInfo {
1772    fn from(update: &crate::protocol::ChannelUpdate) -> Self {
1773        Self {
1774            timestamp: update.timestamp,
1775            enabled: !update.is_disabled(),
1776            outbound_liquidity: None,
1777            tlc_expiry_delta: update.tlc_expiry_delta,
1778            tlc_minimum_value: update.tlc_minimum_value,
1779            fee_rate: update.tlc_fee_proportional_millionths as u64,
1780        }
1781    }
1782}