X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning%2Fsrc%2Futil%2Ftest_utils.rs;h=9f89228bc8ff700313579128ea956713a466bf2e;hb=a8bd4c097f7ae6620eee66c1cb49144e38568439;hp=09a8c3a26612725c07fedadc7cbb269814b54f88;hpb=f98a652f11f598aa828acfff64e5b0df8de80f4f;p=rust-lightning diff --git a/lightning/src/util/test_utils.rs b/lightning/src/util/test_utils.rs index 09a8c3a2..9f89228b 100644 --- a/lightning/src/util/test_utils.rs +++ b/lightning/src/util/test_utils.rs @@ -8,14 +8,15 @@ // licenses. use crate::blinded_path::BlindedPath; +use crate::blinded_path::message::ForwardNode; use crate::blinded_path::payment::ReceiveTlvs; use crate::chain; use crate::chain::WatchedOutput; use crate::chain::chaininterface; use crate::chain::chaininterface::ConfirmationTarget; +#[cfg(test)] use crate::chain::chaininterface::FEERATE_FLOOR_SATS_PER_KW; use crate::chain::chainmonitor; -use crate::chain::chainmonitor::{MonitorUpdateId, UpdateOrigin}; use crate::chain::channelmonitor; use crate::chain::channelmonitor::MonitorEvent; use crate::chain::transaction::OutPoint; @@ -23,8 +24,9 @@ use crate::routing::router::{CandidateRouteHop, FirstHopCandidate, PublicHopCand use crate::sign; use crate::events; use crate::events::bump_transaction::{WalletSource, Utxo}; -use crate::ln::ChannelId; +use crate::ln::types::ChannelId; use crate::ln::channelmanager::{ChannelDetails, self}; +#[cfg(test)] use crate::ln::chan_utils::CommitmentTransaction; use crate::ln::features::{ChannelFeatures, InitFeatures, NodeFeatures}; use crate::ln::{msgs, wire}; @@ -44,14 +46,16 @@ use crate::util::logger::{Logger, Level, Record}; use crate::util::ser::{Readable, ReadableArgs, Writer, Writeable}; use crate::util::persist::KVStore; +use bitcoin::amount::Amount; use bitcoin::blockdata::constants::ChainHash; use bitcoin::blockdata::constants::genesis_block; use bitcoin::blockdata::transaction::{Transaction, TxOut}; use bitcoin::blockdata::script::{Builder, Script, ScriptBuf}; use bitcoin::blockdata::opcodes; use bitcoin::blockdata::block::Block; -use bitcoin::network::constants::Network; +use bitcoin::network::Network; use bitcoin::hash_types::{BlockHash, Txid}; +use bitcoin::hashes::Hash; use bitcoin::sighash::{SighashCache, EcdsaSighashType}; use bitcoin::secp256k1::{PublicKey, Scalar, Secp256k1, SecretKey, self}; @@ -59,9 +63,6 @@ use bitcoin::secp256k1::ecdh::SharedSecret; use bitcoin::secp256k1::ecdsa::{RecoverableSignature, Signature}; use bitcoin::secp256k1::schnorr; -#[cfg(any(test, feature = "_test_utils"))] -use regex; - use crate::io; use crate::prelude::*; use core::cell::RefCell; @@ -69,12 +70,12 @@ use core::time::Duration; use crate::sync::{Mutex, Arc}; use core::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; use core::mem; -use bitcoin::bech32::u5; +use bech32::u5; use crate::sign::{InMemorySigner, RandomBytes, Recipient, EntropySource, NodeSigner, SignerProvider}; #[cfg(feature = "std")] use std::time::{SystemTime, UNIX_EPOCH}; -use bitcoin::psbt::PartiallySignedTransaction; +use bitcoin::psbt::Psbt; use bitcoin::Sequence; pub fn pubkey(byte: u8) -> PublicKey { @@ -114,7 +115,7 @@ pub struct TestRouter<'a> { >, //pub entropy_source: &'a RandomBytes, pub network_graph: Arc>, - pub next_routes: Mutex)>>, + pub next_routes: Mutex>)>>, pub scorer: &'a RwLock, } @@ -134,7 +135,12 @@ impl<'a> TestRouter<'a> { pub fn expect_find_route(&self, query: RouteParameters, result: Result) { let mut expected_routes = self.next_routes.lock().unwrap(); - expected_routes.push_back((query, result)); + expected_routes.push_back((query, Some(result))); + } + + pub fn expect_find_route_query(&self, query: RouteParameters) { + let mut expected_routes = self.next_routes.lock().unwrap(); + expected_routes.push_back((query, None)); } } @@ -143,68 +149,82 @@ impl<'a> Router for TestRouter<'a> { &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() { + let route_res; + let next_route_opt = self.next_routes.lock().unwrap().pop_front(); + if let Some((find_route_query, find_route_res)) = next_route_opt { assert_eq!(find_route_query, *params); - if let Ok(ref route) = find_route_res { - assert_eq!(route.route_params, Some(find_route_query)); - let scorer = self.scorer.read().unwrap(); - 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 { - amount_msat: aggregate_msat, - inflight_htlc_msat: 0, - effective_capacity: EffectiveCapacity::Unknown, - }; - - if idx == path.hops.len() - 1 { - 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; + if let Some(res) = find_route_res { + if let Ok(ref route) = res { + assert_eq!(route.route_params, Some(find_route_query)); + let scorer = self.scorer.read().unwrap(); + 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 { + amount_msat: aggregate_msat, + inflight_htlc_msat: 0, + effective_capacity: EffectiveCapacity::Unknown, + }; + + if idx == path.hops.len() - 1 { + 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 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; } - 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 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; } } + route_res = res; + } else { + route_res = self.router.find_route(payer, params, first_hops, inflight_htlcs); } - return find_route_res; - } + } else { + route_res = self.router.find_route(payer, params, first_hops, inflight_htlcs); + }; - self.router.find_route(payer, params, first_hops, inflight_htlcs) + if let Ok(route) = &route_res { + // Previously, `Route`s failed to round-trip through serialization due to a write/read + // mismatch. Thus, here we test all test-generated routes round-trip: + let ser = route.encode(); + assert_eq!(Route::read(&mut &ser[..]).unwrap(), *route); + } + route_res } fn create_blinded_payment_paths< @@ -229,7 +249,7 @@ impl<'a> MessageRouter for TestRouter<'a> { fn create_blinded_paths< T: secp256k1::Signing + secp256k1::Verification >( - &self, recipient: PublicKey, peers: Vec, secp_ctx: &Secp256k1, + &self, recipient: PublicKey, peers: Vec, secp_ctx: &Secp256k1, ) -> Result, ()> { self.router.create_blinded_paths(recipient, peers, secp_ctx) } @@ -264,7 +284,7 @@ impl<'a> MessageRouter for TestMessageRouter<'a> { } fn create_blinded_paths( - &self, recipient: PublicKey, peers: Vec, secp_ctx: &Secp256k1, + &self, recipient: PublicKey, peers: Vec, secp_ctx: &Secp256k1, ) -> Result, ()> { self.inner.create_blinded_paths(recipient, peers, secp_ctx) } @@ -302,7 +322,7 @@ impl SignerProvider for OnlyReadsKeysInterface { pub struct TestChainMonitor<'a> { pub added_monitors: Mutex)>>, pub monitor_updates: Mutex>>, - pub latest_monitor_update_id: 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 @@ -341,7 +361,7 @@ impl<'a> chain::Watch for TestChainMonitor<'a> { &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(monitor.channel_id(), - (funding_txo, monitor.get_latest_update_id(), MonitorUpdateId::from_new_monitor(&monitor))); + (funding_txo, monitor.get_latest_update_id(), monitor.get_latest_update_id())); self.added_monitors.lock().unwrap().push((funding_txo, monitor)); self.chain_monitor.watch_channel(funding_txo, new_monitor) } @@ -365,7 +385,7 @@ impl<'a> chain::Watch for TestChainMonitor<'a> { } self.latest_monitor_update_id.lock().unwrap().insert(channel_id, - (funding_txo, update.update_id, MonitorUpdateId::from_monitor_update(update))); + (funding_txo, update.update_id, update.update_id)); 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... @@ -389,12 +409,14 @@ impl<'a> chain::Watch for TestChainMonitor<'a> { } } +#[cfg(test)] struct JusticeTxData { justice_tx: Transaction, - value: u64, + value: Amount, commitment_number: u64, } +#[cfg(test)] pub(crate) struct WatchtowerPersister { persister: TestPersister, /// Upon a new commitment_signed, we'll get a @@ -408,6 +430,7 @@ pub(crate) struct WatchtowerPersister { destination_script: ScriptBuf, } +#[cfg(test)] impl WatchtowerPersister { #[cfg(test)] pub(crate) fn new(destination_script: ScriptBuf) -> Self { @@ -438,11 +461,12 @@ impl WatchtowerPersister { } } -impl chainmonitor::Persist for WatchtowerPersister { +#[cfg(test)] +impl chainmonitor::Persist for WatchtowerPersister { fn persist_new_channel(&self, funding_txo: OutPoint, - data: &channelmonitor::ChannelMonitor, id: MonitorUpdateId + data: &channelmonitor::ChannelMonitor ) -> chain::ChannelMonitorUpdateStatus { - let res = self.persister.persist_new_channel(funding_txo, data, id); + let res = self.persister.persist_new_channel(funding_txo, data); assert!(self.unsigned_justice_tx_data.lock().unwrap() .insert(funding_txo, VecDeque::new()).is_none()); @@ -462,9 +486,9 @@ impl chainmonitor::Persist, - data: &channelmonitor::ChannelMonitor, update_id: MonitorUpdateId + data: &channelmonitor::ChannelMonitor ) -> chain::ChannelMonitorUpdateStatus { - let res = self.persister.update_persisted_channel(funding_txo, update, data, update_id); + let res = self.persister.update_persisted_channel(funding_txo, update, data); if let Some(update) = update { let commitment_txs = data.counterparty_commitment_txs_from_update(update); @@ -477,7 +501,7 @@ impl chainmonitor::Persist { let dup = self.watchtower_state.lock().unwrap() .get_mut(&funding_txo).unwrap() @@ -491,24 +515,26 @@ impl chainmonitor::Persist>::archive_persisted_channel(&self.persister, funding_txo); + } } pub struct TestPersister { /// The queue of update statuses we'll return. If none are queued, ::Completed will always be /// returned. pub update_rets: 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>>, + /// [`ChannelMonitor::get_latest_update_id`] here. + /// + /// [`ChannelMonitor`]: channelmonitor::ChannelMonitor + pub offchain_monitor_updates: Mutex>>, } impl TestPersister { pub fn new() -> Self { Self { update_rets: Mutex::new(VecDeque::new()), - chain_sync_monitor_persistences: Mutex::new(new_hash_map()), offchain_monitor_updates: Mutex::new(new_hash_map()), } } @@ -518,27 +544,30 @@ impl TestPersister { self.update_rets.lock().unwrap().push_back(next_ret); } } -impl chainmonitor::Persist for TestPersister { - fn persist_new_channel(&self, _funding_txo: OutPoint, _data: &channelmonitor::ChannelMonitor, _id: MonitorUpdateId) -> chain::ChannelMonitorUpdateStatus { +impl chainmonitor::Persist for TestPersister { + fn persist_new_channel(&self, _funding_txo: OutPoint, _data: &channelmonitor::ChannelMonitor) -> chain::ChannelMonitorUpdateStatus { if let Some(update_ret) = self.update_rets.lock().unwrap().pop_front() { return update_ret } chain::ChannelMonitorUpdateStatus::Completed } - fn update_persisted_channel(&self, funding_txo: OutPoint, _update: Option<&channelmonitor::ChannelMonitorUpdate>, _data: &channelmonitor::ChannelMonitor, update_id: MonitorUpdateId) -> chain::ChannelMonitorUpdateStatus { + fn update_persisted_channel(&self, funding_txo: OutPoint, update: Option<&channelmonitor::ChannelMonitorUpdate>, _data: &channelmonitor::ChannelMonitor) -> chain::ChannelMonitorUpdateStatus { let mut ret = chain::ChannelMonitorUpdateStatus::Completed; if let Some(update_ret) = self.update_rets.lock().unwrap().pop_front() { ret = update_ret; } - 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(new_hash_set()).insert(update_id); - } else { - self.offchain_monitor_updates.lock().unwrap().entry(funding_txo).or_insert(new_hash_set()).insert(update_id); + + if let Some(update) = update { + self.offchain_monitor_updates.lock().unwrap().entry(funding_txo).or_insert(new_hash_set()).insert(update.update_id); } ret } + + fn archive_persisted_channel(&self, funding_txo: OutPoint) { + // remove the channel from the offchain_monitor_updates map + self.offchain_monitor_updates.lock().unwrap().remove(&funding_txo); + } } pub struct TestStore { @@ -632,6 +661,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>>, @@ -752,12 +784,15 @@ impl msgs::ChannelMessageHandler for TestChannelMessageHandler { fn handle_stfu(&self, _their_node_id: &PublicKey, msg: &msgs::Stfu) { self.received_msg(wire::Message::Stfu(msg.clone())); } + #[cfg(splicing)] fn handle_splice(&self, _their_node_id: &PublicKey, msg: &msgs::Splice) { self.received_msg(wire::Message::Splice(msg.clone())); } + #[cfg(splicing)] fn handle_splice_ack(&self, _their_node_id: &PublicKey, msg: &msgs::SpliceAck) { self.received_msg(wire::Message::SpliceAck(msg.clone())); } + #[cfg(splicing)] fn handle_splice_locked(&self, _their_node_id: &PublicKey, msg: &msgs::SpliceLocked) { self.received_msg(wire::Message::SpliceLocked(msg.clone())); } @@ -1137,7 +1172,7 @@ impl NodeSigner for TestNodeSigner { Ok(SharedSecret::new(other_key, &node_secret)) } - fn sign_invoice(&self, _: &[u8], _: &[bitcoin::bech32::u5], _: Recipient) -> Result { + fn sign_invoice(&self, _: &[u8], _: &[bech32::u5], _: Recipient) -> Result { unreachable!() } @@ -1341,12 +1376,16 @@ impl TestChainSource { let script_pubkey = Builder::new().push_opcode(opcodes::OP_TRUE).into_script(); Self { chain_hash: ChainHash::using_genesis_block(network), - utxo_ret: Mutex::new(UtxoResult::Sync(Ok(TxOut { value: u64::max_value(), script_pubkey }))), + utxo_ret: Mutex::new(UtxoResult::Sync(Ok(TxOut { value: Amount::MAX, script_pubkey }))), get_utxo_call_count: AtomicUsize::new(0), watched_txn: Mutex::new(new_hash_set()), watched_outputs: Mutex::new(new_hash_set()), } } + pub fn remove_watched_txn_and_outputs(&self, outpoint: OutPoint, script_pubkey: ScriptBuf) { + self.watched_outputs.lock().unwrap().remove(&(outpoint, script_pubkey.clone())); + self.watched_txn.lock().unwrap().remove(&(outpoint.txid, script_pubkey)); + } } impl UtxoLookup for TestChainSource { @@ -1468,7 +1507,7 @@ impl TestWalletSource { } } - pub fn add_utxo(&self, outpoint: bitcoin::OutPoint, value: u64) -> TxOut { + pub fn add_utxo(&self, outpoint: bitcoin::OutPoint, value: Amount) -> TxOut { let public_key = bitcoin::PublicKey::new(self.secret_key.public_key(&self.secp)); let utxo = Utxo::new_p2pkh(outpoint, value, &public_key.pubkey_hash()); self.utxos.borrow_mut().push(utxo.clone()); @@ -1496,15 +1535,15 @@ impl WalletSource for TestWalletSource { Ok(ScriptBuf::new_p2pkh(&public_key.pubkey_hash())) } - fn sign_psbt(&self, psbt: PartiallySignedTransaction) -> Result { - let mut tx = psbt.extract_tx(); + fn sign_psbt(&self, psbt: Psbt) -> Result { + let mut tx = psbt.extract_tx_unchecked_fee_rate(); 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_raw_hash()).into(), &self.secret_key); + let sig = self.secp.sign_ecdsa(&secp256k1::Message::from_digest(sighash.to_byte_array()), &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.serialize())