libp2p_kad/
record.rs

1// Copyright 2019 Parity Technologies (UK) Ltd.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a
4// copy of this software and associated documentation files (the "Software"),
5// to deal in the Software without restriction, including without limitation
6// the rights to use, copy, modify, merge, publish, distribute, sublicense,
7// and/or sell copies of the Software, and to permit persons to whom the
8// Software is furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in
11// all copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
14// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
19// DEALINGS IN THE SOFTWARE.
20
21//! Records and record storage abstraction of the libp2p Kademlia DHT.
22
23pub mod store;
24
25use std::{
26    borrow::Borrow,
27    hash::{Hash, Hasher},
28};
29
30use bytes::Bytes;
31use libp2p_core::{multihash::Multihash, Multiaddr};
32use libp2p_identity::PeerId;
33#[cfg(feature = "serde")]
34use serde::{Deserialize, Serialize};
35use web_time::Instant;
36
37/// The (opaque) key of a record.
38#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
39#[derive(Clone, Debug, PartialEq, Eq, Hash)]
40pub struct Key(Bytes);
41
42impl Key {
43    /// Creates a new key from the bytes of the input.
44    pub fn new<K: AsRef<[u8]>>(key: &K) -> Self {
45        Key(Bytes::copy_from_slice(key.as_ref()))
46    }
47
48    /// Copies the bytes of the key into a new vector.
49    pub fn to_vec(&self) -> Vec<u8> {
50        Vec::from(&self.0[..])
51    }
52}
53
54impl Borrow<[u8]> for Key {
55    fn borrow(&self) -> &[u8] {
56        &self.0[..]
57    }
58}
59
60impl AsRef<[u8]> for Key {
61    fn as_ref(&self) -> &[u8] {
62        &self.0[..]
63    }
64}
65
66impl From<Vec<u8>> for Key {
67    fn from(v: Vec<u8>) -> Key {
68        Key(Bytes::from(v))
69    }
70}
71
72impl<const S: usize> From<Multihash<S>> for Key {
73    fn from(m: Multihash<S>) -> Key {
74        Key::from(m.to_bytes())
75    }
76}
77
78/// A record stored in the DHT.
79#[derive(Clone, Debug, Eq, PartialEq)]
80pub struct Record {
81    /// Key of the record.
82    pub key: Key,
83    /// Value of the record.
84    pub value: Vec<u8>,
85    /// The (original) publisher of the record.
86    pub publisher: Option<PeerId>,
87    /// The expiration time as measured by a local, monotonic clock.
88    pub expires: Option<Instant>,
89}
90
91impl Record {
92    /// Creates a new record for insertion into the DHT.
93    pub fn new<K>(key: K, value: Vec<u8>) -> Self
94    where
95        K: Into<Key>,
96    {
97        Record {
98            key: key.into(),
99            value,
100            publisher: None,
101            expires: None,
102        }
103    }
104
105    /// Checks whether the record is expired w.r.t. the given `Instant`.
106    pub fn is_expired(&self, now: Instant) -> bool {
107        self.expires.is_some_and(|t| now >= t)
108    }
109}
110
111/// A record stored in the DHT whose value is the ID of a peer
112/// who can provide the value on-demand.
113///
114/// Note: Two [`ProviderRecord`]s as well as their corresponding hashes are
115/// equal iff their `key` and `provider` fields are equal. See the [`Hash`] and
116/// [`PartialEq`] implementations.
117#[derive(Clone, Debug)]
118pub struct ProviderRecord {
119    /// The key whose value is provided by the provider.
120    pub key: Key,
121    /// The provider of the value for the key.
122    pub provider: PeerId,
123    /// The expiration time as measured by a local, monotonic clock.
124    pub expires: Option<Instant>,
125    /// The known addresses that the provider may be listening on.
126    pub addresses: Vec<Multiaddr>,
127}
128
129impl Hash for ProviderRecord {
130    fn hash<H: Hasher>(&self, state: &mut H) {
131        self.key.hash(state);
132        self.provider.hash(state);
133    }
134}
135
136impl PartialEq for ProviderRecord {
137    fn eq(&self, other: &Self) -> bool {
138        self.key == other.key && self.provider == other.provider
139    }
140}
141
142impl Eq for ProviderRecord {}
143
144impl ProviderRecord {
145    /// Creates a new provider record for insertion into a `RecordStore`.
146    pub fn new<K>(key: K, provider: PeerId, addresses: Vec<Multiaddr>) -> Self
147    where
148        K: Into<Key>,
149    {
150        ProviderRecord {
151            key: key.into(),
152            provider,
153            expires: None,
154            addresses,
155        }
156    }
157
158    /// Checks whether the provider record is expired w.r.t. the given `Instant`.
159    pub fn is_expired(&self, now: Instant) -> bool {
160        self.expires.is_some_and(|t| now >= t)
161    }
162}
163
164#[cfg(test)]
165mod tests {
166    use std::time::Duration;
167
168    use quickcheck::*;
169
170    use super::*;
171    use crate::SHA_256_MH;
172
173    impl Arbitrary for Key {
174        fn arbitrary(g: &mut Gen) -> Key {
175            let hash: [u8; 32] = core::array::from_fn(|_| u8::arbitrary(g));
176            Key::from(Multihash::<64>::wrap(SHA_256_MH, &hash).unwrap())
177        }
178    }
179
180    impl Arbitrary for Record {
181        fn arbitrary(g: &mut Gen) -> Record {
182            Record {
183                key: Key::arbitrary(g),
184                value: Vec::arbitrary(g),
185                publisher: if bool::arbitrary(g) {
186                    Some(PeerId::random())
187                } else {
188                    None
189                },
190                expires: if bool::arbitrary(g) {
191                    Some(Instant::now() + Duration::from_secs(g.gen_range(0..60)))
192                } else {
193                    None
194                },
195            }
196        }
197    }
198
199    impl Arbitrary for ProviderRecord {
200        fn arbitrary(g: &mut Gen) -> ProviderRecord {
201            ProviderRecord {
202                key: Key::arbitrary(g),
203                provider: PeerId::random(),
204                expires: if bool::arbitrary(g) {
205                    Some(Instant::now() + Duration::from_secs(g.gen_range(0..60)))
206                } else {
207                    None
208                },
209                addresses: vec![],
210            }
211        }
212    }
213}