diff --git a/src/event.rs b/src/event.rs index d02634d9b..c92661af4 100644 --- a/src/event.rs +++ b/src/event.rs @@ -22,7 +22,7 @@ use lightning::util::errors::APIError; use lightning::util::ser::{Readable, ReadableArgs, Writeable, Writer}; use bitcoin::secp256k1::{PublicKey, Secp256k1}; -use bitcoin::OutPoint; +use bitcoin::{LockTime, OutPoint, PackedLockTime}; use rand::{thread_rng, Rng}; use std::collections::VecDeque; use std::ops::Deref; @@ -292,11 +292,16 @@ where // channel. let confirmation_target = ConfirmationTarget::Normal; + // We set nLockTime to the current height to discourage fee sniping. + let cur_height = self.channel_manager.current_best_block().height(); + let locktime = LockTime::from_height(cur_height).unwrap_or(LockTime::ZERO); + // Sign the final funding transaction and broadcast it. match self.wallet.create_funding_transaction( output_script, channel_value_satoshis, confirmation_target, + locktime, ) { Ok(final_tx) => { // Give the funding transaction back to LDK for opening the channel. @@ -552,13 +557,20 @@ where let output_descriptors = &outputs.iter().collect::>(); let tx_feerate = self.wallet.get_est_sat_per_1000_weight(ConfirmationTarget::Normal); + + // We set nLockTime to the current height to discourage fee sniping. + let cur_height = self.channel_manager.current_best_block().height(); + let locktime: PackedLockTime = + LockTime::from_height(cur_height).map_or(PackedLockTime::ZERO, |l| l.into()); let res = self.keys_manager.spend_spendable_outputs( output_descriptors, Vec::new(), destination_address.script_pubkey(), tx_feerate, + Some(locktime), &Secp256k1::new(), ); + match res { Ok(spending_tx) => self.wallet.broadcast_transactions(&[&spending_tx]), Err(err) => { diff --git a/src/wallet.rs b/src/wallet.rs index d7eb0f3c7..57dd4dc38 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -22,7 +22,7 @@ use bitcoin::bech32::u5; use bitcoin::secp256k1::ecdh::SharedSecret; use bitcoin::secp256k1::ecdsa::{RecoverableSignature, Signature}; use bitcoin::secp256k1::{PublicKey, Scalar, Secp256k1, Signing}; -use bitcoin::{Script, Transaction, TxOut, Txid}; +use bitcoin::{LockTime, PackedLockTime, Script, Transaction, TxOut, Txid}; use std::collections::HashMap; use std::sync::{Arc, Condvar, Mutex, RwLock}; @@ -157,13 +157,18 @@ where pub(crate) fn create_funding_transaction( &self, output_script: Script, value_sats: u64, confirmation_target: ConfirmationTarget, + locktime: LockTime, ) -> Result { let fee_rate = self.estimate_fee_rate(confirmation_target); let locked_wallet = self.inner.lock().unwrap(); let mut tx_builder = locked_wallet.build_tx(); - tx_builder.add_recipient(output_script, value_sats).fee_rate(fee_rate).enable_rbf(); + tx_builder + .add_recipient(output_script, value_sats) + .fee_rate(fee_rate) + .nlocktime(locktime) + .enable_rbf(); let mut psbt = match tx_builder.finish() { Ok((psbt, _)) => { @@ -363,7 +368,7 @@ where pub fn spend_spendable_outputs( &self, descriptors: &[&SpendableOutputDescriptor], outputs: Vec, change_destination_script: Script, feerate_sat_per_1000_weight: u32, - secp_ctx: &Secp256k1, + locktime: Option, secp_ctx: &Secp256k1, ) -> Result { let only_non_static = &descriptors .iter() @@ -375,6 +380,7 @@ where outputs, change_destination_script, feerate_sat_per_1000_weight, + locktime, secp_ctx, ) }