// Copyright 2017 Parity Technologies (UK) Ltd. // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the "Software"), // to deal in the Software without restriction, including without limitation // the rights to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to whom the // Software is furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. //! Implementation of the `Peerstore` trait that uses a single JSON file as backend. use super::TTL; use PeerId; use base58::{FromBase58, ToBase58}; use datastore::{Datastore, Query, JsonFileDatastore, JsonFileDatastoreEntry}; use futures::{Future, Stream}; use multiaddr::Multiaddr; use peer_info::{PeerInfo, AddAddrBehaviour}; use peerstore::{Peerstore, PeerAccess}; use std::io::Error as IoError; use std::iter; use std::path::PathBuf; use std::vec::IntoIter as VecIntoIter; /// Peerstore backend that uses a Json file. pub struct JsonPeerstore { store: JsonFileDatastore, } impl JsonPeerstore { /// Opens a new peerstore tied to a JSON file at the given path. /// /// If the file exists, this function will open it. In any case, flushing the peerstore or /// destroying it will write to the file. #[inline] pub fn new

(path: P) -> Result where P: Into { Ok(JsonPeerstore { store: JsonFileDatastore::new(path)? }) } /// Flushes the content of the peer store to the disk. /// /// This function can only fail in case of a disk access error. If an error occurs, any change /// to the peerstore that was performed since the last successful flush will be lost. No data /// will be corrupted. #[inline] pub fn flush(&self) -> Result<(), IoError> { self.store.flush() } } impl<'a> Peerstore for &'a JsonPeerstore { type PeerAccess = JsonPeerstoreAccess<'a>; type PeersIter = Box>; #[inline] fn peer(self, peer_id: &PeerId) -> Option { let hash = peer_id.to_base58(); self.store.lock(hash.into()).map(JsonPeerstoreAccess) } #[inline] fn peer_or_create(self, peer_id: &PeerId) -> Self::PeerAccess { let hash = peer_id.to_base58(); JsonPeerstoreAccess(self.store.lock_or_create(hash.into())) } fn peers(self) -> Self::PeersIter { let query = self.store.query(Query { prefix: "".into(), filters: vec![], orders: vec![], skip: 0, limit: u64::max_value(), keys_only: true, }); let list = query.filter_map(|(key, _)| { // We filter out invalid elements. This can happen if the JSON storage file was // corrupted or manually modified by the user. key.from_base58().ok() }) .collect() .wait(); // Wait can never block for the JSON datastore. // Need to handle I/O errors. Again we just ignore. if let Ok(list) = list { Box::new(list.into_iter()) as Box<_> } else { Box::new(iter::empty()) as Box<_> } } } pub struct JsonPeerstoreAccess<'a>(JsonFileDatastoreEntry<'a, PeerInfo>); impl<'a> PeerAccess for JsonPeerstoreAccess<'a> { type AddrsIter = VecIntoIter; #[inline] fn addrs(&self) -> Self::AddrsIter { self.0.addrs().cloned().collect::>().into_iter() } #[inline] fn add_addr(&mut self, addr: Multiaddr, ttl: TTL) { self.0.add_addr(addr, ttl, AddAddrBehaviour::IgnoreTtlIfInferior); } #[inline] fn set_addr_ttl(&mut self, addr: Multiaddr, ttl: TTL) { self.0.add_addr(addr, ttl, AddAddrBehaviour::OverwriteTtl); } #[inline] fn clear_addrs(&mut self) { self.0.set_addrs(iter::empty()); } #[inline] fn get_pub_key(&self) -> Option<&[u8]> { self.0.public_key() } #[inline] fn set_pub_key(&mut self, key: Vec) { self.0.set_public_key(key); } } #[cfg(test)] mod tests { extern crate tempfile; peerstore_tests!( {::json_peerstore::JsonPeerstore::new(temp_file.path()).unwrap()} {let temp_file = self::tempfile::NamedTempFile::new().unwrap()} ); }