-
Notifications
You must be signed in to change notification settings - Fork 1
Implement Groth16 ZKP circuits for battle and state verification #88
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
d7c0dc9
e786897
5d33e0c
8680e66
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -422,6 +422,104 @@ fn compare_bits<F: PrimeField>(a: &[Boolean<F>], b: &[Boolean<F>]) -> Result<(Bo | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Ok((greater, equal)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Groth16 proof generation and verification for Bn254 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| use ark_bn254::{Bn254, Fr}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| use ark_groth16::{Groth16, ProvingKey, VerifyingKey}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| use ark_snark::SNARK; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| use ark_std::rand::thread_rng; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| impl BattleCircuit<Fr> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// Setup the circuit and generate proving/verifying keys | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// Returns an error if the circuit setup fails (e.g., due to constraint system issues). | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// **Note on RNG**: Uses `thread_rng()` which is cryptographically secure (ChaCha20-based). | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// For deterministic testing, consider using a seeded RNG from `ark_std::test_rng()`. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| pub fn setup() -> crate::Result<(ProvingKey<Bn254>, VerifyingKey<Bn254>)> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let rng = &mut thread_rng(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Create empty circuit for setup | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let circuit = Self { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| initial_grid: Some(vec![vec![0u8; GRID_SIZE]; GRID_SIZE]), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| final_grid: Some(vec![vec![0u8; GRID_SIZE]; GRID_SIZE]), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| commitment_a: Some(Fr::from(0u64)), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| commitment_b: Some(Fr::from(0u64)), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| winner: Some(0), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| pattern_a: Some(vec![vec![0u8; 3]; 3]), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| pattern_b: Some(vec![vec![0u8; 3]; 3]), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| nonce_a: Some(Fr::from(0u64)), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| nonce_b: Some(Fr::from(0u64)), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Groth16::<Bn254>::circuit_specific_setup(circuit, rng) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .map_err(|e| crate::Error::ProofGeneration(format!("Circuit setup failed: {}", e))) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// Generate a proof for this circuit instance | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| pub fn prove( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| &self, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| pk: &ProvingKey<Bn254>, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) -> crate::Result<crate::Groth16Proof> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let rng = &mut thread_rng(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let proof = Groth16::<Bn254>::prove(pk, self.clone(), rng) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .map_err(|e| crate::Error::ProofGeneration(e.to_string()))?; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Ok(crate::Groth16Proof::new(proof)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// Verify a proof against public inputs | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// Public inputs should be in order: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// 1. Initial grid cells (flattened) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// 2. Final grid cells (flattened) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// 3. Commitment A | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// 4. Commitment B | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// 5. Winner | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| pub fn verify( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| vk: &VerifyingKey<Bn254>, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| proof: &crate::Groth16Proof, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public_inputs: &[Fr], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) -> crate::Result<bool> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Groth16::<Bn254>::verify(vk, public_inputs, &proof.proof) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .map_err(|e| crate::Error::ProofVerification) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .map_err(|e| crate::Error::ProofVerification) | |
| .map_err(|e| crate::Error::ProofVerification(e.to_string())) |
Copilot
AI
Dec 8, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The public_inputs() method uses unwrap_or() with default values, which could silently hide missing data. If a field is None when it shouldn't be, this will use Fr::from(0u64) as the public input, potentially leading to incorrect verification. Consider either:
- Panicking when fields are None (they should be populated for proving/verification)
- Returning Result<Vec> to propagate errors
- Adding debug assertions to catch missing fields during development
Example:
pub fn public_inputs(&self) -> Vec<Fr> {
debug_assert!(self.initial_grid.is_some(), "initial_grid must be set");
let mut inputs = Vec::new();
if let Some(ref grid) = self.initial_grid {
// ...
}
// ...
}| pub fn public_inputs(&self) -> Vec<Fr> { | |
| let mut inputs = Vec::new(); | |
| // Add initial grid (flattened) | |
| if let Some(ref grid) = self.initial_grid { | |
| for row in grid { | |
| for &cell in row { | |
| inputs.push(Fr::from(cell as u64)); | |
| } | |
| } | |
| } | |
| // Add final grid (flattened) | |
| if let Some(ref grid) = self.final_grid { | |
| for row in grid { | |
| for &cell in row { | |
| inputs.push(Fr::from(cell as u64)); | |
| } | |
| } | |
| } | |
| // Add commitments and winner | |
| if let Some(commitment_a) = self.commitment_a { | |
| inputs.push(commitment_a); | |
| } | |
| if let Some(commitment_b) = self.commitment_b { | |
| inputs.push(commitment_b); | |
| } | |
| if let Some(winner) = self.winner { | |
| inputs.push(Fr::from(winner as u64)); | |
| } | |
| inputs | |
| pub fn public_inputs(&self) -> crate::Result<Vec<Fr>> { | |
| let mut inputs = Vec::new(); | |
| // Check required fields | |
| let initial_grid = self.initial_grid.as_ref().ok_or(crate::Error::MissingField("initial_grid"))?; | |
| let final_grid = self.final_grid.as_ref().ok_or(crate::Error::MissingField("final_grid"))?; | |
| let commitment_a = self.commitment_a.ok_or(crate::Error::MissingField("commitment_a"))?; | |
| let commitment_b = self.commitment_b.ok_or(crate::Error::MissingField("commitment_b"))?; | |
| let winner = self.winner.ok_or(crate::Error::MissingField("winner"))?; | |
| // Add initial grid (flattened) | |
| for row in initial_grid { | |
| for &cell in row { | |
| inputs.push(Fr::from(cell as u64)); | |
| } | |
| } | |
| // Add final grid (flattened) | |
| for row in final_grid { | |
| for &cell in row { | |
| inputs.push(Fr::from(cell as u64)); | |
| } | |
| } | |
| // Add commitments and winner | |
| inputs.push(commitment_a); | |
| inputs.push(commitment_b); | |
| inputs.push(Fr::from(winner as u64)); | |
| Ok(inputs) |
Copilot
AI
Dec 8, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The test is marked with #[ignore] and only verifies the happy path. Consider adding negative test cases (can also be marked as ignored if expensive):
- Verification with incorrect public inputs (should return false)
- Verification with wrong verifying key (should fail)
- Invalid CA evolution (final grid doesn't match simulation)
Example:
#[test]
#[ignore]
fn test_verify_with_wrong_public_inputs() {
let (pk, vk) = BattleCircuit::<Fr>::setup().unwrap();
let circuit = /* ... valid circuit ... */;
let proof = circuit.prove(&pk).unwrap();
// Change one public input
let mut wrong_inputs = circuit.public_inputs();
wrong_inputs[0] = Fr::from(999u64);
assert!(!BattleCircuit::verify(&vk, &proof, &wrong_inputs).unwrap());
}| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -5,13 +5,52 @@ | |||||
| //! - State transition verification (Merkle updates) | ||||||
| //! - Merkle tree inclusion proofs | ||||||
| //! | ||||||
| //! Note: v0.1 provides circuit structure and basic constraints. | ||||||
| //! Full CA evolution verification requires extensive constraint programming. | ||||||
| //! ## Circuit Implementations | ||||||
| //! | ||||||
| //! This crate provides two tiers of circuit implementations: | ||||||
| //! | ||||||
| //! ### Simplified Circuits (battle_circuit, state_circuit) | ||||||
| //! - **Purpose**: Fast testing, development, and basic validation | ||||||
| //! - **Constraints**: Minimal (winner validation, root non-equality) | ||||||
| //! - **Performance**: Very fast proof generation (~1-2 seconds) | ||||||
| //! - **Security**: Cryptographically sound but doesn't verify full computation | ||||||
| //! | ||||||
| //! ### Full Constraint Circuits (battle_constraints, state_constraints) | ||||||
| //! - **Purpose**: Production deployment with complete verification | ||||||
| //! - **Constraints**: Complete CA evolution simulation and Merkle tree verification | ||||||
| //! - **Performance**: Slower proof generation (30-60 seconds for battles) | ||||||
| //! - **Security**: Fully verifies all computation steps | ||||||
| //! | ||||||
| //! ## Usage | ||||||
| //! | ||||||
| //! ```rust,ignore | ||||||
| //! use bitcell_zkp::{battle_constraints::BattleCircuit, Groth16Proof}; | ||||||
|
||||||
| //! use bitcell_zkp::{battle_constraints::BattleCircuit, Groth16Proof}; | |
| //! use bitcell_zkp::{BattleCircuit, Groth16Proof}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The
setup()method returnsError::ProofGenerationfor circuit setup failures. Since this is specifically a setup error (not proof generation), consider usingError::Setupinstead for semantic correctness:This better matches the error type defined in lib.rs and makes debugging clearer.