+
+ #[test]
+ fn test_if_monitors_is_not_dir() {
+ let store = FilesystemStore::new("test_monitors_is_not_dir".into());
+
+ fs::create_dir_all(&store.get_data_dir()).unwrap();
+ let mut path = std::path::PathBuf::from(&store.get_data_dir());
+ path.push("monitors");
+ fs::File::create(path).unwrap();
+
+ let chanmon_cfgs = create_chanmon_cfgs(1);
+ let mut node_cfgs = create_node_cfgs(1, &chanmon_cfgs);
+ let chain_mon_0 = test_utils::TestChainMonitor::new(Some(&chanmon_cfgs[0].chain_source), &chanmon_cfgs[0].tx_broadcaster, &chanmon_cfgs[0].logger, &chanmon_cfgs[0].fee_estimator, &store, node_cfgs[0].keys_manager);
+ node_cfgs[0].chain_monitor = chain_mon_0;
+ let node_chanmgrs = create_node_chanmgrs(1, &node_cfgs, &[None]);
+ let nodes = create_network(1, &node_cfgs, &node_chanmgrs);
+
+ // Check that read_channel_monitors() returns error if monitors/ is not a
+ // directory.
+ assert!(read_channel_monitors(&store, nodes[0].keys_manager, nodes[0].keys_manager).is_err());
+ }
+
+ #[test]
+ fn test_filesystem_store() {
+ // Create the nodes, giving them FilesystemStores for data stores.
+ let store_0 = FilesystemStore::new("test_filesystem_store_0".into());
+ let store_1 = FilesystemStore::new("test_filesystem_store_1".into());
+ do_test_store(&store_0, &store_1)
+ }
+
+ // Test that if the store's path to channel data is read-only, writing a
+ // monitor to it results in the store returning an UnrecoverableError.
+ // Windows ignores the read-only flag for folders, so this test is Unix-only.
+ #[cfg(not(target_os = "windows"))]
+ #[test]
+ fn test_readonly_dir_perm_failure() {
+ let store = FilesystemStore::new("test_readonly_dir_perm_failure".into());
+ fs::create_dir_all(&store.get_data_dir()).unwrap();
+
+ // Set up a dummy channel and force close. This will produce a monitor
+ // that we can then use to test persistence.
+ let chanmon_cfgs = create_chanmon_cfgs(2);
+ let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
+ let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
+ let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
+ let chan = create_announced_chan_between_nodes(&nodes, 0, 1);
+ nodes[1].node.force_close_broadcasting_latest_txn(&chan.2, &nodes[0].node.get_our_node_id()).unwrap();
+ check_closed_event!(nodes[1], 1, ClosureReason::HolderForceClosed, [nodes[0].node.get_our_node_id()], 100000);
+ let mut added_monitors = nodes[1].chain_monitor.added_monitors.lock().unwrap();
+ let update_map = nodes[1].chain_monitor.latest_monitor_update_id.lock().unwrap();
+ let update_id = update_map.get(&added_monitors[0].1.channel_id()).unwrap();
+
+ // Set the store's directory to read-only, which should result in
+ // returning an unrecoverable failure when we then attempt to persist a
+ // channel update.
+ let path = &store.get_data_dir();
+ let mut perms = fs::metadata(path).unwrap().permissions();
+ perms.set_readonly(true);
+ fs::set_permissions(path, perms).unwrap();
+
+ let test_txo = OutPoint {
+ txid: Txid::from_str("8984484a580b825b9972d7adb15050b3ab624ccd731946b3eeddb92f4e7ef6be").unwrap(),
+ index: 0
+ };
+ match store.persist_new_channel(test_txo, &added_monitors[0].1, update_id.2) {
+ ChannelMonitorUpdateStatus::UnrecoverableError => {},
+ _ => panic!("unexpected result from persisting new channel")
+ }
+
+ nodes[1].node.get_and_clear_pending_msg_events();
+ added_monitors.clear();
+ }
+
+ // Test that if a store's directory name is invalid, monitor persistence
+ // will fail.
+ #[cfg(target_os = "windows")]
+ #[test]
+ fn test_fail_on_open() {
+ // Set up a dummy channel and force close. This will produce a monitor
+ // that we can then use to test persistence.
+ let chanmon_cfgs = create_chanmon_cfgs(2);
+ let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
+ let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
+ let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
+ let chan = create_announced_chan_between_nodes(&nodes, 0, 1);
+ nodes[1].node.force_close_broadcasting_latest_txn(&chan.2, &nodes[0].node.get_our_node_id()).unwrap();
+ check_closed_event!(nodes[1], 1, ClosureReason::HolderForceClosed, [nodes[0].node.get_our_node_id()], 100000);
+ let mut added_monitors = nodes[1].chain_monitor.added_monitors.lock().unwrap();
+ let update_map = nodes[1].chain_monitor.latest_monitor_update_id.lock().unwrap();
+ let update_id = update_map.get(&added_monitors[0].1.channel_id()).unwrap();
+
+ // Create the store with an invalid directory name and test that the
+ // channel fails to open because the directories fail to be created. There
+ // don't seem to be invalid filename characters on Unix that Rust doesn't
+ // handle, hence why the test is Windows-only.
+ let store = FilesystemStore::new(":<>/".into());
+
+ let test_txo = OutPoint {
+ txid: Txid::from_str("8984484a580b825b9972d7adb15050b3ab624ccd731946b3eeddb92f4e7ef6be").unwrap(),
+ index: 0
+ };
+ match store.persist_new_channel(test_txo, &added_monitors[0].1, update_id.2) {
+ ChannelMonitorUpdateStatus::UnrecoverableError => {},
+ _ => panic!("unexpected result from persisting new channel")
+ }
+
+ nodes[1].node.get_and_clear_pending_msg_events();
+ added_monitors.clear();
+ }
+}
+
+#[cfg(ldk_bench)]
+/// Benches
+pub mod bench {
+ use criterion::Criterion;
+
+ /// Bench!
+ pub fn bench_sends(bench: &mut Criterion) {
+ let store_a = super::FilesystemStore::new("bench_filesystem_store_a".into());
+ let store_b = super::FilesystemStore::new("bench_filesystem_store_b".into());
+ lightning::ln::channelmanager::bench::bench_two_sends(
+ bench, "bench_filesystem_persisted_sends", store_a, store_b);
+ }