mirror of
https://github.com/Qortal/pirate-librustzcash.git
synced 2025-07-29 11:31:22 +00:00
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -362,7 +362,6 @@ dependencies = [
|
||||
"bellman 0.2.0",
|
||||
"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)",
|
||||
"byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ff 0.5.0",
|
||||
"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)",
|
||||
|
@@ -27,7 +27,6 @@ ff = { version = "0.5.0", path = "../ff" }
|
||||
libc = "0.2"
|
||||
pairing = { version = "0.15.0", path = "../pairing" }
|
||||
lazy_static = "1"
|
||||
byteorder = "1"
|
||||
rand_core = "0.5.1"
|
||||
zcash_primitives = { version = "0.1.0", path = "../zcash_primitives" }
|
||||
zcash_proofs = { version = "0.1.0", path = "../zcash_proofs" }
|
||||
|
@@ -1,38 +1,34 @@
|
||||
//! FFI between the C++ zcashd codebase and the Rust Zcash crates.
|
||||
//!
|
||||
//! This is internal to zcashd and is not an officially-supported API.
|
||||
|
||||
// Catch documentation errors caused by code changes.
|
||||
#![deny(intra_doc_link_resolution_failure)]
|
||||
// Clippy has a default-deny lint to prevent dereferencing raw pointer arguments
|
||||
// in a non-unsafe function. However, declaring a function as unsafe has the
|
||||
// side-effect that the entire function body is treated as an unsafe {} block,
|
||||
// and rustc will not enforce full safety checks on the parts of the function
|
||||
// that would otherwise be safe.
|
||||
//
|
||||
// The functions in this crate are all for FFI usage, so it's obvious to the
|
||||
// caller (which is only ever zcashd) that the arguments must satisfy the
|
||||
// necessary assumptions. We therefore ignore this lint to retain the benefit of
|
||||
// explicitly annotating the parts of each function that must themselves satisfy
|
||||
// assumptions of underlying code.
|
||||
//
|
||||
// See https://github.com/rust-lang/rfcs/pull/2585 for more background.
|
||||
#![allow(clippy::not_unsafe_ptr_arg_deref)]
|
||||
|
||||
use lazy_static;
|
||||
|
||||
use ff::{PrimeField, PrimeFieldRepr};
|
||||
use pairing::bls12_381::{Bls12, Fr, FrRepr};
|
||||
|
||||
use zcash_primitives::{
|
||||
constants::CRH_IVK_PERSONALIZATION,
|
||||
jubjub::{
|
||||
edwards,
|
||||
fs::{Fs, FsRepr},
|
||||
FixedGenerators, JubjubEngine, JubjubParams, PrimeOrder, ToUniform, Unknown,
|
||||
},
|
||||
};
|
||||
|
||||
use zcash_proofs::circuit::sapling::TREE_DEPTH as SAPLING_TREE_DEPTH;
|
||||
use zcash_proofs::circuit::sprout::{self, TREE_DEPTH as SPROUT_TREE_DEPTH};
|
||||
|
||||
use bellman::gadgets::multipack;
|
||||
use bellman::groth16::{
|
||||
create_random_proof, verify_proof, Parameters, PreparedVerifyingKey, Proof,
|
||||
};
|
||||
|
||||
use bellman::groth16::{Parameters, PreparedVerifyingKey, Proof};
|
||||
use blake2s_simd::Params as Blake2sParams;
|
||||
|
||||
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
||||
|
||||
use rand_core::{OsRng, RngCore};
|
||||
use std::io::BufReader;
|
||||
|
||||
use ff::{PrimeField, PrimeFieldRepr};
|
||||
use lazy_static;
|
||||
use libc::{c_char, c_uchar, size_t};
|
||||
use pairing::bls12_381::{Bls12, Fr, FrRepr};
|
||||
use rand_core::{OsRng, RngCore};
|
||||
use std::ffi::CStr;
|
||||
use std::fs::File;
|
||||
use std::io::BufReader;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::slice;
|
||||
|
||||
@@ -48,6 +44,12 @@ use std::os::windows::ffi::OsStringExt;
|
||||
|
||||
use zcash_primitives::{
|
||||
block::equihash,
|
||||
constants::CRH_IVK_PERSONALIZATION,
|
||||
jubjub::{
|
||||
edwards,
|
||||
fs::{Fs, FsRepr},
|
||||
FixedGenerators, JubjubEngine, JubjubParams, PrimeOrder, ToUniform, Unknown,
|
||||
},
|
||||
merkle_tree::CommitmentTreeWitness,
|
||||
note_encryption::sapling_ka_agree,
|
||||
primitives::{Diversifier, Note, PaymentAddress, ProofGenerationKey, ViewingKey},
|
||||
@@ -57,8 +59,10 @@ use zcash_primitives::{
|
||||
zip32, JUBJUB,
|
||||
};
|
||||
use zcash_proofs::{
|
||||
circuit::sapling::TREE_DEPTH as SAPLING_TREE_DEPTH,
|
||||
load_parameters,
|
||||
sapling::{SaplingProvingContext, SaplingVerificationContext},
|
||||
sprout,
|
||||
};
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -72,50 +76,33 @@ static mut SAPLING_SPEND_PARAMS: Option<Parameters<Bls12>> = None;
|
||||
static mut SAPLING_OUTPUT_PARAMS: Option<Parameters<Bls12>> = None;
|
||||
static mut SPROUT_GROTH16_PARAMS_PATH: Option<PathBuf> = None;
|
||||
|
||||
/// Writes an FrRepr to [u8] of length 32
|
||||
fn write_le(f: FrRepr, to: &mut [u8]) {
|
||||
assert_eq!(to.len(), 32);
|
||||
|
||||
f.write_le(to).expect("length is 32 bytes");
|
||||
}
|
||||
|
||||
/// Reads an FrRepr from a [u8] of length 32.
|
||||
/// This will panic (abort) if length provided is
|
||||
/// not correct.
|
||||
fn read_le(from: &[u8]) -> FrRepr {
|
||||
assert_eq!(from.len(), 32);
|
||||
|
||||
/// Reads an FrRepr from a [u8; 32].
|
||||
fn read_fr(from: &[u8; 32]) -> FrRepr {
|
||||
let mut f = FrRepr::default();
|
||||
f.read_le(from).expect("length is 32 bytes");
|
||||
|
||||
f.read_le(&from[..]).expect("length is 32 bytes");
|
||||
f
|
||||
}
|
||||
|
||||
/// Reads an FsRepr from [u8] of length 32
|
||||
/// This will panic (abort) if length provided is
|
||||
/// not correct
|
||||
fn read_fs(from: &[u8]) -> FsRepr {
|
||||
assert_eq!(from.len(), 32);
|
||||
|
||||
/// Reads an FsRepr from a [u8; 32].
|
||||
fn read_fs(from: &[u8; 32]) -> FsRepr {
|
||||
let mut f = <<Bls12 as JubjubEngine>::Fs as PrimeField>::Repr::default();
|
||||
f.read_le(from).expect("length is 32 bytes");
|
||||
|
||||
f.read_le(&from[..]).expect("length is 32 bytes");
|
||||
f
|
||||
}
|
||||
|
||||
/// Reads an FsRepr from [u8] of length 32
|
||||
/// Reads an FsRepr from a [u8; 32]
|
||||
/// and multiplies it by the given base.
|
||||
/// This will panic (abort) if length provided is
|
||||
/// not correct
|
||||
fn fixed_scalar_mult(from: &[u8], p_g: FixedGenerators) -> edwards::Point<Bls12, PrimeOrder> {
|
||||
fn fixed_scalar_mult(from: &[u8; 32], p_g: FixedGenerators) -> edwards::Point<Bls12, PrimeOrder> {
|
||||
let f = read_fs(from);
|
||||
|
||||
JUBJUB.generator(p_g).mul(f, &JUBJUB)
|
||||
}
|
||||
|
||||
/// Loads the zk-SNARK parameters into memory and saves paths as necessary.
|
||||
/// Only called once.
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
#[no_mangle]
|
||||
pub extern "system" fn librustzcash_init_zksnark_params(
|
||||
pub extern "C" fn librustzcash_init_zksnark_params(
|
||||
spend_path: *const u8,
|
||||
spend_path_len: usize,
|
||||
spend_hash: *const c_char,
|
||||
@@ -150,9 +137,11 @@ pub extern "system" fn librustzcash_init_zksnark_params(
|
||||
)
|
||||
}
|
||||
|
||||
/// Loads the zk-SNARK parameters into memory and saves paths as necessary.
|
||||
/// Only called once.
|
||||
#[cfg(target_os = "windows")]
|
||||
#[no_mangle]
|
||||
pub extern "system" fn librustzcash_init_zksnark_params(
|
||||
pub extern "C" fn librustzcash_init_zksnark_params(
|
||||
spend_path: *const u16,
|
||||
spend_path_len: usize,
|
||||
spend_hash: *const c_char,
|
||||
@@ -237,19 +226,28 @@ fn init_zksnark_params(
|
||||
}
|
||||
}
|
||||
|
||||
/// Writes the "uncommitted" note value for empty leaves of the Merkle tree.
|
||||
///
|
||||
/// `result` must be a valid pointer to 32 bytes which will be written.
|
||||
#[no_mangle]
|
||||
pub extern "system" fn librustzcash_tree_uncommitted(result: *mut [c_uchar; 32]) {
|
||||
pub extern "C" fn librustzcash_tree_uncommitted(result: *mut [c_uchar; 32]) {
|
||||
let tmp = Note::<Bls12>::uncommitted().into_repr();
|
||||
|
||||
// Should be okay, caller is responsible for ensuring the pointer
|
||||
// is a valid pointer to 32 bytes that can be mutated.
|
||||
let result = unsafe { &mut *result };
|
||||
|
||||
write_le(tmp, &mut result[..]);
|
||||
tmp.write_le(&mut result[..]).expect("length is 32 bytes");
|
||||
}
|
||||
|
||||
/// Computes a merkle tree hash for a given depth. The `depth` parameter should
|
||||
/// not be larger than 62.
|
||||
///
|
||||
/// `a` and `b` each must be of length 32, and must each be scalars of BLS12-381.
|
||||
///
|
||||
/// The result of the merkle tree hash is placed in `result`, which must also be
|
||||
/// of length 32.
|
||||
#[no_mangle]
|
||||
pub extern "system" fn librustzcash_merkle_hash(
|
||||
pub extern "C" fn librustzcash_merkle_hash(
|
||||
depth: size_t,
|
||||
a: *const [c_uchar; 32],
|
||||
b: *const [c_uchar; 32],
|
||||
@@ -258,27 +256,23 @@ pub extern "system" fn librustzcash_merkle_hash(
|
||||
// Should be okay, because caller is responsible for ensuring
|
||||
// the pointer is a valid pointer to 32 bytes, and that is the
|
||||
// size of the representation
|
||||
let a_repr = read_le(unsafe { &(&*a)[..] });
|
||||
let a_repr = read_fr(unsafe { &*a });
|
||||
|
||||
// Should be okay, because caller is responsible for ensuring
|
||||
// the pointer is a valid pointer to 32 bytes, and that is the
|
||||
// size of the representation
|
||||
let b_repr = read_le(unsafe { &(&*b)[..] });
|
||||
let b_repr = read_fr(unsafe { &*b });
|
||||
|
||||
let tmp = merkle_hash(depth, &a_repr, &b_repr);
|
||||
|
||||
// Should be okay, caller is responsible for ensuring the pointer
|
||||
// is a valid pointer to 32 bytes that can be mutated.
|
||||
let result = unsafe { &mut *result };
|
||||
|
||||
write_le(tmp, &mut result[..]);
|
||||
tmp.write_le(&mut result[..]).expect("length is 32 bytes");
|
||||
}
|
||||
|
||||
#[no_mangle] // ToScalar
|
||||
pub extern "system" fn librustzcash_to_scalar(
|
||||
input: *const [c_uchar; 64],
|
||||
result: *mut [c_uchar; 32],
|
||||
) {
|
||||
pub extern "C" fn librustzcash_to_scalar(input: *const [c_uchar; 64], result: *mut [c_uchar; 32]) {
|
||||
// Should be okay, because caller is responsible for ensuring
|
||||
// the pointer is a valid pointer to 32 bytes, and that is the
|
||||
// size of the representation
|
||||
@@ -292,10 +286,7 @@ pub extern "system" fn librustzcash_to_scalar(
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "system" fn librustzcash_ask_to_ak(
|
||||
ask: *const [c_uchar; 32],
|
||||
result: *mut [c_uchar; 32],
|
||||
) {
|
||||
pub extern "C" fn librustzcash_ask_to_ak(ask: *const [c_uchar; 32], result: *mut [c_uchar; 32]) {
|
||||
let ask = unsafe { &*ask };
|
||||
let ak = fixed_scalar_mult(ask, FixedGenerators::SpendingKeyGenerator);
|
||||
|
||||
@@ -305,10 +296,7 @@ pub extern "system" fn librustzcash_ask_to_ak(
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "system" fn librustzcash_nsk_to_nk(
|
||||
nsk: *const [c_uchar; 32],
|
||||
result: *mut [c_uchar; 32],
|
||||
) {
|
||||
pub extern "C" fn librustzcash_nsk_to_nk(nsk: *const [c_uchar; 32], result: *mut [c_uchar; 32]) {
|
||||
let nsk = unsafe { &*nsk };
|
||||
let nk = fixed_scalar_mult(nsk, FixedGenerators::ProofGenerationKey);
|
||||
|
||||
@@ -318,7 +306,7 @@ pub extern "system" fn librustzcash_nsk_to_nk(
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "system" fn librustzcash_crh_ivk(
|
||||
pub extern "C" fn librustzcash_crh_ivk(
|
||||
ak: *const [c_uchar; 32],
|
||||
nk: *const [c_uchar; 32],
|
||||
result: *mut [c_uchar; 32],
|
||||
@@ -343,13 +331,13 @@ pub extern "system" fn librustzcash_crh_ivk(
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "system" fn librustzcash_check_diversifier(diversifier: *const [c_uchar; 11]) -> bool {
|
||||
pub extern "C" fn librustzcash_check_diversifier(diversifier: *const [c_uchar; 11]) -> bool {
|
||||
let diversifier = Diversifier(unsafe { *diversifier });
|
||||
diversifier.g_d::<Bls12>(&JUBJUB).is_some()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "system" fn librustzcash_ivk_to_pkd(
|
||||
pub extern "C" fn librustzcash_ivk_to_pkd(
|
||||
ivk: *const [c_uchar; 32],
|
||||
diversifier: *const [c_uchar; 11],
|
||||
result: *mut [c_uchar; 32],
|
||||
@@ -388,9 +376,9 @@ fn test_gen_r() {
|
||||
let _ = Fs::from_repr(repr).unwrap();
|
||||
}
|
||||
|
||||
/// Return 32 byte random scalar, uniformly.
|
||||
/// Generate uniformly random scalar in Jubjub. The result is of length 32.
|
||||
#[no_mangle]
|
||||
pub extern "system" fn librustzcash_sapling_generate_r(result: *mut [c_uchar; 32]) {
|
||||
pub extern "C" fn librustzcash_sapling_generate_r(result: *mut [c_uchar; 32]) {
|
||||
// create random 64 byte buffer
|
||||
let mut rng = OsRng;
|
||||
let mut buffer = [0u8; 64];
|
||||
@@ -428,7 +416,7 @@ fn priv_get_note(
|
||||
};
|
||||
|
||||
// Deserialize randomness
|
||||
let r = match Fs::from_repr(read_fs(&(unsafe { &*r })[..])) {
|
||||
let r = match Fs::from_repr(read_fs(unsafe { &*r })) {
|
||||
Ok(r) => r,
|
||||
Err(_) => return Err(()),
|
||||
};
|
||||
@@ -443,9 +431,14 @@ fn priv_get_note(
|
||||
Ok(note)
|
||||
}
|
||||
|
||||
/// Compute Sapling note nullifier.
|
||||
/// Compute a Sapling nullifier.
|
||||
///
|
||||
/// The `diversifier` parameter must be 11 bytes in length.
|
||||
/// The `pk_d`, `r`, `ak` and `nk` parameters must be of length 32.
|
||||
/// The result is also of length 32 and placed in `result`.
|
||||
/// Returns false if `diversifier` or `pk_d` is not valid.
|
||||
#[no_mangle]
|
||||
pub extern "system" fn librustzcash_sapling_compute_nf(
|
||||
pub extern "C" fn librustzcash_sapling_compute_nf(
|
||||
diversifier: *const [c_uchar; 11],
|
||||
pk_d: *const [c_uchar; 32],
|
||||
value: u64,
|
||||
@@ -488,9 +481,14 @@ pub extern "system" fn librustzcash_sapling_compute_nf(
|
||||
true
|
||||
}
|
||||
|
||||
/// Compute Sapling note commitment.
|
||||
/// Compute a Sapling commitment.
|
||||
///
|
||||
/// The `diversifier` parameter must be 11 bytes in length.
|
||||
/// The `pk_d` and `r` parameters must be of length 32.
|
||||
/// The result is also of length 32 and placed in `result`.
|
||||
/// Returns false if `diversifier` or `pk_d` is not valid.
|
||||
#[no_mangle]
|
||||
pub extern "system" fn librustzcash_sapling_compute_cm(
|
||||
pub extern "C" fn librustzcash_sapling_compute_cm(
|
||||
diversifier: *const [c_uchar; 11],
|
||||
pk_d: *const [c_uchar; 32],
|
||||
value: u64,
|
||||
@@ -503,13 +501,20 @@ pub extern "system" fn librustzcash_sapling_compute_cm(
|
||||
};
|
||||
|
||||
let result = unsafe { &mut *result };
|
||||
write_le(note.cm(&JUBJUB).into_repr(), &mut result[..]);
|
||||
note.cm(&JUBJUB)
|
||||
.into_repr()
|
||||
.write_le(&mut result[..])
|
||||
.expect("length is 32 bytes");
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
/// Computes \[sk\] \[8\] P for some 32-byte point P, and 32-byte Fs.
|
||||
///
|
||||
/// If P or sk are invalid, returns false. Otherwise, the result is written to
|
||||
/// the 32-byte `result` buffer.
|
||||
#[no_mangle]
|
||||
pub extern "system" fn librustzcash_sapling_ka_agree(
|
||||
pub extern "C" fn librustzcash_sapling_ka_agree(
|
||||
p: *const [c_uchar; 32],
|
||||
sk: *const [c_uchar; 32],
|
||||
result: *mut [c_uchar; 32],
|
||||
@@ -521,7 +526,7 @@ pub extern "system" fn librustzcash_sapling_ka_agree(
|
||||
};
|
||||
|
||||
// Deserialize sk
|
||||
let sk = match Fs::from_repr(read_fs(&(unsafe { &*sk })[..])) {
|
||||
let sk = match Fs::from_repr(read_fs(unsafe { &*sk })) {
|
||||
Ok(p) => p,
|
||||
Err(_) => return false,
|
||||
};
|
||||
@@ -536,8 +541,11 @@ pub extern "system" fn librustzcash_sapling_ka_agree(
|
||||
true
|
||||
}
|
||||
|
||||
/// Compute g_d = GH(diversifier) and returns false if the diversifier is
|
||||
/// invalid. Computes \[esk\] g_d and writes the result to the 32-byte `result`
|
||||
/// buffer. Returns false if `esk` is not a valid scalar.
|
||||
#[no_mangle]
|
||||
pub extern "system" fn librustzcash_sapling_ka_derivepublic(
|
||||
pub extern "C" fn librustzcash_sapling_ka_derivepublic(
|
||||
diversifier: *const [c_uchar; 11],
|
||||
esk: *const [c_uchar; 32],
|
||||
result: *mut [c_uchar; 32],
|
||||
@@ -551,7 +559,7 @@ pub extern "system" fn librustzcash_sapling_ka_derivepublic(
|
||||
};
|
||||
|
||||
// Deserialize esk
|
||||
let esk = match Fs::from_repr(read_fs(&(unsafe { &*esk })[..])) {
|
||||
let esk = match Fs::from_repr(read_fs(unsafe { &*esk })) {
|
||||
Ok(p) => p,
|
||||
Err(_) => return false,
|
||||
};
|
||||
@@ -564,8 +572,10 @@ pub extern "system" fn librustzcash_sapling_ka_derivepublic(
|
||||
true
|
||||
}
|
||||
|
||||
/// Validates the provided Equihash solution against the given parameters, input
|
||||
/// and nonce.
|
||||
#[no_mangle]
|
||||
pub extern "system" fn librustzcash_eh_isvalid(
|
||||
pub extern "C" fn librustzcash_eh_isvalid(
|
||||
n: u32,
|
||||
k: u32,
|
||||
input: *const c_uchar,
|
||||
@@ -584,18 +594,18 @@ pub extern "system" fn librustzcash_eh_isvalid(
|
||||
equihash::is_valid_solution(n, k, rs_input, rs_nonce, rs_soln)
|
||||
}
|
||||
|
||||
/// Creates a Sapling verification context. Please free this when you're done.
|
||||
#[no_mangle]
|
||||
pub extern "system" fn librustzcash_sapling_verification_ctx_init(
|
||||
) -> *mut SaplingVerificationContext {
|
||||
pub extern "C" fn librustzcash_sapling_verification_ctx_init() -> *mut SaplingVerificationContext {
|
||||
let ctx = Box::new(SaplingVerificationContext::new());
|
||||
|
||||
Box::into_raw(ctx)
|
||||
}
|
||||
|
||||
/// Frees a Sapling verification context returned from
|
||||
/// [`librustzcash_sapling_verification_ctx_init`].
|
||||
#[no_mangle]
|
||||
pub extern "system" fn librustzcash_sapling_verification_ctx_free(
|
||||
ctx: *mut SaplingVerificationContext,
|
||||
) {
|
||||
pub extern "C" fn librustzcash_sapling_verification_ctx_free(ctx: *mut SaplingVerificationContext) {
|
||||
drop(unsafe { Box::from_raw(ctx) });
|
||||
}
|
||||
|
||||
@@ -603,8 +613,10 @@ const GROTH_PROOF_SIZE: usize = 48 // π_A
|
||||
+ 96 // π_B
|
||||
+ 48; // π_C
|
||||
|
||||
/// Check the validity of a Sapling Spend description, accumulating the value
|
||||
/// commitment into the context.
|
||||
#[no_mangle]
|
||||
pub extern "system" fn librustzcash_sapling_check_spend(
|
||||
pub extern "C" fn librustzcash_sapling_check_spend(
|
||||
ctx: *mut SaplingVerificationContext,
|
||||
cv: *const [c_uchar; 32],
|
||||
anchor: *const [c_uchar; 32],
|
||||
@@ -622,7 +634,7 @@ pub extern "system" fn librustzcash_sapling_check_spend(
|
||||
|
||||
// Deserialize the anchor, which should be an element
|
||||
// of Fr.
|
||||
let anchor = match Fr::from_repr(read_le(&(unsafe { &*anchor })[..])) {
|
||||
let anchor = match Fr::from_repr(read_fr(unsafe { &*anchor })) {
|
||||
Ok(a) => a,
|
||||
Err(_) => return false,
|
||||
};
|
||||
@@ -658,8 +670,10 @@ pub extern "system" fn librustzcash_sapling_check_spend(
|
||||
)
|
||||
}
|
||||
|
||||
/// Check the validity of a Sapling Output description, accumulating the value
|
||||
/// commitment into the context.
|
||||
#[no_mangle]
|
||||
pub extern "system" fn librustzcash_sapling_check_output(
|
||||
pub extern "C" fn librustzcash_sapling_check_output(
|
||||
ctx: *mut SaplingVerificationContext,
|
||||
cv: *const [c_uchar; 32],
|
||||
cm: *const [c_uchar; 32],
|
||||
@@ -674,7 +688,7 @@ pub extern "system" fn librustzcash_sapling_check_output(
|
||||
|
||||
// Deserialize the commitment, which should be an element
|
||||
// of Fr.
|
||||
let cm = match Fr::from_repr(read_le(&(unsafe { &*cm })[..])) {
|
||||
let cm = match Fr::from_repr(read_fr(unsafe { &*cm })) {
|
||||
Ok(a) => a,
|
||||
Err(_) => return false,
|
||||
};
|
||||
@@ -701,8 +715,10 @@ pub extern "system" fn librustzcash_sapling_check_output(
|
||||
)
|
||||
}
|
||||
|
||||
/// Finally checks the validity of the entire Sapling transaction given
|
||||
/// valueBalance and the binding signature.
|
||||
#[no_mangle]
|
||||
pub extern "system" fn librustzcash_sapling_final_check(
|
||||
pub extern "C" fn librustzcash_sapling_final_check(
|
||||
ctx: *mut SaplingVerificationContext,
|
||||
value_balance: i64,
|
||||
binding_sig: *const [c_uchar; 64],
|
||||
@@ -727,8 +743,9 @@ pub extern "system" fn librustzcash_sapling_final_check(
|
||||
)
|
||||
}
|
||||
|
||||
/// Sprout JoinSplit proof generation.
|
||||
#[no_mangle]
|
||||
pub extern "system" fn librustzcash_sprout_prove(
|
||||
pub extern "C" fn librustzcash_sprout_prove(
|
||||
proof_out: *mut [c_uchar; GROTH_PROOF_SIZE],
|
||||
|
||||
phi: *const [c_uchar; 32],
|
||||
@@ -740,14 +757,14 @@ pub extern "system" fn librustzcash_sprout_prove(
|
||||
in_value1: u64,
|
||||
in_rho1: *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
|
||||
in_sk2: *const [c_uchar; 32],
|
||||
in_value2: u64,
|
||||
in_rho2: *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
|
||||
out_pk1: *const [c_uchar; 32],
|
||||
@@ -763,94 +780,6 @@ pub extern "system" fn librustzcash_sprout_prove(
|
||||
vpub_old: 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
|
||||
let sprout_fs = File::open(
|
||||
unsafe { &SPROUT_GROTH16_PARAMS_PATH }
|
||||
@@ -866,18 +795,39 @@ pub extern "system" fn librustzcash_sprout_prove(
|
||||
|
||||
drop(sprout_fs);
|
||||
|
||||
// Initialize secure RNG
|
||||
let mut rng = OsRng;
|
||||
|
||||
let proof = create_random_proof(js, ¶ms, &mut rng).expect("proving should not fail");
|
||||
let proof = sprout::create_proof(
|
||||
unsafe { *phi },
|
||||
unsafe { *rt },
|
||||
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
|
||||
.write(&mut (unsafe { &mut *proof_out })[..])
|
||||
.expect("should be able to serialize a proof");
|
||||
}
|
||||
|
||||
/// Sprout JoinSplit proof verification.
|
||||
#[no_mangle]
|
||||
pub extern "system" fn librustzcash_sprout_verify(
|
||||
pub extern "C" fn librustzcash_sprout_verify(
|
||||
proof: *const [c_uchar; GROTH_PROOF_SIZE],
|
||||
rt: *const [c_uchar; 32],
|
||||
h_sig: *const [c_uchar; 32],
|
||||
@@ -890,43 +840,26 @@ pub extern "system" fn librustzcash_sprout_verify(
|
||||
vpub_old: u64,
|
||||
vpub_new: u64,
|
||||
) -> bool {
|
||||
// Prepare the public input for the verifier
|
||||
let mut public_input = Vec::with_capacity((32 * 8) + (8 * 2));
|
||||
public_input.extend(unsafe { &(&*rt)[..] });
|
||||
public_input.extend(unsafe { &(&*h_sig)[..] });
|
||||
public_input.extend(unsafe { &(&*nf1)[..] });
|
||||
public_input.extend(unsafe { &(&*mac1)[..] });
|
||||
public_input.extend(unsafe { &(&*nf2)[..] });
|
||||
public_input.extend(unsafe { &(&*mac2)[..] });
|
||||
public_input.extend(unsafe { &(&*cm1)[..] });
|
||||
public_input.extend(unsafe { &(&*cm2)[..] });
|
||||
public_input.write_u64::<LittleEndian>(vpub_old).unwrap();
|
||||
public_input.write_u64::<LittleEndian>(vpub_new).unwrap();
|
||||
|
||||
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(
|
||||
sprout::verify_proof(
|
||||
unsafe { &*proof },
|
||||
unsafe { &*rt },
|
||||
unsafe { &*h_sig },
|
||||
unsafe { &*mac1 },
|
||||
unsafe { &*mac2 },
|
||||
unsafe { &*nf1 },
|
||||
unsafe { &*nf2 },
|
||||
unsafe { &*cm1 },
|
||||
unsafe { &*cm2 },
|
||||
vpub_old,
|
||||
vpub_new,
|
||||
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
|
||||
/// the necessary witness information. It outputs `cv` and the `zkproof`.
|
||||
#[no_mangle]
|
||||
pub extern "system" fn librustzcash_sapling_output_proof(
|
||||
pub extern "C" fn librustzcash_sapling_output_proof(
|
||||
ctx: *mut SaplingProvingContext,
|
||||
esk: *const [c_uchar; 32],
|
||||
payment_address: *const [c_uchar; 43],
|
||||
@@ -936,7 +869,7 @@ pub extern "system" fn librustzcash_sapling_output_proof(
|
||||
zkproof: *mut [c_uchar; GROTH_PROOF_SIZE],
|
||||
) -> bool {
|
||||
// Grab `esk`, which the caller should have constructed for the DH key exchange.
|
||||
let esk = match Fs::from_repr(read_fs(&(unsafe { &*esk })[..])) {
|
||||
let esk = match Fs::from_repr(read_fs(unsafe { &*esk })) {
|
||||
Ok(p) => p,
|
||||
Err(_) => return false,
|
||||
};
|
||||
@@ -949,7 +882,7 @@ pub extern "system" fn librustzcash_sapling_output_proof(
|
||||
};
|
||||
|
||||
// The caller provides the commitment randomness for the output note
|
||||
let rcm = match Fs::from_repr(read_fs(&(unsafe { &*rcm })[..])) {
|
||||
let rcm = match Fs::from_repr(read_fs(unsafe { &*rcm })) {
|
||||
Ok(p) => p,
|
||||
Err(_) => return false,
|
||||
};
|
||||
@@ -977,15 +910,20 @@ pub extern "system" fn librustzcash_sapling_output_proof(
|
||||
true
|
||||
}
|
||||
|
||||
/// Computes the signature for each Spend description, given the key `ask`, the
|
||||
/// re-randomization `ar`, the 32-byte sighash `sighash`, and an output `result`
|
||||
/// buffer of 64-bytes for the signature.
|
||||
///
|
||||
/// This function will fail if the provided `ask` or `ar` are invalid.
|
||||
#[no_mangle]
|
||||
pub extern "system" fn librustzcash_sapling_spend_sig(
|
||||
pub extern "C" fn librustzcash_sapling_spend_sig(
|
||||
ask: *const [c_uchar; 32],
|
||||
ar: *const [c_uchar; 32],
|
||||
sighash: *const [c_uchar; 32],
|
||||
result: *mut [c_uchar; 64],
|
||||
) -> bool {
|
||||
// The caller provides the re-randomization of `ak`.
|
||||
let ar = match Fs::from_repr(read_fs(&(unsafe { &*ar })[..])) {
|
||||
let ar = match Fs::from_repr(read_fs(unsafe { &*ar })) {
|
||||
Ok(p) => p,
|
||||
Err(_) => return false,
|
||||
};
|
||||
@@ -1009,8 +947,12 @@ pub extern "system" fn librustzcash_sapling_spend_sig(
|
||||
true
|
||||
}
|
||||
|
||||
/// This function (using the proving context) constructs a binding signature.
|
||||
///
|
||||
/// You must provide the intended valueBalance so that we can internally check
|
||||
/// consistency.
|
||||
#[no_mangle]
|
||||
pub extern "system" fn librustzcash_sapling_binding_sig(
|
||||
pub extern "C" fn librustzcash_sapling_binding_sig(
|
||||
ctx: *const SaplingProvingContext,
|
||||
value_balance: i64,
|
||||
sighash: *const [c_uchar; 32],
|
||||
@@ -1034,8 +976,11 @@ pub extern "system" fn librustzcash_sapling_binding_sig(
|
||||
true
|
||||
}
|
||||
|
||||
/// This function (using the proving context) constructs a Spend proof given the
|
||||
/// necessary witness information. It outputs `cv` (the value commitment) and
|
||||
/// `rk` (so that you don't have to compute it) along with the proof.
|
||||
#[no_mangle]
|
||||
pub extern "system" fn librustzcash_sapling_spend_proof(
|
||||
pub extern "C" fn librustzcash_sapling_spend_proof(
|
||||
ctx: *mut SaplingProvingContext,
|
||||
ak: *const [c_uchar; 32],
|
||||
nsk: *const [c_uchar; 32],
|
||||
@@ -1062,7 +1007,7 @@ pub extern "system" fn librustzcash_sapling_spend_proof(
|
||||
};
|
||||
|
||||
// Grab `nsk` from the caller
|
||||
let nsk = match Fs::from_repr(read_fs(&(unsafe { &*nsk })[..])) {
|
||||
let nsk = match Fs::from_repr(read_fs(unsafe { &*nsk })) {
|
||||
Ok(p) => p,
|
||||
Err(_) => return false,
|
||||
};
|
||||
@@ -1077,19 +1022,19 @@ pub extern "system" fn librustzcash_sapling_spend_proof(
|
||||
let diversifier = Diversifier(unsafe { *diversifier });
|
||||
|
||||
// The caller chooses the note randomness
|
||||
let rcm = match Fs::from_repr(read_fs(&(unsafe { &*rcm })[..])) {
|
||||
let rcm = match Fs::from_repr(read_fs(unsafe { &*rcm })) {
|
||||
Ok(p) => p,
|
||||
Err(_) => return false,
|
||||
};
|
||||
|
||||
// The caller also chooses the re-randomization of ak
|
||||
let ar = match Fs::from_repr(read_fs(&(unsafe { &*ar })[..])) {
|
||||
let ar = match Fs::from_repr(read_fs(unsafe { &*ar })) {
|
||||
Ok(p) => p,
|
||||
Err(_) => return false,
|
||||
};
|
||||
|
||||
// We need to compute the anchor of the Spend.
|
||||
let anchor = match Fr::from_repr(read_le(unsafe { &(&*anchor)[..] })) {
|
||||
let anchor = match Fr::from_repr(read_fr(unsafe { &*anchor })) {
|
||||
Ok(p) => p,
|
||||
Err(_) => return false,
|
||||
};
|
||||
@@ -1134,20 +1079,24 @@ pub extern "system" fn librustzcash_sapling_spend_proof(
|
||||
true
|
||||
}
|
||||
|
||||
/// Creates a Sapling proving context. Please free this when you're done.
|
||||
#[no_mangle]
|
||||
pub extern "system" fn librustzcash_sapling_proving_ctx_init() -> *mut SaplingProvingContext {
|
||||
pub extern "C" fn librustzcash_sapling_proving_ctx_init() -> *mut SaplingProvingContext {
|
||||
let ctx = Box::new(SaplingProvingContext::new());
|
||||
|
||||
Box::into_raw(ctx)
|
||||
}
|
||||
|
||||
/// Frees a Sapling proving context returned from
|
||||
/// [`librustzcash_sapling_proving_ctx_init`].
|
||||
#[no_mangle]
|
||||
pub extern "system" fn librustzcash_sapling_proving_ctx_free(ctx: *mut SaplingProvingContext) {
|
||||
pub extern "C" fn librustzcash_sapling_proving_ctx_free(ctx: *mut SaplingProvingContext) {
|
||||
drop(unsafe { Box::from_raw(ctx) });
|
||||
}
|
||||
|
||||
/// Derive the master ExtendedSpendingKey from a seed.
|
||||
#[no_mangle]
|
||||
pub extern "system" fn librustzcash_zip32_xsk_master(
|
||||
pub extern "C" fn librustzcash_zip32_xsk_master(
|
||||
seed: *const c_uchar,
|
||||
seedlen: size_t,
|
||||
xsk_master: *mut [c_uchar; 169],
|
||||
@@ -1160,8 +1109,9 @@ pub extern "system" fn librustzcash_zip32_xsk_master(
|
||||
.expect("should be able to serialize an ExtendedSpendingKey");
|
||||
}
|
||||
|
||||
/// Derive a child ExtendedSpendingKey from a parent.
|
||||
#[no_mangle]
|
||||
pub extern "system" fn librustzcash_zip32_xsk_derive(
|
||||
pub extern "C" fn librustzcash_zip32_xsk_derive(
|
||||
xsk_parent: *const [c_uchar; 169],
|
||||
i: u32,
|
||||
xsk_i: *mut [c_uchar; 169],
|
||||
@@ -1176,8 +1126,9 @@ pub extern "system" fn librustzcash_zip32_xsk_derive(
|
||||
.expect("should be able to serialize an ExtendedSpendingKey");
|
||||
}
|
||||
|
||||
/// Derive a child ExtendedFullViewingKey from a parent.
|
||||
#[no_mangle]
|
||||
pub extern "system" fn librustzcash_zip32_xfvk_derive(
|
||||
pub extern "C" fn librustzcash_zip32_xfvk_derive(
|
||||
xfvk_parent: *const [c_uchar; 169],
|
||||
i: u32,
|
||||
xfvk_i: *mut [c_uchar; 169],
|
||||
@@ -1197,8 +1148,9 @@ pub extern "system" fn librustzcash_zip32_xfvk_derive(
|
||||
true
|
||||
}
|
||||
|
||||
/// Derive a PaymentAddress from an ExtendedFullViewingKey.
|
||||
#[no_mangle]
|
||||
pub extern "system" fn librustzcash_zip32_xfvk_address(
|
||||
pub extern "C" fn librustzcash_zip32_xfvk_address(
|
||||
xfvk: *const [c_uchar; 169],
|
||||
j: *const [c_uchar; 11],
|
||||
j_ret: *mut [c_uchar; 11],
|
||||
|
@@ -15,6 +15,7 @@ use std::path::Path;
|
||||
pub mod circuit;
|
||||
mod hashreader;
|
||||
pub mod sapling;
|
||||
pub mod sprout;
|
||||
|
||||
#[cfg(feature = "local-prover")]
|
||||
pub mod prover;
|
||||
|
179
zcash_proofs/src/sprout.rs
Normal file
179
zcash_proofs/src/sprout.rs
Normal file
@@ -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,
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user