X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning%2Fsrc%2Foffers%2Finvoice.rs;h=fb1f78fd64445c406d582830eebd97d4c107c16b;hb=25a707314f70fc2f59488d4de34facb1428fc06d;hp=b2717f7338f193cd17589ee7e3216c972d7463ad;hpb=8afe6940200769b9df9e9ecfda2a8390919a6cf2;p=rust-lightning diff --git a/lightning/src/offers/invoice.rs b/lightning/src/offers/invoice.rs index b2717f73..fb1f78fd 100644 --- a/lightning/src/offers/invoice.rs +++ b/lightning/src/offers/invoice.rs @@ -29,9 +29,9 @@ //! //! # use lightning::ln::PaymentHash; //! # use lightning::offers::invoice::BlindedPayInfo; -//! # use lightning::onion_message::BlindedPath; +//! # use lightning::blinded_path::BlindedPath; //! # -//! # fn create_payment_paths() -> Vec<(BlindedPath, BlindedPayInfo)> { unimplemented!() } +//! # fn create_payment_paths() -> Vec<(BlindedPayInfo, BlindedPath)> { unimplemented!() } //! # fn create_payment_hash() -> PaymentHash { unimplemented!() } //! # //! # fn parse_invoice_request(bytes: Vec) -> Result<(), lightning::offers::parse::ParseError> { @@ -104,6 +104,7 @@ use bitcoin::util::schnorr::TweakedPublicKey; use core::convert::{Infallible, TryFrom}; use core::time::Duration; use crate::io; +use crate::blinded_path::BlindedPath; use crate::ln::PaymentHash; use crate::ln::features::{BlindedHopFeatures, Bolt12InvoiceFeatures}; use crate::ln::inbound_payment::ExpandedKey; @@ -115,8 +116,8 @@ use crate::offers::parse::{ParseError, ParsedMessage, SemanticError}; use crate::offers::payer::{PAYER_METADATA_TYPE, PayerTlvStream, PayerTlvStreamRef}; use crate::offers::refund::{IV_BYTES as REFUND_IV_BYTES, Refund, RefundContents}; use crate::offers::signer; -use crate::onion_message::BlindedPath; use crate::util::ser::{HighZeroBytesDroppedBigSize, Iterable, SeekReadable, WithoutLength, Writeable, Writer}; +use crate::util::string::PrintableString; use crate::prelude::*; @@ -133,6 +134,8 @@ pub(super) const SIGNATURE_TAG: &'static str = concat!("lightning", "invoice", " /// /// See [module-level documentation] for usage. /// +/// This is not exported to bindings users as builder patterns don't map outside of move semantics. +/// /// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest /// [`Refund`]: crate::offers::refund::Refund /// [module-level documentation]: self @@ -144,12 +147,18 @@ pub struct InvoiceBuilder<'a, S: SigningPubkeyStrategy> { } /// Indicates how [`Invoice::signing_pubkey`] was set. +/// +/// This is not exported to bindings users as builder patterns don't map outside of move semantics. pub trait SigningPubkeyStrategy {} /// [`Invoice::signing_pubkey`] was explicitly set. +/// +/// This is not exported to bindings users as builder patterns don't map outside of move semantics. pub struct ExplicitSigningPubkey {} /// [`Invoice::signing_pubkey`] was derived. +/// +/// This is not exported to bindings users as builder patterns don't map outside of move semantics. pub struct DerivedSigningPubkey {} impl SigningPubkeyStrategy for ExplicitSigningPubkey {} @@ -157,7 +166,7 @@ impl SigningPubkeyStrategy for DerivedSigningPubkey {} impl<'a> InvoiceBuilder<'a, ExplicitSigningPubkey> { pub(super) fn for_offer( - invoice_request: &'a InvoiceRequest, payment_paths: Vec<(BlindedPath, BlindedPayInfo)>, + invoice_request: &'a InvoiceRequest, payment_paths: Vec<(BlindedPayInfo, BlindedPath)>, created_at: Duration, payment_hash: PaymentHash ) -> Result { let amount_msats = Self::check_amount_msats(invoice_request)?; @@ -173,7 +182,7 @@ impl<'a> InvoiceBuilder<'a, ExplicitSigningPubkey> { } pub(super) fn for_refund( - refund: &'a Refund, payment_paths: Vec<(BlindedPath, BlindedPayInfo)>, created_at: Duration, + refund: &'a Refund, payment_paths: Vec<(BlindedPayInfo, BlindedPath)>, created_at: Duration, payment_hash: PaymentHash, signing_pubkey: PublicKey ) -> Result { let amount_msats = refund.amount_msats(); @@ -190,7 +199,7 @@ impl<'a> InvoiceBuilder<'a, ExplicitSigningPubkey> { impl<'a> InvoiceBuilder<'a, DerivedSigningPubkey> { pub(super) fn for_offer_using_keys( - invoice_request: &'a InvoiceRequest, payment_paths: Vec<(BlindedPath, BlindedPayInfo)>, + invoice_request: &'a InvoiceRequest, payment_paths: Vec<(BlindedPayInfo, BlindedPath)>, created_at: Duration, payment_hash: PaymentHash, keys: KeyPair ) -> Result { let amount_msats = Self::check_amount_msats(invoice_request)?; @@ -206,7 +215,7 @@ impl<'a> InvoiceBuilder<'a, DerivedSigningPubkey> { } pub(super) fn for_refund_using_keys( - refund: &'a Refund, payment_paths: Vec<(BlindedPath, BlindedPayInfo)>, created_at: Duration, + refund: &'a Refund, payment_paths: Vec<(BlindedPayInfo, BlindedPath)>, created_at: Duration, payment_hash: PaymentHash, keys: KeyPair, ) -> Result { let amount_msats = refund.amount_msats(); @@ -238,7 +247,7 @@ impl<'a, S: SigningPubkeyStrategy> InvoiceBuilder<'a, S> { } fn fields( - payment_paths: Vec<(BlindedPath, BlindedPayInfo)>, created_at: Duration, + payment_paths: Vec<(BlindedPayInfo, BlindedPath)>, created_at: Duration, payment_hash: PaymentHash, amount_msats: u64, signing_pubkey: PublicKey ) -> InvoiceFields { InvoiceFields { @@ -368,6 +377,8 @@ impl<'a> UnsignedInvoice<'a> { } /// Signs the invoice using the given function. + /// + /// This is not exported to bindings users as functions aren't currently mapped. pub fn sign(self, sign: F) -> Result> where F: FnOnce(&Message) -> Result @@ -404,6 +415,8 @@ impl<'a> UnsignedInvoice<'a> { /// An invoice may be sent in response to an [`InvoiceRequest`] in the case of an offer or sent /// directly after scanning a refund. It includes all the information needed to pay a recipient. /// +/// This is not exported to bindings users as its name conflicts with the BOLT 11 Invoice type. +/// /// [`Offer`]: crate::offers::offer::Offer /// [`Refund`]: crate::offers::refund::Refund /// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest @@ -441,7 +454,7 @@ enum InvoiceContents { /// Invoice-specific fields for an `invoice` message. #[derive(Clone, Debug, PartialEq)] struct InvoiceFields { - payment_paths: Vec<(BlindedPath, BlindedPayInfo)>, + payment_paths: Vec<(BlindedPayInfo, BlindedPath)>, created_at: Duration, relative_expiry: Option, payment_hash: PaymentHash, @@ -452,12 +465,18 @@ struct InvoiceFields { } impl Invoice { + /// A complete description of the purpose of the originating offer or refund. Intended to be + /// displayed to the user but with the caveat that it has not been verified in any way. + pub fn description(&self) -> PrintableString { + self.contents.description() + } + /// Paths to the recipient originating from publicly reachable nodes, including information /// needed for routing payments across them. /// /// Blinded paths provide recipient privacy by obfuscating its node id. Note, however, that this /// privacy is lost if a public node id is used for [`Invoice::signing_pubkey`]. - pub fn payment_paths(&self) -> &[(BlindedPath, BlindedPayInfo)] { + pub fn payment_paths(&self) -> &[(BlindedPayInfo, BlindedPath)] { &self.contents.fields().payment_paths[..] } @@ -607,6 +626,15 @@ impl InvoiceContents { } } + fn description(&self) -> PrintableString { + match self { + InvoiceContents::ForOffer { invoice_request, .. } => { + invoice_request.inner.offer.description() + }, + InvoiceContents::ForRefund { refund, .. } => refund.description(), + } + } + fn fields(&self) -> &InvoiceFields { match self { InvoiceContents::ForOffer { fields, .. } => fields, @@ -675,8 +703,8 @@ impl InvoiceFields { }; InvoiceTlvStreamRef { - paths: Some(Iterable(self.payment_paths.iter().map(|(path, _)| path))), - blindedpay: Some(Iterable(self.payment_paths.iter().map(|(_, payinfo)| payinfo))), + paths: Some(Iterable(self.payment_paths.iter().map(|(_, path)| path))), + blindedpay: Some(Iterable(self.payment_paths.iter().map(|(payinfo, _)| payinfo))), created_at: Some(self.created_at.as_secs()), relative_expiry: self.relative_expiry.map(|duration| duration.as_secs() as u32), payment_hash: Some(&self.payment_hash), @@ -722,17 +750,17 @@ tlv_stream!(InvoiceTlvStream, InvoiceTlvStreamRef, 160..240, { }); type BlindedPathIter<'a> = core::iter::Map< - core::slice::Iter<'a, (BlindedPath, BlindedPayInfo)>, - for<'r> fn(&'r (BlindedPath, BlindedPayInfo)) -> &'r BlindedPath, + core::slice::Iter<'a, (BlindedPayInfo, BlindedPath)>, + for<'r> fn(&'r (BlindedPayInfo, BlindedPath)) -> &'r BlindedPath, >; type BlindedPayInfoIter<'a> = core::iter::Map< - core::slice::Iter<'a, (BlindedPath, BlindedPayInfo)>, - for<'r> fn(&'r (BlindedPath, BlindedPayInfo)) -> &'r BlindedPayInfo, + core::slice::Iter<'a, (BlindedPayInfo, BlindedPath)>, + for<'r> fn(&'r (BlindedPayInfo, BlindedPath)) -> &'r BlindedPayInfo, >; /// Information needed to route a payment across a [`BlindedPath`]. -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug, Hash, Eq, PartialEq)] pub struct BlindedPayInfo { /// Base fee charged (in millisatoshi) for the entire blinded path. pub fee_base_msat: u32, @@ -850,15 +878,15 @@ impl TryFrom for InvoiceContents { }, ) = tlv_stream; - let payment_paths = match (paths, blindedpay) { - (None, _) => return Err(SemanticError::MissingPaths), - (_, None) => return Err(SemanticError::InvalidPayInfo), - (Some(paths), _) if paths.is_empty() => return Err(SemanticError::MissingPaths), - (Some(paths), Some(blindedpay)) if paths.len() != blindedpay.len() => { + let payment_paths = match (blindedpay, paths) { + (_, None) => return Err(SemanticError::MissingPaths), + (None, _) => return Err(SemanticError::InvalidPayInfo), + (_, Some(paths)) if paths.is_empty() => return Err(SemanticError::MissingPaths), + (Some(blindedpay), Some(paths)) if paths.len() != blindedpay.len() => { return Err(SemanticError::InvalidPayInfo); }, - (Some(paths), Some(blindedpay)) => { - paths.into_iter().zip(blindedpay.into_iter()).collect::>() + (Some(blindedpay), Some(paths)) => { + blindedpay.into_iter().zip(paths.into_iter()).collect::>() }, }; @@ -926,7 +954,8 @@ mod tests { use bitcoin::util::schnorr::TweakedPublicKey; use core::convert::TryFrom; use core::time::Duration; - use crate::chain::keysinterface::KeyMaterial; + use crate::blinded_path::{BlindedHop, BlindedPath}; + use crate::sign::KeyMaterial; use crate::ln::features::Bolt12InvoiceFeatures; use crate::ln::inbound_payment::ExpandedKey; use crate::ln::msgs::DecodeError; @@ -937,8 +966,8 @@ mod tests { use crate::offers::payer::PayerTlvStreamRef; use crate::offers::refund::RefundBuilder; use crate::offers::test_utils::*; - use crate::onion_message::{BlindedHop, BlindedPath}; use crate::util::ser::{BigSize, Iterable, Writeable}; + use crate::util::string::PrintableString; trait ToBytes { fn to_bytes(&self) -> Vec; @@ -975,6 +1004,7 @@ mod tests { invoice.write(&mut buffer).unwrap(); assert_eq!(invoice.bytes, buffer.as_slice()); + assert_eq!(invoice.description(), PrintableString("foo")); assert_eq!(invoice.payment_paths(), payment_paths.as_slice()); assert_eq!(invoice.created_at(), now); assert_eq!(invoice.relative_expiry(), DEFAULT_RELATIVE_EXPIRY); @@ -1022,8 +1052,8 @@ mod tests { payer_note: None, }, InvoiceTlvStreamRef { - paths: Some(Iterable(payment_paths.iter().map(|(path, _)| path))), - blindedpay: Some(Iterable(payment_paths.iter().map(|(_, payinfo)| payinfo))), + paths: Some(Iterable(payment_paths.iter().map(|(_, path)| path))), + blindedpay: Some(Iterable(payment_paths.iter().map(|(payinfo, _)| payinfo))), created_at: Some(now.as_secs()), relative_expiry: None, payment_hash: Some(&payment_hash), @@ -1057,6 +1087,7 @@ mod tests { invoice.write(&mut buffer).unwrap(); assert_eq!(invoice.bytes, buffer.as_slice()); + assert_eq!(invoice.description(), PrintableString("foo")); assert_eq!(invoice.payment_paths(), payment_paths.as_slice()); assert_eq!(invoice.created_at(), now); assert_eq!(invoice.relative_expiry(), DEFAULT_RELATIVE_EXPIRY); @@ -1099,8 +1130,8 @@ mod tests { payer_note: None, }, InvoiceTlvStreamRef { - paths: Some(Iterable(payment_paths.iter().map(|(path, _)| path))), - blindedpay: Some(Iterable(payment_paths.iter().map(|(_, payinfo)| payinfo))), + paths: Some(Iterable(payment_paths.iter().map(|(_, path)| path))), + blindedpay: Some(Iterable(payment_paths.iter().map(|(payinfo, _)| payinfo))), created_at: Some(now.as_secs()), relative_expiry: None, payment_hash: Some(&payment_hash), @@ -1485,7 +1516,7 @@ mod tests { let empty_payment_paths = vec![]; let mut tlv_stream = invoice.as_tlv_stream(); - tlv_stream.3.paths = Some(Iterable(empty_payment_paths.iter().map(|(path, _)| path))); + tlv_stream.3.paths = Some(Iterable(empty_payment_paths.iter().map(|(_, path)| path))); match Invoice::try_from(tlv_stream.to_bytes()) { Ok(_) => panic!("expected error"), @@ -1495,7 +1526,7 @@ mod tests { let mut payment_paths = payment_paths(); payment_paths.pop(); let mut tlv_stream = invoice.as_tlv_stream(); - tlv_stream.3.blindedpay = Some(Iterable(payment_paths.iter().map(|(_, payinfo)| payinfo))); + tlv_stream.3.blindedpay = Some(Iterable(payment_paths.iter().map(|(payinfo, _)| payinfo))); match Invoice::try_from(tlv_stream.to_bytes()) { Ok(_) => panic!("expected error"),