1 // This file is Copyright its original authors, visible in version control
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
10 //! Data structures and encoding for `offer` messages.
12 use bitcoin::blockdata::constants::ChainHash;
13 use bitcoin::network::constants::Network;
14 use bitcoin::secp256k1::PublicKey;
15 use core::num::NonZeroU64;
16 use core::time::Duration;
17 use crate::ln::features::OfferFeatures;
18 use crate::onion_message::BlindedPath;
19 use crate::util::string::PrintableString;
21 use crate::prelude::*;
23 #[cfg(feature = "std")]
24 use std::time::SystemTime;
26 /// An `Offer` is a potentially long-lived proposal for payment of a good or service.
28 /// An offer is a precursor to an `InvoiceRequest`. A merchant publishes an offer from which a
29 /// customer may request an `Invoice` for a specific quantity and using an amount sufficient to
30 /// cover that quantity (i.e., at least `quantity * amount`). See [`Offer::amount`].
32 /// Offers may be denominated in currency other than bitcoin but are ultimately paid using the
35 /// Through the use of [`BlindedPath`]s, offers provide recipient privacy.
36 #[derive(Clone, Debug)]
38 // The serialized offer. Needed when creating an `InvoiceRequest` if the offer contains unknown
41 contents: OfferContents,
44 /// The contents of an [`Offer`], which may be shared with an `InvoiceRequest` or an `Invoice`.
45 #[derive(Clone, Debug)]
46 pub(crate) struct OfferContents {
47 chains: Option<Vec<ChainHash>>,
48 metadata: Option<Vec<u8>>,
49 amount: Option<Amount>,
51 features: OfferFeatures,
52 absolute_expiry: Option<Duration>,
53 issuer: Option<String>,
54 paths: Option<Vec<BlindedPath>>,
55 quantity_max: Option<u64>,
56 signing_pubkey: Option<PublicKey>,
60 // TODO: Return a slice once ChainHash has constants.
61 // - https://github.com/rust-bitcoin/rust-bitcoin/pull/1283
62 // - https://github.com/rust-bitcoin/rust-bitcoin/pull/1286
63 /// The chains that may be used when paying a requested invoice (e.g., bitcoin mainnet).
64 /// Payments must be denominated in units of the minimal lightning-payable unit (e.g., msats)
65 /// for the selected chain.
66 pub fn chains(&self) -> Vec<ChainHash> {
70 .unwrap_or_else(|| vec![ChainHash::using_genesis_block(Network::Bitcoin)])
73 // TODO: Link to corresponding method in `InvoiceRequest`.
74 /// Opaque bytes set by the originator. Useful for authentication and validating fields since it
75 /// is reflected in `invoice_request` messages along with all the other fields from the `offer`.
76 pub fn metadata(&self) -> Option<&Vec<u8>> {
77 self.contents.metadata.as_ref()
80 /// The minimum amount required for a successful payment of a single item.
81 pub fn amount(&self) -> Option<&Amount> {
82 self.contents.amount.as_ref()
85 /// A complete description of the purpose of the payment. Intended to be displayed to the user
86 /// but with the caveat that it has not been verified in any way.
87 pub fn description(&self) -> PrintableString {
88 PrintableString(&self.contents.description)
91 /// Features pertaining to the offer.
92 pub fn features(&self) -> &OfferFeatures {
93 &self.contents.features
96 /// Duration since the Unix epoch when an invoice should no longer be requested.
98 /// If `None`, the offer does not expire.
99 pub fn absolute_expiry(&self) -> Option<Duration> {
100 self.contents.absolute_expiry
103 /// Whether the offer has expired.
104 #[cfg(feature = "std")]
105 pub fn is_expired(&self) -> bool {
106 match self.absolute_expiry() {
107 Some(seconds_from_epoch) => match SystemTime::UNIX_EPOCH.elapsed() {
108 Ok(elapsed) => elapsed > seconds_from_epoch,
115 /// The issuer of the offer, possibly beginning with `user@domain` or `domain`. Intended to be
116 /// displayed to the user but with the caveat that it has not been verified in any way.
117 pub fn issuer(&self) -> Option<PrintableString> {
118 self.contents.issuer.as_ref().map(|issuer| PrintableString(issuer.as_str()))
121 /// Paths to the recipient originating from publicly reachable nodes. Blinded paths provide
122 /// recipient privacy by obfuscating its node id.
123 pub fn paths(&self) -> &[BlindedPath] {
124 self.contents.paths.as_ref().map(|paths| paths.as_slice()).unwrap_or(&[])
127 /// The quantity of items supported.
128 pub fn supported_quantity(&self) -> Quantity {
129 match self.contents.quantity_max {
130 Some(0) => Quantity::Unbounded,
131 Some(n) => Quantity::Bounded(NonZeroU64::new(n).unwrap()),
132 None => Quantity::Bounded(NonZeroU64::new(1).unwrap()),
136 /// The public key used by the recipient to sign invoices.
137 pub fn signing_pubkey(&self) -> PublicKey {
138 self.contents.signing_pubkey.unwrap()
142 /// The minimum amount required for an item in an [`Offer`], denominated in either bitcoin or
143 /// another currency.
144 #[derive(Clone, Debug)]
146 /// An amount of bitcoin.
148 /// The amount in millisatoshi.
151 /// An amount of currency specified using ISO 4712.
153 /// The currency that the amount is denominated in.
154 iso4217_code: CurrencyCode,
155 /// The amount in the currency unit adjusted by the ISO 4712 exponent (e.g., USD cents).
160 /// An ISO 4712 three-letter currency code (e.g., USD).
161 pub type CurrencyCode = [u8; 3];
163 /// Quantity of items supported by an [`Offer`].
165 /// Up to a specific number of items (inclusive).
167 /// One or more items.