}
}
+/// ClaimRequest is a descriptor structure to communicate between detection
+/// and reaction module. They are generated by ChannelMonitor while parsing
+/// onchain txn leaked from a channel and handed over to OnchainTxHandler which
+/// is responsible for opportunistic aggregation, selecting and enforcing
+/// bumping logic, building and signing transactions.
+pub(crate) struct ClaimRequest {
+ // Block height before which claiming is exclusive to one party,
+ // after reaching it, claiming may be contentious.
+ pub(crate) absolute_timelock: u32,
+ // Timeout tx must have nLocktime set which means aggregating multiple
+ // ones must take the higher nLocktime among them to satisfy all of them.
+ // Sadly it has few pitfalls, a) it takes longuer to get fund back b) CLTV_DELTA
+ // of a sooner-HTLC could be swallowed by the highest nLocktime of the HTLC set.
+ // Do simplify we mark them as non-aggregable.
+ pub(crate) aggregable: bool,
+ // Basic bitcoin outpoint (txid, vout)
+ pub(crate) outpoint: BitcoinOutPoint,
+ // Following outpoint type, set of data needed to generate transaction digest
+ // and satisfy witness program.
+ pub(crate) witness_data: InputMaterial
+}
+
/// Upon discovering of some classes of onchain tx by ChannelMonitor, we may have to take actions on it
/// once they mature to enough confirmations (ANTI_REORG_DELAY)
#[derive(Clone, PartialEq)]
/// HTLC-Success/HTLC-Timeout transactions.
/// Return updates for HTLC pending in the channel and failed automatically by the broadcast of
/// revoked remote commitment tx
- fn check_spend_remote_transaction(&mut self, tx: &Transaction, height: u32) -> (Vec<(u32, bool, BitcoinOutPoint, InputMaterial)>, (Sha256dHash, Vec<TxOut>), Vec<SpendableOutputDescriptor>) {
+ fn check_spend_remote_transaction(&mut self, tx: &Transaction, height: u32) -> (Vec<ClaimRequest>, (Sha256dHash, Vec<TxOut>), Vec<SpendableOutputDescriptor>) {
// Most secp and related errors trying to create keys means we have no hope of constructing
// a spend transaction...so we return no transactions to broadcast
let mut claimable_outpoints = Vec::new();
// First, process non-htlc outputs (to_local & to_remote)
for (idx, outp) in tx.output.iter().enumerate() {
if outp.script_pubkey == revokeable_p2wsh {
- claimable_outpoints.push((height + self.our_to_self_delay as u32, true, BitcoinOutPoint { txid: commitment_txid, vout: idx as u32 }, InputMaterial::Revoked { script: revokeable_redeemscript.clone(), pubkey: Some(revocation_pubkey), key: revocation_key, is_htlc: false, amount: outp.value }));
+ let witness_data = InputMaterial::Revoked { script: revokeable_redeemscript.clone(), pubkey: Some(revocation_pubkey), key: revocation_key, is_htlc: false, amount: outp.value };
+ claimable_outpoints.push(ClaimRequest { absolute_timelock: height + self.our_to_self_delay as u32, aggregable: true, outpoint: BitcoinOutPoint { txid: commitment_txid, vout: idx as u32 }, witness_data});
} else if Some(&outp.script_pubkey) == local_payment_p2wpkh.as_ref() {
spendable_outputs.push(SpendableOutputDescriptor::DynamicOutputP2WPKH {
outpoint: BitcoinOutPoint { txid: commitment_txid, vout: idx as u32 },
tx.output[transaction_output_index as usize].script_pubkey != expected_script.to_v0_p2wsh() {
return (claimable_outpoints, (commitment_txid, watch_outputs), spendable_outputs); // Corrupted per_commitment_data, fuck this user
}
- claimable_outpoints.push((htlc.cltv_expiry, true, BitcoinOutPoint { txid: commitment_txid, vout: transaction_output_index }, InputMaterial::Revoked { script: expected_script, pubkey: Some(revocation_pubkey), key: revocation_key, is_htlc: true, amount: tx.output[transaction_output_index as usize].value }));
+ let witness_data = InputMaterial::Revoked { script: expected_script, pubkey: Some(revocation_pubkey), key: revocation_key, is_htlc: true, amount: tx.output[transaction_output_index as usize].value };
+ claimable_outpoints.push(ClaimRequest { absolute_timelock: htlc.cltv_expiry, aggregable: true, outpoint: BitcoinOutPoint { txid: commitment_txid, vout: transaction_output_index }, witness_data });
}
}
}
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 {
- claimable_outpoints.push((htlc.cltv_expiry, aggregable, BitcoinOutPoint { txid: commitment_txid, vout: transaction_output_index }, InputMaterial::RemoteHTLC { script: expected_script, key: htlc_privkey, preimage, amount: htlc.amount_msat / 1000, locktime: htlc.cltv_expiry }));
+ let witness_data = InputMaterial::RemoteHTLC { 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 });
}
}
}
}
/// Attempts to claim a remote HTLC-Success/HTLC-Timeout's outputs using the revocation key
- fn check_spend_remote_htlc(&mut self, tx: &Transaction, commitment_number: u64, height: u32) -> Vec<(u32, bool, BitcoinOutPoint, InputMaterial)> {
+ fn check_spend_remote_htlc(&mut self, tx: &Transaction, commitment_number: u64, height: u32) -> Vec<ClaimRequest> {
//TODO: send back new outputs to guarantee pending_claim_request consistency
if tx.input.len() != 1 || tx.output.len() != 1 || tx.input[0].witness.len() != 5 {
return Vec::new()
let htlc_txid = tx.txid(); //TODO: This is gonna be a performance bottleneck for watchtowers!
log_trace!(self, "Remote HTLC broadcast {}:{}", htlc_txid, 0);
- let claimable_outpoints = vec!((height + self.our_to_self_delay as u32, true, BitcoinOutPoint { txid: htlc_txid, vout: 0}, InputMaterial::Revoked { script: redeemscript, pubkey: Some(revocation_pubkey), key: revocation_key, is_htlc: false, amount: tx.output[0].value }));
+ let witness_data = InputMaterial::Revoked { script: redeemscript, pubkey: Some(revocation_pubkey), key: revocation_key, is_htlc: false, amount: tx.output[0].value };
+ let claimable_outpoints = vec!(ClaimRequest { absolute_timelock: height + self.our_to_self_delay as u32, aggregable: true, outpoint: BitcoinOutPoint { txid: htlc_txid, vout: 0}, witness_data });
claimable_outpoints
}
}
}
}
- let mut spendable_output = self.onchain_tx_handler.block_connected(txn_matched, claimable_outpoints, height, broadcaster, &*fee_estimator);
+ let mut spendable_output = self.onchain_tx_handler.block_connected(txn_matched, claimable_outpoints, height, &*broadcaster, &*fee_estimator);
spendable_outputs.append(&mut spendable_output);
self.last_block_hash = block_hash.clone();