Skip to content

Commit cf88d1f

Browse files
committed
Introduce RenegotiatedFundingLocked monitor update variant
This is a new `ChannelMonitorUpdateStep` variant intended to be used whenever a new funding transaction that was negotiated and applied via the `RenegotiatedFunding` update reaches its intended confirmation depth and both sides of the channel exchange `channel_ready`/`splice_locked`. This commit primarily focuses on its use for splices, but future work will expand where needed to support RBFs for a dual funded channel. This monitor update ensures that the monitor can safely drop all prior commitment data since it is now considered invalid/unnecessary. Once the update is applied, only state for the new funding transaction is tracked going forward, until the monitor receives another `RenegotiatedFunding` update.
1 parent c48e0a8 commit cf88d1f

File tree

4 files changed

+174
-14
lines changed

4 files changed

+174
-14
lines changed

lightning/src/chain/channelmonitor.rs

Lines changed: 53 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -676,6 +676,9 @@ pub(crate) enum ChannelMonitorUpdateStep {
676676
holder_commitment_tx: HolderCommitmentTransaction,
677677
counterparty_commitment_tx: CommitmentTransaction,
678678
},
679+
RenegotiatedFundingLocked {
680+
funding_txid: Txid,
681+
},
679682
}
680683

681684
impl ChannelMonitorUpdateStep {
@@ -690,6 +693,7 @@ impl ChannelMonitorUpdateStep {
690693
ChannelMonitorUpdateStep::ChannelForceClosed { .. } => "ChannelForceClosed",
691694
ChannelMonitorUpdateStep::ShutdownScript { .. } => "ShutdownScript",
692695
ChannelMonitorUpdateStep::RenegotiatedFunding { .. } => "RenegotiatedFunding",
696+
ChannelMonitorUpdateStep::RenegotiatedFundingLocked { .. } => "RenegotiatedFundingLocked",
693697
}
694698
}
695699
}
@@ -733,6 +737,9 @@ impl_writeable_tlv_based_enum_upgradable!(ChannelMonitorUpdateStep,
733737
(3, holder_commitment_tx, required),
734738
(5, counterparty_commitment_tx, required),
735739
},
740+
(12, RenegotiatedFundingLocked) => {
741+
(1, funding_txid, required),
742+
},
736743
);
737744

738745
/// Indicates whether the balance is derived from a cooperative close, a force-close
@@ -1209,8 +1216,6 @@ pub(crate) struct ChannelMonitorImpl<Signer: EcdsaChannelSigner> {
12091216
// interface knows about the TXOs that we want to be notified of spends of. We could probably
12101217
// be smart and derive them from the above storage fields, but its much simpler and more
12111218
// Obviously Correct (tm) if we just keep track of them explicitly.
1212-
//
1213-
// TODO: Remove entries for stale funding transactions on `splice_locked`.
12141219
outputs_to_watch: HashMap<Txid, Vec<(u32, ScriptBuf)>>,
12151220

12161221
#[cfg(any(test, feature = "_test_utils"))]
@@ -3681,6 +3686,43 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
36813686
Ok(())
36823687
}
36833688

3689+
fn promote_funding(&mut self, new_funding_txid: Txid) -> Result<(), ()> {
3690+
let has_pending_splice = self
3691+
.pending_funding
3692+
.iter()
3693+
.any(|funding| funding.channel_parameters.splice_parent_funding_txid.is_some());
3694+
3695+
let new_funding = self
3696+
.pending_funding
3697+
.iter_mut()
3698+
.find(|funding| funding.funding_txid() == new_funding_txid);
3699+
if new_funding.is_none() {
3700+
return Err(());
3701+
}
3702+
let mut new_funding = new_funding.unwrap();
3703+
3704+
// `first_confirmed_funding_txo` is set to the first outpoint for the channel upon init.
3705+
// If an RBF happens and it confirms, this will no longer be accurate, so update it now
3706+
// if we know the RBF doesn't belong to a splice.
3707+
if !has_pending_splice
3708+
&& self.first_confirmed_funding_txo == self.funding.funding_outpoint()
3709+
{
3710+
self.first_confirmed_funding_txo = new_funding.funding_outpoint();
3711+
}
3712+
3713+
mem::swap(&mut self.funding, &mut new_funding);
3714+
self.onchain_tx_handler.update_after_renegotiated_funding_locked(
3715+
self.funding.current_holder_commitment_tx.clone(),
3716+
self.funding.prev_holder_commitment_tx.clone(),
3717+
);
3718+
3719+
for funding in self.pending_funding.drain(..) {
3720+
self.outputs_to_watch.remove(&funding.funding_txid());
3721+
}
3722+
3723+
Ok(())
3724+
}
3725+
36843726
#[rustfmt::skip]
36853727
fn update_monitor<B: Deref, F: Deref, L: Deref>(
36863728
&mut self, updates: &ChannelMonitorUpdate, broadcaster: &B, fee_estimator: &F, logger: &WithChannelMonitor<L>
@@ -3771,6 +3813,13 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
37713813
ret = Err(());
37723814
}
37733815
},
3816+
ChannelMonitorUpdateStep::RenegotiatedFundingLocked { funding_txid } => {
3817+
log_trace!(logger, "Updating ChannelMonitor with locked renegotiated funding txid {}", funding_txid);
3818+
if let Err(_) = self.promote_funding(*funding_txid) {
3819+
log_error!(logger, "Unknown funding with txid {} became locked", funding_txid);
3820+
ret = Err(());
3821+
}
3822+
},
37743823
ChannelMonitorUpdateStep::ChannelForceClosed { should_broadcast } => {
37753824
log_trace!(logger, "Updating ChannelMonitor: channel force closed, should broadcast: {}", should_broadcast);
37763825
self.lockdown_from_offchain = true;
@@ -3823,7 +3872,8 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
38233872
|ChannelMonitorUpdateStep::LatestCounterpartyCommitmentTX { .. }
38243873
|ChannelMonitorUpdateStep::ShutdownScript { .. }
38253874
|ChannelMonitorUpdateStep::CommitmentSecret { .. }
3826-
|ChannelMonitorUpdateStep::RenegotiatedFunding { .. } =>
3875+
|ChannelMonitorUpdateStep::RenegotiatedFunding { .. }
3876+
|ChannelMonitorUpdateStep::RenegotiatedFundingLocked { .. } =>
38273877
is_pre_close_update = true,
38283878
// After a channel is closed, we don't communicate with our peer about it, so the
38293879
// only things we will update is getting a new preimage (from a different channel)

lightning/src/chain/onchaintx.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1216,6 +1216,15 @@ impl<ChannelSigner: EcdsaChannelSigner> OnchainTxHandler<ChannelSigner> {
12161216
self.prev_holder_commitment = Some(replace(&mut self.holder_commitment, tx));
12171217
}
12181218

1219+
/// Replaces the current/prev holder commitment transactions spending the currently confirmed
1220+
/// funding outpoint with those spending the new funding outpoint.
1221+
pub(crate) fn update_after_renegotiated_funding_locked(
1222+
&mut self, current: HolderCommitmentTransaction, prev: Option<HolderCommitmentTransaction>,
1223+
) {
1224+
self.holder_commitment = current;
1225+
self.prev_holder_commitment = prev;
1226+
}
1227+
12191228
// Deprecated as of 0.2, only use in cases where it was not previously available.
12201229
pub(crate) fn channel_parameters(&self) -> &ChannelTransactionParameters {
12211230
&self.channel_transaction_parameters

lightning/src/ln/channel.rs

Lines changed: 58 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9673,11 +9673,25 @@ where
96739673
.get_funding_txo()
96749674
.expect("Splice FundingScope should always have a funding_txo")
96759675
});
9676+
9677+
let monitor_update_opt = funding_promoted.then(|| {
9678+
self.context.latest_monitor_update_id += 1;
9679+
let monitor_update = ChannelMonitorUpdate {
9680+
update_id: self.context.latest_monitor_update_id,
9681+
updates: vec![ChannelMonitorUpdateStep::RenegotiatedFundingLocked {
9682+
funding_txid: funding_txo.unwrap().txid,
9683+
}],
9684+
channel_id: Some(self.context.channel_id()),
9685+
};
9686+
self.monitor_updating_paused(false, false, false, Vec::new(), Vec::new(), Vec::new());
9687+
self.push_ret_blockable_mon_update(monitor_update)
9688+
}).flatten();
9689+
96769690
let announcement_sigs = funding_promoted
96779691
.then(|| self.get_announcement_sigs(node_signer, chain_hash, user_config, height, logger))
96789692
.flatten();
96799693

9680-
return Ok((Some(FundingConfirmedMessage::Splice(splice_locked, funding_txo)), announcement_sigs));
9694+
return Ok((Some(FundingConfirmedMessage::Splice(splice_locked, funding_txo, monitor_update_opt)), announcement_sigs));
96819695
}
96829696
}
96839697

@@ -9842,6 +9856,20 @@ where
98429856
.get_funding_txo()
98439857
.expect("Splice FundingScope should always have a funding_txo")
98449858
});
9859+
9860+
let monitor_update_opt = funding_promoted.then(|| {
9861+
self.context.latest_monitor_update_id += 1;
9862+
let monitor_update = ChannelMonitorUpdate {
9863+
update_id: self.context.latest_monitor_update_id,
9864+
updates: vec![ChannelMonitorUpdateStep::RenegotiatedFundingLocked {
9865+
funding_txid: funding_txo.unwrap().txid,
9866+
}],
9867+
channel_id: Some(self.context.channel_id()),
9868+
};
9869+
self.monitor_updating_paused(false, false, false, Vec::new(), Vec::new(), Vec::new());
9870+
self.push_ret_blockable_mon_update(monitor_update)
9871+
}).flatten();
9872+
98459873
let announcement_sigs = funding_promoted
98469874
.then(|| chain_node_signer
98479875
.and_then(|(chain_hash, node_signer, user_config)|
@@ -9850,7 +9878,7 @@ where
98509878
)
98519879
.flatten();
98529880

9853-
return Ok((Some(FundingConfirmedMessage::Splice(splice_locked, funding_txo)), timed_out_htlcs, announcement_sigs));
9881+
return Ok((Some(FundingConfirmedMessage::Splice(splice_locked, funding_txo, monitor_update_opt)), timed_out_htlcs, announcement_sigs));
98549882
}
98559883
}
98569884

@@ -10345,7 +10373,10 @@ where
1034510373
pub fn splice_locked<NS: Deref, L: Deref>(
1034610374
&mut self, msg: &msgs::SpliceLocked, node_signer: &NS, chain_hash: ChainHash,
1034710375
user_config: &UserConfig, best_block: &BestBlock, logger: &L,
10348-
) -> Result<(Option<OutPoint>, Option<msgs::AnnouncementSignatures>), ChannelError>
10376+
) -> Result<
10377+
(Option<OutPoint>, Option<ChannelMonitorUpdate>, Option<msgs::AnnouncementSignatures>),
10378+
ChannelError,
10379+
>
1034910380
where
1035010381
NS::Target: NodeSigner,
1035110382
L::Target: Logger,
@@ -10371,25 +10402,47 @@ where
1037110402
.iter_mut()
1037210403
.find(|funding| funding.get_funding_txid() == Some(sent_funding_txid))
1037310404
{
10405+
// TODO: Do we need to do any of this after the channel is resumed?
1037410406
log_info!(
1037510407
logger,
1037610408
"Promoting splice funding txid {} for channel {}",
1037710409
msg.splice_txid,
1037810410
&self.context.channel_id,
1037910411
);
1038010412
promote_splice_funding!(self, funding);
10413+
1038110414
let funding_txo = self
1038210415
.funding
1038310416
.get_funding_txo()
1038410417
.expect("Splice FundingScope should always have a funding_txo");
10418+
10419+
self.context.latest_monitor_update_id += 1;
10420+
let monitor_update = ChannelMonitorUpdate {
10421+
update_id: self.context.latest_monitor_update_id,
10422+
updates: vec![ChannelMonitorUpdateStep::RenegotiatedFundingLocked {
10423+
funding_txid: funding_txo.txid,
10424+
}],
10425+
channel_id: Some(self.context.channel_id()),
10426+
};
10427+
self.monitor_updating_paused(
10428+
false,
10429+
false,
10430+
false,
10431+
Vec::new(),
10432+
Vec::new(),
10433+
Vec::new(),
10434+
);
10435+
let monitor_update_opt = self.push_ret_blockable_mon_update(monitor_update);
10436+
1038510437
let announcement_sigs = self.get_announcement_sigs(
1038610438
node_signer,
1038710439
chain_hash,
1038810440
user_config,
1038910441
best_block.height,
1039010442
logger,
1039110443
);
10392-
return Ok((Some(funding_txo), announcement_sigs));
10444+
10445+
return Ok((Some(funding_txo), monitor_update_opt, announcement_sigs));
1039310446
}
1039410447

1039510448
let err = "unknown splice funding txid";
@@ -10413,7 +10466,7 @@ where
1041310466
}
1041410467

1041510468
pending_splice.received_funding_txid = Some(msg.splice_txid);
10416-
Ok((None, None))
10469+
Ok((None, None, None))
1041710470
}
1041810471

1041910472
// Send stuff to our remote peers:

lightning/src/ln/channelmanager.rs

Lines changed: 54 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10222,7 +10222,7 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
1022210222
&self.best_block.read().unwrap(),
1022310223
&&logger,
1022410224
);
10225-
let (funding_txo, announcement_sigs_opt) =
10225+
let (funding_txo, monitor_update_opt, announcement_sigs_opt) =
1022610226
try_channel_entry!(self, peer_state, result, chan_entry);
1022710227

1022810228
if funding_txo.is_some() {
@@ -10256,6 +10256,21 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
1025610256
},
1025710257
);
1025810258
}
10259+
10260+
if let Some(monitor_update) = monitor_update_opt {
10261+
let funding_txo = funding_txo.expect(
10262+
"Monitor update is only guaranteed if the splice funding was promoted",
10263+
);
10264+
handle_new_monitor_update!(
10265+
self,
10266+
funding_txo,
10267+
monitor_update,
10268+
peer_state_lock,
10269+
peer_state,
10270+
per_peer_state,
10271+
chan
10272+
);
10273+
}
1025910274
} else {
1026010275
return Err(MsgHandleErrInternal::send_err_msg_no_close(
1026110276
"Channel is not funded, cannot splice".to_owned(),
@@ -12280,7 +12295,7 @@ where
1228012295
pub(super) enum FundingConfirmedMessage {
1228112296
Establishment(msgs::ChannelReady),
1228212297
#[cfg(splicing)]
12283-
Splice(msgs::SpliceLocked, Option<OutPoint>),
12298+
Splice(msgs::SpliceLocked, Option<OutPoint>, Option<ChannelMonitorUpdate>),
1228412299
}
1228512300

1228612301
impl<
@@ -12315,6 +12330,7 @@ where
1231512330
// during initialization prior to the chain_monitor being fully configured in some cases.
1231612331
// See the docs for `ChannelManagerReadArgs` for more.
1231712332

12333+
let mut monitor_updates = Vec::new();
1231812334
let mut failed_channels = Vec::new();
1231912335
let mut timed_out_htlcs = Vec::new();
1232012336
{
@@ -12354,25 +12370,32 @@ where
1235412370
}
1235512371
},
1235612372
#[cfg(splicing)]
12357-
Some(FundingConfirmedMessage::Splice(splice_locked, funding_txo)) => {
12373+
Some(FundingConfirmedMessage::Splice(splice_locked, funding_txo, monitor_update_opt)) => {
12374+
let counterparty_node_id = funded_channel.context.get_counterparty_node_id();
12375+
let channel_id = funded_channel.context.channel_id();
12376+
1235812377
if funding_txo.is_some() {
1235912378
let mut short_to_chan_info = self.short_to_chan_info.write().unwrap();
1236012379
insert_short_channel_id!(short_to_chan_info, funded_channel);
1236112380

1236212381
let mut pending_events = self.pending_events.lock().unwrap();
1236312382
pending_events.push_back((events::Event::ChannelReady {
12364-
channel_id: funded_channel.context.channel_id(),
12383+
channel_id,
1236512384
user_channel_id: funded_channel.context.get_user_id(),
12366-
counterparty_node_id: funded_channel.context.get_counterparty_node_id(),
12385+
counterparty_node_id,
1236712386
funding_txo: funding_txo.map(|outpoint| outpoint.into_bitcoin_outpoint()),
1236812387
channel_type: funded_channel.funding.get_channel_type().clone(),
1236912388
}, None));
1237012389
}
1237112390

1237212391
pending_msg_events.push(MessageSendEvent::SendSpliceLocked {
12373-
node_id: funded_channel.context.get_counterparty_node_id(),
12392+
node_id: counterparty_node_id,
1237412393
msg: splice_locked,
1237512394
});
12395+
12396+
if let Some(monitor_update) = monitor_update_opt {
12397+
monitor_updates.push((counterparty_node_id, channel_id, monitor_update));
12398+
}
1237612399
},
1237712400
None => {},
1237812401
}
@@ -12470,6 +12493,31 @@ where
1247012493
}
1247112494
}
1247212495

12496+
#[cfg(splicing)]
12497+
for (counterparty_node_id, channel_id, monitor_update) in monitor_updates {
12498+
let per_peer_state = self.per_peer_state.read().unwrap();
12499+
if let Some(peer_state_mutex) = per_peer_state.get(&counterparty_node_id) {
12500+
let mut peer_state_lock = peer_state_mutex.lock().unwrap();
12501+
let peer_state = &mut *peer_state_lock;
12502+
if let Some(channel) = peer_state
12503+
.channel_by_id
12504+
.get_mut(&channel_id)
12505+
.and_then(Channel::as_funded_mut)
12506+
{
12507+
let funding_txo = channel.funding.get_funding_txo().unwrap();
12508+
handle_new_monitor_update!(
12509+
self,
12510+
funding_txo,
12511+
monitor_update,
12512+
peer_state_lock,
12513+
peer_state,
12514+
per_peer_state,
12515+
channel
12516+
);
12517+
}
12518+
}
12519+
}
12520+
1247312521
if let Some(height) = height_opt {
1247412522
self.claimable_payments.lock().unwrap().claimable_payments.retain(|payment_hash, payment| {
1247512523
payment.htlcs.retain(|htlc| {

0 commit comments

Comments
 (0)