// Copyright 2019 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. //! Records and record storage abstraction of the libp2p Kademlia DHT. pub mod store; use bytes::Bytes; use instant::Instant; use libp2p_core::{multihash::Multihash, Multiaddr}; use libp2p_identity::PeerId; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; use std::borrow::Borrow; use std::hash::{Hash, Hasher}; /// The (opaque) key of a record. #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub struct Key(Bytes); impl Key { /// Creates a new key from the bytes of the input. pub fn new>(key: &K) -> Self { Key(Bytes::copy_from_slice(key.as_ref())) } /// Copies the bytes of the key into a new vector. pub fn to_vec(&self) -> Vec { Vec::from(&self.0[..]) } } impl Borrow<[u8]> for Key { fn borrow(&self) -> &[u8] { &self.0[..] } } impl AsRef<[u8]> for Key { fn as_ref(&self) -> &[u8] { &self.0[..] } } impl From> for Key { fn from(v: Vec) -> Key { Key(Bytes::from(v)) } } impl From for Key { fn from(m: Multihash) -> Key { Key::from(m.to_bytes()) } } /// A record stored in the DHT. #[derive(Clone, Debug, Eq, PartialEq)] pub struct Record { /// Key of the record. pub key: Key, /// Value of the record. pub value: Vec, /// The (original) publisher of the record. pub publisher: Option, /// The expiration time as measured by a local, monotonic clock. pub expires: Option, } impl Record { /// Creates a new record for insertion into the DHT. pub fn new(key: K, value: Vec) -> Self where K: Into, { Record { key: key.into(), value, publisher: None, expires: None, } } /// Checks whether the record is expired w.r.t. the given `Instant`. pub fn is_expired(&self, now: Instant) -> bool { self.expires.map_or(false, |t| now >= t) } } /// A record stored in the DHT whose value is the ID of a peer /// who can provide the value on-demand. /// /// Note: Two [`ProviderRecord`]s as well as their corresponding hashes are /// equal iff their `key` and `provider` fields are equal. See the [`Hash`] and /// [`PartialEq`] implementations. #[derive(Clone, Debug)] pub struct ProviderRecord { /// The key whose value is provided by the provider. pub key: Key, /// The provider of the value for the key. pub provider: PeerId, /// The expiration time as measured by a local, monotonic clock. pub expires: Option, /// The known addresses that the provider may be listening on. pub addresses: Vec, } impl Hash for ProviderRecord { fn hash(&self, state: &mut H) { self.key.hash(state); self.provider.hash(state); } } impl PartialEq for ProviderRecord { fn eq(&self, other: &Self) -> bool { self.key == other.key && self.provider == other.provider } } impl Eq for ProviderRecord {} impl ProviderRecord { /// Creates a new provider record for insertion into a `RecordStore`. pub fn new(key: K, provider: PeerId, addresses: Vec) -> Self where K: Into, { ProviderRecord { key: key.into(), provider, expires: None, addresses, } } /// Checks whether the provider record is expired w.r.t. the given `Instant`. pub fn is_expired(&self, now: Instant) -> bool { self.expires.map_or(false, |t| now >= t) } } #[cfg(test)] mod tests { use super::*; use crate::SHA_256_MH; use quickcheck::*; use std::time::Duration; impl Arbitrary for Key { fn arbitrary(g: &mut Gen) -> Key { let hash: [u8; 32] = core::array::from_fn(|_| u8::arbitrary(g)); Key::from(Multihash::wrap(SHA_256_MH, &hash).unwrap()) } } impl Arbitrary for Record { fn arbitrary(g: &mut Gen) -> Record { Record { key: Key::arbitrary(g), value: Vec::arbitrary(g), publisher: if bool::arbitrary(g) { Some(PeerId::random()) } else { None }, expires: if bool::arbitrary(g) { Some(Instant::now() + Duration::from_secs(g.gen_range(0..60))) } else { None }, } } } impl Arbitrary for ProviderRecord { fn arbitrary(g: &mut Gen) -> ProviderRecord { ProviderRecord { key: Key::arbitrary(g), provider: PeerId::random(), expires: if bool::arbitrary(g) { Some(Instant::now() + Duration::from_secs(g.gen_range(0..60))) } else { None }, addresses: vec![], } } } }