From: Duncan Dean Date: Thu, 1 Jun 2023 10:40:57 +0000 (+0200) Subject: Send and handle `networks` field in `Init` messages X-Git-Tag: v0.0.116-alpha1~22^2 X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=commitdiff_plain;h=b52b936bdd28c4cda482a175a20c5aef1c6e02a5;p=rust-lightning 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. --- diff --git a/lightning-background-processor/src/lib.rs b/lightning-background-processor/src/lib.rs index ef8a07de1..248f79dd5 100644 --- a/lightning-background-processor/src/lib.rs +++ b/lightning-background-processor/src/lib.rs @@ -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{} }; diff --git a/lightning-net-tokio/src/lib.rs b/lightning-net-tokio/src/lib.rs index 2f0c96396..724a57fb1 100644 --- a/lightning-net-tokio/src/lib.rs +++ b/lightning-net-tokio/src/lib.rs @@ -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> { + Some(vec![ChainHash::using_genesis_block(Network::Testnet)]) + } } impl MessageSendEventsProvider for MsgHandler { fn get_and_clear_pending_msg_events(&self) -> Vec { diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index 2f6158b7f..5417fe5fc 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -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> { + 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(), diff --git a/lightning/src/ln/msgs.rs b/lightning/src/ln/msgs.rs index 0132497c6..3dd4a6da7 100644 --- a/lightning/src/ln/msgs.rs +++ b/lightning/src/ln/msgs.rs @@ -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>; } /// A trait to describe an object which can receive routing messages. diff --git a/lightning/src/ln/peer_handler.rs b/lightning/src/ln/peer_handler.rs index 1e9486595..7d85d0ba1 100644 --- a/lightning/src/ln/peer_handler.rs +++ b/lightning/src/ln/peer_handler.rs @@ -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> { + // 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 Vec { + fn create_feature_incompatible_peermgr_cfgs(peer_count: usize) -> Vec { 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 { + 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); diff --git a/lightning/src/util/test_utils.rs b/lightning/src/util/test_utils.rs index 331ef3b11..fe61e9c72 100644 --- a/lightning/src/util/test_utils.rs +++ b/lightning/src/util/test_utils.rs @@ -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>>>, connected_peers: Mutex>, 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> { + 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())); }