X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning%2Fsrc%2Fchain%2Fpackage.rs;h=e8886e5a2aa49958fcac012529a3ae4160348230;hb=ba1349982ba28657c9e2d03a5b02c3ecc054b5cc;hp=5aa55fb197583be8e97f4ecc8c05ee806df2433c;hpb=31042ab7d524d75a1e9eb34080c1bdc666c9101e;p=rust-lightning diff --git a/lightning/src/chain/package.rs b/lightning/src/chain/package.rs index 5aa55fb1..e8886e5a 100644 --- a/lightning/src/chain/package.rs +++ b/lightning/src/chain/package.rs @@ -20,19 +20,20 @@ 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::WriteableEcdsaChannelSigner; +#[cfg(anchors)] +use crate::chain::onchaintx::ExternalHTLCClaim; +use crate::chain::onchaintx::OnchainTxHandler; +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; @@ -202,11 +203,11 @@ impl CounterpartyOfferedHTLCOutput { impl_writeable_tlv_based!(CounterpartyOfferedHTLCOutput, { (0, per_commitment_point, required), - (1, opt_anchors, option), (2, counterparty_delayed_payment_base_key, required), (4, counterparty_htlc_base_key, required), (6, preimage, required), (8, htlc, required), + (10, opt_anchors, option), }); /// A struct to describe a HTLC output on a counterparty commitment transaction. @@ -240,10 +241,10 @@ impl CounterpartyReceivedHTLCOutput { impl_writeable_tlv_based!(CounterpartyReceivedHTLCOutput, { (0, per_commitment_point, required), - (1, opt_anchors, option), (2, counterparty_delayed_payment_base_key, required), (4, counterparty_htlc_base_key, required), (6, htlc, required), + (8, opt_anchors, option), }); /// A struct to describe a HTLC output on holder commitment transaction. @@ -253,33 +254,41 @@ impl_writeable_tlv_based!(CounterpartyReceivedHTLCOutput, { #[derive(Clone, PartialEq, Eq)] pub(crate) struct HolderHTLCOutput { preimage: Option, - amount: u64, + amount_msat: u64, /// Defaults to 0 for HTLC-Success transactions, which have no expiry cltv_expiry: u32, + opt_anchors: Option<()>, } impl HolderHTLCOutput { - pub(crate) fn build_offered(amount: u64, cltv_expiry: u32) -> Self { + pub(crate) fn build_offered(amount_msat: u64, cltv_expiry: u32, opt_anchors: bool) -> Self { HolderHTLCOutput { preimage: None, - amount, + amount_msat, cltv_expiry, + opt_anchors: if opt_anchors { Some(()) } else { None } , } } - pub(crate) fn build_accepted(preimage: PaymentPreimage, amount: u64) -> Self { + pub(crate) fn build_accepted(preimage: PaymentPreimage, amount_msat: u64, opt_anchors: bool) -> Self { HolderHTLCOutput { preimage: Some(preimage), - amount, + amount_msat, cltv_expiry: 0, + opt_anchors: if opt_anchors { Some(()) } else { None } , } } + + fn opt_anchors(&self) -> bool { + self.opt_anchors.is_some() + } } impl_writeable_tlv_based!(HolderHTLCOutput, { - (0, amount, required), + (0, amount_msat, required), (2, cltv_expiry, required), - (4, preimage, option) + (4, preimage, option), + (6, opt_anchors, option) }); /// A struct to describe the channel output on the funding transaction. @@ -309,7 +318,7 @@ impl HolderFundingOutput { impl_writeable_tlv_based!(HolderFundingOutput, { (0, funding_redeemscript, required), - (1, opt_anchors, option), + (2, opt_anchors, option), (3, funding_amount, option), }); @@ -334,10 +343,10 @@ impl PackageSolvingData { 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::HolderHTLCOutput(ref outp) => { + debug_assert!(outp.opt_anchors()); + outp.amount_msat / 1000 + }, PackageSolvingData::HolderFundingOutput(ref outp) => { debug_assert!(outp.opt_anchors()); outp.funding_amount.unwrap() @@ -346,18 +355,23 @@ impl PackageSolvingData { amt } fn weight(&self) -> usize { - let weight = match self { - PackageSolvingData::RevokedOutput(ref outp) => { outp.weight as usize }, - PackageSolvingData::RevokedHTLCOutput(ref outp) => { outp.weight as usize }, - PackageSolvingData::CounterpartyOfferedHTLCOutput(ref outp) => { weight_offered_htlc(outp.opt_anchors()) as usize }, - PackageSolvingData::CounterpartyReceivedHTLCOutput(ref outp) => { weight_received_htlc(outp.opt_anchors()) as usize }, - // Note: Currently, weights 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!() }, - }; - weight + match self { + PackageSolvingData::RevokedOutput(ref outp) => outp.weight as usize, + PackageSolvingData::RevokedHTLCOutput(ref outp) => outp.weight as usize, + PackageSolvingData::CounterpartyOfferedHTLCOutput(ref outp) => weight_offered_htlc(outp.opt_anchors()) as usize, + PackageSolvingData::CounterpartyReceivedHTLCOutput(ref outp) => weight_received_htlc(outp.opt_anchors()) as usize, + PackageSolvingData::HolderHTLCOutput(ref outp) => { + debug_assert!(outp.opt_anchors()); + if outp.preimage.is_none() { + weight_offered_htlc(true) as usize + } else { + weight_received_htlc(true) as usize + } + }, + // Since HolderFundingOutput maps to an untractable package that is already signed, its + // weight can be determined from the transaction itself. + PackageSolvingData::HolderFundingOutput(..) => unreachable!(), + } } fn is_compatible(&self, input: &PackageSolvingData) -> bool { match self { @@ -378,85 +392,90 @@ impl PackageSolvingData { _ => { mem::discriminant(self) == mem::discriminant(&input) } } } - fn finalize_input(&self, bumped_tx: &mut Transaction, i: usize, onchain_handler: &mut OnchainTxHandler) -> bool { + fn finalize_input(&self, bumped_tx: &mut Transaction, i: usize, onchain_handler: &mut OnchainTxHandler) -> bool { match self { PackageSolvingData::RevokedOutput(ref outp) => { - if let Ok(chan_keys) = TxCreationKeys::derive_new(&onchain_handler.secp_ctx, &outp.per_commitment_point, &outp.counterparty_delayed_payment_base_key, &outp.counterparty_htlc_base_key, &onchain_handler.signer.pubkeys().revocation_basepoint, &onchain_handler.signer.pubkeys().htlc_basepoint) { - let witness_script = chan_utils::get_revokeable_redeemscript(&chan_keys.revocation_key, outp.on_counterparty_tx_csv, &chan_keys.broadcaster_delayed_payment_key); - //TODO: should we panic on signer failure ? - if let Ok(sig) = onchain_handler.signer.sign_justice_revoked_output(&bumped_tx, i, outp.amount, &outp.per_commitment_key, &onchain_handler.secp_ctx) { - let mut ser_sig = sig.serialize_der().to_vec(); - ser_sig.push(EcdsaSighashType::All as u8); - bumped_tx.input[i].witness.push(ser_sig); - bumped_tx.input[i].witness.push(vec!(1)); - bumped_tx.input[i].witness.push(witness_script.clone().into_bytes()); - } else { return false; } - } + let chan_keys = TxCreationKeys::derive_new(&onchain_handler.secp_ctx, &outp.per_commitment_point, &outp.counterparty_delayed_payment_base_key, &outp.counterparty_htlc_base_key, &onchain_handler.signer.pubkeys().revocation_basepoint, &onchain_handler.signer.pubkeys().htlc_basepoint); + let witness_script = chan_utils::get_revokeable_redeemscript(&chan_keys.revocation_key, outp.on_counterparty_tx_csv, &chan_keys.broadcaster_delayed_payment_key); + //TODO: should we panic on signer failure ? + if let Ok(sig) = onchain_handler.signer.sign_justice_revoked_output(&bumped_tx, i, outp.amount, &outp.per_commitment_key, &onchain_handler.secp_ctx) { + let mut ser_sig = sig.serialize_der().to_vec(); + ser_sig.push(EcdsaSighashType::All as u8); + bumped_tx.input[i].witness.push(ser_sig); + bumped_tx.input[i].witness.push(vec!(1)); + bumped_tx.input[i].witness.push(witness_script.clone().into_bytes()); + } else { return false; } }, PackageSolvingData::RevokedHTLCOutput(ref outp) => { - if let Ok(chan_keys) = TxCreationKeys::derive_new(&onchain_handler.secp_ctx, &outp.per_commitment_point, &outp.counterparty_delayed_payment_base_key, &outp.counterparty_htlc_base_key, &onchain_handler.signer.pubkeys().revocation_basepoint, &onchain_handler.signer.pubkeys().htlc_basepoint) { - let witness_script = chan_utils::get_htlc_redeemscript_with_explicit_keys(&outp.htlc, onchain_handler.opt_anchors(), &chan_keys.broadcaster_htlc_key, &chan_keys.countersignatory_htlc_key, &chan_keys.revocation_key); - //TODO: should we panic on signer failure ? - if let Ok(sig) = onchain_handler.signer.sign_justice_revoked_htlc(&bumped_tx, i, outp.amount, &outp.per_commitment_key, &outp.htlc, &onchain_handler.secp_ctx) { - let mut ser_sig = sig.serialize_der().to_vec(); - ser_sig.push(EcdsaSighashType::All as u8); - bumped_tx.input[i].witness.push(ser_sig); - bumped_tx.input[i].witness.push(chan_keys.revocation_key.clone().serialize().to_vec()); - bumped_tx.input[i].witness.push(witness_script.clone().into_bytes()); - } else { return false; } - } + let chan_keys = TxCreationKeys::derive_new(&onchain_handler.secp_ctx, &outp.per_commitment_point, &outp.counterparty_delayed_payment_base_key, &outp.counterparty_htlc_base_key, &onchain_handler.signer.pubkeys().revocation_basepoint, &onchain_handler.signer.pubkeys().htlc_basepoint); + let witness_script = chan_utils::get_htlc_redeemscript_with_explicit_keys(&outp.htlc, onchain_handler.opt_anchors(), &chan_keys.broadcaster_htlc_key, &chan_keys.countersignatory_htlc_key, &chan_keys.revocation_key); + //TODO: should we panic on signer failure ? + if let Ok(sig) = onchain_handler.signer.sign_justice_revoked_htlc(&bumped_tx, i, outp.amount, &outp.per_commitment_key, &outp.htlc, &onchain_handler.secp_ctx) { + let mut ser_sig = sig.serialize_der().to_vec(); + ser_sig.push(EcdsaSighashType::All as u8); + bumped_tx.input[i].witness.push(ser_sig); + bumped_tx.input[i].witness.push(chan_keys.revocation_key.clone().serialize().to_vec()); + bumped_tx.input[i].witness.push(witness_script.clone().into_bytes()); + } else { return false; } }, PackageSolvingData::CounterpartyOfferedHTLCOutput(ref outp) => { - if let Ok(chan_keys) = TxCreationKeys::derive_new(&onchain_handler.secp_ctx, &outp.per_commitment_point, &outp.counterparty_delayed_payment_base_key, &outp.counterparty_htlc_base_key, &onchain_handler.signer.pubkeys().revocation_basepoint, &onchain_handler.signer.pubkeys().htlc_basepoint) { - let witness_script = chan_utils::get_htlc_redeemscript_with_explicit_keys(&outp.htlc, onchain_handler.opt_anchors(), &chan_keys.broadcaster_htlc_key, &chan_keys.countersignatory_htlc_key, &chan_keys.revocation_key); - - if let Ok(sig) = onchain_handler.signer.sign_counterparty_htlc_transaction(&bumped_tx, i, &outp.htlc.amount_msat / 1000, &outp.per_commitment_point, &outp.htlc, &onchain_handler.secp_ctx) { - let mut ser_sig = sig.serialize_der().to_vec(); - ser_sig.push(EcdsaSighashType::All as u8); - bumped_tx.input[i].witness.push(ser_sig); - bumped_tx.input[i].witness.push(outp.preimage.0.to_vec()); - bumped_tx.input[i].witness.push(witness_script.clone().into_bytes()); - } + let chan_keys = TxCreationKeys::derive_new(&onchain_handler.secp_ctx, &outp.per_commitment_point, &outp.counterparty_delayed_payment_base_key, &outp.counterparty_htlc_base_key, &onchain_handler.signer.pubkeys().revocation_basepoint, &onchain_handler.signer.pubkeys().htlc_basepoint); + let witness_script = chan_utils::get_htlc_redeemscript_with_explicit_keys(&outp.htlc, onchain_handler.opt_anchors(), &chan_keys.broadcaster_htlc_key, &chan_keys.countersignatory_htlc_key, &chan_keys.revocation_key); + + if let Ok(sig) = onchain_handler.signer.sign_counterparty_htlc_transaction(&bumped_tx, i, &outp.htlc.amount_msat / 1000, &outp.per_commitment_point, &outp.htlc, &onchain_handler.secp_ctx) { + let mut ser_sig = sig.serialize_der().to_vec(); + ser_sig.push(EcdsaSighashType::All as u8); + bumped_tx.input[i].witness.push(ser_sig); + bumped_tx.input[i].witness.push(outp.preimage.0.to_vec()); + bumped_tx.input[i].witness.push(witness_script.clone().into_bytes()); } }, PackageSolvingData::CounterpartyReceivedHTLCOutput(ref outp) => { - if let Ok(chan_keys) = TxCreationKeys::derive_new(&onchain_handler.secp_ctx, &outp.per_commitment_point, &outp.counterparty_delayed_payment_base_key, &outp.counterparty_htlc_base_key, &onchain_handler.signer.pubkeys().revocation_basepoint, &onchain_handler.signer.pubkeys().htlc_basepoint) { - let witness_script = chan_utils::get_htlc_redeemscript_with_explicit_keys(&outp.htlc, onchain_handler.opt_anchors(), &chan_keys.broadcaster_htlc_key, &chan_keys.countersignatory_htlc_key, &chan_keys.revocation_key); - - bumped_tx.lock_time = PackedLockTime(outp.htlc.cltv_expiry); // Right now we don't aggregate time-locked transaction, if we do we should set lock_time before to avoid breaking hash computation - if let Ok(sig) = onchain_handler.signer.sign_counterparty_htlc_transaction(&bumped_tx, i, &outp.htlc.amount_msat / 1000, &outp.per_commitment_point, &outp.htlc, &onchain_handler.secp_ctx) { - let mut ser_sig = sig.serialize_der().to_vec(); - ser_sig.push(EcdsaSighashType::All as u8); - bumped_tx.input[i].witness.push(ser_sig); - // Due to BIP146 (MINIMALIF) this must be a zero-length element to relay. - bumped_tx.input[i].witness.push(vec![]); - bumped_tx.input[i].witness.push(witness_script.clone().into_bytes()); - } + let chan_keys = TxCreationKeys::derive_new(&onchain_handler.secp_ctx, &outp.per_commitment_point, &outp.counterparty_delayed_payment_base_key, &outp.counterparty_htlc_base_key, &onchain_handler.signer.pubkeys().revocation_basepoint, &onchain_handler.signer.pubkeys().htlc_basepoint); + let witness_script = chan_utils::get_htlc_redeemscript_with_explicit_keys(&outp.htlc, onchain_handler.opt_anchors(), &chan_keys.broadcaster_htlc_key, &chan_keys.countersignatory_htlc_key, &chan_keys.revocation_key); + + if let Ok(sig) = onchain_handler.signer.sign_counterparty_htlc_transaction(&bumped_tx, i, &outp.htlc.amount_msat / 1000, &outp.per_commitment_point, &outp.htlc, &onchain_handler.secp_ctx) { + let mut ser_sig = sig.serialize_der().to_vec(); + ser_sig.push(EcdsaSighashType::All as u8); + bumped_tx.input[i].witness.push(ser_sig); + // Due to BIP146 (MINIMALIF) this must be a zero-length element to relay. + bumped_tx.input[i].witness.push(vec![]); + bumped_tx.input[i].witness.push(witness_script.clone().into_bytes()); } }, _ => { panic!("API Error!"); } } true } - fn get_finalized_tx(&self, outpoint: &BitcoinOutPoint, onchain_handler: &mut OnchainTxHandler) -> Option { + fn get_finalized_tx(&self, outpoint: &BitcoinOutPoint, onchain_handler: &mut OnchainTxHandler) -> Option { match self { - PackageSolvingData::HolderHTLCOutput(ref outp) => { return onchain_handler.get_fully_signed_htlc_tx(outpoint, &outp.preimage); } - PackageSolvingData::HolderFundingOutput(ref outp) => { return Some(onchain_handler.get_fully_signed_holder_tx(&outp.funding_redeemscript)); } + PackageSolvingData::HolderHTLCOutput(ref outp) => { + debug_assert!(!outp.opt_anchors()); + return onchain_handler.get_fully_signed_htlc_tx(outpoint, &outp.preimage); + } + PackageSolvingData::HolderFundingOutput(ref outp) => { + return Some(onchain_handler.get_fully_signed_holder_tx(&outp.funding_redeemscript)); + } _ => { panic!("API Error!"); } } } - fn absolute_tx_timelock(&self, output_conf_height: u32) -> u32 { - // Get the absolute timelock at which this output can be spent given the height at which - // this output was confirmed. We use `output_conf_height + 1` as a safe default as we can - // be confirmed in the next block and transactions with time lock `current_height + 1` - // always propagate. + fn absolute_tx_timelock(&self, current_height: u32) -> u32 { + // We use `current_height + 1` as our default locktime to discourage fee sniping and because + // transactions with it always propagate. let absolute_timelock = match self { - PackageSolvingData::RevokedOutput(_) => output_conf_height + 1, - PackageSolvingData::RevokedHTLCOutput(_) => output_conf_height + 1, - PackageSolvingData::CounterpartyOfferedHTLCOutput(_) => output_conf_height + 1, - PackageSolvingData::CounterpartyReceivedHTLCOutput(ref outp) => cmp::max(outp.htlc.cltv_expiry, output_conf_height + 1), - PackageSolvingData::HolderHTLCOutput(ref outp) => cmp::max(outp.cltv_expiry, output_conf_height + 1), - PackageSolvingData::HolderFundingOutput(_) => output_conf_height + 1, + PackageSolvingData::RevokedOutput(_) => current_height + 1, + PackageSolvingData::RevokedHTLCOutput(_) => current_height + 1, + PackageSolvingData::CounterpartyOfferedHTLCOutput(_) => current_height + 1, + PackageSolvingData::CounterpartyReceivedHTLCOutput(ref outp) => cmp::max(outp.htlc.cltv_expiry, current_height + 1), + // HTLC timeout/success transactions rely on a fixed timelock due to the counterparty's + // signature. + PackageSolvingData::HolderHTLCOutput(ref outp) => { + if outp.preimage.is_some() { + debug_assert_eq!(outp.cltv_expiry, 0); + } + outp.cltv_expiry + }, + PackageSolvingData::HolderFundingOutput(_) => current_height + 1, }; absolute_timelock } @@ -623,9 +642,36 @@ impl PackageTemplate { } amounts } - pub(crate) fn package_timelock(&self) -> u32 { - self.inputs.iter().map(|(_, outp)| outp.absolute_tx_timelock(self.height_original)) - .max().expect("There must always be at least one output to spend in a PackageTemplate") + pub(crate) fn package_locktime(&self, current_height: u32) -> u32 { + let locktime = self.inputs.iter().map(|(_, outp)| outp.absolute_tx_timelock(current_height)) + .max().expect("There must always be at least one output to spend in a PackageTemplate"); + + // If we ever try to aggregate a `HolderHTLCOutput`s with another output type, we'll likely + // end up with an incorrect transaction locktime since the counterparty has included it in + // its HTLC signature. This should never happen unless we decide to aggregate outputs across + // different channel commitments. + #[cfg(debug_assertions)] { + if self.inputs.iter().any(|(_, outp)| + if let PackageSolvingData::HolderHTLCOutput(outp) = outp { + outp.preimage.is_some() + } else { + false + } + ) { + debug_assert_eq!(locktime, 0); + }; + for timeout_htlc_expiry in self.inputs.iter().filter_map(|(_, outp)| + if let PackageSolvingData::HolderHTLCOutput(outp) = outp { + if outp.preimage.is_none() { + Some(outp.cltv_expiry) + } else { None } + } else { None } + ) { + debug_assert_eq!(locktime, timeout_htlc_expiry); + } + } + + locktime } pub(crate) fn package_weight(&self, destination_script: &Script) -> usize { let mut inputs_weight = 0; @@ -641,13 +687,33 @@ 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_malleable_package( - &self, onchain_handler: &mut OnchainTxHandler, value: u64, destination_script: Script, logger: &L + #[cfg(anchors)] + pub(crate) fn construct_malleable_package_with_external_funding( + &self, onchain_handler: &mut OnchainTxHandler, + ) -> Option> { + debug_assert!(self.requires_external_funding()); + let mut htlcs: Option> = None; + for (previous_output, input) in &self.inputs { + match input { + PackageSolvingData::HolderHTLCOutput(ref outp) => { + debug_assert!(outp.opt_anchors()); + onchain_handler.generate_external_htlc_claim(&previous_output, &outp.preimage).map(|htlc| { + htlcs.get_or_insert_with(|| Vec::with_capacity(self.inputs.len())).push(htlc); + }); + } + _ => debug_assert!(false, "Expected HolderHTLCOutputs to not be aggregated with other input types"), + } + } + htlcs + } + pub(crate) fn finalize_malleable_package( + &self, current_height: u32, 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, + lock_time: PackedLockTime(self.package_locktime(current_height)), input: vec![], output: vec![TxOut { script_pubkey: destination_script, @@ -669,7 +735,7 @@ impl PackageTemplate { log_debug!(logger, "Finalized transaction {} ready to broadcast", bumped_tx.txid()); Some(bumped_tx) } - pub(crate) fn finalize_untractable_package( + pub(crate) fn finalize_untractable_package( &self, onchain_handler: &mut OnchainTxHandler, logger: &L, ) -> Option where L::Target: Logger { debug_assert!(!self.is_malleable()); @@ -745,6 +811,7 @@ impl PackageTemplate { pub(crate) fn requires_external_funding(&self) -> bool { self.inputs.iter().find(|input| match input.1 { PackageSolvingData::HolderFundingOutput(ref outp) => outp.opt_anchors(), + PackageSolvingData::HolderHTLCOutput(ref outp) => outp.opt_anchors(), _ => false, }).is_some() } @@ -755,7 +822,11 @@ impl PackageTemplate { PackageSolvingData::RevokedHTLCOutput(..) => PackageMalleability::Malleable, PackageSolvingData::CounterpartyOfferedHTLCOutput(..) => PackageMalleability::Malleable, PackageSolvingData::CounterpartyReceivedHTLCOutput(..) => PackageMalleability::Malleable, - PackageSolvingData::HolderHTLCOutput(..) => PackageMalleability::Untractable, + PackageSolvingData::HolderHTLCOutput(ref outp) => if outp.opt_anchors() { + PackageMalleability::Malleable + } else { + PackageMalleability::Untractable + }, PackageSolvingData::HolderFundingOutput(..) => PackageMalleability::Untractable, }; let mut inputs = Vec::with_capacity(1); @@ -774,7 +845,7 @@ impl PackageTemplate { impl Writeable for PackageTemplate { fn write(&self, writer: &mut W) -> Result<(), io::Error> { - writer.write_all(&byte_utils::be64_to_array(self.inputs.len() as u64))?; + writer.write_all(&(self.inputs.len() as u64).to_be_bytes())?; for (ref outpoint, ref rev_outp) in self.inputs.iter() { outpoint.write(writer)?; rev_outp.write(writer)?; @@ -804,7 +875,11 @@ impl Readable for PackageTemplate { PackageSolvingData::RevokedHTLCOutput(..) => { (PackageMalleability::Malleable, true) }, PackageSolvingData::CounterpartyOfferedHTLCOutput(..) => { (PackageMalleability::Malleable, true) }, PackageSolvingData::CounterpartyReceivedHTLCOutput(..) => { (PackageMalleability::Malleable, false) }, - PackageSolvingData::HolderHTLCOutput(..) => { (PackageMalleability::Untractable, false) }, + PackageSolvingData::HolderHTLCOutput(ref outp) => if outp.opt_anchors() { + (PackageMalleability::Malleable, outp.preimage.is_some()) + } else { + (PackageMalleability::Untractable, false) + }, PackageSolvingData::HolderFundingOutput(..) => { (PackageMalleability::Untractable, false) }, } } else { return Err(DecodeError::InvalidValue); }; @@ -911,10 +986,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; @@ -964,7 +1039,7 @@ mod tests { () => { { let preimage = PaymentPreimage([2;32]); - PackageSolvingData::HolderHTLCOutput(HolderHTLCOutput::build_accepted(preimage, 0)) + PackageSolvingData::HolderHTLCOutput(HolderHTLCOutput::build_accepted(preimage, 0, false)) } } }