Skip to content

Commit 02ebbb5

Browse files
committed
Support parsing persisted v2 channel monitor names
ChannelMonitors are persisted using the corresponding channel's funding outpoint for the key. This is fine for v1 channels since the funding output will never change. However, this is not the case for v2 channels since a splice will result in a new funding outpoint. Support parsing a MonitorName for v2 channels as a ChannelId, which still looks like a outpoint only without an index. This allows differentiating monitors for v1 channels from those of v2 channels.
1 parent 0dfce09 commit 02ebbb5

File tree

1 file changed

+112
-32
lines changed

1 file changed

+112
-32
lines changed

lightning/src/util/persist.rs

Lines changed: 112 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
//!
1111
//! [`ChannelManager`]: crate::ln::channelmanager::ChannelManager
1212
13+
use bitcoin::hashes::hex::FromHex;
1314
use bitcoin::{BlockHash, Txid};
1415
use core::cmp;
1516
use core::ops::Deref;
@@ -24,6 +25,7 @@ use crate::chain::chainmonitor::Persist;
2425
use crate::chain::channelmonitor::{ChannelMonitor, ChannelMonitorUpdate};
2526
use crate::chain::transaction::OutPoint;
2627
use crate::ln::channelmanager::AChannelManager;
28+
use crate::ln::types::ChannelId;
2729
use crate::routing::gossip::NetworkGraph;
2830
use crate::routing::scoring::WriteableScore;
2931
use crate::sign::{ecdsa::EcdsaChannelSigner, EntropySource, SignerProvider};
@@ -896,9 +898,10 @@ where
896898
/// A struct representing a name for a channel monitor.
897899
///
898900
/// `MonitorName` is primarily used within the [`MonitorUpdatingPersister`]
899-
/// in functions that store or retrieve channel monitor snapshots.
901+
/// in functions that store or retrieve [`ChannelMonitor`] snapshots.
900902
/// It provides a consistent way to generate a unique key for channel
901-
/// monitors based on their funding outpoints.
903+
/// monitors based on the channel's funding [`OutPoint`] for v1 channels or
904+
/// [`ChannelId`] for v2 channels.
902905
///
903906
/// While users of the Lightning Dev Kit library generally won't need
904907
/// to interact with [`MonitorName`] directly, it can be useful for:
@@ -912,30 +915,49 @@ where
912915
/// use std::str::FromStr;
913916
///
914917
/// use bitcoin::Txid;
918+
/// use bitcoin::hashes::hex::FromHex;
915919
///
916920
/// use lightning::util::persist::MonitorName;
917921
/// use lightning::chain::transaction::OutPoint;
922+
/// use lightning::ln::types::ChannelId;
918923
///
924+
/// // v1 channel
919925
/// let outpoint = OutPoint {
920926
/// txid: Txid::from_str("deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef").unwrap(),
921927
/// index: 1,
922928
/// };
923929
/// let monitor_name = MonitorName::from(outpoint);
924930
/// assert_eq!(monitor_name.as_str(), "deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef_1");
925931
///
932+
/// // v2 channel
933+
/// let channel_id = ChannelId(<[u8; 32]>::from_hex("deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef").unwrap());
934+
/// let monitor_name = MonitorName::from(channel_id);
935+
/// assert_eq!(monitor_name.as_str(), "deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef");
936+
///
926937
/// // Using MonitorName to generate a storage key
927938
/// let storage_key = format!("channel_monitors/{}", monitor_name.as_str());
928939
/// ```
929940
#[derive(Debug)]
930941
pub struct MonitorName(String);
931942

943+
/// The source of the [`MonitorName`], either an [`OutPoint`] for V1 channels or a [`ChannelId`] for
944+
/// V2 channels. The former uses the channel's funding outpoint for historical reasons. The latter
945+
/// uses the channel's id because the funding outpoint may change during splicing.
946+
pub enum MonitorNameSource {
947+
/// The outpoint of the channel's funding transaction.
948+
V1Channel(OutPoint),
949+
950+
/// The id of the channel produced by [`ChannelId::v2_from_revocation_basepoints`].
951+
V2Channel(ChannelId),
952+
}
953+
932954
impl MonitorName {
933955
/// Constructs a [`MonitorName`], after verifying that an [`OutPoint`] can
934956
/// be formed from the given `name`.
935957
/// This method is useful if you have a String and you want to verify that
936958
/// it's a valid storage key for a channel monitor.
937959
pub fn new(name: String) -> Result<Self, io::Error> {
938-
MonitorName::do_try_into_outpoint(&name)?;
960+
MonitorNameSource::try_from(name.as_str())?;
939961
Ok(Self(name))
940962
}
941963

@@ -945,31 +967,48 @@ impl MonitorName {
945967
pub fn as_str(&self) -> &str {
946968
&self.0
947969
}
970+
}
971+
972+
impl TryFrom<&str> for MonitorNameSource {
973+
type Error = io::Error;
948974

949-
/// Attempt to form a valid [`OutPoint`] from a given name string.
950-
fn do_try_into_outpoint(name: &str) -> Result<OutPoint, io::Error> {
975+
/// Attempts to convert a `MonitorName` back into an `OutPoint` or `ChannelId`.
976+
///
977+
/// This is useful when you have a `MonitorName` (perhaps retrieved from storage)
978+
/// and need to reconstruct the original data it represents.
979+
fn try_from(name: &str) -> Result<Self, io::Error> {
951980
let mut parts = name.splitn(2, '_');
952-
let txid = if let Some(part) = parts.next() {
953-
Txid::from_str(part).map_err(|_| {
981+
let id = parts
982+
.next()
983+
.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "Empty stored key"))?;
984+
985+
if let Some(part) = parts.next() {
986+
let txid = Txid::from_str(id).map_err(|_| {
954987
io::Error::new(io::ErrorKind::InvalidData, "Invalid tx ID in stored key")
955-
})?
956-
} else {
957-
return Err(io::Error::new(
958-
io::ErrorKind::InvalidData,
959-
"Stored monitor key is not a splittable string",
960-
));
961-
};
962-
let index = if let Some(part) = parts.next() {
963-
part.parse().map_err(|_| {
988+
})?;
989+
let index: u16 = part.parse().map_err(|_| {
964990
io::Error::new(io::ErrorKind::InvalidData, "Invalid tx index in stored key")
965-
})?
991+
})?;
992+
let outpoint = OutPoint { txid, index };
993+
Ok(MonitorNameSource::V1Channel(outpoint))
966994
} else {
967-
return Err(io::Error::new(
968-
io::ErrorKind::InvalidData,
969-
"No tx index value found after underscore in stored key",
970-
));
971-
};
972-
Ok(OutPoint { txid, index })
995+
let bytes = <[u8; 32]>::from_hex(id).map_err(|_| {
996+
io::Error::new(io::ErrorKind::InvalidData, "Invalid channel ID in stored key")
997+
})?;
998+
Ok(MonitorNameSource::V2Channel(ChannelId(bytes)))
999+
}
1000+
}
1001+
}
1002+
1003+
impl TryFrom<&MonitorName> for MonitorNameSource {
1004+
type Error = io::Error;
1005+
1006+
/// Attempts to convert a `MonitorName` back into an `OutPoint` or `ChannelId`.
1007+
///
1008+
/// This is useful when you have a `MonitorName` (perhaps retrieved from storage)
1009+
/// and need to reconstruct the original data it represents.
1010+
fn try_from(value: &MonitorName) -> Result<Self, io::Error> {
1011+
MonitorNameSource::try_from(value.0.as_str())
9731012
}
9741013
}
9751014

@@ -981,20 +1020,34 @@ impl TryFrom<&MonitorName> for OutPoint {
9811020
/// This is useful when you have a `MonitorName` (perhaps retrieved from storage)
9821021
/// and need to reconstruct the original `OutPoint` it represents.
9831022
fn try_from(value: &MonitorName) -> Result<Self, io::Error> {
984-
MonitorName::do_try_into_outpoint(&value.0)
1023+
if let MonitorNameSource::V1Channel(outpoint) = MonitorNameSource::try_from(value)? {
1024+
Ok(outpoint)
1025+
} else {
1026+
Err(io::Error::new(io::ErrorKind::InvalidData, "Invalid outpoint in stored key"))
1027+
}
9851028
}
9861029
}
9871030

9881031
impl From<OutPoint> for MonitorName {
9891032
/// Creates a `MonitorName` from an `OutPoint`.
9901033
///
9911034
/// This is typically used when you need to generate a storage key or identifier
992-
/// for a new or existing channel monitor.
1035+
/// for a new or existing channel monitor for a v1 channel.
9931036
fn from(value: OutPoint) -> Self {
9941037
MonitorName(format!("{}_{}", value.txid.to_string(), value.index))
9951038
}
9961039
}
9971040

1041+
impl From<ChannelId> for MonitorName {
1042+
/// Creates a `MonitorName` from a `ChannelId`.
1043+
///
1044+
/// This is typically used when you need to generate a storage key or identifier
1045+
/// for a new or existing channel monitor for a v2 channel.
1046+
fn from(id: ChannelId) -> Self {
1047+
MonitorName(id.to_string())
1048+
}
1049+
}
1050+
9981051
/// A struct representing a name for a channel monitor update.
9991052
///
10001053
/// [`UpdateName`] is primarily used within the [`MonitorUpdatingPersister`] in
@@ -1092,6 +1145,7 @@ mod tests {
10921145
use crate::util::test_channel_signer::TestChannelSigner;
10931146
use crate::util::test_utils::{self, TestLogger, TestStore};
10941147
use crate::{check_added_monitors, check_closed_broadcast};
1148+
use bitcoin::hashes::hex::FromHex;
10951149

10961150
const EXPECTED_UPDATES_PER_PAYMENT: u64 = 5;
10971151

@@ -1109,36 +1163,62 @@ mod tests {
11091163
}
11101164

11111165
#[test]
1112-
fn monitor_from_outpoint_works() {
1113-
let monitor_name1 = MonitorName::from(OutPoint {
1166+
fn creates_monitor_from_outpoint() {
1167+
let monitor_name = MonitorName::from(OutPoint {
11141168
txid: Txid::from_str(
11151169
"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef",
11161170
)
11171171
.unwrap(),
11181172
index: 1,
11191173
});
11201174
assert_eq!(
1121-
monitor_name1.as_str(),
1175+
monitor_name.as_str(),
11221176
"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef_1"
11231177
);
1178+
assert!(matches!(
1179+
MonitorNameSource::try_from(&monitor_name),
1180+
Ok(MonitorNameSource::V1Channel(_))
1181+
));
11241182

1125-
let monitor_name2 = MonitorName::from(OutPoint {
1183+
let monitor_name = MonitorName::from(OutPoint {
11261184
txid: Txid::from_str(
11271185
"f33dbeeff33dbeeff33dbeeff33dbeeff33dbeeff33dbeeff33dbeeff33dbeef",
11281186
)
11291187
.unwrap(),
11301188
index: u16::MAX,
11311189
});
11321190
assert_eq!(
1133-
monitor_name2.as_str(),
1191+
monitor_name.as_str(),
11341192
"f33dbeeff33dbeeff33dbeeff33dbeeff33dbeeff33dbeeff33dbeeff33dbeef_65535"
11351193
);
1194+
assert!(matches!(
1195+
MonitorNameSource::try_from(&monitor_name),
1196+
Ok(MonitorNameSource::V1Channel(_))
1197+
));
1198+
}
1199+
1200+
#[test]
1201+
fn creates_monitor_from_channel_id() {
1202+
let monitor_name = MonitorName::from(ChannelId(
1203+
<[u8; 32]>::from_hex(
1204+
"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef",
1205+
)
1206+
.unwrap(),
1207+
));
1208+
assert_eq!(
1209+
monitor_name.as_str(),
1210+
"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"
1211+
);
1212+
assert!(matches!(
1213+
MonitorNameSource::try_from(&monitor_name),
1214+
Ok(MonitorNameSource::V2Channel(_))
1215+
));
11361216
}
11371217

11381218
#[test]
1139-
fn bad_monitor_string_fails() {
1219+
fn fails_parsing_monitor_name() {
11401220
assert!(MonitorName::new(
1141-
"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef".to_string()
1221+
"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef_".to_string()
11421222
)
11431223
.is_err());
11441224
assert!(MonitorName::new(

0 commit comments

Comments
 (0)