X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;ds=sidebyside;f=lightning%2Fsrc%2Foffers%2Frefund.rs;h=4628c334fbefa54d27386a127732c624e2976643;hb=022eadc4dbf0f60179674f936d604cade6c5dd9e;hp=fff33873954a6f92ab55ad2de8d326447dafd0ac;hpb=2a72f4f98caddd38058e520dd522685a19bd4053;p=rust-lightning diff --git a/lightning/src/offers/refund.rs b/lightning/src/offers/refund.rs index fff33873..4628c334 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; @@ -86,6 +86,7 @@ use crate::offers::invoice_request::{InvoiceRequestTlvStream, InvoiceRequestTlvS use crate::offers::offer::{OfferTlvStream, OfferTlvStreamRef}; use crate::offers::parse::{Bech32Encode, ParseError, ParsedMessage, SemanticError}; use crate::offers::payer::{PayerContents, PayerTlvStream, PayerTlvStreamRef}; +use crate::offers::signer::Metadata; use crate::onion_message::BlindedPath; use crate::util::ser::{SeekReadable, WithoutLength, Writeable, Writer}; use crate::util::string::PrintableString; @@ -117,6 +118,7 @@ impl RefundBuilder { return Err(SemanticError::InvalidAmount); } + let metadata = Metadata::Bytes(metadata); let refund = RefundContents { payer: PayerContents(metadata), description, absolute_expiry: None, issuer: None, paths: None, chain: None, amount_msats, features: InvoiceRequestFeatures::empty(), @@ -217,6 +219,7 @@ impl RefundBuilder { /// [`Invoice`]: crate::offers::invoice::Invoice /// [`Offer`]: crate::offers::offer::Offer #[derive(Clone, Debug)] +#[cfg_attr(test, derive(PartialEq))] pub struct Refund { pub(super) bytes: Vec, pub(super) contents: RefundContents, @@ -226,6 +229,7 @@ pub struct Refund { /// /// [`Invoice`]: crate::offers::invoice::Invoice #[derive(Clone, Debug)] +#[cfg_attr(test, derive(PartialEq))] pub(super) struct RefundContents { payer: PayerContents, // offer fields @@ -279,7 +283,7 @@ impl Refund { /// /// [`payer_id`]: Self::payer_id pub fn metadata(&self) -> &[u8] { - &self.contents.payer.0 + &self.contents.payer.0.as_bytes().unwrap()[..] } /// A chain that the refund is valid for. @@ -317,12 +321,30 @@ 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. + /// Creates an [`InvoiceBuilder`] 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. + /// + /// [`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 [`InvoiceBuilder`] 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. @@ -337,23 +359,15 @@ impl Refund { /// /// 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) } @@ -391,7 +405,7 @@ impl RefundContents { pub(super) fn as_tlv_stream(&self) -> RefundTlvStreamRef { let payer = PayerTlvStreamRef { - metadata: Some(&self.payer.0), + metadata: self.payer.0.as_bytes(), }; let offer = OfferTlvStreamRef { @@ -495,7 +509,7 @@ impl TryFrom for RefundContents { let payer = match payer_metadata { None => return Err(SemanticError::MissingPayerMetadata), - Some(metadata) => PayerContents(metadata), + Some(metadata) => PayerContents(Metadata::Bytes(metadata)), }; if metadata.is_some() { @@ -563,7 +577,7 @@ mod tests { use bitcoin::blockdata::constants::ChainHash; use bitcoin::network::constants::Network; - use bitcoin::secp256k1::{KeyPair, PublicKey, Secp256k1, SecretKey}; + use bitcoin::secp256k1::{KeyPair, Secp256k1, SecretKey}; use core::convert::TryFrom; use core::time::Duration; use crate::ln::features::{InvoiceRequestFeatures, OfferFeatures}; @@ -572,24 +586,11 @@ mod tests { use crate::offers::offer::OfferTlvStreamRef; use crate::offers::parse::{ParseError, SemanticError}; use crate::offers::payer::PayerTlvStreamRef; + use crate::offers::test_utils::*; use crate::onion_message::{BlindedHop, BlindedPath}; use crate::util::ser::{BigSize, Writeable}; use crate::util::string::PrintableString; - fn payer_pubkey() -> PublicKey { - let secp_ctx = Secp256k1::new(); - KeyPair::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap()).public_key() - } - - fn pubkey(byte: u8) -> PublicKey { - let secp_ctx = Secp256k1::new(); - PublicKey::from_secret_key(&secp_ctx, &privkey(byte)) - } - - fn privkey(byte: u8) -> SecretKey { - SecretKey::from_slice(&[byte; 32]).unwrap() - } - trait ToBytes { fn to_bytes(&self) -> Vec; } @@ -810,6 +811,18 @@ mod tests { assert_eq!(tlv_stream.payer_note, Some(&String::from("baz"))); } + #[test] + fn fails_responding_with_unknown_required_features() { + match RefundBuilder::new("foo".into(), vec![1; 32], payer_pubkey(), 1000).unwrap() + .features_unchecked(InvoiceRequestFeatures::unknown()) + .build().unwrap() + .respond_with_no_std(payment_paths(), payment_hash(), recipient_pubkey(), now()) + { + Ok(_) => panic!("expected error"), + Err(e) => assert_eq!(e, SemanticError::UnknownRequiredFeatures), + } + } + #[test] fn parses_refund_with_metadata() { let refund = RefundBuilder::new("foo".into(), vec![1; 32], payer_pubkey(), 1000).unwrap()