X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning%2Fsrc%2Foffers%2Finvoice_request.rs;h=a1a0520c62259409b4cd516ed9607eb17bad3296;hb=e44868681dfcb53cafae856e0f798b61e3d90ea8;hp=e863ef6cd613f2c79759e34b5b2df6987e60e448;hpb=ca5b10884ef0bb754cc5f1c1c346eabad82e5296;p=rust-lightning diff --git a/lightning/src/offers/invoice_request.rs b/lightning/src/offers/invoice_request.rs index e863ef6c..a1a0520c 100644 --- a/lightning/src/offers/invoice_request.rs +++ b/lightning/src/offers/invoice_request.rs @@ -19,7 +19,7 @@ //! [`Invoice`]: crate::offers::invoice::Invoice //! [`Refund`]: crate::offers::refund::Refund //! -//! ```ignore +//! ``` //! extern crate bitcoin; //! extern crate lightning; //! @@ -250,7 +250,7 @@ impl<'a> UnsignedInvoiceRequest<'a> { /// /// [`Invoice`]: crate::offers::invoice::Invoice /// [`Offer`]: crate::offers::offer::Offer -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub struct InvoiceRequest { pub(super) bytes: Vec, pub(super) contents: InvoiceRequestContents, @@ -260,7 +260,7 @@ pub struct InvoiceRequest { /// The contents of an [`InvoiceRequest`], which may be shared with an [`Invoice`]. /// /// [`Invoice`]: crate::offers::invoice::Invoice -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub(super) struct InvoiceRequestContents { payer: PayerContents, pub(super) offer: OfferContents, @@ -322,38 +322,51 @@ impl InvoiceRequest { self.signature } + /// Creates an [`Invoice`] for the request with the given required fields and using the + /// [`Duration`] since [`std::time::SystemTime::UNIX_EPOCH`] as the creation time. + /// + /// See [`InvoiceRequest::respond_with_no_std`] for further details where the aforementioned + /// creation time is used for the `created_at` parameter. + /// + /// [`Invoice`]: crate::offers::invoice::Invoice + /// [`Duration`]: core::time::Duration + #[cfg(feature = "std")] + pub fn respond_with( + &self, payment_paths: Vec<(BlindedPath, BlindedPayInfo)>, payment_hash: PaymentHash + ) -> Result { + let created_at = std::time::SystemTime::now() + .duration_since(std::time::SystemTime::UNIX_EPOCH) + .expect("SystemTime::now() should come after SystemTime::UNIX_EPOCH"); + + self.respond_with_no_std(payment_paths, payment_hash, created_at) + } + /// 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. + /// `created_at`, which is used to set [`Invoice::created_at`]. Useful for `no-std` builds where + /// [`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 - /// must contain one or more elements. + /// must contain one or more elements ordered from most-preferred to least-preferred, if there's + /// a preference. Note, however, that any privacy is lost if a public node id was used for + /// [`Offer::signing_pubkey`]. /// /// 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( + pub fn respond_with_no_std( &self, payment_paths: Vec<(BlindedPath, BlindedPayInfo)>, payment_hash: PaymentHash, - #[cfg(any(test, not(feature = "std")))] created_at: core::time::Duration ) -> Result { 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) } @@ -815,6 +828,18 @@ mod tests { Ok(_) => panic!("expected error"), Err(e) => assert_eq!(e, SemanticError::MissingAmount), } + + match OfferBuilder::new("foo".into(), recipient_pubkey()) + .amount_msats(1000) + .supported_quantity(Quantity::Unbounded) + .build().unwrap() + .request_invoice(vec![1; 32], payer_pubkey()).unwrap() + .quantity(u64::max_value()).unwrap() + .build() + { + Ok(_) => panic!("expected error"), + Err(e) => assert_eq!(e, SemanticError::InvalidAmount), + } } #[test] @@ -845,11 +870,12 @@ mod tests { #[test] fn builds_invoice_request_with_quantity() { + let one = NonZeroU64::new(1).unwrap(); let ten = NonZeroU64::new(10).unwrap(); let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey()) .amount_msats(1000) - .supported_quantity(Quantity::one()) + .supported_quantity(Quantity::One) .build().unwrap() .request_invoice(vec![1; 32], payer_pubkey()).unwrap() .build().unwrap() @@ -860,7 +886,7 @@ mod tests { match OfferBuilder::new("foo".into(), recipient_pubkey()) .amount_msats(1000) - .supported_quantity(Quantity::one()) + .supported_quantity(Quantity::One) .build().unwrap() .request_invoice(vec![1; 32], payer_pubkey()).unwrap() .amount_msats(2_000).unwrap() @@ -918,6 +944,17 @@ mod tests { Ok(_) => panic!("expected error"), Err(e) => assert_eq!(e, SemanticError::MissingQuantity), } + + match OfferBuilder::new("foo".into(), recipient_pubkey()) + .amount_msats(1000) + .supported_quantity(Quantity::Bounded(one)) + .build().unwrap() + .request_invoice(vec![1; 32], payer_pubkey()).unwrap() + .build() + { + Ok(_) => panic!("expected error"), + Err(e) => assert_eq!(e, SemanticError::MissingQuantity), + } } #[test] @@ -1098,15 +1135,33 @@ mod tests { assert_eq!(e, ParseError::InvalidSemantics(SemanticError::UnsupportedCurrency)); }, } + + let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey()) + .amount_msats(1000) + .supported_quantity(Quantity::Unbounded) + .build().unwrap() + .request_invoice(vec![1; 32], payer_pubkey()).unwrap() + .quantity(u64::max_value()).unwrap() + .build_unchecked() + .sign(payer_sign).unwrap(); + + let mut buffer = Vec::new(); + invoice_request.write(&mut buffer).unwrap(); + + match InvoiceRequest::try_from(buffer) { + Ok(_) => panic!("expected error"), + Err(e) => assert_eq!(e, ParseError::InvalidSemantics(SemanticError::InvalidAmount)), + } } #[test] fn parses_invoice_request_with_quantity() { + let one = NonZeroU64::new(1).unwrap(); let ten = NonZeroU64::new(10).unwrap(); let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey()) .amount_msats(1000) - .supported_quantity(Quantity::one()) + .supported_quantity(Quantity::One) .build().unwrap() .request_invoice(vec![1; 32], payer_pubkey()).unwrap() .build().unwrap() @@ -1121,7 +1176,7 @@ mod tests { let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey()) .amount_msats(1000) - .supported_quantity(Quantity::one()) + .supported_quantity(Quantity::One) .build().unwrap() .request_invoice(vec![1; 32], payer_pubkey()).unwrap() .amount_msats(2_000).unwrap() @@ -1206,6 +1261,22 @@ mod tests { Ok(_) => panic!("expected error"), Err(e) => assert_eq!(e, ParseError::InvalidSemantics(SemanticError::MissingQuantity)), } + + let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey()) + .amount_msats(1000) + .supported_quantity(Quantity::Bounded(one)) + .build().unwrap() + .request_invoice(vec![1; 32], payer_pubkey()).unwrap() + .build_unchecked() + .sign(payer_sign).unwrap(); + + let mut buffer = Vec::new(); + invoice_request.write(&mut buffer).unwrap(); + + match InvoiceRequest::try_from(buffer) { + Ok(_) => panic!("expected error"), + Err(e) => assert_eq!(e, ParseError::InvalidSemantics(SemanticError::MissingQuantity)), + } } #[test]