|
| 1 | +//! Multi-module tests that use database fixtures |
| 2 | +
|
| 3 | +use std::sync::Arc; |
| 4 | +use std::time::{SystemTime, UNIX_EPOCH}; |
| 5 | +use bitcoin::{BlockHash, Network}; |
| 6 | +use bitcoin::secp256k1::ecdsa::Signature; |
| 7 | +use bitcoin::secp256k1::{Secp256k1, SecretKey}; |
| 8 | +use bitcoin::hashes::Hash; |
| 9 | +use bitcoin::hashes::sha256d::Hash as Sha256dHash; |
| 10 | +use lightning::ln::features::ChannelFeatures; |
| 11 | +use lightning::ln::msgs::{ChannelAnnouncement, ChannelUpdate, UnsignedChannelAnnouncement, UnsignedChannelUpdate}; |
| 12 | +use lightning::routing::gossip::{NetworkGraph, NodeId}; |
| 13 | +use lightning::util::ser::Writeable; |
| 14 | +use lightning_rapid_gossip_sync::RapidGossipSync; |
| 15 | +use tokio_postgres::NoTls; |
| 16 | +use crate::{config, serialize_delta}; |
| 17 | +use crate::persistence::GossipPersister; |
| 18 | +use crate::types::{GossipMessage, tests::TestLogger}; |
| 19 | + |
| 20 | +const CLIENT_BACKDATE_INTERVAL: u32 = 3600 * 24 * 7; // client backdates RGS by a week |
| 21 | + |
| 22 | +fn blank_signature() -> Signature { |
| 23 | + Signature::from_compact(&[0u8; 64]).unwrap() |
| 24 | +} |
| 25 | + |
| 26 | +fn genesis_hash() -> BlockHash { |
| 27 | + bitcoin::blockdata::constants::genesis_block(Network::Bitcoin).block_hash() |
| 28 | +} |
| 29 | + |
| 30 | +fn current_time() -> u32 { |
| 31 | + SystemTime::now().duration_since(UNIX_EPOCH).expect("Time must be > 1970").as_secs() as u32 |
| 32 | +} |
| 33 | + |
| 34 | +fn generate_announcement(short_channel_id: u64) -> ChannelAnnouncement { |
| 35 | + let secp_context = Secp256k1::new(); |
| 36 | + |
| 37 | + let random_private_key_1 = SecretKey::from_slice(&[1; 32]).unwrap(); |
| 38 | + let random_public_key_1 = random_private_key_1.public_key(&secp_context); |
| 39 | + let node_id_1 = NodeId::from_pubkey(&random_public_key_1); |
| 40 | + |
| 41 | + let random_private_key_2 = SecretKey::from_slice(&[2; 32]).unwrap(); |
| 42 | + let random_public_key_2 = random_private_key_2.public_key(&secp_context); |
| 43 | + let node_id_2 = NodeId::from_pubkey(&random_public_key_2); |
| 44 | + |
| 45 | + let announcement = UnsignedChannelAnnouncement { |
| 46 | + features: ChannelFeatures::empty(), |
| 47 | + chain_hash: genesis_hash(), |
| 48 | + short_channel_id, |
| 49 | + node_id_1, |
| 50 | + node_id_2, |
| 51 | + bitcoin_key_1: node_id_1, |
| 52 | + bitcoin_key_2: node_id_2, |
| 53 | + excess_data: vec![], |
| 54 | + }; |
| 55 | + |
| 56 | + let msg_hash = bitcoin::secp256k1::Message::from_slice(&Sha256dHash::hash(&announcement.encode()[..])[..]).unwrap(); |
| 57 | + let node_signature_1 = secp_context.sign_ecdsa(&msg_hash, &random_private_key_1); |
| 58 | + let node_signature_2 = secp_context.sign_ecdsa(&msg_hash, &random_private_key_2); |
| 59 | + |
| 60 | + ChannelAnnouncement { |
| 61 | + node_signature_1, |
| 62 | + node_signature_2, |
| 63 | + bitcoin_signature_1: node_signature_1, |
| 64 | + bitcoin_signature_2: node_signature_2, |
| 65 | + contents: announcement, |
| 66 | + } |
| 67 | +} |
| 68 | + |
| 69 | +fn generate_update(scid: u64, direction: bool, timestamp: u32, expiry_delta: u16, min_msat: u64, max_msat: u64, base_msat: u32, fee_rate: u32) -> ChannelUpdate { |
| 70 | + let flag_mask = if direction { 1 } else { 0 }; |
| 71 | + ChannelUpdate { |
| 72 | + signature: blank_signature(), |
| 73 | + contents: UnsignedChannelUpdate { |
| 74 | + chain_hash: genesis_hash(), |
| 75 | + short_channel_id: scid, |
| 76 | + timestamp, |
| 77 | + flags: 0 | flag_mask, |
| 78 | + cltv_expiry_delta: expiry_delta, |
| 79 | + htlc_minimum_msat: min_msat, |
| 80 | + htlc_maximum_msat: max_msat, |
| 81 | + fee_base_msat: base_msat, |
| 82 | + fee_proportional_millionths: fee_rate, |
| 83 | + excess_data: vec![], |
| 84 | + }, |
| 85 | + } |
| 86 | +} |
| 87 | + |
| 88 | +async fn clean_test_db() { |
| 89 | + let connection_config = config::db_connection_config(); |
| 90 | + let (client, connection) = connection_config.connect(NoTls).await.unwrap(); |
| 91 | + |
| 92 | + tokio::spawn(async move { |
| 93 | + if let Err(e) = connection.await { |
| 94 | + panic!("connection error: {}", e); |
| 95 | + } |
| 96 | + }); |
| 97 | + |
| 98 | + client.query("TRUNCATE TABLE channel_announcements RESTART IDENTITY CASCADE", &[]).await.unwrap(); |
| 99 | + client.query("TRUNCATE TABLE channel_updates RESTART IDENTITY CASCADE", &[]).await.unwrap(); |
| 100 | + client.query("TRUNCATE TABLE config RESTART IDENTITY CASCADE", &[]).await.unwrap(); |
| 101 | +} |
| 102 | + |
| 103 | +#[tokio::test] |
| 104 | +async fn test_trivial_setup() { |
| 105 | + // start off with a clean slate |
| 106 | + clean_test_db().await; |
| 107 | + |
| 108 | + let logger = Arc::new(TestLogger::new()); |
| 109 | + let network_graph = NetworkGraph::new(Network::Bitcoin, logger.clone()); |
| 110 | + let network_graph_arc = Arc::new(network_graph); |
| 111 | + let (mut persister, receiver) = GossipPersister::new(network_graph_arc.clone(), logger.clone()); |
| 112 | + |
| 113 | + let short_channel_id = 1; |
| 114 | + let timestamp = current_time() - 10; |
| 115 | + println!("timestamp: {}", timestamp); |
| 116 | + |
| 117 | + { // seed the db |
| 118 | + let announcement = generate_announcement(short_channel_id); |
| 119 | + let update_1 = generate_update(short_channel_id, false, timestamp, 0, 0, 0, 5, 0); |
| 120 | + let update_2 = generate_update(short_channel_id, true, timestamp, 0, 0, 0, 10, 0); |
| 121 | + |
| 122 | + network_graph_arc.update_channel_from_announcement_no_lookup(&announcement).unwrap(); |
| 123 | + network_graph_arc.update_channel_unsigned(&update_1.contents).unwrap(); |
| 124 | + network_graph_arc.update_channel_unsigned(&update_2.contents).unwrap(); |
| 125 | + |
| 126 | + receiver.send(GossipMessage::ChannelAnnouncement(announcement)).await.unwrap(); |
| 127 | + receiver.send(GossipMessage::ChannelUpdate(update_1)).await.unwrap(); |
| 128 | + receiver.send(GossipMessage::ChannelUpdate(update_2)).await.unwrap(); |
| 129 | + drop(receiver); |
| 130 | + persister.persist_gossip().await; |
| 131 | + } |
| 132 | + |
| 133 | + let serialization = serialize_delta(network_graph_arc.clone(), 0, logger.clone()).await; |
| 134 | + logger.assert_log_contains("rapid_gossip_sync_server", "announcement channel count: 1", 1); |
| 135 | + clean_test_db().await; |
| 136 | + |
| 137 | + let channel_count = network_graph_arc.read_only().channels().len(); |
| 138 | + |
| 139 | + assert_eq!(channel_count, 1); |
| 140 | + assert_eq!(serialization.message_count, 3); |
| 141 | + assert_eq!(serialization.announcement_count, 1); |
| 142 | + assert_eq!(serialization.update_count, 2); |
| 143 | + |
| 144 | + let client_graph = NetworkGraph::new(Network::Bitcoin, logger.clone()); |
| 145 | + let client_graph_arc = Arc::new(client_graph); |
| 146 | + let rgs = RapidGossipSync::new(client_graph_arc.clone(), logger.clone()); |
| 147 | + let update_result = rgs.update_network_graph(&serialization.data).unwrap(); |
| 148 | + println!("update result: {}", update_result); |
| 149 | + // the update result must be a multiple of our snapshot granularity |
| 150 | + assert_eq!(update_result % config::SNAPSHOT_CALCULATION_INTERVAL, 0); |
| 151 | + assert!(update_result < timestamp); |
| 152 | + |
| 153 | + let timestamp_delta = timestamp - update_result; |
| 154 | + println!("timestamp delta: {}", timestamp_delta); |
| 155 | + assert!(timestamp_delta < config::SNAPSHOT_CALCULATION_INTERVAL); |
| 156 | + |
| 157 | + let readonly_graph = client_graph_arc.read_only(); |
| 158 | + let channels = readonly_graph.channels(); |
| 159 | + let client_channel_count = channels.len(); |
| 160 | + assert_eq!(client_channel_count, 1); |
| 161 | + |
| 162 | + let first_channel = channels.get(&short_channel_id).unwrap(); |
| 163 | + assert!(&first_channel.announcement_message.is_none()); |
| 164 | + assert_eq!(first_channel.one_to_two.as_ref().unwrap().fees.base_msat, 5); |
| 165 | + assert_eq!(first_channel.two_to_one.as_ref().unwrap().fees.base_msat, 10); |
| 166 | + let last_update_seen_a = first_channel.one_to_two.as_ref().unwrap().last_update; |
| 167 | + let last_update_seen_b = first_channel.two_to_one.as_ref().unwrap().last_update; |
| 168 | + println!("last update a: {}", last_update_seen_a); |
| 169 | + println!("last update b: {}", last_update_seen_b); |
| 170 | + assert_eq!(last_update_seen_a, update_result - CLIENT_BACKDATE_INTERVAL); |
| 171 | + assert_eq!(last_update_seen_b, update_result - CLIENT_BACKDATE_INTERVAL); |
| 172 | +} |
0 commit comments