Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions bindings/ldk_node.udl
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
namespace ldk_node {
Mnemonic generate_entropy_mnemonic(WordCount? word_count);
Config default_config();

// PaymentKind Accessors
PaymentHash? payment_kind_get_payment_hash(PaymentKind kind);
PaymentPreimage? payment_kind_get_preimage(PaymentKind kind);
PaymentSecret? payment_kind_get_secret(PaymentKind kind);
};

dictionary Config {
Expand Down
1 change: 1 addition & 0 deletions src/ffi/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ pub use crate::graph::{ChannelInfo, ChannelUpdateInfo, NodeAnnouncementInfo, Nod
pub use crate::liquidity::{LSPS1OrderStatus, LSPS2ServiceConfig};
pub use crate::logger::{LogLevel, LogRecord, LogWriter};
pub use crate::payment::store::{
payment_kind_get_payment_hash, payment_kind_get_preimage, payment_kind_get_secret,
ConfirmationStatus, LSPFeeLimits, PaymentDirection, PaymentKind, PaymentStatus,
};
pub use crate::payment::QrPaymentResult;
Expand Down
164 changes: 156 additions & 8 deletions src/payment/store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -483,6 +483,70 @@ impl_writeable_tlv_based_enum!(PaymentKind,
}
);

impl PaymentKind {
/// Returns the payment hash if this payment kind has one.
///
/// Returns `None` for on-chain payments or when the hash hasn't been set yet (e.g., for
/// outbound BOLT 12 payments before receiving an invoice).
pub fn payment_hash(&self) -> Option<PaymentHash> {
match self {
PaymentKind::Onchain { .. } => None,
PaymentKind::Bolt11 { hash, .. } => Some(*hash),
PaymentKind::Bolt11Jit { hash, .. } => Some(*hash),
PaymentKind::Bolt12Offer { hash, .. } => *hash,
PaymentKind::Bolt12Refund { hash, .. } => *hash,
PaymentKind::Spontaneous { hash, .. } => Some(*hash),
}
}

/// Returns the payment preimage if this payment kind has one.
///
/// Returns `None` for on-chain payments or when the preimage hasn't been set yet.
pub fn preimage(&self) -> Option<PaymentPreimage> {
match self {
PaymentKind::Onchain { .. } => None,
PaymentKind::Bolt11 { preimage, .. } => *preimage,
PaymentKind::Bolt11Jit { preimage, .. } => *preimage,
PaymentKind::Bolt12Offer { preimage, .. } => *preimage,
PaymentKind::Bolt12Refund { preimage, .. } => *preimage,
PaymentKind::Spontaneous { preimage, .. } => *preimage,
}
}

/// Returns the payment secret if this payment kind has one.
///
/// Returns `None` for on-chain payments, spontaneous payments, or when the secret hasn't been
/// set yet.
pub fn secret(&self) -> Option<PaymentSecret> {
match self {
PaymentKind::Onchain { .. } => None,
PaymentKind::Bolt11 { secret, .. } => *secret,
PaymentKind::Bolt11Jit { secret, .. } => *secret,
PaymentKind::Bolt12Offer { secret, .. } => *secret,
PaymentKind::Bolt12Refund { secret, .. } => *secret,
PaymentKind::Spontaneous { .. } => None,
}
}
}

#[cfg(feature = "uniffi")]
#[doc(hidden)]
pub fn payment_kind_get_payment_hash(kind: PaymentKind) -> Option<PaymentHash> {
kind.payment_hash()
}

#[cfg(feature = "uniffi")]
#[doc(hidden)]
pub fn payment_kind_get_preimage(kind: PaymentKind) -> Option<PaymentPreimage> {
kind.preimage()
}

#[cfg(feature = "uniffi")]
#[doc(hidden)]
pub fn payment_kind_get_secret(kind: PaymentKind) -> Option<PaymentSecret> {
kind.secret()
}

/// Represents the confirmation status of a transaction.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum ConfirmationStatus {
Expand Down Expand Up @@ -561,14 +625,9 @@ impl PaymentDetailsUpdate {

impl From<&PaymentDetails> for PaymentDetailsUpdate {
fn from(value: &PaymentDetails) -> Self {
let (hash, preimage, secret) = match value.kind {
PaymentKind::Bolt11 { hash, preimage, secret, .. } => (Some(hash), preimage, secret),
PaymentKind::Bolt11Jit { hash, preimage, secret, .. } => (Some(hash), preimage, secret),
PaymentKind::Bolt12Offer { hash, preimage, secret, .. } => (hash, preimage, secret),
PaymentKind::Bolt12Refund { hash, preimage, secret, .. } => (hash, preimage, secret),
PaymentKind::Spontaneous { hash, preimage, .. } => (Some(hash), preimage, None),
_ => (None, None, None),
};
let hash = value.kind.payment_hash();
let preimage = value.kind.preimage();
let secret = value.kind.secret();

let confirmation_status = match value.kind {
PaymentKind::Onchain { status, .. } => Some(status),
Expand Down Expand Up @@ -768,4 +827,93 @@ mod tests {
}
}
}

#[test]
fn payment_kind_accessor_methods() {
use bitcoin::hashes::Hash;

let hash = PaymentHash([42u8; 32]);
let preimage = Some(PaymentPreimage([43u8; 32]));
let secret = Some(PaymentSecret([44u8; 32]));
let txid = bitcoin::Txid::from_byte_array([1u8; 32]);
let offer_id = OfferId([2u8; 32]);

// Test Onchain variant
let onchain_kind = PaymentKind::Onchain { txid, status: ConfirmationStatus::Unconfirmed };
assert_eq!(onchain_kind.payment_hash(), None);
assert_eq!(onchain_kind.preimage(), None);
assert_eq!(onchain_kind.secret(), None);

// Test Bolt11 variant
let bolt11_kind = PaymentKind::Bolt11 { hash, preimage, secret };
assert_eq!(bolt11_kind.payment_hash(), Some(hash));
assert_eq!(bolt11_kind.preimage(), preimage);
assert_eq!(bolt11_kind.secret(), secret);

// Test Bolt11 variant without preimage/secret
let bolt11_kind_empty = PaymentKind::Bolt11 { hash, preimage: None, secret: None };
assert_eq!(bolt11_kind_empty.payment_hash(), Some(hash));
assert_eq!(bolt11_kind_empty.preimage(), None);
assert_eq!(bolt11_kind_empty.secret(), None);

// Test Bolt11Jit variant
let lsp_fee_limits = LSPFeeLimits {
max_total_opening_fee_msat: Some(1000),
max_proportional_opening_fee_ppm_msat: Some(100),
};
let bolt11_jit_kind = PaymentKind::Bolt11Jit {
hash,
preimage,
secret,
counterparty_skimmed_fee_msat: Some(500),
lsp_fee_limits,
};
assert_eq!(bolt11_jit_kind.payment_hash(), Some(hash));
assert_eq!(bolt11_jit_kind.preimage(), preimage);
assert_eq!(bolt11_jit_kind.secret(), secret);

// Test Bolt12Offer variant with hash
let bolt12_offer_kind = PaymentKind::Bolt12Offer {
hash: Some(hash),
preimage,
secret,
offer_id,
payer_note: None,
quantity: None,
};
assert_eq!(bolt12_offer_kind.payment_hash(), Some(hash));
assert_eq!(bolt12_offer_kind.preimage(), preimage);
assert_eq!(bolt12_offer_kind.secret(), secret);

// Test Bolt12Offer variant without hash (e.g., before invoice received)
let bolt12_offer_kind_no_hash = PaymentKind::Bolt12Offer {
hash: None,
preimage: None,
secret: None,
offer_id,
payer_note: None,
quantity: None,
};
assert_eq!(bolt12_offer_kind_no_hash.payment_hash(), None);
assert_eq!(bolt12_offer_kind_no_hash.preimage(), None);
assert_eq!(bolt12_offer_kind_no_hash.secret(), None);

// Test Bolt12Refund variant
let bolt12_refund_kind = PaymentKind::Bolt12Refund {
hash: Some(hash),
preimage,
secret,
payer_note: None,
quantity: None,
};
assert_eq!(bolt12_refund_kind.payment_hash(), Some(hash));
assert_eq!(bolt12_refund_kind.preimage(), preimage);
assert_eq!(bolt12_refund_kind.secret(), secret);

// Test Spontaneous variant (no secret)
let spontaneous_kind = PaymentKind::Spontaneous { hash, preimage };
assert_eq!(spontaneous_kind.payment_hash(), Some(hash));
assert_eq!(spontaneous_kind.preimage(), preimage);
assert_eq!(spontaneous_kind.secret(), None);
}
}