1
//! Verifier implementation for v1 client puzzles
2

            
3
use crate::pow::err::Error;
4
use crate::pow::v1::challenge::Challenge;
5
use crate::pow::v1::{err::RuntimeErrorV1, err::SolutionErrorV1, types::Instance, types::Solution};
6
use equix::{EquiXBuilder, HashError, RuntimeOption};
7

            
8
use 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.
14
pub 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

            
21
impl Verifier {
22
    /// Construct a new [`Verifier`] by wrapping an [`Instance`].
23
56
    pub fn new(instance: Instance) -> Self {
24
56
        Self {
25
56
            instance,
26
56
            equix: Default::default(),
27
56
        }
28
56
    }
29

            
30
    /// Select the HashX runtime to use for this verifier.
31
    ///
32
    /// By default, uses [`RuntimeOption::TryCompile`]
33
    pub fn runtime(&mut self, option: RuntimeOption) -> &mut Self {
34
        self.equix.runtime(option);
35
        self
36
    }
37

            
38
    /// Check whether a solution is valid for this puzzle instance.
39
    ///
40
    /// May return a [`SolutionErrorV1`] or a [`RuntimeErrorV1`]
41
56
    pub fn check(&self, solution: &Solution) -> Result<(), Error> {
42
56
        match self.check_seed(solution) {
43
2
            Err(e) => Err(Error::BadSolution(e.into())),
44
            Ok(()) => {
45
54
                let challenge = Challenge::new(&self.instance, solution.effort(), solution.nonce());
46
54
                match challenge.check_effort(&solution.proof_to_bytes()) {
47
6
                    Err(e) => Err(Error::BadSolution(e.into())),
48
48
                    Ok(()) => match self.equix.verify(challenge.as_ref(), solution.proof()) {
49
46
                        Ok(()) => Ok(()),
50
                        Err(equix::Error::HashSum) => {
51
2
                            Err(Error::BadSolution(SolutionErrorV1::HashSum.into()))
52
                        }
53
                        Err(equix::Error::Hash(HashError::ProgramConstraints)) => Err(
54
                            Error::BadSolution(SolutionErrorV1::ChallengeConstraints.into()),
55
                        ),
56
                        Err(e) => Err(Error::VerifyRuntime(RuntimeErrorV1::EquiX(e).into())),
57
                    },
58
                }
59
            }
60
        }
61
56
    }
62

            
63
    /// Check the [`super::SeedHead`] of a solution against an [`Instance`].
64
    ///
65
    /// This is a very cheap test, this should come first so a service
66
    /// can verify every [`Solution`] against its last two [`Instance`]s.
67
56
    fn check_seed(&self, solution: &Solution) -> Result<(), SolutionErrorV1> {
68
56
        if solution.seed_head() == self.instance.seed().head() {
69
54
            Ok(())
70
        } else {
71
2
            Err(SolutionErrorV1::Seed)
72
        }
73
56
    }
74

            
75
    /// Return the seed for this verifier's instance.
76
    pub fn seed(&self) -> &Seed {
77
        self.instance.seed()
78
    }
79
}