X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning%2Fsrc%2Fln%2Fonchaintx.rs;h=2d7d417d255ac77dfecb1791dd24bca42d4166ca;hb=151d4ac0a3a6aa94d3f93995b71c874b7ea95967;hp=281bc919060a95445b1f8d76a7d1764ee15b500a;hpb=6133498ca1a455c09724ad8e78286e330ea6e9d0;p=rust-lightning diff --git a/lightning/src/ln/onchaintx.rs b/lightning/src/ln/onchaintx.rs index 281bc919..2d7d417d 100644 --- a/lightning/src/ln/onchaintx.rs +++ b/lightning/src/ln/onchaintx.rs @@ -9,7 +9,7 @@ //! The logic to build claims and bump in-flight transactions until confirmations. //! -//! OnchainTxHandler objetcs are fully-part of ChannelMonitor and encapsulates all +//! OnchainTxHandler objects are fully-part of ChannelMonitor and encapsulates all //! building, tracking, bumping and notifications functions. use bitcoin::blockdata::transaction::{Transaction, TxIn, TxOut, SigHashType}; @@ -22,19 +22,20 @@ use bitcoin::secp256k1::{Secp256k1, Signature}; use bitcoin::secp256k1; use ln::msgs::DecodeError; -use ln::channelmonitor::{ANTI_REORG_DELAY, CLTV_SHARED_CLAIM_BUFFER, InputMaterial, ClaimRequest}; use ln::channelmanager::PaymentPreimage; use ln::chan_utils; -use ln::chan_utils::{TxCreationKeys, HolderCommitmentTransaction}; +use ln::chan_utils::{TxCreationKeys, ChannelTransactionParameters, HolderCommitmentTransaction}; use chain::chaininterface::{FeeEstimator, BroadcasterInterface, ConfirmationTarget, MIN_RELAY_FEE_SAT_PER_1000_WEIGHT}; -use chain::keysinterface::ChannelKeys; +use chain::channelmonitor::{ANTI_REORG_DELAY, CLTV_SHARED_CLAIM_BUFFER, InputMaterial, ClaimRequest}; +use chain::keysinterface::{ChannelKeys, KeysInterface}; use util::logger::Logger; -use util::ser::{Readable, Writer, Writeable}; +use util::ser::{Readable, ReadableArgs, Writer, Writeable, VecWriter}; use util::byte_utils; use std::collections::{HashMap, hash_map}; use std::cmp; use std::ops::Deref; +use std::mem::replace; const MAX_ALLOC_SIZE: usize = 64*1024; @@ -241,17 +242,16 @@ impl Writeable for Option>> { /// do RBF bumping if possible. pub struct OnchainTxHandler { destination_script: Script, - holder_commitment: Option, + holder_commitment: HolderCommitmentTransaction, // holder_htlc_sigs and prev_holder_htlc_sigs are in the order as they appear in the commitment // transaction outputs (hence the Option<>s inside the Vec). The first usize is the index in - // the set of HTLCs in the HolderCommitmentTransaction (including those which do not appear in - // the commitment transaction). + // the set of HTLCs in the HolderCommitmentTransaction. holder_htlc_sigs: Option>>, prev_holder_commitment: Option, prev_holder_htlc_sigs: Option>>, - on_holder_tx_csv: u16, key_storage: ChanSigner, + pub(crate) channel_transaction_parameters: ChannelTransactionParameters, // Used to track claiming requests. If claim tx doesn't confirm before height timer expiration we need to bump // it (RBF or CPFP). If an input has been part of an aggregate tx at first claim try, we need to keep it within @@ -282,10 +282,12 @@ pub struct OnchainTxHandler { onchain_events_waiting_threshold_conf: HashMap>, + latest_height: u32, + secp_ctx: Secp256k1, } -impl OnchainTxHandler { +impl OnchainTxHandler { pub(crate) fn write(&self, writer: &mut W) -> Result<(), ::std::io::Error> { self.destination_script.write(writer)?; self.holder_commitment.write(writer)?; @@ -293,9 +295,14 @@ impl OnchainTxHandler { self.prev_holder_commitment.write(writer)?; self.prev_holder_htlc_sigs.write(writer)?; - self.on_holder_tx_csv.write(writer)?; + self.channel_transaction_parameters.write(writer)?; - self.key_storage.write(writer)?; + let mut key_data = VecWriter(Vec::new()); + self.key_storage.write(&mut key_data)?; + assert!(key_data.0.len() < std::usize::MAX); + assert!(key_data.0.len() < std::u32::MAX as usize); + (key_data.0.len() as u32).write(writer)?; + writer.write_all(&key_data.0[..])?; writer.write_all(&byte_utils::be64_to_array(self.pending_claim_requests.len() as u64))?; for (ref ancestor_claim_txid, claim_tx_data) in self.pending_claim_requests.iter() { @@ -328,12 +335,13 @@ impl OnchainTxHandler { } } } + self.latest_height.write(writer)?; Ok(()) } } -impl Readable for OnchainTxHandler { - fn read(reader: &mut R) -> Result { +impl<'a, K: KeysInterface> ReadableArgs<&'a K> for OnchainTxHandler { + fn read(reader: &mut R, keys_manager: &'a K) -> Result { let destination_script = Readable::read(reader)?; let holder_commitment = Readable::read(reader)?; @@ -341,9 +349,18 @@ impl Readable for OnchainTxHandler Readable for OnchainTxHandler Readable for OnchainTxHandler OnchainTxHandler { - pub(super) fn new(destination_script: Script, keys: ChanSigner, on_holder_tx_csv: u16) -> Self { + pub(crate) fn new(destination_script: Script, keys: ChanSigner, channel_parameters: ChannelTransactionParameters, holder_commitment: HolderCommitmentTransaction) -> Self { let key_storage = keys; OnchainTxHandler { destination_script, - holder_commitment: None, + holder_commitment, holder_htlc_sigs: None, prev_holder_commitment: None, prev_holder_htlc_sigs: None, - on_holder_tx_csv, key_storage, + channel_transaction_parameters: channel_parameters, pending_claim_requests: HashMap::new(), claimable_outpoints: HashMap::new(), onchain_events_waiting_threshold_conf: HashMap::new(), + latest_height: 0, secp_ctx: Secp256k1::new(), } } - pub(super) fn get_witnesses_weight(inputs: &[InputDescriptors]) -> usize { + pub(crate) fn get_witnesses_weight(inputs: &[InputDescriptors]) -> usize { let mut tx_weight = 2; // count segwit flags for inp in inputs { // We use expected weight (and not actual) as signatures and time lock delays may vary @@ -471,7 +491,9 @@ impl OnchainTxHandler { /// Lightning security model (i.e being able to redeem/timeout HTLC or penalize coutnerparty onchain) lays on the assumption of claim transactions getting confirmed before timelock expiration /// (CSV or CLTV following cases). In case of high-fee spikes, claim tx may stuck in the mempool, so you need to bump its feerate quickly using Replace-By-Fee or Child-Pay-For-Parent. - fn generate_claim_tx(&mut self, height: u32, cached_claim_datas: &ClaimTxBumpMaterial, fee_estimator: F, logger: L) -> Option<(Option, u32, Transaction)> + /// Panics if there are signing errors, because signing operations in reaction to on-chain events + /// are not expected to fail, and if they do, we may lose funds. + fn generate_claim_tx(&mut self, height: u32, cached_claim_datas: &ClaimTxBumpMaterial, fee_estimator: &F, logger: &L) -> Option<(Option, u32, Transaction)> where F::Target: FeeEstimator, L::Target: Logger, { @@ -591,17 +613,15 @@ impl OnchainTxHandler { chan_utils::get_revokeable_redeemscript(&chan_keys.revocation_key, *on_counterparty_tx_csv, &chan_keys.broadcaster_delayed_payment_key) }; - if let Ok(sig) = self.key_storage.sign_justice_transaction(&bumped_tx, i, *amount, &per_commitment_key, htlc, &self.secp_ctx) { - bumped_tx.input[i].witness.push(sig.serialize_der().to_vec()); - bumped_tx.input[i].witness[0].push(SigHashType::All as u8); - if htlc.is_some() { - bumped_tx.input[i].witness.push(chan_keys.revocation_key.clone().serialize().to_vec()); - } else { - bumped_tx.input[i].witness.push(vec!(1)); - } - bumped_tx.input[i].witness.push(witness_script.clone().into_bytes()); - } else { return None; } - //TODO: panic ? + let sig = self.key_storage.sign_justice_transaction(&bumped_tx, i, *amount, &per_commitment_key, htlc, &self.secp_ctx).expect("sign justice tx"); + bumped_tx.input[i].witness.push(sig.serialize_der().to_vec()); + bumped_tx.input[i].witness[0].push(SigHashType::All as u8); + if htlc.is_some() { + bumped_tx.input[i].witness.push(chan_keys.revocation_key.clone().serialize().to_vec()); + } else { + bumped_tx.input[i].witness.push(vec!(1)); + } + bumped_tx.input[i].witness.push(witness_script.clone().into_bytes()); log_trace!(logger, "Going to broadcast Penalty Transaction {} claiming revoked {} output {} from {} with new feerate {}...", bumped_tx.txid(), if *input_descriptor == InputDescriptors::RevokedOutput { "to_holder" } else if *input_descriptor == InputDescriptors::RevokedOfferedHTLC { "offered" } else if *input_descriptor == InputDescriptors::RevokedReceivedHTLC { "received" } else { "" }, outp.vout, outp.txid, new_feerate); } @@ -611,17 +631,16 @@ impl OnchainTxHandler { let witness_script = chan_utils::get_htlc_redeemscript_with_explicit_keys(&htlc, &chan_keys.broadcaster_htlc_key, &chan_keys.countersignatory_htlc_key, &chan_keys.revocation_key); if !preimage.is_some() { bumped_tx.lock_time = 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) = self.key_storage.sign_counterparty_htlc_transaction(&bumped_tx, i, &htlc.amount_msat / 1000, &per_commitment_point, htlc, &self.secp_ctx) { - bumped_tx.input[i].witness.push(sig.serialize_der().to_vec()); - bumped_tx.input[i].witness[0].push(SigHashType::All as u8); - if let &Some(preimage) = preimage { - bumped_tx.input[i].witness.push(preimage.0.to_vec()); - } else { - // 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 sig = self.key_storage.sign_counterparty_htlc_transaction(&bumped_tx, i, &htlc.amount_msat / 1000, &per_commitment_point, htlc, &self.secp_ctx).expect("sign counterparty HTLC tx"); + bumped_tx.input[i].witness.push(sig.serialize_der().to_vec()); + bumped_tx.input[i].witness[0].push(SigHashType::All as u8); + if let &Some(preimage) = preimage { + bumped_tx.input[i].witness.push(preimage.0.to_vec()); + } else { + // 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()); log_trace!(logger, "Going to broadcast Claim Transaction {} claiming counterparty {} htlc output {} from {} with new feerate {}...", bumped_tx.txid(), if preimage.is_some() { "offered" } else { "received" }, outp.vout, outp.txid, new_feerate); } }, @@ -645,10 +664,10 @@ impl OnchainTxHandler { return None; }, &InputMaterial::Funding { ref funding_redeemscript } => { - let signed_tx = self.get_fully_signed_holder_tx(funding_redeemscript).unwrap(); + let signed_tx = self.get_fully_signed_holder_tx(funding_redeemscript); // Timer set to $NEVER given we can't bump tx without anchor outputs log_trace!(logger, "Going to broadcast Holder Transaction {} claiming funding output {} from {}...", signed_tx.txid(), outp.vout, outp.txid); - return Some((None, self.holder_commitment.as_ref().unwrap().feerate_per_kw, signed_tx)); + return Some((None, self.holder_commitment.feerate_per_kw(), signed_tx)); } _ => unreachable!() } @@ -657,12 +676,20 @@ impl OnchainTxHandler { None } - pub(super) fn block_connected(&mut self, txn_matched: &[&Transaction], claimable_outpoints: Vec, height: u32, broadcaster: B, fee_estimator: F, logger: L) + /// Upon channelmonitor.block_connected(..) or upon provision of a preimage on the forward link + /// for this channel, provide new relevant on-chain transactions and/or new claim requests. + /// Formerly this was named `block_connected`, but it is now also used for claiming an HTLC output + /// if we receive a preimage after force-close. + pub(crate) fn update_claims_view(&mut self, txn_matched: &[&Transaction], claimable_outpoints: Vec, latest_height: Option, broadcaster: &B, fee_estimator: &F, logger: &L) where B::Target: BroadcasterInterface, F::Target: FeeEstimator, L::Target: Logger, { - log_trace!(logger, "Block at height {} connected with {} claim requests", height, claimable_outpoints.len()); + let height = match latest_height { + Some(h) => h, + None => self.latest_height, + }; + log_trace!(logger, "Updating claims view at height {} with {} matched transactions and {} claim requests", height, txn_matched.len(), claimable_outpoints.len()); let mut new_claims = Vec::new(); let mut aggregated_claim = HashMap::new(); let mut aggregated_soonest = ::std::u32::MAX; @@ -829,7 +856,7 @@ impl OnchainTxHandler { } } - pub(super) fn block_disconnected(&mut self, height: u32, broadcaster: B, fee_estimator: F, logger: L) + pub(crate) fn block_disconnected(&mut self, height: u32, broadcaster: B, fee_estimator: F, logger: L) where B::Target: BroadcasterInterface, F::Target: FeeEstimator, L::Target: Logger, @@ -855,7 +882,7 @@ impl OnchainTxHandler { } } for (_, claim_material) in bump_candidates.iter_mut() { - if let Some((new_timer, new_feerate, bump_tx)) = self.generate_claim_tx(height, &claim_material, &*fee_estimator, &*logger) { + if let Some((new_timer, new_feerate, bump_tx)) = self.generate_claim_tx(height, &claim_material, &&*fee_estimator, &&*logger) { claim_material.height_timer = new_timer; claim_material.feerate_previous = new_feerate; broadcaster.broadcast_transaction(&bump_tx); @@ -877,102 +904,86 @@ impl OnchainTxHandler { } } - pub(super) fn provide_latest_holder_tx(&mut self, tx: HolderCommitmentTransaction) -> Result<(), ()> { - // To prevent any unsafe state discrepancy between offchain and onchain, once holder - // commitment transaction has been signed due to an event (either block height for - // HTLC-timeout or channel force-closure), don't allow any further update of holder - // commitment transaction view to avoid delivery of revocation secret to counterparty - // for the aformentionned signed transaction. - if self.holder_htlc_sigs.is_some() || self.prev_holder_htlc_sigs.is_some() { - return Err(()); - } - self.prev_holder_commitment = self.holder_commitment.take(); - self.holder_commitment = Some(tx); - Ok(()) + pub(crate) fn provide_latest_holder_tx(&mut self, tx: HolderCommitmentTransaction) { + self.prev_holder_commitment = Some(replace(&mut self.holder_commitment, tx)); + self.holder_htlc_sigs = None; } + // Normally holder HTLCs are signed at the same time as the holder commitment tx. However, + // in some configurations, the holder commitment tx has been signed and broadcast by a + // ChannelMonitor replica, so we handle that case here. fn sign_latest_holder_htlcs(&mut self) { - if let Some(ref holder_commitment) = self.holder_commitment { - if let Ok(sigs) = self.key_storage.sign_holder_commitment_htlc_transactions(holder_commitment, &self.secp_ctx) { - self.holder_htlc_sigs = Some(Vec::new()); - let ret = self.holder_htlc_sigs.as_mut().unwrap(); - for (htlc_idx, (holder_sig, &(ref htlc, _))) in sigs.iter().zip(holder_commitment.per_htlc.iter()).enumerate() { - if let Some(tx_idx) = htlc.transaction_output_index { - if ret.len() <= tx_idx as usize { ret.resize(tx_idx as usize + 1, None); } - ret[tx_idx as usize] = Some((htlc_idx, holder_sig.expect("Did not receive a signature for a non-dust HTLC"))); - } else { - assert!(holder_sig.is_none(), "Received a signature for a dust HTLC"); - } - } - } + if self.holder_htlc_sigs.is_none() { + let (_sig, sigs) = self.key_storage.sign_holder_commitment_and_htlcs(&self.holder_commitment, &self.secp_ctx).expect("sign holder commitment"); + self.holder_htlc_sigs = Some(Self::extract_holder_sigs(&self.holder_commitment, sigs)); } } + + // Normally only the latest commitment tx and HTLCs need to be signed. However, in some + // configurations we may have updated our holder commitment but a replica of the ChannelMonitor + // broadcast the previous one before we sync with it. We handle that case here. fn sign_prev_holder_htlcs(&mut self) { - if let Some(ref holder_commitment) = self.prev_holder_commitment { - if let Ok(sigs) = self.key_storage.sign_holder_commitment_htlc_transactions(holder_commitment, &self.secp_ctx) { - self.prev_holder_htlc_sigs = Some(Vec::new()); - let ret = self.prev_holder_htlc_sigs.as_mut().unwrap(); - for (htlc_idx, (holder_sig, &(ref htlc, _))) in sigs.iter().zip(holder_commitment.per_htlc.iter()).enumerate() { - if let Some(tx_idx) = htlc.transaction_output_index { - if ret.len() <= tx_idx as usize { ret.resize(tx_idx as usize + 1, None); } - ret[tx_idx as usize] = Some((htlc_idx, holder_sig.expect("Did not receive a signature for a non-dust HTLC"))); - } else { - assert!(holder_sig.is_none(), "Received a signature for a dust HTLC"); - } - } + if self.prev_holder_htlc_sigs.is_none() { + if let Some(ref holder_commitment) = self.prev_holder_commitment { + let (_sig, sigs) = self.key_storage.sign_holder_commitment_and_htlcs(holder_commitment, &self.secp_ctx).expect("sign previous holder commitment"); + self.prev_holder_htlc_sigs = Some(Self::extract_holder_sigs(holder_commitment, sigs)); } } } - //TODO: getting lastest holder transactions should be infaillible and result in us "force-closing the channel", but we may + fn extract_holder_sigs(holder_commitment: &HolderCommitmentTransaction, sigs: Vec) -> Vec> { + let mut ret = Vec::new(); + for (htlc_idx, (holder_sig, htlc)) in sigs.iter().zip(holder_commitment.htlcs().iter()).enumerate() { + let tx_idx = htlc.transaction_output_index.unwrap(); + if ret.len() <= tx_idx as usize { ret.resize(tx_idx as usize + 1, None); } + ret[tx_idx as usize] = Some((htlc_idx, holder_sig.clone())); + } + ret + } + + //TODO: getting lastest holder transactions should be infallible and result in us "force-closing the channel", but we may // have empty holder commitment transaction if a ChannelMonitor is asked to force-close just after Channel::get_outbound_funding_created, // before providing a initial commitment transaction. For outbound channel, init ChannelMonitor at Channel::funding_signed, there is nothing // to monitor before. - pub(super) fn get_fully_signed_holder_tx(&mut self, funding_redeemscript: &Script) -> Option { - if let Some(ref mut holder_commitment) = self.holder_commitment { - match self.key_storage.sign_holder_commitment(holder_commitment, &self.secp_ctx) { - Ok(sig) => Some(holder_commitment.add_holder_sig(funding_redeemscript, sig)), - Err(_) => return None, - } - } else { - None - } + pub(crate) fn get_fully_signed_holder_tx(&mut self, funding_redeemscript: &Script) -> Transaction { + let (sig, htlc_sigs) = self.key_storage.sign_holder_commitment_and_htlcs(&self.holder_commitment, &self.secp_ctx).expect("signing holder commitment"); + self.holder_htlc_sigs = Some(Self::extract_holder_sigs(&self.holder_commitment, htlc_sigs)); + self.holder_commitment.add_holder_sig(funding_redeemscript, sig) } #[cfg(any(test, feature="unsafe_revoked_tx_signing"))] - pub(super) fn get_fully_signed_copy_holder_tx(&mut self, funding_redeemscript: &Script) -> Option { - if let Some(ref mut holder_commitment) = self.holder_commitment { - let holder_commitment = holder_commitment.clone(); - match self.key_storage.sign_holder_commitment(&holder_commitment, &self.secp_ctx) { - Ok(sig) => Some(holder_commitment.add_holder_sig(funding_redeemscript, sig)), - Err(_) => return None, - } - } else { - None - } + pub(crate) fn get_fully_signed_copy_holder_tx(&mut self, funding_redeemscript: &Script) -> Transaction { + let (sig, htlc_sigs) = self.key_storage.unsafe_sign_holder_commitment_and_htlcs(&self.holder_commitment, &self.secp_ctx).expect("sign holder commitment"); + self.holder_htlc_sigs = Some(Self::extract_holder_sigs(&self.holder_commitment, htlc_sigs)); + self.holder_commitment.add_holder_sig(funding_redeemscript, sig) } - pub(super) fn get_fully_signed_htlc_tx(&mut self, outp: &::bitcoin::OutPoint, preimage: &Option) -> Option { + pub(crate) fn get_fully_signed_htlc_tx(&mut self, outp: &::bitcoin::OutPoint, preimage: &Option) -> Option { let mut htlc_tx = None; - if self.holder_commitment.is_some() { - let commitment_txid = self.holder_commitment.as_ref().unwrap().txid(); - if commitment_txid == outp.txid { - self.sign_latest_holder_htlcs(); - if let &Some(ref htlc_sigs) = &self.holder_htlc_sigs { - let &(ref htlc_idx, ref htlc_sig) = htlc_sigs[outp.vout as usize].as_ref().unwrap(); - htlc_tx = Some(self.holder_commitment.as_ref().unwrap() - .get_signed_htlc_tx(*htlc_idx, htlc_sig, preimage, self.on_holder_tx_csv)); - } + let commitment_txid = self.holder_commitment.trust().txid(); + // Check if the HTLC spends from the current holder commitment + if commitment_txid == outp.txid { + self.sign_latest_holder_htlcs(); + if let &Some(ref htlc_sigs) = &self.holder_htlc_sigs { + let &(ref htlc_idx, ref htlc_sig) = htlc_sigs[outp.vout as usize].as_ref().unwrap(); + let trusted_tx = self.holder_commitment.trust(); + let counterparty_htlc_sig = self.holder_commitment.counterparty_htlc_sigs[*htlc_idx]; + htlc_tx = Some(trusted_tx + .get_signed_htlc_tx(&self.channel_transaction_parameters.as_holder_broadcastable(), *htlc_idx, &counterparty_htlc_sig, htlc_sig, preimage)); } } - if self.prev_holder_commitment.is_some() { - let commitment_txid = self.prev_holder_commitment.as_ref().unwrap().txid(); + // If the HTLC doesn't spend the current holder commitment, check if it spends the previous one + if htlc_tx.is_none() && self.prev_holder_commitment.is_some() { + let commitment_txid = self.prev_holder_commitment.as_ref().unwrap().trust().txid(); if commitment_txid == outp.txid { self.sign_prev_holder_htlcs(); if let &Some(ref htlc_sigs) = &self.prev_holder_htlc_sigs { let &(ref htlc_idx, ref htlc_sig) = htlc_sigs[outp.vout as usize].as_ref().unwrap(); - htlc_tx = Some(self.prev_holder_commitment.as_ref().unwrap() - .get_signed_htlc_tx(*htlc_idx, htlc_sig, preimage, self.on_holder_tx_csv)); + let holder_commitment = self.prev_holder_commitment.as_ref().unwrap(); + let trusted_tx = holder_commitment.trust(); + let counterparty_htlc_sig = holder_commitment.counterparty_htlc_sigs[*htlc_idx]; + htlc_tx = Some(trusted_tx + .get_signed_htlc_tx(&self.channel_transaction_parameters.as_holder_broadcastable(), *htlc_idx, &counterparty_htlc_sig, htlc_sig, preimage)); } } } @@ -980,7 +991,7 @@ impl OnchainTxHandler { } #[cfg(any(test,feature = "unsafe_revoked_tx_signing"))] - pub(super) fn unsafe_get_fully_signed_htlc_tx(&mut self, outp: &::bitcoin::OutPoint, preimage: &Option) -> Option { + pub(crate) fn unsafe_get_fully_signed_htlc_tx(&mut self, outp: &::bitcoin::OutPoint, preimage: &Option) -> Option { let latest_had_sigs = self.holder_htlc_sigs.is_some(); let prev_had_sigs = self.prev_holder_htlc_sigs.is_some(); let ret = self.get_fully_signed_htlc_tx(outp, preimage);