X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning%2Fsrc%2Fln%2Ffeatures.rs;fp=lightning%2Fsrc%2Fln%2Ffeatures.rs;h=ac9af1434012be9abdca4ce6bad85d76b3464345;hb=cd5a11fe0d56fc6520fe94bad2cb5ea461608442;hp=0000000000000000000000000000000000000000;hpb=7ec52c6ecbaf36f66f15302cf0b22a84f7dc0fa8;p=rust-lightning diff --git a/lightning/src/ln/features.rs b/lightning/src/ln/features.rs new file mode 100644 index 00000000..ac9af143 --- /dev/null +++ b/lightning/src/ln/features.rs @@ -0,0 +1,261 @@ +//! Lightning exposes sets of supported operations through "feature flags". This module includes +//! types to store those feature flags and query for specific flags. + +use std::{cmp, fmt}; +use std::result::Result; +use std::marker::PhantomData; + +use ln::msgs::DecodeError; +use util::ser::{Readable, Writeable, Writer}; + +/// The context in which a Feature object appears determines which bits of features the node +/// supports will be set. We use this when creating our own Feature objects to select which bits to +/// set and when passing around Feature objects to ensure the bits we're checking for are +/// available. +/// +/// This Context represents when the Feature appears in the init message, sent between peers and not +/// rumored around the P2P network. +pub struct FeatureContextInit {} +/// The context in which a Feature object appears determines which bits of features the node +/// supports will be set. We use this when creating our own Feature objects to select which bits to +/// set and when passing around Feature objects to ensure the bits we're checking for are +/// available. +/// +/// This Context represents when the Feature appears in the node_announcement message, as it is +/// rumored around the P2P network. +pub struct FeatureContextNode {} +/// The context in which a Feature object appears determines which bits of features the node +/// supports will be set. We use this when creating our own Feature objects to select which bits to +/// set and when passing around Feature objects to ensure the bits we're checking for are +/// available. +/// +/// This Context represents when the Feature appears in the ChannelAnnouncement message, as it is +/// rumored around the P2P network. +pub struct FeatureContextChannel {} +/// The context in which a Feature object appears determines which bits of features the node +/// supports will be set. We use this when creating our own Feature objects to select which bits to +/// set and when passing around Feature objects to ensure the bits we're checking for are +/// available. +/// +/// This Context represents when the Feature appears in an invoice, used to determine the different +/// options available for routing a payment. +/// +/// Note that this is currently unused as invoices come to us via a different crate and are not +/// native to rust-lightning directly. +pub struct FeatureContextInvoice {} + +/// An internal trait capturing the various future context types +pub trait FeatureContext {} +impl FeatureContext for FeatureContextInit {} +impl FeatureContext for FeatureContextNode {} +impl FeatureContext for FeatureContextChannel {} +impl FeatureContext for FeatureContextInvoice {} + +/// An internal trait capturing FeatureContextInit and FeatureContextNode +pub trait FeatureContextInitNode : FeatureContext {} +impl FeatureContextInitNode for FeatureContextInit {} +impl FeatureContextInitNode for FeatureContextNode {} + +/// Tracks the set of features which a node implements, templated by the context in which it +/// appears. +pub struct Features { + /// Note that, for convinience, flags is LITTLE endian (despite being big-endian on the wire) + flags: Vec, + mark: PhantomData, +} + +impl Clone for Features { + fn clone(&self) -> Self { + Self { + flags: self.flags.clone(), + mark: PhantomData, + } + } +} +impl PartialEq for Features { + fn eq(&self, o: &Self) -> bool { + self.flags.eq(&o.flags) + } +} +impl fmt::Debug for Features { + fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { + self.flags.fmt(fmt) + } +} + +/// A feature message as it appears in an init message +pub type InitFeatures = Features; +/// A feature message as it appears in a node_announcement message +pub type NodeFeatures = Features; +/// A feature message as it appears in a channel_announcement message +pub type ChannelFeatures = Features; + +impl Features { + /// Create a Features with the features we support + #[cfg(not(feature = "fuzztarget"))] + pub(crate) fn supported() -> Features { + Features { + flags: vec![2 | 1 << 5], + mark: PhantomData, + } + } + #[cfg(feature = "fuzztarget")] + pub fn supported() -> Features { + Features { + flags: vec![2 | 1 << 5], + mark: PhantomData, + } + } +} + +impl Features { + /// Create a Features with the features we support + #[cfg(not(feature = "fuzztarget"))] + pub(crate) fn supported() -> Features { + Features { + flags: Vec::new(), + mark: PhantomData, + } + } + #[cfg(feature = "fuzztarget")] + pub fn supported() -> Features { + Features { + flags: Vec::new(), + mark: PhantomData, + } + } +} + +impl Features { + /// Create a blank Features with no features set + pub fn empty() -> Features { + Features { + flags: Vec::new(), + mark: PhantomData, + } + } + + #[cfg(test)] + /// Create a Features given a set of flags, in LE. + pub fn from_le_bytes(flags: Vec) -> Features { + Features { + flags, + mark: PhantomData, + } + } + + #[cfg(test)] + /// Gets the underlying flags set, in LE. + pub fn le_flags(&self) -> &Vec { + &self.flags + } + + pub(crate) fn requires_unknown_bits(&self) -> bool { + self.flags.iter().enumerate().any(|(idx, &byte)| { + ( idx != 0 && (byte & 0x55) != 0 ) || ( idx == 0 && (byte & 0x14) != 0 ) + }) + } + + pub(crate) fn supports_unknown_bits(&self) -> bool { + self.flags.iter().enumerate().any(|(idx, &byte)| { + ( idx != 0 && byte != 0 ) || ( idx == 0 && (byte & 0xc4) != 0 ) + }) + } + + /// The number of bytes required to represent the feature flags present. This does not include + /// the length bytes which are included in the serialized form. + pub(crate) fn byte_count(&self) -> usize { + self.flags.len() + } + + #[cfg(test)] + pub(crate) fn set_require_unknown_bits(&mut self) { + let newlen = cmp::max(2, self.flags.len()); + self.flags.resize(newlen, 0u8); + self.flags[1] |= 0x40; + } + + #[cfg(test)] + pub(crate) fn clear_require_unknown_bits(&mut self) { + let newlen = cmp::max(2, self.flags.len()); + self.flags.resize(newlen, 0u8); + self.flags[1] &= !0x40; + if self.flags.len() == 2 && self.flags[1] == 0 { + self.flags.resize(1, 0u8); + } + } +} + +impl Features { + pub(crate) fn supports_data_loss_protect(&self) -> bool { + self.flags.len() > 0 && (self.flags[0] & 3) != 0 + } + + pub(crate) fn supports_upfront_shutdown_script(&self) -> bool { + self.flags.len() > 0 && (self.flags[0] & (3 << 4)) != 0 + } + #[cfg(test)] + pub(crate) fn unset_upfront_shutdown_script(&mut self) { + self.flags[0] ^= 1 << 5; + } +} + +impl Features { + pub(crate) fn initial_routing_sync(&self) -> bool { + self.flags.len() > 0 && (self.flags[0] & (1 << 3)) != 0 + } + pub(crate) fn set_initial_routing_sync(&mut self) { + if self.flags.len() == 0 { + self.flags.resize(1, 1 << 3); + } else { + self.flags[0] |= 1 << 3; + } + } + + /// Writes all features present up to, and including, 13. + pub(crate) fn write_up_to_13(&self, w: &mut W) -> Result<(), ::std::io::Error> { + let len = cmp::min(2, self.flags.len()); + w.size_hint(len + 2); + (len as u16).write(w)?; + for i in (0..len).rev() { + if i == 0 { + self.flags[i].write(w)?; + } else { + (self.flags[i] & ((1 << (14 - 8)) - 1)).write(w)?; + } + } + Ok(()) + } + + /// or's another InitFeatures into this one. + pub(crate) fn or(mut self, o: InitFeatures) -> InitFeatures { + let total_feature_len = cmp::max(self.flags.len(), o.flags.len()); + self.flags.resize(total_feature_len, 0u8); + for (feature, o_feature) in self.flags.iter_mut().zip(o.flags.iter()) { + *feature |= *o_feature; + } + self + } +} + +impl Writeable for Features { + fn write(&self, w: &mut W) -> Result<(), ::std::io::Error> { + w.size_hint(self.flags.len() + 2); + (self.flags.len() as u16).write(w)?; + for f in self.flags.iter().rev() { // Swap back to big-endian + f.write(w)?; + } + Ok(()) + } +} + +impl Readable for Features { + fn read(r: &mut R) -> Result { + let mut flags: Vec = Readable::read(r)?; + flags.reverse(); // Swap to little-endian + Ok(Self { + flags, + mark: PhantomData, + }) + } +}