diff --git a/Cargo.toml b/Cargo.toml index 772c232d9..278306fc5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -51,6 +51,7 @@ chrono = "0.4" futures = "0.3" serde_json = { version = "1.0" } tokio = { version = "1", default-features = false, features = [ "rt-multi-thread", "time", "sync" ] } +libc = "0.2" [dev-dependencies] electrsd = { version = "0.22.0", features = ["legacy", "esplora_a33e97e1", "bitcoind_23_0"] } diff --git a/src/event.rs b/src/event.rs index b875e09ee..071864341 100644 --- a/src/event.rs +++ b/src/event.rs @@ -3,6 +3,7 @@ use crate::{ PaymentInfo, PaymentInfoStorage, PaymentStatus, Wallet, }; +use crate::io_utils::KVStoreUnpersister; use crate::logger::{log_error, log_info, Logger}; use lightning::chain::chaininterface::{BroadcasterInterface, ConfirmationTarget, FeeEstimator}; @@ -183,7 +184,7 @@ impl Writeable for EventQueueSerWrapper<'_> { pub(crate) struct EventHandler where - K::Target: KVStorePersister, + K::Target: KVStorePersister + KVStoreUnpersister, L::Target: Logger, { wallet: Arc>, @@ -199,7 +200,7 @@ where impl EventHandler where - K::Target: KVStorePersister, + K::Target: KVStorePersister + KVStoreUnpersister, L::Target: Logger, { pub fn new( @@ -224,7 +225,7 @@ where impl LdkEventHandler for EventHandler where - K::Target: KVStorePersister, + K::Target: KVStorePersister + KVStoreUnpersister, L::Target: Logger, { fn handle_event(&self, event: LdkEvent) { diff --git a/src/io_utils.rs b/src/io_utils.rs index e4813b9ff..796eec839 100644 --- a/src/io_utils.rs +++ b/src/io_utils.rs @@ -3,12 +3,15 @@ use crate::{Config, Error, FilesystemLogger, NetworkGraph, Scorer}; use lightning::routing::scoring::{ProbabilisticScorer, ProbabilisticScoringParameters}; use lightning::util::ser::{Readable, ReadableArgs}; +use lightning_persister::FilesystemPersister; use rand::{thread_rng, RngCore}; use std::fs; use std::io::{BufReader, Write}; +use std::os::unix::io::AsRawFd; use std::path::Path; +use std::path::PathBuf; use std::sync::Arc; pub(crate) fn read_or_generate_seed_file(config: &Config) -> [u8; 32] { @@ -82,3 +85,37 @@ pub(crate) fn read_payment_info(config: &Config) -> Vec { payments } + +/// Provides an interface that allows a previously persisted key to be unpersisted. +pub trait KVStoreUnpersister { + /// Unpersist (i.e., remove) the writeable previously persisted under the provided key. + /// Returns `true` if the key was present, and `false` otherwise. + fn unpersist(&self, key: &str) -> std::io::Result; +} + +impl KVStoreUnpersister for FilesystemPersister { + fn unpersist(&self, key: &str) -> std::io::Result { + let mut dest_file = PathBuf::from(self.get_data_dir()); + dest_file.push(key); + + if !dest_file.is_file() { + return Ok(false); + } + + fs::remove_file(&dest_file)?; + let parent_directory = dest_file.parent().unwrap(); + let dir_file = fs::OpenOptions::new().read(true).open(parent_directory)?; + #[cfg(not(target_os = "windows"))] + { + unsafe { + libc::fsync(dir_file.as_raw_fd()); + } + } + + if dest_file.is_file() { + return Err(std::io::Error::new(std::io::ErrorKind::Other, "Unpersisting key failed")); + } + + return Ok(true); + } +} diff --git a/src/payment_store.rs b/src/payment_store.rs index 3bd1d554c..fc0fa7e01 100644 --- a/src/payment_store.rs +++ b/src/payment_store.rs @@ -1,4 +1,5 @@ use crate::hex_utils; +use crate::io_utils::KVStoreUnpersister; use crate::Error; use lightning::ln::{PaymentHash, PaymentPreimage, PaymentSecret}; @@ -72,7 +73,7 @@ pub(crate) const PAYMENT_INFO_PERSISTENCE_PREFIX: &str = "payments"; pub(crate) struct PaymentInfoStorage where - K::Target: KVStorePersister, + K::Target: KVStorePersister + KVStoreUnpersister, { payments: Mutex>, persister: K, @@ -80,7 +81,7 @@ where impl PaymentInfoStorage where - K::Target: KVStorePersister, + K::Target: KVStorePersister + KVStoreUnpersister, { pub(crate) fn from_payments(mut payments: Vec, persister: K) -> Self { let payments = Mutex::new(HashMap::from_iter( @@ -106,9 +107,15 @@ where return Ok(()); } - // TODO: Need an `unpersist` method for this? - //pub(crate) fn remove_payment(&self, payment_hash: &PaymentHash) -> Result<(), Error> { - //} + pub(crate) fn remove(&self, payment_hash: &PaymentHash) -> Result<(), Error> { + let key = format!( + "{}/{}", + PAYMENT_INFO_PERSISTENCE_PREFIX, + hex_utils::to_string(&payment_hash.0) + ); + self.persister.unpersist(&key).map_err(|_| Error::PersistenceFailed)?; + Ok(()) + } pub(crate) fn get(&self, payment_hash: &PaymentHash) -> Option { self.payments.lock().unwrap().get(payment_hash).cloned()