everscale_network/dht/
entry.rs

1use std::borrow::{Borrow, Cow};
2use std::sync::Arc;
3
4use anyhow::Result;
5use tl_proto::BoxedConstructor;
6
7use super::futures::StoreValue;
8use super::node::Node;
9use super::streams::DhtValuesStream;
10use crate::adnl;
11use crate::proto;
12use crate::util::now;
13
14/// DHT entry builder
15#[must_use]
16#[derive(Copy, Clone)]
17pub struct Entry<'a> {
18    dht: &'a Arc<Node>,
19    id: &'a [u8; 32],
20    name: &'a str,
21    key_index: u32,
22}
23
24impl<'a> Entry<'a> {
25    pub(super) fn new<T>(dht: &'a Arc<Node>, id: &'a T, name: &'a str) -> Self
26    where
27        T: Borrow<[u8; 32]>,
28    {
29        Self {
30            dht,
31            id: id.borrow(),
32            name,
33            key_index: 0,
34        }
35    }
36
37    /// Sets the key index. Default: `0`
38    pub fn with_key_index(mut self, idx: u32) -> Self {
39        self.key_index = idx;
40        self
41    }
42
43    /// Creates a new builder which can store the value in the DHT.
44    ///
45    /// See [`with_data_raw`] for raw API
46    ///
47    /// [`with_data_raw`]: fn@crate::dht::Entry::with_data_raw
48    pub fn with_data<T>(self, data: T) -> EntryWithData<'a>
49    where
50        T: tl_proto::TlWrite<Repr = tl_proto::Boxed>,
51    {
52        EntryWithData {
53            inner: self,
54            data: Cow::Owned(tl_proto::serialize(data)),
55            expire_at: None,
56        }
57    }
58
59    /// Creates a new builder which can store the value in the DHT.
60    ///
61    /// See [`with_data`] for more convenient API
62    ///
63    /// [`with_data`]: fn@crate::dht::Entry::with_data
64    pub fn with_data_raw(self, data: &'a [u8]) -> EntryWithData<'a> {
65        EntryWithData {
66            inner: self,
67            data: Cow::Borrowed(data),
68            expire_at: None,
69        }
70    }
71
72    /// Returns a stream of values for this entry.
73    pub fn values<T>(self) -> DhtValuesStream<T>
74    where
75        for<'tl> T: tl_proto::TlRead<'tl, Repr = tl_proto::Boxed> + Send + 'static,
76    {
77        DhtValuesStream::new(self.dht.clone(), self.key())
78    }
79
80    /// Queries a value from the given peer.
81    pub async fn value_from<T>(
82        self,
83        peer_id: &adnl::NodeIdShort,
84    ) -> Result<Option<(proto::dht::KeyDescriptionOwned, T)>>
85    where
86        for<'tl> T: tl_proto::TlRead<'tl, Repr = tl_proto::Boxed> + Send + 'static,
87    {
88        let key_id = tl_proto::hash_as_boxed(self.key());
89        let query = tl_proto::serialize(proto::rpc::DhtFindValue { key: &key_id, k: 6 }).into();
90
91        match self.dht.query_raw(peer_id, query).await? {
92            Some(result) => self.dht.parse_value_result(&result),
93            None => Ok(None),
94        }
95    }
96
97    /// Returns TL representation of the entry key.
98    pub fn key(&self) -> proto::dht::Key<'a> {
99        proto::dht::Key {
100            id: self.id,
101            name: self.name.as_bytes(),
102            idx: self.key_index,
103        }
104    }
105}
106
107pub struct EntryWithData<'a> {
108    inner: Entry<'a>,
109    data: Cow<'a, [u8]>,
110    expire_at: Option<u32>,
111}
112
113impl<'a> EntryWithData<'a> {
114    /// Sets the expiration time for the value.
115    pub fn expire_at(mut self, timestamp: u32) -> Self {
116        self.expire_at = Some(timestamp);
117        self
118    }
119
120    /// Sets expiration time for the value as `now + ttl`
121    pub fn with_ttl(mut self, ttl: u32) -> Self {
122        self.expire_at = Some(now() + ttl);
123        self
124    }
125
126    /// Creates signed TL representation of the entry.
127    pub fn sign(self, key: &adnl::Key) -> proto::dht::ValueOwned {
128        let mut value = self.make_value(key);
129
130        let key_signature = key.sign(value.key.as_boxed());
131        value.key.signature = &key_signature;
132
133        let value_signature = key.sign(value.as_boxed());
134        value.signature = &value_signature;
135
136        value.as_equivalent_owned()
137    }
138
139    /// Creates signed TL representation of the entry and stores it in the DHT.
140    ///
141    /// See [`StoreValue`]
142    pub fn sign_and_store(self, key: &adnl::Key) -> Result<StoreValue> {
143        let mut value = self.make_value(key);
144
145        let key_signature = key.sign(value.key.as_boxed());
146        value.key.signature = &key_signature;
147
148        let value_signature = key.sign(value.as_boxed());
149        value.signature = &value_signature;
150
151        StoreValue::new(self.inner.dht.clone(), value)
152    }
153
154    fn make_value<'b>(&'b self, key: &'b adnl::Key) -> proto::dht::Value<'b>
155    where
156        'a: 'b,
157    {
158        proto::dht::Value {
159            key: proto::dht::KeyDescription {
160                key: self.inner.key(),
161                id: key.full_id().as_tl(),
162                update_rule: proto::dht::UpdateRule::Signature,
163                signature: Default::default(),
164            },
165            value: &self.data,
166            ttl: self
167                .expire_at
168                .unwrap_or_else(|| now() + self.inner.dht.options().value_ttl_sec),
169            signature: Default::default(),
170        }
171    }
172}