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, TlcErrPacket, TlcErrPacketError};
7use crate::protocol::{ChannelAnnouncement, ChannelUpdate, EcdsaSignature};
8use crate::serde_utils::PartialSignatureAsBytes;
9use crate::serde_utils::PubNonceAsBytes;
10use crate::EntityHex;
11use crate::Hash256;
12use crate::Privkey;
13use crate::Pubkey;
14use bitflags::bitflags;
15use ckb_types::packed::Byte32 as MByte32;
16use ckb_types::packed::Script;
17use ckb_types::packed::Transaction;
18use ckb_types::prelude::{Pack, Unpack};
19use ckb_types::H256;
20use molecule::prelude::{Builder, Entity};
21use musig2::secp::{Point, Scalar};
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 accepted by a participant.
363#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, Default)]
364pub struct ChannelConstraints {
365    /// The maximum total value of pending TLCs this participant will accept.
366    pub max_tlc_value_in_flight: u128,
367    /// The maximum number of pending TLCs this participant will accept.
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_with_shared_secret(
446        prev_channel_id: Hash256,
447        prev_tlc_id: u64,
448        forwarding_fee: u128,
449        shared_secret: [u8; 32],
450    ) -> Self {
451        Self {
452            prev_channel_id,
453            prev_tlc_id,
454            forwarding_fee,
455            shared_secret: Some(shared_secret),
456        }
457    }
458}
459
460#[derive(Clone, Serialize, Deserialize, Eq, PartialEq)]
461pub struct TlcInfo {
462    pub status: TlcStatus,
463    pub tlc_id: TLCId,
464    pub amount: u128,
465    pub payment_hash: Hash256,
466    /// bolt04 total amount of the payment, must exist if payment secret is set
467    pub total_amount: Option<u128>,
468    /// bolt04 payment secret, only exists for last hop in multi-path payment
469    pub payment_secret: Option<Hash256>,
470    /// The attempt id associate with the tlc, only on outbound tlc
471    /// only exists for first hop in multi-path payment
472    pub attempt_id: Option<u64>,
473    pub expiry: u64,
474    pub hash_algorithm: HashAlgorithm,
475    // the onion packet for multi-hop payment
476    pub onion_packet: Option<PaymentOnionPacket>,
477    /// Shared secret used in forwarding.
478    ///
479    /// Save it to backward errors. Use all zeros when no shared secrets are available.
480    pub shared_secret: [u8; 32],
481    /// Compatibility field retained for persisted channel state.
482    ///
483    /// This used to mark a trampoline-boundary TLC for channel-level error wrapping. Trampoline
484    /// payment failures are now resolved at the network/payment layer instead, so this field should
485    /// not be used for new error attribution logic. Removing it requires a storage migration.
486    #[serde(default)]
487    pub is_trampoline_hop: bool,
488    pub created_at: CommitmentNumbers,
489    pub removed_reason: Option<RemoveTlcReason>,
490
491    /// Note: `forwarding_tlc` is used to track the tlc chain for a multi-tlc payment.
492    ///
493    /// For an outbound tlc, this field records the previous (upstream) tlc,
494    /// so we can walk backward when removing tlcs.
495    ///
496    /// For an inbound tlc, this field records the next (downstream) tlc,
497    /// so we can continue tracking the forwarding path.
498    ///
499    /// Example:
500    ///
501    ///   Node A ---------> Node B ------------> Node C ------------> Node D
502    ///   tlc_1  ---------> tlc_1(in) ---------> tlc_2(in) ---------> tlc_3
503    ///                     tlc_2(out)           tlc_3(out)
504    ///                forwarding_tlc        forwarding_tlc
505    ///
506    ///   forwarding_tlc relations:
507    ///
508    ///   - Node B:
509    ///     - inbound: tlc_1.forwarding_tlc = Some((channel_BC, tlc2_id))
510    ///     - outbound: tlc_2.forwarding_tlc = Some((channel_AB, tlc1_id))
511    ///
512    ///   - Node C:
513    ///     - inbound: tlc_2.forwarding_tlc = Some((channel_CD, tlc3_id))
514    ///     - outbound: tlc_3.forwarding_tlc = Some((channel_BC, tlc2_id))
515    ///
516    pub forwarding_tlc: Option<(Hash256, u64)>,
517    pub removed_confirmed_at: Option<u64>,
518    pub applied_flags: AppliedFlags,
519}
520
521use std::fmt;
522use std::time::Duration;
523
524impl fmt::Debug for TlcInfo {
525    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
526        f.debug_struct("TlcInfo")
527            .field("status", &self.status)
528            .field("tlc_id", &self.tlc_id)
529            .field("amount", &self.amount)
530            .field("payment_hash", &self.payment_hash)
531            .field("expiry", &self.expiry)
532            .field("created_at", &self.created_at)
533            .field("removed_reason", &self.removed_reason)
534            .field("applied_flags", &self.applied_flags)
535            .finish()
536    }
537}
538
539impl TlcInfo {
540    pub fn log(&self) -> String {
541        format!(
542            "id: {:?} status: {:?} amount: {:?} removed: {:?} hash: {:?} ",
543            &self.tlc_id, self.status, self.amount, self.removed_reason, self.payment_hash,
544        )
545    }
546
547    pub fn id(&self) -> u64 {
548        self.tlc_id.into()
549    }
550
551    pub fn is_offered(&self) -> bool {
552        self.tlc_id.is_offered()
553    }
554
555    pub fn is_received(&self) -> bool {
556        !self.is_offered()
557    }
558
559    pub fn get_commitment_numbers(&self) -> CommitmentNumbers {
560        self.created_at
561    }
562
563    pub fn flip_mut(&mut self) {
564        self.tlc_id.flip_mut();
565    }
566
567    pub fn outbound_status(&self) -> OutboundTlcStatus {
568        self.status.as_outbound_status()
569    }
570
571    pub fn inbound_status(&self) -> InboundTlcStatus {
572        self.status.as_inbound_status()
573    }
574
575    pub fn is_fail_remove_confirmed(&self) -> bool {
576        matches!(self.removed_reason, Some(RemoveTlcReason::RemoveTlcFail(_)))
577            && matches!(
578                self.status,
579                TlcStatus::Outbound(OutboundTlcStatus::RemoveAckConfirmed)
580                    | TlcStatus::Outbound(OutboundTlcStatus::RemoveWaitAck)
581                    | TlcStatus::Inbound(InboundTlcStatus::RemoveAckConfirmed)
582            )
583    }
584
585    /// Get the value for the field `htlc_type` in commitment lock witness.
586    /// - Lowest 1 bit: 0 if the tlc is offered by the remote party, 1 otherwise.
587    /// - High 7 bits:
588    ///     - 0: ckb hash
589    ///     - 1: sha256
590    pub fn get_htlc_type(&self) -> u8 {
591        let offered_flag = if self.is_offered() { 0u8 } else { 1u8 };
592        ((self.hash_algorithm as u8) << 1) + offered_flag
593    }
594}
595
596/// A collection of pending TLCs.
597#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, Default)]
598pub struct PendingTlcs {
599    pub tlcs: Vec<TlcInfo>,
600    pub next_tlc_id: u64,
601}
602
603impl PendingTlcs {
604    pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut TlcInfo> {
605        self.tlcs.iter_mut()
606    }
607
608    pub fn get_next_id(&self) -> u64 {
609        self.next_tlc_id
610    }
611
612    pub fn increment_next_id(&mut self) {
613        self.next_tlc_id += 1;
614    }
615
616    pub fn add_tlc(&mut self, tlc: TlcInfo) {
617        self.tlcs.push(tlc);
618    }
619}
620
621/// The state of all TLCs for a channel.
622#[derive(Default, Clone, Debug, Serialize, Deserialize)]
623pub struct TlcState {
624    pub offered_tlcs: PendingTlcs,
625    pub received_tlcs: PendingTlcs,
626    pub waiting_ack: bool,
627}
628
629impl TlcState {
630    pub fn info(&self) -> String {
631        format!(
632            "offer_tlcs: {:?} received_tlcs: {:?}",
633            self.offered_tlcs.tlcs.len(),
634            self.received_tlcs.tlcs.len(),
635        )
636    }
637
638    #[cfg(debug_assertions)]
639    pub fn debug(&self) {
640        let format_tlc_list = |tlcs: &[TlcInfo]| -> String {
641            if tlcs.is_empty() {
642                "    <none>".to_string()
643            } else {
644                tlcs.iter()
645                    .map(|tlc| format!("    {}", tlc.log()))
646                    .collect::<Vec<_>>()
647                    .join("\n")
648            }
649        };
650
651        let offered_str = format_tlc_list(&self.offered_tlcs.tlcs);
652        let received_str = format_tlc_list(&self.received_tlcs.tlcs);
653
654        if offered_str.contains("<none>") && received_str.contains("<none>") {
655            tracing::info!("TlcState: <none>");
656        } else {
657            tracing::info!(
658                "TlcState:\n  Offered:\n{}\n  Received:\n{}",
659                offered_str,
660                received_str
661            );
662        }
663    }
664
665    pub fn get_mut(&mut self, tlc_id: &TLCId) -> Option<&mut TlcInfo> {
666        self.offered_tlcs
667            .tlcs
668            .iter_mut()
669            .find(|tlc| tlc.tlc_id == *tlc_id)
670            .or_else(|| {
671                self.received_tlcs
672                    .tlcs
673                    .iter_mut()
674                    .find(|tlc| tlc.tlc_id == *tlc_id)
675            })
676    }
677
678    pub fn get(&self, tlc_id: &TLCId) -> Option<&TlcInfo> {
679        if tlc_id.is_offered() {
680            self.offered_tlcs
681                .tlcs
682                .iter()
683                .find(|tlc| tlc.tlc_id == *tlc_id)
684        } else {
685            self.received_tlcs
686                .tlcs
687                .iter()
688                .find(|tlc| tlc.tlc_id == *tlc_id)
689        }
690    }
691
692    pub fn get_committed_received_tlcs(&self) -> impl Iterator<Item = &TlcInfo> + '_ {
693        self.received_tlcs.tlcs.iter().filter(|tlc| {
694            debug_assert!(tlc.is_received());
695            matches!(tlc.inbound_status(), InboundTlcStatus::Committed)
696        })
697    }
698
699    pub fn get_expired_offered_tlcs(
700        &self,
701        expect_expiry: u64,
702    ) -> impl Iterator<Item = &TlcInfo> + '_ {
703        self.offered_tlcs.tlcs.iter().filter(move |tlc| {
704            tlc.outbound_status() != OutboundTlcStatus::LocalAnnounced
705                && tlc.removed_confirmed_at.is_none()
706                && tlc.expiry < expect_expiry
707        })
708    }
709
710    pub fn get_next_offering(&self) -> u64 {
711        self.offered_tlcs.get_next_id()
712    }
713
714    pub fn get_next_received(&self) -> u64 {
715        self.received_tlcs.get_next_id()
716    }
717
718    pub fn increment_offering(&mut self) {
719        self.offered_tlcs.increment_next_id();
720    }
721
722    pub fn increment_received(&mut self) {
723        self.received_tlcs.increment_next_id();
724    }
725
726    pub fn set_waiting_ack(&mut self, waiting_ack: bool) {
727        self.waiting_ack = waiting_ack;
728    }
729
730    pub fn all_tlcs(&self) -> impl Iterator<Item = &TlcInfo> + '_ {
731        self.offered_tlcs
732            .tlcs
733            .iter()
734            .chain(self.received_tlcs.tlcs.iter())
735    }
736
737    pub fn apply_remove_tlc(&mut self, tlc_id: TLCId) {
738        if tlc_id.is_offered() {
739            self.offered_tlcs.tlcs.retain(|tlc| tlc.tlc_id != tlc_id);
740        } else {
741            self.received_tlcs.tlcs.retain(|tlc| tlc.tlc_id != tlc_id);
742        }
743    }
744
745    pub fn add_offered_tlc(&mut self, tlc: TlcInfo) {
746        self.offered_tlcs.add_tlc(tlc);
747    }
748
749    pub fn add_received_tlc(&mut self, tlc: TlcInfo) {
750        self.received_tlcs.add_tlc(tlc);
751    }
752
753    pub fn set_received_tlc_removed(&mut self, tlc_id: u64, reason: RemoveTlcReason) -> Hash256 {
754        let tlc = self.get_mut(&TLCId::Received(tlc_id)).expect("get tlc");
755        assert_eq!(tlc.inbound_status(), InboundTlcStatus::Committed);
756        tlc.removed_reason = Some(reason);
757        tlc.status = TlcStatus::Inbound(InboundTlcStatus::LocalRemoved);
758        tlc.payment_hash
759    }
760
761    pub fn set_offered_tlc_removed(&mut self, tlc_id: u64, reason: RemoveTlcReason) -> Hash256 {
762        let tlc = self.get_mut(&TLCId::Offered(tlc_id)).expect("get tlc");
763        assert_eq!(tlc.outbound_status(), OutboundTlcStatus::Committed);
764        tlc.removed_reason = Some(reason);
765        tlc.status = TlcStatus::Outbound(OutboundTlcStatus::RemoteRemoved);
766        tlc.payment_hash
767    }
768
769    pub fn commitment_signed_tlcs(&self, for_remote: bool) -> impl Iterator<Item = &TlcInfo> + '_ {
770        self.offered_tlcs
771            .tlcs
772            .iter()
773            .filter(move |tlc| match tlc.outbound_status() {
774                OutboundTlcStatus::LocalAnnounced => for_remote,
775                OutboundTlcStatus::Committed => true,
776                OutboundTlcStatus::RemoteRemoved => for_remote,
777                OutboundTlcStatus::RemoveWaitPrevAck => for_remote,
778                OutboundTlcStatus::RemoveWaitAck => false,
779                OutboundTlcStatus::RemoveAckConfirmed => false,
780            })
781            .chain(
782                self.received_tlcs
783                    .tlcs
784                    .iter()
785                    .filter(move |tlc| match tlc.inbound_status() {
786                        InboundTlcStatus::RemoteAnnounced => !for_remote,
787                        InboundTlcStatus::AnnounceWaitPrevAck => !for_remote,
788                        InboundTlcStatus::AnnounceWaitAck => true,
789                        InboundTlcStatus::Committed => true,
790                        InboundTlcStatus::LocalRemoved => !for_remote,
791                        InboundTlcStatus::RemoveAckConfirmed => false,
792                    }),
793            )
794    }
795
796    pub fn update_for_commitment_signed(&mut self) -> bool {
797        for tlc in self.offered_tlcs.tlcs.iter_mut() {
798            if tlc.outbound_status() == OutboundTlcStatus::RemoteRemoved {
799                let status = if self.waiting_ack {
800                    OutboundTlcStatus::RemoveWaitPrevAck
801                } else {
802                    OutboundTlcStatus::RemoveWaitAck
803                };
804                tlc.status = TlcStatus::Outbound(status);
805            }
806        }
807        for tlc in self.received_tlcs.tlcs.iter_mut() {
808            if tlc.inbound_status() == InboundTlcStatus::RemoteAnnounced {
809                let status = if self.waiting_ack {
810                    InboundTlcStatus::AnnounceWaitPrevAck
811                } else {
812                    InboundTlcStatus::AnnounceWaitAck
813                };
814                tlc.status = TlcStatus::Inbound(status)
815            }
816        }
817        self.need_another_commitment_signed()
818    }
819
820    pub fn update_for_revoke_and_ack(&mut self, commitment_number: CommitmentNumbers) {
821        for tlc in self.offered_tlcs.tlcs.iter_mut() {
822            match tlc.outbound_status() {
823                OutboundTlcStatus::LocalAnnounced => {
824                    tlc.status = TlcStatus::Outbound(OutboundTlcStatus::Committed);
825                }
826                OutboundTlcStatus::RemoveWaitPrevAck => {
827                    tlc.status = TlcStatus::Outbound(OutboundTlcStatus::RemoveWaitAck);
828                }
829                OutboundTlcStatus::RemoveWaitAck => {
830                    tlc.status = TlcStatus::Outbound(OutboundTlcStatus::RemoveAckConfirmed);
831                    tlc.removed_confirmed_at = Some(commitment_number.get_local());
832                }
833                _ => {}
834            }
835        }
836
837        for tlc in self.received_tlcs.tlcs.iter_mut() {
838            match tlc.inbound_status() {
839                InboundTlcStatus::AnnounceWaitPrevAck => {
840                    tlc.status = TlcStatus::Inbound(InboundTlcStatus::AnnounceWaitAck);
841                }
842                InboundTlcStatus::AnnounceWaitAck => {
843                    tlc.status = TlcStatus::Inbound(InboundTlcStatus::Committed);
844                }
845                InboundTlcStatus::LocalRemoved => {
846                    tlc.status = TlcStatus::Inbound(InboundTlcStatus::RemoveAckConfirmed);
847                    tlc.removed_confirmed_at = Some(commitment_number.get_remote());
848                }
849                _ => {}
850            }
851        }
852    }
853
854    pub fn need_another_commitment_signed(&self) -> bool {
855        self.offered_tlcs.tlcs.iter().any(|tlc| {
856            let status = tlc.outbound_status();
857            matches!(
858                status,
859                OutboundTlcStatus::LocalAnnounced
860                    | OutboundTlcStatus::RemoteRemoved
861                    | OutboundTlcStatus::RemoveWaitPrevAck
862                    | OutboundTlcStatus::RemoveWaitAck
863            )
864        }) || self.received_tlcs.tlcs.iter().any(|tlc| {
865            let status = tlc.inbound_status();
866            matches!(
867                status,
868                InboundTlcStatus::RemoteAnnounced
869                    | InboundTlcStatus::AnnounceWaitPrevAck
870                    | InboundTlcStatus::AnnounceWaitAck
871            )
872        })
873    }
874}
875
876/// Command to add a new TLC to a channel.
877#[derive(Clone, Serialize, Deserialize, Eq, PartialEq, Hash)]
878pub struct AddTlcCommand {
879    pub amount: u128,
880    pub payment_hash: Hash256,
881    /// The attempt id associated with the TLC.
882    pub attempt_id: Option<u64>,
883    pub expiry: u64,
884    pub hash_algorithm: HashAlgorithm,
885    /// Onion packet for the next node.
886    pub onion_packet: Option<PaymentOnionPacket>,
887    /// Shared secret used in forwarding.
888    /// Save it for outbound (offered) TLC to backward errors.
889    /// Use all zeros when no shared secrets are available.
890    pub shared_secret: [u8; 32],
891    /// Compatibility field retained for serialized retryable TLC operations.
892    ///
893    /// This used to mark a trampoline-boundary TLC for channel-level error wrapping. Trampoline
894    /// payment failures are now resolved at the network/payment layer instead, so this field should
895    /// not be used for new error attribution logic. Removing it requires a storage migration.
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/// Check if the TLC key derivation for a given base key and commitment point
1196/// would produce a valid (non-infinity) result.
1197///
1198/// This is used to validate peer-provided keys before accepting them,
1199/// preventing a malicious peer from crafting key pairs that would cause
1200/// `derive_tlc_pubkey` to panic with "valid public key" due to infinity.
1201pub fn is_tlc_key_derivation_safe(base_key: &Pubkey, commitment_point: &Pubkey) -> bool {
1202    let tweak = get_tweak_by_commitment_point(commitment_point);
1203    let Ok(scalar) = Scalar::from_slice(&tweak) else {
1204        return false;
1205    };
1206    let base_point = Point::from(base_key);
1207    let result = base_point + scalar.base_point_mul();
1208    result.not_inf().is_ok()
1209}
1210
1211/// Derive the commitment secret for a given commitment number from a seed.
1212///
1213/// The commitment number should be in the range \[0, 2^48).
1214pub fn get_commitment_secret(commitment_seed: &[u8; 32], commitment_number: u64) -> [u8; 32] {
1215    let mut res: [u8; 32] = *commitment_seed;
1216    for i in 0..48 {
1217        let bitpos = 47 - i;
1218        if commitment_number & (1 << bitpos) == (1 << bitpos) {
1219            res[bitpos / 8] ^= 1 << (bitpos & 7);
1220            res = ckb_hash::blake2b_256(res);
1221        }
1222    }
1223    res
1224}
1225
1226/// Derive the commitment point (public key) for a given commitment number from a seed.
1227pub fn get_commitment_point(commitment_seed: &[u8; 32], commitment_number: u64) -> Pubkey {
1228    Privkey::from(&get_commitment_secret(commitment_seed, commitment_number)).pubkey()
1229}
1230
1231/// Context for musig2 nonce derivation.
1232pub enum Musig2Context {
1233    /// Commitment transaction context.
1234    Commitment,
1235    /// Revocation context.
1236    Revoke,
1237}
1238
1239impl std::fmt::Display for Musig2Context {
1240    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1241        let context_str = match self {
1242            Musig2Context::Commitment => "COMMITMENT",
1243            Musig2Context::Revoke => "REVOKE",
1244        };
1245        write!(f, "{}", context_str)
1246    }
1247}
1248
1249impl InMemorySigner {
1250    /// Generate an `InMemorySigner` from a seed.
1251    pub fn generate_from_seed(params: &[u8]) -> InMemorySigner {
1252        let seed = ckb_hash::blake2b_256(params);
1253
1254        let commitment_seed = {
1255            let mut hasher = ckb_hash::new_blake2b();
1256            hasher.update(&seed);
1257            hasher.update(&b"commitment seed"[..]);
1258            let mut result = [0u8; 32];
1259            hasher.finalize(&mut result);
1260            result
1261        };
1262
1263        let key_derive = |seed: &[u8], info: &[u8]| {
1264            let result = blake2b_hash_with_salt(seed, info);
1265            Privkey::from_slice(&result)
1266        };
1267
1268        let funding_key = key_derive(&seed, b"funding key");
1269        let tlc_base_key = key_derive(funding_key.as_ref(), b"HTLC base key");
1270        let musig2_base_nonce = key_derive(tlc_base_key.as_ref(), b"musig nocne");
1271
1272        InMemorySigner {
1273            funding_key,
1274            tlc_base_key,
1275            musig2_base_nonce,
1276            commitment_seed,
1277        }
1278    }
1279
1280    /// Get the base public keys for this signer.
1281    pub fn get_base_public_keys(&self) -> ChannelBasePublicKeys {
1282        ChannelBasePublicKeys {
1283            funding_pubkey: self.funding_key.pubkey(),
1284            tlc_base_key: self.tlc_base_key.pubkey(),
1285        }
1286    }
1287
1288    /// Returns the commitment point for the given commitment number.
1289    ///
1290    /// The commitment point is the public key derived from the commitment seed
1291    /// and the commitment number. It is used to derive the pubkeys used in
1292    /// TLC (htlc and revocation outputs).
1293    pub fn get_commitment_point(&self, commitment_number: u64) -> Pubkey {
1294        get_commitment_point(&self.commitment_seed, commitment_number)
1295    }
1296
1297    /// Returns the commitment secret for the given commitment number.
1298    pub fn get_commitment_secret(&self, commitment_number: u64) -> [u8; 32] {
1299        get_commitment_secret(&self.commitment_seed, commitment_number)
1300    }
1301
1302    /// Derive the TLC key for the given commitment number.
1303    pub fn derive_tlc_key(&self, new_commitment_number: u64) -> Privkey {
1304        let per_commitment_point = self.get_commitment_point(new_commitment_number);
1305        derive_private_key(&self.tlc_base_key, &per_commitment_point)
1306    }
1307
1308    /// Derive a musig2 nonce for the given commitment number and context.
1309    pub fn derive_musig2_nonce(&self, commitment_number: u64, context: Musig2Context) -> SecNonce {
1310        let commitment_point = self.get_commitment_point(commitment_number);
1311        let seckey = derive_private_key(&self.musig2_base_nonce, &commitment_point);
1312
1313        SecNonceBuilder::new(seckey.as_ref())
1314            .with_extra_input(&context.to_string())
1315            .build()
1316    }
1317}
1318
1319/// The status of a channel opening operation initiated by the local node.
1320#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
1321pub enum ChannelOpeningStatus {
1322    /// The `open_channel` RPC has been submitted and the `OpenChannel` message has been sent
1323    /// to the peer. We are waiting for the peer to respond with an `AcceptChannel` message.
1324    WaitingForPeer,
1325    /// The peer accepted the channel. We are now collaborating on the funding transaction.
1326    FundingTxBuilding,
1327    /// The funding transaction has been submitted to the chain and is awaiting confirmation.
1328    FundingTxBroadcasted,
1329    /// The funding transaction has been confirmed and the channel is fully open.
1330    ChannelReady,
1331    /// The channel opening failed. The `failure_detail` field contains the reason.
1332    Failed,
1333}
1334
1335/// A record that tracks a channel-opening attempt — either outbound (initiated by us)
1336/// or inbound (initiated by a remote peer and pending local acceptance).
1337///
1338/// Outbound records are created when `open_channel` is called.
1339/// Inbound records are created when an `OpenChannel` message is received from a peer.
1340#[serde_as]
1341#[derive(Clone, Debug, Serialize, Deserialize)]
1342pub struct ChannelOpenRecord {
1343    /// The channel ID. For outbound channels this is initially the temporary ID; it is
1344    /// updated to the final channel ID once the peer sends `AcceptChannel`. For inbound
1345    /// channels, the temp ID is replaced by the computed new ID when `accept_channel` is
1346    /// called.
1347    pub channel_id: Hash256,
1348    /// The remote peer public key.
1349    pub pubkey: Pubkey,
1350    /// Whether the local node is the accepting side (received the `OpenChannel` request).
1351    pub is_acceptor: bool,
1352    /// Current status of the opening process.
1353    pub status: ChannelOpeningStatus,
1354    /// The local node's funding amount for the channel.
1355    /// For outbound channels this is what the initiator contributes.
1356    /// For inbound channels this is set to the remote peer's funding amount.
1357    pub funding_amount: u128,
1358    /// Human-readable description of why the opening failed, set only when `status == Failed`.
1359    pub failure_detail: Option<String>,
1360    /// Timestamp (milliseconds since UNIX epoch) when the record was created.
1361    pub created_at: u64,
1362    /// Timestamp (milliseconds since UNIX epoch) of the last status update.
1363    pub last_updated_at: u64,
1364}
1365
1366impl ChannelOpenRecord {
1367    /// Create a new outbound record in the `WaitingForPeer` state.
1368    pub fn new(channel_id: Hash256, pubkey: Pubkey, funding_amount: u128) -> Self {
1369        let now = crate::now_timestamp_as_millis_u64();
1370        Self {
1371            channel_id,
1372            pubkey,
1373            is_acceptor: false,
1374            status: ChannelOpeningStatus::WaitingForPeer,
1375            funding_amount,
1376            failure_detail: None,
1377            created_at: now,
1378            last_updated_at: now,
1379        }
1380    }
1381
1382    /// Create a new inbound record in the `WaitingForPeer` state.
1383    /// Used when a remote peer's `OpenChannel` request is queued for local acceptance.
1384    pub fn new_inbound(channel_id: Hash256, pubkey: Pubkey, remote_funding_amount: u128) -> Self {
1385        let mut record = Self::new(channel_id, pubkey, remote_funding_amount);
1386        record.is_acceptor = true;
1387        record
1388    }
1389
1390    /// Transition to a new status.
1391    pub fn update_status(&mut self, status: ChannelOpeningStatus) {
1392        self.status = status;
1393        self.last_updated_at = crate::now_timestamp_as_millis_u64();
1394    }
1395
1396    /// Transition to `Failed` and record the reason.
1397    pub fn fail(&mut self, reason: String) {
1398        self.status = ChannelOpeningStatus::Failed;
1399        self.failure_detail = Some(reason);
1400        self.last_updated_at = crate::now_timestamp_as_millis_u64();
1401    }
1402}
1403
1404/// Store trait for persisting and querying outbound channel-opening records.
1405pub trait ChannelOpenRecordStore {
1406    /// Return all stored channel-opening records.
1407    fn get_channel_open_records(&self) -> Vec<ChannelOpenRecord>;
1408    /// Return the record for the given channel ID, if any.
1409    fn get_channel_open_record(&self, channel_id: &Hash256) -> Option<ChannelOpenRecord>;
1410    /// Persist (insert or overwrite) a channel-opening record.
1411    fn insert_channel_open_record(&self, record: ChannelOpenRecord);
1412    /// Delete the record for the given channel ID.
1413    fn delete_channel_open_record(&self, channel_id: &Hash256);
1414}
1415
1416/// A TLC that is pending notification for settlement.
1417#[derive(Clone, Serialize, Deserialize, Debug)]
1418pub struct PendingNotifySettleTlc {
1419    pub payment_hash: Hash256,
1420    pub tlc_id: u64,
1421    /// The expire time if the TLC should be held.
1422    pub hold_expire_at: Option<u64>,
1423}
1424
1425impl PendingNotifySettleTlc {
1426    /// Check if a PendingNotifySettleTlc should be held.
1427    pub fn pending_notify_should_hold(&self) -> bool {
1428        self.hold_expire_at.is_some()
1429    }
1430
1431    /// Get the remaining hold expiry duration for a PendingNotifySettleTlc.
1432    pub fn pending_notify_hold_expiry_duration(
1433        &self,
1434        now_millis_since_unix_epoch: u64,
1435    ) -> Duration {
1436        Duration::from_millis(
1437            self.hold_expire_at
1438                .unwrap_or_default()
1439                .saturating_sub(now_millis_since_unix_epoch),
1440        )
1441    }
1442}
1443
1444/// The core serializable state of a channel actor.
1445///
1446/// This struct contains all the persistable fields of a channel.
1447/// Runtime-only fields (like actor references) are managed separately in fiber-lib.
1448#[serde_as]
1449#[derive(Clone, Serialize, Deserialize)]
1450pub struct ChannelActorData {
1451    pub state: ChannelState,
1452    /// The data below are only relevant if the channel is public.
1453    pub public_channel_info: Option<PublicChannelInfo>,
1454
1455    pub local_tlc_info: ChannelTlcInfo,
1456    pub remote_tlc_info: Option<ChannelTlcInfo>,
1457
1458    /// The local public key used to establish p2p network connection.
1459    pub local_pubkey: Pubkey,
1460    /// The remote public key used to establish p2p network connection.
1461    pub remote_pubkey: Pubkey,
1462
1463    pub id: Hash256,
1464    #[serde_as(as = "Option<EntityHex>")]
1465    pub funding_tx: Option<Transaction>,
1466
1467    pub funding_tx_confirmed_at: Option<(H256, u32, u64)>,
1468
1469    #[serde_as(as = "Option<EntityHex>")]
1470    pub funding_udt_type_script: Option<Script>,
1471
1472    /// Is this channel initially inbound?
1473    /// An inbound channel is one where the counterparty is the funder of the channel.
1474    pub is_acceptor: bool,
1475
1476    /// Is this channel one-way?
1477    /// Combines with is_acceptor to determine if the channel able to send payment to the counterparty or not.
1478    pub is_one_way: bool,
1479
1480    /// The amount of CKB/UDT that we own in the channel.
1481    /// This value will only change after we have resolved a tlc.
1482    pub to_local_amount: u128,
1483    /// The amount of CKB/UDT that the remote owns in the channel.
1484    /// This value will only change after we have resolved a tlc.
1485    pub to_remote_amount: u128,
1486
1487    /// These two amounts used to keep the minimal ckb amount for the two parties.
1488    /// TLC operations will not affect these two amounts, only used to keep the commitment transactions
1489    /// to be valid, so that any party can close the channel at any time.
1490    pub local_reserved_ckb_amount: u64,
1491    pub remote_reserved_ckb_amount: u64,
1492
1493    /// The commitment fee rate is used to calculate the fee for the commitment transactions.
1494    /// The side who want to submit the commitment transaction will pay fee.
1495    pub commitment_fee_rate: u64,
1496
1497    /// The delay time for the commitment transaction, this value is set by the initiator of the channel.
1498    /// It must be a relative EpochNumberWithFraction in u64 format.
1499    pub commitment_delay_epoch: u64,
1500
1501    /// The fee rate used for funding transaction, the initiator may set it as `funding_fee_rate` option,
1502    /// if it's not set, DEFAULT_FEE_RATE will be used as default value, two sides will use the same fee rate.
1503    pub funding_fee_rate: u64,
1504
1505    /// Signer is used to sign the commitment transactions.
1506    pub signer: InMemorySigner,
1507
1508    /// Cached channel public keys for easier of access.
1509    pub local_channel_public_keys: ChannelBasePublicKeys,
1510
1511    /// Commitment numbers that are used to derive keys.
1512    /// This value is guaranteed to be 0 when channel is just created.
1513    pub commitment_numbers: CommitmentNumbers,
1514
1515    pub local_constraints: ChannelConstraints,
1516    pub remote_constraints: ChannelConstraints,
1517
1518    /// All the TLC related information.
1519    pub tlc_state: TlcState,
1520
1521    /// The retryable tlc operations that are waiting to be processed.
1522    pub retryable_tlc_operations: VecDeque<RetryableTlcOperation>,
1523    pub waiting_forward_tlc_tasks: HashMap<TLCId, [u8; 32]>,
1524
1525    /// The remote lock script for close channel, setup during the channel establishment.
1526    #[serde_as(as = "Option<EntityHex>")]
1527    pub remote_shutdown_script: Option<Script>,
1528    /// The local lock script for close channel.
1529    #[serde_as(as = "EntityHex")]
1530    pub local_shutdown_script: Script,
1531
1532    /// Basically the latest remote nonce sent by the peer with the CommitmentSigned message,
1533    /// but we will only update this field after we have sent a RevokeAndAck to the peer.
1534    #[serde_as(as = "Option<PubNonceAsBytes>")]
1535    pub last_committed_remote_nonce: Option<PubNonce>,
1536
1537    #[serde_as(as = "Option<PubNonceAsBytes>")]
1538    pub remote_revocation_nonce_for_verify: Option<PubNonce>,
1539    #[serde_as(as = "Option<PubNonceAsBytes>")]
1540    pub remote_revocation_nonce_for_send: Option<PubNonce>,
1541    #[serde_as(as = "Option<PubNonceAsBytes>")]
1542    pub remote_revocation_nonce_for_next: Option<PubNonce>,
1543
1544    /// The latest commitment transaction we're holding,
1545    /// it can be broadcasted to blockchain by us to force close the channel.
1546    #[serde_as(as = "Option<EntityHex>")]
1547    pub latest_commitment_transaction: Option<Transaction>,
1548
1549    /// All the commitment point that are sent from the counterparty.
1550    /// We need to save all these points to derive the keys for the commitment transactions.
1551    pub remote_commitment_points: Vec<(u64, Pubkey)>,
1552    pub remote_channel_public_keys: Option<ChannelBasePublicKeys>,
1553
1554    /// The shutdown info for both local and remote, setup by the shutdown command or message.
1555    pub local_shutdown_info: Option<ShutdownInfo>,
1556    pub remote_shutdown_info: Option<ShutdownInfo>,
1557
1558    /// Transaction hash of the shutdown transaction.
1559    /// The shutdown transaction can be COOPERATIVE or UNCOOPERATIVE.
1560    pub shutdown_transaction_hash: Option<H256>,
1561
1562    /// A flag to indicate whether the channel is reestablishing,
1563    /// we won't process any messages until the channel is reestablished.
1564    pub reestablishing: bool,
1565    pub last_revoke_ack_msg: Option<RevokeAndAck>,
1566
1567    pub created_at: SystemTime,
1568
1569    /// TLC updates sent to peer since the last local CommitmentSigned.
1570    /// This preserves send order for reestablish replay.
1571    #[serde(default)]
1572    pub pending_replay_updates: Vec<TlcReplayUpdate>,
1573
1574    /// Tracks whether the last outbound sync message was RevokeAndAck.
1575    #[serde(default)]
1576    pub last_was_revoke: bool,
1577
1578    /// Runtime connectivity state persisted for restart recovery.
1579    pub connectivity_state: ChannelConnectivityState,
1580
1581    /// Persisted state for an in-progress external funding flow.
1582    #[serde(default)]
1583    pub external_funding: Option<ExternalFundingPersistState>,
1584}
1585
1586fn partial_signature_to_molecule(partial_signature: PartialSignature) -> MByte32 {
1587    MByte32::from_slice(partial_signature.serialize().as_ref()).expect("[Byte; 32] from [u8; 32]")
1588}
1589
1590fn pub_nonce_to_molecule(pub_nonce: PubNonce) -> molecule_fiber::PubNonce {
1591    molecule_fiber::PubNonce::from_slice(pub_nonce.to_bytes().as_ref())
1592        .expect("PubNonce from 66 bytes")
1593}
1594
1595impl From<PubNonce> for molecule_fiber::PubNonce {
1596    fn from(value: PubNonce) -> Self {
1597        molecule_fiber::PubNonce::from_slice(value.to_bytes().as_ref())
1598            .expect("valid pubnonce serialized to 66 bytes")
1599    }
1600}
1601
1602impl TryFrom<molecule_fiber::PubNonce> for PubNonce {
1603    type Error = musig2::errors::DecodeError<PubNonce>;
1604
1605    fn try_from(value: molecule_fiber::PubNonce) -> Result<Self, Self::Error> {
1606        PubNonce::from_bytes(value.as_slice())
1607    }
1608}
1609
1610impl From<RevokeAndAck> for molecule_fiber::RevokeAndAck {
1611    fn from(revoke_and_ack: RevokeAndAck) -> Self {
1612        molecule_fiber::RevokeAndAck::new_builder()
1613            .channel_id(revoke_and_ack.channel_id.into())
1614            .revocation_partial_signature(partial_signature_to_molecule(
1615                revoke_and_ack.revocation_partial_signature,
1616            ))
1617            .next_per_commitment_point(revoke_and_ack.next_per_commitment_point.into())
1618            .next_revocation_nonce(pub_nonce_to_molecule(revoke_and_ack.next_revocation_nonce))
1619            .build()
1620    }
1621}
1622
1623impl TryFrom<molecule_fiber::RevokeAndAck> for RevokeAndAck {
1624    type Error = anyhow::Error;
1625
1626    fn try_from(revoke_and_ack: molecule_fiber::RevokeAndAck) -> Result<Self, Self::Error> {
1627        Ok(RevokeAndAck {
1628            channel_id: revoke_and_ack.channel_id().into(),
1629            revocation_partial_signature: PartialSignature::from_slice(
1630                revoke_and_ack.revocation_partial_signature().as_slice(),
1631            )
1632            .map_err(|e| anyhow::anyhow!(e))?,
1633            next_per_commitment_point: revoke_and_ack
1634                .next_per_commitment_point()
1635                .try_into()
1636                .map_err(|e: secp256k1::Error| anyhow::anyhow!(e))?,
1637            next_revocation_nonce: PubNonce::from_bytes(
1638                revoke_and_ack.next_revocation_nonce().as_slice(),
1639            )
1640            .map_err(|e| anyhow::anyhow!("{}", e))?,
1641        })
1642    }
1643}
1644
1645/// The fulfillment of a TLC removal.
1646#[derive(Debug, Copy, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
1647pub struct RemoveTlcFulfill {
1648    pub payment_preimage: Hash256,
1649}
1650
1651/// The reason for removing a TLC.
1652#[derive(Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
1653pub enum RemoveTlcReason {
1654    RemoveTlcFulfill(RemoveTlcFulfill),
1655    RemoveTlcFail(TlcErrPacket),
1656}
1657
1658impl Debug for RemoveTlcReason {
1659    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1660        match self {
1661            RemoveTlcReason::RemoveTlcFulfill(_fulfill) => {
1662                write!(f, "RemoveTlcFulfill")
1663            }
1664            RemoveTlcReason::RemoveTlcFail(_fail) => {
1665                write!(f, "RemoveTlcFail")
1666            }
1667        }
1668    }
1669}
1670
1671impl RemoveTlcReason {
1672    /// Intermediate node backwards the error to the previous hop using the shared secret
1673    /// used in forwarding the onion packet.
1674    pub fn backward(self, shared_secret: &[u8; 32]) -> Result<Self, TlcErrPacketError> {
1675        match self {
1676            RemoveTlcReason::RemoveTlcFulfill(remove_tlc_fulfill) => {
1677                Ok(RemoveTlcReason::RemoveTlcFulfill(remove_tlc_fulfill))
1678            }
1679            RemoveTlcReason::RemoveTlcFail(remove_tlc_fail) => Ok(RemoveTlcReason::RemoveTlcFail(
1680                remove_tlc_fail.backward(shared_secret)?,
1681            )),
1682        }
1683    }
1684}
1685
1686impl From<RemoveTlcReason> for molecule_fiber::RemoveTlcReasonUnion {
1687    fn from(remove_tlc_reason: RemoveTlcReason) -> Self {
1688        match remove_tlc_reason {
1689            RemoveTlcReason::RemoveTlcFulfill(remove_tlc_fulfill) => {
1690                molecule_fiber::RemoveTlcReasonUnion::RemoveTlcFulfill(remove_tlc_fulfill.into())
1691            }
1692            RemoveTlcReason::RemoveTlcFail(remove_tlc_fail) => {
1693                molecule_fiber::RemoveTlcReasonUnion::TlcErrPacket(remove_tlc_fail.into())
1694            }
1695        }
1696    }
1697}
1698
1699impl From<RemoveTlcReason> for molecule_fiber::RemoveTlcReason {
1700    fn from(remove_tlc_reason: RemoveTlcReason) -> Self {
1701        molecule_fiber::RemoveTlcReason::new_builder()
1702            .set(remove_tlc_reason)
1703            .build()
1704    }
1705}
1706
1707impl From<molecule_fiber::RemoveTlcReason> for RemoveTlcReason {
1708    fn from(remove_tlc_reason: molecule_fiber::RemoveTlcReason) -> Self {
1709        match remove_tlc_reason.to_enum() {
1710            molecule_fiber::RemoveTlcReasonUnion::RemoveTlcFulfill(remove_tlc_fulfill) => {
1711                RemoveTlcReason::RemoveTlcFulfill(remove_tlc_fulfill.into())
1712            }
1713            molecule_fiber::RemoveTlcReasonUnion::TlcErrPacket(remove_tlc_fail) => {
1714                RemoveTlcReason::RemoveTlcFail(remove_tlc_fail.into())
1715            }
1716        }
1717    }
1718}
1719
1720impl From<RemoveTlcFulfill> for molecule_fiber::RemoveTlcFulfill {
1721    fn from(remove_tlc_fulfill: RemoveTlcFulfill) -> Self {
1722        molecule_fiber::RemoveTlcFulfill::new_builder()
1723            .payment_preimage(remove_tlc_fulfill.payment_preimage.into())
1724            .build()
1725    }
1726}
1727
1728impl From<molecule_fiber::RemoveTlcFulfill> for RemoveTlcFulfill {
1729    fn from(remove_tlc_fulfill: molecule_fiber::RemoveTlcFulfill) -> Self {
1730        RemoveTlcFulfill {
1731            payment_preimage: remove_tlc_fulfill.payment_preimage().into(),
1732        }
1733    }
1734}
1735
1736/// The channel update info with a single direction of channel.
1737///
1738/// This is a pure data struct used by both the internal graph representation
1739/// and the RPC JSON response types.
1740#[serde_as]
1741#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
1742pub struct ChannelUpdateInfo {
1743    /// The timestamp is the time when the channel update was received by the node.
1744    #[serde_as(as = "crate::U64Hex")]
1745    pub timestamp: u64,
1746    /// Whether the channel can be currently used for payments (in this one direction).
1747    pub enabled: bool,
1748    /// The exact amount of balance that we can send to the other party via the channel.
1749    #[serde_as(as = "Option<crate::U128Hex>")]
1750    pub outbound_liquidity: Option<u128>,
1751    /// The difference in htlc expiry values that you must have when routing through this channel (in milliseconds).
1752    #[serde_as(as = "crate::U64Hex")]
1753    pub tlc_expiry_delta: u64,
1754    /// The minimum value, which must be relayed to the next hop via the channel
1755    #[serde_as(as = "crate::U128Hex")]
1756    pub tlc_minimum_value: u128,
1757    /// The forwarding fee rate for the channel.
1758    #[serde_as(as = "crate::U64Hex")]
1759    pub fee_rate: u64,
1760}
1761
1762impl From<&ChannelTlcInfo> for ChannelUpdateInfo {
1763    fn from(info: &ChannelTlcInfo) -> Self {
1764        Self {
1765            timestamp: info.timestamp,
1766            enabled: info.enabled,
1767            outbound_liquidity: None,
1768            tlc_expiry_delta: info.tlc_expiry_delta,
1769            tlc_minimum_value: info.tlc_minimum_value,
1770            fee_rate: info.tlc_fee_proportional_millionths as u64,
1771        }
1772    }
1773}
1774
1775impl From<ChannelTlcInfo> for ChannelUpdateInfo {
1776    fn from(info: ChannelTlcInfo) -> Self {
1777        Self::from(&info)
1778    }
1779}
1780
1781impl From<crate::protocol::ChannelUpdate> for ChannelUpdateInfo {
1782    fn from(update: crate::protocol::ChannelUpdate) -> Self {
1783        Self::from(&update)
1784    }
1785}
1786
1787impl From<&crate::protocol::ChannelUpdate> for ChannelUpdateInfo {
1788    fn from(update: &crate::protocol::ChannelUpdate) -> Self {
1789        Self {
1790            timestamp: update.timestamp,
1791            enabled: !update.is_disabled(),
1792            outbound_liquidity: None,
1793            tlc_expiry_delta: update.tlc_expiry_delta,
1794            tlc_minimum_value: update.tlc_minimum_value,
1795            fee_rate: update.tlc_fee_proportional_millionths as u64,
1796        }
1797    }
1798}