X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning%2Fsrc%2Fln%2Ffeatures.rs;h=8f3d5cb5a24f1b7bf5b3baf160e08eab6091f323;hb=a8bd4c097f7ae6620eee66c1cb49144e38568439;hp=7d4b86f5acabf6ae81d5f0df9b13f3090fb11d29;hpb=288fe0298ae0dd3550c4618e4f10d67d810aa997;p=rust-lightning diff --git a/lightning/src/ln/features.rs b/lightning/src/ln/features.rs index 7d4b86f5..8f3d5cb5 100644 --- a/lightning/src/ln/features.rs +++ b/lightning/src/ln/features.rs @@ -41,6 +41,12 @@ //! (see [BOLT-4](https://github.com/lightning/bolts/blob/master/04-onion-routing.md#basic-multi-part-payments) for more information). //! - `Wumbo` - requires/supports that a node create large channels. Called `option_support_large_channel` in the spec. //! (see [BOLT-2](https://github.com/lightning/bolts/blob/master/02-peer-protocol.md#the-open_channel-message) for more information). +//! - `AnchorsZeroFeeHtlcTx` - requires/supports that commitment transactions include anchor outputs +//! and HTLC transactions are pre-signed with zero fee (see +//! [BOLT-3](https://github.com/lightning/bolts/blob/master/03-transactions.md) for more +//! information). +//! - `RouteBlinding` - requires/supports that a node can relay payments over blinded paths +//! (see [BOLT-4](https://github.com/lightning/bolts/blob/master/04-onion-routing.md#route-blinding) for more information). //! - `ShutdownAnySegwit` - requires/supports that future segwit versions are allowed in `shutdown` //! (see [BOLT-2](https://github.com/lightning/bolts/blob/master/02-peer-protocol.md) for more information). //! - `OnionMessages` - requires/supports forwarding onion messages @@ -60,26 +66,33 @@ //! for more info). //! - `Keysend` - send funds to a node without an invoice //! (see the [`Keysend` feature assignment proposal](https://github.com/lightning/bolts/issues/605#issuecomment-606679798) for more information). -//! - `AnchorsZeroFeeHtlcTx` - requires/supports that commitment transactions include anchor outputs -//! and HTLC transactions are pre-signed with zero fee (see -//! [BOLT-3](https://github.com/lightning/bolts/blob/master/03-transactions.md) for more -//! information). +//! - `Trampoline` - supports receiving and forwarding Trampoline payments +//! (see the [`Trampoline` feature proposal](https://github.com/lightning/bolts/pull/836) for more information). +//! +//! LDK knows about the following features, but does not support them: +//! - `AnchorsNonzeroFeeHtlcTx` - the initial version of anchor outputs, which was later found to be +//! vulnerable (see this +//! [mailing list post](https://lists.linuxfoundation.org/pipermail/lightning-dev/2020-September/002796.html) +//! for more information). //! //! [BOLT #9]: https://github.com/lightning/bolts/blob/master/09-features.md //! [messages]: crate::ln::msgs -use crate::{io, io_extras}; +#[allow(unused_imports)] use crate::prelude::*; + +use crate::{io, io_extras}; use core::{cmp, fmt}; +use core::borrow::Borrow; use core::hash::{Hash, Hasher}; use core::marker::PhantomData; -use bitcoin::bech32; -use bitcoin::bech32::{Base32Len, FromBase32, ToBase32, u5, WriteBase32}; +use bech32::{Base32Len, FromBase32, ToBase32, u5, WriteBase32}; use crate::ln::msgs::DecodeError; use crate::util::ser::{Readable, WithoutLength, Writeable, Writer}; mod sealed { + #[allow(unused_imports)] use crate::prelude::*; use crate::ln::features::Features; @@ -134,15 +147,17 @@ mod sealed { // Byte 1 VariableLengthOnion | StaticRemoteKey | PaymentSecret, // Byte 2 - BasicMPP | Wumbo | AnchorsZeroFeeHtlcTx, + BasicMPP | Wumbo | AnchorsNonzeroFeeHtlcTx | AnchorsZeroFeeHtlcTx, // Byte 3 - ShutdownAnySegwit, + RouteBlinding | ShutdownAnySegwit | Taproot, // Byte 4 OnionMessages, // Byte 5 ChannelType | SCIDPrivacy, // Byte 6 ZeroConf, + // Byte 7 + Trampoline, ]); define_context!(NodeContext, [ // Byte 0 @@ -150,18 +165,20 @@ mod sealed { // Byte 1 VariableLengthOnion | StaticRemoteKey | PaymentSecret, // Byte 2 - BasicMPP | Wumbo | AnchorsZeroFeeHtlcTx, + BasicMPP | Wumbo | AnchorsNonzeroFeeHtlcTx | AnchorsZeroFeeHtlcTx, // Byte 3 - ShutdownAnySegwit, + RouteBlinding | ShutdownAnySegwit | Taproot, // Byte 4 OnionMessages, // Byte 5 ChannelType | SCIDPrivacy, // Byte 6 ZeroConf | Keysend, + // Byte 7 + Trampoline, ]); define_context!(ChannelContext, []); - define_context!(InvoiceContext, [ + define_context!(Bolt11InvoiceContext, [ // Byte 0 , // Byte 1 @@ -176,6 +193,8 @@ mod sealed { , // Byte 6 PaymentMetadata, + // Byte 7 + Trampoline, ]); define_context!(OfferContext, []); define_context!(InvoiceRequestContext, []); @@ -196,9 +215,9 @@ mod sealed { // Byte 1 StaticRemoteKey, // Byte 2 - AnchorsZeroFeeHtlcTx, + AnchorsNonzeroFeeHtlcTx | AnchorsZeroFeeHtlcTx, // Byte 3 - , + Taproot, // Byte 4 , // Byte 5 @@ -362,28 +381,37 @@ mod sealed { define_feature!(7, GossipQueries, [InitContext, NodeContext], "Feature flags for `gossip_queries`.", set_gossip_queries_optional, set_gossip_queries_required, supports_gossip_queries, requires_gossip_queries); - define_feature!(9, VariableLengthOnion, [InitContext, NodeContext, InvoiceContext], + define_feature!(9, VariableLengthOnion, [InitContext, NodeContext, Bolt11InvoiceContext], "Feature flags for `var_onion_optin`.", set_variable_length_onion_optional, set_variable_length_onion_required, supports_variable_length_onion, requires_variable_length_onion); define_feature!(13, StaticRemoteKey, [InitContext, NodeContext, ChannelTypeContext], "Feature flags for `option_static_remotekey`.", set_static_remote_key_optional, set_static_remote_key_required, supports_static_remote_key, requires_static_remote_key); - define_feature!(15, PaymentSecret, [InitContext, NodeContext, InvoiceContext], + define_feature!(15, PaymentSecret, [InitContext, NodeContext, Bolt11InvoiceContext], "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, Bolt12InvoiceContext], + define_feature!(17, BasicMPP, [InitContext, NodeContext, Bolt11InvoiceContext, 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], "Feature flags for `option_support_large_channel` (aka wumbo channels).", set_wumbo_optional, set_wumbo_required, supports_wumbo, requires_wumbo); + define_feature!(21, AnchorsNonzeroFeeHtlcTx, [InitContext, NodeContext, ChannelTypeContext], + "Feature flags for `option_anchors_nonzero_fee_htlc_tx`.", set_anchors_nonzero_fee_htlc_tx_optional, + set_anchors_nonzero_fee_htlc_tx_required, supports_anchors_nonzero_fee_htlc_tx, requires_anchors_nonzero_fee_htlc_tx); define_feature!(23, AnchorsZeroFeeHtlcTx, [InitContext, NodeContext, ChannelTypeContext], "Feature flags for `option_anchors_zero_fee_htlc_tx`.", set_anchors_zero_fee_htlc_tx_optional, set_anchors_zero_fee_htlc_tx_required, supports_anchors_zero_fee_htlc_tx, requires_anchors_zero_fee_htlc_tx); + define_feature!(25, RouteBlinding, [InitContext, NodeContext], + "Feature flags for `option_route_blinding`.", set_route_blinding_optional, + set_route_blinding_required, supports_route_blinding, requires_route_blinding); define_feature!(27, ShutdownAnySegwit, [InitContext, NodeContext], "Feature flags for `opt_shutdown_anysegwit`.", set_shutdown_any_segwit_optional, set_shutdown_any_segwit_required, supports_shutdown_anysegwit, requires_shutdown_anysegwit); + define_feature!(31, Taproot, [InitContext, NodeContext, ChannelTypeContext], + "Feature flags for `option_taproot`.", set_taproot_optional, + set_taproot_required, supports_taproot, requires_taproot); define_feature!(39, OnionMessages, [InitContext, NodeContext], "Feature flags for `option_onion_messages`.", set_onion_messages_optional, set_onion_messages_required, supports_onion_messages, requires_onion_messages); @@ -393,7 +421,7 @@ mod sealed { define_feature!(47, SCIDPrivacy, [InitContext, NodeContext, ChannelTypeContext], "Feature flags for only forwarding with SCID aliasing. Called `option_scid_alias` in the BOLTs", set_scid_privacy_optional, set_scid_privacy_required, supports_scid_privacy, requires_scid_privacy); - define_feature!(49, PaymentMetadata, [InvoiceContext], + define_feature!(49, PaymentMetadata, [Bolt11InvoiceContext], "Feature flags for payment metadata in invoices.", set_payment_metadata_optional, set_payment_metadata_required, supports_payment_metadata, requires_payment_metadata); define_feature!(51, ZeroConf, [InitContext, NodeContext, ChannelTypeContext], @@ -402,11 +430,14 @@ mod sealed { define_feature!(55, Keysend, [NodeContext], "Feature flags for keysend payments.", set_keysend_optional, set_keysend_required, supports_keysend, requires_keysend); + define_feature!(57, Trampoline, [InitContext, NodeContext, Bolt11InvoiceContext], + "Feature flags for Trampoline routing.", set_trampoline_routing_optional, set_trampoline_routing_required, + supports_trampoline_routing, requires_trampoline_routing); // Note: update the module-level docs when a new feature bit is added! #[cfg(test)] define_feature!(123456789, UnknownFeature, - [NodeContext, ChannelContext, InvoiceContext, OfferContext, InvoiceRequestContext, Bolt12InvoiceContext, BlindedHopContext], + [NodeContext, ChannelContext, Bolt11InvoiceContext, 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); } @@ -422,13 +453,21 @@ pub struct Features { mark: PhantomData, } -impl Features { - pub(crate) fn or(mut self, o: Self) -> Self { - let total_feature_len = cmp::max(self.flags.len(), o.flags.len()); +impl> core::ops::BitOrAssign for Features { + fn bitor_assign(&mut self, rhs: Rhs) { + let total_feature_len = cmp::max(self.flags.len(), rhs.borrow().flags.len()); self.flags.resize(total_feature_len, 0u8); - for (byte, o_byte) in self.flags.iter_mut().zip(o.flags.iter()) { - *byte |= *o_byte; + for (byte, rhs_byte) in self.flags.iter_mut().zip(rhs.borrow().flags.iter()) { + *byte |= *rhs_byte; } + } +} + +impl core::ops::BitOr for Features { + type Output = Self; + + fn bitor(mut self, o: Self) -> Self { + self |= o; self } } @@ -443,12 +482,24 @@ impl Clone for Features { } impl Hash for Features { fn hash(&self, hasher: &mut H) { - self.flags.hash(hasher); + let mut nonzero_flags = &self.flags[..]; + while nonzero_flags.last() == Some(&0) { + nonzero_flags = &nonzero_flags[..nonzero_flags.len() - 1]; + } + nonzero_flags.hash(hasher); } } impl PartialEq for Features { fn eq(&self, o: &Self) -> bool { - self.flags.eq(&o.flags) + let mut o_iter = o.flags.iter(); + let mut self_iter = self.flags.iter(); + loop { + match (o_iter.next(), self_iter.next()) { + (Some(o), Some(us)) => if o != us { return false }, + (Some(b), None) | (None, Some(b)) => if *b != 0 { return false }, + (None, None) => return true, + } + } } } impl PartialOrd for Features { @@ -474,7 +525,7 @@ pub type NodeFeatures = Features; /// Features used within a `channel_announcement` message. pub type ChannelFeatures = Features; /// Features used within an invoice. -pub type InvoiceFeatures = Features; +pub type Bolt11InvoiceFeatures = Features; /// Features used within an `offer`. pub type OfferFeatures = Features; /// Features used within an `invoice_request`. @@ -520,8 +571,8 @@ impl InitFeatures { } } -impl InvoiceFeatures { - /// Converts `InvoiceFeatures` to `Features`. Only known `InvoiceFeatures` relevant to +impl Bolt11InvoiceFeatures { + /// Converts `Bolt11InvoiceFeatures` to `Features`. Only known `Bolt11InvoiceFeatures` relevant to /// context `C` are included in the result. pub(crate) fn to_context(&self) -> Features { self.to_context_internal() @@ -531,20 +582,26 @@ impl InvoiceFeatures { /// features (since they were not announced in a node announcement). However, keysend payments /// don't have an invoice to pull the payee's features from, so this method is provided for use in /// [`PaymentParameters::for_keysend`], thus omitting the need for payers to manually construct an - /// `InvoiceFeatures` for [`find_route`]. + /// `Bolt11InvoiceFeatures` for [`find_route`]. + /// + /// MPP keysend is not widely supported yet, so we parameterize support to allow the user to + /// choose whether their router should find multi-part routes. /// /// [`PaymentParameters::for_keysend`]: crate::routing::router::PaymentParameters::for_keysend /// [`find_route`]: crate::routing::router::find_route - pub(crate) fn for_keysend() -> InvoiceFeatures { - let mut res = InvoiceFeatures::empty(); + pub(crate) fn for_keysend(allow_mpp: bool) -> Bolt11InvoiceFeatures { + let mut res = Bolt11InvoiceFeatures::empty(); res.set_variable_length_onion_optional(); + if allow_mpp { + res.set_basic_mpp_optional(); + } res } } impl Bolt12InvoiceFeatures { - /// Converts `Bolt12InvoiceFeatures` to `Features`. Only known `Bolt12InvoiceFeatures` relevant - /// to context `C` are included in the result. + /// Converts [`Bolt12InvoiceFeatures`] to [`Features`]. Only known [`Bolt12InvoiceFeatures`] + /// relevant to context `C` are included in the result. pub(crate) fn to_context(&self) -> Features { self.to_context_internal() } @@ -570,9 +627,17 @@ impl ChannelTypeFeatures { ::set_required_bit(&mut ret.flags); ret } + + /// Constructs a ChannelTypeFeatures with anchors support + pub(crate) fn anchors_zero_htlc_fee_and_dependencies() -> Self { + let mut ret = Self::empty(); + ::set_required_bit(&mut ret.flags); + ::set_required_bit(&mut ret.flags); + ret + } } -impl ToBase32 for InvoiceFeatures { +impl ToBase32 for Bolt11InvoiceFeatures { fn write_base32(&self, writer: &mut W) -> Result<(), ::Err> { // Explanation for the "4": the normal way to round up when dividing is to add the divisor // minus one before dividing @@ -602,16 +667,16 @@ impl ToBase32 for InvoiceFeatures { } } -impl Base32Len for InvoiceFeatures { +impl Base32Len for Bolt11InvoiceFeatures { fn base32_len(&self) -> usize { self.to_base32().len() } } -impl FromBase32 for InvoiceFeatures { +impl FromBase32 for Bolt11InvoiceFeatures { type Err = bech32::Error; - fn from_base32(field_data: &[u5]) -> Result { + fn from_base32(field_data: &[u5]) -> Result { // Explanation for the "7": the normal way to round up when dividing is to add the divisor // minus one before dividing let length_bytes = (field_data.len() * 5 + 7) / 8 as usize; @@ -630,7 +695,7 @@ impl FromBase32 for InvoiceFeatures { while !res_bytes.is_empty() && res_bytes[res_bytes.len() - 1] == 0 { res_bytes.pop(); } - Ok(InvoiceFeatures::from_le_bytes(res_bytes)) + Ok(Bolt11InvoiceFeatures::from_le_bytes(res_bytes)) } } @@ -683,7 +748,11 @@ impl Features { Ok(()) } - fn from_be_bytes(mut flags: Vec) -> Features { + /// Create a [`Features`] given a set of flags, in big-endian. This is in byte order from + /// most on-the-wire encodings. + /// + /// This is not exported to bindings users as we don't support export across multiple T + pub fn from_be_bytes(mut flags: Vec) -> Features { flags.reverse(); // Swap to little-endian Self { flags, @@ -695,6 +764,25 @@ impl Features { self.flags.iter().any(|&byte| (byte & 0b10_10_10_10) != 0) } + /// Returns true if this `Features` object contains required features unknown by `other`. + pub fn requires_unknown_bits_from(&self, other: &Self) -> bool { + // Bitwise AND-ing with all even bits set except for known features will select required + // unknown features. + self.flags.iter().enumerate().any(|(i, &byte)| { + const REQUIRED_FEATURES: u8 = 0b01_01_01_01; + const OPTIONAL_FEATURES: u8 = 0b10_10_10_10; + let unknown_features = if i < other.flags.len() { + // Form a mask similar to !T::KNOWN_FEATURE_MASK only for `other` + !(other.flags[i] + | ((other.flags[i] >> 1) & REQUIRED_FEATURES) + | ((other.flags[i] << 1) & OPTIONAL_FEATURES)) + } else { + 0b11_11_11_11 + }; + (byte & (REQUIRED_FEATURES & unknown_features)) != 0 + }) + } + /// Returns true if this `Features` object contains unknown feature flags which are set as /// "required". pub fn requires_unknown_bits(&self) -> bool { @@ -743,6 +831,82 @@ impl Features { } true } + + /// Sets a required feature bit. Errors if `bit` is outside the feature range as defined + /// by [BOLT 9]. + /// + /// Note: Required bits are even. If an odd bit is given, then the corresponding even bit will + /// be set instead (i.e., `bit - 1`). + /// + /// [BOLT 9]: https://github.com/lightning/bolts/blob/master/09-features.md + pub fn set_required_feature_bit(&mut self, bit: usize) -> Result<(), ()> { + self.set_feature_bit(bit - (bit % 2)) + } + + /// Sets an optional feature bit. Errors if `bit` is outside the feature range as defined + /// by [BOLT 9]. + /// + /// Note: Optional bits are odd. If an even bit is given, then the corresponding odd bit will be + /// set instead (i.e., `bit + 1`). + /// + /// [BOLT 9]: https://github.com/lightning/bolts/blob/master/09-features.md + pub fn set_optional_feature_bit(&mut self, bit: usize) -> Result<(), ()> { + self.set_feature_bit(bit + (1 - (bit % 2))) + } + + fn set_feature_bit(&mut self, bit: usize) -> Result<(), ()> { + if bit > 255 { + return Err(()); + } + self.set_bit(bit, false) + } + + /// Sets a required custom feature bit. Errors if `bit` is outside the custom range as defined + /// by [bLIP 2] or if it is a known `T` feature. + /// + /// Note: Required bits are even. If an odd bit is given, then the corresponding even bit will + /// be set instead (i.e., `bit - 1`). + /// + /// [bLIP 2]: https://github.com/lightning/blips/blob/master/blip-0002.md#feature-bits + pub fn set_required_custom_bit(&mut self, bit: usize) -> Result<(), ()> { + self.set_custom_bit(bit - (bit % 2)) + } + + /// Sets an optional custom feature bit. Errors if `bit` is outside the custom range as defined + /// by [bLIP 2] or if it is a known `T` feature. + /// + /// Note: Optional bits are odd. If an even bit is given, then the corresponding odd bit will be + /// set instead (i.e., `bit + 1`). + /// + /// [bLIP 2]: https://github.com/lightning/blips/blob/master/blip-0002.md#feature-bits + pub fn set_optional_custom_bit(&mut self, bit: usize) -> Result<(), ()> { + self.set_custom_bit(bit + (1 - (bit % 2))) + } + + fn set_custom_bit(&mut self, bit: usize) -> Result<(), ()> { + if bit < 256 { + return Err(()); + } + self.set_bit(bit, true) + } + + fn set_bit(&mut self, bit: usize, custom: bool) -> Result<(), ()> { + let byte_offset = bit / 8; + let mask = 1 << (bit - 8 * byte_offset); + if byte_offset < T::KNOWN_FEATURE_MASK.len() && custom { + if (T::KNOWN_FEATURE_MASK[byte_offset] & mask) != 0 { + return Err(()); + } + } + + if self.flags.len() <= byte_offset { + self.flags.resize(byte_offset + 1, 0u8); + } + + self.flags[byte_offset] |= mask; + + Ok(()) + } } impl Features { @@ -781,6 +945,13 @@ impl Features { } } +impl Features { + #[cfg(test)] + pub(crate) fn clear_route_blinding(&mut self) { + ::clear_bits(&mut self.flags); + } +} + #[cfg(test)] impl Features { pub(crate) fn unknown() -> Self { @@ -808,7 +979,7 @@ macro_rules! impl_feature_len_prefixed_write { 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!(Bolt11InvoiceFeatures); impl_feature_len_prefixed_write!(Bolt12InvoiceFeatures); impl_feature_len_prefixed_write!(BlindedHopFeatures); @@ -848,8 +1019,8 @@ impl Readable for WithoutLength> { #[cfg(test)] mod tests { - use super::{ChannelFeatures, ChannelTypeFeatures, InitFeatures, InvoiceFeatures, NodeFeatures, OfferFeatures, sealed}; - use bitcoin::bech32::{Base32Len, FromBase32, ToBase32, u5}; + use super::{ChannelFeatures, ChannelTypeFeatures, InitFeatures, Bolt11InvoiceFeatures, NodeFeatures, OfferFeatures, sealed}; + use bech32::{Base32Len, FromBase32, ToBase32, u5}; use crate::util::ser::{Readable, WithoutLength, Writeable}; #[test] @@ -869,6 +1040,43 @@ mod tests { assert!(features.supports_unknown_bits()); } + #[test] + fn requires_unknown_bits_from() { + let mut features1 = InitFeatures::empty(); + let mut features2 = InitFeatures::empty(); + assert!(!features1.requires_unknown_bits_from(&features2)); + assert!(!features2.requires_unknown_bits_from(&features1)); + + features1.set_data_loss_protect_required(); + assert!(features1.requires_unknown_bits_from(&features2)); + assert!(!features2.requires_unknown_bits_from(&features1)); + + features2.set_data_loss_protect_optional(); + assert!(!features1.requires_unknown_bits_from(&features2)); + assert!(!features2.requires_unknown_bits_from(&features1)); + + features2.set_gossip_queries_required(); + assert!(!features1.requires_unknown_bits_from(&features2)); + assert!(features2.requires_unknown_bits_from(&features1)); + + features1.set_gossip_queries_optional(); + assert!(!features1.requires_unknown_bits_from(&features2)); + assert!(!features2.requires_unknown_bits_from(&features1)); + + features1.set_variable_length_onion_required(); + assert!(features1.requires_unknown_bits_from(&features2)); + assert!(!features2.requires_unknown_bits_from(&features1)); + + features2.set_variable_length_onion_optional(); + assert!(!features1.requires_unknown_bits_from(&features2)); + assert!(!features2.requires_unknown_bits_from(&features1)); + + features1.set_basic_mpp_required(); + features2.set_wumbo_required(); + assert!(features1.requires_unknown_bits_from(&features2)); + assert!(features2.requires_unknown_bits_from(&features1)); + } + #[test] fn convert_to_context_with_relevant_flags() { let mut init_features = InitFeatures::empty(); @@ -881,12 +1089,13 @@ mod tests { init_features.set_payment_secret_required(); init_features.set_basic_mpp_optional(); init_features.set_wumbo_optional(); + init_features.set_anchors_zero_fee_htlc_tx_optional(); + init_features.set_route_blinding_optional(); init_features.set_shutdown_any_segwit_optional(); init_features.set_onion_messages_optional(); init_features.set_channel_type_optional(); init_features.set_scid_privacy_optional(); init_features.set_zero_conf_optional(); - init_features.set_anchors_zero_fee_htlc_tx_optional(); assert!(init_features.initial_routing_sync()); assert!(!init_features.supports_upfront_shutdown_script()); @@ -897,8 +1106,8 @@ mod tests { // Check that the flags are as expected: // - option_data_loss_protect (req) // - var_onion_optin (req) | static_remote_key (req) | payment_secret(req) - // - basic_mpp | wumbo - // - opt_shutdown_anysegwit + // - basic_mpp | wumbo | option_anchors_zero_fee_htlc_tx + // - option_route_blinding | opt_shutdown_anysegwit // - onion_messages // - option_channel_type | option_scid_alias // - option_zeroconf @@ -906,7 +1115,7 @@ mod tests { assert_eq!(node_features.flags[0], 0b00000001); assert_eq!(node_features.flags[1], 0b01010001); assert_eq!(node_features.flags[2], 0b10001010); - assert_eq!(node_features.flags[3], 0b00001000); + assert_eq!(node_features.flags[3], 0b00001010); assert_eq!(node_features.flags[4], 0b10000000); assert_eq!(node_features.flags[5], 0b10100000); assert_eq!(node_features.flags[6], 0b00001000); @@ -926,23 +1135,60 @@ mod tests { fn convert_to_context_with_unknown_flags() { // Ensure the `from` context has fewer known feature bytes than the `to` context. assert!(::KNOWN_FEATURE_MASK.len() < - ::KNOWN_FEATURE_MASK.len()); + ::KNOWN_FEATURE_MASK.len()); let mut channel_features = ChannelFeatures::empty(); channel_features.set_unknown_feature_optional(); assert!(channel_features.supports_unknown_bits()); - let invoice_features: InvoiceFeatures = channel_features.to_context_internal(); + let invoice_features: Bolt11InvoiceFeatures = channel_features.to_context_internal(); assert!(!invoice_features.supports_unknown_bits()); } #[test] fn set_feature_bits() { - let mut features = InvoiceFeatures::empty(); + let mut features = Bolt11InvoiceFeatures::empty(); features.set_basic_mpp_optional(); features.set_payment_secret_required(); assert!(features.supports_basic_mpp()); assert!(!features.requires_basic_mpp()); assert!(features.requires_payment_secret()); assert!(features.supports_payment_secret()); + + // Set flags manually + let mut features = NodeFeatures::empty(); + assert!(features.set_optional_feature_bit(55).is_ok()); + assert!(features.supports_keysend()); + assert!(features.set_optional_feature_bit(255).is_ok()); + assert!(features.set_required_feature_bit(256).is_err()); + } + + #[test] + fn set_custom_bits() { + let mut features = Bolt11InvoiceFeatures::empty(); + features.set_variable_length_onion_optional(); + assert_eq!(features.flags[1], 0b00000010); + + assert!(features.set_optional_custom_bit(255).is_err()); + assert!(features.set_required_custom_bit(256).is_ok()); + assert!(features.set_required_custom_bit(258).is_ok()); + assert_eq!(features.flags[31], 0b00000000); + assert_eq!(features.flags[32], 0b00000101); + + let known_bit = ::EVEN_BIT; + let byte_offset = ::BYTE_OFFSET; + assert_eq!(byte_offset, 1); + assert_eq!(features.flags[byte_offset], 0b00000010); + assert!(features.set_required_custom_bit(known_bit).is_err()); + assert_eq!(features.flags[byte_offset], 0b00000010); + + let mut features = Bolt11InvoiceFeatures::empty(); + assert!(features.set_optional_custom_bit(256).is_ok()); + assert!(features.set_optional_custom_bit(259).is_ok()); + assert_eq!(features.flags[32], 0b00001010); + + let mut features = Bolt11InvoiceFeatures::empty(); + assert!(features.set_required_custom_bit(257).is_ok()); + assert!(features.set_required_custom_bit(258).is_ok()); + assert_eq!(features.flags[32], 0b00000101); } #[test] @@ -976,7 +1222,7 @@ mod tests { u5::try_from_u8(16).unwrap(), u5::try_from_u8(1).unwrap(), ]; - let features = InvoiceFeatures::from_le_bytes(vec![1, 2, 3, 4, 5, 42, 100, 101]); + let features = Bolt11InvoiceFeatures::from_le_bytes(vec![1, 2, 3, 4, 5, 42, 100, 101]); // Test length calculation. assert_eq!(features.base32_len(), 13); @@ -986,13 +1232,13 @@ mod tests { assert_eq!(features_as_u5s, features_serialized); // Test deserialization. - let features_deserialized = InvoiceFeatures::from_base32(&features_as_u5s).unwrap(); + let features_deserialized = Bolt11InvoiceFeatures::from_base32(&features_as_u5s).unwrap(); assert_eq!(features, features_deserialized); } #[test] fn test_channel_type_mapping() { - // If we map an InvoiceFeatures with StaticRemoteKey optional, it should map into a + // If we map an Bolt11InvoiceFeatures with StaticRemoteKey optional, it should map into a // required-StaticRemoteKey ChannelTypeFeatures. let mut init_features = InitFeatures::empty(); init_features.set_static_remote_key_optional(); @@ -1001,4 +1247,26 @@ mod tests { assert!(!converted_features.supports_any_optional_bits()); assert!(converted_features.requires_static_remote_key()); } + + #[test] + #[cfg(feature = "std")] + fn test_excess_zero_bytes_ignored() { + // Checks that `Hash` and `PartialEq` ignore excess zero bytes, which may appear due to + // feature conversion or because a peer serialized their feature poorly. + use std::collections::hash_map::DefaultHasher; + use std::hash::{Hash, Hasher}; + + let mut zerod_features = InitFeatures::empty(); + zerod_features.flags = vec![0]; + let empty_features = InitFeatures::empty(); + assert!(empty_features.flags.is_empty()); + + assert_eq!(zerod_features, empty_features); + + let mut zerod_hash = DefaultHasher::new(); + zerod_features.hash(&mut zerod_hash); + let mut empty_hash = DefaultHasher::new(); + empty_features.hash(&mut empty_hash); + assert_eq!(zerod_hash.finish(), empty_hash.finish()); + } }