-
Notifications
You must be signed in to change notification settings - Fork 376
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
Pre-work for BOLT 12 invoices #1927
Changes from all commits
7964b9f
1a437f4
ea1a68c
d985ced
b50fc4e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -65,7 +65,7 @@ use core::marker::PhantomData; | |
use bitcoin::bech32; | ||
use bitcoin::bech32::{Base32Len, FromBase32, ToBase32, u5, WriteBase32}; | ||
use crate::ln::msgs::DecodeError; | ||
use crate::util::ser::{Readable, Writeable, Writer}; | ||
use crate::util::ser::{Readable, WithoutLength, Writeable, Writer}; | ||
|
||
mod sealed { | ||
use crate::prelude::*; | ||
|
@@ -159,6 +159,15 @@ mod sealed { | |
]); | ||
define_context!(OfferContext, []); | ||
define_context!(InvoiceRequestContext, []); | ||
define_context!(Bolt12InvoiceContext, [ | ||
// Byte 0 | ||
, | ||
// Byte 1 | ||
, | ||
// Byte 2 | ||
BasicMPP, | ||
]); | ||
define_context!(BlindedHopContext, []); | ||
// This isn't a "real" feature context, and is only used in the channel_type field in an | ||
// `OpenChannel` message. | ||
define_context!(ChannelTypeContext, [ | ||
|
@@ -342,7 +351,7 @@ mod sealed { | |
define_feature!(15, PaymentSecret, [InitContext, NodeContext, InvoiceContext], | ||
"Feature flags for `payment_secret`.", set_payment_secret_optional, set_payment_secret_required, | ||
supports_payment_secret, requires_payment_secret); | ||
define_feature!(17, BasicMPP, [InitContext, NodeContext, InvoiceContext], | ||
define_feature!(17, BasicMPP, [InitContext, NodeContext, InvoiceContext, Bolt12InvoiceContext], | ||
"Feature flags for `basic_mpp`.", set_basic_mpp_optional, set_basic_mpp_required, | ||
supports_basic_mpp, requires_basic_mpp); | ||
define_feature!(19, Wumbo, [InitContext, NodeContext], | ||
|
@@ -369,7 +378,7 @@ mod sealed { | |
|
||
#[cfg(test)] | ||
define_feature!(123456789, UnknownFeature, | ||
[NodeContext, ChannelContext, InvoiceContext, OfferContext, InvoiceRequestContext], | ||
[NodeContext, ChannelContext, InvoiceContext, OfferContext, InvoiceRequestContext, Bolt12InvoiceContext, BlindedHopContext], | ||
"Feature flags for an unknown feature used in testing.", set_unknown_feature_optional, | ||
set_unknown_feature_required, supports_unknown_test_feature, requires_unknown_test_feature); | ||
} | ||
|
@@ -432,6 +441,10 @@ pub type InvoiceFeatures = Features<sealed::InvoiceContext>; | |
pub type OfferFeatures = Features<sealed::OfferContext>; | ||
/// Features used within an `invoice_request`. | ||
pub type InvoiceRequestFeatures = Features<sealed::InvoiceRequestContext>; | ||
/// Features used within an `invoice`. | ||
pub type Bolt12InvoiceFeatures = Features<sealed::Bolt12InvoiceContext>; | ||
/// Features used within BOLT 4 encrypted_data_tlv and BOLT 12 blinded_payinfo | ||
pub type BlindedHopFeatures = Features<sealed::BlindedHopContext>; | ||
|
||
/// Features used within the channel_type field in an OpenChannel message. | ||
/// | ||
|
@@ -719,32 +732,47 @@ impl_feature_len_prefixed_write!(InitFeatures); | |
impl_feature_len_prefixed_write!(ChannelFeatures); | ||
impl_feature_len_prefixed_write!(NodeFeatures); | ||
impl_feature_len_prefixed_write!(InvoiceFeatures); | ||
impl_feature_len_prefixed_write!(BlindedHopFeatures); | ||
|
||
// Some features only appear inside of TLVs, so they don't have a length prefix when serialized. | ||
macro_rules! impl_feature_tlv_write { | ||
($features: ident) => { | ||
impl Writeable for $features { | ||
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> { | ||
self.write_be(w) | ||
WithoutLength(self).write(w) | ||
} | ||
} | ||
impl Readable for $features { | ||
fn read<R: io::Read>(r: &mut R) -> Result<Self, DecodeError> { | ||
let v = io_extras::read_to_end(r)?; | ||
Ok(Self::from_be_bytes(v)) | ||
Ok(WithoutLength::<Self>::read(r)?.0) | ||
} | ||
} | ||
} | ||
} | ||
|
||
impl_feature_tlv_write!(ChannelTypeFeatures); | ||
impl_feature_tlv_write!(OfferFeatures); | ||
impl_feature_tlv_write!(InvoiceRequestFeatures); | ||
Comment on lines
-741
to
-742
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Tests are passing for me on #1926 with keeping use of the macro for these sets of features:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That is expected because There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm confused why get rid of the macro, it seems cleaner to have it and avoid There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Seems confusing if There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Aren't all features encoded the same everywhere except this weird blinded payinfo one? Seems weird to make several callsites uglier because of one exception, or at least better to do them all on one go rather than have a few flexible exceptions for offers-related feature sets only. Don't think it's weird enough to hold up the PR but just trying to clarify. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There are two cases where the length needs to be included with the features:
The second case is more generally necessary when the subtype contains any variable-length field (e.g., the witness program in fallback addresses). Otherwise, there is no way to tell the length of each element in the collection. See last paragraph in https://github.com/lightning/bolts/blob/master/01-messaging.md#rationale-1. For BOLT 12, yes, only features in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh, I just meant that for any given feature set X with the exception of blinded_payinfo_features, X will always be encoded the same way.
I just don't see what that scenario would be 🤔 this seems like a one-off weird one. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. At very least, if there is a such a scenario, the compiler would complain. Whereas it won't complain when defining the serialization with |
||
|
||
// Some features may appear both in a TLV record and as part of a TLV subtype sequence. The latter | ||
// requires a length but the former does not. | ||
|
||
impl<T: sealed::Context> Writeable for WithoutLength<&Features<T>> { | ||
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> { | ||
self.0.write_be(w) | ||
} | ||
} | ||
|
||
impl<T: sealed::Context> Readable for WithoutLength<Features<T>> { | ||
fn read<R: io::Read>(r: &mut R) -> Result<Self, DecodeError> { | ||
let v = io_extras::read_to_end(r)?; | ||
Ok(WithoutLength(Features::<T>::from_be_bytes(v))) | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::{ChannelFeatures, ChannelTypeFeatures, InitFeatures, InvoiceFeatures, NodeFeatures, sealed}; | ||
use super::{ChannelFeatures, ChannelTypeFeatures, InitFeatures, InvoiceFeatures, NodeFeatures, OfferFeatures, sealed}; | ||
use bitcoin::bech32::{Base32Len, FromBase32, ToBase32, u5}; | ||
use crate::util::ser::{Readable, WithoutLength, Writeable}; | ||
|
||
#[test] | ||
fn sanity_test_unknown_bits() { | ||
|
@@ -838,6 +866,20 @@ mod tests { | |
assert!(features.supports_payment_secret()); | ||
} | ||
|
||
#[test] | ||
fn encodes_features_without_length() { | ||
let features = OfferFeatures::from_le_bytes(vec![1, 2, 3, 4, 5, 42, 100, 101]); | ||
assert_eq!(features.flags.len(), 8); | ||
|
||
let mut serialized_features = Vec::new(); | ||
WithoutLength(&features).write(&mut serialized_features).unwrap(); | ||
assert_eq!(serialized_features.len(), 8); | ||
|
||
let deserialized_features = | ||
WithoutLength::<OfferFeatures>::read(&mut &serialized_features[..]).unwrap().0; | ||
assert_eq!(features, deserialized_features); | ||
} | ||
|
||
#[test] | ||
fn invoice_features_encoding() { | ||
let features_as_u5s = vec![ | ||
|
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.
Should we not change this too? I guess it can wait to avoid conflicting with #1860, but we should probably avoid having two paths for this, no?
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.
Yeah, noted in response to Val as well, we probably want to drop
impl_feature_tlv_write
and make the encoding explicit. Would require allowing encodings inimpl_writeable_msg
, which I think would mostly just work.