From 215dbe8a63ed61c2b2a2e05ad5a599a8a3c6afea Mon Sep 17 00:00:00 2001 From: Sharif Jacobino Date: Mon, 27 Jun 2022 11:00:37 +0200 Subject: [PATCH 1/2] Expose prove_multiple, verify_multiple and pedersen homomorphic computation functions --- src/lib.rs | 122 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 120 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 5ca7365..0dda1c6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,6 +2,7 @@ extern crate rand; extern crate curve25519_dalek_ng; use curve25519_dalek_ng::scalar::Scalar; use curve25519_dalek_ng::ristretto::CompressedRistretto; +use curve25519_dalek_ng::constants::BASEPOINT_ORDER; extern crate merlin; use merlin::Transcript; extern crate bulletproofs; @@ -41,6 +42,11 @@ pub fn lagrange_coefficient(covariate_u64: u64, others_u64: Vec) -> PyResul Ok(coefficient) } +#[pyfunction] +fn get_ed25519_basepoint_order() -> PyResult<[u8; 32]> { + Ok(BASEPOINT_ORDER.to_bytes()) +} + #[pyfunction] fn pedersen_commit(secret_value_bytes: [u8; 32], blinding_bytes: [u8; 32]) -> PyResult<[u8; 32]> { let secret_value = Scalar::from_bytes_mod_order(secret_value_bytes); @@ -86,6 +92,26 @@ fn pedersen_aggregate(commitment_bytes_list: Vec<[u8; 32]>, covariates: Vec Ok(commitment.compress().to_bytes()) } +#[pyfunction] +fn pedersen_sum(commitment_bytes_list: Vec<[u8; 32]>) -> PyResult<[u8;32]> { + let commitment_list: Vec<_> = commitment_bytes_list + .iter() + .map(|v| CompressedRistretto(read32(v)).decompress().unwrap()) + .collect(); + + let commitment = commitment_list.iter().copied().reduce(|a, b| a + b).unwrap(); + Ok(commitment.compress().to_bytes()) +} + +#[pyfunction] +fn pedersen_subtract(commitment_bytes_a: [u8; 32], commitment_bytes_b: [u8; 32]) -> PyResult<[u8;32]> { + let comm_a = CompressedRistretto(read32(&commitment_bytes_a)).decompress().unwrap(); + let comm_b = CompressedRistretto(read32(&commitment_bytes_b)).decompress().unwrap(); + + let commitment = comm_a - comm_b; + Ok(commitment.compress().to_bytes()) +} + #[pyfunction] fn pedersen_compare(recovered_commitment: [u8; 32], onchain_commitment: [u8; 32]) -> PyResult { Ok(recovered_commitment == onchain_commitment) @@ -123,7 +149,73 @@ fn zkrp_prove(secret_value: u64, bits: usize) -> PyResult<(Vec, [u8; 32], [u } #[pyfunction] -fn zkrp_verify(proof_bytes: Vec, committed_value_bytes: [u8; 32], bits: usize) -> PyResult { +fn zkrp_prove_single(secret_value: u64, blinding_bytes: [u8; 32], bits: usize) -> PyResult<(Vec, [u8; 32], [u8; 32])> { + // Generators for Pedersen commitments. These can be selected + // independently of the Bulletproofs generators. + let pc_gens = PedersenGens::default(); + + // Generators for Bulletproofs, valid for proofs up to bitsize 64 + // and aggregation size up to 1. + let bp_gens = BulletproofGens::new(64, 1); + + // The API takes a blinding factor for the commitment. + let blinding = Scalar::from_bytes_mod_order(blinding_bytes); + + // The proof can be chained to an existing transcript. + // Here we create a transcript with a doctest domain separator. + let mut prover_transcript = Transcript::new(b"zkrp"); + + // Create a 32-bit rangeproof. + let (proof, committed_value) = RangeProof::prove_single( + &bp_gens, + &pc_gens, + &mut prover_transcript, + secret_value, + &blinding, + bits, + ).expect("A real program could handle errors"); + + Ok((proof.to_bytes(), committed_value.to_bytes(), blinding_bytes)) +} + +#[pyfunction] +fn zkrp_prove_multiple(secret_values: Vec, blindings_bytes: Vec<[u8; 32]>, bits: usize) -> PyResult<(Vec, Vec<[u8; 32]>, Vec<[u8; 32]>)> { + // Generators for Pedersen commitments. These can be selected + // independently of the Bulletproofs generators. + let pc_gens = PedersenGens::default(); + + // Generators for Bulletproofs, valid for proofs up to bitsize 64 + // and aggregation size up to |commited_values|. + let bp_gens = BulletproofGens::new(64, secret_values.len()); + + // The API takes a blinding factor for the commitment. + let blindings: Vec = blindings_bytes.iter() + .map(|v| Scalar::from_bytes_mod_order(*v)) + .collect(); + + // The proof can be chained to an existing transcript. + // Here we create a transcript with a doctest domain separator. + let mut prover_transcript = Transcript::new(b"zkrp"); + + // Create a 32-bit rangeproof. + let (proof, committed_values) = RangeProof::prove_multiple( + &bp_gens, + &pc_gens, + &mut prover_transcript, + secret_values.as_slice(), + &blindings, + bits, + ).expect("A real program could handle errors"); + + let committed_values_bytes: Vec<_> = committed_values.iter() + .map(|v| v.to_bytes()) + .collect(); + + Ok((proof.to_bytes(), committed_values_bytes, blindings_bytes)) +} + +#[pyfunction] +fn zkrp_verify_single(proof_bytes: Vec, committed_value_bytes: [u8; 32], bits: usize) -> PyResult { // Generators for Pedersen commitments. These can be selected // independently of the Bulletproofs generators. let pc_gens = PedersenGens::default(); @@ -139,14 +231,40 @@ fn zkrp_verify(proof_bytes: Vec, committed_value_bytes: [u8; 32], bits: usiz Ok(proof.verify_single(&bp_gens, &pc_gens, &mut verifier_transcript, &committed_value, bits).is_ok()) } +#[pyfunction] +fn zkrp_verify_multiple(proof_bytes: Vec, committed_values_bytes: Vec<[u8; 32]>, bits: usize) -> PyResult { + // Generators for Pedersen commitments. These can be selected + // independently of the Bulletproofs generators. + let pc_gens = PedersenGens::default(); + + // Generators for Bulletproofs, valid for proofs up to bitsize 64 + // and aggregation size up to |commited_values|. + let bp_gens = BulletproofGens::new(64, committed_values_bytes.len()); + + let proof = RangeProof::from_bytes(proof_bytes.as_slice()).expect("Error: Proof deserialization failed!"); + let committed_values : Vec = committed_values_bytes.iter() + .map(|v| CompressedRistretto(read32(v))) + .collect(); + + // Verification requires a transcript with identical initial state: + let mut verifier_transcript = Transcript::new(b"zkrp"); + Ok(proof.verify_multiple(&bp_gens, &pc_gens, &mut verifier_transcript, &committed_values, bits).is_ok()) +} + /// A Python module implemented in Rust. #[pymodule] fn pybulletproofs(_py: Python, m: &PyModule) -> PyResult<()> { + m.add_function(wrap_pyfunction!(get_ed25519_basepoint_order, m)?)?; m.add_function(wrap_pyfunction!(pedersen_commit, m)?)?; m.add_function(wrap_pyfunction!(pedersen_open, m)?)?; m.add_function(wrap_pyfunction!(pedersen_aggregate, m)?)?; + m.add_function(wrap_pyfunction!(pedersen_sum, m)?)?; + m.add_function(wrap_pyfunction!(pedersen_subtract, m)?)?; m.add_function(wrap_pyfunction!(pedersen_compare, m)?)?; m.add_function(wrap_pyfunction!(zkrp_prove, m)?)?; - m.add_function(wrap_pyfunction!(zkrp_verify, m)?)?; + m.add_function(wrap_pyfunction!(zkrp_prove_single, m)?)?; + m.add_function(wrap_pyfunction!(zkrp_prove_multiple, m)?)?; + m.add_function(wrap_pyfunction!(zkrp_verify_single, m)?)?; + m.add_function(wrap_pyfunction!(zkrp_verify_multiple, m)?)?; Ok(()) } From 602edb908a63b1c3e77d55bd86575b474b680c26 Mon Sep 17 00:00:00 2001 From: Sharif Jacobino Date: Mon, 27 Jun 2022 12:28:08 +0200 Subject: [PATCH 2/2] update README --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 97bbf77..81d7420 100644 --- a/README.md +++ b/README.md @@ -21,13 +21,13 @@ pip install pybulletproofs To test the python library, run the following proof and verification. ```bash -from pybulletproofs import zkrp_prove, zkrp_verify +from pybulletproofs import zkrp_prove, zkrp_verify_single proof1, comm1, _ = zkrp_prove(2022, 32) proof2, comm2, _ = zkrp_prove(2023, 32) -assert zkrp_verify(proof1, comm1) -assert !zkrp_verify(proof2, comm1) +assert zkrp_verify_single(proof1, comm1) +assert !zkrp_verify_single(proof2, comm1) ``` ## Development