Send and handle `networks` field in `Init` messages
authorDuncan Dean <git@dunxen.dev>
Thu, 1 Jun 2023 10:40:57 +0000 (12:40 +0200)
committerDuncan Dean <git@dunxen.dev>
Mon, 5 Jun 2023 07:45:48 +0000 (09:45 +0200)
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.

lightning-background-processor/src/lib.rs
lightning-net-tokio/src/lib.rs
lightning/src/ln/channelmanager.rs
lightning/src/ln/msgs.rs
lightning/src/ln/peer_handler.rs
lightning/src/util/test_utils.rs

index ef8a07de1ca67cb8c3d2245c2fa88a90c1e6ea35..248f79dd59ea5060a0239f566c308cd882120984 100644 (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{}
                        };
index 2f0c96396bf92f69d2c9f88e4a1526c4990c96d7..724a57fb15c19bb1ba9c9c09bd3fbe44fc3a6478 100644 (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> {
index 2f6158b7f5ce2172e2f5a75e5e378de6a2ad6c34..5417fe5fcaaab1f66a2142325e5315a9b1104544 100644 (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(),
index 0132497c6ade90a9ef99ce08bf8c3b3deb3a1a66..3dd4a6da70ccda8bde8a2e22f50db9c12e12d9cd 100644 (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.
index 1e9486595b5694004e24b735c99bd4d6d62c4306..7d85d0ba1e5126a945e8a533fc14a2916b2a1575 100644 (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);
index 331ef3b1172c9492d34c39626b556e31bc2e1a64..fe61e9c7214e25a8ad901f5242aea701e03950fe 100644 (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()));
        }