Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement channel_type negotiation #1078

Merged
merged 4 commits into from
Nov 3, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lightning-background-processor/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ impl BackgroundProcessor {
/// functionality implemented by other handlers.
/// * [`NetGraphMsgHandler`] if given will update the [`NetworkGraph`] based on payment failures.
///
/// [top-level documentation]: Self
/// [top-level documentation]: BackgroundProcessor
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not really part of this PR, but whatever

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤷‍♂️ not worth a separate pr for it.

/// [`join`]: Self::join
/// [`stop`]: Self::stop
/// [`ChannelManager`]: lightning::ln::channelmanager::ChannelManager
Expand Down
44 changes: 43 additions & 1 deletion lightning/src/ln/channel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ use bitcoin::secp256k1::{Secp256k1,Signature};
use bitcoin::secp256k1;

use ln::{PaymentPreimage, PaymentHash};
use ln::features::{ChannelFeatures, InitFeatures};
use ln::features::{ChannelFeatures, ChannelTypeFeatures, InitFeatures};
use ln::msgs;
use ln::msgs::{DecodeError, OptionalField, DataLossProtect};
use ln::script::{self, ShutdownScript};
Expand Down Expand Up @@ -550,6 +550,9 @@ pub(super) struct Channel<Signer: Sign> {
// is fine, but as a sanity check in our failure to generate the second claim, we check here
// that the original was a claim, and that we aren't now trying to fulfill a failed HTLC.
historical_inbound_htlc_fulfills: HashSet<u64>,

/// This channel's type, as negotiated during channel open
channel_type: ChannelTypeFeatures,
}

#[cfg(any(test, feature = "fuzztarget"))]
Expand Down Expand Up @@ -775,6 +778,11 @@ impl<Signer: Sign> Channel<Signer> {

#[cfg(any(test, feature = "fuzztarget"))]
historical_inbound_htlc_fulfills: HashSet::new(),

// We currently only actually support one channel type, so don't retry with new types
// on error messages. When we support more we'll need fallback support (assuming we
// want to support old types).
channel_type: ChannelTypeFeatures::only_static_remote_key(),
})
}

Expand Down Expand Up @@ -803,6 +811,23 @@ impl<Signer: Sign> Channel<Signer> {
where K::Target: KeysInterface<Signer = Signer>,
F::Target: FeeEstimator
{
// First check the channel type is known, failing before we do anything else if we don't
// support this channel type.
let channel_type = if let Some(channel_type) = &msg.channel_type {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From the spec for accept_channel:

if channel_type is set, and channel_type was set in open_channel, and they are not equal types:
    MUST reject the channel.

I don't think we do this?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We kinda do indirectly, but I made it explicit.

if channel_type.supports_any_optional_bits() {
return Err(ChannelError::Close("Channel Type field contained optional bits - this is not allowed".to_owned()));
}
if *channel_type != ChannelTypeFeatures::only_static_remote_key() {
return Err(ChannelError::Close("Channel Type was not understood".to_owned()));
}
channel_type.clone()
} else {
ChannelTypeFeatures::from_counterparty_init(&their_features)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think if we're to include this line, and their Init supports but doesn't require static_remotekey, we should translate their features to require static_remotekey, since bit 13 should never be set in a ChannelTypeFeatures iiuc

};
if !channel_type.supports_static_remote_key() {
return Err(ChannelError::Close("Channel Type was not understood - we require static remote key".to_owned()));
}

let holder_signer = keys_provider.get_channel_signer(true, msg.funding_satoshis);
let pubkeys = holder_signer.pubkeys().clone();
let counterparty_pubkeys = ChannelPublicKeys {
Expand Down Expand Up @@ -1043,6 +1068,8 @@ impl<Signer: Sign> Channel<Signer> {

#[cfg(any(test, feature = "fuzztarget"))]
historical_inbound_htlc_fulfills: HashSet::new(),

channel_type,
};

Ok(chan)
Expand Down Expand Up @@ -4283,6 +4310,7 @@ impl<Signer: Sign> Channel<Signer> {
Some(script) => script.clone().into_inner(),
None => Builder::new().into_script(),
}),
channel_type: Some(self.channel_type.clone()),
}
}

Expand Down Expand Up @@ -5240,6 +5268,7 @@ impl<Signer: Sign> Writeable for Channel<Signer> {
(7, self.shutdown_scriptpubkey, option),
(9, self.target_closing_feerate_sats_per_kw, option),
(11, self.monitor_pending_finalized_fulfills, vec_type),
(13, self.channel_type, required),
});

Ok(())
Expand Down Expand Up @@ -5474,6 +5503,9 @@ impl<'a, Signer: Sign, K: Deref> ReadableArgs<&'a K> for Channel<Signer>
let mut announcement_sigs = None;
let mut target_closing_feerate_sats_per_kw = None;
let mut monitor_pending_finalized_fulfills = Some(Vec::new());
// Prior to supporting channel type negotiation, all of our channels were static_remotekey
// only, so we default to that if none was written.
let mut channel_type = Some(ChannelTypeFeatures::only_static_remote_key());
valentinewallace marked this conversation as resolved.
Show resolved Hide resolved
read_tlv_fields!(reader, {
(0, announcement_sigs, option),
(1, minimum_depth, option),
Expand All @@ -5482,8 +5514,16 @@ impl<'a, Signer: Sign, K: Deref> ReadableArgs<&'a K> for Channel<Signer>
(7, shutdown_scriptpubkey, option),
(9, target_closing_feerate_sats_per_kw, option),
(11, monitor_pending_finalized_fulfills, vec_type),
(13, channel_type, option),
});

let chan_features = channel_type.as_ref().unwrap();
if chan_features.supports_unknown_bits() || chan_features.requires_unknown_bits() {
valentinewallace marked this conversation as resolved.
Show resolved Hide resolved
// If the channel was written by a new version and negotiated with features we don't
// understand yet, refuse to read it.
return Err(DecodeError::UnknownRequiredFeature);
}

let mut secp_ctx = Secp256k1::new();
secp_ctx.seeded_randomize(&keys_source.get_secure_random_bytes());

Expand Down Expand Up @@ -5576,6 +5616,8 @@ impl<'a, Signer: Sign, K: Deref> ReadableArgs<&'a K> for Channel<Signer>

#[cfg(any(test, feature = "fuzztarget"))]
historical_inbound_htlc_fulfills,

channel_type: channel_type.unwrap(),
})
}
}
Expand Down
137 changes: 119 additions & 18 deletions lightning/src/ln/features.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
//! [BOLT #9]: https://github.com/lightningnetwork/lightning-rfc/blob/master/09-features.md
//! [messages]: crate::ln::msgs

use io;
use {io, io_extras};
use prelude::*;
use core::{cmp, fmt};
use core::hash::{Hash, Hasher};
Expand Down Expand Up @@ -194,6 +194,30 @@ mod sealed {
BasicMPP,
],
});
// This isn't a "real" feature context, and is only used in the channel_type field in an
// `OpenChannel` message.
define_context!(ChannelTypeContext {
required_features: [
// Byte 0
,
// Byte 1
StaticRemoteKey,
// Byte 2
,
// Byte 3
,
],
optional_features: [
// Byte 0
,
// Byte 1
,
// Byte 2
,
// Byte 3
,
],
});

/// Defines a feature with the given bits for the specified [`Context`]s. The generated trait is
/// useful for manipulating feature flags.
Expand Down Expand Up @@ -325,7 +349,7 @@ mod sealed {
define_feature!(9, VariableLengthOnion, [InitContext, NodeContext, InvoiceContext],
"Feature flags for `var_onion_optin`.", set_variable_length_onion_optional,
set_variable_length_onion_required);
define_feature!(13, StaticRemoteKey, [InitContext, NodeContext],
define_feature!(13, StaticRemoteKey, [InitContext, NodeContext, ChannelTypeContext],
"Feature flags for `option_static_remotekey`.", set_static_remote_key_optional,
set_static_remote_key_required);
define_feature!(15, PaymentSecret, [InitContext, NodeContext, InvoiceContext],
Expand Down Expand Up @@ -388,6 +412,18 @@ pub type ChannelFeatures = Features<sealed::ChannelContext>;
/// Features used within an invoice.
pub type InvoiceFeatures = Features<sealed::InvoiceContext>;

/// Features used within the channel_type field in an OpenChannel message.
///
/// A channel is always of some known "type", describing the transaction formats used and the exact
/// semantics of our interaction with our peer.
///
/// Note that because a channel is a specific type which is proposed by the opener and accepted by
/// the counterparty, only required features are allowed here.
TheBlueMatt marked this conversation as resolved.
Show resolved Hide resolved
///
/// This is serialized differently from other feature types - it is not prefixed by a length, and
/// thus must only appear inside a TLV where its length is known in advance.
pub type ChannelTypeFeatures = Features<sealed::ChannelTypeContext>;

impl InitFeatures {
/// Writes all features present up to, and including, 13.
pub(crate) fn write_up_to_13<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
Expand Down Expand Up @@ -442,6 +478,28 @@ impl InvoiceFeatures {
}
}

impl ChannelTypeFeatures {
/// Constructs the implicit channel type based on the common supported types between us and our
/// counterparty
pub(crate) fn from_counterparty_init(counterparty_init: &InitFeatures) -> Self {
let mut ret = counterparty_init.to_context_internal();
// ChannelTypeFeatures must only contain required bits, so we OR the required forms of all
// optional bits and then AND out the optional ones.
for byte in ret.flags.iter_mut() {
*byte |= (*byte & 0b10_10_10_10) >> 1;
*byte &= 0b01_01_01_01;
}
ret
}

/// Constructs a ChannelTypeFeatures with only static_remotekey set
pub(crate) fn only_static_remote_key() -> Self {
let mut ret = Self::empty();
<sealed::ChannelTypeContext as sealed::StaticRemoteKey>::set_required_bit(&mut ret.flags);
ret
}
}

impl ToBase32 for InvoiceFeatures {
fn write_base32<W: WriteBase32>(&self, writer: &mut W) -> Result<(), <W as WriteBase32>::Err> {
// Explanation for the "4": the normal way to round up when dividing is to add the divisor
Expand Down Expand Up @@ -553,6 +611,25 @@ impl<T: sealed::Context> Features<T> {
&self.flags
}

fn write_be<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
for f in self.flags.iter().rev() { // Swap back to big-endian
f.write(w)?;
}
Ok(())
}
TheBlueMatt marked this conversation as resolved.
Show resolved Hide resolved

fn from_be_bytes(mut flags: Vec<u8>) -> Features<T> {
flags.reverse(); // Swap to little-endian
Self {
flags,
mark: PhantomData,
}
}

pub(crate) fn supports_any_optional_bits(&self) -> bool {
self.flags.iter().any(|&byte| (byte & 0b10_10_10_10) != 0)
}

/// Returns true if this `Features` object contains unknown feature flags which are set as
/// "required".
pub fn requires_unknown_bits(&self) -> bool {
Expand Down Expand Up @@ -692,31 +769,44 @@ impl<T: sealed::ShutdownAnySegwit> Features<T> {
self
}
}

impl<T: sealed::Context> Writeable for Features<T> {
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
(self.flags.len() as u16).write(w)?;
for f in self.flags.iter().rev() { // Swap back to big-endian
f.write(w)?;
macro_rules! impl_feature_len_prefixed_write {
($features: ident) => {
impl Writeable for $features {
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
(self.flags.len() as u16).write(w)?;
self.write_be(w)
}
}
impl Readable for $features {
fn read<R: io::Read>(r: &mut R) -> Result<Self, DecodeError> {
Ok(Self::from_be_bytes(Vec::<u8>::read(r)?))
}
}
Ok(())
}
}

impl<T: sealed::Context> Readable for Features<T> {
impl_feature_len_prefixed_write!(InitFeatures);
impl_feature_len_prefixed_write!(ChannelFeatures);
impl_feature_len_prefixed_write!(NodeFeatures);
impl_feature_len_prefixed_write!(InvoiceFeatures);

// Because ChannelTypeFeatures only appears inside of TLVs, it doesn't have a length prefix when
// serialized. Thus, we can't use `impl_feature_len_prefixed_write`, above, and have to write our
// own serialization.
impl Writeable for ChannelTypeFeatures {
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
self.write_be(w)
TheBlueMatt marked this conversation as resolved.
Show resolved Hide resolved
}
}
impl Readable for ChannelTypeFeatures {
fn read<R: io::Read>(r: &mut R) -> Result<Self, DecodeError> {
let mut flags: Vec<u8> = Readable::read(r)?;
flags.reverse(); // Swap to little-endian
Ok(Self {
flags,
mark: PhantomData,
})
let v = io_extras::read_to_end(r)?;
Ok(Self::from_be_bytes(v))
}
}

#[cfg(test)]
mod tests {
use super::{ChannelFeatures, InitFeatures, InvoiceFeatures, NodeFeatures};
use super::{ChannelFeatures, ChannelTypeFeatures, InitFeatures, InvoiceFeatures, NodeFeatures};
use bitcoin::bech32::{Base32Len, FromBase32, ToBase32, u5};

#[test]
Expand Down Expand Up @@ -875,4 +965,15 @@ mod tests {
let features_deserialized = InvoiceFeatures::from_base32(&features_as_u5s).unwrap();
assert_eq!(features, features_deserialized);
}

#[test]
fn test_channel_type_mapping() {
// If we map an InvoiceFeatures with StaticRemoteKey optional, it should map into a
// required-StaticRemoteKey ChannelTypeFeatures.
let init_features = InitFeatures::empty().set_static_remote_key_optional();
let converted_features = ChannelTypeFeatures::from_counterparty_init(&init_features);
assert_eq!(converted_features, ChannelTypeFeatures::only_static_remote_key());
assert!(!converted_features.supports_any_optional_bits());
assert!(converted_features.requires_static_remote_key());
}
}
Loading