diff --git a/lightning-net-tokio/src/lib.rs b/lightning-net-tokio/src/lib.rs index 94c3eee15..dd0b861c1 100644 --- a/lightning-net-tokio/src/lib.rs +++ b/lightning-net-tokio/src/lib.rs @@ -507,7 +507,7 @@ mod tests { fn handle_channel_announcement(&self, _msg: &ChannelAnnouncement) -> Result { Ok(false) } fn handle_channel_update(&self, _msg: &ChannelUpdate) -> Result { Ok(false) } fn handle_htlc_fail_channel_update(&self, _update: &HTLCFailChannelUpdate) { } - fn get_next_channel_announcements(&self, _starting_point: u64, _batch_amount: u8) -> Vec<(ChannelAnnouncement, ChannelUpdate, ChannelUpdate)> { Vec::new() } + fn get_next_channel_announcements(&self, _starting_point: u64, _batch_amount: u8) -> Vec<(ChannelAnnouncement, Option, Option)> { Vec::new() } fn get_next_node_announcements(&self, _starting_point: Option<&PublicKey>, _batch_amount: u8) -> Vec { Vec::new() } fn should_request_full_sync(&self, _node_id: &PublicKey) -> bool { false } } diff --git a/lightning/src/chain/chaininterface.rs b/lightning/src/chain/chaininterface.rs index 73aecfe94..b30ebc1ee 100644 --- a/lightning/src/chain/chaininterface.rs +++ b/lightning/src/chain/chaininterface.rs @@ -22,6 +22,7 @@ use std::marker::PhantomData; use std::ptr; /// Used to give chain error details upstream +#[derive(Clone)] pub enum ChainError { /// Client doesn't support UTXO lookup (but the chain hash matches our genesis block hash) NotSupported, diff --git a/lightning/src/ln/msgs.rs b/lightning/src/ln/msgs.rs index 1ad798fd4..85f3026ea 100644 --- a/lightning/src/ln/msgs.rs +++ b/lightning/src/ln/msgs.rs @@ -601,7 +601,7 @@ pub trait RoutingMessageHandler : Send + Sync { /// Gets a subset of the channel announcements and updates required to dump our routing table /// to a remote node, starting at the short_channel_id indicated by starting_point and /// including the batch_amount entries immediately higher in numerical value than starting_point. - fn get_next_channel_announcements(&self, starting_point: u64, batch_amount: u8) -> Vec<(ChannelAnnouncement, ChannelUpdate, ChannelUpdate)>; + fn get_next_channel_announcements(&self, starting_point: u64, batch_amount: u8) -> Vec<(ChannelAnnouncement, Option, Option)>; /// Gets a subset of the node announcements required to dump our routing table to a remote node, /// starting at the node *after* the provided publickey and including batch_amount entries /// immediately higher (as defined by ::cmp) than starting_point. diff --git a/lightning/src/ln/peer_handler.rs b/lightning/src/ln/peer_handler.rs index fc6d1cbcd..15fa105d0 100644 --- a/lightning/src/ln/peer_handler.rs +++ b/lightning/src/ln/peer_handler.rs @@ -355,10 +355,14 @@ impl PeerManager where InitSyncTracker::ChannelsSyncing(c) if c < 0xffff_ffff_ffff_ffff => { let steps = ((MSG_BUFF_SIZE - peer.pending_outbound_buffer.len() + 2) / 3) as u8; let all_messages = self.message_handler.route_handler.get_next_channel_announcements(c, steps); - for &(ref announce, ref update_a, ref update_b) in all_messages.iter() { + for &(ref announce, ref update_a_option, ref update_b_option) in all_messages.iter() { encode_and_send_msg!(announce); - encode_and_send_msg!(update_a); - encode_and_send_msg!(update_b); + if let &Some(ref update_a) = update_a_option { + encode_and_send_msg!(update_a); + } + if let &Some(ref update_b) = update_b_option { + encode_and_send_msg!(update_b); + } peer.sync_status = InitSyncTracker::ChannelsSyncing(announce.contents.short_channel_id + 1); } if all_messages.is_empty() || all_messages.len() != steps as usize { @@ -1313,7 +1317,7 @@ mod tests { Err(msgs::LightningError { err: "", action: msgs::ErrorAction::IgnoreError }) } fn handle_htlc_fail_channel_update(&self, _update: &msgs::HTLCFailChannelUpdate) {} - fn get_next_channel_announcements(&self, starting_point: u64, batch_amount: u8) -> Vec<(msgs::ChannelAnnouncement, msgs::ChannelUpdate,msgs::ChannelUpdate)> { + fn get_next_channel_announcements(&self, starting_point: u64, batch_amount: u8) -> Vec<(msgs::ChannelAnnouncement, Option, Option)> { let mut chan_anns = Vec::new(); const TOTAL_UPDS: u64 = 100; let end: u64 = min(starting_point + batch_amount as u64, TOTAL_UPDS - self.chan_anns_sent.load(Ordering::Acquire) as u64); @@ -1322,7 +1326,7 @@ mod tests { let chan_upd_2 = get_dummy_channel_update(i); let chan_ann = get_dummy_channel_announcement(i); - chan_anns.push((chan_ann, chan_upd_1, chan_upd_2)); + chan_anns.push((chan_ann, Some(chan_upd_1), Some(chan_upd_2))); } self.chan_anns_sent.fetch_add(chan_anns.len(), Ordering::AcqRel); diff --git a/lightning/src/ln/router.rs b/lightning/src/ln/router.rs index 39e8e6114..950072504 100644 --- a/lightning/src/ln/router.rs +++ b/lightning/src/ln/router.rs @@ -559,6 +559,7 @@ impl RoutingMessageHandler for Router { add_channel_to_node!(msg.contents.node_id_1); add_channel_to_node!(msg.contents.node_id_2); + log_trace!(self, "Added channel_announcement for {}{}", msg.contents.short_channel_id, if !should_relay { " with excess uninterpreted data!" } else { "" }); Ok(should_relay) } @@ -663,19 +664,16 @@ impl RoutingMessageHandler for Router { Ok(msg.contents.excess_data.is_empty()) } - - fn get_next_channel_announcements(&self, starting_point: u64, batch_amount: u8) -> Vec<(msgs::ChannelAnnouncement, msgs::ChannelUpdate,msgs::ChannelUpdate)> { + fn get_next_channel_announcements(&self, starting_point: u64, batch_amount: u8) -> Vec<(msgs::ChannelAnnouncement, Option, Option)> { let mut result = Vec::with_capacity(batch_amount as usize); let network = self.network_map.read().unwrap(); let mut iter = network.channels.range(starting_point..); while result.len() < batch_amount as usize { if let Some((_, ref chan)) = iter.next() { - if chan.announcement_message.is_some() && - chan.one_to_two.last_update_message.is_some() && - chan.two_to_one.last_update_message.is_some() { + if chan.announcement_message.is_some() { result.push((chan.announcement_message.clone().unwrap(), - chan.one_to_two.last_update_message.clone().unwrap(), - chan.two_to_one.last_update_message.clone().unwrap())); + chan.one_to_two.last_update_message.clone(), + chan.two_to_one.last_update_message.clone())); } else { // TODO: We may end up sending un-announced channel_updates if we are sending // initial sync data while receiving announce/updates for this channel. @@ -1057,7 +1055,8 @@ mod tests { use ln::channelmanager; use ln::router::{Router,NodeInfo,NetworkMap,ChannelInfo,DirectionalChannelInfo,RouteHint}; use ln::features::{ChannelFeatures, InitFeatures, NodeFeatures}; - use ln::msgs::{ErrorAction, LightningError, RoutingMessageHandler}; + use ln::msgs::{ErrorAction, LightningError, RoutingMessageHandler, UnsignedNodeAnnouncement, NodeAnnouncement, + UnsignedChannelAnnouncement, ChannelAnnouncement, UnsignedChannelUpdate, ChannelUpdate, HTLCFailChannelUpdate}; use util::test_utils; use util::test_utils::TestVecWriter; use util::logger::Logger; @@ -1066,6 +1065,10 @@ mod tests { use bitcoin_hashes::sha256d::Hash as Sha256dHash; use bitcoin_hashes::Hash; use bitcoin::network::constants::Network; + use bitcoin::blockdata::constants::genesis_block; + use bitcoin::blockdata::script::Builder; + use bitcoin::blockdata::opcodes; + use bitcoin::util::hash::BitcoinHash; use hex; @@ -1074,6 +1077,7 @@ mod tests { use secp256k1::Secp256k1; use std::sync::Arc; + use std::collections::btree_map::Entry as BtreeEntry; fn create_router() -> (Secp256k1, PublicKey, Router) { let secp_ctx = Secp256k1::new(); @@ -1864,4 +1868,770 @@ mod tests { assert!(router.should_request_full_sync(&node_id)); assert!(!router.should_request_full_sync(&node_id)); } + + #[test] + fn handling_node_announcements() { + let (secp_ctx, _, router) = create_router(); + + let node_1_privkey = &SecretKey::from_slice(&[42; 32]).unwrap(); + let node_2_privkey = &SecretKey::from_slice(&[41; 32]).unwrap(); + let node_id_1 = PublicKey::from_secret_key(&secp_ctx, node_1_privkey); + let node_id_2 = PublicKey::from_secret_key(&secp_ctx, node_2_privkey); + let node_1_btckey = &SecretKey::from_slice(&[40; 32]).unwrap(); + let node_2_btckey = &SecretKey::from_slice(&[39; 32]).unwrap(); + let zero_hash = Sha256dHash::hash(&[0; 32]); + let first_announcement_time = 500; + + let mut unsigned_announcement = UnsignedNodeAnnouncement { + features: NodeFeatures::supported(), + timestamp: first_announcement_time, + node_id: node_id_1, + rgb: [0; 3], + alias: [0; 32], + addresses: Vec::new(), + excess_address_data: Vec::new(), + excess_data: Vec::new(), + }; + let mut msghash = hash_to_message!(&Sha256dHash::hash(&unsigned_announcement.encode()[..])[..]); + let valid_announcement = NodeAnnouncement { + signature: secp_ctx.sign(&msghash, node_1_privkey), + contents: unsigned_announcement.clone() + }; + + match router.handle_node_announcement(&valid_announcement) { + Ok(_) => panic!(), + Err(e) => assert_eq!("No existing channels for node_announcement", e.err) + }; + + { + // Announce a channel to add a corresponding node. + let unsigned_announcement = UnsignedChannelAnnouncement { + features: ChannelFeatures::supported(), + chain_hash: genesis_block(Network::Testnet).header.bitcoin_hash(), + short_channel_id: 0, + node_id_1, + node_id_2, + bitcoin_key_1: PublicKey::from_secret_key(&secp_ctx, node_1_btckey), + bitcoin_key_2: PublicKey::from_secret_key(&secp_ctx, node_2_btckey), + excess_data: Vec::new(), + }; + + let msghash = hash_to_message!(&Sha256dHash::hash(&unsigned_announcement.encode()[..])[..]); + let valid_announcement = ChannelAnnouncement { + node_signature_1: secp_ctx.sign(&msghash, node_1_privkey), + node_signature_2: secp_ctx.sign(&msghash, node_2_privkey), + bitcoin_signature_1: secp_ctx.sign(&msghash, node_1_btckey), + bitcoin_signature_2: secp_ctx.sign(&msghash, node_2_btckey), + contents: unsigned_announcement.clone(), + }; + match router.handle_channel_announcement(&valid_announcement) { + Ok(res) => assert!(res), + _ => panic!() + }; + } + + match router.handle_node_announcement(&valid_announcement) { + Ok(res) => assert!(res), + Err(_) => panic!() + }; + + let fake_msghash = hash_to_message!(&zero_hash); + match router.handle_node_announcement( + &NodeAnnouncement { + signature: secp_ctx.sign(&fake_msghash, node_1_privkey), + contents: unsigned_announcement.clone() + }) { + Ok(_) => panic!(), + Err(e) => assert_eq!(e.err, "Invalid signature from remote node") + }; + + unsigned_announcement.timestamp += 1000; + unsigned_announcement.excess_data.push(1); + msghash = hash_to_message!(&Sha256dHash::hash(&unsigned_announcement.encode()[..])[..]); + let announcement_with_data = NodeAnnouncement { + signature: secp_ctx.sign(&msghash, node_1_privkey), + contents: unsigned_announcement.clone() + }; + // Return false because contains excess data. + match router.handle_node_announcement(&announcement_with_data) { + Ok(res) => assert!(!res), + Err(_) => panic!() + }; + unsigned_announcement.excess_data = Vec::new(); + + // Even though previous announcement was not relayed further, we still accepted it, + // so we now won't accept announcements before the previous one. + unsigned_announcement.timestamp -= 10; + msghash = hash_to_message!(&Sha256dHash::hash(&unsigned_announcement.encode()[..])[..]); + let outdated_announcement = NodeAnnouncement { + signature: secp_ctx.sign(&msghash, node_1_privkey), + contents: unsigned_announcement.clone() + }; + match router.handle_node_announcement(&outdated_announcement) { + Ok(_) => panic!(), + Err(e) => assert_eq!(e.err, "Update older than last processed update") + }; + } + + #[test] + fn handling_channel_announcements() { + let secp_ctx = Secp256k1::new(); + let our_id = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice( + &hex::decode("0101010101010101010101010101010101010101010101010101010101010101").unwrap()[..]).unwrap()); + let logger: Arc = Arc::new(test_utils::TestLogger::new()); + let chain_monitor = Arc::new(test_utils::TestChainWatcher::new()); + let router = Router::new(our_id, chain_monitor.clone(), Arc::clone(&logger)); + + let node_1_privkey = &SecretKey::from_slice(&[42; 32]).unwrap(); + let node_2_privkey = &SecretKey::from_slice(&[41; 32]).unwrap(); + let node_id_1 = PublicKey::from_secret_key(&secp_ctx, node_1_privkey); + let node_id_2 = PublicKey::from_secret_key(&secp_ctx, node_2_privkey); + let node_1_btckey = &SecretKey::from_slice(&[40; 32]).unwrap(); + let node_2_btckey = &SecretKey::from_slice(&[39; 32]).unwrap(); + + let good_script = Builder::new().push_opcode(opcodes::all::OP_PUSHNUM_2) + .push_slice(&PublicKey::from_secret_key(&secp_ctx, node_1_btckey).serialize()) + .push_slice(&PublicKey::from_secret_key(&secp_ctx, node_2_btckey).serialize()) + .push_opcode(opcodes::all::OP_PUSHNUM_2) + .push_opcode(opcodes::all::OP_CHECKMULTISIG).into_script().to_v0_p2wsh(); + + + let mut unsigned_announcement = UnsignedChannelAnnouncement { + features: ChannelFeatures::supported(), + chain_hash: genesis_block(Network::Testnet).header.bitcoin_hash(), + short_channel_id: 0, + node_id_1, + node_id_2, + bitcoin_key_1: PublicKey::from_secret_key(&secp_ctx, node_1_btckey), + bitcoin_key_2: PublicKey::from_secret_key(&secp_ctx, node_2_btckey), + excess_data: Vec::new(), + }; + + let channel_key = NetworkMap::get_key(unsigned_announcement.short_channel_id, + unsigned_announcement.chain_hash); + + let mut msghash = hash_to_message!(&Sha256dHash::hash(&unsigned_announcement.encode()[..])[..]); + let valid_announcement = ChannelAnnouncement { + node_signature_1: secp_ctx.sign(&msghash, node_1_privkey), + node_signature_2: secp_ctx.sign(&msghash, node_2_privkey), + bitcoin_signature_1: secp_ctx.sign(&msghash, node_1_btckey), + bitcoin_signature_2: secp_ctx.sign(&msghash, node_2_btckey), + contents: unsigned_announcement.clone(), + }; + + // Test if the UTXO lookups were not supported + *chain_monitor.utxo_ret.lock().unwrap() = Err(chaininterface::ChainError::NotSupported); + + match router.handle_channel_announcement(&valid_announcement) { + Ok(res) => assert!(res), + _ => panic!() + }; + { + let network = router.network_map.write().unwrap(); + match network.channels.get(&channel_key) { + None => panic!(), + Some(_) => () + } + } + + // If we receive announcement for the same channel (with UTXO lookups disabled), + // drop new one on the floor, since we can't see any changes. + match router.handle_channel_announcement(&valid_announcement) { + Ok(_) => panic!(), + Err(e) => assert_eq!(e.err, "Already have knowledge of channel") + }; + + + // Test if an associated transaction were not on-chain (or not confirmed). + *chain_monitor.utxo_ret.lock().unwrap() = Err(chaininterface::ChainError::UnknownTx); + unsigned_announcement.short_channel_id += 1; + + msghash = hash_to_message!(&Sha256dHash::hash(&unsigned_announcement.encode()[..])[..]); + let valid_announcement = ChannelAnnouncement { + node_signature_1: secp_ctx.sign(&msghash, node_1_privkey), + node_signature_2: secp_ctx.sign(&msghash, node_2_privkey), + bitcoin_signature_1: secp_ctx.sign(&msghash, node_1_btckey), + bitcoin_signature_2: secp_ctx.sign(&msghash, node_2_btckey), + contents: unsigned_announcement.clone(), + }; + + match router.handle_channel_announcement(&valid_announcement) { + Ok(_) => panic!(), + Err(e) => assert_eq!(e.err, "Channel announced without corresponding UTXO entry") + }; + + + // Now test if the transaction is found in the UTXO set and the script is correct. + unsigned_announcement.short_channel_id += 1; + *chain_monitor.utxo_ret.lock().unwrap() = Ok((good_script.clone(), 0)); + let channel_key = NetworkMap::get_key(unsigned_announcement.short_channel_id, + unsigned_announcement.chain_hash); + + msghash = hash_to_message!(&Sha256dHash::hash(&unsigned_announcement.encode()[..])[..]); + let valid_announcement = ChannelAnnouncement { + node_signature_1: secp_ctx.sign(&msghash, node_1_privkey), + node_signature_2: secp_ctx.sign(&msghash, node_2_privkey), + bitcoin_signature_1: secp_ctx.sign(&msghash, node_1_btckey), + bitcoin_signature_2: secp_ctx.sign(&msghash, node_2_btckey), + contents: unsigned_announcement.clone(), + }; + match router.handle_channel_announcement(&valid_announcement) { + Ok(res) => assert!(res), + _ => panic!() + }; + { + let network = router.network_map.write().unwrap(); + match network.channels.get(&channel_key) { + None => panic!(), + Some(_) => () + } + } + + // If we receive announcement for the same channel (but TX is not confirmed), + // drop new one on the floor, since we can't see any changes. + *chain_monitor.utxo_ret.lock().unwrap() = Err(chaininterface::ChainError::UnknownTx); + match router.handle_channel_announcement(&valid_announcement) { + Ok(_) => panic!(), + Err(e) => assert_eq!(e.err, "Channel announced without corresponding UTXO entry") + }; + + // But if it is confirmed, replace the channel + *chain_monitor.utxo_ret.lock().unwrap() = Ok((good_script, 0)); + unsigned_announcement.features = ChannelFeatures::empty(); + msghash = hash_to_message!(&Sha256dHash::hash(&unsigned_announcement.encode()[..])[..]); + let valid_announcement = ChannelAnnouncement { + node_signature_1: secp_ctx.sign(&msghash, node_1_privkey), + node_signature_2: secp_ctx.sign(&msghash, node_2_privkey), + bitcoin_signature_1: secp_ctx.sign(&msghash, node_1_btckey), + bitcoin_signature_2: secp_ctx.sign(&msghash, node_2_btckey), + contents: unsigned_announcement.clone(), + }; + match router.handle_channel_announcement(&valid_announcement) { + Ok(res) => assert!(res), + _ => panic!() + }; + { + let mut network = router.network_map.write().unwrap(); + match network.channels.entry(channel_key) { + BtreeEntry::Occupied(channel_entry) => { + assert_eq!(channel_entry.get().features, ChannelFeatures::empty()); + }, + _ => panic!() + } + } + + // Don't relay valid channels with excess data + unsigned_announcement.short_channel_id += 1; + unsigned_announcement.excess_data.push(1); + msghash = hash_to_message!(&Sha256dHash::hash(&unsigned_announcement.encode()[..])[..]); + let valid_announcement = ChannelAnnouncement { + node_signature_1: secp_ctx.sign(&msghash, node_1_privkey), + node_signature_2: secp_ctx.sign(&msghash, node_2_privkey), + bitcoin_signature_1: secp_ctx.sign(&msghash, node_1_btckey), + bitcoin_signature_2: secp_ctx.sign(&msghash, node_2_btckey), + contents: unsigned_announcement.clone(), + }; + match router.handle_channel_announcement(&valid_announcement) { + Ok(res) => assert!(!res), + _ => panic!() + }; + + unsigned_announcement.excess_data = Vec::new(); + let invalid_sig_announcement = ChannelAnnouncement { + node_signature_1: secp_ctx.sign(&msghash, node_1_privkey), + node_signature_2: secp_ctx.sign(&msghash, node_2_privkey), + bitcoin_signature_1: secp_ctx.sign(&msghash, node_1_btckey), + bitcoin_signature_2: secp_ctx.sign(&msghash, node_1_btckey), + contents: unsigned_announcement.clone(), + }; + match router.handle_channel_announcement(&invalid_sig_announcement) { + Ok(_) => panic!(), + Err(e) => assert_eq!(e.err, "Invalid signature from remote node") + }; + + unsigned_announcement.node_id_1 = PublicKey::from_secret_key(&secp_ctx, node_2_privkey); + msghash = hash_to_message!(&Sha256dHash::hash(&unsigned_announcement.encode()[..])[..]); + let channel_to_itself_announcement = ChannelAnnouncement { + node_signature_1: secp_ctx.sign(&msghash, node_1_privkey), + node_signature_2: secp_ctx.sign(&msghash, node_1_privkey), + bitcoin_signature_1: secp_ctx.sign(&msghash, node_1_btckey), + bitcoin_signature_2: secp_ctx.sign(&msghash, node_2_btckey), + contents: unsigned_announcement.clone(), + }; + match router.handle_channel_announcement(&channel_to_itself_announcement) { + Ok(_) => panic!(), + Err(e) => assert_eq!(e.err, "Channel announcement node had a channel with itself") + }; + } + + #[test] + fn handling_channel_update() { + let (secp_ctx, _, router) = create_router(); + let node_1_privkey = &SecretKey::from_slice(&[42; 32]).unwrap(); + let node_2_privkey = &SecretKey::from_slice(&[41; 32]).unwrap(); + let node_id_1 = PublicKey::from_secret_key(&secp_ctx, node_1_privkey); + let node_id_2 = PublicKey::from_secret_key(&secp_ctx, node_2_privkey); + let node_1_btckey = &SecretKey::from_slice(&[40; 32]).unwrap(); + let node_2_btckey = &SecretKey::from_slice(&[39; 32]).unwrap(); + + let zero_hash = Sha256dHash::hash(&[0; 32]); + let short_channel_id = 0; + let chain_hash = genesis_block(Network::Testnet).header.bitcoin_hash(); + let channel_key = NetworkMap::get_key(short_channel_id, chain_hash); + + + { + // Announce a channel we will update + let unsigned_announcement = UnsignedChannelAnnouncement { + features: ChannelFeatures::empty(), + chain_hash, + short_channel_id, + node_id_1, + node_id_2, + bitcoin_key_1: PublicKey::from_secret_key(&secp_ctx, node_1_btckey), + bitcoin_key_2: PublicKey::from_secret_key(&secp_ctx, node_2_btckey), + excess_data: Vec::new(), + }; + + let msghash = hash_to_message!(&Sha256dHash::hash(&unsigned_announcement.encode()[..])[..]); + let valid_channel_announcement = ChannelAnnouncement { + node_signature_1: secp_ctx.sign(&msghash, node_1_privkey), + node_signature_2: secp_ctx.sign(&msghash, node_2_privkey), + bitcoin_signature_1: secp_ctx.sign(&msghash, node_1_btckey), + bitcoin_signature_2: secp_ctx.sign(&msghash, node_2_btckey), + contents: unsigned_announcement.clone(), + }; + match router.handle_channel_announcement(&valid_channel_announcement) { + Ok(_) => (), + Err(_) => panic!() + }; + + } + + let mut unsigned_channel_update = UnsignedChannelUpdate { + chain_hash, + short_channel_id, + timestamp: 100, + flags: 0, + cltv_expiry_delta: 144, + htlc_minimum_msat: 1000000, + fee_base_msat: 10000, + fee_proportional_millionths: 20, + excess_data: Vec::new() + }; + let msghash = hash_to_message!(&Sha256dHash::hash(&unsigned_channel_update.encode()[..])[..]); + let valid_channel_update = ChannelUpdate { + signature: secp_ctx.sign(&msghash, node_1_privkey), + contents: unsigned_channel_update.clone() + }; + + match router.handle_channel_update(&valid_channel_update) { + Ok(res) => assert!(res), + _ => panic!() + }; + + { + let network = router.network_map.write().unwrap(); + match network.channels.get(&channel_key) { + None => panic!(), + Some(channel_info) => { + assert_eq!(channel_info.one_to_two.cltv_expiry_delta, 144); + assert_eq!(channel_info.two_to_one.cltv_expiry_delta, u16::max_value()); + } + } + } + + unsigned_channel_update.timestamp += 100; + unsigned_channel_update.excess_data.push(1); + let msghash = hash_to_message!(&Sha256dHash::hash(&unsigned_channel_update.encode()[..])[..]); + let valid_channel_update = ChannelUpdate { + signature: secp_ctx.sign(&msghash, node_1_privkey), + contents: unsigned_channel_update.clone() + }; + // Return false because contains excess data + match router.handle_channel_update(&valid_channel_update) { + Ok(res) => assert!(!res), + _ => panic!() + }; + + unsigned_channel_update.short_channel_id += 1; + let msghash = hash_to_message!(&Sha256dHash::hash(&unsigned_channel_update.encode()[..])[..]); + let valid_channel_update = ChannelUpdate { + signature: secp_ctx.sign(&msghash, node_1_privkey), + contents: unsigned_channel_update.clone() + }; + + match router.handle_channel_update(&valid_channel_update) { + Ok(_) => panic!(), + Err(e) => assert_eq!(e.err, "Couldn't find channel for update") + }; + unsigned_channel_update.short_channel_id = short_channel_id; + + + // Even though previous update was not relayed further, we still accepted it, + // so we now won't accept update before the previous one. + unsigned_channel_update.timestamp -= 10; + let msghash = hash_to_message!(&Sha256dHash::hash(&unsigned_channel_update.encode()[..])[..]); + let valid_channel_update = ChannelUpdate { + signature: secp_ctx.sign(&msghash, node_1_privkey), + contents: unsigned_channel_update.clone() + }; + + match router.handle_channel_update(&valid_channel_update) { + Ok(_) => panic!(), + Err(e) => assert_eq!(e.err, "Update older than last processed update") + }; + unsigned_channel_update.timestamp += 500; + + let fake_msghash = hash_to_message!(&zero_hash); + let invalid_sig_channel_update = ChannelUpdate { + signature: secp_ctx.sign(&fake_msghash, node_1_privkey), + contents: unsigned_channel_update.clone() + }; + + match router.handle_channel_update(&invalid_sig_channel_update) { + Ok(_) => panic!(), + Err(e) => assert_eq!(e.err, "Invalid signature from remote node") + }; + + } + + #[test] + fn handling_htlc_fail_channel_update() { + let (secp_ctx, our_id, router) = create_router(); + let node_1_privkey = &SecretKey::from_slice(&[42; 32]).unwrap(); + let node_2_privkey = &SecretKey::from_slice(&[41; 32]).unwrap(); + let node_id_1 = PublicKey::from_secret_key(&secp_ctx, node_1_privkey); + let node_id_2 = PublicKey::from_secret_key(&secp_ctx, node_2_privkey); + let node_1_btckey = &SecretKey::from_slice(&[40; 32]).unwrap(); + let node_2_btckey = &SecretKey::from_slice(&[39; 32]).unwrap(); + + let short_channel_id = 0; + let chain_hash = genesis_block(Network::Testnet).header.bitcoin_hash(); + let channel_key = NetworkMap::get_key(short_channel_id, chain_hash); + + { + // There is only local node in the table at the beginning. + let network = router.network_map.read().unwrap(); + assert_eq!(network.nodes.len(), 1); + assert_eq!(network.nodes.contains_key(&our_id), true); + } + + { + // Announce a channel we will update + let unsigned_announcement = UnsignedChannelAnnouncement { + features: ChannelFeatures::empty(), + chain_hash, + short_channel_id, + node_id_1, + node_id_2, + bitcoin_key_1: PublicKey::from_secret_key(&secp_ctx, node_1_btckey), + bitcoin_key_2: PublicKey::from_secret_key(&secp_ctx, node_2_btckey), + excess_data: Vec::new(), + }; + + let msghash = hash_to_message!(&Sha256dHash::hash(&unsigned_announcement.encode()[..])[..]); + let valid_channel_announcement = ChannelAnnouncement { + node_signature_1: secp_ctx.sign(&msghash, node_1_privkey), + node_signature_2: secp_ctx.sign(&msghash, node_2_privkey), + bitcoin_signature_1: secp_ctx.sign(&msghash, node_1_btckey), + bitcoin_signature_2: secp_ctx.sign(&msghash, node_2_btckey), + contents: unsigned_announcement.clone(), + }; + match router.handle_channel_announcement(&valid_channel_announcement) { + Ok(_) => (), + Err(_) => panic!() + }; + + } + + let channel_close_msg = HTLCFailChannelUpdate::ChannelClosed { + short_channel_id, + is_permanent: false + }; + + router.handle_htlc_fail_channel_update(&channel_close_msg); + + { + // Non-permanent closing just disables a channel + let network = router.network_map.write().unwrap(); + match network.channels.get(&channel_key) { + None => panic!(), + Some(channel_info) => { + assert!(!channel_info.one_to_two.enabled); + assert!(!channel_info.two_to_one.enabled); + } + } + } + + let channel_close_msg = HTLCFailChannelUpdate::ChannelClosed { + short_channel_id, + is_permanent: true + }; + + router.handle_htlc_fail_channel_update(&channel_close_msg); + + { + // Permanent closing deletes a channel + let network = router.network_map.read().unwrap(); + assert_eq!(network.channels.len(), 0); + // Nodes are also deleted because there are no associated channels anymore + // Only the local node remains in the table. + assert_eq!(network.nodes.len(), 1); + assert_eq!(network.nodes.contains_key(&our_id), true); + } + + // TODO: Test HTLCFailChannelUpdate::NodeFailure, which is not implemented yet. + } + + #[test] + fn getting_next_channel_announcements() { + let (secp_ctx, _, router) = create_router(); + let node_1_privkey = &SecretKey::from_slice(&[42; 32]).unwrap(); + let node_2_privkey = &SecretKey::from_slice(&[41; 32]).unwrap(); + let node_id_1 = PublicKey::from_secret_key(&secp_ctx, node_1_privkey); + let node_id_2 = PublicKey::from_secret_key(&secp_ctx, node_2_privkey); + let node_1_btckey = &SecretKey::from_slice(&[40; 32]).unwrap(); + let node_2_btckey = &SecretKey::from_slice(&[39; 32]).unwrap(); + + let short_channel_id = 1; + let chain_hash = genesis_block(Network::Testnet).header.bitcoin_hash(); + let channel_key = NetworkMap::get_key(short_channel_id, chain_hash); + + // Channels were not announced yet. + let channels_with_announcements = router.get_next_channel_announcements(0, 1); + assert_eq!(channels_with_announcements.len(), 0); + + { + // Announce a channel we will update + let unsigned_announcement = UnsignedChannelAnnouncement { + features: ChannelFeatures::empty(), + chain_hash, + short_channel_id, + node_id_1, + node_id_2, + bitcoin_key_1: PublicKey::from_secret_key(&secp_ctx, node_1_btckey), + bitcoin_key_2: PublicKey::from_secret_key(&secp_ctx, node_2_btckey), + excess_data: Vec::new(), + }; + + let msghash = hash_to_message!(&Sha256dHash::hash(&unsigned_announcement.encode()[..])[..]); + let valid_channel_announcement = ChannelAnnouncement { + node_signature_1: secp_ctx.sign(&msghash, node_1_privkey), + node_signature_2: secp_ctx.sign(&msghash, node_2_privkey), + bitcoin_signature_1: secp_ctx.sign(&msghash, node_1_btckey), + bitcoin_signature_2: secp_ctx.sign(&msghash, node_2_btckey), + contents: unsigned_announcement.clone(), + }; + match router.handle_channel_announcement(&valid_channel_announcement) { + Ok(_) => (), + Err(_) => panic!() + }; + } + + // Contains initial channel announcement now. + let channels_with_announcements = router.get_next_channel_announcements(channel_key, 1); + assert_eq!(channels_with_announcements.len(), 1); + if let Some(channel_announcements) = channels_with_announcements.first() { + let &(_, ref update_1, ref update_2) = channel_announcements; + assert_eq!(update_1, &None); + assert_eq!(update_2, &None); + } else { + panic!(); + } + + + { + // Valid channel update + let unsigned_channel_update = UnsignedChannelUpdate { + chain_hash, + short_channel_id, + timestamp: 101, + flags: 0, + cltv_expiry_delta: 144, + htlc_minimum_msat: 1000000, + fee_base_msat: 10000, + fee_proportional_millionths: 20, + excess_data: Vec::new() + }; + let msghash = hash_to_message!(&Sha256dHash::hash(&unsigned_channel_update.encode()[..])[..]); + let valid_channel_update = ChannelUpdate { + signature: secp_ctx.sign(&msghash, node_1_privkey), + contents: unsigned_channel_update.clone() + }; + match router.handle_channel_update(&valid_channel_update) { + Ok(_) => (), + Err(_) => panic!() + }; + } + + // Now contains an initial announcement and an update. + let channels_with_announcements = router.get_next_channel_announcements(channel_key, 1); + assert_eq!(channels_with_announcements.len(), 1); + if let Some(channel_announcements) = channels_with_announcements.first() { + let &(_, ref update_1, ref update_2) = channel_announcements; + assert_ne!(update_1, &None); + assert_eq!(update_2, &None); + } else { + panic!(); + } + + + { + // Channel update with excess data. + let unsigned_channel_update = UnsignedChannelUpdate { + chain_hash, + short_channel_id, + timestamp: 102, + flags: 0, + cltv_expiry_delta: 144, + htlc_minimum_msat: 1000000, + fee_base_msat: 10000, + fee_proportional_millionths: 20, + excess_data: [1; 3].to_vec() + }; + let msghash = hash_to_message!(&Sha256dHash::hash(&unsigned_channel_update.encode()[..])[..]); + let valid_channel_update = ChannelUpdate { + signature: secp_ctx.sign(&msghash, node_1_privkey), + contents: unsigned_channel_update.clone() + }; + match router.handle_channel_update(&valid_channel_update) { + Ok(_) => (), + Err(_) => panic!() + }; + } + + // Test that announcements with excess data won't be returned + let channels_with_announcements = router.get_next_channel_announcements(channel_key, 1); + assert_eq!(channels_with_announcements.len(), 1); + if let Some(channel_announcements) = channels_with_announcements.first() { + let &(_, ref update_1, ref update_2) = channel_announcements; + assert_eq!(update_1, &None); + assert_eq!(update_2, &None); + } else { + panic!(); + } + + // Further starting point have no channels after it + let channels_with_announcements = router.get_next_channel_announcements(channel_key + 1000, 1); + assert_eq!(channels_with_announcements.len(), 0); + } + + #[test] + fn getting_next_node_announcements() { + let (secp_ctx, _, router) = create_router(); + let node_1_privkey = &SecretKey::from_slice(&[42; 32]).unwrap(); + let node_2_privkey = &SecretKey::from_slice(&[41; 32]).unwrap(); + let node_id_1 = PublicKey::from_secret_key(&secp_ctx, node_1_privkey); + let node_id_2 = PublicKey::from_secret_key(&secp_ctx, node_2_privkey); + let node_1_btckey = &SecretKey::from_slice(&[40; 32]).unwrap(); + let node_2_btckey = &SecretKey::from_slice(&[39; 32]).unwrap(); + + let short_channel_id = 1; + let chain_hash = genesis_block(Network::Testnet).header.bitcoin_hash(); + + // No nodes yet. + let next_announcements = router.get_next_node_announcements(None, 10); + assert_eq!(next_announcements.len(), 0); + + { + // Announce a channel to add 2 nodes + let unsigned_announcement = UnsignedChannelAnnouncement { + features: ChannelFeatures::empty(), + chain_hash, + short_channel_id, + node_id_1, + node_id_2, + bitcoin_key_1: PublicKey::from_secret_key(&secp_ctx, node_1_btckey), + bitcoin_key_2: PublicKey::from_secret_key(&secp_ctx, node_2_btckey), + excess_data: Vec::new(), + }; + + let msghash = hash_to_message!(&Sha256dHash::hash(&unsigned_announcement.encode()[..])[..]); + let valid_channel_announcement = ChannelAnnouncement { + node_signature_1: secp_ctx.sign(&msghash, node_1_privkey), + node_signature_2: secp_ctx.sign(&msghash, node_2_privkey), + bitcoin_signature_1: secp_ctx.sign(&msghash, node_1_btckey), + bitcoin_signature_2: secp_ctx.sign(&msghash, node_2_btckey), + contents: unsigned_announcement.clone(), + }; + match router.handle_channel_announcement(&valid_channel_announcement) { + Ok(_) => (), + Err(_) => panic!() + }; + } + + + // Nodes were never announced + let next_announcements = router.get_next_node_announcements(None, 3); + assert_eq!(next_announcements.len(), 0); + + { + let mut unsigned_announcement = UnsignedNodeAnnouncement { + features: NodeFeatures::supported(), + timestamp: 1000, + node_id: node_id_1, + rgb: [0; 3], + alias: [0; 32], + addresses: Vec::new(), + excess_address_data: Vec::new(), + excess_data: Vec::new(), + }; + let msghash = hash_to_message!(&Sha256dHash::hash(&unsigned_announcement.encode()[..])[..]); + let valid_announcement = NodeAnnouncement { + signature: secp_ctx.sign(&msghash, node_1_privkey), + contents: unsigned_announcement.clone() + }; + match router.handle_node_announcement(&valid_announcement) { + Ok(_) => (), + Err(_) => panic!() + }; + + unsigned_announcement.node_id = node_id_2; + let msghash = hash_to_message!(&Sha256dHash::hash(&unsigned_announcement.encode()[..])[..]); + let valid_announcement = NodeAnnouncement { + signature: secp_ctx.sign(&msghash, node_2_privkey), + contents: unsigned_announcement.clone() + }; + + match router.handle_node_announcement(&valid_announcement) { + Ok(_) => (), + Err(_) => panic!() + }; + } + + let next_announcements = router.get_next_node_announcements(None, 3); + assert_eq!(next_announcements.len(), 2); + + // Skip the first node. + let next_announcements = router.get_next_node_announcements(Some(&node_id_1), 2); + assert_eq!(next_announcements.len(), 1); + + { + // Later announcement which should not be relayed (excess data) prevent us from sharing a node + let unsigned_announcement = UnsignedNodeAnnouncement { + features: NodeFeatures::supported(), + timestamp: 1010, + node_id: node_id_2, + rgb: [0; 3], + alias: [0; 32], + addresses: Vec::new(), + excess_address_data: Vec::new(), + excess_data: [1; 3].to_vec(), + }; + let msghash = hash_to_message!(&Sha256dHash::hash(&unsigned_announcement.encode()[..])[..]); + let valid_announcement = NodeAnnouncement { + signature: secp_ctx.sign(&msghash, node_2_privkey), + contents: unsigned_announcement.clone() + }; + match router.handle_node_announcement(&valid_announcement) { + Ok(res) => assert!(!res), + Err(_) => panic!() + }; + } + + let next_announcements = router.get_next_node_announcements(Some(&node_id_1), 2); + assert_eq!(next_announcements.len(), 0); + + } } diff --git a/lightning/src/util/test_utils.rs b/lightning/src/util/test_utils.rs index 78b81fa63..13cbde4aa 100644 --- a/lightning/src/util/test_utils.rs +++ b/lightning/src/util/test_utils.rs @@ -1,5 +1,5 @@ use chain::chaininterface; -use chain::chaininterface::ConfirmationTarget; +use chain::chaininterface::{ConfirmationTarget, ChainError, ChainWatchInterface}; use chain::transaction::OutPoint; use chain::keysinterface; use ln::channelmonitor; @@ -13,7 +13,9 @@ use util::logger::{Logger, Level, Record}; use util::ser::{Readable, ReadableArgs, Writer, Writeable}; use bitcoin::blockdata::transaction::Transaction; -use bitcoin::blockdata::script::Script; +use bitcoin::blockdata::script::{Builder, Script}; +use bitcoin::blockdata::block::Block; +use bitcoin::blockdata::opcodes; use bitcoin_hashes::sha256d::Hash as Sha256dHash; use bitcoin::network::constants::Network; @@ -176,7 +178,7 @@ impl msgs::RoutingMessageHandler for TestRoutingMessageHandler { Err(LightningError { err: "", action: msgs::ErrorAction::IgnoreError }) } fn handle_htlc_fail_channel_update(&self, _update: &msgs::HTLCFailChannelUpdate) {} - fn get_next_channel_announcements(&self, _starting_point: u64, _batch_amount: u8) -> Vec<(msgs::ChannelAnnouncement, msgs::ChannelUpdate,msgs::ChannelUpdate)> { + fn get_next_channel_announcements(&self, _starting_point: u64, _batch_amount: u8) -> Vec<(msgs::ChannelAnnouncement, Option, Option)> { Vec::new() } fn get_next_node_announcements(&self, _starting_point: Option<&PublicKey>, _batch_amount: u8) -> Vec { @@ -263,3 +265,28 @@ impl TestKeysInterface { } } } + +pub struct TestChainWatcher { + pub utxo_ret: Mutex>, +} + +impl TestChainWatcher { + pub fn new() -> Self { + let script = Builder::new().push_opcode(opcodes::OP_TRUE).into_script(); + Self { utxo_ret: Mutex::new(Ok((script, u64::max_value()))) } + } +} + +impl ChainWatchInterface for TestChainWatcher { + fn install_watch_tx(&self, _txid: &Sha256dHash, _script_pub_key: &Script) { } + fn install_watch_outpoint(&self, _outpoint: (Sha256dHash, u32), _out_script: &Script) { } + fn watch_all_txn(&self) { } + fn filter_block<'a>(&self, _block: &'a Block) -> (Vec<&'a Transaction>, Vec) { + (Vec::new(), Vec::new()) + } + fn reentered(&self) -> usize { 0 } + + fn get_chain_utxo(&self, _genesis_hash: Sha256dHash, _unspent_tx_output_identifier: u64) -> Result<(Script, u64), ChainError> { + self.utxo_ret.lock().unwrap().clone() + } +}