spend proof API

This commit is contained in:
narodnik
2021-04-29 16:36:38 +02:00
parent 895c165433
commit 5534f31d50
3 changed files with 262 additions and 56 deletions

View File

@@ -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", &params);
}
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(&params, 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::<Bls12, _, _>(c, &mut OsRng).unwrap()
};
let pvk = groth16::prepare_verifying_key(&params.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, &params, &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));
}

View File

@@ -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<Bls12>) -> Result<()> {
let buffer = std::fs::File::create(filename)?;

251
src/crypto/spend_proof.rs Normal file
View File

@@ -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::<Lsb0>()) {
*a = *b;
}
tmp
};
let rhs = {
let mut tmp = [false; 256];
for (a, b) in tmp.iter_mut().zip(rhs.to_repr().as_bits::<Lsb0>()) {
*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<Bls12> {
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::<Bls12, _, _>(c, &mut OsRng).unwrap()
};
println!("Setup: [{:?}]", start.elapsed());
params
}
pub fn create_spend_proof(
params: &groth16::Parameters<Bls12>,
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<Bls12>, 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<Bls12>,
proof: &groth16::Proof<Bls12>,
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
}