-
Notifications
You must be signed in to change notification settings - Fork 25
Expand file tree
/
Copy pathproof_parser.rs
More file actions
188 lines (171 loc) · 6.56 KB
/
Copy pathproof_parser.rs
File metadata and controls
188 lines (171 loc) · 6.56 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
//! Proof parser for converting circom-prover proofs to groth16-solana format.
//!
//! This module provides utilities to convert proofs generated by circom-prover
//! into the byte format expected by the groth16-solana verifier.
//!
//! # Example
//!
//! ```rust,ignore
//! use circom_prover::{CircomProver, prover::ProofLib, witness::WitnessFn};
//! use groth16_solana::proof_parser::circom_prover::convert_proof;
//!
//! let proof = CircomProver::prove(
//! ProofLib::Arkworks,
//! WitnessFn::RustWitness(witness_fn),
//! circuit_inputs,
//! zkey_path,
//! )?;
//!
//! let (proof_a, proof_b, proof_c) = convert_proof(&proof.proof)?;
//! ```
#[cfg(feature = "circom")]
pub mod circom_prover {
extern crate alloc;
use crate::errors::Groth16Error;
use alloc::vec;
use alloc::vec::Vec;
use ark_serialize::{CanonicalSerialize, Compress};
use core::ops::Neg;
use solana_bn254::compression::prelude::convert_endianness;
/// Convert circom-prover proof to groth16-solana format
///
/// This follows the exact pattern from groth16.rs test (lines 347-368):
/// 1. Serialize with arkworks (outputs LE)
/// 2. Convert LE to BE using change_endianness/convert_endianness
///
/// # Arguments
///
/// * `circom_proof` - The proof from circom-prover
///
/// # Returns
///
/// A triple of (proof_a, proof_b, proof_c) in the format expected by groth16-solana
///
/// # Errors
///
/// Returns an error if serialization fails or byte conversion fails
/// (proof_a, proof_b, proof_c) in groth16-solana's uncompressed
/// big-endian byte format.
pub type ProofBytes = ([u8; 64], [u8; 128], [u8; 64]);
/// (proof_a, proof_b, proof_c) compressed, one coordinate per
/// point.
pub type CompressedProofBytes = ([u8; 32], [u8; 64], [u8; 32]);
pub fn convert_proof(
circom_proof: &::circom_prover::prover::circom::Proof,
) -> Result<ProofBytes, Groth16Error> {
// Convert to arkworks proof
let ark_proof: ark_groth16::Proof<ark_bn254::Bn254> = circom_proof.clone().into();
// Serialize proof_a: negate it, serialize to LE, convert to BE
let mut proof_a_serialized = [0u8; 65];
ark_proof
.a
.neg()
.x
.serialize_with_mode(&mut proof_a_serialized[..32], Compress::No)?;
ark_proof
.a
.neg()
.y
.serialize_with_mode(&mut proof_a_serialized[32..64], Compress::No)?;
// Convert LE to BE using convert_endianness::<32, 64> (reverses each 32-byte chunk)
let proof_a: [u8; 64] = convert_endianness::<32, 64>(
&proof_a_serialized[..64]
.try_into()
.map_err(|_| Groth16Error::ProofConversionError)?,
);
// Serialize proof_b: serialize to LE, convert to BE
let mut proof_b_serialized = [0u8; 129];
ark_proof
.b
.serialize_with_mode(&mut proof_b_serialized[..], Compress::No)?;
// Convert LE to BE using convert_endianness::<64, 128> (reverses each 64-byte chunk)
let proof_b: [u8; 128] = convert_endianness::<64, 128>(
&proof_b_serialized[..128]
.try_into()
.map_err(|_| Groth16Error::ProofConversionError)?,
);
// Serialize proof_c: serialize to LE, convert to BE
let mut proof_c_serialized = [0u8; 65];
ark_proof
.c
.serialize_with_mode(&mut proof_c_serialized[..], Compress::No)?;
// Convert LE to BE using convert_endianness::<32, 64> (reverses each 32-byte chunk)
let proof_c: [u8; 64] = convert_endianness::<32, 64>(
&proof_c_serialized[..64]
.try_into()
.map_err(|_| Groth16Error::ProofConversionError)?,
);
Ok((proof_a, proof_b, proof_c))
}
/// Convert uncompressed proof to compressed format
///
/// Compresses proof_a (64 bytes -> 32 bytes), proof_b (128 bytes -> 64 bytes),
/// and proof_c (64 bytes -> 32 bytes) using arkworks compressed serialization.
///
/// # Arguments
///
/// * `proof_a` - Uncompressed proof_a (64 bytes, big-endian)
/// * `proof_b` - Proof_b (128 bytes, big-endian)
/// * `proof_c` - Uncompressed proof_c (64 bytes, big-endian)
///
/// # Returns
///
/// A triple of (compressed_proof_a, compressed_proof_b, compressed_proof_c) where:
/// - compressed_proof_a: 32 bytes
/// - compressed_proof_b: 64 bytes
/// - compressed_proof_c: 32 bytes
///
/// # Errors
///
/// Returns an error if deserialization or compression fails
pub fn convert_proof_to_compressed(
proof_a: &[u8; 64],
proof_b: &[u8; 128],
proof_c: &[u8; 64],
) -> Result<CompressedProofBytes, Groth16Error> {
use solana_bn254::compression::prelude::{
alt_bn128_g1_compress_be, alt_bn128_g2_compress_be,
};
// Compress G1 points using solana_bn254
let compressed_a =
alt_bn128_g1_compress_be(proof_a).map_err(|_| Groth16Error::ProofConversionError)?;
let compressed_c =
alt_bn128_g1_compress_be(proof_c).map_err(|_| Groth16Error::ProofConversionError)?;
// Compress G2 point using solana_bn254
let compressed_b =
alt_bn128_g2_compress_be(proof_b).map_err(|_| Groth16Error::ProofConversionError)?;
Ok((compressed_a, compressed_b, compressed_c))
}
/// Convert circom-prover public inputs to groth16-solana format
///
/// Circom-prover gives us BigUint in BE format.
/// Groth16-solana expects BE byte arrays (no conversion needed).
///
/// # Arguments
///
/// * `pub_inputs` - The public inputs from circom-prover
///
/// # Returns
///
/// An array of public inputs in the format expected by groth16-solana
///
/// # Panics
///
/// Panics if the number of public inputs doesn't match N
pub fn convert_public_inputs<const N: usize>(
pub_inputs: &::circom_prover::prover::PublicInputs,
) -> [[u8; 32]; N] {
let mut public_inputs_vec: Vec<[u8; 32]> = Vec::new();
for signal_bigint in &pub_inputs.0 {
let mut bytes = signal_bigint.to_bytes_be();
// Pad to 32 bytes
if bytes.len() < 32 {
let mut padded = vec![0u8; 32 - bytes.len()];
padded.extend_from_slice(&bytes);
bytes = padded;
}
public_inputs_vec.push(bytes[..32].try_into().unwrap());
}
public_inputs_vec.try_into().unwrap()
}
}