Skip to main content

content_addressable/
trait_def.rs

1//! The [`ContentAddressable`] trait — the one thing a type implements to gain
2//! a self-certifying identity.
3//!
4//! A type becomes content-addressable by defining a single method,
5//! [`canonical_form`](ContentAddressable::canonical_form), that produces its
6//! deterministic byte representation. Everything else — computing the
7//! [`ContentId`] and verifying against an expected id — is provided.
8
9use serde::de::DeserializeOwned;
10
11use crate::canonical;
12use crate::content_id::ContentId;
13use crate::error::ContentError;
14
15/// A value that can name itself by its content.
16///
17/// Implementors provide [`canonical_form`](Self::canonical_form); the
18/// [`content_id`](Self::content_id) and [`verify`](Self::verify) methods are
19/// derived from it for free.
20///
21/// # Implementing
22///
23/// For any `#[derive(serde::Serialize)]` type, the canonical form is just its
24/// canonical dag-cbor encoding, so the implementation is one line:
25///
26/// ```
27/// use content_addressable::{canonical, ContentAddressable, ContentError};
28/// use serde::Serialize;
29///
30/// #[derive(Serialize)]
31/// struct Block {
32///     parent: Option<String>,
33///     payload: Vec<u8>,
34/// }
35///
36/// impl ContentAddressable for Block {
37///     fn canonical_form(&self) -> Result<Vec<u8>, ContentError> {
38///         canonical::to_canonical_dagcbor(self)
39///     }
40/// }
41///
42/// let block = Block { parent: None, payload: vec![1, 2, 3] };
43/// let id = block.content_id().unwrap();
44/// assert!(block.verify(&id).unwrap());
45/// ```
46pub trait ContentAddressable {
47    /// Produce the deterministic byte representation of this value.
48    ///
49    /// This is the *only* required method. The canonical bytes must be a
50    /// function of the value alone: equal values must produce equal bytes.
51    /// The recommended implementation defers to
52    /// [`to_canonical_dagcbor`](crate::canonical::to_canonical_dagcbor) on a
53    /// `Serialize` type, which guarantees that property.
54    ///
55    /// # Errors
56    ///
57    /// Returns [`ContentError`] if the value cannot be canonically encoded.
58    fn canonical_form(&self) -> Result<Vec<u8>, ContentError>;
59
60    /// Compute this value's [`ContentId`].
61    ///
62    /// Hashes the [`canonical_form`](Self::canonical_form) into a BLAKE3
63    /// CIDv1. Override only if you have a faster path that is provably
64    /// identical to the default.
65    ///
66    /// # Errors
67    ///
68    /// Propagates any error from [`canonical_form`](Self::canonical_form).
69    fn content_id(&self) -> Result<ContentId, ContentError> {
70        Ok(ContentId::from_canonical_bytes(&self.canonical_form()?))
71    }
72
73    /// Check whether this value's content id matches an `expected` id.
74    ///
75    /// Returns `Ok(true)` if the recomputed id equals `expected`, `Ok(false)`
76    /// otherwise. This is the integrity check at the heart of the doctrine:
77    /// the value re-derives its own identity and compares it to the claim.
78    ///
79    /// # Mismatch is `Ok(false)`, not an error (FROZEN at 0.1.0)
80    ///
81    /// A *mismatch* is **not** an error — it returns `Ok(false)`. A negative
82    /// answer to "do these match?" is a successful, expected result, not a
83    /// failure to check; forcing it through the `Err` channel would conflate "I
84    /// checked, the answer is no" with "I couldn't check". The `?`-friendly
85    /// `Result<bool>` also composes cleanly in boolean logic
86    /// (`if a.verify(&id1)? && b.verify(&id2)? { … }`). This return contract is a
87    /// **frozen** part of the `0.1.0` API surface (README gate item #8) —
88    /// flipping `Ok(false)` to an `Err` arm later would be a breaking change.
89    ///
90    /// If you want a mismatch to short-circuit via `?`, use the strict helper
91    /// [`ensure_content_id`](Self::ensure_content_id), which returns
92    /// `Err(`[`ContentError::VerificationFailed`]`)` on mismatch — don't hand-roll
93    /// it.
94    ///
95    /// # Errors
96    ///
97    /// Propagates any error from [`content_id`](Self::content_id) only (i.e. an
98    /// encoding failure in [`canonical_form`](Self::canonical_form)); never an `Err` on a clean
99    /// mismatch.
100    fn verify(&self, expected: &ContentId) -> Result<bool, ContentError> {
101        Ok(&self.content_id()? == expected)
102    }
103
104    /// Verify, returning an **error on mismatch** instead of `Ok(false)`.
105    ///
106    /// Like [`verify`](Self::verify), but a mismatch is reported as
107    /// `Err(`[`ContentError::VerificationFailed`]`)` carrying both ids (as their
108    /// [`Display`](core::fmt::Display) strings, multibase base32-lower `b…`),
109    /// rather than `Ok(false)`. On a match it returns `Ok(())`. Use this when a
110    /// mismatch should short-circuit through `?`; use [`verify`](Self::verify)
111    /// when you want the boolean to compose in further logic.
112    ///
113    /// This is the strict form the crate's doctrine promises: it makes
114    /// [`ContentError::VerificationFailed`] a real, reachable, tested error path
115    /// rather than a name callers must construct by hand. It is a defaulted trait
116    /// method, so every implementor gets it for free. Both [`verify`](Self::verify) and this
117    /// helper are **frozen** for the `0.1.0` API surface (README gate item #8).
118    ///
119    /// # Errors
120    ///
121    /// - [`ContentError::VerificationFailed`] if the recomputed id differs from
122    ///   `expected`.
123    /// - Otherwise propagates any error from [`content_id`](Self::content_id)
124    ///   (e.g. an encoding failure in [`canonical_form`](Self::canonical_form)).
125    fn ensure_content_id(&self, expected: &ContentId) -> Result<(), ContentError> {
126        let computed = self.content_id()?;
127        if &computed == expected {
128            Ok(())
129        } else {
130            Err(ContentError::VerificationFailed {
131                expected: expected.to_string(),
132                computed: computed.to_string(),
133            })
134        }
135    }
136
137    /// **Checked ingress**: decode a value from its canonical form, proving the
138    /// bytes reproduce it — a checked *partial* inverse of
139    /// [`canonical_form`](Self::canonical_form).
140    ///
141    /// This is the door for bytes you did not produce: foreign, stored, or off
142    /// the wire.
143    ///
144    /// # The enforced invariant
145    ///
146    /// On success, and unconditionally:
147    ///
148    /// ```
149    /// # use content_addressable::{canonical, ContentAddressable, ContentError};
150    /// # use serde::{Deserialize, Serialize};
151    /// # #[derive(Serialize, Deserialize)] struct T { a: u64 }
152    /// # impl ContentAddressable for T {
153    /// #     fn canonical_form(&self) -> Result<Vec<u8>, ContentError> {
154    /// #         canonical::to_canonical_dagcbor(self)
155    /// #     }
156    /// # }
157    /// # let bytes: &[u8] = &T { a: 1 }.canonical_form()?;
158    /// let value = T::from_canonical_form(bytes)?;
159    /// assert_eq!(value.canonical_form()?, bytes);
160    /// # Ok::<(), ContentError>(())
161    /// ```
162    ///
163    /// That is the whole guarantee, and it is a claim about **bytes**, not about
164    /// the codec: it is established by re-encoding the decoded value and
165    /// comparing, not by appealing to dag-cbor being canonical. Exact byte
166    /// equality is also strictly stronger than comparing two
167    /// [`ContentId`]s — it assumes no collision resistance.
168    ///
169    /// ## Corollary for a lawful implementation
170    ///
171    /// Call an implementation **lawful** when its
172    /// [`content_id`](Self::content_id) agrees with the trait law —
173    /// `content_id() == ContentId::from_canonical_bytes(&canonical_form()?)`,
174    /// which the provided default satisfies by construction. For a lawful
175    /// implementation the invariant above gives:
176    ///
177    /// ```text
178    /// value.content_id()? == ContentId::from_canonical_bytes(bytes)
179    /// ```
180    ///
181    /// This is a **corollary, not a guarantee**. [`content_id`](Self::content_id)
182    /// is overridable, and an override that is not provably identical to the
183    /// default breaks the equation. Nothing here calls [`content_id`](Self::content_id), so **the
184    /// crate cannot detect that**. A caller who needs the equation held rather
185    /// than assumed checks it, with
186    /// [`ensure_content_id`](Self::ensure_content_id):
187    ///
188    /// ```text
189    /// value.ensure_content_id(&ContentId::from_canonical_bytes(bytes))?;
190    /// ```
191    ///
192    /// # Partial: the domain, and which prerequisite failed
193    ///
194    /// It succeeds on exactly those `bytes` that are
195    ///
196    /// 1. **canonical dag-cbor** — otherwise [`ContentError::DecodingError`] (not
197    ///    dag-cbor at all) or [`ContentError::NonCanonical`] (dag-cbor, but not
198    ///    the canonical encoding). Established generically, *before* any `Self`
199    ///    is constructed, so the result does not lean on this type's serde impl
200    ///    being well behaved;
201    /// 2. **directly deserializable as `Self`** — otherwise
202    ///    [`ContentError::DecodingError`];
203    /// 3. **reproduced byte-for-byte by the decoded value's [`canonical_form`](Self::canonical_form)** —
204    ///    otherwise [`ContentError::LossyDecode`].
205    ///
206    /// Every failure names the prerequisite that was not met.
207    ///
208    /// # What "inverse" does and does not mean here
209    ///
210    /// On its accepted byte domain, decoding and re-encoding reproduces the
211    /// original canonical bytes. The returned value therefore has the same
212    /// canonical representation and content identity; it is **not necessarily
213    /// equal to a previously encoded in-memory value** unless the type separately
214    /// guarantees a value-preserving serde round trip.
215    ///
216    /// `Serialize + DeserializeOwned` does not establish that. A
217    /// `#[serde(skip)]` field is the clean counterexample: it is absent from the
218    /// encoding entirely, so two values differing only in it share **identical**
219    /// canonical bytes, and decoding returns the default rather than what was
220    /// encoded. This door correctly accepts that — those bytes really are that
221    /// value's canonical representation. Identity is preserved; the in-memory
222    /// value need not be.
223    ///
224    /// A plain `#[serde(default)]` field is **not** such a case. It only supplies
225    /// a value when one is missing on the way in; ordinary derived serialization
226    /// writes it back out, so the re-encoding differs from the input and stage 3
227    /// reports [`ContentError::LossyDecode`]. It joins `#[serde(skip)]` here only
228    /// if it is *also* omitted when serializing.
229    ///
230    /// A second, separate limit applies to the *shape* of the type:
231    ///
232    /// A type may define a [`canonical_form`](Self::canonical_form) that is deliberately **not** its
233    /// serde representation — an envelope, a versioned framing, a projection —
234    /// and still be perfectly lawful for content addressing: its identity is
235    /// well defined and reproducible. But its canonical bytes are then not
236    /// directly deserializable as `Self` unless it also supplies a matching
237    /// `Deserialize`, so [`from_canonical_form`](Self::from_canonical_form) on its *own* output fails
238    /// prerequisite 2 with [`ContentError::DecodingError`].
239    ///
240    /// That is not a defect in the type and does not make its [`canonical_form`](Self::canonical_form)
241    /// unlawful. It means this method is checked ingress for the common
242    /// representation, not a universal inverse: a custom canonical form needs a
243    /// custom decoder to match it. What the method never does is *guess* —
244    /// it refuses rather than return a value the bytes do not reproduce.
245    ///
246    /// # Why stage 3 re-encodes through [`canonical_form`](Self::canonical_form)
247    ///
248    /// Deliberately, rather than through
249    /// [`to_canonical_dagcbor`](crate::canonical::to_canonical_dagcbor):
250    /// [`canonical_form`](Self::canonical_form) is the function that *defines* this type's identity, so
251    /// it is the only re-encode whose equality proves the invariant. For the
252    /// recommended one-line implementation the two coincide; for a type with its
253    /// own canonical form they do not, and only this one is sound. It is also why
254    /// the bound is `DeserializeOwned + Sized` and not also `Serialize`.
255    ///
256    /// For a plain `Serialize + Deserialize` value that is not
257    /// [`ContentAddressable`](Self), use
258    /// [`canonical::from_canonical_dagcbor_checked`], whose stage 3 is
259    /// [`to_canonical_dagcbor`](crate::canonical::to_canonical_dagcbor) instead.
260    ///
261    /// # Overriding
262    ///
263    /// Like [`content_id`](Self::content_id), override this only if you have a
264    /// path that is *provably identical*. An override that skips a stage does
265    /// **not** weaken the store seam — `NodeStoreExt::get_node` (feature
266    /// `unstable-store`) deliberately runs the shared body rather than this
267    /// method, so a `T` cannot hand itself a pass — but it does weaken every
268    /// direct caller of `T::from_canonical_form`.
269    ///
270    /// # Availability
271    ///
272    /// Added in `0.1.2` (issue #90) as a defaulted associated function, so every
273    /// implementor gets it for free; it carries `where Self: Sized`, so the trait
274    /// stays dyn-compatible. It is the one deliberate exception `0.1.2` makes to
275    /// the otherwise frozen `0.1.x` Rust API — see `docs/STABILITY.md`.
276    ///
277    /// # Errors
278    ///
279    /// - [`ContentError::DecodingError`] — the bytes are not dag-cbor, or do not
280    ///   decode as a `Self` (prerequisites 1 and 2).
281    /// - [`ContentError::NonCanonical`] — the bytes are dag-cbor but not the
282    ///   canonical encoding (prerequisite 1).
283    /// - [`ContentError::LossyDecode`] — the decoded value's [`canonical_form`](Self::canonical_form)
284    ///   differs from the input (prerequisite 3).
285    /// - Anything [`canonical_form`](Self::canonical_form) itself returns,
286    ///   propagated verbatim — typically [`ContentError::EncodingError`].
287    fn from_canonical_form(bytes: &[u8]) -> Result<Self, ContentError>
288    where
289        Self: DeserializeOwned + Sized,
290    {
291        match checked_decode::<Self>(bytes)? {
292            CheckedDecode::Value(value) => Ok(value),
293            CheckedDecode::Lossy => Err(ContentError::LossyDecode),
294        }
295    }
296}
297
298/// The verdict of [`checked_decode`].
299///
300/// The lossy case is a **value, not an error**, for two reasons. It lets each
301/// caller name the verdict in its own vocabulary (the store seam calls it
302/// `RepresentationMismatch`, which can point at the id that lied). And it keeps a
303/// [`ContentError::LossyDecode`] that came out of the *caller's own*
304/// [`canonical_form`](crate::ContentAddressable::canonical_form) distinguishable from this function's own byte comparison — an
305/// error value alone could not tell the two apart, and the seam would blame the
306/// stored bytes for a failure inside the type's encoder.
307pub(crate) enum CheckedDecode<T> {
308    /// The bytes decoded as a `T` whose [`canonical_form`](crate::ContentAddressable::canonical_form) reproduces them exactly.
309    Value(T),
310    /// Typed round-trip mismatch: the decode succeeded, but the decoded value's
311    /// [`canonical_form`](crate::ContentAddressable::canonical_form) differs from
312    /// the input bytes, so those bytes are not its canonical representation.
313    Lossy,
314}
315
316/// The crate's one identity-preserving decode: canonical bytes, a typed decode,
317/// and a forward re-encode through the type's own [`canonical_form`](crate::ContentAddressable::canonical_form).
318///
319/// This is the body of [`ContentAddressable::from_canonical_form`], factored out
320/// so the **store seam can run it without going through that method**.
321/// [`from_canonical_form`](crate::ContentAddressable::from_canonical_form) is defaulted on an unsealed trait, so a downstream `T`
322/// can override it — including with a bare, unverified decode. A seam whose
323/// integrity guarantee routed through it would be handing that guarantee to `T`,
324/// which is precisely the bug class this line of work exists to close. Stage 3
325/// necessarily consults `T::canonical_form` (that is what identity *means* for a
326/// `ContentAddressable`); stages 1 and 2 must not.
327///
328/// # Errors
329///
330/// [`ContentError::NonCanonical`], [`ContentError::DecodingError`] and
331/// [`ContentError::EncodingError`] from the three stages, in that order. Whatever
332/// [`canonical_form`](crate::ContentAddressable::canonical_form) returns is propagated **verbatim** — the lossy verdict
333/// travels as `Ok(`[`CheckedDecode::Lossy`]`)`, never as an error.
334pub(crate) fn checked_decode<T>(bytes: &[u8]) -> Result<CheckedDecode<T>, ContentError>
335where
336    T: DeserializeOwned + ContentAddressable,
337{
338    // 1. The bytes are canonical — proven generically, so this does not rely on
339    //    `T::canonical_form` being a lawful (canonical) implementation.
340    canonical::ensure_canonical(bytes)?;
341    // 2. They decode as a `T`.
342    let value: T = canonical::decode_dagcbor(bytes)?;
343    // 3. …and re-encoding through the function that defines this type's identity
344    //    reproduces them exactly. Inequality is decisive: the value is NOT the one
345    //    these bytes name.
346    if value.canonical_form()? != bytes {
347        return Ok(CheckedDecode::Lossy);
348    }
349    Ok(CheckedDecode::Value(value))
350}