const EXPERIMENTAL_TYPES: core::ops::Range<u64> =
EXPERIMENTAL_OFFER_TYPES.start..EXPERIMENTAL_INVOICE_REQUEST_TYPES.end;
- let (_, _, _, invoice_tlv_stream, _, _) = contents.as_tlv_stream();
+ let (_, _, _, invoice_tlv_stream, _, _, experimental_invoice_tlv_stream) =
+ contents.as_tlv_stream();
// Allocate enough space for the invoice, which will include:
// - all TLV records from `invreq_bytes` except signatures,
invreq_bytes.len()
+ invoice_tlv_stream.serialized_length()
+ if contents.is_for_offer() { 0 } else { SIGNATURE_TLV_RECORD_SIZE }
+ + experimental_invoice_tlv_stream.serialized_length(),
);
// Use the invoice_request bytes instead of the invoice_request TLV stream as the latter may
- experimental_tlv_stream
.peek()
.map_or(remaining_bytes.len(), |first_record| first_record.start)
+ + experimental_invoice_tlv_stream.serialized_length(),
);
for record in experimental_tlv_stream {
record.write(&mut experimental_bytes).unwrap();
}
+ experimental_invoice_tlv_stream.write(&mut experimental_bytes).unwrap();
debug_assert_eq!(experimental_bytes.len(), experimental_bytes.capacity());
let tlv_stream = TlvStream::new(&bytes).chain(TlvStream::new(&experimental_bytes));
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),
(
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,
)
}
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,
+ )
}
}
}
impl InvoiceFields {
- fn as_tlv_stream(&self) -> InvoiceTlvStreamRef {
+ fn as_tlv_stream(&self) -> (InvoiceTlvStreamRef, ExperimentalInvoiceTlvStreamRef) {
let features = {
if self.features == Bolt12InvoiceFeatures::empty() { None }
else { Some(&self.features) }
};
- InvoiceTlvStreamRef {
- paths: Some(Iterable(self.payment_paths.iter().map(|path| path.inner_blinded_path()))),
- blindedpay: Some(Iterable(self.payment_paths.iter().map(|path| &path.payinfo))),
- created_at: Some(self.created_at.as_secs()),
- relative_expiry: self.relative_expiry.map(|duration| duration.as_secs() as u32),
- payment_hash: Some(&self.payment_hash),
- amount: Some(self.amount_msats),
- fallbacks: self.fallbacks.as_ref(),
- features,
- node_id: Some(&self.signing_pubkey),
- message_paths: None,
- }
+ (
+ InvoiceTlvStreamRef {
+ paths: Some(Iterable(self.payment_paths.iter().map(|path| path.inner_blinded_path()))),
+ blindedpay: Some(Iterable(self.payment_paths.iter().map(|path| &path.payinfo))),
+ created_at: Some(self.created_at.as_secs()),
+ relative_expiry: self.relative_expiry.map(|duration| duration.as_secs() as u32),
+ payment_hash: Some(&self.payment_hash),
+ amount: Some(self.amount_msats),
+ fallbacks: self.fallbacks.as_ref(),
+ features,
+ node_id: Some(&self.signing_pubkey),
+ message_paths: None,
+ },
+ ExperimentalInvoiceTlvStreamRef {},
+ )
}
}
(236, message_paths: (Vec<BlindedMessagePath>, WithoutLength)),
});
+/// Valid type range for experimental invoice TLV records.
+const EXPERIMENTAL_INVOICE_TYPES: core::ops::RangeFrom<u64> = 3_000_000_000..;
+
+tlv_stream!(
+ ExperimentalInvoiceTlvStream, ExperimentalInvoiceTlvStreamRef, EXPERIMENTAL_INVOICE_TYPES, {}
+);
+
pub(super) type BlindedPathIter<'a> = core::iter::Map<
core::slice::Iter<'a, BlindedPaymentPath>,
for<'r> fn(&'r BlindedPaymentPath) -> &'r BlindedPath,
type FullInvoiceTlvStream =(
PayerTlvStream, OfferTlvStream, InvoiceRequestTlvStream, InvoiceTlvStream, SignatureTlvStream,
- ExperimentalOfferTlvStream, ExperimentalInvoiceRequestTlvStream,
+ ExperimentalOfferTlvStream, ExperimentalInvoiceRequestTlvStream, ExperimentalInvoiceTlvStream,
);
type FullInvoiceTlvStreamRef<'a> = (
SignatureTlvStreamRef<'a>,
ExperimentalOfferTlvStreamRef,
ExperimentalInvoiceRequestTlvStreamRef,
+ ExperimentalInvoiceTlvStreamRef,
);
impl CursorReadable for FullInvoiceTlvStream {
let signature = CursorReadable::read(r)?;
let experimental_offer = CursorReadable::read(r)?;
let experimental_invoice_request = CursorReadable::read(r)?;
+ let experimental_invoice = CursorReadable::read(r)?;
Ok(
(
payer, offer, invoice_request, invoice, signature, experimental_offer,
- experimental_invoice_request,
+ experimental_invoice_request, experimental_invoice,
)
)
}
type PartialInvoiceTlvStream = (
PayerTlvStream, OfferTlvStream, InvoiceRequestTlvStream, InvoiceTlvStream,
- ExperimentalOfferTlvStream, ExperimentalInvoiceRequestTlvStream,
+ ExperimentalOfferTlvStream, ExperimentalInvoiceRequestTlvStream, ExperimentalInvoiceTlvStream,
);
type PartialInvoiceTlvStreamRef<'a> = (
InvoiceTlvStreamRef<'a>,
ExperimentalOfferTlvStreamRef,
ExperimentalInvoiceRequestTlvStreamRef,
+ ExperimentalInvoiceTlvStreamRef,
);
impl CursorReadable for PartialInvoiceTlvStream {
let invoice = CursorReadable::read(r)?;
let experimental_offer = CursorReadable::read(r)?;
let experimental_invoice_request = CursorReadable::read(r)?;
+ let experimental_invoice = CursorReadable::read(r)?;
Ok(
(
payer, offer, invoice_request, invoice, experimental_offer,
- experimental_invoice_request,
+ experimental_invoice_request, experimental_invoice,
)
)
}
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,
)
)?;
},
experimental_offer_tlv_stream,
experimental_invoice_request_tlv_stream,
+ ExperimentalInvoiceTlvStream {},
) = tlv_stream;
if message_paths.is_some() { return Err(Bolt12SemanticError::UnexpectedPaths) }
#[cfg(test)]
mod tests {
- use super::{Bolt12Invoice, DEFAULT_RELATIVE_EXPIRY, FallbackAddress, FullInvoiceTlvStreamRef, INVOICE_TYPES, InvoiceTlvStreamRef, SIGNATURE_TAG, UnsignedBolt12Invoice};
+ use super::{Bolt12Invoice, DEFAULT_RELATIVE_EXPIRY, ExperimentalInvoiceTlvStreamRef, FallbackAddress, FullInvoiceTlvStreamRef, INVOICE_TYPES, InvoiceTlvStreamRef, SIGNATURE_TAG, UnsignedBolt12Invoice};
use bitcoin::{CompressedPublicKey, WitnessProgram, WitnessVersion};
use bitcoin::constants::ChainHash;
ExperimentalInvoiceRequestTlvStreamRef {
experimental_bar: None,
},
+ ExperimentalInvoiceTlvStreamRef {},
),
);
ExperimentalInvoiceRequestTlvStreamRef {
experimental_bar: None,
},
+ ExperimentalInvoiceTlvStreamRef {},
),
);
.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);
.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));
.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));
}
.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));
.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![
.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));
}
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);
(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();
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);
(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();
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);
(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();
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);
(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();
use crate::ln::inbound_payment::ExpandedKey;
use crate::ln::msgs::DecodeError;
use crate::offers::invoice::{
- check_invoice_signing_pubkey, construct_payment_paths, filter_fallbacks, FallbackAddress,
+ check_invoice_signing_pubkey, construct_payment_paths, filter_fallbacks,
+ ExperimentalInvoiceTlvStream, ExperimentalInvoiceTlvStreamRef, FallbackAddress,
InvoiceTlvStream, InvoiceTlvStreamRef,
};
use crate::offers::invoice_macros::{invoice_accessors_common, invoice_builder_methods_common};
impl UnsignedStaticInvoice {
fn new(offer_bytes: &Vec<u8>, contents: InvoiceContents) -> Self {
- let (_, invoice_tlv_stream, _) = contents.as_tlv_stream();
+ let (_, invoice_tlv_stream, _, experimental_invoice_tlv_stream) = contents.as_tlv_stream();
// Allocate enough space for the invoice, which will include:
// - all TLV records from `offer_bytes`,
// - all invoice-specific TLV records, and
// - a signature TLV record once the invoice is signed.
let mut bytes = Vec::with_capacity(
- offer_bytes.len() + invoice_tlv_stream.serialized_length() + SIGNATURE_TLV_RECORD_SIZE,
+ offer_bytes.len()
+ + invoice_tlv_stream.serialized_length()
+ + SIGNATURE_TLV_RECORD_SIZE
+ + experimental_invoice_tlv_stream.serialized_length(),
);
// Use the offer bytes instead of the offer TLV stream as the latter may have contained
remaining_bytes.len()
- experimental_tlv_stream
.peek()
- .map_or(remaining_bytes.len(), |first_record| first_record.start),
+ .map_or(remaining_bytes.len(), |first_record| first_record.start)
+ + experimental_invoice_tlv_stream.serialized_length(),
);
for record in experimental_tlv_stream {
record.write(&mut experimental_bytes).unwrap();
}
+ experimental_invoice_tlv_stream.write(&mut experimental_bytes).unwrap();
debug_assert_eq!(experimental_bytes.len(), experimental_bytes.capacity());
let tlv_stream = TlvStream::new(&bytes).chain(TlvStream::new(&experimental_bytes));
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 {
}
}
-type FullInvoiceTlvStream =
- (OfferTlvStream, InvoiceTlvStream, SignatureTlvStream, ExperimentalOfferTlvStream);
+type FullInvoiceTlvStream = (
+ OfferTlvStream,
+ InvoiceTlvStream,
+ SignatureTlvStream,
+ ExperimentalOfferTlvStream,
+ ExperimentalInvoiceTlvStream,
+);
impl CursorReadable for FullInvoiceTlvStream {
fn read<R: AsRef<[u8]>>(r: &mut io::Cursor<R>) -> Result<Self, DecodeError> {
let invoice = CursorReadable::read(r)?;
let signature = CursorReadable::read(r)?;
let experimental_offer = CursorReadable::read(r)?;
+ let experimental_invoice = CursorReadable::read(r)?;
- Ok((offer, invoice, signature, experimental_offer))
+ Ok((offer, invoice, signature, experimental_offer, experimental_invoice))
}
}
-type PartialInvoiceTlvStream = (OfferTlvStream, InvoiceTlvStream, ExperimentalOfferTlvStream);
+type PartialInvoiceTlvStream =
+ (OfferTlvStream, InvoiceTlvStream, ExperimentalOfferTlvStream, ExperimentalInvoiceTlvStream);
-type PartialInvoiceTlvStreamRef<'a> =
- (OfferTlvStreamRef<'a>, InvoiceTlvStreamRef<'a>, ExperimentalOfferTlvStreamRef);
+type PartialInvoiceTlvStreamRef<'a> = (
+ OfferTlvStreamRef<'a>,
+ InvoiceTlvStreamRef<'a>,
+ ExperimentalOfferTlvStreamRef,
+ ExperimentalInvoiceTlvStreamRef,
+);
impl TryFrom<ParsedMessage<FullInvoiceTlvStream>> for StaticInvoice {
type Error = Bolt12ParseError;
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 {
amount,
},
experimental_offer_tlv_stream,
+ ExperimentalInvoiceTlvStream {},
) = tlv_stream;
if payment_hash.is_some() {
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;
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,
)
}
}
InvoiceTlvStreamRef,
SignatureTlvStreamRef,
ExperimentalOfferTlvStreamRef,
+ ExperimentalInvoiceTlvStreamRef,
),
) -> Vec<u8> {
let mut buffer = Vec::new();
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
}
},
SignatureTlvStreamRef { signature: Some(&invoice.signature()) },
ExperimentalOfferTlvStreamRef { experimental_foo: None },
+ ExperimentalInvoiceTlvStreamRef {},
)
);