Skip to content

Commit eea56e9

Browse files
authored
Merge pull request #1825 from wpaulino/anchors-bump-htlc-resolution-event
Introduce new BumpTransactionEvent variant HTLCResolution
2 parents 2390dbc + ec1f334 commit eea56e9

File tree

7 files changed

+599
-155
lines changed

7 files changed

+599
-155
lines changed

lightning/src/chain/channelmonitor.rs

+143-57
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ use crate::util::ser::{Readable, ReadableArgs, MaybeReadable, Writer, Writeable,
5353
use crate::util::byte_utils;
5454
use crate::util::events::Event;
5555
#[cfg(anchors)]
56-
use crate::util::events::{AnchorDescriptor, BumpTransactionEvent};
56+
use crate::util::events::{AnchorDescriptor, HTLCDescriptor, BumpTransactionEvent};
5757

5858
use crate::prelude::*;
5959
use core::{cmp, mem};
@@ -647,6 +647,7 @@ struct IrrevocablyResolvedHTLC {
647647
/// was not present in the confirmed commitment transaction), HTLC-Success, or HTLC-Timeout
648648
/// transaction.
649649
resolving_txid: Option<Txid>, // Added as optional, but always filled in, in 0.0.110
650+
resolving_tx: Option<Transaction>,
650651
/// Only set if the HTLC claim was ours using a payment preimage
651652
payment_preimage: Option<PaymentPreimage>,
652653
}
@@ -662,6 +663,7 @@ impl Writeable for IrrevocablyResolvedHTLC {
662663
(0, mapped_commitment_tx_output_idx, required),
663664
(1, self.resolving_txid, option),
664665
(2, self.payment_preimage, option),
666+
(3, self.resolving_tx, option),
665667
});
666668
Ok(())
667669
}
@@ -672,15 +674,18 @@ impl Readable for IrrevocablyResolvedHTLC {
672674
let mut mapped_commitment_tx_output_idx = 0;
673675
let mut resolving_txid = None;
674676
let mut payment_preimage = None;
677+
let mut resolving_tx = None;
675678
read_tlv_fields!(reader, {
676679
(0, mapped_commitment_tx_output_idx, required),
677680
(1, resolving_txid, option),
678681
(2, payment_preimage, option),
682+
(3, resolving_tx, option),
679683
});
680684
Ok(Self {
681685
commitment_tx_output_idx: if mapped_commitment_tx_output_idx == u32::max_value() { None } else { Some(mapped_commitment_tx_output_idx) },
682686
resolving_txid,
683687
payment_preimage,
688+
resolving_tx,
684689
})
685690
}
686691
}
@@ -1526,6 +1531,7 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
15261531
if let Some(v) = htlc.transaction_output_index { v } else { return None; };
15271532

15281533
let mut htlc_spend_txid_opt = None;
1534+
let mut htlc_spend_tx_opt = None;
15291535
let mut holder_timeout_spend_pending = None;
15301536
let mut htlc_spend_pending = None;
15311537
let mut holder_delayed_output_pending = None;
@@ -1534,15 +1540,19 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
15341540
OnchainEvent::HTLCUpdate { commitment_tx_output_idx, htlc_value_satoshis, .. }
15351541
if commitment_tx_output_idx == Some(htlc_commitment_tx_output_idx) => {
15361542
debug_assert!(htlc_spend_txid_opt.is_none());
1537-
htlc_spend_txid_opt = event.transaction.as_ref().map(|tx| tx.txid());
1543+
htlc_spend_txid_opt = Some(&event.txid);
1544+
debug_assert!(htlc_spend_tx_opt.is_none());
1545+
htlc_spend_tx_opt = event.transaction.as_ref();
15381546
debug_assert!(holder_timeout_spend_pending.is_none());
15391547
debug_assert_eq!(htlc_value_satoshis.unwrap(), htlc.amount_msat / 1000);
15401548
holder_timeout_spend_pending = Some(event.confirmation_threshold());
15411549
},
15421550
OnchainEvent::HTLCSpendConfirmation { commitment_tx_output_idx, preimage, .. }
15431551
if commitment_tx_output_idx == htlc_commitment_tx_output_idx => {
15441552
debug_assert!(htlc_spend_txid_opt.is_none());
1545-
htlc_spend_txid_opt = event.transaction.as_ref().map(|tx| tx.txid());
1553+
htlc_spend_txid_opt = Some(&event.txid);
1554+
debug_assert!(htlc_spend_tx_opt.is_none());
1555+
htlc_spend_tx_opt = event.transaction.as_ref();
15461556
debug_assert!(htlc_spend_pending.is_none());
15471557
htlc_spend_pending = Some((event.confirmation_threshold(), preimage.is_some()));
15481558
},
@@ -1558,19 +1568,32 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
15581568
let htlc_resolved = self.htlcs_resolved_on_chain.iter()
15591569
.find(|v| if v.commitment_tx_output_idx == Some(htlc_commitment_tx_output_idx) {
15601570
debug_assert!(htlc_spend_txid_opt.is_none());
1561-
htlc_spend_txid_opt = v.resolving_txid;
1571+
htlc_spend_txid_opt = v.resolving_txid.as_ref();
1572+
debug_assert!(htlc_spend_tx_opt.is_none());
1573+
htlc_spend_tx_opt = v.resolving_tx.as_ref();
15621574
true
15631575
} else { false });
15641576
debug_assert!(holder_timeout_spend_pending.is_some() as u8 + htlc_spend_pending.is_some() as u8 + htlc_resolved.is_some() as u8 <= 1);
15651577

1578+
let htlc_commitment_outpoint = BitcoinOutPoint::new(confirmed_txid.unwrap(), htlc_commitment_tx_output_idx);
15661579
let htlc_output_to_spend =
15671580
if let Some(txid) = htlc_spend_txid_opt {
1568-
debug_assert!(
1569-
self.onchain_tx_handler.channel_transaction_parameters.opt_anchors.is_none(),
1570-
"This code needs updating for anchors");
1571-
BitcoinOutPoint::new(txid, 0)
1581+
// Because HTLC transactions either only have 1 input and 1 output (pre-anchors) or
1582+
// are signed with SIGHASH_SINGLE|ANYONECANPAY under BIP-0143 (post-anchors), we can
1583+
// locate the correct output by ensuring its adjacent input spends the HTLC output
1584+
// in the commitment.
1585+
if let Some(ref tx) = htlc_spend_tx_opt {
1586+
let htlc_input_idx_opt = tx.input.iter().enumerate()
1587+
.find(|(_, input)| input.previous_output == htlc_commitment_outpoint)
1588+
.map(|(idx, _)| idx as u32);
1589+
debug_assert!(htlc_input_idx_opt.is_some());
1590+
BitcoinOutPoint::new(*txid, htlc_input_idx_opt.unwrap_or(0))
1591+
} else {
1592+
debug_assert!(!self.onchain_tx_handler.opt_anchors());
1593+
BitcoinOutPoint::new(*txid, 0)
1594+
}
15721595
} else {
1573-
BitcoinOutPoint::new(confirmed_txid.unwrap(), htlc_commitment_tx_output_idx)
1596+
htlc_commitment_outpoint
15741597
};
15751598
let htlc_output_spend_pending = self.onchain_tx_handler.is_output_spend_pending(&htlc_output_to_spend);
15761599

@@ -1594,8 +1617,7 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
15941617
} = &event.event {
15951618
if event.transaction.as_ref().map(|tx| tx.input.iter().any(|inp| {
15961619
if let Some(htlc_spend_txid) = htlc_spend_txid_opt {
1597-
Some(tx.txid()) == htlc_spend_txid_opt ||
1598-
inp.previous_output.txid == htlc_spend_txid
1620+
tx.txid() == *htlc_spend_txid || inp.previous_output.txid == *htlc_spend_txid
15991621
} else {
16001622
Some(inp.previous_output.txid) == confirmed_txid &&
16011623
inp.previous_output.vout == htlc_commitment_tx_output_idx
@@ -2403,6 +2425,27 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
24032425
pending_htlcs,
24042426
}));
24052427
},
2428+
ClaimEvent::BumpHTLC {
2429+
target_feerate_sat_per_1000_weight, htlcs,
2430+
} => {
2431+
let mut htlc_descriptors = Vec::with_capacity(htlcs.len());
2432+
for htlc in htlcs {
2433+
htlc_descriptors.push(HTLCDescriptor {
2434+
channel_keys_id: self.channel_keys_id,
2435+
channel_value_satoshis: self.channel_value_satoshis,
2436+
channel_parameters: self.onchain_tx_handler.channel_transaction_parameters.clone(),
2437+
commitment_txid: htlc.commitment_txid,
2438+
per_commitment_number: htlc.per_commitment_number,
2439+
htlc: htlc.htlc,
2440+
preimage: htlc.preimage,
2441+
counterparty_sig: htlc.counterparty_sig,
2442+
});
2443+
}
2444+
ret.push(Event::BumpTransaction(BumpTransactionEvent::HTLCResolution {
2445+
target_feerate_sat_per_1000_weight,
2446+
htlc_descriptors,
2447+
}));
2448+
}
24062449
}
24072450
}
24082451
ret
@@ -2623,31 +2666,49 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
26232666
}
26242667

26252668
/// Attempts to claim a counterparty HTLC-Success/HTLC-Timeout's outputs using the revocation key
2626-
fn check_spend_counterparty_htlc<L: Deref>(&mut self, tx: &Transaction, commitment_number: u64, height: u32, logger: &L) -> (Vec<PackageTemplate>, Option<TransactionOutputs>) where L::Target: Logger {
2627-
let htlc_txid = tx.txid();
2628-
if tx.input.len() != 1 || tx.output.len() != 1 || tx.input[0].witness.len() != 5 {
2629-
return (Vec::new(), None)
2630-
}
2631-
2632-
macro_rules! ignore_error {
2633-
( $thing : expr ) => {
2634-
match $thing {
2635-
Ok(a) => a,
2636-
Err(_) => return (Vec::new(), None)
2637-
}
2638-
};
2639-
}
2640-
2669+
fn check_spend_counterparty_htlc<L: Deref>(
2670+
&mut self, tx: &Transaction, commitment_number: u64, commitment_txid: &Txid, height: u32, logger: &L
2671+
) -> (Vec<PackageTemplate>, Option<TransactionOutputs>) where L::Target: Logger {
26412672
let secret = if let Some(secret) = self.get_secret(commitment_number) { secret } else { return (Vec::new(), None); };
2642-
let per_commitment_key = ignore_error!(SecretKey::from_slice(&secret));
2673+
let per_commitment_key = match SecretKey::from_slice(&secret) {
2674+
Ok(key) => key,
2675+
Err(_) => return (Vec::new(), None)
2676+
};
26432677
let per_commitment_point = PublicKey::from_secret_key(&self.secp_ctx, &per_commitment_key);
26442678

2645-
log_error!(logger, "Got broadcast of revoked counterparty HTLC transaction, spending {}:{}", htlc_txid, 0);
2646-
let revk_outp = RevokedOutput::build(per_commitment_point, self.counterparty_commitment_params.counterparty_delayed_payment_base_key, self.counterparty_commitment_params.counterparty_htlc_base_key, per_commitment_key, tx.output[0].value, self.counterparty_commitment_params.on_counterparty_tx_csv);
2647-
let justice_package = PackageTemplate::build_package(htlc_txid, 0, PackageSolvingData::RevokedOutput(revk_outp), height + self.counterparty_commitment_params.on_counterparty_tx_csv as u32, true, height);
2648-
let claimable_outpoints = vec!(justice_package);
2649-
let outputs = vec![(0, tx.output[0].clone())];
2650-
(claimable_outpoints, Some((htlc_txid, outputs)))
2679+
let htlc_txid = tx.txid();
2680+
let mut claimable_outpoints = vec![];
2681+
let mut outputs_to_watch = None;
2682+
// Previously, we would only claim HTLCs from revoked HTLC transactions if they had 1 input
2683+
// with a witness of 5 elements and 1 output. This wasn't enough for anchor outputs, as the
2684+
// counterparty can now aggregate multiple HTLCs into a single transaction thanks to
2685+
// `SIGHASH_SINGLE` remote signatures, leading us to not claim any HTLCs upon seeing a
2686+
// confirmed revoked HTLC transaction (for more details, see
2687+
// https://lists.linuxfoundation.org/pipermail/lightning-dev/2022-April/003561.html).
2688+
//
2689+
// We make sure we're not vulnerable to this case by checking all inputs of the transaction,
2690+
// and claim those which spend the commitment transaction, have a witness of 5 elements, and
2691+
// have a corresponding output at the same index within the transaction.
2692+
for (idx, input) in tx.input.iter().enumerate() {
2693+
if input.previous_output.txid == *commitment_txid && input.witness.len() == 5 && tx.output.get(idx).is_some() {
2694+
log_error!(logger, "Got broadcast of revoked counterparty HTLC transaction, spending {}:{}", htlc_txid, idx);
2695+
let revk_outp = RevokedOutput::build(
2696+
per_commitment_point, self.counterparty_commitment_params.counterparty_delayed_payment_base_key,
2697+
self.counterparty_commitment_params.counterparty_htlc_base_key, per_commitment_key,
2698+
tx.output[idx].value, self.counterparty_commitment_params.on_counterparty_tx_csv
2699+
);
2700+
let justice_package = PackageTemplate::build_package(
2701+
htlc_txid, idx as u32, PackageSolvingData::RevokedOutput(revk_outp),
2702+
height + self.counterparty_commitment_params.on_counterparty_tx_csv as u32, true, height
2703+
);
2704+
claimable_outpoints.push(justice_package);
2705+
if outputs_to_watch.is_none() {
2706+
outputs_to_watch = Some((htlc_txid, vec![]));
2707+
}
2708+
outputs_to_watch.as_mut().unwrap().1.push((idx as u32, tx.output[idx].clone()));
2709+
}
2710+
}
2711+
(claimable_outpoints, outputs_to_watch)
26512712
}
26522713

26532714
// Returns (1) `PackageTemplate`s that can be given to the OnchainTxHandler, so that the handler can
@@ -2661,18 +2722,28 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
26612722

26622723
for &(ref htlc, _, _) in holder_tx.htlc_outputs.iter() {
26632724
if let Some(transaction_output_index) = htlc.transaction_output_index {
2664-
let htlc_output = if htlc.offered {
2665-
HolderHTLCOutput::build_offered(htlc.amount_msat, htlc.cltv_expiry)
2725+
let (htlc_output, aggregable) = if htlc.offered {
2726+
let htlc_output = HolderHTLCOutput::build_offered(
2727+
htlc.amount_msat, htlc.cltv_expiry, self.onchain_tx_handler.opt_anchors()
2728+
);
2729+
(htlc_output, false)
2730+
} else {
2731+
let payment_preimage = if let Some(preimage) = self.payment_preimages.get(&htlc.payment_hash) {
2732+
preimage.clone()
26662733
} else {
2667-
let payment_preimage = if let Some(preimage) = self.payment_preimages.get(&htlc.payment_hash) {
2668-
preimage.clone()
2669-
} else {
2670-
// We can't build an HTLC-Success transaction without the preimage
2671-
continue;
2672-
};
2673-
HolderHTLCOutput::build_accepted(payment_preimage, htlc.amount_msat)
2734+
// We can't build an HTLC-Success transaction without the preimage
2735+
continue;
26742736
};
2675-
let htlc_package = PackageTemplate::build_package(holder_tx.txid, transaction_output_index, PackageSolvingData::HolderHTLCOutput(htlc_output), htlc.cltv_expiry, false, conf_height);
2737+
let htlc_output = HolderHTLCOutput::build_accepted(
2738+
payment_preimage, htlc.amount_msat, self.onchain_tx_handler.opt_anchors()
2739+
);
2740+
(htlc_output, self.onchain_tx_handler.opt_anchors())
2741+
};
2742+
let htlc_package = PackageTemplate::build_package(
2743+
holder_tx.txid, transaction_output_index,
2744+
PackageSolvingData::HolderHTLCOutput(htlc_output),
2745+
htlc.cltv_expiry, aggregable, conf_height
2746+
);
26762747
claim_requests.push(htlc_package);
26772748
}
26782749
}
@@ -2905,9 +2976,9 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
29052976

29062977
if tx.input.len() == 1 {
29072978
// Assuming our keys were not leaked (in which case we're screwed no matter what),
2908-
// commitment transactions and HTLC transactions will all only ever have one input,
2909-
// which is an easy way to filter out any potential non-matching txn for lazy
2910-
// filters.
2979+
// commitment transactions and HTLC transactions will all only ever have one input
2980+
// (except for HTLC transactions for channels with anchor outputs), which is an easy
2981+
// way to filter out any potential non-matching txn for lazy filters.
29112982
let prevout = &tx.input[0].previous_output;
29122983
if prevout.txid == self.funding_info.0.txid && prevout.vout == self.funding_info.0.index as u32 {
29132984
let mut balance_spendable_csv = None;
@@ -2945,22 +3016,33 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
29453016
commitment_tx_to_counterparty_output,
29463017
},
29473018
});
2948-
} else {
2949-
if let Some(&commitment_number) = self.counterparty_commitment_txn_on_chain.get(&prevout.txid) {
2950-
let (mut new_outpoints, new_outputs_option) = self.check_spend_counterparty_htlc(&tx, commitment_number, height, &logger);
3019+
}
3020+
}
3021+
if tx.input.len() >= 1 {
3022+
// While all commitment transactions have one input, HTLC transactions may have more
3023+
// if the HTLC was present in an anchor channel. HTLCs can also be resolved in a few
3024+
// other ways which can have more than one output.
3025+
for tx_input in &tx.input {
3026+
let commitment_txid = tx_input.previous_output.txid;
3027+
if let Some(&commitment_number) = self.counterparty_commitment_txn_on_chain.get(&commitment_txid) {
3028+
let (mut new_outpoints, new_outputs_option) = self.check_spend_counterparty_htlc(
3029+
&tx, commitment_number, &commitment_txid, height, &logger
3030+
);
29513031
claimable_outpoints.append(&mut new_outpoints);
29523032
if let Some(new_outputs) = new_outputs_option {
29533033
watch_outputs.push(new_outputs);
29543034
}
3035+
// Since there may be multiple HTLCs (all from the same commitment) being
3036+
// claimed by the counterparty within the same transaction, and
3037+
// `check_spend_counterparty_htlc` already checks for all of them, we can
3038+
// safely break from our loop.
3039+
break;
29553040
}
29563041
}
2957-
}
2958-
// While all commitment/HTLC-Success/HTLC-Timeout transactions have one input, HTLCs
2959-
// can also be resolved in a few other ways which can have more than one output. Thus,
2960-
// we call is_resolving_htlc_output here outside of the tx.input.len() == 1 check.
2961-
self.is_resolving_htlc_output(&tx, height, &block_hash, &logger);
3042+
self.is_resolving_htlc_output(&tx, height, &block_hash, &logger);
29623043

2963-
self.is_paying_spendable_output(&tx, height, &block_hash, &logger);
3044+
self.is_paying_spendable_output(&tx, height, &block_hash, &logger);
3045+
}
29643046
}
29653047

29663048
if height > self.best_block.height() {
@@ -3074,7 +3156,9 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
30743156
htlc_value_satoshis,
30753157
}));
30763158
self.htlcs_resolved_on_chain.push(IrrevocablyResolvedHTLC {
3077-
commitment_tx_output_idx, resolving_txid: Some(entry.txid),
3159+
commitment_tx_output_idx,
3160+
resolving_txid: Some(entry.txid),
3161+
resolving_tx: entry.transaction,
30783162
payment_preimage: None,
30793163
});
30803164
},
@@ -3087,7 +3171,9 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
30873171
},
30883172
OnchainEvent::HTLCSpendConfirmation { commitment_tx_output_idx, preimage, .. } => {
30893173
self.htlcs_resolved_on_chain.push(IrrevocablyResolvedHTLC {
3090-
commitment_tx_output_idx: Some(commitment_tx_output_idx), resolving_txid: Some(entry.txid),
3174+
commitment_tx_output_idx: Some(commitment_tx_output_idx),
3175+
resolving_txid: Some(entry.txid),
3176+
resolving_tx: entry.transaction,
30913177
payment_preimage: preimage,
30923178
});
30933179
},

0 commit comments

Comments
 (0)