Skip to content

Commit c02b6a3

Browse files
committed
Track how our HTLCs are resolved on-chain persistently
This tracks how any HTLC outputs in broadcast commitment transactions are resolved on-chain, storing the result of the HTLC resolution persistently in the ChannelMonitor. This can be used to determine which outputs may still be available for claiming on-chain.
1 parent 73ee30d commit c02b6a3

File tree

1 file changed

+136
-22
lines changed

1 file changed

+136
-22
lines changed

lightning/src/chain/channelmonitor.rs

Lines changed: 136 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -364,7 +364,8 @@ impl OnchainEventEntry {
364364
// it's broadcastable when we see the previous block.
365365
conf_threshold = cmp::max(conf_threshold, self.height + descriptor.to_self_delay as u32 - 1);
366366
},
367-
OnchainEvent::FundingSpendConfirmation { on_local_output_csv: Some(csv), .. } => {
367+
OnchainEvent::FundingSpendConfirmation { on_local_output_csv: Some(csv), .. } |
368+
OnchainEvent::HTLCSpendConfirmation { on_to_local_output_csv: Some(csv), .. } => {
368369
// A CSV'd transaction is confirmable in block (input height) + CSV delay, which means
369370
// it's broadcastable when we see the previous block.
370371
conf_threshold = cmp::max(conf_threshold, self.height + csv as u32 - 1);
@@ -383,25 +384,47 @@ impl OnchainEventEntry {
383384
/// once they mature to enough confirmations (ANTI_REORG_DELAY)
384385
#[derive(PartialEq)]
385386
enum OnchainEvent {
386-
/// HTLC output getting solved by a timeout, at maturation we pass upstream payment source information to solve
387-
/// inbound HTLC in backward channel. Note, in case of preimage, we pass info to upstream without delay as we can
388-
/// only win from it, so it's never an OnchainEvent
387+
/// An outbound HTLC failing after a transaction is confirmed. Used
388+
/// * when an outbound HTLC output is spent by us after the HTLC timed out
389+
/// * an outbound HTLC which was not present in the commitment transaction which appeared
390+
/// on-chain (either because it was not fully committed to or it was dust).
391+
/// Note that this is *not* used for preimage claims, as those are passed upstream immediately,
392+
/// appearing only as an `HTLCSpendConfirmation`, below.
389393
HTLCUpdate {
390394
source: HTLCSource,
391395
payment_hash: PaymentHash,
392396
onchain_value_satoshis: Option<u64>,
397+
/// None in the second case, above, ie when there is no relevant output in the commitment
398+
/// transaction which appeared on chain.
399+
input_idx: Option<u32>,
393400
},
394401
MaturingOutput {
395402
descriptor: SpendableOutputDescriptor,
396403
},
397404
/// A spend of the funding output, either a commitment transaction or a cooperative closing
398405
/// transaction.
399406
FundingSpendConfirmation {
400-
txid: Txid,
401407
/// The CSV delay for the output of the funding spend transaction (implying it is a local
402408
/// commitment transaction, and this is the delay on the to_self output).
403409
on_local_output_csv: Option<u16>,
404410
},
411+
/// A spend of a commitment transaction HTLC output, set in the cases where *no* `HTLCUpdate`
412+
/// is constructed. This is used when
413+
/// * an outbound HTLC is claimed by our counterparty with a preimage, causing us to
414+
/// immediately claim the HTLC on the inbound edge and track the resolution here,
415+
/// * an inbound HTLC is claimed by our counterparty (with a timeout),
416+
/// * an inbound HTLC is claimed by us (with a preimage).
417+
/// * a revoked-state HTLC transaction was broadcasted, which was claimed by the revocation
418+
/// signature.
419+
HTLCSpendConfirmation {
420+
input_idx: u32,
421+
/// If the claim was made by either party with a preimage, this is filled in
422+
preimage: Option<PaymentPreimage>,
423+
/// If the claim was made by us on an inbound HTLC against a local commitment transaction,
424+
/// we set this to the output CSV value which we will have to wait until to spend the
425+
/// output (and generate a SpendableOutput event).
426+
on_to_local_output_csv: Option<u16>,
427+
},
405428
}
406429

407430
impl Writeable for OnchainEventEntry {
@@ -438,14 +461,20 @@ impl_writeable_tlv_based_enum_upgradable!(OnchainEvent,
438461
(0, source, required),
439462
(1, onchain_value_satoshis, option),
440463
(2, payment_hash, required),
464+
(3, input_idx, option),
441465
},
442466
(1, MaturingOutput) => {
443467
(0, descriptor, required),
444468
},
445469
(3, FundingSpendConfirmation) => {
446-
(0, txid, required),
447-
(2, on_local_output_csv, option),
470+
(0, on_local_output_csv, option),
448471
},
472+
(5, HTLCSpendConfirmation) => {
473+
(0, input_idx, required),
474+
(2, preimage, option),
475+
(4, on_to_local_output_csv, option),
476+
},
477+
449478
);
450479

451480
#[cfg_attr(any(test, feature = "fuzztarget", feature = "_test_utils"), derive(PartialEq))]
@@ -506,6 +535,19 @@ impl_writeable_tlv_based_enum_upgradable!(ChannelMonitorUpdateStep,
506535
},
507536
);
508537

538+
/// An HTLC which has been irrevocably resolved on-chain, and has reached ANTI_REORG_DELAY.
539+
#[derive(PartialEq)]
540+
struct IrrevocablyResolvedHTLC {
541+
input_idx: u32,
542+
/// Only set if the HTLC claim was ours using a payment preimage
543+
payment_preimage: Option<PaymentPreimage>,
544+
}
545+
546+
impl_writeable_tlv_based!(IrrevocablyResolvedHTLC, {
547+
(0, input_idx, required),
548+
(2, payment_preimage, option),
549+
});
550+
509551
/// A ChannelMonitor handles chain events (blocks connected and disconnected) and generates
510552
/// on-chain transactions to ensure no loss of funds occurs.
511553
///
@@ -619,6 +661,10 @@ pub(crate) struct ChannelMonitorImpl<Signer: Sign> {
619661
holder_tx_signed: bool,
620662

621663
funding_spend_confirmed: Option<Txid>,
664+
/// The set of HTLCs which have been either claimed or failed on chain and have reached
665+
/// the requisite confirmations on the claim/fail transaction (either ANTI_REORG_DELAY or the
666+
/// spending CSV for revocable outputs).
667+
htlcs_resolved_on_chain: Vec<IrrevocablyResolvedHTLC>,
622668

623669
// We simply modify best_block in Channel's block_connected so that serialization is
624670
// consistent but hopefully the users' copy handles block_connected in a consistent way.
@@ -679,7 +725,8 @@ impl<Signer: Sign> PartialEq for ChannelMonitorImpl<Signer> {
679725
self.outputs_to_watch != other.outputs_to_watch ||
680726
self.lockdown_from_offchain != other.lockdown_from_offchain ||
681727
self.holder_tx_signed != other.holder_tx_signed ||
682-
self.funding_spend_confirmed != other.funding_spend_confirmed
728+
self.funding_spend_confirmed != other.funding_spend_confirmed ||
729+
self.htlcs_resolved_on_chain != other.htlcs_resolved_on_chain
683730
{
684731
false
685732
} else {
@@ -846,6 +893,7 @@ impl<Signer: Sign> Writeable for ChannelMonitorImpl<Signer> {
846893

847894
write_tlv_fields!(writer, {
848895
(1, self.funding_spend_confirmed, option),
896+
(3, self.htlcs_resolved_on_chain, vec_type),
849897
});
850898

851899
Ok(())
@@ -945,6 +993,7 @@ impl<Signer: Sign> ChannelMonitor<Signer> {
945993
lockdown_from_offchain: false,
946994
holder_tx_signed: false,
947995
funding_spend_confirmed: None,
996+
htlcs_resolved_on_chain: Vec::new(),
948997

949998
best_block,
950999

@@ -1311,6 +1360,7 @@ macro_rules! fail_unbroadcast_htlcs {
13111360
source: (**source).clone(),
13121361
payment_hash: htlc.payment_hash.clone(),
13131362
onchain_value_satoshis: Some(htlc.amount_msat / 1000),
1363+
input_idx: None,
13141364
},
13151365
};
13161366
log_trace!($logger, "Failing HTLC with payment_hash {} from {} counterparty commitment tx due to broadcast of {} commitment transaction, waiting for confirmation (at height {})",
@@ -2024,7 +2074,6 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
20242074
txid,
20252075
height: height,
20262076
event: OnchainEvent::FundingSpendConfirmation {
2027-
txid,
20282077
on_local_output_csv: balance_spendable_csv,
20292078
},
20302079
});
@@ -2115,8 +2164,7 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
21152164
.iter()
21162165
.filter_map(|entry| match &entry.event {
21172166
OnchainEvent::HTLCUpdate { source, .. } => Some(source),
2118-
OnchainEvent::MaturingOutput { .. } => None,
2119-
OnchainEvent::FundingSpendConfirmation { .. } => None,
2167+
_ => None,
21202168
})
21212169
.collect();
21222170
#[cfg(debug_assertions)]
@@ -2125,7 +2173,7 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
21252173
// Produce actionable events from on-chain events having reached their threshold.
21262174
for entry in onchain_events_reaching_threshold_conf.drain(..) {
21272175
match entry.event {
2128-
OnchainEvent::HTLCUpdate { ref source, payment_hash, onchain_value_satoshis } => {
2176+
OnchainEvent::HTLCUpdate { ref source, payment_hash, onchain_value_satoshis, input_idx } => {
21292177
// Check for duplicate HTLC resolutions.
21302178
#[cfg(debug_assertions)]
21312179
{
@@ -2149,15 +2197,21 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
21492197
source: source.clone(),
21502198
onchain_value_satoshis,
21512199
}));
2200+
if let Some(idx) = input_idx {
2201+
self.htlcs_resolved_on_chain.push(IrrevocablyResolvedHTLC { input_idx: idx, payment_preimage: None });
2202+
}
21522203
},
21532204
OnchainEvent::MaturingOutput { descriptor } => {
21542205
log_debug!(logger, "Descriptor {} has got enough confirmations to be passed upstream", log_spendable!(descriptor));
21552206
self.pending_events.push(Event::SpendableOutputs {
21562207
outputs: vec![descriptor]
21572208
});
21582209
},
2159-
OnchainEvent::FundingSpendConfirmation { txid, .. } => {
2160-
self.funding_spend_confirmed = Some(txid);
2210+
OnchainEvent::HTLCSpendConfirmation { input_idx, preimage, .. } => {
2211+
self.htlcs_resolved_on_chain.push(IrrevocablyResolvedHTLC { input_idx, payment_preimage: preimage });
2212+
},
2213+
OnchainEvent::FundingSpendConfirmation { .. } => {
2214+
self.funding_spend_confirmed = Some(entry.txid);
21612215
},
21622216
}
21632217
}
@@ -2336,15 +2390,32 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
23362390
let revocation_sig_claim = (input.witness.len() == 3 && HTLCType::scriptlen_to_htlctype(input.witness[2].len()) == Some(HTLCType::OfferedHTLC) && input.witness[1].len() == 33)
23372391
|| (input.witness.len() == 3 && HTLCType::scriptlen_to_htlctype(input.witness[2].len()) == Some(HTLCType::AcceptedHTLC) && input.witness[1].len() == 33);
23382392
let accepted_preimage_claim = input.witness.len() == 5 && HTLCType::scriptlen_to_htlctype(input.witness[4].len()) == Some(HTLCType::AcceptedHTLC);
2339-
let offered_preimage_claim = input.witness.len() == 3 && HTLCType::scriptlen_to_htlctype(input.witness[2].len()) == Some(HTLCType::OfferedHTLC);
2393+
let accepted_timeout_claim = input.witness.len() == 3 && HTLCType::scriptlen_to_htlctype(input.witness[2].len()) == Some(HTLCType::AcceptedHTLC) && !revocation_sig_claim;
2394+
let offered_preimage_claim = input.witness.len() == 3 && HTLCType::scriptlen_to_htlctype(input.witness[2].len()) == Some(HTLCType::OfferedHTLC) && !revocation_sig_claim;
2395+
let offered_timeout_claim = input.witness.len() == 5 && HTLCType::scriptlen_to_htlctype(input.witness[4].len()) == Some(HTLCType::OfferedHTLC);
2396+
2397+
let mut payment_preimage = PaymentPreimage([0; 32]);
2398+
if accepted_preimage_claim {
2399+
payment_preimage.0.copy_from_slice(&input.witness[3]);
2400+
} else if offered_preimage_claim {
2401+
payment_preimage.0.copy_from_slice(&input.witness[1]);
2402+
}
23402403

23412404
macro_rules! log_claim {
23422405
($tx_info: expr, $holder_tx: expr, $htlc: expr, $source_avail: expr) => {
2343-
// We found the output in question, but aren't failing it backwards
2344-
// as we have no corresponding source and no valid counterparty commitment txid
2345-
// to try a weak source binding with same-hash, same-value still-valid offered HTLC.
2346-
// This implies either it is an inbound HTLC or an outbound HTLC on a revoked transaction.
23472406
let outbound_htlc = $holder_tx == $htlc.offered;
2407+
// HTLCs must either be claimed by a matching script type or through the
2408+
// revocation path:
2409+
#[cfg(not(fuzzing))] // Note that the fuzzer is not bound by pesky things like "signatures"
2410+
debug_assert!(!$htlc.offered || offered_preimage_claim || offered_timeout_claim || revocation_sig_claim);
2411+
#[cfg(not(fuzzing))] // Note that the fuzzer is not bound by pesky things like "signatures"
2412+
debug_assert!($htlc.offered || accepted_preimage_claim || accepted_timeout_claim || revocation_sig_claim);
2413+
// Further, only exactly one of the possible spend paths should have been
2414+
// matched by any HTLC spend:
2415+
#[cfg(not(fuzzing))] // Note that the fuzzer is not bound by pesky things like "signatures"
2416+
debug_assert_eq!(accepted_preimage_claim as u8 + accepted_timeout_claim as u8 +
2417+
offered_preimage_claim as u8 + offered_timeout_claim as u8 +
2418+
revocation_sig_claim as u8, 1);
23482419
if ($holder_tx && revocation_sig_claim) ||
23492420
(outbound_htlc && !$source_avail && (accepted_preimage_claim || offered_preimage_claim)) {
23502421
log_error!(logger, "Input spending {} ({}:{}) in {} resolves {} HTLC with payment hash {} with {}!",
@@ -2396,6 +2467,30 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
23962467
}
23972468
if payment_data.is_none() {
23982469
log_claim!($tx_info, $holder_tx, htlc_output, false);
2470+
let outbound_htlc = $holder_tx == htlc_output.offered;
2471+
if !outbound_htlc || revocation_sig_claim {
2472+
self.onchain_events_awaiting_threshold_conf.push(OnchainEventEntry {
2473+
txid: tx.txid(), height,
2474+
event: OnchainEvent::HTLCSpendConfirmation {
2475+
input_idx: input.previous_output.vout,
2476+
preimage: if accepted_preimage_claim || offered_preimage_claim {
2477+
Some(payment_preimage) } else { None },
2478+
// If this is a payment to us (!outbound_htlc, above),
2479+
// wait for the CSV delay before dropping the HTLC from
2480+
// claimable balance if the claim was an HTLC-Success
2481+
// transaction.
2482+
on_to_local_output_csv: if accepted_preimage_claim {
2483+
Some(self.on_holder_tx_csv) } else { None },
2484+
},
2485+
});
2486+
} else {
2487+
// Outbound claims should always have payment_data, unless
2488+
// we've already failed the HTLC as the commitment transaction
2489+
// which was broadcasted was revoked. In that case, we should
2490+
// spend the HTLC output here immediately, and expose that fact
2491+
// as a ClaimableBalance, something which we do not yet do.
2492+
// TODO: Track the above as claimable!
2493+
}
23992494
continue 'outer_loop;
24002495
}
24012496
}
@@ -2421,11 +2516,18 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
24212516
// Check that scan_commitment, above, decided there is some source worth relaying an
24222517
// HTLC resolution backwards to and figure out whether we learned a preimage from it.
24232518
if let Some((source, payment_hash, amount_msat)) = payment_data {
2424-
let mut payment_preimage = PaymentPreimage([0; 32]);
24252519
if accepted_preimage_claim {
24262520
if !self.pending_monitor_events.iter().any(
24272521
|update| if let &MonitorEvent::HTLCEvent(ref upd) = update { upd.source == source } else { false }) {
2428-
payment_preimage.0.copy_from_slice(&input.witness[3]);
2522+
self.onchain_events_awaiting_threshold_conf.push(OnchainEventEntry {
2523+
txid: tx.txid(),
2524+
height,
2525+
event: OnchainEvent::HTLCSpendConfirmation {
2526+
input_idx: input.previous_output.vout,
2527+
preimage: Some(payment_preimage),
2528+
on_to_local_output_csv: None,
2529+
},
2530+
});
24292531
self.pending_monitor_events.push(MonitorEvent::HTLCEvent(HTLCUpdate {
24302532
source,
24312533
payment_preimage: Some(payment_preimage),
@@ -2438,7 +2540,15 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
24382540
|update| if let &MonitorEvent::HTLCEvent(ref upd) = update {
24392541
upd.source == source
24402542
} else { false }) {
2441-
payment_preimage.0.copy_from_slice(&input.witness[1]);
2543+
self.onchain_events_awaiting_threshold_conf.push(OnchainEventEntry {
2544+
txid: tx.txid(),
2545+
height,
2546+
event: OnchainEvent::HTLCSpendConfirmation {
2547+
input_idx: input.previous_output.vout,
2548+
preimage: Some(payment_preimage),
2549+
on_to_local_output_csv: None,
2550+
},
2551+
});
24422552
self.pending_monitor_events.push(MonitorEvent::HTLCEvent(HTLCUpdate {
24432553
source,
24442554
payment_preimage: Some(payment_preimage),
@@ -2462,6 +2572,7 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
24622572
event: OnchainEvent::HTLCUpdate {
24632573
source, payment_hash,
24642574
onchain_value_satoshis: Some(amount_msat / 1000),
2575+
input_idx: Some(input.previous_output.vout),
24652576
},
24662577
};
24672578
log_info!(logger, "Failing HTLC with payment_hash {} timeout by a spend tx, waiting for confirmation (at height {})", log_bytes!(payment_hash.0), entry.confirmation_threshold());
@@ -2834,8 +2945,10 @@ impl<'a, Signer: Sign, K: KeysInterface<Signer = Signer>> ReadableArgs<&'a K>
28342945
}
28352946

28362947
let mut funding_spend_confirmed = None;
2948+
let mut htlcs_resolved_on_chain = Some(Vec::new());
28372949
read_tlv_fields!(reader, {
28382950
(1, funding_spend_confirmed, option),
2951+
(3, htlcs_resolved_on_chain, vec_type),
28392952
});
28402953

28412954
let mut secp_ctx = Secp256k1::new();
@@ -2886,6 +2999,7 @@ impl<'a, Signer: Sign, K: KeysInterface<Signer = Signer>> ReadableArgs<&'a K>
28862999
lockdown_from_offchain,
28873000
holder_tx_signed,
28883001
funding_spend_confirmed,
3002+
htlcs_resolved_on_chain: htlcs_resolved_on_chain.unwrap(),
28893003

28903004
best_block,
28913005

0 commit comments

Comments
 (0)