X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning%2Fsrc%2Fln%2Ffeatures.rs;h=79f869a4c5864761f6b1438ce8e3d6613e9e0a0a;hb=cf2c27800a1b30e72d4f7397c931cf6624594233;hp=2580874640e438e611bea9b88ddda0384dc9db5d;hpb=23240125b96647dc36f234f6b53acc62059ee113;p=rust-lightning diff --git a/lightning/src/ln/features.rs b/lightning/src/ln/features.rs index 25808746..79f869a4 100644 --- a/lightning/src/ln/features.rs +++ b/lightning/src/ln/features.rs @@ -19,33 +19,84 @@ //! supports a feature if it advertises the feature (as either required or optional) to its peers. //! And the implementation can interpret a feature if the feature is known to it. //! -//! [BOLT #9]: https://github.com/lightningnetwork/lightning-rfc/blob/master/09-features.md +//! The following features are currently required in the LDK: +//! - `VariableLengthOnion` - requires/supports variable-length routing onion payloads +//! (see [BOLT-4](https://github.com/lightning/bolts/blob/master/04-onion-routing.md) for more information). +//! - `StaticRemoteKey` - requires/supports static key for remote output +//! (see [BOLT-3](https://github.com/lightning/bolts/blob/master/03-transactions.md) for more information). +//! +//! The following features are currently supported in the LDK: +//! - `DataLossProtect` - requires/supports that a node which has somehow fallen behind, e.g., has been restored from an old backup, +//! can detect that it has fallen behind +//! (see [BOLT-2](https://github.com/lightning/bolts/blob/master/02-peer-protocol.md) for more information). +//! - `InitialRoutingSync` - requires/supports that the sending node needs a complete routing information dump +//! (see [BOLT-7](https://github.com/lightning/bolts/blob/master/07-routing-gossip.md#initial-sync) for more information). +//! - `UpfrontShutdownScript` - commits to a shutdown scriptpubkey when opening a channel +//! (see [BOLT-2](https://github.com/lightning/bolts/blob/master/02-peer-protocol.md#the-open_channel-message) for more information). +//! - `GossipQueries` - requires/supports more sophisticated gossip control +//! (see [BOLT-7](https://github.com/lightning/bolts/blob/master/07-routing-gossip.md) for more information). +//! - `PaymentSecret` - requires/supports that a node supports payment_secret field +//! (see [BOLT-4](https://github.com/lightning/bolts/blob/master/04-onion-routing.md) for more information). +//! - `BasicMPP` - requires/supports that a node can receive basic multi-part payments +//! (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 +//! (see [BOLT-7](https://github.com/lightning/bolts/pull/759/files) for more information). +// TODO: update link +//! - `ChannelType` - node supports the channel_type field in open/accept +//! (see [BOLT-2](https://github.com/lightning/bolts/blob/master/02-peer-protocol.md) for more information). +//! - `SCIDPrivacy` - supply channel aliases for routing +//! (see [BOLT-2](https://github.com/lightning/bolts/blob/master/02-peer-protocol.md) for more information). +//! - `PaymentMetadata` - include additional data in invoices which is passed to recipients in the +//! onion. +//! (see [BOLT-11](https://github.com/lightning/bolts/blob/master/11-payment-encoding.md) for +//! more). +//! - `ZeroConf` - supports accepting HTLCs and using channels prior to funding confirmation +//! (see +//! [BOLT-2](https://github.com/lightning/bolts/blob/master/02-peer-protocol.md#the-channel_ready-message) +//! 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). +//! +//! 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 {io, io_extras}; -use prelude::*; +use crate::{io, io_extras}; +use crate::prelude::*; 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 ln::msgs::DecodeError; -use util::ser::{Readable, Writeable, Writer}; +use crate::ln::msgs::DecodeError; +use crate::util::ser::{Readable, WithoutLength, Writeable, Writer}; mod sealed { - use prelude::*; - use ln::features::Features; + use crate::prelude::*; + use crate::ln::features::Features; - /// The context in which [`Features`] are applicable. Defines which features are required and - /// which are optional for the context. + /// The context in which [`Features`] are applicable. Defines which features are known to the + /// implementation, though specification of them as required or optional is up to the code + /// constructing a features object. pub trait Context { - /// Features that are known to the implementation, where a required feature is indicated by - /// its even bit and an optional feature is indicated by its odd bit. - const KNOWN_FEATURE_FLAGS: &'static [u8]; - - /// Bitmask for selecting features that are known to the implementation, regardless of - /// whether each feature is required or optional. + /// Bitmask for selecting features that are known to the implementation. const KNOWN_FEATURE_MASK: &'static [u8]; } @@ -53,41 +104,16 @@ mod sealed { /// are specified as a comma-separated list of bytes where each byte is a pipe-delimited list of /// feature identifiers. macro_rules! define_context { - ($context: ident { - required_features: [$( $( $required_feature: ident )|*, )*], - optional_features: [$( $( $optional_feature: ident )|*, )*], - }) => { + ($context: ident, [$( $( $known_feature: ident )|*, )*]) => { #[derive(Eq, PartialEq)] pub struct $context {} impl Context for $context { - const KNOWN_FEATURE_FLAGS: &'static [u8] = &[ - // For each byte, use bitwise-OR to compute the applicable flags for known - // required features `r_i` and optional features `o_j` for all `i` and `j` such - // that the following slice is formed: - // - // [ - // `r_0` | `r_1` | ... | `o_0` | `o_1` | ..., - // ..., - // ] - $( - 0b00_00_00_00 $(| - ::REQUIRED_MASK)* - $(| - ::OPTIONAL_MASK)*, - )* - ]; - const KNOWN_FEATURE_MASK: &'static [u8] = &[ - // Similar as above, but set both flags for each feature regardless of whether - // the feature is required or optional. $( 0b00_00_00_00 $(| - ::REQUIRED_MASK | - ::OPTIONAL_MASK)* - $(| - ::REQUIRED_MASK | - ::OPTIONAL_MASK)*, + ::REQUIRED_MASK | + ::OPTIONAL_MASK)*, )* ]; } @@ -96,17 +122,12 @@ mod sealed { fn fmt(&self, fmt: &mut alloc::fmt::Formatter) -> Result<(), alloc::fmt::Error> { $( $( - fmt.write_fmt(format_args!("{}: {}, ", stringify!($required_feature), - if <$context as $required_feature>::requires_feature(&self.flags) { "required" } - else if <$context as $required_feature>::supports_feature(&self.flags) { "supported" } - else { "not supported" }))?; - )* - $( - fmt.write_fmt(format_args!("{}: {}, ", stringify!($optional_feature), - if <$context as $optional_feature>::requires_feature(&self.flags) { "required" } - else if <$context as $optional_feature>::supports_feature(&self.flags) { "supported" } + fmt.write_fmt(format_args!("{}: {}, ", stringify!($known_feature), + if <$context as $known_feature>::requires_feature(&self.flags) { "required" } + else if <$context as $known_feature>::supports_feature(&self.flags) { "supported" } else { "not supported" }))?; )* + {} // Rust gets mad if we only have a $()* block here, so add a dummy {} )* fmt.write_fmt(format_args!("unknown flags: {}", if self.requires_unknown_bits() { "required" } @@ -116,124 +137,84 @@ mod sealed { }; } - define_context!(InitContext { - required_features: [ - // Byte 0 - , - // Byte 1 - VariableLengthOnion | StaticRemoteKey | PaymentSecret, - // Byte 2 - , - // Byte 3 - , - // Byte 4 - , - // Byte 5 - , - ], - optional_features: [ - // Byte 0 - DataLossProtect | InitialRoutingSync | UpfrontShutdownScript | GossipQueries, - // Byte 1 - , - // Byte 2 - BasicMPP | Wumbo, - // Byte 3 - ShutdownAnySegwit, - // Byte 4 - , - // Byte 5 - ChannelType | SCIDPrivacy, - ], - }); - define_context!(NodeContext { - required_features: [ - // Byte 0 - , - // Byte 1 - VariableLengthOnion | StaticRemoteKey | PaymentSecret, - // Byte 2 - , - // Byte 3 - , - // Byte 4 - , - // Byte 5 - , - // Byte 6 - , - ], - optional_features: [ - // Byte 0 - DataLossProtect | UpfrontShutdownScript | GossipQueries, - // Byte 1 - , - // Byte 2 - BasicMPP | Wumbo, - // Byte 3 - ShutdownAnySegwit, - // Byte 4 - , - // Byte 5 - ChannelType | SCIDPrivacy, - // Byte 6 - Keysend, - ], - }); - define_context!(ChannelContext { - required_features: [], - optional_features: [], - }); - define_context!(InvoiceContext { - required_features: [ - // Byte 0 - , - // Byte 1 - VariableLengthOnion | PaymentSecret, - // Byte 2 - , - ], - optional_features: [ - // Byte 0 - , - // Byte 1 - , - // Byte 2 - BasicMPP, - ], - }); + define_context!(InitContext, [ + // Byte 0 + DataLossProtect | InitialRoutingSync | UpfrontShutdownScript | GossipQueries, + // Byte 1 + VariableLengthOnion | StaticRemoteKey | PaymentSecret, + // Byte 2 + BasicMPP | Wumbo | AnchorsNonzeroFeeHtlcTx | AnchorsZeroFeeHtlcTx, + // Byte 3 + RouteBlinding | ShutdownAnySegwit | Taproot, + // Byte 4 + OnionMessages, + // Byte 5 + ChannelType | SCIDPrivacy, + // Byte 6 + ZeroConf, + ]); + define_context!(NodeContext, [ + // Byte 0 + DataLossProtect | UpfrontShutdownScript | GossipQueries, + // Byte 1 + VariableLengthOnion | StaticRemoteKey | PaymentSecret, + // Byte 2 + BasicMPP | Wumbo | AnchorsNonzeroFeeHtlcTx | AnchorsZeroFeeHtlcTx, + // Byte 3 + RouteBlinding | ShutdownAnySegwit | Taproot, + // Byte 4 + OnionMessages, + // Byte 5 + ChannelType | SCIDPrivacy, + // Byte 6 + ZeroConf | Keysend, + ]); + define_context!(ChannelContext, []); + define_context!(Bolt11InvoiceContext, [ + // Byte 0 + , + // Byte 1 + VariableLengthOnion | PaymentSecret, + // Byte 2 + BasicMPP, + // Byte 3 + , + // Byte 4 + , + // Byte 5 + , + // Byte 6 + PaymentMetadata, + ]); + 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 { - required_features: [ - // Byte 0 - , - // Byte 1 - StaticRemoteKey, - // Byte 2 - , - // Byte 3 - , - // Byte 4 - , - // Byte 5 - SCIDPrivacy, - ], - optional_features: [ - // Byte 0 - , - // Byte 1 - , - // Byte 2 - , - // Byte 3 - , - // Byte 4 - , - // Byte 5 - , - ], - }); + define_context!(ChannelTypeContext, [ + // Byte 0 + , + // Byte 1 + StaticRemoteKey, + // Byte 2 + AnchorsNonzeroFeeHtlcTx | AnchorsZeroFeeHtlcTx, + // Byte 3 + Taproot, + // Byte 4 + , + // Byte 5 + SCIDPrivacy, + // Byte 6 + ZeroConf, + ]); /// Defines a feature with the given bits for the specified [`Context`]s. The generated trait is /// useful for manipulating feature flags. @@ -244,7 +225,7 @@ mod sealed { /// /// See [BOLT #9] for details. /// - /// [BOLT #9]: https://github.com/lightningnetwork/lightning-rfc/blob/master/09-features.md + /// [BOLT #9]: https://github.com/lightning/bolts/blob/master/09-features.md pub trait $feature: Context { /// The bit used to signify that the feature is required. const EVEN_BIT: usize = $odd_bit - 1; @@ -262,6 +243,12 @@ mod sealed { /// [`ODD_BIT`]: #associatedconstant.ODD_BIT const ASSERT_ODD_BIT_PARITY: usize; + /// Assertion that the bits are set in the context's [`KNOWN_FEATURE_MASK`]. + /// + /// [`KNOWN_FEATURE_MASK`]: Context::KNOWN_FEATURE_MASK + #[cfg(not(test))] // We violate this constraint with `UnknownFeature` + const ASSERT_BITS_IN_MASK: u8; + /// The byte where the feature is set. const BYTE_OFFSET: usize = Self::EVEN_BIT / 8; @@ -297,6 +284,7 @@ mod sealed { } flags[Self::BYTE_OFFSET] |= Self::REQUIRED_MASK; + flags[Self::BYTE_OFFSET] &= !Self::OPTIONAL_MASK; } /// Sets the feature's optional (odd) bit in the given flags. @@ -348,6 +336,12 @@ mod sealed { // ODD_BIT % 2 == 1 const ASSERT_ODD_BIT_PARITY: usize = (::ODD_BIT % 2) - 1; + + // (byte & (REQUIRED_MASK | OPTIONAL_MASK)) >> (EVEN_BIT % 8) == 3 + #[cfg(not(test))] // We violate this constraint with `UnknownFeature` + const ASSERT_BITS_IN_MASK: u8 = + ((<$context>::KNOWN_FEATURE_MASK[::BYTE_OFFSET] & (::REQUIRED_MASK | ::OPTIONAL_MASK)) + >> (::EVEN_BIT % 8)) - 3; } )* }; @@ -377,38 +371,60 @@ 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], + 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); define_feature!(45, ChannelType, [InitContext, NodeContext], "Feature flags for `option_channel_type`.", set_channel_type_optional, set_channel_type_required, supports_channel_type, requires_channel_type); 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, [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], + "Feature flags for accepting channels with zero confirmations. Called `option_zeroconf` in the BOLTs", + set_zero_conf_optional, set_zero_conf_required, supports_zero_conf, requires_zero_conf); define_feature!(55, Keysend, [NodeContext], "Feature flags for keysend payments.", set_keysend_optional, set_keysend_required, supports_keysend, requires_keysend); + // Note: update the module-level docs when a new feature bit is added! #[cfg(test)] - define_feature!(123456789, UnknownFeature, [NodeContext, ChannelContext, InvoiceContext], + define_feature!(123456789, UnknownFeature, + [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); } @@ -416,7 +432,7 @@ mod sealed { /// Tracks the set of features which a node implements, templated by the context in which it /// appears. /// -/// (C-not exported) as we map the concrete feature types below directly instead +/// This is not exported to bindings users as we map the concrete feature types below directly instead #[derive(Eq)] pub struct Features { /// Note that, for convenience, flags is LITTLE endian (despite being big-endian on the wire) @@ -424,6 +440,25 @@ pub struct Features { mark: PhantomData, } +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, 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 + } +} + impl Clone for Features { fn clone(&self) -> Self { Self { @@ -434,12 +469,34 @@ 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 { + fn partial_cmp(&self, other: &Self) -> Option { + self.flags.partial_cmp(&other.flags) + } +} +impl Ord for Features { + fn cmp(&self, other: &Self) -> cmp::Ordering { + self.flags.cmp(&other.flags) } } impl fmt::Debug for Features { @@ -455,7 +512,15 @@ 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`. +pub type InvoiceRequestFeatures = Features; +/// Features used within an `invoice`. +pub type Bolt12InvoiceFeatures = Features; +/// Features used within BOLT 4 encrypted_data_tlv and BOLT 12 blinded_payinfo +pub type BlindedHopFeatures = Features; /// Features used within the channel_type field in an OpenChannel message. /// @@ -486,16 +551,6 @@ impl InitFeatures { Ok(()) } - /// or's another InitFeatures into this one. - pub(crate) fn or(mut self, o: InitFeatures) -> InitFeatures { - let total_feature_len = cmp::max(self.flags.len(), o.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; - } - self - } - /// Converts `InitFeatures` to `Features`. Only known `InitFeatures` relevant to context `C` /// are included in the result. pub(crate) fn to_context(&self) -> Features { @@ -503,8 +558,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() @@ -514,22 +569,36 @@ 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. + pub(crate) fn to_context(&self) -> Features { + self.to_context_internal() + } +} + impl ChannelTypeFeatures { - /// Constructs the implicit channel type based on the common supported types between us and our - /// counterparty - pub(crate) fn from_counterparty_init(counterparty_init: &InitFeatures) -> Self { - let mut ret = counterparty_init.to_context_internal(); + // Maps the relevant `InitFeatures` to `ChannelTypeFeatures`. Any unknown features to + // `ChannelTypeFeatures` are not included in the result. + pub(crate) fn from_init(init: &InitFeatures) -> Self { + let mut ret = init.to_context_internal(); // ChannelTypeFeatures must only contain required bits, so we OR the required forms of all // optional bits and then AND out the optional ones. for byte in ret.flags.iter_mut() { @@ -545,9 +614,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 @@ -577,16 +654,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; @@ -605,7 +682,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)) } } @@ -618,14 +695,6 @@ impl Features { } } - /// Creates a Features with the bits set which are known by the implementation - pub fn known() -> Self { - Self { - flags: T::KNOWN_FEATURE_FLAGS.to_vec(), - mark: PhantomData, - } - } - /// Converts `Features` to `Features`. Only known `T` features relevant to context `C` are /// included in the result. fn to_context_internal(&self) -> Features { @@ -644,7 +713,8 @@ impl Features { /// Create a Features given a set of flags, in little-endian. This is in reverse byte order from /// most on-the-wire encodings. - /// (C-not exported) as we don't support export across multiple T + /// + /// This is not exported to bindings users as we don't support export across multiple T pub fn from_le_bytes(flags: Vec) -> Features { Features { flags, @@ -665,7 +735,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, @@ -677,6 +751,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 { @@ -707,32 +800,107 @@ impl Features { (byte & unknown_features) != 0 }) } -} -impl Features { - #[cfg(test)] - pub(crate) fn clear_upfront_shutdown_script(mut self) -> Self { - ::clear_bits(&mut self.flags); - self + // Returns true if the features within `self` are a subset of the features within `other`. + pub(crate) fn is_subset(&self, other: &Self) -> bool { + for (idx, byte) in self.flags.iter().enumerate() { + if let Some(other_byte) = other.flags.get(idx) { + if byte & other_byte != *byte { + // `self` has bits set that `other` doesn't. + return false; + } + } else { + if *byte > 0 { + // `self` has a non-zero byte that `other` doesn't. + return false; + } + } + } + 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))) + } -impl Features { - #[cfg(test)] - pub(crate) fn clear_gossip_queries(mut self) -> Self { - ::clear_bits(&mut self.flags); - self + 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 { - // We are no longer setting initial_routing_sync now that gossip_queries - // is enabled. This feature is ignored by a peer when gossip_queries has - // been negotiated. +impl Features { #[cfg(test)] - pub(crate) fn clear_initial_routing_sync(&mut self) { - ::clear_bits(&mut self.flags) + pub(crate) fn clear_upfront_shutdown_script(mut self) -> Self { + ::clear_bits(&mut self.flags); + self } } @@ -752,6 +920,34 @@ impl Features { } } +impl Features { + pub(crate) fn clear_scid_privacy(&mut self) { + ::clear_bits(&mut self.flags); + } +} + +impl Features { + pub(crate) fn clear_anchors_zero_fee_htlc_tx(&mut self) { + ::clear_bits(&mut self.flags); + } +} + +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 { + let mut features = Self::empty(); + features.set_unknown_feature_required(); + features + } +} + macro_rules! impl_feature_len_prefixed_write { ($features: ident) => { impl Writeable for $features { @@ -770,101 +966,49 @@ 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); -// Because ChannelTypeFeatures only appears inside of TLVs, it doesn't have a length prefix when -// serialized. Thus, we can't use `impl_feature_len_prefixed_write`, above, and have to write our -// own serialization. -impl Writeable for ChannelTypeFeatures { +// 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(&self, w: &mut W) -> Result<(), io::Error> { + WithoutLength(self).write(w) + } + } + impl Readable for $features { + fn read(r: &mut R) -> Result { + Ok(WithoutLength::::read(r)?.0) + } + } + } +} + +impl_feature_tlv_write!(ChannelTypeFeatures); + +// 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 Writeable for WithoutLength<&Features> { fn write(&self, w: &mut W) -> Result<(), io::Error> { - self.write_be(w) + self.0.write_be(w) } } -impl Readable for ChannelTypeFeatures { + +impl Readable for WithoutLength> { fn read(r: &mut R) -> Result { let v = io_extras::read_to_end(r)?; - Ok(Self::from_be_bytes(v)) + Ok(WithoutLength(Features::::from_be_bytes(v))) } } #[cfg(test)] mod tests { - use super::{ChannelFeatures, ChannelTypeFeatures, InitFeatures, InvoiceFeatures, NodeFeatures}; + use super::{ChannelFeatures, ChannelTypeFeatures, InitFeatures, Bolt11InvoiceFeatures, NodeFeatures, OfferFeatures, sealed}; use bitcoin::bech32::{Base32Len, FromBase32, ToBase32, u5}; - - #[test] - fn sanity_test_known_features() { - assert!(!ChannelFeatures::known().requires_unknown_bits()); - assert!(!ChannelFeatures::known().supports_unknown_bits()); - assert!(!InitFeatures::known().requires_unknown_bits()); - assert!(!InitFeatures::known().supports_unknown_bits()); - assert!(!NodeFeatures::known().requires_unknown_bits()); - assert!(!NodeFeatures::known().supports_unknown_bits()); - - assert!(InitFeatures::known().supports_upfront_shutdown_script()); - assert!(NodeFeatures::known().supports_upfront_shutdown_script()); - assert!(!InitFeatures::known().requires_upfront_shutdown_script()); - assert!(!NodeFeatures::known().requires_upfront_shutdown_script()); - - assert!(InitFeatures::known().supports_gossip_queries()); - assert!(NodeFeatures::known().supports_gossip_queries()); - assert!(!InitFeatures::known().requires_gossip_queries()); - assert!(!NodeFeatures::known().requires_gossip_queries()); - - assert!(InitFeatures::known().supports_data_loss_protect()); - assert!(NodeFeatures::known().supports_data_loss_protect()); - assert!(!InitFeatures::known().requires_data_loss_protect()); - assert!(!NodeFeatures::known().requires_data_loss_protect()); - - assert!(InitFeatures::known().supports_variable_length_onion()); - assert!(NodeFeatures::known().supports_variable_length_onion()); - assert!(InvoiceFeatures::known().supports_variable_length_onion()); - assert!(InitFeatures::known().requires_variable_length_onion()); - assert!(NodeFeatures::known().requires_variable_length_onion()); - assert!(InvoiceFeatures::known().requires_variable_length_onion()); - - assert!(InitFeatures::known().supports_static_remote_key()); - assert!(NodeFeatures::known().supports_static_remote_key()); - assert!(InitFeatures::known().requires_static_remote_key()); - assert!(NodeFeatures::known().requires_static_remote_key()); - - assert!(InitFeatures::known().supports_payment_secret()); - assert!(NodeFeatures::known().supports_payment_secret()); - assert!(InvoiceFeatures::known().supports_payment_secret()); - assert!(InitFeatures::known().requires_payment_secret()); - assert!(NodeFeatures::known().requires_payment_secret()); - assert!(InvoiceFeatures::known().requires_payment_secret()); - - assert!(InitFeatures::known().supports_basic_mpp()); - assert!(NodeFeatures::known().supports_basic_mpp()); - assert!(InvoiceFeatures::known().supports_basic_mpp()); - assert!(!InitFeatures::known().requires_basic_mpp()); - assert!(!NodeFeatures::known().requires_basic_mpp()); - assert!(!InvoiceFeatures::known().requires_basic_mpp()); - - assert!(InitFeatures::known().supports_channel_type()); - assert!(NodeFeatures::known().supports_channel_type()); - assert!(!InitFeatures::known().requires_channel_type()); - assert!(!NodeFeatures::known().requires_channel_type()); - - assert!(InitFeatures::known().supports_shutdown_anysegwit()); - assert!(NodeFeatures::known().supports_shutdown_anysegwit()); - - assert!(InitFeatures::known().supports_scid_privacy()); - assert!(NodeFeatures::known().supports_scid_privacy()); - assert!(!InitFeatures::known().requires_scid_privacy()); - assert!(!NodeFeatures::known().requires_scid_privacy()); - - assert!(InitFeatures::known().supports_wumbo()); - assert!(NodeFeatures::known().supports_wumbo()); - assert!(!InitFeatures::known().requires_wumbo()); - assert!(!NodeFeatures::known().requires_wumbo()); - - let mut init_features = InitFeatures::known(); - assert!(init_features.initial_routing_sync()); - init_features.clear_initial_routing_sync(); - assert!(!init_features.initial_routing_sync()); - } + use crate::util::ser::{Readable, WithoutLength, Writeable}; #[test] fn sanity_test_unknown_bits() { @@ -883,9 +1027,63 @@ 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 init_features = InitFeatures::known().clear_upfront_shutdown_script().clear_gossip_queries(); + let mut init_features = InitFeatures::empty(); + // Set a bunch of features we use, plus initial_routing_sync_required (which shouldn't get + // converted as it's only relevant in an init context). + init_features.set_initial_routing_sync_required(); + init_features.set_data_loss_protect_required(); + init_features.set_variable_length_onion_required(); + init_features.set_static_remote_key_required(); + 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(); + assert!(init_features.initial_routing_sync()); assert!(!init_features.supports_upfront_shutdown_script()); assert!(!init_features.supports_gossip_queries()); @@ -893,19 +1091,21 @@ mod tests { let node_features: NodeFeatures = init_features.to_context(); { // Check that the flags are as expected: - // - option_data_loss_protect + // - 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 - assert_eq!(node_features.flags.len(), 6); - assert_eq!(node_features.flags[0], 0b00000010); + // - option_zeroconf + assert_eq!(node_features.flags.len(), 7); + assert_eq!(node_features.flags[0], 0b00000001); assert_eq!(node_features.flags[1], 0b01010001); - assert_eq!(node_features.flags[2], 0b00001010); - assert_eq!(node_features.flags[3], 0b00001000); - assert_eq!(node_features.flags[4], 0b00000000); + assert_eq!(node_features.flags[2], 0b10001010); + 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); } // Check that cleared flags are kept blank when converting back: @@ -921,23 +1121,75 @@ mod tests { #[test] fn convert_to_context_with_unknown_flags() { // Ensure the `from` context has fewer known feature bytes than the `to` context. - assert!(InvoiceFeatures::known().flags.len() < NodeFeatures::known().flags.len()); - let mut invoice_features = InvoiceFeatures::known(); - invoice_features.set_unknown_feature_optional(); - assert!(invoice_features.supports_unknown_bits()); - let node_features: NodeFeatures = invoice_features.to_context(); - assert!(!node_features.supports_unknown_bits()); + assert!(::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: 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] + 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::::read(&mut &serialized_features[..]).unwrap().0; + assert_eq!(features, deserialized_features); } #[test] @@ -957,7 +1209,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); @@ -967,19 +1219,41 @@ 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(); - let converted_features = ChannelTypeFeatures::from_counterparty_init(&init_features); + let converted_features = ChannelTypeFeatures::from_init(&init_features); assert_eq!(converted_features, ChannelTypeFeatures::only_static_remote_key()); 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()); + } }