Skip to content

Commit 87e5255

Browse files
author
Antoine Riard
committed
Build and broadcast CPFP for HolderCommitmentTx
This commit build a CPFP tx for HolderCommitmentTx and broadcast them as a whole thus bumping package feerate. A next commit condition CPFP on feerate fluctuation observation.
1 parent ac64b68 commit 87e5255

File tree

2 files changed

+86
-26
lines changed

2 files changed

+86
-26
lines changed

lightning/src/ln/onchain_utils.rs

Lines changed: 61 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -490,15 +490,16 @@ impl PackageTemplate {
490490
};
491491
bumped_tx.get_weight() + witnesses_weight
492492
}
493-
pub(crate) fn package_finalize<L: Deref, ChanSigner: ChannelKeys>(&self, onchain_handler: &mut OnchainTxHandler<ChanSigner>, value: u64, destination_script: Script, logger: &L) -> Option<Transaction>
493+
pub(crate) fn package_finalize<L: Deref, ChanSigner: ChannelKeys, U: Deref>(&self, onchain_handler: &mut OnchainTxHandler<ChanSigner>, value: u64, destination_script: Script, logger: &L, utxo_pool: &U) -> Option<Vec<Transaction>>
494494
where L::Target: Logger,
495+
U::Target: UtxoPool,
495496
{
496497
let mut bumped_tx = Transaction {
497498
version: 2,
498499
lock_time: 0,
499500
input: vec![],
500501
output: vec![TxOut {
501-
script_pubkey: destination_script,
502+
script_pubkey: destination_script.clone(),
502503
value,
503504
}],
504505
};
@@ -535,7 +536,7 @@ impl PackageTemplate {
535536
}
536537
}
537538
log_trace!(logger, "Going to broadcast Penalty Transaction {}...", bumped_tx.txid());
538-
return Some(bumped_tx);
539+
return Some(vec![bumped_tx]);
539540
},
540541
PackageTemplate::CounterpartyHTLCTx { ref inputs } => {
541542
for outp in inputs.keys() {
@@ -566,22 +567,73 @@ impl PackageTemplate {
566567
}
567568
}
568569
log_trace!(logger, "Going to broadcast Claim Transaction {} claiming counterparty htlc output...", bumped_tx.txid());
569-
return Some(bumped_tx);
570+
return Some(vec![bumped_tx]);
570571
},
571572
PackageTemplate::HolderHTLCTx { ref input } => {
572573
let htlc_tx = onchain_handler.get_fully_signed_htlc_tx(&input.0, &input.1.preimage);
573574
if let Some(htlc_tx) = htlc_tx {
574575
// Timer set to $NEVER given we can't bump tx without anchor outputs
575-
log_trace!(logger, "Going to broadcast Holder HTLC-{} claiming HTLC output {} from {}...", if input.1.preimage.is_some() { "Success" } else { "Timeout" }, input.0.vout, input.0.txid);
576-
return Some(htlc_tx);
576+
log_trace!(logger, "Going to broadcast Local HTLC-{} claiming HTLC output {} from {}...", if input.1.preimage.is_some() { "Success" } else { "Timeout" }, input.0.vout, input.0.txid);
577+
return Some(vec![htlc_tx]);
577578
}
578579
return None;
579580
},
580-
PackageTemplate::HolderCommitmentTx { ref input, .. } => {
581+
PackageTemplate::HolderCommitmentTx { ref input, ref utxo_input } => {
582+
583+
// We sign our commitment transaction
581584
let signed_tx = onchain_handler.get_fully_signed_holder_tx(&input.1.funding_redeemscript).unwrap();
582-
// Timer set to $NEVER given we can't bump tx without anchor outputs
585+
let mut cpfp_tx = Transaction {
586+
version: 2,
587+
lock_time: 0,
588+
input: Vec::with_capacity(2),
589+
output: vec![TxOut {
590+
script_pubkey: destination_script.clone(),
591+
value,
592+
}],
593+
};
594+
// TODO: make CPFP generation conditional on utxo input
595+
if let Some(ref holder_tx) = onchain_handler.holder_commitment.as_ref() {
596+
// We find & select our anchor output
597+
let our_anchor_output_script = chan_utils::get_anchor_redeemscript(&onchain_handler.key_storage.pubkeys().funding_pubkey);
598+
let mut vout = ::std::u32::MAX;
599+
for (idx, outp) in holder_tx.unsigned_tx.output.iter().enumerate() {
600+
if outp.script_pubkey == our_anchor_output_script.to_v0_p2wsh() {
601+
vout = idx as u32;
602+
}
603+
}
604+
if vout == ::std::u32::MAX { return None; }
605+
let anchor_outpoint = BitcoinOutPoint {
606+
txid: holder_tx.unsigned_tx.txid(),
607+
vout,
608+
};
609+
// We take our bumping outpoint
610+
let bumping_outpoint = utxo_input.as_ref().unwrap().0;
611+
// We build our CPFP transaction
612+
cpfp_tx.input.push(TxIn {
613+
previous_output: anchor_outpoint,
614+
script_sig: Script::new(),
615+
sequence: 0xfffffffd,
616+
witness: Vec::new(),
617+
});
618+
cpfp_tx.input.push(TxIn {
619+
previous_output: bumping_outpoint,
620+
script_sig: Script::new(),
621+
sequence: 0xfffffffd,
622+
witness: Vec::new(),
623+
});
624+
// We sign and witness finalize anchor input
625+
if let Ok(anchor_sig) = onchain_handler.key_storage.sign_cpfp(&cpfp_tx, 0, ANCHOR_OUTPUT_VALUE, &onchain_handler.secp_ctx) {
626+
cpfp_tx.input[0].witness.push(anchor_sig.serialize_der().to_vec());
627+
cpfp_tx.input[0].witness[0].push(SigHashType::All as u8);
628+
cpfp_tx.input[0].witness.push(our_anchor_output_script.into_bytes());
629+
}
630+
//// We sign and witness finalize bumping input
631+
if let Ok(witness) = utxo_pool.provide_utxo_witness(&cpfp_tx, 1) {
632+
cpfp_tx.input[1].witness = witness;
633+
}
634+
}
583635
log_trace!(logger, "Going to broadcast Holder Transaction {} claiming funding output {} from {}...", signed_tx.txid(), input.0.vout, input.0.txid);
584-
return Some(signed_tx);
636+
return Some(vec![signed_tx, cpfp_tx]);
585637
}
586638
}
587639
}

lightning/src/ln/onchaintx.rs

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ impl Writeable for Option<Vec<Option<(usize, Signature)>>> {
106106
/// do RBF bumping if possible.
107107
pub struct OnchainTxHandler<ChanSigner: ChannelKeys> {
108108
destination_script: Script,
109-
holder_commitment: Option<HolderCommitmentTransaction>,
109+
pub(super) holder_commitment: Option<HolderCommitmentTransaction>,
110110
// holder_htlc_sigs and prev_holder_htlc_sigs are in the order as they appear in the commitment
111111
// transaction outputs (hence the Option<>s inside the Vec). The first usize is the index in
112112
// the set of HTLCs in the HolderCommitmentTransaction (including those which do not appear in
@@ -303,7 +303,7 @@ impl<ChanSigner: ChannelKeys> OnchainTxHandler<ChanSigner> {
303303

304304
/// Lightning security model (i.e being able to redeem/timeout HTLC or penalize coutnerparty onchain) lays on the assumption of claim transactions getting confirmed before timelock expiration
305305
/// (CSV or CLTV following cases). In case of high-fee spikes, claim tx may stuck in the mempool, so you need to bump its feerate quickly using Replace-By-Fee or Child-Pay-For-Parent.
306-
fn generate_claim_tx<F: Deref, L: Deref, U: Deref>(&mut self, height: u32, cached_request: &mut OnchainRequest, fee_estimator: F, logger: L, utxo_pool: U) -> Option<(Option<u32>, u64, Transaction)>
306+
fn generate_claim_tx<F: Deref, L: Deref, U: Deref>(&mut self, height: u32, cached_request: &mut OnchainRequest, fee_estimator: F, logger: L, utxo_pool: U) -> Option<(Option<u32>, u64, Vec<Transaction>)>
307307
where F::Target: FeeEstimator,
308308
L::Target: Logger,
309309
U::Target: UtxoPool,
@@ -323,15 +323,17 @@ impl<ChanSigner: ChannelKeys> OnchainTxHandler<ChanSigner> {
323323
if let Some((output_value, new_feerate)) = onchain_utils::compute_output_value(predicted_weight, amt, cached_request.feerate_previous, &fee_estimator, &logger) {
324324
assert!(new_feerate != 0);
325325

326-
let transaction = cached_request.content.package_finalize(self, output_value, self.destination_script.clone(), &logger).unwrap();
327-
log_trace!(logger, "...with timer {} and feerate {}", new_timer.unwrap(), new_feerate);
328-
assert!(predicted_weight >= transaction.get_weight());
329-
// Temporary: disable timer for CPFP-package
330-
if cached_request.bump_strategy == BumpStrategy::CPFP {
326+
let txn = cached_request.content.package_finalize(self, output_value, self.destination_script.clone(), &logger, &utxo_pool).unwrap();
327+
log_trace!(logger, "...with timer {} weight {} feerate {} CPFP: {}", new_timer.unwrap(), predicted_weight, new_feerate, txn.len() > 1);
328+
assert!(predicted_weight >= txn[0].get_weight() + if txn.len() == 2 { txn[1].get_weight() } else { 0 });
329+
//TODO: for now disable timer for CPFP-package (2nd-stage HTLC only).
330+
// Enabling them is pending on refactoring first holder HTLCs construction
331+
// and signing.
332+
if predicted_weight == 706 || predicted_weight == 666 {
331333
new_timer = None;
332334
}
333335

334-
return Some((new_timer, new_feerate, transaction))
336+
return Some((new_timer, new_feerate, txn))
335337
}
336338
None
337339
}
@@ -365,17 +367,19 @@ impl<ChanSigner: ChannelKeys> OnchainTxHandler<ChanSigner> {
365367
// Generate claim transactions and track them to bump if necessary at
366368
// height timer expiration (i.e in how many blocks we're going to take action).
367369
for mut req in preprocessed_requests {
368-
if let Some((new_timer, new_feerate, tx)) = self.generate_claim_tx(height, &mut req, &*fee_estimator, &*logger, &*utxo_pool) {
370+
if let Some((new_timer, new_feerate, txn)) = self.generate_claim_tx(height, &mut req, &*fee_estimator, &*logger, &*utxo_pool) {
369371
req.height_timer = new_timer;
370372
req.feerate_previous = new_feerate;
371-
let txid = tx.txid();
373+
let txid = txn[0].txid();
372374
for k in req.content.outpoints() {
373375
log_trace!(logger, "Registering claiming request for {}:{}", k.txid, k.vout);
374376
self.claimable_outpoints.insert(k.clone(), (txid, height));
375377
}
376378
self.pending_claim_requests.insert(txid, req);
377-
log_trace!(logger, "Broadcast onchain {}", log_tx!(tx));
378-
broadcaster.broadcast_transaction(&tx);
379+
for tx in txn {
380+
log_trace!(logger, "Broadcast onchain {}", log_tx!(tx));
381+
broadcaster.broadcast_transaction(&tx);
382+
}
379383
}
380384
}
381385

@@ -492,9 +496,11 @@ impl<ChanSigner: ChannelKeys> OnchainTxHandler<ChanSigner> {
492496
// Build, bump and rebroadcast tx accordingly
493497
log_trace!(logger, "Bumping {} candidates", bump_candidates.len());
494498
for (first_claim_txid, ref mut request) in bump_candidates.iter_mut() {
495-
if let Some((new_timer, new_feerate, bump_tx)) = self.generate_claim_tx(height, request, &*fee_estimator, &*logger, &*utxo_pool) {
496-
log_trace!(logger, "Broadcast onchain {}", log_tx!(bump_tx));
497-
broadcaster.broadcast_transaction(&bump_tx);
499+
if let Some((new_timer, new_feerate, txn)) = self.generate_claim_tx(height, request, &*fee_estimator, &*logger, &*utxo_pool) {
500+
for tx in txn {
501+
log_trace!(logger, "Broadcast onchain {}", log_tx!(tx));
502+
broadcaster.broadcast_transaction(&tx);
503+
}
498504
if let Some(request) = self.pending_claim_requests.get_mut(first_claim_txid) {
499505
request.height_timer = new_timer;
500506
request.feerate_previous = new_feerate;
@@ -530,10 +536,12 @@ impl<ChanSigner: ChannelKeys> OnchainTxHandler<ChanSigner> {
530536
}
531537
}
532538
for (_, ref mut request) in bump_candidates.iter_mut() {
533-
if let Some((new_timer, new_feerate, bump_tx)) = self.generate_claim_tx(height, request, &*fee_estimator, &*logger, &*utxo_pool) {
539+
if let Some((new_timer, new_feerate, txn)) = self.generate_claim_tx(height, request, &*fee_estimator, &*logger, &*utxo_pool) {
534540
request.height_timer = new_timer;
535541
request.feerate_previous = new_feerate;
536-
broadcaster.broadcast_transaction(&bump_tx);
542+
for tx in txn {
543+
broadcaster.broadcast_transaction(&tx);
544+
}
537545
}
538546
}
539547
for (ancestor_claim_txid, request) in bump_candidates.drain() {

0 commit comments

Comments
 (0)