Skip to content

Commit 59739ef

Browse files
authored
Merge pull request #3569 from jkczyz/2025-01-v2-channel-monitor-persist
Support persisting `ChannelMonitor`s after splicing
2 parents 18166d0 + 372e7f0 commit 59739ef

File tree

9 files changed

+247
-235
lines changed

9 files changed

+247
-235
lines changed

fuzz/src/utils/test_persister.rs

+5-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use lightning::chain;
2-
use lightning::chain::transaction::OutPoint;
32
use lightning::chain::{chainmonitor, channelmonitor};
3+
use lightning::util::persist::MonitorName;
44
use lightning::util::test_channel_signer::TestChannelSigner;
55

66
use std::sync::Mutex;
@@ -10,17 +10,18 @@ pub struct TestPersister {
1010
}
1111
impl chainmonitor::Persist<TestChannelSigner> for TestPersister {
1212
fn persist_new_channel(
13-
&self, _funding_txo: OutPoint, _data: &channelmonitor::ChannelMonitor<TestChannelSigner>,
13+
&self, _monitor_name: MonitorName,
14+
_data: &channelmonitor::ChannelMonitor<TestChannelSigner>,
1415
) -> chain::ChannelMonitorUpdateStatus {
1516
self.update_ret.lock().unwrap().clone()
1617
}
1718

1819
fn update_persisted_channel(
19-
&self, _funding_txo: OutPoint, _update: Option<&channelmonitor::ChannelMonitorUpdate>,
20+
&self, _monitor_name: MonitorName, _update: Option<&channelmonitor::ChannelMonitorUpdate>,
2021
_data: &channelmonitor::ChannelMonitor<TestChannelSigner>,
2122
) -> chain::ChannelMonitorUpdateStatus {
2223
self.update_ret.lock().unwrap().clone()
2324
}
2425

25-
fn archive_persisted_channel(&self, _: OutPoint) {}
26+
fn archive_persisted_channel(&self, _monitor_name: MonitorName) {}
2627
}

lightning-persister/src/fs_store.rs

+4-20
Original file line numberDiff line numberDiff line change
@@ -498,17 +498,13 @@ mod tests {
498498
do_read_write_remove_list_persist, do_test_data_migration, do_test_store,
499499
};
500500

501-
use bitcoin::Txid;
502-
503501
use lightning::chain::chainmonitor::Persist;
504-
use lightning::chain::transaction::OutPoint;
505502
use lightning::chain::ChannelMonitorUpdateStatus;
506503
use lightning::check_closed_event;
507504
use lightning::events::{ClosureReason, MessageSendEventsProvider};
508505
use lightning::ln::functional_test_utils::*;
509506
use lightning::util::persist::read_channel_monitors;
510507
use lightning::util::test_utils;
511-
use std::str::FromStr;
512508

513509
impl Drop for FilesystemStore {
514510
fn drop(&mut self) {
@@ -622,14 +618,8 @@ mod tests {
622618
perms.set_readonly(true);
623619
fs::set_permissions(path, perms).unwrap();
624620

625-
let test_txo = OutPoint {
626-
txid: Txid::from_str(
627-
"8984484a580b825b9972d7adb15050b3ab624ccd731946b3eeddb92f4e7ef6be",
628-
)
629-
.unwrap(),
630-
index: 0,
631-
};
632-
match store.persist_new_channel(test_txo, &added_monitors[0].1) {
621+
let monitor_name = added_monitors[0].1.persistence_key();
622+
match store.persist_new_channel(monitor_name, &added_monitors[0].1) {
633623
ChannelMonitorUpdateStatus::UnrecoverableError => {},
634624
_ => panic!("unexpected result from persisting new channel"),
635625
}
@@ -676,14 +666,8 @@ mod tests {
676666
// handle, hence why the test is Windows-only.
677667
let store = FilesystemStore::new(":<>/".into());
678668

679-
let test_txo = OutPoint {
680-
txid: Txid::from_str(
681-
"8984484a580b825b9972d7adb15050b3ab624ccd731946b3eeddb92f4e7ef6be",
682-
)
683-
.unwrap(),
684-
index: 0,
685-
};
686-
match store.persist_new_channel(test_txo, &added_monitors[0].1) {
669+
let monitor_name = added_monitors[0].1.persistence_key();
670+
match store.persist_new_channel(monitor_name, &added_monitors[0].1) {
687671
ChannelMonitorUpdateStatus::UnrecoverableError => {},
688672
_ => panic!("unexpected result from persisting new channel"),
689673
}

lightning/src/chain/chainmonitor.rs

+16-15
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ use crate::sign::ecdsa::EcdsaChannelSigner;
3636
use crate::events::{self, Event, EventHandler, ReplayEvent};
3737
use crate::util::logger::{Logger, WithContext};
3838
use crate::util::errors::APIError;
39+
use crate::util::persist::MonitorName;
3940
use crate::util::wakers::{Future, Notifier};
4041
use crate::ln::channel_state::ChannelDetails;
4142

@@ -102,11 +103,13 @@ use bitcoin::secp256k1::PublicKey;
102103
/// [`TrustedCommitmentTransaction::build_to_local_justice_tx`]: crate::ln::chan_utils::TrustedCommitmentTransaction::build_to_local_justice_tx
103104
pub trait Persist<ChannelSigner: EcdsaChannelSigner> {
104105
/// Persist a new channel's data in response to a [`chain::Watch::watch_channel`] call. This is
105-
/// called by [`ChannelManager`] for new channels, or may be called directly, e.g. on startup.
106+
/// called by [`ChannelManager`] for new channels, or may be called directly, e.g. on startup,
107+
/// with the `monitor_name` returned by [`ChannelMonitor::persistence_key`].
106108
///
107-
/// The data can be stored any way you want, but the identifier provided by LDK is the
108-
/// channel's outpoint (and it is up to you to maintain a correct mapping between the outpoint
109-
/// and the stored channel data). Note that you **must** persist every new monitor to disk.
109+
/// The data can be stored any way you want, so long as `monitor_name` is used to maintain a
110+
/// correct mapping with the stored channel data (i.e., calls to `update_persisted_channel` with
111+
/// the same `monitor_name` must be applied to or overwrite this data). Note that you **must**
112+
/// persist every new monitor to disk.
110113
///
111114
/// The [`ChannelMonitor::get_latest_update_id`] uniquely links this call to [`ChainMonitor::channel_monitor_updated`].
112115
/// For [`Persist::persist_new_channel`], it is only necessary to call [`ChainMonitor::channel_monitor_updated`]
@@ -117,7 +120,7 @@ pub trait Persist<ChannelSigner: EcdsaChannelSigner> {
117120
///
118121
/// [`ChannelManager`]: crate::ln::channelmanager::ChannelManager
119122
/// [`Writeable::write`]: crate::util::ser::Writeable::write
120-
fn persist_new_channel(&self, channel_funding_outpoint: OutPoint, monitor: &ChannelMonitor<ChannelSigner>) -> ChannelMonitorUpdateStatus;
123+
fn persist_new_channel(&self, monitor_name: MonitorName, monitor: &ChannelMonitor<ChannelSigner>) -> ChannelMonitorUpdateStatus;
121124

122125
/// Update one channel's data. The provided [`ChannelMonitor`] has already applied the given
123126
/// update.
@@ -156,7 +159,7 @@ pub trait Persist<ChannelSigner: EcdsaChannelSigner> {
156159
/// [`ChannelMonitorUpdateStatus`] for requirements when returning errors.
157160
///
158161
/// [`Writeable::write`]: crate::util::ser::Writeable::write
159-
fn update_persisted_channel(&self, channel_funding_outpoint: OutPoint, monitor_update: Option<&ChannelMonitorUpdate>, monitor: &ChannelMonitor<ChannelSigner>) -> ChannelMonitorUpdateStatus;
162+
fn update_persisted_channel(&self, monitor_name: MonitorName, monitor_update: Option<&ChannelMonitorUpdate>, monitor: &ChannelMonitor<ChannelSigner>) -> ChannelMonitorUpdateStatus;
160163
/// Prevents the channel monitor from being loaded on startup.
161164
///
162165
/// Archiving the data in a backup location (rather than deleting it fully) is useful for
@@ -168,7 +171,7 @@ pub trait Persist<ChannelSigner: EcdsaChannelSigner> {
168171
/// the archive process. Additionally, because the archive operation could be retried on
169172
/// restart, this method must in that case be idempotent, ensuring it can handle scenarios where
170173
/// the monitor already exists in the archive.
171-
fn archive_persisted_channel(&self, channel_funding_outpoint: OutPoint);
174+
fn archive_persisted_channel(&self, monitor_name: MonitorName);
172175
}
173176

174177
struct MonitorHolder<ChannelSigner: EcdsaChannelSigner> {
@@ -342,8 +345,7 @@ where C::Target: chain::Filter,
342345
// `ChannelMonitorUpdate` after a channel persist for a channel with the same
343346
// `latest_update_id`.
344347
let _pending_monitor_updates = monitor_state.pending_monitor_updates.lock().unwrap();
345-
let funding_txo = monitor.get_funding_txo();
346-
match self.persister.update_persisted_channel(funding_txo, None, monitor) {
348+
match self.persister.update_persisted_channel(monitor.persistence_key(), None, monitor) {
347349
ChannelMonitorUpdateStatus::Completed =>
348350
log_trace!(logger, "Finished syncing Channel Monitor for channel {} for block-data",
349351
log_funding_info!(monitor)
@@ -642,7 +644,7 @@ where C::Target: chain::Filter,
642644
have_monitors_to_prune = true;
643645
}
644646
if needs_persistence {
645-
self.persister.update_persisted_channel(monitor_holder.monitor.get_funding_txo(), None, &monitor_holder.monitor);
647+
self.persister.update_persisted_channel(monitor_holder.monitor.persistence_key(), None, &monitor_holder.monitor);
646648
}
647649
}
648650
if have_monitors_to_prune {
@@ -655,7 +657,7 @@ where C::Target: chain::Filter,
655657
"Archiving fully resolved ChannelMonitor for channel ID {}",
656658
channel_id
657659
);
658-
self.persister.archive_persisted_channel(monitor_holder.monitor.get_funding_txo());
660+
self.persister.archive_persisted_channel(monitor_holder.monitor.persistence_key());
659661
false
660662
} else {
661663
true
@@ -769,7 +771,7 @@ where C::Target: chain::Filter,
769771
log_trace!(logger, "Got new ChannelMonitor for channel {}", log_funding_info!(monitor));
770772
let update_id = monitor.get_latest_update_id();
771773
let mut pending_monitor_updates = Vec::new();
772-
let persist_res = self.persister.persist_new_channel(monitor.get_funding_txo(), &monitor);
774+
let persist_res = self.persister.persist_new_channel(monitor.persistence_key(), &monitor);
773775
match persist_res {
774776
ChannelMonitorUpdateStatus::InProgress => {
775777
log_info!(logger, "Persistence of new ChannelMonitor for channel {} in progress", log_funding_info!(monitor));
@@ -825,17 +827,16 @@ where C::Target: chain::Filter,
825827
let update_res = monitor.update_monitor(update, &self.broadcaster, &self.fee_estimator, &self.logger);
826828

827829
let update_id = update.update_id;
828-
let funding_txo = monitor.get_funding_txo();
829830
let persist_res = if update_res.is_err() {
830831
// Even if updating the monitor returns an error, the monitor's state will
831832
// still be changed. Therefore, we should persist the updated monitor despite the error.
832833
// We don't want to persist a `monitor_update` which results in a failure to apply later
833834
// while reading `channel_monitor` with updates from storage. Instead, we should persist
834835
// the entire `channel_monitor` here.
835836
log_warn!(logger, "Failed to update ChannelMonitor for channel {}. Going ahead and persisting the entire ChannelMonitor", log_funding_info!(monitor));
836-
self.persister.update_persisted_channel(funding_txo, None, monitor)
837+
self.persister.update_persisted_channel(monitor.persistence_key(), None, monitor)
837838
} else {
838-
self.persister.update_persisted_channel(funding_txo, Some(update), monitor)
839+
self.persister.update_persisted_channel(monitor.persistence_key(), Some(update), monitor)
839840
};
840841
match persist_res {
841842
ChannelMonitorUpdateStatus::InProgress => {

lightning/src/chain/channelmonitor.rs

+29
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ use crate::chain::onchaintx::{ClaimEvent, FeerateStrategy, OnchainTxHandler};
4848
use crate::chain::package::{CounterpartyOfferedHTLCOutput, CounterpartyReceivedHTLCOutput, HolderFundingOutput, HolderHTLCOutput, PackageSolvingData, PackageTemplate, RevokedOutput, RevokedHTLCOutput};
4949
use crate::chain::Filter;
5050
use crate::util::logger::{Logger, Record};
51+
use crate::util::persist::MonitorName;
5152
use crate::util::ser::{Readable, ReadableArgs, RequiredWrapper, MaybeReadable, UpgradableRequired, Writer, Writeable, U48};
5253
use crate::util::byte_utils;
5354
use crate::events::{ClosureReason, Event, EventHandler, ReplayEvent};
@@ -879,6 +880,7 @@ pub(crate) struct ChannelMonitorImpl<Signer: EcdsaChannelSigner> {
879880
holder_revocation_basepoint: RevocationBasepoint,
880881
channel_id: ChannelId,
881882
funding_info: (OutPoint, ScriptBuf),
883+
first_confirmed_funding_txo: OutPoint,
882884
current_counterparty_commitment_txid: Option<Txid>,
883885
prev_counterparty_commitment_txid: Option<Txid>,
884886

@@ -1246,6 +1248,7 @@ impl<Signer: EcdsaChannelSigner> Writeable for ChannelMonitorImpl<Signer> {
12461248
(21, self.balances_empty_height, option),
12471249
(23, self.holder_pays_commitment_tx_fee, option),
12481250
(25, self.payment_preimages, required),
1251+
(27, self.first_confirmed_funding_txo, required),
12491252
});
12501253

12511254
Ok(())
@@ -1398,6 +1401,8 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitor<Signer> {
13981401
let mut outputs_to_watch = new_hash_map();
13991402
outputs_to_watch.insert(funding_info.0.txid, vec![(funding_info.0.index as u32, funding_info.1.clone())]);
14001403

1404+
let first_confirmed_funding_txo = funding_info.0;
1405+
14011406
Self::from_impl(ChannelMonitorImpl {
14021407
latest_update_id: 0,
14031408
commitment_transaction_number_obscure_factor,
@@ -1411,6 +1416,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitor<Signer> {
14111416
holder_revocation_basepoint,
14121417
channel_id,
14131418
funding_info,
1419+
first_confirmed_funding_txo,
14141420
current_counterparty_commitment_txid: None,
14151421
prev_counterparty_commitment_txid: None,
14161422

@@ -1460,6 +1466,26 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitor<Signer> {
14601466
})
14611467
}
14621468

1469+
/// Returns a unique id for persisting the [`ChannelMonitor`], which is used as a key in a
1470+
/// key-value store.
1471+
///
1472+
/// Note: Previously, the funding outpoint was used in the [`Persist`] trait. However, since the
1473+
/// outpoint may change during splicing, this method is used to obtain a unique key instead. For
1474+
/// v1 channels, the funding outpoint is still used for backwards compatibility, whereas v2
1475+
/// channels use the channel id since it is fixed.
1476+
///
1477+
/// [`Persist`]: crate::chain::chainmonitor::Persist
1478+
pub fn persistence_key(&self) -> MonitorName {
1479+
let inner = self.inner.lock().unwrap();
1480+
let funding_outpoint = inner.first_confirmed_funding_txo;
1481+
let channel_id = inner.channel_id;
1482+
if ChannelId::v1_from_funding_outpoint(funding_outpoint) == channel_id {
1483+
MonitorName::V1Channel(funding_outpoint)
1484+
} else {
1485+
MonitorName::V2Channel(channel_id)
1486+
}
1487+
}
1488+
14631489
#[cfg(test)]
14641490
fn provide_secret(&self, idx: u64, secret: [u8; 32]) -> Result<(), &'static str> {
14651491
self.inner.lock().unwrap().provide_secret(idx, secret)
@@ -5047,6 +5073,7 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP
50475073
let mut channel_id = None;
50485074
let mut holder_pays_commitment_tx_fee = None;
50495075
let mut payment_preimages_with_info: Option<HashMap<_, _>> = None;
5076+
let mut first_confirmed_funding_txo = RequiredWrapper(None);
50505077
read_tlv_fields!(reader, {
50515078
(1, funding_spend_confirmed, option),
50525079
(3, htlcs_resolved_on_chain, optional_vec),
@@ -5061,6 +5088,7 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP
50615088
(21, balances_empty_height, option),
50625089
(23, holder_pays_commitment_tx_fee, option),
50635090
(25, payment_preimages_with_info, option),
5091+
(27, first_confirmed_funding_txo, (default_value, funding_info.0)),
50645092
});
50655093
if let Some(payment_preimages_with_info) = payment_preimages_with_info {
50665094
if payment_preimages_with_info.len() != payment_preimages.len() {
@@ -5113,6 +5141,7 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP
51135141
holder_revocation_basepoint,
51145142
channel_id: channel_id.unwrap_or(ChannelId::v1_from_funding_outpoint(outpoint)),
51155143
funding_info,
5144+
first_confirmed_funding_txo: first_confirmed_funding_txo.0.unwrap(),
51165145
current_counterparty_commitment_txid,
51175146
prev_counterparty_commitment_txid,
51185147

lightning/src/ln/channel.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1965,6 +1965,8 @@ trait InitialRemoteCommitmentReceiver<SP: Deref> where SP::Target: SignerProvide
19651965
let shutdown_script = context.shutdown_scriptpubkey.clone().map(|script| script.into_inner());
19661966
let mut monitor_signer = signer_provider.derive_channel_signer(context.channel_value_satoshis, context.channel_keys_id);
19671967
monitor_signer.provide_channel_parameters(&context.channel_transaction_parameters);
1968+
// TODO(RBF): When implementing RBF, the funding_txo passed here must only update
1969+
// ChannelMonitorImp::first_confirmed_funding_txo during channel establishment, not splicing
19681970
let channel_monitor = ChannelMonitor::new(context.secp_ctx.clone(), monitor_signer,
19691971
shutdown_script, context.get_holder_selected_contest_delay(),
19701972
&context.destination_script, (funding_txo, funding_txo_script),

lightning/src/ln/dual_funding_tests.rs

+5-2
Original file line numberDiff line numberDiff line change
@@ -229,14 +229,17 @@ fn do_test_v2_channel_establishment(
229229
chanmon_cfgs[1]
230230
.persister
231231
.set_update_ret(crate::chain::ChannelMonitorUpdateStatus::Completed);
232-
let (outpoint, latest_update, _) = *nodes[1]
232+
let (latest_update, _) = *nodes[1]
233233
.chain_monitor
234234
.latest_monitor_update_id
235235
.lock()
236236
.unwrap()
237237
.get(&channel_id)
238238
.unwrap();
239-
nodes[1].chain_monitor.chain_monitor.force_channel_monitor_updated(outpoint, latest_update);
239+
nodes[1]
240+
.chain_monitor
241+
.chain_monitor
242+
.force_channel_monitor_updated(channel_id, latest_update);
240243
}
241244

242245
let events = nodes[1].node.get_and_clear_pending_events();

lightning/src/ln/functional_tests.rs

+2-3
Original file line numberDiff line numberDiff line change
@@ -2771,8 +2771,7 @@ fn do_test_forming_justice_tx_from_monitor_updates(broadcast_initial_commitment:
27712771
let node_cfgs = create_node_cfgs_with_persisters(2, &chanmon_cfgs, persisters.iter().collect());
27722772
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
27732773
let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
2774-
let (_, _, channel_id, funding_tx) = create_announced_chan_between_nodes(&nodes, 0, 1);
2775-
let funding_txo = OutPoint { txid: funding_tx.compute_txid(), index: 0 };
2774+
let (_, _, channel_id, _) = create_announced_chan_between_nodes(&nodes, 0, 1);
27762775

27772776
if !broadcast_initial_commitment {
27782777
// Send a payment to move the channel forward
@@ -2788,7 +2787,7 @@ fn do_test_forming_justice_tx_from_monitor_updates(broadcast_initial_commitment:
27882787
// Send another payment, now revoking the previous commitment tx
27892788
send_payment(&nodes[0], &vec!(&nodes[1])[..], 5_000_000);
27902789

2791-
let justice_tx = persisters[1].justice_tx(funding_txo, &revoked_commitment_tx.compute_txid()).unwrap();
2790+
let justice_tx = persisters[1].justice_tx(channel_id, &revoked_commitment_tx.compute_txid()).unwrap();
27922791
check_spends!(justice_tx, revoked_commitment_tx);
27932792

27942793
mine_transactions(&nodes[1], &[revoked_commitment_tx, &justice_tx]);

0 commit comments

Comments
 (0)