X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning%2Fsrc%2Futil%2Ftest_utils.rs;h=8fc5a8197a78c38e13740ea663e678582676515f;hb=3fd85c8cf8be08480a3959bd2b829be17bdfdaa9;hp=8dc0111ef95c128d66671d498996c1860028955f;hpb=9c9e5f896c967d459d18d0c592c0a7a9efb3df98;p=rust-lightning diff --git a/lightning/src/util/test_utils.rs b/lightning/src/util/test_utils.rs index 8dc0111e..8fc5a819 100644 --- a/lightning/src/util/test_utils.rs +++ b/lightning/src/util/test_utils.rs @@ -7,6 +7,8 @@ // You may not use this file except in accordance with one or both of these // licenses. +use crate::blinded_path::BlindedPath; +use crate::blinded_path::payment::ReceiveTlvs; use crate::chain; use crate::chain::WatchedOutput; use crate::chain::chaininterface; @@ -17,21 +19,23 @@ use crate::chain::chainmonitor::{MonitorUpdateId, UpdateOrigin}; use crate::chain::channelmonitor; use crate::chain::channelmonitor::MonitorEvent; use crate::chain::transaction::OutPoint; +use crate::routing::router::{CandidateRouteHop, FirstHopCandidate, PublicHopCandidate, PrivateHopCandidate}; use crate::sign; use crate::events; use crate::events::bump_transaction::{WalletSource, Utxo}; use crate::ln::ChannelId; -use crate::ln::channelmanager; +use crate::ln::channelmanager::{ChannelDetails, self}; use crate::ln::chan_utils::CommitmentTransaction; use crate::ln::features::{ChannelFeatures, InitFeatures, NodeFeatures}; use crate::ln::{msgs, wire}; use crate::ln::msgs::LightningError; use crate::ln::script::ShutdownScript; -use crate::offers::invoice::UnsignedBolt12Invoice; +use crate::offers::invoice::{BlindedPayInfo, UnsignedBolt12Invoice}; use crate::offers::invoice_request::UnsignedInvoiceRequest; -use crate::routing::gossip::{EffectiveCapacity, NetworkGraph, NodeId}; +use crate::onion_message::messenger::{DefaultMessageRouter, Destination, MessageRouter, OnionMessagePath}; +use crate::routing::gossip::{EffectiveCapacity, NetworkGraph, NodeId, RoutingFees}; use crate::routing::utxo::{UtxoLookup, UtxoLookupError, UtxoResult}; -use crate::routing::router::{find_route, InFlightHtlcs, Path, Route, RouteParameters, Router, ScorerAccountingForInFlightHtlcs}; +use crate::routing::router::{DefaultRouter, InFlightHtlcs, Path, Route, RouteParameters, RouteHintHop, Router, ScorerAccountingForInFlightHtlcs}; use crate::routing::scoring::{ChannelUsage, ScoreUpdate, ScoreLookUp}; use crate::sync::RwLock; use crate::util::config::UserConfig; @@ -40,18 +44,17 @@ use crate::util::logger::{Logger, Level, Record}; use crate::util::ser::{Readable, ReadableArgs, Writer, Writeable}; use crate::util::persist::KVStore; -use bitcoin::EcdsaSighashType; use bitcoin::blockdata::constants::ChainHash; use bitcoin::blockdata::constants::genesis_block; use bitcoin::blockdata::transaction::{Transaction, TxOut}; -use bitcoin::blockdata::script::{Builder, Script}; +use bitcoin::blockdata::script::{Builder, Script, ScriptBuf}; use bitcoin::blockdata::opcodes; use bitcoin::blockdata::block::Block; use bitcoin::network::constants::Network; use bitcoin::hash_types::{BlockHash, Txid}; -use bitcoin::util::sighash::SighashCache; +use bitcoin::sighash::{SighashCache, EcdsaSighashType}; -use bitcoin::secp256k1::{PublicKey, Scalar, Secp256k1, SecretKey}; +use bitcoin::secp256k1::{PublicKey, Scalar, Secp256k1, SecretKey, self}; use bitcoin::secp256k1::ecdh::SharedSecret; use bitcoin::secp256k1::ecdsa::{RecoverableSignature, Signature}; use bitcoin::secp256k1::schnorr; @@ -67,10 +70,11 @@ use crate::sync::{Mutex, Arc}; use core::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; use core::mem; use bitcoin::bech32::u5; -use crate::sign::{InMemorySigner, Recipient, EntropySource, NodeSigner, SignerProvider}; +use crate::sign::{InMemorySigner, RandomBytes, Recipient, EntropySource, NodeSigner, SignerProvider}; #[cfg(feature = "std")] use std::time::{SystemTime, UNIX_EPOCH}; +use bitcoin::psbt::PartiallySignedTransaction; use bitcoin::Sequence; pub fn pubkey(byte: u8) -> PublicKey { @@ -100,14 +104,32 @@ impl chaininterface::FeeEstimator for TestFeeEstimator { } pub struct TestRouter<'a> { + pub router: DefaultRouter< + Arc>, + &'a TestLogger, + Arc, + &'a RwLock, + (), + TestScorer, + >, + //pub entropy_source: &'a RandomBytes, pub network_graph: Arc>, pub next_routes: Mutex)>>, pub scorer: &'a RwLock, } impl<'a> TestRouter<'a> { - pub fn new(network_graph: Arc>, scorer: &'a RwLock) -> Self { - Self { network_graph, next_routes: Mutex::new(VecDeque::new()), scorer } + pub fn new( + network_graph: Arc>, logger: &'a TestLogger, + scorer: &'a RwLock, + ) -> Self { + let entropy_source = Arc::new(RandomBytes::new([42; 32])); + Self { + router: DefaultRouter::new(network_graph.clone(), logger, entropy_source, scorer, ()), + network_graph, + next_routes: Mutex::new(VecDeque::new()), + scorer, + } } pub fn expect_find_route(&self, query: RouteParameters, result: Result) { @@ -118,7 +140,7 @@ impl<'a> TestRouter<'a> { impl<'a> Router for TestRouter<'a> { fn find_route( - &self, payer: &PublicKey, params: &RouteParameters, first_hops: Option<&[&channelmanager::ChannelDetails]>, + &self, payer: &PublicKey, params: &RouteParameters, first_hops: Option<&[&ChannelDetails]>, inflight_htlcs: InFlightHtlcs ) -> Result { if let Some((find_route_query, find_route_res)) = self.next_routes.lock().unwrap().pop_front() { @@ -129,6 +151,7 @@ impl<'a> Router for TestRouter<'a> { let scorer = ScorerAccountingForInFlightHtlcs::new(scorer, &inflight_htlcs); for path in &route.paths { let mut aggregate_msat = 0u64; + let mut prev_hop_node = payer; for (idx, hop) in path.hops.iter().rev().enumerate() { aggregate_msat += hop.fee_msat; let usage = ChannelUsage { @@ -137,28 +160,81 @@ impl<'a> Router for TestRouter<'a> { effective_capacity: EffectiveCapacity::Unknown, }; - // Since the path is reversed, the last element in our iteration is the first - // hop. if idx == path.hops.len() - 1 { - scorer.channel_penalty_msat(hop.short_channel_id, &NodeId::from_pubkey(payer), &NodeId::from_pubkey(&hop.pubkey), usage, &Default::default()); + if let Some(first_hops) = first_hops { + if let Some(idx) = first_hops.iter().position(|h| h.get_outbound_payment_scid() == Some(hop.short_channel_id)) { + let node_id = NodeId::from_pubkey(payer); + let candidate = CandidateRouteHop::FirstHop(FirstHopCandidate { + details: first_hops[idx], + payer_node_id: &node_id, + }); + scorer.channel_penalty_msat(&candidate, usage, &Default::default()); + continue; + } + } + } + let network_graph = self.network_graph.read_only(); + if let Some(channel) = network_graph.channel(hop.short_channel_id) { + let (directed, _) = channel.as_directed_to(&NodeId::from_pubkey(&hop.pubkey)).unwrap(); + let candidate = CandidateRouteHop::PublicHop(PublicHopCandidate { + info: directed, + short_channel_id: hop.short_channel_id, + }); + scorer.channel_penalty_msat(&candidate, usage, &Default::default()); } else { - let curr_hop_path_idx = path.hops.len() - 1 - idx; - scorer.channel_penalty_msat(hop.short_channel_id, &NodeId::from_pubkey(&path.hops[curr_hop_path_idx - 1].pubkey), &NodeId::from_pubkey(&hop.pubkey), usage, &Default::default()); + let target_node_id = NodeId::from_pubkey(&hop.pubkey); + let route_hint = RouteHintHop { + src_node_id: *prev_hop_node, + short_channel_id: hop.short_channel_id, + fees: RoutingFees { base_msat: 0, proportional_millionths: 0 }, + cltv_expiry_delta: 0, + htlc_minimum_msat: None, + htlc_maximum_msat: None, + }; + let candidate = CandidateRouteHop::PrivateHop(PrivateHopCandidate { + hint: &route_hint, + target_node_id: &target_node_id, + }); + scorer.channel_penalty_msat(&candidate, usage, &Default::default()); } + prev_hop_node = &hop.pubkey; } } } return find_route_res; } - let logger = TestLogger::new(); - find_route( - payer, params, &self.network_graph, first_hops, &logger, - &ScorerAccountingForInFlightHtlcs::new(self.scorer.read().unwrap(), &inflight_htlcs), &Default::default(), - &[42; 32] + + self.router.find_route(payer, params, first_hops, inflight_htlcs) + } + + fn create_blinded_payment_paths< + T: secp256k1::Signing + secp256k1::Verification + >( + &self, recipient: PublicKey, first_hops: Vec, tlvs: ReceiveTlvs, + amount_msats: u64, secp_ctx: &Secp256k1, + ) -> Result, ()> { + self.router.create_blinded_payment_paths( + recipient, first_hops, tlvs, amount_msats, secp_ctx ) } } +impl<'a> MessageRouter for TestRouter<'a> { + fn find_path( + &self, sender: PublicKey, peers: Vec, destination: Destination + ) -> Result { + self.router.find_path(sender, peers, destination) + } + + fn create_blinded_paths< + T: secp256k1::Signing + secp256k1::Verification + >( + &self, recipient: PublicKey, peers: Vec, secp_ctx: &Secp256k1, + ) -> Result, ()> { + self.router.create_blinded_paths(recipient, peers, secp_ctx) + } +} + impl<'a> Drop for TestRouter<'a> { fn drop(&mut self) { #[cfg(feature = "std")] { @@ -170,19 +246,45 @@ impl<'a> Drop for TestRouter<'a> { } } +pub struct TestMessageRouter<'a> { + inner: DefaultMessageRouter>, &'a TestLogger, &'a TestKeysInterface>, +} + +impl<'a> TestMessageRouter<'a> { + pub fn new(network_graph: Arc>, entropy_source: &'a TestKeysInterface) -> Self { + Self { inner: DefaultMessageRouter::new(network_graph, entropy_source) } + } +} + +impl<'a> MessageRouter for TestMessageRouter<'a> { + fn find_path( + &self, sender: PublicKey, peers: Vec, destination: Destination + ) -> Result { + self.inner.find_path(sender, peers, destination) + } + + fn create_blinded_paths( + &self, recipient: PublicKey, peers: Vec, secp_ctx: &Secp256k1, + ) -> Result, ()> { + self.inner.create_blinded_paths(recipient, peers, secp_ctx) + } +} + pub struct OnlyReadsKeysInterface {} impl EntropySource for OnlyReadsKeysInterface { fn get_secure_random_bytes(&self) -> [u8; 32] { [0; 32] }} impl SignerProvider for OnlyReadsKeysInterface { - type Signer = TestChannelSigner; + type EcdsaSigner = TestChannelSigner; + #[cfg(taproot)] + type TaprootSigner = TestChannelSigner; fn generate_channel_keys_id(&self, _inbound: bool, _channel_value_satoshis: u64, _user_channel_id: u128) -> [u8; 32] { unreachable!(); } - fn derive_channel_signer(&self, _channel_value_satoshis: u64, _channel_keys_id: [u8; 32]) -> Self::Signer { unreachable!(); } + fn derive_channel_signer(&self, _channel_value_satoshis: u64, _channel_keys_id: [u8; 32]) -> Self::EcdsaSigner { unreachable!(); } - fn read_chan_signer(&self, mut reader: &[u8]) -> Result { + fn read_chan_signer(&self, mut reader: &[u8]) -> Result { let inner: InMemorySigner = ReadableArgs::read(&mut reader, self)?; let state = Arc::new(Mutex::new(EnforcementState::new())); @@ -193,7 +295,7 @@ impl SignerProvider for OnlyReadsKeysInterface { )) } - fn get_destination_script(&self) -> Result { Err(()) } + fn get_destination_script(&self, _channel_keys_id: [u8; 32]) -> Result { Err(()) } fn get_shutdown_scriptpubkey(&self) -> Result { Err(()) } } @@ -201,7 +303,7 @@ pub struct TestChainMonitor<'a> { pub added_monitors: Mutex)>>, pub monitor_updates: Mutex>>, pub latest_monitor_update_id: Mutex>, - pub chain_monitor: chainmonitor::ChainMonitor>, + 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 @@ -212,11 +314,11 @@ pub struct TestChainMonitor<'a> { pub expect_monitor_round_trip_fail: Mutex>, } 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 { + pub fn new(chain_source: Option<&'a TestChainSource>, broadcaster: &'a dyn chaininterface::BroadcasterInterface, logger: &'a TestLogger, fee_estimator: &'a TestFeeEstimator, persister: &'a dyn 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()), + monitor_updates: Mutex::new(new_hash_map()), + latest_monitor_update_id: Mutex::new(new_hash_map()), chain_monitor: chainmonitor::ChainMonitor::new(chain_source, broadcaster, logger, fee_estimator, persister), keys_manager, expect_channel_force_closed: Mutex::new(None), @@ -238,7 +340,7 @@ impl<'a> chain::Watch for TestChainMonitor<'a> { let new_monitor = <(BlockHash, channelmonitor::ChannelMonitor)>::read( &mut io::Cursor::new(&w.0), (self.keys_manager, self.keys_manager)).unwrap().1; assert!(new_monitor == monitor); - self.latest_monitor_update_id.lock().unwrap().insert(funding_txo.to_channel_id(), + self.latest_monitor_update_id.lock().unwrap().insert(monitor.channel_id(), (funding_txo, monitor.get_latest_update_id(), MonitorUpdateId::from_new_monitor(&monitor))); self.added_monitors.lock().unwrap().push((funding_txo, monitor)); self.chain_monitor.watch_channel(funding_txo, new_monitor) @@ -250,18 +352,19 @@ impl<'a> chain::Watch for TestChainMonitor<'a> { update.write(&mut w).unwrap(); assert!(channelmonitor::ChannelMonitorUpdate::read( &mut io::Cursor::new(&w.0)).unwrap() == *update); + let channel_id = update.channel_id.unwrap_or(ChannelId::v1_from_funding_outpoint(funding_txo)); - self.monitor_updates.lock().unwrap().entry(funding_txo.to_channel_id()).or_insert(Vec::new()).push(update.clone()); + self.monitor_updates.lock().unwrap().entry(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!(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(), + self.latest_monitor_update_id.lock().unwrap().insert(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 @@ -272,7 +375,7 @@ impl<'a> chain::Watch for TestChainMonitor<'a> { let new_monitor = <(BlockHash, channelmonitor::ChannelMonitor)>::read( &mut io::Cursor::new(&w.0), (self.keys_manager, self.keys_manager)).unwrap().1; if let Some(chan_id) = self.expect_monitor_round_trip_fail.lock().unwrap().take() { - assert_eq!(chan_id, funding_txo.to_channel_id()); + assert_eq!(chan_id, channel_id); assert!(new_monitor != *monitor); } else { assert!(new_monitor == *monitor); @@ -281,7 +384,7 @@ impl<'a> chain::Watch for TestChainMonitor<'a> { update_res } - fn release_pending_monitor_events(&self) -> Vec<(OutPoint, Vec, Option)> { + fn release_pending_monitor_events(&self) -> Vec<(OutPoint, ChannelId, Vec, Option)> { return self.chain_monitor.release_pending_monitor_events(); } } @@ -302,16 +405,16 @@ pub(crate) struct WatchtowerPersister { /// After receiving a revoke_and_ack for a commitment number, we'll form and store the justice /// tx which would be used to provide a watchtower with the data it needs. watchtower_state: Mutex>>, - destination_script: Script, + destination_script: ScriptBuf, } impl WatchtowerPersister { #[cfg(test)] - pub(crate) fn new(destination_script: Script) -> Self { + pub(crate) fn new(destination_script: ScriptBuf) -> Self { WatchtowerPersister { persister: TestPersister::new(), - unsigned_justice_tx_data: Mutex::new(HashMap::new()), - watchtower_state: Mutex::new(HashMap::new()), + unsigned_justice_tx_data: Mutex::new(new_hash_map()), + watchtower_state: Mutex::new(new_hash_map()), destination_script, } } @@ -335,7 +438,7 @@ impl WatchtowerPersister { } } -impl chainmonitor::Persist for WatchtowerPersister { +impl chainmonitor::Persist for WatchtowerPersister { fn persist_new_channel(&self, funding_txo: OutPoint, data: &channelmonitor::ChannelMonitor, id: MonitorUpdateId ) -> chain::ChannelMonitorUpdateStatus { @@ -344,7 +447,7 @@ impl chainmonitor::Persist fo assert!(self.unsigned_justice_tx_data.lock().unwrap() .insert(funding_txo, VecDeque::new()).is_none()); assert!(self.watchtower_state.lock().unwrap() - .insert(funding_txo, HashMap::new()).is_none()); + .insert(funding_txo, new_hash_map()).is_none()); let initial_counterparty_commitment_tx = data.initial_counterparty_commitment_tx() .expect("First and only call expects Some"); @@ -405,8 +508,8 @@ impl TestPersister { pub fn new() -> Self { Self { update_rets: Mutex::new(VecDeque::new()), - chain_sync_monitor_persistences: Mutex::new(HashMap::new()), - offchain_monitor_updates: Mutex::new(HashMap::new()), + chain_sync_monitor_persistences: Mutex::new(new_hash_map()), + offchain_monitor_updates: Mutex::new(new_hash_map()), } } @@ -415,7 +518,7 @@ impl TestPersister { self.update_rets.lock().unwrap().push_back(next_ret); } } -impl chainmonitor::Persist for TestPersister { +impl chainmonitor::Persist for TestPersister { fn persist_new_channel(&self, _funding_txo: OutPoint, _data: &channelmonitor::ChannelMonitor, _id: MonitorUpdateId) -> chain::ChannelMonitorUpdateStatus { if let Some(update_ret) = self.update_rets.lock().unwrap().pop_front() { return update_ret @@ -430,9 +533,9 @@ impl chainmonitor::Persist fo } let is_chain_sync = if let UpdateOrigin::ChainSync(_) = update_id.contents { true } else { false }; if is_chain_sync { - self.chain_sync_monitor_persistences.lock().unwrap().entry(funding_txo).or_insert(HashSet::new()).insert(update_id); + self.chain_sync_monitor_persistences.lock().unwrap().entry(funding_txo).or_insert(new_hash_set()).insert(update_id); } else { - self.offchain_monitor_updates.lock().unwrap().entry(funding_txo).or_insert(HashSet::new()).insert(update_id); + self.offchain_monitor_updates.lock().unwrap().entry(funding_txo).or_insert(new_hash_set()).insert(update_id); } ret } @@ -445,7 +548,7 @@ pub struct TestStore { impl TestStore { pub fn new(read_only: bool) -> Self { - let persisted_bytes = Mutex::new(HashMap::new()); + let persisted_bytes = Mutex::new(new_hash_map()); Self { persisted_bytes, read_only } } } @@ -485,7 +588,7 @@ impl KVStore for TestStore { } else { format!("{}/{}", primary_namespace, secondary_namespace) }; - let outer_e = persisted_lock.entry(prefixed).or_insert(HashMap::new()); + let outer_e = persisted_lock.entry(prefixed).or_insert(new_hash_map()); let mut bytes = Vec::new(); bytes.write_all(buf)?; outer_e.insert(key.to_string(), bytes); @@ -529,6 +632,9 @@ impl KVStore for TestStore { } } +unsafe impl Sync for TestStore {} +unsafe impl Send for TestStore {} + pub struct TestBroadcaster { pub txn_broadcasted: Mutex>, pub blocks: Arc>>, @@ -552,7 +658,7 @@ impl TestBroadcaster { pub fn unique_txn_broadcast(&self) -> Vec { let mut txn = self.txn_broadcasted.lock().unwrap().split_off(0); - let mut seen = HashSet::new(); + let mut seen = new_hash_set(); txn.retain(|tx| seen.insert(tx.txid())); txn } @@ -561,9 +667,9 @@ impl TestBroadcaster { impl chaininterface::BroadcasterInterface for TestBroadcaster { fn broadcast_transactions(&self, txs: &[&Transaction]) { for tx in txs { - let lock_time = tx.lock_time.0; + let lock_time = tx.lock_time.to_consensus_u32(); assert!(lock_time < 1_500_000_000); - if bitcoin::LockTime::from(tx.lock_time).is_block_height() && lock_time > self.blocks.lock().unwrap().last().unwrap().1 { + if tx.lock_time.is_block_height() && lock_time > self.blocks.lock().unwrap().last().unwrap().1 { for inp in tx.input.iter() { if inp.sequence != Sequence::MAX { panic!("We should never broadcast a transaction before its locktime ({})!", tx.lock_time); @@ -589,7 +695,7 @@ impl TestChannelMessageHandler { TestChannelMessageHandler { pending_events: Mutex::new(Vec::new()), expected_recv_msgs: Mutex::new(None), - connected_peers: Mutex::new(HashSet::new()), + connected_peers: Mutex::new(new_hash_set()), message_fetch_counter: AtomicUsize::new(0), chain_hash, } @@ -929,7 +1035,8 @@ impl events::MessageSendEventsProvider for TestRoutingMessageHandler { pub struct TestLogger { level: Level, pub(crate) id: String, - pub lines: Mutex>, + pub lines: Mutex>, + pub context: Mutex, Option), usize>>, } impl TestLogger { @@ -940,13 +1047,14 @@ impl TestLogger { TestLogger { level: Level::Trace, id, - lines: Mutex::new(HashMap::new()) + lines: Mutex::new(new_hash_map()), + context: Mutex::new(new_hash_map()), } } pub fn enable(&mut self, level: Level) { self.level = level; } - pub fn assert_log(&self, module: String, line: String, count: usize) { + pub fn assert_log(&self, module: &str, line: String, count: usize) { let log_entries = self.lines.lock().unwrap(); assert_eq!(log_entries.get(&(module, line)), Some(&count)); } @@ -958,7 +1066,7 @@ impl TestLogger { pub fn assert_log_contains(&self, module: &str, line: &str, 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) + *m == module && l.contains(line) }).map(|(_, c) | { c }).sum(); assert_eq!(l, count) } @@ -971,15 +1079,24 @@ impl TestLogger { pub fn assert_log_regex(&self, module: &str, 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) + *m == module && pattern.is_match(&l) }).map(|(_, c) | { c }).sum(); assert_eq!(l, count) } + + pub fn assert_log_context_contains( + &self, module: &str, peer_id: Option, channel_id: Option, count: usize + ) { + let context_entries = self.context.lock().unwrap(); + let l = context_entries.get(&(module, peer_id, channel_id)).unwrap(); + 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; + fn log(&self, record: Record) { + *self.lines.lock().unwrap().entry((record.module_path, format!("{}", record.args))).or_insert(0) += 1; + *self.context.lock().unwrap().entry((record.module_path, record.peer_id, record.channel_id)).or_insert(0) += 1; if record.level >= self.level { #[cfg(all(not(ldk_bench), feature = "std"))] { let pfx = format!("{} {} [{}:{}]", self.id, record.level.to_string(), record.module_path, record.line); @@ -1050,6 +1167,7 @@ pub struct TestKeysInterface { pub disable_revocation_policy_check: bool, enforcement_states: Mutex>>>, expectations: Mutex>>, + pub unavailable_signers: Mutex>, } impl EntropySource for TestKeysInterface { @@ -1097,7 +1215,9 @@ impl NodeSigner for TestKeysInterface { } impl SignerProvider for TestKeysInterface { - type Signer = TestChannelSigner; + type EcdsaSigner = TestChannelSigner; + #[cfg(taproot)] + type TaprootSigner = TestChannelSigner; fn generate_channel_keys_id(&self, inbound: bool, channel_value_satoshis: u64, user_channel_id: u128) -> [u8; 32] { self.backing.generate_channel_keys_id(inbound, channel_value_satoshis, user_channel_id) @@ -1106,10 +1226,14 @@ impl SignerProvider for TestKeysInterface { fn derive_channel_signer(&self, channel_value_satoshis: u64, channel_keys_id: [u8; 32]) -> TestChannelSigner { let keys = self.backing.derive_channel_signer(channel_value_satoshis, channel_keys_id); let state = self.make_enforcement_state_cell(keys.commitment_seed); - TestChannelSigner::new_with_revoked(keys, state, self.disable_revocation_policy_check) + let signer = TestChannelSigner::new_with_revoked(keys, state, self.disable_revocation_policy_check); + if self.unavailable_signers.lock().unwrap().contains(&channel_keys_id) { + signer.set_available(false); + } + signer } - fn read_chan_signer(&self, buffer: &[u8]) -> Result { + fn read_chan_signer(&self, buffer: &[u8]) -> Result { let mut reader = io::Cursor::new(buffer); let inner: InMemorySigner = ReadableArgs::read(&mut reader, self)?; @@ -1122,7 +1246,7 @@ impl SignerProvider for TestKeysInterface { )) } - fn get_destination_script(&self) -> Result { self.backing.get_destination_script() } + fn get_destination_script(&self, channel_keys_id: [u8; 32]) -> Result { self.backing.get_destination_script(channel_keys_id) } fn get_shutdown_scriptpubkey(&self) -> Result { match &mut *self.expectations.lock().unwrap() { @@ -1142,8 +1266,9 @@ impl TestKeysInterface { backing: sign::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()), + enforcement_states: Mutex::new(new_hash_map()), expectations: Mutex::new(None), + unavailable_signers: Mutex::new(new_hash_set()), } } @@ -1157,9 +1282,7 @@ impl TestKeysInterface { } pub fn derive_channel_keys(&self, channel_value_satoshis: u64, id: &[u8; 32]) -> TestChannelSigner { - let keys = self.backing.derive_channel_keys(channel_value_satoshis, id); - let state = self.make_enforcement_state_cell(keys.commitment_seed); - TestChannelSigner::new_with_revoked(keys, state, self.disable_revocation_policy_check) + self.derive_channel_signer(channel_value_satoshis, *id) } fn make_enforcement_state_cell(&self, commitment_seed: [u8; 32]) -> Arc> { @@ -1212,8 +1335,8 @@ pub struct TestChainSource { pub chain_hash: ChainHash, pub utxo_ret: Mutex, pub get_utxo_call_count: AtomicUsize, - pub watched_txn: Mutex>, - pub watched_outputs: Mutex>, + pub watched_txn: Mutex>, + pub watched_outputs: Mutex>, } impl TestChainSource { @@ -1223,8 +1346,8 @@ impl TestChainSource { chain_hash: ChainHash::using_genesis_block(network), utxo_ret: Mutex::new(UtxoResult::Sync(Ok(TxOut { value: u64::max_value(), script_pubkey }))), get_utxo_call_count: AtomicUsize::new(0), - watched_txn: Mutex::new(HashSet::new()), - watched_outputs: Mutex::new(HashSet::new()), + watched_txn: Mutex::new(new_hash_set()), + watched_outputs: Mutex::new(new_hash_set()), } } } @@ -1242,7 +1365,7 @@ impl UtxoLookup for TestChainSource { impl chain::Filter for TestChainSource { fn register_tx(&self, txid: &Txid, script_pubkey: &Script) { - self.watched_txn.lock().unwrap().insert((*txid, script_pubkey.clone())); + self.watched_txn.lock().unwrap().insert((*txid, script_pubkey.into())); } fn register_output(&self, output: WatchedOutput) { @@ -1283,8 +1406,12 @@ impl crate::util::ser::Writeable for TestScorer { impl ScoreLookUp for TestScorer { type ScoreParams = (); fn channel_penalty_msat( - &self, short_channel_id: u64, _source: &NodeId, _target: &NodeId, usage: ChannelUsage, _score_params: &Self::ScoreParams + &self, candidate: &CandidateRouteHop, usage: ChannelUsage, _score_params: &Self::ScoreParams ) -> u64 { + let short_channel_id = match candidate.globally_unique_short_channel_id() { + Some(scid) => scid, + None => return 0, + }; if let Some(scorer_expectations) = self.scorer_expectations.borrow_mut().as_mut() { match scorer_expectations.pop_front() { Some((scid, expectation)) => { @@ -1299,15 +1426,20 @@ impl ScoreLookUp for TestScorer { } impl ScoreUpdate for TestScorer { - fn payment_path_failed(&mut self, _actual_path: &Path, _actual_short_channel_id: u64) {} + fn payment_path_failed(&mut self, _actual_path: &Path, _actual_short_channel_id: u64, _duration_since_epoch: Duration) {} - fn payment_path_successful(&mut self, _actual_path: &Path) {} + fn payment_path_successful(&mut self, _actual_path: &Path, _duration_since_epoch: Duration) {} - fn probe_failed(&mut self, _actual_path: &Path, _: u64) {} + fn probe_failed(&mut self, _actual_path: &Path, _: u64, _duration_since_epoch: Duration) {} - fn probe_successful(&mut self, _actual_path: &Path) {} + fn probe_successful(&mut self, _actual_path: &Path, _duration_since_epoch: Duration) {} + + fn time_passed(&mut self, _duration_since_epoch: Duration) {} } +#[cfg(c_bindings)] +impl crate::routing::scoring::Score for TestScorer {} + impl Drop for TestScorer { fn drop(&mut self) { #[cfg(feature = "std")] { @@ -1362,22 +1494,23 @@ impl WalletSource for TestWalletSource { Ok(self.utxos.borrow().clone()) } - fn get_change_script(&self) -> Result { + fn get_change_script(&self) -> Result { let public_key = bitcoin::PublicKey::new(self.secret_key.public_key(&self.secp)); - Ok(Script::new_p2pkh(&public_key.pubkey_hash())) + Ok(ScriptBuf::new_p2pkh(&public_key.pubkey_hash())) } - fn sign_tx(&self, mut tx: Transaction) -> Result { + fn sign_psbt(&self, psbt: PartiallySignedTransaction) -> Result { + let mut tx = psbt.extract_tx(); let utxos = self.utxos.borrow(); for i in 0..tx.input.len() { if let Some(utxo) = utxos.iter().find(|utxo| utxo.outpoint == tx.input[i].previous_output) { let sighash = SighashCache::new(&tx) .legacy_signature_hash(i, &utxo.output.script_pubkey, EcdsaSighashType::All as u32) .map_err(|_| ())?; - let sig = self.secp.sign_ecdsa(&sighash.as_hash().into(), &self.secret_key); - let bitcoin_sig = bitcoin::EcdsaSig { sig, hash_ty: EcdsaSighashType::All }.to_vec(); + let sig = self.secp.sign_ecdsa(&(*sighash.as_raw_hash()).into(), &self.secret_key); + let bitcoin_sig = bitcoin::ecdsa::Signature { sig, hash_ty: EcdsaSighashType::All }; tx.input[i].script_sig = Builder::new() - .push_slice(&bitcoin_sig) + .push_slice(&bitcoin_sig.serialize()) .push_slice(&self.secret_key.public_key(&self.secp).serialize()) .into_script(); }