Skip to content

Commit d6ddf2f

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 85d3e5f commit d6ddf2f

File tree

1 file changed

+75
-11
lines changed

1 file changed

+75
-11
lines changed

lightning/src/chain/channelmonitor.rs

Lines changed: 75 additions & 11 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,8 @@ impl OnchainEventEntry {
345346
}
346347
}
347348

349+
type CommitmentTxCounterpartyOutputInfo = Option<(u32, u64)>;
350+
348351
/// Upon discovering of some classes of onchain tx by ChannelMonitor, we may have to take actions on it
349352
/// once they mature to enough confirmations (ANTI_REORG_DELAY)
350353
#[derive(PartialEq)]
@@ -372,6 +375,9 @@ enum OnchainEvent {
372375
/// The CSV delay for the output of the funding spend transaction (implying it is a local
373376
/// commitment transaction, and this is the delay on the to_self output).
374377
on_local_output_csv: Option<u16>,
378+
/// If the funding spend transaction was a known remote commitment transaction, we track
379+
/// the output index and amount of the counterparty's `to_self` output here.
380+
commitment_tx_to_counterparty_output: CommitmentTxCounterpartyOutputInfo,
375381
},
376382
/// A spend of a commitment transaction HTLC output, set in the cases where *no* `HTLCUpdate`
377383
/// is constructed. This is used when
@@ -436,6 +442,7 @@ impl_writeable_tlv_based_enum_upgradable!(OnchainEvent,
436442
},
437443
(3, FundingSpendConfirmation) => {
438444
(0, on_local_output_csv, option),
445+
(1, commitment_tx_to_counterparty_output, option),
439446
},
440447
(5, HTLCSpendConfirmation) => {
441448
(0, commitment_tx_output_idx, required),
@@ -714,6 +721,7 @@ pub(crate) struct ChannelMonitorImpl<Signer: Sign> {
714721
funding_spend_seen: bool,
715722

716723
funding_spend_confirmed: Option<Txid>,
724+
confirmed_commitment_tx_counterparty_output: CommitmentTxCounterpartyOutputInfo,
717725
/// The set of HTLCs which have been either claimed or failed on chain and have reached
718726
/// the requisite confirmations on the claim/fail transaction (either ANTI_REORG_DELAY or the
719727
/// spending CSV for revocable outputs).
@@ -783,6 +791,7 @@ impl<Signer: Sign> PartialEq for ChannelMonitorImpl<Signer> {
783791
self.holder_tx_signed != other.holder_tx_signed ||
784792
self.funding_spend_seen != other.funding_spend_seen ||
785793
self.funding_spend_confirmed != other.funding_spend_confirmed ||
794+
self.confirmed_commitment_tx_counterparty_output != other.confirmed_commitment_tx_counterparty_output ||
786795
self.htlcs_resolved_on_chain != other.htlcs_resolved_on_chain
787796
{
788797
false
@@ -962,6 +971,7 @@ impl<Signer: Sign> Writeable for ChannelMonitorImpl<Signer> {
962971
(5, self.pending_monitor_events, vec_type),
963972
(7, self.funding_spend_seen, required),
964973
(9, self.counterparty_node_id, option),
974+
(11, self.confirmed_commitment_tx_counterparty_output, option),
965975
});
966976

967977
Ok(())
@@ -1062,6 +1072,7 @@ impl<Signer: Sign> ChannelMonitor<Signer> {
10621072
holder_tx_signed: false,
10631073
funding_spend_seen: false,
10641074
funding_spend_confirmed: None,
1075+
confirmed_commitment_tx_counterparty_output: None,
10651076
htlcs_resolved_on_chain: Vec::new(),
10661077

10671078
best_block,
@@ -1900,7 +1911,7 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
19001911
// First check if a counterparty commitment transaction has been broadcasted:
19011912
macro_rules! claim_htlcs {
19021913
($commitment_number: expr, $txid: expr) => {
1903-
let htlc_claim_reqs = self.get_counterparty_htlc_output_claim_reqs($commitment_number, $txid, None);
1914+
let (htlc_claim_reqs, _) = self.get_counterparty_output_claim_info($commitment_number, $txid, None);
19041915
self.onchain_tx_handler.update_claims_view(&Vec::new(), htlc_claim_reqs, self.best_block.height(), self.best_block.height(), broadcaster, fee_estimator, logger);
19051916
}
19061917
}
@@ -2080,11 +2091,14 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
20802091
/// HTLC-Success/HTLC-Timeout transactions.
20812092
/// Return updates for HTLC pending in the channel and failed automatically by the broadcast of
20822093
/// revoked counterparty commitment tx
2083-
fn check_spend_counterparty_transaction<L: Deref>(&mut self, tx: &Transaction, height: u32, logger: &L) -> (Vec<PackageTemplate>, TransactionOutputs) where L::Target: Logger {
2094+
fn check_spend_counterparty_transaction<L: Deref>(&mut self, tx: &Transaction, height: u32, logger: &L)
2095+
-> (Vec<PackageTemplate>, TransactionOutputs, CommitmentTxCounterpartyOutputInfo)
2096+
where L::Target: Logger {
20842097
// Most secp and related errors trying to create keys means we have no hope of constructing
20852098
// a spend transaction...so we return no transactions to broadcast
20862099
let mut claimable_outpoints = Vec::new();
20872100
let mut watch_outputs = Vec::new();
2101+
let mut to_counterparty_output_info = None;
20882102

20892103
let commitment_txid = tx.txid(); //TODO: This is gonna be a performance bottleneck for watchtowers!
20902104
let per_commitment_option = self.counterparty_claimable_outpoints.get(&commitment_txid);
@@ -2093,7 +2107,7 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
20932107
( $thing : expr ) => {
20942108
match $thing {
20952109
Ok(a) => a,
2096-
Err(_) => return (claimable_outpoints, (commitment_txid, watch_outputs))
2110+
Err(_) => return (claimable_outpoints, (commitment_txid, watch_outputs), to_counterparty_output_info)
20972111
}
20982112
};
20992113
}
@@ -2115,6 +2129,8 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
21152129
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);
21162130
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);
21172131
claimable_outpoints.push(justice_package);
2132+
to_counterparty_output_info =
2133+
Some((idx.try_into().expect("Txn can't have more than 2^32 outputs"), outp.value));
21182134
}
21192135
}
21202136

@@ -2124,7 +2140,9 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
21242140
if let Some(transaction_output_index) = htlc.transaction_output_index {
21252141
if transaction_output_index as usize >= tx.output.len() ||
21262142
tx.output[transaction_output_index as usize].value != htlc.amount_msat / 1000 {
2127-
return (claimable_outpoints, (commitment_txid, watch_outputs)); // Corrupted per_commitment_data, fuck this user
2143+
// per_commitment_data is corrupt or our commitment signing key leaked!
2144+
return (claimable_outpoints, (commitment_txid, watch_outputs),
2145+
to_counterparty_output_info);
21282146
}
21292147
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());
21302148
let justice_package = PackageTemplate::build_package(commitment_txid, transaction_output_index, PackageSolvingData::RevokedHTLCOutput(revk_htlc_outp), htlc.cltv_expiry, true, height);
@@ -2172,17 +2190,22 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
21722190
(htlc, htlc_source.as_ref().map(|htlc_source| htlc_source.as_ref()))
21732191
), logger);
21742192

2175-
let htlc_claim_reqs = self.get_counterparty_htlc_output_claim_reqs(commitment_number, commitment_txid, Some(tx));
2193+
let (htlc_claim_reqs, counterparty_output_info) =
2194+
self.get_counterparty_output_claim_info(commitment_number, commitment_txid, Some(tx));
2195+
to_counterparty_output_info = counterparty_output_info;
21762196
for req in htlc_claim_reqs {
21772197
claimable_outpoints.push(req);
21782198
}
21792199

21802200
}
2181-
(claimable_outpoints, (commitment_txid, watch_outputs))
2201+
(claimable_outpoints, (commitment_txid, watch_outputs), to_counterparty_output_info)
21822202
}
21832203

2184-
fn get_counterparty_htlc_output_claim_reqs(&self, commitment_number: u64, commitment_txid: Txid, tx: Option<&Transaction>) -> Vec<PackageTemplate> {
2204+
/// Returns the HTLC claim package templates and the counterparty output info
2205+
fn get_counterparty_output_claim_info(&self, commitment_number: u64, commitment_txid: Txid, tx: Option<&Transaction>)
2206+
-> (Vec<PackageTemplate>, CommitmentTxCounterpartyOutputInfo) {
21852207
let mut claimable_outpoints = Vec::new();
2208+
let mut to_counterparty_output_info: CommitmentTxCounterpartyOutputInfo = None;
21862209
if let Some(htlc_outputs) = self.counterparty_claimable_outpoints.get(&commitment_txid) {
21872210
if let Some(per_commitment_points) = self.their_cur_per_commitment_points {
21882211
let per_commitment_point_option =
@@ -2196,12 +2219,43 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
21962219
if per_commitment_points.0 == commitment_number + 1 { Some(point) } else { None }
21972220
} else { None };
21982221
if let Some(per_commitment_point) = per_commitment_point_option {
2222+
if let Some(transaction) = tx {
2223+
let revokeable_p2wsh_opt =
2224+
if let Ok(revocation_pubkey) = chan_utils::derive_public_revocation_key(
2225+
&self.secp_ctx, &per_commitment_point, &self.holder_revocation_basepoint)
2226+
{
2227+
if let Ok(delayed_key) = chan_utils::derive_public_key(&self.secp_ctx,
2228+
&per_commitment_point,
2229+
&self.counterparty_commitment_params.counterparty_delayed_payment_base_key)
2230+
{
2231+
Some(chan_utils::get_revokeable_redeemscript(&revocation_pubkey,
2232+
self.counterparty_commitment_params.on_counterparty_tx_csv,
2233+
&delayed_key).to_v0_p2wsh())
2234+
} else {
2235+
debug_assert!(false, "Failed to derive a delayed payment key for a commitment state we accepted");
2236+
None
2237+
}
2238+
} else {
2239+
debug_assert!(false, "Failed to derive a delayed payment key for a commitment state we accepted");
2240+
None
2241+
};
2242+
if let Some(revokeable_p2wsh) = revokeable_p2wsh_opt {
2243+
for (idx, outp) in transaction.output.iter().enumerate() {
2244+
if outp.script_pubkey == revokeable_p2wsh {
2245+
to_counterparty_output_info =
2246+
Some((idx.try_into().expect("Can't have > 2^32 outputs"), outp.value));
2247+
}
2248+
}
2249+
}
2250+
}
2251+
21992252
for (_, &(ref htlc, _)) in htlc_outputs.iter().enumerate() {
22002253
if let Some(transaction_output_index) = htlc.transaction_output_index {
22012254
if let Some(transaction) = tx {
22022255
if transaction_output_index as usize >= transaction.output.len() ||
22032256
transaction.output[transaction_output_index as usize].value != htlc.amount_msat / 1000 {
2204-
return claimable_outpoints; // Corrupted per_commitment_data, fuck this user
2257+
// per_commitment_data is corrupt or our commitment signing key leaked!
2258+
return (claimable_outpoints, to_counterparty_output_info);
22052259
}
22062260
}
22072261
let preimage = if htlc.offered { if let Some(p) = self.payment_preimages.get(&htlc.payment_hash) { Some(*p) } else { None } } else { None };
@@ -2228,7 +2282,7 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
22282282
}
22292283
}
22302284
}
2231-
claimable_outpoints
2285+
(claimable_outpoints, to_counterparty_output_info)
22322286
}
22332287

22342288
/// Attempts to claim a counterparty HTLC-Success/HTLC-Timeout's outputs using the revocation key
@@ -2482,14 +2536,19 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
24822536
log_info!(logger, "Channel {} closed by funding output spend in txid {}.",
24832537
log_bytes!(self.funding_info.0.to_channel_id()), tx.txid());
24842538
self.funding_spend_seen = true;
2539+
let mut commitment_tx_to_counterparty_output = None;
24852540
if (tx.input[0].sequence >> 8*3) as u8 == 0x80 && (tx.lock_time >> 8*3) as u8 == 0x20 {
2486-
let (mut new_outpoints, new_outputs) = self.check_spend_counterparty_transaction(&tx, height, &logger);
2541+
let (mut new_outpoints, new_outputs, counterparty_output_idx_sats) =
2542+
self.check_spend_counterparty_transaction(&tx, height, &logger);
2543+
commitment_tx_to_counterparty_output = counterparty_output_idx_sats;
24872544
if !new_outputs.1.is_empty() {
24882545
watch_outputs.push(new_outputs);
24892546
}
24902547
claimable_outpoints.append(&mut new_outpoints);
24912548
if new_outpoints.is_empty() {
24922549
if let Some((mut new_outpoints, new_outputs)) = self.check_spend_holder_transaction(&tx, height, &logger) {
2550+
debug_assert!(commitment_tx_to_counterparty_output.is_none(),
2551+
"A commitment transaction matched as both a counterparty and local commitment tx?");
24932552
if !new_outputs.1.is_empty() {
24942553
watch_outputs.push(new_outputs);
24952554
}
@@ -2505,6 +2564,7 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
25052564
height: height,
25062565
event: OnchainEvent::FundingSpendConfirmation {
25072566
on_local_output_csv: balance_spendable_csv,
2567+
commitment_tx_to_counterparty_output,
25082568
},
25092569
});
25102570
} else {
@@ -2641,8 +2701,9 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
26412701
OnchainEvent::HTLCSpendConfirmation { commitment_tx_output_idx, preimage, .. } => {
26422702
self.htlcs_resolved_on_chain.push(IrrevocablyResolvedHTLC { commitment_tx_output_idx, payment_preimage: preimage });
26432703
},
2644-
OnchainEvent::FundingSpendConfirmation { .. } => {
2704+
OnchainEvent::FundingSpendConfirmation { commitment_tx_to_counterparty_output, .. } => {
26452705
self.funding_spend_confirmed = Some(entry.txid);
2706+
self.confirmed_commitment_tx_counterparty_output = commitment_tx_to_counterparty_output;
26462707
},
26472708
}
26482709
}
@@ -3354,12 +3415,14 @@ impl<'a, Signer: Sign, K: KeysInterface<Signer = Signer>> ReadableArgs<&'a K>
33543415
let mut htlcs_resolved_on_chain = Some(Vec::new());
33553416
let mut funding_spend_seen = Some(false);
33563417
let mut counterparty_node_id = None;
3418+
let mut confirmed_commitment_tx_counterparty_output = None;
33573419
read_tlv_fields!(reader, {
33583420
(1, funding_spend_confirmed, option),
33593421
(3, htlcs_resolved_on_chain, vec_type),
33603422
(5, pending_monitor_events, vec_type),
33613423
(7, funding_spend_seen, option),
33623424
(9, counterparty_node_id, option),
3425+
(11, confirmed_commitment_tx_counterparty_output, option),
33633426
});
33643427

33653428
let mut secp_ctx = Secp256k1::new();
@@ -3411,6 +3474,7 @@ impl<'a, Signer: Sign, K: KeysInterface<Signer = Signer>> ReadableArgs<&'a K>
34113474
holder_tx_signed,
34123475
funding_spend_seen: funding_spend_seen.unwrap(),
34133476
funding_spend_confirmed,
3477+
confirmed_commitment_tx_counterparty_output,
34143478
htlcs_resolved_on_chain: htlcs_resolved_on_chain.unwrap(),
34153479

34163480
best_block,

0 commit comments

Comments
 (0)