Skip to content

Commit ea2b210

Browse files
committed
Offer features for BOLT 12
The offer message in BOLT 12 contains a features TLV record. Add a corresponding OfferFeatures type where the length is not included in the serialization as it would be redundant with the record length. Otherwise, define the features to be the same as InvoiceFeatures.
1 parent b414c06 commit ea2b210

File tree

1 file changed

+57
-17
lines changed

1 file changed

+57
-17
lines changed

lightning/src/ln/features.rs

Lines changed: 57 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,24 @@ mod sealed {
235235
BasicMPP,
236236
],
237237
});
238+
define_context!(OfferContext {
239+
required_features: [
240+
// Byte 0
241+
,
242+
// Byte 1
243+
VariableLengthOnion | PaymentSecret,
244+
// Byte 2
245+
,
246+
],
247+
optional_features: [
248+
// Byte 0
249+
,
250+
// Byte 1
251+
,
252+
// Byte 2
253+
BasicMPP,
254+
],
255+
});
238256
// This isn't a "real" feature context, and is only used in the channel_type field in an
239257
// `OpenChannel` message.
240258
define_context!(ChannelTypeContext {
@@ -414,17 +432,17 @@ mod sealed {
414432
define_feature!(7, GossipQueries, [InitContext, NodeContext],
415433
"Feature flags for `gossip_queries`.", set_gossip_queries_optional, set_gossip_queries_required,
416434
supports_gossip_queries, requires_gossip_queries);
417-
define_feature!(9, VariableLengthOnion, [InitContext, NodeContext, InvoiceContext],
435+
define_feature!(9, VariableLengthOnion, [InitContext, NodeContext, InvoiceContext, OfferContext],
418436
"Feature flags for `var_onion_optin`.", set_variable_length_onion_optional,
419437
set_variable_length_onion_required, supports_variable_length_onion,
420438
requires_variable_length_onion);
421439
define_feature!(13, StaticRemoteKey, [InitContext, NodeContext, ChannelTypeContext],
422440
"Feature flags for `option_static_remotekey`.", set_static_remote_key_optional,
423441
set_static_remote_key_required, supports_static_remote_key, requires_static_remote_key);
424-
define_feature!(15, PaymentSecret, [InitContext, NodeContext, InvoiceContext],
442+
define_feature!(15, PaymentSecret, [InitContext, NodeContext, InvoiceContext, OfferContext],
425443
"Feature flags for `payment_secret`.", set_payment_secret_optional, set_payment_secret_required,
426444
supports_payment_secret, requires_payment_secret);
427-
define_feature!(17, BasicMPP, [InitContext, NodeContext, InvoiceContext],
445+
define_feature!(17, BasicMPP, [InitContext, NodeContext, InvoiceContext, OfferContext],
428446
"Feature flags for `basic_mpp`.", set_basic_mpp_optional, set_basic_mpp_required,
429447
supports_basic_mpp, requires_basic_mpp);
430448
define_feature!(19, Wumbo, [InitContext, NodeContext],
@@ -447,7 +465,7 @@ mod sealed {
447465
supports_keysend, requires_keysend);
448466

449467
#[cfg(test)]
450-
define_feature!(123456789, UnknownFeature, [NodeContext, ChannelContext, InvoiceContext],
468+
define_feature!(123456789, UnknownFeature, [NodeContext, ChannelContext, InvoiceContext, OfferContext],
451469
"Feature flags for an unknown feature used in testing.", set_unknown_feature_optional,
452470
set_unknown_feature_required, supports_unknown_test_feature, requires_unknown_test_feature);
453471
}
@@ -495,6 +513,8 @@ pub type NodeFeatures = Features<sealed::NodeContext>;
495513
pub type ChannelFeatures = Features<sealed::ChannelContext>;
496514
/// Features used within an invoice.
497515
pub type InvoiceFeatures = Features<sealed::InvoiceContext>;
516+
/// Features used within an offer.
517+
pub type OfferFeatures = Features<sealed::OfferContext>;
498518

499519
/// Features used within the channel_type field in an OpenChannel message.
500520
///
@@ -564,6 +584,15 @@ impl InvoiceFeatures {
564584
}
565585
}
566586

587+
#[cfg(test)]
588+
impl OfferFeatures {
589+
/// Converts `OfferFeatures` to `Features<C>`. Only known `InvoiceFeatures` relevant to context
590+
/// `C` are included in the result.
591+
pub(crate) fn to_context<C: sealed::Context>(&self) -> Features<C> {
592+
self.to_context_internal()
593+
}
594+
}
595+
567596
impl ChannelTypeFeatures {
568597
/// Constructs the implicit channel type based on the common supported types between us and our
569598
/// counterparty
@@ -811,24 +840,29 @@ impl_feature_len_prefixed_write!(ChannelFeatures);
811840
impl_feature_len_prefixed_write!(NodeFeatures);
812841
impl_feature_len_prefixed_write!(InvoiceFeatures);
813842

814-
// Because ChannelTypeFeatures only appears inside of TLVs, it doesn't have a length prefix when
815-
// serialized. Thus, we can't use `impl_feature_len_prefixed_write`, above, and have to write our
816-
// own serialization.
817-
impl Writeable for ChannelTypeFeatures {
818-
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
819-
self.write_be(w)
820-
}
821-
}
822-
impl Readable for ChannelTypeFeatures {
823-
fn read<R: io::Read>(r: &mut R) -> Result<Self, DecodeError> {
824-
let v = io_extras::read_to_end(r)?;
825-
Ok(Self::from_be_bytes(v))
843+
// Some features only appear inside of TLVs, so they don't have a length prefix when serialized.
844+
macro_rules! impl_feature_tlv_value_write {
845+
($features: ident) => {
846+
impl Writeable for $features {
847+
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
848+
self.write_be(w)
849+
}
850+
}
851+
impl Readable for $features {
852+
fn read<R: io::Read>(r: &mut R) -> Result<Self, DecodeError> {
853+
let v = io_extras::read_to_end(r)?;
854+
Ok(Self::from_be_bytes(v))
855+
}
856+
}
826857
}
827858
}
828859

860+
impl_feature_tlv_value_write!(ChannelTypeFeatures);
861+
impl_feature_tlv_value_write!(OfferFeatures);
862+
829863
#[cfg(test)]
830864
mod tests {
831-
use super::{ChannelFeatures, ChannelTypeFeatures, InitFeatures, InvoiceFeatures, NodeFeatures};
865+
use super::{ChannelFeatures, ChannelTypeFeatures, InitFeatures, InvoiceFeatures, NodeFeatures, OfferFeatures};
832866
use bitcoin::bech32::{Base32Len, FromBase32, ToBase32, u5};
833867

834868
#[test]
@@ -1021,6 +1055,12 @@ mod tests {
10211055
assert_eq!(features, features_deserialized);
10221056
}
10231057

1058+
#[test]
1059+
fn invoice_features_equal_offer_features() {
1060+
assert_eq!(InvoiceFeatures::known(), OfferFeatures::known().to_context());
1061+
assert_eq!(OfferFeatures::known(), InvoiceFeatures::known().to_context());
1062+
}
1063+
10241064
#[test]
10251065
fn test_channel_type_mapping() {
10261066
// If we map an InvoiceFeatures with StaticRemoteKey optional, it should map into a

0 commit comments

Comments
 (0)