Skip to main content

export_sui_verifier_core/
model.rs

1use crate::error::{Error, Result};
2use crate::snarkjs::{
3    validate_curve_match, validate_protocol, validate_public_counts,
4    validate_verification_key_geometry, Proof as LegacyProof, SnarkJsG1, SnarkJsG2,
5    VerificationKey as LegacyVerificationKey,
6};
7
8pub type DecimalValue = String;
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum CurveKind {
12    Bn254,
13    Bls12_381,
14}
15
16impl CurveKind {
17    pub fn from_name(value: &str) -> Result<Self> {
18        match normalize_curve_name(value).as_str() {
19            "bn128" | "bn254" | "altbn128" => Ok(Self::Bn254),
20            "bls12381" => Ok(Self::Bls12_381),
21            _ => Err(Error::UnsupportedCurve(format!(
22                "unsupported curve: {value}"
23            ))),
24        }
25    }
26
27    pub fn canonical_name(self) -> &'static str {
28        match self {
29            Self::Bn254 => "bn254",
30            Self::Bls12_381 => "bls12381",
31        }
32    }
33}
34
35#[derive(Debug, Clone, Copy, PartialEq, Eq)]
36pub enum SourceFormat {
37    SnarkjsJson,
38    Arkworks,
39    GnarkJson,
40    GnarkBin,
41    Sp1,
42}
43
44#[derive(Debug, Clone)]
45pub struct Groth16G1Point {
46    pub x: DecimalValue,
47    pub y: DecimalValue,
48    pub z: DecimalValue,
49}
50
51#[derive(Debug, Clone)]
52pub struct Groth16G2Point {
53    pub x0: DecimalValue,
54    pub x1: DecimalValue,
55    pub y0: DecimalValue,
56    pub y1: DecimalValue,
57    pub z0: DecimalValue,
58    pub z1: DecimalValue,
59}
60
61#[derive(Debug, Clone)]
62pub struct Groth16VerificationKey {
63    pub n_public: usize,
64    pub vk_alpha_1: Groth16G1Point,
65    pub vk_beta_2: Groth16G2Point,
66    pub vk_gamma_2: Groth16G2Point,
67    pub vk_delta_2: Groth16G2Point,
68    pub ic: Vec<Groth16G1Point>,
69}
70
71#[derive(Debug, Clone)]
72pub struct Groth16Proof {
73    pub pi_a: Groth16G1Point,
74    pub pi_b: Groth16G2Point,
75    pub pi_c: Groth16G1Point,
76}
77
78#[derive(Debug, Clone)]
79pub struct Groth16VerifierInputs {
80    pub curve: CurveKind,
81    pub protocol: String,
82    pub verifying_key: Groth16VerificationKey,
83    pub proof: Option<Groth16Proof>,
84    pub public_inputs: Vec<DecimalValue>,
85    pub source_format: SourceFormat,
86}
87
88impl Groth16VerifierInputs {
89    pub fn from_legacy(
90        vk: LegacyVerificationKey,
91        proof: LegacyProof,
92        public_inputs: Vec<DecimalValue>,
93        source_format: SourceFormat,
94    ) -> Result<Self> {
95        validate_protocol(vk.protocol.as_ref(), proof.protocol.as_ref())?;
96        validate_verification_key_geometry(&vk)?;
97        validate_public_counts(&vk, &public_inputs)?;
98
99        let curve_name = validate_curve_match(vk.curve.as_ref(), proof.curve.as_ref())?;
100        let curve = CurveKind::from_name(&curve_name)?;
101        let protocol = vk
102            .protocol
103            .clone()
104            .or_else(|| proof.protocol.clone())
105            .unwrap_or_else(|| "groth16".to_string());
106
107        Ok(Self {
108            curve,
109            protocol,
110            verifying_key: Groth16VerificationKey {
111                n_public: vk.n_public,
112                vk_alpha_1: vk.vk_alpha_1.into(),
113                vk_beta_2: vk.vk_beta_2.into(),
114                vk_gamma_2: vk.vk_gamma_2.into(),
115                vk_delta_2: vk.vk_delta_2.into(),
116                ic: vk.ic.into_iter().map(Into::into).collect(),
117            },
118            proof: Some(Groth16Proof {
119                pi_a: proof.pi_a.into(),
120                pi_b: proof.pi_b.into(),
121                pi_c: proof.pi_c.into(),
122            }),
123            public_inputs,
124            source_format,
125        })
126    }
127
128    pub fn from_legacy_vk_only(
129        vk: LegacyVerificationKey,
130        public_inputs: Vec<DecimalValue>,
131        source_format: SourceFormat,
132    ) -> Result<Self> {
133        validate_protocol(vk.protocol.as_ref(), None)?;
134        validate_verification_key_geometry(&vk)?;
135
136        if vk.ic.len() != vk.n_public + 1 {
137            return Err(Error::IcLengthMismatch(format!(
138                "expected IC length = nPublic + 1, got {}",
139                vk.ic.len()
140            )));
141        }
142        if !public_inputs.is_empty() && vk.n_public != public_inputs.len() {
143            return Err(Error::PublicInputCountMismatch(format!(
144                "expected nPublic={}, got {}",
145                vk.n_public,
146                public_inputs.len()
147            )));
148        }
149
150        let curve_name = validate_curve_match(vk.curve.as_ref(), None)?;
151        let curve = CurveKind::from_name(&curve_name)?;
152        let protocol = vk.protocol.clone().unwrap_or_else(|| "groth16".to_string());
153
154        Ok(Self {
155            curve,
156            protocol,
157            verifying_key: Groth16VerificationKey {
158                n_public: vk.n_public,
159                vk_alpha_1: vk.vk_alpha_1.into(),
160                vk_beta_2: vk.vk_beta_2.into(),
161                vk_gamma_2: vk.vk_gamma_2.into(),
162                vk_delta_2: vk.vk_delta_2.into(),
163                ic: vk.ic.into_iter().map(Into::into).collect(),
164            },
165            proof: None,
166            public_inputs,
167            source_format,
168        })
169    }
170
171    pub fn from_parts(
172        curve: CurveKind,
173        verifying_key: Groth16VerificationKey,
174        proof: Option<Groth16Proof>,
175        public_inputs: Vec<DecimalValue>,
176        source_format: SourceFormat,
177    ) -> Result<Self> {
178        if verifying_key.ic.len() != verifying_key.n_public + 1 {
179            return Err(Error::IcLengthMismatch(format!(
180                "expected {} IC points, got {}",
181                verifying_key.n_public + 1,
182                verifying_key.ic.len()
183            )));
184        }
185        if proof.is_some() && public_inputs.len() != verifying_key.n_public {
186            return Err(Error::PublicInputCountMismatch(format!(
187                "verification key expects {} public inputs, got {}",
188                verifying_key.n_public,
189                public_inputs.len()
190            )));
191        }
192        if proof.is_none()
193            && !public_inputs.is_empty()
194            && public_inputs.len() != verifying_key.n_public
195        {
196            return Err(Error::PublicInputCountMismatch(format!(
197                "verification key expects {} public inputs, got {}",
198                verifying_key.n_public,
199                public_inputs.len()
200            )));
201        }
202
203        Ok(Self {
204            curve,
205            protocol: "groth16".to_string(),
206            verifying_key,
207            proof,
208            public_inputs,
209            source_format,
210        })
211    }
212
213    pub fn has_test_vectors(&self) -> bool {
214        self.proof.is_some()
215    }
216}
217
218impl From<SnarkJsG1> for Groth16G1Point {
219    fn from(value: SnarkJsG1) -> Self {
220        Self {
221            x: value.x,
222            y: value.y,
223            z: value.z,
224        }
225    }
226}
227
228impl From<SnarkJsG2> for Groth16G2Point {
229    fn from(value: SnarkJsG2) -> Self {
230        Self {
231            x0: value.x0,
232            x1: value.x1,
233            y0: value.y0,
234            y1: value.y1,
235            z0: value.z0,
236            z1: value.z1,
237        }
238    }
239}
240
241fn normalize_curve_name(value: &str) -> String {
242    value.to_lowercase().replace(['-', '_'], "")
243}