mirror of
https://github.com/lightningdevkit/rust-lightning.git
synced 2025-02-25 07:17:40 +01:00
Structurify claim request handed between detection/reaction
This commit is contained in:
parent
3d640da5c3
commit
14335358d0
2 changed files with 44 additions and 18 deletions
|
@ -554,6 +554,28 @@ impl Readable for InputMaterial {
|
|||
}
|
||||
}
|
||||
|
||||
/// 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)]
|
||||
|
@ -1385,7 +1407,7 @@ impl<ChanSigner: ChannelKeys> ChannelMonitor<ChanSigner> {
|
|||
/// 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();
|
||||
|
@ -1439,7 +1461,8 @@ impl<ChanSigner: ChannelKeys> ChannelMonitor<ChanSigner> {
|
|||
// 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 },
|
||||
|
@ -1459,7 +1482,8 @@ impl<ChanSigner: ChannelKeys> ChannelMonitor<ChanSigner> {
|
|||
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 });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1622,7 +1646,8 @@ impl<ChanSigner: ChannelKeys> ChannelMonitor<ChanSigner> {
|
|||
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 });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1643,7 +1668,7 @@ impl<ChanSigner: ChannelKeys> ChannelMonitor<ChanSigner> {
|
|||
}
|
||||
|
||||
/// 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()
|
||||
|
@ -1676,7 +1701,8 @@ impl<ChanSigner: ChannelKeys> ChannelMonitor<ChanSigner> {
|
|||
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
|
||||
}
|
||||
|
||||
|
@ -2050,7 +2076,7 @@ impl<ChanSigner: ChannelKeys> ChannelMonitor<ChanSigner> {
|
|||
}
|
||||
}
|
||||
}
|
||||
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();
|
||||
|
|
|
@ -14,7 +14,7 @@ use secp256k1::Secp256k1;
|
|||
use secp256k1;
|
||||
|
||||
use ln::msgs::DecodeError;
|
||||
use ln::channelmonitor::{ANTI_REORG_DELAY, CLTV_SHARED_CLAIM_BUFFER, InputMaterial};
|
||||
use ln::channelmonitor::{ANTI_REORG_DELAY, CLTV_SHARED_CLAIM_BUFFER, InputMaterial, ClaimRequest};
|
||||
use ln::chan_utils::HTLCType;
|
||||
use chain::chaininterface::{FeeEstimator, BroadcasterInterface, ConfirmationTarget, MIN_RELAY_FEE_SAT_PER_1000_WEIGHT};
|
||||
use chain::keysinterface::SpendableOutputDescriptor;
|
||||
|
@ -471,7 +471,7 @@ impl OnchainTxHandler {
|
|||
Some((new_timer, new_feerate, bumped_tx))
|
||||
}
|
||||
|
||||
pub(super) fn block_connected<B: Deref, F: Deref>(&mut self, txn_matched: &[&Transaction], claimable_outpoints: Vec<Vec<(u32, bool, BitcoinOutPoint, InputMaterial)>>, height: u32, broadcaster: B, fee_estimator: F) -> Vec<SpendableOutputDescriptor>
|
||||
pub(super) fn block_connected<B: Deref, F: Deref>(&mut self, txn_matched: &[&Transaction], claimable_outpoints: Vec<Vec<ClaimRequest>>, height: u32, broadcaster: B, fee_estimator: F) -> Vec<SpendableOutputDescriptor>
|
||||
where B::Target: BroadcasterInterface,
|
||||
F::Target: FeeEstimator
|
||||
{
|
||||
|
@ -483,18 +483,18 @@ impl OnchainTxHandler {
|
|||
// Try to aggregate outputs if they're 1) belong to same parent tx, 2) their
|
||||
// timelock expiration isn't imminent (<= CLTV_SHARED_CLAIM_BUFFER).
|
||||
for siblings_outpoints in claimable_outpoints {
|
||||
for outp in siblings_outpoints {
|
||||
for req in siblings_outpoints {
|
||||
// Don't claim a outpoint twice that would be bad for privacy and may uselessly lock a CPFP input for a while
|
||||
if let Some(_) = self.claimable_outpoints.get(&outp.2) { log_trace!(self, "Bouncing off outpoint {}:{}, already registered its claiming request", outp.2.txid, outp.2.vout); } else {
|
||||
log_trace!(self, "Test if outpoint can be aggregated with expiration {} against {}", outp.0, height + CLTV_SHARED_CLAIM_BUFFER);
|
||||
if outp.0 <= height + CLTV_SHARED_CLAIM_BUFFER || !outp.1 { // Don't aggregate if outpoint absolute timelock is soon or marked as non-aggregable
|
||||
if let Some(_) = self.claimable_outpoints.get(&req.outpoint) { log_trace!(self, "Bouncing off outpoint {}:{}, already registered its claiming request", req.outpoint.txid, req.outpoint.vout); } else {
|
||||
log_trace!(self, "Test if outpoint can be aggregated with expiration {} against {}", req.absolute_timelock, height + CLTV_SHARED_CLAIM_BUFFER);
|
||||
if req.absolute_timelock <= height + CLTV_SHARED_CLAIM_BUFFER || !req.aggregable { // Don't aggregate if outpoint absolute timelock is soon or marked as non-aggregable
|
||||
let mut single_input = HashMap::new();
|
||||
single_input.insert(outp.2, outp.3);
|
||||
new_claims.push((outp.0, single_input));
|
||||
single_input.insert(req.outpoint, req.witness_data);
|
||||
new_claims.push((req.absolute_timelock, single_input));
|
||||
} else {
|
||||
aggregated_claim.insert(outp.2, outp.3);
|
||||
if outp.0 < aggregated_soonest {
|
||||
aggregated_soonest = outp.0;
|
||||
aggregated_claim.insert(req.outpoint, req.witness_data);
|
||||
if req.absolute_timelock < aggregated_soonest {
|
||||
aggregated_soonest = req.absolute_timelock;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue