Skip to content

Commit 680dc7b

Browse files
committed
Add tests for handling channel announcements
1 parent b75c8d4 commit 680dc7b

File tree

3 files changed

+224
-2
lines changed

3 files changed

+224
-2
lines changed

lightning/src/chain/chaininterface.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ use std::marker::PhantomData;
2222
use std::ptr;
2323

2424
/// Used to give chain error details upstream
25+
#[derive(Clone)]
2526
pub enum ChainError {
2627
/// Client doesn't support UTXO lookup (but the chain hash matches our genesis block hash)
2728
NotSupported,

lightning/src/ln/router.rs

Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1068,6 +1068,8 @@ mod tests {
10681068
use bitcoin_hashes::Hash;
10691069
use bitcoin::network::constants::Network;
10701070
use bitcoin::blockdata::constants::genesis_block;
1071+
use bitcoin::blockdata::script::Builder;
1072+
use bitcoin::blockdata::opcodes;
10711073
use bitcoin::util::hash::BitcoinHash;
10721074

10731075
use hex;
@@ -1077,6 +1079,7 @@ mod tests {
10771079
use secp256k1::Secp256k1;
10781080

10791081
use std::sync::Arc;
1082+
use std::collections::btree_map::Entry as BtreeEntry;
10801083

10811084
fn create_router() -> (Secp256k1<All>, PublicKey, Router) {
10821085
let secp_ctx = Secp256k1::new();
@@ -1969,4 +1972,195 @@ mod tests {
19691972
Err(e) => assert_eq!(e.err, "Update older than last processed update")
19701973
};
19711974
}
1975+
1976+
#[test]
1977+
fn handling_channel_announcements() {
1978+
let secp_ctx = Secp256k1::new();
1979+
let our_id = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(
1980+
&hex::decode("0101010101010101010101010101010101010101010101010101010101010101").unwrap()[..]).unwrap());
1981+
let logger: Arc<Logger> = Arc::new(test_utils::TestLogger::new());
1982+
let chain_monitor = Arc::new(test_utils::TestChainWatcher::new());
1983+
let router = Router::new(our_id, chain_monitor.clone(), Arc::clone(&logger));
1984+
1985+
let node_1_privkey = &SecretKey::from_slice(&[42; 32]).unwrap();
1986+
let node_2_privkey = &SecretKey::from_slice(&[41; 32]).unwrap();
1987+
let node_id_1 = PublicKey::from_secret_key(&secp_ctx, node_1_privkey);
1988+
let node_id_2 = PublicKey::from_secret_key(&secp_ctx, node_2_privkey);
1989+
let node_1_btckey = &SecretKey::from_slice(&[40; 32]).unwrap();
1990+
let node_2_btckey = &SecretKey::from_slice(&[39; 32]).unwrap();
1991+
1992+
let good_script = Builder::new().push_opcode(opcodes::all::OP_PUSHNUM_2)
1993+
.push_slice(&PublicKey::from_secret_key(&secp_ctx, node_1_btckey).serialize())
1994+
.push_slice(&PublicKey::from_secret_key(&secp_ctx, node_2_btckey).serialize())
1995+
.push_opcode(opcodes::all::OP_PUSHNUM_2)
1996+
.push_opcode(opcodes::all::OP_CHECKMULTISIG).into_script().to_v0_p2wsh();
1997+
1998+
1999+
let mut unsigned_announcement = UnsignedChannelAnnouncement {
2000+
features: ChannelFeatures::supported(),
2001+
chain_hash: genesis_block(Network::Testnet).header.bitcoin_hash(),
2002+
short_channel_id: 0,
2003+
node_id_1,
2004+
node_id_2,
2005+
bitcoin_key_1: PublicKey::from_secret_key(&secp_ctx, node_1_btckey),
2006+
bitcoin_key_2: PublicKey::from_secret_key(&secp_ctx, node_2_btckey),
2007+
excess_data: Vec::new(),
2008+
};
2009+
2010+
let channel_key = NetworkMap::get_key(unsigned_announcement.short_channel_id,
2011+
unsigned_announcement.chain_hash);
2012+
2013+
let mut msghash = hash_to_message!(&Sha256dHash::hash(&unsigned_announcement.encode()[..])[..]);
2014+
let valid_announcement = ChannelAnnouncement {
2015+
node_signature_1: secp_ctx.sign(&msghash, node_1_privkey),
2016+
node_signature_2: secp_ctx.sign(&msghash, node_2_privkey),
2017+
bitcoin_signature_1: secp_ctx.sign(&msghash, node_1_btckey),
2018+
bitcoin_signature_2: secp_ctx.sign(&msghash, node_2_btckey),
2019+
contents: unsigned_announcement.clone(),
2020+
};
2021+
2022+
// Test if the UTXO lookups were not supported
2023+
*chain_monitor.utxo_ret.lock().unwrap() = Err(chaininterface::ChainError::NotSupported);
2024+
2025+
match router.handle_channel_announcement(&valid_announcement) {
2026+
Ok(res) => assert!(res),
2027+
_ => panic!()
2028+
};
2029+
{
2030+
let network = router.network_map.write().unwrap();
2031+
match network.channels.get(&channel_key) {
2032+
None => panic!(),
2033+
Some(_) => ()
2034+
}
2035+
}
2036+
2037+
// If we receive announcement for the same channel (with UTXO lookups disabled),
2038+
// drop new one on the floor, since we can't see any changes.
2039+
match router.handle_channel_announcement(&valid_announcement) {
2040+
Ok(_) => panic!(),
2041+
Err(e) => assert_eq!(e.err, "Already have knowledge of channel")
2042+
};
2043+
2044+
2045+
// Test if an associated transaction were not on-chain (or not confirmed).
2046+
*chain_monitor.utxo_ret.lock().unwrap() = Err(chaininterface::ChainError::UnknownTx);
2047+
unsigned_announcement.short_channel_id += 1;
2048+
2049+
msghash = hash_to_message!(&Sha256dHash::hash(&unsigned_announcement.encode()[..])[..]);
2050+
let valid_announcement = ChannelAnnouncement {
2051+
node_signature_1: secp_ctx.sign(&msghash, node_1_privkey),
2052+
node_signature_2: secp_ctx.sign(&msghash, node_2_privkey),
2053+
bitcoin_signature_1: secp_ctx.sign(&msghash, node_1_btckey),
2054+
bitcoin_signature_2: secp_ctx.sign(&msghash, node_2_btckey),
2055+
contents: unsigned_announcement.clone(),
2056+
};
2057+
2058+
match router.handle_channel_announcement(&valid_announcement) {
2059+
Ok(_) => panic!(),
2060+
Err(e) => assert_eq!(e.err, "Channel announced without corresponding UTXO entry")
2061+
};
2062+
2063+
2064+
// Now test if the transaction is found in the UTXO set and the script is correct.
2065+
unsigned_announcement.short_channel_id += 1;
2066+
*chain_monitor.utxo_ret.lock().unwrap() = Ok((good_script.clone(), 0));
2067+
let channel_key = NetworkMap::get_key(unsigned_announcement.short_channel_id,
2068+
unsigned_announcement.chain_hash);
2069+
2070+
msghash = hash_to_message!(&Sha256dHash::hash(&unsigned_announcement.encode()[..])[..]);
2071+
let valid_announcement = ChannelAnnouncement {
2072+
node_signature_1: secp_ctx.sign(&msghash, node_1_privkey),
2073+
node_signature_2: secp_ctx.sign(&msghash, node_2_privkey),
2074+
bitcoin_signature_1: secp_ctx.sign(&msghash, node_1_btckey),
2075+
bitcoin_signature_2: secp_ctx.sign(&msghash, node_2_btckey),
2076+
contents: unsigned_announcement.clone(),
2077+
};
2078+
match router.handle_channel_announcement(&valid_announcement) {
2079+
Ok(res) => assert!(res),
2080+
_ => panic!()
2081+
};
2082+
{
2083+
let network = router.network_map.write().unwrap();
2084+
match network.channels.get(&channel_key) {
2085+
None => panic!(),
2086+
Some(_) => ()
2087+
}
2088+
}
2089+
2090+
// If we receive announcement for the same channel (but TX is not confirmed),
2091+
// drop new one on the floor, since we can't see any changes.
2092+
*chain_monitor.utxo_ret.lock().unwrap() = Err(chaininterface::ChainError::UnknownTx);
2093+
match router.handle_channel_announcement(&valid_announcement) {
2094+
Ok(_) => panic!(),
2095+
Err(e) => assert_eq!(e.err, "Channel announced without corresponding UTXO entry")
2096+
};
2097+
2098+
// But if it is confirmed, replace the channel
2099+
*chain_monitor.utxo_ret.lock().unwrap() = Ok((good_script, 0));
2100+
unsigned_announcement.features = ChannelFeatures::empty();
2101+
msghash = hash_to_message!(&Sha256dHash::hash(&unsigned_announcement.encode()[..])[..]);
2102+
let valid_announcement = ChannelAnnouncement {
2103+
node_signature_1: secp_ctx.sign(&msghash, node_1_privkey),
2104+
node_signature_2: secp_ctx.sign(&msghash, node_2_privkey),
2105+
bitcoin_signature_1: secp_ctx.sign(&msghash, node_1_btckey),
2106+
bitcoin_signature_2: secp_ctx.sign(&msghash, node_2_btckey),
2107+
contents: unsigned_announcement.clone(),
2108+
};
2109+
match router.handle_channel_announcement(&valid_announcement) {
2110+
Ok(res) => assert!(res),
2111+
_ => panic!()
2112+
};
2113+
{
2114+
let mut network = router.network_map.write().unwrap();
2115+
match network.channels.entry(channel_key) {
2116+
BtreeEntry::Occupied(channel_entry) => {
2117+
assert_eq!(channel_entry.get().features, ChannelFeatures::empty());
2118+
},
2119+
_ => panic!()
2120+
}
2121+
}
2122+
2123+
// Don't relay valid channels with excess data
2124+
unsigned_announcement.short_channel_id += 1;
2125+
unsigned_announcement.excess_data.push(1);
2126+
msghash = hash_to_message!(&Sha256dHash::hash(&unsigned_announcement.encode()[..])[..]);
2127+
let valid_announcement = ChannelAnnouncement {
2128+
node_signature_1: secp_ctx.sign(&msghash, node_1_privkey),
2129+
node_signature_2: secp_ctx.sign(&msghash, node_2_privkey),
2130+
bitcoin_signature_1: secp_ctx.sign(&msghash, node_1_btckey),
2131+
bitcoin_signature_2: secp_ctx.sign(&msghash, node_2_btckey),
2132+
contents: unsigned_announcement.clone(),
2133+
};
2134+
match router.handle_channel_announcement(&valid_announcement) {
2135+
Ok(res) => assert!(!res),
2136+
_ => panic!()
2137+
};
2138+
2139+
unsigned_announcement.excess_data = Vec::new();
2140+
let invalid_sig_announcement = ChannelAnnouncement {
2141+
node_signature_1: secp_ctx.sign(&msghash, node_1_privkey),
2142+
node_signature_2: secp_ctx.sign(&msghash, node_2_privkey),
2143+
bitcoin_signature_1: secp_ctx.sign(&msghash, node_1_btckey),
2144+
bitcoin_signature_2: secp_ctx.sign(&msghash, node_1_btckey),
2145+
contents: unsigned_announcement.clone(),
2146+
};
2147+
match router.handle_channel_announcement(&invalid_sig_announcement) {
2148+
Ok(_) => panic!(),
2149+
Err(e) => assert_eq!(e.err, "Invalid signature from remote node")
2150+
};
2151+
2152+
unsigned_announcement.node_id_1 = PublicKey::from_secret_key(&secp_ctx, node_2_privkey);
2153+
msghash = hash_to_message!(&Sha256dHash::hash(&unsigned_announcement.encode()[..])[..]);
2154+
let channel_to_itself_announcement = ChannelAnnouncement {
2155+
node_signature_1: secp_ctx.sign(&msghash, node_1_privkey),
2156+
node_signature_2: secp_ctx.sign(&msghash, node_1_privkey),
2157+
bitcoin_signature_1: secp_ctx.sign(&msghash, node_1_btckey),
2158+
bitcoin_signature_2: secp_ctx.sign(&msghash, node_2_btckey),
2159+
contents: unsigned_announcement.clone(),
2160+
};
2161+
match router.handle_channel_announcement(&channel_to_itself_announcement) {
2162+
Ok(_) => panic!(),
2163+
Err(e) => assert_eq!(e.err, "Channel announcement node had a channel with itself")
2164+
};
2165+
}
19722166
}

lightning/src/util/test_utils.rs

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use chain::chaininterface;
2-
use chain::chaininterface::ConfirmationTarget;
2+
use chain::chaininterface::{ConfirmationTarget, ChainError, ChainWatchInterface};
33
use chain::transaction::OutPoint;
44
use chain::keysinterface;
55
use ln::channelmonitor;
@@ -13,7 +13,9 @@ use util::logger::{Logger, Level, Record};
1313
use util::ser::{Readable, ReadableArgs, Writer, Writeable};
1414

1515
use bitcoin::blockdata::transaction::Transaction;
16-
use bitcoin::blockdata::script::Script;
16+
use bitcoin::blockdata::script::{Builder, Script};
17+
use bitcoin::blockdata::block::Block;
18+
use bitcoin::blockdata::opcodes;
1719
use bitcoin_hashes::sha256d::Hash as Sha256dHash;
1820
use bitcoin::network::constants::Network;
1921

@@ -263,3 +265,28 @@ impl TestKeysInterface {
263265
}
264266
}
265267
}
268+
269+
pub struct TestChainWatcher {
270+
pub utxo_ret: Mutex<Result<(Script, u64), ChainError>>,
271+
}
272+
273+
impl TestChainWatcher {
274+
pub fn new() -> Self {
275+
let script = Builder::new().push_opcode(opcodes::OP_TRUE).into_script();
276+
Self { utxo_ret: Mutex::new(Ok((script, u64::max_value()))) }
277+
}
278+
}
279+
280+
impl ChainWatchInterface for TestChainWatcher {
281+
fn install_watch_tx(&self, _txid: &Sha256dHash, _script_pub_key: &Script) { }
282+
fn install_watch_outpoint(&self, _outpoint: (Sha256dHash, u32), _out_script: &Script) { }
283+
fn watch_all_txn(&self) { }
284+
fn filter_block<'a>(&self, _block: &'a Block) -> (Vec<&'a Transaction>, Vec<u32>) {
285+
(Vec::new(), Vec::new())
286+
}
287+
fn reentered(&self) -> usize { 0 }
288+
289+
fn get_chain_utxo(&self, _genesis_hash: Sha256dHash, _unspent_tx_output_identifier: u64) -> Result<(Script, u64), ChainError> {
290+
self.utxo_ret.lock().unwrap().clone()
291+
}
292+
}

0 commit comments

Comments
 (0)