- let (revocation_pubkey, b_htlc_key) = match self.key_storage {
- Storage::Local { ref keys, .. } => {
- (ignore_error!(chan_utils::derive_public_revocation_key(&self.secp_ctx, revocation_point, &keys.pubkeys().revocation_basepoint)),
- ignore_error!(chan_utils::derive_public_key(&self.secp_ctx, revocation_point, &keys.pubkeys().htlc_basepoint)))
- },
- 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),
- 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 inputs = Vec::new();
- let mut inputs_desc = Vec::new();
- let mut inputs_info = Vec::new();
-
- macro_rules! sign_input {
- ($sighash_parts: expr, $input: expr, $amount: expr, $preimage: expr, $idx: expr) => {
- {
- let (sig, redeemscript, htlc_key) = match self.key_storage {
- Storage::Local { ref htlc_base_key, .. } => {
- let htlc = &per_commitment_option.unwrap()[$idx as usize].0;
- let redeemscript = chan_utils::get_htlc_redeemscript_with_explicit_keys(htlc, &a_htlc_key, &b_htlc_key, &revocation_pubkey);
- let sighash = hash_to_message!(&$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, htlc_key)
- },
- Storage::Watchtower { .. } => {
- unimplemented!();
- }
- };
- $input.witness.push(sig.serialize_der().to_vec());
- $input.witness[0].push(SigHashType::All as u8);
- $input.witness.push($preimage);
- $input.witness.push(redeemscript.clone().into_bytes());
- (redeemscript, htlc_key)
- }
- }
- }
-
- 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() ||
- tx.output[transaction_output_index as usize].value != htlc.amount_msat / 1000 ||
- tx.output[transaction_output_index as usize].script_pubkey != expected_script.to_v0_p2wsh() {
- return (txn_to_broadcast, (commitment_txid, watch_outputs), spendable_outputs); // Corrupted per_commitment_data, fuck this user
- }
- if let Some(payment_preimage) = self.payment_preimages.get(&htlc.payment_hash) {
- if htlc.offered {
- let input = TxIn {
- previous_output: BitcoinOutPoint {
- txid: commitment_txid,
- vout: transaction_output_index,
- },
- script_sig: Script::new(),
- sequence: 0xff_ff_ff_fd,
- witness: Vec::new(),
- };
- if htlc.cltv_expiry > height + CLTV_SHARED_CLAIM_BUFFER {
- inputs.push(input);
- inputs_desc.push(if htlc.offered { InputDescriptors::OfferedHTLC } else { InputDescriptors::ReceivedHTLC });
- inputs_info.push((payment_preimage, tx.output[transaction_output_index as usize].value, htlc.cltv_expiry, idx));
- total_value += tx.output[transaction_output_index as usize].value;
- } 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,
- }),
- };
- let predicted_weight = single_htlc_tx.get_weight() + Self::get_witnesses_weight(&[if htlc.offered { InputDescriptors::OfferedHTLC } else { InputDescriptors::ReceivedHTLC }]);
- let height_timer = Self::get_height_timer(height, htlc.cltv_expiry);
- let mut used_feerate;
- if subtract_high_prio_fee!(self, fee_estimator, single_htlc_tx.output[0].value, predicted_weight, used_feerate) {
- let sighash_parts = bip143::SighashComponents::new(&single_htlc_tx);
- let (redeemscript, htlc_key) = sign_input!(sighash_parts, single_htlc_tx.input[0], htlc.amount_msat / 1000, payment_preimage.0.to_vec(), idx);
- assert!(predicted_weight >= single_htlc_tx.get_weight());
- spendable_outputs.push(SpendableOutputDescriptor::StaticOutput {
- outpoint: BitcoinOutPoint { txid: single_htlc_tx.txid(), vout: 0 },
- output: single_htlc_tx.output[0].clone(),
- });
- log_trace!(self, "Outpoint {}:{} is being being claimed, if it doesn't succeed, a bumped claiming txn is going to be broadcast at height {}", single_htlc_tx.input[0].previous_output.txid, single_htlc_tx.input[0].previous_output.vout, height_timer);
- let mut per_input_material = HashMap::with_capacity(1);
- per_input_material.insert(single_htlc_tx.input[0].previous_output, InputMaterial::RemoteHTLC { script: redeemscript, key: htlc_key, preimage: Some(*payment_preimage), amount: htlc.amount_msat / 1000, locktime: 0 });
- match self.claimable_outpoints.entry(single_htlc_tx.input[0].previous_output) {
- hash_map::Entry::Occupied(_) => {},
- hash_map::Entry::Vacant(entry) => { entry.insert((single_htlc_tx.txid(), height)); }
- }
- match self.pending_claim_requests.entry(single_htlc_tx.txid()) {
- hash_map::Entry::Occupied(_) => {},
- hash_map::Entry::Vacant(entry) => { entry.insert(ClaimTxBumpMaterial { height_timer, feerate_previous: used_feerate, soonest_timelock: htlc.cltv_expiry, per_input_material}); }
- }
- txn_to_broadcast.push(single_htlc_tx);
- }
- }
- }
- }
- if !htlc.offered {
- // TODO: If the HTLC has already expired, potentially merge it with the
- // rest of the claim transaction, as above.
- let input = TxIn {
- previous_output: BitcoinOutPoint {
- txid: commitment_txid,
- vout: transaction_output_index,
- },
- script_sig: Script::new(),
- sequence: 0xff_ff_ff_fd,
- witness: Vec::new(),
- };
- let mut timeout_tx = Transaction {
- version: 2,
- lock_time: htlc.cltv_expiry,
- input: vec![input],
- output: vec!(TxOut {
- script_pubkey: self.destination_script.clone(),
- value: htlc.amount_msat / 1000,
- }),
- };
- let predicted_weight = timeout_tx.get_weight() + Self::get_witnesses_weight(&[InputDescriptors::ReceivedHTLC]);
- let height_timer = Self::get_height_timer(height, htlc.cltv_expiry);
- let mut used_feerate;
- if subtract_high_prio_fee!(self, fee_estimator, timeout_tx.output[0].value, predicted_weight, used_feerate) {
- let sighash_parts = bip143::SighashComponents::new(&timeout_tx);
- let (redeemscript, htlc_key) = sign_input!(sighash_parts, timeout_tx.input[0], htlc.amount_msat / 1000, vec![0], idx);
- assert!(predicted_weight >= timeout_tx.get_weight());
- //TODO: track SpendableOutputDescriptor
- log_trace!(self, "Outpoint {}:{} is being being claimed, if it doesn't succeed, a bumped claiming txn is going to be broadcast at height {}", timeout_tx.input[0].previous_output.txid, timeout_tx.input[0].previous_output.vout, height_timer);
- let mut per_input_material = HashMap::with_capacity(1);
- per_input_material.insert(timeout_tx.input[0].previous_output, InputMaterial::RemoteHTLC { script : redeemscript, key: htlc_key, preimage: None, amount: htlc.amount_msat / 1000, locktime: htlc.cltv_expiry });
- match self.claimable_outpoints.entry(timeout_tx.input[0].previous_output) {
- hash_map::Entry::Occupied(_) => {},
- hash_map::Entry::Vacant(entry) => { entry.insert((timeout_tx.txid(), height)); }
- }
- match self.pending_claim_requests.entry(timeout_tx.txid()) {
- hash_map::Entry::Occupied(_) => {},
- hash_map::Entry::Vacant(entry) => { entry.insert(ClaimTxBumpMaterial { height_timer, feerate_previous: used_feerate, soonest_timelock: htlc.cltv_expiry, per_input_material }); }
- }
- }
- txn_to_broadcast.push(timeout_tx);
- }
- }
- }
-
- if inputs.is_empty() { return (txn_to_broadcast, (commitment_txid, watch_outputs), spendable_outputs); } // Nothing to be done...probably a false positive/local tx
-
- let outputs = vec!(TxOut {
- script_pubkey: self.destination_script.clone(),
- value: total_value
- });
- let mut spend_tx = Transaction {
- version: 2,
- lock_time: 0,
- input: inputs,
- output: outputs,