Skip to content

Commit

Permalink
Add a supported_quantity during offer generation
Browse files Browse the repository at this point in the history
Bolt12Payment receive is updated to handle and set a quantity.
If the quantiy is Some and set to 0 the new error type
InvalidQuantity is returned. Otherwise we convert the set
quantity to Quantity::Bounded(). If quantity is None we use
Quantity::Unbounded.
  • Loading branch information
slanesuke committed Aug 13, 2024
1 parent de31355 commit c34abe3
Show file tree
Hide file tree
Showing 5 changed files with 48 additions and 21 deletions.
3 changes: 2 additions & 1 deletion bindings/ldk_node.udl
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ interface Bolt12Payment {
[Throws=NodeError]
PaymentId send_using_amount([ByRef]Offer offer, u64 amount_msat, u64? quantity, string? payer_note);
[Throws=NodeError]
Offer receive(u64 amount_msat, [ByRef]string description);
Offer receive(u64 amount_msat, [ByRef]string description, u64? quantity);
[Throws=NodeError]
Offer receive_variable_amount([ByRef]string description);
[Throws=NodeError]
Expand Down Expand Up @@ -201,6 +201,7 @@ enum NodeError {
"InvalidChannelId",
"InvalidNetwork",
"InvalidUri",
"InvalidQuantity",
"DuplicatePayment",
"UnsupportedCurrency",
"InsufficientFunds",
Expand Down
3 changes: 3 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ pub enum Error {
InvalidNetwork,
/// The given URI is invalid.
InvalidUri,
/// The given quantity is invalid.
InvalidQuantity,
/// A payment with the given hash has already been initiated.
DuplicatePayment,
/// The provided offer was denonminated in an unsupported currency.
Expand Down Expand Up @@ -153,6 +155,7 @@ impl fmt::Display for Error {
Self::InvalidChannelId => write!(f, "The given channel ID is invalid."),
Self::InvalidNetwork => write!(f, "The given network is invalid."),
Self::InvalidUri => write!(f, "The given URI is invalid."),
Self::InvalidQuantity => write!(f, "The given quantity is invalid."),
Self::DuplicatePayment => {
write!(f, "A payment with the given hash has already been initiated.")
},
Expand Down
19 changes: 17 additions & 2 deletions src/payment/bolt12.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,14 @@ use crate::types::ChannelManager;

use lightning::ln::channelmanager::{PaymentId, Retry};
use lightning::offers::invoice::Bolt12Invoice;
use lightning::offers::offer::{Amount, Offer};
use lightning::offers::offer::{Amount, Offer, Quantity};
use lightning::offers::parse::Bolt12SemanticError;
use lightning::offers::refund::Refund;
use lightning::util::string::UntrustedString;

use rand::RngCore;

use std::num::NonZeroU64;
use std::sync::{Arc, RwLock};
use std::time::{Duration, SystemTime, UNIX_EPOCH};

Expand Down Expand Up @@ -246,14 +247,28 @@ impl Bolt12Payment {

/// Returns a payable offer that can be used to request and receive a payment of the amount
/// given.
pub fn receive(&self, amount_msat: u64, description: &str) -> Result<Offer, Error> {
pub fn receive(
&self, amount_msat: u64, description: &str, quantity: Option<u64>,
) -> Result<Offer, Error> {
let offer_builder = self.channel_manager.create_offer_builder().map_err(|e| {
log_error!(self.logger, "Failed to create offer builder: {:?}", e);
Error::OfferCreationFailed
})?;

let user_set_qty = if let Some(qty) = quantity {
if qty == 0 {
return Err(Error::InvalidQuantity);
} else {
Quantity::Bounded(NonZeroU64::new(qty).unwrap())
}
} else {
Quantity::Unbounded
};

let offer = offer_builder
.amount_msats(amount_msat)
.description(description.to_string())
.supported_quantity(user_set_qty)
.build()
.map_err(|e| {
log_error!(self.logger, "Failed to create offer: {:?}", e);
Expand Down
2 changes: 1 addition & 1 deletion src/payment/unified_qr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ impl UnifiedQrPayment {

let amount_msats = amount_sats * 1_000;

let bolt12_offer = match self.bolt12_payment.receive(amount_msats, description) {
let bolt12_offer = match self.bolt12_payment.receive(amount_msats, description, None) {
Ok(offer) => Some(offer),
Err(e) => {
log_error!(self.logger, "Failed to create offer: {}", e);
Expand Down
42 changes: 25 additions & 17 deletions tests/integration_tests_rust.rs
Original file line number Diff line number Diff line change
Expand Up @@ -424,10 +424,13 @@ fn simple_bolt12_send_receive() {
std::thread::sleep(std::time::Duration::from_secs(1));

let expected_amount_msat = 100_000_000;
let offer = node_b.bolt12_payment().receive(expected_amount_msat, "asdf").unwrap();
let quantity = Some(1);
let payer_note = Some("Test".to_string());
let payment_id = node_a.bolt12_payment().send(&offer, quantity, payer_note.clone()).unwrap();
let offer = node_b.bolt12_payment().receive(expected_amount_msat, "asdf", None).unwrap();
let expected_quantity = Some(1);
let expected_payer_note = Some("Test".to_string());
let payment_id = node_a
.bolt12_payment()
.send(&offer, expected_quantity, expected_payer_note.clone())
.unwrap();

expect_payment_successful_event!(node_a, Some(payment_id), None);
let node_a_payments = node_a.list_payments();
Expand All @@ -444,8 +447,8 @@ fn simple_bolt12_send_receive() {
assert!(hash.is_some());
assert!(preimage.is_some());
assert_eq!(offer_id, offer.id());
assert_eq!(&quantity, qty);
assert_eq!(payer_note.unwrap(), note.clone().unwrap().0);
assert_eq!(&expected_quantity, qty);
assert_eq!(expected_payer_note.unwrap(), note.clone().unwrap().0);
//TODO: We should eventually set and assert the secret sender-side, too, but the BOLT12
//API currently doesn't allow to do that.
},
Expand Down Expand Up @@ -475,16 +478,21 @@ fn simple_bolt12_send_receive() {
let offer_amount_msat = 100_000_000;
let less_than_offer_amount = offer_amount_msat - 10_000;
let expected_amount_msat = offer_amount_msat + 10_000;
let offer = node_b.bolt12_payment().receive(offer_amount_msat, "asdf").unwrap();
let quantity = Some(1);
let payer_note = Some("Test".to_string());
let offer = node_b.bolt12_payment().receive(offer_amount_msat, "asdf", None).unwrap();
let expected_quantity = Some(1);
let expected_payer_note = Some("Test".to_string());
assert!(node_a
.bolt12_payment()
.send_using_amount(&offer, less_than_offer_amount, None, None)
.is_err());
let payment_id = node_a
.bolt12_payment()
.send_using_amount(&offer, expected_amount_msat, quantity, payer_note.clone())
.send_using_amount(
&offer,
expected_amount_msat,
expected_quantity,
expected_payer_note.clone(),
)
.unwrap();

expect_payment_successful_event!(node_a, Some(payment_id), None);
Expand All @@ -502,8 +510,8 @@ fn simple_bolt12_send_receive() {
assert!(hash.is_some());
assert!(preimage.is_some());
assert_eq!(offer_id, offer.id());
assert_eq!(&quantity, qty);
assert_eq!(payer_note.unwrap(), note.clone().unwrap().0);
assert_eq!(&expected_quantity, qty);
assert_eq!(expected_payer_note.unwrap(), note.clone().unwrap().0);
//TODO: We should eventually set and assert the secret sender-side, too, but the BOLT12
//API currently doesn't allow to do that.
hash.unwrap()
Expand Down Expand Up @@ -533,11 +541,11 @@ fn simple_bolt12_send_receive() {

// Now node_b refunds the amount node_a just overpaid.
let overpaid_amount = expected_amount_msat - offer_amount_msat;
let quantity = Some(1);
let payer_note = Some("Test".to_string());
let expected_quantity = Some(1);
let expected_payer_note = Some("Test".to_string());
let refund = node_b
.bolt12_payment()
.initiate_refund(overpaid_amount, 3600, quantity, payer_note.clone())
.initiate_refund(overpaid_amount, 3600, expected_quantity, expected_payer_note.clone())
.unwrap();
let invoice = node_a.bolt12_payment().request_refund_payment(&refund).unwrap();
expect_payment_received_event!(node_a, overpaid_amount);
Expand All @@ -561,8 +569,8 @@ fn simple_bolt12_send_receive() {
} => {
assert!(hash.is_some());
assert!(preimage.is_some());
assert_eq!(&quantity, qty);
assert_eq!(payer_note.unwrap(), note.clone().unwrap().0)
assert_eq!(&expected_quantity, qty);
assert_eq!(expected_payer_note.unwrap(), note.clone().unwrap().0)
//TODO: We should eventually set and assert the secret sender-side, too, but the BOLT12
//API currently doesn't allow to do that.
},
Expand Down

0 comments on commit c34abe3

Please sign in to comment.