willow_data_model/entry/
entry.rs

1use core::fmt::Debug;
2
3#[cfg(feature = "dev")]
4use arbitrary::Arbitrary;
5
6use compact_u64::{cu64_decode_canonic_standalone, cu64_decode_standalone};
7use ufotofu::codec_prelude::*;
8
9use crate::{groupings::Coordinatelike, prelude::*};
10
11/// The metadata associated with each Willow [Payload](https://willowprotocol.org/specs/data-model/index.html#Payload) string.
12///
13/// [Entries](https://willowprotocol.org/specs/data-model/index.html#Entry) are the central concept in Willow. In order to make any bytestring of data accessible to Willow, you need to create an Entry describing its metadata. Specifically, an Entry consists of
14///
15/// - a [namespace_id](https://willowprotocol.org/specs/data-model/index.html#entry_namespace_id) (roughly, this addresses a universe of Willow data, fully independent from all data (i.e., Entries) of different namespace ids) of type `N`,
16/// - a [subspace_id](https://willowprotocol.org/specs/data-model/index.html#entry_subspace_id) (roughly, a fully indendent part of a namespace, typically subspaces correspond to individual users) of type `S`,
17/// - a [path](https://willowprotocol.org/specs/data-model/index.html#entry_path) (roughly, a file-system-like way of arranging payloads hierarchically within a subspace) of type [`Path`],
18/// - a [timestamp](https://willowprotocol.org/specs/data-model/index.html#entry_timestamp) (newer Entries can overwrite certain older Entries),
19/// - a [payload_length](https://willowprotocol.org/specs/data-model/index.html#entry_payload_length) (the length of the payload string), and
20/// - a [payload_digest](https://willowprotocol.org/specs/data-model/index.html#entry_payload_digest) (a secure hash of the payload string being inserted into Willow).
21///
22/// To access these six fields, use the methods of the [`Entrylike`] trait (which [`Entry`] implements). The [`EntrylikeExt`] trait provides additional helper methods, for example, methods to check which Entries can delete which other Entries.
23///
24/// To create Entries, use the [`Entry::builder`] or [`Entry::prefilled_builder`] functions.
25///
26/// # Example
27///
28/// ```
29/// use willow_data_model::prelude::*;
30///
31/// let entry = Entry::builder()
32///     .namespace_id("family")
33///     .subspace_id("alfie")
34///     .path(Path::<4, 4, 4>::new())
35///     .timestamp(12345)
36///     .payload_digest("some_hash")
37///     .payload_length(17)
38///     .build().unwrap();
39///
40/// assert_eq!(*entry.wdm_subspace_id(), "alfie");
41///
42/// let newer = Entry::prefilled_builder(&entry).timestamp(99999).build().unwrap();
43/// assert!(newer.wdm_prunes(&entry));
44/// ```
45///
46/// [Spec definition](https://willowprotocol.org/specs/data-model/index.html#Entry).
47#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Hash)]
48#[cfg_attr(feature = "dev", derive(Arbitrary))]
49pub struct Entry<const MCL: usize, const MCC: usize, const MPL: usize, N, S, PD> {
50    pub(crate) namespace_id: N,
51    pub(crate) subspace_id: S,
52    pub(crate) path: Path<MCL, MCC, MPL>,
53    pub(crate) timestamp: Timestamp,
54    pub(crate) payload_length: u64,
55    pub(crate) payload_digest: PD,
56}
57
58impl<const MCL: usize, const MCC: usize, const MPL: usize, N, S, PD>
59    Entry<MCL, MCC, MPL, N, S, PD>
60{
61    /// Creates a builder for [`Entry`].
62    ///
63    /// # Examples
64    ///
65    /// ```
66    /// use willow_data_model::prelude::*;
67    ///
68    /// // Supplying incomplete data errors.
69    /// assert!(
70    ///     Entry::builder()
71    ///     .path(Path::<4, 4, 4>::new())
72    ///     .namespace_id("family")
73    ///     .subspace_id("alfie")
74    ///     .payload_digest("some_hash")
75    ///     // timestamp and payload_length are missing!
76    ///     .build().is_err()
77    /// );
78    ///
79    /// // Supplying all necessary data yields an entry.
80    /// let entry = Entry::builder()
81    ///     .namespace_id("family")
82    ///     .subspace_id("alfie")
83    ///     .path(Path::<4, 4, 4>::new())
84    ///     .timestamp(12345)
85    ///     .payload_digest("some_hash")
86    ///     .payload_length(17)
87    ///     .build().unwrap();
88    ///
89    /// assert_eq!(*entry.wdm_subspace_id(), "alfie");
90    /// ```
91    pub fn builder() -> EntryBuilder<MCL, MCC, MPL, N, S, PD> {
92        EntryBuilder::create_empty()
93    }
94
95    /// Creates a builder which is prefilled with the data from some other entry.
96    ///
97    /// Use this function to create modified copies of entries.
98    ///
99    /// # Examples
100    ///
101    /// ```
102    /// use willow_data_model::prelude::*;
103    ///
104    /// // Supplying all necessary data yields an entry.
105    /// let first_entry = Entry::builder()
106    ///     .namespace_id("family")
107    ///     .subspace_id("alfie")
108    ///     .path(Path::<4, 4, 4>::new())
109    ///     .timestamp(12345)
110    ///     .payload_digest("some_hash")
111    ///     .payload_length(17)
112    ///     .build().unwrap();
113    ///
114    /// assert_eq!(*first_entry.wdm_payload_digest(), "some_hash");
115    ///
116    /// let second_entry = Entry::prefilled_builder(&first_entry)
117    ///     .timestamp(67890)
118    ///     .payload_digest("another_hash")
119    ///     .payload_length(4)
120    ///     .build().unwrap();
121    ///
122    /// assert_eq!(*second_entry.wdm_payload_digest(), "another_hash");
123    /// ```
124    pub fn prefilled_builder<E>(source: &E) -> EntryBuilder<MCL, MCC, MPL, N, S, PD>
125    where
126        E: Entrylike<MCL, MCC, MPL, N, S, PD> + ?Sized,
127        N: Clone,
128        S: Clone,
129        PD: Clone,
130    {
131        let mut builder = Self::builder();
132
133        builder
134            .namespace_id(source.wdm_namespace_id().clone())
135            .subspace_id(source.wdm_subspace_id().clone())
136            .path(source.wdm_path().clone())
137            .timestamp(source.wdm_timestamp())
138            .payload_digest(source.wdm_payload_digest().clone())
139            .payload_length(source.wdm_payload_length());
140
141        builder
142    }
143
144    /// Creates an [`Entry`] with [equal data](EntrylikeExt::wdm_entry_eq) to that of the given [`Entrylike`].
145    ///
146    /// ```
147    /// # #[cfg(feature = "dev")] {
148    /// use willow_data_model::prelude::*;
149    /// use willow_data_model::test_parameters::*;
150    ///
151    /// let entry1 = Entry::builder()
152    ///     .namespace_id(Family)
153    ///     .subspace_id(Alfie)
154    ///     .path(Path::<4, 4, 4>::new())
155    ///     .timestamp(12345)
156    ///     .payload_digest(Spades)
157    ///     .payload_length(2)
158    ///     .build().unwrap();
159    ///
160    /// let entry2 = Entry::from_entrylike(&entry1);
161    /// assert_eq!(entry1, entry2);
162    /// # }
163    /// ```
164    pub fn from_entrylike<E>(entrylike_to_clone: &E) -> Self
165    where
166        E: Entrylike<MCL, MCC, MPL, N, S, PD> + ?Sized,
167        N: Clone,
168        S: Clone,
169        PD: Clone,
170    {
171        Self::prefilled_builder(entrylike_to_clone).build().unwrap()
172    }
173
174    /// Turns `self` into an [`AuthorisedEntry`] by creating an authorisation token for it.
175    ///
176    /// ```
177    /// # #[cfg(feature = "dev")] {
178    /// use willow_data_model::prelude::*;
179    /// use willow_data_model::test_parameters::*;
180    ///
181    /// let entry = Entry::builder()
182    ///     .namespace_id(Family)
183    ///     .subspace_id(Alfie)
184    ///     .path(Path::<4, 4, 4>::new())
185    ///     .timestamp(12345)
186    ///     .payload_digest(Spades)
187    ///     .payload_length(2)
188    ///     .build().unwrap();
189    ///
190    /// let authed = entry.into_authorised_entry::<TestSubspaceSignature>(&AlfieSecret).unwrap();
191    /// assert_eq!(authed.as_authorisation_token(), &AlfieSignature);
192    /// # }
193    /// ```
194    pub fn into_authorised_entry<AT>(
195        self,
196        ingredients: &AT::Ingredients,
197    ) -> Result<AuthorisedEntry<MCL, MCC, MPL, N, S, PD, AT>, AT::CreationError>
198    where
199        AT: AuthorisationToken<MCL, MCC, MPL, N, S, PD> + Debug,
200        N: Clone + Debug,
201        S: Clone + Debug,
202        PD: Clone + Debug,
203    {
204        let authorisation_token = AuthorisationToken::new_for_entry(&self, ingredients)?;
205
206        Ok(PossiblyAuthorisedEntry {
207                entry: self,
208                authorisation_token,
209            }
210            .into_authorised_entry().expect("AuthorisationToken::new_for_entry must produce an authorisation token that authorises the entry"))
211    }
212}
213
214impl<const MCL: usize, const MCC: usize, const MPL: usize, N, S, PD> Keylike<MCL, MCC, MPL, S>
215    for Entry<MCL, MCC, MPL, N, S, PD>
216{
217    fn wdm_subspace_id(&self) -> &S {
218        &self.subspace_id
219    }
220
221    fn wdm_path(&self) -> &Path<MCL, MCC, MPL> {
222        &self.path
223    }
224}
225
226impl<const MCL: usize, const MCC: usize, const MPL: usize, N, S, PD>
227    Coordinatelike<MCL, MCC, MPL, S> for Entry<MCL, MCC, MPL, N, S, PD>
228{
229    fn wdm_timestamp(&self) -> Timestamp {
230        self.timestamp
231    }
232}
233
234impl<const MCL: usize, const MCC: usize, const MPL: usize, N, S, PD> Namespaced<N>
235    for Entry<MCL, MCC, MPL, N, S, PD>
236{
237    fn wdm_namespace_id(&self) -> &N {
238        &self.namespace_id
239    }
240}
241
242impl<const MCL: usize, const MCC: usize, const MPL: usize, N, S, PD>
243    Entrylike<MCL, MCC, MPL, N, S, PD> for Entry<MCL, MCC, MPL, N, S, PD>
244{
245    fn wdm_payload_length(&self) -> u64 {
246        self.payload_length
247    }
248
249    fn wdm_payload_digest(&self) -> &PD {
250        &self.payload_digest
251    }
252}
253
254/// Implements [encode_entry](https://willowprotocol.org/specs/encodings/index.html#encode_entry).
255impl<const MCL: usize, const MCC: usize, const MPL: usize, N, S, PD> Encodable
256    for Entry<MCL, MCC, MPL, N, S, PD>
257where
258    N: Encodable,
259    S: Encodable,
260    PD: Encodable,
261{
262    async fn encode<C>(&self, consumer: &mut C) -> Result<(), C::Error>
263    where
264        C: BulkConsumer<Item = u8> + ?Sized,
265    {
266        self.wdm_encode_entry(consumer).await
267    }
268}
269
270/// Implements [encode_entry](https://willowprotocol.org/specs/encodings/index.html#encode_entry).
271impl<const MCL: usize, const MCC: usize, const MPL: usize, N, S, PD> EncodableKnownLength
272    for Entry<MCL, MCC, MPL, N, S, PD>
273where
274    N: EncodableKnownLength,
275    S: EncodableKnownLength,
276    PD: EncodableKnownLength,
277{
278    fn len_of_encoding(&self) -> usize {
279        self.wdm_length_of_entry_encoding()
280    }
281}
282
283/// Implements [EncodeEntry](https://willowprotocol.org/specs/encodings/index.html#EncodeEntry).
284impl<const MCL: usize, const MCC: usize, const MPL: usize, N, S, PD> Decodable
285    for Entry<MCL, MCC, MPL, N, S, PD>
286where
287    N: DecodableCanonic<ErrorReason: Into<Blame>, ErrorCanonic: Into<Blame>>,
288    S: DecodableCanonic<ErrorReason: Into<Blame>, ErrorCanonic: Into<Blame>>,
289    PD: DecodableCanonic<ErrorReason: Into<Blame>, ErrorCanonic: Into<Blame>>,
290{
291    type ErrorReason = Blame;
292
293    async fn decode<P>(
294        producer: &mut P,
295    ) -> Result<Self, DecodeError<P::Final, P::Error, Self::ErrorReason>>
296    where
297        P: BulkProducer<Item = u8> + ?Sized,
298        Self: Sized,
299    {
300        Ok(Self {
301            namespace_id: producer
302                .produce_decoded_canonic()
303                .await
304                .map_err(|err| err.map_other(Into::into))?,
305            subspace_id: producer
306                .produce_decoded_canonic()
307                .await
308                .map_err(|err| err.map_other(Into::into))?,
309            path: producer.produce_decoded().await?,
310            timestamp: cu64_decode_standalone(producer)
311                .await
312                .map_err(|err| err.map_other(|_| unreachable!()))?
313                .into(),
314            payload_length: cu64_decode_standalone(producer)
315                .await
316                .map_err(|err| err.map_other(|_| unreachable!()))?,
317            payload_digest: producer
318                .produce_decoded_canonic()
319                .await
320                .map_err(|err| err.map_other(Into::into))?,
321        })
322    }
323}
324
325/// Implements [encode_entry](https://willowprotocol.org/specs/encodings/index.html#encode_entry).
326impl<const MCL: usize, const MCC: usize, const MPL: usize, N, S, PD> DecodableCanonic
327    for Entry<MCL, MCC, MPL, N, S, PD>
328where
329    N: DecodableCanonic<ErrorReason: Into<Blame>, ErrorCanonic: Into<Blame>>,
330    S: DecodableCanonic<ErrorReason: Into<Blame>, ErrorCanonic: Into<Blame>>,
331    PD: DecodableCanonic<ErrorReason: Into<Blame>, ErrorCanonic: Into<Blame>>,
332{
333    type ErrorCanonic = Blame;
334
335    async fn decode_canonic<P>(
336        producer: &mut P,
337    ) -> Result<Self, DecodeError<P::Final, P::Error, Self::ErrorCanonic>>
338    where
339        P: BulkProducer<Item = u8> + ?Sized,
340        Self: Sized,
341    {
342        Ok(Self {
343            namespace_id: producer
344                .produce_decoded_canonic()
345                .await
346                .map_err(|err| err.map_other(Into::into))?,
347            subspace_id: producer
348                .produce_decoded_canonic()
349                .await
350                .map_err(|err| err.map_other(Into::into))?,
351            path: producer.produce_decoded_canonic().await?,
352            timestamp: cu64_decode_canonic_standalone(producer)
353                .await
354                .map_err(|err| err.map_other(|_| unreachable!()))?
355                .into(),
356            payload_length: cu64_decode_canonic_standalone(producer)
357                .await
358                .map_err(|err| err.map_other(|_| unreachable!()))?,
359            payload_digest: producer
360                .produce_decoded_canonic()
361                .await
362                .map_err(|err| err.map_other(Into::into))?,
363        })
364    }
365}