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)
}
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<msgs::ChannelUpdate>, Option<msgs::ChannelUpdate>)> {
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.
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};
use util::test_utils;
use util::test_utils::TestVecWriter;
use util::logger::Logger;
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;
use secp256k1::Secp256k1;
use std::sync::Arc;
+ use std::collections::btree_map::Entry as BtreeEntry;
fn create_router() -> (Secp256k1<All>, PublicKey, Router) {
let secp_ctx = Secp256k1::new();
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<Logger> = 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")
+ };
+
+ }
}