X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning%2Fsrc%2Fevents%2Fbump_transaction.rs;h=35e9da60544ddc680535983767117e9faf732afd;hb=a8fa5a1685d84d3b07732e4ecc04e2dc7ba06a00;hp=bf56a84ca469b3bac5b4bff53eac17134800e251;hpb=ba8af2280de6ee906f4d3cc5185884d9faf31e66;p=rust-lightning diff --git a/lightning/src/events/bump_transaction.rs b/lightning/src/events/bump_transaction.rs index bf56a84c..35e9da60 100644 --- a/lightning/src/events/bump_transaction.rs +++ b/lightning/src/events/bump_transaction.rs @@ -14,7 +14,7 @@ use alloc::collections::BTreeMap; use core::ops::Deref; -use crate::chain::chaininterface::{BroadcasterInterface, compute_feerate_sat_per_1000_weight, fee_for_weight, FEERATE_FLOOR_SATS_PER_KW}; +use crate::chain::chaininterface::{BroadcasterInterface, fee_for_weight}; use crate::chain::ClaimId; use crate::io_extras::sink; use crate::ln::channel::ANCHOR_OUTPUT_VALUE_SATOSHI; @@ -26,7 +26,7 @@ use crate::ln::chan_utils::{ use crate::ln::features::ChannelTypeFeatures; use crate::ln::PaymentPreimage; use crate::prelude::*; -use crate::sign::{ChannelSigner, EcdsaChannelSigner, SignerProvider}; +use crate::sign::{EcdsaChannelSigner, SignerProvider, WriteableEcdsaChannelSigner}; use crate::sync::Mutex; use crate::util::logger::Logger; @@ -57,6 +57,12 @@ pub struct ChannelDerivationParameters { pub transaction_parameters: ChannelTransactionParameters, } +impl_writeable_tlv_based!(ChannelDerivationParameters, { + (0, value_satoshis, required), + (2, keys_id, required), + (4, transaction_parameters, required), +}); + /// A descriptor used to sign for a commitment transaction's anchor output. #[derive(Clone, Debug, PartialEq, Eq)] pub struct AnchorDescriptor { @@ -102,9 +108,9 @@ impl AnchorDescriptor { } /// Derives the channel signer required to sign the anchor input. - pub fn derive_channel_signer(&self, signer_provider: &SP) -> ::Signer + pub fn derive_channel_signer(&self, signer_provider: &SP) -> S where - SP::Target: SignerProvider + SP::Target: SignerProvider { let mut signer = signer_provider.derive_channel_signer( self.channel_derivation_parameters.value_satoshis, @@ -139,6 +145,16 @@ pub struct HTLCDescriptor { pub counterparty_sig: Signature } +impl_writeable_tlv_based!(HTLCDescriptor, { + (0, channel_derivation_parameters, required), + (2, commitment_txid, required), + (4, per_commitment_number, required), + (6, per_commitment_point, required), + (8, htlc, required), + (10, preimage, option), + (12, counterparty_sig, required), +}); + impl HTLCDescriptor { /// Returns the outpoint of the HTLC output in the commitment transaction. This is the outpoint /// being spent by the HTLC input in the HTLC transaction. @@ -211,9 +227,9 @@ impl HTLCDescriptor { } /// Derives the channel signer required to sign the HTLC input. - pub fn derive_channel_signer(&self, signer_provider: &SP) -> ::Signer + pub fn derive_channel_signer(&self, signer_provider: &SP) -> S where - SP::Target: SignerProvider + SP::Target: SignerProvider { let mut signer = signer_provider.derive_channel_signer( self.channel_derivation_parameters.value_satoshis, @@ -341,6 +357,7 @@ pub enum BumpTransactionEvent { /// An input that must be included in a transaction when performing coin selection through /// [`CoinSelectionSource::select_confirmed_utxos`]. It is guaranteed to be a SegWit input, so it /// must have an empty [`TxIn::script_sig`] when spent. +#[derive(Clone, Debug, Hash, PartialOrd, Ord, PartialEq, Eq)] pub struct Input { /// The unique identifier of the input. pub outpoint: OutPoint, @@ -354,7 +371,7 @@ pub struct Input { /// An unspent transaction output that is available to spend resulting from a successful /// [`CoinSelection`] attempt. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Hash, PartialOrd, Ord, PartialEq, Eq)] pub struct Utxo { /// The unique identifier of the output. pub outpoint: OutPoint, @@ -421,6 +438,7 @@ impl Utxo { /// The result of a successful coin selection attempt for a transaction requiring additional UTXOs /// to cover its fees. +#[derive(Clone, Debug)] pub struct CoinSelection { /// The set of UTXOs (with at least 1 confirmation) to spend and use within a transaction /// requiring additional fees. @@ -464,12 +482,12 @@ pub trait CoinSelectionSource { /// which UTXOs to double spend is left to the implementation, but it must strive to keep the /// set of other claims being double spent to a minimum. fn select_confirmed_utxos( - &self, claim_id: ClaimId, must_spend: &[Input], must_pay_to: &[TxOut], + &self, claim_id: ClaimId, must_spend: Vec, must_pay_to: &[TxOut], target_feerate_sat_per_1000_weight: u32, ) -> Result; /// Signs and provides the full witness for all inputs within the transaction known to the /// trait (i.e., any provided via [`CoinSelectionSource::select_confirmed_utxos`]). - fn sign_tx(&self, tx: &mut Transaction) -> Result<(), ()>; + fn sign_tx(&self, tx: Transaction) -> Result; } /// An alternative to [`CoinSelectionSource`] that can be implemented and used along [`Wallet`] to @@ -483,7 +501,7 @@ pub trait WalletSource { /// Signs and provides the full [`TxIn::script_sig`] and [`TxIn::witness`] for all inputs within /// the transaction known to the wallet (i.e., any provided via /// [`WalletSource::list_confirmed_utxos`]). - fn sign_tx(&self, tx: &mut Transaction) -> Result<(), ()>; + fn sign_tx(&self, tx: Transaction) -> Result; } /// A wrapper over [`WalletSource`] that implements [`CoinSelection`] by preferring UTXOs that would @@ -524,7 +542,7 @@ where fn select_confirmed_utxos_internal( &self, utxos: &[Utxo], claim_id: ClaimId, force_conflicting_utxo_spend: bool, tolerate_high_network_feerates: bool, target_feerate_sat_per_1000_weight: u32, - preexisting_tx_weight: u64, target_amount_sat: u64, + preexisting_tx_weight: u64, input_amount_sat: u64, target_amount_sat: u64, ) -> Result { let mut locked_utxos = self.locked_utxos.lock().unwrap(); let mut eligible_utxos = utxos.iter().filter_map(|utxo| { @@ -551,7 +569,7 @@ where }).collect::>(); eligible_utxos.sort_unstable_by_key(|(utxo, _)| utxo.output.value); - let mut selected_amount = 0; + let mut selected_amount = input_amount_sat; let mut total_fees = 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 { @@ -600,7 +618,7 @@ where L::Target: Logger { fn select_confirmed_utxos( - &self, claim_id: ClaimId, must_spend: &[Input], must_pay_to: &[TxOut], + &self, claim_id: ClaimId, must_spend: Vec, must_pay_to: &[TxOut], target_feerate_sat_per_1000_weight: u32, ) -> Result { let utxos = self.source.list_confirmed_utxos()?; @@ -614,13 +632,14 @@ where 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: u64 = 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| { 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, target_amount_sat, + target_feerate_sat_per_1000_weight, preexisting_tx_weight, input_amount_sat, target_amount_sat, ) }; do_coin_selection(false, false) @@ -629,7 +648,7 @@ where .or_else(|_| do_coin_selection(true, true)) } - fn sign_tx(&self, tx: &mut Transaction) -> Result<(), ()> { + fn sign_tx(&self, tx: Transaction) -> Result { self.source.sign_tx(tx) } } @@ -706,27 +725,22 @@ where 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 compute its feerate and subtract it from the package target, using the result - // as the target feerate for our anchor transaction. Unfortunately, this results in users - // overpaying by a small margin since we don't yet know the anchor transaction size, and - // avoiding the small overpayment only makes our API even more complex. - let commitment_tx_sat_per_1000_weight: u32 = compute_feerate_sat_per_1000_weight( - commitment_tx_fee_sat, commitment_tx.weight() as u64, - ); - let anchor_target_feerate_sat_per_1000_weight = core::cmp::max( - package_target_feerate_sat_per_1000_weight - commitment_tx_sat_per_1000_weight, - FEERATE_FLOOR_SATS_PER_KW, - ); - - log_debug!(self.logger, "Peforming coin selection for anchor transaction targeting {} sat/kW", - anchor_target_feerate_sat_per_1000_weight); + // account. We do so by pretending the commitment tranasction's fee and weight are part of + // the anchor input. + let mut anchor_utxo = anchor_descriptor.previous_utxo(); + anchor_utxo.value += commitment_tx_fee_sat; let must_spend = vec![Input { outpoint: anchor_descriptor.outpoint, - previous_utxo: anchor_descriptor.previous_utxo(), + previous_utxo: anchor_utxo, satisfaction_weight: commitment_tx.weight() as u64 + ANCHOR_INPUT_WITNESS_WEIGHT + EMPTY_SCRIPT_SIG_WEIGHT, }]; + #[cfg(debug_assertions)] + let must_spend_amount = must_spend.iter().map(|input| input.previous_utxo.value).sum::(); + + log_debug!(self.logger, "Peforming coin selection for commitment package (commitment and anchor transaction) targeting {} sat/kW", + package_target_feerate_sat_per_1000_weight); let coin_selection = self.utxo_source.select_confirmed_utxos( - claim_id, &must_spend, &[], anchor_target_feerate_sat_per_1000_weight, + claim_id, must_spend, &[], package_target_feerate_sat_per_1000_weight, )?; let mut anchor_tx = Transaction { @@ -735,10 +749,13 @@ where input: vec![anchor_descriptor.unsigned_tx_input()], output: vec![], }; + + #[cfg(debug_assertions)] + let total_satisfaction_weight = ANCHOR_INPUT_WITNESS_WEIGHT + EMPTY_SCRIPT_SIG_WEIGHT + + coin_selection.confirmed_utxos.iter().map(|utxo| utxo.satisfaction_weight).sum::(); #[cfg(debug_assertions)] - let total_satisfaction_weight = - coin_selection.confirmed_utxos.iter().map(|utxo| utxo.satisfaction_weight).sum::() + - ANCHOR_INPUT_WITNESS_WEIGHT + EMPTY_SCRIPT_SIG_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.txid(); @@ -748,7 +765,8 @@ where let unsigned_tx_weight = anchor_tx.weight() as u64 - (anchor_tx.input.len() as u64 * EMPTY_SCRIPT_SIG_WEIGHT); log_debug!(self.logger, "Signing anchor transaction {}", anchor_txid); - self.utxo_source.sign_tx(&mut anchor_tx)?; + anchor_tx = self.utxo_source.sign_tx(anchor_tx)?; + let signer = anchor_descriptor.derive_channel_signer(&self.signer_provider); let anchor_sig = signer.sign_holder_anchor_input(&anchor_tx, 0, &self.secp)?; anchor_tx.input[0].witness = anchor_descriptor.tx_input_witness(&anchor_sig); @@ -760,6 +778,16 @@ where // never underestimate. assert!(expected_signed_tx_weight >= signed_tx_weight && expected_signed_tx_weight - (expected_signed_tx_weight / 100) <= signed_tx_weight); + + let expected_package_fee = fee_for_weight(package_target_feerate_sat_per_1000_weight, + signed_tx_weight + commitment_tx.weight() as u64); + let package_fee = total_input_amount - + anchor_tx.output.iter().map(|output| output.value).sum::(); + // Our fee should be within a 5% error margin of the expected fee based on the + // feerate and transaction weight and we should never pay less than required. + let fee_error_margin = expected_package_fee * 5 / 100; + assert!(package_fee >= expected_package_fee && + package_fee - fee_error_margin <= expected_package_fee); } log_info!(self.logger, "Broadcasting anchor transaction {} to bump channel close with txid {}", @@ -799,20 +827,32 @@ where log_debug!(self.logger, "Peforming 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).sum::(); + let coin_selection = 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::(); #[cfg(debug_assertions)] - let total_satisfaction_weight = - coin_selection.confirmed_utxos.iter().map(|utxo| utxo.satisfaction_weight).sum::() + - must_spend.iter().map(|input| input.satisfaction_weight).sum::(); + let total_input_amount = must_spend_amount + + coin_selection.confirmed_utxos.iter().map(|utxo| utxo.output.value).sum::(); + self.process_coin_selection(&mut htlc_tx, coin_selection); #[cfg(debug_assertions)] let unsigned_tx_weight = htlc_tx.weight() as u64 - (htlc_tx.input.len() as u64 * EMPTY_SCRIPT_SIG_WEIGHT); log_debug!(self.logger, "Signing HTLC transaction {}", htlc_tx.txid()); - self.utxo_source.sign_tx(&mut htlc_tx)?; + htlc_tx = self.utxo_source.sign_tx(htlc_tx)?; + let mut signers = BTreeMap::new(); for (idx, htlc_descriptor) in htlc_descriptors.iter().enumerate() { let signer = signers.entry(htlc_descriptor.channel_derivation_parameters.keys_id) @@ -829,6 +869,15 @@ where // never underestimate. assert!(expected_signed_tx_weight >= signed_tx_weight && expected_signed_tx_weight - (expected_signed_tx_weight / 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).sum::(); + // Our fee should be within a 5% error margin of the expected fee based on the + // feerate and transaction weight and we should never pay less than required. + let fee_error_margin = expected_signed_tx_fee * 5 / 100; + assert!(signed_tx_fee >= expected_signed_tx_fee && + signed_tx_fee - fee_error_margin <= expected_signed_tx_fee); } log_info!(self.logger, "Broadcasting {}", log_tx!(htlc_tx));