tor_hscrypto/pow/v1/
verify.rs

1//! Verifier implementation for v1 client puzzles
2
3use crate::pow::err::Error;
4use crate::pow::v1::challenge::Challenge;
5use crate::pow::v1::{err::RuntimeErrorV1, err::SolutionErrorV1, types::Instance, types::Solution};
6use equix::{EquiXBuilder, HashError, RuntimeOption};
7
8use super::Seed;
9
10/// Checker for potential [`Solution`]s to a particular puzzle [`Instance`]
11///
12/// Holds information about the puzzle instance, and optional configuration
13/// settings.
14pub struct Verifier {
15    /// The puzzle instance we're verifying
16    instance: Instance,
17    /// Configuration settings for Equi-X, as an [`EquiXBuilder`] instance
18    equix: EquiXBuilder,
19}
20
21impl Verifier {
22    /// Construct a new [`Verifier`] by wrapping an [`Instance`].
23    pub fn new(instance: Instance) -> Self {
24        Self {
25            instance,
26            equix: Default::default(),
27        }
28    }
29
30    /// Construct a new [`Verifier`], explicitly taking a [`EquiXBuilder`].
31    pub fn new_with_equix(instance: Instance, equix: EquiXBuilder) -> Self {
32        Self { instance, equix }
33    }
34
35    /// Select the HashX runtime to use for this verifier.
36    ///
37    /// By default, uses [`RuntimeOption::TryCompile`]
38    pub fn runtime(&mut self, option: RuntimeOption) -> &mut Self {
39        self.equix.runtime(option);
40        self
41    }
42
43    /// Check whether a solution is valid for this puzzle instance.
44    ///
45    /// May return a [`SolutionErrorV1`] or a [`RuntimeErrorV1`]
46    pub fn check(&self, solution: &Solution) -> Result<(), Error> {
47        match self.check_seed(solution) {
48            Err(e) => Err(Error::BadSolution(e.into())),
49            Ok(()) => {
50                let challenge = Challenge::new(&self.instance, solution.effort(), solution.nonce());
51                match challenge.check_effort(&solution.proof_to_bytes()) {
52                    Err(e) => Err(Error::BadSolution(e.into())),
53                    Ok(()) => match self.equix.verify(challenge.as_ref(), solution.proof()) {
54                        Ok(()) => Ok(()),
55                        Err(equix::Error::HashSum) => {
56                            Err(Error::BadSolution(SolutionErrorV1::HashSum.into()))
57                        }
58                        Err(equix::Error::Hash(HashError::ProgramConstraints)) => Err(
59                            Error::BadSolution(SolutionErrorV1::ChallengeConstraints.into()),
60                        ),
61                        Err(e) => Err(Error::VerifyRuntime(RuntimeErrorV1::EquiX(e).into())),
62                    },
63                }
64            }
65        }
66    }
67
68    /// Check the [`super::SeedHead`] of a solution against an [`Instance`].
69    ///
70    /// This is a very cheap test, this should come first so a service
71    /// can verify every [`Solution`] against its last two [`Instance`]s.
72    fn check_seed(&self, solution: &Solution) -> Result<(), SolutionErrorV1> {
73        if solution.seed_head() == self.instance.seed().head() {
74            Ok(())
75        } else {
76            Err(SolutionErrorV1::Seed)
77        }
78    }
79
80    /// Return the seed for this verifier's instance.
81    pub fn seed(&self) -> &Seed {
82        self.instance.seed()
83    }
84}