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 bytes::Bytes;
26use libp2p_core::{PeerId, Multiaddr, multihash::Multihash};
27use std::borrow::Borrow;
28use std::hash::{Hash, Hasher};
29use wasm_timer::Instant;
30
31/// The (opaque) key of a record.
32#[derive(Clone, Debug, PartialEq, Eq, Hash)]
33pub struct Key(Bytes);
34
35impl Key {
36    /// Creates a new key from the bytes of the input.
37    pub fn new<K: AsRef<[u8]>>(key: &K) -> Self {
38        Key(Bytes::copy_from_slice(key.as_ref()))
39    }
40
41    /// Copies the bytes of the key into a new vector.
42    pub fn to_vec(&self) -> Vec<u8> {
43        Vec::from(&self.0[..])
44    }
45}
46
47impl Borrow<[u8]> for Key {
48    fn borrow(&self) -> &[u8] {
49        &self.0[..]
50    }
51}
52
53impl AsRef<[u8]> for Key {
54    fn as_ref(&self) -> &[u8] {
55        &self.0[..]
56    }
57}
58
59impl From<Vec<u8>> for Key {
60    fn from(v: Vec<u8>) -> Key {
61        Key(Bytes::from(v))
62    }
63}
64
65impl From<Multihash> for Key {
66    fn from(m: Multihash) -> Key {
67        Key::from(m.to_bytes())
68    }
69}
70
71/// A record stored in the DHT.
72#[derive(Clone, Debug, Eq, PartialEq)]
73pub struct Record {
74    /// Key of the record.
75    pub key: Key,
76    /// Value of the record.
77    pub value: Vec<u8>,
78    /// The (original) publisher of the record.
79    pub publisher: Option<PeerId>,
80    /// The expiration time as measured by a local, monotonic clock.
81    pub expires: Option<Instant>,
82}
83
84impl Record {
85    /// Creates a new record for insertion into the DHT.
86    pub fn new<K>(key: K, value: Vec<u8>) -> Self
87    where
88        K: Into<Key>
89    {
90        Record {
91            key: key.into(),
92            value,
93            publisher: None,
94            expires: None,
95        }
96    }
97
98    /// Checks whether the record is expired w.r.t. the given `Instant`.
99    pub fn is_expired(&self, now: Instant) -> bool {
100        self.expires.map_or(false, |t| now >= t)
101    }
102}
103
104/// A record stored in the DHT whose value is the ID of a peer
105/// who can provide the value on-demand.
106///
107/// Note: Two [`ProviderRecord`]s as well as their corresponding hashes are
108/// equal iff their `key` and `provider` fields are equal. See the [`Hash`] and
109/// [`PartialEq`] implementations.
110#[derive(Clone, Debug)]
111pub struct ProviderRecord {
112    /// The key whose value is provided by the provider.
113    pub key: Key,
114    /// The provider of the value for the key.
115    pub provider: PeerId,
116    /// The expiration time as measured by a local, monotonic clock.
117    pub expires: Option<Instant>,
118    /// The known addresses that the provider may be listening on.
119    pub addresses: Vec<Multiaddr>
120    // pub weight: u32 // TODO: weight
121}
122
123impl Hash for ProviderRecord {
124    fn hash<H: Hasher>(&self, state: &mut H) {
125        self.key.hash(state);
126        self.provider.hash(state);
127    }
128}
129
130impl PartialEq for ProviderRecord {
131    fn eq(&self, other: &Self) -> bool {
132        self.key == other.key && self.provider == other.provider
133    }
134}
135
136impl Eq for ProviderRecord {}
137
138impl ProviderRecord {
139    /// Creates a new provider record for insertion into a `RecordStore`.
140    pub fn new<K>(key: K, provider: PeerId, addresses: Vec<Multiaddr>) -> Self
141    where
142        K: Into<Key>
143    {
144        ProviderRecord {
145            key: key.into(),
146            provider,
147            expires: None,
148            addresses,
149        }
150    }
151
152    /// Checks whether the provider record is expired w.r.t. the given `Instant`.
153    pub fn is_expired(&self, now: Instant) -> bool {
154        self.expires.map_or(false, |t| now >= t)
155    }
156}
157
158#[cfg(test)]
159mod tests {
160    use super::*;
161    use quickcheck::*;
162    use libp2p_core::multihash::Code;
163    use rand::Rng;
164    use std::time::Duration;
165
166    impl Arbitrary for Key {
167        fn arbitrary<G: Gen>(_: &mut G) -> Key {
168            let hash = rand::thread_rng().gen::<[u8; 32]>();
169            Key::from(Multihash::wrap(Code::Sha2_256.into(), &hash).unwrap())
170        }
171    }
172
173    impl Arbitrary for Record {
174        fn arbitrary<G: Gen>(g: &mut G) -> Record {
175            Record {
176                key: Key::arbitrary(g),
177                value: Vec::arbitrary(g),
178                publisher: if g.gen() { Some(PeerId::random()) } else { None },
179                expires: if g.gen() {
180                    Some(Instant::now() + Duration::from_secs(g.gen_range(0, 60)))
181                } else {
182                    None
183                },
184            }
185        }
186    }
187
188    impl Arbitrary for ProviderRecord {
189        fn arbitrary<G: Gen>(g: &mut G) -> ProviderRecord {
190            ProviderRecord {
191                key: Key::arbitrary(g),
192                provider: PeerId::random(),
193                expires: if g.gen() {
194                    Some(Instant::now() + Duration::from_secs(g.gen_range(0, 60)))
195                } else {
196                    None
197                },
198                addresses: vec![],
199            }
200        }
201    }
202}