diff --git a/lightning-background-processor/src/lib.rs b/lightning-background-processor/src/lib.rs index e38a4a975b2..0fef55a95c2 100644 --- a/lightning-background-processor/src/lib.rs +++ b/lightning-background-processor/src/lib.rs @@ -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 /// [`join`]: Self::join /// [`stop`]: Self::stop /// [`ChannelManager`]: lightning::ln::channelmanager::ChannelManager diff --git a/lightning/src/ln/channel.rs b/lightning/src/ln/channel.rs index c2e158627ad..68bcf899851 100644 --- a/lightning/src/ln/channel.rs +++ b/lightning/src/ln/channel.rs @@ -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}; @@ -550,6 +550,9 @@ pub(super) struct Channel { // 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, + + /// This channel's type, as negotiated during channel open + channel_type: ChannelTypeFeatures, } #[cfg(any(test, feature = "fuzztarget"))] @@ -775,6 +778,11 @@ impl Channel { #[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(), }) } @@ -803,6 +811,23 @@ impl Channel { where K::Target: KeysInterface, 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 { + 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) + }; + 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 { @@ -1043,6 +1068,8 @@ impl Channel { #[cfg(any(test, feature = "fuzztarget"))] historical_inbound_htlc_fulfills: HashSet::new(), + + channel_type, }; Ok(chan) @@ -4283,6 +4310,7 @@ impl Channel { Some(script) => script.clone().into_inner(), None => Builder::new().into_script(), }), + channel_type: Some(self.channel_type.clone()), } } @@ -5240,6 +5268,7 @@ impl Writeable for Channel { (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(()) @@ -5474,6 +5503,9 @@ impl<'a, Signer: Sign, K: Deref> ReadableArgs<&'a K> for Channel 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()); read_tlv_fields!(reader, { (0, announcement_sigs, option), (1, minimum_depth, option), @@ -5482,8 +5514,16 @@ impl<'a, Signer: Sign, K: Deref> ReadableArgs<&'a K> for Channel (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() { + // 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()); @@ -5576,6 +5616,8 @@ impl<'a, Signer: Sign, K: Deref> ReadableArgs<&'a K> for Channel #[cfg(any(test, feature = "fuzztarget"))] historical_inbound_htlc_fulfills, + + channel_type: channel_type.unwrap(), }) } } diff --git a/lightning/src/ln/features.rs b/lightning/src/ln/features.rs index 8f251d8478c..46a296001ef 100644 --- a/lightning/src/ln/features.rs +++ b/lightning/src/ln/features.rs @@ -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}; @@ -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. @@ -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], @@ -388,6 +412,18 @@ pub type ChannelFeatures = Features; /// Features used within an invoice. pub type InvoiceFeatures = Features; +/// 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. +/// +/// 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; + impl InitFeatures { /// Writes all features present up to, and including, 13. pub(crate) fn write_up_to_13(&self, w: &mut W) -> Result<(), io::Error> { @@ -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(); + ::set_required_bit(&mut ret.flags); + ret + } +} + impl ToBase32 for InvoiceFeatures { fn write_base32(&self, writer: &mut W) -> Result<(), ::Err> { // Explanation for the "4": the normal way to round up when dividing is to add the divisor @@ -553,6 +611,25 @@ impl Features { &self.flags } + fn write_be(&self, w: &mut W) -> Result<(), io::Error> { + for f in self.flags.iter().rev() { // Swap back to big-endian + f.write(w)?; + } + Ok(()) + } + + fn from_be_bytes(mut flags: Vec) -> Features { + 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 { @@ -692,31 +769,44 @@ impl Features { self } } - -impl Writeable for Features { - fn write(&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(&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: &mut R) -> Result { + Ok(Self::from_be_bytes(Vec::::read(r)?)) + } } - Ok(()) } } - -impl Readable for Features { +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(&self, w: &mut W) -> Result<(), io::Error> { + self.write_be(w) + } +} +impl Readable for ChannelTypeFeatures { fn read(r: &mut R) -> Result { - let mut flags: Vec = 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] @@ -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()); + } } diff --git a/lightning/src/ln/msgs.rs b/lightning/src/ln/msgs.rs index 122dd7737b9..87944ccae63 100644 --- a/lightning/src/ln/msgs.rs +++ b/lightning/src/ln/msgs.rs @@ -30,7 +30,7 @@ use bitcoin::secp256k1; use bitcoin::blockdata::script::Script; use bitcoin::hash_types::{Txid, BlockHash}; -use ln::features::{ChannelFeatures, InitFeatures, NodeFeatures}; +use ln::features::{ChannelFeatures, ChannelTypeFeatures, InitFeatures, NodeFeatures}; use prelude::*; use core::{cmp, fmt}; @@ -148,6 +148,10 @@ pub struct OpenChannel { pub channel_flags: u8, /// Optionally, a request to pre-set the to-sender output's scriptPubkey for when we collaboratively close pub shutdown_scriptpubkey: OptionalField