Send and handle networks field in Init messages

If the `networks` field is present in a received `Init` message, then
we need to make sure our genesis chain hash matches one of those, otherwise
we should disconnect the peer.

We now also always send our genesis chain hash in `Init` messages to
our peers.
This commit is contained in:
Duncan Dean 2023-06-01 12:40:57 +02:00
parent e23102f565
commit b52b936bdd
No known key found for this signature in database
6 changed files with 122 additions and 13 deletions

View File

@ -838,7 +838,7 @@ impl Drop for BackgroundProcessor {
#[cfg(all(feature = "std", test))]
mod tests {
use bitcoin::blockdata::constants::genesis_block;
use bitcoin::blockdata::constants::{genesis_block, ChainHash};
use bitcoin::blockdata::locktime::PackedLockTime;
use bitcoin::blockdata::transaction::{Transaction, TxOut};
use bitcoin::network::constants::Network;
@ -1146,7 +1146,7 @@ mod tests {
let p2p_gossip_sync = Arc::new(P2PGossipSync::new(network_graph.clone(), Some(chain_source.clone()), logger.clone()));
let rapid_gossip_sync = Arc::new(RapidGossipSync::new(network_graph.clone(), logger.clone()));
let msg_handler = MessageHandler {
chan_handler: Arc::new(test_utils::TestChannelMessageHandler::new()),
chan_handler: Arc::new(test_utils::TestChannelMessageHandler::new(ChainHash::using_genesis_block(Network::Testnet))),
route_handler: Arc::new(test_utils::TestRoutingMessageHandler::new()),
onion_message_handler: IgnoringMessageHandler{}, custom_message_handler: IgnoringMessageHandler{}
};

View File

@ -474,6 +474,8 @@ mod tests {
use lightning::routing::gossip::NodeId;
use lightning::events::*;
use lightning::util::test_utils::TestNodeSigner;
use bitcoin::Network;
use bitcoin::blockdata::constants::ChainHash;
use bitcoin::secp256k1::{Secp256k1, SecretKey, PublicKey};
use tokio::sync::mpsc;
@ -556,6 +558,9 @@ mod tests {
fn handle_error(&self, _their_node_id: &PublicKey, _msg: &ErrorMessage) {}
fn provided_node_features(&self) -> NodeFeatures { NodeFeatures::empty() }
fn provided_init_features(&self, _their_node_id: &PublicKey) -> InitFeatures { InitFeatures::empty() }
fn get_genesis_hashes(&self) -> Option<Vec<ChainHash>> {
Some(vec![ChainHash::using_genesis_block(Network::Testnet)])
}
}
impl MessageSendEventsProvider for MsgHandler {
fn get_and_clear_pending_msg_events(&self) -> Vec<MessageSendEvent> {

View File

@ -19,7 +19,7 @@
use bitcoin::blockdata::block::BlockHeader;
use bitcoin::blockdata::transaction::Transaction;
use bitcoin::blockdata::constants::genesis_block;
use bitcoin::blockdata::constants::{genesis_block, ChainHash};
use bitcoin::network::constants::Network;
use bitcoin::hashes::Hash;
@ -6987,6 +6987,10 @@ where
provided_init_features(&self.default_configuration)
}
fn get_genesis_hashes(&self) -> Option<Vec<ChainHash>> {
Some(vec![ChainHash::from(&self.genesis_hash[..])])
}
fn handle_tx_add_input(&self, counterparty_node_id: &PublicKey, msg: &msgs::TxAddInput) {
let _: Result<(), _> = handle_error!(self, Err(MsgHandleErrInternal::send_err_msg_no_close(
"Dual-funded channels not supported".to_owned(),

View File

@ -1300,6 +1300,12 @@ pub trait ChannelMessageHandler : MessageSendEventsProvider {
///
/// Note that this method is called before [`Self::peer_connected`].
fn provided_init_features(&self, their_node_id: &PublicKey) -> InitFeatures;
/// Gets the genesis hashes for this `ChannelMessageHandler` indicating which chains it supports.
///
/// If it's `None`, then no particular network chain hash compatibility will be enforced when
/// connecting to peers.
fn get_genesis_hashes(&self) -> Option<Vec<ChainHash>>;
}
/// A trait to describe an object which can receive routing messages.

View File

@ -15,6 +15,7 @@
//! call into the provided message handlers (probably a ChannelManager and P2PGossipSync) with
//! messages they should handle, and encoding/sending response messages.
use bitcoin::blockdata::constants::ChainHash;
use bitcoin::secp256k1::{self, Secp256k1, SecretKey, PublicKey};
use crate::sign::{KeysManager, NodeSigner, Recipient};
@ -273,6 +274,13 @@ impl ChannelMessageHandler for ErroringMessageHandler {
features
}
fn get_genesis_hashes(&self) -> Option<Vec<ChainHash>> {
// We don't enforce any chains upon peer connection for `ErroringMessageHandler` and leave it up
// to users of `ErroringMessageHandler` to make decisions on network compatiblility.
// There's not really any way to pull in specific networks here, and hardcoding can cause breakages.
None
}
fn handle_open_channel_v2(&self, their_node_id: &PublicKey, msg: &msgs::OpenChannelV2) {
ErroringMessageHandler::push_error(self, their_node_id, msg.temporary_channel_id);
}
@ -1333,7 +1341,8 @@ 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.init_features(&their_node_id);
let resp = msgs::Init { features, networks: None, remote_network_address: filter_addresses(peer.their_net_address.clone()) };
let networks = self.message_handler.chan_handler.get_genesis_hashes();
let resp = msgs::Init { features, networks, remote_network_address: filter_addresses(peer.their_net_address.clone()) };
self.enqueue_message(peer, &resp);
peer.awaiting_pong_timer_tick_intervals = 0;
},
@ -1345,7 +1354,8 @@ 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.init_features(&their_node_id);
let resp = msgs::Init { features, networks: None, remote_network_address: filter_addresses(peer.their_net_address.clone()) };
let networks = self.message_handler.chan_handler.get_genesis_hashes();
let resp = msgs::Init { features, networks, remote_network_address: filter_addresses(peer.their_net_address.clone()) };
self.enqueue_message(peer, &resp);
peer.awaiting_pong_timer_tick_intervals = 0;
},
@ -1460,6 +1470,25 @@ 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 {
// Check if we have any compatible chains if the `networks` field is specified.
if let Some(networks) = &msg.networks {
if let Some(our_chains) = self.message_handler.chan_handler.get_genesis_hashes() {
let mut have_compatible_chains = false;
'our_chains: for our_chain in our_chains.iter() {
for their_chain in networks {
if our_chain == their_chain {
have_compatible_chains = true;
break 'our_chains;
}
}
}
if !have_compatible_chains {
log_debug!(self.logger, "Peer does not support any of our supported chains");
return Err(PeerHandleError { }.into());
}
}
}
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");
@ -2459,6 +2488,8 @@ mod tests {
use crate::ln::msgs::{LightningError, NetAddress};
use crate::util::test_utils;
use bitcoin::Network;
use bitcoin::blockdata::constants::ChainHash;
use bitcoin::secp256k1::{PublicKey, SecretKey};
use crate::prelude::*;
@ -2537,7 +2568,7 @@ mod tests {
};
cfgs.push(
PeerManagerCfg{
chan_handler: test_utils::TestChannelMessageHandler::new(),
chan_handler: test_utils::TestChannelMessageHandler::new(ChainHash::using_genesis_block(Network::Testnet)),
logger: test_utils::TestLogger::new(),
routing_handler: test_utils::TestRoutingMessageHandler::new(),
custom_handler: TestCustomMessageHandler { features },
@ -2549,7 +2580,7 @@ mod tests {
cfgs
}
fn create_incompatible_peermgr_cfgs(peer_count: usize) -> Vec<PeerManagerCfg> {
fn create_feature_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();
@ -2560,7 +2591,27 @@ mod tests {
};
cfgs.push(
PeerManagerCfg{
chan_handler: test_utils::TestChannelMessageHandler::new(),
chan_handler: test_utils::TestChannelMessageHandler::new(ChainHash::using_genesis_block(Network::Testnet)),
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_chain_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 = InitFeatures::from_le_bytes(vec![0u8; 33]);
let network = ChainHash::from(&[i as u8; 32][..]);
cfgs.push(
PeerManagerCfg{
chan_handler: test_utils::TestChannelMessageHandler::new(network),
logger: test_utils::TestLogger::new(),
routing_handler: test_utils::TestRoutingMessageHandler::new(),
custom_handler: TestCustomMessageHandler { features },
@ -2703,9 +2754,9 @@ mod tests {
}
#[test]
fn test_incompatible_peers() {
fn test_feature_incompatible_peers() {
let cfgs = create_peermgr_cfgs(2);
let incompatible_cfgs = create_incompatible_peermgr_cfgs(2);
let incompatible_cfgs = create_feature_incompatible_peermgr_cfgs(2);
let peers = create_network(2, &cfgs);
let incompatible_peers = create_network(2, &incompatible_cfgs);
@ -2738,6 +2789,42 @@ mod tests {
}
}
#[test]
fn test_chain_incompatible_peers() {
let cfgs = create_peermgr_cfgs(2);
let incompatible_cfgs = create_chain_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 incompatible chains
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
@ -2762,8 +2849,8 @@ mod tests {
// Simple test which builds a network of PeerManager, connects and brings them to NoiseState::Finished and
// push a message from one peer to another.
let cfgs = create_peermgr_cfgs(2);
let a_chan_handler = test_utils::TestChannelMessageHandler::new();
let b_chan_handler = test_utils::TestChannelMessageHandler::new();
let a_chan_handler = test_utils::TestChannelMessageHandler::new(ChainHash::using_genesis_block(Network::Testnet));
let b_chan_handler = test_utils::TestChannelMessageHandler::new(ChainHash::using_genesis_block(Network::Testnet));
let mut peers = create_network(2, &cfgs);
let (fd_a, mut fd_b) = establish_connection(&peers[0], &peers[1]);
assert_eq!(peers[0].peers.read().unwrap().len(), 1);

View File

@ -32,6 +32,7 @@ use crate::util::enforcing_trait_impls::{EnforcingSigner, EnforcementState};
use crate::util::logger::{Logger, Level, Record};
use crate::util::ser::{Readable, ReadableArgs, Writer, Writeable};
use bitcoin::blockdata::constants::ChainHash;
use bitcoin::blockdata::constants::genesis_block;
use bitcoin::blockdata::transaction::{Transaction, TxOut};
use bitcoin::blockdata::script::{Builder, Script};
@ -363,15 +364,17 @@ pub struct TestChannelMessageHandler {
expected_recv_msgs: Mutex<Option<Vec<wire::Message<()>>>>,
connected_peers: Mutex<HashSet<PublicKey>>,
pub message_fetch_counter: AtomicUsize,
genesis_hash: ChainHash,
}
impl TestChannelMessageHandler {
pub fn new() -> Self {
pub fn new(genesis_hash: ChainHash) -> Self {
TestChannelMessageHandler {
pending_events: Mutex::new(Vec::new()),
expected_recv_msgs: Mutex::new(None),
connected_peers: Mutex::new(HashSet::new()),
message_fetch_counter: AtomicUsize::new(0),
genesis_hash,
}
}
@ -475,6 +478,10 @@ impl msgs::ChannelMessageHandler for TestChannelMessageHandler {
channelmanager::provided_init_features(&UserConfig::default())
}
fn get_genesis_hashes(&self) -> Option<Vec<ChainHash>> {
Some(vec![self.genesis_hash])
}
fn handle_open_channel_v2(&self, _their_node_id: &PublicKey, msg: &msgs::OpenChannelV2) {
self.received_msg(wire::Message::OpenChannelV2(msg.clone()));
}