Skip to content

Commit 8e37543

Browse files
committed
Remove duplicate specification of features
Features for a given context are duplicated throughout the features module. Use a macro for defining a Context and the applicable features such that features only need to be defined for a Context in one place. The Context provides bitmasks for selecting known and unknown feature flags.
1 parent 3b52b2c commit 8e37543

File tree

1 file changed

+138
-134
lines changed

1 file changed

+138
-134
lines changed

lightning/src/ln/features.rs

Lines changed: 138 additions & 134 deletions
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,106 @@ use std::marker::PhantomData;
1818
use ln::msgs::DecodeError;
1919
use util::ser::{Readable, Writeable, Writer};
2020

21-
#[macro_use]
22-
mod sealed { // You should just use the type aliases instead.
23-
pub struct InitContext {}
24-
pub struct NodeContext {}
25-
pub struct ChannelContext {}
26-
27-
/// An internal trait capturing the various feature context types
28-
pub trait Context {}
29-
impl Context for InitContext {}
30-
impl Context for NodeContext {}
31-
impl Context for ChannelContext {}
21+
mod sealed {
22+
/// The context in which [`Features`] are applicable. Defines which features are supported.
23+
///
24+
/// [`Features`]: ../struct.Features.html
25+
pub trait Context {
26+
/// Features that are supported by the implementation, indicated by setting their
27+
/// optional (odd) bits.
28+
const SUPPORTED_FEATURE_FLAGS: &'static [u8];
29+
30+
/// Bitmask for selecting features that are known though not necessarily implemented.
31+
const KNOWN_FEATURE_MASK: &'static [u8];
32+
33+
/// Bitmask for selecting features that are unknown to the implementation.
34+
const UNKNOWN_FEATURE_MASK: &'static [u8];
35+
}
36+
37+
/// Defines a [`Context`] by stating which features it does and does not support. Features are
38+
/// specified as a comma-separated list of bytes where each byte is a pipe-delimited list of
39+
/// feature identifiers.
40+
///
41+
/// [`Context`]: trait.Context.html
42+
macro_rules! define_context {
43+
($context: ident {
44+
supported_features: [$( $( $supported_feature: ident )|*, )*],
45+
unsupported_features: [$( $( $unsupported_feature: ident )|*, )*],
46+
}) => {
47+
pub struct $context {}
48+
49+
impl Context for $context {
50+
const SUPPORTED_FEATURE_FLAGS: &'static [u8] = &[
51+
$(
52+
0b00_00_00_00 $(| <Self as $supported_feature>::OPTIONAL_MASK)*,
53+
)*
54+
];
55+
56+
const KNOWN_FEATURE_MASK: &'static [u8] = &[
57+
$(
58+
0b00_00_00_00 $(|
59+
<Self as $supported_feature>::REQUIRED_MASK |
60+
<Self as $supported_feature>::OPTIONAL_MASK)*
61+
$(|
62+
<Self as $unsupported_feature>::REQUIRED_MASK |
63+
<Self as $unsupported_feature>::OPTIONAL_MASK)*,
64+
)*
65+
];
66+
67+
const UNKNOWN_FEATURE_MASK: &'static [u8] = &[
68+
$(
69+
0b11_11_11_11 $(&
70+
!<Self as $supported_feature>::REQUIRED_MASK &
71+
!<Self as $supported_feature>::OPTIONAL_MASK)*
72+
$(&
73+
!<Self as $unsupported_feature>::REQUIRED_MASK &
74+
!<Self as $unsupported_feature>::OPTIONAL_MASK)*,
75+
)*
76+
];
77+
}
78+
};
79+
}
80+
81+
define_context!(InitContext {
82+
supported_features: [
83+
// Byte 0
84+
DataLossProtect | UpfrontShutdownScript,
85+
// Byte 1
86+
VariableLengthOnion | PaymentSecret,
87+
// Byte 2
88+
BasicMPP,
89+
],
90+
unsupported_features: [
91+
// Byte 0
92+
InitialRoutingSync,
93+
// Byte 1
94+
,
95+
// Byte 2
96+
,
97+
],
98+
});
99+
define_context!(NodeContext {
100+
supported_features: [
101+
// Byte 0
102+
DataLossProtect | UpfrontShutdownScript,
103+
// Byte 1
104+
VariableLengthOnion | PaymentSecret,
105+
// Byte 2
106+
BasicMPP,
107+
],
108+
unsupported_features: [
109+
// Byte 0
110+
,
111+
// Byte 1
112+
,
113+
// Byte 2
114+
,
115+
],
116+
});
117+
define_context!(ChannelContext {
118+
supported_features: [],
119+
unsupported_features: [],
120+
});
32121

33122
/// Defines a feature with the given bits for the specified [`Context`]s. The generated trait is
34123
/// useful for manipulating feature flags.
@@ -120,20 +209,6 @@ mod sealed { // You should just use the type aliases instead.
120209
"Feature flags for `payment_secret`.");
121210
define_feature!(16, 17, BasicMPP, [InitContext, NodeContext],
122211
"Feature flags for `basic_mpp`.");
123-
124-
/// Generates a feature flag byte with the given features set as optional. Useful for initializing
125-
/// the flags within [`Features`].
126-
///
127-
/// [`Features`]: struct.Features.html
128-
macro_rules! feature_flags {
129-
($context: ty; $($feature: ident)|*) => {
130-
(0b00_00_00_00
131-
$(
132-
| <$context as sealed::$feature>::OPTIONAL_MASK
133-
)*
134-
)
135-
}
136-
}
137212
}
138213

139214
/// Tracks the set of features which a node implements, templated by the context in which it
@@ -171,18 +246,6 @@ pub type NodeFeatures = Features<sealed::NodeContext>;
171246
pub type ChannelFeatures = Features<sealed::ChannelContext>;
172247

173248
impl InitFeatures {
174-
/// Create a Features with the features we support
175-
pub fn supported() -> InitFeatures {
176-
InitFeatures {
177-
flags: vec![
178-
feature_flags![sealed::InitContext; DataLossProtect | UpfrontShutdownScript],
179-
feature_flags![sealed::InitContext; VariableLengthOnion | PaymentSecret],
180-
feature_flags![sealed::InitContext; BasicMPP],
181-
],
182-
mark: PhantomData,
183-
}
184-
}
185-
186249
/// Writes all features present up to, and including, 13.
187250
pub(crate) fn write_up_to_13<W: Writer>(&self, w: &mut W) -> Result<(), ::std::io::Error> {
188251
let len = cmp::min(2, self.flags.len());
@@ -212,22 +275,6 @@ impl InitFeatures {
212275
}
213276

214277
impl ChannelFeatures {
215-
/// Create a Features with the features we support
216-
#[cfg(not(feature = "fuzztarget"))]
217-
pub(crate) fn supported() -> ChannelFeatures {
218-
ChannelFeatures {
219-
flags: Vec::new(),
220-
mark: PhantomData,
221-
}
222-
}
223-
#[cfg(feature = "fuzztarget")]
224-
pub fn supported() -> ChannelFeatures {
225-
ChannelFeatures {
226-
flags: Vec::new(),
227-
mark: PhantomData,
228-
}
229-
}
230-
231278
/// Takes the flags that we know how to interpret in an init-context features that are also
232279
/// relevant in a channel-context features and creates a channel-context features from them.
233280
pub(crate) fn with_known_relevant_init_flags(_init_ctx: &InitFeatures) -> Self {
@@ -237,54 +284,17 @@ impl ChannelFeatures {
237284
}
238285

239286
impl NodeFeatures {
240-
/// Create a Features with the features we support
241-
#[cfg(not(feature = "fuzztarget"))]
242-
pub(crate) fn supported() -> NodeFeatures {
243-
NodeFeatures {
244-
flags: vec![
245-
feature_flags![sealed::NodeContext; DataLossProtect | UpfrontShutdownScript],
246-
feature_flags![sealed::NodeContext; VariableLengthOnion | PaymentSecret],
247-
feature_flags![sealed::NodeContext; BasicMPP],
248-
],
249-
mark: PhantomData,
250-
}
251-
}
252-
#[cfg(feature = "fuzztarget")]
253-
pub fn supported() -> NodeFeatures {
254-
NodeFeatures {
255-
flags: vec![
256-
feature_flags![sealed::NodeContext; DataLossProtect | UpfrontShutdownScript],
257-
feature_flags![sealed::NodeContext; VariableLengthOnion | PaymentSecret],
258-
feature_flags![sealed::NodeContext; BasicMPP],
259-
],
260-
mark: PhantomData,
261-
}
262-
}
263-
264287
/// Takes the flags that we know how to interpret in an init-context features that are also
265288
/// relevant in a node-context features and creates a node-context features from them.
266289
/// Be sure to blank out features that are unknown to us.
267290
pub(crate) fn with_known_relevant_init_flags(init_ctx: &InitFeatures) -> Self {
268-
// Generates a bitmask with both even and odd bits set for the given features. Bitwise
269-
// AND-ing it with a byte will select only common features.
270-
macro_rules! features_including {
271-
($($feature: ident)|*) => {
272-
(0b00_00_00_00
273-
$(
274-
| <sealed::NodeContext as sealed::$feature>::REQUIRED_MASK
275-
| <sealed::NodeContext as sealed::$feature>::OPTIONAL_MASK
276-
)*
277-
)
278-
}
279-
}
291+
use ln::features::sealed::Context;
292+
let byte_count = sealed::NodeContext::KNOWN_FEATURE_MASK.len();
280293

281294
let mut flags = Vec::new();
282-
for (i, feature_byte)in init_ctx.flags.iter().enumerate() {
283-
match i {
284-
0 => flags.push(feature_byte & features_including![DataLossProtect | UpfrontShutdownScript]),
285-
1 => flags.push(feature_byte & features_including![VariableLengthOnion | PaymentSecret]),
286-
2 => flags.push(feature_byte & features_including![BasicMPP]),
287-
_ => (),
295+
for (i, feature_byte) in init_ctx.flags.iter().enumerate() {
296+
if i < byte_count {
297+
flags.push(feature_byte & sealed::NodeContext::KNOWN_FEATURE_MASK[i]);
288298
}
289299
}
290300
Self { flags, mark: PhantomData, }
@@ -300,6 +310,14 @@ impl<T: sealed::Context> Features<T> {
300310
}
301311
}
302312

313+
/// Creates features supported by the implementation.
314+
pub fn supported() -> Features<T> {
315+
Self {
316+
flags: T::SUPPORTED_FEATURE_FLAGS.to_vec(),
317+
mark: PhantomData,
318+
}
319+
}
320+
303321
#[cfg(test)]
304322
/// Create a Features given a set of flags, in LE.
305323
pub fn from_le_bytes(flags: Vec<u8>) -> Features<T> {
@@ -316,49 +334,35 @@ impl<T: sealed::Context> Features<T> {
316334
}
317335

318336
pub(crate) fn requires_unknown_bits(&self) -> bool {
319-
// Generates a bitmask with all even bits set except for the given features. Bitwise
320-
// AND-ing it with a byte will select unknown required features.
321-
macro_rules! features_excluding {
322-
($($feature: ident)|*) => {
323-
(0b01_01_01_01
324-
$(
325-
& !(<sealed::InitContext as sealed::$feature>::REQUIRED_MASK)
326-
)*
327-
)
328-
}
329-
}
330-
331-
self.flags.iter().enumerate().any(|(idx, &byte)| {
332-
(match idx {
333-
0 => (byte & features_excluding![DataLossProtect | InitialRoutingSync | UpfrontShutdownScript]),
334-
1 => (byte & features_excluding![VariableLengthOnion | PaymentSecret]),
335-
2 => (byte & features_excluding![BasicMPP]),
336-
_ => (byte & features_excluding![]),
337-
}) != 0
337+
use ln::features::sealed::Context;
338+
let byte_count = sealed::InitContext::UNKNOWN_FEATURE_MASK.len();
339+
340+
// Bitwise AND-ing with all even bits set except for known features will select unknown
341+
// required features.
342+
self.flags.iter().enumerate().any(|(i, &byte)| {
343+
let required_features = 0b01_01_01_01;
344+
let unknown_features = if i < byte_count {
345+
sealed::InitContext::UNKNOWN_FEATURE_MASK[i]
346+
} else {
347+
0b11_11_11_11
348+
};
349+
(byte & (required_features & unknown_features)) != 0
338350
})
339351
}
340352

341353
pub(crate) fn supports_unknown_bits(&self) -> bool {
342-
// Generates a bitmask with all even and odd bits set except for the given features. Bitwise
343-
// AND-ing it with a byte will select unknown supported features.
344-
macro_rules! features_excluding {
345-
($($feature: ident)|*) => {
346-
(0b11_11_11_11
347-
$(
348-
& !(<sealed::InitContext as sealed::$feature>::REQUIRED_MASK)
349-
& !(<sealed::InitContext as sealed::$feature>::OPTIONAL_MASK)
350-
)*
351-
)
352-
}
353-
}
354-
355-
self.flags.iter().enumerate().any(|(idx, &byte)| {
356-
(match idx {
357-
0 => (byte & features_excluding![DataLossProtect | InitialRoutingSync | UpfrontShutdownScript]),
358-
1 => (byte & features_excluding![VariableLengthOnion | PaymentSecret]),
359-
2 => (byte & features_excluding![BasicMPP]),
360-
_ => byte,
361-
}) != 0
354+
use ln::features::sealed::Context;
355+
let byte_count = sealed::InitContext::UNKNOWN_FEATURE_MASK.len();
356+
357+
// Bitwise AND-ing with all even and odd bits set except for known features will select
358+
// unknown features.
359+
self.flags.iter().enumerate().any(|(i, &byte)| {
360+
let unknown_features = if i < byte_count {
361+
sealed::InitContext::UNKNOWN_FEATURE_MASK[i]
362+
} else {
363+
0b11_11_11_11
364+
};
365+
(byte & unknown_features) != 0
362366
})
363367
}
364368

0 commit comments

Comments
 (0)