//! (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
//! 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).
//!
//! LDK knows about the following features, but does not support them:
//! - `AnchorsNonzeroFeeHtlcTx` - the initial version of anchor outputs, which was later found to be
// Byte 2
BasicMPP | Wumbo | AnchorsNonzeroFeeHtlcTx | AnchorsZeroFeeHtlcTx,
// Byte 3
- ShutdownAnySegwit | Taproot,
+ RouteBlinding | ShutdownAnySegwit | Taproot,
// Byte 4
OnionMessages,
// Byte 5
// Byte 2
BasicMPP | Wumbo | AnchorsNonzeroFeeHtlcTx | AnchorsZeroFeeHtlcTx,
// Byte 3
- ShutdownAnySegwit | Taproot,
+ RouteBlinding | ShutdownAnySegwit | Taproot,
// Byte 4
OnionMessages,
// Byte 5
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);
}
impl<T: sealed::Context> Hash for Features<T> {
fn hash<H: Hasher>(&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<T: sealed::Context> PartialEq for Features<T> {
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<T: sealed::Context> PartialOrd for Features<T> {
Ok(())
}
- fn from_be_bytes(mut flags: Vec<u8>) -> Features<T> {
+ /// 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<u8>) -> Features<T> {
flags.reverse(); // Swap to little-endian
Self {
flags,
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();
// 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 | anchors_zero_fee_htlc_tx
- // - 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
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);
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());
+ }
}