ckb_network/peer_store/
peer_store_db.rs

1use crate::{
2    errors::{Error, PeerStoreError},
3    peer_store::{
4        PeerStore,
5        addr_manager::AddrManager,
6        anchors::Anchors,
7        ban_list::BanList,
8        types::{AddrInfo, BannedAddr},
9    },
10};
11use ckb_logger::{debug, error};
12use p2p::multiaddr::Multiaddr;
13use std::path::Path;
14use std::{
15    fs::{File, OpenOptions, copy, create_dir_all, remove_file, rename},
16    io::{Read, Write},
17};
18
19const DEFAULT_ADDR_MANAGER_DB: &str = "addr_manager.db";
20const DEFAULT_BAN_LIST_DB: &str = "ban_list.db";
21const DEFAULT_ANCHORS_DB: &str = "anchors.db";
22
23impl AddrManager {
24    /// Load address list from disk
25    pub fn load<R: Read>(r: R) -> Result<Self, Error> {
26        let addrs: Vec<AddrInfo> = serde_json::from_reader(r).map_err(PeerStoreError::Serde)?;
27        let mut addr_manager = AddrManager::default();
28        addrs.into_iter().for_each(|addr| addr_manager.add(addr));
29        Ok(addr_manager)
30    }
31
32    /// Dump address list to disk
33    pub fn dump(&self, mut file: File) -> Result<(), Error> {
34        let addrs: Vec<_> = self.addrs_iter().collect();
35        debug!("Dump {} addrs", addrs.len());
36        // empty file and dump the json string to it
37        file.set_len(0)
38            .and_then(|_| serde_json::to_string(&addrs).map_err(Into::into))
39            .and_then(|json_string| file.write_all(json_string.as_bytes()))
40            .and_then(|_| file.sync_all())
41            .map_err(Into::into)
42    }
43
44    #[cfg(target_family = "wasm")]
45    pub fn dump_data(&self) -> Vec<u8> {
46        let addrs: Vec<_> = self.addrs_iter().collect();
47        serde_json::to_string(&addrs).unwrap().into_bytes()
48    }
49}
50
51impl BanList {
52    /// Load ban list from disk
53    pub fn load<R: Read>(r: R) -> Result<Self, Error> {
54        let banned_addrs: Vec<BannedAddr> =
55            serde_json::from_reader(r).map_err(PeerStoreError::Serde)?;
56        let mut ban_list = BanList::default();
57        banned_addrs
58            .into_iter()
59            .for_each(|banned_addr| ban_list.ban(banned_addr));
60        Ok(ban_list)
61    }
62
63    /// Dump ban list to disk
64    pub fn dump(&self, mut file: File) -> Result<(), Error> {
65        let banned_addrs = self.get_banned_addrs();
66        debug!("Dump {} banned addrs", banned_addrs.len());
67        // empty file and dump the json string to it
68        file.set_len(0)
69            .and_then(|_| serde_json::to_string(&banned_addrs).map_err(Into::into))
70            .and_then(|json_string| file.write_all(json_string.as_bytes()))
71            .and_then(|_| file.sync_all())
72            .map_err(Into::into)
73    }
74
75    #[cfg(target_family = "wasm")]
76    pub fn dump_data(&self) -> Vec<u8> {
77        let banned_addrs = self.get_banned_addrs();
78        serde_json::to_string(&banned_addrs).unwrap().into_bytes()
79    }
80}
81
82impl Anchors {
83    /// Load address list from disk
84    pub fn load<R: Read>(r: R) -> Result<Self, Error> {
85        let addrs: Vec<Multiaddr> = serde_json::from_reader(r).map_err(PeerStoreError::Serde)?;
86        let mut anchors = Anchors::default();
87        addrs.into_iter().for_each(|addr| anchors.add(addr));
88        Ok(anchors)
89    }
90
91    /// Dump address list to disk
92    pub fn dump(&self, mut file: File) -> Result<(), Error> {
93        let addrs: Vec<_> = self.dump_iter().collect();
94        debug!("Anchors dump {} addrs", addrs.len());
95        // empty file and dump the json string to it
96        file.set_len(0)
97            .and_then(|_| serde_json::to_string(&addrs).map_err(Into::into))
98            .and_then(|json_string| file.write_all(json_string.as_bytes()))
99            .and_then(|_| file.sync_all())
100            .map_err(Into::into)
101    }
102
103    #[cfg(target_family = "wasm")]
104    pub fn dump_data(&self) -> Vec<u8> {
105        let addrs: Vec<_> = self.dump_iter().collect();
106        serde_json::to_string(&addrs).unwrap().into_bytes()
107    }
108}
109
110impl PeerStore {
111    /// Init peer store from disk
112    pub fn load_from_dir_or_default<P: AsRef<Path>>(path: P) -> Self {
113        let addr_manager_path = path.as_ref().join(DEFAULT_ADDR_MANAGER_DB);
114        let ban_list_path = path.as_ref().join(DEFAULT_BAN_LIST_DB);
115        let anchors_path = path.as_ref().join(DEFAULT_ANCHORS_DB);
116
117        let addr_manager = open_read(&addr_manager_path)
118            .map_err(|err| {
119                debug!(
120                    "Failed to open AddrManager db, file: {:?}, error: {:?}",
121                    addr_manager_path, err
122                )
123            })
124            .and_then(|reader| {
125                AddrManager::load(reader).map_err(|err| {
126                    error!(
127                        "Failed to load AddrManager db, file: {:?}, error: {:?}",
128                        addr_manager_path, err
129                    );
130                })
131            })
132            .unwrap_or_default();
133
134        let ban_list = open_read(&ban_list_path)
135            .map_err(|err| {
136                debug!(
137                    "Failed to open BanList db, file: {:?}, error: {:?}",
138                    ban_list_path, err
139                )
140            })
141            .and_then(|reader| {
142                BanList::load(reader).map_err(|err| {
143                    error!(
144                        "Failed to load BanList db, file: {:?}, error: {:?}",
145                        ban_list_path, err
146                    )
147                })
148            })
149            .unwrap_or_default();
150
151        let anchors = open_read(&anchors_path)
152            .map_err(|err| {
153                debug!(
154                    "Failed to open Anchors db, file: {:?}, error: {:?}",
155                    anchors_path, err
156                )
157            })
158            .and_then(|reader| {
159                Anchors::load(reader).map_err(|err| {
160                    error!(
161                        "Failed to load Anchors db, file: {:?}, error: {:?}",
162                        anchors_path, err
163                    )
164                })
165            })
166            .unwrap_or_default();
167
168        PeerStore::new(addr_manager, ban_list, anchors)
169    }
170
171    #[cfg(target_family = "wasm")]
172    pub async fn load_from_idb<P: AsRef<Path>>(path: P) -> Self {
173        use crate::peer_store::browser::get_db;
174
175        let addr_manager_path = path
176            .as_ref()
177            .join(DEFAULT_ADDR_MANAGER_DB)
178            .to_str()
179            .unwrap()
180            .to_owned()
181            .into_bytes();
182        let ban_list_path = path
183            .as_ref()
184            .join(DEFAULT_BAN_LIST_DB)
185            .to_str()
186            .unwrap()
187            .to_owned()
188            .into_bytes();
189        let anchors_path = path
190            .as_ref()
191            .join(DEFAULT_ANCHORS_DB)
192            .to_str()
193            .unwrap()
194            .to_owned()
195            .into_bytes();
196
197        let db = get_db(path).await;
198
199        let addr_manager = db
200            .get(&addr_manager_path)
201            .await
202            .map_err(|err| debug!("Failed to get indexdb value, error: {:?}", err))
203            .and_then(|data| {
204                AddrManager::load(std::io::Cursor::new(data.unwrap_or_default()))
205                    .map_err(|err| debug!("Failed to load peer store value, error: {:?}", err))
206            })
207            .unwrap_or_default();
208
209        let ban_list = db
210            .get(&ban_list_path)
211            .await
212            .map_err(|err| debug!("Failed to get indexdb value, error: {:?}", err))
213            .and_then(|data| {
214                BanList::load(std::io::Cursor::new(data.unwrap_or_default()))
215                    .map_err(|err| debug!("Failed to load BanList value, error: {:?}", err))
216            })
217            .unwrap_or_default();
218
219        let anchors = db
220            .get(&anchors_path)
221            .await
222            .map_err(|err| debug!("Failed to get indexdb value, error: {:?}", err))
223            .and_then(|data| {
224                Anchors::load(std::io::Cursor::new(data.unwrap_or_default()))
225                    .map_err(|err| debug!("Failed to load Anchors value, error: {:?}", err))
226            })
227            .unwrap_or_default();
228
229        PeerStore::new(addr_manager, ban_list, anchors)
230    }
231
232    /// Dump all info to disk
233    pub fn dump_to_dir<P: AsRef<Path>>(&self, path: P) -> Result<(), Error> {
234        // create dir
235        create_dir_all(&path)?;
236        // dump file to a temporary sub-directory
237        let tmp_dir = path.as_ref().join("tmp");
238        create_dir_all(&tmp_dir)?;
239        let tmp_addr_manager = tmp_dir.join(DEFAULT_ADDR_MANAGER_DB);
240        let tmp_ban_list = tmp_dir.join(DEFAULT_BAN_LIST_DB);
241        let tmp_anchors_list = tmp_dir.join(DEFAULT_ANCHORS_DB);
242        self.addr_manager().dump(dump_open(&tmp_addr_manager)?)?;
243        move_file(
244            tmp_addr_manager,
245            path.as_ref().join(DEFAULT_ADDR_MANAGER_DB),
246        )?;
247        self.ban_list().dump(dump_open(&tmp_ban_list)?)?;
248        move_file(tmp_ban_list, path.as_ref().join(DEFAULT_BAN_LIST_DB))?;
249        self.anchors().dump(dump_open(&tmp_anchors_list)?)?;
250        move_file(tmp_anchors_list, path.as_ref().join(DEFAULT_ANCHORS_DB))?;
251        Ok(())
252    }
253
254    #[cfg(target_family = "wasm")]
255    pub fn dump_to_idb<P: AsRef<Path>>(
256        &self,
257        path: P,
258    ) -> impl std::future::Future<Output = ()> + use<P> {
259        use crate::peer_store::browser::get_db;
260        let ban_list = self.ban_list().dump_data();
261        let addr_manager = self.addr_manager().dump_data();
262        let anchors = self.anchors().dump_data();
263
264        let addr_manager_path = path
265            .as_ref()
266            .join(DEFAULT_ADDR_MANAGER_DB)
267            .to_str()
268            .unwrap()
269            .to_owned();
270        let ban_list_path = path
271            .as_ref()
272            .join(DEFAULT_BAN_LIST_DB)
273            .to_str()
274            .unwrap()
275            .to_owned();
276        let anchors_path = path
277            .as_ref()
278            .join(DEFAULT_ANCHORS_DB)
279            .to_str()
280            .unwrap()
281            .to_owned();
282        async {
283            let db = get_db(path).await;
284
285            let _ignore = db.put(addr_manager_path.into_bytes(), addr_manager).await;
286            let _ignore = db.put(ban_list_path.into_bytes(), ban_list).await;
287            let _ignore = db.put(anchors_path.into_bytes(), anchors).await;
288        }
289    }
290}
291
292/// This function use `copy` then `remove_file` as a fallback when `rename` failed,
293/// this maybe happen when src and dst on different file systems.
294fn move_file<P: AsRef<Path>>(src: P, dst: P) -> Result<(), Error> {
295    if rename(&src, &dst).is_err() {
296        copy(&src, &dst)?;
297        remove_file(&src)?;
298    }
299    Ok(())
300}
301
302fn open_read<P: AsRef<Path>>(path: P) -> std::io::Result<std::io::BufReader<File>> {
303    File::open(path.as_ref()).map(std::io::BufReader::new)
304}
305
306fn dump_open<P: AsRef<Path>>(path: P) -> std::io::Result<File> {
307    OpenOptions::new()
308        .write(true)
309        .create(true)
310        .truncate(true)
311        .append(false)
312        .open(path.as_ref())
313}