Skip to content

Commit 53af4ee

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 4d9f0a1 commit 53af4ee

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 | InitialRoutingSync | UpfrontShutdownScript,
85+
// Byte 1
86+
VariableLengthOnion | PaymentSecret,
87+
// Byte 2
88+
BasicMPP,
89+
],
90+
unsupported_features: [
91+
// Byte 0
92+
,
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.
@@ -122,20 +211,6 @@ mod sealed { // You should just use the type aliases instead.
122211
"Feature flags for `payment_secret`.");
123212
define_feature!(17, BasicMPP, [InitContext, NodeContext],
124213
"Feature flags for `basic_mpp`.");
125-
126-
/// Generates a feature flag byte with the given features set as optional. Useful for initializing
127-
/// the flags within [`Features`].
128-
///
129-
/// [`Features`]: struct.Features.html
130-
macro_rules! feature_flags {
131-
($context: ty; $($feature: ident)|*) => {
132-
(0b00_00_00_00
133-
$(
134-
| <$context as sealed::$feature>::OPTIONAL_MASK
135-
)*
136-
)
137-
}
138-
}
139214
}
140215

141216
/// Tracks the set of features which a node implements, templated by the context in which it
@@ -173,18 +248,6 @@ pub type NodeFeatures = Features<sealed::NodeContext>;
173248
pub type ChannelFeatures = Features<sealed::ChannelContext>;
174249

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

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

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

283296
let mut flags = Vec::new();
284-
for (i, feature_byte)in init_ctx.flags.iter().enumerate() {
285-
match i {
286-
0 => flags.push(feature_byte & features_including![DataLossProtect | UpfrontShutdownScript]),
287-
1 => flags.push(feature_byte & features_including![VariableLengthOnion | PaymentSecret]),
288-
2 => flags.push(feature_byte & features_including![BasicMPP]),
289-
_ => (),
297+
for (i, feature_byte) in init_ctx.flags.iter().enumerate() {
298+
if i < byte_count {
299+
flags.push(feature_byte & sealed::NodeContext::KNOWN_FEATURE_MASK[i]);
290300
}
291301
}
292302
Self { flags, mark: PhantomData, }
@@ -302,6 +312,14 @@ impl<T: sealed::Context> Features<T> {
302312
}
303313
}
304314

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

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

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

0 commit comments

Comments
 (0)