Skip to content

Commit a201837

Browse files
committed
Introduce RenegotiatedFunding monitor update variant
This is a new `ChannelMonitorUpdateStep` variant intended to be used whenever a new funding transaction for the channel has been negotiated via the `InteractiveTxConstructor`. This commit primarily focuses on its use for splices, but future work will expand where needed to support RBFs (both for the initial dual funding transaction, and splice transactions). To draw a parallel to channel open, we generally want to have the commitment transactions negotiated for the funding transaction and committed to the respective `ChannelMonitor` before attempting to sign the funding transaction itself. This monitor update fulfills this need for a newly negotiated splice; it includes both the new holder and counterparty commitment transactions, and the new set of applicable `ChannelTransactionParameters`. Once the monitor update has been applied to the monitor and persisted, we allow the release of our `tx_signatures` for the splice transaction to wait for its confirmation.
1 parent db165ad commit a201837

File tree

3 files changed

+363
-6
lines changed

3 files changed

+363
-6
lines changed

lightning/src/chain/chainmonitor.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1180,6 +1180,24 @@ where
11801180
panic!("{}", err_str);
11811181
},
11821182
}
1183+
1184+
// We may need to start monitoring for any alternative funding transactions.
1185+
if let Some(ref chain_source) = self.chain_source {
1186+
for (funding_outpoint, funding_script) in update.renegotiated_funding_data() {
1187+
log_trace!(
1188+
logger,
1189+
"Registering renegotiated funding outpoint {} with the filter to monitor confirmations and spends",
1190+
funding_outpoint
1191+
);
1192+
chain_source.register_tx(&funding_outpoint.txid, &funding_script);
1193+
chain_source.register_output(WatchedOutput {
1194+
block_hash: None,
1195+
outpoint: funding_outpoint,
1196+
script_pubkey: funding_script,
1197+
});
1198+
}
1199+
}
1200+
11831201
if update_res.is_err() {
11841202
ChannelMonitorUpdateStatus::InProgress
11851203
} else {

lightning/src/chain/channelmonitor.rs

Lines changed: 235 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,23 @@ pub struct ChannelMonitorUpdate {
113113
pub channel_id: Option<ChannelId>,
114114
}
115115

116+
impl ChannelMonitorUpdate {
117+
/// Returns an iterator of new (funding outpoint, funding script) to monitor the chain for as
118+
/// a result of a renegotiated funding transaction.
119+
pub fn renegotiated_funding_data(&self) -> impl Iterator<Item = (OutPoint, ScriptBuf)> + '_ {
120+
self.updates.iter().filter_map(|update| match update {
121+
ChannelMonitorUpdateStep::RenegotiatedFunding { channel_parameters, .. } => {
122+
let funding_outpoint = channel_parameters
123+
.funding_outpoint
124+
.expect("Renegotiated funding must always have known outpoint");
125+
let funding_script = channel_parameters.make_funding_redeemscript().to_p2wsh();
126+
Some((funding_outpoint, funding_script))
127+
},
128+
_ => None,
129+
})
130+
}
131+
}
132+
116133
/// LDK prior to 0.1 used this constant as the [`ChannelMonitorUpdate::update_id`] for any
117134
/// [`ChannelMonitorUpdate`]s which were generated after the channel was closed.
118135
const LEGACY_CLOSED_CHANNEL_UPDATE_ID: u64 = u64::MAX;
@@ -640,6 +657,11 @@ pub(crate) enum ChannelMonitorUpdateStep {
640657
ShutdownScript {
641658
scriptpubkey: ScriptBuf,
642659
},
660+
RenegotiatedFunding {
661+
channel_parameters: ChannelTransactionParameters,
662+
holder_commitment_tx: HolderCommitmentTransaction,
663+
counterparty_commitment_tx: CommitmentTransaction,
664+
},
643665
}
644666

645667
impl ChannelMonitorUpdateStep {
@@ -653,6 +675,7 @@ impl ChannelMonitorUpdateStep {
653675
ChannelMonitorUpdateStep::CommitmentSecret { .. } => "CommitmentSecret",
654676
ChannelMonitorUpdateStep::ChannelForceClosed { .. } => "ChannelForceClosed",
655677
ChannelMonitorUpdateStep::ShutdownScript { .. } => "ShutdownScript",
678+
ChannelMonitorUpdateStep::RenegotiatedFunding { .. } => "RenegotiatedFunding",
656679
}
657680
}
658681
}
@@ -691,6 +714,11 @@ impl_writeable_tlv_based_enum_upgradable!(ChannelMonitorUpdateStep,
691714
(0, htlc_outputs, required_vec),
692715
(2, commitment_tx, required),
693716
},
717+
(10, RenegotiatedFunding) => {
718+
(1, channel_parameters, (required: ReadableArgs, None)),
719+
(3, holder_commitment_tx, required),
720+
(5, counterparty_commitment_tx, required),
721+
},
694722
);
695723

696724
/// Indicates whether the balance is derived from a cooperative close, a force-close
@@ -1024,9 +1052,69 @@ struct FundingScope {
10241052
prev_holder_commitment_tx: Option<HolderCommitmentTransaction>,
10251053
}
10261054

1055+
impl FundingScope {
1056+
fn funding_outpoint(&self) -> OutPoint {
1057+
let funding_outpoint = self.channel_parameters.funding_outpoint.as_ref();
1058+
*funding_outpoint.expect("Funding outpoint must be set for active monitor")
1059+
}
1060+
1061+
fn funding_txid(&self) -> Txid {
1062+
self.funding_outpoint().txid
1063+
}
1064+
}
1065+
1066+
impl Writeable for FundingScope {
1067+
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
1068+
write_tlv_fields!(w, {
1069+
(1, self.channel_parameters, (required: ReadableArgs, None)),
1070+
(3, self.current_counterparty_commitment_txid, required),
1071+
(5, self.prev_counterparty_commitment_txid, option),
1072+
(7, self.current_holder_commitment_tx, required),
1073+
(9, self.prev_holder_commitment_tx, option),
1074+
(11, self.counterparty_claimable_outpoints, required),
1075+
});
1076+
Ok(())
1077+
}
1078+
}
1079+
1080+
impl Readable for FundingScope {
1081+
fn read<R: io::Read>(r: &mut R) -> Result<Self, DecodeError> {
1082+
let mut channel_parameters = RequiredWrapper(None);
1083+
let mut current_counterparty_commitment_txid = RequiredWrapper(None);
1084+
let mut prev_counterparty_commitment_txid = None;
1085+
let mut current_holder_commitment_tx = RequiredWrapper(None);
1086+
let mut prev_holder_commitment_tx = None;
1087+
let mut counterparty_claimable_outpoints = RequiredWrapper(None);
1088+
1089+
read_tlv_fields!(r, {
1090+
(1, channel_parameters, (required: ReadableArgs, None)),
1091+
(3, current_counterparty_commitment_txid, required),
1092+
(5, prev_counterparty_commitment_txid, option),
1093+
(7, current_holder_commitment_tx, required),
1094+
(9, prev_holder_commitment_tx, option),
1095+
(11, counterparty_claimable_outpoints, required),
1096+
});
1097+
1098+
let channel_parameters: ChannelTransactionParameters = channel_parameters.0.unwrap();
1099+
let redeem_script = channel_parameters.make_funding_redeemscript();
1100+
1101+
Ok(Self {
1102+
script_pubkey: redeem_script.to_p2wsh(),
1103+
redeem_script,
1104+
channel_parameters,
1105+
current_counterparty_commitment_txid: current_counterparty_commitment_txid.0.unwrap(),
1106+
prev_counterparty_commitment_txid,
1107+
current_holder_commitment_tx: current_holder_commitment_tx.0.unwrap(),
1108+
prev_holder_commitment_tx,
1109+
counterparty_claimable_outpoints: counterparty_claimable_outpoints.0.unwrap(),
1110+
})
1111+
}
1112+
}
1113+
10271114
#[derive(Clone, PartialEq)]
10281115
pub(crate) struct ChannelMonitorImpl<Signer: EcdsaChannelSigner> {
10291116
funding: FundingScope,
1117+
pending_funding: Vec<FundingScope>,
10301118

10311119
latest_update_id: u64,
10321120
commitment_transaction_number_obscure_factor: u64,
@@ -1467,6 +1555,7 @@ impl<Signer: EcdsaChannelSigner> Writeable for ChannelMonitorImpl<Signer> {
14671555
(27, self.first_confirmed_funding_txo, required),
14681556
(29, self.initial_counterparty_commitment_tx, option),
14691557
(31, self.funding.channel_parameters, required),
1558+
(32, self.pending_funding, optional_vec),
14701559
});
14711560

14721561
Ok(())
@@ -1636,6 +1725,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitor<Signer> {
16361725
current_holder_commitment_tx: initial_holder_commitment_tx,
16371726
prev_holder_commitment_tx: None,
16381727
},
1728+
pending_funding: vec![],
16391729

16401730
latest_update_id: 0,
16411731
commitment_transaction_number_obscure_factor,
@@ -1862,14 +1952,16 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitor<Signer> {
18621952
{
18631953
let lock = self.inner.lock().unwrap();
18641954
let logger = WithChannelMonitor::from_impl(logger, &*lock, None);
1865-
log_trace!(&logger, "Registering funding outpoint {}", &lock.get_funding_txo());
1866-
let funding_outpoint = lock.get_funding_txo();
1867-
filter.register_tx(&funding_outpoint.txid, &lock.funding.script_pubkey);
1955+
for funding in core::iter::once(&lock.funding).chain(&lock.pending_funding) {
1956+
let funding_outpoint = funding.funding_outpoint();
1957+
log_trace!(&logger, "Registering funding outpoint {} with the filter to monitor confirmations", &funding_outpoint);
1958+
filter.register_tx(&funding_outpoint.txid, &funding.script_pubkey);
1959+
}
18681960
for (txid, outputs) in lock.get_outputs_to_watch().iter() {
18691961
for (index, script_pubkey) in outputs.iter() {
18701962
assert!(*index <= u16::MAX as u32);
18711963
let outpoint = OutPoint { txid: *txid, index: *index as u16 };
1872-
log_trace!(logger, "Registering outpoint {} with the filter for monitoring spends", outpoint);
1964+
log_trace!(logger, "Registering outpoint {} with the filter to monitor spend", outpoint);
18731965
filter.register_output(WatchedOutput {
18741966
block_hash: None,
18751967
outpoint,
@@ -3453,6 +3545,126 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
34533545
);
34543546
}
34553547

3548+
fn renegotiated_funding<L: Deref>(
3549+
&mut self, logger: &WithChannelMonitor<L>,
3550+
channel_parameters: &ChannelTransactionParameters,
3551+
alternative_holder_commitment_tx: &HolderCommitmentTransaction,
3552+
alternative_counterparty_commitment_tx: &CommitmentTransaction,
3553+
) -> Result<(), ()>
3554+
where
3555+
L::Target: Logger,
3556+
{
3557+
let redeem_script = channel_parameters.make_funding_redeemscript();
3558+
let script_pubkey = redeem_script.to_p2wsh();
3559+
let alternative_counterparty_commitment_txid =
3560+
alternative_counterparty_commitment_tx.trust().txid();
3561+
3562+
// Both the current counterparty commitment and the alternative one share the same set of
3563+
// non-dust and dust HTLCs in the same order, though the index of each non-dust HTLC may be
3564+
// different.
3565+
//
3566+
// We clone all HTLCs and their sources to use in the alternative funding scope, and update
3567+
// each non-dust HTLC with their corresponding index in the alternative counterparty
3568+
// commitment.
3569+
let current_counterparty_commitment_htlcs =
3570+
if let Some(txid) = &self.funding.current_counterparty_commitment_txid {
3571+
self.funding.counterparty_claimable_outpoints.get(txid).unwrap()
3572+
} else {
3573+
debug_assert!(false);
3574+
log_error!(
3575+
logger,
3576+
"Received funding renegotiation while initial funding negotiation is still pending"
3577+
);
3578+
return Err(());
3579+
};
3580+
let mut htlcs_with_sources = current_counterparty_commitment_htlcs.clone();
3581+
let alternative_htlcs = alternative_counterparty_commitment_tx.nondust_htlcs();
3582+
3583+
let expected_non_dust_htlc_count = htlcs_with_sources
3584+
.iter()
3585+
.filter(|(htlc, _)| htlc.transaction_output_index.is_some())
3586+
.count();
3587+
if alternative_htlcs.len() != expected_non_dust_htlc_count {
3588+
log_error!(
3589+
logger,
3590+
"Received alternative counterparty commitment with HTLC count mismatch"
3591+
);
3592+
return Err(());
3593+
}
3594+
3595+
for (alternative_htlc, (htlc, _)) in
3596+
alternative_htlcs.iter().zip(htlcs_with_sources.iter_mut())
3597+
{
3598+
debug_assert!(htlc.transaction_output_index.is_some());
3599+
debug_assert!(alternative_htlc.transaction_output_index.is_some());
3600+
if !alternative_htlc.is_data_equal(htlc) {
3601+
log_error!(
3602+
logger,
3603+
"Received alternative counterparty commitment with non-dust HTLC mismatch"
3604+
);
3605+
return Err(());
3606+
}
3607+
htlc.transaction_output_index = alternative_htlc.transaction_output_index;
3608+
}
3609+
3610+
let mut counterparty_claimable_outpoints = new_hash_map();
3611+
counterparty_claimable_outpoints
3612+
.insert(alternative_counterparty_commitment_txid, htlcs_with_sources);
3613+
3614+
// TODO(splicing): Enforce any necessary RBF validity checks.
3615+
let alternative_funding = FundingScope {
3616+
script_pubkey: script_pubkey.clone(),
3617+
redeem_script,
3618+
channel_parameters: channel_parameters.clone(),
3619+
current_counterparty_commitment_txid: Some(alternative_counterparty_commitment_txid),
3620+
prev_counterparty_commitment_txid: None,
3621+
counterparty_claimable_outpoints,
3622+
current_holder_commitment_tx: alternative_holder_commitment_tx.clone(),
3623+
prev_holder_commitment_tx: None,
3624+
};
3625+
let alternative_funding_outpoint = alternative_funding.funding_outpoint();
3626+
3627+
if self
3628+
.pending_funding
3629+
.iter()
3630+
.any(|funding| funding.funding_txid() == alternative_funding_outpoint.txid)
3631+
{
3632+
log_error!(
3633+
logger,
3634+
"Renegotiated funding transaction with a duplicate funding txid {}",
3635+
alternative_funding_outpoint.txid
3636+
);
3637+
return Err(());
3638+
}
3639+
3640+
if let Some(parent_funding_txid) = channel_parameters.splice_parent_funding_txid.as_ref() {
3641+
// Only one splice can be negotiated at a time after we've exchanged `channel_ready`
3642+
// (implying our funding is confirmed) that spends our currently locked funding.
3643+
if !self.pending_funding.is_empty() {
3644+
log_error!(
3645+
logger,
3646+
"Negotiated splice while channel is pending channel_ready/splice_locked"
3647+
);
3648+
return Err(());
3649+
}
3650+
if *parent_funding_txid != self.funding.funding_txid() {
3651+
log_error!(
3652+
logger,
3653+
"Negotiated splice that does not spend currently locked funding transaction"
3654+
);
3655+
return Err(());
3656+
}
3657+
}
3658+
3659+
self.outputs_to_watch.insert(
3660+
alternative_funding_outpoint.txid,
3661+
vec![(alternative_funding_outpoint.index as u32, script_pubkey)],
3662+
);
3663+
self.pending_funding.push(alternative_funding);
3664+
3665+
Ok(())
3666+
}
3667+
34563668
#[rustfmt::skip]
34573669
fn update_monitor<B: Deref, F: Deref, L: Deref>(
34583670
&mut self, updates: &ChannelMonitorUpdate, broadcaster: &B, fee_estimator: &F, logger: &WithChannelMonitor<L>
@@ -3532,6 +3744,17 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
35323744
ret = Err(());
35333745
}
35343746
},
3747+
ChannelMonitorUpdateStep::RenegotiatedFunding {
3748+
channel_parameters, holder_commitment_tx, counterparty_commitment_tx,
3749+
} => {
3750+
log_trace!(logger, "Updating ChannelMonitor with alternative holder and counterparty commitment transactions for funding txid {}",
3751+
channel_parameters.funding_outpoint.unwrap().txid);
3752+
if let Err(_) = self.renegotiated_funding(
3753+
logger, channel_parameters, holder_commitment_tx, counterparty_commitment_tx,
3754+
) {
3755+
ret = Err(());
3756+
}
3757+
},
35353758
ChannelMonitorUpdateStep::ChannelForceClosed { should_broadcast } => {
35363759
log_trace!(logger, "Updating ChannelMonitor: channel force closed, should broadcast: {}", should_broadcast);
35373760
self.lockdown_from_offchain = true;
@@ -3583,7 +3806,8 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
35833806
|ChannelMonitorUpdateStep::LatestCounterpartyCommitmentTXInfo { .. }
35843807
|ChannelMonitorUpdateStep::LatestCounterpartyCommitmentTX { .. }
35853808
|ChannelMonitorUpdateStep::ShutdownScript { .. }
3586-
|ChannelMonitorUpdateStep::CommitmentSecret { .. } =>
3809+
|ChannelMonitorUpdateStep::CommitmentSecret { .. }
3810+
|ChannelMonitorUpdateStep::RenegotiatedFunding { .. } =>
35873811
is_pre_close_update = true,
35883812
// After a channel is closed, we don't communicate with our peer about it, so the
35893813
// only things we will update is getting a new preimage (from a different channel)
@@ -3765,6 +3989,9 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
37653989
} => {
37663990
Some(commitment_tx.clone())
37673991
},
3992+
&ChannelMonitorUpdateStep::RenegotiatedFunding { ref counterparty_commitment_tx, .. } => {
3993+
Some(counterparty_commitment_tx.clone())
3994+
},
37683995
_ => None,
37693996
}
37703997
}).collect()
@@ -5438,6 +5665,7 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP
54385665
let mut payment_preimages_with_info: Option<HashMap<_, _>> = None;
54395666
let mut first_confirmed_funding_txo = RequiredWrapper(None);
54405667
let mut channel_parameters = None;
5668+
let mut pending_funding = None;
54415669
read_tlv_fields!(reader, {
54425670
(1, funding_spend_confirmed, option),
54435671
(3, htlcs_resolved_on_chain, optional_vec),
@@ -5455,6 +5683,7 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP
54555683
(27, first_confirmed_funding_txo, (default_value, outpoint)),
54565684
(29, initial_counterparty_commitment_tx, option),
54575685
(31, channel_parameters, (option: ReadableArgs, None)),
5686+
(32, pending_funding, optional_vec),
54585687
});
54595688
if let Some(payment_preimages_with_info) = payment_preimages_with_info {
54605689
if payment_preimages_with_info.len() != payment_preimages.len() {
@@ -5570,6 +5799,7 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP
55705799
current_holder_commitment_tx,
55715800
prev_holder_commitment_tx,
55725801
},
5802+
pending_funding: pending_funding.unwrap_or(vec![]),
55735803

55745804
latest_update_id,
55755805
commitment_transaction_number_obscure_factor,

0 commit comments

Comments
 (0)