X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning%2Fsrc%2Fchain%2Fpackage.rs;h=1307ad0eb3f1a23bc7eed7f613eb61900cbbcb64;hb=384c4dc7753e4b7ac53ea380e52809babd8f0f9b;hp=3b6ebb4223ce9febe42516ad41c26a4bc38efcdc;hpb=492b24059ea278e7d99d68f5a7422ccd5abec7eb;p=rust-lightning diff --git a/lightning/src/chain/package.rs b/lightning/src/chain/package.rs index 3b6ebb42..1307ad0e 100644 --- a/lightning/src/chain/package.rs +++ b/lightning/src/chain/package.rs @@ -20,20 +20,22 @@ use bitcoin::hash_types::Txid; use bitcoin::secp256k1::{SecretKey,PublicKey}; -use ln::PaymentPreimage; -use ln::chan_utils::{TxCreationKeys, HTLCOutputInCommitment}; -use ln::chan_utils; -use ln::msgs::DecodeError; -use chain::chaininterface::{FeeEstimator, ConfirmationTarget, MIN_RELAY_FEE_SAT_PER_1000_WEIGHT}; -use chain::keysinterface::Sign; -use chain::onchaintx::OnchainTxHandler; -use util::byte_utils; -use util::logger::Logger; -use util::ser::{Readable, Writer, Writeable}; - -use io; -use prelude::*; +use crate::ln::PaymentPreimage; +use crate::ln::chan_utils::{TxCreationKeys, HTLCOutputInCommitment}; +use crate::ln::chan_utils; +use crate::ln::msgs::DecodeError; +use crate::chain::chaininterface::{FeeEstimator, ConfirmationTarget, MIN_RELAY_FEE_SAT_PER_1000_WEIGHT}; +use crate::chain::keysinterface::Sign; +use crate::chain::onchaintx::OnchainTxHandler; +use crate::util::byte_utils; +use crate::util::logger::Logger; +use crate::util::ser::{Readable, Writer, Writeable}; + +use crate::io; +use crate::prelude::*; use core::cmp; +#[cfg(anchors)] +use core::convert::TryInto; use core::mem; use core::ops::Deref; use bitcoin::{PackedLockTime, Sequence, Witness}; @@ -286,14 +288,16 @@ impl_writeable_tlv_based!(HolderHTLCOutput, { #[derive(Clone, PartialEq, Eq)] pub(crate) struct HolderFundingOutput { funding_redeemscript: Script, + funding_amount: Option, opt_anchors: Option<()>, } impl HolderFundingOutput { - pub(crate) fn build(funding_redeemscript: Script, opt_anchors: bool) -> Self { + pub(crate) fn build(funding_redeemscript: Script, funding_amount: u64, opt_anchors: bool) -> Self { HolderFundingOutput { funding_redeemscript, + funding_amount: Some(funding_amount), opt_anchors: if opt_anchors { Some(()) } else { None }, } } @@ -306,6 +310,7 @@ impl HolderFundingOutput { impl_writeable_tlv_based!(HolderFundingOutput, { (0, funding_redeemscript, required), (1, opt_anchors, option), + (3, funding_amount, option), }); /// A wrapper encapsulating all in-protocol differing outputs types. @@ -325,15 +330,18 @@ pub(crate) enum PackageSolvingData { impl PackageSolvingData { fn amount(&self) -> u64 { let amt = match self { - PackageSolvingData::RevokedOutput(ref outp) => { outp.amount }, - PackageSolvingData::RevokedHTLCOutput(ref outp) => { outp.amount }, - PackageSolvingData::CounterpartyOfferedHTLCOutput(ref outp) => { outp.htlc.amount_msat / 1000 }, - PackageSolvingData::CounterpartyReceivedHTLCOutput(ref outp) => { outp.htlc.amount_msat / 1000 }, + PackageSolvingData::RevokedOutput(ref outp) => outp.amount, + PackageSolvingData::RevokedHTLCOutput(ref outp) => outp.amount, + PackageSolvingData::CounterpartyOfferedHTLCOutput(ref outp) => outp.htlc.amount_msat / 1000, + PackageSolvingData::CounterpartyReceivedHTLCOutput(ref outp) => outp.htlc.amount_msat / 1000, // Note: Currently, amounts of holder outputs spending witnesses aren't used // as we can't malleate spending package to increase their feerate. This // should change with the remaining anchor output patchset. - PackageSolvingData::HolderHTLCOutput(..) => { unreachable!() }, - PackageSolvingData::HolderFundingOutput(..) => { unreachable!() }, + PackageSolvingData::HolderHTLCOutput(..) => unreachable!(), + PackageSolvingData::HolderFundingOutput(ref outp) => { + debug_assert!(outp.opt_anchors()); + outp.funding_amount.unwrap() + } }; amt } @@ -542,6 +550,9 @@ impl PackageTemplate { pub(crate) fn outpoints(&self) -> Vec<&BitcoinOutPoint> { self.inputs.iter().map(|(o, _)| o).collect() } + pub(crate) fn inputs(&self) -> impl ExactSizeIterator { + self.inputs.iter().map(|(_, i)| i) + } pub(crate) fn split_package(&mut self, split_outp: &BitcoinOutPoint) -> Option { match self.malleability { PackageMalleability::Malleable => { @@ -605,7 +616,7 @@ impl PackageTemplate { } /// Gets the amount of all outptus being spent by this package, only valid for malleable /// packages. - fn package_amount(&self) -> u64 { + pub(crate) fn package_amount(&self) -> u64 { let mut amounts = 0; for (_, outp) in self.inputs.iter() { amounts += outp.amount(); @@ -630,47 +641,46 @@ impl PackageTemplate { let output_weight = (8 + 1 + destination_script.len()) * WITNESS_SCALE_FACTOR; inputs_weight + witnesses_weight + transaction_weight + output_weight } - pub(crate) fn finalize_package(&self, onchain_handler: &mut OnchainTxHandler, value: u64, destination_script: Script, logger: &L) -> Option - where L::Target: Logger, - { - match self.malleability { - PackageMalleability::Malleable => { - let mut bumped_tx = Transaction { - version: 2, - lock_time: PackedLockTime::ZERO, - input: vec![], - output: vec![TxOut { - script_pubkey: destination_script, - value, - }], - }; - for (outpoint, _) in self.inputs.iter() { - bumped_tx.input.push(TxIn { - previous_output: *outpoint, - script_sig: Script::new(), - sequence: Sequence::ENABLE_RBF_NO_LOCKTIME, - witness: Witness::new(), - }); - } - for (i, (outpoint, out)) in self.inputs.iter().enumerate() { - log_debug!(logger, "Adding claiming input for outpoint {}:{}", outpoint.txid, outpoint.vout); - if !out.finalize_input(&mut bumped_tx, i, onchain_handler) { return None; } - } - log_debug!(logger, "Finalized transaction {} ready to broadcast", bumped_tx.txid()); - return Some(bumped_tx); - }, - PackageMalleability::Untractable => { - debug_assert_eq!(value, 0, "value is ignored for non-malleable packages, should be zero to ensure callsites are correct"); - if let Some((outpoint, outp)) = self.inputs.first() { - if let Some(final_tx) = outp.get_finalized_tx(outpoint, onchain_handler) { - log_debug!(logger, "Adding claiming input for outpoint {}:{}", outpoint.txid, outpoint.vout); - log_debug!(logger, "Finalized transaction {} ready to broadcast", final_tx.txid()); - return Some(final_tx); - } - return None; - } else { panic!("API Error: Package must not be inputs empty"); } - }, + pub(crate) fn finalize_malleable_package( + &self, onchain_handler: &mut OnchainTxHandler, value: u64, destination_script: Script, logger: &L + ) -> Option where L::Target: Logger { + debug_assert!(self.is_malleable()); + let mut bumped_tx = Transaction { + version: 2, + lock_time: PackedLockTime::ZERO, + input: vec![], + output: vec![TxOut { + script_pubkey: destination_script, + value, + }], + }; + for (outpoint, _) in self.inputs.iter() { + bumped_tx.input.push(TxIn { + previous_output: *outpoint, + script_sig: Script::new(), + sequence: Sequence::ENABLE_RBF_NO_LOCKTIME, + witness: Witness::new(), + }); } + for (i, (outpoint, out)) in self.inputs.iter().enumerate() { + log_debug!(logger, "Adding claiming input for outpoint {}:{}", outpoint.txid, outpoint.vout); + if !out.finalize_input(&mut bumped_tx, i, onchain_handler) { return None; } + } + log_debug!(logger, "Finalized transaction {} ready to broadcast", bumped_tx.txid()); + Some(bumped_tx) + } + pub(crate) fn finalize_untractable_package( + &self, onchain_handler: &mut OnchainTxHandler, logger: &L, + ) -> Option where L::Target: Logger { + debug_assert!(!self.is_malleable()); + if let Some((outpoint, outp)) = self.inputs.first() { + if let Some(final_tx) = outp.get_finalized_tx(outpoint, onchain_handler) { + log_debug!(logger, "Adding claiming input for outpoint {}:{}", outpoint.txid, outpoint.vout); + log_debug!(logger, "Finalized transaction {} ready to broadcast", final_tx.txid()); + return Some(final_tx); + } + return None; + } else { panic!("API Error: Package must not be inputs empty"); } } /// In LN, output claimed are time-sensitive, which means we have to spend them before reaching some timelock expiration. At in-channel /// output detection, we generate a first version of a claim tx and associate to it a height timer. A height timer is an absolute block @@ -708,14 +718,45 @@ impl PackageTemplate { } None } + + #[cfg(anchors)] + /// Computes a feerate based on the given confirmation target. If a previous feerate was used, + /// and the new feerate is below it, we'll use a 25% increase of the previous feerate instead of + /// the new one. + pub(crate) fn compute_package_feerate( + &self, fee_estimator: &LowerBoundedFeeEstimator, conf_target: ConfirmationTarget, + ) -> u32 where F::Target: FeeEstimator { + let feerate_estimate = fee_estimator.bounded_sat_per_1000_weight(conf_target); + if self.feerate_previous != 0 { + // If old feerate inferior to actual one given back by Fee Estimator, use it to compute new fee... + if feerate_estimate as u64 > self.feerate_previous { + feerate_estimate + } else { + // ...else just increase the previous feerate by 25% (because that's a nice number) + (self.feerate_previous + (self.feerate_previous / 4)).try_into().unwrap_or(u32::max_value()) + } + } else { + feerate_estimate + } + } + + /// Determines whether a package contains an input which must have additional external inputs + /// attached to help the spending transaction reach confirmation. + pub(crate) fn requires_external_funding(&self) -> bool { + self.inputs.iter().find(|input| match input.1 { + PackageSolvingData::HolderFundingOutput(ref outp) => outp.opt_anchors(), + _ => false, + }).is_some() + } + pub (crate) fn build_package(txid: Txid, vout: u32, input_solving_data: PackageSolvingData, soonest_conf_deadline: u32, aggregable: bool, height_original: u32) -> Self { let malleability = match input_solving_data { - PackageSolvingData::RevokedOutput(..) => { PackageMalleability::Malleable }, - PackageSolvingData::RevokedHTLCOutput(..) => { PackageMalleability::Malleable }, - PackageSolvingData::CounterpartyOfferedHTLCOutput(..) => { PackageMalleability::Malleable }, - PackageSolvingData::CounterpartyReceivedHTLCOutput(..) => { PackageMalleability::Malleable }, - PackageSolvingData::HolderHTLCOutput(..) => { PackageMalleability::Untractable }, - PackageSolvingData::HolderFundingOutput(..) => { PackageMalleability::Untractable }, + PackageSolvingData::RevokedOutput(..) => PackageMalleability::Malleable, + PackageSolvingData::RevokedHTLCOutput(..) => PackageMalleability::Malleable, + PackageSolvingData::CounterpartyOfferedHTLCOutput(..) => PackageMalleability::Malleable, + PackageSolvingData::CounterpartyReceivedHTLCOutput(..) => PackageMalleability::Malleable, + PackageSolvingData::HolderHTLCOutput(..) => PackageMalleability::Untractable, + PackageSolvingData::HolderFundingOutput(..) => PackageMalleability::Untractable, }; let mut inputs = Vec::with_capacity(1); inputs.push((BitcoinOutPoint { txid, vout }, input_solving_data)); @@ -870,10 +911,10 @@ fn feerate_bump(predicted_weight: usize, input_amounts: u64, #[cfg(test)] mod tests { - use chain::package::{CounterpartyOfferedHTLCOutput, CounterpartyReceivedHTLCOutput, HolderHTLCOutput, PackageTemplate, PackageSolvingData, RevokedOutput, WEIGHT_REVOKED_OUTPUT, weight_offered_htlc, weight_received_htlc}; - use chain::Txid; - use ln::chan_utils::HTLCOutputInCommitment; - use ln::{PaymentPreimage, PaymentHash}; + use crate::chain::package::{CounterpartyOfferedHTLCOutput, CounterpartyReceivedHTLCOutput, HolderHTLCOutput, PackageTemplate, PackageSolvingData, RevokedOutput, WEIGHT_REVOKED_OUTPUT, weight_offered_htlc, weight_received_htlc}; + use crate::chain::Txid; + use crate::ln::chan_utils::HTLCOutputInCommitment; + use crate::ln::{PaymentPreimage, PaymentHash}; use bitcoin::blockdata::constants::WITNESS_SCALE_FACTOR; use bitcoin::blockdata::script::Script;