]> git.bitcoin.ninja Git - rust-lightning/commitdiff
Parse experimental invreq TLV records
authorJeffrey Czyz <jkczyz@gmail.com>
Thu, 8 Aug 2024 16:44:03 +0000 (11:44 -0500)
committerJeffrey Czyz <jkczyz@gmail.com>
Tue, 5 Nov 2024 00:00:23 +0000 (18:00 -0600)
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.

lightning/src/offers/invoice.rs
lightning/src/offers/invoice_request.rs
lightning/src/offers/refund.rs
lightning/src/util/ser.rs

index fcc723474cf3d51747f0d4e3b125eef6bbd39521..2c4e4e5ac12b228c472151ba10b5f34f4dcf05df 100644 (file)
@@ -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<u64> =
                        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<ParsedMessage<FullInvoiceTlvStream>> 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<PartialInvoiceTlvStream> 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<PartialInvoiceTlvStream> 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<PartialInvoiceTlvStream> 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));
        }
index b80d82ce38257b309263a58efe07025510ea4e91..dd0d523588c4ff5c62c420137d461cba0e669455 100644 (file)
@@ -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<u64> =
        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<Vec<u8>> for UnsignedInvoiceRequest {
@@ -1192,11 +1213,12 @@ impl TryFrom<Vec<u8>> 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<PartialInvoiceRequestTlvStream> 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")));
        }
index a9f18d1b94de730ff70e65d852837f3a49190304..2eb54b12e10b816080b30c21a1ca58dce021c134 100644 (file)
@@ -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<RefundTlvStream> 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")));
        }
index 3e528ce93e899cb1fa2356c50cfec607789c9442..a3b56fa849421b8a394e8e1b72b700226fd83d8d 100644 (file)
@@ -1466,6 +1466,28 @@ impl<A: Writeable, B: Writeable, C: Writeable, D: Writeable, E: Writeable> Write
        }
 }
 
+impl<A: Readable, B: Readable, C: Readable, D: Readable, E: Readable, F: Readable> Readable for (A, B, C, D, E, F) {
+       fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {
+               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<A: Writeable, B: Writeable, C: Writeable, D: Writeable, E: Writeable, F: Writeable> Writeable for (A, B, C, D, E, F) {
+       fn write<W: Writer>(&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<W: Writer>(&self, _: &mut W) -> Result<(), io::Error> {
                Ok(())