Skip to content

Commit df1f981

Browse files
committed
Fix Feature eq + hash to ignore excess zero bytes
If we get a `Feature` object which has excess zero bytes, we shouldn't consider it a different `Feature` from another with the same bits set, but no excess zero bytes. Here we fix both the `Hash` and `PartialEq` implementation for `Features` to ignore excess zero bytes.
1 parent 4deb263 commit df1f981

File tree

1 file changed

+36
-2
lines changed

1 file changed

+36
-2
lines changed

lightning/src/ln/features.rs

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -469,12 +469,24 @@ impl<T: sealed::Context> Clone for Features<T> {
469469
}
470470
impl<T: sealed::Context> Hash for Features<T> {
471471
fn hash<H: Hasher>(&self, hasher: &mut H) {
472-
self.flags.hash(hasher);
472+
let mut nonzero_flags = &self.flags[..];
473+
while nonzero_flags.last() == Some(&0) {
474+
nonzero_flags = &nonzero_flags[..nonzero_flags.len() - 1];
475+
}
476+
nonzero_flags.hash(hasher);
473477
}
474478
}
475479
impl<T: sealed::Context> PartialEq for Features<T> {
476480
fn eq(&self, o: &Self) -> bool {
477-
self.flags.eq(&o.flags)
481+
let mut o_iter = o.flags.iter();
482+
let mut self_iter = self.flags.iter();
483+
loop {
484+
match (o_iter.next(), self_iter.next()) {
485+
(Some(o), Some(us)) => if o != us { return false },
486+
(Some(b), None) | (None, Some(b)) => if *b != 0 { return false },
487+
(None, None) => return true,
488+
}
489+
}
478490
}
479491
}
480492
impl<T: sealed::Context> PartialOrd for Features<T> {
@@ -1215,4 +1227,26 @@ mod tests {
12151227
assert!(!converted_features.supports_any_optional_bits());
12161228
assert!(converted_features.requires_static_remote_key());
12171229
}
1230+
1231+
#[test]
1232+
#[cfg(feature = "std")]
1233+
fn test_excess_zero_bytes_ignored() {
1234+
// Checks that `Hash` and `PartialEq` ignore excess zero bytes, which may appear due to
1235+
// feature conversion or because a peer serialized their feature poorly.
1236+
use std::collections::hash_map::DefaultHasher;
1237+
use std::hash::{Hash, Hasher};
1238+
1239+
let mut zerod_features = InitFeatures::empty();
1240+
zerod_features.flags = vec![0];
1241+
let empty_features = InitFeatures::empty();
1242+
assert!(empty_features.flags.is_empty());
1243+
1244+
assert_eq!(zerod_features, empty_features);
1245+
1246+
let mut zerod_hash = DefaultHasher::new();
1247+
zerod_features.hash(&mut zerod_hash);
1248+
let mut empty_hash = DefaultHasher::new();
1249+
empty_features.hash(&mut empty_hash);
1250+
assert_eq!(zerod_hash.finish(), empty_hash.finish());
1251+
}
12181252
}

0 commit comments

Comments
 (0)