hyphae_engine/proof/
model.rs1use std::{io, time::Duration};
4
5use hyphae_query::{ExecutionLimits, Query, QueryError, QueryResult, Record};
6use hyphae_storage::{SnapshotError, SnapshotInfo, SnapshotReadLimits};
7use thiserror::Error;
8
9use crate::DocumentError;
10
11pub const RESULT_PROOF_FORMAT_VERSION: u16 = 1;
13
14pub const MAX_RESULT_PROOF_BYTES: u64 = 64 * 1024 * 1024;
16
17#[derive(Clone, Debug, Eq, PartialEq)]
19pub struct ProofAnchor {
20 pub checkpoint_sequence: u64,
22 pub checkpoint_digest: Option<[u8; 32]>,
24 pub snapshot_digest: [u8; 32],
26}
27
28impl ProofAnchor {
29 pub fn from_snapshot(snapshot: &SnapshotInfo) -> Self {
31 Self {
32 checkpoint_sequence: snapshot.checkpoint_sequence,
33 checkpoint_digest: snapshot.checkpoint_digest,
34 snapshot_digest: snapshot.snapshot_digest,
35 }
36 }
37
38 pub fn digest(&self) -> [u8; 32] {
40 let mut hasher = blake3::Hasher::new();
41 hasher.update(b"hyphae-proof-anchor-v1");
42 hasher.update(&self.checkpoint_sequence.to_le_bytes());
43 hasher.update(&self.checkpoint_digest.unwrap_or([0; 32]));
44 hasher.update(&self.snapshot_digest);
45 *hasher.finalize().as_bytes()
46 }
47}
48
49#[derive(Clone, Debug, Eq, PartialEq)]
51pub enum ProvenOperation {
52 Get {
54 key: Vec<u8>,
56 },
57 Query(Query),
59}
60
61#[derive(Clone, Debug, Eq, PartialEq)]
63pub enum ProvenResult {
64 Get(Option<Record>),
66 Query(QueryResult),
68}
69
70#[derive(Clone, Debug, Eq, PartialEq)]
72pub struct ResultProof {
73 pub(crate) anchor: ProofAnchor,
74 pub(crate) operation: ProvenOperation,
75 pub(crate) result: ProvenResult,
76 pub(crate) proof_digest: [u8; 32],
77}
78
79#[derive(Clone, Debug, Eq, PartialEq)]
81pub struct ResultProofArtifact {
82 pub proof: ResultProof,
84 pub snapshot: SnapshotInfo,
86}
87
88impl ResultProof {
89 pub fn anchor(&self) -> &ProofAnchor {
91 &self.anchor
92 }
93
94 pub fn anchor_digest(&self) -> [u8; 32] {
96 self.anchor.digest()
97 }
98
99 pub fn operation(&self) -> &ProvenOperation {
101 &self.operation
102 }
103
104 pub fn result(&self) -> &ProvenResult {
106 &self.result
107 }
108
109 pub fn proof_digest(&self) -> [u8; 32] {
111 self.proof_digest
112 }
113}
114
115#[derive(Clone, Debug, Eq, PartialEq)]
117pub struct VerificationLimits {
118 pub proof_bytes: u64,
120 pub snapshot: SnapshotReadLimits,
122 pub query: ExecutionLimits,
124 pub timeout: Duration,
126}
127
128impl Default for VerificationLimits {
129 fn default() -> Self {
130 Self {
131 proof_bytes: MAX_RESULT_PROOF_BYTES,
132 snapshot: SnapshotReadLimits::default(),
133 query: ExecutionLimits::default(),
134 timeout: Duration::from_secs(60),
135 }
136 }
137}
138
139#[derive(Clone, Debug, Eq, PartialEq)]
141pub struct VerificationReport {
142 pub anchor: ProofAnchor,
144 pub anchor_digest: [u8; 32],
146 pub proof_digest: [u8; 32],
148 pub result: ProvenResult,
150}
151
152#[derive(Debug, Error)]
154pub enum ProofError {
155 #[error(transparent)]
157 Io(#[from] io::Error),
158
159 #[error(transparent)]
161 Document(#[from] DocumentError),
162
163 #[error("snapshot witness failed: {source}")]
165 Snapshot {
166 #[source]
168 source: Box<SnapshotError>,
169 },
170
171 #[error("query reexecution failed: {source}")]
173 Query {
174 #[source]
176 source: Box<QueryError>,
177 },
178
179 #[error("invalid result proof: {reason}")]
181 Invalid {
182 reason: &'static str,
184 },
185
186 #[error("unsupported result-proof format {found}; supported format is {supported}")]
188 UnsupportedVersion {
189 found: u16,
191 supported: u16,
193 },
194
195 #[error("result proof is {actual} bytes; verification limit is {maximum}")]
197 ProofLimitExceeded {
198 actual: u64,
200 maximum: u64,
202 },
203
204 #[error("result-proof length overflow")]
206 LengthOverflow,
207
208 #[error("result-proof CRC32C mismatch")]
210 ChecksumMismatch,
211
212 #[error("result-proof BLAKE3 mismatch")]
214 DigestMismatch,
215
216 #[error("result-proof anchor does not match the trusted anchor digest")]
218 AnchorMismatch,
219
220 #[error("snapshot witness does not match the result-proof anchor")]
222 SnapshotAnchorMismatch,
223
224 #[error("result-proof operation and result variants do not match")]
226 OperationResultMismatch,
227
228 #[error("offline reexecution does not match the result proof")]
230 ReexecutionMismatch,
231
232 #[error("result-proof verification timed out")]
234 TimedOut,
235}
236
237impl From<SnapshotError> for ProofError {
238 fn from(source: SnapshotError) -> Self {
239 Self::Snapshot {
240 source: Box::new(source),
241 }
242 }
243}
244
245impl From<QueryError> for ProofError {
246 fn from(source: QueryError) -> Self {
247 Self::Query {
248 source: Box::new(source),
249 }
250 }
251}