-
Notifications
You must be signed in to change notification settings - Fork 367
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
BOLT 12 invoice
encoding and building
#1926
Changes from all commits
bd0040a
6b35354
243f448
e1aa18a
88c5197
f779bc0
7f52d26
fe83aed
bf1147f
a452551
15f1295
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,11 +11,12 @@ | |
//! | ||
//! An [`InvoiceRequest`] can be built from a parsed [`Offer`] as an "offer to be paid". It is | ||
//! typically constructed by a customer and sent to the merchant who had published the corresponding | ||
//! offer. The recipient of the request responds with an `Invoice`. | ||
//! offer. The recipient of the request responds with an [`Invoice`]. | ||
//! | ||
//! For an "offer for money" (e.g., refund, ATM withdrawal), where an offer doesn't exist as a | ||
//! precursor, see [`Refund`]. | ||
//! | ||
//! [`Invoice`]: crate::offers::invoice::Invoice | ||
//! [`Refund`]: crate::offers::refund::Refund | ||
//! | ||
//! ```ignore | ||
|
@@ -57,12 +58,15 @@ use bitcoin::secp256k1::{Message, PublicKey}; | |
use bitcoin::secp256k1::schnorr::Signature; | ||
use core::convert::TryFrom; | ||
use crate::io; | ||
use crate::ln::PaymentHash; | ||
use crate::ln::features::InvoiceRequestFeatures; | ||
use crate::ln::msgs::DecodeError; | ||
use crate::offers::invoice::{BlindedPayInfo, InvoiceBuilder}; | ||
use crate::offers::merkle::{SignError, SignatureTlvStream, SignatureTlvStreamRef, self}; | ||
use crate::offers::offer::{Offer, OfferContents, OfferTlvStream, OfferTlvStreamRef}; | ||
use crate::offers::parse::{ParseError, ParsedMessage, SemanticError}; | ||
use crate::offers::payer::{PayerContents, PayerTlvStream, PayerTlvStreamRef}; | ||
use crate::onion_message::BlindedPath; | ||
use crate::util::ser::{HighZeroBytesDroppedBigSize, SeekReadable, WithoutLength, Writeable, Writer}; | ||
use crate::util::string::PrintableString; | ||
|
||
|
@@ -239,24 +243,27 @@ impl<'a> UnsignedInvoiceRequest<'a> { | |
} | ||
} | ||
|
||
/// An `InvoiceRequest` is a request for an `Invoice` formulated from an [`Offer`]. | ||
/// An `InvoiceRequest` is a request for an [`Invoice`] formulated from an [`Offer`]. | ||
/// | ||
/// An offer may provide choices such as quantity, amount, chain, features, etc. An invoice request | ||
/// specifies these such that its recipient can send an invoice for payment. | ||
/// | ||
/// [`Invoice`]: crate::offers::invoice::Invoice | ||
/// [`Offer`]: crate::offers::offer::Offer | ||
#[derive(Clone, Debug)] | ||
pub struct InvoiceRequest { | ||
pub(super) bytes: Vec<u8>, | ||
contents: InvoiceRequestContents, | ||
pub(super) contents: InvoiceRequestContents, | ||
signature: Signature, | ||
} | ||
|
||
/// The contents of an [`InvoiceRequest`], which may be shared with an `Invoice`. | ||
/// The contents of an [`InvoiceRequest`], which may be shared with an [`Invoice`]. | ||
/// | ||
/// [`Invoice`]: crate::offers::invoice::Invoice | ||
#[derive(Clone, Debug)] | ||
pub(super) struct InvoiceRequestContents { | ||
payer: PayerContents, | ||
offer: OfferContents, | ||
pub(super) offer: OfferContents, | ||
chain: Option<ChainHash>, | ||
amount_msats: Option<u64>, | ||
features: InvoiceRequestFeatures, | ||
|
@@ -315,6 +322,41 @@ impl InvoiceRequest { | |
self.signature | ||
} | ||
|
||
/// Creates an [`Invoice`] for the request with the given required fields. | ||
/// | ||
/// Unless [`InvoiceBuilder::relative_expiry`] is set, the invoice will expire two hours after | ||
/// calling this method in `std` builds. For `no-std` builds, a final [`Duration`] parameter | ||
/// must be given, which is used to set [`Invoice::created_at`] since [`std::time::SystemTime`] | ||
/// is not available. | ||
/// | ||
/// The caller is expected to remember the preimage of `payment_hash` in order to claim a payment | ||
/// for the invoice. | ||
/// | ||
/// The `payment_paths` parameter is useful for maintaining the payment recipient's privacy. It | ||
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 think this could use more details about why it's required even when you're using a public node id. Maybe could live on |
||
/// must contain one or more elements. | ||
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. Should also note that these should be specified in order of most preferred to least preferred |
||
/// | ||
/// Errors if the request contains unknown required features. | ||
/// | ||
/// [`Duration`]: core::time::Duration | ||
/// [`Invoice`]: crate::offers::invoice::Invoice | ||
/// [`Invoice::created_at`]: crate::offers::invoice::Invoice::created_at | ||
pub fn respond_with( | ||
&self, payment_paths: Vec<(BlindedPath, BlindedPayInfo)>, payment_hash: PaymentHash, | ||
#[cfg(any(test, not(feature = "std")))] | ||
created_at: core::time::Duration | ||
) -> Result<InvoiceBuilder, SemanticError> { | ||
if self.features().requires_unknown_bits() { | ||
return Err(SemanticError::UnknownRequiredFeatures); | ||
} | ||
|
||
#[cfg(all(not(test), feature = "std"))] | ||
let created_at = std::time::SystemTime::now() | ||
.duration_since(std::time::SystemTime::UNIX_EPOCH) | ||
.expect("SystemTime::now() should come after SystemTime::UNIX_EPOCH"); | ||
|
||
InvoiceBuilder::for_offer(self, payment_paths, created_at, payment_hash) | ||
} | ||
|
||
#[cfg(test)] | ||
fn as_tlv_stream(&self) -> FullInvoiceRequestTlvStreamRef { | ||
let (payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream) = | ||
|
@@ -327,7 +369,7 @@ impl InvoiceRequest { | |
} | ||
|
||
impl InvoiceRequestContents { | ||
fn chain(&self) -> ChainHash { | ||
pub(super) fn chain(&self) -> ChainHash { | ||
self.chain.unwrap_or_else(|| self.offer.implied_chain()) | ||
} | ||
|
||
|
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.
lightning-invoice
has links toChannelManager::create_inbound_payment*
, may want some here tooThere 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.
We'll wait until the code is used in onion message handling before documenting. For
Offer
s that code will construct the payment hash, though responding to aRefund
may need some utility.