From: Jeffrey Czyz Date: Thu, 8 Aug 2024 16:44:03 +0000 (-0500) Subject: Parse experimental invreq TLV records X-Git-Url: http://git.bitcoin.ninja/?a=commitdiff_plain;h=5590bc2fa8b28fc9c8f3999bd91e7655fccce5f7;p=rust-lightning Parse experimental invreq TLV records The BOLT12 spec defines an experimental TLV range that are allowed in invoice_request messages. Allow this range when parsing an invoice request and include those bytes in any invoice. Also include those bytes when verifying that a Bolt12Invoice is for a valid InvoiceRequest. --- diff --git a/lightning/src/offers/invoice.rs b/lightning/src/offers/invoice.rs index fcc723474..2c4e4e5ac 100644 --- a/lightning/src/offers/invoice.rs +++ b/lightning/src/offers/invoice.rs @@ -121,7 +121,7 @@ use crate::ln::msgs::DecodeError; use crate::offers::invoice_macros::{invoice_accessors_common, invoice_builder_methods_common}; #[cfg(test)] use crate::offers::invoice_macros::invoice_builder_methods_test; -use crate::offers::invoice_request::{EXPERIMENTAL_INVOICE_REQUEST_TYPES, INVOICE_REQUEST_PAYER_ID_TYPE, INVOICE_REQUEST_TYPES, IV_BYTES as INVOICE_REQUEST_IV_BYTES, InvoiceRequest, InvoiceRequestContents, InvoiceRequestTlvStream, InvoiceRequestTlvStreamRef}; +use crate::offers::invoice_request::{EXPERIMENTAL_INVOICE_REQUEST_TYPES, ExperimentalInvoiceRequestTlvStream, ExperimentalInvoiceRequestTlvStreamRef, INVOICE_REQUEST_PAYER_ID_TYPE, INVOICE_REQUEST_TYPES, IV_BYTES as INVOICE_REQUEST_IV_BYTES, InvoiceRequest, InvoiceRequestContents, InvoiceRequestTlvStream, InvoiceRequestTlvStreamRef}; use crate::offers::merkle::{SignError, SignFn, SignatureTlvStream, SignatureTlvStreamRef, TaggedHash, TlvStream, self, SIGNATURE_TLV_RECORD_SIZE}; use crate::offers::nonce::Nonce; use crate::offers::offer::{Amount, EXPERIMENTAL_OFFER_TYPES, ExperimentalOfferTlvStream, ExperimentalOfferTlvStreamRef, OFFER_TYPES, OfferTlvStream, OfferTlvStreamRef, Quantity}; @@ -497,7 +497,7 @@ impl UnsignedBolt12Invoice { const EXPERIMENTAL_TYPES: core::ops::Range = EXPERIMENTAL_OFFER_TYPES.start..EXPERIMENTAL_INVOICE_REQUEST_TYPES.end; - let (_, _, _, invoice_tlv_stream, _) = contents.as_tlv_stream(); + let (_, _, _, invoice_tlv_stream, _, _) = contents.as_tlv_stream(); // Allocate enough space for the invoice, which will include: // - all TLV records from `invreq_bytes` except signatures, @@ -903,7 +903,7 @@ impl Bolt12Invoice { pub(crate) fn as_tlv_stream(&self) -> FullInvoiceTlvStreamRef { let ( payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, invoice_tlv_stream, - experimental_offer_tlv_stream, + experimental_offer_tlv_stream, experimental_invoice_request_tlv_stream, ) = self.contents.as_tlv_stream(); let signature_tlv_stream = SignatureTlvStreamRef { signature: Some(&self.signature), @@ -911,6 +911,7 @@ impl Bolt12Invoice { ( payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, invoice_tlv_stream, signature_tlv_stream, experimental_offer_tlv_stream, + experimental_invoice_request_tlv_stream, ) } @@ -1172,13 +1173,15 @@ impl InvoiceContents { } fn as_tlv_stream(&self) -> PartialInvoiceTlvStreamRef { - let (payer, offer, invoice_request, experimental_offer) = match self { + let ( + payer, offer, invoice_request, experimental_offer, experimental_invoice_request, + ) = match self { InvoiceContents::ForOffer { invoice_request, .. } => invoice_request.as_tlv_stream(), InvoiceContents::ForRefund { refund, .. } => refund.as_tlv_stream(), }; let invoice = self.fields().as_tlv_stream(); - (payer, offer, invoice_request, invoice, experimental_offer) + (payer, offer, invoice_request, invoice, experimental_offer, experimental_invoice_request) } } @@ -1339,7 +1342,7 @@ impl_writeable!(FallbackAddress, { version, program }); type FullInvoiceTlvStream =( PayerTlvStream, OfferTlvStream, InvoiceRequestTlvStream, InvoiceTlvStream, SignatureTlvStream, - ExperimentalOfferTlvStream, + ExperimentalOfferTlvStream, ExperimentalInvoiceRequestTlvStream, ); type FullInvoiceTlvStreamRef<'a> = ( @@ -1349,6 +1352,7 @@ type FullInvoiceTlvStreamRef<'a> = ( InvoiceTlvStreamRef<'a>, SignatureTlvStreamRef<'a>, ExperimentalOfferTlvStreamRef, + ExperimentalInvoiceRequestTlvStreamRef, ); impl CursorReadable for FullInvoiceTlvStream { @@ -1359,14 +1363,20 @@ impl CursorReadable for FullInvoiceTlvStream { let invoice = CursorReadable::read(r)?; let signature = CursorReadable::read(r)?; let experimental_offer = CursorReadable::read(r)?; + let experimental_invoice_request = CursorReadable::read(r)?; - Ok((payer, offer, invoice_request, invoice, signature, experimental_offer)) + Ok( + ( + payer, offer, invoice_request, invoice, signature, experimental_offer, + experimental_invoice_request, + ) + ) } } type PartialInvoiceTlvStream = ( PayerTlvStream, OfferTlvStream, InvoiceRequestTlvStream, InvoiceTlvStream, - ExperimentalOfferTlvStream, + ExperimentalOfferTlvStream, ExperimentalInvoiceRequestTlvStream, ); type PartialInvoiceTlvStreamRef<'a> = ( @@ -1375,6 +1385,7 @@ type PartialInvoiceTlvStreamRef<'a> = ( InvoiceRequestTlvStreamRef<'a>, InvoiceTlvStreamRef<'a>, ExperimentalOfferTlvStreamRef, + ExperimentalInvoiceRequestTlvStreamRef, ); impl CursorReadable for PartialInvoiceTlvStream { @@ -1384,8 +1395,14 @@ impl CursorReadable for PartialInvoiceTlvStream { let invoice_request = CursorReadable::read(r)?; let invoice = CursorReadable::read(r)?; let experimental_offer = CursorReadable::read(r)?; + let experimental_invoice_request = CursorReadable::read(r)?; - Ok((payer, offer, invoice_request, invoice, experimental_offer)) + Ok( + ( + payer, offer, invoice_request, invoice, experimental_offer, + experimental_invoice_request, + ) + ) } } @@ -1398,11 +1415,12 @@ impl TryFrom> for Bolt12Invoice { payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, invoice_tlv_stream, SignatureTlvStream { signature }, experimental_offer_tlv_stream, + experimental_invoice_request_tlv_stream, ) = tlv_stream; let contents = InvoiceContents::try_from( ( payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, invoice_tlv_stream, - experimental_offer_tlv_stream, + experimental_offer_tlv_stream, experimental_invoice_request_tlv_stream, ) )?; @@ -1430,6 +1448,7 @@ impl TryFrom for InvoiceContents { features, node_id, message_paths, }, experimental_offer_tlv_stream, + experimental_invoice_request_tlv_stream, ) = tlv_stream; if message_paths.is_some() { return Err(Bolt12SemanticError::UnexpectedPaths) } @@ -1464,7 +1483,7 @@ impl TryFrom for InvoiceContents { let refund = RefundContents::try_from( ( payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, - experimental_offer_tlv_stream, + experimental_offer_tlv_stream, experimental_invoice_request_tlv_stream, ) )?; Ok(InvoiceContents::ForRefund { refund, fields }) @@ -1472,7 +1491,7 @@ impl TryFrom for InvoiceContents { let invoice_request = InvoiceRequestContents::try_from( ( payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, - experimental_offer_tlv_stream, + experimental_offer_tlv_stream, experimental_invoice_request_tlv_stream, ) )?; Ok(InvoiceContents::ForOffer { invoice_request, fields }) @@ -1545,7 +1564,7 @@ mod tests { use crate::ln::features::{Bolt12InvoiceFeatures, InvoiceRequestFeatures, OfferFeatures}; use crate::ln::inbound_payment::ExpandedKey; use crate::ln::msgs::DecodeError; - use crate::offers::invoice_request::InvoiceRequestTlvStreamRef; + use crate::offers::invoice_request::{ExperimentalInvoiceRequestTlvStreamRef, InvoiceRequestTlvStreamRef}; use crate::offers::merkle::{SignError, SignatureTlvStreamRef, TaggedHash, self}; use crate::offers::nonce::Nonce; use crate::offers::offer::{Amount, ExperimentalOfferTlvStreamRef, OfferTlvStreamRef, Quantity}; @@ -1719,6 +1738,7 @@ mod tests { ExperimentalOfferTlvStreamRef { experimental_foo: None, }, + ExperimentalInvoiceRequestTlvStreamRef {}, ), ); @@ -1815,6 +1835,7 @@ mod tests { ExperimentalOfferTlvStreamRef { experimental_foo: None, }, + ExperimentalInvoiceRequestTlvStreamRef {}, ), ); @@ -2011,7 +2032,7 @@ mod tests { .relative_expiry(one_hour.as_secs() as u32) .build().unwrap() .sign(recipient_sign).unwrap(); - let (_, _, _, tlv_stream, _, _) = invoice.as_tlv_stream(); + let (_, _, _, tlv_stream, _, _, _) = invoice.as_tlv_stream(); #[cfg(feature = "std")] assert!(!invoice.is_expired()); assert_eq!(invoice.relative_expiry(), one_hour); @@ -2027,7 +2048,7 @@ mod tests { .relative_expiry(one_hour.as_secs() as u32 - 1) .build().unwrap() .sign(recipient_sign).unwrap(); - let (_, _, _, tlv_stream, _, _) = invoice.as_tlv_stream(); + let (_, _, _, tlv_stream, _, _, _) = invoice.as_tlv_stream(); #[cfg(feature = "std")] assert!(invoice.is_expired()); assert_eq!(invoice.relative_expiry(), one_hour - Duration::from_secs(1)); @@ -2046,7 +2067,7 @@ mod tests { .respond_with_no_std(payment_paths(), payment_hash(), now()).unwrap() .build().unwrap() .sign(recipient_sign).unwrap(); - let (_, _, _, tlv_stream, _, _) = invoice.as_tlv_stream(); + let (_, _, _, tlv_stream, _, _, _) = invoice.as_tlv_stream(); assert_eq!(invoice.amount_msats(), 1001); assert_eq!(tlv_stream.amount, Some(1001)); } @@ -2064,7 +2085,7 @@ mod tests { .respond_with_no_std(payment_paths(), payment_hash(), now()).unwrap() .build().unwrap() .sign(recipient_sign).unwrap(); - let (_, _, _, tlv_stream, _, _) = invoice.as_tlv_stream(); + let (_, _, _, tlv_stream, _, _, _) = invoice.as_tlv_stream(); assert_eq!(invoice.amount_msats(), 2000); assert_eq!(tlv_stream.amount, Some(2000)); @@ -2102,7 +2123,7 @@ mod tests { .fallback_v1_p2tr_tweaked(&tweaked_pubkey) .build().unwrap() .sign(recipient_sign).unwrap(); - let (_, _, _, tlv_stream, _, _) = invoice.as_tlv_stream(); + let (_, _, _, tlv_stream, _, _, _) = invoice.as_tlv_stream(); assert_eq!( invoice.fallbacks(), vec![ @@ -2145,7 +2166,7 @@ mod tests { .allow_mpp() .build().unwrap() .sign(recipient_sign).unwrap(); - let (_, _, _, tlv_stream, _, _) = invoice.as_tlv_stream(); + let (_, _, _, tlv_stream, _, _, _) = invoice.as_tlv_stream(); assert_eq!(invoice.invoice_features(), &features); assert_eq!(tlv_stream.features, Some(&features)); } diff --git a/lightning/src/offers/invoice_request.rs b/lightning/src/offers/invoice_request.rs index b80d82ce3..dd0d52358 100644 --- a/lightning/src/offers/invoice_request.rs +++ b/lightning/src/offers/invoice_request.rs @@ -523,7 +523,7 @@ impl UnsignedInvoiceRequest { // unknown TLV records, which are not stored in `OfferContents`. let ( payer_tlv_stream, _offer_tlv_stream, invoice_request_tlv_stream, - _experimental_offer_tlv_stream, + _experimental_offer_tlv_stream, experimental_invoice_request_tlv_stream, ) = contents.as_tlv_stream(); // Allocate enough space for the invoice_request, which will include: @@ -535,6 +535,7 @@ impl UnsignedInvoiceRequest { + payer_tlv_stream.serialized_length() + invoice_request_tlv_stream.serialized_length() + SIGNATURE_TLV_RECORD_SIZE + + experimental_invoice_request_tlv_stream.serialized_length(), ); payer_tlv_stream.write(&mut bytes).unwrap(); @@ -555,12 +556,14 @@ impl UnsignedInvoiceRequest { - experimental_tlv_stream .peek() .map_or(remaining_bytes.len(), |first_record| first_record.start) + + experimental_invoice_request_tlv_stream.serialized_length(), ); for record in experimental_tlv_stream { record.write(&mut experimental_bytes).unwrap(); } + experimental_invoice_request_tlv_stream.write(&mut experimental_bytes).unwrap(); debug_assert_eq!(experimental_bytes.len(), experimental_bytes.capacity()); let tlv_stream = TlvStream::new(&bytes).chain(TlvStream::new(&experimental_bytes)); @@ -910,7 +913,7 @@ impl InvoiceRequest { pub(crate) fn as_tlv_stream(&self) -> FullInvoiceRequestTlvStreamRef { let ( payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, - experimental_offer_tlv_stream, + experimental_offer_tlv_stream, experimental_invoice_request_tlv_stream, ) = self.contents.as_tlv_stream(); let signature_tlv_stream = SignatureTlvStreamRef { signature: Some(&self.signature), @@ -918,6 +921,7 @@ impl InvoiceRequest { ( payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, signature_tlv_stream, experimental_offer_tlv_stream, + experimental_invoice_request_tlv_stream, ) } } @@ -1034,9 +1038,10 @@ impl InvoiceRequestContents { } pub(super) fn as_tlv_stream(&self) -> PartialInvoiceRequestTlvStreamRef { - let (payer, offer, mut invoice_request, experimental_offer) = self.inner.as_tlv_stream(); + let (payer, offer, mut invoice_request, experimental_offer, experimental_invoice_request) = + self.inner.as_tlv_stream(); invoice_request.payer_id = Some(&self.payer_signing_pubkey); - (payer, offer, invoice_request, experimental_offer) + (payer, offer, invoice_request, experimental_offer, experimental_invoice_request) } } @@ -1071,7 +1076,9 @@ impl InvoiceRequestContentsWithoutPayerSigningPubkey { paths: None, }; - (payer, offer, invoice_request, experimental_offer) + let experimental_invoice_request = ExperimentalInvoiceRequestTlvStreamRef {}; + + (payer, offer, invoice_request, experimental_offer, experimental_invoice_request) } } @@ -1126,9 +1133,14 @@ tlv_stream!(InvoiceRequestTlvStream, InvoiceRequestTlvStreamRef<'a>, INVOICE_REQ pub(super) const EXPERIMENTAL_INVOICE_REQUEST_TYPES: core::ops::Range = 2_000_000_000..3_000_000_000; +tlv_stream!( + ExperimentalInvoiceRequestTlvStream, ExperimentalInvoiceRequestTlvStreamRef, + EXPERIMENTAL_INVOICE_REQUEST_TYPES, {} +); + type FullInvoiceRequestTlvStream = ( PayerTlvStream, OfferTlvStream, InvoiceRequestTlvStream, SignatureTlvStream, - ExperimentalOfferTlvStream, + ExperimentalOfferTlvStream, ExperimentalInvoiceRequestTlvStream, ); type FullInvoiceRequestTlvStreamRef<'a> = ( @@ -1137,6 +1149,7 @@ type FullInvoiceRequestTlvStreamRef<'a> = ( InvoiceRequestTlvStreamRef<'a>, SignatureTlvStreamRef<'a>, ExperimentalOfferTlvStreamRef, + ExperimentalInvoiceRequestTlvStreamRef, ); impl CursorReadable for FullInvoiceRequestTlvStream { @@ -1146,13 +1159,20 @@ impl CursorReadable for FullInvoiceRequestTlvStream { let invoice_request = CursorReadable::read(r)?; let signature = CursorReadable::read(r)?; let experimental_offer = CursorReadable::read(r)?; + let experimental_invoice_request = CursorReadable::read(r)?; - Ok((payer, offer, invoice_request, signature, experimental_offer)) + Ok( + ( + payer, offer, invoice_request, signature, experimental_offer, + experimental_invoice_request, + ) + ) } } type PartialInvoiceRequestTlvStream = ( PayerTlvStream, OfferTlvStream, InvoiceRequestTlvStream, ExperimentalOfferTlvStream, + ExperimentalInvoiceRequestTlvStream, ); type PartialInvoiceRequestTlvStreamRef<'a> = ( @@ -1160,6 +1180,7 @@ type PartialInvoiceRequestTlvStreamRef<'a> = ( OfferTlvStreamRef<'a>, InvoiceRequestTlvStreamRef<'a>, ExperimentalOfferTlvStreamRef, + ExperimentalInvoiceRequestTlvStreamRef, ); impl TryFrom> for UnsignedInvoiceRequest { @@ -1192,11 +1213,12 @@ impl TryFrom> for InvoiceRequest { payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, SignatureTlvStream { signature }, experimental_offer_tlv_stream, + experimental_invoice_request_tlv_stream, ) = tlv_stream; let contents = InvoiceRequestContents::try_from( ( payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, - experimental_offer_tlv_stream, + experimental_offer_tlv_stream, experimental_invoice_request_tlv_stream, ) )?; @@ -1222,6 +1244,7 @@ impl TryFrom for InvoiceRequestContents { chain, amount, features, quantity, payer_id, payer_note, paths, }, experimental_offer_tlv_stream, + ExperimentalInvoiceRequestTlvStream {}, ) = tlv_stream; let payer = match metadata { @@ -1309,7 +1332,7 @@ impl Readable for InvoiceRequestFields { #[cfg(test)] mod tests { - use super::{INVOICE_REQUEST_TYPES, InvoiceRequest, InvoiceRequestFields, InvoiceRequestTlvStreamRef, PAYER_NOTE_LIMIT, SIGNATURE_TAG, UnsignedInvoiceRequest}; + use super::{ExperimentalInvoiceRequestTlvStreamRef, INVOICE_REQUEST_TYPES, InvoiceRequest, InvoiceRequestFields, InvoiceRequestTlvStreamRef, PAYER_NOTE_LIMIT, SIGNATURE_TAG, UnsignedInvoiceRequest}; use bitcoin::constants::ChainHash; use bitcoin::network::Network; @@ -1437,6 +1460,7 @@ mod tests { ExperimentalOfferTlvStreamRef { experimental_foo: None, }, + ExperimentalInvoiceRequestTlvStreamRef {}, ), ); @@ -1509,24 +1533,27 @@ mod tests { let ( payer_tlv_stream, offer_tlv_stream, mut invoice_request_tlv_stream, mut invoice_tlv_stream, mut signature_tlv_stream, experimental_offer_tlv_stream, + experimental_invoice_request_tlv_stream, ) = invoice.as_tlv_stream(); invoice_request_tlv_stream.amount = Some(2000); invoice_tlv_stream.amount = Some(2000); let tlv_stream = (payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, invoice_tlv_stream); + let experimental_tlv_stream = ( + experimental_offer_tlv_stream, experimental_invoice_request_tlv_stream, + ); let mut bytes = Vec::new(); - tlv_stream.write(&mut bytes).unwrap(); - experimental_offer_tlv_stream.write(&mut bytes).unwrap(); + (&tlv_stream, &experimental_tlv_stream).write(&mut bytes).unwrap(); let message = TaggedHash::from_valid_tlv_stream_bytes(INVOICE_SIGNATURE_TAG, &bytes); let signature = merkle::sign_message(recipient_sign, &message, recipient_pubkey()).unwrap(); signature_tlv_stream.signature = Some(&signature); let mut encoded_invoice = Vec::new(); - tlv_stream.write(&mut encoded_invoice).unwrap(); - signature_tlv_stream.write(&mut encoded_invoice).unwrap(); - experimental_offer_tlv_stream.write(&mut encoded_invoice).unwrap(); + (tlv_stream, signature_tlv_stream, experimental_tlv_stream) + .write(&mut encoded_invoice) + .unwrap(); let invoice = Bolt12Invoice::try_from(encoded_invoice).unwrap(); assert!(invoice.verify_using_metadata(&expanded_key, &secp_ctx).is_err()); @@ -1535,24 +1562,27 @@ mod tests { let ( mut payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, invoice_tlv_stream, mut signature_tlv_stream, experimental_offer_tlv_stream, + experimental_invoice_request_tlv_stream, ) = invoice.as_tlv_stream(); let metadata = payer_tlv_stream.metadata.unwrap().iter().copied().rev().collect(); payer_tlv_stream.metadata = Some(&metadata); let tlv_stream = (payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, invoice_tlv_stream); + let experimental_tlv_stream = ( + experimental_offer_tlv_stream, experimental_invoice_request_tlv_stream, + ); let mut bytes = Vec::new(); - tlv_stream.write(&mut bytes).unwrap(); - experimental_offer_tlv_stream.write(&mut bytes).unwrap(); + (&tlv_stream, &experimental_tlv_stream).write(&mut bytes).unwrap(); let message = TaggedHash::from_valid_tlv_stream_bytes(INVOICE_SIGNATURE_TAG, &bytes); let signature = merkle::sign_message(recipient_sign, &message, recipient_pubkey()).unwrap(); signature_tlv_stream.signature = Some(&signature); let mut encoded_invoice = Vec::new(); - tlv_stream.write(&mut encoded_invoice).unwrap(); - signature_tlv_stream.write(&mut encoded_invoice).unwrap(); - experimental_offer_tlv_stream.write(&mut encoded_invoice).unwrap(); + (tlv_stream, signature_tlv_stream, experimental_tlv_stream) + .write(&mut encoded_invoice) + .unwrap(); let invoice = Bolt12Invoice::try_from(encoded_invoice).unwrap(); assert!(invoice.verify_using_metadata(&expanded_key, &secp_ctx).is_err()); @@ -1589,24 +1619,27 @@ mod tests { let ( payer_tlv_stream, offer_tlv_stream, mut invoice_request_tlv_stream, mut invoice_tlv_stream, mut signature_tlv_stream, experimental_offer_tlv_stream, + experimental_invoice_request_tlv_stream, ) = invoice.as_tlv_stream(); invoice_request_tlv_stream.amount = Some(2000); invoice_tlv_stream.amount = Some(2000); let tlv_stream = (payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, invoice_tlv_stream); + let experimental_tlv_stream = ( + experimental_offer_tlv_stream, experimental_invoice_request_tlv_stream, + ); let mut bytes = Vec::new(); - tlv_stream.write(&mut bytes).unwrap(); - experimental_offer_tlv_stream.write(&mut bytes).unwrap(); + (&tlv_stream, &experimental_tlv_stream).write(&mut bytes).unwrap(); let message = TaggedHash::from_valid_tlv_stream_bytes(INVOICE_SIGNATURE_TAG, &bytes); let signature = merkle::sign_message(recipient_sign, &message, recipient_pubkey()).unwrap(); signature_tlv_stream.signature = Some(&signature); let mut encoded_invoice = Vec::new(); - tlv_stream.write(&mut encoded_invoice).unwrap(); - signature_tlv_stream.write(&mut encoded_invoice).unwrap(); - experimental_offer_tlv_stream.write(&mut encoded_invoice).unwrap(); + (tlv_stream, signature_tlv_stream, experimental_tlv_stream) + .write(&mut encoded_invoice) + .unwrap(); let invoice = Bolt12Invoice::try_from(encoded_invoice).unwrap(); assert!( @@ -1617,24 +1650,27 @@ mod tests { let ( payer_tlv_stream, offer_tlv_stream, mut invoice_request_tlv_stream, invoice_tlv_stream, mut signature_tlv_stream, experimental_offer_tlv_stream, + experimental_invoice_request_tlv_stream, ) = invoice.as_tlv_stream(); let payer_id = pubkey(1); invoice_request_tlv_stream.payer_id = Some(&payer_id); let tlv_stream = (payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, invoice_tlv_stream); + let experimental_tlv_stream = ( + experimental_offer_tlv_stream, experimental_invoice_request_tlv_stream, + ); let mut bytes = Vec::new(); - tlv_stream.write(&mut bytes).unwrap(); - experimental_offer_tlv_stream.write(&mut bytes).unwrap(); + (&tlv_stream, &experimental_tlv_stream).write(&mut bytes).unwrap(); let message = TaggedHash::from_valid_tlv_stream_bytes(INVOICE_SIGNATURE_TAG, &bytes); let signature = merkle::sign_message(recipient_sign, &message, recipient_pubkey()).unwrap(); signature_tlv_stream.signature = Some(&signature); let mut encoded_invoice = Vec::new(); - tlv_stream.write(&mut encoded_invoice).unwrap(); - signature_tlv_stream.write(&mut encoded_invoice).unwrap(); - experimental_offer_tlv_stream.write(&mut encoded_invoice).unwrap(); + (tlv_stream, signature_tlv_stream, experimental_tlv_stream) + .write(&mut encoded_invoice) + .unwrap(); let invoice = Bolt12Invoice::try_from(encoded_invoice).unwrap(); assert!( @@ -1654,7 +1690,7 @@ mod tests { .chain(Network::Bitcoin).unwrap() .build().unwrap() .sign(payer_sign).unwrap(); - let (_, _, tlv_stream, _, _) = invoice_request.as_tlv_stream(); + let (_, _, tlv_stream, _, _, _) = invoice_request.as_tlv_stream(); assert_eq!(invoice_request.chain(), mainnet); assert_eq!(tlv_stream.chain, None); @@ -1666,7 +1702,7 @@ mod tests { .chain(Network::Testnet).unwrap() .build().unwrap() .sign(payer_sign).unwrap(); - let (_, _, tlv_stream, _, _) = invoice_request.as_tlv_stream(); + let (_, _, tlv_stream, _, _, _) = invoice_request.as_tlv_stream(); assert_eq!(invoice_request.chain(), testnet); assert_eq!(tlv_stream.chain, Some(&testnet)); @@ -1679,7 +1715,7 @@ mod tests { .chain(Network::Bitcoin).unwrap() .build().unwrap() .sign(payer_sign).unwrap(); - let (_, _, tlv_stream, _, _) = invoice_request.as_tlv_stream(); + let (_, _, tlv_stream, _, _, _) = invoice_request.as_tlv_stream(); assert_eq!(invoice_request.chain(), mainnet); assert_eq!(tlv_stream.chain, None); @@ -1693,7 +1729,7 @@ mod tests { .chain(Network::Testnet).unwrap() .build().unwrap() .sign(payer_sign).unwrap(); - let (_, _, tlv_stream, _, _) = invoice_request.as_tlv_stream(); + let (_, _, tlv_stream, _, _, _) = invoice_request.as_tlv_stream(); assert_eq!(invoice_request.chain(), testnet); assert_eq!(tlv_stream.chain, Some(&testnet)); @@ -1729,7 +1765,7 @@ mod tests { .amount_msats(1000).unwrap() .build().unwrap() .sign(payer_sign).unwrap(); - let (_, _, tlv_stream, _, _) = invoice_request.as_tlv_stream(); + let (_, _, tlv_stream, _, _, _) = invoice_request.as_tlv_stream(); assert_eq!(invoice_request.amount_msats(), Some(1000)); assert_eq!(tlv_stream.amount, Some(1000)); @@ -1741,7 +1777,7 @@ mod tests { .amount_msats(1000).unwrap() .build().unwrap() .sign(payer_sign).unwrap(); - let (_, _, tlv_stream, _, _) = invoice_request.as_tlv_stream(); + let (_, _, tlv_stream, _, _, _) = invoice_request.as_tlv_stream(); assert_eq!(invoice_request.amount_msats(), Some(1000)); assert_eq!(tlv_stream.amount, Some(1000)); @@ -1752,7 +1788,7 @@ mod tests { .amount_msats(1001).unwrap() .build().unwrap() .sign(payer_sign).unwrap(); - let (_, _, tlv_stream, _, _) = invoice_request.as_tlv_stream(); + let (_, _, tlv_stream, _, _, _) = invoice_request.as_tlv_stream(); assert_eq!(invoice_request.amount_msats(), Some(1001)); assert_eq!(tlv_stream.amount, Some(1001)); @@ -1832,7 +1868,7 @@ mod tests { .features_unchecked(InvoiceRequestFeatures::unknown()) .build().unwrap() .sign(payer_sign).unwrap(); - let (_, _, tlv_stream, _, _) = invoice_request.as_tlv_stream(); + let (_, _, tlv_stream, _, _, _) = invoice_request.as_tlv_stream(); assert_eq!(invoice_request.invoice_request_features(), &InvoiceRequestFeatures::unknown()); assert_eq!(tlv_stream.features, Some(&InvoiceRequestFeatures::unknown())); @@ -1844,7 +1880,7 @@ mod tests { .features_unchecked(InvoiceRequestFeatures::empty()) .build().unwrap() .sign(payer_sign).unwrap(); - let (_, _, tlv_stream, _, _) = invoice_request.as_tlv_stream(); + let (_, _, tlv_stream, _, _, _) = invoice_request.as_tlv_stream(); assert_eq!(invoice_request.invoice_request_features(), &InvoiceRequestFeatures::empty()); assert_eq!(tlv_stream.features, None); } @@ -1861,7 +1897,7 @@ mod tests { .request_invoice(vec![1; 32], payer_pubkey()).unwrap() .build().unwrap() .sign(payer_sign).unwrap(); - let (_, _, tlv_stream, _, _) = invoice_request.as_tlv_stream(); + let (_, _, tlv_stream, _, _, _) = invoice_request.as_tlv_stream(); assert_eq!(invoice_request.quantity(), None); assert_eq!(tlv_stream.quantity, None); @@ -1886,7 +1922,7 @@ mod tests { .quantity(10).unwrap() .build().unwrap() .sign(payer_sign).unwrap(); - let (_, _, tlv_stream, _, _) = invoice_request.as_tlv_stream(); + let (_, _, tlv_stream, _, _, _) = invoice_request.as_tlv_stream(); assert_eq!(invoice_request.amount_msats(), Some(10_000)); assert_eq!(tlv_stream.amount, Some(10_000)); @@ -1911,7 +1947,7 @@ mod tests { .quantity(2).unwrap() .build().unwrap() .sign(payer_sign).unwrap(); - let (_, _, tlv_stream, _, _) = invoice_request.as_tlv_stream(); + let (_, _, tlv_stream, _, _, _) = invoice_request.as_tlv_stream(); assert_eq!(invoice_request.amount_msats(), Some(2_000)); assert_eq!(tlv_stream.amount, Some(2_000)); @@ -1947,7 +1983,7 @@ mod tests { .payer_note("bar".into()) .build().unwrap() .sign(payer_sign).unwrap(); - let (_, _, tlv_stream, _, _) = invoice_request.as_tlv_stream(); + let (_, _, tlv_stream, _, _, _) = invoice_request.as_tlv_stream(); assert_eq!(invoice_request.payer_note(), Some(PrintableString("bar"))); assert_eq!(tlv_stream.payer_note, Some(&String::from("bar"))); @@ -1959,7 +1995,7 @@ mod tests { .payer_note("baz".into()) .build().unwrap() .sign(payer_sign).unwrap(); - let (_, _, tlv_stream, _, _) = invoice_request.as_tlv_stream(); + let (_, _, tlv_stream, _, _, _) = invoice_request.as_tlv_stream(); assert_eq!(invoice_request.payer_note(), Some(PrintableString("baz"))); assert_eq!(tlv_stream.payer_note, Some(&String::from("baz"))); } diff --git a/lightning/src/offers/refund.rs b/lightning/src/offers/refund.rs index a9f18d1b9..2eb54b12e 100644 --- a/lightning/src/offers/refund.rs +++ b/lightning/src/offers/refund.rs @@ -98,7 +98,7 @@ use crate::ln::channelmanager::PaymentId; use crate::ln::features::InvoiceRequestFeatures; use crate::ln::inbound_payment::{ExpandedKey, IV_LEN}; use crate::ln::msgs::{DecodeError, MAX_VALUE_MSAT}; -use crate::offers::invoice_request::{InvoiceRequestTlvStream, InvoiceRequestTlvStreamRef}; +use crate::offers::invoice_request::{ExperimentalInvoiceRequestTlvStream, ExperimentalInvoiceRequestTlvStreamRef, InvoiceRequestTlvStream, InvoiceRequestTlvStreamRef}; use crate::offers::nonce::Nonce; use crate::offers::offer::{ExperimentalOfferTlvStream, ExperimentalOfferTlvStreamRef, OfferTlvStream, OfferTlvStreamRef}; use crate::offers::parse::{Bech32Encode, Bolt12ParseError, Bolt12SemanticError, ParsedMessage}; @@ -787,7 +787,9 @@ impl RefundContents { experimental_foo: self.experimental_foo, }; - (payer, offer, invoice_request, experimental_offer) + let experimental_invoice_request = ExperimentalInvoiceRequestTlvStreamRef {}; + + (payer, offer, invoice_request, experimental_offer, experimental_invoice_request) } } @@ -812,6 +814,7 @@ impl Writeable for RefundContents { type RefundTlvStream = ( PayerTlvStream, OfferTlvStream, InvoiceRequestTlvStream, ExperimentalOfferTlvStream, + ExperimentalInvoiceRequestTlvStream, ); type RefundTlvStreamRef<'a> = ( @@ -819,6 +822,7 @@ type RefundTlvStreamRef<'a> = ( OfferTlvStreamRef<'a>, InvoiceRequestTlvStreamRef<'a>, ExperimentalOfferTlvStreamRef, + ExperimentalInvoiceRequestTlvStreamRef, ); impl CursorReadable for RefundTlvStream { @@ -827,8 +831,9 @@ impl CursorReadable for RefundTlvStream { let offer = CursorReadable::read(r)?; let invoice_request = CursorReadable::read(r)?; let experimental_offer = CursorReadable::read(r)?; + let experimental_invoice_request = CursorReadable::read(r)?; - Ok((payer, offer, invoice_request, experimental_offer)) + Ok((payer, offer, invoice_request, experimental_offer, experimental_invoice_request)) } } @@ -874,6 +879,7 @@ impl TryFrom for RefundContents { #[cfg(test)] experimental_foo, }, + ExperimentalInvoiceRequestTlvStream {}, ) = tlv_stream; let payer = match payer_metadata { @@ -971,7 +977,7 @@ mod tests { use crate::ln::features::{InvoiceRequestFeatures, OfferFeatures}; use crate::ln::inbound_payment::ExpandedKey; use crate::ln::msgs::{DecodeError, MAX_VALUE_MSAT}; - use crate::offers::invoice_request::{INVOICE_REQUEST_TYPES, InvoiceRequestTlvStreamRef}; + use crate::offers::invoice_request::{ExperimentalInvoiceRequestTlvStreamRef, INVOICE_REQUEST_TYPES, InvoiceRequestTlvStreamRef}; use crate::offers::nonce::Nonce; use crate::offers::offer::{ExperimentalOfferTlvStreamRef, OfferTlvStreamRef}; use crate::offers::parse::{Bolt12ParseError, Bolt12SemanticError}; @@ -1044,6 +1050,7 @@ mod tests { ExperimentalOfferTlvStreamRef { experimental_foo: None, }, + ExperimentalInvoiceRequestTlvStreamRef {}, ), ); @@ -1198,7 +1205,7 @@ mod tests { .absolute_expiry(future_expiry) .build() .unwrap(); - let (_, tlv_stream, _, _) = refund.as_tlv_stream(); + let (_, tlv_stream, _, _, _) = refund.as_tlv_stream(); #[cfg(feature = "std")] assert!(!refund.is_expired()); assert!(!refund.is_expired_no_std(now)); @@ -1210,7 +1217,7 @@ mod tests { .absolute_expiry(past_expiry) .build() .unwrap(); - let (_, tlv_stream, _, _) = refund.as_tlv_stream(); + let (_, tlv_stream, _, _, _) = refund.as_tlv_stream(); #[cfg(feature = "std")] assert!(refund.is_expired()); assert!(refund.is_expired_no_std(now)); @@ -1242,7 +1249,7 @@ mod tests { .path(paths[1].clone()) .build() .unwrap(); - let (_, _, invoice_request_tlv_stream, _) = refund.as_tlv_stream(); + let (_, _, invoice_request_tlv_stream, _, _) = refund.as_tlv_stream(); assert_eq!(refund.payer_signing_pubkey(), pubkey(42)); assert_eq!(refund.paths(), paths.as_slice()); assert_ne!(pubkey(42), pubkey(44)); @@ -1256,7 +1263,7 @@ mod tests { .issuer("bar".into()) .build() .unwrap(); - let (_, tlv_stream, _, _) = refund.as_tlv_stream(); + let (_, tlv_stream, _, _, _) = refund.as_tlv_stream(); assert_eq!(refund.issuer(), Some(PrintableString("bar"))); assert_eq!(tlv_stream.issuer, Some(&String::from("bar"))); @@ -1265,7 +1272,7 @@ mod tests { .issuer("baz".into()) .build() .unwrap(); - let (_, tlv_stream, _, _) = refund.as_tlv_stream(); + let (_, tlv_stream, _, _, _) = refund.as_tlv_stream(); assert_eq!(refund.issuer(), Some(PrintableString("baz"))); assert_eq!(tlv_stream.issuer, Some(&String::from("baz"))); } @@ -1278,14 +1285,14 @@ mod tests { let refund = RefundBuilder::new(vec![1; 32], payer_pubkey(), 1000).unwrap() .chain(Network::Bitcoin) .build().unwrap(); - let (_, _, tlv_stream, _) = refund.as_tlv_stream(); + let (_, _, tlv_stream, _, _) = refund.as_tlv_stream(); assert_eq!(refund.chain(), mainnet); assert_eq!(tlv_stream.chain, None); let refund = RefundBuilder::new(vec![1; 32], payer_pubkey(), 1000).unwrap() .chain(Network::Testnet) .build().unwrap(); - let (_, _, tlv_stream, _) = refund.as_tlv_stream(); + let (_, _, tlv_stream, _, _) = refund.as_tlv_stream(); assert_eq!(refund.chain(), testnet); assert_eq!(tlv_stream.chain, Some(&testnet)); @@ -1293,7 +1300,7 @@ mod tests { .chain(Network::Regtest) .chain(Network::Testnet) .build().unwrap(); - let (_, _, tlv_stream, _) = refund.as_tlv_stream(); + let (_, _, tlv_stream, _, _) = refund.as_tlv_stream(); assert_eq!(refund.chain(), testnet); assert_eq!(tlv_stream.chain, Some(&testnet)); } @@ -1303,7 +1310,7 @@ mod tests { let refund = RefundBuilder::new(vec![1; 32], payer_pubkey(), 1000).unwrap() .quantity(10) .build().unwrap(); - let (_, _, tlv_stream, _) = refund.as_tlv_stream(); + let (_, _, tlv_stream, _, _) = refund.as_tlv_stream(); assert_eq!(refund.quantity(), Some(10)); assert_eq!(tlv_stream.quantity, Some(10)); @@ -1311,7 +1318,7 @@ mod tests { .quantity(10) .quantity(1) .build().unwrap(); - let (_, _, tlv_stream, _) = refund.as_tlv_stream(); + let (_, _, tlv_stream, _, _) = refund.as_tlv_stream(); assert_eq!(refund.quantity(), Some(1)); assert_eq!(tlv_stream.quantity, Some(1)); } @@ -1321,7 +1328,7 @@ mod tests { let refund = RefundBuilder::new(vec![1; 32], payer_pubkey(), 1000).unwrap() .payer_note("bar".into()) .build().unwrap(); - let (_, _, tlv_stream, _) = refund.as_tlv_stream(); + let (_, _, tlv_stream, _, _) = refund.as_tlv_stream(); assert_eq!(refund.payer_note(), Some(PrintableString("bar"))); assert_eq!(tlv_stream.payer_note, Some(&String::from("bar"))); @@ -1329,7 +1336,7 @@ mod tests { .payer_note("bar".into()) .payer_note("baz".into()) .build().unwrap(); - let (_, _, tlv_stream, _) = refund.as_tlv_stream(); + let (_, _, tlv_stream, _, _) = refund.as_tlv_stream(); assert_eq!(refund.payer_note(), Some(PrintableString("baz"))); assert_eq!(tlv_stream.payer_note, Some(&String::from("baz"))); } diff --git a/lightning/src/util/ser.rs b/lightning/src/util/ser.rs index 3e528ce93..a3b56fa84 100644 --- a/lightning/src/util/ser.rs +++ b/lightning/src/util/ser.rs @@ -1466,6 +1466,28 @@ impl Write } } +impl Readable for (A, B, C, D, E, F) { + fn read(r: &mut R) -> Result { + let a: A = Readable::read(r)?; + let b: B = Readable::read(r)?; + let c: C = Readable::read(r)?; + let d: D = Readable::read(r)?; + let e: E = Readable::read(r)?; + let f: F = Readable::read(r)?; + Ok((a, b, c, d, e, f)) + } +} +impl Writeable for (A, B, C, D, E, F) { + fn write(&self, w: &mut W) -> Result<(), io::Error> { + self.0.write(w)?; + self.1.write(w)?; + self.2.write(w)?; + self.3.write(w)?; + self.4.write(w)?; + self.5.write(w) + } +} + impl Writeable for () { fn write(&self, _: &mut W) -> Result<(), io::Error> { Ok(())