X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning%2Fsrc%2Foffers%2Foffer.rs;h=d92d0d8bb4ca9ec3812fc6ad3dd37b5b7b948f9e;hb=e0a0add9fe0b239f8a5176ab62e7ba3cb4f541ca;hp=a401ec1cc08388a633c7d91f44b362f9996488f0;hpb=04d64f08087788d2ceafd842fe1e7e2ef4e8f275;p=rust-lightning diff --git a/lightning/src/offers/offer.rs b/lightning/src/offers/offer.rs index a401ec1c..d92d0d8b 100644 --- a/lightning/src/offers/offer.rs +++ b/lightning/src/offers/offer.rs @@ -106,7 +106,7 @@ impl OfferBuilder { let offer = OfferContents { chains: None, metadata: None, amount: None, description, features: OfferFeatures::empty(), absolute_expiry: None, issuer: None, paths: None, - supported_quantity: Quantity::one(), signing_pubkey: Some(signing_pubkey), + supported_quantity: Quantity::one(), signing_pubkey, }; OfferBuilder { offer } } @@ -232,7 +232,7 @@ impl OfferBuilder { /// An `Offer` is a potentially long-lived proposal for payment of a good or service. /// /// An offer is a precursor to an [`InvoiceRequest`]. A merchant publishes an offer from which a -/// customer may request an `Invoice` for a specific quantity and using an amount sufficient to +/// customer may request an [`Invoice`] for a specific quantity and using an amount sufficient to /// cover that quantity (i.e., at least `quantity * amount`). See [`Offer::amount`]. /// /// Offers may be denominated in currency other than bitcoin but are ultimately paid using the @@ -241,6 +241,7 @@ impl OfferBuilder { /// Through the use of [`BlindedPath`]s, offers provide recipient privacy. /// /// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest +/// [`Invoice`]: crate::offers::invoice::Invoice #[derive(Clone, Debug)] pub struct Offer { // The serialized offer. Needed when creating an `InvoiceRequest` if the offer contains unknown @@ -249,9 +250,10 @@ pub struct Offer { pub(super) contents: OfferContents, } -/// The contents of an [`Offer`], which may be shared with an [`InvoiceRequest`] or an `Invoice`. +/// The contents of an [`Offer`], which may be shared with an [`InvoiceRequest`] or an [`Invoice`]. /// /// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest +/// [`Invoice`]: crate::offers::invoice::Invoice #[derive(Clone, Debug)] pub(super) struct OfferContents { chains: Option>, @@ -263,7 +265,7 @@ pub(super) struct OfferContents { issuer: Option, paths: Option>, supported_quantity: Quantity, - signing_pubkey: Option, + signing_pubkey: PublicKey, } impl Offer { @@ -319,13 +321,7 @@ impl Offer { /// Whether the offer has expired. #[cfg(feature = "std")] pub fn is_expired(&self) -> bool { - match self.absolute_expiry() { - Some(seconds_from_epoch) => match SystemTime::UNIX_EPOCH.elapsed() { - Ok(elapsed) => elapsed > seconds_from_epoch, - Err(_) => false, - }, - None => false, - } + self.contents.is_expired() } /// The issuer of the offer, possibly beginning with `user@domain` or `domain`. Intended to be @@ -359,7 +355,7 @@ impl Offer { /// The public key used by the recipient to sign invoices. pub fn signing_pubkey(&self) -> PublicKey { - self.contents.signing_pubkey.unwrap() + self.contents.signing_pubkey() } /// Creates an [`InvoiceRequest`] for the offer with the given `metadata` and `payer_id`, which @@ -410,6 +406,17 @@ impl OfferContents { self.chains().contains(&chain) } + #[cfg(feature = "std")] + pub(super) fn is_expired(&self) -> bool { + match self.absolute_expiry { + Some(seconds_from_epoch) => match SystemTime::UNIX_EPOCH.elapsed() { + Ok(elapsed) => elapsed > seconds_from_epoch, + Err(_) => false, + }, + None => false, + } + } + pub fn amount(&self) -> Option<&Amount> { self.amount.as_ref() } @@ -473,6 +480,10 @@ impl OfferContents { } } + pub(super) fn signing_pubkey(&self) -> PublicKey { + self.signing_pubkey + } + pub(super) fn as_tlv_stream(&self) -> OfferTlvStreamRef { let (currency, amount) = match &self.amount { None => (None, None), @@ -497,7 +508,7 @@ impl OfferContents { paths: self.paths.as_ref(), issuer: self.issuer.as_ref(), quantity_max: self.supported_quantity.to_tlv_record(), - node_id: self.signing_pubkey.as_ref(), + node_id: Some(&self.signing_pubkey), } } } @@ -567,7 +578,7 @@ tlv_stream!(OfferTlvStream, OfferTlvStreamRef, 1..80, { (6, currency: CurrencyCode), (8, amount: (u64, HighZeroBytesDroppedBigSize)), (10, description: (String, WithoutLength)), - (12, features: OfferFeatures), + (12, features: (OfferFeatures, WithoutLength)), (14, absolute_expiry: (u64, HighZeroBytesDroppedBigSize)), (16, paths: (Vec, WithoutLength)), (18, issuer: (String, WithoutLength)), @@ -634,13 +645,14 @@ impl TryFrom for OfferContents { Some(n) => Quantity::Bounded(NonZeroU64::new(n).unwrap()), }; - if node_id.is_none() { - return Err(SemanticError::MissingSigningPubkey); - } + let signing_pubkey = match node_id { + None => return Err(SemanticError::MissingSigningPubkey), + Some(node_id) => node_id, + }; Ok(OfferContents { chains, metadata, amount, description, features, absolute_expiry, issuer, paths, - supported_quantity, signing_pubkey: node_id, + supported_quantity, signing_pubkey, }) } } @@ -1126,11 +1138,13 @@ mod tests { panic!("error parsing offer: {:?}", e); } - let mut builder = OfferBuilder::new("foo".into(), pubkey(42)); - builder.offer.signing_pubkey = None; + let mut tlv_stream = offer.as_tlv_stream(); + tlv_stream.node_id = None; - let offer = builder.build().unwrap(); - match offer.to_string().parse::() { + let mut encoded_offer = Vec::new(); + tlv_stream.write(&mut encoded_offer).unwrap(); + + match Offer::try_from(encoded_offer) { Ok(_) => panic!("expected error"), Err(e) => { assert_eq!(e, ParseError::InvalidSemantics(SemanticError::MissingSigningPubkey));