Skip to main content

willow_data_model/paths/
private.rs

1//! Module providing abstractions for [`PrivatePathContext`](https://willowprotocol.org/specs/encodings/index.html#PrivatePathContext)s, which facilitate [private encodings](https://willowprotocol.org/specs/encodings/index.html#enc_private). The [`PrivatePathContext`] is the common context between two parties which makes it possible to decode a private encoding. You probably don't need to interact with this unless you are building your own private encoding scheme.
2
3use crate::paths::{Path, path_extends_path};
4#[cfg(feature = "dev")]
5use arbitrary::Arbitrary;
6use compact_u64::{cu64_decode_standalone, cu64_encode_standalone};
7use derive_more::{Display, Error};
8use ufotofu::{
9    codec::{Blame, DecodeError},
10    codec_prelude::RelativeDecodable,
11    codec_relative::RelativeEncodable,
12};
13
14#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Hash)]
15/// The immutable [`PrivatePathContext`](https://willowprotocol.org/specs/encodings/index.html#PrivatePathContext) necessary to privately encode a [`Path`] relative to one of its prefixes, while keeping secret all Components that coincide with a third Path.
16pub struct PrivatePathContext<const MCL: usize, const MCC: usize, const MPL: usize> {
17    /// The Path whose Components are to be kept private.
18    private: Path<MCL, MCC, MPL>,
19    /// The prefix relative to which we encode.
20    rel: Path<MCL, MCC, MPL>,
21}
22
23/// An error arising from trying to construct an invalid [`PrivatePathContext`]
24#[derive(Debug, Display, Error, Clone, Copy)]
25#[display("private and relative paths are not related")]
26pub struct ComponentsNotRelatedError;
27
28impl<const MCL: usize, const MCC: usize, const MPL: usize> PrivatePathContext<MCL, MCC, MPL> {
29    /// Returns a new [`PrivatePathContext`] with the given private and relative paths.
30    ///
31    /// Will return a [`ComponentsNotRelatedError`] if the given private and relative paths are not [related](https://willowprotocol.org/specs/data-model/index.html#path_related).
32    pub fn new(
33        private: Path<MCL, MCC, MPL>,
34        rel: Path<MCL, MCC, MPL>,
35    ) -> Result<Self, ComponentsNotRelatedError> {
36        if !private.is_related_to(&rel) {
37            return Err(ComponentsNotRelatedError {});
38        }
39
40        Ok(Self { private, rel })
41    }
42
43    /// Returns a new [`PrivatePathContext`] with the given private and relative paths *without* checking if the given private and relative paths are [related](https://willowprotocol.org/specs/data-model/index.html#path_related).
44    ///
45    /// #### Safety
46    ///
47    /// Undefined behaviour if private and rel are not [related](https://willowprotocol.org/specs/data-model/index.html#path_related).
48    pub unsafe fn new_unchecked(private: Path<MCL, MCC, MPL>, rel: Path<MCL, MCC, MPL>) -> Self {
49        Self { private, rel }
50    }
51
52    /// Returns the private path of `&self`.
53    pub fn private(&self) -> &Path<MCL, MCC, MPL> {
54        &self.private
55    }
56
57    /// Returns the relative path of `&self`.
58    pub fn rel(&self) -> &Path<MCL, MCC, MPL> {
59        &self.rel
60    }
61}
62
63#[cfg(feature = "dev")]
64impl<'a, const MCL: usize, const MCC: usize, const MPL: usize> Arbitrary<'a>
65    for PrivatePathContext<MCL, MCC, MPL>
66{
67    fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
68        let private: Path<MCL, MCC, MPL> = Arbitrary::arbitrary(u)?;
69        let rel: Path<MCL, MCC, MPL> = Arbitrary::arbitrary(u)?;
70
71        if !private.is_related_to(&rel) {
72            return Err(arbitrary::Error::IncorrectFormat);
73        }
74
75        Ok(Self { private, rel })
76    }
77}
78
79impl<const MCL: usize, const MCC: usize, const MPL: usize>
80    RelativeEncodable<PrivatePathContext<MCL, MCC, MPL>> for Path<MCL, MCC, MPL>
81{
82    async fn relative_encode<C>(
83        &self,
84        rel: &PrivatePathContext<MCL, MCC, MPL>,
85        consumer: &mut C,
86    ) -> Result<(), C::Error>
87    where
88        C: ufotofu::BulkConsumer<Item = u8> + ?Sized,
89    {
90        let rel_count = rel.rel.component_count();
91        let private_count = rel.private.component_count();
92
93        if private_count <= rel_count {
94            path_extends_path::encode_path_extends_path(self, &rel.rel, consumer).await?;
95        } else {
96            let lcp = self.longest_common_prefix(&rel.private);
97
98            let lcp_len = lcp.component_count();
99            cu64_encode_standalone(lcp_len as u64, consumer).await?;
100
101            if lcp_len >= private_count {
102                path_extends_path::encode_path_extends_path(self, rel.private(), consumer).await?;
103            }
104        }
105
106        Ok(())
107    }
108
109    /// Fails if the path is not a [prefix](https://willowprotocol.org/specs/data-model/index.html#path_prefix) of `rel.rel`, OR if `self` is not [related to]((https://willowprotocol.org/specs/data-model/index.html#path_related)) to `rel.private`.
110    fn can_be_encoded_relative_to(&self, rel: &PrivatePathContext<MCL, MCC, MPL>) -> bool {
111        if !rel.rel().is_prefix_of(self) {
112            return false;
113        }
114
115        if !self.is_related_to(rel.private()) {
116            return false;
117        }
118
119        true
120    }
121}
122
123impl<const MCL: usize, const MCC: usize, const MPL: usize>
124    RelativeDecodable<PrivatePathContext<MCL, MCC, MPL>> for Path<MCL, MCC, MPL>
125{
126    type ErrorReason = Blame;
127
128    async fn relative_decode<P>(
129        rel: &PrivatePathContext<MCL, MCC, MPL>,
130        producer: &mut P,
131    ) -> Result<Self, ufotofu::codec::DecodeError<P::Final, P::Error, Self::ErrorReason>>
132    where
133        P: ufotofu::BulkProducer<Item = u8> + ?Sized,
134        Self: Sized,
135    {
136        let rel_count = rel.rel.component_count();
137        let private_count = rel.private.component_count();
138
139        if private_count <= rel_count {
140            path_extends_path::decode_path_extends_path(rel.rel(), producer).await
141        } else {
142            let private_component_count = cu64_decode_standalone(producer)
143                .await
144                .map_err(|err| err.map_other(|_| Blame::TheirFault))?;
145
146            // This filters out the case where the decoded value is not a prefix of rel.rel.
147            if private_component_count < rel.rel().component_count() as u64 {
148                return Err(DecodeError::Other(Blame::TheirFault));
149            }
150
151            if private_component_count >= private_count as u64 {
152                path_extends_path::decode_path_extends_path(rel.private(), producer).await
153            } else {
154                let decoded = rel
155                    .private
156                    .create_prefix(private_component_count as usize)
157                    .ok_or(DecodeError::Other(Blame::TheirFault))?;
158
159                Ok(decoded)
160            }
161        }
162    }
163}