use crate::crate_time::SystemTime;
use crate::gen::fiber as molecule_fiber;
use crate::invoice::HashAlgorithm;
use crate::onion::PaymentOnionPacket;
use crate::onion::TlcErrPacket;
use crate::protocol::{ChannelAnnouncement, ChannelUpdate, EcdsaSignature};
use crate::serde_utils::PartialSignatureAsBytes;
use crate::serde_utils::PubNonceAsBytes;
use crate::EntityHex;
use crate::Hash256;
use crate::Privkey;
use crate::Pubkey;
use bitflags::bitflags;
use ckb_types::packed::Byte32 as MByte32;
use ckb_types::packed::Script;
use ckb_types::packed::Transaction;
use ckb_types::prelude::{Pack, Unpack};
use ckb_types::H256;
use molecule::prelude::{Builder, Entity};
use musig2::BinaryEncoding;
use musig2::PartialSignature;
use musig2::PubNonce;
use musig2::{SecNonce, SecNonceBuilder};
use serde::{Deserialize, Serialize};
use serde_with::serde_as;
use std::collections::{HashMap, VecDeque};
use std::fmt::{Debug, Formatter};
bitflags! {
#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(transparent)]
pub struct ChannelFlags: u8 {
const PUBLIC = 1;
const ONE_WAY = 1 << 1;
const EXTERNAL_FUNDING = 1 << 2;
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(transparent)]
pub struct ChannelUpdateChannelFlags: u32 {
const DISABLED = 1;
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(transparent)]
pub struct ChannelUpdateMessageFlags: u32 {
const UPDATE_OF_NODE1 = 0;
const UPDATE_OF_NODE2 = 1;
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(transparent)]
pub struct NegotiatingFundingFlags: u32 {
const OUR_INIT_SENT = 1;
const THEIR_INIT_SENT = 1 << 1;
const INIT_SENT = NegotiatingFundingFlags::OUR_INIT_SENT.bits() | NegotiatingFundingFlags::THEIR_INIT_SENT.bits();
const AWAITING_EXTERNAL_FUNDING = 1 << 2;
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(transparent)]
pub struct CollaboratingFundingTxFlags: u32 {
const AWAITING_REMOTE_TX_COLLABORATION_MSG = 1;
const PREPARING_LOCAL_TX_COLLABORATION_MSG = 1 << 1;
const OUR_TX_COMPLETE_SENT = 1 << 2;
const THEIR_TX_COMPLETE_SENT = 1 << 3;
const COLLABORATION_COMPLETED = CollaboratingFundingTxFlags::OUR_TX_COMPLETE_SENT.bits() | CollaboratingFundingTxFlags::THEIR_TX_COMPLETE_SENT.bits();
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(transparent)]
pub struct SigningCommitmentFlags: u32 {
const OUR_COMMITMENT_SIGNED_SENT = 1;
const THEIR_COMMITMENT_SIGNED_SENT = 1 << 1;
const COMMITMENT_SIGNED_SENT = SigningCommitmentFlags::OUR_COMMITMENT_SIGNED_SENT.bits() | SigningCommitmentFlags::THEIR_COMMITMENT_SIGNED_SENT.bits();
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(transparent)]
pub struct AwaitingTxSignaturesFlags: u32 {
const OUR_TX_SIGNATURES_SENT = 1;
const THEIR_TX_SIGNATURES_SENT = 1 << 1;
const TX_SIGNATURES_SENT = AwaitingTxSignaturesFlags::OUR_TX_SIGNATURES_SENT.bits() | AwaitingTxSignaturesFlags::THEIR_TX_SIGNATURES_SENT.bits();
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(transparent)]
pub struct AwaitingChannelReadyFlags: u32 {
const OUR_CHANNEL_READY = 1;
const THEIR_CHANNEL_READY = 1 << 1;
const CHANNEL_READY = AwaitingChannelReadyFlags::OUR_CHANNEL_READY.bits() | AwaitingChannelReadyFlags::THEIR_CHANNEL_READY.bits();
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(transparent)]
pub struct ShuttingDownFlags: u32 {
const OUR_SHUTDOWN_SENT = 1;
const THEIR_SHUTDOWN_SENT = 1 << 1;
const AWAITING_PENDING_TLCS = ShuttingDownFlags::OUR_SHUTDOWN_SENT.bits() | ShuttingDownFlags::THEIR_SHUTDOWN_SENT.bits();
const DROPPING_PENDING = 1 << 2;
const WAITING_COMMITMENT_CONFIRMATION = 1 << 3;
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(transparent)]
pub struct CloseFlags: u32 {
const COOPERATIVE = 1;
const UNCOOPERATIVE_LOCAL = 1 << 1;
const ABANDONED = 1 << 2;
const FUNDING_ABORTED = 1 << 3;
const UNCOOPERATIVE_REMOTE = 1 << 4;
const WAITING_ONCHAIN_SETTLEMENT = 1 << 5;
}
#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
#[serde(transparent)]
pub struct AppliedFlags: u8 {
const ADD = 1;
const REMOVE = 1 << 1;
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize, PartialOrd, Ord, Hash)]
pub enum TLCId {
Offered(u64),
Received(u64),
}
impl From<TLCId> for u64 {
fn from(id: TLCId) -> u64 {
match id {
TLCId::Offered(id) => id,
TLCId::Received(id) => id,
}
}
}
impl TLCId {
pub fn is_offered(&self) -> bool {
matches!(self, TLCId::Offered(_))
}
pub fn is_received(&self) -> bool {
!self.is_offered()
}
pub fn flip(&self) -> Self {
match self {
TLCId::Offered(id) => TLCId::Received(*id),
TLCId::Received(id) => TLCId::Offered(*id),
}
}
pub fn flip_mut(&mut self) {
*self = self.flip();
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
pub enum OutboundTlcStatus {
LocalAnnounced,
Committed,
RemoteRemoved,
RemoveWaitPrevAck,
RemoveWaitAck,
RemoveAckConfirmed,
}
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
pub enum InboundTlcStatus {
RemoteAnnounced,
AnnounceWaitPrevAck,
AnnounceWaitAck,
Committed,
LocalRemoved,
RemoveAckConfirmed,
}
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
pub enum TlcStatus {
Outbound(OutboundTlcStatus),
Inbound(InboundTlcStatus),
}
impl TlcStatus {
pub fn as_outbound_status(&self) -> OutboundTlcStatus {
match self {
TlcStatus::Outbound(status) => status.clone(),
_ => {
unreachable!("unexpected status")
}
}
}
pub fn as_inbound_status(&self) -> InboundTlcStatus {
match self {
TlcStatus::Inbound(status) => status.clone(),
_ => {
unreachable!("unexpected status ")
}
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum ChannelState {
NegotiatingFunding(NegotiatingFundingFlags),
CollaboratingFundingTx(CollaboratingFundingTxFlags),
SigningCommitment(SigningCommitmentFlags),
AwaitingTxSignatures(AwaitingTxSignaturesFlags),
AwaitingChannelReady(AwaitingChannelReadyFlags),
ChannelReady,
ShuttingDown(ShuttingDownFlags),
Closed(CloseFlags),
}
impl ChannelState {
pub fn is_awaiting_external_funding(&self) -> bool {
matches!(
self,
ChannelState::NegotiatingFunding(flags)
if flags.contains(NegotiatingFundingFlags::AWAITING_EXTERNAL_FUNDING)
)
}
pub fn is_closed(&self) -> bool {
matches!(
self,
ChannelState::Closed(_)
| ChannelState::ShuttingDown(ShuttingDownFlags::WAITING_COMMITMENT_CONFIRMATION)
)
}
pub fn can_abort_funding(&self) -> bool {
match self {
ChannelState::NegotiatingFunding(_)
| ChannelState::CollaboratingFundingTx(_)
| ChannelState::SigningCommitment(_) => true,
ChannelState::AwaitingTxSignatures(flags)
if !flags.contains(AwaitingTxSignaturesFlags::OUR_TX_SIGNATURES_SENT) =>
{
true
}
_ => false,
}
}
}
impl ShuttingDownFlags {
pub fn is_ok_for_commitment_operation(&self) -> bool {
!self.contains(ShuttingDownFlags::DROPPING_PENDING)
&& !self.contains(ShuttingDownFlags::WAITING_COMMITMENT_CONFIRMATION)
}
}
pub const INITIAL_COMMITMENT_NUMBER: u64 = 0;
#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct CommitmentNumbers {
pub local: u64,
pub remote: u64,
}
impl Default for CommitmentNumbers {
fn default() -> Self {
Self::new()
}
}
impl CommitmentNumbers {
pub fn new() -> Self {
Self {
local: INITIAL_COMMITMENT_NUMBER,
remote: INITIAL_COMMITMENT_NUMBER,
}
}
pub fn get_local(&self) -> u64 {
self.local
}
pub fn get_remote(&self) -> u64 {
self.remote
}
pub fn increment_local(&mut self) {
self.local += 1;
}
pub fn increment_remote(&mut self) {
self.remote += 1;
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, Default)]
pub struct ChannelConstraints {
pub max_tlc_value_in_flight: u128,
pub max_tlc_number_in_flight: u64,
}
impl ChannelConstraints {
pub fn new(max_tlc_value_in_flight: u128, max_tlc_number_in_flight: u64) -> Self {
Self {
max_tlc_value_in_flight,
max_tlc_number_in_flight,
}
}
}
#[derive(Default, Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub struct ChannelTlcInfo {
pub timestamp: u64,
pub enabled: bool,
pub tlc_fee_proportional_millionths: u128,
pub tlc_expiry_delta: u64,
pub tlc_minimum_value: u128,
}
impl ChannelTlcInfo {
pub fn new(
tlc_minimum_value: u128,
tlc_expiry_delta: u64,
tlc_fee_proportional_millionths: u128,
timestamp: u64,
) -> Self {
Self {
tlc_minimum_value,
tlc_expiry_delta,
tlc_fee_proportional_millionths,
enabled: true,
timestamp,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct ChannelBasePublicKeys {
pub funding_pubkey: Pubkey,
pub tlc_base_key: Pubkey,
}
#[derive(Debug, Copy, Clone, Serialize, Deserialize, Eq, PartialEq, Hash)]
pub struct PrevTlcInfo {
pub prev_channel_id: Hash256,
pub prev_tlc_id: u64,
pub forwarding_fee: u128,
pub shared_secret: Option<[u8; 32]>,
}
impl PrevTlcInfo {
pub fn new(prev_channel_id: Hash256, prev_tlc_id: u64, forwarding_fee: u128) -> Self {
Self {
prev_channel_id,
prev_tlc_id,
forwarding_fee,
shared_secret: None,
}
}
pub fn new_with_shared_secret(
prev_channel_id: Hash256,
prev_tlc_id: u64,
forwarding_fee: u128,
shared_secret: [u8; 32],
) -> Self {
Self {
prev_channel_id,
prev_tlc_id,
forwarding_fee,
shared_secret: Some(shared_secret),
}
}
}
#[derive(Clone, Serialize, Deserialize, Eq, PartialEq)]
pub struct TlcInfo {
pub status: TlcStatus,
pub tlc_id: TLCId,
pub amount: u128,
pub payment_hash: Hash256,
pub total_amount: Option<u128>,
pub payment_secret: Option<Hash256>,
pub attempt_id: Option<u64>,
pub expiry: u64,
pub hash_algorithm: HashAlgorithm,
pub onion_packet: Option<PaymentOnionPacket>,
pub shared_secret: [u8; 32],
#[serde(default)]
pub is_trampoline_hop: bool,
pub created_at: CommitmentNumbers,
pub removed_reason: Option<RemoveTlcReason>,
pub forwarding_tlc: Option<(Hash256, u64)>,
pub removed_confirmed_at: Option<u64>,
pub applied_flags: AppliedFlags,
}
use std::fmt;
use std::time::Duration;
impl fmt::Debug for TlcInfo {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("TlcInfo")
.field("status", &self.status)
.field("tlc_id", &self.tlc_id)
.field("amount", &self.amount)
.field("payment_hash", &self.payment_hash)
.field("expiry", &self.expiry)
.field("created_at", &self.created_at)
.field("removed_reason", &self.removed_reason)
.field("applied_flags", &self.applied_flags)
.finish()
}
}
impl TlcInfo {
pub fn log(&self) -> String {
format!(
"id: {:?} status: {:?} amount: {:?} removed: {:?} hash: {:?} ",
&self.tlc_id, self.status, self.amount, self.removed_reason, self.payment_hash,
)
}
pub fn id(&self) -> u64 {
self.tlc_id.into()
}
pub fn is_offered(&self) -> bool {
self.tlc_id.is_offered()
}
pub fn is_received(&self) -> bool {
!self.is_offered()
}
pub fn get_commitment_numbers(&self) -> CommitmentNumbers {
self.created_at
}
pub fn flip_mut(&mut self) {
self.tlc_id.flip_mut();
}
pub fn outbound_status(&self) -> OutboundTlcStatus {
self.status.as_outbound_status()
}
pub fn inbound_status(&self) -> InboundTlcStatus {
self.status.as_inbound_status()
}
pub fn is_fail_remove_confirmed(&self) -> bool {
matches!(self.removed_reason, Some(RemoveTlcReason::RemoveTlcFail(_)))
&& matches!(
self.status,
TlcStatus::Outbound(OutboundTlcStatus::RemoveAckConfirmed)
| TlcStatus::Outbound(OutboundTlcStatus::RemoveWaitAck)
| TlcStatus::Inbound(InboundTlcStatus::RemoveAckConfirmed)
)
}
pub fn get_htlc_type(&self) -> u8 {
let offered_flag = if self.is_offered() { 0u8 } else { 1u8 };
((self.hash_algorithm as u8) << 1) + offered_flag
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, Default)]
pub struct PendingTlcs {
pub tlcs: Vec<TlcInfo>,
pub next_tlc_id: u64,
}
impl PendingTlcs {
pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut TlcInfo> {
self.tlcs.iter_mut()
}
pub fn get_next_id(&self) -> u64 {
self.next_tlc_id
}
pub fn increment_next_id(&mut self) {
self.next_tlc_id += 1;
}
pub fn add_tlc(&mut self, tlc: TlcInfo) {
self.tlcs.push(tlc);
}
}
#[derive(Default, Clone, Debug, Serialize, Deserialize)]
pub struct TlcState {
pub offered_tlcs: PendingTlcs,
pub received_tlcs: PendingTlcs,
pub waiting_ack: bool,
}
impl TlcState {
pub fn info(&self) -> String {
format!(
"offer_tlcs: {:?} received_tlcs: {:?}",
self.offered_tlcs.tlcs.len(),
self.received_tlcs.tlcs.len(),
)
}
#[cfg(debug_assertions)]
pub fn debug(&self) {
let format_tlc_list = |tlcs: &[TlcInfo]| -> String {
if tlcs.is_empty() {
" <none>".to_string()
} else {
tlcs.iter()
.map(|tlc| format!(" {}", tlc.log()))
.collect::<Vec<_>>()
.join("\n")
}
};
let offered_str = format_tlc_list(&self.offered_tlcs.tlcs);
let received_str = format_tlc_list(&self.received_tlcs.tlcs);
if offered_str.contains("<none>") && received_str.contains("<none>") {
tracing::info!("TlcState: <none>");
} else {
tracing::info!(
"TlcState:\n Offered:\n{}\n Received:\n{}",
offered_str,
received_str
);
}
}
pub fn get_mut(&mut self, tlc_id: &TLCId) -> Option<&mut TlcInfo> {
self.offered_tlcs
.tlcs
.iter_mut()
.find(|tlc| tlc.tlc_id == *tlc_id)
.or_else(|| {
self.received_tlcs
.tlcs
.iter_mut()
.find(|tlc| tlc.tlc_id == *tlc_id)
})
}
pub fn get(&self, tlc_id: &TLCId) -> Option<&TlcInfo> {
if tlc_id.is_offered() {
self.offered_tlcs
.tlcs
.iter()
.find(|tlc| tlc.tlc_id == *tlc_id)
} else {
self.received_tlcs
.tlcs
.iter()
.find(|tlc| tlc.tlc_id == *tlc_id)
}
}
pub fn get_committed_received_tlcs(&self) -> impl Iterator<Item = &TlcInfo> + '_ {
self.received_tlcs.tlcs.iter().filter(|tlc| {
debug_assert!(tlc.is_received());
matches!(tlc.inbound_status(), InboundTlcStatus::Committed)
})
}
pub fn get_expired_offered_tlcs(
&self,
expect_expiry: u64,
) -> impl Iterator<Item = &TlcInfo> + '_ {
self.offered_tlcs.tlcs.iter().filter(move |tlc| {
tlc.outbound_status() != OutboundTlcStatus::LocalAnnounced
&& tlc.removed_confirmed_at.is_none()
&& tlc.expiry < expect_expiry
})
}
pub fn get_next_offering(&self) -> u64 {
self.offered_tlcs.get_next_id()
}
pub fn get_next_received(&self) -> u64 {
self.received_tlcs.get_next_id()
}
pub fn increment_offering(&mut self) {
self.offered_tlcs.increment_next_id();
}
pub fn increment_received(&mut self) {
self.received_tlcs.increment_next_id();
}
pub fn set_waiting_ack(&mut self, waiting_ack: bool) {
self.waiting_ack = waiting_ack;
}
pub fn all_tlcs(&self) -> impl Iterator<Item = &TlcInfo> + '_ {
self.offered_tlcs
.tlcs
.iter()
.chain(self.received_tlcs.tlcs.iter())
}
pub fn apply_remove_tlc(&mut self, tlc_id: TLCId) {
if tlc_id.is_offered() {
self.offered_tlcs.tlcs.retain(|tlc| tlc.tlc_id != tlc_id);
} else {
self.received_tlcs.tlcs.retain(|tlc| tlc.tlc_id != tlc_id);
}
}
pub fn add_offered_tlc(&mut self, tlc: TlcInfo) {
self.offered_tlcs.add_tlc(tlc);
}
pub fn add_received_tlc(&mut self, tlc: TlcInfo) {
self.received_tlcs.add_tlc(tlc);
}
pub fn set_received_tlc_removed(&mut self, tlc_id: u64, reason: RemoveTlcReason) -> Hash256 {
let tlc = self.get_mut(&TLCId::Received(tlc_id)).expect("get tlc");
assert_eq!(tlc.inbound_status(), InboundTlcStatus::Committed);
tlc.removed_reason = Some(reason);
tlc.status = TlcStatus::Inbound(InboundTlcStatus::LocalRemoved);
tlc.payment_hash
}
pub fn set_offered_tlc_removed(&mut self, tlc_id: u64, reason: RemoveTlcReason) -> Hash256 {
let tlc = self.get_mut(&TLCId::Offered(tlc_id)).expect("get tlc");
assert_eq!(tlc.outbound_status(), OutboundTlcStatus::Committed);
tlc.removed_reason = Some(reason);
tlc.status = TlcStatus::Outbound(OutboundTlcStatus::RemoteRemoved);
tlc.payment_hash
}
pub fn commitment_signed_tlcs(&self, for_remote: bool) -> impl Iterator<Item = &TlcInfo> + '_ {
self.offered_tlcs
.tlcs
.iter()
.filter(move |tlc| match tlc.outbound_status() {
OutboundTlcStatus::LocalAnnounced => for_remote,
OutboundTlcStatus::Committed => true,
OutboundTlcStatus::RemoteRemoved => for_remote,
OutboundTlcStatus::RemoveWaitPrevAck => for_remote,
OutboundTlcStatus::RemoveWaitAck => false,
OutboundTlcStatus::RemoveAckConfirmed => false,
})
.chain(
self.received_tlcs
.tlcs
.iter()
.filter(move |tlc| match tlc.inbound_status() {
InboundTlcStatus::RemoteAnnounced => !for_remote,
InboundTlcStatus::AnnounceWaitPrevAck => !for_remote,
InboundTlcStatus::AnnounceWaitAck => true,
InboundTlcStatus::Committed => true,
InboundTlcStatus::LocalRemoved => !for_remote,
InboundTlcStatus::RemoveAckConfirmed => false,
}),
)
}
pub fn update_for_commitment_signed(&mut self) -> bool {
for tlc in self.offered_tlcs.tlcs.iter_mut() {
if tlc.outbound_status() == OutboundTlcStatus::RemoteRemoved {
let status = if self.waiting_ack {
OutboundTlcStatus::RemoveWaitPrevAck
} else {
OutboundTlcStatus::RemoveWaitAck
};
tlc.status = TlcStatus::Outbound(status);
}
}
for tlc in self.received_tlcs.tlcs.iter_mut() {
if tlc.inbound_status() == InboundTlcStatus::RemoteAnnounced {
let status = if self.waiting_ack {
InboundTlcStatus::AnnounceWaitPrevAck
} else {
InboundTlcStatus::AnnounceWaitAck
};
tlc.status = TlcStatus::Inbound(status)
}
}
self.need_another_commitment_signed()
}
pub fn update_for_revoke_and_ack(&mut self, commitment_number: CommitmentNumbers) {
for tlc in self.offered_tlcs.tlcs.iter_mut() {
match tlc.outbound_status() {
OutboundTlcStatus::LocalAnnounced => {
tlc.status = TlcStatus::Outbound(OutboundTlcStatus::Committed);
}
OutboundTlcStatus::RemoveWaitPrevAck => {
tlc.status = TlcStatus::Outbound(OutboundTlcStatus::RemoveWaitAck);
}
OutboundTlcStatus::RemoveWaitAck => {
tlc.status = TlcStatus::Outbound(OutboundTlcStatus::RemoveAckConfirmed);
tlc.removed_confirmed_at = Some(commitment_number.get_local());
}
_ => {}
}
}
for tlc in self.received_tlcs.tlcs.iter_mut() {
match tlc.inbound_status() {
InboundTlcStatus::AnnounceWaitPrevAck => {
tlc.status = TlcStatus::Inbound(InboundTlcStatus::AnnounceWaitAck);
}
InboundTlcStatus::AnnounceWaitAck => {
tlc.status = TlcStatus::Inbound(InboundTlcStatus::Committed);
}
InboundTlcStatus::LocalRemoved => {
tlc.status = TlcStatus::Inbound(InboundTlcStatus::RemoveAckConfirmed);
tlc.removed_confirmed_at = Some(commitment_number.get_remote());
}
_ => {}
}
}
}
pub fn need_another_commitment_signed(&self) -> bool {
self.offered_tlcs.tlcs.iter().any(|tlc| {
let status = tlc.outbound_status();
matches!(
status,
OutboundTlcStatus::LocalAnnounced
| OutboundTlcStatus::RemoteRemoved
| OutboundTlcStatus::RemoveWaitPrevAck
| OutboundTlcStatus::RemoveWaitAck
)
}) || self.received_tlcs.tlcs.iter().any(|tlc| {
let status = tlc.inbound_status();
matches!(
status,
InboundTlcStatus::RemoteAnnounced
| InboundTlcStatus::AnnounceWaitPrevAck
| InboundTlcStatus::AnnounceWaitAck
)
})
}
}
#[derive(Clone, Serialize, Deserialize, Eq, PartialEq, Hash)]
pub struct AddTlcCommand {
pub amount: u128,
pub payment_hash: Hash256,
pub attempt_id: Option<u64>,
pub expiry: u64,
pub hash_algorithm: HashAlgorithm,
pub onion_packet: Option<PaymentOnionPacket>,
pub shared_secret: [u8; 32],
pub is_trampoline_hop: bool,
pub previous_tlc: Option<PrevTlcInfo>,
}
impl fmt::Debug for AddTlcCommand {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("AddTlcCommand")
.field("amount", &self.amount)
.field("payment_hash", &self.payment_hash)
.field("attempt_id", &self.attempt_id)
.field("expiry", &self.expiry)
.field("hash_algorithm", &self.hash_algorithm)
.field("is_trampoline_hop", &self.is_trampoline_hop)
.field("previous_tlc", &self.previous_tlc)
.finish()
}
}
#[derive(Clone, Serialize, Deserialize, Eq, PartialEq, Debug, Hash)]
pub enum RetryableTlcOperation {
RemoveTlc(TLCId, RemoveTlcReason),
AddTlc(AddTlcCommand),
}
#[derive(Clone, Serialize, Deserialize, Eq, PartialEq, Hash)]
pub struct AddTlc {
pub channel_id: Hash256,
pub tlc_id: u64,
pub amount: u128,
pub payment_hash: Hash256,
pub expiry: u64,
pub hash_algorithm: HashAlgorithm,
pub onion_packet: Option<PaymentOnionPacket>,
}
impl fmt::Debug for AddTlc {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("AddTlc")
.field("channel_id", &self.channel_id)
.field("tlc_id", &self.tlc_id)
.field("amount", &self.amount)
.field("payment_hash", &self.payment_hash)
.field("expiry", &self.expiry)
.field("hash_algorithm", &self.hash_algorithm)
.finish()
}
}
impl From<AddTlc> for molecule_fiber::AddTlc {
fn from(add_tlc: AddTlc) -> Self {
molecule_fiber::AddTlc::new_builder()
.channel_id(add_tlc.channel_id.into())
.tlc_id(add_tlc.tlc_id.pack())
.amount(add_tlc.amount.pack())
.payment_hash(add_tlc.payment_hash.into())
.expiry(add_tlc.expiry.pack())
.hash_algorithm(molecule::prelude::Byte::new(add_tlc.hash_algorithm as u8))
.onion_packet(
add_tlc
.onion_packet
.map(|p| p.into_bytes())
.unwrap_or_default()
.pack(),
)
.build()
}
}
impl TryFrom<molecule_fiber::AddTlc> for AddTlc {
type Error = anyhow::Error;
fn try_from(add_tlc: molecule_fiber::AddTlc) -> Result<Self, Self::Error> {
let onion_packet_bytes: Vec<u8> = add_tlc.onion_packet().unpack();
let onion_packet =
(!onion_packet_bytes.is_empty()).then(|| PaymentOnionPacket::new(onion_packet_bytes));
Ok(AddTlc {
onion_packet,
channel_id: add_tlc.channel_id().into(),
tlc_id: add_tlc.tlc_id().unpack(),
amount: add_tlc.amount().unpack(),
payment_hash: add_tlc.payment_hash().into(),
expiry: add_tlc.expiry().unpack(),
hash_algorithm: add_tlc
.hash_algorithm()
.try_into()
.map_err(|e: crate::invoice::UnknownHashAlgorithmError| anyhow::anyhow!(e))?,
})
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, Hash)]
pub struct RemoveTlc {
pub channel_id: Hash256,
pub tlc_id: u64,
pub reason: RemoveTlcReason,
}
impl From<RemoveTlc> for molecule_fiber::RemoveTlc {
fn from(remove_tlc: RemoveTlc) -> Self {
molecule_fiber::RemoveTlc::new_builder()
.channel_id(remove_tlc.channel_id.into())
.tlc_id(remove_tlc.tlc_id.pack())
.reason(
molecule_fiber::RemoveTlcReason::new_builder()
.set(remove_tlc.reason)
.build(),
)
.build()
}
}
impl TryFrom<molecule_fiber::RemoveTlc> for RemoveTlc {
type Error = anyhow::Error;
fn try_from(remove_tlc: molecule_fiber::RemoveTlc) -> Result<Self, Self::Error> {
Ok(RemoveTlc {
channel_id: remove_tlc.channel_id().into(),
tlc_id: remove_tlc.tlc_id().unpack(),
reason: remove_tlc.reason().into(),
})
}
}
#[derive(Clone, Serialize, Deserialize, Eq, PartialEq, Debug, Hash)]
pub enum TlcReplayUpdate {
Add(AddTlc),
Remove(RemoveTlc),
}
pub const CURRENT_COMMIT_DIFF_VERSION: u8 = 2;
fn default_commit_diff_version() -> u8 {
CURRENT_COMMIT_DIFF_VERSION
}
#[serde_as]
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct CommitmentSignedTemplate {
#[serde_as(as = "PubNonceAsBytes")]
pub next_commitment_nonce: PubNonce,
#[serde(default)]
#[serde_as(as = "Option<PartialSignatureAsBytes>")]
pub funding_tx_partial_signature: Option<PartialSignature>,
}
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub enum ReplayOrderHint {
RevokeThenCommit,
CommitThenRevoke,
}
#[serde_as]
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct CommitDiff {
#[serde(default = "default_commit_diff_version")]
pub version: u8,
#[serde(default)]
pub channel_id: Hash256,
#[serde(default)]
pub local_commitment_number_at_send: u64,
#[serde(default)]
pub remote_commitment_number_at_send: u64,
#[serde_as(as = "EntityHex")]
pub commit_tx: Transaction,
#[serde(default, alias = "tlc_updates")]
pub replay_updates: Vec<TlcReplayUpdate>,
#[serde(default)]
pub commitment_signed_template: Option<CommitmentSignedTemplate>,
#[serde(default)]
pub replay_order_hint: Option<ReplayOrderHint>,
#[serde(default, alias = "created_at")]
pub created_at_ms: u64,
}
#[serde_as]
#[derive(Clone, Serialize, Deserialize, Eq, PartialEq, Debug)]
pub struct ShutdownInfo {
#[serde_as(as = "EntityHex")]
pub close_script: Script,
pub fee_rate: u64,
#[serde_as(as = "Option<PartialSignatureAsBytes>")]
pub signature: Option<PartialSignature>,
}
#[serde_as]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RevokeAndAck {
pub channel_id: Hash256,
#[serde_as(as = "PartialSignatureAsBytes")]
pub revocation_partial_signature: PartialSignature,
pub next_per_commitment_point: Pubkey,
#[serde_as(as = "PubNonceAsBytes")]
pub next_revocation_nonce: PubNonce,
}
#[serde_as]
#[derive(Default, Clone, Debug, Serialize, Deserialize)]
pub struct PublicChannelInfo {
#[serde_as(as = "Option<(_, PartialSignatureAsBytes)>")]
pub local_channel_announcement_signature: Option<(EcdsaSignature, PartialSignature)>,
#[serde_as(as = "Option<(_, PartialSignatureAsBytes)>")]
pub remote_channel_announcement_signature: Option<(EcdsaSignature, PartialSignature)>,
#[serde_as(as = "Option<PubNonceAsBytes>")]
pub remote_channel_announcement_nonce: Option<PubNonce>,
pub channel_announcement: Option<ChannelAnnouncement>,
pub channel_update: Option<ChannelUpdate>,
}
impl PublicChannelInfo {
pub fn new() -> Self {
Default::default()
}
}
#[derive(Clone, Eq, PartialEq, Serialize, Deserialize)]
pub struct InMemorySigner {
pub funding_key: Privkey,
pub tlc_base_key: Privkey,
pub musig2_base_nonce: Privkey,
pub commitment_seed: [u8; 32],
}
impl fmt::Debug for InMemorySigner {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("InMemorySigner")
.field("funding_key", &"[REDACTED]")
.field("tlc_base_key", &"[REDACTED]")
.field("musig2_base_nonce", &"[REDACTED]")
.field("commitment_seed", &"[REDACTED]")
.finish()
}
}
pub fn blake2b_hash_with_salt(data: &[u8], salt: &[u8]) -> [u8; 32] {
let mut hasher = ckb_hash::new_blake2b();
hasher.update(salt);
hasher.update(data);
let mut result = [0u8; 32];
hasher.finalize(&mut result);
result
}
pub fn get_tweak_by_commitment_point(commitment_point: &Pubkey) -> [u8; 32] {
let mut hasher = ckb_hash::new_blake2b();
hasher.update(&commitment_point.serialize());
let mut result = [0u8; 32];
hasher.finalize(&mut result);
result
}
pub fn derive_private_key(secret: &Privkey, commitment_point: &Pubkey) -> Privkey {
secret.tweak(get_tweak_by_commitment_point(commitment_point))
}
pub fn derive_public_key(base_key: &Pubkey, commitment_point: &Pubkey) -> Pubkey {
base_key.tweak(get_tweak_by_commitment_point(commitment_point))
}
pub fn derive_tlc_pubkey(base_key: &Pubkey, commitment_point: &Pubkey) -> Pubkey {
derive_public_key(base_key, commitment_point)
}
pub fn get_commitment_secret(commitment_seed: &[u8; 32], commitment_number: u64) -> [u8; 32] {
let mut res: [u8; 32] = *commitment_seed;
for i in 0..48 {
let bitpos = 47 - i;
if commitment_number & (1 << bitpos) == (1 << bitpos) {
res[bitpos / 8] ^= 1 << (bitpos & 7);
res = ckb_hash::blake2b_256(res);
}
}
res
}
pub fn get_commitment_point(commitment_seed: &[u8; 32], commitment_number: u64) -> Pubkey {
Privkey::from(&get_commitment_secret(commitment_seed, commitment_number)).pubkey()
}
pub enum Musig2Context {
Commitment,
Revoke,
}
impl std::fmt::Display for Musig2Context {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let context_str = match self {
Musig2Context::Commitment => "COMMITMENT",
Musig2Context::Revoke => "REVOKE",
};
write!(f, "{}", context_str)
}
}
impl InMemorySigner {
pub fn generate_from_seed(params: &[u8]) -> InMemorySigner {
let seed = ckb_hash::blake2b_256(params);
let commitment_seed = {
let mut hasher = ckb_hash::new_blake2b();
hasher.update(&seed);
hasher.update(&b"commitment seed"[..]);
let mut result = [0u8; 32];
hasher.finalize(&mut result);
result
};
let key_derive = |seed: &[u8], info: &[u8]| {
let result = blake2b_hash_with_salt(seed, info);
Privkey::from_slice(&result)
};
let funding_key = key_derive(&seed, b"funding key");
let tlc_base_key = key_derive(funding_key.as_ref(), b"HTLC base key");
let musig2_base_nonce = key_derive(tlc_base_key.as_ref(), b"musig nocne");
InMemorySigner {
funding_key,
tlc_base_key,
musig2_base_nonce,
commitment_seed,
}
}
pub fn get_base_public_keys(&self) -> ChannelBasePublicKeys {
ChannelBasePublicKeys {
funding_pubkey: self.funding_key.pubkey(),
tlc_base_key: self.tlc_base_key.pubkey(),
}
}
pub fn get_commitment_point(&self, commitment_number: u64) -> Pubkey {
get_commitment_point(&self.commitment_seed, commitment_number)
}
pub fn get_commitment_secret(&self, commitment_number: u64) -> [u8; 32] {
get_commitment_secret(&self.commitment_seed, commitment_number)
}
pub fn derive_tlc_key(&self, new_commitment_number: u64) -> Privkey {
let per_commitment_point = self.get_commitment_point(new_commitment_number);
derive_private_key(&self.tlc_base_key, &per_commitment_point)
}
pub fn derive_musig2_nonce(&self, commitment_number: u64, context: Musig2Context) -> SecNonce {
let commitment_point = self.get_commitment_point(commitment_number);
let seckey = derive_private_key(&self.musig2_base_nonce, &commitment_point);
SecNonceBuilder::new(seckey.as_ref())
.with_extra_input(&context.to_string())
.build()
}
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum ChannelOpeningStatus {
WaitingForPeer,
FundingTxBuilding,
FundingTxBroadcasted,
ChannelReady,
Failed,
}
#[serde_as]
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ChannelOpenRecord {
pub channel_id: Hash256,
pub pubkey: Pubkey,
pub is_acceptor: bool,
pub status: ChannelOpeningStatus,
pub funding_amount: u128,
pub failure_detail: Option<String>,
pub created_at: u64,
pub last_updated_at: u64,
}
impl ChannelOpenRecord {
pub fn new(channel_id: Hash256, pubkey: Pubkey, funding_amount: u128) -> Self {
let now = crate::now_timestamp_as_millis_u64();
Self {
channel_id,
pubkey,
is_acceptor: false,
status: ChannelOpeningStatus::WaitingForPeer,
funding_amount,
failure_detail: None,
created_at: now,
last_updated_at: now,
}
}
pub fn new_inbound(channel_id: Hash256, pubkey: Pubkey, remote_funding_amount: u128) -> Self {
let mut record = Self::new(channel_id, pubkey, remote_funding_amount);
record.is_acceptor = true;
record
}
pub fn update_status(&mut self, status: ChannelOpeningStatus) {
self.status = status;
self.last_updated_at = crate::now_timestamp_as_millis_u64();
}
pub fn fail(&mut self, reason: String) {
self.status = ChannelOpeningStatus::Failed;
self.failure_detail = Some(reason);
self.last_updated_at = crate::now_timestamp_as_millis_u64();
}
}
pub trait ChannelOpenRecordStore {
fn get_channel_open_records(&self) -> Vec<ChannelOpenRecord>;
fn get_channel_open_record(&self, channel_id: &Hash256) -> Option<ChannelOpenRecord>;
fn insert_channel_open_record(&self, record: ChannelOpenRecord);
fn delete_channel_open_record(&self, channel_id: &Hash256);
}
#[derive(Clone, Serialize, Deserialize, Debug)]
pub struct PendingNotifySettleTlc {
pub payment_hash: Hash256,
pub tlc_id: u64,
pub hold_expire_at: Option<u64>,
}
impl PendingNotifySettleTlc {
pub fn pending_notify_should_hold(&self) -> bool {
self.hold_expire_at.is_some()
}
pub fn pending_notify_hold_expiry_duration(
&self,
now_millis_since_unix_epoch: u64,
) -> Duration {
Duration::from_millis(
self.hold_expire_at
.unwrap_or_default()
.saturating_sub(now_millis_since_unix_epoch),
)
}
}
#[serde_as]
#[derive(Clone, Serialize, Deserialize)]
pub struct ChannelActorData {
pub state: ChannelState,
pub public_channel_info: Option<PublicChannelInfo>,
pub local_tlc_info: ChannelTlcInfo,
pub remote_tlc_info: Option<ChannelTlcInfo>,
pub local_pubkey: Pubkey,
pub remote_pubkey: Pubkey,
pub id: Hash256,
#[serde_as(as = "Option<EntityHex>")]
pub funding_tx: Option<Transaction>,
pub funding_tx_confirmed_at: Option<(H256, u32, u64)>,
#[serde_as(as = "Option<EntityHex>")]
pub funding_udt_type_script: Option<Script>,
pub is_acceptor: bool,
pub is_one_way: bool,
pub to_local_amount: u128,
pub to_remote_amount: u128,
pub local_reserved_ckb_amount: u64,
pub remote_reserved_ckb_amount: u64,
pub commitment_fee_rate: u64,
pub commitment_delay_epoch: u64,
pub funding_fee_rate: u64,
pub signer: InMemorySigner,
pub local_channel_public_keys: ChannelBasePublicKeys,
pub commitment_numbers: CommitmentNumbers,
pub local_constraints: ChannelConstraints,
pub remote_constraints: ChannelConstraints,
pub tlc_state: TlcState,
pub retryable_tlc_operations: VecDeque<RetryableTlcOperation>,
pub waiting_forward_tlc_tasks: HashMap<TLCId, [u8; 32]>,
#[serde_as(as = "Option<EntityHex>")]
pub remote_shutdown_script: Option<Script>,
#[serde_as(as = "EntityHex")]
pub local_shutdown_script: Script,
#[serde_as(as = "Option<PubNonceAsBytes>")]
pub last_committed_remote_nonce: Option<PubNonce>,
#[serde_as(as = "Option<PubNonceAsBytes>")]
pub remote_revocation_nonce_for_verify: Option<PubNonce>,
#[serde_as(as = "Option<PubNonceAsBytes>")]
pub remote_revocation_nonce_for_send: Option<PubNonce>,
#[serde_as(as = "Option<PubNonceAsBytes>")]
pub remote_revocation_nonce_for_next: Option<PubNonce>,
#[serde_as(as = "Option<EntityHex>")]
pub latest_commitment_transaction: Option<Transaction>,
pub remote_commitment_points: Vec<(u64, Pubkey)>,
pub remote_channel_public_keys: Option<ChannelBasePublicKeys>,
pub local_shutdown_info: Option<ShutdownInfo>,
pub remote_shutdown_info: Option<ShutdownInfo>,
pub shutdown_transaction_hash: Option<H256>,
pub reestablishing: bool,
pub last_revoke_ack_msg: Option<RevokeAndAck>,
pub created_at: SystemTime,
#[serde(default)]
pub pending_replay_updates: Vec<TlcReplayUpdate>,
#[serde(default)]
pub last_was_revoke: bool,
}
fn partial_signature_to_molecule(partial_signature: PartialSignature) -> MByte32 {
MByte32::from_slice(partial_signature.serialize().as_ref()).expect("[Byte; 32] from [u8; 32]")
}
fn pub_nonce_to_molecule(pub_nonce: PubNonce) -> molecule_fiber::PubNonce {
molecule_fiber::PubNonce::from_slice(pub_nonce.to_bytes().as_ref())
.expect("PubNonce from 66 bytes")
}
impl From<PubNonce> for molecule_fiber::PubNonce {
fn from(value: PubNonce) -> Self {
molecule_fiber::PubNonce::from_slice(value.to_bytes().as_ref())
.expect("valid pubnonce serialized to 66 bytes")
}
}
impl TryFrom<molecule_fiber::PubNonce> for PubNonce {
type Error = musig2::errors::DecodeError<PubNonce>;
fn try_from(value: molecule_fiber::PubNonce) -> Result<Self, Self::Error> {
PubNonce::from_bytes(value.as_slice())
}
}
impl From<RevokeAndAck> for molecule_fiber::RevokeAndAck {
fn from(revoke_and_ack: RevokeAndAck) -> Self {
molecule_fiber::RevokeAndAck::new_builder()
.channel_id(revoke_and_ack.channel_id.into())
.revocation_partial_signature(partial_signature_to_molecule(
revoke_and_ack.revocation_partial_signature,
))
.next_per_commitment_point(revoke_and_ack.next_per_commitment_point.into())
.next_revocation_nonce(pub_nonce_to_molecule(revoke_and_ack.next_revocation_nonce))
.build()
}
}
impl TryFrom<molecule_fiber::RevokeAndAck> for RevokeAndAck {
type Error = anyhow::Error;
fn try_from(revoke_and_ack: molecule_fiber::RevokeAndAck) -> Result<Self, Self::Error> {
Ok(RevokeAndAck {
channel_id: revoke_and_ack.channel_id().into(),
revocation_partial_signature: PartialSignature::from_slice(
revoke_and_ack.revocation_partial_signature().as_slice(),
)
.map_err(|e| anyhow::anyhow!(e))?,
next_per_commitment_point: revoke_and_ack
.next_per_commitment_point()
.try_into()
.map_err(|e: secp256k1::Error| anyhow::anyhow!(e))?,
next_revocation_nonce: PubNonce::from_bytes(
revoke_and_ack.next_revocation_nonce().as_slice(),
)
.map_err(|e| anyhow::anyhow!("{}", e))?,
})
}
}
#[derive(Debug, Copy, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub struct RemoveTlcFulfill {
pub payment_preimage: Hash256,
}
#[derive(Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub enum RemoveTlcReason {
RemoveTlcFulfill(RemoveTlcFulfill),
RemoveTlcFail(TlcErrPacket),
}
impl Debug for RemoveTlcReason {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
RemoveTlcReason::RemoveTlcFulfill(_fulfill) => {
write!(f, "RemoveTlcFulfill")
}
RemoveTlcReason::RemoveTlcFail(_fail) => {
write!(f, "RemoveTlcFail")
}
}
}
}
impl RemoveTlcReason {
pub fn backward(self, shared_secret: &[u8; 32]) -> Self {
match self {
RemoveTlcReason::RemoveTlcFulfill(remove_tlc_fulfill) => {
RemoveTlcReason::RemoveTlcFulfill(remove_tlc_fulfill)
}
RemoveTlcReason::RemoveTlcFail(remove_tlc_fail) => {
RemoveTlcReason::RemoveTlcFail(remove_tlc_fail.backward(shared_secret))
}
}
}
}
impl From<RemoveTlcReason> for molecule_fiber::RemoveTlcReasonUnion {
fn from(remove_tlc_reason: RemoveTlcReason) -> Self {
match remove_tlc_reason {
RemoveTlcReason::RemoveTlcFulfill(remove_tlc_fulfill) => {
molecule_fiber::RemoveTlcReasonUnion::RemoveTlcFulfill(remove_tlc_fulfill.into())
}
RemoveTlcReason::RemoveTlcFail(remove_tlc_fail) => {
molecule_fiber::RemoveTlcReasonUnion::TlcErrPacket(remove_tlc_fail.into())
}
}
}
}
impl From<RemoveTlcReason> for molecule_fiber::RemoveTlcReason {
fn from(remove_tlc_reason: RemoveTlcReason) -> Self {
molecule_fiber::RemoveTlcReason::new_builder()
.set(remove_tlc_reason)
.build()
}
}
impl From<molecule_fiber::RemoveTlcReason> for RemoveTlcReason {
fn from(remove_tlc_reason: molecule_fiber::RemoveTlcReason) -> Self {
match remove_tlc_reason.to_enum() {
molecule_fiber::RemoveTlcReasonUnion::RemoveTlcFulfill(remove_tlc_fulfill) => {
RemoveTlcReason::RemoveTlcFulfill(remove_tlc_fulfill.into())
}
molecule_fiber::RemoveTlcReasonUnion::TlcErrPacket(remove_tlc_fail) => {
RemoveTlcReason::RemoveTlcFail(remove_tlc_fail.into())
}
}
}
}
impl From<RemoveTlcFulfill> for molecule_fiber::RemoveTlcFulfill {
fn from(remove_tlc_fulfill: RemoveTlcFulfill) -> Self {
molecule_fiber::RemoveTlcFulfill::new_builder()
.payment_preimage(remove_tlc_fulfill.payment_preimage.into())
.build()
}
}
impl From<molecule_fiber::RemoveTlcFulfill> for RemoveTlcFulfill {
fn from(remove_tlc_fulfill: molecule_fiber::RemoveTlcFulfill) -> Self {
RemoveTlcFulfill {
payment_preimage: remove_tlc_fulfill.payment_preimage().into(),
}
}
}
#[serde_as]
#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub struct ChannelUpdateInfo {
#[serde_as(as = "crate::U64Hex")]
pub timestamp: u64,
pub enabled: bool,
#[serde_as(as = "Option<crate::U128Hex>")]
pub outbound_liquidity: Option<u128>,
#[serde_as(as = "crate::U64Hex")]
pub tlc_expiry_delta: u64,
#[serde_as(as = "crate::U128Hex")]
pub tlc_minimum_value: u128,
#[serde_as(as = "crate::U64Hex")]
pub fee_rate: u64,
}
impl From<&ChannelTlcInfo> for ChannelUpdateInfo {
fn from(info: &ChannelTlcInfo) -> Self {
Self {
timestamp: info.timestamp,
enabled: info.enabled,
outbound_liquidity: None,
tlc_expiry_delta: info.tlc_expiry_delta,
tlc_minimum_value: info.tlc_minimum_value,
fee_rate: info.tlc_fee_proportional_millionths as u64,
}
}
}
impl From<ChannelTlcInfo> for ChannelUpdateInfo {
fn from(info: ChannelTlcInfo) -> Self {
Self::from(&info)
}
}
impl From<crate::protocol::ChannelUpdate> for ChannelUpdateInfo {
fn from(update: crate::protocol::ChannelUpdate) -> Self {
Self::from(&update)
}
}
impl From<&crate::protocol::ChannelUpdate> for ChannelUpdateInfo {
fn from(update: &crate::protocol::ChannelUpdate) -> Self {
Self {
timestamp: update.timestamp,
enabled: !update.is_disabled(),
outbound_liquidity: None,
tlc_expiry_delta: update.tlc_expiry_delta,
tlc_minimum_value: update.tlc_minimum_value,
fee_rate: update.tlc_fee_proportional_millionths as u64,
}
}
}