diff --git a/lightning/src/events/bump_transaction.rs b/lightning/src/events/bump_transaction.rs index a9550a67236..6ecd6075712 100644 --- a/lightning/src/events/bump_transaction.rs +++ b/lightning/src/events/bump_transaction.rs @@ -14,35 +14,38 @@ use alloc::collections::BTreeMap; use core::ops::Deref; -use crate::chain::chaininterface::{BroadcasterInterface, fee_for_weight}; +use crate::chain::chaininterface::{fee_for_weight, BroadcasterInterface}; use crate::chain::ClaimId; use crate::io_extras::sink; -use crate::ln::channel::ANCHOR_OUTPUT_VALUE_SATOSHI; -use crate::ln::types::ChannelId; use crate::ln::chan_utils; use crate::ln::chan_utils::{ - ANCHOR_INPUT_WITNESS_WEIGHT, HTLC_SUCCESS_INPUT_ANCHOR_WITNESS_WEIGHT, - HTLC_TIMEOUT_INPUT_ANCHOR_WITNESS_WEIGHT, HTLCOutputInCommitment, shared_anchor_script_pubkey, + shared_anchor_script_pubkey, HTLCOutputInCommitment, ANCHOR_INPUT_WITNESS_WEIGHT, + HTLC_SUCCESS_INPUT_ANCHOR_WITNESS_WEIGHT, HTLC_TIMEOUT_INPUT_ANCHOR_WITNESS_WEIGHT, }; +use crate::ln::channel::ANCHOR_OUTPUT_VALUE_SATOSHI; +use crate::ln::types::ChannelId; use crate::prelude::*; +use crate::sign::ecdsa::EcdsaChannelSigner; use crate::sign::{ - ChannelDerivationParameters, HTLCDescriptor, SignerProvider, P2WPKH_WITNESS_WEIGHT + ChannelDerivationParameters, HTLCDescriptor, SignerProvider, P2WPKH_WITNESS_WEIGHT, }; -use crate::sign::ecdsa::EcdsaChannelSigner; use crate::sync::Mutex; use crate::util::logger::Logger; -use bitcoin::{OutPoint, Psbt, PubkeyHash, Sequence, ScriptBuf, Transaction, TxIn, TxOut, Witness, WPubkeyHash}; use bitcoin::amount::Amount; +use bitcoin::consensus::Encodable; use bitcoin::constants::WITNESS_SCALE_FACTOR; use bitcoin::locktime::absolute::LockTime; -use bitcoin::consensus::Encodable; use bitcoin::secp256k1; -use bitcoin::secp256k1::{PublicKey, Secp256k1}; use bitcoin::secp256k1::ecdsa::Signature; +use bitcoin::secp256k1::{PublicKey, Secp256k1}; use bitcoin::transaction::Version; +use bitcoin::{ + OutPoint, Psbt, PubkeyHash, ScriptBuf, Sequence, Transaction, TxIn, TxOut, WPubkeyHash, Witness, +}; -pub(crate) const EMPTY_SCRIPT_SIG_WEIGHT: u64 = 1 /* empty script_sig */ * WITNESS_SCALE_FACTOR as u64; +pub(crate) const EMPTY_SCRIPT_SIG_WEIGHT: u64 = + 1 /* empty script_sig */ * WITNESS_SCALE_FACTOR as u64; const BASE_INPUT_SIZE: u64 = 32 /* txid */ + 4 /* vout */ + 4 /* sequence */; @@ -63,18 +66,16 @@ impl AnchorDescriptor { /// [`Self::unsigned_tx_input`]. pub fn previous_utxo(&self) -> TxOut { let tx_params = &self.channel_derivation_parameters.transaction_parameters; - let script_pubkey = - if tx_params.channel_type_features.supports_anchors_zero_fee_htlc_tx() { - let channel_params = tx_params.as_holder_broadcastable(); - chan_utils::get_keyed_anchor_redeemscript(&channel_params.broadcaster_pubkeys().funding_pubkey) - } else { - assert!(tx_params.channel_type_features.supports_anchor_zero_fee_commitments()); - shared_anchor_script_pubkey() - }; - TxOut { - script_pubkey, - value: Amount::from_sat(ANCHOR_OUTPUT_VALUE_SATOSHI), - } + let script_pubkey = if tx_params.channel_type_features.supports_anchors_zero_fee_htlc_tx() { + let channel_params = tx_params.as_holder_broadcastable(); + chan_utils::get_keyed_anchor_redeemscript( + &channel_params.broadcaster_pubkeys().funding_pubkey, + ) + } else { + assert!(tx_params.channel_type_features.supports_anchor_zero_fee_commitments()); + shared_anchor_script_pubkey() + }; + TxOut { script_pubkey, value: Amount::from_sat(ANCHOR_OUTPUT_VALUE_SATOSHI) } } /// Returns the unsigned transaction input spending the anchor output in the commitment @@ -93,8 +94,12 @@ impl AnchorDescriptor { pub fn tx_input_witness(&self, signature: &Signature) -> Witness { let tx_params = &self.channel_derivation_parameters.transaction_parameters; if tx_params.channel_type_features.supports_anchors_zero_fee_htlc_tx() { - let channel_params = self.channel_derivation_parameters.transaction_parameters.as_holder_broadcastable(); - chan_utils::build_keyed_anchor_input_witness(&channel_params.broadcaster_pubkeys().funding_pubkey, signature) + let channel_params = + self.channel_derivation_parameters.transaction_parameters.as_holder_broadcastable(); + chan_utils::build_keyed_anchor_input_witness( + &channel_params.broadcaster_pubkeys().funding_pubkey, + signature, + ) } else { debug_assert!(tx_params.channel_type_features.supports_anchor_zero_fee_commitments()); Witness::from_slice(&[&[]]) @@ -262,11 +267,8 @@ impl Utxo { 33 /* pubkey */; Self { outpoint, - output: TxOut { - value, - script_pubkey: ScriptBuf::new_p2pkh(pubkey_hash), - }, - satisfaction_weight: script_sig_size * WITNESS_SCALE_FACTOR as u64 + 1 /* empty witness */, + output: TxOut { value, script_pubkey: ScriptBuf::new_p2pkh(pubkey_hash) }, + satisfaction_weight: script_sig_size * WITNESS_SCALE_FACTOR as u64 + 1, /* empty witness */ } } @@ -280,9 +282,12 @@ impl Utxo { outpoint, output: TxOut { value, - script_pubkey: ScriptBuf::new_p2sh(&ScriptBuf::new_p2wpkh(pubkey_hash).script_hash()), + script_pubkey: ScriptBuf::new_p2sh( + &ScriptBuf::new_p2wpkh(pubkey_hash).script_hash(), + ), }, - satisfaction_weight: script_sig_size * WITNESS_SCALE_FACTOR as u64 + P2WPKH_WITNESS_WEIGHT, + satisfaction_weight: script_sig_size * WITNESS_SCALE_FACTOR as u64 + + P2WPKH_WITNESS_WEIGHT, } } @@ -290,10 +295,7 @@ impl Utxo { pub fn new_v0_p2wpkh(outpoint: OutPoint, value: Amount, pubkey_hash: &WPubkeyHash) -> Self { Self { outpoint, - output: TxOut { - value, - script_pubkey: ScriptBuf::new_p2wpkh(pubkey_hash), - }, + output: TxOut { value, script_pubkey: ScriptBuf::new_p2wpkh(pubkey_hash) }, satisfaction_weight: EMPTY_SCRIPT_SIG_WEIGHT + P2WPKH_WITNESS_WEIGHT, } } @@ -379,7 +381,7 @@ pub trait WalletSource { pub struct Wallet where W::Target: WalletSource, - L::Target: Logger + L::Target: Logger, { source: W, logger: L, @@ -392,7 +394,7 @@ where impl Wallet where W::Target: WalletSource, - L::Target: Logger + L::Target: Logger, { /// Returns a new instance backed by the given [`WalletSource`] that serves as an implementation /// of [`CoinSelectionSource`]. @@ -414,32 +416,47 @@ where preexisting_tx_weight: u64, input_amount_sat: Amount, target_amount_sat: Amount, ) -> Result { let mut locked_utxos = self.locked_utxos.lock().unwrap(); - let mut eligible_utxos = utxos.iter().filter_map(|utxo| { - if let Some(utxo_claim_id) = locked_utxos.get(&utxo.outpoint) { - if *utxo_claim_id != claim_id && !force_conflicting_utxo_spend { - log_trace!(self.logger, "Skipping UTXO {} to prevent conflicting spend", utxo.outpoint); - return None; + let mut eligible_utxos = utxos + .iter() + .filter_map(|utxo| { + if let Some(utxo_claim_id) = locked_utxos.get(&utxo.outpoint) { + if *utxo_claim_id != claim_id && !force_conflicting_utxo_spend { + log_trace!( + self.logger, + "Skipping UTXO {} to prevent conflicting spend", + utxo.outpoint + ); + return None; + } } - } - let fee_to_spend_utxo = Amount::from_sat(fee_for_weight( - target_feerate_sat_per_1000_weight, BASE_INPUT_WEIGHT + utxo.satisfaction_weight, - )); - let should_spend = if tolerate_high_network_feerates { - utxo.output.value > fee_to_spend_utxo - } else { - utxo.output.value >= fee_to_spend_utxo * 2 - }; - if should_spend { - Some((utxo, fee_to_spend_utxo)) - } else { - log_trace!(self.logger, "Skipping UTXO {} due to dust proximity after spend", utxo.outpoint); - None - } - }).collect::>(); + let fee_to_spend_utxo = Amount::from_sat(fee_for_weight( + target_feerate_sat_per_1000_weight, + BASE_INPUT_WEIGHT + utxo.satisfaction_weight, + )); + let should_spend = if tolerate_high_network_feerates { + utxo.output.value > fee_to_spend_utxo + } else { + utxo.output.value >= fee_to_spend_utxo * 2 + }; + if should_spend { + Some((utxo, fee_to_spend_utxo)) + } else { + log_trace!( + self.logger, + "Skipping UTXO {} due to dust proximity after spend", + utxo.outpoint + ); + None + } + }) + .collect::>(); eligible_utxos.sort_unstable_by_key(|(utxo, _)| utxo.output.value); let mut selected_amount = input_amount_sat; - let mut total_fees = Amount::from_sat(fee_for_weight(target_feerate_sat_per_1000_weight, preexisting_tx_weight)); + let mut total_fees = Amount::from_sat(fee_for_weight( + target_feerate_sat_per_1000_weight, + preexisting_tx_weight, + )); let mut selected_utxos = Vec::new(); for (utxo, fee_to_spend_utxo) in eligible_utxos { if selected_amount >= target_amount_sat + total_fees { @@ -450,8 +467,11 @@ where selected_utxos.push(utxo.clone()); } if selected_amount < target_amount_sat + total_fees { - log_debug!(self.logger, "Insufficient funds to meet target feerate {} sat/kW", - target_feerate_sat_per_1000_weight); + log_debug!( + self.logger, + "Insufficient funds to meet target feerate {} sat/kW", + target_feerate_sat_per_1000_weight + ); return Err(()); } for utxo in &selected_utxos { @@ -463,10 +483,11 @@ where let change_script = self.source.get_change_script()?; let change_output_fee = fee_for_weight( target_feerate_sat_per_1000_weight, - (8 /* value */ + change_script.consensus_encode(&mut sink()).unwrap() as u64) * - WITNESS_SCALE_FACTOR as u64, + (8 /* value */ + change_script.consensus_encode(&mut sink()).unwrap() as u64) + * WITNESS_SCALE_FACTOR as u64, ); - let change_output_amount = Amount::from_sat(remaining_amount.to_sat().saturating_sub(change_output_fee)); + let change_output_amount = + Amount::from_sat(remaining_amount.to_sat().saturating_sub(change_output_fee)); let change_output = if change_output_amount < change_script.minimal_non_dust() { log_debug!(self.logger, "Coin selection attempt did not yield change output"); None @@ -474,17 +495,14 @@ where Some(TxOut { script_pubkey: change_script, value: change_output_amount }) }; - Ok(CoinSelection { - confirmed_utxos: selected_utxos, - change_output, - }) + Ok(CoinSelection { confirmed_utxos: selected_utxos, change_output }) } } impl CoinSelectionSource for Wallet where W::Target: WalletSource, - L::Target: Logger + L::Target: Logger, { fn select_confirmed_utxos( &self, claim_id: ClaimId, must_spend: Vec, must_pay_to: &[TxOut], @@ -493,22 +511,32 @@ where let utxos = self.source.list_confirmed_utxos()?; // TODO: Use fee estimation utils when we upgrade to bitcoin v0.30.0. const BASE_TX_SIZE: u64 = 4 /* version */ + 1 /* input count */ + 1 /* output count */ + 4 /* locktime */; - let total_output_size: u64 = must_pay_to.iter().map(|output| - 8 /* value */ + 1 /* script len */ + output.script_pubkey.len() as u64 - ).sum(); - let total_satisfaction_weight: u64 = must_spend.iter().map(|input| input.satisfaction_weight).sum(); - let total_input_weight = (BASE_INPUT_WEIGHT * must_spend.len() as u64) + total_satisfaction_weight; + let total_output_size: u64 = must_pay_to + .iter() + .map(|output| 8 /* value */ + 1 /* script len */ + output.script_pubkey.len() as u64) + .sum(); + let total_satisfaction_weight: u64 = + must_spend.iter().map(|input| input.satisfaction_weight).sum(); + let total_input_weight = + (BASE_INPUT_WEIGHT * must_spend.len() as u64) + total_satisfaction_weight; let preexisting_tx_weight = 2 /* segwit marker & flag */ + total_input_weight + ((BASE_TX_SIZE + total_output_size) * WITNESS_SCALE_FACTOR as u64); let input_amount_sat = must_spend.iter().map(|input| input.previous_utxo.value).sum(); let target_amount_sat = must_pay_to.iter().map(|output| output.value).sum(); - let do_coin_selection = |force_conflicting_utxo_spend: bool, tolerate_high_network_feerates: bool| { + let do_coin_selection = |force_conflicting_utxo_spend: bool, + tolerate_high_network_feerates: bool| { log_debug!(self.logger, "Attempting coin selection targeting {} sat/kW (force_conflicting_utxo_spend = {}, tolerate_high_network_feerates = {})", target_feerate_sat_per_1000_weight, force_conflicting_utxo_spend, tolerate_high_network_feerates); self.select_confirmed_utxos_internal( - &utxos, claim_id, force_conflicting_utxo_spend, tolerate_high_network_feerates, - target_feerate_sat_per_1000_weight, preexisting_tx_weight, input_amount_sat, target_amount_sat, + &utxos, + claim_id, + force_conflicting_utxo_spend, + tolerate_high_network_feerates, + target_feerate_sat_per_1000_weight, + preexisting_tx_weight, + input_amount_sat, + target_amount_sat, ) }; do_coin_selection(false, false) @@ -552,13 +580,7 @@ where /// /// [`Event::BumpTransaction`]: crate::events::Event::BumpTransaction pub fn new(broadcaster: B, utxo_source: C, signer_provider: SP, logger: L) -> Self { - Self { - broadcaster, - utxo_source, - signer_provider, - logger, - secp: Secp256k1::new(), - } + Self { broadcaster, utxo_source, signer_provider, logger, secp: Secp256k1::new() } } /// Updates a transaction with the result of a successful coin selection attempt. @@ -605,7 +627,8 @@ where /// broadcasts them to the network as a package. fn handle_channel_close( &self, claim_id: ClaimId, package_target_feerate_sat_per_1000_weight: u32, - commitment_tx: &Transaction, commitment_tx_fee_sat: u64, anchor_descriptor: &AnchorDescriptor, + commitment_tx: &Transaction, commitment_tx_fee_sat: u64, + anchor_descriptor: &AnchorDescriptor, ) -> Result<(), ()> { // Our commitment transaction already has fees allocated to it, so we should take them into // account. We do so by pretending the commitment transaction's fee and weight are part of @@ -624,12 +647,16 @@ where previous_utxo: anchor_utxo.clone(), satisfaction_weight: package_and_fixed_input_satisfaction_weight, }]; - let must_spend_amount = must_spend.iter().map(|input| input.previous_utxo.value).sum::(); + let must_spend_amount = + must_spend.iter().map(|input| input.previous_utxo.value).sum::(); log_debug!(self.logger, "Performing coin selection for commitment package (commitment and anchor transaction) targeting {} sat/kW", package_target_feerate_sat_per_1000_weight); let coin_selection: CoinSelection = self.utxo_source.select_confirmed_utxos( - claim_id, must_spend, &[], package_target_feerate_sat_per_1000_weight, + claim_id, + must_spend, + &[], + package_target_feerate_sat_per_1000_weight, )?; let mut anchor_tx = Transaction { @@ -639,10 +666,12 @@ where output: vec![], }; - let total_satisfaction_weight = ANCHOR_INPUT_WITNESS_WEIGHT + EMPTY_SCRIPT_SIG_WEIGHT + - coin_selection.confirmed_utxos.iter().map(|utxo| utxo.satisfaction_weight).sum::(); - let total_input_amount = must_spend_amount + - coin_selection.confirmed_utxos.iter().map(|utxo| utxo.output.value).sum(); + let input_satisfaction_weight: u64 = + coin_selection.confirmed_utxos.iter().map(|utxo| utxo.satisfaction_weight).sum(); + let total_satisfaction_weight = + ANCHOR_INPUT_WITNESS_WEIGHT + EMPTY_SCRIPT_SIG_WEIGHT + input_satisfaction_weight; + let total_input_amount = must_spend_amount + + coin_selection.confirmed_utxos.iter().map(|utxo| utxo.output.value).sum(); self.process_coin_selection(&mut anchor_tx, &coin_selection); let anchor_txid = anchor_tx.compute_txid(); @@ -655,27 +684,38 @@ where for (idx, utxo) in coin_selection.confirmed_utxos.into_iter().enumerate() { // add 1 to skip the anchor input let index = idx + 1; - debug_assert_eq!(anchor_psbt.unsigned_tx.input[index].previous_output, utxo.outpoint); + debug_assert_eq!( + anchor_psbt.unsigned_tx.input[index].previous_output, + utxo.outpoint + ); if utxo.output.script_pubkey.is_witness_program() { anchor_psbt.inputs[index].witness_utxo = Some(utxo.output); } } debug_assert_eq!(anchor_psbt.unsigned_tx.output.len(), 1); - let unsigned_tx_weight = anchor_psbt.unsigned_tx.weight().to_wu() - (anchor_psbt.unsigned_tx.input.len() as u64 * EMPTY_SCRIPT_SIG_WEIGHT); + let unsigned_tx_weight = anchor_psbt.unsigned_tx.weight().to_wu() + - (anchor_psbt.unsigned_tx.input.len() as u64 * EMPTY_SCRIPT_SIG_WEIGHT); - let package_fee = total_input_amount - - anchor_psbt.unsigned_tx.output.iter().map(|output| output.value).sum(); + let package_fee = total_input_amount + - anchor_psbt.unsigned_tx.output.iter().map(|output| output.value).sum(); let package_weight = unsigned_tx_weight + 2 /* wit marker */ + total_satisfaction_weight + commitment_tx.weight().to_wu(); - if package_fee.to_sat() * 1000 / package_weight < package_target_feerate_sat_per_1000_weight.into() { + if package_fee.to_sat() * 1000 / package_weight + < package_target_feerate_sat_per_1000_weight.into() + { // On the first iteration of the loop, we may undershoot the target feerate because // we had to add an OP_RETURN output in `process_coin_selection` which we didn't // select sufficient coins for. Here we detect that case and go around again // seeking additional weight. - if package_and_fixed_input_satisfaction_weight == starting_package_and_fixed_input_satisfaction_weight { - debug_assert!(anchor_psbt.unsigned_tx.output[0].script_pubkey.is_op_return(), - "Coin selection failed to select sufficient coins for its change output"); - package_and_fixed_input_satisfaction_weight += anchor_psbt.unsigned_tx.output[0].weight().to_wu(); + if package_and_fixed_input_satisfaction_weight + == starting_package_and_fixed_input_satisfaction_weight + { + debug_assert!( + anchor_psbt.unsigned_tx.output[0].script_pubkey.is_op_return(), + "Coin selection failed to select sufficient coins for its change output" + ); + package_and_fixed_input_satisfaction_weight += + anchor_psbt.unsigned_tx.output[0].weight().to_wu(); continue; } else { debug_assert!(false, "Coin selection failed to select sufficient coins"); @@ -685,28 +725,44 @@ where log_debug!(self.logger, "Signing anchor transaction {}", anchor_txid); anchor_tx = self.utxo_source.sign_psbt(anchor_psbt)?; - let signer = self.signer_provider.derive_channel_signer(anchor_descriptor.channel_derivation_parameters.keys_id); - let channel_parameters = &anchor_descriptor.channel_derivation_parameters.transaction_parameters; - let anchor_sig = signer.sign_holder_keyed_anchor_input(channel_parameters, &anchor_tx, 0, &self.secp)?; + let signer = self + .signer_provider + .derive_channel_signer(anchor_descriptor.channel_derivation_parameters.keys_id); + let channel_parameters = + &anchor_descriptor.channel_derivation_parameters.transaction_parameters; + let anchor_sig = signer.sign_holder_keyed_anchor_input( + channel_parameters, + &anchor_tx, + 0, + &self.secp, + )?; anchor_tx.input[0].witness = anchor_descriptor.tx_input_witness(&anchor_sig); - #[cfg(debug_assertions)] { + #[cfg(debug_assertions)] + { let signed_tx_weight = anchor_tx.weight().to_wu(); - let expected_signed_tx_weight = unsigned_tx_weight + 2 /* wit marker */ + total_satisfaction_weight; + let expected_signed_tx_weight = + unsigned_tx_weight + 2 /* wit marker */ + total_satisfaction_weight; // Our estimate should be within a 1% error margin of the actual weight and we should // never underestimate. - assert!(expected_signed_tx_weight >= signed_tx_weight && - expected_signed_tx_weight - (expected_signed_tx_weight / 100) <= signed_tx_weight); + assert!(expected_signed_tx_weight >= signed_tx_weight); + assert!(expected_signed_tx_weight * 99 / 100 <= signed_tx_weight); - let expected_package_fee = Amount::from_sat(fee_for_weight(package_target_feerate_sat_per_1000_weight, - signed_tx_weight + commitment_tx.weight().to_wu())); + let expected_package_fee = Amount::from_sat(fee_for_weight( + package_target_feerate_sat_per_1000_weight, + signed_tx_weight + commitment_tx.weight().to_wu(), + )); // Our feerate should always be at least what we were seeking. It may overshoot if // the coin selector burned funds to an OP_RETURN without a change output. assert!(package_fee >= expected_package_fee); } - log_info!(self.logger, "Broadcasting anchor transaction {} to bump channel close with txid {}", - anchor_txid, commitment_tx.compute_txid()); + log_info!( + self.logger, + "Broadcasting anchor transaction {} to bump channel close with txid {}", + anchor_txid, + commitment_tx.compute_txid() + ); self.broadcaster.broadcast_transactions(&[&commitment_tx, &anchor_tx]); return Ok(()); } @@ -730,36 +786,48 @@ where must_spend.push(Input { outpoint: htlc_input.previous_output.clone(), previous_utxo: htlc_descriptor.previous_utxo(&self.secp), - satisfaction_weight: EMPTY_SCRIPT_SIG_WEIGHT + if htlc_descriptor.preimage.is_some() { - HTLC_SUCCESS_INPUT_ANCHOR_WITNESS_WEIGHT - } else { - HTLC_TIMEOUT_INPUT_ANCHOR_WITNESS_WEIGHT - }, + satisfaction_weight: EMPTY_SCRIPT_SIG_WEIGHT + + if htlc_descriptor.preimage.is_some() { + HTLC_SUCCESS_INPUT_ANCHOR_WITNESS_WEIGHT + } else { + HTLC_TIMEOUT_INPUT_ANCHOR_WITNESS_WEIGHT + }, }); htlc_tx.input.push(htlc_input); let htlc_output = htlc_descriptor.tx_output(&self.secp); htlc_tx.output.push(htlc_output); } - log_debug!(self.logger, "Performing coin selection for HTLC transaction targeting {} sat/kW", - target_feerate_sat_per_1000_weight); + log_debug!( + self.logger, + "Performing coin selection for HTLC transaction targeting {} sat/kW", + target_feerate_sat_per_1000_weight + ); #[cfg(debug_assertions)] let must_spend_satisfaction_weight = must_spend.iter().map(|input| input.satisfaction_weight).sum::(); #[cfg(debug_assertions)] - let must_spend_amount = must_spend.iter().map(|input| input.previous_utxo.value.to_sat()).sum::(); + let must_spend_amount = + must_spend.iter().map(|input| input.previous_utxo.value.to_sat()).sum::(); let coin_selection: CoinSelection = self.utxo_source.select_confirmed_utxos( - claim_id, must_spend, &htlc_tx.output, target_feerate_sat_per_1000_weight, + claim_id, + must_spend, + &htlc_tx.output, + target_feerate_sat_per_1000_weight, )?; #[cfg(debug_assertions)] - let total_satisfaction_weight = must_spend_satisfaction_weight + - coin_selection.confirmed_utxos.iter().map(|utxo| utxo.satisfaction_weight).sum::(); + let input_satisfaction_weight: u64 = + coin_selection.confirmed_utxos.iter().map(|utxo| utxo.satisfaction_weight).sum(); + #[cfg(debug_assertions)] + let total_satisfaction_weight = must_spend_satisfaction_weight + input_satisfaction_weight; #[cfg(debug_assertions)] - let total_input_amount = must_spend_amount + - coin_selection.confirmed_utxos.iter().map(|utxo| utxo.output.value.to_sat()).sum::(); + let input_value: u64 = + coin_selection.confirmed_utxos.iter().map(|utxo| utxo.output.value.to_sat()).sum(); + #[cfg(debug_assertions)] + let total_input_amount = must_spend_amount + input_value; self.process_coin_selection(&mut htlc_tx, &coin_selection); @@ -767,7 +835,10 @@ where let mut htlc_psbt = Psbt::from_unsigned_tx(htlc_tx).unwrap(); // add witness_utxo to htlc inputs for (i, htlc_descriptor) in htlc_descriptors.iter().enumerate() { - debug_assert_eq!(htlc_psbt.unsigned_tx.input[i].previous_output, htlc_descriptor.outpoint()); + debug_assert_eq!( + htlc_psbt.unsigned_tx.input[i].previous_output, + htlc_descriptor.outpoint() + ); htlc_psbt.inputs[i].witness_utxo = Some(htlc_descriptor.previous_utxo(&self.secp)); } // add witness_utxo to remaining inputs @@ -781,32 +852,42 @@ where } #[cfg(debug_assertions)] - let unsigned_tx_weight = htlc_psbt.unsigned_tx.weight().to_wu() - (htlc_psbt.unsigned_tx.input.len() as u64 * EMPTY_SCRIPT_SIG_WEIGHT); + let unsigned_tx_weight = htlc_psbt.unsigned_tx.weight().to_wu() + - (htlc_psbt.unsigned_tx.input.len() as u64 * EMPTY_SCRIPT_SIG_WEIGHT); - log_debug!(self.logger, "Signing HTLC transaction {}", htlc_psbt.unsigned_tx.compute_txid()); + log_debug!( + self.logger, + "Signing HTLC transaction {}", + htlc_psbt.unsigned_tx.compute_txid() + ); htlc_tx = self.utxo_source.sign_psbt(htlc_psbt)?; let mut signers = BTreeMap::new(); for (idx, htlc_descriptor) in htlc_descriptors.iter().enumerate() { let keys_id = htlc_descriptor.channel_derivation_parameters.keys_id; - let signer = signers.entry(keys_id) + let signer = signers + .entry(keys_id) .or_insert_with(|| self.signer_provider.derive_channel_signer(keys_id)); - let htlc_sig = signer.sign_holder_htlc_transaction(&htlc_tx, idx, htlc_descriptor, &self.secp)?; + let htlc_sig = + signer.sign_holder_htlc_transaction(&htlc_tx, idx, htlc_descriptor, &self.secp)?; let witness_script = htlc_descriptor.witness_script(&self.secp); - htlc_tx.input[idx].witness = htlc_descriptor.tx_input_witness(&htlc_sig, &witness_script); + htlc_tx.input[idx].witness = + htlc_descriptor.tx_input_witness(&htlc_sig, &witness_script); } - #[cfg(debug_assertions)] { + #[cfg(debug_assertions)] + { let signed_tx_weight = htlc_tx.weight().to_wu(); let expected_signed_tx_weight = unsigned_tx_weight + total_satisfaction_weight; // Our estimate should be within a 1% error margin of the actual weight and we should // never underestimate. - assert!(expected_signed_tx_weight >= signed_tx_weight && - expected_signed_tx_weight - (expected_signed_tx_weight / 100) <= signed_tx_weight); + assert!(expected_signed_tx_weight >= signed_tx_weight); + assert!(expected_signed_tx_weight * 99 / 100 <= signed_tx_weight); - let expected_signed_tx_fee = fee_for_weight(target_feerate_sat_per_1000_weight, signed_tx_weight); - let signed_tx_fee = total_input_amount - - htlc_tx.output.iter().map(|output| output.value.to_sat()).sum::(); + let expected_signed_tx_fee = + fee_for_weight(target_feerate_sat_per_1000_weight, signed_tx_weight); + let signed_tx_fee = total_input_amount + - htlc_tx.output.iter().map(|output| output.value.to_sat()).sum::(); // Our feerate should always be at least what we were seeking. It may overshoot if // the coin selector burned funds to an OP_RETURN without a change output. assert!(signed_tx_fee >= expected_signed_tx_fee); @@ -821,31 +902,59 @@ where pub fn handle_event(&self, event: &BumpTransactionEvent) { match event { BumpTransactionEvent::ChannelClose { - claim_id, package_target_feerate_sat_per_1000_weight, commitment_tx, - commitment_tx_fee_satoshis, anchor_descriptor, .. + claim_id, + package_target_feerate_sat_per_1000_weight, + commitment_tx, + commitment_tx_fee_satoshis, + anchor_descriptor, + .. } => { - log_info!(self.logger, "Handling channel close bump (claim_id = {}, commitment_txid = {})", - log_bytes!(claim_id.0), commitment_tx.compute_txid()); + log_info!( + self.logger, + "Handling channel close bump (claim_id = {}, commitment_txid = {})", + log_bytes!(claim_id.0), + commitment_tx.compute_txid() + ); if let Err(_) = self.handle_channel_close( - *claim_id, *package_target_feerate_sat_per_1000_weight, commitment_tx, - *commitment_tx_fee_satoshis, anchor_descriptor, + *claim_id, + *package_target_feerate_sat_per_1000_weight, + commitment_tx, + *commitment_tx_fee_satoshis, + anchor_descriptor, ) { - log_error!(self.logger, "Failed bumping commitment transaction fee for {}", - commitment_tx.compute_txid()); + log_error!( + self.logger, + "Failed bumping commitment transaction fee for {}", + commitment_tx.compute_txid() + ); } - } + }, BumpTransactionEvent::HTLCResolution { - claim_id, target_feerate_sat_per_1000_weight, htlc_descriptors, tx_lock_time, .. + claim_id, + target_feerate_sat_per_1000_weight, + htlc_descriptors, + tx_lock_time, + .. } => { - log_info!(self.logger, "Handling HTLC bump (claim_id = {}, htlcs_to_claim = {})", - log_bytes!(claim_id.0), log_iter!(htlc_descriptors.iter().map(|d| d.outpoint()))); + log_info!( + self.logger, + "Handling HTLC bump (claim_id = {}, htlcs_to_claim = {})", + log_bytes!(claim_id.0), + log_iter!(htlc_descriptors.iter().map(|d| d.outpoint())) + ); if let Err(_) = self.handle_htlc_resolution( - *claim_id, *target_feerate_sat_per_1000_weight, htlc_descriptors, *tx_lock_time, + *claim_id, + *target_feerate_sat_per_1000_weight, + htlc_descriptors, + *tx_lock_time, ) { - log_error!(self.logger, "Failed bumping HTLC transaction fee for commitment {}", - htlc_descriptors[0].commitment_txid); + log_error!( + self.logger, + "Failed bumping HTLC transaction fee for commitment {}", + htlc_descriptors[0].commitment_txid + ); } - } + }, } } } @@ -856,10 +965,10 @@ mod tests { use crate::io::Cursor; use crate::ln::chan_utils::ChannelTransactionParameters; - use crate::util::ser::Readable; - use crate::util::test_utils::{TestBroadcaster, TestLogger}; use crate::sign::KeysManager; use crate::types::features::ChannelTypeFeatures; + use crate::util::ser::Readable; + use crate::util::test_utils::{TestBroadcaster, TestLogger}; use bitcoin::hashes::Hash; use bitcoin::hex::FromHex; @@ -871,11 +980,8 @@ mod tests { } impl CoinSelectionSource for TestCoinSelectionSource { fn select_confirmed_utxos( - &self, - _claim_id: ClaimId, - must_spend: Vec, - _must_pay_to: &[TxOut], - target_feerate_sat_per_1000_weight: u32 + &self, _claim_id: ClaimId, must_spend: Vec, _must_pay_to: &[TxOut], + target_feerate_sat_per_1000_weight: u32, ) -> Result { let mut expected_selects = self.expected_selects.lock().unwrap(); let (weight, value, feerate, res) = expected_selects.remove(0); @@ -918,26 +1024,41 @@ mod tests { // Tx 18032ad172a5f28fa6e16392d6cc57ea47895781434ce15d03766cc47a955fb9 let commitment_tx_bytes = Vec::::from_hex("02000000000101cc6b0a9dd84b52c07340fff6fab002fc37b4bdccfdce9f39c5ec8391a56b652907000000009b948b80044a01000000000000220020b4182433fdfdfbf894897c98f84d92cec815cee222755ffd000ae091c9dadc2d4a01000000000000220020f83f7dbf90e2de325b5bb6bab0ae370151278c6964739242b2e7ce0cb68a5d81cb4a02000000000022002024add256b3dccee772610caef82a601045ab6f98fd6d5df608cc756b891ccfe63ffa490000000000220020894bf32b37906a643625e87131897c3714c71b3ac9b161862c9aa6c8d468b4c70400473044022060abd347bff2cca0212b660e6addff792b3356bd4a1b5b26672dc2e694c3c5f002202b40b7e346b494a7b1d048b4ec33ba99c90a09ab48eb1df64ccdc768066c865c014730440220554d8361e04dc0ee178dcb23d2d23f53ec7a1ae4312a5be76bd9e83ab8981f3d0220501f23ffb18cb81ccea72d30252f88d5e69fd28ba4992803d03c00d06fa8899e0147522102817f6ce189ab7114f89e8d5df58cdbbaf272dc8e71b92982d47456a0b6a0ceee2102c9b4d2f24aca54f65e13f4c83e2a8d8e877e12d3c71a76e81f28a5cabc652aa352ae626c7620").unwrap(); - let commitment_tx: Transaction = Readable::read(&mut Cursor::new(&commitment_tx_bytes)).unwrap(); - let total_commitment_weight = commitment_tx.weight().to_wu() + ANCHOR_INPUT_WITNESS_WEIGHT + EMPTY_SCRIPT_SIG_WEIGHT; + let commitment_tx: Transaction = + Readable::read(&mut Cursor::new(&commitment_tx_bytes)).unwrap(); + let total_commitment_weight = + commitment_tx.weight().to_wu() + ANCHOR_INPUT_WITNESS_WEIGHT + EMPTY_SCRIPT_SIG_WEIGHT; let commitment_and_anchor_fee = 930 + 330; - let op_return_weight = TxOut { - value: Amount::ZERO, - script_pubkey: ScriptBuf::new_op_return(&[0; 3]), - }.weight().to_wu(); + let op_return_weight = + TxOut { value: Amount::ZERO, script_pubkey: ScriptBuf::new_op_return(&[0; 3]) } + .weight() + .to_wu(); let broadcaster = TestBroadcaster::new(Network::Testnet); let source = TestCoinSelectionSource { expected_selects: Mutex::new(vec![ - (total_commitment_weight, commitment_and_anchor_fee, 868, CoinSelection { confirmed_utxos: Vec::new(), change_output: None }), - (total_commitment_weight + op_return_weight, commitment_and_anchor_fee, 868, CoinSelection { - confirmed_utxos: vec![Utxo { - outpoint: OutPoint { txid: Txid::from_byte_array([44; 32]), vout: 0 }, - output: TxOut { value: Amount::from_sat(200), script_pubkey: ScriptBuf::new() }, - satisfaction_weight: 5, // Just the script_sig and witness lengths - }], - change_output: None, - }) + ( + total_commitment_weight, + commitment_and_anchor_fee, + 868, + CoinSelection { confirmed_utxos: Vec::new(), change_output: None }, + ), + ( + total_commitment_weight + op_return_weight, + commitment_and_anchor_fee, + 868, + CoinSelection { + confirmed_utxos: vec![Utxo { + outpoint: OutPoint { txid: Txid::from_byte_array([44; 32]), vout: 0 }, + output: TxOut { + value: Amount::from_sat(200), + script_pubkey: ScriptBuf::new(), + }, + satisfaction_weight: 5, // Just the script_sig and witness lengths + }], + change_output: None, + }, + ), ]), }; let signer = KeysManager::new(&[42; 32], 42, 42); diff --git a/lightning/src/events/mod.rs b/lightning/src/events/mod.rs index 9e92d3b942a..c16305bcca0 100644 --- a/lightning/src/events/mod.rs +++ b/lightning/src/events/mod.rs @@ -19,33 +19,38 @@ pub mod bump_transaction; pub use bump_transaction::BumpTransactionEvent; use crate::blinded_path::message::OffersContext; -use crate::blinded_path::payment::{Bolt12OfferContext, Bolt12RefundContext, PaymentContext, PaymentContextRef}; +use crate::blinded_path::payment::{ + Bolt12OfferContext, Bolt12RefundContext, PaymentContext, PaymentContextRef, +}; use crate::chain::transaction; -use crate::ln::channelmanager::{InterceptId, PaymentId, RecipientOnionFields}; use crate::ln::channel::FUNDING_CONF_DEADLINE_BLOCKS; +use crate::ln::channelmanager::{InterceptId, PaymentId, RecipientOnionFields}; +use crate::ln::types::ChannelId; +use crate::ln::{msgs, LocalHTLCFailureReason}; use crate::offers::invoice::Bolt12Invoice; use crate::offers::static_invoice::StaticInvoice; -use crate::types::features::ChannelTypeFeatures; -use crate::ln::{msgs, LocalHTLCFailureReason}; -use crate::ln::types::ChannelId; -use crate::types::payment::{PaymentPreimage, PaymentHash, PaymentSecret}; use crate::onion_message::messenger::Responder; use crate::routing::gossip::NetworkUpdate; use crate::routing::router::{BlindedTail, Path, RouteHop, RouteParameters}; use crate::sign::SpendableOutputDescriptor; +use crate::types::features::ChannelTypeFeatures; +use crate::types::payment::{PaymentHash, PaymentPreimage, PaymentSecret}; use crate::util::errors::APIError; -use crate::util::ser::{BigSize, FixedLengthReader, Writeable, Writer, MaybeReadable, Readable, RequiredWrapper, UpgradableRequired, WithoutLength}; +use crate::util::ser::{ + BigSize, FixedLengthReader, MaybeReadable, Readable, RequiredWrapper, UpgradableRequired, + WithoutLength, Writeable, Writer, +}; use crate::util::string::UntrustedString; -use bitcoin::{Transaction, OutPoint}; -use bitcoin::script::ScriptBuf; -use bitcoin::hashes::Hash; +use crate::io; +use crate::sync::Arc; use bitcoin::hashes::sha256::Hash as Sha256; +use bitcoin::hashes::Hash; +use bitcoin::script::ScriptBuf; use bitcoin::secp256k1::PublicKey; -use crate::io; -use core::time::Duration; +use bitcoin::{OutPoint, Transaction}; use core::ops::Deref; -use crate::sync::Arc; +use core::time::Duration; #[allow(unused_imports)] use crate::prelude::*; @@ -63,12 +68,12 @@ pub enum FundingInfo { /// The full funding `Transaction`. Tx { /// The funding transaction - transaction: Transaction + transaction: Transaction, }, /// The `OutPoint` of the funding. OutPoint { /// The outpoint of the funding - outpoint: transaction::OutPoint + outpoint: transaction::OutPoint, }, } @@ -81,7 +86,6 @@ impl_writeable_tlv_based_enum!(FundingInfo, } ); - /// Some information provided on receipt of payment depends on whether the payment received is a /// spontaneous payment or a "conventional" lightning payment that's paying an invoice. #[derive(Clone, Debug, PartialEq, Eq)] @@ -182,22 +186,15 @@ impl PaymentPurpose { /// Errors when provided an `AsyncBolt12OfferContext`, see below. pub(crate) fn from_parts( payment_preimage: Option, payment_secret: PaymentSecret, - payment_context: Option + payment_context: Option, ) -> Result { match payment_context { - None => { - Ok(PaymentPurpose::Bolt11InvoicePayment { - payment_preimage, - payment_secret, - }) - }, - Some(PaymentContext::Bolt12Offer(context)) => { - Ok(PaymentPurpose::Bolt12OfferPayment { - payment_preimage, - payment_secret, - payment_context: context, - }) - }, + None => Ok(PaymentPurpose::Bolt11InvoicePayment { payment_preimage, payment_secret }), + Some(PaymentContext::Bolt12Offer(context)) => Ok(PaymentPurpose::Bolt12OfferPayment { + payment_preimage, + payment_secret, + payment_context: context, + }), Some(PaymentContext::Bolt12Refund(context)) => { Ok(PaymentPurpose::Bolt12RefundPayment { payment_preimage, @@ -210,7 +207,7 @@ impl PaymentPurpose { // using the invoice request provided in the payment onion prior to calling this method. debug_assert!(false); Err(()) - } + }, } } } @@ -337,7 +334,7 @@ pub enum ClosureReason { /// /// [`ChannelManager::force_close_broadcasting_latest_txn`]: crate::ln::channelmanager::ChannelManager::force_close_broadcasting_latest_txn. /// [`ChannelManager::force_close_without_broadcasting_txn`]: crate::ln::channelmanager::ChannelManager::force_close_without_broadcasting_txn. - broadcasted_latest_txn: Option + broadcasted_latest_txn: Option, }, /// The channel was closed after negotiating a cooperative close and we've now broadcasted /// the cooperative close transaction. Note the shutdown may have been initiated by us. @@ -418,30 +415,56 @@ impl core::fmt::Display for ClosureReason { ClosureReason::HolderForceClosed { broadcasted_latest_txn } => { f.write_str("user force-closed the channel")?; if let Some(brodcasted) = broadcasted_latest_txn { - write!(f, " and {} the latest transaction", if *brodcasted { "broadcasted" } else { "elected not to broadcast" }) + write!( + f, + " and {} the latest transaction", + if *brodcasted { "broadcasted" } else { "elected not to broadcast" } + ) } else { Ok(()) } }, - ClosureReason::LegacyCooperativeClosure => f.write_str("the channel was cooperatively closed"), - ClosureReason::CounterpartyInitiatedCooperativeClosure => f.write_str("the channel was cooperatively closed by our peer"), - ClosureReason::LocallyInitiatedCooperativeClosure => f.write_str("the channel was cooperatively closed by us"), - ClosureReason::CommitmentTxConfirmed => f.write_str("commitment or closing transaction was confirmed on chain."), - ClosureReason::FundingTimedOut => write!(f, "funding transaction failed to confirm within {} blocks", FUNDING_CONF_DEADLINE_BLOCKS), + ClosureReason::LegacyCooperativeClosure => { + f.write_str("the channel was cooperatively closed") + }, + ClosureReason::CounterpartyInitiatedCooperativeClosure => { + f.write_str("the channel was cooperatively closed by our peer") + }, + ClosureReason::LocallyInitiatedCooperativeClosure => { + f.write_str("the channel was cooperatively closed by us") + }, + ClosureReason::CommitmentTxConfirmed => { + f.write_str("commitment or closing transaction was confirmed on chain.") + }, + ClosureReason::FundingTimedOut => write!( + f, + "funding transaction failed to confirm within {} blocks", + FUNDING_CONF_DEADLINE_BLOCKS + ), ClosureReason::ProcessingError { err } => { f.write_str("of an exception: ")?; f.write_str(&err) }, - ClosureReason::DisconnectedPeer => f.write_str("the peer disconnected prior to the channel being funded"), - ClosureReason::OutdatedChannelManager => f.write_str("the ChannelManager read from disk was stale compared to ChannelMonitor(s)"), - ClosureReason::CounterpartyCoopClosedUnfundedChannel => f.write_str("the peer requested the unfunded channel be closed"), - ClosureReason::FundingBatchClosure => f.write_str("another channel in the same funding batch closed"), + ClosureReason::DisconnectedPeer => { + f.write_str("the peer disconnected prior to the channel being funded") + }, + ClosureReason::OutdatedChannelManager => f.write_str( + "the ChannelManager read from disk was stale compared to ChannelMonitor(s)", + ), + ClosureReason::CounterpartyCoopClosedUnfundedChannel => { + f.write_str("the peer requested the unfunded channel be closed") + }, + ClosureReason::FundingBatchClosure => { + f.write_str("another channel in the same funding batch closed") + }, ClosureReason::HTLCsTimedOut => f.write_str("htlcs on the channel timed out"), - ClosureReason::PeerFeerateTooLow { peer_feerate_sat_per_kw, required_feerate_sat_per_kw } => - f.write_fmt(format_args!( - "peer provided a feerate ({} sat/kw) which was below our lower bound ({} sat/kw)", - peer_feerate_sat_per_kw, required_feerate_sat_per_kw, - )), + ClosureReason::PeerFeerateTooLow { + peer_feerate_sat_per_kw, + required_feerate_sat_per_kw, + } => f.write_fmt(format_args!( + "peer provided a feerate ({} sat/kw) which was below our lower bound ({} sat/kw)", + peer_feerate_sat_per_kw, required_feerate_sat_per_kw, + )), } } } @@ -493,7 +516,7 @@ pub enum HTLCHandlingFailureType { /// In LDK v0.2.0 and greater, this variant replaces [`Self::UnknownNextHop`]. InvalidForward { /// Short channel id we are requesting to forward an HTLC to. - requested_forward_scid: u64 + requested_forward_scid: u64, }, /// We couldn't decode the incoming onion to obtain the forwarding details. InvalidOnion, @@ -509,7 +532,7 @@ pub enum HTLCHandlingFailureType { /// recipient for a payment. Receive { /// The payment hash of the payment we attempted to process. - payment_hash: PaymentHash + payment_hash: PaymentHash, }, } @@ -560,9 +583,7 @@ impl From for HTLCHandlingFailureReason { /// will be added for general-purpose HTLC forward intercepts as well as trampoline forward /// intercepts in upcoming work. enum InterceptNextHop { - FakeScid { - requested_next_hop_scid: u64, - }, + FakeScid { requested_next_hop_scid: u64 }, } impl_writeable_tlv_based_enum!(InterceptNextHop, @@ -586,13 +607,22 @@ pub enum PaymentFailureReason { /// /// [`ChannelManager::abandon_payment`]: crate::ln::channelmanager::ChannelManager::abandon_payment UserAbandoned, - #[cfg_attr(feature = "std", doc = "We exhausted all of our retry attempts while trying to send the payment, or we")] + #[cfg_attr( + feature = "std", + doc = "We exhausted all of our retry attempts while trying to send the payment, or we" + )] #[cfg_attr(feature = "std", doc = "exhausted the [`Retry::Timeout`] if the user set one.")] - #[cfg_attr(not(feature = "std"), doc = "We exhausted all of our retry attempts while trying to send the payment.")] + #[cfg_attr( + not(feature = "std"), + doc = "We exhausted all of our retry attempts while trying to send the payment." + )] /// If at any point a retry attempt failed while being forwarded along the path, an [`Event::PaymentPathFailed`] will /// have come before this. #[cfg_attr(feature = "std", doc = "")] - #[cfg_attr(feature = "std", doc = "[`Retry::Timeout`]: crate::ln::channelmanager::Retry::Timeout")] + #[cfg_attr( + feature = "std", + doc = "[`Retry::Timeout`]: crate::ln::channelmanager::Retry::Timeout" + )] RetriesExhausted, /// Either the BOLT 12 invoice was expired by the time we received it or the payment expired while /// retrying based on the provided [`PaymentParameters::expiry_time`]. @@ -1495,7 +1525,7 @@ pub enum Event { /// The reason that the HTLC failed. /// /// This field will be `None` only for objects serialized prior to LDK 0.2.0. - failure_reason: Option + failure_reason: Option, }, /// Indicates that a transaction originating from LDK needs to have its fee bumped. This event /// requires confirmed external funds to be readily available to spend. @@ -1541,7 +1571,7 @@ pub enum Event { /// The node id of the peer we just connected to, who advertises support for /// onion messages. peer_node_id: PublicKey, - } + }, } impl Writeable for Event { @@ -1552,9 +1582,16 @@ impl Writeable for Event { // We never write out FundingGenerationReady events as, upon disconnection, peers // drop any channels which have not yet exchanged funding_signed. }, - &Event::PaymentClaimable { ref payment_hash, ref amount_msat, counterparty_skimmed_fee_msat, - ref purpose, ref receiver_node_id, ref via_channel_ids, - ref claim_deadline, ref onion_fields, ref payment_id, + &Event::PaymentClaimable { + ref payment_hash, + ref amount_msat, + counterparty_skimmed_fee_msat, + ref purpose, + ref receiver_node_id, + ref via_channel_ids, + ref claim_deadline, + ref onion_fields, + ref payment_id, } => { 1u8.write(writer)?; let mut payment_secret = None; @@ -1562,20 +1599,25 @@ impl Writeable for Event { let mut payment_context = None; match &purpose { PaymentPurpose::Bolt11InvoicePayment { - payment_preimage: preimage, payment_secret: secret + payment_preimage: preimage, + payment_secret: secret, } => { payment_secret = Some(secret); payment_preimage = *preimage; }, PaymentPurpose::Bolt12OfferPayment { - payment_preimage: preimage, payment_secret: secret, payment_context: context + payment_preimage: preimage, + payment_secret: secret, + payment_context: context, } => { payment_secret = Some(secret); payment_preimage = *preimage; payment_context = Some(PaymentContextRef::Bolt12Offer(context)); }, PaymentPurpose::Bolt12RefundPayment { - payment_preimage: preimage, payment_secret: secret, payment_context: context + payment_preimage: preimage, + payment_secret: secret, + payment_context: context, } => { payment_secret = Some(secret); payment_preimage = *preimage; @@ -1583,15 +1625,19 @@ impl Writeable for Event { }, PaymentPurpose::SpontaneousPayment(preimage) => { payment_preimage = Some(*preimage); - } + }, } - let skimmed_fee_opt = if counterparty_skimmed_fee_msat == 0 { None } - else { Some(counterparty_skimmed_fee_msat) }; - - let (via_channel_id_legacy, via_user_channel_id_legacy) = match via_channel_ids.last() { - Some((chan_id, user_chan_id)) => (Some(*chan_id), *user_chan_id), - None => (None, None), + let skimmed_fee_opt = if counterparty_skimmed_fee_msat == 0 { + None + } else { + Some(counterparty_skimmed_fee_msat) }; + + let (via_channel_id_legacy, via_user_channel_id_legacy) = + match via_channel_ids.last() { + Some((chan_id, user_chan_id)) => (Some(*chan_id), *user_chan_id), + None => (None, None), + }; write_tlv_fields!(writer, { (0, payment_hash, required), (1, receiver_node_id, option), @@ -1613,7 +1659,14 @@ impl Writeable for Event { (15, *via_channel_ids, optional_vec), }); }, - &Event::PaymentSent { ref payment_id, ref payment_preimage, ref payment_hash, ref amount_msat, ref fee_paid_msat, ref bolt12_invoice } => { + &Event::PaymentSent { + ref payment_id, + ref payment_preimage, + ref payment_hash, + ref amount_msat, + ref fee_paid_msat, + ref bolt12_invoice, + } => { 2u8.write(writer)?; write_tlv_fields!(writer, { (0, payment_preimage, required), @@ -1625,8 +1678,12 @@ impl Writeable for Event { }); }, &Event::PaymentPathFailed { - ref payment_id, ref payment_hash, ref payment_failed_permanently, ref failure, - ref path, ref short_channel_id, + ref payment_id, + ref payment_hash, + ref payment_failed_permanently, + ref failure, + ref path, + ref short_channel_id, #[cfg(any(test, feature = "_test_utils"))] ref error_code, #[cfg(any(test, feature = "_test_utils"))] @@ -1662,7 +1719,13 @@ impl Writeable for Event { (1, channel_id, option), }); }, - &Event::HTLCIntercepted { requested_next_hop_scid, payment_hash, inbound_amount_msat, expected_outbound_amount_msat, intercept_id } => { + &Event::HTLCIntercepted { + requested_next_hop_scid, + payment_hash, + inbound_amount_msat, + expected_outbound_amount_msat, + intercept_id, + } => { 6u8.write(writer)?; let intercept_scid = InterceptNextHop::FakeScid { requested_next_hop_scid }; write_tlv_fields!(writer, { @@ -1672,11 +1735,18 @@ impl Writeable for Event { (6, inbound_amount_msat, required), (8, expected_outbound_amount_msat, required), }); - } + }, &Event::PaymentForwarded { - prev_channel_id, next_channel_id, prev_user_channel_id, next_user_channel_id, - prev_node_id, next_node_id, total_fee_earned_msat, skimmed_fee_msat, - claim_from_onchain_tx, outbound_amount_forwarded_msat, + prev_channel_id, + next_channel_id, + prev_user_channel_id, + next_user_channel_id, + prev_node_id, + next_node_id, + total_fee_earned_msat, + skimmed_fee_msat, + claim_from_onchain_tx, + outbound_amount_forwarded_msat, } => { 7u8.write(writer)?; write_tlv_fields!(writer, { @@ -1692,8 +1762,13 @@ impl Writeable for Event { (15, next_node_id, option), }); }, - &Event::ChannelClosed { ref channel_id, ref user_channel_id, ref reason, - ref counterparty_node_id, ref channel_capacity_sats, ref channel_funding_txo, + &Event::ChannelClosed { + ref channel_id, + ref user_channel_id, + ref reason, + ref counterparty_node_id, + ref channel_capacity_sats, + ref channel_funding_txo, ref last_local_balance_msat, } => { 9u8.write(writer)?; @@ -1746,21 +1821,25 @@ impl Writeable for Event { None => &None, // Variants available prior to version 0.0.124. Some(PaymentFailureReason::RecipientRejected) - | Some(PaymentFailureReason::UserAbandoned) - | Some(PaymentFailureReason::RetriesExhausted) - | Some(PaymentFailureReason::PaymentExpired) - | Some(PaymentFailureReason::RouteNotFound) - | Some(PaymentFailureReason::UnexpectedError) => reason, + | Some(PaymentFailureReason::UserAbandoned) + | Some(PaymentFailureReason::RetriesExhausted) + | Some(PaymentFailureReason::PaymentExpired) + | Some(PaymentFailureReason::RouteNotFound) + | Some(PaymentFailureReason::UnexpectedError) => reason, // Variants introduced at version 0.0.124 or later. Prior versions fail to parse // unknown variants, while versions 0.0.124 or later will use None. - Some(PaymentFailureReason::UnknownRequiredFeatures) => - &Some(PaymentFailureReason::RecipientRejected), - Some(PaymentFailureReason::InvoiceRequestExpired) => - &Some(PaymentFailureReason::RetriesExhausted), - Some(PaymentFailureReason::InvoiceRequestRejected) => - &Some(PaymentFailureReason::RecipientRejected), - Some(PaymentFailureReason::BlindedPathCreationFailed) => + Some(PaymentFailureReason::UnknownRequiredFeatures) => { + &Some(PaymentFailureReason::RecipientRejected) + }, + Some(PaymentFailureReason::InvoiceRequestExpired) => { + &Some(PaymentFailureReason::RetriesExhausted) + }, + Some(PaymentFailureReason::InvoiceRequestRejected) => { + &Some(PaymentFailureReason::RecipientRejected) + }, + Some(PaymentFailureReason::BlindedPathCreationFailed) => { &Some(PaymentFailureReason::RouteNotFound) + }, }; write_tlv_fields!(writer, { (0, payment_id, required), @@ -1775,8 +1854,14 @@ impl Writeable for Event { // We never write the OpenChannelRequest events as, upon disconnection, peers // drop any channels which have not yet exchanged funding_signed. }, - &Event::PaymentClaimed { ref payment_hash, ref amount_msat, ref purpose, - ref receiver_node_id, ref htlcs, ref sender_intended_total_msat, ref onion_fields, + &Event::PaymentClaimed { + ref payment_hash, + ref amount_msat, + ref purpose, + ref receiver_node_id, + ref htlcs, + ref sender_intended_total_msat, + ref onion_fields, ref payment_id, } => { 19u8.write(writer)?; @@ -1800,7 +1885,12 @@ impl Writeable for Event { (6, path.blinded_tail, option), }) }, - &Event::ProbeFailed { ref payment_id, ref payment_hash, ref path, ref short_channel_id } => { + &Event::ProbeFailed { + ref payment_id, + ref payment_hash, + ref path, + ref short_channel_id, + } => { 23u8.write(writer)?; write_tlv_fields!(writer, { (0, payment_id, required), @@ -1810,7 +1900,11 @@ impl Writeable for Event { (8, path.blinded_tail, option), }) }, - &Event::HTLCHandlingFailed { ref prev_channel_id, ref failure_type, ref failure_reason } => { + &Event::HTLCHandlingFailed { + ref prev_channel_id, + ref failure_type, + ref failure_reason, + } => { 25u8.write(writer)?; write_tlv_fields!(writer, { (0, prev_channel_id, required), @@ -1818,17 +1912,22 @@ impl Writeable for Event { (2, failure_type, required), }) }, - &Event::BumpTransaction(ref event)=> { + &Event::BumpTransaction(ref event) => { 27u8.write(writer)?; match event { // We never write the ChannelClose|HTLCResolution events as they'll be replayed // upon restarting anyway if they remain unresolved. - BumpTransactionEvent::ChannelClose { .. } => {} - BumpTransactionEvent::HTLCResolution { .. } => {} + BumpTransactionEvent::ChannelClose { .. } => {}, + BumpTransactionEvent::HTLCResolution { .. } => {}, } write_tlv_fields!(writer, {}); // Write a length field for forwards compat - } - &Event::ChannelReady { ref channel_id, ref user_channel_id, ref counterparty_node_id, ref channel_type } => { + }, + &Event::ChannelReady { + ref channel_id, + ref user_channel_id, + ref counterparty_node_id, + ref channel_type, + } => { 29u8.write(writer)?; write_tlv_fields!(writer, { (0, channel_id, required), @@ -1837,9 +1936,13 @@ impl Writeable for Event { (6, channel_type, required), }); }, - &Event::ChannelPending { ref channel_id, ref user_channel_id, - ref former_temporary_channel_id, ref counterparty_node_id, ref funding_txo, - ref channel_type + &Event::ChannelPending { + ref channel_id, + ref user_channel_id, + ref former_temporary_channel_id, + ref counterparty_node_id, + ref funding_txo, + ref channel_type, } => { 31u8.write(writer)?; write_tlv_fields!(writer, { @@ -1877,7 +1980,13 @@ impl Writeable for Event { (6, responder, option), }); }, - &Event::FundingTxBroadcastSafe { ref channel_id, ref user_channel_id, ref funding_txo, ref counterparty_node_id, ref former_temporary_channel_id} => { + &Event::FundingTxBroadcastSafe { + ref channel_id, + ref user_channel_id, + ref funding_txo, + ref counterparty_node_id, + ref former_temporary_channel_id, + } => { 43u8.write(writer)?; write_tlv_fields!(writer, { (0, channel_id, required), @@ -1932,21 +2041,29 @@ impl MaybeReadable for Event { (15, via_channel_ids_opt, optional_vec), }); let purpose = match payment_secret { - Some(secret) => PaymentPurpose::from_parts(payment_preimage, secret, payment_context) - .map_err(|()| msgs::DecodeError::InvalidValue)?, - None if payment_preimage.is_some() => PaymentPurpose::SpontaneousPayment(payment_preimage.unwrap()), + Some(secret) => { + PaymentPurpose::from_parts(payment_preimage, secret, payment_context) + .map_err(|()| msgs::DecodeError::InvalidValue)? + }, + None if payment_preimage.is_some() => { + PaymentPurpose::SpontaneousPayment(payment_preimage.unwrap()) + }, None => return Err(msgs::DecodeError::InvalidValue), }; let via_channel_ids = via_channel_ids_opt - .or_else(|| via_channel_id_legacy.map(|chan_id| vec![(chan_id, via_user_channel_id_legacy)])) + .or_else(|| { + via_channel_id_legacy + .map(|chan_id| vec![(chan_id, via_user_channel_id_legacy)]) + }) .unwrap_or_default(); Ok(Some(Event::PaymentClaimable { receiver_node_id, payment_hash, amount_msat, - counterparty_skimmed_fee_msat: counterparty_skimmed_fee_msat_opt.unwrap_or(0), + counterparty_skimmed_fee_msat: counterparty_skimmed_fee_msat_opt + .unwrap_or(0), purpose, via_channel_ids, claim_deadline, @@ -1973,7 +2090,9 @@ impl MaybeReadable for Event { (9, bolt12_invoice, option), }); if payment_hash.is_none() { - payment_hash = Some(PaymentHash(Sha256::hash(&payment_preimage.0[..]).to_byte_array())); + payment_hash = Some(PaymentHash( + Sha256::hash(&payment_preimage.0[..]).to_byte_array(), + )); } Ok(Some(Event::PaymentSent { payment_id, @@ -2012,7 +2131,8 @@ impl MaybeReadable for Event { (11, payment_id, option), (13, failure_opt, upgradable_option), }); - let failure = failure_opt.unwrap_or_else(|| PathFailure::OnPath { network_update }); + let failure = + failure_opt.unwrap_or_else(|| PathFailure::OnPath { network_update }); Ok(Some(Event::PaymentPathFailed { payment_id, payment_hash, @@ -2044,7 +2164,8 @@ impl MaybeReadable for Event { 6u8 => { let mut payment_hash = PaymentHash([0; 32]); let mut intercept_id = InterceptId([0; 32]); - let mut requested_next_hop_scid = InterceptNextHop::FakeScid { requested_next_hop_scid: 0 }; + let mut requested_next_hop_scid = + InterceptNextHop::FakeScid { requested_next_hop_scid: 0 }; let mut inbound_amount_msat = 0; let mut expected_outbound_amount_msat = 0; read_tlv_fields!(reader, { @@ -2055,7 +2176,7 @@ impl MaybeReadable for Event { (8, expected_outbound_amount_msat, required), }); let next_scid = match requested_next_hop_scid { - InterceptNextHop::FakeScid { requested_next_hop_scid: scid } => scid + InterceptNextHop::FakeScid { requested_next_hop_scid: scid } => scid, }; Ok(Some(Event::HTLCIntercepted { payment_hash, @@ -2090,9 +2211,15 @@ impl MaybeReadable for Event { (15, next_node_id, option), }); Ok(Some(Event::PaymentForwarded { - prev_channel_id, next_channel_id, prev_user_channel_id, - next_user_channel_id, prev_node_id, next_node_id, - total_fee_earned_msat, skimmed_fee_msat, claim_from_onchain_tx, + prev_channel_id, + next_channel_id, + prev_user_channel_id, + next_user_channel_id, + prev_node_id, + next_node_id, + total_fee_earned_msat, + skimmed_fee_msat, + claim_from_onchain_tx, outbound_amount_forwarded_msat, })) }; @@ -2122,12 +2249,17 @@ impl MaybeReadable for Event { // `user_channel_id` used to be a single u64 value. In order to remain // backwards compatible with versions prior to 0.0.113, the u128 is serialized // as two separate u64 values. - let user_channel_id = (user_channel_id_low_opt.unwrap_or(0) as u128) + - ((user_channel_id_high_opt.unwrap_or(0) as u128) << 64); + let user_channel_id = (user_channel_id_low_opt.unwrap_or(0) as u128) + + ((user_channel_id_high_opt.unwrap_or(0) as u128) << 64); Ok(Some(Event::ChannelClosed { - channel_id, user_channel_id, reason: _init_tlv_based_struct_field!(reason, upgradable_required), - counterparty_node_id, channel_capacity_sats, channel_funding_txo, last_local_balance_msat, + channel_id, + user_channel_id, + reason: _init_tlv_based_struct_field!(reason, upgradable_required), + counterparty_node_id, + channel_capacity_sats, + channel_funding_txo, + last_local_balance_msat, })) }; f() @@ -2148,7 +2280,7 @@ impl MaybeReadable for Event { } else { funding_info.ok_or(msgs::DecodeError::InvalidValue)? }; - Ok(Some(Event::DiscardFunding { channel_id, funding_info } )) + Ok(Some(Event::DiscardFunding { channel_id, funding_info })) }; f() }, @@ -2279,14 +2411,22 @@ impl MaybeReadable for Event { // If a legacy HTLCHandlingFailureType::UnknownNextHop was written, upgrade // it to its new representation, otherwise leave unchanged. - if let Some(HTLCHandlingFailureType::UnknownNextHop { requested_forward_scid }) = failure_type_opt.0 { - failure_type_opt.0 = Some(HTLCHandlingFailureType::InvalidForward { requested_forward_scid }); + if let Some(HTLCHandlingFailureType::UnknownNextHop { + requested_forward_scid, + }) = failure_type_opt.0 + { + failure_type_opt.0 = Some(HTLCHandlingFailureType::InvalidForward { + requested_forward_scid, + }); failure_reason = Some(LocalHTLCFailureReason::UnknownNextPeer.into()); } Ok(Some(Event::HTLCHandlingFailed { prev_channel_id, - failure_type: _init_tlv_based_struct_field!(failure_type_opt, upgradable_required), - failure_reason + failure_type: _init_tlv_based_struct_field!( + failure_type_opt, + upgradable_required + ), + failure_reason, })) }; f() @@ -2309,7 +2449,7 @@ impl MaybeReadable for Event { channel_id, user_channel_id, counterparty_node_id: counterparty_node_id.0.unwrap(), - channel_type: channel_type.0.unwrap() + channel_type: channel_type.0.unwrap(), })) }; f() @@ -2365,7 +2505,8 @@ impl MaybeReadable for Event { (2, message, required), }); Ok(Some(Event::OnionMessageIntercepted { - peer_node_id: peer_node_id.0.unwrap(), message: message.0.unwrap() + peer_node_id: peer_node_id.0.unwrap(), + message: message.0.unwrap(), })) }; f() @@ -2376,7 +2517,7 @@ impl MaybeReadable for Event { (0, peer_node_id, required), }); Ok(Some(Event::OnionMessagePeerConnected { - peer_node_id: peer_node_id.0.unwrap() + peer_node_id: peer_node_id.0.unwrap(), })) }; f() @@ -2430,10 +2571,11 @@ impl MaybeReadable for Event { // exactly the number of bytes specified, ignoring them entirely. let tlv_len: BigSize = Readable::read(reader)?; FixedLengthReader::new(reader, tlv_len.0) - .eat_remaining().map_err(|_| msgs::DecodeError::ShortRead)?; + .eat_remaining() + .map_err(|_| msgs::DecodeError::ShortRead)?; Ok(None) }, - _ => Err(msgs::DecodeError::InvalidValue) + _ => Err(msgs::DecodeError::InvalidValue), } } } @@ -2482,7 +2624,9 @@ pub trait EventsProvider { /// Processes any events generated since the last call using the given event handler. /// /// See the trait-level documentation for requirements. - fn process_pending_events(&self, handler: H) where H::Target: EventHandler; + fn process_pending_events(&self, handler: H) + where + H::Target: EventHandler; } /// An error type that may be returned to LDK in order to safely abort event handling if it can't @@ -2505,7 +2649,10 @@ pub trait EventHandler { fn handle_event(&self, event: Event) -> Result<(), ReplayEvent>; } -impl EventHandler for F where F: Fn(Event) -> Result<(), ReplayEvent> { +impl EventHandler for F +where + F: Fn(Event) -> Result<(), ReplayEvent>, +{ fn handle_event(&self, event: Event) -> Result<(), ReplayEvent> { self(event) } @@ -2526,7 +2673,7 @@ pub enum PaidBolt12Invoice { /// The Static invoice, used in the async payment specification update proposal, /// where the user cannot perform proof of payment. StaticInvoice(StaticInvoice), - } +} impl_writeable_tlv_based_enum!(PaidBolt12Invoice, {0, Bolt12Invoice} => (), diff --git a/rustfmt_excluded_files b/rustfmt_excluded_files index fceda5042f0..5d8e47b2501 100644 --- a/rustfmt_excluded_files +++ b/rustfmt_excluded_files @@ -5,8 +5,6 @@ lightning/src/chain/mod.rs lightning/src/chain/onchaintx.rs lightning/src/chain/package.rs lightning/src/chain/transaction.rs -lightning/src/events/bump_transaction.rs -lightning/src/events/mod.rs lightning/src/lib.rs lightning/src/ln/async_signer_tests.rs lightning/src/ln/blinded_payment_tests.rs