- 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 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,
- };
-
- let predicted_weight = spend_tx.get_weight() + Self::get_witnesses_weight(&inputs_desc[..]);
-
- let mut used_feerate;
- if !subtract_high_prio_fee!(self, fee_estimator, spend_tx.output[0].value, predicted_weight, used_feerate) {
- return (txn_to_broadcast, (commitment_txid, watch_outputs), spendable_outputs);
- }
-
- let sighash_parts = bip143::SighashComponents::new(&spend_tx);
-
- let mut per_input_material = HashMap::with_capacity(spend_tx.input.len());
- let mut soonest_timelock = ::std::u32::MAX;
- for info in inputs_info.iter() {
- if info.2 <= soonest_timelock {
- soonest_timelock = info.2;
- }
- }
- let height_timer = Self::get_height_timer(height, soonest_timelock);
- let spend_txid = spend_tx.txid();
- for (input, info) in spend_tx.input.iter_mut().zip(inputs_info.iter()) {
- let (redeemscript, htlc_key) = sign_input!(sighash_parts, input, info.1, (info.0).0.to_vec(), info.3);
- log_trace!(self, "Outpoint {}:{} is being being claimed, if it doesn't succeed, a bumped claiming txn is going to be broadcast at height {}", input.previous_output.txid, input.previous_output.vout, height_timer);
- per_input_material.insert(input.previous_output, InputMaterial::RemoteHTLC { script: redeemscript, key: htlc_key, preimage: Some(*(info.0)), amount: info.1, locktime: 0});
- match self.claimable_outpoints.entry(input.previous_output) {
- hash_map::Entry::Occupied(_) => {},
- hash_map::Entry::Vacant(entry) => { entry.insert((spend_txid, height)); }
- }
- }
- match self.pending_claim_requests.entry(spend_txid) {
- hash_map::Entry::Occupied(_) => {},
- hash_map::Entry::Vacant(entry) => { entry.insert(ClaimTxBumpMaterial { height_timer, feerate_previous: used_feerate, soonest_timelock, per_input_material }); }
- }
- assert!(predicted_weight >= spend_tx.get_weight());
- 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);
- }
- }
- } else if let Some((ref to_remote_rescue, ref local_key)) = self.to_remote_rescue {
- for (idx, outp) in tx.output.iter().enumerate() {
- if to_remote_rescue == &outp.script_pubkey {
- spendable_outputs.push(SpendableOutputDescriptor::DynamicOutputP2WPKH {
- outpoint: BitcoinOutPoint { txid: commitment_txid, vout: idx as u32 },
- key: local_key.clone(),
- output: outp.clone(),
- });
+ if let Some(ref txid) = self.current_remote_commitment_txid {
+ check_htlc_fails!(txid, "current", 'current_loop);
+ }
+ if let Some(ref txid) = self.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 = ignore_error!(chan_utils::derive_public_revocation_key(&self.secp_ctx, revocation_point, &self.keys.pubkeys().revocation_basepoint));
+ let b_htlc_key = ignore_error!(chan_utils::derive_public_key(&self.secp_ctx, revocation_point, &self.keys.pubkeys().htlc_basepoint));
+ let htlc_privkey = ignore_error!(chan_utils::derive_private_key(&self.secp_ctx, revocation_point, &self.keys.htlc_base_key()));
+ let a_htlc_key = ignore_error!(chan_utils::derive_public_key(&self.secp_ctx, revocation_point, &self.their_htlc_base_key));
+ let local_payment_key = ignore_error!(chan_utils::derive_private_key(&self.secp_ctx, revocation_point, &self.keys.payment_base_key()));
+
+ self.broadcasted_remote_payment_script = {
+ // Note that the Network here is ignored as we immediately drop the address for the
+ // script_pubkey version
+ let payment_hash160 = Hash160::hash(&PublicKey::from_secret_key(&self.secp_ctx, &local_payment_key).serialize());
+ Some((Builder::new().push_opcode(opcodes::all::OP_PUSHBYTES_0).push_slice(&payment_hash160[..]).into_script(), local_payment_key))
+ };
+
+ // Then, try to find htlc outputs
+ for (_, &(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 (claimable_outpoints, (commitment_txid, watch_outputs)); // Corrupted per_commitment_data, fuck this user
+ }
+ let preimage = if htlc.offered { if let Some(p) = self.payment_preimages.get(&htlc.payment_hash) { Some(*p) } else { None } } else { None };
+ let aggregable = if !htlc.offered { false } else { true };
+ if preimage.is_some() || !htlc.offered {
+ let witness_data = InputMaterial::RemoteHTLC { witness_script: expected_script, key: htlc_privkey, preimage, amount: htlc.amount_msat / 1000, locktime: htlc.cltv_expiry };
+ claimable_outpoints.push(ClaimRequest { absolute_timelock: htlc.cltv_expiry, aggregable, outpoint: BitcoinOutPoint { txid: commitment_txid, vout: transaction_output_index }, witness_data });
+ }
+ }
+ }