+
+ /// Check if any transaction broadcasted is resolving HTLC output by a success or timeout on a local
+ /// or remote commitment tx, if so send back the source, preimage if found and payment_hash of resolved HTLC
+ fn is_resolving_htlc_output(&mut self, tx: &Transaction) -> Vec<(HTLCSource, Option<PaymentPreimage>, PaymentHash)> {
+ let mut htlc_updated = Vec::new();
+
+ 'outer_loop: for input in &tx.input {
+ let mut payment_data = None;
+ let revocation_sig_claim = (input.witness.len() == 3 && input.witness[2].len() == OFFERED_HTLC_SCRIPT_WEIGHT && input.witness[1].len() == 33)
+ || (input.witness.len() == 3 && input.witness[2].len() == ACCEPTED_HTLC_SCRIPT_WEIGHT && input.witness[1].len() == 33);
+ let accepted_preimage_claim = input.witness.len() == 5 && input.witness[4].len() == ACCEPTED_HTLC_SCRIPT_WEIGHT;
+ let offered_preimage_claim = input.witness.len() == 3 && input.witness[2].len() == OFFERED_HTLC_SCRIPT_WEIGHT;
+
+ macro_rules! log_claim {
+ ($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 and no valid remote commitment txid
+ // to try a weak source binding with same-hash, same-value still-valid offered HTLC.
+ // 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)) {
+ log_error!(self, "Input spending {} ({}:{}) in {} resolves {} HTLC with payment hash {} with {}!",
+ $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 {}",
+ $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! check_htlc_valid_remote {
+ ($remote_txid: expr, $htlc_output: expr) => {
+ if let &Some(txid) = $remote_txid {
+ for &(ref pending_htlc, ref pending_source) in self.remote_claimable_outpoints.get(&txid).unwrap() {
+ if pending_htlc.payment_hash == $htlc_output.payment_hash && pending_htlc.amount_msat == $htlc_output.amount_msat {
+ if let &Some(ref source) = pending_source {
+ log_claim!("revoked remote commitment tx", false, pending_htlc, true);
+ payment_data = Some(((**source).clone(), $htlc_output.payment_hash));
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ macro_rules! scan_commitment {
+ ($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 if !$local_tx {
+ if let Storage::Local { ref current_remote_commitment_txid, .. } = self.key_storage {
+ check_htlc_valid_remote!(current_remote_commitment_txid, htlc_output);
+ }
+ if payment_data.is_none() {
+ if let Storage::Local { ref prev_remote_commitment_txid, .. } = self.key_storage {
+ check_htlc_valid_remote!(prev_remote_commitment_txid, htlc_output);
+ }
+ }
+ }
+ if payment_data.is_none() {
+ log_claim!($tx_info, $local_tx, htlc_output, false);
+ continue 'outer_loop;
+ }
+ }
+ }
+ }
+ }
+
+ 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, _, 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, _, ref b)| (a, b.as_ref())),
+ "our previous local commitment tx", true);
+ }
+ }
+ 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
+ // HTLC resolution backwards to and figure out whether we learned a preimage from it.
+ if let Some((source, payment_hash)) = payment_data {
+ let mut payment_preimage = PaymentPreimage([0; 32]);
+ if accepted_preimage_claim {
+ payment_preimage.0.copy_from_slice(&input.witness[3]);
+ htlc_updated.push((source, Some(payment_preimage), payment_hash));
+ } else if offered_preimage_claim {
+ payment_preimage.0.copy_from_slice(&input.witness[1]);
+ htlc_updated.push((source, Some(payment_preimage), payment_hash));
+ } else {
+ htlc_updated.push((source, None, payment_hash));
+ }
+ }
+ }
+ htlc_updated
+ }