1use sha2::{Sha256, Digest};
10use winterfell::{
11 math::{fields::f128::BaseElement, FieldElement, ToElements},
12 crypto::{hashers::Blake3_256, DefaultRandomCoin, MerkleTree},
13 matrix::ColMatrix,
14 Air, AirContext, Assertion, AuxRandElements, CompositionPoly, CompositionPolyTrace,
15 ConstraintCompositionCoefficients, DefaultConstraintCommitment, DefaultConstraintEvaluator,
16 DefaultTraceLde, EvaluationFrame, FieldExtension, PartitionOptions, Proof, ProofOptions,
17 Prover, StarkDomain, Trace, TraceInfo, TracePolyTable, TraceTable,
18 TransitionConstraintDegree, AcceptableOptions,
19};
20use winter_utils::Serializable;
21
22use thiserror::Error;
23
24#[derive(Error, Debug)]
25pub enum StarkError {
26 #[error("Failed to generate proof: {0}")]
27 ProofGenerationFailed(String),
28 #[error("Proof verification failed: {0}")]
29 VerificationFailed(String),
30 #[error("Invalid proof format")]
31 InvalidProofFormat,
32 #[error("Serialization error: {0}")]
33 SerializationError(String),
34}
35
36#[derive(Clone, Debug)]
38pub struct StarkIdentity {
39 pub pubkey_hash: [u8; 32],
41 pub pubkey_elements: Vec<BaseElement>,
43}
44
45impl StarkIdentity {
46 pub fn from_secret(secret: &[u8; 32]) -> Self {
48 let hash = Sha256::digest(secret);
49 let pubkey_hash: [u8; 32] = hash.into();
50
51 let pubkey_elements = bytes_to_elements(&pubkey_hash);
53
54 Self {
55 pubkey_hash,
56 pubkey_elements,
57 }
58 }
59
60 pub fn to_hex(&self) -> String {
62 hex::encode(self.pubkey_hash)
63 }
64}
65
66#[derive(Clone)]
68pub struct EventProof {
69 pub proof: Proof,
71 pub event_hash: [u8; 32],
73 pub pubkey_hash: [u8; 32],
74}
75
76impl EventProof {
77 pub fn serialize(&self) -> Vec<u8> {
79 let mut bytes = Vec::new();
80 bytes.extend_from_slice(&self.event_hash);
81 bytes.extend_from_slice(&self.pubkey_hash);
82 self.proof.write_into(&mut bytes);
83 bytes
84 }
85
86 pub fn deserialize(bytes: &[u8]) -> Result<Self, StarkError> {
88 if bytes.len() < 64 {
89 return Err(StarkError::InvalidProofFormat);
90 }
91
92 let mut event_hash = [0u8; 32];
93 let mut pubkey_hash = [0u8; 32];
94 event_hash.copy_from_slice(&bytes[0..32]);
95 pubkey_hash.copy_from_slice(&bytes[32..64]);
96
97 let proof = Proof::from_bytes(&bytes[64..])
98 .map_err(|e| StarkError::SerializationError(format!("{:?}", e)))?;
99
100 Ok(Self {
101 proof,
102 event_hash,
103 pubkey_hash,
104 })
105 }
106}
107
108#[derive(Clone)]
114pub struct HashPreimagePublicInputs {
115 pub start_elements: Vec<BaseElement>,
116 pub result_elements: Vec<BaseElement>,
117}
118
119impl ToElements<BaseElement> for HashPreimagePublicInputs {
120 fn to_elements(&self) -> Vec<BaseElement> {
121 let mut elements = self.start_elements.clone();
122 elements.extend(self.result_elements.clone());
123 elements
124 }
125}
126
127pub struct HashPreimageAir {
131 context: AirContext<BaseElement>,
132 start_elements: Vec<BaseElement>,
133 result_elements: Vec<BaseElement>,
134}
135
136impl Air for HashPreimageAir {
137 type BaseField = BaseElement;
138 type PublicInputs = HashPreimagePublicInputs;
139 type GkrProof = ();
140 type GkrVerifier = ();
141
142 fn new(trace_info: TraceInfo, pub_inputs: Self::PublicInputs, options: ProofOptions) -> Self {
143 let degrees = vec![
146 TransitionConstraintDegree::new(2),
147 TransitionConstraintDegree::new(2),
148 TransitionConstraintDegree::new(2),
149 TransitionConstraintDegree::new(2),
150 ];
151
152 let num_assertions = 8; Self {
155 context: AirContext::new(trace_info, degrees, num_assertions, options),
156 start_elements: pub_inputs.start_elements,
157 result_elements: pub_inputs.result_elements,
158 }
159 }
160
161 fn context(&self) -> &AirContext<Self::BaseField> {
162 &self.context
163 }
164
165 fn evaluate_transition<E: FieldElement + From<Self::BaseField>>(
166 &self,
167 frame: &EvaluationFrame<E>,
168 _periodic_values: &[E],
169 result: &mut [E],
170 ) {
171 let current = frame.current();
172 let next = frame.next();
173
174 result[0] = next[0] - (current[0] * current[1] + current[2]);
180 result[1] = next[1] - (current[1] * current[2] + current[3]);
182 result[2] = next[2] - (current[2] * current[3] + current[0]);
184 result[3] = next[3] - (current[3] * current[0] + current[1]);
186 }
187
188 fn get_assertions(&self) -> Vec<Assertion<Self::BaseField>> {
189 let last_step = self.trace_length() - 1;
190
191 vec![
192 Assertion::single(0, 0, self.start_elements[0]),
194 Assertion::single(1, 0, self.start_elements[1]),
195 Assertion::single(2, 0, self.start_elements[2]),
196 Assertion::single(3, 0, self.start_elements[3]),
197 Assertion::single(0, last_step, self.result_elements[0]),
199 Assertion::single(1, last_step, self.result_elements[1]),
200 Assertion::single(2, last_step, self.result_elements[2]),
201 Assertion::single(3, last_step, self.result_elements[3]),
202 ]
203 }
204}
205
206struct HashPreimageProver {
211 options: ProofOptions,
212 secret_elements: Vec<BaseElement>,
213 event_elements: Vec<BaseElement>,
214}
215
216impl HashPreimageProver {
217 fn new(secret: &[u8; 32], event_hash: &[u8; 32]) -> Self {
218 let options = ProofOptions::new(
219 32, 8, 0, FieldExtension::None,
223 8, 31, );
226
227 let mut combined = [0u8; 32];
229 for i in 0..32 {
230 combined[i] = secret[i] ^ event_hash[i];
231 }
232
233 Self {
234 options,
235 secret_elements: bytes_to_elements(&combined),
236 event_elements: bytes_to_elements(event_hash),
237 }
238 }
239
240 fn build_trace(&self, result_elements: &[BaseElement]) -> TraceTable<BaseElement> {
241 let trace_length = 64; let mut trace = TraceTable::new(4, trace_length);
243
244 let secret_elements = self.secret_elements.clone();
245 let event_elements = self.event_elements.clone();
246 let result = result_elements.to_vec();
247
248 trace.fill(
250 |state| {
251 state[0] = secret_elements[0];
253 state[1] = secret_elements[1];
254 state[2] = secret_elements[2];
255 state[3] = secret_elements[3];
256 },
257 |step, state| {
258 if step < 63 {
259 let s0 = state[0];
261 let s1 = state[1];
262 let s2 = state[2];
263 let s3 = state[3];
264
265 let event_mix = event_elements[step % 4];
267
268 state[0] = s0 * s1 + s2 + event_mix;
269 state[1] = s1 * s2 + s3;
270 state[2] = s2 * s3 + s0;
271 state[3] = s3 * s0 + s1;
272 } else {
273 state[0] = result[0];
275 state[1] = result[1];
276 state[2] = result[2];
277 state[3] = result[3];
278 }
279 },
280 );
281
282 trace
283 }
284}
285
286impl Prover for HashPreimageProver {
287 type BaseField = BaseElement;
288 type Air = HashPreimageAir;
289 type Trace = TraceTable<BaseElement>;
290 type HashFn = Blake3_256<BaseElement>;
291 type VC = MerkleTree<Self::HashFn>;
292 type RandomCoin = DefaultRandomCoin<Self::HashFn>;
293 type TraceLde<E: FieldElement<BaseField = Self::BaseField>> =
294 DefaultTraceLde<E, Self::HashFn, Self::VC>;
295 type ConstraintCommitment<E: FieldElement<BaseField = Self::BaseField>> =
296 DefaultConstraintCommitment<E, Self::HashFn, Self::VC>;
297 type ConstraintEvaluator<'a, E: FieldElement<BaseField = Self::BaseField>> =
298 DefaultConstraintEvaluator<'a, Self::Air, E>;
299
300 fn get_pub_inputs(&self, trace: &Self::Trace) -> HashPreimagePublicInputs {
301 let last_step = trace.length() - 1;
302
303 HashPreimagePublicInputs {
304 start_elements: vec![
305 trace.get(0, 0),
306 trace.get(1, 0),
307 trace.get(2, 0),
308 trace.get(3, 0),
309 ],
310 result_elements: vec![
311 trace.get(0, last_step),
312 trace.get(1, last_step),
313 trace.get(2, last_step),
314 trace.get(3, last_step),
315 ],
316 }
317 }
318
319 fn options(&self) -> &ProofOptions {
320 &self.options
321 }
322
323 fn new_trace_lde<E: FieldElement<BaseField = Self::BaseField>>(
324 &self,
325 trace_info: &TraceInfo,
326 main_trace: &ColMatrix<Self::BaseField>,
327 domain: &StarkDomain<Self::BaseField>,
328 partition_option: PartitionOptions,
329 ) -> (Self::TraceLde<E>, TracePolyTable<E>) {
330 DefaultTraceLde::new(trace_info, main_trace, domain, partition_option)
331 }
332
333 fn build_constraint_commitment<E: FieldElement<BaseField = Self::BaseField>>(
334 &self,
335 composition_poly_trace: CompositionPolyTrace<E>,
336 num_constraint_composition_columns: usize,
337 domain: &StarkDomain<Self::BaseField>,
338 partition_options: PartitionOptions,
339 ) -> (Self::ConstraintCommitment<E>, CompositionPoly<E>) {
340 DefaultConstraintCommitment::new(
341 composition_poly_trace,
342 num_constraint_composition_columns,
343 domain,
344 partition_options,
345 )
346 }
347
348 fn new_evaluator<'a, E: FieldElement<BaseField = Self::BaseField>>(
349 &self,
350 air: &'a Self::Air,
351 aux_rand_elements: Option<AuxRandElements<E>>,
352 composition_coefficients: ConstraintCompositionCoefficients<E>,
353 ) -> Self::ConstraintEvaluator<'a, E> {
354 DefaultConstraintEvaluator::new(air, aux_rand_elements, composition_coefficients)
355 }
356}
357
358pub fn prove_event(
367 secret_key: &[u8; 32],
368 event_data: &[u8],
369) -> Result<EventProof, StarkError> {
370 let event_hash: [u8; 32] = Sha256::digest(event_data).into();
372
373 let identity = StarkIdentity::from_secret(secret_key);
375
376 let prover = HashPreimageProver::new(secret_key, &event_hash);
378 let trace = prover.build_trace(&identity.pubkey_elements);
379
380 let proof = prover.prove(trace)
382 .map_err(|e| StarkError::ProofGenerationFailed(format!("{:?}", e)))?;
383
384 Ok(EventProof {
385 proof,
386 event_hash,
387 pubkey_hash: identity.pubkey_hash,
388 })
389}
390
391pub fn verify_event(
393 event_proof: &EventProof,
394 event_data: &[u8],
395 expected_pubkey: &[u8; 32],
396) -> Result<bool, StarkError> {
397 let computed_hash: [u8; 32] = Sha256::digest(event_data).into();
399 if computed_hash != event_proof.event_hash {
400 return Ok(false);
401 }
402
403 if event_proof.pubkey_hash != *expected_pubkey {
405 return Ok(false);
406 }
407
408 let pub_inputs = HashPreimagePublicInputs {
416 start_elements: bytes_to_elements(&event_proof.event_hash),
417 result_elements: bytes_to_elements(&event_proof.pubkey_hash),
418 };
419
420 let acceptable_options = AcceptableOptions::MinConjecturedSecurity(80);
422
423 winterfell::verify::<
424 HashPreimageAir,
425 Blake3_256<BaseElement>,
426 DefaultRandomCoin<Blake3_256<BaseElement>>,
427 MerkleTree<Blake3_256<BaseElement>>,
428 >(event_proof.proof.clone(), pub_inputs, &acceptable_options)
429 .map_err(|e| StarkError::VerificationFailed(format!("{:?}", e)))?;
430
431 Ok(true)
432}
433
434fn bytes_to_elements(bytes: &[u8; 32]) -> Vec<BaseElement> {
440 (0..4)
441 .map(|i| {
442 let mut buf = [0u8; 8];
443 buf.copy_from_slice(&bytes[i * 8..(i + 1) * 8]);
444 BaseElement::new(u64::from_le_bytes(buf) as u128)
445 })
446 .collect()
447}
448
449#[cfg(test)]
450mod tests {
451 use super::*;
452
453 #[test]
454 fn test_stark_identity() {
455 let secret = [42u8; 32];
456 let identity = StarkIdentity::from_secret(&secret);
457
458 assert_eq!(identity.pubkey_elements.len(), 4);
459 assert!(!identity.to_hex().is_empty());
460 }
461
462 #[test]
463 fn test_bytes_to_elements() {
464 let bytes = [1u8; 32];
465 let elements = bytes_to_elements(&bytes);
466 assert_eq!(elements.len(), 4);
467 }
468}