Skip to content
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

Expose prove_multiple, verify_multiple and Pedersen homomorphic computation functions #15

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
122 changes: 120 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -41,6 +42,11 @@ pub fn lagrange_coefficient(covariate_u64: u64, others_u64: Vec<u64>) -> 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);
Expand Down Expand Up @@ -86,6 +92,26 @@ fn pedersen_aggregate(commitment_bytes_list: Vec<[u8; 32]>, covariates: Vec<u64>
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<bool> {
Ok(recovered_commitment == onchain_commitment)
Expand Down Expand Up @@ -123,7 +149,73 @@ fn zkrp_prove(secret_value: u64, bits: usize) -> PyResult<(Vec<u8>, [u8; 32], [u
}

#[pyfunction]
fn zkrp_verify(proof_bytes: Vec<u8>, committed_value_bytes: [u8; 32], bits: usize) -> PyResult<bool> {
fn zkrp_prove_single(secret_value: u64, blinding_bytes: [u8; 32], bits: usize) -> PyResult<(Vec<u8>, [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<u64>, blindings_bytes: Vec<[u8; 32]>, bits: usize) -> PyResult<(Vec<u8>, 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<Scalar> = 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<u8>, committed_value_bytes: [u8; 32], bits: usize) -> PyResult<bool> {
// Generators for Pedersen commitments. These can be selected
// independently of the Bulletproofs generators.
let pc_gens = PedersenGens::default();
Expand All @@ -139,14 +231,40 @@ fn zkrp_verify(proof_bytes: Vec<u8>, 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<u8>, committed_values_bytes: Vec<[u8; 32]>, bits: usize) -> PyResult<bool> {
// 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<CompressedRistretto> = 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(())
}