-
Notifications
You must be signed in to change notification settings - Fork 103
Extend API to allow invoice creation with a description hash #438
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,6 +12,7 @@ | |
use crate::config::{Config, LDK_PAYMENT_RETRY_TIMEOUT}; | ||
use crate::connection::ConnectionManager; | ||
use crate::error::Error; | ||
use crate::hex_utils; | ||
use crate::liquidity::LiquiditySource; | ||
use crate::logger::{log_error, log_info, FilesystemLogger, Logger}; | ||
use crate::payment::store::{ | ||
|
@@ -30,11 +31,12 @@ use lightning::routing::router::{PaymentParameters, RouteParameters}; | |
|
||
use lightning_types::payment::{PaymentHash, PaymentPreimage}; | ||
|
||
use lightning_invoice::{Bolt11Invoice, Bolt11InvoiceDescription, Description}; | ||
use lightning_invoice::{Bolt11Invoice, Description}; | ||
|
||
use bitcoin::hashes::sha256::Hash as Sha256; | ||
use bitcoin::hashes::Hash; | ||
|
||
use std::str::FromStr; | ||
use std::sync::{Arc, RwLock}; | ||
|
||
/// A payment handler allowing to create and pay [BOLT 11] invoices. | ||
|
@@ -403,12 +405,23 @@ impl Bolt11Payment { | |
/// given. | ||
/// | ||
/// The inbound payment will be automatically claimed upon arrival. | ||
#[cfg(not(feature = "uniffi"))] | ||
pub fn receive( | ||
&self, amount_msat: u64, description: &str, expiry_secs: u32, | ||
&self, amount_msat: u64, description: &lightning_invoice::Bolt11InvoiceDescription, | ||
expiry_secs: u32, | ||
) -> Result<Bolt11Invoice, Error> { | ||
self.receive_inner(Some(amount_msat), description, expiry_secs, None) | ||
} | ||
|
||
#[cfg(feature = "uniffi")] | ||
pub fn receive( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I double checked this as it confused me that it be possible to omit docs here. And indeed it doesn't work: note that if you run For non-Uniffi we forbid missing docs on a crate-wide level (see top of |
||
&self, amount_msat: u64, description: &Bolt11InvoiceDescription, expiry_secs: u32, | ||
) -> Result<Bolt11Invoice, Error> { | ||
let invoice_description = | ||
lightning_invoice::Bolt11InvoiceDescription::try_from(description)?; | ||
self.receive_inner(Some(amount_msat), &invoice_description, expiry_secs, None) | ||
} | ||
|
||
/// Returns a payable invoice that can be used to request a payment of the amount | ||
/// given for the given payment hash. | ||
/// | ||
|
@@ -423,22 +436,44 @@ impl Bolt11Payment { | |
/// [`PaymentClaimable`]: crate::Event::PaymentClaimable | ||
/// [`claim_for_hash`]: Self::claim_for_hash | ||
/// [`fail_for_hash`]: Self::fail_for_hash | ||
#[cfg(not(feature = "uniffi"))] | ||
pub fn receive_for_hash( | ||
&self, amount_msat: u64, description: &str, expiry_secs: u32, payment_hash: PaymentHash, | ||
&self, amount_msat: u64, description: &lightning_invoice::Bolt11InvoiceDescription, | ||
expiry_secs: u32, payment_hash: PaymentHash, | ||
) -> Result<Bolt11Invoice, Error> { | ||
self.receive_inner(Some(amount_msat), description, expiry_secs, Some(payment_hash)) | ||
} | ||
|
||
#[cfg(feature = "uniffi")] | ||
pub fn receive_for_hash( | ||
&self, amount_msat: u64, description: &Bolt11InvoiceDescription, expiry_secs: u32, | ||
payment_hash: PaymentHash, | ||
) -> Result<Bolt11Invoice, Error> { | ||
let invoice_description = | ||
lightning_invoice::Bolt11InvoiceDescription::try_from(description)?; | ||
self.receive_inner(Some(amount_msat), &invoice_description, expiry_secs, Some(payment_hash)) | ||
} | ||
|
||
/// Returns a payable invoice that can be used to request and receive a payment for which the | ||
/// amount is to be determined by the user, also known as a "zero-amount" invoice. | ||
/// | ||
/// The inbound payment will be automatically claimed upon arrival. | ||
#[cfg(not(feature = "uniffi"))] | ||
pub fn receive_variable_amount( | ||
&self, description: &str, expiry_secs: u32, | ||
&self, description: &lightning_invoice::Bolt11InvoiceDescription, expiry_secs: u32, | ||
) -> Result<Bolt11Invoice, Error> { | ||
self.receive_inner(None, description, expiry_secs, None) | ||
} | ||
|
||
#[cfg(feature = "uniffi")] | ||
pub fn receive_variable_amount( | ||
&self, description: &Bolt11InvoiceDescription, expiry_secs: u32, | ||
) -> Result<Bolt11Invoice, Error> { | ||
let invoice_description = | ||
lightning_invoice::Bolt11InvoiceDescription::try_from(description)?; | ||
self.receive_inner(None, &invoice_description, expiry_secs, None) | ||
} | ||
|
||
/// Returns a payable invoice that can be used to request a payment for the given payment hash | ||
/// and the amount to be determined by the user, also known as a "zero-amount" invoice. | ||
/// | ||
|
@@ -453,24 +488,32 @@ impl Bolt11Payment { | |
/// [`PaymentClaimable`]: crate::Event::PaymentClaimable | ||
/// [`claim_for_hash`]: Self::claim_for_hash | ||
/// [`fail_for_hash`]: Self::fail_for_hash | ||
#[cfg(not(feature = "uniffi"))] | ||
pub fn receive_variable_amount_for_hash( | ||
&self, description: &str, expiry_secs: u32, payment_hash: PaymentHash, | ||
&self, description: &lightning_invoice::Bolt11InvoiceDescription, expiry_secs: u32, | ||
payment_hash: PaymentHash, | ||
) -> Result<Bolt11Invoice, Error> { | ||
self.receive_inner(None, description, expiry_secs, Some(payment_hash)) | ||
} | ||
|
||
fn receive_inner( | ||
&self, amount_msat: Option<u64>, description: &str, expiry_secs: u32, | ||
manual_claim_payment_hash: Option<PaymentHash>, | ||
#[cfg(feature = "uniffi")] | ||
pub fn receive_variable_amount_for_hash( | ||
&self, description: &Bolt11InvoiceDescription, expiry_secs: u32, payment_hash: PaymentHash, | ||
) -> Result<Bolt11Invoice, Error> { | ||
let invoice_description = Bolt11InvoiceDescription::Direct( | ||
Description::new(description.to_string()).map_err(|_| Error::InvoiceCreationFailed)?, | ||
); | ||
let invoice_description = | ||
lightning_invoice::Bolt11InvoiceDescription::try_from(description)?; | ||
self.receive_inner(None, &invoice_description, expiry_secs, Some(payment_hash)) | ||
} | ||
|
||
pub(crate) fn receive_inner( | ||
&self, amount_msat: Option<u64>, | ||
invoice_description: &lightning_invoice::Bolt11InvoiceDescription, expiry_secs: u32, | ||
manual_claim_payment_hash: Option<PaymentHash>, | ||
) -> Result<Bolt11Invoice, Error> { | ||
let invoice = { | ||
let invoice_params = Bolt11InvoiceParameters { | ||
amount_msats: amount_msat, | ||
description: invoice_description, | ||
description: invoice_description.clone(), | ||
invoice_expiry_delta_secs: Some(expiry_secs), | ||
payment_hash: manual_claim_payment_hash, | ||
..Default::default() | ||
|
@@ -530,9 +573,10 @@ impl Bolt11Payment { | |
/// channel to us. We'll use its cheapest offer otherwise. | ||
/// | ||
/// [LSPS2]: https://github.com/BitcoinAndLightningLayerSpecs/lsp/blob/main/LSPS2/README.md | ||
#[cfg(not(feature = "uniffi"))] | ||
pub fn receive_via_jit_channel( | ||
&self, amount_msat: u64, description: &str, expiry_secs: u32, | ||
max_total_lsp_fee_limit_msat: Option<u64>, | ||
&self, amount_msat: u64, description: &lightning_invoice::Bolt11InvoiceDescription, | ||
expiry_secs: u32, max_total_lsp_fee_limit_msat: Option<u64>, | ||
) -> Result<Bolt11Invoice, Error> { | ||
self.receive_via_jit_channel_inner( | ||
Some(amount_msat), | ||
|
@@ -543,6 +587,22 @@ impl Bolt11Payment { | |
) | ||
} | ||
|
||
#[cfg(feature = "uniffi")] | ||
pub fn receive_via_jit_channel( | ||
&self, amount_msat: u64, description: &Bolt11InvoiceDescription, expiry_secs: u32, | ||
max_total_lsp_fee_limit_msat: Option<u64>, | ||
) -> Result<Bolt11Invoice, Error> { | ||
let invoice_description = | ||
lightning_invoice::Bolt11InvoiceDescription::try_from(description)?; | ||
self.receive_via_jit_channel_inner( | ||
Some(amount_msat), | ||
&invoice_description, | ||
expiry_secs, | ||
max_total_lsp_fee_limit_msat, | ||
None, | ||
) | ||
} | ||
|
||
/// Returns a payable invoice that can be used to request a variable amount payment (also known | ||
/// as "zero-amount" invoice) and receive it via a newly created just-in-time (JIT) channel. | ||
/// | ||
|
@@ -554,8 +614,9 @@ impl Bolt11Payment { | |
/// We'll use its cheapest offer otherwise. | ||
/// | ||
/// [LSPS2]: https://github.com/BitcoinAndLightningLayerSpecs/lsp/blob/main/LSPS2/README.md | ||
#[cfg(not(feature = "uniffi"))] | ||
pub fn receive_variable_amount_via_jit_channel( | ||
&self, description: &str, expiry_secs: u32, | ||
&self, description: &lightning_invoice::Bolt11InvoiceDescription, expiry_secs: u32, | ||
max_proportional_lsp_fee_limit_ppm_msat: Option<u64>, | ||
) -> Result<Bolt11Invoice, Error> { | ||
self.receive_via_jit_channel_inner( | ||
|
@@ -567,9 +628,25 @@ impl Bolt11Payment { | |
) | ||
} | ||
|
||
#[cfg(feature = "uniffi")] | ||
pub fn receive_variable_amount_via_jit_channel( | ||
&self, description: &Bolt11InvoiceDescription, expiry_secs: u32, | ||
max_proportional_lsp_fee_limit_ppm_msat: Option<u64>, | ||
) -> Result<Bolt11Invoice, Error> { | ||
let invoice_description = | ||
lightning_invoice::Bolt11InvoiceDescription::try_from(description)?; | ||
self.receive_via_jit_channel_inner( | ||
None, | ||
&invoice_description, | ||
expiry_secs, | ||
None, | ||
max_proportional_lsp_fee_limit_ppm_msat, | ||
) | ||
} | ||
|
||
fn receive_via_jit_channel_inner( | ||
&self, amount_msat: Option<u64>, description: &str, expiry_secs: u32, | ||
max_total_lsp_fee_limit_msat: Option<u64>, | ||
&self, amount_msat: Option<u64>, description: &lightning_invoice::Bolt11InvoiceDescription, | ||
expiry_secs: u32, max_total_lsp_fee_limit_msat: Option<u64>, | ||
max_proportional_lsp_fee_limit_ppm_msat: Option<u64>, | ||
) -> Result<Bolt11Invoice, Error> { | ||
let liquidity_source = | ||
|
@@ -740,3 +817,49 @@ impl Bolt11Payment { | |
Ok(()) | ||
} | ||
} | ||
|
||
/// Represents the description of an invoice which has to be either a directly included string or | ||
/// a hash of a description provided out of band. | ||
pub enum Bolt11InvoiceDescription { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's move this type definition to |
||
/// Contains a full description. | ||
Direct { | ||
/// Description of what the invoice is for | ||
description: String, | ||
}, | ||
/// Contains a hash. | ||
Hash { | ||
/// Hash of the description of what the invoice is for | ||
hash: String, | ||
}, | ||
} | ||
|
||
impl TryFrom<&Bolt11InvoiceDescription> for lightning_invoice::Bolt11InvoiceDescription { | ||
type Error = Error; | ||
|
||
fn try_from(value: &Bolt11InvoiceDescription) -> Result<Self, Self::Error> { | ||
match value { | ||
Bolt11InvoiceDescription::Direct { description } => { | ||
Description::new(description.clone()) | ||
.map(lightning_invoice::Bolt11InvoiceDescription::Direct) | ||
.map_err(|_| Error::InvoiceCreationFailed) | ||
}, | ||
Bolt11InvoiceDescription::Hash { hash } => Sha256::from_str(&hash) | ||
.map(lightning_invoice::Sha256) | ||
.map(lightning_invoice::Bolt11InvoiceDescription::Hash) | ||
.map_err(|_| Error::InvoiceCreationFailed), | ||
} | ||
} | ||
} | ||
|
||
impl From<lightning_invoice::Bolt11InvoiceDescription> for Bolt11InvoiceDescription { | ||
fn from(value: lightning_invoice::Bolt11InvoiceDescription) -> Self { | ||
match value { | ||
lightning_invoice::Bolt11InvoiceDescription::Direct(description) => { | ||
Bolt11InvoiceDescription::Direct { description: description.to_string() } | ||
}, | ||
lightning_invoice::Bolt11InvoiceDescription::Hash(hash) => { | ||
Bolt11InvoiceDescription::Hash { hash: hex_utils::to_string(hash.0.as_ref()) } | ||
}, | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think rather than duplicating all of these, we should be able to follow the same approach as #434, i.e.,
a) add a local type alias
above and have the
receive
s use a macro like this:(could also consider using it in the
receive_inner
/receive_via_jit_channel_inner
, but the former is reused inunified_qr
, which complicates things. So probably easier to do the conversion before giving the description to the_inner
s).