1use super::dht_protocol;
4use super::dht_protocol::FirstDhTupleProverMessage;
5use super::fiat_shamir::FiatShamirTreeSerializationError;
6use super::prover::ProofBytes;
7use super::sig_serializer::SigParsingError;
8use super::unchecked_tree::UncheckedDhTuple;
9use super::{
10 dlog_protocol,
11 fiat_shamir::{fiat_shamir_hash_fn, fiat_shamir_tree_to_bytes},
12 sig_serializer::parse_sig_compute_challenges,
13 unchecked_tree::{UncheckedLeaf, UncheckedSchnorr},
14 SigmaBoolean, UncheckedTree,
15};
16use crate::eval::context::Context;
17use crate::eval::EvalError;
18use crate::eval::{reduce_to_crypto, ReductionDiagnosticInfo};
19use dlog_protocol::FirstDlogProverMessage;
20use ergotree_ir::ergo_tree::ErgoTree;
21use ergotree_ir::ergo_tree::ErgoTreeError;
22
23use derive_more::From;
24use thiserror::Error;
25
26#[derive(Error, Debug, From)]
28pub enum VerifierError {
29 #[error("ErgoTreeError: {0}")]
31 ErgoTreeError(ErgoTreeError),
32 #[error("EvalError: {0}")]
34 EvalError(EvalError),
35 #[error("SigParsingError: {0}")]
37 SigParsingError(SigParsingError),
38 #[error("Fiat-Shamir tree serialization error: {0}")]
40 FiatShamirTreeSerializationError(FiatShamirTreeSerializationError),
41}
42
43#[derive(Debug, Clone)]
45pub struct VerificationResult {
46 pub result: bool,
48 pub cost: u64,
50 pub diag: ReductionDiagnosticInfo,
52}
53
54pub trait Verifier {
56 fn verify<'ctx>(
61 &self,
62 tree: &ErgoTree,
63 ctx: &Context<'ctx>,
64 proof: ProofBytes,
65 message: &[u8],
66 ) -> Result<VerificationResult, VerifierError> {
67 let expr = tree.proposition()?;
68 let reduction_result = reduce_to_crypto(&expr, ctx)?;
69 let res: bool = match reduction_result.sigma_prop {
70 SigmaBoolean::TrivialProp(b) => b,
71 sb => {
72 match proof {
73 ProofBytes::Empty => false,
74 ProofBytes::Some(proof_bytes) => {
75 let unchecked_tree = parse_sig_compute_challenges(&sb, proof_bytes)?;
77 check_commitments(unchecked_tree, message)?
79 }
80 }
81 }
82 };
83 Ok(VerificationResult {
84 result: res,
85 cost: 0,
86 diag: reduction_result.diag,
87 })
88 }
89}
90
91pub fn verify_signature(
93 sigma_tree: SigmaBoolean,
94 message: &[u8],
95 signature: &[u8],
96) -> Result<bool, VerifierError> {
97 let res: bool = match sigma_tree {
98 SigmaBoolean::TrivialProp(b) => b,
99 sb => {
100 match signature {
101 [] => false,
102 _ => {
103 let unchecked_tree = parse_sig_compute_challenges(&sb, signature.to_vec())?;
105 check_commitments(unchecked_tree, message)?
107 }
108 }
109 }
110 };
111 Ok(res)
112}
113
114fn check_commitments(sp: UncheckedTree, message: &[u8]) -> Result<bool, VerifierError> {
116 let new_root = compute_commitments(sp);
118 let mut s = fiat_shamir_tree_to_bytes(&new_root.clone().into())?;
119 s.append(&mut message.to_vec());
120 let expected_challenge = fiat_shamir_hash_fn(s.as_slice());
125 Ok(new_root.challenge() == expected_challenge.into())
126}
127
128pub fn compute_commitments(sp: UncheckedTree) -> UncheckedTree {
132 match sp {
133 UncheckedTree::UncheckedLeaf(leaf) => match leaf {
134 UncheckedLeaf::UncheckedSchnorr(sn) => {
135 let a = dlog_protocol::interactive_prover::compute_commitment(
136 &sn.proposition,
137 &sn.challenge,
138 &sn.second_message,
139 );
140 UncheckedSchnorr {
141 commitment_opt: Some(FirstDlogProverMessage { a: a.into() }),
142 ..sn
143 }
144 .into()
145 }
146 UncheckedLeaf::UncheckedDhTuple(dh) => {
147 let (a, b) = dht_protocol::interactive_prover::compute_commitment(
148 &dh.proposition,
149 &dh.challenge,
150 &dh.second_message,
151 );
152 UncheckedDhTuple {
153 commitment_opt: Some(FirstDhTupleProverMessage::new(a, b)),
154 ..dh
155 }
156 .into()
157 }
158 },
159 UncheckedTree::UncheckedConjecture(conj) => conj
160 .clone()
161 .with_children(conj.children_ust().mapped(compute_commitments))
162 .into(),
163 }
164}
165
166pub struct TestVerifier;
168
169impl Verifier for TestVerifier {}
170
171#[allow(clippy::unwrap_used)]
172#[allow(clippy::panic)]
173#[cfg(test)]
174#[cfg(feature = "arbitrary")]
175mod tests {
176 use std::convert::TryFrom;
177
178 use crate::sigma_protocol::private_input::{DhTupleProverInput, DlogProverInput, PrivateInput};
179 use crate::sigma_protocol::prover::hint::HintsBag;
180 use crate::sigma_protocol::prover::{Prover, TestProver};
181
182 use super::*;
183 use ergotree_ir::mir::atleast::Atleast;
184 use ergotree_ir::mir::constant::{Constant, Literal};
185 use ergotree_ir::mir::expr::Expr;
186 use ergotree_ir::mir::sigma_and::SigmaAnd;
187 use ergotree_ir::mir::sigma_or::SigmaOr;
188 use ergotree_ir::mir::value::CollKind;
189 use ergotree_ir::sigma_protocol::sigma_boolean::SigmaProp;
190 use ergotree_ir::types::stype::SType;
191 use proptest::collection::vec;
192 use proptest::prelude::*;
193 use sigma_test_util::force_any_val;
194
195 fn proof_append_some_byte(proof: &ProofBytes) -> ProofBytes {
196 match proof {
197 ProofBytes::Empty => panic!(),
198 ProofBytes::Some(bytes) => {
199 let mut new_bytes = bytes.clone();
200 new_bytes.push(1u8);
201 ProofBytes::Some(new_bytes)
202 }
203 }
204 }
205 proptest! {
206
207 #![proptest_config(ProptestConfig::with_cases(16))]
208
209 #[test]
210 fn test_prover_verifier_p2pk(secret in any::<DlogProverInput>(), message in vec(any::<u8>(), 100..200)) {
211 let pk = secret.public_image();
212 let tree = ErgoTree::try_from(Expr::Const(pk.into())).unwrap();
213
214 let prover = TestProver {
215 secrets: vec![PrivateInput::DlogProverInput(secret)],
216 };
217 let res = prover.prove(&tree,
218 &force_any_val::<Context>(),
219 message.as_slice(),
220 &HintsBag::empty());
221 let proof = res.unwrap().proof;
222 let verifier = TestVerifier;
223 prop_assert_eq!(verifier.verify(&tree,
224 &force_any_val::<Context>(),
225 proof.clone(),
226 message.as_slice())
227 .unwrap().result,
228 true);
229
230 prop_assert_eq!(verifier.verify(&tree,
232 &force_any_val::<Context>(),
233 proof_append_some_byte(&proof),
234 message.as_slice())
235 .unwrap().result,
236 true);
237
238 prop_assert_eq!(verifier.verify(&tree,
240 &force_any_val::<Context>(),
241 proof,
242 vec![1u8; 100].as_slice())
243 .unwrap().result,
244 false);
245 }
246
247 #[test]
248 fn test_prover_verifier_dht(secret in any::<DhTupleProverInput>(), message in vec(any::<u8>(), 100..200)) {
249 let pk = secret.public_image().clone();
250 let tree = ErgoTree::try_from(Expr::Const(pk.into())).unwrap();
251
252 let prover = TestProver {
253 secrets: vec![PrivateInput::DhTupleProverInput(secret)],
254 };
255 let res = prover.prove(&tree,
256 &force_any_val::<Context>(),
257 message.as_slice(),
258 &HintsBag::empty());
259 let proof = res.unwrap().proof;
260 let verifier = TestVerifier;
261 prop_assert_eq!(verifier.verify(&tree,
262 &force_any_val::<Context>(),
263 proof.clone(),
264 message.as_slice())
265 .unwrap().result,
266 true);
267
268 prop_assert_eq!(verifier.verify(&tree,
270 &force_any_val::<Context>(),
271 proof_append_some_byte(&proof),
272 message.as_slice())
273 .unwrap().result,
274 true);
275
276 prop_assert_eq!(verifier.verify(&tree,
278 &force_any_val::<Context>(),
279 proof,
280 vec![1u8; 100].as_slice())
281 .unwrap().result,
282 false);
283 }
284
285 #[test]
286 fn test_prover_verifier_conj_and(secret1 in any::<PrivateInput>(),
287 secret2 in any::<PrivateInput>(),
288 message in vec(any::<u8>(), 100..200)) {
289 let pk1 = secret1.public_image();
290 let pk2 = secret2.public_image();
291 let expr: Expr = SigmaAnd::new(vec![Expr::Const(pk1.into()), Expr::Const(pk2.into())])
292 .unwrap()
293 .into();
294 let tree = ErgoTree::try_from(expr).unwrap();
295 let prover = TestProver {
296 secrets: vec![secret1, secret2],
297 };
298 let res = prover.prove(&tree,
299 &force_any_val::<Context>(),
300 message.as_slice(),
301 &HintsBag::empty());
302 let proof = res.unwrap().proof;
303 let verifier = TestVerifier;
304 let ver_res = verifier.verify(&tree,
305 &force_any_val::<Context>(),
306 proof,
307 message.as_slice());
308 prop_assert_eq!(ver_res.unwrap().result, true);
309 }
310
311 #[test]
312 fn test_prover_verifier_conj_and_and(secret1 in any::<PrivateInput>(),
313 secret2 in any::<PrivateInput>(),
314 secret3 in any::<PrivateInput>(),
315 message in vec(any::<u8>(), 100..200)) {
316 let pk1 = secret1.public_image();
317 let pk2 = secret2.public_image();
318 let pk3 = secret3.public_image();
319 let expr: Expr = SigmaAnd::new(vec![
320 Expr::Const(pk1.into()),
321 SigmaAnd::new(vec![Expr::Const(pk2.into()), Expr::Const(pk3.into())])
322 .unwrap()
323 .into(),
324 ]).unwrap().into();
325 let tree = ErgoTree::try_from(expr).unwrap();
326 let prover = TestProver { secrets: vec![secret1, secret2, secret3] };
327 let res = prover.prove(&tree,
328 &force_any_val::<Context>(),
329 message.as_slice(),
330 &HintsBag::empty());
331 let proof = res.unwrap().proof;
332 let verifier = TestVerifier;
333 let ver_res = verifier.verify(&tree,
334 &force_any_val::<Context>(),
335 proof,
336 message.as_slice());
337 prop_assert_eq!(ver_res.unwrap().result, true);
338 }
339
340 #[test]
341 fn test_prover_verifier_conj_or(secret1 in any::<PrivateInput>(),
342 secret2 in any::<PrivateInput>(),
343 message in vec(any::<u8>(), 100..200)) {
344 let pk1 = secret1.public_image();
345 let pk2 = secret2.public_image();
346 let expr: Expr = SigmaOr::new(vec![Expr::Const(pk1.into()), Expr::Const(pk2.into())])
347 .unwrap()
348 .into();
349 let tree = ErgoTree::try_from(expr).unwrap();
350 let secrets = vec![secret1, secret2];
351 for secret in secrets {
353 let prover = TestProver {
354 secrets: vec![secret.clone()],
355 };
356 let res = prover.prove(&tree,
357 &force_any_val::<Context>(),
358 message.as_slice(),
359 &HintsBag::empty());
360 let proof = res.unwrap_or_else(|_| panic!("proof failed for secret: {:?}", secret)).proof;
361 let verifier = TestVerifier;
362 let ver_res = verifier.verify(&tree,
363 &force_any_val::<Context>(),
364 proof,
365 message.as_slice());
366 prop_assert_eq!(ver_res.unwrap().result, true, "verify failed on secret: {:?}", &secret);
367 }
368 }
369
370 #[test]
371 fn test_prover_verifier_conj_or_or(secret1 in any::<PrivateInput>(),
372 secret2 in any::<PrivateInput>(),
373 secret3 in any::<PrivateInput>(),
374 message in vec(any::<u8>(), 100..200)) {
375 let pk1 = secret1.public_image();
376 let pk2 = secret2.public_image();
377 let pk3 = secret3.public_image();
378 let expr: Expr = SigmaOr::new(vec![
379 Expr::Const(pk1.into()),
380 SigmaOr::new(vec![Expr::Const(pk2.into()), Expr::Const(pk3.into())])
381 .unwrap()
382 .into(),
383 ]).unwrap().into();
384 let tree = ErgoTree::try_from(expr).unwrap();
385 let secrets = vec![secret1, secret2, secret3];
386 for secret in secrets {
388 let prover = TestProver {
389 secrets: vec![secret.clone()],
390 };
391 let res = prover.prove(&tree,
392 &force_any_val::<Context>(),
393 message.as_slice(),
394 &HintsBag::empty());
395 let proof = res.unwrap_or_else(|_| panic!("proof failed for secret: {:?}", secret)).proof;
396 let verifier = TestVerifier;
397 let ver_res = verifier.verify(&tree,
398 &force_any_val::<Context>(),
399 proof,
400 message.as_slice());
401 prop_assert_eq!(ver_res.unwrap().result, true, "verify failed on secret: {:?}", &secret);
402 }
403 }
404
405 #[test]
406 fn test_prover_verifier_atleast(secret1 in any::<DlogProverInput>(),
407 secret2 in any::<DlogProverInput>(),
408 secret3 in any::<DlogProverInput>(),
409 message in vec(any::<u8>(), 100..200)) {
410 let bound = Expr::Const(2i32.into());
411 let inputs = Literal::Coll(
412 CollKind::from_collection(
413 SType::SSigmaProp,
414 [
415 SigmaProp::from(secret1.public_image()).into(),
416 SigmaProp::from(secret2.public_image()).into(),
417 SigmaProp::from(secret3.public_image()).into(),
418 ],
419 )
420 .unwrap(),
421 );
422 let input = Constant {
423 tpe: SType::SColl(SType::SSigmaProp.into()),
424 v: inputs,
425 }
426 .into();
427 let expr: Expr = Atleast::new(bound, input).unwrap().into();
428 let tree = ErgoTree::try_from(expr).unwrap();
429 let prover = TestProver {
430 secrets: vec![secret1.into(), secret3.into()],
431 };
432
433 let res = prover.prove(&tree,
434 &force_any_val::<Context>(),
435 message.as_slice(),
436 &HintsBag::empty());
437 let proof = res.unwrap().proof;
438 let verifier = TestVerifier;
439 let ver_res = verifier.verify(&tree,
440 &force_any_val::<Context>(),
441 proof,
442 message.as_slice());
443 prop_assert_eq!(ver_res.unwrap().result, true)
444 }
445 }
446}