-
Notifications
You must be signed in to change notification settings - Fork 366
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Experimental offer TLVs #3237
Experimental offer TLVs #3237
Conversation
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #3237 +/- ##
==========================================
+ Coverage 89.68% 89.69% +0.01%
==========================================
Files 126 127 +1
Lines 103306 107769 +4463
Branches 103306 107769 +4463
==========================================
+ Hits 92648 96664 +4016
- Misses 7945 8381 +436
- Partials 2713 2724 +11 ☔ View full report in Codecov by Sentry. |
1d15a6f
to
55c6c56
Compare
3f5521c
to
694533b
Compare
694533b
to
1fc4d51
Compare
1fc4d51
to
b84d36b
Compare
Rebased |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I find this pretty hard to review because several commits rewrite code that was rewritten in previous commits, making it hard to figure out what's wrong or what's just wrong until the last commit.
let tagged_hash = TaggedHash::from_valid_tlv_stream_bytes(SIGNATURE_TAG, &bytes); | ||
let experimental_bytes = Vec::new(); | ||
|
||
let tlv_stream = TlvStream::new(&bytes).chain(TlvStream::new(&experimental_bytes)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I find this really hard to read as of this commit - we're writing all the contents into bytes
but then using an empty vec for the experimental set? Why is that not gonna result in TLVs in the wrong buffer? Later on there's a new set returned by as_tlv_stream
but by doing that in a separate commit this is really hard to review.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At this point, we will fail parsing any Offer
with TLVs in the experimental range. So offer_bytes
will not contain any experimental TLVs.
It seemed easier to reason about by adding experimental_bytes
in a dedicated commit. Otherwise, the change gets lost in the parsing commits (e.g., 927c477), which are large changesets.
Also note that we never use parts from as_tlv_stream
that don't originated from us. We need to use the bytes instead because they may contain unknown odd TLVs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seemed easier to reason about by adding experimental_bytes in a dedicated commit. Otherwise, the change gets lost in the parsing commits (e.g., 927c477), which are large changesets.
Currently with things repeatedly changing in later commits I find myself reviewing the entire PR as one big diff because I can't keep track of what is going to stay the same and what's gonna change by the end, so I'm definitely okay with things being a part of larger commits as long as things aren't getting rewritten twice in the same PR :/
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added another fixup for UnsignedStaticInvoice
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Feel free to squash all the fixups and commits and I'll review again from the top anyway.
@@ -254,6 +254,7 @@ pub(super) struct TlvRecord<'a> { | |||
type_bytes: &'a [u8], | |||
// The entire TLV record. | |||
pub(super) record_bytes: &'a [u8], | |||
pub(super) end: usize, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks unused in this commit.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Used in try_from
for UnsignedBolt12Invoice
and UnsignedInvoiceRequest
.
lightning/src/offers/invoice.rs
Outdated
@@ -492,17 +492,26 @@ where | |||
|
|||
impl UnsignedBolt12Invoice { | |||
fn new(invreq_bytes: &[u8], contents: InvoiceContents) -> Self { | |||
const NON_EXPERIMENTAL_TYPES: core::ops::Range<u64> = 0..INVOICE_REQUEST_TYPES.end; | |||
const EXPERIMENTAL_TYPES: core::ops::Range<u64> = EXPERIMENTAL_OFFER_TYPES; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't this be EXPERIMENTAL_OFFER_TYPES.start - EXPERIMENTAL_INVOICE_REQUEST_TYPES.end
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nope... at this point we've only added support for parsing experimental offer TLVs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, which makes reviewing this patchset pretty confusing - we add things that don't really make sense as of the commit they're added in, and changed later to be correct. I'm not sure how to review that except to review the whole PR in one big diff-tree.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't believe that is the case. Before this commit we didn't support parsing experimental types and after it we support experimental types in the offer range as indicated by this line and the commit message. In a later commit the range is expanded to support parsing experimental types in the invoice request range. Correctness is maintained the entire way since we will fail to parse anything that includes data in a yet-to-be supported experimental range.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This now is set to the ultimate range upfront.
lightning/src/offers/invoice.rs
Outdated
) -> Result<PaymentId, ()> { | ||
const EXPERIMENTAL_TYPES: core::ops::Range<u64> = EXPERIMENTAL_OFFER_TYPES; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Lets not rename constants to make them less specific.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is later expanded to cover other experimental ranges as support is added.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, but at each turn the commits are confusing because they have a wrong value for where we're going. I find it really confusing to have to read some code, see one value, think hmm, that's not right, then go read the final state of the PR, realize no, indeed, it was not right, but its changed later, so I have no idea what the final code is actually going to look like.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This now is set to the ultimate range upfront.
@@ -1684,7 +1684,9 @@ mod tests { | |||
message_paths: None, | |||
}, | |||
SignatureTlvStreamRef { signature: Some(&invoice.signature()) }, | |||
ExperimentalOfferTlvStreamRef {}, | |||
ExperimentalOfferTlvStreamRef { | |||
experimental_foo: None, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rather than testing by overriding specific fields, can we just add support for passing TLV streams into the builders? We'll need/want to support passing custom types soon anyway (in #2829 or a followup, at least).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm... right, though it seems there's a larger question as what sort of interface we should have when reading such TLVs. I think my assumption was we'd want to directly add TLVs that we support. If it's odd and we don't know it, we can ignore it. If it's even and we don't know it, we'd failed to parse. Unless instead we want ways to configure support kinda like custom messages. But that seems like a much larger change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yea, I think we definitely should support passing custom TLVs, but I'm not sure it needs to be complicated. The logic in RecipientOnionFields
to handle custom TLVs is pretty simple, and I'd imagined we'd duplicate basically that here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we should do that in this PR. Sure, changing the builder isn't too difficult, but It's a significant change to the tlv_stream!
macro to be able to support that. I'm not even sure if it can be done, TBH, without a substantial re-write since custom experimental TLVs may have types less than LDK-supported experimental TLVs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alright, sounds good.
b84d36b
to
5f2991d
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I find this pretty hard to review because several commits rewrite code that was rewritten in previous commits, making it hard to figure out what's wrong or what's just wrong until the last commit.
I don't believe that the case anywhere. The commits were written to maintain correctness from start to finish. Do you have a specific example in mind?
lightning/src/offers/invoice.rs
Outdated
@@ -492,17 +492,26 @@ where | |||
|
|||
impl UnsignedBolt12Invoice { | |||
fn new(invreq_bytes: &[u8], contents: InvoiceContents) -> Self { | |||
const NON_EXPERIMENTAL_TYPES: core::ops::Range<u64> = 0..INVOICE_REQUEST_TYPES.end; | |||
const EXPERIMENTAL_TYPES: core::ops::Range<u64> = EXPERIMENTAL_OFFER_TYPES; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't believe that is the case. Before this commit we didn't support parsing experimental types and after it we support experimental types in the offer range as indicated by this line and the commit message. In a later commit the range is expanded to support parsing experimental types in the invoice request range. Correctness is maintained the entire way since we will fail to parse anything that includes data in a yet-to-be supported experimental range.
let tagged_hash = TaggedHash::from_valid_tlv_stream_bytes(SIGNATURE_TAG, &bytes); | ||
let experimental_bytes = Vec::new(); | ||
|
||
let tlv_stream = TlvStream::new(&bytes).chain(TlvStream::new(&experimental_bytes)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@@ -1684,7 +1684,9 @@ mod tests { | |||
message_paths: None, | |||
}, | |||
SignatureTlvStreamRef { signature: Some(&invoice.signature()) }, | |||
ExperimentalOfferTlvStreamRef {}, | |||
ExperimentalOfferTlvStreamRef { | |||
experimental_foo: None, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we should do that in this PR. Sure, changing the builder isn't too difficult, but It's a significant change to the tlv_stream!
macro to be able to support that. I'm not even sure if it can be done, TBH, without a substantial re-write since custom experimental TLVs may have types less than LDK-supported experimental TLVs.
5f2991d
to
be40cc7
Compare
be40cc7
to
fda4b8d
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This LGTM, I think. I started trying to review it per-commit again and got very very confused, so just gave up and reviewed the whole patchset, which is much easier, if also pretty annoying.
I do wonder if we're gonna end up wanting to change things a decent chunk when we go to actually support setting experimental entries in the structs. I assume we will, but probably not enough to merit changing this PR, though.
lightning/src/offers/invoice.rs
Outdated
@@ -491,19 +492,32 @@ where | |||
|
|||
impl UnsignedBolt12Invoice { | |||
fn new(invreq_bytes: &[u8], contents: InvoiceContents) -> Self { | |||
const NON_EXPERIMENTAL_TYPES: core::ops::Range<u64> = 0..INVOICE_REQUEST_TYPES.end; | |||
const EXPERIMENTAL_TYPES: core::ops::Range<u64> = 0..0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please don't add things and then change them in later commits, if at all possible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alright, I moved the constants ultimately used here up to this commit. Shouldn't affect the behavior since we don't support parsing messages with experimental TLV records at this point in the commit history.
lightning/src/offers/invoice.rs
Outdated
const NON_EXPERIMENTAL_TYPES: core::ops::Range<u64> = 0..INVOICE_REQUEST_TYPES.end; | ||
const EXPERIMENTAL_TYPES: core::ops::Range<u64> = 0..0; | ||
|
||
let mut bytes = Vec::new(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unrelated to this PR (aside from it adding another vec), but rather than rebuilding the bytes here can we just store the invoice request (bytes) in the UnsignedBolt12Invoice
and then do the work here in sign
, building a single, new bytes
with the invreq and invoice bytes in one go? Alternatively, we could iterate the bytes in sign
to build the bytes without storing an extra vec here, but not sure its really worth it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Possibly... I'd have to take a more thorough look to see if there's anything preventing it. Right now, this is also used to calculate the TaggedHash
, so it may result in some duplication or need some more refactoring to avoid that.
It would also add a lifetime on the structs, which we may want to avoid? Forgetting if that will be problematic for bindings.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was thinking move InvoiceContents
and store invreq_bytes
as a Vec
, rather than a slice (we currently always actually have an owned Vec
at the callsite when calling here, afaict.
If we don't do this at a minimum we need a with_capacity
here (overshooting a bit is fine, I think, this struct shouldn't be around long).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was thinking move
InvoiceContents
and storeinvreq_bytes
as aVec
, rather than a slice (we currently always actually have an ownedVec
at the callsite when calling here, afaict.
Hmm... invreq_bytes
comes from InvoiceBuilder
as a slice from either &InvoiceRequest
or &Refund
, so we don't own it. We could copy it, but that leaves us with a Vec<u8>
containing a both experimental and non-experimental ranges for which we need to insert a signature.
If we don't do this at a minimum we need a
with_capacity
here (overshooting a bit is fine, I think, this struct shouldn't be around long).
Done.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm... invreq_bytes comes from InvoiceBuilder as a slice from either &InvoiceRequest or &Refund, so we don't own it. We could copy it, but that leaves us with a Vec containing a both experimental and non-experimental ranges for which we need to insert a signature.
Oh sorry I had been looking at the deserialization logic and had that cached lol.
|
||
invoice_request_tlv_stream.write(&mut bytes).unwrap(); | ||
|
||
const EXPERIMENTAL_OFFER_TYPES: core::ops::Range<u64> = 0..0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please don't add things and then change them in later commits, if at all possible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Likewise here.
@@ -687,6 +687,12 @@ impl Offer { | |||
self.contents.expects_quantity() | |||
} | |||
|
|||
pub(super) fn tlv_stream_iter<'a>( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The commit message ends with "Using a common function ensures th", which looks cut off.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed
@@ -845,7 +845,7 @@ impl Bolt12Invoice { | |||
(&refund.payer.0, REFUND_IV_BYTES_WITH_METADATA) | |||
}, | |||
}; | |||
self.contents.verify(TlvStream::new(&self.bytes), metadata, key, iv_bytes, secp_ctx) | |||
self.contents.verify(&self.bytes, metadata, key, iv_bytes, secp_ctx) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The commit message for this change says "Passing bytes directly to InvoiceContents::verify improves readability.", but this is also used to deal with the filtering in verify
, which is useful in the coming commits, no? Would be nice if the commit message described this a bit more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good call. Added more context.
lightning/src/offers/invoice.rs
Outdated
) -> Result<PaymentId, ()> { | ||
const EXPERIMENTAL_TYPES: core::ops::Range<u64> = EXPERIMENTAL_OFFER_TYPES; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, but at each turn the commits are confusing because they have a wrong value for where we're going. I find it really confusing to have to read some code, see one value, think hmm, that's not right, then go read the final state of the PR, realize no, indeed, it was not right, but its changed later, so I have no idea what the final code is actually going to look like.
const NON_EXPERIMENTAL_TYPES: core::ops::Range<u64> = 0..INVOICE_REQUEST_TYPES.end; | ||
const EXPERIMENTAL_TYPES: core::ops::Range<u64> = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These constants are a bit confusingly named - they are actually only filters for invreq TLVs, but we're defining them in invoice.rs
in a struct about invoices.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added a clarifying comment here and in static_invoice.rs
. They are local constants, so I don't think it's worth using overly elaborate names.
fda4b8d
to
0a116c2
Compare
2f9d44a and 906a6fb should address these concerns.
Arbitrary ones set by users, yes. Any we support should be the same as any TLV. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Still need to look at the tests in more detail but I think this basically looks good!
19f5e42
to
afbe380
Compare
Yeah, But for |
Ah, ok... some of the failure are because we directly modify |
lightning/src/offers/invoice.rs
Outdated
@@ -528,6 +571,9 @@ macro_rules! unsigned_invoice_sign_method { ($self: ident, $self_type: ty $(, $s | |||
}; | |||
signature_tlv_stream.write(&mut $self.bytes).unwrap(); | |||
|
|||
// Append the experimental bytes after the signature. | |||
WithoutLength(&$self.experimental_bytes).write(&mut $self.bytes).unwrap(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: would be more readable as $self.bytes.extend_from_slice(&$self.experimental_bytes)
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated in latest push.
diff --git a/lightning/src/offers/invoice.rs b/lightning/src/offers/invoice.rs
index 4f6c3a917..3b406daa7 100644
--- a/lightning/src/offers/invoice.rs
+++ b/lightning/src/offers/invoice.rs
@@ -573,7 +573,7 @@ macro_rules! unsigned_invoice_sign_method { ($self: ident, $self_type: ty $(, $s
signature_tlv_stream.write(&mut $self.bytes).unwrap();
// Append the experimental bytes after the signature.
- WithoutLength(&$self.experimental_bytes).write(&mut $self.bytes).unwrap();
+ $self.bytes.extend_from_slice(&$self.experimental_bytes);
Ok(Bolt12Invoice {
#[cfg(not(c_bindings))]
diff --git a/lightning/src/offers/invoice_request.rs b/lightning/src/offers/invoice_request.rs
index 94c309844..a9beb4a2f 100644
--- a/lightning/src/offers/invoice_request.rs
+++ b/lightning/src/offers/invoice_request.rs
@@ -604,7 +604,7 @@ macro_rules! unsigned_invoice_request_sign_method { (
signature_tlv_stream.write(&mut $self.bytes).unwrap();
// Append the experimental bytes after the signature.
- WithoutLength(&$self.experimental_bytes).write(&mut $self.bytes).unwrap();
+ $self.bytes.extend_from_slice(&$self.experimental_bytes);
Ok(InvoiceRequest {
#[cfg(not(c_bindings))]
diff --git a/lightning/src/offers/static_invoice.rs b/lightning/src/offers/static_invoice.rs
index 228b9191a..dd1c191e6 100644
--- a/lightning/src/offers/static_invoice.rs
+++ b/lightning/src/offers/static_invoice.rs
@@ -343,7 +343,7 @@ impl UnsignedStaticInvoice {
signature_tlv_stream.write(&mut self.bytes).unwrap();
// Append the experimental bytes after the signature.
- WithoutLength(&self.experimental_bytes).write(&mut self.bytes).unwrap();
+ self.bytes.extend_from_slice(&self.experimental_bytes);
Ok(StaticInvoice { bytes: self.bytes, contents: self.contents, signature })
}
I believe that fails not just for invoices but also invoice_requests and maybe others. |
92201c5
to
1c88664
Compare
With the latest fixes, the following checks should pass except for the unknown / experimental cases where diff --git a/lightning/src/offers/invoice.rs b/lightning/src/offers/invoice.rs
index a201cdd58..e14ccce28 100644
--- a/lightning/src/offers/invoice.rs
+++ b/lightning/src/offers/invoice.rs
@@ -543,6 +543,7 @@ impl UnsignedBolt12Invoice {
}
experimental_invoice_tlv_stream.write(&mut experimental_bytes).unwrap();
+ assert_eq!(experimental_bytes.len(), experimental_bytes.capacity());
let tlv_stream = TlvStream::new(&bytes).chain(TlvStream::new(&experimental_bytes));
let tagged_hash = TaggedHash::from_tlv_stream(SIGNATURE_TAG, tlv_stream);
@@ -575,6 +576,7 @@ macro_rules! unsigned_invoice_sign_method { ($self: ident, $self_type: ty $(, $s
// Append the experimental bytes after the signature.
WithoutLength(&$self.experimental_bytes).write(&mut $self.bytes).unwrap();
+ assert_eq!($self.bytes.len() + if $self.contents.is_for_offer() { 0 } else { 2 }, $self.bytes.capacity());
Ok(Bolt12Invoice {
#[cfg(not(c_bindings))]
bytes: $self.bytes,
diff --git a/lightning/src/offers/invoice_request.rs b/lightning/src/offers/invoice_request.rs
index dd0c1f61d..af5ea401d 100644
--- a/lightning/src/offers/invoice_request.rs
+++ b/lightning/src/offers/invoice_request.rs
@@ -502,6 +502,7 @@ impl UnsignedInvoiceRequest {
}
experimental_invoice_request_tlv_stream.write(&mut experimental_bytes).unwrap();
+ assert_eq!(experimental_bytes.len(), experimental_bytes.capacity());
let tlv_stream = TlvStream::new(&bytes).chain(TlvStream::new(&experimental_bytes));
let tagged_hash = TaggedHash::from_tlv_stream(SIGNATURE_TAG, tlv_stream);
@@ -536,6 +537,7 @@ macro_rules! unsigned_invoice_request_sign_method { (
// Append the experimental bytes after the signature.
WithoutLength(&$self.experimental_bytes).write(&mut $self.bytes).unwrap();
+ assert_eq!($self.bytes.len() + 2, $self.bytes.capacity());
Ok(InvoiceRequest {
#[cfg(not(c_bindings))]
bytes: $self.bytes,
diff --git a/lightning/src/offers/static_invoice.rs b/lightning/src/offers/static_invoice.rs
index 228b9191a..87deb3646 100644
--- a/lightning/src/offers/static_invoice.rs
+++ b/lightning/src/offers/static_invoice.rs
@@ -324,6 +324,7 @@ impl UnsignedStaticInvoice {
}
experimental_invoice_tlv_stream.write(&mut experimental_bytes).unwrap();
+ assert_eq!(experimental_bytes.len(), experimental_bytes.capacity());
let tlv_stream = TlvStream::new(&bytes).chain(TlvStream::new(&experimental_bytes));
let tagged_hash = TaggedHash::from_tlv_stream(SIGNATURE_TAG, tlv_stream);
@@ -345,6 +346,7 @@ impl UnsignedStaticInvoice {
// Append the experimental bytes after the signature.
WithoutLength(&self.experimental_bytes).write(&mut self.bytes).unwrap();
+ assert_eq!(self.bytes.len() + 2, self.bytes.capacity());
Ok(StaticInvoice { bytes: self.bytes, contents: self.contents, signature })
} |
Most recent changes: diff --git a/lightning/src/offers/invoice.rs b/lightning/src/offers/invoice.rs
index 02eeaf4b3..4f6c3a917 100644
--- a/lightning/src/offers/invoice.rs
+++ b/lightning/src/offers/invoice.rs
@@ -122,7 +122,7 @@ use crate::offers::invoice_macros::{invoice_accessors_common, invoice_builder_me
#[cfg(test)]
use crate::offers::invoice_macros::invoice_builder_methods_test;
use crate::offers::invoice_request::{EXPERIMENTAL_INVOICE_REQUEST_TYPES, ExperimentalInvoiceRequestTlvStream, ExperimentalInvoiceRequestTlvStreamRef, INVOICE_REQUEST_PAYER_ID_TYPE, INVOICE_REQUEST_TYPES, IV_BYTES as INVOICE_REQUEST_IV_BYTES, InvoiceRequest, InvoiceRequestContents, InvoiceRequestTlvStream, InvoiceRequestTlvStreamRef};
-use crate::offers::merkle::{SignError, SignFn, SignatureTlvStream, SignatureTlvStreamRef, TaggedHash, TlvStream, self};
+use crate::offers::merkle::{SignError, SignFn, SignatureTlvStream, SignatureTlvStreamRef, TaggedHash, TlvStream, self, SIGNATURE_TLV_RECORD_SIZE};
use crate::offers::nonce::Nonce;
use crate::offers::offer::{Amount, EXPERIMENTAL_OFFER_TYPES, ExperimentalOfferTlvStream, ExperimentalOfferTlvStreamRef, OFFER_TYPES, OfferTlvStream, OfferTlvStreamRef, Quantity};
use crate::offers::parse::{Bolt12ParseError, Bolt12SemanticError, ParsedMessage};
@@ -512,6 +512,7 @@ impl UnsignedBolt12Invoice {
let mut bytes = Vec::with_capacity(
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(),
);
@@ -530,10 +531,10 @@ impl UnsignedBolt12Invoice {
.range(EXPERIMENTAL_TYPES)
.peekable();
let mut experimental_bytes = Vec::with_capacity(
- invreq_bytes.len()
+ remaining_bytes.len()
- experimental_tlv_stream
.peek()
- .map_or(invreq_bytes.len(), |first_record| first_record.start)
+ .map_or(remaining_bytes.len(), |first_record| first_record.start)
+ experimental_invoice_tlv_stream.serialized_length(),
);
@@ -937,6 +938,13 @@ impl Hash for Bolt12Invoice {
}
impl InvoiceContents {
+ fn is_for_offer(&self) -> bool {
+ match self {
+ InvoiceContents::ForOffer { .. } => true,
+ InvoiceContents::ForRefund { .. } => false,
+ }
+ }
+
/// Whether the original offer or refund has expired.
#[cfg(feature = "std")]
fn is_offer_or_refund_expired(&self) -> bool {
diff --git a/lightning/src/offers/invoice_request.rs b/lightning/src/offers/invoice_request.rs
index e0a6a8787..94c309844 100644
--- a/lightning/src/offers/invoice_request.rs
+++ b/lightning/src/offers/invoice_request.rs
@@ -560,10 +560,10 @@ impl UnsignedInvoiceRequest {
.range(EXPERIMENTAL_OFFER_TYPES)
.peekable();
let mut experimental_bytes = Vec::with_capacity(
- offer.bytes.len()
+ remaining_bytes.len()
- experimental_tlv_stream
.peek()
- .map_or(offer.bytes.len(), |first_record| first_record.start)
+ .map_or(remaining_bytes.len(), |first_record| first_record.start)
+ experimental_invoice_request_tlv_stream.serialized_length(),
);
diff --git a/lightning/src/offers/static_invoice.rs b/lightning/src/offers/static_invoice.rs
index fa19040fb..228b9191a 100644
--- a/lightning/src/offers/static_invoice.rs
+++ b/lightning/src/offers/static_invoice.rs
@@ -312,10 +312,10 @@ impl UnsignedStaticInvoice {
let mut experimental_tlv_stream =
TlvStream::new(remaining_bytes).range(EXPERIMENTAL_OFFER_TYPES).peekable();
let mut experimental_bytes = Vec::with_capacity(
- offer_bytes.len()
+ remaining_bytes.len()
- experimental_tlv_stream
.peek()
- .map_or(offer_bytes.len(), |first_record| first_record.start)
+ .map_or(remaining_bytes.len(), |first_record| first_record.start)
+ experimental_invoice_tlv_stream.serialized_length(),
); |
1c88664
to
665a622
Compare
Is it possible to go ahead and add the checks as |
When constructing UnsignedInvoiceRequest or UnsignedBolt12Invoice, use a separate field for experimental TLV bytes. This allows for properly inserting the signature TLVs before the experimental TLVs when signing.
Add a utility function for iterating over Offer TLV records contained in any valid TLV stream bytes. Using a common function ensures that experimental TLV records are included once they are supported.
Passing bytes directly to InvoiceContents::verify improves readability as then a TlvStream for each TLV record range can be created from the bytes instead of needing to clone the TlvStream upfront. In an upcoming commit, the experimental TLV record range will utilize this.
Upcoming commits will allow parsing BOLT12 messages that include TLV records in the experimental range. Include these ranges when verifying messages since they will be included in the message bytes.
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.
Offer metadata is generated from the offer TLVs and should included those in the experimental range. When verifying invoice request and invoice messages, these TLVs must be included. Similarly, OfferId construction should included these TLVs as well. Modify the BOLT12 verification tests to cover these TLVs.
The BOLT12 spec defines an experimental TLV range that are allowed in invoice_request messages. Allow this range when parsing an invoice request and include those bytes in any invoice. Also include those bytes when verifying that a Bolt12Invoice is for a valid InvoiceRequest.
Payer metadata is generated from the invreq TLVs and should included those in the experimental range. When verifying invoice messages, these TLVs must be included. Modify the BOLT12 verification tests to cover them.
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.
665a622
to
169b260
Compare
Done. |
The BOLT12 spec defines experimental TLV ranges that are allowed in messages. Allow this range when parsing those messages and include those bytes in subsequent messages (i.e., experimental
offer
TLVs are included in aninvreq
after the signature). Account for these bytes when computing metadata, verifying messages, and constructingOfferId
s.Based on #3218.Fixes #3168.