]> git.bitcoin.ninja Git - rust-lightning/commitdiff
Parse experimental offer TLV records
authorJeffrey Czyz <jkczyz@gmail.com>
Mon, 5 Aug 2024 23:51:32 +0000 (18:51 -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
offer messages. Allow this range when parsing an offer and include those
bytes in any invoice requests. Also include those bytes when computing
an OfferId and verifying that an InvoiceRequest is for a valid Offer.

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

index 0dfe49cec9f9c662be2cb8aecfb683d754669651..c8be0e3771e89c03f585e7ad43dedbf5b4f2279c 100644 (file)
@@ -124,7 +124,7 @@ 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::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, OFFER_TYPES, OfferTlvStream, OfferTlvStreamRef, Quantity};
+use crate::offers::offer::{Amount, EXPERIMENTAL_OFFER_TYPES, ExperimentalOfferTlvStream, ExperimentalOfferTlvStreamRef, OFFER_TYPES, OfferTlvStream, OfferTlvStreamRef, Quantity};
 use crate::offers::parse::{Bolt12ParseError, Bolt12SemanticError, ParsedMessage};
 use crate::offers::payer::{PAYER_METADATA_TYPE, PayerTlvStream, PayerTlvStreamRef};
 use crate::offers::refund::{IV_BYTES_WITH_METADATA as REFUND_IV_BYTES_WITH_METADATA, IV_BYTES_WITHOUT_METADATA as REFUND_IV_BYTES_WITHOUT_METADATA, Refund, RefundContents};
@@ -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,
@@ -901,13 +901,17 @@ impl Bolt12Invoice {
        }
 
        pub(crate) fn as_tlv_stream(&self) -> FullInvoiceTlvStreamRef {
-               let (payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, invoice_tlv_stream) =
-                       self.contents.as_tlv_stream();
+               let (
+                       payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, invoice_tlv_stream,
+                       experimental_offer_tlv_stream,
+               ) = self.contents.as_tlv_stream();
                let signature_tlv_stream = SignatureTlvStreamRef {
                        signature: Some(&self.signature),
                };
-               (payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, invoice_tlv_stream,
-                signature_tlv_stream)
+               (
+                       payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, invoice_tlv_stream,
+                       signature_tlv_stream, experimental_offer_tlv_stream,
+               )
        }
 
        pub(crate) fn is_for_refund_without_paths(&self) -> bool {
@@ -1145,7 +1149,7 @@ impl InvoiceContents {
 
        fn verify<T: secp256k1::Signing>(
                &self, bytes: &[u8], metadata: &Metadata, key: &ExpandedKey, iv_bytes: &[u8; IV_LEN],
-               secp_ctx: &Secp256k1<T>
+               secp_ctx: &Secp256k1<T>,
        ) -> Result<PaymentId, ()> {
                const EXPERIMENTAL_TYPES: core::ops::Range<u64> =
                        EXPERIMENTAL_OFFER_TYPES.start..EXPERIMENTAL_INVOICE_REQUEST_TYPES.end;
@@ -1168,13 +1172,13 @@ impl InvoiceContents {
        }
 
        fn as_tlv_stream(&self) -> PartialInvoiceTlvStreamRef {
-               let (payer, offer, invoice_request) = match self {
+               let (payer, offer, invoice_request, experimental_offer) = 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)
+               (payer, offer, invoice_request, invoice, experimental_offer)
        }
 }
 
@@ -1333,8 +1337,10 @@ pub(super) struct FallbackAddress {
 
 impl_writeable!(FallbackAddress, { version, program });
 
-type FullInvoiceTlvStream =
-       (PayerTlvStream, OfferTlvStream, InvoiceRequestTlvStream, InvoiceTlvStream, SignatureTlvStream);
+type FullInvoiceTlvStream =(
+       PayerTlvStream, OfferTlvStream, InvoiceRequestTlvStream, InvoiceTlvStream, SignatureTlvStream,
+       ExperimentalOfferTlvStream,
+);
 
 type FullInvoiceTlvStreamRef<'a> = (
        PayerTlvStreamRef<'a>,
@@ -1342,6 +1348,7 @@ type FullInvoiceTlvStreamRef<'a> = (
        InvoiceRequestTlvStreamRef<'a>,
        InvoiceTlvStreamRef<'a>,
        SignatureTlvStreamRef<'a>,
+       ExperimentalOfferTlvStreamRef,
 );
 
 impl CursorReadable for FullInvoiceTlvStream {
@@ -1351,19 +1358,23 @@ impl CursorReadable for FullInvoiceTlvStream {
                let invoice_request = CursorReadable::read(r)?;
                let invoice = CursorReadable::read(r)?;
                let signature = CursorReadable::read(r)?;
+               let experimental_offer = CursorReadable::read(r)?;
 
-               Ok((payer, offer, invoice_request, invoice, signature))
+               Ok((payer, offer, invoice_request, invoice, signature, experimental_offer))
        }
 }
 
-type PartialInvoiceTlvStream =
-       (PayerTlvStream, OfferTlvStream, InvoiceRequestTlvStream, InvoiceTlvStream);
+type PartialInvoiceTlvStream = (
+       PayerTlvStream, OfferTlvStream, InvoiceRequestTlvStream, InvoiceTlvStream,
+       ExperimentalOfferTlvStream,
+);
 
 type PartialInvoiceTlvStreamRef<'a> = (
        PayerTlvStreamRef<'a>,
        OfferTlvStreamRef<'a>,
        InvoiceRequestTlvStreamRef<'a>,
        InvoiceTlvStreamRef<'a>,
+       ExperimentalOfferTlvStreamRef,
 );
 
 impl CursorReadable for PartialInvoiceTlvStream {
@@ -1372,8 +1383,9 @@ impl CursorReadable for PartialInvoiceTlvStream {
                let offer = CursorReadable::read(r)?;
                let invoice_request = CursorReadable::read(r)?;
                let invoice = CursorReadable::read(r)?;
+               let experimental_offer = CursorReadable::read(r)?;
 
-               Ok((payer, offer, invoice_request, invoice))
+               Ok((payer, offer, invoice_request, invoice, experimental_offer))
        }
 }
 
@@ -1385,9 +1397,13 @@ impl TryFrom<ParsedMessage<FullInvoiceTlvStream>> for Bolt12Invoice {
                let (
                        payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, invoice_tlv_stream,
                        SignatureTlvStream { signature },
+                       experimental_offer_tlv_stream,
                ) = tlv_stream;
                let contents = InvoiceContents::try_from(
-                       (payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, invoice_tlv_stream)
+                       (
+                               payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, invoice_tlv_stream,
+                               experimental_offer_tlv_stream,
+                       )
                )?;
 
                let signature = signature.ok_or(
@@ -1413,6 +1429,7 @@ impl TryFrom<PartialInvoiceTlvStream> for InvoiceContents {
                                paths, blindedpay, created_at, relative_expiry, payment_hash, amount, fallbacks,
                                features, node_id, message_paths,
                        },
+                       experimental_offer_tlv_stream,
                ) = tlv_stream;
 
                if message_paths.is_some() { return Err(Bolt12SemanticError::UnexpectedPaths) }
@@ -1445,12 +1462,18 @@ impl TryFrom<PartialInvoiceTlvStream> for InvoiceContents {
 
                if offer_tlv_stream.issuer_id.is_none() && offer_tlv_stream.paths.is_none() {
                        let refund = RefundContents::try_from(
-                               (payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream)
+                               (
+                                       payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream,
+                                       experimental_offer_tlv_stream,
+                               )
                        )?;
                        Ok(InvoiceContents::ForRefund { refund, fields })
                } else {
                        let invoice_request = InvoiceRequestContents::try_from(
-                               (payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream)
+                               (
+                                       payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream,
+                                       experimental_offer_tlv_stream,
+                               )
                        )?;
                        Ok(InvoiceContents::ForOffer { invoice_request, fields })
                }
@@ -1525,7 +1548,7 @@ mod tests {
        use crate::offers::invoice_request::InvoiceRequestTlvStreamRef;
        use crate::offers::merkle::{SignError, SignatureTlvStreamRef, TaggedHash, self};
        use crate::offers::nonce::Nonce;
-       use crate::offers::offer::{Amount, OfferTlvStreamRef, Quantity};
+       use crate::offers::offer::{Amount, ExperimentalOfferTlvStreamRef, OfferTlvStreamRef, Quantity};
        use crate::prelude::*;
        #[cfg(not(c_bindings))]
        use {
@@ -1693,6 +1716,7 @@ mod tests {
                                        message_paths: None,
                                },
                                SignatureTlvStreamRef { signature: Some(&invoice.signature()) },
+                               ExperimentalOfferTlvStreamRef {},
                        ),
                );
 
@@ -1786,6 +1810,7 @@ mod tests {
                                        message_paths: None,
                                },
                                SignatureTlvStreamRef { signature: Some(&invoice.signature()) },
+                               ExperimentalOfferTlvStreamRef {},
                        ),
                );
 
@@ -1979,7 +2004,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);
@@ -1995,7 +2020,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));
@@ -2014,7 +2039,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));
        }
@@ -2032,7 +2057,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));
 
@@ -2070,7 +2095,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![
@@ -2113,7 +2138,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 5ec4ab8a7a297a352ca48d4258be408a6b2c8d1f..231c582d599c506ccb6795479d965cdcd935902e 100644 (file)
@@ -71,7 +71,7 @@ use crate::ln::inbound_payment::{ExpandedKey, IV_LEN};
 use crate::ln::msgs::DecodeError;
 use crate::offers::merkle::{SignError, SignFn, SignatureTlvStream, SignatureTlvStreamRef, TaggedHash, TlvStream, self, SIGNATURE_TLV_RECORD_SIZE};
 use crate::offers::nonce::Nonce;
-use crate::offers::offer::{EXPERIMENTAL_OFFER_TYPES, OFFER_TYPES, Offer, OfferContents, OfferId, OfferTlvStream, OfferTlvStreamRef};
+use crate::offers::offer::{EXPERIMENTAL_OFFER_TYPES, ExperimentalOfferTlvStream, ExperimentalOfferTlvStreamRef, OFFER_TYPES, Offer, OfferContents, OfferId, OfferTlvStream, OfferTlvStreamRef};
 use crate::offers::parse::{Bolt12ParseError, ParsedMessage, Bolt12SemanticError};
 use crate::offers::payer::{PayerContents, PayerTlvStream, PayerTlvStreamRef};
 use crate::offers::signer::{Metadata, MetadataMaterial};
@@ -523,6 +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,
                ) = contents.as_tlv_stream();
 
                // Allocate enough space for the invoice_request, which will include:
@@ -907,12 +908,17 @@ impl InvoiceRequest {
        }
 
        pub(crate) fn as_tlv_stream(&self) -> FullInvoiceRequestTlvStreamRef {
-               let (payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream) =
-                       self.contents.as_tlv_stream();
+               let (
+                       payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream,
+                       experimental_offer_tlv_stream,
+               ) = self.contents.as_tlv_stream();
                let signature_tlv_stream = SignatureTlvStreamRef {
                        signature: Some(&self.signature),
                };
-               (payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, signature_tlv_stream)
+               (
+                       payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream,
+                       signature_tlv_stream, experimental_offer_tlv_stream,
+               )
        }
 }
 
@@ -1028,9 +1034,9 @@ impl InvoiceRequestContents {
        }
 
        pub(super) fn as_tlv_stream(&self) -> PartialInvoiceRequestTlvStreamRef {
-               let (payer, offer, mut invoice_request) = self.inner.as_tlv_stream();
+               let (payer, offer, mut invoice_request, experimental_offer) = self.inner.as_tlv_stream();
                invoice_request.payer_id = Some(&self.payer_signing_pubkey);
-               (payer, offer, invoice_request)
+               (payer, offer, invoice_request, experimental_offer)
        }
 }
 
@@ -1048,7 +1054,7 @@ impl InvoiceRequestContentsWithoutPayerSigningPubkey {
                        metadata: self.payer.0.as_bytes(),
                };
 
-               let offer = self.offer.as_tlv_stream();
+               let (offer, experimental_offer) = self.offer.as_tlv_stream();
 
                let features = {
                        if self.features == InvoiceRequestFeatures::empty() { None }
@@ -1065,7 +1071,7 @@ impl InvoiceRequestContentsWithoutPayerSigningPubkey {
                        paths: None,
                };
 
-               (payer, offer, invoice_request)
+               (payer, offer, invoice_request, experimental_offer)
        }
 }
 
@@ -1120,14 +1126,17 @@ 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;
 
-type FullInvoiceRequestTlvStream =
-       (PayerTlvStream, OfferTlvStream, InvoiceRequestTlvStream, SignatureTlvStream);
+type FullInvoiceRequestTlvStream = (
+       PayerTlvStream, OfferTlvStream, InvoiceRequestTlvStream, SignatureTlvStream,
+       ExperimentalOfferTlvStream,
+);
 
 type FullInvoiceRequestTlvStreamRef<'a> = (
        PayerTlvStreamRef<'a>,
        OfferTlvStreamRef<'a>,
        InvoiceRequestTlvStreamRef<'a>,
        SignatureTlvStreamRef<'a>,
+       ExperimentalOfferTlvStreamRef,
 );
 
 impl CursorReadable for FullInvoiceRequestTlvStream {
@@ -1136,17 +1145,21 @@ impl CursorReadable for FullInvoiceRequestTlvStream {
                let offer = CursorReadable::read(r)?;
                let invoice_request = CursorReadable::read(r)?;
                let signature = CursorReadable::read(r)?;
+               let experimental_offer = CursorReadable::read(r)?;
 
-               Ok((payer, offer, invoice_request, signature))
+               Ok((payer, offer, invoice_request, signature, experimental_offer))
        }
 }
 
-type PartialInvoiceRequestTlvStream = (PayerTlvStream, OfferTlvStream, InvoiceRequestTlvStream);
+type PartialInvoiceRequestTlvStream = (
+       PayerTlvStream, OfferTlvStream, InvoiceRequestTlvStream, ExperimentalOfferTlvStream,
+);
 
 type PartialInvoiceRequestTlvStreamRef<'a> = (
        PayerTlvStreamRef<'a>,
        OfferTlvStreamRef<'a>,
        InvoiceRequestTlvStreamRef<'a>,
+       ExperimentalOfferTlvStreamRef,
 );
 
 impl TryFrom<Vec<u8>> for UnsignedInvoiceRequest {
@@ -1155,13 +1168,8 @@ impl TryFrom<Vec<u8>> for UnsignedInvoiceRequest {
        fn try_from(bytes: Vec<u8>) -> Result<Self, Self::Error> {
                let invoice_request = ParsedMessage::<PartialInvoiceRequestTlvStream>::try_from(bytes)?;
                let ParsedMessage { mut bytes, tlv_stream } = invoice_request;
-               let (
-                       payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream,
-               ) = tlv_stream;
-               let contents = InvoiceRequestContents::try_from(
-                       (payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream)
-               )?;
 
+               let contents = InvoiceRequestContents::try_from(tlv_stream)?;
                let tagged_hash = TaggedHash::from_valid_tlv_stream_bytes(SIGNATURE_TAG, &bytes);
 
                let offset = TlvStream::new(&bytes)
@@ -1183,9 +1191,13 @@ impl TryFrom<Vec<u8>> for InvoiceRequest {
                let (
                        payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream,
                        SignatureTlvStream { signature },
+                       experimental_offer_tlv_stream,
                ) = tlv_stream;
                let contents = InvoiceRequestContents::try_from(
-                       (payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream)
+                       (
+                               payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream,
+                               experimental_offer_tlv_stream,
+                       )
                )?;
 
                let signature = match signature {
@@ -1209,13 +1221,14 @@ impl TryFrom<PartialInvoiceRequestTlvStream> for InvoiceRequestContents {
                        InvoiceRequestTlvStream {
                                chain, amount, features, quantity, payer_id, payer_note, paths,
                        },
+                       experimental_offer_tlv_stream,
                ) = tlv_stream;
 
                let payer = match metadata {
                        None => return Err(Bolt12SemanticError::MissingPayerMetadata),
                        Some(metadata) => PayerContents(Metadata::Bytes(metadata)),
                };
-               let offer = OfferContents::try_from(offer_tlv_stream)?;
+               let offer = OfferContents::try_from((offer_tlv_stream, experimental_offer_tlv_stream))?;
 
                if !offer.supports_chain(chain.unwrap_or_else(|| offer.implied_chain())) {
                        return Err(Bolt12SemanticError::UnsupportedChain);
@@ -1312,7 +1325,7 @@ mod tests {
        use crate::offers::invoice::{Bolt12Invoice, SIGNATURE_TAG as INVOICE_SIGNATURE_TAG};
        use crate::offers::merkle::{SignError, SignatureTlvStreamRef, TaggedHash, self};
        use crate::offers::nonce::Nonce;
-       use crate::offers::offer::{Amount, OfferTlvStreamRef, Quantity};
+       use crate::offers::offer::{Amount, ExperimentalOfferTlvStreamRef, OfferTlvStreamRef, Quantity};
        #[cfg(not(c_bindings))]
        use {
                crate::offers::offer::OfferBuilder,
@@ -1421,6 +1434,7 @@ mod tests {
                                        paths: None,
                                },
                                SignatureTlvStreamRef { signature: Some(&invoice_request.signature()) },
+                               ExperimentalOfferTlvStreamRef {},
                        ),
                );
 
@@ -1491,7 +1505,7 @@ mod tests {
                // Fails verification with altered fields
                let (
                        payer_tlv_stream, offer_tlv_stream, mut invoice_request_tlv_stream,
-                       mut invoice_tlv_stream, mut signature_tlv_stream
+                       mut invoice_tlv_stream, mut signature_tlv_stream, experimental_offer_tlv_stream,
                ) = invoice.as_tlv_stream();
                invoice_request_tlv_stream.amount = Some(2000);
                invoice_tlv_stream.amount = Some(2000);
@@ -1500,13 +1514,16 @@ mod tests {
                        (payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, invoice_tlv_stream);
                let mut bytes = Vec::new();
                tlv_stream.write(&mut bytes).unwrap();
+               experimental_offer_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 = bytes;
+               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();
 
                let invoice = Bolt12Invoice::try_from(encoded_invoice).unwrap();
                assert!(invoice.verify_using_metadata(&expanded_key, &secp_ctx).is_err());
@@ -1514,7 +1531,7 @@ mod tests {
                // Fails verification with altered metadata
                let (
                        mut payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, invoice_tlv_stream,
-                       mut signature_tlv_stream
+                       mut signature_tlv_stream, experimental_offer_tlv_stream,
                ) = invoice.as_tlv_stream();
                let metadata = payer_tlv_stream.metadata.unwrap().iter().copied().rev().collect();
                payer_tlv_stream.metadata = Some(&metadata);
@@ -1523,13 +1540,16 @@ mod tests {
                        (payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, invoice_tlv_stream);
                let mut bytes = Vec::new();
                tlv_stream.write(&mut bytes).unwrap();
+               experimental_offer_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 = bytes;
+               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();
 
                let invoice = Bolt12Invoice::try_from(encoded_invoice).unwrap();
                assert!(invoice.verify_using_metadata(&expanded_key, &secp_ctx).is_err());
@@ -1564,7 +1584,7 @@ mod tests {
                // Fails verification with altered fields
                let (
                        payer_tlv_stream, offer_tlv_stream, mut invoice_request_tlv_stream,
-                       mut invoice_tlv_stream, mut signature_tlv_stream
+                       mut invoice_tlv_stream, mut signature_tlv_stream, experimental_offer_tlv_stream,
                ) = invoice.as_tlv_stream();
                invoice_request_tlv_stream.amount = Some(2000);
                invoice_tlv_stream.amount = Some(2000);
@@ -1573,13 +1593,16 @@ mod tests {
                        (payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, invoice_tlv_stream);
                let mut bytes = Vec::new();
                tlv_stream.write(&mut bytes).unwrap();
+               experimental_offer_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 = bytes;
+               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();
 
                let invoice = Bolt12Invoice::try_from(encoded_invoice).unwrap();
                assert!(
@@ -1589,7 +1612,7 @@ mod tests {
                // Fails verification with altered payer id
                let (
                        payer_tlv_stream, offer_tlv_stream, mut invoice_request_tlv_stream, invoice_tlv_stream,
-                       mut signature_tlv_stream
+                       mut signature_tlv_stream, experimental_offer_tlv_stream,
                ) = invoice.as_tlv_stream();
                let payer_id = pubkey(1);
                invoice_request_tlv_stream.payer_id = Some(&payer_id);
@@ -1598,13 +1621,16 @@ mod tests {
                        (payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, invoice_tlv_stream);
                let mut bytes = Vec::new();
                tlv_stream.write(&mut bytes).unwrap();
+               experimental_offer_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 = bytes;
+               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();
 
                let invoice = Bolt12Invoice::try_from(encoded_invoice).unwrap();
                assert!(
@@ -1624,7 +1650,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);
 
@@ -1636,7 +1662,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));
 
@@ -1649,7 +1675,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);
 
@@ -1663,7 +1689,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));
 
@@ -1699,7 +1725,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));
 
@@ -1711,7 +1737,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));
 
@@ -1722,7 +1748,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));
 
@@ -1802,7 +1828,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()));
 
@@ -1814,7 +1840,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);
        }
@@ -1831,7 +1857,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);
 
@@ -1856,7 +1882,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));
 
@@ -1881,7 +1907,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));
 
@@ -1917,7 +1943,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")));
 
@@ -1929,7 +1955,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 81078726591c6522b483feab939a92bf2d541e44..1c7eac6167e63f302e917c8dd8d5e8c20b8c9065 100644 (file)
@@ -94,7 +94,7 @@ use crate::offers::merkle::{TaggedHash, TlvRecord, TlvStream};
 use crate::offers::nonce::Nonce;
 use crate::offers::parse::{Bech32Encode, Bolt12ParseError, Bolt12SemanticError, ParsedMessage};
 use crate::offers::signer::{Metadata, MetadataMaterial, self};
-use crate::util::ser::{HighZeroBytesDroppedBigSize, Readable, WithoutLength, Writeable, Writer};
+use crate::util::ser::{CursorReadable, HighZeroBytesDroppedBigSize, Readable, WithoutLength, Writeable, Writer};
 use crate::util::string::PrintableString;
 
 #[cfg(not(c_bindings))]
@@ -400,10 +400,10 @@ macro_rules! offer_builder_methods { (
                                };
 
                                let mut tlv_stream = $self.offer.as_tlv_stream();
-                               debug_assert_eq!(tlv_stream.metadata, None);
-                               tlv_stream.metadata = None;
+                               debug_assert_eq!(tlv_stream.0.metadata, None);
+                               tlv_stream.0.metadata = None;
                                if metadata.derives_recipient_keys() {
-                                       tlv_stream.issuer_id = None;
+                                       tlv_stream.0.issuer_id = None;
                                }
 
                                // Either replace the signing pubkey with the derived pubkey or include the metadata
@@ -691,6 +691,7 @@ impl Offer {
                bytes: &'a [u8]
        ) -> impl core::iter::Iterator<Item = TlvRecord<'a>> {
                TlvStream::new(bytes).range(OFFER_TYPES)
+                       .chain(TlvStream::new(bytes).range(EXPERIMENTAL_OFFER_TYPES))
        }
 
        #[cfg(async_payments)]
@@ -795,7 +796,7 @@ impl Offer {
 
 #[cfg(test)]
 impl Offer {
-       pub(super) fn as_tlv_stream(&self) -> OfferTlvStreamRef {
+       pub(super) fn as_tlv_stream(&self) -> FullOfferTlvStreamRef {
                self.contents.as_tlv_stream()
        }
 }
@@ -982,7 +983,7 @@ impl OfferContents {
                }
        }
 
-       pub(super) fn as_tlv_stream(&self) -> OfferTlvStreamRef {
+       pub(super) fn as_tlv_stream(&self) -> FullOfferTlvStreamRef {
                let (currency, amount) = match &self.amount {
                        None => (None, None),
                        Some(Amount::Bitcoin { amount_msats }) => (None, Some(*amount_msats)),
@@ -995,7 +996,7 @@ impl OfferContents {
                        if self.features == OfferFeatures::empty() { None } else { Some(&self.features) }
                };
 
-               OfferTlvStreamRef {
+               let offer = OfferTlvStreamRef {
                        chains: self.chains.as_ref(),
                        metadata: self.metadata(),
                        currency,
@@ -1007,7 +1008,11 @@ impl OfferContents {
                        issuer: self.issuer.as_ref(),
                        quantity_max: self.supported_quantity.to_tlv_record(),
                        issuer_id: self.issuer_signing_pubkey.as_ref(),
-               }
+               };
+
+               let experimental_offer = ExperimentalOfferTlvStreamRef {};
+
+               (offer, experimental_offer)
        }
 }
 
@@ -1102,6 +1107,22 @@ tlv_stream!(OfferTlvStream, OfferTlvStreamRef<'a>, OFFER_TYPES, {
 /// Valid type range for experimental offer TLV records.
 pub(super) const EXPERIMENTAL_OFFER_TYPES: core::ops::Range<u64> = 1_000_000_000..2_000_000_000;
 
+tlv_stream!(ExperimentalOfferTlvStream, ExperimentalOfferTlvStreamRef, EXPERIMENTAL_OFFER_TYPES, {
+});
+
+type FullOfferTlvStream = (OfferTlvStream, ExperimentalOfferTlvStream);
+
+type FullOfferTlvStreamRef<'a> = (OfferTlvStreamRef<'a>, ExperimentalOfferTlvStreamRef);
+
+impl CursorReadable for FullOfferTlvStream {
+       fn read<R: AsRef<[u8]>>(r: &mut io::Cursor<R>) -> Result<Self, DecodeError> {
+               let offer = CursorReadable::read(r)?;
+               let experimental_offer = CursorReadable::read(r)?;
+
+               Ok((offer, experimental_offer))
+       }
+}
+
 impl Bech32Encode for Offer {
        const BECH32_HRP: &'static str = "lno";
 }
@@ -1118,7 +1139,7 @@ impl TryFrom<Vec<u8>> for Offer {
        type Error = Bolt12ParseError;
 
        fn try_from(bytes: Vec<u8>) -> Result<Self, Self::Error> {
-               let offer = ParsedMessage::<OfferTlvStream>::try_from(bytes)?;
+               let offer = ParsedMessage::<FullOfferTlvStream>::try_from(bytes)?;
                let ParsedMessage { bytes, tlv_stream } = offer;
                let contents = OfferContents::try_from(tlv_stream)?;
                let id = OfferId::from_valid_offer_tlv_stream(&bytes);
@@ -1127,14 +1148,17 @@ impl TryFrom<Vec<u8>> for Offer {
        }
 }
 
-impl TryFrom<OfferTlvStream> for OfferContents {
+impl TryFrom<FullOfferTlvStream> for OfferContents {
        type Error = Bolt12SemanticError;
 
-       fn try_from(tlv_stream: OfferTlvStream) -> Result<Self, Self::Error> {
-               let OfferTlvStream {
-                       chains, metadata, currency, amount, description, features, absolute_expiry, paths,
-                       issuer, quantity_max, issuer_id,
-               } = tlv_stream;
+       fn try_from(tlv_stream: FullOfferTlvStream) -> Result<Self, Self::Error> {
+               let (
+                       OfferTlvStream {
+                               chains, metadata, currency, amount, description, features, absolute_expiry, paths,
+                               issuer, quantity_max, issuer_id,
+                       },
+                       ExperimentalOfferTlvStream {},
+               ) = tlv_stream;
 
                let metadata = metadata.map(|metadata| Metadata::Bytes(metadata));
 
@@ -1184,7 +1208,7 @@ impl core::fmt::Display for Offer {
 
 #[cfg(test)]
 mod tests {
-       use super::{Amount, OFFER_TYPES, Offer, OfferTlvStreamRef, Quantity};
+       use super::{Amount, ExperimentalOfferTlvStreamRef, OFFER_TYPES, Offer, OfferTlvStreamRef, Quantity};
        #[cfg(not(c_bindings))]
        use {
                super::OfferBuilder,
@@ -1236,19 +1260,22 @@ mod tests {
 
                assert_eq!(
                        offer.as_tlv_stream(),
-                       OfferTlvStreamRef {
-                               chains: None,
-                               metadata: None,
-                               currency: None,
-                               amount: None,
-                               description: None,
-                               features: None,
-                               absolute_expiry: None,
-                               paths: None,
-                               issuer: None,
-                               quantity_max: None,
-                               issuer_id: Some(&pubkey(42)),
-                       },
+                       (
+                               OfferTlvStreamRef {
+                                       chains: None,
+                                       metadata: None,
+                                       currency: None,
+                                       amount: None,
+                                       description: None,
+                                       features: None,
+                                       absolute_expiry: None,
+                                       paths: None,
+                                       issuer: None,
+                                       quantity_max: None,
+                                       issuer_id: Some(&pubkey(42)),
+                               },
+                               ExperimentalOfferTlvStreamRef {},
+                       ),
                );
 
                if let Err(e) = Offer::try_from(buffer) {
@@ -1267,7 +1294,7 @@ mod tests {
                        .unwrap();
                assert!(offer.supports_chain(mainnet));
                assert_eq!(offer.chains(), vec![mainnet]);
-               assert_eq!(offer.as_tlv_stream().chains, None);
+               assert_eq!(offer.as_tlv_stream().0.chains, None);
 
                let offer = OfferBuilder::new(pubkey(42))
                        .chain(Network::Testnet)
@@ -1275,7 +1302,7 @@ mod tests {
                        .unwrap();
                assert!(offer.supports_chain(testnet));
                assert_eq!(offer.chains(), vec![testnet]);
-               assert_eq!(offer.as_tlv_stream().chains, Some(&vec![testnet]));
+               assert_eq!(offer.as_tlv_stream().0.chains, Some(&vec![testnet]));
 
                let offer = OfferBuilder::new(pubkey(42))
                        .chain(Network::Testnet)
@@ -1284,7 +1311,7 @@ mod tests {
                        .unwrap();
                assert!(offer.supports_chain(testnet));
                assert_eq!(offer.chains(), vec![testnet]);
-               assert_eq!(offer.as_tlv_stream().chains, Some(&vec![testnet]));
+               assert_eq!(offer.as_tlv_stream().0.chains, Some(&vec![testnet]));
 
                let offer = OfferBuilder::new(pubkey(42))
                        .chain(Network::Bitcoin)
@@ -1294,7 +1321,7 @@ mod tests {
                assert!(offer.supports_chain(mainnet));
                assert!(offer.supports_chain(testnet));
                assert_eq!(offer.chains(), vec![mainnet, testnet]);
-               assert_eq!(offer.as_tlv_stream().chains, Some(&vec![mainnet, testnet]));
+               assert_eq!(offer.as_tlv_stream().0.chains, Some(&vec![mainnet, testnet]));
        }
 
        #[test]
@@ -1304,7 +1331,7 @@ mod tests {
                        .build()
                        .unwrap();
                assert_eq!(offer.metadata(), Some(&vec![42; 32]));
-               assert_eq!(offer.as_tlv_stream().metadata, Some(&vec![42; 32]));
+               assert_eq!(offer.as_tlv_stream().0.metadata, Some(&vec![42; 32]));
 
                let offer = OfferBuilder::new(pubkey(42))
                        .metadata(vec![42; 32]).unwrap()
@@ -1312,7 +1339,7 @@ mod tests {
                        .build()
                        .unwrap();
                assert_eq!(offer.metadata(), Some(&vec![43; 32]));
-               assert_eq!(offer.as_tlv_stream().metadata, Some(&vec![43; 32]));
+               assert_eq!(offer.as_tlv_stream().0.metadata, Some(&vec![43; 32]));
        }
 
        #[test]
@@ -1349,7 +1376,7 @@ mod tests {
 
                // Fails verification with altered offer field
                let mut tlv_stream = offer.as_tlv_stream();
-               tlv_stream.amount = Some(100);
+               tlv_stream.0.amount = Some(100);
 
                let mut encoded_offer = Vec::new();
                tlv_stream.write(&mut encoded_offer).unwrap();
@@ -1362,8 +1389,8 @@ mod tests {
 
                // Fails verification with altered metadata
                let mut tlv_stream = offer.as_tlv_stream();
-               let metadata = tlv_stream.metadata.unwrap().iter().copied().rev().collect();
-               tlv_stream.metadata = Some(&metadata);
+               let metadata = tlv_stream.0.metadata.unwrap().iter().copied().rev().collect();
+               tlv_stream.0.metadata = Some(&metadata);
 
                let mut encoded_offer = Vec::new();
                tlv_stream.write(&mut encoded_offer).unwrap();
@@ -1416,7 +1443,7 @@ mod tests {
 
                // Fails verification with altered offer field
                let mut tlv_stream = offer.as_tlv_stream();
-               tlv_stream.amount = Some(100);
+               tlv_stream.0.amount = Some(100);
 
                let mut encoded_offer = Vec::new();
                tlv_stream.write(&mut encoded_offer).unwrap();
@@ -1432,7 +1459,7 @@ mod tests {
                // Fails verification with altered signing pubkey
                let mut tlv_stream = offer.as_tlv_stream();
                let issuer_id = pubkey(1);
-               tlv_stream.issuer_id = Some(&issuer_id);
+               tlv_stream.0.issuer_id = Some(&issuer_id);
 
                let mut encoded_offer = Vec::new();
                tlv_stream.write(&mut encoded_offer).unwrap();
@@ -1457,8 +1484,8 @@ mod tests {
                        .unwrap();
                let tlv_stream = offer.as_tlv_stream();
                assert_eq!(offer.amount(), Some(bitcoin_amount));
-               assert_eq!(tlv_stream.amount, Some(1000));
-               assert_eq!(tlv_stream.currency, None);
+               assert_eq!(tlv_stream.0.amount, Some(1000));
+               assert_eq!(tlv_stream.0.currency, None);
 
                #[cfg(not(c_bindings))]
                let builder = OfferBuilder::new(pubkey(42))
@@ -1469,8 +1496,8 @@ mod tests {
                builder.amount(currency_amount.clone());
                let tlv_stream = builder.offer.as_tlv_stream();
                assert_eq!(builder.offer.amount, Some(currency_amount.clone()));
-               assert_eq!(tlv_stream.amount, Some(10));
-               assert_eq!(tlv_stream.currency, Some(b"USD"));
+               assert_eq!(tlv_stream.0.amount, Some(10));
+               assert_eq!(tlv_stream.0.currency, Some(b"USD"));
                match builder.build() {
                        Ok(_) => panic!("expected error"),
                        Err(e) => assert_eq!(e, Bolt12SemanticError::UnsupportedCurrency),
@@ -1482,8 +1509,8 @@ mod tests {
                        .build()
                        .unwrap();
                let tlv_stream = offer.as_tlv_stream();
-               assert_eq!(tlv_stream.amount, Some(1000));
-               assert_eq!(tlv_stream.currency, None);
+               assert_eq!(tlv_stream.0.amount, Some(1000));
+               assert_eq!(tlv_stream.0.currency, None);
 
                let invalid_amount = Amount::Bitcoin { amount_msats: MAX_VALUE_MSAT + 1 };
                match OfferBuilder::new(pubkey(42)).amount(invalid_amount).build() {
@@ -1499,7 +1526,7 @@ mod tests {
                        .build()
                        .unwrap();
                assert_eq!(offer.description(), Some(PrintableString("foo")));
-               assert_eq!(offer.as_tlv_stream().description, Some(&String::from("foo")));
+               assert_eq!(offer.as_tlv_stream().0.description, Some(&String::from("foo")));
 
                let offer = OfferBuilder::new(pubkey(42))
                        .description("foo".into())
@@ -1507,14 +1534,14 @@ mod tests {
                        .build()
                        .unwrap();
                assert_eq!(offer.description(), Some(PrintableString("bar")));
-               assert_eq!(offer.as_tlv_stream().description, Some(&String::from("bar")));
+               assert_eq!(offer.as_tlv_stream().0.description, Some(&String::from("bar")));
 
                let offer = OfferBuilder::new(pubkey(42))
                        .amount_msats(1000)
                        .build()
                        .unwrap();
                assert_eq!(offer.description(), Some(PrintableString("")));
-               assert_eq!(offer.as_tlv_stream().description, Some(&String::from("")));
+               assert_eq!(offer.as_tlv_stream().0.description, Some(&String::from("")));
        }
 
        #[test]
@@ -1524,7 +1551,7 @@ mod tests {
                        .build()
                        .unwrap();
                assert_eq!(offer.offer_features(), &OfferFeatures::unknown());
-               assert_eq!(offer.as_tlv_stream().features, Some(&OfferFeatures::unknown()));
+               assert_eq!(offer.as_tlv_stream().0.features, Some(&OfferFeatures::unknown()));
 
                let offer = OfferBuilder::new(pubkey(42))
                        .features_unchecked(OfferFeatures::unknown())
@@ -1532,7 +1559,7 @@ mod tests {
                        .build()
                        .unwrap();
                assert_eq!(offer.offer_features(), &OfferFeatures::empty());
-               assert_eq!(offer.as_tlv_stream().features, None);
+               assert_eq!(offer.as_tlv_stream().0.features, None);
        }
 
        #[test]
@@ -1549,7 +1576,7 @@ mod tests {
                assert!(!offer.is_expired());
                assert!(!offer.is_expired_no_std(now));
                assert_eq!(offer.absolute_expiry(), Some(future_expiry));
-               assert_eq!(offer.as_tlv_stream().absolute_expiry, Some(future_expiry.as_secs()));
+               assert_eq!(offer.as_tlv_stream().0.absolute_expiry, Some(future_expiry.as_secs()));
 
                let offer = OfferBuilder::new(pubkey(42))
                        .absolute_expiry(future_expiry)
@@ -1560,7 +1587,7 @@ mod tests {
                assert!(offer.is_expired());
                assert!(offer.is_expired_no_std(now));
                assert_eq!(offer.absolute_expiry(), Some(past_expiry));
-               assert_eq!(offer.as_tlv_stream().absolute_expiry, Some(past_expiry.as_secs()));
+               assert_eq!(offer.as_tlv_stream().0.absolute_expiry, Some(past_expiry.as_secs()));
        }
 
        #[test]
@@ -1591,8 +1618,8 @@ mod tests {
                assert_eq!(offer.paths(), paths.as_slice());
                assert_eq!(offer.issuer_signing_pubkey(), Some(pubkey(42)));
                assert_ne!(pubkey(42), pubkey(44));
-               assert_eq!(tlv_stream.paths, Some(&paths));
-               assert_eq!(tlv_stream.issuer_id, Some(&pubkey(42)));
+               assert_eq!(tlv_stream.0.paths, Some(&paths));
+               assert_eq!(tlv_stream.0.issuer_id, Some(&pubkey(42)));
        }
 
        #[test]
@@ -1602,7 +1629,7 @@ mod tests {
                        .build()
                        .unwrap();
                assert_eq!(offer.issuer(), Some(PrintableString("foo")));
-               assert_eq!(offer.as_tlv_stream().issuer, Some(&String::from("foo")));
+               assert_eq!(offer.as_tlv_stream().0.issuer, Some(&String::from("foo")));
 
                let offer = OfferBuilder::new(pubkey(42))
                        .issuer("foo".into())
@@ -1610,7 +1637,7 @@ mod tests {
                        .build()
                        .unwrap();
                assert_eq!(offer.issuer(), Some(PrintableString("bar")));
-               assert_eq!(offer.as_tlv_stream().issuer, Some(&String::from("bar")));
+               assert_eq!(offer.as_tlv_stream().0.issuer, Some(&String::from("bar")));
        }
 
        #[test]
@@ -1625,7 +1652,7 @@ mod tests {
                let tlv_stream = offer.as_tlv_stream();
                assert!(!offer.expects_quantity());
                assert_eq!(offer.supported_quantity(), Quantity::One);
-               assert_eq!(tlv_stream.quantity_max, None);
+               assert_eq!(tlv_stream.0.quantity_max, None);
 
                let offer = OfferBuilder::new(pubkey(42))
                        .supported_quantity(Quantity::Unbounded)
@@ -1634,7 +1661,7 @@ mod tests {
                let tlv_stream = offer.as_tlv_stream();
                assert!(offer.expects_quantity());
                assert_eq!(offer.supported_quantity(), Quantity::Unbounded);
-               assert_eq!(tlv_stream.quantity_max, Some(0));
+               assert_eq!(tlv_stream.0.quantity_max, Some(0));
 
                let offer = OfferBuilder::new(pubkey(42))
                        .supported_quantity(Quantity::Bounded(ten))
@@ -1643,7 +1670,7 @@ mod tests {
                let tlv_stream = offer.as_tlv_stream();
                assert!(offer.expects_quantity());
                assert_eq!(offer.supported_quantity(), Quantity::Bounded(ten));
-               assert_eq!(tlv_stream.quantity_max, Some(10));
+               assert_eq!(tlv_stream.0.quantity_max, Some(10));
 
                let offer = OfferBuilder::new(pubkey(42))
                        .supported_quantity(Quantity::Bounded(one))
@@ -1652,7 +1679,7 @@ mod tests {
                let tlv_stream = offer.as_tlv_stream();
                assert!(offer.expects_quantity());
                assert_eq!(offer.supported_quantity(), Quantity::Bounded(one));
-               assert_eq!(tlv_stream.quantity_max, Some(1));
+               assert_eq!(tlv_stream.0.quantity_max, Some(1));
 
                let offer = OfferBuilder::new(pubkey(42))
                        .supported_quantity(Quantity::Bounded(ten))
@@ -1662,7 +1689,7 @@ mod tests {
                let tlv_stream = offer.as_tlv_stream();
                assert!(!offer.expects_quantity());
                assert_eq!(offer.supported_quantity(), Quantity::One);
-               assert_eq!(tlv_stream.quantity_max, None);
+               assert_eq!(tlv_stream.0.quantity_max, None);
        }
 
        #[test]
@@ -1700,8 +1727,8 @@ mod tests {
                }
 
                let mut tlv_stream = offer.as_tlv_stream();
-               tlv_stream.amount = Some(1000);
-               tlv_stream.currency = Some(b"USD");
+               tlv_stream.0.amount = Some(1000);
+               tlv_stream.0.currency = Some(b"USD");
 
                let mut encoded_offer = Vec::new();
                tlv_stream.write(&mut encoded_offer).unwrap();
@@ -1711,8 +1738,8 @@ mod tests {
                }
 
                let mut tlv_stream = offer.as_tlv_stream();
-               tlv_stream.amount = None;
-               tlv_stream.currency = Some(b"USD");
+               tlv_stream.0.amount = None;
+               tlv_stream.0.currency = Some(b"USD");
 
                let mut encoded_offer = Vec::new();
                tlv_stream.write(&mut encoded_offer).unwrap();
@@ -1723,8 +1750,8 @@ mod tests {
                }
 
                let mut tlv_stream = offer.as_tlv_stream();
-               tlv_stream.amount = Some(MAX_VALUE_MSAT + 1);
-               tlv_stream.currency = None;
+               tlv_stream.0.amount = Some(MAX_VALUE_MSAT + 1);
+               tlv_stream.0.currency = None;
 
                let mut encoded_offer = Vec::new();
                tlv_stream.write(&mut encoded_offer).unwrap();
@@ -1751,7 +1778,7 @@ mod tests {
                }
 
                let mut tlv_stream = offer.as_tlv_stream();
-               tlv_stream.description = None;
+               tlv_stream.0.description = None;
 
                let mut encoded_offer = Vec::new();
                tlv_stream.write(&mut encoded_offer).unwrap();
@@ -1857,7 +1884,7 @@ mod tests {
                }
 
                let mut tlv_stream = offer.as_tlv_stream();
-               tlv_stream.issuer_id = None;
+               tlv_stream.0.issuer_id = None;
 
                let mut encoded_offer = Vec::new();
                tlv_stream.write(&mut encoded_offer).unwrap();
index 860de534ce03312fa0ff56f1302f761cefc39549..8808c7c5002e5c3243907dbe163567252774bef8 100644 (file)
@@ -100,7 +100,7 @@ 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::nonce::Nonce;
-use crate::offers::offer::{OfferTlvStream, OfferTlvStreamRef};
+use crate::offers::offer::{ExperimentalOfferTlvStream, ExperimentalOfferTlvStreamRef, OfferTlvStream, OfferTlvStreamRef};
 use crate::offers::parse::{Bech32Encode, Bolt12ParseError, Bolt12SemanticError, ParsedMessage};
 use crate::offers::payer::{PayerContents, PayerTlvStream, PayerTlvStreamRef};
 use crate::offers::signer::{Metadata, MetadataMaterial, self};
@@ -770,7 +770,9 @@ impl RefundContents {
                        paths: self.paths.as_ref(),
                };
 
-               (payer, offer, invoice_request)
+               let experimental_offer = ExperimentalOfferTlvStreamRef {};
+
+               (payer, offer, invoice_request, experimental_offer)
        }
 }
 
@@ -793,12 +795,15 @@ impl Writeable for RefundContents {
        }
 }
 
-type RefundTlvStream = (PayerTlvStream, OfferTlvStream, InvoiceRequestTlvStream);
+type RefundTlvStream = (
+       PayerTlvStream, OfferTlvStream, InvoiceRequestTlvStream, ExperimentalOfferTlvStream,
+);
 
 type RefundTlvStreamRef<'a> = (
        PayerTlvStreamRef<'a>,
        OfferTlvStreamRef<'a>,
        InvoiceRequestTlvStreamRef<'a>,
+       ExperimentalOfferTlvStreamRef,
 );
 
 impl CursorReadable for RefundTlvStream {
@@ -806,8 +811,9 @@ impl CursorReadable for RefundTlvStream {
                let payer = CursorReadable::read(r)?;
                let offer = CursorReadable::read(r)?;
                let invoice_request = CursorReadable::read(r)?;
+               let experimental_offer = CursorReadable::read(r)?;
 
-               Ok((payer, offer, invoice_request))
+               Ok((payer, offer, invoice_request, experimental_offer))
        }
 }
 
@@ -849,6 +855,7 @@ impl TryFrom<RefundTlvStream> for RefundContents {
                        InvoiceRequestTlvStream {
                                chain, amount, features, quantity, payer_id, payer_note, paths
                        },
+                       _experimental_offer_tlv_stream,
                ) = tlv_stream;
 
                let payer = match payer_metadata {
@@ -946,7 +953,7 @@ mod tests {
        use crate::ln::msgs::{DecodeError, MAX_VALUE_MSAT};
        use crate::offers::invoice_request::{INVOICE_REQUEST_TYPES, InvoiceRequestTlvStreamRef};
        use crate::offers::nonce::Nonce;
-       use crate::offers::offer::OfferTlvStreamRef;
+       use crate::offers::offer::{ExperimentalOfferTlvStreamRef, OfferTlvStreamRef};
        use crate::offers::parse::{Bolt12ParseError, Bolt12SemanticError};
        use crate::offers::payer::PayerTlvStreamRef;
        use crate::offers::test_utils::*;
@@ -1014,6 +1021,7 @@ mod tests {
                                        payer_note: None,
                                        paths: None,
                                },
+                               ExperimentalOfferTlvStreamRef {},
                        ),
                );
 
@@ -1166,7 +1174,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));
@@ -1178,7 +1186,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));
@@ -1210,7 +1218,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));
@@ -1224,7 +1232,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")));
 
@@ -1233,7 +1241,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")));
        }
@@ -1246,14 +1254,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));
 
@@ -1261,7 +1269,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));
        }
@@ -1271,7 +1279,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));
 
@@ -1279,7 +1287,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));
        }
@@ -1289,7 +1297,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")));
 
@@ -1297,7 +1305,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 b82a369bad333a40945c03a572297c98904f1d65..104588d921759a420a0fd211067259fcfd01de25 100644 (file)
@@ -27,8 +27,8 @@ use crate::offers::merkle::{
 };
 use crate::offers::nonce::Nonce;
 use crate::offers::offer::{
-       Amount, Offer, OfferContents, OfferTlvStream, OfferTlvStreamRef, Quantity,
-       EXPERIMENTAL_OFFER_TYPES, OFFER_TYPES,
+       Amount, ExperimentalOfferTlvStream, ExperimentalOfferTlvStreamRef, Offer, OfferContents,
+       OfferTlvStream, OfferTlvStreamRef, Quantity, EXPERIMENTAL_OFFER_TYPES, OFFER_TYPES,
 };
 use crate::offers::parse::{Bolt12ParseError, Bolt12SemanticError, ParsedMessage};
 use crate::util::ser::{CursorReadable, Iterable, WithoutLength, Writeable, Writer};
@@ -278,7 +278,7 @@ 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, _) = contents.as_tlv_stream();
 
                // Allocate enough space for the invoice, which will include:
                // - all TLV records from `offer_bytes`,
@@ -445,7 +445,9 @@ impl InvoiceContents {
                        payment_hash: None,
                };
 
-               (self.offer.as_tlv_stream(), invoice)
+               let (offer, experimental_offer) = self.offer.as_tlv_stream();
+
+               (offer, invoice, experimental_offer)
        }
 
        fn chain(&self) -> ChainHash {
@@ -542,29 +544,41 @@ impl TryFrom<Vec<u8>> for StaticInvoice {
        }
 }
 
-type FullInvoiceTlvStream = (OfferTlvStream, InvoiceTlvStream, SignatureTlvStream);
+type FullInvoiceTlvStream =
+       (OfferTlvStream, InvoiceTlvStream, SignatureTlvStream, ExperimentalOfferTlvStream);
 
 impl CursorReadable for FullInvoiceTlvStream {
        fn read<R: AsRef<[u8]>>(r: &mut io::Cursor<R>) -> Result<Self, DecodeError> {
                let offer = CursorReadable::read(r)?;
                let invoice = CursorReadable::read(r)?;
                let signature = CursorReadable::read(r)?;
+               let experimental_offer = CursorReadable::read(r)?;
 
-               Ok((offer, invoice, signature))
+               Ok((offer, invoice, signature, experimental_offer))
        }
 }
 
-type PartialInvoiceTlvStream = (OfferTlvStream, InvoiceTlvStream);
+type PartialInvoiceTlvStream = (OfferTlvStream, InvoiceTlvStream, ExperimentalOfferTlvStream);
 
-type PartialInvoiceTlvStreamRef<'a> = (OfferTlvStreamRef<'a>, InvoiceTlvStreamRef<'a>);
+type PartialInvoiceTlvStreamRef<'a> =
+       (OfferTlvStreamRef<'a>, InvoiceTlvStreamRef<'a>, ExperimentalOfferTlvStreamRef);
 
 impl TryFrom<ParsedMessage<FullInvoiceTlvStream>> for StaticInvoice {
        type Error = Bolt12ParseError;
 
        fn try_from(invoice: ParsedMessage<FullInvoiceTlvStream>) -> Result<Self, Self::Error> {
                let ParsedMessage { bytes, tlv_stream } = invoice;
-               let (offer_tlv_stream, invoice_tlv_stream, SignatureTlvStream { signature }) = tlv_stream;
-               let contents = InvoiceContents::try_from((offer_tlv_stream, invoice_tlv_stream))?;
+               let (
+                       offer_tlv_stream,
+                       invoice_tlv_stream,
+                       SignatureTlvStream { signature },
+                       experimental_offer_tlv_stream,
+               ) = tlv_stream;
+               let contents = InvoiceContents::try_from((
+                       offer_tlv_stream,
+                       invoice_tlv_stream,
+                       experimental_offer_tlv_stream,
+               ))?;
 
                let signature = match signature {
                        None => {
@@ -600,6 +614,7 @@ impl TryFrom<PartialInvoiceTlvStream> for InvoiceContents {
                                payment_hash,
                                amount,
                        },
+                       experimental_offer_tlv_stream,
                ) = tlv_stream;
 
                if payment_hash.is_some() {
@@ -632,7 +647,7 @@ impl TryFrom<PartialInvoiceTlvStream> for InvoiceContents {
                }
 
                Ok(InvoiceContents {
-                       offer: OfferContents::try_from(offer_tlv_stream)?,
+                       offer: OfferContents::try_from((offer_tlv_stream, experimental_offer_tlv_stream))?,
                        payment_paths,
                        message_paths,
                        created_at,
@@ -655,7 +670,9 @@ mod tests {
        use crate::offers::merkle;
        use crate::offers::merkle::{SignatureTlvStreamRef, TaggedHash};
        use crate::offers::nonce::Nonce;
-       use crate::offers::offer::{Offer, OfferBuilder, OfferTlvStreamRef, Quantity};
+       use crate::offers::offer::{
+               ExperimentalOfferTlvStreamRef, Offer, OfferBuilder, OfferTlvStreamRef, Quantity,
+       };
        use crate::offers::parse::{Bolt12ParseError, Bolt12SemanticError};
        use crate::offers::static_invoice::{
                StaticInvoice, StaticInvoiceBuilder, UnsignedStaticInvoice, DEFAULT_RELATIVE_EXPIRY,
@@ -669,27 +686,39 @@ mod tests {
        use bitcoin::Network;
        use core::time::Duration;
 
-       type FullInvoiceTlvStreamRef<'a> =
-               (OfferTlvStreamRef<'a>, InvoiceTlvStreamRef<'a>, SignatureTlvStreamRef<'a>);
+       type FullInvoiceTlvStreamRef<'a> = (
+               OfferTlvStreamRef<'a>,
+               InvoiceTlvStreamRef<'a>,
+               SignatureTlvStreamRef<'a>,
+               ExperimentalOfferTlvStreamRef,
+       );
 
        impl StaticInvoice {
                fn as_tlv_stream(&self) -> FullInvoiceTlvStreamRef {
-                       let (offer_tlv_stream, invoice_tlv_stream) = self.contents.as_tlv_stream();
+                       let (offer_tlv_stream, invoice_tlv_stream, experimental_offer_tlv_stream) =
+                               self.contents.as_tlv_stream();
                        (
                                offer_tlv_stream,
                                invoice_tlv_stream,
                                SignatureTlvStreamRef { signature: Some(&self.signature) },
+                               experimental_offer_tlv_stream,
                        )
                }
        }
 
        fn tlv_stream_to_bytes(
-               tlv_stream: &(OfferTlvStreamRef, InvoiceTlvStreamRef, SignatureTlvStreamRef),
+               tlv_stream: &(
+                       OfferTlvStreamRef,
+                       InvoiceTlvStreamRef,
+                       SignatureTlvStreamRef,
+                       ExperimentalOfferTlvStreamRef,
+               ),
        ) -> Vec<u8> {
                let mut buffer = Vec::new();
                tlv_stream.0.write(&mut buffer).unwrap();
                tlv_stream.1.write(&mut buffer).unwrap();
                tlv_stream.2.write(&mut buffer).unwrap();
+               tlv_stream.3.write(&mut buffer).unwrap();
                buffer
        }
 
@@ -819,6 +848,7 @@ mod tests {
                                        message_paths: Some(&paths),
                                },
                                SignatureTlvStreamRef { signature: Some(&invoice.signature()) },
+                               ExperimentalOfferTlvStreamRef {},
                        )
                );
 
@@ -933,7 +963,7 @@ mod tests {
 
                // Error if offer paths are missing.
                let mut offer_without_paths = valid_offer.clone();
-               let mut offer_tlv_stream = offer_without_paths.as_tlv_stream();
+               let (mut offer_tlv_stream, _) = offer_without_paths.as_tlv_stream();
                offer_tlv_stream.paths.take();
                let mut buffer = Vec::new();
                offer_tlv_stream.write(&mut buffer).unwrap();
@@ -969,7 +999,7 @@ mod tests {
                                .unwrap();
 
                let mut offer_missing_issuer_id = valid_offer.clone();
-               let mut offer_tlv_stream = offer_missing_issuer_id.as_tlv_stream();
+               let (mut offer_tlv_stream, _) = offer_missing_issuer_id.as_tlv_stream();
                offer_tlv_stream.issuer_id.take();
                let mut buffer = Vec::new();
                offer_tlv_stream.write(&mut buffer).unwrap();
index 99d20b927b6734764fcfec1100e2d299f6bca391..3e528ce93e899cb1fa2356c50cfec607789c9442 100644 (file)
@@ -1446,6 +1446,26 @@ impl<A: Writeable, B: Writeable, C: Writeable, D: Writeable> Writeable for (A, B
        }
 }
 
+impl<A: Readable, B: Readable, C: Readable, D: Readable, E: Readable> Readable for (A, B, C, D, E) {
+       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)?;
+               Ok((a, b, c, d, e))
+       }
+}
+impl<A: Writeable, B: Writeable, C: Writeable, D: Writeable, E: Writeable> Writeable for (A, B, C, D, E) {
+       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)
+       }
+}
+
 impl Writeable for () {
        fn write<W: Writer>(&self, _: &mut W) -> Result<(), io::Error> {
                Ok(())