Skip to main content

sigma_proof_compiler/sigmas/
zero.rs

1use crate::{
2    absorb::{SymInstance, SymPoint, SymScalar, SymWitness},
3    compiler::SigmaProof,
4    sigmas::G,
5};
6
7pub struct ZeroCheckProtocol;
8
9#[derive(SymWitness, Clone)]
10pub struct ZeroCheckWitness {
11    secret_key: SymScalar,
12}
13
14#[derive(SymInstance, Clone)]
15pub struct ZeroCheckInstance {
16    pubkey: SymPoint,
17    commitment: SymPoint,
18    handle: SymPoint,
19}
20
21impl SigmaProof for ZeroCheckProtocol {
22    const LABEL: &'static [u8] = b"zero-check-protocol";
23
24    type WITNESS = ZeroCheckWitness;
25    type INSTANCE = ZeroCheckInstance;
26
27    fn f(instance: &Self::INSTANCE) -> Vec<SymPoint> {
28        let Self::INSTANCE {
29            pubkey: _,
30            commitment,
31            handle,
32        } = instance.clone();
33        vec![commitment, handle]
34    }
35
36    fn psi(witness: &Self::WITNESS, instance: &Self::INSTANCE) -> Vec<SymPoint> {
37        let ZeroCheckWitness { secret_key } = witness;
38
39        vec![
40            secret_key * SymPoint::Const(*G),
41            secret_key * instance.pubkey.clone(),
42        ]
43    }
44}
45
46#[cfg(test)]
47mod tests {
48    use super::*;
49    use curve25519_dalek::constants::RISTRETTO_BASEPOINT_POINT;
50    use curve25519_dalek::Scalar;
51
52    #[test]
53    fn test_zero_check_protocol() {
54        let rng = &mut rand::rngs::OsRng;
55
56        // Generate a random secret key
57        let secret = Scalar::random(rng);
58        let witness = ZeroCheckWitness {
59            secret_key: SymScalar::Const(secret),
60        };
61
62        // Generate public key P (in practice, this would be the ElGamal public key)
63        let public_key_scalar = Scalar::random(rng);
64        let public_key = public_key_scalar * *G;
65
66        // Compute the commitment C = s*H (where H is the Pedersen generator)
67        // For simplicity, using base point as H
68        let h_generator = *G;
69        let commitment = secret * h_generator;
70
71        // Compute the decrypt handle D = s*P
72        let handle = secret * public_key;
73
74        let instance = ZeroCheckInstance {
75            pubkey: SymPoint::Const(public_key),
76            commitment: SymPoint::Const(commitment),
77            handle: SymPoint::Const(handle),
78        };
79
80        // Generate and verify proof
81        let proof = ZeroCheckProtocol::prove(&witness, &instance).unwrap();
82        println!("Zero check proof: {} bytes", proof.len());
83
84        ZeroCheckProtocol::verify(&instance, &proof).unwrap();
85    }
86
87    #[test]
88    fn test_zero_check_spec_generation() {
89        let spec = ZeroCheckProtocol::spec();
90        println!("{spec}");
91    }
92
93    #[test]
94    fn test_zero_check_invalid_proof() {
95        let rng = &mut rand::rngs::OsRng;
96
97        // Generate a valid witness
98        let secret = Scalar::random(rng);
99        let witness = ZeroCheckWitness {
100            secret_key: SymScalar::Const(secret),
101        };
102
103        // Generate public key
104        let public_key_scalar = Scalar::random(rng);
105        let public_key = public_key_scalar * RISTRETTO_BASEPOINT_POINT;
106
107        // Generate INVALID instance (commitment and handle don't match the secret)
108        let wrong_secret = Scalar::random(rng);
109        let h_generator = RISTRETTO_BASEPOINT_POINT;
110        let commitment = wrong_secret * h_generator; // Wrong commitment
111        let handle = wrong_secret * public_key; // Wrong handle
112
113        let instance = ZeroCheckInstance {
114            pubkey: SymPoint::Const(public_key),
115            commitment: SymPoint::Const(commitment),
116            handle: SymPoint::Const(handle),
117        };
118
119        // Generate proof with mismatched witness and instance
120        let proof = ZeroCheckProtocol::prove(&witness, &instance).unwrap();
121
122        // Verification should fail
123        assert!(ZeroCheckProtocol::verify(&instance, &proof).is_err());
124    }
125}