]> git.bitcoin.ninja Git - rust-lightning/commitdiff
Parse experimental invoice TLV records
authorJeffrey Czyz <jkczyz@gmail.com>
Mon, 12 Aug 2024 21:54:55 +0000 (16:54 -0500)
committerJeffrey Czyz <jkczyz@gmail.com>
Tue, 5 Nov 2024 00:00:24 +0000 (18:00 -0600)
The BOLT12 spec defines an experimental TLV range that is allowed in
offer and invoice_request messages. The remaining TLV-space is for
experimental use in invoice messages. Allow this range when parsing an
invoice and include it when signing one.

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

index eb2a0cdccea76210efbda4eac9718369564ad62e..08c4c6a78b730a06aff3cb9cd9596337212da903 100644 (file)
@@ -497,7 +497,8 @@ 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, _, _, experimental_invoice_tlv_stream) =
+                       contents.as_tlv_stream();
 
                // Allocate enough space for the invoice, which will include:
                // - all TLV records from `invreq_bytes` except signatures,
@@ -510,6 +511,7 @@ impl UnsignedBolt12Invoice {
                        invreq_bytes.len()
                                + invoice_tlv_stream.serialized_length()
                                + if contents.is_for_offer() { 0 } else { SIGNATURE_TLV_RECORD_SIZE }
+                               + experimental_invoice_tlv_stream.serialized_length(),
                );
 
                // Use the invoice_request bytes instead of the invoice_request TLV stream as the latter may
@@ -531,12 +533,14 @@ impl UnsignedBolt12Invoice {
                                - experimental_tlv_stream
                                        .peek()
                                        .map_or(remaining_bytes.len(), |first_record| first_record.start)
+                               + experimental_invoice_tlv_stream.serialized_length(),
                );
 
                for record in experimental_tlv_stream {
                        record.write(&mut experimental_bytes).unwrap();
                }
 
+               experimental_invoice_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));
@@ -904,6 +908,7 @@ impl Bolt12Invoice {
                let (
                        payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, invoice_tlv_stream,
                        experimental_offer_tlv_stream, experimental_invoice_request_tlv_stream,
+                       experimental_invoice_tlv_stream,
                ) = self.contents.as_tlv_stream();
                let signature_tlv_stream = SignatureTlvStreamRef {
                        signature: Some(&self.signature),
@@ -911,7 +916,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,
+                       experimental_invoice_request_tlv_stream, experimental_invoice_tlv_stream,
                )
        }
 
@@ -1179,9 +1184,12 @@ impl InvoiceContents {
                        InvoiceContents::ForOffer { invoice_request, .. } => invoice_request.as_tlv_stream(),
                        InvoiceContents::ForRefund { refund, .. } => refund.as_tlv_stream(),
                };
-               let invoice = self.fields().as_tlv_stream();
+               let (invoice, experimental_invoice) = self.fields().as_tlv_stream();
 
-               (payer, offer, invoice_request, invoice, experimental_offer, experimental_invoice_request)
+               (
+                       payer, offer, invoice_request, invoice, experimental_offer,
+                       experimental_invoice_request, experimental_invoice,
+               )
        }
 }
 
@@ -1229,24 +1237,27 @@ pub(super) fn filter_fallbacks(
 }
 
 impl InvoiceFields {
-       fn as_tlv_stream(&self) -> InvoiceTlvStreamRef {
+       fn as_tlv_stream(&self) -> (InvoiceTlvStreamRef, ExperimentalInvoiceTlvStreamRef) {
                let features = {
                        if self.features == Bolt12InvoiceFeatures::empty() { None }
                        else { Some(&self.features) }
                };
 
-               InvoiceTlvStreamRef {
-                       paths: Some(Iterable(self.payment_paths.iter().map(|path| path.inner_blinded_path()))),
-                       blindedpay: Some(Iterable(self.payment_paths.iter().map(|path| &path.payinfo))),
-                       created_at: Some(self.created_at.as_secs()),
-                       relative_expiry: self.relative_expiry.map(|duration| duration.as_secs() as u32),
-                       payment_hash: Some(&self.payment_hash),
-                       amount: Some(self.amount_msats),
-                       fallbacks: self.fallbacks.as_ref(),
-                       features,
-                       node_id: Some(&self.signing_pubkey),
-                       message_paths: None,
-               }
+               (
+                       InvoiceTlvStreamRef {
+                               paths: Some(Iterable(self.payment_paths.iter().map(|path| path.inner_blinded_path()))),
+                               blindedpay: Some(Iterable(self.payment_paths.iter().map(|path| &path.payinfo))),
+                               created_at: Some(self.created_at.as_secs()),
+                               relative_expiry: self.relative_expiry.map(|duration| duration.as_secs() as u32),
+                               payment_hash: Some(&self.payment_hash),
+                               amount: Some(self.amount_msats),
+                               fallbacks: self.fallbacks.as_ref(),
+                               features,
+                               node_id: Some(&self.signing_pubkey),
+                               message_paths: None,
+                       },
+                       ExperimentalInvoiceTlvStreamRef {},
+               )
        }
 }
 
@@ -1321,6 +1332,13 @@ tlv_stream!(InvoiceTlvStream, InvoiceTlvStreamRef<'a>, INVOICE_TYPES, {
        (236, message_paths: (Vec<BlindedMessagePath>, WithoutLength)),
 });
 
+/// Valid type range for experimental invoice TLV records.
+const EXPERIMENTAL_INVOICE_TYPES: core::ops::RangeFrom<u64> = 3_000_000_000..;
+
+tlv_stream!(
+       ExperimentalInvoiceTlvStream, ExperimentalInvoiceTlvStreamRef, EXPERIMENTAL_INVOICE_TYPES, {}
+);
+
 pub(super) type BlindedPathIter<'a> = core::iter::Map<
        core::slice::Iter<'a, BlindedPaymentPath>,
        for<'r> fn(&'r BlindedPaymentPath) -> &'r BlindedPath,
@@ -1342,7 +1360,7 @@ impl_writeable!(FallbackAddress, { version, program });
 
 type FullInvoiceTlvStream =(
        PayerTlvStream, OfferTlvStream, InvoiceRequestTlvStream, InvoiceTlvStream, SignatureTlvStream,
-       ExperimentalOfferTlvStream, ExperimentalInvoiceRequestTlvStream,
+       ExperimentalOfferTlvStream, ExperimentalInvoiceRequestTlvStream, ExperimentalInvoiceTlvStream,
 );
 
 type FullInvoiceTlvStreamRef<'a> = (
@@ -1353,6 +1371,7 @@ type FullInvoiceTlvStreamRef<'a> = (
        SignatureTlvStreamRef<'a>,
        ExperimentalOfferTlvStreamRef,
        ExperimentalInvoiceRequestTlvStreamRef,
+       ExperimentalInvoiceTlvStreamRef,
 );
 
 impl CursorReadable for FullInvoiceTlvStream {
@@ -1364,11 +1383,12 @@ impl CursorReadable for FullInvoiceTlvStream {
                let signature = CursorReadable::read(r)?;
                let experimental_offer = CursorReadable::read(r)?;
                let experimental_invoice_request = CursorReadable::read(r)?;
+               let experimental_invoice = CursorReadable::read(r)?;
 
                Ok(
                        (
                                payer, offer, invoice_request, invoice, signature, experimental_offer,
-                               experimental_invoice_request,
+                               experimental_invoice_request, experimental_invoice,
                        )
                )
        }
@@ -1376,7 +1396,7 @@ impl CursorReadable for FullInvoiceTlvStream {
 
 type PartialInvoiceTlvStream = (
        PayerTlvStream, OfferTlvStream, InvoiceRequestTlvStream, InvoiceTlvStream,
-       ExperimentalOfferTlvStream, ExperimentalInvoiceRequestTlvStream,
+       ExperimentalOfferTlvStream, ExperimentalInvoiceRequestTlvStream, ExperimentalInvoiceTlvStream,
 );
 
 type PartialInvoiceTlvStreamRef<'a> = (
@@ -1386,6 +1406,7 @@ type PartialInvoiceTlvStreamRef<'a> = (
        InvoiceTlvStreamRef<'a>,
        ExperimentalOfferTlvStreamRef,
        ExperimentalInvoiceRequestTlvStreamRef,
+       ExperimentalInvoiceTlvStreamRef,
 );
 
 impl CursorReadable for PartialInvoiceTlvStream {
@@ -1396,11 +1417,12 @@ impl CursorReadable for PartialInvoiceTlvStream {
                let invoice = CursorReadable::read(r)?;
                let experimental_offer = CursorReadable::read(r)?;
                let experimental_invoice_request = CursorReadable::read(r)?;
+               let experimental_invoice = CursorReadable::read(r)?;
 
                Ok(
                        (
                                payer, offer, invoice_request, invoice, experimental_offer,
-                               experimental_invoice_request,
+                               experimental_invoice_request, experimental_invoice,
                        )
                )
        }
@@ -1416,11 +1438,13 @@ impl TryFrom<ParsedMessage<FullInvoiceTlvStream>> for Bolt12Invoice {
                        SignatureTlvStream { signature },
                        experimental_offer_tlv_stream,
                        experimental_invoice_request_tlv_stream,
+                       experimental_invoice_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_invoice_request_tlv_stream,
+                               experimental_invoice_tlv_stream,
                        )
                )?;
 
@@ -1449,6 +1473,7 @@ impl TryFrom<PartialInvoiceTlvStream> for InvoiceContents {
                        },
                        experimental_offer_tlv_stream,
                        experimental_invoice_request_tlv_stream,
+                       ExperimentalInvoiceTlvStream {},
                ) = tlv_stream;
 
                if message_paths.is_some() { return Err(Bolt12SemanticError::UnexpectedPaths) }
@@ -1545,7 +1570,7 @@ pub(super) fn check_invoice_signing_pubkey(
 
 #[cfg(test)]
 mod tests {
-       use super::{Bolt12Invoice, DEFAULT_RELATIVE_EXPIRY, FallbackAddress, FullInvoiceTlvStreamRef, INVOICE_TYPES, InvoiceTlvStreamRef, SIGNATURE_TAG, UnsignedBolt12Invoice};
+       use super::{Bolt12Invoice, DEFAULT_RELATIVE_EXPIRY, ExperimentalInvoiceTlvStreamRef, FallbackAddress, FullInvoiceTlvStreamRef, INVOICE_TYPES, InvoiceTlvStreamRef, SIGNATURE_TAG, UnsignedBolt12Invoice};
 
        use bitcoin::{CompressedPublicKey, WitnessProgram, WitnessVersion};
        use bitcoin::constants::ChainHash;
@@ -1741,6 +1766,7 @@ mod tests {
                                ExperimentalInvoiceRequestTlvStreamRef {
                                        experimental_bar: None,
                                },
+                               ExperimentalInvoiceTlvStreamRef {},
                        ),
                );
 
@@ -1840,6 +1866,7 @@ mod tests {
                                ExperimentalInvoiceRequestTlvStreamRef {
                                        experimental_bar: None,
                                },
+                               ExperimentalInvoiceTlvStreamRef {},
                        ),
                );
 
@@ -2036,7 +2063,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);
@@ -2052,7 +2079,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));
@@ -2071,7 +2098,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));
        }
@@ -2089,7 +2116,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));
 
@@ -2127,7 +2154,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![
@@ -2170,7 +2197,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 ba66297f33c5a98c7f76836ff869d2897c7f1bc1..55fe9af1102aa813ab5faaf6200c33491fabe7fe 100644 (file)
@@ -1565,7 +1565,7 @@ 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,
+                       experimental_invoice_request_tlv_stream, experimental_invoice_tlv_stream,
                ) = invoice.as_tlv_stream();
                invoice_request_tlv_stream.amount = Some(2000);
                invoice_tlv_stream.amount = Some(2000);
@@ -1574,6 +1574,7 @@ mod tests {
                        (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,
+                       experimental_invoice_tlv_stream,
                );
                let mut bytes = Vec::new();
                (&tlv_stream, &experimental_tlv_stream).write(&mut bytes).unwrap();
@@ -1594,7 +1595,7 @@ 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,
+                       experimental_invoice_request_tlv_stream, experimental_invoice_tlv_stream,
                ) = invoice.as_tlv_stream();
                let metadata = payer_tlv_stream.metadata.unwrap().iter().copied().rev().collect();
                payer_tlv_stream.metadata = Some(&metadata);
@@ -1603,6 +1604,7 @@ mod tests {
                        (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,
+                       experimental_invoice_tlv_stream,
                );
                let mut bytes = Vec::new();
                (&tlv_stream, &experimental_tlv_stream).write(&mut bytes).unwrap();
@@ -1652,7 +1654,7 @@ 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,
+                       experimental_invoice_request_tlv_stream, experimental_invoice_tlv_stream,
                ) = invoice.as_tlv_stream();
                invoice_request_tlv_stream.amount = Some(2000);
                invoice_tlv_stream.amount = Some(2000);
@@ -1661,6 +1663,7 @@ mod tests {
                        (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,
+                       experimental_invoice_tlv_stream,
                );
                let mut bytes = Vec::new();
                (&tlv_stream, &experimental_tlv_stream).write(&mut bytes).unwrap();
@@ -1683,7 +1686,7 @@ 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,
+                       experimental_invoice_request_tlv_stream, experimental_invoice_tlv_stream,
                ) = invoice.as_tlv_stream();
                let payer_id = pubkey(1);
                invoice_request_tlv_stream.payer_id = Some(&payer_id);
@@ -1692,6 +1695,7 @@ mod tests {
                        (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,
+                       experimental_invoice_tlv_stream,
                );
                let mut bytes = Vec::new();
                (&tlv_stream, &experimental_tlv_stream).write(&mut bytes).unwrap();
index a2ea4032c643550fa5ca9a92300bd18e5f6692bb..56ef40aab73ef5125e3a7390d5578866872aa0cc 100644 (file)
@@ -16,7 +16,8 @@ use crate::ln::features::{Bolt12InvoiceFeatures, OfferFeatures};
 use crate::ln::inbound_payment::ExpandedKey;
 use crate::ln::msgs::DecodeError;
 use crate::offers::invoice::{
-       check_invoice_signing_pubkey, construct_payment_paths, filter_fallbacks, FallbackAddress,
+       check_invoice_signing_pubkey, construct_payment_paths, filter_fallbacks,
+       ExperimentalInvoiceTlvStream, ExperimentalInvoiceTlvStreamRef, FallbackAddress,
        InvoiceTlvStream, InvoiceTlvStreamRef,
 };
 use crate::offers::invoice_macros::{invoice_accessors_common, invoice_builder_methods_common};
@@ -278,14 +279,17 @@ macro_rules! invoice_accessors_signing_pubkey {
 
 impl UnsignedStaticInvoice {
        fn new(offer_bytes: &Vec<u8>, contents: InvoiceContents) -> Self {
-               let (_, invoice_tlv_stream, _) = contents.as_tlv_stream();
+               let (_, invoice_tlv_stream, _, experimental_invoice_tlv_stream) = contents.as_tlv_stream();
 
                // Allocate enough space for the invoice, which will include:
                // - all TLV records from `offer_bytes`,
                // - all invoice-specific TLV records, and
                // - a signature TLV record once the invoice is signed.
                let mut bytes = Vec::with_capacity(
-                       offer_bytes.len() + invoice_tlv_stream.serialized_length() + SIGNATURE_TLV_RECORD_SIZE,
+                       offer_bytes.len()
+                               + invoice_tlv_stream.serialized_length()
+                               + SIGNATURE_TLV_RECORD_SIZE
+                               + experimental_invoice_tlv_stream.serialized_length(),
                );
 
                // Use the offer bytes instead of the offer TLV stream as the latter may have contained
@@ -304,13 +308,15 @@ impl UnsignedStaticInvoice {
                        remaining_bytes.len()
                                - experimental_tlv_stream
                                        .peek()
-                                       .map_or(remaining_bytes.len(), |first_record| first_record.start),
+                                       .map_or(remaining_bytes.len(), |first_record| first_record.start)
+                               + experimental_invoice_tlv_stream.serialized_length(),
                );
 
                for record in experimental_tlv_stream {
                        record.write(&mut experimental_bytes).unwrap();
                }
 
+               experimental_invoice_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));
@@ -445,9 +451,11 @@ impl InvoiceContents {
                        payment_hash: None,
                };
 
+               let experimental_invoice = ExperimentalInvoiceTlvStreamRef {};
+
                let (offer, experimental_offer) = self.offer.as_tlv_stream();
 
-               (offer, invoice, experimental_offer)
+               (offer, invoice, experimental_offer, experimental_invoice)
        }
 
        fn chain(&self) -> ChainHash {
@@ -544,8 +552,13 @@ impl TryFrom<Vec<u8>> for StaticInvoice {
        }
 }
 
-type FullInvoiceTlvStream =
-       (OfferTlvStream, InvoiceTlvStream, SignatureTlvStream, ExperimentalOfferTlvStream);
+type FullInvoiceTlvStream = (
+       OfferTlvStream,
+       InvoiceTlvStream,
+       SignatureTlvStream,
+       ExperimentalOfferTlvStream,
+       ExperimentalInvoiceTlvStream,
+);
 
 impl CursorReadable for FullInvoiceTlvStream {
        fn read<R: AsRef<[u8]>>(r: &mut io::Cursor<R>) -> Result<Self, DecodeError> {
@@ -553,15 +566,21 @@ impl CursorReadable for FullInvoiceTlvStream {
                let invoice = CursorReadable::read(r)?;
                let signature = CursorReadable::read(r)?;
                let experimental_offer = CursorReadable::read(r)?;
+               let experimental_invoice = CursorReadable::read(r)?;
 
-               Ok((offer, invoice, signature, experimental_offer))
+               Ok((offer, invoice, signature, experimental_offer, experimental_invoice))
        }
 }
 
-type PartialInvoiceTlvStream = (OfferTlvStream, InvoiceTlvStream, ExperimentalOfferTlvStream);
+type PartialInvoiceTlvStream =
+       (OfferTlvStream, InvoiceTlvStream, ExperimentalOfferTlvStream, ExperimentalInvoiceTlvStream);
 
-type PartialInvoiceTlvStreamRef<'a> =
-       (OfferTlvStreamRef<'a>, InvoiceTlvStreamRef<'a>, ExperimentalOfferTlvStreamRef);
+type PartialInvoiceTlvStreamRef<'a> = (
+       OfferTlvStreamRef<'a>,
+       InvoiceTlvStreamRef<'a>,
+       ExperimentalOfferTlvStreamRef,
+       ExperimentalInvoiceTlvStreamRef,
+);
 
 impl TryFrom<ParsedMessage<FullInvoiceTlvStream>> for StaticInvoice {
        type Error = Bolt12ParseError;
@@ -573,11 +592,13 @@ impl TryFrom<ParsedMessage<FullInvoiceTlvStream>> for StaticInvoice {
                        invoice_tlv_stream,
                        SignatureTlvStream { signature },
                        experimental_offer_tlv_stream,
+                       experimental_invoice_tlv_stream,
                ) = tlv_stream;
                let contents = InvoiceContents::try_from((
                        offer_tlv_stream,
                        invoice_tlv_stream,
                        experimental_offer_tlv_stream,
+                       experimental_invoice_tlv_stream,
                ))?;
 
                let signature = match signature {
@@ -615,6 +636,7 @@ impl TryFrom<PartialInvoiceTlvStream> for InvoiceContents {
                                amount,
                        },
                        experimental_offer_tlv_stream,
+                       ExperimentalInvoiceTlvStream {},
                ) = tlv_stream;
 
                if payment_hash.is_some() {
@@ -666,7 +688,9 @@ mod tests {
        use crate::ln::features::{Bolt12InvoiceFeatures, OfferFeatures};
        use crate::ln::inbound_payment::ExpandedKey;
        use crate::ln::msgs::DecodeError;
-       use crate::offers::invoice::{InvoiceTlvStreamRef, INVOICE_TYPES};
+       use crate::offers::invoice::{
+               ExperimentalInvoiceTlvStreamRef, InvoiceTlvStreamRef, INVOICE_TYPES,
+       };
        use crate::offers::merkle;
        use crate::offers::merkle::{SignatureTlvStreamRef, TaggedHash};
        use crate::offers::nonce::Nonce;
@@ -691,17 +715,23 @@ mod tests {
                InvoiceTlvStreamRef<'a>,
                SignatureTlvStreamRef<'a>,
                ExperimentalOfferTlvStreamRef,
+               ExperimentalInvoiceTlvStreamRef,
        );
 
        impl StaticInvoice {
                fn as_tlv_stream(&self) -> FullInvoiceTlvStreamRef {
-                       let (offer_tlv_stream, invoice_tlv_stream, experimental_offer_tlv_stream) =
-                               self.contents.as_tlv_stream();
+                       let (
+                               offer_tlv_stream,
+                               invoice_tlv_stream,
+                               experimental_offer_tlv_stream,
+                               experimental_invoice_tlv_stream,
+                       ) = self.contents.as_tlv_stream();
                        (
                                offer_tlv_stream,
                                invoice_tlv_stream,
                                SignatureTlvStreamRef { signature: Some(&self.signature) },
                                experimental_offer_tlv_stream,
+                               experimental_invoice_tlv_stream,
                        )
                }
        }
@@ -712,6 +742,7 @@ mod tests {
                        InvoiceTlvStreamRef,
                        SignatureTlvStreamRef,
                        ExperimentalOfferTlvStreamRef,
+                       ExperimentalInvoiceTlvStreamRef,
                ),
        ) -> Vec<u8> {
                let mut buffer = Vec::new();
@@ -719,6 +750,7 @@ mod tests {
                tlv_stream.1.write(&mut buffer).unwrap();
                tlv_stream.2.write(&mut buffer).unwrap();
                tlv_stream.3.write(&mut buffer).unwrap();
+               tlv_stream.4.write(&mut buffer).unwrap();
                buffer
        }
 
@@ -849,6 +881,7 @@ mod tests {
                                },
                                SignatureTlvStreamRef { signature: Some(&invoice.signature()) },
                                ExperimentalOfferTlvStreamRef { experimental_foo: None },
+                               ExperimentalInvoiceTlvStreamRef {},
                        )
                );
 
index a3b56fa849421b8a394e8e1b72b700226fd83d8d..2c88f913433ada5f5438a0acba9285a2208d6b06 100644 (file)
@@ -1488,6 +1488,30 @@ impl<A: Writeable, B: Writeable, C: Writeable, D: Writeable, E: Writeable, F: Wr
        }
 }
 
+impl<A: Readable, B: Readable, C: Readable, D: Readable, E: Readable, F: Readable, G: Readable> Readable for (A, B, C, D, E, F, G) {
+       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)?;
+               let g: G = Readable::read(r)?;
+               Ok((a, b, c, d, e, f, g))
+       }
+}
+impl<A: Writeable, B: Writeable, C: Writeable, D: Writeable, E: Writeable, F: Writeable, G: Writeable> Writeable for (A, B, C, D, E, F, G) {
+       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)?;
+               self.6.write(w)
+       }
+}
+
 impl Writeable for () {
        fn write<W: Writer>(&self, _: &mut W) -> Result<(), io::Error> {
                Ok(())