mirror of
https://github.com/lightningdevkit/rust-lightning.git
synced 2025-01-18 21:34:48 +01:00
Merge pull request #1078 from TheBlueMatt/2021-09-chan-types
Implement channel_type negotiation
This commit is contained in:
commit
c0bbd4d918
@ -155,7 +155,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
|
||||
|
@ -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<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"))]
|
||||
@ -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(),
|
||||
})
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
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<Signer: Sign> Channel<Signer> {
|
||||
|
||||
#[cfg(any(test, feature = "fuzztarget"))]
|
||||
historical_inbound_htlc_fulfills: HashSet::new(),
|
||||
|
||||
channel_type,
|
||||
};
|
||||
|
||||
Ok(chan)
|
||||
@ -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()),
|
||||
}
|
||||
}
|
||||
|
||||
@ -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(())
|
||||
@ -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());
|
||||
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<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() {
|
||||
// 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<Signer>
|
||||
|
||||
#[cfg(any(test, feature = "fuzztarget"))]
|
||||
historical_inbound_htlc_fulfills,
|
||||
|
||||
channel_type: channel_type.unwrap(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -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<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.
|
||||
///
|
||||
/// 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> {
|
||||
@ -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
|
||||
@ -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(())
|
||||
}
|
||||
|
||||
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 {
|
||||
@ -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_feature_len_prefixed_write!(InitFeatures);
|
||||
impl_feature_len_prefixed_write!(ChannelFeatures);
|
||||
impl_feature_len_prefixed_write!(NodeFeatures);
|
||||
impl_feature_len_prefixed_write!(InvoiceFeatures);
|
||||
|
||||
impl<T: sealed::Context> Readable for Features<T> {
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
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]
|
||||
@ -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());
|
||||
}
|
||||
}
|
||||
|
@ -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<Script>,
|
||||
/// The channel type that this channel will represent. If none is set, we derive the channel
|
||||
/// type from the intersection of our feature bits with our counterparty's feature bits from
|
||||
/// the Init message.
|
||||
pub channel_type: Option<ChannelTypeFeatures>,
|
||||
}
|
||||
|
||||
/// An accept_channel message to be sent or received from a peer
|
||||
@ -1162,7 +1166,9 @@ impl_writeable_msg!(OpenChannel, {
|
||||
first_per_commitment_point,
|
||||
channel_flags,
|
||||
shutdown_scriptpubkey
|
||||
}, {});
|
||||
}, {
|
||||
(1, channel_type, option),
|
||||
});
|
||||
|
||||
impl_writeable_msg!(RevokeAndACK, {
|
||||
channel_id,
|
||||
@ -1747,8 +1753,9 @@ impl_writeable_msg!(GossipTimestampFilter, {
|
||||
mod tests {
|
||||
use hex;
|
||||
use ln::{PaymentPreimage, PaymentHash, PaymentSecret};
|
||||
use ln::features::{ChannelFeatures, ChannelTypeFeatures, InitFeatures, NodeFeatures};
|
||||
use ln::msgs;
|
||||
use ln::msgs::{ChannelFeatures, FinalOnionHopData, InitFeatures, NodeFeatures, OptionalField, OnionErrorPacket, OnionHopDataFormat};
|
||||
use ln::msgs::{FinalOnionHopData, OptionalField, OnionErrorPacket, OnionHopDataFormat};
|
||||
use util::ser::{Writeable, Readable};
|
||||
|
||||
use bitcoin::hashes::hex::FromHex;
|
||||
@ -2052,7 +2059,7 @@ mod tests {
|
||||
do_encoding_channel_update(true, true, true, true);
|
||||
}
|
||||
|
||||
fn do_encoding_open_channel(random_bit: bool, shutdown: bool) {
|
||||
fn do_encoding_open_channel(random_bit: bool, shutdown: bool, incl_chan_type: bool) {
|
||||
let secp_ctx = Secp256k1::new();
|
||||
let (_, pubkey_1) = get_keys_from!("0101010101010101010101010101010101010101010101010101010101010101", secp_ctx);
|
||||
let (_, pubkey_2) = get_keys_from!("0202020202020202020202020202020202020202020202020202020202020202", secp_ctx);
|
||||
@ -2079,7 +2086,8 @@ mod tests {
|
||||
htlc_basepoint: pubkey_5,
|
||||
first_per_commitment_point: pubkey_6,
|
||||
channel_flags: if random_bit { 1 << 5 } else { 0 },
|
||||
shutdown_scriptpubkey: if shutdown { OptionalField::Present(Address::p2pkh(&::bitcoin::PublicKey{compressed: true, key: pubkey_1}, Network::Testnet).script_pubkey()) } else { OptionalField::Absent }
|
||||
shutdown_scriptpubkey: if shutdown { OptionalField::Present(Address::p2pkh(&::bitcoin::PublicKey{compressed: true, key: pubkey_1}, Network::Testnet).script_pubkey()) } else { OptionalField::Absent },
|
||||
channel_type: if incl_chan_type { Some(ChannelTypeFeatures::empty()) } else { None },
|
||||
};
|
||||
let encoded_value = open_channel.encode();
|
||||
let mut target_value = Vec::new();
|
||||
@ -2093,15 +2101,22 @@ mod tests {
|
||||
if shutdown {
|
||||
target_value.append(&mut hex::decode("001976a91479b000887626b294a914501a4cd226b58b23598388ac").unwrap());
|
||||
}
|
||||
if incl_chan_type {
|
||||
target_value.append(&mut hex::decode("0100").unwrap());
|
||||
}
|
||||
assert_eq!(encoded_value, target_value);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn encoding_open_channel() {
|
||||
do_encoding_open_channel(false, false);
|
||||
do_encoding_open_channel(true, false);
|
||||
do_encoding_open_channel(false, true);
|
||||
do_encoding_open_channel(true, true);
|
||||
do_encoding_open_channel(false, false, false);
|
||||
do_encoding_open_channel(false, false, true);
|
||||
do_encoding_open_channel(false, true, false);
|
||||
do_encoding_open_channel(false, true, true);
|
||||
do_encoding_open_channel(true, false, false);
|
||||
do_encoding_open_channel(true, false, true);
|
||||
do_encoding_open_channel(true, true, false);
|
||||
do_encoding_open_channel(true, true, true);
|
||||
}
|
||||
|
||||
fn do_encoding_accept_channel(shutdown: bool) {
|
||||
|
Loading…
Reference in New Issue
Block a user