Skip to content

Commit f07d15b

Browse files
committed
Drop quantity support from BOLT 12 logic
The BOLT 12 `quantity` field is incredibly unlikely to get any use in practice (it assumes an online store which places some kind of static offer for each item on its site, assuming that the merchant doesn't want structured customer information nor the ability to sell more than one item in a single order, both of which do not exist in practice). Worse, supporting it requires an entire UI flow built around the "quantity" concept, something which is a nontrivial investment for downstream users of ldk-node. Because the cost/utility tradeoff isn't nearly worth it, it was dropped from the main offer-payment API in the upstream `lightning` crate (requiring a separate `ChannelManager::pay_for_offer_with_quantity` call). For the same reason, we simply drop it from our API entirely here. Absent someone who actually wants to use the `quantity` logic, there is really no reason to support it.
1 parent 0f1d66c commit f07d15b

File tree

7 files changed

+53
-131
lines changed

7 files changed

+53
-131
lines changed

bindings/ldk_node.udl

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -199,17 +199,17 @@ interface Bolt11Payment {
199199

200200
interface Bolt12Payment {
201201
[Throws=NodeError]
202-
PaymentId send([ByRef]Offer offer, u64? quantity, string? payer_note);
202+
PaymentId send([ByRef]Offer offer, string? payer_note);
203203
[Throws=NodeError]
204-
PaymentId send_using_amount([ByRef]Offer offer, u64 amount_msat, u64? quantity, string? payer_note);
204+
PaymentId send_using_amount([ByRef]Offer offer, u64 amount_msat, string? payer_note);
205205
[Throws=NodeError]
206-
Offer receive(u64 amount_msat, [ByRef]string description, u32? expiry_secs, u64? quantity);
206+
Offer receive(u64 amount_msat, [ByRef]string description, u32? expiry_secs);
207207
[Throws=NodeError]
208208
Offer receive_variable_amount([ByRef]string description, u32? expiry_secs);
209209
[Throws=NodeError]
210210
Bolt12Invoice request_refund_payment([ByRef]Refund refund);
211211
[Throws=NodeError]
212-
Refund initiate_refund(u64 amount_msat, u32 expiry_secs, u64? quantity, string? payer_note);
212+
Refund initiate_refund(u64 amount_msat, u32 expiry_secs, string? payer_note);
213213
[Throws=NodeError]
214214
Offer receive_async();
215215
[Throws=NodeError]
@@ -422,8 +422,8 @@ interface PaymentKind {
422422
Onchain(Txid txid, ConfirmationStatus status);
423423
Bolt11(PaymentHash hash, PaymentPreimage? preimage, PaymentSecret? secret);
424424
Bolt11Jit(PaymentHash hash, PaymentPreimage? preimage, PaymentSecret? secret, u64? counterparty_skimmed_fee_msat, LSPFeeLimits lsp_fee_limits);
425-
Bolt12Offer(PaymentHash? hash, PaymentPreimage? preimage, PaymentSecret? secret, OfferId offer_id, UntrustedString? payer_note, u64? quantity);
426-
Bolt12Refund(PaymentHash? hash, PaymentPreimage? preimage, PaymentSecret? secret, UntrustedString? payer_note, u64? quantity);
425+
Bolt12Offer(PaymentHash? hash, PaymentPreimage? preimage, PaymentSecret? secret, OfferId offer_id, UntrustedString? payer_note);
426+
Bolt12Refund(PaymentHash? hash, PaymentPreimage? preimage, PaymentSecret? secret, UntrustedString? payer_note);
427427
Spontaneous(PaymentHash hash, PaymentPreimage? preimage);
428428
};
429429

bindings/swift/Sources/LDKNode/LDKNode.swift

Lines changed: 16 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1015,17 +1015,17 @@ public func FfiConverterTypeBolt11Payment_lower(_ value: Bolt11Payment) -> Unsaf
10151015

10161016
public protocol Bolt12PaymentProtocol : AnyObject {
10171017

1018-
func initiateRefund(amountMsat: UInt64, expirySecs: UInt32, quantity: UInt64?, payerNote: String?) throws -> Refund
1018+
func initiateRefund(amountMsat: UInt64, expirySecs: UInt32, payerNote: String?) throws -> Refund
10191019

1020-
func receive(amountMsat: UInt64, description: String, expirySecs: UInt32?, quantity: UInt64?) throws -> Offer
1020+
func receive(amountMsat: UInt64, description: String, expirySecs: UInt32?) throws -> Offer
10211021

10221022
func receiveVariableAmount(description: String, expirySecs: UInt32?) throws -> Offer
10231023

10241024
func requestRefundPayment(refund: Refund) throws -> Bolt12Invoice
10251025

1026-
func send(offer: Offer, quantity: UInt64?, payerNote: String?) throws -> PaymentId
1026+
func send(offer: Offer, payerNote: String?) throws -> PaymentId
10271027

1028-
func sendUsingAmount(offer: Offer, amountMsat: UInt64, quantity: UInt64?, payerNote: String?) throws -> PaymentId
1028+
func sendUsingAmount(offer: Offer, amountMsat: UInt64, payerNote: String?) throws -> PaymentId
10291029

10301030
}
10311031

@@ -1070,24 +1070,22 @@ open class Bolt12Payment:
10701070

10711071

10721072

1073-
open func initiateRefund(amountMsat: UInt64, expirySecs: UInt32, quantity: UInt64?, payerNote: String?)throws -> Refund {
1073+
open func initiateRefund(amountMsat: UInt64, expirySecs: UInt32, payerNote: String?)throws -> Refund {
10741074
return try FfiConverterTypeRefund.lift(try rustCallWithError(FfiConverterTypeNodeError.lift) {
10751075
uniffi_ldk_node_fn_method_bolt12payment_initiate_refund(self.uniffiClonePointer(),
10761076
FfiConverterUInt64.lower(amountMsat),
10771077
FfiConverterUInt32.lower(expirySecs),
1078-
FfiConverterOptionUInt64.lower(quantity),
10791078
FfiConverterOptionString.lower(payerNote),$0
10801079
)
10811080
})
10821081
}
10831082

1084-
open func receive(amountMsat: UInt64, description: String, expirySecs: UInt32?, quantity: UInt64?)throws -> Offer {
1083+
open func receive(amountMsat: UInt64, description: String, expirySecs: UInt32?)throws -> Offer {
10851084
return try FfiConverterTypeOffer.lift(try rustCallWithError(FfiConverterTypeNodeError.lift) {
10861085
uniffi_ldk_node_fn_method_bolt12payment_receive(self.uniffiClonePointer(),
10871086
FfiConverterUInt64.lower(amountMsat),
10881087
FfiConverterString.lower(description),
1089-
FfiConverterOptionUInt32.lower(expirySecs),
1090-
FfiConverterOptionUInt64.lower(quantity),$0
1088+
FfiConverterOptionUInt32.lower(expirySecs),$0
10911089
)
10921090
})
10931091
}
@@ -1109,22 +1107,20 @@ open func requestRefundPayment(refund: Refund)throws -> Bolt12Invoice {
11091107
})
11101108
}
11111109

1112-
open func send(offer: Offer, quantity: UInt64?, payerNote: String?)throws -> PaymentId {
1110+
open func send(offer: Offer, payerNote: String?)throws -> PaymentId {
11131111
return try FfiConverterTypePaymentId.lift(try rustCallWithError(FfiConverterTypeNodeError.lift) {
11141112
uniffi_ldk_node_fn_method_bolt12payment_send(self.uniffiClonePointer(),
11151113
FfiConverterTypeOffer.lower(offer),
1116-
FfiConverterOptionUInt64.lower(quantity),
11171114
FfiConverterOptionString.lower(payerNote),$0
11181115
)
11191116
})
11201117
}
11211118

1122-
open func sendUsingAmount(offer: Offer, amountMsat: UInt64, quantity: UInt64?, payerNote: String?)throws -> PaymentId {
1119+
open func sendUsingAmount(offer: Offer, amountMsat: UInt64, payerNote: String?)throws -> PaymentId {
11231120
return try FfiConverterTypePaymentId.lift(try rustCallWithError(FfiConverterTypeNodeError.lift) {
11241121
uniffi_ldk_node_fn_method_bolt12payment_send_using_amount(self.uniffiClonePointer(),
11251122
FfiConverterTypeOffer.lower(offer),
11261123
FfiConverterUInt64.lower(amountMsat),
1127-
FfiConverterOptionUInt64.lower(quantity),
11281124
FfiConverterOptionString.lower(payerNote),$0
11291125
)
11301126
})
@@ -7098,9 +7094,9 @@ public enum PaymentKind {
70987094
)
70997095
case bolt11Jit(hash: PaymentHash, preimage: PaymentPreimage?, secret: PaymentSecret?, counterpartySkimmedFeeMsat: UInt64?, lspFeeLimits: LspFeeLimits
71007096
)
7101-
case bolt12Offer(hash: PaymentHash?, preimage: PaymentPreimage?, secret: PaymentSecret?, offerId: OfferId, payerNote: UntrustedString?, quantity: UInt64?
7097+
case bolt12Offer(hash: PaymentHash?, preimage: PaymentPreimage?, secret: PaymentSecret?, offerId: OfferId, payerNote: UntrustedString?
71027098
)
7103-
case bolt12Refund(hash: PaymentHash?, preimage: PaymentPreimage?, secret: PaymentSecret?, payerNote: UntrustedString?, quantity: UInt64?
7099+
case bolt12Refund(hash: PaymentHash?, preimage: PaymentPreimage?, secret: PaymentSecret?, payerNote: UntrustedString?
71047100
)
71057101
case spontaneous(hash: PaymentHash, preimage: PaymentPreimage?
71067102
)
@@ -7123,10 +7119,10 @@ public struct FfiConverterTypePaymentKind: FfiConverterRustBuffer {
71237119
case 3: return .bolt11Jit(hash: try FfiConverterTypePaymentHash.read(from: &buf), preimage: try FfiConverterOptionTypePaymentPreimage.read(from: &buf), secret: try FfiConverterOptionTypePaymentSecret.read(from: &buf), counterpartySkimmedFeeMsat: try FfiConverterOptionUInt64.read(from: &buf), lspFeeLimits: try FfiConverterTypeLSPFeeLimits.read(from: &buf)
71247120
)
71257121

7126-
case 4: return .bolt12Offer(hash: try FfiConverterOptionTypePaymentHash.read(from: &buf), preimage: try FfiConverterOptionTypePaymentPreimage.read(from: &buf), secret: try FfiConverterOptionTypePaymentSecret.read(from: &buf), offerId: try FfiConverterTypeOfferId.read(from: &buf), payerNote: try FfiConverterOptionTypeUntrustedString.read(from: &buf), quantity: try FfiConverterOptionUInt64.read(from: &buf)
7122+
case 4: return .bolt12Offer(hash: try FfiConverterOptionTypePaymentHash.read(from: &buf), preimage: try FfiConverterOptionTypePaymentPreimage.read(from: &buf), secret: try FfiConverterOptionTypePaymentSecret.read(from: &buf), offerId: try FfiConverterTypeOfferId.read(from: &buf), payerNote: try FfiConverterOptionTypeUntrustedString.read(from: &buf)
71277123
)
71287124

7129-
case 5: return .bolt12Refund(hash: try FfiConverterOptionTypePaymentHash.read(from: &buf), preimage: try FfiConverterOptionTypePaymentPreimage.read(from: &buf), secret: try FfiConverterOptionTypePaymentSecret.read(from: &buf), payerNote: try FfiConverterOptionTypeUntrustedString.read(from: &buf), quantity: try FfiConverterOptionUInt64.read(from: &buf)
7125+
case 5: return .bolt12Refund(hash: try FfiConverterOptionTypePaymentHash.read(from: &buf), preimage: try FfiConverterOptionTypePaymentPreimage.read(from: &buf), secret: try FfiConverterOptionTypePaymentSecret.read(from: &buf), payerNote: try FfiConverterOptionTypeUntrustedString.read(from: &buf)
71307126
)
71317127

71327128
case 6: return .spontaneous(hash: try FfiConverterTypePaymentHash.read(from: &buf), preimage: try FfiConverterOptionTypePaymentPreimage.read(from: &buf)
@@ -7162,23 +7158,21 @@ public struct FfiConverterTypePaymentKind: FfiConverterRustBuffer {
71627158
FfiConverterTypeLSPFeeLimits.write(lspFeeLimits, into: &buf)
71637159

71647160

7165-
case let .bolt12Offer(hash,preimage,secret,offerId,payerNote,quantity):
7161+
case let .bolt12Offer(hash,preimage,secret,offerId,payerNote):
71667162
writeInt(&buf, Int32(4))
71677163
FfiConverterOptionTypePaymentHash.write(hash, into: &buf)
71687164
FfiConverterOptionTypePaymentPreimage.write(preimage, into: &buf)
71697165
FfiConverterOptionTypePaymentSecret.write(secret, into: &buf)
71707166
FfiConverterTypeOfferId.write(offerId, into: &buf)
71717167
FfiConverterOptionTypeUntrustedString.write(payerNote, into: &buf)
7172-
FfiConverterOptionUInt64.write(quantity, into: &buf)
71737168

71747169

7175-
case let .bolt12Refund(hash,preimage,secret,payerNote,quantity):
7170+
case let .bolt12Refund(hash,preimage,secret,payerNote):
71767171
writeInt(&buf, Int32(5))
71777172
FfiConverterOptionTypePaymentHash.write(hash, into: &buf)
71787173
FfiConverterOptionTypePaymentPreimage.write(preimage, into: &buf)
71797174
FfiConverterOptionTypePaymentSecret.write(secret, into: &buf)
71807175
FfiConverterOptionTypeUntrustedString.write(payerNote, into: &buf)
7181-
FfiConverterOptionUInt64.write(quantity, into: &buf)
71827176

71837177

71847178
case let .spontaneous(hash,preimage):
@@ -9840,4 +9834,4 @@ private func uniffiEnsureInitialized() {
98409834
}
98419835
}
98429836

9843-
// swiftlint:enable all
9837+
// swiftlint:enable all

src/event.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -742,14 +742,12 @@ where
742742
} => {
743743
let payer_note = payment_context.invoice_request.payer_note_truncated;
744744
let offer_id = payment_context.offer_id;
745-
let quantity = payment_context.invoice_request.quantity;
746745
let kind = PaymentKind::Bolt12Offer {
747746
hash: Some(payment_hash),
748747
preimage: payment_preimage,
749748
secret: Some(payment_secret),
750749
offer_id,
751750
payer_note,
752-
quantity,
753751
};
754752

755753
let payment = PaymentDetails::new(

src/payment/bolt12.rs

Lines changed: 10 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use crate::types::{ChannelManager, PaymentStore};
1818

1919
use lightning::blinded_path::message::BlindedMessagePath;
2020
use lightning::ln::channelmanager::{PaymentId, Retry};
21-
use lightning::offers::offer::{Amount, Offer as LdkOffer, Quantity};
21+
use lightning::offers::offer::{Amount, Offer as LdkOffer};
2222
use lightning::offers::parse::Bolt12SemanticError;
2323
use lightning::routing::router::RouteParametersConfig;
2424

@@ -28,7 +28,6 @@ use lightning_types::string::UntrustedString;
2828

2929
use rand::RngCore;
3030

31-
use std::num::NonZeroU64;
3231
use std::sync::{Arc, RwLock};
3332
use std::time::{Duration, SystemTime, UNIX_EPOCH};
3433

@@ -73,11 +72,7 @@ impl Bolt12Payment {
7372
///
7473
/// If `payer_note` is `Some` it will be seen by the recipient and reflected back in the invoice
7574
/// response.
76-
///
77-
/// If `quantity` is `Some` it represents the number of items requested.
78-
pub fn send(
79-
&self, offer: &Offer, quantity: Option<u64>, payer_note: Option<String>,
80-
) -> Result<PaymentId, Error> {
75+
pub fn send(&self, offer: &Offer, payer_note: Option<String>) -> Result<PaymentId, Error> {
8176
if !*self.is_running.read().unwrap() {
8277
return Err(Error::NotRunning);
8378
}
@@ -104,7 +99,7 @@ impl Bolt12Payment {
10499

105100
match self.channel_manager.pay_for_offer(
106101
&offer,
107-
quantity,
102+
if offer.expects_quantity() { Some(1) } else { None },
108103
None,
109104
payer_note.clone(),
110105
payment_id,
@@ -126,7 +121,6 @@ impl Bolt12Payment {
126121
secret: None,
127122
offer_id: offer.id(),
128123
payer_note: payer_note.map(UntrustedString),
129-
quantity,
130124
};
131125
let payment = PaymentDetails::new(
132126
payment_id,
@@ -151,7 +145,6 @@ impl Bolt12Payment {
151145
secret: None,
152146
offer_id: offer.id(),
153147
payer_note: payer_note.map(UntrustedString),
154-
quantity,
155148
};
156149
let payment = PaymentDetails::new(
157150
payment_id,
@@ -179,7 +172,7 @@ impl Bolt12Payment {
179172
/// If `payer_note` is `Some` it will be seen by the recipient and reflected back in the invoice
180173
/// response.
181174
pub fn send_using_amount(
182-
&self, offer: &Offer, amount_msat: u64, quantity: Option<u64>, payer_note: Option<String>,
175+
&self, offer: &Offer, amount_msat: u64, payer_note: Option<String>,
183176
) -> Result<PaymentId, Error> {
184177
if !*self.is_running.read().unwrap() {
185178
return Err(Error::NotRunning);
@@ -211,7 +204,7 @@ impl Bolt12Payment {
211204

212205
match self.channel_manager.pay_for_offer(
213206
&offer,
214-
quantity,
207+
if offer.expects_quantity() { Some(1) } else { None },
215208
Some(amount_msat),
216209
payer_note.clone(),
217210
payment_id,
@@ -233,7 +226,6 @@ impl Bolt12Payment {
233226
secret: None,
234227
offer_id: offer.id(),
235228
payer_note: payer_note.map(UntrustedString),
236-
quantity,
237229
};
238230
let payment = PaymentDetails::new(
239231
payment_id,
@@ -258,7 +250,6 @@ impl Bolt12Payment {
258250
secret: None,
259251
offer_id: offer.id(),
260252
payer_note: payer_note.map(UntrustedString),
261-
quantity,
262253
};
263254
let payment = PaymentDetails::new(
264255
payment_id,
@@ -277,7 +268,7 @@ impl Bolt12Payment {
277268
}
278269

279270
pub(crate) fn receive_inner(
280-
&self, amount_msat: u64, description: &str, expiry_secs: Option<u32>, quantity: Option<u64>,
271+
&self, amount_msat: u64, description: &str, expiry_secs: Option<u32>,
281272
) -> Result<LdkOffer, Error> {
282273
let mut offer_builder = self.channel_manager.create_offer_builder().map_err(|e| {
283274
log_error!(self.logger, "Failed to create offer builder: {:?}", e);
@@ -291,17 +282,7 @@ impl Bolt12Payment {
291282
offer_builder = offer_builder.absolute_expiry(absolute_expiry);
292283
}
293284

294-
let mut offer =
295-
offer_builder.amount_msats(amount_msat).description(description.to_string());
296-
297-
if let Some(qty) = quantity {
298-
if qty == 0 {
299-
log_error!(self.logger, "Failed to create offer: quantity can't be zero.");
300-
return Err(Error::InvalidQuantity);
301-
} else {
302-
offer = offer.supported_quantity(Quantity::Bounded(NonZeroU64::new(qty).unwrap()))
303-
};
304-
};
285+
let offer = offer_builder.amount_msats(amount_msat).description(description.to_string());
305286

306287
let finalized_offer = offer.build().map_err(|e| {
307288
log_error!(self.logger, "Failed to create offer: {:?}", e);
@@ -314,9 +295,9 @@ impl Bolt12Payment {
314295
/// Returns a payable offer that can be used to request and receive a payment of the amount
315296
/// given.
316297
pub fn receive(
317-
&self, amount_msat: u64, description: &str, expiry_secs: Option<u32>, quantity: Option<u64>,
298+
&self, amount_msat: u64, description: &str, expiry_secs: Option<u32>,
318299
) -> Result<Offer, Error> {
319-
let offer = self.receive_inner(amount_msat, description, expiry_secs, quantity)?;
300+
let offer = self.receive_inner(amount_msat, description, expiry_secs)?;
320301
Ok(maybe_wrap(offer))
321302
}
322303

@@ -371,7 +352,6 @@ impl Bolt12Payment {
371352
preimage: None,
372353
secret: None,
373354
payer_note: refund.payer_note().map(|note| UntrustedString(note.0.to_string())),
374-
quantity: refund.quantity(),
375355
};
376356

377357
let payment = PaymentDetails::new(
@@ -392,8 +372,7 @@ impl Bolt12Payment {
392372
///
393373
/// [`Refund`]: lightning::offers::refund::Refund
394374
pub fn initiate_refund(
395-
&self, amount_msat: u64, expiry_secs: u32, quantity: Option<u64>,
396-
payer_note: Option<String>,
375+
&self, amount_msat: u64, expiry_secs: u32, payer_note: Option<String>,
397376
) -> Result<Refund, Error> {
398377
let mut random_bytes = [0u8; 32];
399378
rand::thread_rng().fill_bytes(&mut random_bytes);
@@ -419,10 +398,6 @@ impl Bolt12Payment {
419398
Error::RefundCreationFailed
420399
})?;
421400

422-
if let Some(qty) = quantity {
423-
refund_builder = refund_builder.quantity(qty);
424-
}
425-
426401
if let Some(note) = payer_note.clone() {
427402
refund_builder = refund_builder.payer_note(note);
428403
}
@@ -439,7 +414,6 @@ impl Bolt12Payment {
439414
preimage: None,
440415
secret: None,
441416
payer_note: payer_note.map(|note| UntrustedString(note)),
442-
quantity,
443417
};
444418
let payment = PaymentDetails::new(
445419
payment_id,

src/payment/store.rs

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -413,10 +413,6 @@ pub enum PaymentKind {
413413
///
414414
/// [`PAYER_NOTE_LIMIT`]: lightning::offers::invoice_request::PAYER_NOTE_LIMIT
415415
payer_note: Option<UntrustedString>,
416-
/// The quantity of an item requested in the offer.
417-
///
418-
/// This will always be `None` for payments serialized with version `v0.3.0`.
419-
quantity: Option<u64>,
420416
},
421417
/// A [BOLT 12] 'refund' payment, i.e., a payment for a [`Refund`].
422418
///
@@ -433,10 +429,6 @@ pub enum PaymentKind {
433429
///
434430
/// This will always be `None` for payments serialized with version `v0.3.0`.
435431
payer_note: Option<UntrustedString>,
436-
/// The quantity of an item that the refund is for.
437-
///
438-
/// This will always be `None` for payments serialized with version `v0.3.0`.
439-
quantity: Option<u64>,
440432
},
441433
/// A spontaneous ("keysend") payment.
442434
Spontaneous {
@@ -468,7 +460,7 @@ impl_writeable_tlv_based_enum!(PaymentKind,
468460
(0, hash, option),
469461
(1, payer_note, option),
470462
(2, preimage, option),
471-
(3, quantity, option),
463+
// Type 3 was formerly the quantity
472464
(4, secret, option),
473465
(6, offer_id, required),
474466
},
@@ -480,7 +472,7 @@ impl_writeable_tlv_based_enum!(PaymentKind,
480472
(0, hash, option),
481473
(1, payer_note, option),
482474
(2, preimage, option),
483-
(3, quantity, option),
475+
// Type 3 was formerly the quantity
484476
(4, secret, option),
485477
}
486478
);

0 commit comments

Comments
 (0)