Skip to content

Commit 7c77a01

Browse files
committed
Invoice request message interface and data format
Define an interface for BOLT 12 `invoice_request` messages. The underlying format consists of the original bytes and the parsed contents. The bytes are later needed when constructing an `invoice` message. This is because it must mirror all the `offer` and `invoice_request` TLV records, including unknown ones, which aren't represented in the contents. The contents will be used in `invoice` messages to avoid duplication. Some fields while required in a typical user-pays-merchant flow may not be necessary in the merchant-pays-user flow (e.g., refund, ATM).
1 parent c9fc32f commit 7c77a01

File tree

4 files changed

+138
-6
lines changed

4 files changed

+138
-6
lines changed
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
// This file is Copyright its original authors, visible in version control
2+
// history.
3+
//
4+
// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
5+
// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
7+
// You may not use this file except in accordance with one or both of these
8+
// licenses.
9+
10+
//! Data structures and encoding for `invoice_request` messages.
11+
12+
use bitcoin::blockdata::constants::ChainHash;
13+
use bitcoin::secp256k1::PublicKey;
14+
use bitcoin::secp256k1::schnorr::Signature;
15+
use ln::features::OfferFeatures;
16+
use offers::offer::OfferContents;
17+
use offers::payer::PayerContents;
18+
19+
use prelude::*;
20+
21+
/// An `InvoiceRequest` is a request for an `Invoice` formulated from an [`Offer`].
22+
///
23+
/// An offer may provided choices such as quantity, amount, chain, features, etc. An invoice request
24+
/// specifies these such that the recipient can send an invoice for payment.
25+
///
26+
/// [`Offer`]: crate::offers::offer::Offer
27+
#[derive(Clone, Debug)]
28+
pub struct InvoiceRequest {
29+
bytes: Vec<u8>,
30+
contents: InvoiceRequestContents,
31+
signature: Option<Signature>,
32+
}
33+
34+
/// The contents of an [`InvoiceRequest`], which may be shared with an `Invoice`.
35+
#[derive(Clone, Debug)]
36+
pub(crate) struct InvoiceRequestContents {
37+
payer: PayerContents,
38+
offer: OfferContents,
39+
chain: Option<ChainHash>,
40+
amount_msats: Option<u64>,
41+
features: Option<OfferFeatures>,
42+
quantity: Option<u64>,
43+
payer_id: PublicKey,
44+
payer_note: Option<String>,
45+
}
46+
47+
impl InvoiceRequest {
48+
/// An unpredictable series of bytes, typically containing information about the derivation of
49+
/// [`payer_id`].
50+
///
51+
/// [`payer_id`]: Self::payer_id
52+
pub fn payer_info(&self) -> Option<&Vec<u8>> {
53+
self.contents.payer.0.as_ref()
54+
}
55+
56+
/// A chain from [`Offer::chains`] that the offer is valid for.
57+
///
58+
/// [`Offer::chains`]: crate::offers::offer::Offer::chains
59+
pub fn chain(&self) -> ChainHash {
60+
self.contents.chain.unwrap_or_else(|| self.contents.offer.implied_chain())
61+
}
62+
63+
/// The amount to pay in msats (i.e., the minimum lightning-payable unit for [`chain`]), which
64+
/// must be greater than or equal to [`Offer::amount`], converted if necessary.
65+
///
66+
/// [`chain`]: Self::chain
67+
/// [`Offer::amount`]: crate::offers::offer::Offer::amount
68+
pub fn amount_msats(&self) -> Option<u64> {
69+
self.contents.amount_msats
70+
}
71+
72+
/// Features for paying the invoice.
73+
pub fn features(&self) -> Option<&OfferFeatures> {
74+
self.contents.features.as_ref()
75+
}
76+
77+
/// The quantity for the offer's item within the range [`Offer::quantity_min`] to
78+
/// [`Offer::quantity_max`], inclusive.
79+
///
80+
/// [`Offer::quantity_min`]: crate::offers::offer::Offer::quantity_min
81+
/// [`Offer::quantity_max`]: crate::offers::offer::Offer::quantity_max
82+
pub fn quantity(&self) -> Option<u64> {
83+
self.contents.quantity
84+
}
85+
86+
/// A transient pubkey used to sign the invoice request.
87+
pub fn payer_id(&self) -> PublicKey {
88+
self.contents.payer_id
89+
}
90+
91+
/// Payer provided note to include in the invoice.
92+
pub fn payer_note(&self) -> Option<&String> {
93+
self.contents.payer_note.as_ref()
94+
}
95+
96+
/// Signature of the invoice request using [`payer_id`].
97+
///
98+
/// [`payer_id`]: Self::payer_id
99+
pub fn signature(&self) -> Option<Signature> {
100+
self.signature
101+
}
102+
}

lightning/src/offers/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,7 @@
1212
//!
1313
//! Offers are a flexible protocol for Lightning payments.
1414
15+
pub mod invoice_request;
1516
pub mod offer;
1617
pub mod parse;
18+
mod payer;

lightning/src/offers/offer.rs

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -249,14 +249,16 @@ impl OfferBuilder {
249249

250250
/// An `Offer` is a potentially long-lived proposal for payment of a good or service.
251251
///
252-
/// An offer is precursor to an `InvoiceRequest`. A merchant publishes an offer from which a
252+
/// An offer is precursor to an [`InvoiceRequest`]. A merchant publishes an offer from which a
253253
/// customer may request an `Invoice` for a specific quantity and using an amount enough to cover
254254
/// that quantity (i.e., at least `quantity * amount`). See [`Offer::amount`].
255255
///
256256
/// Offers may be denominated in currency other than bitcoin but are ultimately paid using the
257257
/// latter.
258258
///
259259
/// Through the use of [`BlindedPath`]s, offers provide recipient privacy.
260+
///
261+
/// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
260262
#[derive(Clone, Debug)]
261263
pub struct Offer {
262264
// The serialized offer. Needed when creating an `InvoiceRequest` if the offer contains unknown
@@ -265,7 +267,9 @@ pub struct Offer {
265267
contents: OfferContents,
266268
}
267269

268-
/// The contents of an [`Offer`], which may be shared with an `InvoiceRequest` or an `Invoice`.
270+
/// The contents of an [`Offer`], which may be shared with an [`InvoiceRequest`] or an `Invoice`.
271+
///
272+
/// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
269273
#[derive(Clone, Debug)]
270274
pub(crate) struct OfferContents {
271275
chains: Option<Vec<ChainHash>>,
@@ -287,10 +291,7 @@ impl Offer {
287291
// - https://github.com/rust-bitcoin/rust-bitcoin/pull/1286
288292
/// The chains that may be used when paying a requested invoice.
289293
pub fn chains(&self) -> Vec<ChainHash> {
290-
self.contents.chains
291-
.as_ref()
292-
.cloned()
293-
.unwrap_or_else(|| vec![ChainHash::using_genesis_block(Network::Bitcoin)])
294+
self.contents.chains()
294295
}
295296

296297
// TODO: Link to corresponding method in `InvoiceRequest`.
@@ -374,6 +375,14 @@ impl AsRef<[u8]> for Offer {
374375
}
375376

376377
impl OfferContents {
378+
pub fn chains(&self) -> Vec<ChainHash> {
379+
self.chains.as_ref().cloned().unwrap_or_else(|| vec![self.implied_chain()])
380+
}
381+
382+
pub fn implied_chain(&self) -> ChainHash {
383+
ChainHash::using_genesis_block(Network::Bitcoin)
384+
}
385+
377386
pub fn amount_msats(&self) -> u64 {
378387
self.amount.as_ref().map(Amount::as_msats).unwrap_or(0)
379388
}

lightning/src/offers/payer.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// This file is Copyright its original authors, visible in version control
2+
// history.
3+
//
4+
// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
5+
// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
7+
// You may not use this file except in accordance with one or both of these
8+
// licenses.
9+
10+
//! Data structures and encoding for `invoice_request_payer_info` records.
11+
12+
use prelude::*;
13+
14+
/// An unpredictable sequence of bytes typically containing information needed to derive
15+
/// [`InvoiceRequestContents::payer_id`].
16+
///
17+
/// [`InvoiceRequestContents::payer_id`]: invoice_request::InvoiceRequestContents::payer_id
18+
#[derive(Clone, Debug)]
19+
pub(crate) struct PayerContents(pub Option<Vec<u8>>);

0 commit comments

Comments
 (0)