+ sequence: 0xfffffffd,
+ witness: Vec::new(),
+ };
+ if htlc.cltv_expiry > height + CLTV_SHARED_CLAIM_BUFFER {
+ inputs.push(input);
+ htlc_idxs.push(Some(idx));
+ values.push(tx.output[htlc.transaction_output_index as usize].value);
+ total_value += htlc.amount_msat / 1000;
+ } else {
+ let mut single_htlc_tx = Transaction {
+ version: 2,
+ lock_time: 0,
+ input: vec![input],
+ output: vec!(TxOut {
+ script_pubkey: self.destination_script.clone(),
+ value: htlc.amount_msat / 1000, //TODO: - fee
+ }),
+ };
+ let sighash_parts = bip143::SighashComponents::new(&single_htlc_tx);
+ sign_input!(sighash_parts, single_htlc_tx.input[0], Some(idx), htlc.amount_msat / 1000);
+ txn_to_broadcast.push(single_htlc_tx);
+ }
+ }
+ }
+
+ if !inputs.is_empty() || !txn_to_broadcast.is_empty() { // ie we're confident this is actually ours
+ // We're definitely a remote commitment transaction!
+ log_trace!(self, "Got broadcast of revoked remote commitment transaction, generating general spend tx with {} inputs and {} other txn to broadcast", inputs.len(), txn_to_broadcast.len());
+ watch_outputs.append(&mut tx.output.clone());
+ self.remote_commitment_txn_on_chain.insert(commitment_txid, (commitment_number, tx.output.iter().map(|output| { output.script_pubkey.clone() }).collect()));
+ }
+ if inputs.is_empty() { return (txn_to_broadcast, (commitment_txid, watch_outputs), spendable_outputs, htlc_updated); } // Nothing to be done...probably a false positive/local tx
+
+ let outputs = vec!(TxOut {
+ script_pubkey: self.destination_script.clone(),
+ value: total_value, //TODO: - fee
+ });
+ let mut spend_tx = Transaction {
+ version: 2,
+ lock_time: 0,
+ input: inputs,
+ output: outputs,
+ };
+
+ let mut values_drain = values.drain(..);
+ let sighash_parts = bip143::SighashComponents::new(&spend_tx);
+
+ for (input, htlc_idx) in spend_tx.input.iter_mut().zip(htlc_idxs.iter()) {
+ let value = values_drain.next().unwrap();
+ sign_input!(sighash_parts, input, htlc_idx, value);
+ }
+
+ spendable_outputs.push(SpendableOutputDescriptor::StaticOutput {
+ outpoint: BitcoinOutPoint { txid: spend_tx.txid(), vout: 0 },
+ output: spend_tx.output[0].clone(),
+ });
+ txn_to_broadcast.push(spend_tx);
+
+ // TODO: We really should only fail backwards after our revocation claims have been
+ // confirmed, but we also need to do more other tracking of in-flight pre-confirm
+ // on-chain claims, so we can do that at the same time.
+ if let Storage::Local { ref current_remote_commitment_txid, ref prev_remote_commitment_txid, .. } = self.key_storage {
+ if let &Some(ref txid) = current_remote_commitment_txid {
+ if let Some(&(_, ref latest_outpoints)) = self.remote_claimable_outpoints.get(&txid) {
+ for &(ref payment_hash, ref source, _) in latest_outpoints.iter() {
+ log_trace!(self, "Failing HTLC with payment_hash {} from current remote commitment tx due to broadcast of revoked remote commitment transaction", log_bytes!(payment_hash.0));
+ htlc_updated.push(((*source).clone(), None, payment_hash.clone()));
+ }
+ }
+ }
+ if let &Some(ref txid) = prev_remote_commitment_txid {
+ if let Some(&(_, ref prev_outpoint)) = self.remote_claimable_outpoints.get(&txid) {
+ for &(ref payment_hash, ref source, _) in prev_outpoint.iter() {
+ log_trace!(self, "Failing HTLC with payment_hash {} from previous remote commitment tx due to broadcast of revoked remote commitment transaction", log_bytes!(payment_hash.0));
+ htlc_updated.push(((*source).clone(), None, payment_hash.clone()));
+ }
+ }
+ }
+ }
+ // No need to check local commitment txn, symmetric HTLCSource must be present as per-htlc data on remote commitment tx
+ } else if let Some(per_commitment_data) = per_commitment_option {
+ // While this isn't useful yet, there is a potential race where if a counterparty
+ // revokes a state at the same time as the commitment transaction for that state is
+ // confirmed, and the watchtower receives the block before the user, the user could
+ // upload a new ChannelMonitor with the revocation secret but the watchtower has
+ // already processed the block, resulting in the remote_commitment_txn_on_chain entry
+ // not being generated by the above conditional. Thus, to be safe, we go ahead and
+ // insert it here.
+ watch_outputs.append(&mut tx.output.clone());
+ self.remote_commitment_txn_on_chain.insert(commitment_txid, (commitment_number, tx.output.iter().map(|output| { output.script_pubkey.clone() }).collect()));
+
+ log_trace!(self, "Got broadcast of non-revoked remote commitment transaction {}", commitment_txid);
+
+ // TODO: We really should only fail backwards after our revocation claims have been
+ // confirmed, but we also need to do more other tracking of in-flight pre-confirm
+ // 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;
+ }
+ }
+ 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()));
+ }
+ }
+ }
+ }
+ if let Storage::Local { ref current_remote_commitment_txid, ref prev_remote_commitment_txid, .. } = self.key_storage {
+ if let &Some(ref txid) = current_remote_commitment_txid {
+ check_htlc_fails!(txid, "current", 'current_loop);
+ }
+ if let &Some(ref txid) = prev_remote_commitment_txid {
+ check_htlc_fails!(txid, "previous", 'prev_loop);
+ }
+ }
+
+ if let Some(revocation_points) = self.their_cur_revocation_points {
+ let revocation_point_option =
+ if revocation_points.0 == commitment_number { Some(&revocation_points.1) }
+ else if let Some(point) = revocation_points.2.as_ref() {
+ if revocation_points.0 == commitment_number + 1 { Some(point) } else { None }
+ } else { None };
+ if let Some(revocation_point) = revocation_point_option {
+ let (revocation_pubkey, b_htlc_key) = match self.key_storage {
+ Storage::Local { ref revocation_base_key, ref htlc_base_key, .. } => {
+ (ignore_error!(chan_utils::derive_public_revocation_key(&self.secp_ctx, revocation_point, &PublicKey::from_secret_key(&self.secp_ctx, &revocation_base_key))),
+ ignore_error!(chan_utils::derive_public_key(&self.secp_ctx, revocation_point, &PublicKey::from_secret_key(&self.secp_ctx, &htlc_base_key))))
+ },
+ Storage::Watchtower { ref revocation_base_key, ref htlc_base_key, .. } => {
+ (ignore_error!(chan_utils::derive_public_revocation_key(&self.secp_ctx, revocation_point, &revocation_base_key)),
+ ignore_error!(chan_utils::derive_public_key(&self.secp_ctx, revocation_point, &htlc_base_key)))
+ },
+ };
+ let a_htlc_key = match self.their_htlc_base_key {
+ None => return (txn_to_broadcast, (commitment_txid, watch_outputs), spendable_outputs, htlc_updated),
+ Some(their_htlc_base_key) => ignore_error!(chan_utils::derive_public_key(&self.secp_ctx, revocation_point, &their_htlc_base_key)),
+ };
+
+ for (idx, outp) in tx.output.iter().enumerate() {
+ if outp.script_pubkey.is_v0_p2wpkh() {
+ match self.key_storage {
+ Storage::Local { ref payment_base_key, .. } => {
+ if let Ok(local_key) = chan_utils::derive_private_key(&self.secp_ctx, &revocation_point, &payment_base_key) {
+ spendable_outputs.push(SpendableOutputDescriptor::DynamicOutputP2WPKH {
+ outpoint: BitcoinOutPoint { txid: commitment_txid, vout: idx as u32 },
+ key: local_key,
+ output: outp.clone(),
+ });
+ }
+ },
+ Storage::Watchtower { .. } => {}
+ }
+ break; // Only to_remote ouput is claimable
+ }
+ }
+
+ let mut total_value = 0;
+ let mut values = Vec::new();
+ let mut inputs = Vec::new();
+
+ macro_rules! sign_input {
+ ($sighash_parts: expr, $input: expr, $amount: expr, $preimage: expr) => {
+ {
+ 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 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));
+ (self.secp_ctx.sign(&sighash, &htlc_key), redeemscript)
+ },
+ Storage::Watchtower { .. } => {
+ unimplemented!();
+ }
+ };
+ $input.witness.push(sig.serialize_der(&self.secp_ctx).to_vec());
+ $input.witness[0].push(SigHashType::All as u8);
+ $input.witness.push($preimage);
+ $input.witness.push(redeemscript.into_bytes());
+ }
+ }
+ }