use crate::chain::transaction::OutPoint;
use crate::sign;
use crate::events;
+use crate::events::bump_transaction::{WalletSource, Utxo};
use crate::ln::channelmanager;
use crate::ln::features::{ChannelFeatures, InitFeatures, NodeFeatures};
use crate::ln::{msgs, wire};
use crate::util::logger::{Logger, Level, Record};
use crate::util::ser::{Readable, ReadableArgs, Writer, Writeable};
+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::block::Block;
use bitcoin::network::constants::Network;
use bitcoin::hash_types::{BlockHash, Txid};
+use bitcoin::util::sighash::SighashCache;
use bitcoin::secp256k1::{SecretKey, PublicKey, Secp256k1, ecdsa::Signature, Scalar};
use bitcoin::secp256k1::ecdh::SharedSecret;
use bitcoin::secp256k1::ecdsa::RecoverableSignature;
+#[cfg(any(test, feature = "_test_utils"))]
use regex;
use crate::io;
use crate::prelude::*;
use core::cell::RefCell;
+use core::ops::DerefMut;
use core::time::Duration;
use crate::sync::{Mutex, Arc};
use core::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
impl<'a> Router for TestRouter<'a> {
fn find_route(
&self, payer: &PublicKey, params: &RouteParameters, first_hops: Option<&[&channelmanager::ChannelDetails]>,
- inflight_htlcs: &InFlightHtlcs
+ inflight_htlcs: InFlightHtlcs
) -> Result<Route, msgs::LightningError> {
if let Some((find_route_query, find_route_res)) = self.next_routes.lock().unwrap().pop_front() {
assert_eq!(find_route_query, *params);
if let Ok(ref route) = find_route_res {
- let locked_scorer = self.scorer.lock().unwrap();
- let scorer = ScorerAccountingForInFlightHtlcs::new(locked_scorer, inflight_htlcs);
+ let mut binding = self.scorer.lock().unwrap();
+ let scorer = ScorerAccountingForInFlightHtlcs::new(binding.deref_mut(), &inflight_htlcs);
for path in &route.paths {
let mut aggregate_msat = 0u64;
for (idx, hop) in path.hops.iter().rev().enumerate() {
// 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);
+ scorer.channel_penalty_msat(hop.short_channel_id, &NodeId::from_pubkey(payer), &NodeId::from_pubkey(&hop.pubkey), usage, &());
} 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);
+ 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, &());
}
}
}
return find_route_res;
}
let logger = TestLogger::new();
- let scorer = self.scorer.lock().unwrap();
find_route(
payer, params, &self.network_graph, first_hops, &logger,
- &ScorerAccountingForInFlightHtlcs::new(scorer, &inflight_htlcs),
+ &ScorerAccountingForInFlightHtlcs::new(self.scorer.lock().unwrap().deref_mut(), &inflight_htlcs), &(),
&[42; 32]
)
}
}
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 bitcoin::LockTime::from(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);
+ fn broadcast_transactions(&self, txs: &[&Transaction]) {
+ for tx in txs {
+ let lock_time = tx.lock_time.0;
+ 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 {
+ 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());
+ let owned_txs: Vec<Transaction> = txs.iter().map(|tx| (*tx).clone()).collect();
+ self.txn_broadcasted.lock().unwrap().extend(owned_txs);
}
}
pub pending_events: Mutex<Vec<events::MessageSendEvent>>,
expected_recv_msgs: Mutex<Option<Vec<wire::Message<()>>>>,
connected_peers: Mutex<HashSet<PublicKey>>,
+ pub message_fetch_counter: AtomicUsize,
+ genesis_hash: ChainHash,
}
impl TestChannelMessageHandler {
- pub fn new() -> Self {
+ pub fn new(genesis_hash: ChainHash) -> Self {
TestChannelMessageHandler {
pending_events: Mutex::new(Vec::new()),
expected_recv_msgs: Mutex::new(None),
connected_peers: Mutex::new(HashSet::new()),
+ message_fetch_counter: AtomicUsize::new(0),
+ genesis_hash,
}
}
fn provided_init_features(&self, _their_init_features: &PublicKey) -> InitFeatures {
channelmanager::provided_init_features(&UserConfig::default())
}
+
+ fn get_genesis_hashes(&self) -> Option<Vec<ChainHash>> {
+ Some(vec![self.genesis_hash])
+ }
+
+ fn handle_open_channel_v2(&self, _their_node_id: &PublicKey, msg: &msgs::OpenChannelV2) {
+ self.received_msg(wire::Message::OpenChannelV2(msg.clone()));
+ }
+
+ fn handle_accept_channel_v2(&self, _their_node_id: &PublicKey, msg: &msgs::AcceptChannelV2) {
+ self.received_msg(wire::Message::AcceptChannelV2(msg.clone()));
+ }
+
+ fn handle_tx_add_input(&self, _their_node_id: &PublicKey, msg: &msgs::TxAddInput) {
+ self.received_msg(wire::Message::TxAddInput(msg.clone()));
+ }
+
+ fn handle_tx_add_output(&self, _their_node_id: &PublicKey, msg: &msgs::TxAddOutput) {
+ self.received_msg(wire::Message::TxAddOutput(msg.clone()));
+ }
+
+ fn handle_tx_remove_input(&self, _their_node_id: &PublicKey, msg: &msgs::TxRemoveInput) {
+ self.received_msg(wire::Message::TxRemoveInput(msg.clone()));
+ }
+
+ fn handle_tx_remove_output(&self, _their_node_id: &PublicKey, msg: &msgs::TxRemoveOutput) {
+ self.received_msg(wire::Message::TxRemoveOutput(msg.clone()));
+ }
+
+ fn handle_tx_complete(&self, _their_node_id: &PublicKey, msg: &msgs::TxComplete) {
+ self.received_msg(wire::Message::TxComplete(msg.clone()));
+ }
+
+ fn handle_tx_signatures(&self, _their_node_id: &PublicKey, msg: &msgs::TxSignatures) {
+ self.received_msg(wire::Message::TxSignatures(msg.clone()));
+ }
+
+ fn handle_tx_init_rbf(&self, _their_node_id: &PublicKey, msg: &msgs::TxInitRbf) {
+ self.received_msg(wire::Message::TxInitRbf(msg.clone()));
+ }
+
+ fn handle_tx_ack_rbf(&self, _their_node_id: &PublicKey, msg: &msgs::TxAckRbf) {
+ self.received_msg(wire::Message::TxAckRbf(msg.clone()));
+ }
+
+ fn handle_tx_abort(&self, _their_node_id: &PublicKey, msg: &msgs::TxAbort) {
+ self.received_msg(wire::Message::TxAbort(msg.clone()));
+ }
}
impl events::MessageSendEventsProvider for TestChannelMessageHandler {
fn get_and_clear_pending_msg_events(&self) -> Vec<events::MessageSendEvent> {
+ self.message_fetch_counter.fetch_add(1, Ordering::AcqRel);
let mut pending_events = self.pending_events.lock().unwrap();
let mut ret = Vec::new();
mem::swap(&mut ret, &mut *pending_events);
/// 1. belong to the specified module and
/// 2. match the given regex pattern.
/// Assert that the number of occurrences equals the given `count`
+ #[cfg(any(test, feature = "_test_utils"))]
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)| {
fn log(&self, record: &Record) {
*self.lines.lock().unwrap().entry((record.module_path.to_string(), format!("{}", record.args))).or_insert(0) += 1;
if record.level >= self.level {
- #[cfg(feature = "std")]
+ #[cfg(all(not(ldk_bench), feature = "std"))]
println!("{:<5} {} [{} : {}, {}] {}", record.level.to_string(), self.id, record.module_path, record.file, record.line, record.args);
}
}
}
impl Score for TestScorer {
+ type ScoreParams = ();
fn channel_penalty_msat(
- &self, short_channel_id: u64, _source: &NodeId, _target: &NodeId, usage: ChannelUsage
+ &self, short_channel_id: u64, _source: &NodeId, _target: &NodeId, usage: ChannelUsage, _score_params: &Self::ScoreParams
) -> u64 {
if let Some(scorer_expectations) = self.scorer_expectations.borrow_mut().as_mut() {
match scorer_expectations.pop_front() {
}
}
}
+
+pub struct TestWalletSource {
+ secret_key: SecretKey,
+ utxos: RefCell<Vec<Utxo>>,
+ secp: Secp256k1<bitcoin::secp256k1::All>,
+}
+
+impl TestWalletSource {
+ pub fn new(secret_key: SecretKey) -> Self {
+ Self {
+ secret_key,
+ utxos: RefCell::new(Vec::new()),
+ secp: Secp256k1::new(),
+ }
+ }
+
+ pub fn add_utxo(&self, outpoint: bitcoin::OutPoint, value: u64) -> 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());
+ utxo.output
+ }
+
+ pub fn add_custom_utxo(&self, utxo: Utxo) -> TxOut {
+ let output = utxo.output.clone();
+ self.utxos.borrow_mut().push(utxo);
+ output
+ }
+
+ pub fn remove_utxo(&self, outpoint: bitcoin::OutPoint) {
+ self.utxos.borrow_mut().retain(|utxo| utxo.outpoint != outpoint);
+ }
+}
+
+impl WalletSource for TestWalletSource {
+ fn list_confirmed_utxos(&self) -> Result<Vec<Utxo>, ()> {
+ Ok(self.utxos.borrow().clone())
+ }
+
+ fn get_change_script(&self) -> Result<Script, ()> {
+ let public_key = bitcoin::PublicKey::new(self.secret_key.public_key(&self.secp));
+ Ok(Script::new_p2pkh(&public_key.pubkey_hash()))
+ }
+
+ fn sign_tx(&self, mut tx: Transaction) -> Result<Transaction, ()> {
+ 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();
+ tx.input[i].script_sig = Builder::new()
+ .push_slice(&bitcoin_sig)
+ .push_slice(&self.secret_key.public_key(&self.secp).serialize())
+ .into_script();
+ }
+ }
+ Ok(tx)
+ }
+}