//! (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
+// 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).
//! - `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).
+//! 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).
//!
//! [BOLT #9]: https://github.com/lightning/bolts/blob/master/09-features.md
//! [messages]: crate::ln::msgs
VariableLengthOnion | PaymentSecret,
// Byte 2
BasicMPP,
+ // Byte 3
+ ,
+ // Byte 4
+ ,
+ // Byte 5
+ ,
+ // Byte 6
+ PaymentMetadata,
]);
define_context!(OfferContext, []);
define_context!(InvoiceRequestContext, []);
}
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.
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],
+ "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,
/// 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<T: sealed::Context> {
/// Note that, for convenience, flags is LITTLE endian (despite being big-endian on the wire)
mark: PhantomData<T>,
}
-impl <T: sealed::Context> Features<T> {
- pub(crate) fn or(mut self, o: Self) -> Self {
+impl<T: sealed::Context> core::ops::BitOr for Features<T> {
+ type Output = Self;
+
+ fn bitor(mut self, o: Self) -> Self {
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()) {
/// 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<u8>) -> Features<T> {
Features {
flags,
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: &Features<T>) -> 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 {
}
true
}
+
+ /// 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(());
+ }
+
+ let byte_offset = bit / 8;
+ let mask = 1 << (bit - 8 * byte_offset);
+ if byte_offset < T::KNOWN_FEATURE_MASK.len() {
+ 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<T: sealed::UpfrontShutdownScript> Features<T> {
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();
// 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_optional();
+ 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_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());
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
+ // - basic_mpp | wumbo | anchors_zero_fee_htlc_tx
// - opt_shutdown_anysegwit
// - onion_messages
// - option_channel_type | option_scid_alias
// - option_zeroconf
assert_eq!(node_features.flags.len(), 7);
- assert_eq!(node_features.flags[0], 0b00000010);
+ 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);
#[test]
fn convert_to_context_with_unknown_flags() {
// Ensure the `from` context has fewer known feature bytes than the `to` context.
- assert!(<sealed::InvoiceContext as sealed::Context>::KNOWN_FEATURE_MASK.len() <
- <sealed::NodeContext as sealed::Context>::KNOWN_FEATURE_MASK.len());
- let mut invoice_features = InvoiceFeatures::empty();
- 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!(<sealed::ChannelContext as sealed::Context>::KNOWN_FEATURE_MASK.len() <
+ <sealed::InvoiceContext as sealed::Context>::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();
+ assert!(!invoice_features.supports_unknown_bits());
}
#[test]
assert!(features.supports_payment_secret());
}
+ #[test]
+ fn set_custom_bits() {
+ let mut features = InvoiceFeatures::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 = <sealed::InvoiceContext as sealed::PaymentSecret>::EVEN_BIT;
+ let byte_offset = <sealed::InvoiceContext as sealed::PaymentSecret>::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 = InvoiceFeatures::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 = InvoiceFeatures::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]);