X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning%2Fsrc%2Fln%2Ffeatures.rs;h=28710c343649e813f5e9581f9fad5fa618c4430b;hb=b530cdc25227acf512b6cf32ac4fcf815c67dff9;hp=1992b8db9e565b7696e60bc286581583ca8beed7;hpb=0c250468d632ed6df93d8a9d34389a2cb61eac40;p=rust-lightning diff --git a/lightning/src/ln/features.rs b/lightning/src/ln/features.rs index 1992b8db9..28710c343 100644 --- a/lightning/src/ln/features.rs +++ b/lightning/src/ln/features.rs @@ -718,7 +718,11 @@ impl Features { Ok(()) } - fn from_be_bytes(mut flags: Vec) -> Features { + /// 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) -> Features { flags.reverse(); // Swap to little-endian Self { flags, @@ -754,16 +758,23 @@ impl Features { pub fn requires_unknown_bits(&self) -> bool { // Bitwise AND-ing with all even bits set except for known features will select required // unknown features. - let byte_count = T::KNOWN_FEATURE_MASK.len(); - self.flags.iter().enumerate().any(|(i, &byte)| { - let required_features = 0b01_01_01_01; - let unknown_features = if i < byte_count { - !T::KNOWN_FEATURE_MASK[i] - } else { - 0b11_11_11_11 - }; - (byte & (required_features & unknown_features)) != 0 - }) + let mut known_chunks = T::KNOWN_FEATURE_MASK.chunks(8); + for chunk in self.flags.chunks(8) { + let mut flag_bytes = [0; 8]; + flag_bytes[..chunk.len()].copy_from_slice(&chunk); + let flag_int = u64::from_le_bytes(flag_bytes); + + let known_chunk = known_chunks.next().unwrap_or(&[0; 0]); + let mut known_bytes = [0; 8]; + known_bytes[..known_chunk.len()].copy_from_slice(&known_chunk); + let known_int = u64::from_le_bytes(known_bytes); + + const REQ_MASK: u64 = 0x55555555_55555555; + if flag_int & (REQ_MASK & !known_int) != 0 { + return true; + } + } + false } pub(crate) fn supports_unknown_bits(&self) -> bool { @@ -798,6 +809,35 @@ impl Features { true } + /// Sets a required feature bit. Errors if `bit` is outside the feature range as defined + /// by [BOLT 9]. + /// + /// Note: Required bits are even. If an odd bit is given, then the corresponding even bit will + /// be set instead (i.e., `bit - 1`). + /// + /// [BOLT 9]: https://github.com/lightning/bolts/blob/master/09-features.md + pub fn set_required_feature_bit(&mut self, bit: usize) -> Result<(), ()> { + self.set_feature_bit(bit - (bit % 2)) + } + + /// Sets an optional feature bit. Errors if `bit` is outside the feature range as defined + /// by [BOLT 9]. + /// + /// Note: Optional bits are odd. If an even bit is given, then the corresponding odd bit will be + /// set instead (i.e., `bit + 1`). + /// + /// [BOLT 9]: https://github.com/lightning/bolts/blob/master/09-features.md + pub fn set_optional_feature_bit(&mut self, bit: usize) -> Result<(), ()> { + self.set_feature_bit(bit + (1 - (bit % 2))) + } + + fn set_feature_bit(&mut self, bit: usize) -> Result<(), ()> { + if bit > 255 { + return Err(()); + } + self.set_bit(bit, false) + } + /// 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. /// @@ -824,10 +864,13 @@ impl Features { if bit < 256 { return Err(()); } + self.set_bit(bit, true) + } + fn set_bit(&mut self, bit: usize, custom: bool) -> Result<(), ()> { let byte_offset = bit / 8; let mask = 1 << (bit - 8 * byte_offset); - if byte_offset < T::KNOWN_FEATURE_MASK.len() { + if byte_offset < T::KNOWN_FEATURE_MASK.len() && custom { if (T::KNOWN_FEATURE_MASK[byte_offset] & mask) != 0 { return Err(()); } @@ -1078,6 +1121,13 @@ mod tests { assert!(!features.requires_basic_mpp()); assert!(features.requires_payment_secret()); assert!(features.supports_payment_secret()); + + // Set flags manually + let mut features = NodeFeatures::empty(); + assert!(features.set_optional_feature_bit(55).is_ok()); + assert!(features.supports_keysend()); + assert!(features.set_optional_feature_bit(255).is_ok()); + assert!(features.set_required_feature_bit(256).is_err()); } #[test]