From 5534f31d50633b7c7284d3cc2ce2b2ad457c07b2 Mon Sep 17 00:00:00 2001 From: narodnik Date: Thu, 29 Apr 2021 16:36:38 +0200 Subject: [PATCH] spend proof API --- src/bin/spend-classic.rs | 65 ++-------- src/crypto/mod.rs | 2 + src/crypto/spend_proof.rs | 251 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 262 insertions(+), 56 deletions(-) create mode 100644 src/crypto/spend_proof.rs diff --git a/src/bin/spend-classic.rs b/src/bin/spend-classic.rs index dad16d4d4..e1a769fdf 100644 --- a/src/bin/spend-classic.rs +++ b/src/bin/spend-classic.rs @@ -7,6 +7,7 @@ use ff::{Field, PrimeField}; use group::{Curve, GroupEncoding}; use sapvi::circuit::spend_contract::SpendContract; +use sapvi::crypto::{save_params, load_params, setup_spend_prover, create_spend_proof, verify_spend_proof}; // This thing is nasty lol pub fn merkle_hash( @@ -183,62 +184,14 @@ fn main() { (bls12_381::Scalar::random(&mut OsRng), true), ]; - let revealed = SpendRevealedValues::compute( - value, - &randomness_value, - &serial, - &randomness_coin, - &secret, - &merkle_path, - ); + { + let params = setup_spend_prover(); + save_params("spend.params", ¶ms); + } + let (params, pvk) = load_params("spend.params").expect("params should load"); - let start = Instant::now(); - let params = { - let c = SpendContract { - value: None, - randomness_value: None, - serial: None, - randomness_coin: None, - secret: None, + let (proof, revealed) = create_spend_proof(¶ms, value, randomness_value, serial, randomness_coin, + secret, merkle_path); - branch_0: None, - is_right_0: None, - branch_1: None, - is_right_1: None, - branch_2: None, - is_right_2: None, - branch_3: None, - is_right_3: None, - }; - groth16::generate_random_parameters::(c, &mut OsRng).unwrap() - }; - let pvk = groth16::prepare_verifying_key(¶ms.vk); - println!("Setup: [{:?}]", start.elapsed()); - - let c = SpendContract { - value: Some(value), - randomness_value: Some(randomness_value), - serial: Some(serial), - randomness_coin: Some(randomness_coin), - secret: Some(secret), - - branch_0: Some(merkle_path[0].0), - is_right_0: Some(merkle_path[0].1), - branch_1: Some(merkle_path[1].0), - is_right_1: Some(merkle_path[1].1), - branch_2: Some(merkle_path[2].0), - is_right_2: Some(merkle_path[2].1), - branch_3: Some(merkle_path[3].0), - is_right_3: Some(merkle_path[3].1), - }; - - let start = Instant::now(); - let proof = groth16::create_random_proof(c, ¶ms, &mut OsRng).unwrap(); - println!("Prove: [{:?}]", start.elapsed()); - - let public_input = revealed.make_outputs(); - - let start = Instant::now(); - assert!(groth16::verify_proof(&pvk, &proof, &public_input).is_ok()); - println!("Verify: [{:?}]", start.elapsed()); + assert!(verify_spend_proof(&pvk, &proof, &revealed)); } diff --git a/src/crypto/mod.rs b/src/crypto/mod.rs index 395370341..18f96eda0 100644 --- a/src/crypto/mod.rs +++ b/src/crypto/mod.rs @@ -1,10 +1,12 @@ pub mod mint_proof; +pub mod spend_proof; use bellman::groth16; use bls12_381::Bls12; use crate::error::Result; pub use mint_proof::{setup_mint_prover, create_mint_proof, verify_mint_proof}; +pub use spend_proof::{setup_spend_prover, create_spend_proof, verify_spend_proof}; pub fn save_params(filename: &str, params: &groth16::Parameters) -> Result<()> { let buffer = std::fs::File::create(filename)?; diff --git a/src/crypto/spend_proof.rs b/src/crypto/spend_proof.rs new file mode 100644 index 000000000..53623e120 --- /dev/null +++ b/src/crypto/spend_proof.rs @@ -0,0 +1,251 @@ +use rand::rngs::OsRng; +use std::time::Instant; +use bellman::gadgets::multipack; +use bellman::groth16; +use bitvec::{order::Lsb0, view::AsBits}; +use blake2s_simd::Params as Blake2sParams; +use bls12_381::Bls12; +use ff::{Field, PrimeField}; +use group::{Curve, GroupEncoding}; + +use crate::error::Result; +use crate::circuit::spend_contract::SpendContract; + +// This thing is nasty lol +pub fn merkle_hash( + depth: usize, + lhs: &bls12_381::Scalar, + rhs: &bls12_381::Scalar, +) -> bls12_381::Scalar { + let lhs = { + let mut tmp = [false; 256]; + for (a, b) in tmp.iter_mut().zip(lhs.to_repr().as_bits::()) { + *a = *b; + } + tmp + }; + + let rhs = { + let mut tmp = [false; 256]; + for (a, b) in tmp.iter_mut().zip(rhs.to_repr().as_bits::()) { + *a = *b; + } + tmp + }; + + jubjub::ExtendedPoint::from(zcash_primitives::pedersen_hash::pedersen_hash( + zcash_primitives::pedersen_hash::Personalization::MerkleTree(depth), + lhs.iter() + .copied() + .take(bls12_381::Scalar::NUM_BITS as usize) + .chain( + rhs.iter() + .copied() + .take(bls12_381::Scalar::NUM_BITS as usize), + ), + )) + .to_affine() + .get_u() +} + +pub struct SpendRevealedValues { + pub value_commit: jubjub::SubgroupPoint, + pub nullifier: [u8; 32], + // This should not be here, we just have it for debugging + //coin: [u8; 32], + pub merkle_root: bls12_381::Scalar, +} + +impl SpendRevealedValues { + fn compute( + value: u64, + randomness_value: &jubjub::Fr, + serial: &jubjub::Fr, + randomness_coin: &jubjub::Fr, + secret: &jubjub::Fr, + merkle_path: &[(bls12_381::Scalar, bool)], + ) -> Self { + let value_commit = (zcash_primitives::constants::VALUE_COMMITMENT_VALUE_GENERATOR + * jubjub::Fr::from(value)) + + (zcash_primitives::constants::VALUE_COMMITMENT_RANDOMNESS_GENERATOR + * randomness_value); + + let mut nullifier = [0; 32]; + nullifier.copy_from_slice( + Blake2sParams::new() + .hash_length(32) + .personal(zcash_primitives::constants::PRF_NF_PERSONALIZATION) + .to_state() + .update(&secret.to_bytes()) + .update(&serial.to_bytes()) + .finalize() + .as_bytes(), + ); + + let public = zcash_primitives::constants::SPENDING_KEY_GENERATOR * secret; + + let mut coin = [0; 32]; + coin.copy_from_slice( + Blake2sParams::new() + .hash_length(32) + .personal(zcash_primitives::constants::CRH_IVK_PERSONALIZATION) + .to_state() + .update(&public.to_bytes()) + .update(&value.to_le_bytes()) + .update(&serial.to_bytes()) + .update(&randomness_coin.to_bytes()) + .finalize() + .as_bytes(), + ); + + let merkle_root = + jubjub::ExtendedPoint::from(zcash_primitives::pedersen_hash::pedersen_hash( + zcash_primitives::pedersen_hash::Personalization::NoteCommitment, + multipack::bytes_to_bits_le(&coin), + )); + let affine = merkle_root.to_affine(); + let mut merkle_root = affine.get_u(); + + for (i, (right, is_right)) in merkle_path.iter().enumerate() { + if *is_right { + merkle_root = merkle_hash(i, &right, &merkle_root); + } else { + merkle_root = merkle_hash(i, &merkle_root, &right); + } + } + + SpendRevealedValues { + value_commit, + nullifier, + merkle_root, + } + } + + fn make_outputs(&self) -> [bls12_381::Scalar; 5] { + let mut public_input = [bls12_381::Scalar::zero(); 5]; + + // CV + { + let result = jubjub::ExtendedPoint::from(self.value_commit); + let affine = result.to_affine(); + //let (u, v) = (affine.get_u(), affine.get_v()); + let u = affine.get_u(); + let v = affine.get_v(); + public_input[0] = u; + public_input[1] = v; + } + + // NF + { + // Pack the hash as inputs for proof verification. + let hash = multipack::bytes_to_bits_le(&self.nullifier); + let hash = multipack::compute_multipacking(&hash); + + // There are 2 chunks for a blake hash + assert_eq!(hash.len(), 2); + + public_input[2] = hash[0]; + public_input[3] = hash[1]; + } + + // Not revealed. We leave this code here for debug + // Coin + /*{ + // Pack the hash as inputs for proof verification. + let hash = multipack::bytes_to_bits_le(&self.coin); + let hash = multipack::compute_multipacking(&hash); + + // There are 2 chunks for a blake hash + assert_eq!(hash.len(), 2); + + public_input[4] = hash[0]; + public_input[5] = hash[1]; + }*/ + + public_input[4] = self.merkle_root; + + public_input + } +} + +pub fn setup_spend_prover() -> groth16::Parameters { + println!("Making random params..."); + let start = Instant::now(); + let params = { + let c = SpendContract { + value: None, + randomness_value: None, + serial: None, + randomness_coin: None, + secret: None, + + branch_0: None, + is_right_0: None, + branch_1: None, + is_right_1: None, + branch_2: None, + is_right_2: None, + branch_3: None, + is_right_3: None, + }; + groth16::generate_random_parameters::(c, &mut OsRng).unwrap() + }; + println!("Setup: [{:?}]", start.elapsed()); + params +} + +pub fn create_spend_proof( + params: &groth16::Parameters, + value: u64, + randomness_value: jubjub::Fr, + serial: jubjub::Fr, + randomness_coin: jubjub::Fr, + secret: jubjub::Fr, + merkle_path: [(bls12_381::Scalar, bool); 4] + ) -> (groth16::Proof, SpendRevealedValues) { + let c = SpendContract { + value: Some(value), + randomness_value: Some(randomness_value), + serial: Some(serial), + randomness_coin: Some(randomness_coin), + secret: Some(secret), + + branch_0: Some(merkle_path[0].0), + is_right_0: Some(merkle_path[0].1), + branch_1: Some(merkle_path[1].0), + is_right_1: Some(merkle_path[1].1), + branch_2: Some(merkle_path[2].0), + is_right_2: Some(merkle_path[2].1), + branch_3: Some(merkle_path[3].0), + is_right_3: Some(merkle_path[3].1), + }; + + let start = Instant::now(); + let proof = groth16::create_random_proof(c, params, &mut OsRng).unwrap(); + println!("Prove: [{:?}]", start.elapsed()); + + let revealed = SpendRevealedValues::compute( + value, + &randomness_value, + &serial, + &randomness_coin, + &secret, + &merkle_path, + ); + + (proof, revealed) +} + +pub fn verify_spend_proof( + pvk: &groth16::PreparedVerifyingKey, + proof: &groth16::Proof, + revealed: &SpendRevealedValues + ) -> bool { + let public_input = revealed.make_outputs(); + + let start = Instant::now(); + let result = groth16::verify_proof(pvk, proof, &public_input).is_ok(); + println!("Verify: [{:?}]", start.elapsed()); + result +} +