Skip to content

Commit

Permalink
Seal the features contexts
Browse files Browse the repository at this point in the history
  • Loading branch information
TheBlueMatt committed Jan 7, 2020
1 parent 0e6f437 commit 90421e5
Showing 1 changed file with 89 additions and 93 deletions.
182 changes: 89 additions & 93 deletions lightning/src/ln/features.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,57 +8,32 @@ 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 {}
mod sealed { // You should just use the type aliases instead.
pub struct InitContext {}
pub struct NodeContext {}
pub struct ChannelContext {}

/// An internal trait capturing the various feature context types
pub trait Context {}
impl Context for InitContext {}
impl Context for NodeContext {}
impl Context for ChannelContext {}

pub trait DataLossProtect: Context {}
impl DataLossProtect for InitContext {}
impl DataLossProtect for NodeContext {}

pub trait InitialRoutingSync: Context {}
impl InitialRoutingSync for InitContext {}

pub trait UpfrontShutdownScript: Context {}
impl UpfrontShutdownScript for InitContext {}
impl UpfrontShutdownScript for NodeContext {}
}

/// Tracks the set of features which a node implements, templated by the context in which it
/// appears.
pub struct Features<T: FeatureContext> {
pub struct Features<T: sealed::Context> {
#[cfg(not(test))]
/// Note that, for convinience, flags is LITTLE endian (despite being big-endian on the wire)
flags: Vec<u8>,
Expand All @@ -72,69 +47,112 @@ pub struct Features<T: FeatureContext> {
pub mark: PhantomData<T>,
}

impl<T: FeatureContext> Clone for Features<T> {
impl<T: sealed::Context> Clone for Features<T> {
fn clone(&self) -> Self {
Self {
flags: self.flags.clone(),
mark: PhantomData,
}
}
}
impl<T: FeatureContext> PartialEq for Features<T> {
impl<T: sealed::Context> PartialEq for Features<T> {
fn eq(&self, o: &Self) -> bool {
self.flags.eq(&o.flags)
}
}
impl<T: FeatureContext> fmt::Debug for Features<T> {
impl<T: sealed::Context> fmt::Debug for Features<T> {
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<FeatureContextInit>;
pub type InitFeatures = Features<sealed::InitContext>;
/// A feature message as it appears in a node_announcement message
pub type NodeFeatures = Features<FeatureContextNode>;
pub type NodeFeatures = Features<sealed::NodeContext>;
/// A feature message as it appears in a channel_announcement message
pub type ChannelFeatures = Features<FeatureContextChannel>;
pub type ChannelFeatures = Features<sealed::ChannelContext>;

impl<T: FeatureContextInitNode> Features<T> {
impl InitFeatures {
/// Create a Features with the features we support
#[cfg(not(feature = "fuzztarget"))]
pub(crate) fn our_features() -> Features<T> {
Features {
pub(crate) fn our_features() -> InitFeatures {
InitFeatures {
flags: vec![2 | 1 << 5],
mark: PhantomData,
}
}
#[cfg(feature = "fuzztarget")]
pub fn our_features() -> Features<T> {
Features {
pub fn our_features() -> InitFeatures {
InitFeatures {
flags: vec![2 | 1 << 5],
mark: PhantomData,
}
}

/// Writes all features present up to, and including, 13.
pub(crate) fn write_up_to_13<W: Writer>(&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 {
// On byte 1, check (1 << 14) - 1 up all bits-up-to-but-not-including-bit-14.
(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) {
let total_feature_len = cmp::max(self.flags.len(), o.flags.len());
self.flags.resize(total_feature_len, 0u8);
for (byte, o_byte) in self.flags.iter_mut().zip(o.flags.iter()) {
*byte |= *o_byte;
}
}
}

impl Features<FeatureContextChannel> {
impl ChannelFeatures {
/// Create a Features with the features we support
#[cfg(not(feature = "fuzztarget"))]
pub(crate) fn our_features() -> Features<FeatureContextChannel> {
Features {
pub(crate) fn our_features() -> ChannelFeatures {
ChannelFeatures {
flags: Vec::new(),
mark: PhantomData,
}
}
#[cfg(feature = "fuzztarget")]
pub fn our_features() -> Features<FeatureContextChannel> {
Features {
pub fn our_features() -> ChannelFeatures {
ChannelFeatures {
flags: Vec::new(),
mark: PhantomData,
}
}
}

impl<T: FeatureContext> Features<T> {
impl NodeFeatures {
/// Create a Features with the features we support
#[cfg(not(feature = "fuzztarget"))]
pub(crate) fn our_features() -> NodeFeatures {
NodeFeatures {
flags: vec![2 | 1 << 5],
mark: PhantomData,
}
}
#[cfg(feature = "fuzztarget")]
pub fn our_features() -> NodeFeatures {
NodeFeatures {
flags: vec![2 | 1 << 5],
mark: PhantomData,
}
}
}

impl<T: sealed::Context> Features<T> {
/// Create a blank Features with no fetures set
pub fn empty() -> Features<T> {
Features {
Expand Down Expand Up @@ -179,11 +197,13 @@ impl<T: FeatureContext> Features<T> {
}
}

impl<T: FeatureContextInitNode> Features<T> {
impl<T: sealed::DataLossProtect> Features<T> {
pub(crate) fn supports_data_loss_protect(&self) -> bool {
self.flags.len() > 0 && (self.flags[0] & 3) != 0
}
}

impl<T: sealed::UpfrontShutdownScript> Features<T> {
pub(crate) fn supports_upfront_shutdown_script(&self) -> bool {
self.flags.len() > 0 && (self.flags[0] & (3 << 4)) != 0
}
Expand All @@ -193,7 +213,7 @@ impl<T: FeatureContextInitNode> Features<T> {
}
}

impl Features<FeatureContextInit> {
impl<T: sealed::InitialRoutingSync> Features<T> {
pub(crate) fn initial_routing_sync(&self) -> bool {
self.flags.len() > 0 && (self.flags[0] & (1 << 3)) != 0
}
Expand All @@ -204,33 +224,9 @@ impl Features<FeatureContextInit> {
self.flags[0] |= 1 << 3;
}
}

/// Writes all features present up to, and including, 13.
pub(crate) fn write_up_to_13<W: Writer>(&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) {
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;
}
}
}

impl<T: FeatureContext> Writeable for Features<T> {
impl<T: sealed::Context> Writeable for Features<T> {
fn write<W: Writer>(&self, w: &mut W) -> Result<(), ::std::io::Error> {
w.size_hint(self.flags.len() + 2);
(self.flags.len() as u16).write(w)?;
Expand All @@ -241,7 +237,7 @@ impl<T: FeatureContext> Writeable for Features<T> {
}
}

impl<R: ::std::io::Read, T: FeatureContext> Readable<R> for Features<T> {
impl<R: ::std::io::Read, T: sealed::Context> Readable<R> for Features<T> {
fn read(r: &mut R) -> Result<Self, DecodeError> {
let mut flags: Vec<u8> = Readable::read(r)?;
flags.reverse(); // Swap to little-endian
Expand Down

0 comments on commit 90421e5

Please sign in to comment.