Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add ChannelDataPersister trait and point SMCM to it.
Intended to be a simple way for users to know where and how to put their backup and persistence logic.
- Loading branch information
1 parent
3defcc8
commit 595db8a
Showing
14 changed files
with
304 additions
and
55 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,3 +8,4 @@ | |
// licenses. | ||
|
||
pub mod test_logger; | ||
pub mod test_data_persister; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
use lightning::ln::data_persister::ChannelDataPersister; | ||
use lightning::ln::channelmonitor; | ||
use lightning::chain::transaction::OutPoint; | ||
use lightning::util::enforcing_trait_impls::EnforcingChannelKeys; | ||
|
||
use std::collections::HashMap; | ||
|
||
pub struct TestChanDataPersister {} | ||
impl ChannelDataPersister for TestChanDataPersister { | ||
type Keys = EnforcingChannelKeys; | ||
|
||
fn persist_channel_data(&self, _funding_txo: OutPoint, _data: &channelmonitor::ChannelMonitor<EnforcingChannelKeys>) -> Result<(), channelmonitor::ChannelMonitorUpdateErr> { | ||
Ok(()) | ||
} | ||
|
||
fn update_channel_data(&self, _funding_txo: OutPoint, _update: &channelmonitor::ChannelMonitorUpdate, _data: &channelmonitor::ChannelMonitor<EnforcingChannelKeys>) -> Result<(), channelmonitor::ChannelMonitorUpdateErr> { | ||
Ok(()) | ||
} | ||
|
||
fn load_channel_data(&self) -> Result<HashMap<OutPoint, channelmonitor::ChannelMonitor<EnforcingChannelKeys>>, channelmonitor::ChannelMonitorUpdateErr> { | ||
Ok(HashMap::new()) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
[package] | ||
name = "lightning-data-persister" | ||
version = "0.0.1" | ||
authors = ["Valentine Wallace"] | ||
license = "Apache-2.0" | ||
edition = "2018" | ||
description = """ | ||
Utilities to manage channel data persistence and retrieval. | ||
""" | ||
|
||
[dependencies] | ||
bitcoin = "0.23" | ||
lightning = { version = "0.0.11", path = "../lightning" } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
use lightning::chain::keysinterface::ChannelKeys; | ||
use lightning::ln::data_persister::ChannelDataPersister; | ||
use lightning::ln::channelmonitor::{ChannelMonitor, ChannelMonitorUpdate, ChannelMonitorUpdateErr}; | ||
use lightning::util::ser::{Writeable, Readable}; | ||
use lightning::chain::transaction::OutPoint; | ||
use bitcoin::hash_types::{BlockHash, Txid}; | ||
use bitcoin::hashes::hex::{ToHex, FromHex}; | ||
use std::collections::HashMap; | ||
use std::fs; | ||
use std::io::{Error, ErrorKind, Cursor}; | ||
use std::marker::PhantomData; | ||
|
||
/// LinuxPersister can persist channel data on disk on Linux machines, where | ||
/// each channel's data is stored in a file named after its outpoint. | ||
pub struct LinuxPersister<ChanSigner: ChannelKeys + Readable + Writeable> { | ||
path_to_channel_data: String, | ||
phantom: PhantomData<ChanSigner>, // TODO: is there a way around this? | ||
} | ||
|
||
impl<ChanSigner: ChannelKeys + Readable + Writeable> LinuxPersister<ChanSigner> { | ||
/// Initialize a new LinuxPersister and set the path to the individual channels' | ||
/// files. | ||
pub fn new(path_to_channel_data: String) -> Self { | ||
return Self { | ||
path_to_channel_data, | ||
phantom: PhantomData, | ||
} | ||
} | ||
|
||
fn get_full_filepath(&self, funding_txo: OutPoint) -> String { | ||
format!("{}/{}_{}", self.path_to_channel_data, funding_txo.txid.to_hex(), funding_txo.index) | ||
} | ||
|
||
fn write_channel_data(&self, funding_txo: OutPoint, monitor: &ChannelMonitor<ChanSigner>) -> std::io::Result<()> { | ||
// 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 | ||
// old data on power loss after we've returned | ||
// Note that this actually *isn't* enough (at least on Linux)! We need to fsync an fd with | ||
// the containing dir, but Rust doesn't let us do that directly, sadly. TODO: Fix this with | ||
// the libc crate! | ||
let filename = self.get_full_filepath(funding_txo); | ||
let tmp_filename = filename.clone() + ".tmp"; | ||
|
||
{ | ||
let mut f = fs::File::create(&tmp_filename)?; | ||
monitor.write_for_disk(&mut f)?; | ||
f.sync_all()?; | ||
} | ||
// We don't need to create a backup if didn't already have the file, but in any other case | ||
// try to create the backup and expect failure on fs::copy() if eg there's a perms issue. | ||
let need_bk = match fs::metadata(&filename) { | ||
Ok(data) => { | ||
if !data.is_file() { return Err(Error::new(ErrorKind::InvalidInput, "Filename given was not a file")); } | ||
true | ||
}, | ||
Err(e) => match e.kind() { | ||
std::io::ErrorKind::NotFound => false, | ||
_ => true, | ||
} | ||
}; | ||
let bk_filename = filename.clone() + ".bk"; | ||
if need_bk { | ||
fs::copy(&filename, &bk_filename)?; | ||
{ | ||
let f = fs::File::open(&bk_filename)?; | ||
f.sync_all()?; | ||
} | ||
} | ||
fs::rename(&tmp_filename, &filename)?; | ||
{ | ||
let f = fs::File::open(&filename)?; | ||
f.sync_all()?; | ||
} | ||
if need_bk { | ||
fs::remove_file(&bk_filename)?; | ||
} | ||
Ok(()) | ||
} | ||
} | ||
|
||
impl<ChanSigner: ChannelKeys + Readable + Writeable + Send + Sync> ChannelDataPersister for LinuxPersister<ChanSigner> { | ||
type Keys = ChanSigner; | ||
|
||
fn persist_channel_data(&self, funding_txo: OutPoint, monitor: &ChannelMonitor<Self::Keys>) -> Result<(), ChannelMonitorUpdateErr> { | ||
match self.write_channel_data(funding_txo, monitor) { | ||
Ok(_) => Ok(()), | ||
Err(_) => Err(ChannelMonitorUpdateErr::TemporaryFailure) | ||
} | ||
} | ||
|
||
fn update_channel_data(&self, funding_txo: OutPoint, _update: &ChannelMonitorUpdate, monitor: &ChannelMonitor<ChanSigner>) -> Result<(), ChannelMonitorUpdateErr> { | ||
match self.write_channel_data(funding_txo, monitor) { | ||
Ok(_) => Ok(()), | ||
Err(_) => Err(ChannelMonitorUpdateErr::TemporaryFailure) | ||
} | ||
} | ||
|
||
fn load_channel_data(&self) -> Result<HashMap<OutPoint, ChannelMonitor<ChanSigner>>, ChannelMonitorUpdateErr> { | ||
let mut res = HashMap::new(); | ||
for file_option in fs::read_dir(&self.path_to_channel_data).unwrap() { | ||
let mut loaded = false; | ||
let file = file_option.unwrap(); | ||
if let Some(filename) = file.file_name().to_str() { | ||
if filename.is_ascii() && filename.len() > 65 { | ||
if let Ok(txid) = Txid::from_hex(filename.split_at(64).0) { | ||
if let Ok(index) = filename.split_at(65).1.split('.').next().unwrap().parse() { | ||
if let Ok(contents) = fs::read(&file.path()) { | ||
if let Ok((_, loaded_monitor)) = <(BlockHash, ChannelMonitor<ChanSigner>)>::read(&mut Cursor::new(&contents)) { | ||
res.insert(OutPoint { txid, index }, loaded_monitor); | ||
loaded = true; | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
if !loaded { | ||
// TODO(val): this should prob error not just print something | ||
println!("WARNING: Failed to read one of the channel monitor storage files! Check perms!"); | ||
} | ||
} | ||
Ok(res) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.