Skip to content

Commit 324fbd5

Browse files
implement Persist and Persister with generic PersistObject trait
1 parent 711bcef commit 324fbd5

File tree

3 files changed

+77
-92
lines changed

3 files changed

+77
-92
lines changed

lightning-background-processor/src/lib.rs

+2-16
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ use lightning::ln::peer_handler::{CustomMessageHandler, PeerManager, SocketDescr
2020
use lightning::routing::network_graph::{NetworkGraph, NetGraphMsgHandler};
2121
use lightning::util::events::{Event, EventHandler, EventsProvider};
2222
use lightning::util::logger::Logger;
23+
use lightning_persister::Persister;
2324
use std::sync::Arc;
2425
use std::sync::atomic::{AtomicBool, Ordering};
2526
use std::thread;
@@ -80,22 +81,7 @@ const FIRST_NETWORK_PRUNE_TIMER: u64 = 60;
8081
#[cfg(test)]
8182
const FIRST_NETWORK_PRUNE_TIMER: u64 = 1;
8283

83-
/// Trait that handles persisting a [`ChannelManager`] and [`NetworkGraph`] to disk.
84-
pub trait Persister<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref>
85-
where
86-
M::Target: 'static + chain::Watch<Signer>,
87-
T::Target: 'static + BroadcasterInterface,
88-
K::Target: 'static + KeysInterface<Signer = Signer>,
89-
F::Target: 'static + FeeEstimator,
90-
L::Target: 'static + Logger,
91-
{
92-
/// Persist the given [`ChannelManager`] to disk, returning an error if persistence failed
93-
/// (which will cause the [`BackgroundProcessor`] which called this method to exit).
94-
fn persist_manager(&self, channel_manager: &ChannelManager<Signer, M, T, K, F, L>) -> Result<(), std::io::Error>;
95-
96-
/// Persist the given [`NetworkGraph`] to disk, returning an error if persistence failed.
97-
fn persist_graph(&self, network_graph: &NetworkGraph) -> Result<(), std::io::Error>;
98-
}
84+
9985

10086
/// Decorates an [`EventHandler`] with common functionality provided by standard [`EventHandler`]s.
10187
struct DecoratingEventHandler<

lightning-persister/src/lib.rs

+62-60
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ extern crate libc;
1717
use bitcoin::hash_types::{BlockHash, Txid};
1818
use bitcoin::hashes::hex::{FromHex, ToHex};
1919
use lightning::routing::network_graph::NetworkGraph;
20-
use crate::util::DiskWriteable;
2120
use lightning::chain;
2221
use lightning::chain::chaininterface::{BroadcasterInterface, FeeEstimator};
2322
use lightning::chain::channelmonitor::{ChannelMonitor, ChannelMonitorUpdate};
@@ -28,10 +27,39 @@ use lightning::ln::channelmanager::ChannelManager;
2827
use lightning::util::logger::Logger;
2928
use lightning::util::ser::{ReadableArgs, Writeable};
3029
use std::fs;
31-
use std::io::{Cursor, Error};
30+
use std::io::{Cursor, Error, Write};
3231
use std::ops::Deref;
3332
use std::path::{Path, PathBuf};
3433

34+
/// Trait that handles persisting a [`ChannelManager`] and [`NetworkGraph`] to disk.
35+
pub trait Persister<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref>
36+
where
37+
M::Target: 'static + chain::Watch<Signer>,
38+
T::Target: 'static + BroadcasterInterface,
39+
K::Target: 'static + KeysInterface<Signer = Signer>,
40+
F::Target: 'static + FeeEstimator,
41+
L::Target: 'static + Logger,
42+
{
43+
/// Persist the given [`ChannelManager`] to disk, returning an error if persistence failed
44+
/// (which will cause the BackgroundProcessor which called this method to exit).
45+
fn persist_manager(&self, channel_manager: &ChannelManager<Signer, M, T, K, F, L>) -> Result<(), std::io::Error>;
46+
47+
/// Persist the given [`NetworkGraph`] to disk, returning an error if persistence failed.
48+
fn persist_graph(&self, network_graph: &NetworkGraph) -> Result<(), std::io::Error>;
49+
}
50+
51+
/// Trait for a key-value store for persisting some writeable object at some key
52+
pub trait PersistObject<W: Writeable> {
53+
/// Persist the given writeable using the provided key
54+
fn persist_object(&self, key: String, object: &W) -> std::io::Result<()>;
55+
}
56+
57+
impl<W: Writeable> PersistObject<W> for FilesystemPersister {
58+
fn persist_object(&self, key: String, object: &W) -> std::io::Result<()> {
59+
util::write_to_file(key, object)
60+
}
61+
}
62+
3563
/// FilesystemPersister persists channel data on disk, where each channel's
3664
/// data is stored in a file named after its funding outpoint.
3765
///
@@ -48,31 +76,6 @@ pub struct FilesystemPersister {
4876
path_to_channel_data: String,
4977
}
5078

51-
impl<Signer: Sign> DiskWriteable for ChannelMonitor<Signer> {
52-
fn write_to_file(&self, writer: &mut fs::File) -> Result<(), Error> {
53-
self.write(writer)
54-
}
55-
}
56-
57-
impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> DiskWriteable for ChannelManager<Signer, M, T, K, F, L>
58-
where
59-
M::Target: chain::Watch<Signer>,
60-
T::Target: BroadcasterInterface,
61-
K::Target: KeysInterface<Signer=Signer>,
62-
F::Target: FeeEstimator,
63-
L::Target: Logger,
64-
{
65-
fn write_to_file(&self, writer: &mut fs::File) -> Result<(), std::io::Error> {
66-
self.write(writer)
67-
}
68-
}
69-
70-
impl DiskWriteable for NetworkGraph {
71-
fn write_to_file(&self, writer: &mut fs::File) -> Result<(), std::io::Error> {
72-
self.write(writer)
73-
}
74-
}
75-
7679
impl FilesystemPersister {
7780
/// Initialize a new FilesystemPersister and set the path to the individual channels'
7881
/// files.
@@ -87,34 +90,8 @@ impl FilesystemPersister {
8790
self.path_to_channel_data.clone()
8891
}
8992

90-
pub(crate) fn path_to_monitor_data(&self) -> PathBuf {
91-
let mut path = PathBuf::from(self.path_to_channel_data.clone());
92-
path.push("monitors");
93-
path
94-
}
95-
96-
/// Writes the provided `ChannelManager` to the path provided at `FilesystemPersister`
97-
/// initialization, within a file called "manager".
98-
pub fn persist_manager<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref>(
99-
data_dir: String,
100-
manager: &ChannelManager<Signer, M, T, K, F, L>
101-
) -> Result<(), std::io::Error>
102-
where
103-
M::Target: chain::Watch<Signer>,
104-
T::Target: BroadcasterInterface,
105-
K::Target: KeysInterface<Signer=Signer>,
106-
F::Target: FeeEstimator,
107-
L::Target: Logger,
108-
{
109-
let path = PathBuf::from(data_dir);
110-
util::write_to_file(path, "manager".to_string(), manager)
111-
}
112-
113-
/// Write the provided `NetworkGraph` to the path provided at `FilesystemPersister`
114-
/// initialization, within a file called "network_graph"
115-
pub fn persist_network_graph(data_dir: String, network_graph: &NetworkGraph) -> Result<(), std::io::Error> {
116-
let path = PathBuf::from(data_dir);
117-
util::write_to_file(path, "network_graph".to_string(), network_graph)
93+
pub(crate) fn path_to_monitor_data(&self) -> String {
94+
format!("{}/monitors", self.path_to_channel_data)
11895
}
11996

12097
/// Read `ChannelMonitor`s from disk.
@@ -124,7 +101,7 @@ impl FilesystemPersister {
124101
where K::Target: KeysInterface<Signer=Signer> + Sized,
125102
{
126103
let path = self.path_to_monitor_data();
127-
if !Path::new(&path).exists() {
104+
if !Path::new(&PathBuf::from(path.clone())).exists() {
128105
return Ok(Vec::new());
129106
}
130107
let mut res = Vec::new();
@@ -187,18 +164,43 @@ impl<ChannelSigner: Sign> chainmonitor::Persist<ChannelSigner> for FilesystemPer
187164
// even broadcasting!
188165

189166
fn persist_new_channel(&self, funding_txo: OutPoint, monitor: &ChannelMonitor<ChannelSigner>, _update_id: chainmonitor::MonitorUpdateId) -> Result<(), chain::ChannelMonitorUpdateErr> {
190-
let filename = format!("{}_{}", funding_txo.txid.to_hex(), funding_txo.index);
191-
util::write_to_file(self.path_to_monitor_data(), filename, monitor)
167+
let key = format!("{}/{}_{}", self.path_to_monitor_data(), funding_txo.txid.to_hex(), funding_txo.index);
168+
self.persist_object(key, monitor)
192169
.map_err(|_| chain::ChannelMonitorUpdateErr::PermanentFailure)
193170
}
194171

195172
fn update_persisted_channel(&self, funding_txo: OutPoint, _update: &Option<ChannelMonitorUpdate>, monitor: &ChannelMonitor<ChannelSigner>, _update_id: chainmonitor::MonitorUpdateId) -> Result<(), chain::ChannelMonitorUpdateErr> {
196-
let filename = format!("{}_{}", funding_txo.txid.to_hex(), funding_txo.index);
197-
util::write_to_file(self.path_to_monitor_data(), filename, monitor)
173+
let key = format!("{}/{}_{}", self.path_to_monitor_data(), funding_txo.txid.to_hex(), funding_txo.index);
174+
self.persist_object(key, monitor)
198175
.map_err(|_| chain::ChannelMonitorUpdateErr::PermanentFailure)
199176
}
200177
}
201178

179+
impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> Persister<Signer, M, T, K, F, L> for FilesystemPersister
180+
where
181+
M::Target: 'static + chain::Watch<Signer>,
182+
T::Target: 'static + BroadcasterInterface,
183+
K::Target: 'static + KeysInterface<Signer=Signer>,
184+
F::Target: 'static + FeeEstimator,
185+
L::Target: 'static + Logger,
186+
{
187+
fn persist_manager(&self, channel_manager: &ChannelManager<Signer, M, T, K, F, L>) -> Result<(), std::io::Error> {
188+
let key = format!("{}/manager", self.get_data_dir());
189+
self.persist_object(key, channel_manager)
190+
}
191+
192+
fn persist_graph(&self, network_graph: &NetworkGraph) -> Result<(), std::io::Error> {
193+
let key = format!("{}/network_graph", self.get_data_dir());
194+
if self.persist_object(key, network_graph).is_err()
195+
{
196+
// Persistence errors here are non-fatal as we can just fetch the routing graph
197+
// again later, but they may indicate a disk error which could be fatal elsewhere.
198+
eprintln!("Warning: Failed to persist network graph, check your disk and permissions");
199+
}
200+
Ok(())
201+
}
202+
}
203+
202204
#[cfg(test)]
203205
mod tests {
204206
extern crate lightning;

lightning-persister/src/util.rs

+13-16
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,14 @@ use std::path::{Path, PathBuf};
77
#[cfg(not(target_os = "windows"))]
88
use std::os::unix::io::AsRawFd;
99

10+
use lightning::util::ser::Writeable;
11+
1012
#[cfg(target_os = "windows")]
1113
use {
1214
std::ffi::OsStr,
1315
std::os::windows::ffi::OsStrExt
1416
};
1517

16-
pub(crate) trait DiskWriteable {
17-
fn write_to_file(&self, writer: &mut fs::File) -> Result<(), std::io::Error>;
18-
}
19-
20-
pub(crate) fn get_full_filepath(mut filepath: PathBuf, filename: String) -> String {
21-
filepath.push(filename);
22-
filepath.to_str().unwrap().to_string()
23-
}
24-
2518
#[cfg(target_os = "windows")]
2619
macro_rules! call {
2720
($e: expr) => (
@@ -39,21 +32,23 @@ fn path_to_windows_str<T: AsRef<OsStr>>(path: T) -> Vec<winapi::shared::ntdef::W
3932
}
4033

4134
#[allow(bare_trait_objects)]
42-
pub(crate) fn write_to_file<D: DiskWriteable>(path: PathBuf, filename: String, data: &D) -> std::io::Result<()> {
43-
fs::create_dir_all(path.clone())?;
35+
pub(crate) fn write_to_file<W: Writeable>(filename_with_path: String, data: &W) -> std::io::Result<()> {
36+
let mut path_parts: Vec<&str> = filename_with_path.split("/").collect();
37+
let _filename = path_parts.pop();
38+
let path = path_parts.join("/");
39+
fs::create_dir_all(path)?;
4440
// Do a crazy dance with lots of fsync()s to be overly cautious here...
4541
// We never want to end up in a state where we've lost the old data, or end up using the
4642
// old data on power loss after we've returned.
4743
// The way to atomically write a file on Unix platforms is:
4844
// open(tmpname), write(tmpfile), fsync(tmpfile), close(tmpfile), rename(), fsync(dir)
49-
let filename_with_path = get_full_filepath(path, filename);
5045
let tmp_filename = format!("{}.tmp", filename_with_path.clone());
5146

5247
{
5348
// Note that going by rust-lang/rust@d602a6b, on MacOS it is only safe to use
5449
// rust stdlib 1.36 or higher.
5550
let mut f = fs::File::create(&tmp_filename)?;
56-
data.write_to_file(&mut f)?;
51+
data.write(&mut f)?;
5752
f.sync_all()?;
5853
}
5954
// Fsync the parent directory on Unix.
@@ -87,15 +82,17 @@ pub(crate) fn write_to_file<D: DiskWriteable>(path: PathBuf, filename: String, d
8782

8883
#[cfg(test)]
8984
mod tests {
90-
use super::{DiskWriteable, get_full_filepath, write_to_file};
85+
use lightning::util::ser::Writeable;
86+
87+
use super::{write_to_file};
9188
use std::fs;
9289
use std::io;
9390
use std::io::Write;
9491
use std::path::PathBuf;
9592

9693
struct TestWriteable{}
97-
impl DiskWriteable for TestWriteable {
98-
fn write_to_file(&self, writer: &mut fs::File) -> Result<(), io::Error> {
94+
impl Writeable for TestWriteable {
95+
fn write(&self, writer: &mut fs::File) -> Result<(), io::Error> {
9996
writer.write_all(&[42; 1])
10097
}
10198
}

0 commit comments

Comments
 (0)