Skip to content

Commit dfa0373

Browse files
committed
Track counterparty payout info in counterparty commitment txn
When handling a revoked counterparty commitment transaction which was broadcast on-chain, we occasionally need to look up which output (and its value) was to the counterparty (the `to_self` output). This will allow us to generate `Balance`s for the user for the revoked output.
1 parent feeb550 commit dfa0373

File tree

1 file changed

+82
-13
lines changed

1 file changed

+82
-13
lines changed

lightning/src/chain/channelmonitor.rs

Lines changed: 82 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ use util::events::Event;
5454
use prelude::*;
5555
use core::{cmp, mem};
5656
use io::{self, Error};
57+
use core::convert::TryInto;
5758
use core::ops::Deref;
5859
use sync::Mutex;
5960

@@ -345,6 +346,11 @@ impl OnchainEventEntry {
345346
}
346347
}
347348

349+
/// The (output index, sats value) for the counterparty's output in a commitment transaction.
350+
///
351+
/// This was added as an `Option` in 0.0.110.
352+
type CommitmentTxCounterpartyOutputInfo = Option<(u32, u64)>;
353+
348354
/// Upon discovering of some classes of onchain tx by ChannelMonitor, we may have to take actions on it
349355
/// once they mature to enough confirmations (ANTI_REORG_DELAY)
350356
#[derive(PartialEq)]
@@ -372,6 +378,9 @@ enum OnchainEvent {
372378
/// The CSV delay for the output of the funding spend transaction (implying it is a local
373379
/// commitment transaction, and this is the delay on the to_self output).
374380
on_local_output_csv: Option<u16>,
381+
/// If the funding spend transaction was a known remote commitment transaction, we track
382+
/// the output index and amount of the counterparty's `to_self` output here.
383+
commitment_tx_to_counterparty_output: CommitmentTxCounterpartyOutputInfo,
375384
},
376385
/// A spend of a commitment transaction HTLC output, set in the cases where *no* `HTLCUpdate`
377386
/// is constructed. This is used when
@@ -436,6 +445,7 @@ impl_writeable_tlv_based_enum_upgradable!(OnchainEvent,
436445
},
437446
(3, FundingSpendConfirmation) => {
438447
(0, on_local_output_csv, option),
448+
(1, commitment_tx_to_counterparty_output, option),
439449
},
440450
(5, HTLCSpendConfirmation) => {
441451
(0, commitment_tx_output_idx, required),
@@ -714,6 +724,7 @@ pub(crate) struct ChannelMonitorImpl<Signer: Sign> {
714724
funding_spend_seen: bool,
715725

716726
funding_spend_confirmed: Option<Txid>,
727+
confirmed_commitment_tx_counterparty_output: CommitmentTxCounterpartyOutputInfo,
717728
/// The set of HTLCs which have been either claimed or failed on chain and have reached
718729
/// the requisite confirmations on the claim/fail transaction (either ANTI_REORG_DELAY or the
719730
/// spending CSV for revocable outputs).
@@ -783,6 +794,7 @@ impl<Signer: Sign> PartialEq for ChannelMonitorImpl<Signer> {
783794
self.holder_tx_signed != other.holder_tx_signed ||
784795
self.funding_spend_seen != other.funding_spend_seen ||
785796
self.funding_spend_confirmed != other.funding_spend_confirmed ||
797+
self.confirmed_commitment_tx_counterparty_output != other.confirmed_commitment_tx_counterparty_output ||
786798
self.htlcs_resolved_on_chain != other.htlcs_resolved_on_chain
787799
{
788800
false
@@ -962,6 +974,7 @@ impl<Signer: Sign> Writeable for ChannelMonitorImpl<Signer> {
962974
(5, self.pending_monitor_events, vec_type),
963975
(7, self.funding_spend_seen, required),
964976
(9, self.counterparty_node_id, option),
977+
(11, self.confirmed_commitment_tx_counterparty_output, option),
965978
});
966979

967980
Ok(())
@@ -1068,6 +1081,7 @@ impl<Signer: Sign> ChannelMonitor<Signer> {
10681081
holder_tx_signed: false,
10691082
funding_spend_seen: false,
10701083
funding_spend_confirmed: None,
1084+
confirmed_commitment_tx_counterparty_output: None,
10711085
htlcs_resolved_on_chain: Vec::new(),
10721086

10731087
best_block,
@@ -1914,7 +1928,7 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
19141928
// First check if a counterparty commitment transaction has been broadcasted:
19151929
macro_rules! claim_htlcs {
19161930
($commitment_number: expr, $txid: expr) => {
1917-
let htlc_claim_reqs = self.get_counterparty_htlc_output_claim_reqs($commitment_number, $txid, None);
1931+
let (htlc_claim_reqs, _) = self.get_counterparty_output_claim_info($commitment_number, $txid, None);
19181932
self.onchain_tx_handler.update_claims_view(&Vec::new(), htlc_claim_reqs, self.best_block.height(), self.best_block.height(), broadcaster, fee_estimator, logger);
19191933
}
19201934
}
@@ -2093,13 +2107,18 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
20932107
/// data in counterparty_claimable_outpoints. Will directly claim any HTLC outputs which expire at a
20942108
/// height > height + CLTV_SHARED_CLAIM_BUFFER. In any case, will install monitoring for
20952109
/// HTLC-Success/HTLC-Timeout transactions.
2096-
/// Return updates for HTLC pending in the channel and failed automatically by the broadcast of
2097-
/// revoked counterparty commitment tx
2098-
fn check_spend_counterparty_transaction<L: Deref>(&mut self, tx: &Transaction, height: u32, logger: &L) -> (Vec<PackageTemplate>, TransactionOutputs) where L::Target: Logger {
2110+
///
2111+
/// Returns packages to claim the revoked output(s), as well as additional outputs to watch and
2112+
/// general information about the output that is to the counterparty in the commitment
2113+
/// transaction.
2114+
fn check_spend_counterparty_transaction<L: Deref>(&mut self, tx: &Transaction, height: u32, logger: &L)
2115+
-> (Vec<PackageTemplate>, TransactionOutputs, CommitmentTxCounterpartyOutputInfo)
2116+
where L::Target: Logger {
20992117
// Most secp and related errors trying to create keys means we have no hope of constructing
21002118
// a spend transaction...so we return no transactions to broadcast
21012119
let mut claimable_outpoints = Vec::new();
21022120
let mut watch_outputs = Vec::new();
2121+
let mut to_counterparty_output_info = None;
21032122

21042123
let commitment_txid = tx.txid(); //TODO: This is gonna be a performance bottleneck for watchtowers!
21052124
let per_commitment_option = self.counterparty_claimable_outpoints.get(&commitment_txid);
@@ -2108,7 +2127,7 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
21082127
( $thing : expr ) => {
21092128
match $thing {
21102129
Ok(a) => a,
2111-
Err(_) => return (claimable_outpoints, (commitment_txid, watch_outputs))
2130+
Err(_) => return (claimable_outpoints, (commitment_txid, watch_outputs), to_counterparty_output_info)
21122131
}
21132132
};
21142133
}
@@ -2130,6 +2149,8 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
21302149
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, outp.value, self.counterparty_commitment_params.on_counterparty_tx_csv);
21312150
let justice_package = PackageTemplate::build_package(commitment_txid, idx as u32, PackageSolvingData::RevokedOutput(revk_outp), height + self.counterparty_commitment_params.on_counterparty_tx_csv as u32, true, height);
21322151
claimable_outpoints.push(justice_package);
2152+
to_counterparty_output_info =
2153+
Some((idx.try_into().expect("Txn can't have more than 2^32 outputs"), outp.value));
21332154
}
21342155
}
21352156

@@ -2139,7 +2160,9 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
21392160
if let Some(transaction_output_index) = htlc.transaction_output_index {
21402161
if transaction_output_index as usize >= tx.output.len() ||
21412162
tx.output[transaction_output_index as usize].value != htlc.amount_msat / 1000 {
2142-
return (claimable_outpoints, (commitment_txid, watch_outputs)); // Corrupted per_commitment_data, fuck this user
2163+
// per_commitment_data is corrupt or our commitment signing key leaked!
2164+
return (claimable_outpoints, (commitment_txid, watch_outputs),
2165+
to_counterparty_output_info);
21432166
}
21442167
let revk_htlc_outp = RevokedHTLCOutput::build(per_commitment_point, self.counterparty_commitment_params.counterparty_delayed_payment_base_key, self.counterparty_commitment_params.counterparty_htlc_base_key, per_commitment_key, htlc.amount_msat / 1000, htlc.clone(), self.onchain_tx_handler.channel_transaction_parameters.opt_anchors.is_some());
21452168
let justice_package = PackageTemplate::build_package(commitment_txid, transaction_output_index, PackageSolvingData::RevokedHTLCOutput(revk_htlc_outp), htlc.cltv_expiry, true, height);
@@ -2187,17 +2210,22 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
21872210
(htlc, htlc_source.as_ref().map(|htlc_source| htlc_source.as_ref()))
21882211
), logger);
21892212

2190-
let htlc_claim_reqs = self.get_counterparty_htlc_output_claim_reqs(commitment_number, commitment_txid, Some(tx));
2213+
let (htlc_claim_reqs, counterparty_output_info) =
2214+
self.get_counterparty_output_claim_info(commitment_number, commitment_txid, Some(tx));
2215+
to_counterparty_output_info = counterparty_output_info;
21912216
for req in htlc_claim_reqs {
21922217
claimable_outpoints.push(req);
21932218
}
21942219

21952220
}
2196-
(claimable_outpoints, (commitment_txid, watch_outputs))
2221+
(claimable_outpoints, (commitment_txid, watch_outputs), to_counterparty_output_info)
21972222
}
21982223

2199-
fn get_counterparty_htlc_output_claim_reqs(&self, commitment_number: u64, commitment_txid: Txid, tx: Option<&Transaction>) -> Vec<PackageTemplate> {
2224+
/// Returns the HTLC claim package templates and the counterparty output info
2225+
fn get_counterparty_output_claim_info(&self, commitment_number: u64, commitment_txid: Txid, tx: Option<&Transaction>)
2226+
-> (Vec<PackageTemplate>, CommitmentTxCounterpartyOutputInfo) {
22002227
let mut claimable_outpoints = Vec::new();
2228+
let mut to_counterparty_output_info: CommitmentTxCounterpartyOutputInfo = None;
22012229
if let Some(htlc_outputs) = self.counterparty_claimable_outpoints.get(&commitment_txid) {
22022230
if let Some(per_commitment_points) = self.their_cur_per_commitment_points {
22032231
let per_commitment_point_option =
@@ -2211,12 +2239,43 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
22112239
if per_commitment_points.0 == commitment_number + 1 { Some(point) } else { None }
22122240
} else { None };
22132241
if let Some(per_commitment_point) = per_commitment_point_option {
2242+
if let Some(transaction) = tx {
2243+
let revokeable_p2wsh_opt =
2244+
if let Ok(revocation_pubkey) = chan_utils::derive_public_revocation_key(
2245+
&self.secp_ctx, &per_commitment_point, &self.holder_revocation_basepoint)
2246+
{
2247+
if let Ok(delayed_key) = chan_utils::derive_public_key(&self.secp_ctx,
2248+
&per_commitment_point,
2249+
&self.counterparty_commitment_params.counterparty_delayed_payment_base_key)
2250+
{
2251+
Some(chan_utils::get_revokeable_redeemscript(&revocation_pubkey,
2252+
self.counterparty_commitment_params.on_counterparty_tx_csv,
2253+
&delayed_key).to_v0_p2wsh())
2254+
} else {
2255+
debug_assert!(false, "Failed to derive a delayed payment key for a commitment state we accepted");
2256+
None
2257+
}
2258+
} else {
2259+
debug_assert!(false, "Failed to derive a revocation pubkey key for a commitment state we accepted");
2260+
None
2261+
};
2262+
if let Some(revokeable_p2wsh) = revokeable_p2wsh_opt {
2263+
for (idx, outp) in transaction.output.iter().enumerate() {
2264+
if outp.script_pubkey == revokeable_p2wsh {
2265+
to_counterparty_output_info =
2266+
Some((idx.try_into().expect("Can't have > 2^32 outputs"), outp.value));
2267+
}
2268+
}
2269+
}
2270+
}
2271+
22142272
for (_, &(ref htlc, _)) in htlc_outputs.iter().enumerate() {
22152273
if let Some(transaction_output_index) = htlc.transaction_output_index {
22162274
if let Some(transaction) = tx {
22172275
if transaction_output_index as usize >= transaction.output.len() ||
22182276
transaction.output[transaction_output_index as usize].value != htlc.amount_msat / 1000 {
2219-
return claimable_outpoints; // Corrupted per_commitment_data, fuck this user
2277+
// per_commitment_data is corrupt or our commitment signing key leaked!
2278+
return (claimable_outpoints, to_counterparty_output_info);
22202279
}
22212280
}
22222281
let preimage = if htlc.offered { if let Some(p) = self.payment_preimages.get(&htlc.payment_hash) { Some(*p) } else { None } } else { None };
@@ -2243,7 +2302,7 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
22432302
}
22442303
}
22452304
}
2246-
claimable_outpoints
2305+
(claimable_outpoints, to_counterparty_output_info)
22472306
}
22482307

22492308
/// Attempts to claim a counterparty HTLC-Success/HTLC-Timeout's outputs using the revocation key
@@ -2498,14 +2557,19 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
24982557
log_info!(logger, "Channel {} closed by funding output spend in txid {}.",
24992558
log_bytes!(self.funding_info.0.to_channel_id()), tx.txid());
25002559
self.funding_spend_seen = true;
2560+
let mut commitment_tx_to_counterparty_output = None;
25012561
if (tx.input[0].sequence >> 8*3) as u8 == 0x80 && (tx.lock_time >> 8*3) as u8 == 0x20 {
2502-
let (mut new_outpoints, new_outputs) = self.check_spend_counterparty_transaction(&tx, height, &logger);
2562+
let (mut new_outpoints, new_outputs, counterparty_output_idx_sats) =
2563+
self.check_spend_counterparty_transaction(&tx, height, &logger);
2564+
commitment_tx_to_counterparty_output = counterparty_output_idx_sats;
25032565
if !new_outputs.1.is_empty() {
25042566
watch_outputs.push(new_outputs);
25052567
}
25062568
claimable_outpoints.append(&mut new_outpoints);
25072569
if new_outpoints.is_empty() {
25082570
if let Some((mut new_outpoints, new_outputs)) = self.check_spend_holder_transaction(&tx, height, &logger) {
2571+
debug_assert!(commitment_tx_to_counterparty_output.is_none(),
2572+
"A commitment transaction matched as both a counterparty and local commitment tx?");
25092573
if !new_outputs.1.is_empty() {
25102574
watch_outputs.push(new_outputs);
25112575
}
@@ -2521,6 +2585,7 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
25212585
height: height,
25222586
event: OnchainEvent::FundingSpendConfirmation {
25232587
on_local_output_csv: balance_spendable_csv,
2588+
commitment_tx_to_counterparty_output,
25242589
},
25252590
});
25262591
} else {
@@ -2657,8 +2722,9 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
26572722
OnchainEvent::HTLCSpendConfirmation { commitment_tx_output_idx, preimage, .. } => {
26582723
self.htlcs_resolved_on_chain.push(IrrevocablyResolvedHTLC { commitment_tx_output_idx, payment_preimage: preimage });
26592724
},
2660-
OnchainEvent::FundingSpendConfirmation { .. } => {
2725+
OnchainEvent::FundingSpendConfirmation { commitment_tx_to_counterparty_output, .. } => {
26612726
self.funding_spend_confirmed = Some(entry.txid);
2727+
self.confirmed_commitment_tx_counterparty_output = commitment_tx_to_counterparty_output;
26622728
},
26632729
}
26642730
}
@@ -3371,12 +3437,14 @@ impl<'a, Signer: Sign, K: KeysInterface<Signer = Signer>> ReadableArgs<&'a K>
33713437
let mut htlcs_resolved_on_chain = Some(Vec::new());
33723438
let mut funding_spend_seen = Some(false);
33733439
let mut counterparty_node_id = None;
3440+
let mut confirmed_commitment_tx_counterparty_output = None;
33743441
read_tlv_fields!(reader, {
33753442
(1, funding_spend_confirmed, option),
33763443
(3, htlcs_resolved_on_chain, vec_type),
33773444
(5, pending_monitor_events, vec_type),
33783445
(7, funding_spend_seen, option),
33793446
(9, counterparty_node_id, option),
3447+
(11, confirmed_commitment_tx_counterparty_output, option),
33803448
});
33813449

33823450
let mut secp_ctx = Secp256k1::new();
@@ -3427,6 +3495,7 @@ impl<'a, Signer: Sign, K: KeysInterface<Signer = Signer>> ReadableArgs<&'a K>
34273495
holder_tx_signed,
34283496
funding_spend_seen: funding_spend_seen.unwrap(),
34293497
funding_spend_confirmed,
3498+
confirmed_commitment_tx_counterparty_output,
34303499
htlcs_resolved_on_chain: htlcs_resolved_on_chain.unwrap(),
34313500

34323501
best_block,

0 commit comments

Comments
 (0)