1pub mod codecs;
7mod mock_sponge;
8
9#[cfg(debug_assertions)]
10use std::any::type_name;
11use std::fmt::Debug;
12
13use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
14use ark_std::rand::{rngs::StdRng, CryptoRng, RngCore};
15use serde::{Deserialize, Serialize};
16use sha3::{Digest, Sha3_256, Sha3_512};
17use spongefish::StdHash;
18pub use spongefish::{
19 Codec, Decoding, DuplexSpongeInterface, Encoding, NargDeserialize, NargSerialize,
20 VerificationError, VerificationResult,
21};
22
23#[cfg(test)]
24pub use self::mock_sponge::MockSponge;
25
26#[macro_export]
27macro_rules! verify {
28 ($cond:expr) => {
29 #[allow(clippy::neg_cmp_op_on_partial_ord)]
30 if !$cond {
31 #[cfg(feature = "verifier_panics")]
32 panic!("Verification failed: {}", stringify!($cond));
33
34 #[cfg(not(feature = "verifier_panics"))]
35 return Err(spongefish::VerificationError);
36 };
37 };
38}
39
40pub trait ProverMessage<U = [u8]>: NargDeserialize + NargSerialize + Encoding<U>
44where
45 U: ?Sized,
46{
47}
48
49#[derive(Clone, Copy, Debug)]
50pub struct DomainSeparator<'a, I> {
51 protocol_id: [u8; 64],
52 session_id: [u8; 32],
53 instance: &'a I,
54}
55
56#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug)]
57pub enum Interaction {
58 ProverMessage(String),
59 VerifierMessage(String),
60 Hint(String),
61}
62
63#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug)]
64pub struct Proof {
65 pub narg_string: Vec<u8>,
66 pub hints: Vec<u8>,
67
68 #[cfg(debug_assertions)]
69 pub pattern: Vec<Interaction>,
70}
71
72pub struct ProverState<H = StdHash, R = StdRng>
73where
74 H: DuplexSpongeInterface,
75 R: RngCore + CryptoRng,
76{
77 inner: spongefish::ProverState<H, R>,
78 hints: Vec<u8>,
79
80 #[cfg(debug_assertions)]
81 pattern: Vec<Interaction>,
82}
83
84pub struct VerifierState<'a, H = StdHash>
85where
86 H: DuplexSpongeInterface,
87{
88 inner: spongefish::VerifierState<'a, H>,
89 hints: &'a [u8],
90
91 #[cfg(debug_assertions)]
92 pattern: &'a [Interaction],
93}
94
95pub trait VerifierMessage {
96 type U;
97
98 fn verifier_message<T>(&mut self) -> T
99 where
100 T: Decoding<[Self::U]>;
101
102 fn verifier_message_vec<T>(&mut self, count: usize) -> Vec<T>
103 where
104 T: Decoding<[Self::U]>,
105 {
106 (0..count).map(|_| self.verifier_message()).collect()
107 }
108}
109
110impl DomainSeparator<'static, ()> {
111 pub fn protocol<C: Serialize>(config: &C) -> Self {
112 const INSTANCE: &() = &();
113 let mut hash = Sha3_512::new();
114 ciborium::into_writer(config, &mut hash).expect("Computing protocol hash failed");
115 let protocol_id: [u8; 64] = hash.finalize().into();
116 Self {
117 protocol_id,
118 session_id: [0; 32],
119 instance: INSTANCE,
120 }
121 }
122
123 #[must_use]
124 pub fn session<S: Serialize>(self, session: &S) -> Self {
125 let mut hash = Sha3_256::new();
126 ciborium::into_writer(session, &mut hash).expect("Computing session hash failed");
127 let session_id: [u8; 32] = hash.finalize().into();
128 Self { session_id, ..self }
129 }
130
131 pub const fn instance<I>(self, instance: &I) -> DomainSeparator<'_, I> {
132 DomainSeparator {
133 protocol_id: self.protocol_id,
134 session_id: self.session_id,
135 instance,
136 }
137 }
138}
139
140impl<T, U> ProverMessage<U> for T
141where
142 T: NargSerialize + NargDeserialize + Encoding<U>,
143 U: ?Sized,
144{
145}
146
147impl<H> ProverState<H, StdRng>
148where
149 H: DuplexSpongeInterface,
150{
151 pub fn new<I>(ds: &DomainSeparator<'_, I>, duplex: H) -> Self
156 where
157 u8: Encoding<[H::U]>,
158 I: Encoding<[H::U]>,
159 {
160 Self {
161 inner: spongefish::DomainSeparator::new(ds.protocol_id)
162 .session(ds.session_id)
163 .instance(ds.instance)
164 .to_prover(duplex),
165 hints: Vec::new(),
166
167 #[cfg(debug_assertions)]
168 pattern: Vec::new(),
169 }
170 }
171}
172
173impl ProverState<StdHash, StdRng> {
174 pub fn new_std<I>(ds: &DomainSeparator<'_, I>) -> Self
176 where
177 I: Encoding<[u8]>,
178 {
179 Self::new(ds, StdHash::default())
180 }
181}
182
183impl<H, R> ProverState<H, R>
184where
185 H: DuplexSpongeInterface,
186 R: RngCore + CryptoRng,
187{
188 pub fn rng(&mut self) -> &mut (impl RngCore + CryptoRng) {
190 self.inner.rng()
191 }
192
193 #[cfg_attr(test, track_caller)]
194 pub fn prover_message<T>(&mut self, message: &T)
195 where
196 T: Encoding<[H::U]> + NargSerialize + ?Sized,
197 {
198 #[cfg(debug_assertions)]
199 self.push(Interaction::ProverMessage(type_name::<T>().to_owned()));
200 self.inner.prover_message(message);
201 }
202
203 #[cfg_attr(test, track_caller)]
204 pub fn prover_hint<T>(&mut self, hint: &T)
205 where
206 T: NargSerialize,
207 {
208 #[cfg(debug_assertions)]
209 self.push(Interaction::Hint(type_name::<T>().to_owned()));
210 hint.serialize_into_narg(&mut self.hints);
211 }
212
213 #[cfg_attr(test, track_caller)]
214 pub fn prover_hint_ark<T>(&mut self, value: &T)
215 where
216 T: CanonicalSerialize + ?Sized,
217 {
218 #[cfg(debug_assertions)]
219 self.push(Interaction::Hint(type_name::<T>().to_owned()));
220 value
221 .serialize_compressed(&mut self.hints)
222 .expect("Failed to serialize hint");
223 }
224
225 pub fn proof(self) -> Proof {
226 Proof {
227 narg_string: self.inner.narg_string().to_owned(),
228 hints: self.hints,
229
230 #[cfg(debug_assertions)]
231 pattern: self.pattern,
232 }
233 }
234
235 #[cfg(debug_assertions)]
236 fn push(&mut self, interaction: Interaction) {
237 self.pattern.push(interaction);
238 }
239}
240
241impl<H, R> VerifierMessage for ProverState<H, R>
242where
243 H: DuplexSpongeInterface,
244 R: RngCore + CryptoRng,
245{
246 type U = H::U;
247
248 #[cfg_attr(test, track_caller)]
249 fn verifier_message<T>(&mut self) -> T
250 where
251 T: Decoding<[H::U]>,
252 {
253 #[cfg(debug_assertions)]
254 self.push(Interaction::VerifierMessage(type_name::<T>().to_owned()));
255 self.inner.verifier_message()
256 }
257}
258
259impl<'a, H> VerifierState<'a, H>
260where
261 H: DuplexSpongeInterface,
262{
263 pub fn new<'b, I>(ds: &DomainSeparator<'b, I>, proof: &'a Proof, duplex: H) -> Self
264 where
265 u8: Encoding<[H::U]>,
266 I: Encoding<[H::U]>,
267 {
268 Self {
269 inner: spongefish::DomainSeparator::new(ds.protocol_id)
270 .session(ds.session_id)
271 .instance(ds.instance)
272 .to_verifier(duplex, &proof.narg_string),
273 hints: &proof.hints,
274 #[cfg(debug_assertions)]
275 pattern: &proof.pattern,
276 }
277 }
278
279 pub const fn as_spongefish(&mut self) -> &mut spongefish::VerifierState<'a, H> {
280 &mut self.inner
281 }
282
283 #[cfg_attr(debug_assertions, track_caller)]
284 pub fn check_eof(self) -> VerificationResult<()> {
285 #[cfg(debug_assertions)]
286 assert!(self.pattern.is_empty());
287 verify!(self.inner.check_eof().is_ok());
288 verify!(self.hints.is_empty());
289 Ok(())
290 }
291
292 #[cfg_attr(test, track_caller)]
293 pub fn prover_message<T>(&mut self) -> VerificationResult<T>
294 where
295 T: Encoding<[H::U]> + NargDeserialize,
296 {
297 #[cfg(debug_assertions)]
298 self.pop_pattern(&Interaction::ProverMessage(type_name::<T>().to_owned()));
299 self.inner.prover_message()
300 }
301
302 #[cfg_attr(test, track_caller)]
303 pub fn prover_messages_vec<T>(&mut self, len: usize) -> VerificationResult<Vec<T>>
304 where
305 T: Encoding<[H::U]> + NargDeserialize,
306 {
307 (0..len).map(|_| self.prover_message()).collect()
308 }
309
310 #[cfg_attr(test, track_caller)]
311 pub fn prover_hint<T>(&mut self) -> VerificationResult<T>
312 where
313 T: NargDeserialize,
314 {
315 #[cfg(debug_assertions)]
316 self.pop_pattern(&Interaction::Hint(type_name::<T>().to_owned()));
317 T::deserialize_from_narg(&mut self.hints)
318 }
319
320 #[cfg_attr(test, track_caller)]
321 pub fn prover_hint_ark<T>(&mut self) -> VerificationResult<T>
322 where
323 T: CanonicalDeserialize,
324 {
325 #[cfg(debug_assertions)]
326 self.pop_pattern(&Interaction::Hint(type_name::<T>().to_owned()));
327 T::deserialize_compressed(&mut self.hints).map_err(|_| VerificationError)
328 }
329
330 #[cfg(debug_assertions)]
331 #[track_caller]
332 fn pop_pattern(&mut self, interaction: &Interaction) {
333 assert!(!self.pattern.is_empty());
334 let (expected, tail) = self.pattern.split_first().unwrap();
335 assert_eq!(
336 interaction, expected,
337 "Transcript error: Expected interaction {expected:?} got {interaction:?}"
338 );
339 self.pattern = tail;
340 }
341}
342
343impl<'a> VerifierState<'a, StdHash> {
344 pub fn new_std<'b, I>(ds: &DomainSeparator<'b, I>, proof: &'a Proof) -> Self
346 where
347 I: Encoding<[u8]>,
348 {
349 Self::new(ds, proof, StdHash::default())
350 }
351}
352
353impl<H> VerifierMessage for VerifierState<'_, H>
354where
355 H: DuplexSpongeInterface,
356{
357 type U = H::U;
358
359 #[cfg_attr(test, track_caller)]
360 fn verifier_message<T>(&mut self) -> T
361 where
362 T: Decoding<[H::U]>,
363 {
364 #[cfg(debug_assertions)]
365 self.pop_pattern(&Interaction::VerifierMessage(type_name::<T>().to_owned()));
366 self.inner.verifier_message()
367 }
368}