Move Sprout proof logic into zcash_proofs
This commit is contained in:
parent
de5943aea4
commit
d1bc61800c
|
@ -362,7 +362,6 @@ dependencies = [
|
||||||
"bellman 0.2.0",
|
"bellman 0.2.0",
|
||||||
"blake2b_simd 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"blake2b_simd 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"blake2s_simd 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"blake2s_simd 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"ff 0.5.0",
|
"ff 0.5.0",
|
||||||
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
|
|
@ -27,7 +27,6 @@ ff = { version = "0.5.0", path = "../ff" }
|
||||||
libc = "0.2"
|
libc = "0.2"
|
||||||
pairing = { version = "0.15.0", path = "../pairing" }
|
pairing = { version = "0.15.0", path = "../pairing" }
|
||||||
lazy_static = "1"
|
lazy_static = "1"
|
||||||
byteorder = "1"
|
|
||||||
rand_core = "0.5.1"
|
rand_core = "0.5.1"
|
||||||
zcash_primitives = { version = "0.1.0", path = "../zcash_primitives" }
|
zcash_primitives = { version = "0.1.0", path = "../zcash_primitives" }
|
||||||
zcash_proofs = { version = "0.1.0", path = "../zcash_proofs" }
|
zcash_proofs = { version = "0.1.0", path = "../zcash_proofs" }
|
||||||
|
|
|
@ -15,12 +15,8 @@
|
||||||
// See https://github.com/rust-lang/rfcs/pull/2585 for more background.
|
// See https://github.com/rust-lang/rfcs/pull/2585 for more background.
|
||||||
#![allow(clippy::not_unsafe_ptr_arg_deref)]
|
#![allow(clippy::not_unsafe_ptr_arg_deref)]
|
||||||
|
|
||||||
use bellman::{
|
use bellman::groth16::{Parameters, PreparedVerifyingKey, Proof};
|
||||||
gadgets::multipack,
|
|
||||||
groth16::{create_random_proof, verify_proof, Parameters, PreparedVerifyingKey, Proof},
|
|
||||||
};
|
|
||||||
use blake2s_simd::Params as Blake2sParams;
|
use blake2s_simd::Params as Blake2sParams;
|
||||||
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
|
||||||
use ff::{PrimeField, PrimeFieldRepr};
|
use ff::{PrimeField, PrimeFieldRepr};
|
||||||
use lazy_static;
|
use lazy_static;
|
||||||
use libc::{c_char, c_uchar, size_t};
|
use libc::{c_char, c_uchar, size_t};
|
||||||
|
@ -59,12 +55,10 @@ use zcash_primitives::{
|
||||||
zip32, JUBJUB,
|
zip32, JUBJUB,
|
||||||
};
|
};
|
||||||
use zcash_proofs::{
|
use zcash_proofs::{
|
||||||
circuit::{
|
circuit::sapling::TREE_DEPTH as SAPLING_TREE_DEPTH,
|
||||||
sapling::TREE_DEPTH as SAPLING_TREE_DEPTH,
|
|
||||||
sprout::{self, TREE_DEPTH as SPROUT_TREE_DEPTH},
|
|
||||||
},
|
|
||||||
load_parameters,
|
load_parameters,
|
||||||
sapling::{SaplingProvingContext, SaplingVerificationContext},
|
sapling::{SaplingProvingContext, SaplingVerificationContext},
|
||||||
|
sprout,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -759,14 +753,14 @@ pub extern "C" fn librustzcash_sprout_prove(
|
||||||
in_value1: u64,
|
in_value1: u64,
|
||||||
in_rho1: *const [c_uchar; 32],
|
in_rho1: *const [c_uchar; 32],
|
||||||
in_r1: *const [c_uchar; 32],
|
in_r1: *const [c_uchar; 32],
|
||||||
in_auth1: *const [c_uchar; 1 + 33 * SPROUT_TREE_DEPTH + 8],
|
in_auth1: *const [c_uchar; sprout::WITNESS_PATH_SIZE],
|
||||||
|
|
||||||
// Second input
|
// Second input
|
||||||
in_sk2: *const [c_uchar; 32],
|
in_sk2: *const [c_uchar; 32],
|
||||||
in_value2: u64,
|
in_value2: u64,
|
||||||
in_rho2: *const [c_uchar; 32],
|
in_rho2: *const [c_uchar; 32],
|
||||||
in_r2: *const [c_uchar; 32],
|
in_r2: *const [c_uchar; 32],
|
||||||
in_auth2: *const [c_uchar; 1 + 33 * SPROUT_TREE_DEPTH + 8],
|
in_auth2: *const [c_uchar; sprout::WITNESS_PATH_SIZE],
|
||||||
|
|
||||||
// First output
|
// First output
|
||||||
out_pk1: *const [c_uchar; 32],
|
out_pk1: *const [c_uchar; 32],
|
||||||
|
@ -782,94 +776,6 @@ pub extern "C" fn librustzcash_sprout_prove(
|
||||||
vpub_old: u64,
|
vpub_old: u64,
|
||||||
vpub_new: u64,
|
vpub_new: u64,
|
||||||
) {
|
) {
|
||||||
let phi = unsafe { *phi };
|
|
||||||
let rt = unsafe { *rt };
|
|
||||||
let h_sig = unsafe { *h_sig };
|
|
||||||
let in_sk1 = unsafe { *in_sk1 };
|
|
||||||
let in_rho1 = unsafe { *in_rho1 };
|
|
||||||
let in_r1 = unsafe { *in_r1 };
|
|
||||||
let in_auth1 = unsafe { *in_auth1 };
|
|
||||||
let in_sk2 = unsafe { *in_sk2 };
|
|
||||||
let in_rho2 = unsafe { *in_rho2 };
|
|
||||||
let in_r2 = unsafe { *in_r2 };
|
|
||||||
let in_auth2 = unsafe { *in_auth2 };
|
|
||||||
let out_pk1 = unsafe { *out_pk1 };
|
|
||||||
let out_r1 = unsafe { *out_r1 };
|
|
||||||
let out_pk2 = unsafe { *out_pk2 };
|
|
||||||
let out_r2 = unsafe { *out_r2 };
|
|
||||||
|
|
||||||
let mut inputs = Vec::with_capacity(2);
|
|
||||||
{
|
|
||||||
let mut handle_input = |sk, value, rho, r, mut auth: &[u8]| {
|
|
||||||
let value = Some(value);
|
|
||||||
let rho = Some(sprout::UniqueRandomness(rho));
|
|
||||||
let r = Some(sprout::CommitmentRandomness(r));
|
|
||||||
let a_sk = Some(sprout::SpendingKey(sk));
|
|
||||||
|
|
||||||
// skip the first byte
|
|
||||||
assert_eq!(auth[0], SPROUT_TREE_DEPTH as u8);
|
|
||||||
auth = &auth[1..];
|
|
||||||
|
|
||||||
let mut auth_path = [None; SPROUT_TREE_DEPTH];
|
|
||||||
for i in (0..SPROUT_TREE_DEPTH).rev() {
|
|
||||||
// skip length of inner vector
|
|
||||||
assert_eq!(auth[0], 32);
|
|
||||||
auth = &auth[1..];
|
|
||||||
|
|
||||||
let mut sibling = [0u8; 32];
|
|
||||||
sibling.copy_from_slice(&auth[0..32]);
|
|
||||||
auth = &auth[32..];
|
|
||||||
|
|
||||||
auth_path[i] = Some((sibling, false));
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut position = auth
|
|
||||||
.read_u64::<LittleEndian>()
|
|
||||||
.expect("should have had index at the end");
|
|
||||||
|
|
||||||
for i in 0..SPROUT_TREE_DEPTH {
|
|
||||||
auth_path[i].as_mut().map(|p| p.1 = (position & 1) == 1);
|
|
||||||
|
|
||||||
position >>= 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
inputs.push(sprout::JSInput {
|
|
||||||
value: value,
|
|
||||||
a_sk: a_sk,
|
|
||||||
rho: rho,
|
|
||||||
r: r,
|
|
||||||
auth_path: auth_path,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
handle_input(in_sk1, in_value1, in_rho1, in_r1, &in_auth1[..]);
|
|
||||||
handle_input(in_sk2, in_value2, in_rho2, in_r2, &in_auth2[..]);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut outputs = Vec::with_capacity(2);
|
|
||||||
{
|
|
||||||
let mut handle_output = |a_pk, value, r| {
|
|
||||||
outputs.push(sprout::JSOutput {
|
|
||||||
value: Some(value),
|
|
||||||
a_pk: Some(sprout::PayingKey(a_pk)),
|
|
||||||
r: Some(sprout::CommitmentRandomness(r)),
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
handle_output(out_pk1, out_value1, out_r1);
|
|
||||||
handle_output(out_pk2, out_value2, out_r2);
|
|
||||||
}
|
|
||||||
|
|
||||||
let js = sprout::JoinSplit {
|
|
||||||
vpub_old: Some(vpub_old),
|
|
||||||
vpub_new: Some(vpub_new),
|
|
||||||
h_sig: Some(h_sig),
|
|
||||||
phi: Some(phi),
|
|
||||||
inputs: inputs,
|
|
||||||
outputs: outputs,
|
|
||||||
rt: Some(rt),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Load parameters from disk
|
// Load parameters from disk
|
||||||
let sprout_fs = File::open(
|
let sprout_fs = File::open(
|
||||||
unsafe { &SPROUT_GROTH16_PARAMS_PATH }
|
unsafe { &SPROUT_GROTH16_PARAMS_PATH }
|
||||||
|
@ -885,10 +791,30 @@ pub extern "C" fn librustzcash_sprout_prove(
|
||||||
|
|
||||||
drop(sprout_fs);
|
drop(sprout_fs);
|
||||||
|
|
||||||
// Initialize secure RNG
|
let proof = sprout::create_proof(
|
||||||
let mut rng = OsRng;
|
unsafe { *phi },
|
||||||
|
unsafe { *rt },
|
||||||
let proof = create_random_proof(js, ¶ms, &mut rng).expect("proving should not fail");
|
unsafe { *h_sig },
|
||||||
|
unsafe { *in_sk1 },
|
||||||
|
in_value1,
|
||||||
|
unsafe { *in_rho1 },
|
||||||
|
unsafe { *in_r1 },
|
||||||
|
unsafe { &*in_auth1 },
|
||||||
|
unsafe { *in_sk2 },
|
||||||
|
in_value2,
|
||||||
|
unsafe { *in_rho2 },
|
||||||
|
unsafe { *in_r2 },
|
||||||
|
unsafe { &*in_auth2 },
|
||||||
|
unsafe { *out_pk1 },
|
||||||
|
out_value1,
|
||||||
|
unsafe { *out_r1 },
|
||||||
|
unsafe { *out_pk2 },
|
||||||
|
out_value2,
|
||||||
|
unsafe { *out_r2 },
|
||||||
|
vpub_old,
|
||||||
|
vpub_new,
|
||||||
|
¶ms,
|
||||||
|
);
|
||||||
|
|
||||||
proof
|
proof
|
||||||
.write(&mut (unsafe { &mut *proof_out })[..])
|
.write(&mut (unsafe { &mut *proof_out })[..])
|
||||||
|
@ -910,39 +836,20 @@ pub extern "C" fn librustzcash_sprout_verify(
|
||||||
vpub_old: u64,
|
vpub_old: u64,
|
||||||
vpub_new: u64,
|
vpub_new: u64,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
// Prepare the public input for the verifier
|
sprout::verify_proof(
|
||||||
let mut public_input = Vec::with_capacity((32 * 8) + (8 * 2));
|
unsafe { &*proof },
|
||||||
public_input.extend(unsafe { &(&*rt)[..] });
|
unsafe { &*rt },
|
||||||
public_input.extend(unsafe { &(&*h_sig)[..] });
|
unsafe { &*h_sig },
|
||||||
public_input.extend(unsafe { &(&*nf1)[..] });
|
unsafe { &*mac1 },
|
||||||
public_input.extend(unsafe { &(&*mac1)[..] });
|
unsafe { &*mac2 },
|
||||||
public_input.extend(unsafe { &(&*nf2)[..] });
|
unsafe { &*nf1 },
|
||||||
public_input.extend(unsafe { &(&*mac2)[..] });
|
unsafe { &*nf2 },
|
||||||
public_input.extend(unsafe { &(&*cm1)[..] });
|
unsafe { &*cm1 },
|
||||||
public_input.extend(unsafe { &(&*cm2)[..] });
|
unsafe { &*cm2 },
|
||||||
public_input.write_u64::<LittleEndian>(vpub_old).unwrap();
|
vpub_old,
|
||||||
public_input.write_u64::<LittleEndian>(vpub_new).unwrap();
|
vpub_new,
|
||||||
|
|
||||||
let public_input = multipack::bytes_to_bits(&public_input);
|
|
||||||
let public_input = multipack::compute_multipacking::<Bls12>(&public_input);
|
|
||||||
|
|
||||||
let proof = match Proof::read(unsafe { &(&*proof)[..] }) {
|
|
||||||
Ok(p) => p,
|
|
||||||
Err(_) => return false,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Verify the proof
|
|
||||||
match verify_proof(
|
|
||||||
unsafe { SPROUT_GROTH16_VK.as_ref() }.expect("parameters should have been initialized"),
|
unsafe { SPROUT_GROTH16_VK.as_ref() }.expect("parameters should have been initialized"),
|
||||||
&proof,
|
)
|
||||||
&public_input[..],
|
|
||||||
) {
|
|
||||||
// No error, and proof verification successful
|
|
||||||
Ok(true) => true,
|
|
||||||
|
|
||||||
// Any other case
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This function (using the proving context) constructs an Output proof given
|
/// This function (using the proving context) constructs an Output proof given
|
||||||
|
|
|
@ -15,6 +15,7 @@ use std::path::Path;
|
||||||
pub mod circuit;
|
pub mod circuit;
|
||||||
mod hashreader;
|
mod hashreader;
|
||||||
pub mod sapling;
|
pub mod sapling;
|
||||||
|
pub mod sprout;
|
||||||
|
|
||||||
#[cfg(feature = "local-prover")]
|
#[cfg(feature = "local-prover")]
|
||||||
pub mod prover;
|
pub mod prover;
|
||||||
|
|
|
@ -0,0 +1,179 @@
|
||||||
|
//! APIs for creating and verifying Sprout proofs.
|
||||||
|
|
||||||
|
use bellman::{
|
||||||
|
gadgets::multipack,
|
||||||
|
groth16::{self, create_random_proof, Parameters, PreparedVerifyingKey, Proof},
|
||||||
|
};
|
||||||
|
use pairing::bls12_381::Bls12;
|
||||||
|
use rand_core::OsRng;
|
||||||
|
|
||||||
|
use crate::circuit::sprout::*;
|
||||||
|
|
||||||
|
const GROTH_PROOF_SIZE: usize = 48 // π_A
|
||||||
|
+ 96 // π_B
|
||||||
|
+ 48; // π_C
|
||||||
|
pub const WITNESS_PATH_SIZE: usize = 1 + 33 * TREE_DEPTH + 8;
|
||||||
|
|
||||||
|
/// Sprout JoinSplit proof generation.
|
||||||
|
pub fn create_proof(
|
||||||
|
phi: [u8; 32],
|
||||||
|
rt: [u8; 32],
|
||||||
|
h_sig: [u8; 32],
|
||||||
|
|
||||||
|
// First input
|
||||||
|
in_sk1: [u8; 32],
|
||||||
|
in_value1: u64,
|
||||||
|
in_rho1: [u8; 32],
|
||||||
|
in_r1: [u8; 32],
|
||||||
|
in_auth1: &[u8; WITNESS_PATH_SIZE],
|
||||||
|
|
||||||
|
// Second input
|
||||||
|
in_sk2: [u8; 32],
|
||||||
|
in_value2: u64,
|
||||||
|
in_rho2: [u8; 32],
|
||||||
|
in_r2: [u8; 32],
|
||||||
|
in_auth2: &[u8; WITNESS_PATH_SIZE],
|
||||||
|
|
||||||
|
// First output
|
||||||
|
out_pk1: [u8; 32],
|
||||||
|
out_value1: u64,
|
||||||
|
out_r1: [u8; 32],
|
||||||
|
|
||||||
|
// Second output
|
||||||
|
out_pk2: [u8; 32],
|
||||||
|
out_value2: u64,
|
||||||
|
out_r2: [u8; 32],
|
||||||
|
|
||||||
|
// Public value
|
||||||
|
vpub_old: u64,
|
||||||
|
vpub_new: u64,
|
||||||
|
|
||||||
|
proving_key: &Parameters<Bls12>,
|
||||||
|
) -> Proof<Bls12> {
|
||||||
|
let mut inputs = Vec::with_capacity(2);
|
||||||
|
{
|
||||||
|
let mut handle_input = |sk, value, rho, r, mut auth: &[u8]| {
|
||||||
|
let value = Some(value);
|
||||||
|
let rho = Some(UniqueRandomness(rho));
|
||||||
|
let r = Some(CommitmentRandomness(r));
|
||||||
|
let a_sk = Some(SpendingKey(sk));
|
||||||
|
|
||||||
|
// skip the first byte
|
||||||
|
assert_eq!(auth[0], TREE_DEPTH as u8);
|
||||||
|
auth = &auth[1..];
|
||||||
|
|
||||||
|
let mut auth_path = [None; TREE_DEPTH];
|
||||||
|
for i in (0..TREE_DEPTH).rev() {
|
||||||
|
// skip length of inner vector
|
||||||
|
assert_eq!(auth[0], 32);
|
||||||
|
auth = &auth[1..];
|
||||||
|
|
||||||
|
let mut sibling = [0u8; 32];
|
||||||
|
sibling.copy_from_slice(&auth[0..32]);
|
||||||
|
auth = &auth[32..];
|
||||||
|
|
||||||
|
auth_path[i] = Some((sibling, false));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut position = {
|
||||||
|
let mut bytes = [0; 8];
|
||||||
|
bytes.copy_from_slice(&auth[0..8]);
|
||||||
|
u64::from_le_bytes(bytes)
|
||||||
|
};
|
||||||
|
|
||||||
|
for entry in auth_path.iter_mut() {
|
||||||
|
if let Some(p) = entry {
|
||||||
|
p.1 = (position & 1) == 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
position >>= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
inputs.push(JSInput {
|
||||||
|
value,
|
||||||
|
a_sk,
|
||||||
|
rho,
|
||||||
|
r,
|
||||||
|
auth_path,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
handle_input(in_sk1, in_value1, in_rho1, in_r1, &in_auth1[..]);
|
||||||
|
handle_input(in_sk2, in_value2, in_rho2, in_r2, &in_auth2[..]);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut outputs = Vec::with_capacity(2);
|
||||||
|
{
|
||||||
|
let mut handle_output = |a_pk, value, r| {
|
||||||
|
outputs.push(JSOutput {
|
||||||
|
value: Some(value),
|
||||||
|
a_pk: Some(PayingKey(a_pk)),
|
||||||
|
r: Some(CommitmentRandomness(r)),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
handle_output(out_pk1, out_value1, out_r1);
|
||||||
|
handle_output(out_pk2, out_value2, out_r2);
|
||||||
|
}
|
||||||
|
|
||||||
|
let js = JoinSplit {
|
||||||
|
vpub_old: Some(vpub_old),
|
||||||
|
vpub_new: Some(vpub_new),
|
||||||
|
h_sig: Some(h_sig),
|
||||||
|
phi: Some(phi),
|
||||||
|
inputs,
|
||||||
|
outputs,
|
||||||
|
rt: Some(rt),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Initialize secure RNG
|
||||||
|
let mut rng = OsRng;
|
||||||
|
|
||||||
|
create_random_proof(js, proving_key, &mut rng).expect("proving should not fail")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sprout JoinSplit proof verification.
|
||||||
|
pub fn verify_proof(
|
||||||
|
proof: &[u8; GROTH_PROOF_SIZE],
|
||||||
|
rt: &[u8; 32],
|
||||||
|
h_sig: &[u8; 32],
|
||||||
|
mac1: &[u8; 32],
|
||||||
|
mac2: &[u8; 32],
|
||||||
|
nf1: &[u8; 32],
|
||||||
|
nf2: &[u8; 32],
|
||||||
|
cm1: &[u8; 32],
|
||||||
|
cm2: &[u8; 32],
|
||||||
|
vpub_old: u64,
|
||||||
|
vpub_new: u64,
|
||||||
|
verifying_key: &PreparedVerifyingKey<Bls12>,
|
||||||
|
) -> bool {
|
||||||
|
// Prepare the public input for the verifier
|
||||||
|
let mut public_input = Vec::with_capacity((32 * 8) + (8 * 2));
|
||||||
|
public_input.extend(rt);
|
||||||
|
public_input.extend(h_sig);
|
||||||
|
public_input.extend(nf1);
|
||||||
|
public_input.extend(mac1);
|
||||||
|
public_input.extend(nf2);
|
||||||
|
public_input.extend(mac2);
|
||||||
|
public_input.extend(cm1);
|
||||||
|
public_input.extend(cm2);
|
||||||
|
public_input.extend(&vpub_old.to_le_bytes());
|
||||||
|
public_input.extend(&vpub_new.to_le_bytes());
|
||||||
|
|
||||||
|
let public_input = multipack::bytes_to_bits(&public_input);
|
||||||
|
let public_input = multipack::compute_multipacking::<Bls12>(&public_input);
|
||||||
|
|
||||||
|
let proof = match Proof::read(&proof[..]) {
|
||||||
|
Ok(p) => p,
|
||||||
|
Err(_) => return false,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Verify the proof
|
||||||
|
match groth16::verify_proof(verifying_key, &proof, &public_input[..]) {
|
||||||
|
// No error, and proof verification successful
|
||||||
|
Ok(true) => true,
|
||||||
|
|
||||||
|
// Any other case
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue