Skip to content

Commit ca819d9

Browse files
committed
Produce FundingInfo::Contribution variants in ChannelMonitor
Similar to the `ChannelManager`, we expose the contributed inputs and outputs of a splice via `FundingInfo::Contribution` at the `ChannelMonitor` level such that we don't lose the context when the channel closes while a splice is still pending.
1 parent 8283ad4 commit ca819d9

4 files changed

Lines changed: 126 additions & 29 deletions

File tree

lightning/src/chain/channelmonitor.rs

Lines changed: 98 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ use crate::chain::package::{
4444
use crate::chain::transaction::{OutPoint, TransactionData};
4545
use crate::chain::{BestBlock, WatchedOutput};
4646
use crate::events::bump_transaction::{AnchorDescriptor, BumpTransactionEvent};
47-
use crate::events::{ClosureReason, Event, EventHandler, ReplayEvent};
47+
use crate::events::{ClosureReason, Event, EventHandler, FundingInfo, ReplayEvent};
4848
use crate::ln::chan_utils::{
4949
self, ChannelTransactionParameters, CommitmentTransaction, CounterpartyCommitmentSecrets,
5050
HTLCClaim, HTLCOutputInCommitment, HolderCommitmentTransaction,
@@ -688,6 +688,8 @@ pub(crate) enum ChannelMonitorUpdateStep {
688688
channel_parameters: ChannelTransactionParameters,
689689
holder_commitment_tx: HolderCommitmentTransaction,
690690
counterparty_commitment_tx: CommitmentTransaction,
691+
contributed_inputs: Vec<BitcoinOutPoint>,
692+
contributed_outputs: Vec<ScriptBuf>,
691693
},
692694
RenegotiatedFundingLocked {
693695
funding_txid: Txid,
@@ -773,6 +775,8 @@ impl_writeable_tlv_based_enum_upgradable!(ChannelMonitorUpdateStep,
773775
(1, channel_parameters, (required: ReadableArgs, None)),
774776
(3, holder_commitment_tx, required),
775777
(5, counterparty_commitment_tx, required),
778+
(7, contributed_inputs, optional_vec),
779+
(9, contributed_outputs, optional_vec),
776780
},
777781
(12, RenegotiatedFundingLocked) => {
778782
(1, funding_txid, required),
@@ -1166,6 +1170,10 @@ struct FundingScope {
11661170
// transaction for which we have deleted claim information on some watchtowers.
11671171
current_holder_commitment_tx: HolderCommitmentTransaction,
11681172
prev_holder_commitment_tx: Option<HolderCommitmentTransaction>,
1173+
1174+
/// Inputs and outputs we contributed when negotiating the corresponding funding transaction.
1175+
contributed_inputs: Option<Vec<BitcoinOutPoint>>,
1176+
contributed_outputs: Option<Vec<ScriptBuf>>,
11691177
}
11701178

11711179
impl FundingScope {
@@ -1194,6 +1202,8 @@ impl_writeable_tlv_based!(FundingScope, {
11941202
(7, current_holder_commitment_tx, required),
11951203
(9, prev_holder_commitment_tx, option),
11961204
(11, counterparty_claimable_outpoints, required),
1205+
(13, contributed_inputs, option),
1206+
(15, contributed_outputs, option),
11971207
});
11981208

11991209
#[derive(Clone, PartialEq)]
@@ -1755,6 +1765,8 @@ pub(crate) fn write_chanmon_internal<Signer: EcdsaChannelSigner, W: Writer>(
17551765
(34, channel_monitor.alternative_funding_confirmed, option),
17561766
(35, channel_monitor.is_manual_broadcast, required),
17571767
(37, channel_monitor.funding_seen_onchain, required),
1768+
(39, channel_monitor.funding.contributed_inputs, option),
1769+
(41, channel_monitor.funding.contributed_outputs, option),
17581770
});
17591771

17601772
Ok(())
@@ -1904,6 +1916,9 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitor<Signer> {
19041916

19051917
current_holder_commitment_tx: initial_holder_commitment_tx,
19061918
prev_holder_commitment_tx: None,
1919+
1920+
contributed_inputs: None,
1921+
contributed_outputs: None,
19071922
},
19081923
pending_funding: vec![],
19091924

@@ -3958,6 +3973,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
39583973
&mut self, logger: &WithContext<L>, channel_parameters: &ChannelTransactionParameters,
39593974
alternative_holder_commitment_tx: &HolderCommitmentTransaction,
39603975
alternative_counterparty_commitment_tx: &CommitmentTransaction,
3976+
contributed_inputs: &[BitcoinOutPoint], contributed_outputs: &[ScriptBuf],
39613977
) -> Result<(), ()> {
39623978
let alternative_counterparty_commitment_txid =
39633979
alternative_counterparty_commitment_tx.trust().txid();
@@ -4024,6 +4040,8 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
40244040
counterparty_claimable_outpoints,
40254041
current_holder_commitment_tx: alternative_holder_commitment_tx.clone(),
40264042
prev_holder_commitment_tx: None,
4043+
contributed_inputs: Some(contributed_inputs.to_vec()),
4044+
contributed_outputs: Some(contributed_outputs.to_vec()),
40274045
};
40284046
let alternative_funding_outpoint = alternative_funding.funding_outpoint();
40294047

@@ -4080,6 +4098,58 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
40804098
Ok(())
40814099
}
40824100

4101+
fn queue_discard_funding_event(
4102+
&mut self, discarded_funding: impl Iterator<Item = FundingScope>,
4103+
) {
4104+
let (mut discarded_inputs, mut discarded_outputs) = (new_hash_set(), new_hash_set());
4105+
for funding in discarded_funding {
4106+
if funding.contributed_inputs.is_none() && funding.contributed_outputs.is_none() {
4107+
self.pending_events.push(Event::DiscardFunding {
4108+
channel_id: self.channel_id,
4109+
funding_info: FundingInfo::OutPoint { outpoint: funding.funding_outpoint() },
4110+
});
4111+
} else {
4112+
// Filter out any inputs/outputs that were already included in the locked funding
4113+
// transaction.
4114+
if let Some(mut maybe_discarded_inputs) = funding.contributed_inputs {
4115+
maybe_discarded_inputs.retain(|input| {
4116+
let is_input_in_locked_funding = self
4117+
.funding
4118+
.contributed_inputs
4119+
.as_ref()
4120+
.map(|inputs| inputs.contains(input))
4121+
// The recently locked funding did not contain any contributed inputs.
4122+
.unwrap_or(false);
4123+
!is_input_in_locked_funding
4124+
});
4125+
discarded_inputs.extend(maybe_discarded_inputs);
4126+
}
4127+
if let Some(mut maybe_discarded_outputs) = funding.contributed_outputs {
4128+
maybe_discarded_outputs.retain(|output| {
4129+
let is_output_in_locked_funding = self
4130+
.funding
4131+
.contributed_outputs
4132+
.as_ref()
4133+
.map(|outputs| outputs.contains(output))
4134+
// The recently locked funding did not contain any contributed outputs.
4135+
.unwrap_or(false);
4136+
!is_output_in_locked_funding
4137+
});
4138+
discarded_outputs.extend(maybe_discarded_outputs);
4139+
}
4140+
}
4141+
}
4142+
if !discarded_inputs.is_empty() || !discarded_outputs.is_empty() {
4143+
self.pending_events.push(Event::DiscardFunding {
4144+
channel_id: self.channel_id,
4145+
funding_info: FundingInfo::Contribution {
4146+
inputs: discarded_inputs.into_iter().collect(),
4147+
outputs: discarded_outputs.into_iter().collect(),
4148+
},
4149+
});
4150+
}
4151+
}
4152+
40834153
fn promote_funding(&mut self, new_funding_txid: Txid) -> Result<(), ()> {
40844154
let prev_funding_txid = self.funding.funding_txid();
40854155

@@ -4110,18 +4180,20 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
41104180
let no_further_updates_allowed = self.no_further_updates_allowed();
41114181

41124182
// The swap above places the previous `FundingScope` into `pending_funding`.
4113-
for funding in self.pending_funding.drain(..) {
4114-
let funding_txid = funding.funding_txid();
4115-
self.outputs_to_watch.remove(&funding_txid);
4116-
if no_further_updates_allowed && funding_txid != prev_funding_txid {
4117-
self.pending_events.push(Event::DiscardFunding {
4118-
channel_id: self.channel_id,
4119-
funding_info: crate::events::FundingInfo::OutPoint {
4120-
outpoint: funding.funding_outpoint(),
4121-
},
4122-
});
4123-
}
4183+
for funding in &self.pending_funding {
4184+
self.outputs_to_watch.remove(&funding.funding_txid());
41244185
}
4186+
let mut discarded_funding = Vec::new();
4187+
mem::swap(&mut self.pending_funding, &mut discarded_funding);
4188+
let discarded_funding = discarded_funding
4189+
.into_iter()
4190+
// The previous funding is filtered out since it was already locked, so nothing needs to
4191+
// be discarded.
4192+
.filter(|funding| {
4193+
no_further_updates_allowed && funding.funding_txid() != prev_funding_txid
4194+
});
4195+
self.queue_discard_funding_event(discarded_funding);
4196+
41254197
if let Some((alternative_funding_txid, _)) = self.alternative_funding_confirmed.take() {
41264198
// In exceedingly rare cases, it's possible there was a reorg that caused a potential funding to
41274199
// be locked in that this `ChannelMonitor` has not yet seen. Thus, we avoid a runtime assertion
@@ -4238,11 +4310,13 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
42384310
},
42394311
ChannelMonitorUpdateStep::RenegotiatedFunding {
42404312
channel_parameters, holder_commitment_tx, counterparty_commitment_tx,
4313+
contributed_inputs, contributed_outputs,
42414314
} => {
42424315
log_trace!(logger, "Updating ChannelMonitor with alternative holder and counterparty commitment transactions for funding txid {}",
42434316
channel_parameters.funding_outpoint.unwrap().txid);
42444317
if let Err(_) = self.renegotiated_funding(
42454318
logger, channel_parameters, holder_commitment_tx, counterparty_commitment_tx,
4319+
contributed_inputs, contributed_outputs,
42464320
) {
42474321
ret = Err(());
42484322
}
@@ -5809,15 +5883,14 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
58095883
self.funding_spend_confirmed = Some(entry.txid);
58105884
self.confirmed_commitment_tx_counterparty_output = commitment_tx_to_counterparty_output;
58115885
if self.alternative_funding_confirmed.is_none() {
5812-
for funding in self.pending_funding.drain(..) {
5886+
// We saw a confirmed commitment for our currently locked funding, so
5887+
// discard all pending ones.
5888+
for funding in &self.pending_funding {
58135889
self.outputs_to_watch.remove(&funding.funding_txid());
5814-
self.pending_events.push(Event::DiscardFunding {
5815-
channel_id: self.channel_id,
5816-
funding_info: crate::events::FundingInfo::OutPoint {
5817-
outpoint: funding.funding_outpoint(),
5818-
},
5819-
});
58205890
}
5891+
let mut discarded_funding = Vec::new();
5892+
mem::swap(&mut self.pending_funding, &mut discarded_funding);
5893+
self.queue_discard_funding_event(discarded_funding.into_iter());
58215894
}
58225895
},
58235896
OnchainEvent::AlternativeFundingConfirmation {} => {
@@ -6694,6 +6767,8 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP
66946767
let mut alternative_funding_confirmed = None;
66956768
let mut is_manual_broadcast = RequiredWrapper(None);
66966769
let mut funding_seen_onchain = RequiredWrapper(None);
6770+
let mut current_funding_contributed_inputs = None;
6771+
let mut current_funding_contributed_outputs = None;
66976772
read_tlv_fields!(reader, {
66986773
(1, funding_spend_confirmed, option),
66996774
(3, htlcs_resolved_on_chain, optional_vec),
@@ -6716,6 +6791,8 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP
67166791
(34, alternative_funding_confirmed, option),
67176792
(35, is_manual_broadcast, (default_value, false)),
67186793
(37, funding_seen_onchain, (default_value, true)),
6794+
(39, current_funding_contributed_inputs, option),
6795+
(41, current_funding_contributed_outputs, option),
67196796
});
67206797
// Note that `payment_preimages_with_info` was added (and is always written) in LDK 0.1, so
67216798
// we can use it to determine if this monitor was last written by LDK 0.1 or later.
@@ -6830,6 +6907,8 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP
68306907

68316908
current_holder_commitment_tx,
68326909
prev_holder_commitment_tx,
6910+
contributed_inputs: current_funding_contributed_inputs,
6911+
contributed_outputs: current_funding_contributed_outputs,
68336912
},
68346913
pending_funding: pending_funding.unwrap_or(vec![]),
68356914
is_manual_broadcast: is_manual_broadcast.0.unwrap(),

lightning/src/ln/channel.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7925,12 +7925,12 @@ where
79257925
&mut self, msg: &msgs::CommitmentSigned, fee_estimator: &LowerBoundedFeeEstimator<F>,
79267926
logger: &L,
79277927
) -> Result<Option<ChannelMonitorUpdate>, ChannelError> {
7928-
debug_assert!(self
7928+
let signing_session = self
79297929
.context
79307930
.interactive_tx_signing_session
79317931
.as_ref()
7932-
.map(|signing_session| !signing_session.has_received_tx_signatures())
7933-
.unwrap_or(false));
7932+
.expect("Signing session must exist for negotiated pending splice");
7933+
debug_assert!(!signing_session.has_received_tx_signatures());
79347934

79357935
let pending_splice_funding = self
79367936
.pending_splice
@@ -7982,6 +7982,9 @@ where
79827982
);
79837983
}
79847984

7985+
let (contributed_inputs, contributed_outputs) =
7986+
signing_session.to_contributed_inputs_and_outputs();
7987+
79857988
log_info!(
79867989
logger,
79877990
"Received splice initial commitment_signed from peer with funding txid {}",
@@ -7995,6 +7998,8 @@ where
79957998
channel_parameters: pending_splice_funding.channel_transaction_parameters.clone(),
79967999
holder_commitment_tx,
79978000
counterparty_commitment_tx,
8001+
contributed_inputs,
8002+
contributed_outputs,
79988003
}],
79998004
channel_id: Some(self.context.channel_id()),
80008005
};

lightning/src/ln/splicing_tests.rs

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1897,6 +1897,8 @@ fn do_test_splice_commitment_broadcast(splice_status: SpliceStatus, claim_htlcs:
18971897
let splice_in_amount = initial_channel_capacity / 2;
18981898
let initiator_contribution =
18991899
do_initiate_splice_in(&nodes[0], &nodes[1], channel_id, Amount::from_sat(splice_in_amount));
1900+
let (expected_discarded_inputs, expected_discarded_outputs) =
1901+
initiator_contribution.clone().into_contributed_inputs_and_outputs();
19001902
let (splice_tx, _) = splice_channel(&nodes[0], &nodes[1], channel_id, initiator_contribution);
19011903
let (preimage2, payment_hash2, ..) = route_payment(&nodes[0], &[&nodes[1]], payment_amount);
19021904
let htlc_expiry = nodes[0].best_block_info().1 + TEST_FINAL_CLTV + LATENCY_GRACE_PERIOD_BLOCKS;
@@ -2039,20 +2041,29 @@ fn do_test_splice_commitment_broadcast(splice_status: SpliceStatus, claim_htlcs:
20392041
.chain_source
20402042
.remove_watched_txn_and_outputs(funding_outpoint, txout.script_pubkey.clone());
20412043

2042-
// `SpendableOutputs` events are also included here, but we don't care for them.
20432044
let events = nodes[0].chain_monitor.chain_monitor.get_and_clear_pending_events();
20442045
assert_eq!(events.len(), if claim_htlcs { 2 } else { 4 }, "{events:?}");
20452046
if let Event::DiscardFunding { funding_info, .. } = &events[0] {
2046-
assert_eq!(*funding_info, FundingInfo::OutPoint { outpoint: funding_outpoint });
2047+
assert_eq!(
2048+
*funding_info,
2049+
FundingInfo::Contribution {
2050+
inputs: expected_discarded_inputs,
2051+
outputs: expected_discarded_outputs,
2052+
}
2053+
);
20472054
} else {
20482055
panic!();
20492056
}
2057+
assert!(matches!(&events[1], Event::SpendableOutputs { .. }));
2058+
if !claim_htlcs {
2059+
assert!(matches!(&events[2], Event::SpendableOutputs { .. }));
2060+
assert!(matches!(&events[3], Event::SpendableOutputs { .. }));
2061+
}
2062+
20502063
let events = nodes[1].chain_monitor.chain_monitor.get_and_clear_pending_events();
2051-
assert_eq!(events.len(), if claim_htlcs { 2 } else { 1 }, "{events:?}");
2052-
if let Event::DiscardFunding { funding_info, .. } = &events[0] {
2053-
assert_eq!(*funding_info, FundingInfo::OutPoint { outpoint: funding_outpoint });
2054-
} else {
2055-
panic!();
2064+
assert_eq!(events.len(), if claim_htlcs { 1 } else { 0 }, "{events:?}");
2065+
if claim_htlcs {
2066+
assert!(matches!(&events[0], Event::SpendableOutputs { .. }));
20562067
}
20572068
}
20582069
}

lightning/src/util/ser.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1102,6 +1102,8 @@ impl_for_vec!(crate::ln::channelmanager::MonitorUpdateCompletionAction);
11021102
impl_for_vec!(crate::ln::channelmanager::PaymentClaimDetails);
11031103
impl_for_vec!(crate::ln::msgs::SocketAddress);
11041104
impl_for_vec!((A, B), A, B);
1105+
impl_for_vec!(OutPoint);
1106+
impl_for_vec!(ScriptBuf);
11051107
impl_for_vec!(SerialId);
11061108
impl_for_vec!(TxInMetadata);
11071109
impl_for_vec!(TxOutMetadata);

0 commit comments

Comments
 (0)