Skip to content

Commit dddecba

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 9a59c35 commit dddecba

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,
@@ -1910,7 +1924,7 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
19101924
// First check if a counterparty commitment transaction has been broadcasted:
19111925
macro_rules! claim_htlcs {
19121926
($commitment_number: expr, $txid: expr) => {
1913-
let htlc_claim_reqs = self.get_counterparty_htlc_output_claim_reqs($commitment_number, $txid, None);
1927+
let (htlc_claim_reqs, _) = self.get_counterparty_output_claim_info($commitment_number, $txid, None);
19141928
self.onchain_tx_handler.update_claims_view(&Vec::new(), htlc_claim_reqs, self.best_block.height(), self.best_block.height(), broadcaster, fee_estimator, logger);
19151929
}
19161930
}
@@ -2089,13 +2103,18 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
20892103
/// data in counterparty_claimable_outpoints. Will directly claim any HTLC outputs which expire at a
20902104
/// height > height + CLTV_SHARED_CLAIM_BUFFER. In any case, will install monitoring for
20912105
/// HTLC-Success/HTLC-Timeout transactions.
2092-
/// Return updates for HTLC pending in the channel and failed automatically by the broadcast of
2093-
/// revoked counterparty commitment tx
2094-
fn check_spend_counterparty_transaction<L: Deref>(&mut self, tx: &Transaction, height: u32, logger: &L) -> (Vec<PackageTemplate>, TransactionOutputs) where L::Target: Logger {
2106+
///
2107+
/// Returns packages to claim the revoked output(s), as well as additional outputs to watch and
2108+
/// general information about the output that is to the counterparty in the commitment
2109+
/// transaction.
2110+
fn check_spend_counterparty_transaction<L: Deref>(&mut self, tx: &Transaction, height: u32, logger: &L)
2111+
-> (Vec<PackageTemplate>, TransactionOutputs, CommitmentTxCounterpartyOutputInfo)
2112+
where L::Target: Logger {
20952113
// Most secp and related errors trying to create keys means we have no hope of constructing
20962114
// a spend transaction...so we return no transactions to broadcast
20972115
let mut claimable_outpoints = Vec::new();
20982116
let mut watch_outputs = Vec::new();
2117+
let mut to_counterparty_output_info = None;
20992118

21002119
let commitment_txid = tx.txid(); //TODO: This is gonna be a performance bottleneck for watchtowers!
21012120
let per_commitment_option = self.counterparty_claimable_outpoints.get(&commitment_txid);
@@ -2104,7 +2123,7 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
21042123
( $thing : expr ) => {
21052124
match $thing {
21062125
Ok(a) => a,
2107-
Err(_) => return (claimable_outpoints, (commitment_txid, watch_outputs))
2126+
Err(_) => return (claimable_outpoints, (commitment_txid, watch_outputs), to_counterparty_output_info)
21082127
}
21092128
};
21102129
}
@@ -2126,6 +2145,8 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
21262145
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);
21272146
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);
21282147
claimable_outpoints.push(justice_package);
2148+
to_counterparty_output_info =
2149+
Some((idx.try_into().expect("Txn can't have more than 2^32 outputs"), outp.value));
21292150
}
21302151
}
21312152

@@ -2135,7 +2156,9 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
21352156
if let Some(transaction_output_index) = htlc.transaction_output_index {
21362157
if transaction_output_index as usize >= tx.output.len() ||
21372158
tx.output[transaction_output_index as usize].value != htlc.amount_msat / 1000 {
2138-
return (claimable_outpoints, (commitment_txid, watch_outputs)); // Corrupted per_commitment_data, fuck this user
2159+
// per_commitment_data is corrupt or our commitment signing key leaked!
2160+
return (claimable_outpoints, (commitment_txid, watch_outputs),
2161+
to_counterparty_output_info);
21392162
}
21402163
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());
21412164
let justice_package = PackageTemplate::build_package(commitment_txid, transaction_output_index, PackageSolvingData::RevokedHTLCOutput(revk_htlc_outp), htlc.cltv_expiry, true, height);
@@ -2183,17 +2206,22 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
21832206
(htlc, htlc_source.as_ref().map(|htlc_source| htlc_source.as_ref()))
21842207
), logger);
21852208

2186-
let htlc_claim_reqs = self.get_counterparty_htlc_output_claim_reqs(commitment_number, commitment_txid, Some(tx));
2209+
let (htlc_claim_reqs, counterparty_output_info) =
2210+
self.get_counterparty_output_claim_info(commitment_number, commitment_txid, Some(tx));
2211+
to_counterparty_output_info = counterparty_output_info;
21872212
for req in htlc_claim_reqs {
21882213
claimable_outpoints.push(req);
21892214
}
21902215

21912216
}
2192-
(claimable_outpoints, (commitment_txid, watch_outputs))
2217+
(claimable_outpoints, (commitment_txid, watch_outputs), to_counterparty_output_info)
21932218
}
21942219

2195-
fn get_counterparty_htlc_output_claim_reqs(&self, commitment_number: u64, commitment_txid: Txid, tx: Option<&Transaction>) -> Vec<PackageTemplate> {
2220+
/// Returns the HTLC claim package templates and the counterparty output info
2221+
fn get_counterparty_output_claim_info(&self, commitment_number: u64, commitment_txid: Txid, tx: Option<&Transaction>)
2222+
-> (Vec<PackageTemplate>, CommitmentTxCounterpartyOutputInfo) {
21962223
let mut claimable_outpoints = Vec::new();
2224+
let mut to_counterparty_output_info: CommitmentTxCounterpartyOutputInfo = None;
21972225
if let Some(htlc_outputs) = self.counterparty_claimable_outpoints.get(&commitment_txid) {
21982226
if let Some(per_commitment_points) = self.their_cur_per_commitment_points {
21992227
let per_commitment_point_option =
@@ -2207,12 +2235,43 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
22072235
if per_commitment_points.0 == commitment_number + 1 { Some(point) } else { None }
22082236
} else { None };
22092237
if let Some(per_commitment_point) = per_commitment_point_option {
2238+
if let Some(transaction) = tx {
2239+
let revokeable_p2wsh_opt =
2240+
if let Ok(revocation_pubkey) = chan_utils::derive_public_revocation_key(
2241+
&self.secp_ctx, &per_commitment_point, &self.holder_revocation_basepoint)
2242+
{
2243+
if let Ok(delayed_key) = chan_utils::derive_public_key(&self.secp_ctx,
2244+
&per_commitment_point,
2245+
&self.counterparty_commitment_params.counterparty_delayed_payment_base_key)
2246+
{
2247+
Some(chan_utils::get_revokeable_redeemscript(&revocation_pubkey,
2248+
self.counterparty_commitment_params.on_counterparty_tx_csv,
2249+
&delayed_key).to_v0_p2wsh())
2250+
} else {
2251+
debug_assert!(false, "Failed to derive a delayed payment key for a commitment state we accepted");
2252+
None
2253+
}
2254+
} else {
2255+
debug_assert!(false, "Failed to derive a revocation pubkey key for a commitment state we accepted");
2256+
None
2257+
};
2258+
if let Some(revokeable_p2wsh) = revokeable_p2wsh_opt {
2259+
for (idx, outp) in transaction.output.iter().enumerate() {
2260+
if outp.script_pubkey == revokeable_p2wsh {
2261+
to_counterparty_output_info =
2262+
Some((idx.try_into().expect("Can't have > 2^32 outputs"), outp.value));
2263+
}
2264+
}
2265+
}
2266+
}
2267+
22102268
for (_, &(ref htlc, _)) in htlc_outputs.iter().enumerate() {
22112269
if let Some(transaction_output_index) = htlc.transaction_output_index {
22122270
if let Some(transaction) = tx {
22132271
if transaction_output_index as usize >= transaction.output.len() ||
22142272
transaction.output[transaction_output_index as usize].value != htlc.amount_msat / 1000 {
2215-
return claimable_outpoints; // Corrupted per_commitment_data, fuck this user
2273+
// per_commitment_data is corrupt or our commitment signing key leaked!
2274+
return (claimable_outpoints, to_counterparty_output_info);
22162275
}
22172276
}
22182277
let preimage = if htlc.offered { if let Some(p) = self.payment_preimages.get(&htlc.payment_hash) { Some(*p) } else { None } } else { None };
@@ -2239,7 +2298,7 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
22392298
}
22402299
}
22412300
}
2242-
claimable_outpoints
2301+
(claimable_outpoints, to_counterparty_output_info)
22432302
}
22442303

22452304
/// Attempts to claim a counterparty HTLC-Success/HTLC-Timeout's outputs using the revocation key
@@ -2494,14 +2553,19 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
24942553
log_info!(logger, "Channel {} closed by funding output spend in txid {}.",
24952554
log_bytes!(self.funding_info.0.to_channel_id()), tx.txid());
24962555
self.funding_spend_seen = true;
2556+
let mut commitment_tx_to_counterparty_output = None;
24972557
if (tx.input[0].sequence >> 8*3) as u8 == 0x80 && (tx.lock_time >> 8*3) as u8 == 0x20 {
2498-
let (mut new_outpoints, new_outputs) = self.check_spend_counterparty_transaction(&tx, height, &logger);
2558+
let (mut new_outpoints, new_outputs, counterparty_output_idx_sats) =
2559+
self.check_spend_counterparty_transaction(&tx, height, &logger);
2560+
commitment_tx_to_counterparty_output = counterparty_output_idx_sats;
24992561
if !new_outputs.1.is_empty() {
25002562
watch_outputs.push(new_outputs);
25012563
}
25022564
claimable_outpoints.append(&mut new_outpoints);
25032565
if new_outpoints.is_empty() {
25042566
if let Some((mut new_outpoints, new_outputs)) = self.check_spend_holder_transaction(&tx, height, &logger) {
2567+
debug_assert!(commitment_tx_to_counterparty_output.is_none(),
2568+
"A commitment transaction matched as both a counterparty and local commitment tx?");
25052569
if !new_outputs.1.is_empty() {
25062570
watch_outputs.push(new_outputs);
25072571
}
@@ -2517,6 +2581,7 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
25172581
height: height,
25182582
event: OnchainEvent::FundingSpendConfirmation {
25192583
on_local_output_csv: balance_spendable_csv,
2584+
commitment_tx_to_counterparty_output,
25202585
},
25212586
});
25222587
} else {
@@ -2653,8 +2718,9 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
26532718
OnchainEvent::HTLCSpendConfirmation { commitment_tx_output_idx, preimage, .. } => {
26542719
self.htlcs_resolved_on_chain.push(IrrevocablyResolvedHTLC { commitment_tx_output_idx, payment_preimage: preimage });
26552720
},
2656-
OnchainEvent::FundingSpendConfirmation { .. } => {
2721+
OnchainEvent::FundingSpendConfirmation { commitment_tx_to_counterparty_output, .. } => {
26572722
self.funding_spend_confirmed = Some(entry.txid);
2723+
self.confirmed_commitment_tx_counterparty_output = commitment_tx_to_counterparty_output;
26582724
},
26592725
}
26602726
}
@@ -3367,12 +3433,14 @@ impl<'a, Signer: Sign, K: KeysInterface<Signer = Signer>> ReadableArgs<&'a K>
33673433
let mut htlcs_resolved_on_chain = Some(Vec::new());
33683434
let mut funding_spend_seen = Some(false);
33693435
let mut counterparty_node_id = None;
3436+
let mut confirmed_commitment_tx_counterparty_output = None;
33703437
read_tlv_fields!(reader, {
33713438
(1, funding_spend_confirmed, option),
33723439
(3, htlcs_resolved_on_chain, vec_type),
33733440
(5, pending_monitor_events, vec_type),
33743441
(7, funding_spend_seen, option),
33753442
(9, counterparty_node_id, option),
3443+
(11, confirmed_commitment_tx_counterparty_output, option),
33763444
});
33773445

33783446
let mut secp_ctx = Secp256k1::new();
@@ -3423,6 +3491,7 @@ impl<'a, Signer: Sign, K: KeysInterface<Signer = Signer>> ReadableArgs<&'a K>
34233491
holder_tx_signed,
34243492
funding_spend_seen: funding_spend_seen.unwrap(),
34253493
funding_spend_confirmed,
3494+
confirmed_commitment_tx_counterparty_output,
34263495
htlcs_resolved_on_chain: htlcs_resolved_on_chain.unwrap(),
34273496

34283497
best_block,

0 commit comments

Comments
 (0)