Merge pull request #2204 from jkczyz/2023-04-custom-feature-bits

Support for custom feature bits
This commit is contained in:
Matt Corallo 2023-05-18 19:28:19 +00:00 committed by GitHub
commit 498f233145
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 326 additions and 34 deletions

File diff suppressed because one or more lines are too long

View file

@ -20,6 +20,7 @@
//! # use bitcoin::secp256k1::PublicKey;
//! # use lightning::io;
//! # use lightning::ln::msgs::{DecodeError, LightningError};
//! # use lightning::ln::features::{InitFeatures, NodeFeatures};
//! use lightning::ln::peer_handler::CustomMessageHandler;
//! use lightning::ln::wire::{CustomMessageReader, self};
//! use lightning::util::ser::Writeable;
@ -66,6 +67,12 @@
//! # fn get_and_clear_pending_msg(&self) -> Vec<(PublicKey, Self::CustomMessage)> {
//! # unimplemented!()
//! # }
//! # fn provided_node_features(&self) -> NodeFeatures {
//! # unimplemented!()
//! # }
//! # fn provided_init_features(&self, _their_node_id: &PublicKey) -> InitFeatures {
//! # unimplemented!()
//! # }
//! }
//!
//! #[derive(Debug)]
@ -106,6 +113,12 @@
//! # fn get_and_clear_pending_msg(&self) -> Vec<(PublicKey, Self::CustomMessage)> {
//! # unimplemented!()
//! # }
//! # fn provided_node_features(&self) -> NodeFeatures {
//! # unimplemented!()
//! # }
//! # fn provided_init_features(&self, _their_node_id: &PublicKey) -> InitFeatures {
//! # unimplemented!()
//! # }
//! }
//!
//! #[derive(Debug)]
@ -146,6 +159,12 @@
//! # fn get_and_clear_pending_msg(&self) -> Vec<(PublicKey, Self::CustomMessage)> {
//! # unimplemented!()
//! # }
//! # fn provided_node_features(&self) -> NodeFeatures {
//! # unimplemented!()
//! # }
//! # fn provided_init_features(&self, _their_node_id: &PublicKey) -> InitFeatures {
//! # unimplemented!()
//! # }
//! }
//!
//! # fn main() {
@ -268,6 +287,22 @@ macro_rules! composite_custom_message_handler {
)*
.collect()
}
fn provided_node_features(&self) -> $crate::lightning::ln::features::NodeFeatures {
$crate::lightning::ln::features::NodeFeatures::empty()
$(
| self.$field.provided_node_features()
)*
}
fn provided_init_features(
&self, their_node_id: &$crate::bitcoin::secp256k1::PublicKey
) -> $crate::lightning::ln::features::InitFeatures {
$crate::lightning::ln::features::InitFeatures::empty()
$(
| self.$field.provided_init_features(their_node_id)
)*
}
}
impl $crate::lightning::ln::wire::CustomMessageReader for $handler {

View file

@ -422,8 +422,10 @@ pub struct Features<T: sealed::Context> {
mark: PhantomData<T>,
}
impl <T: sealed::Context> Features<T> {
pub(crate) fn or(mut self, o: Self) -> Self {
impl<T: sealed::Context> core::ops::BitOr for Features<T> {
type Output = Self;
fn bitor(mut self, o: Self) -> Self {
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()) {
@ -695,6 +697,25 @@ impl<T: sealed::Context> Features<T> {
self.flags.iter().any(|&byte| (byte & 0b10_10_10_10) != 0)
}
/// Returns true if this `Features` object contains required features unknown by `other`.
pub fn requires_unknown_bits_from(&self, other: &Features<T>) -> bool {
// Bitwise AND-ing with all even bits set except for known features will select required
// unknown features.
self.flags.iter().enumerate().any(|(i, &byte)| {
const REQUIRED_FEATURES: u8 = 0b01_01_01_01;
const OPTIONAL_FEATURES: u8 = 0b10_10_10_10;
let unknown_features = if i < other.flags.len() {
// Form a mask similar to !T::KNOWN_FEATURE_MASK only for `other`
!(other.flags[i]
| ((other.flags[i] >> 1) & REQUIRED_FEATURES)
| ((other.flags[i] << 1) & OPTIONAL_FEATURES))
} else {
0b11_11_11_11
};
(byte & (REQUIRED_FEATURES & unknown_features)) != 0
})
}
/// Returns true if this `Features` object contains unknown feature flags which are set as
/// "required".
pub fn requires_unknown_bits(&self) -> bool {
@ -743,6 +764,50 @@ impl<T: sealed::Context> Features<T> {
}
true
}
/// Sets a required custom feature bit. Errors if `bit` is outside the custom range as defined
/// by [bLIP 2] or if it is a known `T` feature.
///
/// Note: Required bits are even. If an odd bit is given, then the corresponding even bit will
/// be set instead (i.e., `bit - 1`).
///
/// [bLIP 2]: https://github.com/lightning/blips/blob/master/blip-0002.md#feature-bits
pub fn set_required_custom_bit(&mut self, bit: usize) -> Result<(), ()> {
self.set_custom_bit(bit - (bit % 2))
}
/// Sets an optional custom feature bit. Errors if `bit` is outside the custom range as defined
/// by [bLIP 2] or if it is a known `T` feature.
///
/// Note: Optional bits are odd. If an even bit is given, then the corresponding odd bit will be
/// set instead (i.e., `bit + 1`).
///
/// [bLIP 2]: https://github.com/lightning/blips/blob/master/blip-0002.md#feature-bits
pub fn set_optional_custom_bit(&mut self, bit: usize) -> Result<(), ()> {
self.set_custom_bit(bit + (1 - (bit % 2)))
}
fn set_custom_bit(&mut self, bit: usize) -> Result<(), ()> {
if bit < 256 {
return Err(());
}
let byte_offset = bit / 8;
let mask = 1 << (bit - 8 * byte_offset);
if byte_offset < T::KNOWN_FEATURE_MASK.len() {
if (T::KNOWN_FEATURE_MASK[byte_offset] & mask) != 0 {
return Err(());
}
}
if self.flags.len() <= byte_offset {
self.flags.resize(byte_offset + 1, 0u8);
}
self.flags[byte_offset] |= mask;
Ok(())
}
}
impl<T: sealed::UpfrontShutdownScript> Features<T> {
@ -869,6 +934,43 @@ mod tests {
assert!(features.supports_unknown_bits());
}
#[test]
fn requires_unknown_bits_from() {
let mut features1 = InitFeatures::empty();
let mut features2 = InitFeatures::empty();
assert!(!features1.requires_unknown_bits_from(&features2));
assert!(!features2.requires_unknown_bits_from(&features1));
features1.set_data_loss_protect_required();
assert!(features1.requires_unknown_bits_from(&features2));
assert!(!features2.requires_unknown_bits_from(&features1));
features2.set_data_loss_protect_optional();
assert!(!features1.requires_unknown_bits_from(&features2));
assert!(!features2.requires_unknown_bits_from(&features1));
features2.set_gossip_queries_required();
assert!(!features1.requires_unknown_bits_from(&features2));
assert!(features2.requires_unknown_bits_from(&features1));
features1.set_gossip_queries_optional();
assert!(!features1.requires_unknown_bits_from(&features2));
assert!(!features2.requires_unknown_bits_from(&features1));
features1.set_variable_length_onion_required();
assert!(features1.requires_unknown_bits_from(&features2));
assert!(!features2.requires_unknown_bits_from(&features1));
features2.set_variable_length_onion_optional();
assert!(!features1.requires_unknown_bits_from(&features2));
assert!(!features2.requires_unknown_bits_from(&features1));
features1.set_basic_mpp_required();
features2.set_wumbo_required();
assert!(features1.requires_unknown_bits_from(&features2));
assert!(features2.requires_unknown_bits_from(&features1));
}
#[test]
fn convert_to_context_with_relevant_flags() {
let mut init_features = InitFeatures::empty();
@ -881,12 +983,12 @@ mod tests {
init_features.set_payment_secret_required();
init_features.set_basic_mpp_optional();
init_features.set_wumbo_optional();
init_features.set_anchors_zero_fee_htlc_tx_optional();
init_features.set_shutdown_any_segwit_optional();
init_features.set_onion_messages_optional();
init_features.set_channel_type_optional();
init_features.set_scid_privacy_optional();
init_features.set_zero_conf_optional();
init_features.set_anchors_zero_fee_htlc_tx_optional();
assert!(init_features.initial_routing_sync());
assert!(!init_features.supports_upfront_shutdown_script());
@ -897,7 +999,7 @@ mod tests {
// Check that the flags are as expected:
// - option_data_loss_protect (req)
// - var_onion_optin (req) | static_remote_key (req) | payment_secret(req)
// - basic_mpp | wumbo
// - basic_mpp | wumbo | anchors_zero_fee_htlc_tx
// - opt_shutdown_anysegwit
// - onion_messages
// - option_channel_type | option_scid_alias
@ -945,6 +1047,36 @@ mod tests {
assert!(features.supports_payment_secret());
}
#[test]
fn set_custom_bits() {
let mut features = InvoiceFeatures::empty();
features.set_variable_length_onion_optional();
assert_eq!(features.flags[1], 0b00000010);
assert!(features.set_optional_custom_bit(255).is_err());
assert!(features.set_required_custom_bit(256).is_ok());
assert!(features.set_required_custom_bit(258).is_ok());
assert_eq!(features.flags[31], 0b00000000);
assert_eq!(features.flags[32], 0b00000101);
let known_bit = <sealed::InvoiceContext as sealed::PaymentSecret>::EVEN_BIT;
let byte_offset = <sealed::InvoiceContext as sealed::PaymentSecret>::BYTE_OFFSET;
assert_eq!(byte_offset, 1);
assert_eq!(features.flags[byte_offset], 0b00000010);
assert!(features.set_required_custom_bit(known_bit).is_err());
assert_eq!(features.flags[byte_offset], 0b00000010);
let mut features = InvoiceFeatures::empty();
assert!(features.set_optional_custom_bit(256).is_ok());
assert!(features.set_optional_custom_bit(259).is_ok());
assert_eq!(features.flags[32], 0b00001010);
let mut features = InvoiceFeatures::empty();
assert!(features.set_required_custom_bit(257).is_ok());
assert!(features.set_required_custom_bit(258).is_ok());
assert_eq!(features.flags[32], 0b00000101);
}
#[test]
fn encodes_features_without_length() {
let features = OfferFeatures::from_le_bytes(vec![1, 2, 3, 4, 5, 42, 100, 101]);

View file

@ -1738,7 +1738,7 @@ impl Readable for Init {
(3, remote_network_address, option)
});
Ok(Init {
features: features.or(global_features),
features: features | global_features,
remote_network_address,
})
}

View file

@ -64,6 +64,20 @@ pub trait CustomMessageHandler: wire::CustomMessageReader {
/// in the process. Each message is paired with the node id of the intended recipient. If no
/// connection to the node exists, then the message is simply not sent.
fn get_and_clear_pending_msg(&self) -> Vec<(PublicKey, Self::CustomMessage)>;
/// Gets the node feature flags which this handler itself supports. All available handlers are
/// queried similarly and their feature flags are OR'd together to form the [`NodeFeatures`]
/// which are broadcasted in our [`NodeAnnouncement`] message.
///
/// [`NodeAnnouncement`]: crate::ln::msgs::NodeAnnouncement
fn provided_node_features(&self) -> NodeFeatures;
/// Gets the init feature flags which should be sent to the given peer. All available handlers
/// are queried similarly and their feature flags are OR'd together to form the [`InitFeatures`]
/// which are sent in our [`Init`] message.
///
/// [`Init`]: crate::ln::msgs::Init
fn provided_init_features(&self, their_node_id: &PublicKey) -> InitFeatures;
}
/// A dummy struct which implements `RoutingMessageHandler` without storing any routing information
@ -149,6 +163,12 @@ impl CustomMessageHandler for IgnoringMessageHandler {
}
fn get_and_clear_pending_msg(&self) -> Vec<(PublicKey, Self::CustomMessage)> { Vec::new() }
fn provided_node_features(&self) -> NodeFeatures { NodeFeatures::empty() }
fn provided_init_features(&self, _their_node_id: &PublicKey) -> InitFeatures {
InitFeatures::empty()
}
}
/// A dummy struct which implements `ChannelMessageHandler` without having any channels.
@ -895,6 +915,13 @@ impl<Descriptor: SocketDescriptor, CM: Deref, RM: Deref, OM: Deref, L: Deref, CM
SecretKey::from_slice(&Sha256::from_engine(ephemeral_hash).into_inner()).expect("You broke SHA-256!")
}
fn init_features(&self, their_node_id: &PublicKey) -> InitFeatures {
self.message_handler.chan_handler.provided_init_features(their_node_id)
| self.message_handler.route_handler.provided_init_features(their_node_id)
| self.message_handler.onion_message_handler.provided_init_features(their_node_id)
| self.message_handler.custom_message_handler.provided_init_features(their_node_id)
}
/// Indicates a new outbound connection has been established to a node with the given `node_id`
/// and an optional remote network address.
///
@ -1290,9 +1317,7 @@ impl<Descriptor: SocketDescriptor, CM: Deref, RM: Deref, OM: Deref, L: Deref, CM
peer.set_their_node_id(their_node_id);
insert_node_id!();
let features = self.message_handler.chan_handler.provided_init_features(&their_node_id)
.or(self.message_handler.route_handler.provided_init_features(&their_node_id))
.or(self.message_handler.onion_message_handler.provided_init_features(&their_node_id));
let features = self.init_features(&their_node_id);
let resp = msgs::Init { features, remote_network_address: filter_addresses(peer.their_net_address.clone()) };
self.enqueue_message(peer, &resp);
peer.awaiting_pong_timer_tick_intervals = 0;
@ -1304,9 +1329,7 @@ impl<Descriptor: SocketDescriptor, CM: Deref, RM: Deref, OM: Deref, L: Deref, CM
peer.pending_read_is_header = true;
peer.set_their_node_id(their_node_id);
insert_node_id!();
let features = self.message_handler.chan_handler.provided_init_features(&their_node_id)
.or(self.message_handler.route_handler.provided_init_features(&their_node_id))
.or(self.message_handler.onion_message_handler.provided_init_features(&their_node_id));
let features = self.init_features(&their_node_id);
let resp = msgs::Init { features, remote_network_address: filter_addresses(peer.their_net_address.clone()) };
self.enqueue_message(peer, &resp);
peer.awaiting_pong_timer_tick_intervals = 0;
@ -1423,10 +1446,17 @@ impl<Descriptor: SocketDescriptor, CM: Deref, RM: Deref, OM: Deref, L: Deref, CM
// Need an Init as first message
if let wire::Message::Init(msg) = message {
if msg.features.requires_unknown_bits() {
log_debug!(self.logger, "Peer features required unknown version bits");
let our_features = self.init_features(&their_node_id);
if msg.features.requires_unknown_bits_from(&our_features) {
log_debug!(self.logger, "Peer requires features unknown to us");
return Err(PeerHandleError { }.into());
}
if our_features.requires_unknown_bits_from(&msg.features) {
log_debug!(self.logger, "We require features unknown to our peer");
return Err(PeerHandleError { }.into());
}
if peer_lock.their_features.is_some() {
return Err(PeerHandleError { }.into());
}
@ -2348,8 +2378,9 @@ impl<Descriptor: SocketDescriptor, CM: Deref, RM: Deref, OM: Deref, L: Deref, CM
addresses.sort_by_key(|addr| addr.get_id());
let features = self.message_handler.chan_handler.provided_node_features()
.or(self.message_handler.route_handler.provided_node_features())
.or(self.message_handler.onion_message_handler.provided_node_features());
| self.message_handler.route_handler.provided_node_features()
| self.message_handler.onion_message_handler.provided_node_features()
| self.message_handler.custom_message_handler.provided_node_features();
let announcement = msgs::UnsignedNodeAnnouncement {
features,
timestamp: self.last_node_announcement_serial.fetch_add(1, Ordering::AcqRel),
@ -2398,16 +2429,19 @@ fn is_gossip_msg(type_id: u16) -> bool {
mod tests {
use crate::sign::{NodeSigner, Recipient};
use crate::events;
use crate::io;
use crate::ln::features::{InitFeatures, NodeFeatures};
use crate::ln::peer_channel_encryptor::PeerChannelEncryptor;
use crate::ln::peer_handler::{PeerManager, MessageHandler, SocketDescriptor, IgnoringMessageHandler, filter_addresses};
use crate::ln::peer_handler::{CustomMessageHandler, PeerManager, MessageHandler, SocketDescriptor, IgnoringMessageHandler, filter_addresses};
use crate::ln::{msgs, wire};
use crate::ln::msgs::NetAddress;
use crate::ln::msgs::{LightningError, NetAddress};
use crate::util::test_utils;
use bitcoin::secp256k1::SecretKey;
use bitcoin::secp256k1::{PublicKey, SecretKey};
use crate::prelude::*;
use crate::sync::{Arc, Mutex};
use core::convert::Infallible;
use core::sync::atomic::{AtomicBool, Ordering};
#[derive(Clone)]
@ -2440,19 +2474,51 @@ mod tests {
struct PeerManagerCfg {
chan_handler: test_utils::TestChannelMessageHandler,
routing_handler: test_utils::TestRoutingMessageHandler,
custom_handler: TestCustomMessageHandler,
logger: test_utils::TestLogger,
node_signer: test_utils::TestNodeSigner,
}
struct TestCustomMessageHandler {
features: InitFeatures,
}
impl wire::CustomMessageReader for TestCustomMessageHandler {
type CustomMessage = Infallible;
fn read<R: io::Read>(&self, _: u16, _: &mut R) -> Result<Option<Self::CustomMessage>, msgs::DecodeError> {
Ok(None)
}
}
impl CustomMessageHandler for TestCustomMessageHandler {
fn handle_custom_message(&self, _: Infallible, _: &PublicKey) -> Result<(), LightningError> {
unreachable!();
}
fn get_and_clear_pending_msg(&self) -> Vec<(PublicKey, Self::CustomMessage)> { Vec::new() }
fn provided_node_features(&self) -> NodeFeatures { NodeFeatures::empty() }
fn provided_init_features(&self, _: &PublicKey) -> InitFeatures {
self.features.clone()
}
}
fn create_peermgr_cfgs(peer_count: usize) -> Vec<PeerManagerCfg> {
let mut cfgs = Vec::new();
for i in 0..peer_count {
let node_secret = SecretKey::from_slice(&[42 + i as u8; 32]).unwrap();
let features = {
let mut feature_bits = vec![0u8; 33];
feature_bits[32] = 0b00000001;
InitFeatures::from_le_bytes(feature_bits)
};
cfgs.push(
PeerManagerCfg{
chan_handler: test_utils::TestChannelMessageHandler::new(),
logger: test_utils::TestLogger::new(),
routing_handler: test_utils::TestRoutingMessageHandler::new(),
custom_handler: TestCustomMessageHandler { features },
node_signer: test_utils::TestNodeSigner::new(node_secret),
}
);
@ -2461,13 +2527,36 @@ mod tests {
cfgs
}
fn create_network<'a>(peer_count: usize, cfgs: &'a Vec<PeerManagerCfg>) -> Vec<PeerManager<FileDescriptor, &'a test_utils::TestChannelMessageHandler, &'a test_utils::TestRoutingMessageHandler, IgnoringMessageHandler, &'a test_utils::TestLogger, IgnoringMessageHandler, &'a test_utils::TestNodeSigner>> {
fn create_incompatible_peermgr_cfgs(peer_count: usize) -> Vec<PeerManagerCfg> {
let mut cfgs = Vec::new();
for i in 0..peer_count {
let node_secret = SecretKey::from_slice(&[42 + i as u8; 32]).unwrap();
let features = {
let mut feature_bits = vec![0u8; 33 + i + 1];
feature_bits[33 + i] = 0b00000001;
InitFeatures::from_le_bytes(feature_bits)
};
cfgs.push(
PeerManagerCfg{
chan_handler: test_utils::TestChannelMessageHandler::new(),
logger: test_utils::TestLogger::new(),
routing_handler: test_utils::TestRoutingMessageHandler::new(),
custom_handler: TestCustomMessageHandler { features },
node_signer: test_utils::TestNodeSigner::new(node_secret),
}
);
}
cfgs
}
fn create_network<'a>(peer_count: usize, cfgs: &'a Vec<PeerManagerCfg>) -> Vec<PeerManager<FileDescriptor, &'a test_utils::TestChannelMessageHandler, &'a test_utils::TestRoutingMessageHandler, IgnoringMessageHandler, &'a test_utils::TestLogger, &'a TestCustomMessageHandler, &'a test_utils::TestNodeSigner>> {
let mut peers = Vec::new();
for i in 0..peer_count {
let ephemeral_bytes = [i as u8; 32];
let msg_handler = MessageHandler {
chan_handler: &cfgs[i].chan_handler, route_handler: &cfgs[i].routing_handler,
onion_message_handler: IgnoringMessageHandler {}, custom_message_handler: IgnoringMessageHandler {}
onion_message_handler: IgnoringMessageHandler {}, custom_message_handler: &cfgs[i].custom_handler
};
let peer = PeerManager::new(msg_handler, 0, &ephemeral_bytes, &cfgs[i].logger, &cfgs[i].node_signer);
peers.push(peer);
@ -2476,7 +2565,7 @@ mod tests {
peers
}
fn establish_connection<'a>(peer_a: &PeerManager<FileDescriptor, &'a test_utils::TestChannelMessageHandler, &'a test_utils::TestRoutingMessageHandler, IgnoringMessageHandler, &'a test_utils::TestLogger, IgnoringMessageHandler, &'a test_utils::TestNodeSigner>, peer_b: &PeerManager<FileDescriptor, &'a test_utils::TestChannelMessageHandler, &'a test_utils::TestRoutingMessageHandler, IgnoringMessageHandler, &'a test_utils::TestLogger, IgnoringMessageHandler, &'a test_utils::TestNodeSigner>) -> (FileDescriptor, FileDescriptor) {
fn establish_connection<'a>(peer_a: &PeerManager<FileDescriptor, &'a test_utils::TestChannelMessageHandler, &'a test_utils::TestRoutingMessageHandler, IgnoringMessageHandler, &'a test_utils::TestLogger, &'a TestCustomMessageHandler, &'a test_utils::TestNodeSigner>, peer_b: &PeerManager<FileDescriptor, &'a test_utils::TestChannelMessageHandler, &'a test_utils::TestRoutingMessageHandler, IgnoringMessageHandler, &'a test_utils::TestLogger, &'a TestCustomMessageHandler, &'a test_utils::TestNodeSigner>) -> (FileDescriptor, FileDescriptor) {
let id_a = peer_a.node_signer.get_node_id(Recipient::Node).unwrap();
let mut fd_a = FileDescriptor {
fd: 1, outbound_data: Arc::new(Mutex::new(Vec::new())),
@ -2591,6 +2680,42 @@ mod tests {
thrd_b.join().unwrap();
}
#[test]
fn test_incompatible_peers() {
let cfgs = create_peermgr_cfgs(2);
let incompatible_cfgs = create_incompatible_peermgr_cfgs(2);
let peers = create_network(2, &cfgs);
let incompatible_peers = create_network(2, &incompatible_cfgs);
let peer_pairs = [(&peers[0], &incompatible_peers[0]), (&incompatible_peers[1], &peers[1])];
for (peer_a, peer_b) in peer_pairs.iter() {
let id_a = peer_a.node_signer.get_node_id(Recipient::Node).unwrap();
let mut fd_a = FileDescriptor {
fd: 1, outbound_data: Arc::new(Mutex::new(Vec::new())),
disconnect: Arc::new(AtomicBool::new(false)),
};
let addr_a = NetAddress::IPv4{addr: [127, 0, 0, 1], port: 1000};
let mut fd_b = FileDescriptor {
fd: 1, outbound_data: Arc::new(Mutex::new(Vec::new())),
disconnect: Arc::new(AtomicBool::new(false)),
};
let addr_b = NetAddress::IPv4{addr: [127, 0, 0, 1], port: 1001};
let initial_data = peer_b.new_outbound_connection(id_a, fd_b.clone(), Some(addr_a.clone())).unwrap();
peer_a.new_inbound_connection(fd_a.clone(), Some(addr_b.clone())).unwrap();
assert_eq!(peer_a.read_event(&mut fd_a, &initial_data).unwrap(), false);
peer_a.process_events();
let a_data = fd_a.outbound_data.lock().unwrap().split_off(0);
assert_eq!(peer_b.read_event(&mut fd_b, &a_data).unwrap(), false);
peer_b.process_events();
let b_data = fd_b.outbound_data.lock().unwrap().split_off(0);
// Should fail because of unknown required features
assert!(peer_a.read_event(&mut fd_a, &b_data).is_err());
}
}
#[test]
fn test_disconnect_peer() {
// Simple test which builds a network of PeerManager, connects and brings them to NoiseState::Finished and