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}