1use log::info;
4use std::collections::HashMap;
5use std::convert::TryFrom;
6use std::fs::File;
7use std::io::{self, BufReader, BufWriter};
8use std::path::Path;
9use std::str::FromStr;
10use std::sync::{Arc, Mutex};
11
12use ff::PrimeField;
13use hex::FromHex;
14use libipld::{
15 cbor::DagCborCodec,
16 json::DagJsonCodec,
17 multihash::{Code, MultihashDigest},
18 prelude::Codec,
19 serde::{from_ipld, to_ipld},
20 Cid, Ipld,
21};
22use lurk::{
23 circuit::ToInputs,
24 eval::{empty_sym_env, Evaluable, Evaluator, Status, IO},
25 field::LurkField,
26 proof::nova::{self, NovaProver, PublicParams},
27 proof::Prover,
28 scalar_store::ScalarStore,
29 store::{Pointer, Ptr, ScalarPtr, Store},
30 tag::ExprTag,
31 writer::Write,
32};
33use once_cell::sync::OnceCell;
34use pasta_curves::pallas;
35use rand::rngs::OsRng;
36use serde::de::DeserializeOwned;
37use serde::{Deserialize, Deserializer, Serialize, Serializer};
38
39pub mod error;
40mod file_map;
41
42use error::Error;
43use file_map::FileMap;
44
45pub const DEFAULT_REDUCTION_COUNT: ReductionCount = ReductionCount::Ten;
46pub static VERBOSE: OnceCell<bool> = OnceCell::new();
47
48pub type S1 = pallas::Scalar;
49
50mod base64 {
51 use serde::{Deserialize, Serialize};
52 use serde::{Deserializer, Serializer};
53
54 pub fn serialize<S: Serializer>(v: &Vec<u8>, s: S) -> Result<S::Ok, S::Error> {
55 let base64 = base64::encode(v);
56 String::serialize(&base64, s)
57 }
58
59 pub fn deserialize<'de, D: Deserializer<'de>>(d: D) -> Result<Vec<u8>, D::Error> {
60 let base64 = String::deserialize(d)?;
61 base64::decode(base64.as_bytes()).map_err(serde::de::Error::custom)
62 }
63}
64
65pub type NovaProofCache = FileMap<Cid, Proof<'static, S1>>;
66pub fn nova_proof_cache(reduction_count: usize) -> NovaProofCache {
67 FileMap::<Cid, Proof<S1>>::new(format!("nova_proofs.{}", reduction_count)).unwrap()
68}
69
70pub type CommittedExpressionMap = FileMap<Commitment<S1>, CommittedExpression<S1>>;
71pub fn committed_expression_store() -> CommittedExpressionMap {
72 FileMap::<Commitment<S1>, CommittedExpression<S1>>::new("committed_expressions").unwrap()
73}
74
75pub type PublicParamMemCache = Mutex<HashMap<usize, Arc<PublicParams<'static>>>>;
76fn public_param_mem_cache() -> &'static PublicParamMemCache {
77 static CACHE: OnceCell<PublicParamMemCache> = OnceCell::new();
78 CACHE.get_or_init(|| Mutex::new(HashMap::new()))
79}
80
81pub type PublicParamDiskCache = FileMap<String, PublicParams<'static>>;
82fn public_param_disk_cache() -> PublicParamDiskCache {
83 FileMap::new("public_params").unwrap()
84}
85
86pub fn public_params(rc: usize) -> Result<Arc<PublicParams<'static>>, Error> {
87 let mut mem_cache = public_param_mem_cache().lock().unwrap();
88 match mem_cache.get(&rc) {
89 Some(pp) => Ok(pp.clone()),
90 None => {
91 let disk_cache = public_param_disk_cache();
92 let key = format!("public-params-rc-{rc}");
94 if let Some(pp) = disk_cache.get(&key) {
95 let pp = Arc::new(pp);
96 mem_cache.insert(rc, pp.clone());
97 Ok(pp)
98 } else {
99 let pp = Arc::new(nova::public_params(rc));
100 mem_cache.insert(rc, pp.clone());
101 disk_cache
102 .set(key, &pp)
103 .map_err(|e| Error::CacheError(format!("Disk write error: {e}")))?;
104 Ok(pp)
105 }
106 }
107 }
108}
109
110#[derive(Clone, Copy, Hash, PartialEq, Eq, Serialize, Deserialize)]
112pub enum ReductionCount {
113 One,
114 Five,
115 Ten,
116 OneHundred,
117}
118
119#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq, Eq)]
120pub struct Evaluation {
121 pub expr: String,
122 pub env: String,
123 pub cont: String,
124 pub expr_out: String,
125 pub env_out: String,
126 pub cont_out: String,
127 pub status: Status,
128 pub iterations: Option<usize>,
129}
130
131#[derive(Clone, Copy, Debug, Eq, PartialEq)]
132pub struct Commitment<F: LurkField> {
133 pub comm: F,
134}
135
136#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
137pub struct OpeningRequest<F: LurkField> {
138 pub commitment: Commitment<F>,
139 pub input: Expression,
140 pub chain: bool,
141}
142
143impl<F: LurkField> ToString for Commitment<F> {
144 fn to_string(&self) -> String {
145 let s = serde_json::to_string(&self).unwrap();
146 s[1..s.len() - 1].to_string()
148 }
149}
150
151impl<F: LurkField> Serialize for Commitment<F> {
152 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
153 where
154 S: Serializer,
155 {
156 let be_bytes: Vec<u8> = self
158 .comm
159 .to_repr()
160 .as_ref()
161 .iter()
162 .rev()
163 .map(|x| x.to_owned())
164 .collect();
165
166 hex::serde::serialize(be_bytes, serializer)
167 }
168}
169
170impl<'de, F: LurkField> Deserialize<'de> for Commitment<F> {
171 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
172 where
173 D: Deserializer<'de>,
174 {
175 hex::serde::deserialize(deserializer)
176 }
177}
178
179impl<F: LurkField> FromHex for Commitment<F> {
180 type Error = hex::FromHexError;
181
182 fn from_hex<T>(s: T) -> Result<Self, <Self as FromHex>::Error>
183 where
184 T: AsRef<[u8]>,
185 {
186 let mut v = Vec::from_hex(s)?;
187 v.reverse();
188 let mut repr = <F as PrimeField>::Repr::default();
189 repr.as_mut()[..32].copy_from_slice(&v[..]);
190
191 Ok(Commitment {
192 comm: F::from_repr(repr).unwrap(),
193 })
194 }
195}
196
197#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
198pub struct Expression {
199 pub expr: LurkPtr,
200}
201
202#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)]
203pub struct Opening<F: LurkField> {
204 pub input: String,
205 pub output: String,
206 pub status: Status,
207 pub commitment: Commitment<F>,
208 pub new_commitment: Option<Commitment<F>>,
209}
210
211#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)]
212pub struct LurkScalarBytes {
213 #[serde(with = "base64")]
214 scalar_store: Vec<u8>,
215 #[serde(with = "base64")]
216 scalar_ptr: Vec<u8>,
217}
218
219#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
220pub struct LurkScalarIpld {
221 scalar_store: Ipld,
222 scalar_ptr: Ipld,
223}
224
225#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
226pub enum LurkPtr {
227 Source(String),
228 ScalarBytes(LurkScalarBytes),
229 Ipld(LurkScalarIpld),
230}
231
232#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
233pub struct CommittedExpression<F: LurkField + Serialize> {
234 pub expr: LurkPtr,
235 pub secret: Option<F>,
236 pub commitment: Option<Commitment<F>>,
237}
238
239#[derive(Debug, Serialize, Deserialize)]
240pub struct VerificationResult {
241 pub verified: bool,
242}
243
244#[derive(Serialize, Deserialize)]
245pub struct Proof<'a, F: LurkField> {
246 pub claim: Claim<F>,
247 pub proof: nova::Proof<'a>,
248 pub num_steps: usize,
249 pub reduction_count: ReductionCount,
250}
251
252#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
253pub enum Claim<F: LurkField> {
254 Evaluation(Evaluation),
255 Opening(Opening<F>),
256}
257
258#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
264pub struct Cert {
265 #[serde(serialize_with = "cid_string", deserialize_with = "string_cid")]
266 pub claim_cid: Cid,
267 #[serde(serialize_with = "cid_string", deserialize_with = "string_cid")]
268 pub proof_cid: Cid,
269 pub verified: bool,
270 pub verifier_id: String,
271 pub signature: String,
272}
273
274fn cid_string<S>(c: &Cid, s: S) -> Result<S::Ok, S::Error>
275where
276 S: Serializer,
277{
278 s.serialize_str(&c.to_string())
279}
280
281pub fn string_cid<'de, D>(d: D) -> Result<Cid, D::Error>
282where
283 D: Deserializer<'de>,
284{
285 use serde::de::Error;
286
287 let string = String::deserialize(d)?;
288
289 Cid::from_str(&string).map_err(|e| D::Error::custom(e.to_string()))
290}
291
292pub fn cid_from_string(s: &str) -> Result<Cid, libipld::cid::Error> {
293 Cid::from_str(s)
294}
295
296#[allow(dead_code)]
297impl<F: LurkField> Claim<F> {
298 pub fn is_evaluation(&self) -> bool {
299 self.evaluation().is_some()
300 }
301 pub fn is_opening(&self) -> bool {
302 self.opening().is_some()
303 }
304 pub fn evaluation(&self) -> Option<Evaluation> {
305 match self {
306 Self::Evaluation(e) => Some(e.clone()),
307 _ => None,
308 }
309 }
310 pub fn opening(&self) -> Option<Opening<F>> {
311 match self {
312 Self::Opening(o) => Some(o.clone()),
313 _ => None,
314 }
315 }
316}
317
318type E = Error;
319impl TryFrom<usize> for ReductionCount {
320 type Error = E;
321
322 fn try_from(count: usize) -> Result<Self, <Self as TryFrom<usize>>::Error> {
323 match count {
324 1 => Ok(ReductionCount::One),
325 5 => Ok(ReductionCount::Five),
326 10 => Ok(ReductionCount::Ten),
327 100 => Ok(ReductionCount::OneHundred),
328 c => Err(Error::UnsupportedReductionCount(c)),
329 }
330 }
331}
332impl ReductionCount {
333 pub fn count(&self) -> usize {
334 match self {
335 Self::One => 1,
336 Self::Five => 5,
337 Self::Ten => 10,
338 Self::OneHundred => 100,
339 }
340 }
341}
342
343pub trait Id
344where
345 Self: Sized,
346{
347 fn id(&self) -> String;
348 fn cid(&self) -> Cid;
349 fn has_id(&self, id: String) -> bool;
350}
351
352impl<T: Serialize> Id for T
353where
354 for<'de> T: Deserialize<'de>,
355{
356 fn cid(&self) -> Cid {
357 let ipld = to_ipld(self).unwrap();
358 let dag_json = DagJsonCodec.encode(&ipld).unwrap();
359
360 let digest = Code::Blake3_256.digest(&dag_json);
361 Cid::new_v1(0x55, digest)
362 }
363
364 fn id(&self) -> String {
365 self.cid().to_string()
366 }
367
368 fn has_id(&self, id: String) -> bool {
369 self.id() == id
370 }
371}
372
373pub trait FileStore
374where
375 Self: Sized,
376{
377 fn write_to_path<P: AsRef<Path>>(&self, path: P);
378 fn read_from_path<P: AsRef<Path>>(path: P) -> Result<Self, Error>;
379 fn read_from_stdin() -> Result<Self, Error>;
380}
381
382impl<T: Serialize> FileStore for T
383where
384 for<'de> T: Deserialize<'de>, {
386 fn write_to_path<P: AsRef<Path>>(&self, path: P) {
387 let file = File::create(path).expect("failed to create file");
388 let writer = BufWriter::new(&file);
389
390 serde_json::to_writer(writer, &self).expect("failed to write file");
391 }
392
393 fn read_from_path<P: AsRef<Path>>(path: P) -> Result<Self, Error> {
394 let file = File::open(path)?;
395 let reader = BufReader::new(file);
396 Ok(serde_json::from_reader(reader)?)
397 }
398
399 fn read_from_stdin() -> Result<Self, Error> {
400 let reader = BufReader::new(io::stdin());
401 Ok(serde_json::from_reader(reader).expect("failed to read from stdin"))
402 }
403}
404
405impl Evaluation {
406 fn new<F: LurkField>(
407 s: &mut Store<F>,
408 input: IO<F>,
409 output: IO<F>,
410 iterations: Option<usize>, ) -> Self {
413 let status: Status = output.cont.into();
414 let terminal = status.is_terminal();
415
416 macro_rules! maybe_hide {
420 ($x:expr) => {
421 if terminal {
422 $x
423 } else {
424 "".to_string()
425 }
426 };
427 }
428
429 let expr = input.expr.fmt_to_string(s);
430 let env = input.env.fmt_to_string(s);
431 let cont = input.cont.fmt_to_string(s);
432
433 let expr_out = maybe_hide!(output.expr.fmt_to_string(s));
434 let env_out = maybe_hide!(output.env.fmt_to_string(s));
435 let cont_out = maybe_hide!(output.cont.fmt_to_string(s));
436
437 Self {
438 expr,
439 env,
440 cont,
441 expr_out,
442 env_out,
443 cont_out,
444 status,
445 iterations,
446 }
447 }
448
449 pub fn eval<F: LurkField + Serialize>(
450 store: &mut Store<F>,
451 expr: Ptr<F>,
452 limit: usize,
453 ) -> Result<Self, Error> {
454 let env = empty_sym_env(store);
455 let mut evaluator = Evaluator::new(expr, env, store, limit);
456
457 let input = evaluator.initial();
458
459 let (output, iterations, _) = evaluator.eval().map_err(|_| Error::EvaluationFailure)?;
460
461 Ok(Self::new(store, input, output, Some(iterations)))
462 }
463}
464
465impl<F: LurkField + Serialize + DeserializeOwned> Commitment<F> {
466 pub fn from_comm(s: &mut Store<F>, ptr: &Ptr<F>) -> Self {
467 let digest = *s.hash_expr(ptr).expect("couldn't hash ptr").value();
468
469 assert_eq!(ExprTag::Comm, ptr.tag());
470
471 Commitment { comm: digest }
472 }
473
474 pub fn ptr(&self, s: &mut Store<F>) -> Ptr<F> {
475 s.intern_opaque_comm(self.comm)
476 }
477
478 pub fn from_ptr_with_hiding(s: &mut Store<F>, ptr: &Ptr<F>) -> (Self, F) {
479 let secret = F::random(OsRng);
480
481 let commitment = Self::from_ptr_and_secret(s, ptr, secret);
482
483 (commitment, secret)
484 }
485
486 pub fn from_ptr_and_secret(s: &mut Store<F>, ptr: &Ptr<F>, secret: F) -> Self {
487 let hidden = s.hide(secret, *ptr);
488
489 Self::from_comm(s, &hidden)
490 }
491
492 fn construct_with_fun_application(
494 s: &mut Store<F>,
495 function: CommittedExpression<F>,
496 input: Ptr<F>,
497 limit: usize,
498 ) -> Result<(Self, Ptr<F>), Error> {
499 let fun_ptr = function.expr_ptr(s, limit)?;
500 let secret = function.secret.expect("CommittedExpression secret missing");
501
502 let commitment = Self::from_ptr_and_secret(s, &fun_ptr, secret);
503
504 let open = s.sym("open");
505 let comm_ptr = s.hide(secret, fun_ptr);
506
507 let fun_expr = s.list(&[open, comm_ptr]);
509
510 let expression = s.list(&[fun_expr, input]);
512
513 Ok((commitment, expression))
514 }
515
516 fn fun_application(&self, s: &mut Store<F>, input: Ptr<F>) -> Ptr<F> {
517 let open = s.sym("open");
518 let comm_ptr = self.ptr(s);
519
520 let fun_expr = s.list(&[open, comm_ptr]);
522
523 s.list(&[fun_expr, input])
525 }
526}
527
528impl<F: LurkField + Serialize + DeserializeOwned> CommittedExpression<F> {
529 pub fn expr_ptr(&self, s: &mut Store<F>, limit: usize) -> Result<Ptr<F>, Error> {
530 let source_ptr = self.expr.ptr(s, limit);
531
532 Ok(source_ptr)
533 }
534}
535
536impl LurkPtr {
537 pub fn ptr<F: LurkField + Serialize + DeserializeOwned>(
538 &self,
539 s: &mut Store<F>,
540 limit: usize,
541 ) -> Ptr<F> {
542 match self {
543 LurkPtr::Source(source) => {
544 let ptr = s.read(source).expect("could not read source");
545 let (out, _) = evaluate(s, ptr, limit).unwrap();
546
547 out.expr
548 }
549 LurkPtr::ScalarBytes(lurk_scalar_bytes) => {
550 let scalar_store: Ipld = DagCborCodec
551 .decode(&lurk_scalar_bytes.scalar_store)
552 .expect("could not read opaque scalar store");
553 let scalar_ptr: Ipld = DagCborCodec
554 .decode(&lurk_scalar_bytes.scalar_ptr)
555 .expect("could not read opaque scalar ptr");
556
557 let lurk_ptr = LurkPtr::Ipld(LurkScalarIpld {
558 scalar_store,
559 scalar_ptr,
560 });
561
562 lurk_ptr.ptr(s, limit)
563 }
564 LurkPtr::Ipld(lurk_scalar_ipld) => {
565 let fun_scalar_store: ScalarStore<F> =
567 from_ipld(lurk_scalar_ipld.scalar_store.clone()).unwrap();
568 let fun_scalar_ptr: ScalarPtr<F> =
569 from_ipld(lurk_scalar_ipld.scalar_ptr.clone()).unwrap();
570 s.intern_scalar_ptr(fun_scalar_ptr, &fun_scalar_store)
571 .expect("failed to intern scalar_ptr for fun")
572 }
573 }
574 }
575
576 pub fn from_ptr<F: LurkField + Serialize>(s: &mut Store<F>, ptr: &Ptr<F>) -> Self {
577 let (scalar_store, scalar_ptr) = ScalarStore::new_with_expr(s, ptr);
578 let scalar_ptr = scalar_ptr.unwrap();
579
580 let scalar_store_ipld = to_ipld(scalar_store).unwrap();
581 let new_fun_ipld = to_ipld(scalar_ptr).unwrap();
582
583 let scalar_store_bytes = DagCborCodec.encode(&scalar_store_ipld).unwrap();
584 let new_fun_bytes = DagCborCodec.encode(&new_fun_ipld).unwrap();
585
586 let again = from_ipld(new_fun_ipld).unwrap();
587 assert_eq!(&scalar_ptr, &again);
588
589 Self::ScalarBytes(LurkScalarBytes {
590 scalar_store: scalar_store_bytes,
591 scalar_ptr: new_fun_bytes,
592 })
593 }
594}
595
596impl Expression {
597 pub fn eval<F: LurkField + Serialize + DeserializeOwned>(
598 &self,
599 s: &mut Store<F>,
600 limit: usize,
601 ) -> Result<Ptr<F>, Error> {
602 let expr = self.expr.ptr(s, limit);
603 let (io, _iterations) = evaluate(s, expr, limit)?;
604
605 Ok(io.expr)
606 }
607}
608
609impl<'a> Opening<S1> {
610 #[allow(clippy::too_many_arguments)]
611 pub fn apply_and_prove(
612 s: &'a mut Store<S1>,
613 input: Ptr<S1>,
614 function: CommittedExpression<S1>,
615 limit: usize,
616 chain: bool,
617 only_use_cached_proofs: bool,
618 nova_prover: &'a NovaProver<S1>,
619 pp: &'a PublicParams,
620 ) -> Result<Proof<'a, S1>, Error> {
621 let claim = Self::apply(s, input, function, limit, chain)?;
622 Proof::prove_claim(s, &claim, limit, only_use_cached_proofs, nova_prover, pp)
623 }
624
625 pub fn open_and_prove(
626 s: &'a mut Store<S1>,
627 request: OpeningRequest<S1>,
628 limit: usize,
629 only_use_cached_proofs: bool,
630 nova_prover: &'a NovaProver<S1>,
631 pp: &'a PublicParams,
632 ) -> Result<Proof<'a, S1>, Error> {
633 let input = request.input.expr.ptr(s, limit);
634 let commitment = request.commitment;
635
636 let function_map = committed_expression_store();
637 let function = function_map
638 .get(&commitment)
639 .ok_or(Error::UnknownCommitment)?;
640
641 Self::apply_and_prove(
642 s,
643 input,
644 function,
645 limit,
646 request.chain,
647 only_use_cached_proofs,
648 nova_prover,
649 pp,
650 )
651 }
652
653 pub fn open(
654 s: &mut Store<S1>,
655 request: OpeningRequest<S1>,
656 limit: usize,
657 chain: bool,
658 ) -> Result<Claim<S1>, Error> {
659 let input = request.input.expr.ptr(s, limit);
660 let commitment = request.commitment;
661
662 let function_map = committed_expression_store();
663 let function = function_map
664 .get(&commitment)
665 .ok_or(Error::UnknownCommitment)?;
666
667 Self::apply(s, input, function, limit, chain)
668 }
669
670 fn _is_chained(&self) -> bool {
671 self.new_commitment.is_some()
672 }
673
674 fn public_output_expression(&self, s: &mut Store<S1>) -> Ptr<S1> {
675 let result = s.read(&self.output).expect("unreadable result");
676
677 if let Some(commitment) = self.new_commitment {
678 let c = commitment.ptr(s);
679
680 s.cons(result, c)
681 } else {
682 result
683 }
684 }
685
686 pub fn apply(
687 s: &mut Store<S1>,
688 input: Ptr<S1>,
689 function: CommittedExpression<S1>,
690 limit: usize,
691 chain: bool,
692 ) -> Result<Claim<S1>, Error> {
693 let (commitment, expression) =
694 Commitment::construct_with_fun_application(s, function, input, limit)?;
695 let (public_output, _iterations) = evaluate(s, expression, limit)?;
696
697 let (new_commitment, output_expr) = if chain {
698 let cons = public_output.expr;
699 let result_expr = s.car(&cons)?;
700 let new_comm = s.cdr(&cons)?;
701
702 let new_secret0 = s.secret(new_comm).expect("secret missing");
703 let new_secret = *s.get_expr_hash(&new_secret0).expect("hash missing").value();
704
705 let (_, new_fun) = s.open(new_comm).expect("opening missing");
706 let new_commitment = Commitment::from_comm(s, &new_comm);
707
708 s.hydrate_scalar_cache();
709
710 let expr = LurkPtr::from_ptr(s, &new_fun);
711
712 let new_function = CommittedExpression::<S1> {
713 expr,
714 secret: Some(new_secret),
715 commitment: Some(new_commitment),
716 };
717
718 let function_map = committed_expression_store();
719 function_map.set(new_commitment, &new_function)?;
720
721 (Some(new_commitment), result_expr)
722 } else {
723 (None, public_output.expr)
724 };
725
726 let input_string = input.fmt_to_string(s);
727 let status = public_output.status();
728 let output_string = if status.is_terminal() {
729 output_expr.fmt_to_string(s)
731 } else {
732 "".to_string()
736 };
737
738 let claim = Claim::Opening(Opening {
739 commitment,
740 new_commitment,
741 input: input_string,
742 output: output_string,
743 status,
744 });
745
746 Ok(claim)
747 }
748}
749
750impl<'a> Proof<'a, S1> {
751 pub fn eval_and_prove(
752 s: &'a mut Store<S1>,
753 expr: Ptr<S1>,
754 limit: usize,
755 only_use_cached_proofs: bool,
756 nova_prover: &'a NovaProver<S1>,
757 pp: &'a PublicParams,
758 ) -> Result<Self, Error> {
759 let env = empty_sym_env(s);
760 let cont = s.intern_cont_outermost();
761 let input = IO { expr, env, cont };
762
763 let (public_output, _iterations) = evaluate(s, expr, limit)?;
764 let evaluation = Evaluation::new(s, input, public_output, None);
765 let claim = Claim::Evaluation(evaluation);
766
767 Self::prove_claim(s, &claim, limit, only_use_cached_proofs, nova_prover, pp)
768 }
769
770 pub fn prove_claim(
771 s: &'a mut Store<S1>,
772 claim: &Claim<S1>,
773 limit: usize,
774 only_use_cached_proofs: bool,
775 nova_prover: &'a NovaProver<S1>,
776 pp: &'a PublicParams,
777 ) -> Result<Self, Error> {
778 let reduction_count = nova_prover.reduction_count();
779
780 let proof_map = nova_proof_cache(reduction_count);
781 let function_map = committed_expression_store();
782
783 let cid = claim.cid();
784
785 if let Some(proof) = proof_map.get(&cid) {
786 return Ok(proof);
787 }
788
789 if only_use_cached_proofs {
790 panic!("no cached proof");
792 }
793
794 info!("Starting Proving");
795
796 let (expr, env) = match &claim {
797 Claim::Evaluation(e) => (
798 s.read(&e.expr).expect("bad expression"),
799 s.read(&e.env).expect("bad env"),
800 ),
801 Claim::Opening(o) => {
802 let commitment = o.commitment;
803
804 let function = function_map
806 .get(&commitment)
807 .expect("function for commitment missing");
808
809 let input = s.read(&o.input).expect("bad expression");
810 let (c, expression) =
811 Commitment::construct_with_fun_application(s, function, input, limit)?;
812
813 assert_eq!(commitment, c);
814 (expression, empty_sym_env(s))
815 }
816 };
817
818 let (proof, _public_input, _public_output, num_steps) = nova_prover
819 .evaluate_and_prove(pp, expr, env, s, limit)
820 .expect("Nova proof failed");
821
822 let proof = Self {
823 claim: claim.clone(),
824 proof,
825 num_steps,
826 reduction_count: ReductionCount::try_from(reduction_count)?,
827 };
828
829 match &claim {
830 Claim::Opening(o) => {
831 if o.status != Status::Terminal {
832 return Err(Error::OpeningFailure("Claim status is not Terminal".into()));
833 };
834 }
835 Claim::Evaluation(e) => {
836 if e.status != Status::Terminal {
837 return Err(Error::EvaluationFailure);
838 };
839 }
840 };
841
842 proof.verify(pp).expect("Nova verification failed");
843
844 proof_map.set(cid, &proof).unwrap();
845
846 Ok(proof)
847 }
848
849 pub fn verify(&self, pp: &PublicParams) -> Result<VerificationResult, Error> {
850 let (public_inputs, public_outputs) = self.io_vecs()?;
851
852 let claim_iterations_and_num_steps_are_consistent = if let Claim::Evaluation(Evaluation {
853 iterations: Some(iterations),
854 ..
855 }) = self.claim
856 {
857 let num_steps = self.num_steps;
867
868 let chunk_frame_count = self.reduction_count.count();
869 let expected_steps =
870 (iterations / chunk_frame_count) + (iterations % chunk_frame_count != 0) as usize;
871
872 expected_steps == num_steps
873 } else {
874 true
875 };
876
877 let verified = claim_iterations_and_num_steps_are_consistent
878 && self
879 .proof
880 .verify(pp, self.num_steps, public_inputs, &public_outputs)
881 .expect("error verifying");
882
883 let result = VerificationResult::new(verified);
884
885 Ok(result)
886 }
887
888 pub fn evaluation_io(&self, s: &mut Store<S1>) -> Result<(IO<S1>, IO<S1>), Error> {
889 let evaluation = &self.claim.evaluation().expect("expected evaluation claim");
890
891 let input_io = {
892 let expr = s
893 .read(&evaluation.expr)
894 .map_err(|_| Error::VerificationError("failed to read expr".into()))?;
895
896 let env = s
897 .read(&evaluation.env)
898 .map_err(|_| Error::VerificationError("failed to read env".into()))?;
899
900 let cont = s.intern_cont_outermost();
902
903 IO::<S1> { expr, env, cont }
904 };
905
906 let output_io = {
907 let expr = s
908 .read(&evaluation.expr_out)
909 .map_err(|_| Error::VerificationError("failed to read expr out".into()))?;
910
911 let env = s
912 .read(&evaluation.env_out)
913 .map_err(|_| Error::VerificationError("failed to read env out".into()))?;
914 let cont = evaluation
915 .status
916 .to_cont(s)
917 .ok_or_else(|| Error::VerificationError("continuation cannot be proved".into()))?;
918
919 IO::<S1> { expr, env, cont }
920 };
921
922 Ok((input_io, output_io))
923 }
924
925 pub fn opening_io(&self, s: &mut Store<S1>) -> Result<(IO<S1>, IO<S1>), Error> {
926 assert!(self.claim.is_opening());
927
928 let opening = self.claim.opening().expect("expected opening claim");
929 let output = opening.public_output_expression(s);
930 let input = s.read(&opening.input).expect("could not read input");
931
932 let expression = opening.commitment.fun_application(s, input);
933 let outermost = s.intern_cont_outermost();
934
935 let input_io = IO::<S1> {
936 expr: expression,
937 env: empty_sym_env(s),
938 cont: outermost,
939 };
940
941 let output_io = IO::<S1> {
942 expr: output,
943 env: empty_sym_env(s),
944 cont: s.intern_cont_terminal(),
945 };
946
947 Ok((input_io, output_io))
948 }
949
950 pub fn io(&self, s: &mut Store<S1>) -> Result<(IO<S1>, IO<S1>), Error> {
951 match self.claim {
952 Claim::Evaluation(_) => self.evaluation_io(s),
953 Claim::Opening(_) => self.opening_io(s),
954 }
955 }
956 fn io_vecs(&self) -> Result<(Vec<S1>, Vec<S1>), Error> {
957 let s = &mut Store::<S1>::default();
958
959 self.io(s).map(|(i, o)| (i.to_inputs(s), o.to_inputs(s)))
960 }
961}
962
963impl VerificationResult {
964 fn new(verified: bool) -> Self {
965 Self { verified }
966 }
967}
968
969pub fn evaluate<F: LurkField>(
970 store: &mut Store<F>,
971 expr: Ptr<F>,
972 limit: usize,
973) -> Result<(IO<F>, usize), Error> {
974 let env = empty_sym_env(store);
975 let mut evaluator = Evaluator::new(expr, env, store, limit);
976
977 let (io, iterations, _) = evaluator.eval().map_err(|_| Error::EvaluationFailure)?;
978
979 assert!(io.is_terminal());
980 Ok((io, iterations))
981}
982
983#[cfg(test)]
984mod test {
985 use super::*;
986
987 #[test]
988 fn test_cert_serialization() {
989 use serde_json::json;
990
991 let c = Commitment {
992 comm: S1::from(123),
993 };
994
995 let cid = c.cid();
996 let cert = Cert {
997 claim_cid: cid,
998 proof_cid: cid,
999 verified: true,
1000 verifier_id: "asdf".to_string(),
1001 signature: "fdsa".to_string(),
1002 };
1003 let json = json!(cert);
1004
1005 let string = json.to_string();
1006
1007 let cert_again: Cert = serde_json::from_str(&string).unwrap();
1008 assert_eq!(cert, cert_again);
1009 }
1010}