Skip to main content

voting_circuits/
prove_error.rs

1use halo2_proofs::{
2    pasta::EqAffine,
3    plonk::{self, create_proof},
4    poly::commitment::Params,
5    transcript::{Blake2bWrite, Challenge255},
6};
7use pasta_curves::vesta;
8use rand::rngs::OsRng;
9use std::vec::Vec;
10
11pub(crate) fn create_proof_bytes<ConcreteCircuit>(
12    params: &Params<EqAffine>,
13    pk: &plonk::ProvingKey<EqAffine>,
14    circuit: ConcreteCircuit,
15    public_inputs: &[vesta::Scalar],
16) -> Result<Vec<u8>, ProveError>
17where
18    ConcreteCircuit: plonk::Circuit<vesta::Scalar>,
19{
20    let mut transcript = Blake2bWrite::<_, EqAffine, Challenge255<_>>::init(vec![]);
21    create_proof(
22        params,
23        pk,
24        &[circuit],
25        &[&[public_inputs]],
26        OsRng,
27        &mut transcript,
28    )?;
29    Ok(transcript.finalize())
30}
31
32/// Error returned when Halo2 proof creation fails.
33#[derive(Debug)]
34#[non_exhaustive]
35pub enum ProveError {
36    /// Halo2 failed while generating a verifying key.
37    KeygenVk(plonk::Error),
38    /// Halo2 failed while generating a proving key.
39    KeygenPk(plonk::Error),
40    /// Cached key generation previously failed.
41    CachedKeygen(String),
42    /// Halo2 rejected the proof inputs or failed during synthesis.
43    Halo2(plonk::Error),
44}
45
46impl From<plonk::Error> for ProveError {
47    fn from(error: plonk::Error) -> Self {
48        ProveError::Halo2(error)
49    }
50}
51
52impl core::fmt::Display for ProveError {
53    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
54        match self {
55            ProveError::KeygenVk(error) => {
56                write!(f, "Halo2 verifying key generation failed: {error}")
57            }
58            ProveError::KeygenPk(error) => {
59                write!(f, "Halo2 proving key generation failed: {error}")
60            }
61            ProveError::CachedKeygen(error) => write!(f, "Halo2 key generation failed: {error}"),
62            ProveError::Halo2(error) => write!(f, "Halo2 proof creation failed: {error}"),
63        }
64    }
65}
66
67impl std::error::Error for ProveError {
68    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
69        match self {
70            ProveError::KeygenVk(error) => Some(error),
71            ProveError::KeygenPk(error) => Some(error),
72            ProveError::CachedKeygen(_) => None,
73            ProveError::Halo2(error) => Some(error),
74        }
75    }
76}
77
78#[cfg(test)]
79mod tests {
80    use super::*;
81    use halo2_proofs::{
82        circuit::{Layouter, SimpleFloorPlanner, Value},
83        plonk::{Advice, Column, ConstraintSystem, Error as PlonkError, Instance},
84    };
85
86    #[derive(Clone, Debug)]
87    struct TinyCircuit {
88        witness: Value<vesta::Scalar>,
89    }
90
91    #[derive(Clone, Debug)]
92    struct TinyConfig {
93        advice: Column<Advice>,
94        instance: Column<Instance>,
95    }
96
97    impl plonk::Circuit<vesta::Scalar> for TinyCircuit {
98        type Config = TinyConfig;
99        type FloorPlanner = SimpleFloorPlanner;
100
101        fn without_witnesses(&self) -> Self {
102            Self {
103                witness: Value::unknown(),
104            }
105        }
106
107        fn configure(meta: &mut ConstraintSystem<vesta::Scalar>) -> Self::Config {
108            let advice = meta.advice_column();
109            let instance = meta.instance_column();
110            meta.enable_equality(advice);
111            meta.enable_equality(instance);
112            TinyConfig { advice, instance }
113        }
114
115        fn synthesize(
116            &self,
117            config: Self::Config,
118            mut layouter: impl Layouter<vesta::Scalar>,
119        ) -> Result<(), PlonkError> {
120            let cell = layouter.assign_region(
121                || "witness",
122                |mut region| region.assign_advice(|| "witness", config.advice, 0, || self.witness),
123            )?;
124
125            layouter.constrain_instance(cell.cell(), config.instance, 0)
126        }
127    }
128
129    #[test]
130    fn create_proof_bytes_returns_err_for_missing_witness() {
131        let params = Params::<EqAffine>::new(4);
132        let empty_circuit = TinyCircuit {
133            witness: Value::unknown(),
134        };
135        let vk = plonk::keygen_vk(&params, &empty_circuit).expect("tiny keygen_vk should succeed");
136        let pk =
137            plonk::keygen_pk(&params, vk, &empty_circuit).expect("tiny keygen_pk should succeed");
138        let public_inputs = [vesta::Scalar::from(1)];
139
140        let err = create_proof_bytes(&params, &pk, empty_circuit, &public_inputs).unwrap_err();
141
142        assert!(matches!(err, ProveError::Halo2(plonk::Error::Synthesis)));
143    }
144}