From 92424ebbfe6d0ffd9f9312df2dcb04d7ee934718 Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Fri, 4 Jan 2019 14:37:48 -0500 Subject: [PATCH] Merge tracking of HTLCs-in-commitment with outbound-HTLCs This simplifies a few things, deduplicates a some small memory overhead, and, most importantly, is a first step to fixing would_broadcast_at_height. --- src/ln/channel.rs | 79 ++++++----- src/ln/channelmonitor.rs | 298 +++++++++++++++++---------------------- 2 files changed, 173 insertions(+), 204 deletions(-) diff --git a/src/ln/channel.rs b/src/ln/channel.rs index 2d579e970..237ccd840 100644 --- a/src/ln/channel.rs +++ b/src/ln/channel.rs @@ -747,8 +747,12 @@ impl Channel { /// have not yet committed it. Such HTLCs will only be included in transactions which are being /// generated by the peer which proposed adding the HTLCs, and thus we need to understand both /// which peer generated this transaction and "to whom" this transaction flows. + /// Returns (the transaction built, the number of HTLC outputs which were present in the + /// transaction, the list of HTLCs which were not ignored when building the transaction). + /// Note that below-dust HTLCs are included in the third return value, but not the second, and + /// sources are provided only for outbound HTLCs in the third return value. #[inline] - fn build_commitment_transaction(&self, commitment_number: u64, keys: &TxCreationKeys, local: bool, generated_by_local: bool, feerate_per_kw: u64) -> (Transaction, Vec, Vec<(PaymentHash, &HTLCSource, Option)>) { + fn build_commitment_transaction(&self, commitment_number: u64, keys: &TxCreationKeys, local: bool, generated_by_local: bool, feerate_per_kw: u64) -> (Transaction, usize, Vec<(HTLCOutputInCommitment, Option<&HTLCSource>)>) { let obscured_commitment_transaction_number = self.get_commitment_transaction_number_obscure_factor() ^ (INITIAL_COMMITMENT_NUMBER - commitment_number); let txins = { @@ -915,30 +919,23 @@ impl Channel { transaction_utils::sort_outputs(&mut txouts); let mut outputs: Vec = Vec::with_capacity(txouts.len()); - let mut htlcs_included: Vec = Vec::with_capacity(txouts.len()); - let mut htlc_sources: Vec<(PaymentHash, &HTLCSource, Option)> = Vec::with_capacity(txouts.len() + included_dust_htlcs.len()); - for (idx, out) in txouts.drain(..).enumerate() { + let mut htlcs_included: Vec<(HTLCOutputInCommitment, Option<&HTLCSource>)> = Vec::with_capacity(txouts.len() + included_dust_htlcs.len()); + for (idx, mut out) in txouts.drain(..).enumerate() { outputs.push(out.0); - if let Some((mut htlc, source_option)) = out.1 { + if let Some((mut htlc, source_option)) = out.1.take() { htlc.transaction_output_index = Some(idx as u32); - if let Some(source) = source_option { - htlc_sources.push((htlc.payment_hash, source, Some(idx as u32))); - } - htlcs_included.push(htlc); - } - } - for (htlc, source_option) in included_dust_htlcs.drain(..) { - if let Some(source) = source_option { - htlc_sources.push((htlc.payment_hash, source, None)); + htlcs_included.push((htlc, source_option)); } } + let non_dust_htlc_count = htlcs_included.len(); + htlcs_included.append(&mut included_dust_htlcs); (Transaction { version: 2, lock_time: ((0x20 as u32) << 8*3) | ((obscured_commitment_transaction_number & 0xffffffu64) as u32), input: txins, output: outputs, - }, htlcs_included, htlc_sources) + }, non_dust_htlc_count, htlcs_included) } #[inline] @@ -1451,9 +1448,9 @@ impl Channel { // Now that we're past error-generating stuff, update our local state: - self.channel_monitor.provide_latest_remote_commitment_tx_info(&remote_initial_commitment_tx, Vec::new(), Vec::new(), self.cur_remote_commitment_transaction_number, self.their_cur_commitment_point.unwrap()); + self.channel_monitor.provide_latest_remote_commitment_tx_info(&remote_initial_commitment_tx, Vec::new(), self.cur_remote_commitment_transaction_number, self.their_cur_commitment_point.unwrap()); self.last_local_commitment_txn = vec![local_initial_commitment_tx.clone()]; - self.channel_monitor.provide_latest_local_commitment_tx_info(local_initial_commitment_tx, local_keys, self.feerate_per_kw, Vec::new(), Vec::new()); + self.channel_monitor.provide_latest_local_commitment_tx_info(local_initial_commitment_tx, local_keys, self.feerate_per_kw, Vec::new()); self.channel_state = ChannelState::FundingSent as u32; self.channel_id = funding_txo.to_channel_id(); self.cur_remote_commitment_transaction_number -= 1; @@ -1490,7 +1487,7 @@ impl Channel { secp_check!(self.secp_ctx.verify(&local_sighash, &msg.signature, &self.their_funding_pubkey.unwrap()), "Invalid funding_signed signature from peer"); self.sign_commitment_transaction(&mut local_initial_commitment_tx, &msg.signature); - self.channel_monitor.provide_latest_local_commitment_tx_info(local_initial_commitment_tx.clone(), local_keys, self.feerate_per_kw, Vec::new(), Vec::new()); + self.channel_monitor.provide_latest_local_commitment_tx_info(local_initial_commitment_tx.clone(), local_keys, self.feerate_per_kw, Vec::new()); self.last_local_commitment_txn = vec![local_initial_commitment_tx]; self.channel_state = ChannelState::FundingSent as u32; self.cur_local_commitment_transaction_number -= 1; @@ -1693,7 +1690,7 @@ impl Channel { let mut local_commitment_tx = { let mut commitment_tx = self.build_commitment_transaction(self.cur_local_commitment_transaction_number, &local_keys, true, false, feerate_per_kw); - let htlcs_cloned: Vec<_> = commitment_tx.2.drain(..).map(|htlc_source| (htlc_source.0, htlc_source.1.clone(), htlc_source.2)).collect(); + let htlcs_cloned: Vec<_> = commitment_tx.2.drain(..).map(|htlc| (htlc.0, htlc.1.map(|h| h.clone()))).collect(); (commitment_tx.0, commitment_tx.1, htlcs_cloned) }; let local_commitment_txid = local_commitment_tx.0.txid(); @@ -1702,7 +1699,7 @@ impl Channel { //If channel fee was updated by funder confirm funder can afford the new fee rate when applied to the current local commitment transaction if update_fee { - let num_htlcs = local_commitment_tx.1.len(); + let num_htlcs = local_commitment_tx.1; let total_fee: u64 = feerate_per_kw as u64 * (COMMITMENT_TX_BASE_WEIGHT + (num_htlcs as u64) * COMMITMENT_TX_WEIGHT_PER_HTLC) / 1000; if self.channel_value_satoshis - self.value_to_self_msat / 1000 < total_fee + self.their_channel_reserve_satoshis { @@ -1710,16 +1707,16 @@ impl Channel { } } - if msg.htlc_signatures.len() != local_commitment_tx.1.len() { + if msg.htlc_signatures.len() != local_commitment_tx.1 { return Err(ChannelError::Close("Got wrong number of HTLC signatures from remote")); } - let mut new_local_commitment_txn = Vec::with_capacity(local_commitment_tx.1.len() + 1); + let mut new_local_commitment_txn = Vec::with_capacity(local_commitment_tx.1 + 1); self.sign_commitment_transaction(&mut local_commitment_tx.0, &msg.signature); new_local_commitment_txn.push(local_commitment_tx.0.clone()); - let mut htlcs_and_sigs = Vec::with_capacity(local_commitment_tx.1.len()); - for (idx, htlc) in local_commitment_tx.1.drain(..).enumerate() { + let mut htlcs_and_sigs = Vec::with_capacity(local_commitment_tx.2.len()); + for (idx, (htlc, source)) in local_commitment_tx.2.drain(..).enumerate() { if let Some(_) = htlc.transaction_output_index { let mut htlc_tx = self.build_htlc_transaction(&local_commitment_txid, &htlc, true, &local_keys, feerate_per_kw); let htlc_redeemscript = chan_utils::get_htlc_redeemscript(&htlc, &local_keys); @@ -1732,7 +1729,9 @@ impl Channel { } else { self.create_htlc_tx_signature(&htlc_tx, &htlc, &local_keys)?.1 }; - htlcs_and_sigs.push((htlc, msg.htlc_signatures[idx], htlc_sig)); + htlcs_and_sigs.push((htlc, Some((msg.htlc_signatures[idx], htlc_sig)), source)); + } else { + htlcs_and_sigs.push((htlc, None, source)); } } @@ -1760,7 +1759,7 @@ impl Channel { self.monitor_pending_order = None; } - self.channel_monitor.provide_latest_local_commitment_tx_info(local_commitment_tx.0, local_keys, self.feerate_per_kw, htlcs_and_sigs, local_commitment_tx.2); + self.channel_monitor.provide_latest_local_commitment_tx_info(local_commitment_tx.0, local_keys, self.feerate_per_kw, htlcs_and_sigs); for htlc in self.pending_inbound_htlcs.iter_mut() { let new_forward = if let &InboundHTLCState::RemoteAnnounced(ref forward_info) = &htlc.state { @@ -3030,7 +3029,7 @@ impl Channel { let temporary_channel_id = self.channel_id; // Now that we're past error-generating stuff, update our local state: - self.channel_monitor.provide_latest_remote_commitment_tx_info(&commitment_tx, Vec::new(), Vec::new(), self.cur_remote_commitment_transaction_number, self.their_cur_commitment_point.unwrap()); + self.channel_monitor.provide_latest_remote_commitment_tx_info(&commitment_tx, Vec::new(), self.cur_remote_commitment_transaction_number, self.their_cur_commitment_point.unwrap()); self.channel_state = ChannelState::FundingCreated as u32; self.channel_id = funding_txo.to_channel_id(); self.cur_remote_commitment_transaction_number -= 1; @@ -3262,23 +3261,23 @@ impl Channel { } } - let (res, remote_commitment_tx, htlcs, htlc_sources) = match self.send_commitment_no_state_update() { - Ok((res, (remote_commitment_tx, htlcs, mut htlc_sources))) => { + let (res, remote_commitment_tx, htlcs) = match self.send_commitment_no_state_update() { + Ok((res, (remote_commitment_tx, mut htlcs))) => { // Update state now that we've passed all the can-fail calls... - let htlc_sources_no_ref = htlc_sources.drain(..).map(|htlc_source| (htlc_source.0, htlc_source.1.clone(), htlc_source.2)).collect(); - (res, remote_commitment_tx, htlcs, htlc_sources_no_ref) + let htlcs_no_ref = htlcs.drain(..).map(|(htlc, htlc_source)| (htlc, htlc_source.map(|source_ref| Box::new(source_ref.clone())))).collect(); + (res, remote_commitment_tx, htlcs_no_ref) }, Err(e) => return Err(e), }; - self.channel_monitor.provide_latest_remote_commitment_tx_info(&remote_commitment_tx, htlcs, htlc_sources, self.cur_remote_commitment_transaction_number, self.their_cur_commitment_point.unwrap()); + self.channel_monitor.provide_latest_remote_commitment_tx_info(&remote_commitment_tx, htlcs, self.cur_remote_commitment_transaction_number, self.their_cur_commitment_point.unwrap()); self.channel_state |= ChannelState::AwaitingRemoteRevoke as u32; Ok((res, self.channel_monitor.clone())) } /// Only fails in case of bad keys. Used for channel_reestablish commitment_signed generation /// when we shouldn't change HTLC/channel state. - fn send_commitment_no_state_update(&self) -> Result<(msgs::CommitmentSigned, (Transaction, Vec, Vec<(PaymentHash, &HTLCSource, Option)>)), ChannelError> { + fn send_commitment_no_state_update(&self) -> Result<(msgs::CommitmentSigned, (Transaction, Vec<(HTLCOutputInCommitment, Option<&HTLCSource>)>)), ChannelError> { let funding_script = self.get_funding_redeemscript(); let mut feerate_per_kw = self.feerate_per_kw; @@ -3294,9 +3293,8 @@ impl Channel { let remote_sighash = Message::from_slice(&bip143::SighashComponents::new(&remote_commitment_tx.0).sighash_all(&remote_commitment_tx.0.input[0], &funding_script, self.channel_value_satoshis)[..]).unwrap(); let our_sig = self.secp_ctx.sign(&remote_sighash, &self.local_keys.funding_key); - let mut htlc_sigs = Vec::new(); - - for ref htlc in remote_commitment_tx.1.iter() { + let mut htlc_sigs = Vec::with_capacity(remote_commitment_tx.1); + for &(ref htlc, _) in remote_commitment_tx.2.iter() { if let Some(_) = htlc.transaction_output_index { let htlc_tx = self.build_htlc_transaction(&remote_commitment_txid, htlc, false, &remote_keys, feerate_per_kw); let htlc_redeemscript = chan_utils::get_htlc_redeemscript(&htlc, &remote_keys); @@ -3310,7 +3308,7 @@ impl Channel { channel_id: self.channel_id, signature: our_sig, htlc_signatures: htlc_sigs, - }, remote_commitment_tx)) + }, (remote_commitment_tx.0, remote_commitment_tx.2))) } /// Adds a pending outbound HTLC to this channel, and creates a signed commitment transaction @@ -4024,8 +4022,11 @@ mod tests { macro_rules! test_commitment { ( $their_sig_hex: expr, $our_sig_hex: expr, $tx_hex: expr) => { unsigned_tx = { - let res = chan.build_commitment_transaction(0xffffffffffff - 42, &keys, true, false, chan.feerate_per_kw); - (res.0, res.1) + let mut res = chan.build_commitment_transaction(0xffffffffffff - 42, &keys, true, false, chan.feerate_per_kw); + let htlcs = res.2.drain(..) + .filter_map(|(htlc, _)| if htlc.transaction_output_index.is_some() { Some(htlc) } else { None }) + .collect(); + (res.0, htlcs) }; let their_signature = Signature::from_der(&secp_ctx, &hex::decode($their_sig_hex).unwrap()[..]).unwrap(); let sighash = Message::from_slice(&bip143::SighashComponents::new(&unsigned_tx.0).sighash_all(&unsigned_tx.0.input[0], &chan.get_funding_redeemscript(), chan.channel_value_satoshis)[..]).unwrap(); diff --git a/src/ln/channelmonitor.rs b/src/ln/channelmonitor.rs index 04efd0a8d..c1c24f5fa 100644 --- a/src/ln/channelmonitor.rs +++ b/src/ln/channelmonitor.rs @@ -332,8 +332,7 @@ struct LocalSignedTx { b_htlc_key: PublicKey, delayed_payment_key: PublicKey, feerate_per_kw: u64, - htlc_outputs: Vec<(HTLCOutputInCommitment, Signature, Signature)>, - htlc_sources: Vec<(PaymentHash, HTLCSource, Option)>, + htlc_outputs: Vec<(HTLCOutputInCommitment, Option<(Signature, Signature)>, Option)>, } const SERIALIZATION_VERSION: u8 = 1; @@ -358,7 +357,7 @@ pub struct ChannelMonitor { their_to_self_delay: Option, old_secrets: [([u8; 32], u64); 49], - remote_claimable_outpoints: HashMap, Vec<(PaymentHash, HTLCSource, Option)>)>, + remote_claimable_outpoints: HashMap>)>>, /// We cannot identify HTLC-Success or HTLC-Timeout transactions by themselves on the chain. /// Nor can we figure out their commitment numbers without the commitment transaction they are /// spending. Thus, in order to claim them via revocation key, we track all the remote @@ -515,7 +514,9 @@ impl ChannelMonitor { // TODO: We should probably consider whether we're really getting the next secret here. if let Storage::Local { ref mut prev_remote_commitment_txid, .. } = self.key_storage { if let Some(txid) = prev_remote_commitment_txid.take() { - self.remote_claimable_outpoints.get_mut(&txid).unwrap().1 = Vec::new(); + for &mut (_, ref mut source) in self.remote_claimable_outpoints.get_mut(&txid).unwrap() { + *source = None; + } } } @@ -558,12 +559,12 @@ impl ChannelMonitor { /// The monitor watches for it to be broadcasted and then uses the HTLC information (and /// possibly future revocation/preimage information) to claim outputs where possible. /// We cache also the mapping hash:commitment number to lighten pruning of old preimages by watchtowers. - pub(super) fn provide_latest_remote_commitment_tx_info(&mut self, unsigned_commitment_tx: &Transaction, htlc_outputs: Vec, htlc_sources: Vec<(PaymentHash, HTLCSource, Option)>, commitment_number: u64, their_revocation_point: PublicKey) { + pub(super) fn provide_latest_remote_commitment_tx_info(&mut self, unsigned_commitment_tx: &Transaction, htlc_outputs: Vec<(HTLCOutputInCommitment, Option>)>, commitment_number: u64, their_revocation_point: PublicKey) { // TODO: Encrypt the htlc_outputs data with the single-hash of the commitment transaction // so that a remote monitor doesn't learn anything unless there is a malicious close. // (only maybe, sadly we cant do the same for local info, as we need to be aware of // timeouts) - for ref htlc in &htlc_outputs { + for &(ref htlc, _) in &htlc_outputs { self.remote_hash_commitment_number.insert(htlc.payment_hash, commitment_number); } @@ -574,7 +575,7 @@ impl ChannelMonitor { *prev_remote_commitment_txid = current_remote_commitment_txid.take(); *current_remote_commitment_txid = Some(new_txid); } - self.remote_claimable_outpoints.insert(new_txid, (htlc_outputs, htlc_sources)); + self.remote_claimable_outpoints.insert(new_txid, htlc_outputs); self.current_remote_commitment_number = commitment_number; //TODO: Merge this into the other per-remote-transaction output storage stuff match self.their_cur_revocation_points { @@ -604,7 +605,7 @@ impl ChannelMonitor { /// Panics if set_their_to_self_delay has never been called. /// Also update Storage with latest local per_commitment_point to derive local_delayedkey in /// case of onchain HTLC tx - pub(super) fn provide_latest_local_commitment_tx_info(&mut self, signed_commitment_tx: Transaction, local_keys: chan_utils::TxCreationKeys, feerate_per_kw: u64, htlc_outputs: Vec<(HTLCOutputInCommitment, Signature, Signature)>, htlc_sources: Vec<(PaymentHash, HTLCSource, Option)>) { + pub(super) fn provide_latest_local_commitment_tx_info(&mut self, signed_commitment_tx: Transaction, local_keys: chan_utils::TxCreationKeys, feerate_per_kw: u64, htlc_outputs: Vec<(HTLCOutputInCommitment, Option<(Signature, Signature)>, Option)>) { assert!(self.their_to_self_delay.is_some()); self.prev_local_signed_commitment_tx = self.current_local_signed_commitment_tx.take(); self.current_local_signed_commitment_tx = Some(LocalSignedTx { @@ -616,7 +617,6 @@ impl ChannelMonitor { delayed_payment_key: local_keys.a_delayed_payment_key, feerate_per_kw, htlc_outputs, - htlc_sources, }); if let Storage::Local { ref mut latest_per_commitment_point, .. } = self.key_storage { @@ -863,30 +863,13 @@ impl ChannelMonitor { } } - macro_rules! serialize_htlc_source { - ($htlc_source: expr) => { - $htlc_source.0.write(writer)?; - $htlc_source.1.write(writer)?; - if let &Some(ref txo) = &$htlc_source.2 { - writer.write_all(&[1; 1])?; - txo.write(writer)?; - } else { - writer.write_all(&[0; 1])?; - } - } - } - - writer.write_all(&byte_utils::be64_to_array(self.remote_claimable_outpoints.len() as u64))?; - for (ref txid, &(ref htlc_infos, ref htlc_sources)) in self.remote_claimable_outpoints.iter() { + for (ref txid, ref htlc_infos) in self.remote_claimable_outpoints.iter() { writer.write_all(&txid[..])?; writer.write_all(&byte_utils::be64_to_array(htlc_infos.len() as u64))?; - for ref htlc_output in htlc_infos.iter() { + for &(ref htlc_output, ref htlc_source) in htlc_infos.iter() { serialize_htlc_in_commitment!(htlc_output); - } - writer.write_all(&byte_utils::be64_to_array(htlc_sources.len() as u64))?; - for ref htlc_source in htlc_sources.iter() { - serialize_htlc_source!(htlc_source); + write_option!(htlc_source); } } @@ -926,14 +909,16 @@ impl ChannelMonitor { writer.write_all(&byte_utils::be64_to_array($local_tx.feerate_per_kw))?; writer.write_all(&byte_utils::be64_to_array($local_tx.htlc_outputs.len() as u64))?; - for &(ref htlc_output, ref their_sig, ref our_sig) in $local_tx.htlc_outputs.iter() { + for &(ref htlc_output, ref sigs, ref htlc_source) in $local_tx.htlc_outputs.iter() { serialize_htlc_in_commitment!(htlc_output); - writer.write_all(&their_sig.serialize_compact(&self.secp_ctx))?; - writer.write_all(&our_sig.serialize_compact(&self.secp_ctx))?; - } - writer.write_all(&byte_utils::be64_to_array($local_tx.htlc_sources.len() as u64))?; - for ref htlc_source in $local_tx.htlc_sources.iter() { - serialize_htlc_source!(htlc_source); + if let &Some((ref their_sig, ref our_sig)) = sigs { + 1u8.write(writer)?; + writer.write_all(&their_sig.serialize_compact(&self.secp_ctx))?; + writer.write_all(&our_sig.serialize_compact(&self.secp_ctx))?; + } else { + 0u8.write(writer)?; + } + write_option!(htlc_source); } } } @@ -1117,7 +1102,7 @@ impl ChannelMonitor { let (sig, redeemscript) = match self.key_storage { Storage::Local { ref revocation_base_key, .. } => { let redeemscript = if $htlc_idx.is_none() { revokeable_redeemscript.clone() } else { - let htlc = &per_commitment_option.unwrap().0[$htlc_idx.unwrap()]; + let htlc = &per_commitment_option.unwrap()[$htlc_idx.unwrap()].0; chan_utils::get_htlc_redeemscript_with_explicit_keys(htlc, &a_htlc_key, &b_htlc_key, &revocation_pubkey) }; let sighash = ignore_error!(Message::from_slice(&$sighash_parts.sighash_all(&$input, &redeemscript, $amount)[..])); @@ -1140,10 +1125,10 @@ impl ChannelMonitor { } } - if let Some(&(ref per_commitment_data, _)) = per_commitment_option { + if let Some(ref per_commitment_data) = per_commitment_option { inputs.reserve_exact(per_commitment_data.len()); - for (idx, ref htlc) in per_commitment_data.iter().enumerate() { + for (idx, &(ref htlc, _)) in per_commitment_data.iter().enumerate() { if let Some(transaction_output_index) = htlc.transaction_output_index { let expected_script = chan_utils::get_htlc_redeemscript_with_explicit_keys(&htlc, &a_htlc_key, &b_htlc_key, &revocation_pubkey); if transaction_output_index as usize >= tx.output.len() || @@ -1194,10 +1179,12 @@ impl ChannelMonitor { // on-chain claims, so we can do that at the same time. macro_rules! check_htlc_fails { ($txid: expr, $commitment_tx: expr) => { - if let Some(&(_, ref outpoints)) = self.remote_claimable_outpoints.get(&$txid) { - for &(ref payment_hash, ref source, _) in outpoints.iter() { - log_trace!(self, "Failing HTLC with payment_hash {} from {} remote commitment tx due to broadcast of revoked remote commitment transaction", log_bytes!(payment_hash.0), $commitment_tx); - htlc_updated.push(((*source).clone(), None, payment_hash.clone())); + if let Some(ref outpoints) = self.remote_claimable_outpoints.get(&$txid) { + for &(ref htlc, ref source_option) in outpoints.iter() { + if let &Some(ref source) = source_option { + log_trace!(self, "Failing HTLC with payment_hash {} from {} remote commitment tx due to broadcast of revoked remote commitment transaction", log_bytes!(htlc.payment_hash.0), $commitment_tx); + htlc_updated.push(((**source).clone(), None, htlc.payment_hash.clone())); + } } } } @@ -1256,24 +1243,26 @@ impl ChannelMonitor { // on-chain claims, so we can do that at the same time. macro_rules! check_htlc_fails { ($txid: expr, $commitment_tx: expr, $id: tt) => { - if let Some(&(_, ref latest_outpoints)) = self.remote_claimable_outpoints.get(&$txid) { - $id: for &(ref payment_hash, ref source, _) in latest_outpoints.iter() { - // Check if the HTLC is present in the commitment transaction that was - // broadcast, but not if it was below the dust limit, which we should - // fail backwards immediately as there is no way for us to learn the - // payment_preimage. - // Note that if the dust limit were allowed to change between - // commitment transactions we'd want to be check whether *any* - // broadcastable commitment transaction has the HTLC in it, but it - // cannot currently change after channel initialization, so we don't - // need to here. - for &(_, ref broadcast_source, ref output_idx) in per_commitment_data.1.iter() { - if output_idx.is_some() && source == broadcast_source { - continue $id; + if let Some(ref latest_outpoints) = self.remote_claimable_outpoints.get(&$txid) { + $id: for &(ref htlc, ref source_option) in latest_outpoints.iter() { + if let &Some(ref source) = source_option { + // Check if the HTLC is present in the commitment transaction that was + // broadcast, but not if it was below the dust limit, which we should + // fail backwards immediately as there is no way for us to learn the + // payment_preimage. + // Note that if the dust limit were allowed to change between + // commitment transactions we'd want to be check whether *any* + // broadcastable commitment transaction has the HTLC in it, but it + // cannot currently change after channel initialization, so we don't + // need to here. + for &(ref broadcast_htlc, ref broadcast_source) in per_commitment_data.iter() { + if broadcast_htlc.transaction_output_index.is_some() && Some(source) == broadcast_source.as_ref() { + continue $id; + } } + log_trace!(self, "Failing HTLC with payment_hash {} from {} remote commitment tx due to broadcast of remote commitment transaction", log_bytes!(htlc.payment_hash.0), $commitment_tx); + htlc_updated.push(((**source).clone(), None, htlc.payment_hash.clone())); } - log_trace!(self, "Failing HTLC with payment_hash {} from {} remote commitment tx due to broadcast of remote commitment transaction", log_bytes!(payment_hash.0), $commitment_tx); - htlc_updated.push(((*source).clone(), None, payment_hash.clone())); } } } @@ -1336,7 +1325,7 @@ impl ChannelMonitor { { let (sig, redeemscript) = match self.key_storage { Storage::Local { ref htlc_base_key, .. } => { - let htlc = &per_commitment_option.unwrap().0[$input.sequence as usize]; + let htlc = &per_commitment_option.unwrap()[$input.sequence as usize].0; let redeemscript = chan_utils::get_htlc_redeemscript_with_explicit_keys(htlc, &a_htlc_key, &b_htlc_key, &revocation_pubkey); let sighash = ignore_error!(Message::from_slice(&$sighash_parts.sighash_all(&$input, &redeemscript, $amount)[..])); let htlc_key = ignore_error!(chan_utils::derive_private_key(&self.secp_ctx, revocation_point, &htlc_base_key)); @@ -1354,7 +1343,7 @@ impl ChannelMonitor { } } - for (idx, ref htlc) in per_commitment_data.0.iter().enumerate() { + for (idx, &(ref htlc, _)) in per_commitment_data.iter().enumerate() { if let Some(transaction_output_index) = htlc.transaction_output_index { let expected_script = chan_utils::get_htlc_redeemscript_with_explicit_keys(&htlc, &a_htlc_key, &b_htlc_key, &revocation_pubkey); if transaction_output_index as usize >= tx.output.len() || @@ -1575,42 +1564,44 @@ impl ChannelMonitor { } } - for &(ref htlc, ref their_sig, ref our_sig) in local_tx.htlc_outputs.iter() { + for &(ref htlc, ref sigs, _) in local_tx.htlc_outputs.iter() { if let Some(transaction_output_index) = htlc.transaction_output_index { - if htlc.offered { - let mut htlc_timeout_tx = chan_utils::build_htlc_transaction(&local_tx.txid, local_tx.feerate_per_kw, self.their_to_self_delay.unwrap(), htlc, &local_tx.delayed_payment_key, &local_tx.revocation_key); + if let &Some((ref their_sig, ref our_sig)) = sigs { + if htlc.offered { + let mut htlc_timeout_tx = chan_utils::build_htlc_transaction(&local_tx.txid, local_tx.feerate_per_kw, self.their_to_self_delay.unwrap(), htlc, &local_tx.delayed_payment_key, &local_tx.revocation_key); - htlc_timeout_tx.input[0].witness.push(Vec::new()); // First is the multisig dummy + htlc_timeout_tx.input[0].witness.push(Vec::new()); // First is the multisig dummy - htlc_timeout_tx.input[0].witness.push(their_sig.serialize_der(&self.secp_ctx).to_vec()); - htlc_timeout_tx.input[0].witness[1].push(SigHashType::All as u8); - htlc_timeout_tx.input[0].witness.push(our_sig.serialize_der(&self.secp_ctx).to_vec()); - htlc_timeout_tx.input[0].witness[2].push(SigHashType::All as u8); + htlc_timeout_tx.input[0].witness.push(their_sig.serialize_der(&self.secp_ctx).to_vec()); + htlc_timeout_tx.input[0].witness[1].push(SigHashType::All as u8); + htlc_timeout_tx.input[0].witness.push(our_sig.serialize_der(&self.secp_ctx).to_vec()); + htlc_timeout_tx.input[0].witness[2].push(SigHashType::All as u8); - htlc_timeout_tx.input[0].witness.push(Vec::new()); - htlc_timeout_tx.input[0].witness.push(chan_utils::get_htlc_redeemscript_with_explicit_keys(htlc, &local_tx.a_htlc_key, &local_tx.b_htlc_key, &local_tx.revocation_key).into_bytes()); + htlc_timeout_tx.input[0].witness.push(Vec::new()); + htlc_timeout_tx.input[0].witness.push(chan_utils::get_htlc_redeemscript_with_explicit_keys(htlc, &local_tx.a_htlc_key, &local_tx.b_htlc_key, &local_tx.revocation_key).into_bytes()); - add_dynamic_output!(htlc_timeout_tx, 0); - res.push(htlc_timeout_tx); - } else { - if let Some(payment_preimage) = self.payment_preimages.get(&htlc.payment_hash) { - let mut htlc_success_tx = chan_utils::build_htlc_transaction(&local_tx.txid, local_tx.feerate_per_kw, self.their_to_self_delay.unwrap(), htlc, &local_tx.delayed_payment_key, &local_tx.revocation_key); + add_dynamic_output!(htlc_timeout_tx, 0); + res.push(htlc_timeout_tx); + } else { + if let Some(payment_preimage) = self.payment_preimages.get(&htlc.payment_hash) { + let mut htlc_success_tx = chan_utils::build_htlc_transaction(&local_tx.txid, local_tx.feerate_per_kw, self.their_to_self_delay.unwrap(), htlc, &local_tx.delayed_payment_key, &local_tx.revocation_key); - htlc_success_tx.input[0].witness.push(Vec::new()); // First is the multisig dummy + htlc_success_tx.input[0].witness.push(Vec::new()); // First is the multisig dummy - htlc_success_tx.input[0].witness.push(their_sig.serialize_der(&self.secp_ctx).to_vec()); - htlc_success_tx.input[0].witness[1].push(SigHashType::All as u8); - htlc_success_tx.input[0].witness.push(our_sig.serialize_der(&self.secp_ctx).to_vec()); - htlc_success_tx.input[0].witness[2].push(SigHashType::All as u8); + htlc_success_tx.input[0].witness.push(their_sig.serialize_der(&self.secp_ctx).to_vec()); + htlc_success_tx.input[0].witness[1].push(SigHashType::All as u8); + htlc_success_tx.input[0].witness.push(our_sig.serialize_der(&self.secp_ctx).to_vec()); + htlc_success_tx.input[0].witness[2].push(SigHashType::All as u8); - htlc_success_tx.input[0].witness.push(payment_preimage.0.to_vec()); - htlc_success_tx.input[0].witness.push(chan_utils::get_htlc_redeemscript_with_explicit_keys(htlc, &local_tx.a_htlc_key, &local_tx.b_htlc_key, &local_tx.revocation_key).into_bytes()); + htlc_success_tx.input[0].witness.push(payment_preimage.0.to_vec()); + htlc_success_tx.input[0].witness.push(chan_utils::get_htlc_redeemscript_with_explicit_keys(htlc, &local_tx.a_htlc_key, &local_tx.b_htlc_key, &local_tx.revocation_key).into_bytes()); - add_dynamic_output!(htlc_success_tx, 0); - res.push(htlc_success_tx); + add_dynamic_output!(htlc_success_tx, 0); + res.push(htlc_success_tx); + } } - } - watch_outputs.push(local_tx.tx.output[transaction_output_index as usize].clone()); + watch_outputs.push(local_tx.tx.output[transaction_output_index as usize].clone()); + } else { panic!("Should have sigs for non-dust local tx outputs!") } } } @@ -1801,11 +1792,12 @@ impl ChannelMonitor { // needs to use the same logic as the revoked-tx-announe logic - checking the last two // remote commitment transactions. This probably has implications for what data we need to // store in local commitment transactions. - // TODO: We need to consider HTLCs which were below dust threshold here - while they don't + // Note that we consider HTLCs which were below dust threshold here - while they don't // strictly imply that we need to fail the channel, we need to go ahead and fail them back // to the source, and if we don't fail the channel we will have to ensure that the next // updates that peer sends us are update_fails, failing the channel if not. It's probably // easier to just fail the channel as this case should be rare enough anyway. + // TODO: Test below dust HTLC channel failing if let Some(ref cur_local_tx) = self.current_local_signed_commitment_tx { for &(ref htlc, _, _) in cur_local_tx.htlc_outputs.iter() { // For inbound HTLCs which we know the preimage for, we have to ensure we hit the @@ -1850,42 +1842,40 @@ impl ChannelMonitor { let offered_preimage_claim = input.witness.len() == 3 && input.witness[2].len() == OFFERED_HTLC_SCRIPT_WEIGHT; macro_rules! log_claim { - ($source: expr, $local_tx: expr, $outbound_htlc: expr, $payment_hash: expr, $source_avail: expr) => { + ($tx_info: expr, $local_tx: expr, $htlc: expr, $source_avail: expr) => { // We found the output in question, but aren't failing it backwards // as we have no corresponding source. This implies either it is an // inbound HTLC or an outbound HTLC on a revoked transaction. + let outbound_htlc = $local_tx == $htlc.offered; if ($local_tx && revocation_sig_claim) || - ($outbound_htlc && !$source_avail && (accepted_preimage_claim || offered_preimage_claim)) { + (outbound_htlc && !$source_avail && (accepted_preimage_claim || offered_preimage_claim)) { log_error!(self, "Input spending {} ({}:{}) in {} resolves {} HTLC with payment hash {} with {}!", - $source, input.previous_output.txid, input.previous_output.vout, tx.txid(), - if $outbound_htlc { "outbound" } else { "inbound" }, log_bytes!($payment_hash.0), + $tx_info, input.previous_output.txid, input.previous_output.vout, tx.txid(), + if outbound_htlc { "outbound" } else { "inbound" }, log_bytes!($htlc.payment_hash.0), if revocation_sig_claim { "revocation sig" } else { "preimage claim after we'd passed the HTLC resolution back" }); } else { log_info!(self, "Input spending {} ({}:{}) in {} resolves {} HTLC with payment hash {} with {}", - $source, input.previous_output.txid, input.previous_output.vout, tx.txid(), - if $outbound_htlc { "outbound" } else { "inbound" }, log_bytes!($payment_hash.0), + $tx_info, input.previous_output.txid, input.previous_output.vout, tx.txid(), + if outbound_htlc { "outbound" } else { "inbound" }, log_bytes!($htlc.payment_hash.0), if revocation_sig_claim { "revocation sig" } else if accepted_preimage_claim || offered_preimage_claim { "preimage" } else { "timeout" }); } } } macro_rules! scan_commitment { - ($htlc_outputs: expr, $htlc_sources: expr, $source: expr, $local_tx: expr) => { - for &(ref payment_hash, ref source, ref vout) in $htlc_sources.iter() { - if &Some(input.previous_output.vout) == vout { - log_claim!($source, $local_tx, true, payment_hash, true); - // We have a resolution of an HTLC either from one of our latest - // local commitment transactions or an unrevoked remote commitment - // transaction. This implies we either learned a preimage, the HTLC - // has timed out, or we screwed up. In any case, we should now - // resolve the source HTLC with the original sender. - payment_data = Some((source.clone(), *payment_hash)); - } - } - if payment_data.is_none() { - for htlc_output in $htlc_outputs { - if Some(input.previous_output.vout) == htlc_output.transaction_output_index { - log_claim!($source, $local_tx, $local_tx == htlc_output.offered, htlc_output.payment_hash, false); + ($htlcs: expr, $tx_info: expr, $local_tx: expr) => { + for (ref htlc_output, source_option) in $htlcs { + if Some(input.previous_output.vout) == htlc_output.transaction_output_index { + if let Some(ref source) = source_option { + log_claim!($tx_info, $local_tx, htlc_output, true); + // We have a resolution of an HTLC either from one of our latest + // local commitment transactions or an unrevoked remote commitment + // transaction. This implies we either learned a preimage, the HTLC + // has timed out, or we screwed up. In any case, we should now + // resolve the source HTLC with the original sender. + payment_data = Some(((*source).clone(), htlc_output.payment_hash)); + } else { + log_claim!($tx_info, $local_tx, htlc_output, false); continue 'outer_loop; } } @@ -1895,20 +1885,19 @@ impl ChannelMonitor { if let Some(ref current_local_signed_commitment_tx) = self.current_local_signed_commitment_tx { if input.previous_output.txid == current_local_signed_commitment_tx.txid { - scan_commitment!(current_local_signed_commitment_tx.htlc_outputs.iter().map(|&(ref a, _, _)| a), - current_local_signed_commitment_tx.htlc_sources, + scan_commitment!(current_local_signed_commitment_tx.htlc_outputs.iter().map(|&(ref a, _, ref b)| (a, b.as_ref())), "our latest local commitment tx", true); } } if let Some(ref prev_local_signed_commitment_tx) = self.prev_local_signed_commitment_tx { if input.previous_output.txid == prev_local_signed_commitment_tx.txid { - scan_commitment!(prev_local_signed_commitment_tx.htlc_outputs.iter().map(|&(ref a, _, _)| a), - prev_local_signed_commitment_tx.htlc_sources, + scan_commitment!(prev_local_signed_commitment_tx.htlc_outputs.iter().map(|&(ref a, _, ref b)| (a, b.as_ref())), "our previous local commitment tx", true); } } - if let Some(&(ref htlc_outputs, ref htlc_sources)) = self.remote_claimable_outpoints.get(&input.previous_output.txid) { - scan_commitment!(htlc_outputs, htlc_sources, "remote commitment tx", false); + if let Some(ref htlc_outputs) = self.remote_claimable_outpoints.get(&input.previous_output.txid) { + scan_commitment!(htlc_outputs.iter().map(|&(ref a, ref b)| (a, (b.as_ref().clone()).map(|boxed| &**boxed))), + "remote commitment tx", false); } // Check that scan_commitment, above, decided there is some source worth relaying an @@ -2044,35 +2033,16 @@ impl ReadableArgs> for (Sha256dHash, ChannelM } } - macro_rules! read_htlc_source { - () => { - { - (Readable::read(reader)?, Readable::read(reader)?, - match >::read(reader)? { - 0 => None, - 1 => Some(Readable::read(reader)?), - _ => return Err(DecodeError::InvalidValue), - } - ) - } - } - } - let remote_claimable_outpoints_len: u64 = Readable::read(reader)?; let mut remote_claimable_outpoints = HashMap::with_capacity(cmp::min(remote_claimable_outpoints_len as usize, MAX_ALLOC_SIZE / 64)); for _ in 0..remote_claimable_outpoints_len { let txid: Sha256dHash = Readable::read(reader)?; - let outputs_count: u64 = Readable::read(reader)?; - let mut outputs = Vec::with_capacity(cmp::min(outputs_count as usize, MAX_ALLOC_SIZE / 32)); - for _ in 0..outputs_count { - outputs.push(read_htlc_in_commitment!()); - } - let sources_count: u64 = Readable::read(reader)?; - let mut sources = Vec::with_capacity(cmp::min(sources_count as usize, MAX_ALLOC_SIZE / 32)); - for _ in 0..sources_count { - sources.push(read_htlc_source!()); + let htlcs_count: u64 = Readable::read(reader)?; + let mut htlcs = Vec::with_capacity(cmp::min(htlcs_count as usize, MAX_ALLOC_SIZE / 32)); + for _ in 0..htlcs_count { + htlcs.push((read_htlc_in_commitment!(), read_option!().map(|o: HTLCSource| Box::new(o)))); } - if let Some(_) = remote_claimable_outpoints.insert(txid, (outputs, sources)) { + if let Some(_) = remote_claimable_outpoints.insert(txid, htlcs) { return Err(DecodeError::InvalidValue); } } @@ -2124,23 +2094,22 @@ impl ReadableArgs> for (Sha256dHash, ChannelM let delayed_payment_key = Readable::read(reader)?; let feerate_per_kw: u64 = Readable::read(reader)?; - let htlc_outputs_len: u64 = Readable::read(reader)?; - let mut htlc_outputs = Vec::with_capacity(cmp::min(htlc_outputs_len as usize, MAX_ALLOC_SIZE / 128)); - for _ in 0..htlc_outputs_len { - let out = read_htlc_in_commitment!(); - let sigs = (Readable::read(reader)?, Readable::read(reader)?); - htlc_outputs.push((out, sigs.0, sigs.1)); - } - - let htlc_sources_len: u64 = Readable::read(reader)?; - let mut htlc_sources = Vec::with_capacity(cmp::min(htlc_outputs_len as usize, MAX_ALLOC_SIZE / 128)); - for _ in 0..htlc_sources_len { - htlc_sources.push(read_htlc_source!()); + let htlcs_len: u64 = Readable::read(reader)?; + let mut htlcs = Vec::with_capacity(cmp::min(htlcs_len as usize, MAX_ALLOC_SIZE / 128)); + for _ in 0..htlcs_len { + let htlc = read_htlc_in_commitment!(); + let sigs = match >::read(reader)? { + 0 => None, + 1 => Some((Readable::read(reader)?, Readable::read(reader)?)), + _ => return Err(DecodeError::InvalidValue), + }; + htlcs.push((htlc, sigs, read_option!())); } LocalSignedTx { txid: tx.txid(), - tx, revocation_key, a_htlc_key, b_htlc_key, delayed_payment_key, feerate_per_kw, htlc_outputs, htlc_sources + tx, revocation_key, a_htlc_key, b_htlc_key, delayed_payment_key, feerate_per_kw, + htlc_outputs: htlcs } } } @@ -2220,7 +2189,7 @@ mod tests { use ln::chan_utils::{HTLCOutputInCommitment, TxCreationKeys}; use util::test_utils::TestLogger; use secp256k1::key::{SecretKey,PublicKey}; - use secp256k1::{Secp256k1, Signature}; + use secp256k1::Secp256k1; use rand::{thread_rng,Rng}; use std::sync::Arc; @@ -2583,7 +2552,6 @@ mod tests { fn test_prune_preimages() { let secp_ctx = Secp256k1::new(); let logger = Arc::new(TestLogger::new()); - let dummy_sig = Signature::from_der(&secp_ctx, &hex::decode("3045022100fa86fa9a36a8cd6a7bb8f06a541787d51371d067951a9461d5404de6b928782e02201c8b7c334c10aed8976a3a465be9a28abff4cb23acbf00022295b378ce1fa3cd").unwrap()[..]).unwrap(); let dummy_key = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&secp_ctx, &[42; 32]).unwrap()); macro_rules! dummy_keys { @@ -2618,13 +2586,13 @@ mod tests { { let mut res = Vec::new(); for (idx, preimage) in $preimages_slice.iter().enumerate() { - res.push(HTLCOutputInCommitment { + res.push((HTLCOutputInCommitment { offered: true, amount_msat: 0, cltv_expiry: 0, payment_hash: preimage.1.clone(), transaction_output_index: Some(idx as u32), - }); + }, None)); } res } @@ -2634,7 +2602,7 @@ mod tests { ($preimages_slice: expr) => { { let mut inp = preimages_slice_to_htlc_outputs!($preimages_slice); - let res: Vec<_> = inp.drain(..).map(|e| { (e, dummy_sig.clone(), dummy_sig.clone()) }).collect(); + let res: Vec<_> = inp.drain(..).map(|e| { (e.0, None, e.1) }).collect(); res } } @@ -2653,11 +2621,11 @@ mod tests { let mut monitor = ChannelMonitor::new(&SecretKey::from_slice(&secp_ctx, &[42; 32]).unwrap(), &SecretKey::from_slice(&secp_ctx, &[43; 32]).unwrap(), &SecretKey::from_slice(&secp_ctx, &[44; 32]).unwrap(), &SecretKey::from_slice(&secp_ctx, &[44; 32]).unwrap(), &PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&secp_ctx, &[45; 32]).unwrap()), 0, Script::new(), logger.clone()); monitor.set_their_to_self_delay(10); - monitor.provide_latest_local_commitment_tx_info(dummy_tx.clone(), dummy_keys!(), 0, preimages_to_local_htlcs!(preimages[0..10]), Vec::new()); - monitor.provide_latest_remote_commitment_tx_info(&dummy_tx, preimages_slice_to_htlc_outputs!(preimages[5..15]), Vec::new(), 281474976710655, dummy_key); - monitor.provide_latest_remote_commitment_tx_info(&dummy_tx, preimages_slice_to_htlc_outputs!(preimages[15..20]), Vec::new(), 281474976710654, dummy_key); - monitor.provide_latest_remote_commitment_tx_info(&dummy_tx, preimages_slice_to_htlc_outputs!(preimages[17..20]), Vec::new(), 281474976710653, dummy_key); - monitor.provide_latest_remote_commitment_tx_info(&dummy_tx, preimages_slice_to_htlc_outputs!(preimages[18..20]), Vec::new(), 281474976710652, dummy_key); + monitor.provide_latest_local_commitment_tx_info(dummy_tx.clone(), dummy_keys!(), 0, preimages_to_local_htlcs!(preimages[0..10])); + monitor.provide_latest_remote_commitment_tx_info(&dummy_tx, preimages_slice_to_htlc_outputs!(preimages[5..15]), 281474976710655, dummy_key); + monitor.provide_latest_remote_commitment_tx_info(&dummy_tx, preimages_slice_to_htlc_outputs!(preimages[15..20]), 281474976710654, dummy_key); + monitor.provide_latest_remote_commitment_tx_info(&dummy_tx, preimages_slice_to_htlc_outputs!(preimages[17..20]), 281474976710653, dummy_key); + monitor.provide_latest_remote_commitment_tx_info(&dummy_tx, preimages_slice_to_htlc_outputs!(preimages[18..20]), 281474976710652, dummy_key); for &(ref preimage, ref hash) in preimages.iter() { monitor.provide_payment_preimage(hash, preimage); } @@ -2679,7 +2647,7 @@ mod tests { // Now update local commitment tx info, pruning only element 18 as we still care about the // previous commitment tx's preimages too - monitor.provide_latest_local_commitment_tx_info(dummy_tx.clone(), dummy_keys!(), 0, preimages_to_local_htlcs!(preimages[0..5]), Vec::new()); + monitor.provide_latest_local_commitment_tx_info(dummy_tx.clone(), dummy_keys!(), 0, preimages_to_local_htlcs!(preimages[0..5])); secret[0..32].clone_from_slice(&hex::decode("2273e227a5b7449b6e70f1fb4652864038b1cbf9cd7c043a7d6456b7fc275ad8").unwrap()); monitor.provide_secret(281474976710653, secret.clone()).unwrap(); assert_eq!(monitor.payment_preimages.len(), 12); @@ -2687,7 +2655,7 @@ mod tests { test_preimages_exist!(&preimages[18..20], monitor); // But if we do it again, we'll prune 5-10 - monitor.provide_latest_local_commitment_tx_info(dummy_tx.clone(), dummy_keys!(), 0, preimages_to_local_htlcs!(preimages[0..3]), Vec::new()); + monitor.provide_latest_local_commitment_tx_info(dummy_tx.clone(), dummy_keys!(), 0, preimages_to_local_htlcs!(preimages[0..3])); secret[0..32].clone_from_slice(&hex::decode("27cddaa5624534cb6cb9d7da077cf2b22ab21e9b506fd4998a51d54502e99116").unwrap()); monitor.provide_secret(281474976710652, secret.clone()).unwrap(); assert_eq!(monitor.payment_preimages.len(), 5); -- 2.39.5