Skip to main content

hyphae_engine/retrieval_proof/
model.rs

1// SPDX-License-Identifier: Apache-2.0
2
3use std::{io, time::Duration};
4
5use hyphae_core::VectorValueError;
6use hyphae_retrieval::{
7    ExactRetrievalError, ExactRetrievalOutcome, ExactRetrievalRequest, HybridError, HybridOutcome,
8    HybridRequest, LexicalError, LexicalOutcome, LexicalRequest,
9};
10use hyphae_storage::{SnapshotError, SnapshotInfo, SnapshotReadLimits};
11use thiserror::Error;
12
13use crate::DocumentError;
14
15/// Version of the canonical retrieval-proof envelope.
16pub const RETRIEVAL_PROOF_FORMAT_VERSION: u16 = 1;
17
18/// Durable exact-retrieval semantics version bound into proofs.
19pub const EXACT_RETRIEVAL_SEMANTICS_VERSION: u16 = 2;
20
21/// Durable lexical-retrieval semantics version bound into proofs.
22pub const LEXICAL_RETRIEVAL_SEMANTICS_VERSION: u16 = 1;
23
24/// Deterministic hybrid-retrieval semantics version bound into proofs.
25pub const HYBRID_RETRIEVAL_SEMANTICS_VERSION: u16 = 1;
26
27/// Hard maximum canonical retrieval-proof file length.
28pub const MAX_RETRIEVAL_PROOF_BYTES: u64 = 64 * 1024 * 1024;
29
30/// Snapshot and log checkpoint identity trusted by one retrieval proof.
31#[derive(Clone, Debug, Eq, PartialEq)]
32pub struct RetrievalProofAnchor {
33    /// Materialized commit sequence captured by the snapshot.
34    pub checkpoint_sequence: u64,
35    /// Commit digest captured by the snapshot, absent only for empty history.
36    pub checkpoint_digest: Option<[u8; 32]>,
37    /// Canonical logical snapshot digest.
38    pub snapshot_digest: [u8; 32],
39}
40
41impl RetrievalProofAnchor {
42    /// Creates an anchor from already verified snapshot metadata.
43    pub fn from_snapshot(snapshot: &SnapshotInfo) -> Self {
44        Self {
45            checkpoint_sequence: snapshot.checkpoint_sequence,
46            checkpoint_digest: snapshot.checkpoint_digest,
47            snapshot_digest: snapshot.snapshot_digest,
48        }
49    }
50
51    /// Computes the caller-pinnable retrieval-specific anchor digest.
52    pub fn digest(&self) -> [u8; 32] {
53        let mut hasher = blake3::Hasher::new();
54        hasher.update(b"hyphae-retrieval-anchor-v1");
55        hasher.update(&self.checkpoint_sequence.to_le_bytes());
56        hasher.update(&self.checkpoint_digest.unwrap_or([0; 32]));
57        hasher.update(&self.snapshot_digest);
58        *hasher.finalize().as_bytes()
59    }
60}
61
62/// Canonical exact-retrieval proof with embedded request and outcome.
63#[derive(Clone, Debug, Eq, PartialEq)]
64pub struct ExactRetrievalProof {
65    pub(crate) anchor: RetrievalProofAnchor,
66    pub(crate) semantics_version: u16,
67    pub(crate) request: ExactRetrievalRequest,
68    pub(crate) outcome: ExactRetrievalOutcome,
69    pub(crate) proof_digest: [u8; 32],
70}
71
72/// Newly created proof and the canonical snapshot witness it references.
73#[derive(Clone, Debug, Eq, PartialEq)]
74pub struct ExactRetrievalProofArtifact {
75    /// Portable canonical proof.
76    pub proof: ExactRetrievalProof,
77    /// Verified local snapshot witness metadata and path.
78    pub snapshot: SnapshotInfo,
79}
80
81/// Canonical lexical-retrieval proof with embedded request and outcome.
82#[derive(Clone, Debug, Eq, PartialEq)]
83pub struct LexicalRetrievalProof {
84    pub(crate) anchor: RetrievalProofAnchor,
85    pub(crate) semantics_version: u16,
86    pub(crate) request: LexicalRequest,
87    pub(crate) outcome: LexicalOutcome,
88    pub(crate) proof_digest: [u8; 32],
89}
90
91/// Newly created lexical proof and canonical snapshot witness.
92#[derive(Clone, Debug, Eq, PartialEq)]
93pub struct LexicalRetrievalProofArtifact {
94    /// Portable canonical proof.
95    pub proof: LexicalRetrievalProof,
96    /// Verified local snapshot witness metadata and path.
97    pub snapshot: SnapshotInfo,
98}
99
100/// Canonical hybrid-retrieval proof with both complete branch executions.
101#[derive(Clone, Debug, Eq, PartialEq)]
102pub struct HybridRetrievalProof {
103    pub(crate) anchor: RetrievalProofAnchor,
104    pub(crate) semantics_version: u16,
105    pub(crate) lexical_request: LexicalRequest,
106    pub(crate) lexical_outcome: LexicalOutcome,
107    pub(crate) vector_request: ExactRetrievalRequest,
108    pub(crate) vector_outcome: ExactRetrievalOutcome,
109    pub(crate) fusion_request: HybridRequest,
110    pub(crate) outcome: HybridOutcome,
111    pub(crate) proof_digest: [u8; 32],
112}
113
114/// Newly created hybrid proof and canonical snapshot witness.
115#[derive(Clone, Debug, Eq, PartialEq)]
116pub struct HybridRetrievalProofArtifact {
117    /// Portable canonical proof.
118    pub proof: HybridRetrievalProof,
119    /// Verified local snapshot witness metadata and path.
120    pub snapshot: SnapshotInfo,
121}
122
123impl ExactRetrievalProof {
124    /// Returns the exact snapshot/log anchor.
125    pub fn anchor(&self) -> &RetrievalProofAnchor {
126        &self.anchor
127    }
128
129    /// Returns the caller-pinnable anchor digest.
130    pub fn anchor_digest(&self) -> [u8; 32] {
131        self.anchor.digest()
132    }
133
134    /// Returns the bound exact-retrieval semantics version.
135    pub fn semantics_version(&self) -> u16 {
136        self.semantics_version
137    }
138
139    /// Returns the complete proven request.
140    pub fn request(&self) -> &ExactRetrievalRequest {
141        &self.request
142    }
143
144    /// Returns the complete proven outcome.
145    pub fn outcome(&self) -> &ExactRetrievalOutcome {
146        &self.outcome
147    }
148
149    /// Returns the digest of the complete canonical proof bytes.
150    pub fn proof_digest(&self) -> [u8; 32] {
151        self.proof_digest
152    }
153}
154
155impl LexicalRetrievalProof {
156    /// Returns the exact snapshot/log anchor.
157    pub fn anchor(&self) -> &RetrievalProofAnchor {
158        &self.anchor
159    }
160
161    /// Returns the caller-pinnable anchor digest.
162    pub fn anchor_digest(&self) -> [u8; 32] {
163        self.anchor.digest()
164    }
165
166    /// Returns the bound lexical semantics version.
167    pub fn semantics_version(&self) -> u16 {
168        self.semantics_version
169    }
170
171    /// Returns the complete proven lexical request.
172    pub fn request(&self) -> &LexicalRequest {
173        &self.request
174    }
175
176    /// Returns the complete proven lexical outcome.
177    pub fn outcome(&self) -> &LexicalOutcome {
178        &self.outcome
179    }
180
181    /// Returns the digest of the canonical proof bytes.
182    pub fn proof_digest(&self) -> [u8; 32] {
183        self.proof_digest
184    }
185}
186
187impl HybridRetrievalProof {
188    /// Returns the exact snapshot/log anchor.
189    pub fn anchor(&self) -> &RetrievalProofAnchor {
190        &self.anchor
191    }
192
193    /// Returns the caller-pinnable anchor digest.
194    pub fn anchor_digest(&self) -> [u8; 32] {
195        self.anchor.digest()
196    }
197
198    /// Returns the bound hybrid semantics version.
199    pub fn semantics_version(&self) -> u16 {
200        self.semantics_version
201    }
202
203    /// Returns the complete proven lexical branch request.
204    pub fn lexical_request(&self) -> &LexicalRequest {
205        &self.lexical_request
206    }
207
208    /// Returns the complete proven lexical branch outcome.
209    pub fn lexical_outcome(&self) -> &LexicalOutcome {
210        &self.lexical_outcome
211    }
212
213    /// Returns the complete proven exact-vector branch request.
214    pub fn vector_request(&self) -> &ExactRetrievalRequest {
215        &self.vector_request
216    }
217
218    /// Returns the complete proven exact-vector branch outcome.
219    pub fn vector_outcome(&self) -> &ExactRetrievalOutcome {
220        &self.vector_outcome
221    }
222
223    /// Returns the deterministic fusion request.
224    pub fn fusion_request(&self) -> &HybridRequest {
225        &self.fusion_request
226    }
227
228    /// Returns the complete proven hybrid outcome.
229    pub fn outcome(&self) -> &HybridOutcome {
230        &self.outcome
231    }
232
233    /// Returns the digest of the canonical proof bytes.
234    pub fn proof_digest(&self) -> [u8; 32] {
235        self.proof_digest
236    }
237}
238
239/// Resource limits for complete offline exact-retrieval verification.
240#[derive(Clone, Debug, Eq, PartialEq)]
241pub struct RetrievalVerificationLimits {
242    /// Maximum proof file bytes accepted before allocation.
243    pub proof_bytes: u64,
244    /// Limits for loading the canonical snapshot witness.
245    pub snapshot: SnapshotReadLimits,
246    /// Maximum candidates accepted by exact replay.
247    pub max_candidates: u64,
248    /// Maximum aggregate key and vector bytes accepted by exact replay.
249    pub max_candidate_bytes: u64,
250    /// Maximum result count accepted by exact replay.
251    pub max_returned: usize,
252    /// Maximum durable documents accepted by lexical replay.
253    pub max_documents: u64,
254    /// Maximum normalized tokens accepted by lexical replay.
255    pub max_tokens: u64,
256    /// Maximum matching documents retained by lexical replay.
257    pub max_lexical_candidates: u64,
258    /// Maximum lexical result count accepted by replay.
259    pub max_lexical_returned: usize,
260    /// Maximum hybrid result count accepted by replay.
261    pub max_hybrid_returned: usize,
262    /// End-to-end cooperative verification deadline.
263    pub timeout: Duration,
264}
265
266impl Default for RetrievalVerificationLimits {
267    fn default() -> Self {
268        Self {
269            proof_bytes: MAX_RETRIEVAL_PROOF_BYTES,
270            snapshot: SnapshotReadLimits::default(),
271            max_candidates: 100_000,
272            max_candidate_bytes: 1024 * 1024 * 1024,
273            max_returned: 1_000,
274            max_documents: 1_000_000,
275            max_tokens: 10_000_000,
276            max_lexical_candidates: 100_000,
277            max_lexical_returned: 1_000,
278            max_hybrid_returned: 1_000,
279            timeout: Duration::from_secs(60),
280        }
281    }
282}
283
284/// Successful offline exact-retrieval verification evidence.
285#[derive(Clone, Debug, Eq, PartialEq)]
286pub struct ExactRetrievalVerificationReport {
287    /// Trusted anchor accepted by the verifier.
288    pub anchor: RetrievalProofAnchor,
289    /// Caller-pinnable anchor digest that matched expectation.
290    pub anchor_digest: [u8; 32],
291    /// Digest of the verified canonical proof file.
292    pub proof_digest: [u8; 32],
293    /// Complete reexecuted and verified outcome.
294    pub outcome: ExactRetrievalOutcome,
295}
296
297/// Successful offline lexical-retrieval verification evidence.
298#[derive(Clone, Debug, Eq, PartialEq)]
299pub struct LexicalRetrievalVerificationReport {
300    /// Trusted anchor accepted by the verifier.
301    pub anchor: RetrievalProofAnchor,
302    /// Caller-pinnable anchor digest that matched expectation.
303    pub anchor_digest: [u8; 32],
304    /// Digest of the verified canonical proof file.
305    pub proof_digest: [u8; 32],
306    /// Complete reexecuted lexical outcome.
307    pub outcome: LexicalOutcome,
308}
309
310/// Successful offline hybrid-retrieval verification evidence.
311#[derive(Clone, Debug, Eq, PartialEq)]
312pub struct HybridRetrievalVerificationReport {
313    /// Trusted anchor accepted by the verifier.
314    pub anchor: RetrievalProofAnchor,
315    /// Caller-pinnable anchor digest that matched expectation.
316    pub anchor_digest: [u8; 32],
317    /// Digest of the verified canonical proof file.
318    pub proof_digest: [u8; 32],
319    /// Complete reexecuted hybrid outcome.
320    pub outcome: HybridOutcome,
321}
322
323/// Failure while encoding, reading, or verifying a retrieval proof.
324#[derive(Debug, Error)]
325pub enum RetrievalProofError {
326    /// Proof file I/O failed.
327    #[error(transparent)]
328    Io(#[from] io::Error),
329
330    /// Snapshot witness verification failed.
331    #[error("snapshot witness failed: {source}")]
332    Snapshot {
333        /// Underlying snapshot failure.
334        #[source]
335        source: Box<SnapshotError>,
336    },
337
338    /// Canonical vector value is invalid.
339    #[error(transparent)]
340    Vector(#[from] VectorValueError),
341
342    /// Exact-retrieval replay failed.
343    #[error("exact retrieval replay failed: {source}")]
344    Retrieval {
345        /// Underlying deterministic retrieval failure.
346        #[source]
347        source: Box<ExactRetrievalError>,
348    },
349
350    /// Lexical-retrieval replay failed.
351    #[error("lexical retrieval replay failed: {source}")]
352    Lexical {
353        /// Underlying deterministic lexical failure.
354        #[source]
355        source: Box<LexicalError>,
356    },
357
358    /// Hybrid fusion replay failed.
359    #[error("hybrid retrieval replay failed: {source}")]
360    Hybrid {
361        /// Underlying deterministic hybrid failure.
362        #[source]
363        source: Box<HybridError>,
364    },
365
366    /// Canonical durable document decoding failed.
367    #[error("snapshot document decoding failed: {source}")]
368    Document {
369        /// Underlying canonical document failure.
370        #[source]
371        source: Box<DocumentError>,
372    },
373
374    /// Proof bytes violate the canonical format.
375    #[error("invalid retrieval proof: {reason}")]
376    Invalid {
377        /// Stable diagnostic reason.
378        reason: &'static str,
379    },
380
381    /// Proof format is newer than this verifier.
382    #[error("unsupported retrieval-proof format {found}; supported format is {supported}")]
383    UnsupportedVersion {
384        /// Version found in proof bytes.
385        found: u16,
386        /// Highest supported version.
387        supported: u16,
388    },
389
390    /// Retrieval operation is not supported.
391    #[error("unsupported retrieval-proof operation {found}")]
392    UnsupportedOperation {
393        /// Operation tag found in proof bytes.
394        found: u16,
395    },
396
397    /// Retrieval semantics version is not supported.
398    #[error("unsupported exact-retrieval semantics {found}; supported semantics is {supported}")]
399    UnsupportedSemantics {
400        /// Semantics version found in proof bytes.
401        found: u16,
402        /// Supported semantics version.
403        supported: u16,
404    },
405
406    /// Proof file exceeds caller policy.
407    #[error("retrieval proof is {actual} bytes; verification limit is {maximum}")]
408    ProofLimitExceeded {
409        /// Observed proof bytes.
410        actual: u64,
411        /// Configured maximum.
412        maximum: u64,
413    },
414
415    /// A canonical length or count cannot be represented safely.
416    #[error("retrieval-proof length overflow")]
417    LengthOverflow,
418
419    /// Fast accidental-corruption check failed.
420    #[error("retrieval-proof CRC32C mismatch")]
421    ChecksumMismatch,
422
423    /// Canonical proof content digest failed.
424    #[error("retrieval-proof BLAKE3 mismatch")]
425    DigestMismatch,
426
427    /// Proof anchor did not match caller-pinned trust state.
428    #[error("retrieval-proof anchor does not match the trusted anchor digest")]
429    AnchorMismatch,
430
431    /// Snapshot metadata does not match the proof anchor.
432    #[error("snapshot witness does not match the retrieval-proof anchor")]
433    SnapshotAnchorMismatch,
434
435    /// Snapshot uses a format that cannot witness durable vectors.
436    #[error("retrieval proofs require a disk-format-2 snapshot witness")]
437    SnapshotFormatMismatch,
438
439    /// Deterministic replay did not reproduce the embedded outcome.
440    #[error("offline reexecution does not match the retrieval proof")]
441    ReexecutionMismatch,
442
443    /// End-to-end cooperative verification deadline expired.
444    #[error("retrieval-proof verification timed out")]
445    TimedOut,
446}
447
448impl From<SnapshotError> for RetrievalProofError {
449    fn from(source: SnapshotError) -> Self {
450        Self::Snapshot {
451            source: Box::new(source),
452        }
453    }
454}
455
456impl From<ExactRetrievalError> for RetrievalProofError {
457    fn from(source: ExactRetrievalError) -> Self {
458        Self::Retrieval {
459            source: Box::new(source),
460        }
461    }
462}
463
464impl From<LexicalError> for RetrievalProofError {
465    fn from(source: LexicalError) -> Self {
466        Self::Lexical {
467            source: Box::new(source),
468        }
469    }
470}
471
472impl From<HybridError> for RetrievalProofError {
473    fn from(source: HybridError) -> Self {
474        Self::Hybrid {
475            source: Box::new(source),
476        }
477    }
478}
479
480impl From<DocumentError> for RetrievalProofError {
481    fn from(source: DocumentError) -> Self {
482        Self::Document {
483            source: Box::new(source),
484        }
485    }
486}