Persist channel scores
[ldk-sample] / src / disk.rs
1 use crate::cli;
2 use bitcoin::secp256k1::key::PublicKey;
3 use bitcoin::BlockHash;
4 use chrono::Utc;
5 use lightning::routing::network_graph::NetworkGraph;
6 use lightning::routing::scorer::Scorer;
7 use lightning::util::logger::{Logger, Record};
8 use lightning::util::ser::{Readable, Writeable, Writer};
9 use std::collections::HashMap;
10 use std::fs;
11 use std::fs::File;
12 use std::io::{BufRead, BufReader, BufWriter};
13 use std::net::SocketAddr;
14 use std::path::Path;
15
16 pub(crate) struct FilesystemLogger {
17         data_dir: String,
18 }
19 impl FilesystemLogger {
20         pub(crate) fn new(data_dir: String) -> Self {
21                 let logs_path = format!("{}/logs", data_dir);
22                 fs::create_dir_all(logs_path.clone()).unwrap();
23                 Self { data_dir: logs_path }
24         }
25 }
26 impl Logger for FilesystemLogger {
27         fn log(&self, record: &Record) {
28                 let raw_log = record.args.to_string();
29                 let log = format!(
30                         "{} {:<5} [{}:{}] {}\n",
31                         // Note that a "real" lightning node almost certainly does *not* want subsecond
32                         // precision for message-receipt information as it makes log entries a target for
33                         // deanonymization attacks. For testing, however, its quite useful.
34                         Utc::now().format("%Y-%m-%d %H:%M:%S%.3f"),
35                         record.level.to_string(),
36                         record.module_path,
37                         record.line,
38                         raw_log
39                 );
40                 let logs_file_path = format!("{}/logs.txt", self.data_dir.clone());
41                 fs::OpenOptions::new()
42                         .create(true)
43                         .append(true)
44                         .open(logs_file_path)
45                         .unwrap()
46                         .write_all(log.as_bytes())
47                         .unwrap();
48         }
49 }
50 pub(crate) fn persist_channel_peer(path: &Path, peer_info: &str) -> std::io::Result<()> {
51         let mut file = fs::OpenOptions::new().create(true).append(true).open(path)?;
52         file.write_all(format!("{}\n", peer_info).as_bytes())
53 }
54
55 pub(crate) fn read_channel_peer_data(
56         path: &Path,
57 ) -> Result<HashMap<PublicKey, SocketAddr>, std::io::Error> {
58         let mut peer_data = HashMap::new();
59         if !Path::new(&path).exists() {
60                 return Ok(HashMap::new());
61         }
62         let file = File::open(path)?;
63         let reader = BufReader::new(file);
64         for line in reader.lines() {
65                 match cli::parse_peer_info(line.unwrap()) {
66                         Ok((pubkey, socket_addr)) => {
67                                 peer_data.insert(pubkey, socket_addr);
68                         }
69                         Err(e) => return Err(e),
70                 }
71         }
72         Ok(peer_data)
73 }
74
75 pub(crate) fn persist_network(path: &Path, network_graph: &NetworkGraph) -> std::io::Result<()> {
76         let mut tmp_path = path.to_path_buf().into_os_string();
77         tmp_path.push(".tmp");
78         let file = fs::OpenOptions::new().write(true).create(true).open(&tmp_path)?;
79         let write_res = network_graph.write(&mut BufWriter::new(file));
80         if let Err(e) = write_res.and_then(|_| fs::rename(&tmp_path, path)) {
81                 let _ = fs::remove_file(&tmp_path);
82                 Err(e)
83         } else {
84                 Ok(())
85         }
86 }
87
88 pub(crate) fn read_network(path: &Path, genesis_hash: BlockHash) -> NetworkGraph {
89         if let Ok(file) = File::open(path) {
90                 if let Ok(graph) = NetworkGraph::read(&mut BufReader::new(file)) {
91                         return graph;
92                 }
93         }
94         NetworkGraph::new(genesis_hash)
95 }
96
97 pub(crate) fn persist_scorer(path: &Path, scorer: &Scorer) -> std::io::Result<()> {
98         let mut tmp_path = path.to_path_buf().into_os_string();
99         tmp_path.push(".tmp");
100         let file = fs::OpenOptions::new().write(true).create(true).open(&tmp_path)?;
101         let write_res = scorer.write(&mut BufWriter::new(file));
102         if let Err(e) = write_res.and_then(|_| fs::rename(&tmp_path, path)) {
103                 let _ = fs::remove_file(&tmp_path);
104                 Err(e)
105         } else {
106                 Ok(())
107         }
108 }
109
110 pub(crate) fn read_scorer(path: &Path) -> Scorer {
111         if let Ok(file) = File::open(path) {
112                 if let Ok(scorer) = Scorer::read(&mut BufReader::new(file)) {
113                         return scorer;
114                 }
115         }
116         Scorer::default()
117 }