Skip to content

persist: Persist ChannelMonitors in their own directory. #806

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 33 additions & 25 deletions lightning-persister/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ use lightning::util::logger::Logger;
use lightning::util::ser::Writeable;
use std::fs;
use std::io::Error;
use std::path::PathBuf;
use std::sync::Arc;

#[cfg(test)]
Expand Down Expand Up @@ -75,6 +76,12 @@ impl FilesystemPersister {
self.path_to_channel_data.clone()
}

pub(crate) fn path_to_monitor_data(&self) -> PathBuf {
let mut path = PathBuf::from(self.path_to_channel_data.clone());
path.push("monitors");
path
}

/// Writes the provided `ChannelManager` to the path provided at `FilesystemPersister`
/// initialization, within a file called "manager".
pub fn persist_manager<Signer, M, T, K, F, L>(
Expand All @@ -88,54 +95,55 @@ impl FilesystemPersister {
F: FeeEstimator,
L: Logger
{
util::write_to_file(data_dir, "manager".to_string(), manager)
let path = PathBuf::from(data_dir);
util::write_to_file(path, "manager".to_string(), manager)
}

#[cfg(test)]
fn load_channel_data<Keys: KeysInterface>(&self, keys: &Keys) ->
Result<HashMap<OutPoint, ChannelMonitor<Keys::Signer>>, ChannelMonitorUpdateErr> {
if let Err(_) = fs::create_dir_all(&self.path_to_channel_data) {
return Err(ChannelMonitorUpdateErr::PermanentFailure);
}
let mut res = HashMap::new();
for file_option in fs::read_dir(&self.path_to_channel_data).unwrap() {
let file = file_option.unwrap();
let owned_file_name = file.file_name();
let filename = owned_file_name.to_str();
if !filename.is_some() || !filename.unwrap().is_ascii() || filename.unwrap().len() < 65 {
if let Err(_) = fs::create_dir_all(self.path_to_monitor_data()) {
return Err(ChannelMonitorUpdateErr::PermanentFailure);
}
let mut res = HashMap::new();
for file_option in fs::read_dir(self.path_to_monitor_data()).unwrap() {
let file = file_option.unwrap();
let owned_file_name = file.file_name();
let filename = owned_file_name.to_str();
if !filename.is_some() || !filename.unwrap().is_ascii() || filename.unwrap().len() < 65 {
return Err(ChannelMonitorUpdateErr::PermanentFailure);
}

let txid = Txid::from_hex(filename.unwrap().split_at(64).0);
if txid.is_err() { return Err(ChannelMonitorUpdateErr::PermanentFailure); }
let txid = Txid::from_hex(filename.unwrap().split_at(64).0);
if txid.is_err() { return Err(ChannelMonitorUpdateErr::PermanentFailure); }

let index = filename.unwrap().split_at(65).1.split('.').next().unwrap().parse();
if index.is_err() { return Err(ChannelMonitorUpdateErr::PermanentFailure); }
let index = filename.unwrap().split_at(65).1.split('.').next().unwrap().parse();
if index.is_err() { return Err(ChannelMonitorUpdateErr::PermanentFailure); }

let contents = fs::read(&file.path());
if contents.is_err() { return Err(ChannelMonitorUpdateErr::PermanentFailure); }
let contents = fs::read(&file.path());
if contents.is_err() { return Err(ChannelMonitorUpdateErr::PermanentFailure); }

if let Ok((_, loaded_monitor)) =
<(BlockHash, ChannelMonitor<Keys::Signer>)>::read(&mut Cursor::new(&contents.unwrap()), keys) {
res.insert(OutPoint { txid: txid.unwrap(), index: index.unwrap() }, loaded_monitor);
} else {
return Err(ChannelMonitorUpdateErr::PermanentFailure);
if let Ok((_, loaded_monitor)) =
<(BlockHash, ChannelMonitor<Keys::Signer>)>::read(&mut Cursor::new(&contents.unwrap()), keys) {
res.insert(OutPoint { txid: txid.unwrap(), index: index.unwrap() }, loaded_monitor);
} else {
return Err(ChannelMonitorUpdateErr::PermanentFailure);
}
}
Ok(res)
}
Ok(res)
}
}
Comment on lines 104 to 135
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indentation looks off by one. I've started following these conventions when wrapping long method signatures:

https://github.com/rust-dev-tools/fmt-rfcs/blob/master/guide/items.md

With an extra line for the opening brace, you can reduce the indent to how it was before.


impl<ChannelSigner: Sign + Send + Sync> channelmonitor::Persist<ChannelSigner> for FilesystemPersister {
fn persist_new_channel(&self, funding_txo: OutPoint, monitor: &ChannelMonitor<ChannelSigner>) -> Result<(), ChannelMonitorUpdateErr> {
let filename = format!("{}_{}", funding_txo.txid.to_hex(), funding_txo.index);
util::write_to_file(self.path_to_channel_data.clone(), filename, monitor)
util::write_to_file(self.path_to_monitor_data(), filename, monitor)
.map_err(|_| ChannelMonitorUpdateErr::PermanentFailure)
}

fn update_persisted_channel(&self, funding_txo: OutPoint, _update: &ChannelMonitorUpdate, monitor: &ChannelMonitor<ChannelSigner>) -> Result<(), ChannelMonitorUpdateErr> {
let filename = format!("{}_{}", funding_txo.txid.to_hex(), funding_txo.index);
util::write_to_file(self.path_to_channel_data.clone(), filename, monitor)
util::write_to_file(self.path_to_monitor_data(), filename, monitor)
.map_err(|_| ChannelMonitorUpdateErr::PermanentFailure)
}
}
Expand Down
24 changes: 12 additions & 12 deletions lightning-persister/src/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,9 @@ pub(crate) trait DiskWriteable {
fn write_to_file(&self, writer: &mut fs::File) -> Result<(), std::io::Error>;
}

pub(crate) fn get_full_filepath(filepath: String, filename: String) -> String {
let mut path = PathBuf::from(filepath);
path.push(filename);
path.to_str().unwrap().to_string()
pub(crate) fn get_full_filepath(mut filepath: PathBuf, filename: String) -> String {
filepath.push(filename);
filepath.to_str().unwrap().to_string()
}

#[cfg(target_os = "windows")]
Expand All @@ -40,7 +39,7 @@ fn path_to_windows_str<T: AsRef<OsStr>>(path: T) -> Vec<winapi::shared::ntdef::W
}

#[allow(bare_trait_objects)]
pub(crate) fn write_to_file<D: DiskWriteable>(path: String, filename: String, data: &D) -> std::io::Result<()> {
pub(crate) fn write_to_file<D: DiskWriteable>(path: PathBuf, filename: String, data: &D) -> std::io::Result<()> {
fs::create_dir_all(path.clone())?;
// Do a crazy dance with lots of fsync()s to be overly cautious here...
// We never want to end up in a state where we've lost the old data, or end up using the
Expand Down Expand Up @@ -92,6 +91,7 @@ mod tests {
use std::fs;
use std::io;
use std::io::Write;
use std::path::PathBuf;

struct TestWriteable{}
impl DiskWriteable for TestWriteable {
Expand All @@ -113,7 +113,7 @@ mod tests {
let mut perms = fs::metadata(path.to_string()).unwrap().permissions();
perms.set_readonly(true);
fs::set_permissions(path.to_string(), perms).unwrap();
match write_to_file(path.to_string(), filename, &test_writeable) {
match write_to_file(PathBuf::from(path.to_string()), filename, &test_writeable) {
Err(e) => assert_eq!(e.kind(), io::ErrorKind::PermissionDenied),
_ => panic!("Unexpected error message")
}
Expand All @@ -131,10 +131,10 @@ mod tests {
fn test_rename_failure() {
let test_writeable = TestWriteable{};
let filename = "test_rename_failure_filename";
let path = "test_rename_failure_dir";
let path = PathBuf::from("test_rename_failure_dir");
// Create the channel data file and make it a directory.
fs::create_dir_all(get_full_filepath(path.to_string(), filename.to_string())).unwrap();
match write_to_file(path.to_string(), filename.to_string(), &test_writeable) {
fs::create_dir_all(get_full_filepath(path.clone(), filename.to_string())).unwrap();
match write_to_file(path.clone(), filename.to_string(), &test_writeable) {
Err(e) => assert_eq!(e.kind(), io::ErrorKind::Other),
_ => panic!("Unexpected Ok(())")
}
Expand All @@ -151,9 +151,9 @@ mod tests {
}

let filename = "test_diskwriteable_failure";
let path = "test_diskwriteable_failure_dir";
let path = PathBuf::from("test_diskwriteable_failure_dir");
let test_writeable = FailingWriteable{};
match write_to_file(path.to_string(), filename.to_string(), &test_writeable) {
match write_to_file(path.clone(), filename.to_string(), &test_writeable) {
Err(e) => {
assert_eq!(e.kind(), std::io::ErrorKind::Other);
assert_eq!(e.get_ref().unwrap().to_string(), "expected failure");
Expand All @@ -170,7 +170,7 @@ mod tests {
fn test_tmp_file_creation_failure() {
let test_writeable = TestWriteable{};
let filename = "test_tmp_file_creation_failure_filename".to_string();
let path = "test_tmp_file_creation_failure_dir".to_string();
let path = PathBuf::from("test_tmp_file_creation_failure_dir");

// Create the tmp file and make it a directory.
let tmp_path = get_full_filepath(path.clone(), format!("{}.tmp", filename.clone()));
Expand Down