X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning%2Fsrc%2Foffers%2Finvoice_request.rs;h=03af068d1d61912738f23ac71ff3c91079161ffe;hb=2cb25576697f368f1807d6a1f2db3d6086beb84b;hp=c3b9f5bdd20386cb45cd26334f341919bf6806cf;hpb=85c471aac254cb4b7a93f0fa38a26ad0b637cbe3;p=rust-lightning diff --git a/lightning/src/offers/invoice_request.rs b/lightning/src/offers/invoice_request.rs index c3b9f5bd..03af068d 100644 --- a/lightning/src/offers/invoice_request.rs +++ b/lightning/src/offers/invoice_request.rs @@ -372,6 +372,11 @@ impl UnsignedInvoiceRequest { Self { bytes, contents, tagged_hash } } + /// Returns the [`TaggedHash`] of the invoice to sign. + pub fn tagged_hash(&self) -> &TaggedHash { + &self.tagged_hash + } + /// Signs the [`TaggedHash`] of the invoice request using the given function. /// /// Note: The hash computation may have included unknown, odd TLV records. @@ -441,48 +446,58 @@ pub(super) struct InvoiceRequestContentsWithoutPayerId { payer_note: Option, } -impl InvoiceRequest { +macro_rules! invoice_request_accessors { ($self: ident, $contents: expr) => { /// An unpredictable series of bytes, typically containing information about the derivation of /// [`payer_id`]. /// /// [`payer_id`]: Self::payer_id - pub fn metadata(&self) -> &[u8] { - self.contents.metadata() + pub fn payer_metadata(&$self) -> &[u8] { + $contents.metadata() } /// A chain from [`Offer::chains`] that the offer is valid for. - pub fn chain(&self) -> ChainHash { - self.contents.chain() + pub fn chain(&$self) -> ChainHash { + $contents.chain() } /// The amount to pay in msats (i.e., the minimum lightning-payable unit for [`chain`]), which /// must be greater than or equal to [`Offer::amount`], converted if necessary. /// /// [`chain`]: Self::chain - pub fn amount_msats(&self) -> Option { - self.contents.amount_msats() + pub fn amount_msats(&$self) -> Option { + $contents.amount_msats() } /// Features pertaining to requesting an invoice. - pub fn features(&self) -> &InvoiceRequestFeatures { - &self.contents.features() + pub fn invoice_request_features(&$self) -> &InvoiceRequestFeatures { + &$contents.features() } /// The quantity of the offer's item conforming to [`Offer::is_valid_quantity`]. - pub fn quantity(&self) -> Option { - self.contents.quantity() + pub fn quantity(&$self) -> Option { + $contents.quantity() } /// A possibly transient pubkey used to sign the invoice request. - pub fn payer_id(&self) -> PublicKey { - self.contents.payer_id() + pub fn payer_id(&$self) -> PublicKey { + $contents.payer_id() } /// A payer-provided note which will be seen by the recipient and reflected back in the invoice /// response. - pub fn payer_note(&self) -> Option { - self.contents.payer_note() + pub fn payer_note(&$self) -> Option { + $contents.payer_note() } +} } + +impl UnsignedInvoiceRequest { + offer_accessors!(self, self.contents.inner.offer); + invoice_request_accessors!(self, self.contents); +} + +impl InvoiceRequest { + offer_accessors!(self, self.contents.inner.offer); + invoice_request_accessors!(self, self.contents); /// Signature of the invoice request using [`payer_id`]. /// @@ -534,7 +549,7 @@ impl InvoiceRequest { &self, payment_paths: Vec<(BlindedPayInfo, BlindedPath)>, payment_hash: PaymentHash, created_at: core::time::Duration ) -> Result, Bolt12SemanticError> { - if self.features().requires_unknown_bits() { + if self.invoice_request_features().requires_unknown_bits() { return Err(Bolt12SemanticError::UnknownRequiredFeatures); } @@ -577,7 +592,7 @@ impl InvoiceRequest { &self, payment_paths: Vec<(BlindedPayInfo, BlindedPath)>, payment_hash: PaymentHash, created_at: core::time::Duration, expanded_key: &ExpandedKey, secp_ctx: &Secp256k1 ) -> Result, Bolt12SemanticError> { - if self.features().requires_unknown_bits() { + if self.invoice_request_features().requires_unknown_bits() { return Err(Bolt12SemanticError::UnknownRequiredFeatures); } @@ -625,15 +640,15 @@ impl InvoiceRequestContents { self.inner.chain() } - fn amount_msats(&self) -> Option { + pub(super) fn amount_msats(&self) -> Option { self.inner.amount_msats } - fn features(&self) -> &InvoiceRequestFeatures { + pub(super) fn features(&self) -> &InvoiceRequestFeatures { &self.inner.features } - fn quantity(&self) -> Option { + pub(super) fn quantity(&self) -> Option { self.inner.quantity } @@ -641,7 +656,7 @@ impl InvoiceRequestContents { self.payer_id } - fn payer_note(&self) -> Option { + pub(super) fn payer_note(&self) -> Option { self.inner.payer_note.as_ref() .map(|payer_note| PrintableString(payer_note.as_str())) } @@ -789,7 +804,8 @@ impl TryFrom> for InvoiceRequest { None => return Err(Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::MissingSignature)), Some(signature) => signature, }; - merkle::verify_signature(&signature, SIGNATURE_TAG, &bytes, contents.payer_id)?; + let message = TaggedHash::new(SIGNATURE_TAG, &bytes); + merkle::verify_signature(&signature, message, contents.payer_id)?; Ok(InvoiceRequest { bytes, contents, signature }) } @@ -850,7 +866,7 @@ mod tests { #[cfg(feature = "std")] use core::time::Duration; use crate::sign::KeyMaterial; - use crate::ln::features::InvoiceRequestFeatures; + use crate::ln::features::{InvoiceRequestFeatures, OfferFeatures}; use crate::ln::inbound_payment::ExpandedKey; use crate::ln::msgs::{DecodeError, MAX_VALUE_MSAT}; use crate::offers::invoice::{Bolt12Invoice, SIGNATURE_TAG as INVOICE_SIGNATURE_TAG}; @@ -873,6 +889,25 @@ mod tests { let mut buffer = Vec::new(); unsigned_invoice_request.write(&mut buffer).unwrap(); + assert_eq!(unsigned_invoice_request.bytes, buffer.as_slice()); + assert_eq!(unsigned_invoice_request.payer_metadata(), &[1; 32]); + assert_eq!(unsigned_invoice_request.chains(), vec![ChainHash::using_genesis_block(Network::Bitcoin)]); + assert_eq!(unsigned_invoice_request.metadata(), None); + assert_eq!(unsigned_invoice_request.amount(), Some(&Amount::Bitcoin { amount_msats: 1000 })); + assert_eq!(unsigned_invoice_request.description(), PrintableString("foo")); + assert_eq!(unsigned_invoice_request.offer_features(), &OfferFeatures::empty()); + assert_eq!(unsigned_invoice_request.absolute_expiry(), None); + assert_eq!(unsigned_invoice_request.paths(), &[]); + assert_eq!(unsigned_invoice_request.issuer(), None); + assert_eq!(unsigned_invoice_request.supported_quantity(), Quantity::One); + assert_eq!(unsigned_invoice_request.signing_pubkey(), recipient_pubkey()); + assert_eq!(unsigned_invoice_request.chain(), ChainHash::using_genesis_block(Network::Bitcoin)); + assert_eq!(unsigned_invoice_request.amount_msats(), None); + assert_eq!(unsigned_invoice_request.invoice_request_features(), &InvoiceRequestFeatures::empty()); + assert_eq!(unsigned_invoice_request.quantity(), None); + assert_eq!(unsigned_invoice_request.payer_id(), payer_pubkey()); + assert_eq!(unsigned_invoice_request.payer_note(), None); + match UnsignedInvoiceRequest::try_from(buffer) { Err(e) => panic!("error parsing unsigned invoice request: {:?}", e), Ok(parsed) => { @@ -887,18 +922,26 @@ mod tests { invoice_request.write(&mut buffer).unwrap(); assert_eq!(invoice_request.bytes, buffer.as_slice()); - assert_eq!(invoice_request.metadata(), &[1; 32]); + assert_eq!(invoice_request.payer_metadata(), &[1; 32]); + assert_eq!(invoice_request.chains(), vec![ChainHash::using_genesis_block(Network::Bitcoin)]); + assert_eq!(invoice_request.metadata(), None); + assert_eq!(invoice_request.amount(), Some(&Amount::Bitcoin { amount_msats: 1000 })); + assert_eq!(invoice_request.description(), PrintableString("foo")); + assert_eq!(invoice_request.offer_features(), &OfferFeatures::empty()); + assert_eq!(invoice_request.absolute_expiry(), None); + assert_eq!(invoice_request.paths(), &[]); + assert_eq!(invoice_request.issuer(), None); + assert_eq!(invoice_request.supported_quantity(), Quantity::One); + assert_eq!(invoice_request.signing_pubkey(), recipient_pubkey()); assert_eq!(invoice_request.chain(), ChainHash::using_genesis_block(Network::Bitcoin)); assert_eq!(invoice_request.amount_msats(), None); - assert_eq!(invoice_request.features(), &InvoiceRequestFeatures::empty()); + assert_eq!(invoice_request.invoice_request_features(), &InvoiceRequestFeatures::empty()); assert_eq!(invoice_request.quantity(), None); assert_eq!(invoice_request.payer_id(), payer_pubkey()); assert_eq!(invoice_request.payer_note(), None); - assert!( - merkle::verify_signature( - &invoice_request.signature, SIGNATURE_TAG, &invoice_request.bytes, payer_pubkey() - ).is_ok() - ); + + let message = TaggedHash::new(SIGNATURE_TAG, &invoice_request.bytes); + assert!(merkle::verify_signature(&invoice_request.signature, message, payer_pubkey()).is_ok()); assert_eq!( invoice_request.as_tlv_stream(), @@ -1291,7 +1334,7 @@ mod tests { .build().unwrap() .sign(payer_sign).unwrap(); let (_, _, tlv_stream, _) = invoice_request.as_tlv_stream(); - assert_eq!(invoice_request.features(), &InvoiceRequestFeatures::unknown()); + assert_eq!(invoice_request.invoice_request_features(), &InvoiceRequestFeatures::unknown()); assert_eq!(tlv_stream.features, Some(&InvoiceRequestFeatures::unknown())); let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey()) @@ -1303,7 +1346,7 @@ mod tests { .build().unwrap() .sign(payer_sign).unwrap(); let (_, _, tlv_stream, _) = invoice_request.as_tlv_stream(); - assert_eq!(invoice_request.features(), &InvoiceRequestFeatures::empty()); + assert_eq!(invoice_request.invoice_request_features(), &InvoiceRequestFeatures::empty()); assert_eq!(tlv_stream.features, None); }