Skip to content

Commit 0ec54ec

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 9115f66 commit 0ec54ec

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,
@@ -1918,7 +1932,7 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
19181932
// First check if a counterparty commitment transaction has been broadcasted:
19191933
macro_rules! claim_htlcs {
19201934
($commitment_number: expr, $txid: expr) => {
1921-
let htlc_claim_reqs = self.get_counterparty_htlc_output_claim_reqs($commitment_number, $txid, None);
1935+
let (htlc_claim_reqs, _) = self.get_counterparty_output_claim_info($commitment_number, $txid, None);
19221936
self.onchain_tx_handler.update_claims_view(&Vec::new(), htlc_claim_reqs, self.best_block.height(), self.best_block.height(), broadcaster, fee_estimator, logger);
19231937
}
19241938
}
@@ -2097,13 +2111,18 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
20972111
/// data in counterparty_claimable_outpoints. Will directly claim any HTLC outputs which expire at a
20982112
/// height > height + CLTV_SHARED_CLAIM_BUFFER. In any case, will install monitoring for
20992113
/// HTLC-Success/HTLC-Timeout transactions.
2100-
/// Return updates for HTLC pending in the channel and failed automatically by the broadcast of
2101-
/// revoked counterparty commitment tx
2102-
fn check_spend_counterparty_transaction<L: Deref>(&mut self, tx: &Transaction, height: u32, logger: &L) -> (Vec<PackageTemplate>, TransactionOutputs) where L::Target: Logger {
2114+
///
2115+
/// Returns packages to claim the revoked output(s), as well as additional outputs to watch and
2116+
/// general information about the output that is to the counterparty in the commitment
2117+
/// transaction.
2118+
fn check_spend_counterparty_transaction<L: Deref>(&mut self, tx: &Transaction, height: u32, logger: &L)
2119+
-> (Vec<PackageTemplate>, TransactionOutputs, CommitmentTxCounterpartyOutputInfo)
2120+
where L::Target: Logger {
21032121
// Most secp and related errors trying to create keys means we have no hope of constructing
21042122
// a spend transaction...so we return no transactions to broadcast
21052123
let mut claimable_outpoints = Vec::new();
21062124
let mut watch_outputs = Vec::new();
2125+
let mut to_counterparty_output_info = None;
21072126

21082127
let commitment_txid = tx.txid(); //TODO: This is gonna be a performance bottleneck for watchtowers!
21092128
let per_commitment_option = self.counterparty_claimable_outpoints.get(&commitment_txid);
@@ -2112,7 +2131,7 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
21122131
( $thing : expr ) => {
21132132
match $thing {
21142133
Ok(a) => a,
2115-
Err(_) => return (claimable_outpoints, (commitment_txid, watch_outputs))
2134+
Err(_) => return (claimable_outpoints, (commitment_txid, watch_outputs), to_counterparty_output_info)
21162135
}
21172136
};
21182137
}
@@ -2134,6 +2153,8 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
21342153
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);
21352154
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);
21362155
claimable_outpoints.push(justice_package);
2156+
to_counterparty_output_info =
2157+
Some((idx.try_into().expect("Txn can't have more than 2^32 outputs"), outp.value));
21372158
}
21382159
}
21392160

@@ -2143,7 +2164,9 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
21432164
if let Some(transaction_output_index) = htlc.transaction_output_index {
21442165
if transaction_output_index as usize >= tx.output.len() ||
21452166
tx.output[transaction_output_index as usize].value != htlc.amount_msat / 1000 {
2146-
return (claimable_outpoints, (commitment_txid, watch_outputs)); // Corrupted per_commitment_data, fuck this user
2167+
// per_commitment_data is corrupt or our commitment signing key leaked!
2168+
return (claimable_outpoints, (commitment_txid, watch_outputs),
2169+
to_counterparty_output_info);
21472170
}
21482171
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());
21492172
let justice_package = PackageTemplate::build_package(commitment_txid, transaction_output_index, PackageSolvingData::RevokedHTLCOutput(revk_htlc_outp), htlc.cltv_expiry, true, height);
@@ -2191,17 +2214,22 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
21912214
(htlc, htlc_source.as_ref().map(|htlc_source| htlc_source.as_ref()))
21922215
), logger);
21932216

2194-
let htlc_claim_reqs = self.get_counterparty_htlc_output_claim_reqs(commitment_number, commitment_txid, Some(tx));
2217+
let (htlc_claim_reqs, counterparty_output_info) =
2218+
self.get_counterparty_output_claim_info(commitment_number, commitment_txid, Some(tx));
2219+
to_counterparty_output_info = counterparty_output_info;
21952220
for req in htlc_claim_reqs {
21962221
claimable_outpoints.push(req);
21972222
}
21982223

21992224
}
2200-
(claimable_outpoints, (commitment_txid, watch_outputs))
2225+
(claimable_outpoints, (commitment_txid, watch_outputs), to_counterparty_output_info)
22012226
}
22022227

2203-
fn get_counterparty_htlc_output_claim_reqs(&self, commitment_number: u64, commitment_txid: Txid, tx: Option<&Transaction>) -> Vec<PackageTemplate> {
2228+
/// Returns the HTLC claim package templates and the counterparty output info
2229+
fn get_counterparty_output_claim_info(&self, commitment_number: u64, commitment_txid: Txid, tx: Option<&Transaction>)
2230+
-> (Vec<PackageTemplate>, CommitmentTxCounterpartyOutputInfo) {
22042231
let mut claimable_outpoints = Vec::new();
2232+
let mut to_counterparty_output_info: CommitmentTxCounterpartyOutputInfo = None;
22052233
if let Some(htlc_outputs) = self.counterparty_claimable_outpoints.get(&commitment_txid) {
22062234
if let Some(per_commitment_points) = self.their_cur_per_commitment_points {
22072235
let per_commitment_point_option =
@@ -2215,12 +2243,43 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
22152243
if per_commitment_points.0 == commitment_number + 1 { Some(point) } else { None }
22162244
} else { None };
22172245
if let Some(per_commitment_point) = per_commitment_point_option {
2246+
if let Some(transaction) = tx {
2247+
let revokeable_p2wsh_opt =
2248+
if let Ok(revocation_pubkey) = chan_utils::derive_public_revocation_key(
2249+
&self.secp_ctx, &per_commitment_point, &self.holder_revocation_basepoint)
2250+
{
2251+
if let Ok(delayed_key) = chan_utils::derive_public_key(&self.secp_ctx,
2252+
&per_commitment_point,
2253+
&self.counterparty_commitment_params.counterparty_delayed_payment_base_key)
2254+
{
2255+
Some(chan_utils::get_revokeable_redeemscript(&revocation_pubkey,
2256+
self.counterparty_commitment_params.on_counterparty_tx_csv,
2257+
&delayed_key).to_v0_p2wsh())
2258+
} else {
2259+
debug_assert!(false, "Failed to derive a delayed payment key for a commitment state we accepted");
2260+
None
2261+
}
2262+
} else {
2263+
debug_assert!(false, "Failed to derive a revocation pubkey key for a commitment state we accepted");
2264+
None
2265+
};
2266+
if let Some(revokeable_p2wsh) = revokeable_p2wsh_opt {
2267+
for (idx, outp) in transaction.output.iter().enumerate() {
2268+
if outp.script_pubkey == revokeable_p2wsh {
2269+
to_counterparty_output_info =
2270+
Some((idx.try_into().expect("Can't have > 2^32 outputs"), outp.value));
2271+
}
2272+
}
2273+
}
2274+
}
2275+
22182276
for (_, &(ref htlc, _)) in htlc_outputs.iter().enumerate() {
22192277
if let Some(transaction_output_index) = htlc.transaction_output_index {
22202278
if let Some(transaction) = tx {
22212279
if transaction_output_index as usize >= transaction.output.len() ||
22222280
transaction.output[transaction_output_index as usize].value != htlc.amount_msat / 1000 {
2223-
return claimable_outpoints; // Corrupted per_commitment_data, fuck this user
2281+
// per_commitment_data is corrupt or our commitment signing key leaked!
2282+
return (claimable_outpoints, to_counterparty_output_info);
22242283
}
22252284
}
22262285
let preimage = if htlc.offered { if let Some(p) = self.payment_preimages.get(&htlc.payment_hash) { Some(*p) } else { None } } else { None };
@@ -2247,7 +2306,7 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
22472306
}
22482307
}
22492308
}
2250-
claimable_outpoints
2309+
(claimable_outpoints, to_counterparty_output_info)
22512310
}
22522311

22532312
/// Attempts to claim a counterparty HTLC-Success/HTLC-Timeout's outputs using the revocation key
@@ -2502,14 +2561,19 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
25022561
log_info!(logger, "Channel {} closed by funding output spend in txid {}.",
25032562
log_bytes!(self.funding_info.0.to_channel_id()), tx.txid());
25042563
self.funding_spend_seen = true;
2564+
let mut commitment_tx_to_counterparty_output = None;
25052565
if (tx.input[0].sequence.0 >> 8*3) as u8 == 0x80 && (tx.lock_time.0 >> 8*3) as u8 == 0x20 {
2506-
let (mut new_outpoints, new_outputs) = self.check_spend_counterparty_transaction(&tx, height, &logger);
2566+
let (mut new_outpoints, new_outputs, counterparty_output_idx_sats) =
2567+
self.check_spend_counterparty_transaction(&tx, height, &logger);
2568+
commitment_tx_to_counterparty_output = counterparty_output_idx_sats;
25072569
if !new_outputs.1.is_empty() {
25082570
watch_outputs.push(new_outputs);
25092571
}
25102572
claimable_outpoints.append(&mut new_outpoints);
25112573
if new_outpoints.is_empty() {
25122574
if let Some((mut new_outpoints, new_outputs)) = self.check_spend_holder_transaction(&tx, height, &logger) {
2575+
debug_assert!(commitment_tx_to_counterparty_output.is_none(),
2576+
"A commitment transaction matched as both a counterparty and local commitment tx?");
25132577
if !new_outputs.1.is_empty() {
25142578
watch_outputs.push(new_outputs);
25152579
}
@@ -2525,6 +2589,7 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
25252589
height: height,
25262590
event: OnchainEvent::FundingSpendConfirmation {
25272591
on_local_output_csv: balance_spendable_csv,
2592+
commitment_tx_to_counterparty_output,
25282593
},
25292594
});
25302595
} else {
@@ -2661,8 +2726,9 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
26612726
OnchainEvent::HTLCSpendConfirmation { commitment_tx_output_idx, preimage, .. } => {
26622727
self.htlcs_resolved_on_chain.push(IrrevocablyResolvedHTLC { commitment_tx_output_idx, payment_preimage: preimage });
26632728
},
2664-
OnchainEvent::FundingSpendConfirmation { .. } => {
2729+
OnchainEvent::FundingSpendConfirmation { commitment_tx_to_counterparty_output, .. } => {
26652730
self.funding_spend_confirmed = Some(entry.txid);
2731+
self.confirmed_commitment_tx_counterparty_output = commitment_tx_to_counterparty_output;
26662732
},
26672733
}
26682734
}
@@ -3375,12 +3441,14 @@ impl<'a, Signer: Sign, K: KeysInterface<Signer = Signer>> ReadableArgs<&'a K>
33753441
let mut htlcs_resolved_on_chain = Some(Vec::new());
33763442
let mut funding_spend_seen = Some(false);
33773443
let mut counterparty_node_id = None;
3444+
let mut confirmed_commitment_tx_counterparty_output = None;
33783445
read_tlv_fields!(reader, {
33793446
(1, funding_spend_confirmed, option),
33803447
(3, htlcs_resolved_on_chain, vec_type),
33813448
(5, pending_monitor_events, vec_type),
33823449
(7, funding_spend_seen, option),
33833450
(9, counterparty_node_id, option),
3451+
(11, confirmed_commitment_tx_counterparty_output, option),
33843452
});
33853453

33863454
let mut secp_ctx = Secp256k1::new();
@@ -3431,6 +3499,7 @@ impl<'a, Signer: Sign, K: KeysInterface<Signer = Signer>> ReadableArgs<&'a K>
34313499
holder_tx_signed,
34323500
funding_spend_seen: funding_spend_seen.unwrap(),
34333501
funding_spend_confirmed,
3502+
confirmed_commitment_tx_counterparty_output,
34343503
htlcs_resolved_on_chain: htlcs_resolved_on_chain.unwrap(),
34353504

34363505
best_block,

0 commit comments

Comments
 (0)