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