X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning%2Fsrc%2Foffers%2Frefund.rs;h=cc0388c0241b5f77a125ab0dabe4da8427504e8f;hb=16b3c720a6a228856427e8e729ce794d2900ef9b;hp=f800274c973341d0ed33fd9c89dffc8fcb475b19;hpb=6236e0d4722cdc9ccf3617f9c2ce73782213c08c;p=rust-lightning diff --git a/lightning/src/offers/refund.rs b/lightning/src/offers/refund.rs index f800274c..cc0388c0 100644 --- a/lightning/src/offers/refund.rs +++ b/lightning/src/offers/refund.rs @@ -18,7 +18,7 @@ //! [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest //! [`Offer`]: crate::offers::offer::Offer //! -//! ```ignore +//! ``` //! extern crate bitcoin; //! extern crate core; //! extern crate lightning; @@ -118,9 +118,9 @@ impl RefundBuilder { } let refund = RefundContents { - payer: PayerContents(metadata), metadata: None, description, absolute_expiry: None, - issuer: None, paths: None, chain: None, amount_msats, - features: InvoiceRequestFeatures::empty(), quantity: None, payer_id, payer_note: None, + payer: PayerContents(metadata), description, absolute_expiry: None, issuer: None, + paths: None, chain: None, amount_msats, features: InvoiceRequestFeatures::empty(), + quantity: None, payer_id, payer_note: None, }; Ok(RefundBuilder { refund }) @@ -216,7 +216,7 @@ impl RefundBuilder { /// /// [`Invoice`]: crate::offers::invoice::Invoice /// [`Offer`]: crate::offers::offer::Offer -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub struct Refund { pub(super) bytes: Vec, pub(super) contents: RefundContents, @@ -225,11 +225,10 @@ pub struct Refund { /// The contents of a [`Refund`], which may be shared with an [`Invoice`]. /// /// [`Invoice`]: crate::offers::invoice::Invoice -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub(super) struct RefundContents { payer: PayerContents, // offer fields - metadata: Option>, description: String, absolute_expiry: Option, issuer: Option, @@ -318,12 +317,31 @@ impl Refund { self.contents.payer_note.as_ref().map(|payer_note| PrintableString(payer_note.as_str())) } + /// Creates an [`Invoice`] for the refund with the given required fields and using the + /// [`Duration`] since [`std::time::SystemTime::UNIX_EPOCH`] as the creation time. + /// + /// See [`Refund::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, + signing_pubkey: PublicKey, + ) -> 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, signing_pubkey, created_at) + } + /// Creates an [`Invoice`] for the refund 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. @@ -332,27 +350,22 @@ impl Refund { /// offer, which does have a `signing_pubkey`. /// /// 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 is used for + /// `signing_pubkey`. /// /// Errors if the request contains unknown required features. /// /// [`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, - signing_pubkey: PublicKey, - #[cfg(any(test, not(feature = "std")))] - created_at: Duration + signing_pubkey: PublicKey, created_at: 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_refund(self, payment_paths, created_at, payment_hash, signing_pubkey) } @@ -395,7 +408,7 @@ impl RefundContents { let offer = OfferTlvStreamRef { chains: None, - metadata: self.metadata.as_ref(), + metadata: None, currency: None, amount: None, description: Some(&self.description), @@ -497,6 +510,10 @@ impl TryFrom for RefundContents { Some(metadata) => PayerContents(metadata), }; + if metadata.is_some() { + return Err(SemanticError::UnexpectedMetadata); + } + if chains.is_some() { return Err(SemanticError::UnexpectedChain); } @@ -539,10 +556,9 @@ impl TryFrom for RefundContents { Some(payer_id) => payer_id, }; - // TODO: Should metadata be included? Ok(RefundContents { - payer, metadata, description, absolute_expiry, issuer, paths, chain, amount_msats, - features, quantity, payer_id, payer_note, + payer, description, absolute_expiry, issuer, paths, chain, amount_msats, features, + quantity, payer_id, payer_note, }) } } @@ -949,6 +965,17 @@ mod tests { panic!("error parsing refund: {:?}", e); } + let metadata = vec![42; 32]; + let mut tlv_stream = refund.as_tlv_stream(); + tlv_stream.1.metadata = Some(&metadata); + + match Refund::try_from(tlv_stream.to_bytes()) { + Ok(_) => panic!("expected error"), + Err(e) => { + assert_eq!(e, ParseError::InvalidSemantics(SemanticError::UnexpectedMetadata)); + }, + } + let chains = vec![ChainHash::using_genesis_block(Network::Testnet)]; let mut tlv_stream = refund.as_tlv_stream(); tlv_stream.1.chains = Some(&chains);