X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning%2Fsrc%2Futil%2Ftest_utils.rs;h=9b2f222c519f9c672bf6d538ef23898de2e8020f;hb=7544030bb63fee6484fc178bb2ac8f382fe3b5b1;hp=e426ec2d340a6967955016352a6b78aa8e09569c;hpb=fab5d7e0aeea24660b59bb7a1bf94c4dc33dc78b;p=rust-lightning diff --git a/lightning/src/util/test_utils.rs b/lightning/src/util/test_utils.rs index e426ec2d..9b2f222c 100644 --- a/lightning/src/util/test_utils.rs +++ b/lightning/src/util/test_utils.rs @@ -1,115 +1,214 @@ +// This file is Copyright its original authors, visible in version control +// history. +// +// This file is licensed under the Apache License, Version 2.0 or the MIT license +// , at your option. +// You may not use this file except in accordance with one or both of these +// licenses. + +use chain; +use chain::WatchedOutput; use chain::chaininterface; -use chain::chaininterface::{ConfirmationTarget, ChainError, ChainWatchInterface}; +use chain::chaininterface::ConfirmationTarget; +use chain::chainmonitor; +use chain::chainmonitor::MonitorUpdateId; +use chain::channelmonitor; +use chain::channelmonitor::MonitorEvent; use chain::transaction::OutPoint; use chain::keysinterface; -use ln::channelmonitor; -use ln::features::{ChannelFeatures, InitFeatures}; -use ln::msgs; -use ln::channelmonitor::HTLCUpdate; -use util::enforcing_trait_impls::EnforcingChannelKeys; +use ln::channelmanager; +use ln::features::{ChannelFeatures, InitFeatures, NodeFeatures}; +use ln::{msgs, wire}; +use ln::script::ShutdownScript; +use routing::scoring::FixedPenaltyScorer; +use util::enforcing_trait_impls::{EnforcingSigner, EnforcementState}; use util::events; use util::logger::{Logger, Level, Record}; -use util::ser::{Readable, Writer, Writeable}; +use util::ser::{Readable, ReadableArgs, Writer, Writeable}; -use bitcoin::BitcoinHash; use bitcoin::blockdata::constants::genesis_block; -use bitcoin::blockdata::transaction::Transaction; +use bitcoin::blockdata::transaction::{Transaction, TxOut}; use bitcoin::blockdata::script::{Builder, Script}; -use bitcoin::blockdata::block::Block; use bitcoin::blockdata::opcodes; +use bitcoin::blockdata::block::Block; use bitcoin::network::constants::Network; -use bitcoin::hash_types::{Txid, BlockHash}; +use bitcoin::hash_types::{BlockHash, Txid}; + +use bitcoin::secp256k1::{SecretKey, PublicKey, Secp256k1, ecdsa::Signature, Scalar}; +use bitcoin::secp256k1::ecdh::SharedSecret; +use bitcoin::secp256k1::ecdsa::RecoverableSignature; -use bitcoin::secp256k1::{SecretKey, PublicKey, Secp256k1, Signature}; +use regex; +use io; +use prelude::*; +use core::time::Duration; +use sync::{Mutex, Arc}; +use core::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; +use core::mem; +use bitcoin::bech32::u5; +use chain::keysinterface::{InMemorySigner, Recipient, KeyMaterial}; + +#[cfg(feature = "std")] use std::time::{SystemTime, UNIX_EPOCH}; -use std::sync::Mutex; -use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; -use std::{cmp, mem}; -use std::collections::HashMap; +use bitcoin::Sequence; pub struct TestVecWriter(pub Vec); impl Writer for TestVecWriter { - fn write_all(&mut self, buf: &[u8]) -> Result<(), ::std::io::Error> { + fn write_all(&mut self, buf: &[u8]) -> Result<(), io::Error> { self.0.extend_from_slice(buf); Ok(()) } - fn size_hint(&mut self, size: usize) { - self.0.reserve_exact(size); - } } pub struct TestFeeEstimator { - pub sat_per_kw: u64, + pub sat_per_kw: Mutex, } impl chaininterface::FeeEstimator for TestFeeEstimator { - fn get_est_sat_per_1000_weight(&self, _confirmation_target: ConfirmationTarget) -> u64 { - self.sat_per_kw + fn get_est_sat_per_1000_weight(&self, _confirmation_target: ConfirmationTarget) -> u32 { + *self.sat_per_kw.lock().unwrap() } } -pub struct TestChannelMonitor<'a> { - pub added_monitors: Mutex)>>, - pub latest_monitor_update_id: Mutex>, - pub simple_monitor: channelmonitor::SimpleManyChannelMonitor, - pub update_ret: Mutex>, - // If this is set to Some(), after the next return, we'll always return this until update_ret - // is changed: - pub next_update_ret: Mutex>>, +pub struct OnlyReadsKeysInterface {} +impl keysinterface::KeysInterface for OnlyReadsKeysInterface { + type Signer = EnforcingSigner; + + fn get_node_secret(&self, _recipient: Recipient) -> Result { unreachable!(); } + fn ecdh(&self, _recipient: Recipient, _other_key: &PublicKey, _tweak: Option<&Scalar>) -> Result { unreachable!(); } + fn get_inbound_payment_key_material(&self) -> KeyMaterial { unreachable!(); } + fn get_destination_script(&self) -> Script { unreachable!(); } + fn get_shutdown_scriptpubkey(&self) -> ShutdownScript { unreachable!(); } + fn get_channel_signer(&self, _inbound: bool, _channel_value_satoshis: u64) -> EnforcingSigner { unreachable!(); } + fn get_secure_random_bytes(&self) -> [u8; 32] { [0; 32] } + + fn read_chan_signer(&self, mut reader: &[u8]) -> Result { + let dummy_sk = SecretKey::from_slice(&[42; 32]).unwrap(); + let inner: InMemorySigner = ReadableArgs::read(&mut reader, dummy_sk)?; + let state = Arc::new(Mutex::new(EnforcementState::new())); + + Ok(EnforcingSigner::new_with_revoked( + inner, + state, + false + )) + } + fn sign_invoice(&self, _hrp_bytes: &[u8], _invoice_data: &[u5], _recipient: Recipient) -> Result { unreachable!(); } +} + +pub struct TestChainMonitor<'a> { + pub added_monitors: Mutex)>>, + pub monitor_updates: Mutex>>, + pub latest_monitor_update_id: Mutex>, + pub chain_monitor: chainmonitor::ChainMonitor>, + pub keys_manager: &'a TestKeysInterface, + /// If this is set to Some(), the next update_channel call (not watch_channel) must be a + /// ChannelForceClosed event for the given channel_id with should_broadcast set to the given + /// boolean. + pub expect_channel_force_closed: Mutex>, } -impl<'a> TestChannelMonitor<'a> { - pub fn new(chain_monitor: &'a chaininterface::ChainWatchInterface, broadcaster: &'a chaininterface::BroadcasterInterface, logger: &'a TestLogger, fee_estimator: &'a TestFeeEstimator) -> Self { +impl<'a> TestChainMonitor<'a> { + pub fn new(chain_source: Option<&'a TestChainSource>, broadcaster: &'a chaininterface::BroadcasterInterface, logger: &'a TestLogger, fee_estimator: &'a TestFeeEstimator, persister: &'a chainmonitor::Persist, keys_manager: &'a TestKeysInterface) -> Self { Self { added_monitors: Mutex::new(Vec::new()), + monitor_updates: Mutex::new(HashMap::new()), latest_monitor_update_id: Mutex::new(HashMap::new()), - simple_monitor: channelmonitor::SimpleManyChannelMonitor::new(chain_monitor, broadcaster, logger, fee_estimator), - update_ret: Mutex::new(Ok(())), - next_update_ret: Mutex::new(None), + chain_monitor: chainmonitor::ChainMonitor::new(chain_source, broadcaster, logger, fee_estimator, persister), + keys_manager, + expect_channel_force_closed: Mutex::new(None), } } -} -impl<'a> channelmonitor::ManyChannelMonitor for TestChannelMonitor<'a> { - type Keys = EnforcingChannelKeys; - fn add_monitor(&self, funding_txo: OutPoint, monitor: channelmonitor::ChannelMonitor) -> Result<(), channelmonitor::ChannelMonitorUpdateErr> { + pub fn complete_sole_pending_chan_update(&self, channel_id: &[u8; 32]) { + let (outpoint, _, latest_update) = self.latest_monitor_update_id.lock().unwrap().get(channel_id).unwrap().clone(); + self.chain_monitor.channel_monitor_updated(outpoint, latest_update).unwrap(); + } +} +impl<'a> chain::Watch for TestChainMonitor<'a> { + fn watch_channel(&self, funding_txo: OutPoint, monitor: channelmonitor::ChannelMonitor) -> chain::ChannelMonitorUpdateStatus { // At every point where we get a monitor update, we should be able to send a useful monitor // to a watchtower and disk... let mut w = TestVecWriter(Vec::new()); - monitor.write_for_disk(&mut w).unwrap(); - let new_monitor = <(BlockHash, channelmonitor::ChannelMonitor)>::read( - &mut ::std::io::Cursor::new(&w.0)).unwrap().1; + monitor.write(&mut w).unwrap(); + let new_monitor = <(BlockHash, channelmonitor::ChannelMonitor)>::read( + &mut io::Cursor::new(&w.0), self.keys_manager).unwrap().1; assert!(new_monitor == monitor); - self.latest_monitor_update_id.lock().unwrap().insert(funding_txo.to_channel_id(), (funding_txo, monitor.get_latest_update_id())); + self.latest_monitor_update_id.lock().unwrap().insert(funding_txo.to_channel_id(), + (funding_txo, monitor.get_latest_update_id(), MonitorUpdateId::from_new_monitor(&monitor))); self.added_monitors.lock().unwrap().push((funding_txo, monitor)); - assert!(self.simple_monitor.add_monitor(funding_txo, new_monitor).is_ok()); - - let ret = self.update_ret.lock().unwrap().clone(); - if let Some(next_ret) = self.next_update_ret.lock().unwrap().take() { - *self.update_ret.lock().unwrap() = next_ret; - } - ret + self.chain_monitor.watch_channel(funding_txo, new_monitor) } - fn update_monitor(&self, funding_txo: OutPoint, update: channelmonitor::ChannelMonitorUpdate) -> Result<(), channelmonitor::ChannelMonitorUpdateErr> { + fn update_channel(&self, funding_txo: OutPoint, update: channelmonitor::ChannelMonitorUpdate) -> chain::ChannelMonitorUpdateStatus { // Every monitor update should survive roundtrip let mut w = TestVecWriter(Vec::new()); update.write(&mut w).unwrap(); assert!(channelmonitor::ChannelMonitorUpdate::read( - &mut ::std::io::Cursor::new(&w.0)).unwrap() == update); + &mut io::Cursor::new(&w.0)).unwrap() == update); + + self.monitor_updates.lock().unwrap().entry(funding_txo.to_channel_id()).or_insert(Vec::new()).push(update.clone()); + + if let Some(exp) = self.expect_channel_force_closed.lock().unwrap().take() { + assert_eq!(funding_txo.to_channel_id(), exp.0); + assert_eq!(update.updates.len(), 1); + if let channelmonitor::ChannelMonitorUpdateStep::ChannelForceClosed { should_broadcast } = update.updates[0] { + assert_eq!(should_broadcast, exp.1); + } else { panic!(); } + } - self.latest_monitor_update_id.lock().unwrap().insert(funding_txo.to_channel_id(), (funding_txo, update.update_id)); - assert!(self.simple_monitor.update_monitor(funding_txo, update).is_ok()); + self.latest_monitor_update_id.lock().unwrap().insert(funding_txo.to_channel_id(), + (funding_txo, update.update_id, MonitorUpdateId::from_monitor_update(&update))); + let update_res = self.chain_monitor.update_channel(funding_txo, update); // At every point where we get a monitor update, we should be able to send a useful monitor // to a watchtower and disk... - let monitors = self.simple_monitor.monitors.lock().unwrap(); - let monitor = monitors.get(&funding_txo).unwrap(); + let monitor = self.chain_monitor.get_monitor(funding_txo).unwrap(); w.0.clear(); - monitor.write_for_disk(&mut w).unwrap(); - let new_monitor = <(BlockHash, channelmonitor::ChannelMonitor)>::read( - &mut ::std::io::Cursor::new(&w.0)).unwrap().1; + monitor.write(&mut w).unwrap(); + let new_monitor = <(BlockHash, channelmonitor::ChannelMonitor)>::read( + &mut io::Cursor::new(&w.0), self.keys_manager).unwrap().1; assert!(new_monitor == *monitor); self.added_monitors.lock().unwrap().push((funding_txo, new_monitor)); + update_res + } + + fn release_pending_monitor_events(&self) -> Vec<(OutPoint, Vec, Option)> { + return self.chain_monitor.release_pending_monitor_events(); + } +} + +pub struct TestPersister { + pub update_ret: Mutex, + /// If this is set to Some(), after the next return, we'll always return this until update_ret + /// is changed: + pub next_update_ret: Mutex>, + /// When we get an update_persisted_channel call with no ChannelMonitorUpdate, we insert the + /// MonitorUpdateId here. + pub chain_sync_monitor_persistences: Mutex>>, + /// When we get an update_persisted_channel call *with* a ChannelMonitorUpdate, we insert the + /// MonitorUpdateId here. + pub offchain_monitor_updates: Mutex>>, +} +impl TestPersister { + pub fn new() -> Self { + Self { + update_ret: Mutex::new(chain::ChannelMonitorUpdateStatus::Completed), + next_update_ret: Mutex::new(None), + chain_sync_monitor_persistences: Mutex::new(HashMap::new()), + offchain_monitor_updates: Mutex::new(HashMap::new()), + } + } + pub fn set_update_ret(&self, ret: chain::ChannelMonitorUpdateStatus) { + *self.update_ret.lock().unwrap() = ret; + } + + pub fn set_next_update_ret(&self, next_ret: Option) { + *self.next_update_ret.lock().unwrap() = next_ret; + } +} +impl chainmonitor::Persist for TestPersister { + fn persist_new_channel(&self, _funding_txo: OutPoint, _data: &channelmonitor::ChannelMonitor, _id: MonitorUpdateId) -> chain::ChannelMonitorUpdateStatus { let ret = self.update_ret.lock().unwrap().clone(); if let Some(next_ret) = self.next_update_ret.lock().unwrap().take() { *self.update_ret.lock().unwrap() = next_ret; @@ -117,52 +216,155 @@ impl<'a> channelmonitor::ManyChannelMonitor for TestChannelMonitor<'a> { ret } - fn get_and_clear_pending_htlcs_updated(&self) -> Vec { - return self.simple_monitor.get_and_clear_pending_htlcs_updated(); + fn update_persisted_channel(&self, funding_txo: OutPoint, update: &Option, _data: &channelmonitor::ChannelMonitor, update_id: MonitorUpdateId) -> chain::ChannelMonitorUpdateStatus { + let ret = self.update_ret.lock().unwrap().clone(); + if let Some(next_ret) = self.next_update_ret.lock().unwrap().take() { + *self.update_ret.lock().unwrap() = next_ret; + } + if update.is_none() { + self.chain_sync_monitor_persistences.lock().unwrap().entry(funding_txo).or_insert(HashSet::new()).insert(update_id); + } else { + self.offchain_monitor_updates.lock().unwrap().entry(funding_txo).or_insert(HashSet::new()).insert(update_id); + } + ret } } pub struct TestBroadcaster { pub txn_broadcasted: Mutex>, + pub blocks: Arc>>, +} + +impl TestBroadcaster { + pub fn new(blocks: Arc>>) -> TestBroadcaster { + TestBroadcaster { txn_broadcasted: Mutex::new(Vec::new()), blocks } + } } + impl chaininterface::BroadcasterInterface for TestBroadcaster { fn broadcast_transaction(&self, tx: &Transaction) { + let lock_time = tx.lock_time.0; + assert!(lock_time < 1_500_000_000); + if lock_time > self.blocks.lock().unwrap().len() as u32 + 1 && lock_time < 500_000_000 { + for inp in tx.input.iter() { + if inp.sequence != Sequence::MAX { + panic!("We should never broadcast a transaction before its locktime ({})!", tx.lock_time); + } + } + } self.txn_broadcasted.lock().unwrap().push(tx.clone()); } } pub struct TestChannelMessageHandler { pub pending_events: Mutex>, + expected_recv_msgs: Mutex>>>, } impl TestChannelMessageHandler { pub fn new() -> Self { TestChannelMessageHandler { pending_events: Mutex::new(Vec::new()), + expected_recv_msgs: Mutex::new(None), + } + } + + #[cfg(test)] + pub(crate) fn expect_receive_msg(&self, ev: wire::Message<()>) { + let mut expected_msgs = self.expected_recv_msgs.lock().unwrap(); + if expected_msgs.is_none() { *expected_msgs = Some(Vec::new()); } + expected_msgs.as_mut().unwrap().push(ev); + } + + fn received_msg(&self, _ev: wire::Message<()>) { + let mut msgs = self.expected_recv_msgs.lock().unwrap(); + if msgs.is_none() { return; } + assert!(!msgs.as_ref().unwrap().is_empty(), "Received message when we weren't expecting one"); + #[cfg(test)] + assert_eq!(msgs.as_ref().unwrap()[0], _ev); + msgs.as_mut().unwrap().remove(0); + } +} + +impl Drop for TestChannelMessageHandler { + fn drop(&mut self) { + #[cfg(feature = "std")] + { + let l = self.expected_recv_msgs.lock().unwrap(); + if !std::thread::panicking() { + assert!(l.is_none() || l.as_ref().unwrap().is_empty()); + } } } } impl msgs::ChannelMessageHandler for TestChannelMessageHandler { - fn handle_open_channel(&self, _their_node_id: &PublicKey, _their_features: InitFeatures, _msg: &msgs::OpenChannel) {} - fn handle_accept_channel(&self, _their_node_id: &PublicKey, _their_features: InitFeatures, _msg: &msgs::AcceptChannel) {} - fn handle_funding_created(&self, _their_node_id: &PublicKey, _msg: &msgs::FundingCreated) {} - fn handle_funding_signed(&self, _their_node_id: &PublicKey, _msg: &msgs::FundingSigned) {} - fn handle_funding_locked(&self, _their_node_id: &PublicKey, _msg: &msgs::FundingLocked) {} - fn handle_shutdown(&self, _their_node_id: &PublicKey, _msg: &msgs::Shutdown) {} - fn handle_closing_signed(&self, _their_node_id: &PublicKey, _msg: &msgs::ClosingSigned) {} - fn handle_update_add_htlc(&self, _their_node_id: &PublicKey, _msg: &msgs::UpdateAddHTLC) {} - fn handle_update_fulfill_htlc(&self, _their_node_id: &PublicKey, _msg: &msgs::UpdateFulfillHTLC) {} - fn handle_update_fail_htlc(&self, _their_node_id: &PublicKey, _msg: &msgs::UpdateFailHTLC) {} - fn handle_update_fail_malformed_htlc(&self, _their_node_id: &PublicKey, _msg: &msgs::UpdateFailMalformedHTLC) {} - fn handle_commitment_signed(&self, _their_node_id: &PublicKey, _msg: &msgs::CommitmentSigned) {} - fn handle_revoke_and_ack(&self, _their_node_id: &PublicKey, _msg: &msgs::RevokeAndACK) {} - fn handle_update_fee(&self, _their_node_id: &PublicKey, _msg: &msgs::UpdateFee) {} - fn handle_announcement_signatures(&self, _their_node_id: &PublicKey, _msg: &msgs::AnnouncementSignatures) {} - fn handle_channel_reestablish(&self, _their_node_id: &PublicKey, _msg: &msgs::ChannelReestablish) {} + fn handle_open_channel(&self, _their_node_id: &PublicKey, _their_features: InitFeatures, msg: &msgs::OpenChannel) { + self.received_msg(wire::Message::OpenChannel(msg.clone())); + } + fn handle_accept_channel(&self, _their_node_id: &PublicKey, _their_features: InitFeatures, msg: &msgs::AcceptChannel) { + self.received_msg(wire::Message::AcceptChannel(msg.clone())); + } + fn handle_funding_created(&self, _their_node_id: &PublicKey, msg: &msgs::FundingCreated) { + self.received_msg(wire::Message::FundingCreated(msg.clone())); + } + fn handle_funding_signed(&self, _their_node_id: &PublicKey, msg: &msgs::FundingSigned) { + self.received_msg(wire::Message::FundingSigned(msg.clone())); + } + fn handle_channel_ready(&self, _their_node_id: &PublicKey, msg: &msgs::ChannelReady) { + self.received_msg(wire::Message::ChannelReady(msg.clone())); + } + fn handle_shutdown(&self, _their_node_id: &PublicKey, _their_features: &InitFeatures, msg: &msgs::Shutdown) { + self.received_msg(wire::Message::Shutdown(msg.clone())); + } + fn handle_closing_signed(&self, _their_node_id: &PublicKey, msg: &msgs::ClosingSigned) { + self.received_msg(wire::Message::ClosingSigned(msg.clone())); + } + fn handle_update_add_htlc(&self, _their_node_id: &PublicKey, msg: &msgs::UpdateAddHTLC) { + self.received_msg(wire::Message::UpdateAddHTLC(msg.clone())); + } + fn handle_update_fulfill_htlc(&self, _their_node_id: &PublicKey, msg: &msgs::UpdateFulfillHTLC) { + self.received_msg(wire::Message::UpdateFulfillHTLC(msg.clone())); + } + fn handle_update_fail_htlc(&self, _their_node_id: &PublicKey, msg: &msgs::UpdateFailHTLC) { + self.received_msg(wire::Message::UpdateFailHTLC(msg.clone())); + } + fn handle_update_fail_malformed_htlc(&self, _their_node_id: &PublicKey, msg: &msgs::UpdateFailMalformedHTLC) { + self.received_msg(wire::Message::UpdateFailMalformedHTLC(msg.clone())); + } + fn handle_commitment_signed(&self, _their_node_id: &PublicKey, msg: &msgs::CommitmentSigned) { + self.received_msg(wire::Message::CommitmentSigned(msg.clone())); + } + fn handle_revoke_and_ack(&self, _their_node_id: &PublicKey, msg: &msgs::RevokeAndACK) { + self.received_msg(wire::Message::RevokeAndACK(msg.clone())); + } + fn handle_update_fee(&self, _their_node_id: &PublicKey, msg: &msgs::UpdateFee) { + self.received_msg(wire::Message::UpdateFee(msg.clone())); + } + fn handle_channel_update(&self, _their_node_id: &PublicKey, _msg: &msgs::ChannelUpdate) { + // Don't call `received_msg` here as `TestRoutingMessageHandler` generates these sometimes + } + fn handle_announcement_signatures(&self, _their_node_id: &PublicKey, msg: &msgs::AnnouncementSignatures) { + self.received_msg(wire::Message::AnnouncementSignatures(msg.clone())); + } + fn handle_channel_reestablish(&self, _their_node_id: &PublicKey, msg: &msgs::ChannelReestablish) { + self.received_msg(wire::Message::ChannelReestablish(msg.clone())); + } fn peer_disconnected(&self, _their_node_id: &PublicKey, _no_connection_possible: bool) {} - fn peer_connected(&self, _their_node_id: &PublicKey, _msg: &msgs::Init) {} - fn handle_error(&self, _their_node_id: &PublicKey, _msg: &msgs::ErrorMessage) {} + fn peer_connected(&self, _their_node_id: &PublicKey, _msg: &msgs::Init) -> Result<(), ()> { + // Don't bother with `received_msg` for Init as its auto-generated and we don't want to + // bother re-generating the expected Init message in all tests. + Ok(()) + } + fn handle_error(&self, _their_node_id: &PublicKey, msg: &msgs::ErrorMessage) { + self.received_msg(wire::Message::Error(msg.clone())); + } + fn provided_node_features(&self) -> NodeFeatures { + channelmanager::provided_node_features() + } + fn provided_init_features(&self, _their_init_features: &PublicKey) -> InitFeatures { + channelmanager::provided_init_features() + } } impl events::MessageSendEventsProvider for TestChannelMessageHandler { @@ -183,8 +385,8 @@ fn get_dummy_channel_announcement(short_chan_id: u64) -> msgs::ChannelAnnounceme let node_1_btckey = SecretKey::from_slice(&[40; 32]).unwrap(); let node_2_btckey = SecretKey::from_slice(&[39; 32]).unwrap(); let unsigned_ann = msgs::UnsignedChannelAnnouncement { - features: ChannelFeatures::known(), - chain_hash: genesis_block(network).header.bitcoin_hash(), + features: ChannelFeatures::empty(), + chain_hash: genesis_block(network).header.block_hash(), short_channel_id: short_chan_id, node_id_1: PublicKey::from_secret_key(&secp_ctx, &node_1_privkey), node_id_2: PublicKey::from_secret_key(&secp_ctx, &node_2_privkey), @@ -193,12 +395,14 @@ fn get_dummy_channel_announcement(short_chan_id: u64) -> msgs::ChannelAnnounceme excess_data: Vec::new(), }; - msgs::ChannelAnnouncement { - node_signature_1: Signature::from(FFISignature::new()), - node_signature_2: Signature::from(FFISignature::new()), - bitcoin_signature_1: Signature::from(FFISignature::new()), - bitcoin_signature_2: Signature::from(FFISignature::new()), - contents: unsigned_ann, + unsafe { + msgs::ChannelAnnouncement { + node_signature_1: Signature::from(FFISignature::new()), + node_signature_2: Signature::from(FFISignature::new()), + bitcoin_signature_1: Signature::from(FFISignature::new()), + bitcoin_signature_2: Signature::from(FFISignature::new()), + contents: unsigned_ann, + } } } @@ -206,14 +410,15 @@ fn get_dummy_channel_update(short_chan_id: u64) -> msgs::ChannelUpdate { use bitcoin::secp256k1::ffi::Signature as FFISignature; let network = Network::Testnet; msgs::ChannelUpdate { - signature: Signature::from(FFISignature::new()), + signature: Signature::from(unsafe { FFISignature::new() }), contents: msgs::UnsignedChannelUpdate { - chain_hash: genesis_block(network).header.bitcoin_hash(), + chain_hash: genesis_block(network).header.block_hash(), short_channel_id: short_chan_id, timestamp: 0, flags: 0, cltv_expiry_delta: 0, htlc_minimum_msat: 0, + htlc_maximum_msat: msgs::MAX_VALUE_MSAT, fee_base_msat: 0, fee_proportional_millionths: 0, excess_data: vec![], @@ -224,7 +429,7 @@ fn get_dummy_channel_update(short_chan_id: u64) -> msgs::ChannelUpdate { pub struct TestRoutingMessageHandler { pub chan_upds_recvd: AtomicUsize, pub chan_anns_recvd: AtomicUsize, - pub chan_anns_sent: AtomicUsize, + pub pending_events: Mutex>, pub request_full_sync: AtomicBool, } @@ -233,52 +438,105 @@ impl TestRoutingMessageHandler { TestRoutingMessageHandler { chan_upds_recvd: AtomicUsize::new(0), chan_anns_recvd: AtomicUsize::new(0), - chan_anns_sent: AtomicUsize::new(0), + pending_events: Mutex::new(vec![]), request_full_sync: AtomicBool::new(false), } } } impl msgs::RoutingMessageHandler for TestRoutingMessageHandler { fn handle_node_announcement(&self, _msg: &msgs::NodeAnnouncement) -> Result { - Err(msgs::LightningError { err: "", action: msgs::ErrorAction::IgnoreError }) + Err(msgs::LightningError { err: "".to_owned(), action: msgs::ErrorAction::IgnoreError }) } fn handle_channel_announcement(&self, _msg: &msgs::ChannelAnnouncement) -> Result { self.chan_anns_recvd.fetch_add(1, Ordering::AcqRel); - Err(msgs::LightningError { err: "", action: msgs::ErrorAction::IgnoreError }) + Err(msgs::LightningError { err: "".to_owned(), action: msgs::ErrorAction::IgnoreError }) } fn handle_channel_update(&self, _msg: &msgs::ChannelUpdate) -> Result { self.chan_upds_recvd.fetch_add(1, Ordering::AcqRel); - 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, Option, Option)> { - let mut chan_anns = Vec::new(); - const TOTAL_UPDS: u64 = 100; - let end: u64 = cmp::min(starting_point + batch_amount as u64, TOTAL_UPDS - self.chan_anns_sent.load(Ordering::Acquire) as u64); - for i in starting_point..end { - let chan_upd_1 = get_dummy_channel_update(i); - let chan_upd_2 = get_dummy_channel_update(i); - let chan_ann = get_dummy_channel_announcement(i); - - chan_anns.push((chan_ann, Some(chan_upd_1), Some(chan_upd_2))); + Err(msgs::LightningError { err: "".to_owned(), action: msgs::ErrorAction::IgnoreError }) + } + fn get_next_channel_announcement(&self, starting_point: u64) -> Option<(msgs::ChannelAnnouncement, Option, Option)> { + let chan_upd_1 = get_dummy_channel_update(starting_point); + let chan_upd_2 = get_dummy_channel_update(starting_point); + let chan_ann = get_dummy_channel_announcement(starting_point); + + Some((chan_ann, Some(chan_upd_1), Some(chan_upd_2))) + } + + fn get_next_node_announcement(&self, _starting_point: Option<&PublicKey>) -> Option { + None + } + + fn peer_connected(&self, their_node_id: &PublicKey, init_msg: &msgs::Init) -> Result<(), ()> { + if !init_msg.features.supports_gossip_queries() { + return Ok(()); } - self.chan_anns_sent.fetch_add(chan_anns.len(), Ordering::AcqRel); - chan_anns + #[allow(unused_mut, unused_assignments)] + let mut gossip_start_time = 0; + #[cfg(feature = "std")] + { + gossip_start_time = SystemTime::now().duration_since(UNIX_EPOCH).expect("Time must be > 1970").as_secs(); + if self.request_full_sync.load(Ordering::Acquire) { + gossip_start_time -= 60 * 60 * 24 * 7 * 2; // 2 weeks ago + } else { + gossip_start_time -= 60 * 60; // an hour ago + } + } + + let mut pending_events = self.pending_events.lock().unwrap(); + pending_events.push(events::MessageSendEvent::SendGossipTimestampFilter { + node_id: their_node_id.clone(), + msg: msgs::GossipTimestampFilter { + chain_hash: genesis_block(Network::Testnet).header.block_hash(), + first_timestamp: gossip_start_time as u32, + timestamp_range: u32::max_value(), + }, + }); + Ok(()) + } + + fn handle_reply_channel_range(&self, _their_node_id: &PublicKey, _msg: msgs::ReplyChannelRange) -> Result<(), msgs::LightningError> { + Ok(()) + } + + fn handle_reply_short_channel_ids_end(&self, _their_node_id: &PublicKey, _msg: msgs::ReplyShortChannelIdsEnd) -> Result<(), msgs::LightningError> { + Ok(()) + } + + fn handle_query_channel_range(&self, _their_node_id: &PublicKey, _msg: msgs::QueryChannelRange) -> Result<(), msgs::LightningError> { + Ok(()) + } + + fn handle_query_short_channel_ids(&self, _their_node_id: &PublicKey, _msg: msgs::QueryShortChannelIds) -> Result<(), msgs::LightningError> { + Ok(()) } - fn get_next_node_announcements(&self, _starting_point: Option<&PublicKey>, _batch_amount: u8) -> Vec { - Vec::new() + fn provided_node_features(&self) -> NodeFeatures { + let mut features = NodeFeatures::empty(); + features.set_gossip_queries_optional(); + features } - fn should_request_full_sync(&self, _node_id: &PublicKey) -> bool { - self.request_full_sync.load(Ordering::Acquire) + fn provided_init_features(&self, _their_init_features: &PublicKey) -> InitFeatures { + let mut features = InitFeatures::empty(); + features.set_gossip_queries_optional(); + features + } +} + +impl events::MessageSendEventsProvider for TestRoutingMessageHandler { + fn get_and_clear_pending_msg_events(&self) -> Vec { + let mut ret = Vec::new(); + let mut pending_events = self.pending_events.lock().unwrap(); + core::mem::swap(&mut ret, &mut pending_events); + ret } } pub struct TestLogger { level: Level, - id: String, + pub(crate) id: String, pub lines: Mutex>, } @@ -300,80 +558,225 @@ impl TestLogger { let log_entries = self.lines.lock().unwrap(); assert_eq!(log_entries.get(&(module, line)), Some(&count)); } + + /// Search for the number of occurrence of the logged lines which + /// 1. belongs to the specified module and + /// 2. contains `line` in it. + /// And asserts if the number of occurrences is the same with the given `count` + pub fn assert_log_contains(&self, module: String, line: String, count: usize) { + let log_entries = self.lines.lock().unwrap(); + let l: usize = log_entries.iter().filter(|&(&(ref m, ref l), _c)| { + m == &module && l.contains(line.as_str()) + }).map(|(_, c) | { c }).sum(); + assert_eq!(l, count) + } + + /// Search for the number of occurrences of logged lines which + /// 1. belong to the specified module and + /// 2. match the given regex pattern. + /// Assert that the number of occurrences equals the given `count` + pub fn assert_log_regex(&self, module: String, pattern: regex::Regex, count: usize) { + let log_entries = self.lines.lock().unwrap(); + let l: usize = log_entries.iter().filter(|&(&(ref m, ref l), _c)| { + m == &module && pattern.is_match(&l) + }).map(|(_, c) | { c }).sum(); + assert_eq!(l, count) + } } impl Logger for TestLogger { fn log(&self, record: &Record) { *self.lines.lock().unwrap().entry((record.module_path.to_string(), format!("{}", record.args))).or_insert(0) += 1; - if self.level >= record.level { + if record.level >= self.level { + #[cfg(feature = "std")] println!("{:<5} {} [{} : {}, {}] {}", record.level.to_string(), self.id, record.module_path, record.file, record.line, record.args); } } } pub struct TestKeysInterface { - backing: keysinterface::KeysManager, - pub override_session_priv: Mutex>, - pub override_channel_id_priv: Mutex>, + pub backing: keysinterface::PhantomKeysManager, + pub override_random_bytes: Mutex>, + pub disable_revocation_policy_check: bool, + enforcement_states: Mutex>>>, + expectations: Mutex>>, } impl keysinterface::KeysInterface for TestKeysInterface { - type ChanKeySigner = EnforcingChannelKeys; + type Signer = EnforcingSigner; - fn get_node_secret(&self) -> SecretKey { self.backing.get_node_secret() } - fn get_destination_script(&self) -> Script { self.backing.get_destination_script() } - fn get_shutdown_pubkey(&self) -> PublicKey { self.backing.get_shutdown_pubkey() } - fn get_channel_keys(&self, inbound: bool, channel_value_satoshis: u64) -> EnforcingChannelKeys { - EnforcingChannelKeys::new(self.backing.get_channel_keys(inbound, channel_value_satoshis)) + fn get_node_secret(&self, recipient: Recipient) -> Result { + self.backing.get_node_secret(recipient) } + fn ecdh(&self, recipient: Recipient, other_key: &PublicKey, tweak: Option<&Scalar>) -> Result { + self.backing.ecdh(recipient, other_key, tweak) + } + fn get_inbound_payment_key_material(&self) -> keysinterface::KeyMaterial { + self.backing.get_inbound_payment_key_material() + } + fn get_destination_script(&self) -> Script { self.backing.get_destination_script() } - fn get_onion_rand(&self) -> (SecretKey, [u8; 32]) { - match *self.override_session_priv.lock().unwrap() { - Some(key) => (key.clone(), [0; 32]), - None => self.backing.get_onion_rand() + fn get_shutdown_scriptpubkey(&self) -> ShutdownScript { + match &mut *self.expectations.lock().unwrap() { + None => self.backing.get_shutdown_scriptpubkey(), + Some(expectations) => match expectations.pop_front() { + None => panic!("Unexpected get_shutdown_scriptpubkey"), + Some(expectation) => expectation.returns, + }, } } - fn get_channel_id(&self) -> [u8; 32] { - match *self.override_channel_id_priv.lock().unwrap() { - Some(key) => key.clone(), - None => self.backing.get_channel_id() + fn get_channel_signer(&self, inbound: bool, channel_value_satoshis: u64) -> EnforcingSigner { + let keys = self.backing.get_channel_signer(inbound, channel_value_satoshis); + let state = self.make_enforcement_state_cell(keys.commitment_seed); + EnforcingSigner::new_with_revoked(keys, state, self.disable_revocation_policy_check) + } + + fn get_secure_random_bytes(&self) -> [u8; 32] { + let override_random_bytes = self.override_random_bytes.lock().unwrap(); + if let Some(bytes) = &*override_random_bytes { + return *bytes; } + self.backing.get_secure_random_bytes() + } + + fn read_chan_signer(&self, buffer: &[u8]) -> Result { + let mut reader = io::Cursor::new(buffer); + + let inner: InMemorySigner = ReadableArgs::read(&mut reader, self.get_node_secret(Recipient::Node).unwrap())?; + let state = self.make_enforcement_state_cell(inner.commitment_seed); + + Ok(EnforcingSigner::new_with_revoked( + inner, + state, + self.disable_revocation_policy_check + )) + } + + fn sign_invoice(&self, hrp_bytes: &[u8], invoice_data: &[u5], recipient: Recipient) -> Result { + self.backing.sign_invoice(hrp_bytes, invoice_data, recipient) } } impl TestKeysInterface { pub fn new(seed: &[u8; 32], network: Network) -> Self { - let now = SystemTime::now().duration_since(UNIX_EPOCH).expect("Time went backwards"); + let now = Duration::from_secs(genesis_block(network).header.time as u64); Self { - backing: keysinterface::KeysManager::new(seed, network, now.as_secs(), now.subsec_nanos()), - override_session_priv: Mutex::new(None), - override_channel_id_priv: Mutex::new(None), + backing: keysinterface::PhantomKeysManager::new(seed, now.as_secs(), now.subsec_nanos(), seed), + override_random_bytes: Mutex::new(None), + disable_revocation_policy_check: false, + enforcement_states: Mutex::new(HashMap::new()), + expectations: Mutex::new(None), + } + } + + /// Sets an expectation that [`keysinterface::KeysInterface::get_shutdown_scriptpubkey`] is + /// called. + pub fn expect(&self, expectation: OnGetShutdownScriptpubkey) -> &Self { + self.expectations.lock().unwrap() + .get_or_insert_with(|| VecDeque::new()) + .push_back(expectation); + self + } + + pub fn derive_channel_keys(&self, channel_value_satoshis: u64, id: &[u8; 32]) -> EnforcingSigner { + let keys = self.backing.derive_channel_keys(channel_value_satoshis, id); + let state = self.make_enforcement_state_cell(keys.commitment_seed); + EnforcingSigner::new_with_revoked(keys, state, self.disable_revocation_policy_check) + } + + fn make_enforcement_state_cell(&self, commitment_seed: [u8; 32]) -> Arc> { + let mut states = self.enforcement_states.lock().unwrap(); + if !states.contains_key(&commitment_seed) { + let state = EnforcementState::new(); + states.insert(commitment_seed, Arc::new(Mutex::new(state))); } + let cell = states.get(&commitment_seed).unwrap(); + Arc::clone(cell) } } -pub struct TestChainWatcher { - pub utxo_ret: Mutex>, +pub(crate) fn panicking() -> bool { + #[cfg(feature = "std")] + let panicking = ::std::thread::panicking(); + #[cfg(not(feature = "std"))] + let panicking = false; + return panicking; } -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 Drop for TestKeysInterface { + fn drop(&mut self) { + if panicking() { + return; + } + + if let Some(expectations) = &*self.expectations.lock().unwrap() { + if !expectations.is_empty() { + panic!("Unsatisfied expectations: {:?}", expectations); + } + } } } -impl ChainWatchInterface for TestChainWatcher { - fn install_watch_tx(&self, _txid: &Txid, _script_pub_key: &Script) { } - fn install_watch_outpoint(&self, _outpoint: (Txid, 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()) +/// An expectation that [`keysinterface::KeysInterface::get_shutdown_scriptpubkey`] was called and +/// returns a [`ShutdownScript`]. +pub struct OnGetShutdownScriptpubkey { + /// A shutdown script used to close a channel. + pub returns: ShutdownScript, +} + +impl core::fmt::Debug for OnGetShutdownScriptpubkey { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("OnGetShutdownScriptpubkey").finish() } - fn reentered(&self) -> usize { 0 } +} + +pub struct TestChainSource { + pub genesis_hash: BlockHash, + pub utxo_ret: Mutex>, + pub watched_txn: Mutex>, + pub watched_outputs: Mutex>, +} + +impl TestChainSource { + pub fn new(network: Network) -> Self { + let script_pubkey = Builder::new().push_opcode(opcodes::OP_TRUE).into_script(); + Self { + genesis_hash: genesis_block(network).block_hash(), + utxo_ret: Mutex::new(Ok(TxOut { value: u64::max_value(), script_pubkey })), + watched_txn: Mutex::new(HashSet::new()), + watched_outputs: Mutex::new(HashSet::new()), + } + } +} + +impl chain::Access for TestChainSource { + fn get_utxo(&self, genesis_hash: &BlockHash, _short_channel_id: u64) -> Result { + if self.genesis_hash != *genesis_hash { + return Err(chain::AccessError::UnknownChain); + } - fn get_chain_utxo(&self, _genesis_hash: BlockHash, _unspent_tx_output_identifier: u64) -> Result<(Script, u64), ChainError> { self.utxo_ret.lock().unwrap().clone() } } + +impl chain::Filter for TestChainSource { + fn register_tx(&self, txid: &Txid, script_pubkey: &Script) { + self.watched_txn.lock().unwrap().insert((*txid, script_pubkey.clone())); + } + + fn register_output(&self, output: WatchedOutput) { + self.watched_outputs.lock().unwrap().insert((output.outpoint, output.script_pubkey)); + } +} + +impl Drop for TestChainSource { + fn drop(&mut self) { + if panicking() { + return; + } + } +} + +/// A scorer useful in testing, when the passage of time isn't a concern. +pub type TestScorer = FixedPenaltyScorer;