From 03e4a96061aa7d08f48f09e3fe56abc24e9ac262 Mon Sep 17 00:00:00 2001 From: Jeffrey Czyz Date: Mon, 12 Aug 2024 16:54:55 -0500 Subject: [PATCH] Parse experimental invoice TLV records 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 | 85 +++++++++++++++++-------- lightning/src/offers/invoice_request.rs | 12 ++-- lightning/src/offers/static_invoice.rs | 52 +++++++++++---- lightning/src/util/ser.rs | 24 +++++++ 4 files changed, 129 insertions(+), 44 deletions(-) diff --git a/lightning/src/offers/invoice.rs b/lightning/src/offers/invoice.rs index b09c19644c2..fd98d5b293c 100644 --- a/lightning/src/offers/invoice.rs +++ b/lightning/src/offers/invoice.rs @@ -496,7 +496,9 @@ impl UnsignedBolt12Invoice { record.write(&mut bytes).unwrap(); } - let (_, _, _, invoice_tlv_stream, _, _) = contents.as_tlv_stream(); + let (_, _, _, invoice_tlv_stream, _, _, experimental_invoice_tlv_stream) = + contents.as_tlv_stream(); + invoice_tlv_stream.write(&mut bytes).unwrap(); let mut experimental_bytes = Vec::new(); @@ -505,6 +507,8 @@ impl UnsignedBolt12Invoice { record.write(&mut experimental_bytes).unwrap(); } + experimental_invoice_tlv_stream.write(&mut experimental_bytes).unwrap(); + let tlv_stream = TlvStream::new(&bytes).chain(TlvStream::new(&experimental_bytes)); let tagged_hash = TaggedHash::from_tlv_stream(SIGNATURE_TAG, tlv_stream); @@ -862,6 +866,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), @@ -869,7 +874,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, ) } @@ -1130,9 +1135,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, + ) } } @@ -1181,24 +1189,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))), - blindedpay: Some(Iterable(self.payment_paths.iter().map(|(payinfo, _)| 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))), + blindedpay: Some(Iterable(self.payment_paths.iter().map(|(payinfo, _)| 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 {}, + ) } } @@ -1236,11 +1247,13 @@ impl TryFrom> for UnsignedBolt12Invoice { 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, ) = 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, ) )?; @@ -1283,6 +1296,13 @@ tlv_stream!(InvoiceTlvStream, InvoiceTlvStreamRef<'a>, INVOICE_TYPES, { (236, message_paths: (Vec, WithoutLength)), }); +/// Valid type range for experimental invoice TLV records. +const EXPERIMENTAL_INVOICE_TYPES: core::ops::RangeFrom = 3_000_000_000..; + +tlv_stream!( + ExperimentalInvoiceTlvStream, ExperimentalInvoiceTlvStreamRef, EXPERIMENTAL_INVOICE_TYPES, {} +); + pub(super) type BlindedPathIter<'a> = core::iter::Map< core::slice::Iter<'a, (BlindedPayInfo, BlindedPath)>, for<'r> fn(&'r (BlindedPayInfo, BlindedPath)) -> &'r BlindedPath, @@ -1342,7 +1362,7 @@ impl_writeable!(FallbackAddress, { version, program }); type FullInvoiceTlvStream =( PayerTlvStream, OfferTlvStream, InvoiceRequestTlvStream, InvoiceTlvStream, SignatureTlvStream, - ExperimentalOfferTlvStream, ExperimentalInvoiceRequestTlvStream, + ExperimentalOfferTlvStream, ExperimentalInvoiceRequestTlvStream, ExperimentalInvoiceTlvStream, ); type FullInvoiceTlvStreamRef<'a> = ( @@ -1353,6 +1373,7 @@ type FullInvoiceTlvStreamRef<'a> = ( SignatureTlvStreamRef<'a>, ExperimentalOfferTlvStreamRef, ExperimentalInvoiceRequestTlvStreamRef, + ExperimentalInvoiceTlvStreamRef, ); impl SeekReadable for FullInvoiceTlvStream { @@ -1364,11 +1385,12 @@ impl SeekReadable for FullInvoiceTlvStream { let signature = SeekReadable::read(r)?; let experimental_offer = SeekReadable::read(r)?; let experimental_invoice_request = SeekReadable::read(r)?; + let experimental_invoice = SeekReadable::read(r)?; Ok( ( payer, offer, invoice_request, invoice, signature, experimental_offer, - experimental_invoice_request, + experimental_invoice_request, experimental_invoice, ) ) } @@ -1376,7 +1398,7 @@ impl SeekReadable for FullInvoiceTlvStream { type PartialInvoiceTlvStream = ( PayerTlvStream, OfferTlvStream, InvoiceRequestTlvStream, InvoiceTlvStream, - ExperimentalOfferTlvStream, ExperimentalInvoiceRequestTlvStream, + ExperimentalOfferTlvStream, ExperimentalInvoiceRequestTlvStream, ExperimentalInvoiceTlvStream, ); type PartialInvoiceTlvStreamRef<'a> = ( @@ -1386,6 +1408,7 @@ type PartialInvoiceTlvStreamRef<'a> = ( InvoiceTlvStreamRef<'a>, ExperimentalOfferTlvStreamRef, ExperimentalInvoiceRequestTlvStreamRef, + ExperimentalInvoiceTlvStreamRef, ); impl SeekReadable for PartialInvoiceTlvStream { @@ -1396,11 +1419,12 @@ impl SeekReadable for PartialInvoiceTlvStream { let invoice = SeekReadable::read(r)?; let experimental_offer = SeekReadable::read(r)?; let experimental_invoice_request = SeekReadable::read(r)?; + let experimental_invoice= SeekReadable::read(r)?; Ok( ( payer, offer, invoice_request, invoice, experimental_offer, - experimental_invoice_request, + experimental_invoice_request, experimental_invoice, ) ) } @@ -1416,11 +1440,13 @@ impl TryFrom> 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 +1475,7 @@ impl TryFrom for InvoiceContents { }, experimental_offer_tlv_stream, experimental_invoice_request_tlv_stream, + ExperimentalInvoiceTlvStream {}, ) = tlv_stream; if message_paths.is_some() { return Err(Bolt12SemanticError::UnexpectedPaths) } @@ -1540,7 +1567,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::{WitnessProgram, WitnessVersion}; use bitcoin::blockdata::constants::ChainHash; @@ -1735,6 +1762,7 @@ mod tests { ExperimentalInvoiceRequestTlvStreamRef { experimental_bar: None, }, + ExperimentalInvoiceTlvStreamRef {}, ), ); @@ -1834,6 +1862,7 @@ mod tests { ExperimentalInvoiceRequestTlvStreamRef { experimental_bar: None, }, + ExperimentalInvoiceTlvStreamRef {}, ), ); @@ -2032,7 +2061,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); @@ -2048,7 +2077,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)); @@ -2067,7 +2096,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)); } @@ -2085,7 +2114,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)); @@ -2123,7 +2152,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![ @@ -2166,7 +2195,7 @@ mod tests { .allow_mpp() .build().unwrap() .sign(recipient_sign).unwrap(); - let (_, _, _, tlv_stream, _, _, _) = invoice.as_tlv_stream(); + let (_, _, _, tlv_stream, _, _, _, _) = invoice.as_tlv_stream(); assert_eq!(invoice.invoice_features(), &features); assert_eq!(tlv_stream.features, Some(&features)); } diff --git a/lightning/src/offers/invoice_request.rs b/lightning/src/offers/invoice_request.rs index fffd86bc6d5..d12dcfa3d08 100644 --- a/lightning/src/offers/invoice_request.rs +++ b/lightning/src/offers/invoice_request.rs @@ -1527,7 +1527,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); @@ -1536,6 +1536,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(); @@ -1556,7 +1557,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); @@ -1565,6 +1566,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(); @@ -1614,7 +1616,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); @@ -1623,6 +1625,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(); @@ -1645,7 +1648,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); @@ -1654,6 +1657,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(); diff --git a/lightning/src/offers/static_invoice.rs b/lightning/src/offers/static_invoice.rs index 615d00ece3a..25cab93cbce 100644 --- a/lightning/src/offers/static_invoice.rs +++ b/lightning/src/offers/static_invoice.rs @@ -16,7 +16,8 @@ use crate::ln::inbound_payment::ExpandedKey; use crate::ln::msgs::DecodeError; use crate::offers::invoice::{ check_invoice_signing_pubkey, construct_payment_paths, filter_fallbacks, BlindedPayInfo, - FallbackAddress, InvoiceTlvStream, InvoiceTlvStreamRef, + ExperimentalInvoiceTlvStream, ExperimentalInvoiceTlvStreamRef, FallbackAddress, + InvoiceTlvStream, InvoiceTlvStreamRef, }; use crate::offers::invoice_macros::{invoice_accessors_common, invoice_builder_methods_common}; use crate::offers::merkle::{ @@ -284,7 +285,7 @@ impl UnsignedStaticInvoice { record.write(&mut bytes).unwrap(); } - let (_, invoice_tlv_stream, _) = contents.as_tlv_stream(); + let (_, invoice_tlv_stream, _, experimental_invoice_tlv_stream) = contents.as_tlv_stream(); invoice_tlv_stream.write(&mut bytes).unwrap(); let mut experimental_bytes = Vec::new(); @@ -293,6 +294,8 @@ impl UnsignedStaticInvoice { record.write(&mut experimental_bytes).unwrap(); } + experimental_invoice_tlv_stream.write(&mut experimental_bytes).unwrap(); + let tlv_stream = TlvStream::new(&bytes).chain(TlvStream::new(&experimental_bytes)); let tagged_hash = TaggedHash::from_tlv_stream(SIGNATURE_TAG, tlv_stream); @@ -411,9 +414,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 { @@ -510,8 +515,13 @@ impl TryFrom> for StaticInvoice { } } -type FullInvoiceTlvStream = - (OfferTlvStream, InvoiceTlvStream, SignatureTlvStream, ExperimentalOfferTlvStream); +type FullInvoiceTlvStream = ( + OfferTlvStream, + InvoiceTlvStream, + SignatureTlvStream, + ExperimentalOfferTlvStream, + ExperimentalInvoiceTlvStream, +); impl SeekReadable for FullInvoiceTlvStream { fn read(r: &mut R) -> Result { @@ -519,15 +529,21 @@ impl SeekReadable for FullInvoiceTlvStream { let invoice = SeekReadable::read(r)?; let signature = SeekReadable::read(r)?; let experimental_offer = SeekReadable::read(r)?; + let experimental_invoice = SeekReadable::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> for StaticInvoice { type Error = Bolt12ParseError; @@ -539,11 +555,13 @@ impl TryFrom> 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 { @@ -581,6 +599,7 @@ impl TryFrom for InvoiceContents { amount, }, experimental_offer_tlv_stream, + ExperimentalInvoiceTlvStream {}, ) = tlv_stream; if payment_hash.is_some() { @@ -631,7 +650,7 @@ 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; @@ -656,17 +675,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, ) } } @@ -677,6 +702,7 @@ mod tests { InvoiceTlvStreamRef, SignatureTlvStreamRef, ExperimentalOfferTlvStreamRef, + ExperimentalInvoiceTlvStreamRef, ), ) -> Vec { let mut buffer = Vec::new(); @@ -684,6 +710,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 } @@ -812,6 +839,7 @@ mod tests { }, SignatureTlvStreamRef { signature: Some(&invoice.signature()) }, ExperimentalOfferTlvStreamRef { experimental_foo: None }, + ExperimentalInvoiceTlvStreamRef {}, ) ); diff --git a/lightning/src/util/ser.rs b/lightning/src/util/ser.rs index 98e8e3e1a31..201611099ef 100644 --- a/lightning/src/util/ser.rs +++ b/lightning/src/util/ser.rs @@ -1363,6 +1363,30 @@ impl Readable for (A, B, C, D, E, F, G) { + fn read(r: &mut R) -> Result { + let a: A = Readable::read(r)?; + let b: B = Readable::read(r)?; + let c: C = Readable::read(r)?; + let d: D = Readable::read(r)?; + let e: E = Readable::read(r)?; + let f: F = Readable::read(r)?; + let g: G = Readable::read(r)?; + Ok((a, b, c, d, e, f, g)) + } +} +impl Writeable for (A, B, C, D, E, F, G) { + fn write(&self, w: &mut W) -> Result<(), io::Error> { + self.0.write(w)?; + self.1.write(w)?; + self.2.write(w)?; + self.3.write(w)?; + self.4.write(w)?; + self.5.write(w)?; + self.6.write(w) + } +} + impl Writeable for () { fn write(&self, _: &mut W) -> Result<(), io::Error> { Ok(())