mirror of
https://github.com/Qortal/pirate-librustzcash.git
synced 2025-11-01 20:07:02 +00:00
Merge pull request #30 from str4d/sapling-api-cleanup
Sapling proving and verifying API
This commit is contained in:
8
Cargo.lock
generated
8
Cargo.lock
generated
@@ -344,6 +344,7 @@ dependencies = [
|
|||||||
"pairing 0.14.2",
|
"pairing 0.14.2",
|
||||||
"rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"sapling-crypto 0.0.1",
|
"sapling-crypto 0.0.1",
|
||||||
|
"zcash_proofs 0.0.0",
|
||||||
"zip32 0.0.0",
|
"zip32 0.0.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -716,6 +717,13 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "zcash_proofs"
|
name = "zcash_proofs"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
|
dependencies = [
|
||||||
|
"bellman 0.1.0",
|
||||||
|
"byteorder 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"pairing 0.14.2",
|
||||||
|
"rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"sapling-crypto 0.0.1",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zcash_wallet"
|
name = "zcash_wallet"
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ lazy_static = "1"
|
|||||||
byteorder = "1"
|
byteorder = "1"
|
||||||
rand = "0.4"
|
rand = "0.4"
|
||||||
sapling-crypto = { path = "../sapling-crypto" }
|
sapling-crypto = { path = "../sapling-crypto" }
|
||||||
|
zcash_proofs = { path = "../zcash_proofs" }
|
||||||
zip32 = { path = "../zip32" }
|
zip32 = { path = "../zip32" }
|
||||||
|
|
||||||
[dependencies.blake2-rfc]
|
[dependencies.blake2-rfc]
|
||||||
|
|||||||
@@ -149,8 +149,7 @@ fn expand_array(vin: &[u8], bit_len: usize, byte_pad: usize) -> Vec<u8> {
|
|||||||
vout[j + x] = ((
|
vout[j + x] = ((
|
||||||
// Big-endian
|
// Big-endian
|
||||||
acc_value >> (acc_bits + (8 * (out_width - x - 1)))
|
acc_value >> (acc_bits + (8 * (out_width - x - 1)))
|
||||||
)
|
) & (
|
||||||
& (
|
|
||||||
// Apply bit_len_mask across byte boundaries
|
// Apply bit_len_mask across byte boundaries
|
||||||
(bit_len_mask >> (8 * (out_width - x - 1))) & 0xFF
|
(bit_len_mask >> (8 * (out_width - x - 1))) & 0xFF
|
||||||
)) as u8;
|
)) as u8;
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ extern crate libc;
|
|||||||
extern crate pairing;
|
extern crate pairing;
|
||||||
extern crate rand;
|
extern crate rand;
|
||||||
extern crate sapling_crypto;
|
extern crate sapling_crypto;
|
||||||
|
extern crate zcash_proofs;
|
||||||
extern crate zip32;
|
extern crate zip32;
|
||||||
|
|
||||||
mod hashreader;
|
mod hashreader;
|
||||||
@@ -14,7 +15,7 @@ extern crate lazy_static;
|
|||||||
|
|
||||||
use pairing::{
|
use pairing::{
|
||||||
bls12_381::{Bls12, Fr, FrRepr},
|
bls12_381::{Bls12, Fr, FrRepr},
|
||||||
BitIterator, Field, PrimeField, PrimeFieldRepr,
|
BitIterator, PrimeField, PrimeFieldRepr,
|
||||||
};
|
};
|
||||||
|
|
||||||
use sapling_crypto::{
|
use sapling_crypto::{
|
||||||
@@ -29,9 +30,8 @@ use sapling_crypto::{
|
|||||||
redjubjub::{self, Signature},
|
redjubjub::{self, Signature},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use sapling_crypto::circuit::sapling::TREE_DEPTH as SAPLING_TREE_DEPTH;
|
||||||
use sapling_crypto::circuit::sprout::{self, TREE_DEPTH as SPROUT_TREE_DEPTH};
|
use sapling_crypto::circuit::sprout::{self, TREE_DEPTH as SPROUT_TREE_DEPTH};
|
||||||
// TODO: make these consistent
|
|
||||||
const SAPLING_TREE_DEPTH: usize = 32;
|
|
||||||
|
|
||||||
use bellman::groth16::{
|
use bellman::groth16::{
|
||||||
create_random_proof, prepare_verifying_key, verify_proof, Parameters, PreparedVerifyingKey,
|
create_random_proof, prepare_verifying_key, verify_proof, Parameters, PreparedVerifyingKey,
|
||||||
@@ -42,7 +42,7 @@ use blake2_rfc::blake2s::Blake2s;
|
|||||||
|
|
||||||
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
||||||
|
|
||||||
use rand::{OsRng, Rand, Rng};
|
use rand::{OsRng, Rng};
|
||||||
use std::io::{self, BufReader};
|
use std::io::{self, BufReader};
|
||||||
|
|
||||||
use libc::{c_char, c_uchar, int64_t, size_t, uint32_t, uint64_t};
|
use libc::{c_char, c_uchar, int64_t, size_t, uint32_t, uint64_t};
|
||||||
@@ -61,7 +61,10 @@ use std::ffi::OsString;
|
|||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
use std::os::windows::ffi::OsStringExt;
|
use std::os::windows::ffi::OsStringExt;
|
||||||
|
|
||||||
use sapling_crypto::primitives::{ProofGenerationKey, ValueCommitment, ViewingKey};
|
use sapling_crypto::primitives::{ProofGenerationKey, ViewingKey};
|
||||||
|
use zcash_proofs::sapling::{
|
||||||
|
CommitmentTreeWitness, SaplingProvingContext, SaplingVerificationContext,
|
||||||
|
};
|
||||||
|
|
||||||
pub mod equihash;
|
pub mod equihash;
|
||||||
|
|
||||||
@@ -80,10 +83,6 @@ static mut SAPLING_SPEND_PARAMS: Option<Parameters<Bls12>> = None;
|
|||||||
static mut SAPLING_OUTPUT_PARAMS: Option<Parameters<Bls12>> = None;
|
static mut SAPLING_OUTPUT_PARAMS: Option<Parameters<Bls12>> = None;
|
||||||
static mut SPROUT_GROTH16_PARAMS_PATH: Option<PathBuf> = None;
|
static mut SPROUT_GROTH16_PARAMS_PATH: Option<PathBuf> = None;
|
||||||
|
|
||||||
fn is_small_order<Order>(p: &edwards::Point<Bls12, Order>) -> bool {
|
|
||||||
p.double(&JUBJUB).double(&JUBJUB).double(&JUBJUB) == edwards::Point::zero()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Writes an FrRepr to [u8] of length 32
|
/// Writes an FrRepr to [u8] of length 32
|
||||||
fn write_le(f: FrRepr, to: &mut [u8]) {
|
fn write_le(f: FrRepr, to: &mut [u8]) {
|
||||||
assert_eq!(to.len(), 32);
|
assert_eq!(to.len(), 32);
|
||||||
@@ -325,9 +324,10 @@ pub extern "system" fn librustzcash_merkle_hash(
|
|||||||
.take(Fr::NUM_BITS as usize)
|
.take(Fr::NUM_BITS as usize)
|
||||||
.chain(rhs.iter().map(|&x| x).take(Fr::NUM_BITS as usize)),
|
.chain(rhs.iter().map(|&x| x).take(Fr::NUM_BITS as usize)),
|
||||||
&JUBJUB,
|
&JUBJUB,
|
||||||
).into_xy()
|
)
|
||||||
.0
|
.into_xy()
|
||||||
.into_repr();
|
.0
|
||||||
|
.into_repr();
|
||||||
|
|
||||||
// Should be okay, caller is responsible for ensuring the pointer
|
// Should be okay, caller is responsible for ensuring the pointer
|
||||||
// is a valid pointer to 32 bytes that can be mutated.
|
// is a valid pointer to 32 bytes that can be mutated.
|
||||||
@@ -648,16 +648,10 @@ pub extern "system" fn librustzcash_eh_isvalid(
|
|||||||
equihash::is_valid_solution(n, k, rs_input, rs_nonce, rs_soln)
|
equihash::is_valid_solution(n, k, rs_input, rs_nonce, rs_soln)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct SaplingVerificationContext {
|
|
||||||
bvk: edwards::Point<Bls12, Unknown>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "system" fn librustzcash_sapling_verification_ctx_init(
|
pub extern "system" fn librustzcash_sapling_verification_ctx_init(
|
||||||
) -> *mut SaplingVerificationContext {
|
) -> *mut SaplingVerificationContext {
|
||||||
let ctx = Box::new(SaplingVerificationContext {
|
let ctx = Box::new(SaplingVerificationContext::new());
|
||||||
bvk: edwards::Point::zero(),
|
|
||||||
});
|
|
||||||
|
|
||||||
Box::into_raw(ctx)
|
Box::into_raw(ctx)
|
||||||
}
|
}
|
||||||
@@ -690,19 +684,6 @@ pub extern "system" fn librustzcash_sapling_check_spend(
|
|||||||
Err(_) => return false,
|
Err(_) => return false,
|
||||||
};
|
};
|
||||||
|
|
||||||
if is_small_order(&cv) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Accumulate the value commitment in the context
|
|
||||||
{
|
|
||||||
let mut tmp = cv.clone();
|
|
||||||
tmp = tmp.add(&unsafe { &*ctx }.bvk, &JUBJUB);
|
|
||||||
|
|
||||||
// Update the context
|
|
||||||
unsafe { &mut *ctx }.bvk = tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deserialize the anchor, which should be an element
|
// Deserialize the anchor, which should be an element
|
||||||
// of Fr.
|
// of Fr.
|
||||||
let anchor = match Fr::from_repr(read_le(&(unsafe { &*anchor })[..])) {
|
let anchor = match Fr::from_repr(read_le(&(unsafe { &*anchor })[..])) {
|
||||||
@@ -710,83 +691,35 @@ pub extern "system" fn librustzcash_sapling_check_spend(
|
|||||||
Err(_) => return false,
|
Err(_) => return false,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Grab the nullifier as a sequence of bytes
|
|
||||||
let nullifier = &unsafe { &*nullifier }[..];
|
|
||||||
|
|
||||||
// Compute the signature's message for rk/spend_auth_sig
|
|
||||||
let mut data_to_be_signed = [0u8; 64];
|
|
||||||
(&mut data_to_be_signed[0..32]).copy_from_slice(&(unsafe { &*rk })[..]);
|
|
||||||
(&mut data_to_be_signed[32..64]).copy_from_slice(&(unsafe { &*sighash_value })[..]);
|
|
||||||
|
|
||||||
// Deserialize rk
|
// Deserialize rk
|
||||||
let rk = match redjubjub::PublicKey::<Bls12>::read(&(unsafe { &*rk })[..], &JUBJUB) {
|
let rk = match redjubjub::PublicKey::<Bls12>::read(&(unsafe { &*rk })[..], &JUBJUB) {
|
||||||
Ok(p) => p,
|
Ok(p) => p,
|
||||||
Err(_) => return false,
|
Err(_) => return false,
|
||||||
};
|
};
|
||||||
|
|
||||||
if is_small_order(&rk.0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deserialize the signature
|
// Deserialize the signature
|
||||||
let spend_auth_sig = match Signature::read(&(unsafe { &*spend_auth_sig })[..]) {
|
let spend_auth_sig = match Signature::read(&(unsafe { &*spend_auth_sig })[..]) {
|
||||||
Ok(sig) => sig,
|
Ok(sig) => sig,
|
||||||
Err(_) => return false,
|
Err(_) => return false,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Verify the spend_auth_sig
|
|
||||||
if !rk.verify(
|
|
||||||
&data_to_be_signed,
|
|
||||||
&spend_auth_sig,
|
|
||||||
FixedGenerators::SpendingKeyGenerator,
|
|
||||||
&JUBJUB,
|
|
||||||
) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Construct public input for circuit
|
|
||||||
let mut public_input = [Fr::zero(); 7];
|
|
||||||
{
|
|
||||||
let (x, y) = rk.0.into_xy();
|
|
||||||
public_input[0] = x;
|
|
||||||
public_input[1] = y;
|
|
||||||
}
|
|
||||||
{
|
|
||||||
let (x, y) = cv.into_xy();
|
|
||||||
public_input[2] = x;
|
|
||||||
public_input[3] = y;
|
|
||||||
}
|
|
||||||
public_input[4] = anchor;
|
|
||||||
|
|
||||||
// Add the nullifier through multiscalar packing
|
|
||||||
{
|
|
||||||
let nullifier = multipack::bytes_to_bits_le(nullifier);
|
|
||||||
let nullifier = multipack::compute_multipacking::<Bls12>(&nullifier);
|
|
||||||
|
|
||||||
assert_eq!(nullifier.len(), 2);
|
|
||||||
|
|
||||||
public_input[5] = nullifier[0];
|
|
||||||
public_input[6] = nullifier[1];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deserialize the proof
|
// Deserialize the proof
|
||||||
let zkproof = match Proof::<Bls12>::read(&(unsafe { &*zkproof })[..]) {
|
let zkproof = match Proof::<Bls12>::read(&(unsafe { &*zkproof })[..]) {
|
||||||
Ok(p) => p,
|
Ok(p) => p,
|
||||||
Err(_) => return false,
|
Err(_) => return false,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Verify the proof
|
unsafe { &mut *ctx }.check_spend(
|
||||||
match verify_proof(
|
cv,
|
||||||
|
anchor,
|
||||||
|
unsafe { &*nullifier },
|
||||||
|
rk,
|
||||||
|
unsafe { &*sighash_value },
|
||||||
|
spend_auth_sig,
|
||||||
|
zkproof,
|
||||||
unsafe { SAPLING_SPEND_VK.as_ref() }.unwrap(),
|
unsafe { SAPLING_SPEND_VK.as_ref() }.unwrap(),
|
||||||
&zkproof,
|
&JUBJUB,
|
||||||
&public_input[..],
|
)
|
||||||
) {
|
|
||||||
// No error, and proof verification successful
|
|
||||||
Ok(true) => true,
|
|
||||||
|
|
||||||
// Any other case
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
@@ -803,20 +736,6 @@ pub extern "system" fn librustzcash_sapling_check_output(
|
|||||||
Err(_) => return false,
|
Err(_) => return false,
|
||||||
};
|
};
|
||||||
|
|
||||||
if is_small_order(&cv) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Accumulate the value commitment in the context
|
|
||||||
{
|
|
||||||
let mut tmp = cv.clone();
|
|
||||||
tmp = tmp.negate(); // Outputs subtract from the total.
|
|
||||||
tmp = tmp.add(&unsafe { &*ctx }.bvk, &JUBJUB);
|
|
||||||
|
|
||||||
// Update the context
|
|
||||||
unsafe { &mut *ctx }.bvk = tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deserialize the commitment, which should be an element
|
// Deserialize the commitment, which should be an element
|
||||||
// of Fr.
|
// of Fr.
|
||||||
let cm = match Fr::from_repr(read_le(&(unsafe { &*cm })[..])) {
|
let cm = match Fr::from_repr(read_le(&(unsafe { &*cm })[..])) {
|
||||||
@@ -830,68 +749,20 @@ pub extern "system" fn librustzcash_sapling_check_output(
|
|||||||
Err(_) => return false,
|
Err(_) => return false,
|
||||||
};
|
};
|
||||||
|
|
||||||
if is_small_order(&epk) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Construct public input for circuit
|
|
||||||
let mut public_input = [Fr::zero(); 5];
|
|
||||||
{
|
|
||||||
let (x, y) = cv.into_xy();
|
|
||||||
public_input[0] = x;
|
|
||||||
public_input[1] = y;
|
|
||||||
}
|
|
||||||
{
|
|
||||||
let (x, y) = epk.into_xy();
|
|
||||||
public_input[2] = x;
|
|
||||||
public_input[3] = y;
|
|
||||||
}
|
|
||||||
public_input[4] = cm;
|
|
||||||
|
|
||||||
// Deserialize the proof
|
// Deserialize the proof
|
||||||
let zkproof = match Proof::<Bls12>::read(&(unsafe { &*zkproof })[..]) {
|
let zkproof = match Proof::<Bls12>::read(&(unsafe { &*zkproof })[..]) {
|
||||||
Ok(p) => p,
|
Ok(p) => p,
|
||||||
Err(_) => return false,
|
Err(_) => return false,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Verify the proof
|
unsafe { &mut *ctx }.check_output(
|
||||||
match verify_proof(
|
cv,
|
||||||
|
cm,
|
||||||
|
epk,
|
||||||
|
zkproof,
|
||||||
unsafe { SAPLING_OUTPUT_VK.as_ref() }.unwrap(),
|
unsafe { SAPLING_OUTPUT_VK.as_ref() }.unwrap(),
|
||||||
&zkproof,
|
&JUBJUB,
|
||||||
&public_input[..],
|
)
|
||||||
) {
|
|
||||||
// No error, and proof verification successful
|
|
||||||
Ok(true) => true,
|
|
||||||
|
|
||||||
// Any other case
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// This function computes `value` in the exponent of the value commitment base
|
|
||||||
fn compute_value_balance(value: int64_t) -> Option<edwards::Point<Bls12, Unknown>> {
|
|
||||||
// Compute the absolute value (failing if -i64::MAX is
|
|
||||||
// the value)
|
|
||||||
let abs = match value.checked_abs() {
|
|
||||||
Some(a) => a as u64,
|
|
||||||
None => return None,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Is it negative? We'll have to negate later if so.
|
|
||||||
let is_negative = value.is_negative();
|
|
||||||
|
|
||||||
// Compute it in the exponent
|
|
||||||
let mut value_balance = JUBJUB
|
|
||||||
.generator(FixedGenerators::ValueCommitmentValue)
|
|
||||||
.mul(FsRepr::from(abs), &JUBJUB);
|
|
||||||
|
|
||||||
// Negate if necessary
|
|
||||||
if is_negative {
|
|
||||||
value_balance = value_balance.negate();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert to unknown order point
|
|
||||||
Some(value_balance.into())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
@@ -901,43 +772,18 @@ pub extern "system" fn librustzcash_sapling_final_check(
|
|||||||
binding_sig: *const [c_uchar; 64],
|
binding_sig: *const [c_uchar; 64],
|
||||||
sighash_value: *const [c_uchar; 32],
|
sighash_value: *const [c_uchar; 32],
|
||||||
) -> bool {
|
) -> bool {
|
||||||
// Obtain current bvk from the context
|
|
||||||
let mut bvk = redjubjub::PublicKey(unsafe { &*ctx }.bvk.clone());
|
|
||||||
|
|
||||||
// Compute value balance
|
|
||||||
let mut value_balance = match compute_value_balance(value_balance) {
|
|
||||||
Some(a) => a,
|
|
||||||
None => return false,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Subtract value_balance from current bvk to get final bvk
|
|
||||||
value_balance = value_balance.negate();
|
|
||||||
bvk.0 = bvk.0.add(&value_balance, &JUBJUB);
|
|
||||||
|
|
||||||
// Compute the signature's message for bvk/binding_sig
|
|
||||||
let mut data_to_be_signed = [0u8; 64];
|
|
||||||
bvk.0
|
|
||||||
.write(&mut data_to_be_signed[0..32])
|
|
||||||
.expect("bvk is 32 bytes");
|
|
||||||
(&mut data_to_be_signed[32..64]).copy_from_slice(&(unsafe { &*sighash_value })[..]);
|
|
||||||
|
|
||||||
// Deserialize the signature
|
// Deserialize the signature
|
||||||
let binding_sig = match Signature::read(&(unsafe { &*binding_sig })[..]) {
|
let binding_sig = match Signature::read(&(unsafe { &*binding_sig })[..]) {
|
||||||
Ok(sig) => sig,
|
Ok(sig) => sig,
|
||||||
Err(_) => return false,
|
Err(_) => return false,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Verify the binding_sig
|
unsafe { &*ctx }.final_check(
|
||||||
if !bvk.verify(
|
value_balance,
|
||||||
&data_to_be_signed,
|
unsafe { &*sighash_value },
|
||||||
&binding_sig,
|
binding_sig,
|
||||||
FixedGenerators::ValueCommitmentRandomness,
|
|
||||||
&JUBJUB,
|
&JUBJUB,
|
||||||
) {
|
)
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
@@ -1069,7 +915,8 @@ pub extern "system" fn librustzcash_sprout_prove(
|
|||||||
unsafe { &SPROUT_GROTH16_PARAMS_PATH }
|
unsafe { &SPROUT_GROTH16_PARAMS_PATH }
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.expect("parameters should have been initialized"),
|
.expect("parameters should have been initialized"),
|
||||||
).expect("couldn't load Sprout groth16 parameters file");
|
)
|
||||||
|
.expect("couldn't load Sprout groth16 parameters file");
|
||||||
|
|
||||||
let mut sprout_fs = BufReader::with_capacity(1024 * 1024, sprout_fs);
|
let mut sprout_fs = BufReader::with_capacity(1024 * 1024, sprout_fs);
|
||||||
|
|
||||||
@@ -1137,11 +984,6 @@ pub extern "system" fn librustzcash_sprout_verify(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct SaplingProvingContext {
|
|
||||||
bsk: Fs,
|
|
||||||
bvk: edwards::Point<Bls12, Unknown>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "system" fn librustzcash_sapling_output_proof(
|
pub extern "system" fn librustzcash_sapling_output_proof(
|
||||||
ctx: *mut SaplingProvingContext,
|
ctx: *mut SaplingProvingContext,
|
||||||
@@ -1180,70 +1022,27 @@ pub extern "system" fn librustzcash_sapling_output_proof(
|
|||||||
diversifier: diversifier,
|
diversifier: diversifier,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Initialize secure RNG
|
|
||||||
let mut rng = OsRng::new().expect("should be able to construct RNG");
|
|
||||||
|
|
||||||
// The caller provides the commitment randomness for the output note
|
// 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,
|
Ok(p) => p,
|
||||||
Err(_) => return false,
|
Err(_) => return false,
|
||||||
};
|
};
|
||||||
|
|
||||||
// We construct ephemeral randomness for the value commitment. This
|
|
||||||
// randomness is not given back to the caller, but the synthetic
|
|
||||||
// blinding factor `bsk` is accumulated in the context.
|
|
||||||
let rcv = Fs::rand(&mut rng);
|
|
||||||
|
|
||||||
// Accumulate the value commitment randomness in the context
|
|
||||||
{
|
|
||||||
let mut tmp = rcv.clone();
|
|
||||||
tmp.negate(); // Outputs subtract from the total.
|
|
||||||
tmp.add_assign(&unsafe { &*ctx }.bsk);
|
|
||||||
|
|
||||||
// Update the context
|
|
||||||
unsafe { &mut *ctx }.bsk = tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Construct the value commitment for the proof instance
|
|
||||||
let value_commitment = sapling_crypto::primitives::ValueCommitment::<Bls12> {
|
|
||||||
value: value,
|
|
||||||
randomness: rcv,
|
|
||||||
};
|
|
||||||
|
|
||||||
// We now have a full witness for the output proof.
|
|
||||||
let instance = sapling_crypto::circuit::sapling::Output {
|
|
||||||
params: &*JUBJUB,
|
|
||||||
value_commitment: Some(value_commitment.clone()),
|
|
||||||
payment_address: Some(payment_address.clone()),
|
|
||||||
commitment_randomness: Some(rcm),
|
|
||||||
esk: Some(esk.clone()),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Create proof
|
// Create proof
|
||||||
let proof = create_random_proof(
|
let (proof, value_commitment) = unsafe { &mut *ctx }.output_proof(
|
||||||
instance,
|
esk,
|
||||||
|
payment_address,
|
||||||
|
rcm,
|
||||||
|
value,
|
||||||
unsafe { SAPLING_OUTPUT_PARAMS.as_ref() }.unwrap(),
|
unsafe { SAPLING_OUTPUT_PARAMS.as_ref() }.unwrap(),
|
||||||
&mut rng,
|
&JUBJUB,
|
||||||
).expect("proving should not fail");
|
);
|
||||||
|
|
||||||
// Write the proof out to the caller
|
// Write the proof out to the caller
|
||||||
proof
|
proof
|
||||||
.write(&mut (unsafe { &mut *zkproof })[..])
|
.write(&mut (unsafe { &mut *zkproof })[..])
|
||||||
.expect("should be able to serialize a proof");
|
.expect("should be able to serialize a proof");
|
||||||
|
|
||||||
// Compute the value commitment
|
|
||||||
let value_commitment: edwards::Point<Bls12, Unknown> = value_commitment.cm(&JUBJUB).into();
|
|
||||||
|
|
||||||
// Accumulate the value commitment in the context. We do this to check internal consistency.
|
|
||||||
{
|
|
||||||
let mut tmp = value_commitment.clone();
|
|
||||||
tmp = tmp.negate(); // Outputs subtract from the total.
|
|
||||||
tmp = tmp.add(&unsafe { &*ctx }.bvk, &JUBJUB);
|
|
||||||
|
|
||||||
// Update the context
|
|
||||||
unsafe { &mut *ctx }.bvk = tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write the value commitment to the caller
|
// Write the value commitment to the caller
|
||||||
value_commitment
|
value_commitment
|
||||||
.write(&mut (unsafe { &mut *cv })[..])
|
.write(&mut (unsafe { &mut *cv })[..])
|
||||||
@@ -1280,8 +1079,7 @@ pub extern "system" fn librustzcash_sapling_spend_sig(
|
|||||||
|
|
||||||
// Compute the signature's message for rk/spend_auth_sig
|
// Compute the signature's message for rk/spend_auth_sig
|
||||||
let mut data_to_be_signed = [0u8; 64];
|
let mut data_to_be_signed = [0u8; 64];
|
||||||
rk.0
|
rk.0.write(&mut data_to_be_signed[0..32])
|
||||||
.write(&mut data_to_be_signed[0..32])
|
|
||||||
.expect("message buffer should be 32 bytes");
|
.expect("message buffer should be 32 bytes");
|
||||||
(&mut data_to_be_signed[32..64]).copy_from_slice(&(unsafe { &*sighash })[..]);
|
(&mut data_to_be_signed[32..64]).copy_from_slice(&(unsafe { &*sighash })[..]);
|
||||||
|
|
||||||
@@ -1308,52 +1106,11 @@ pub extern "system" fn librustzcash_sapling_binding_sig(
|
|||||||
sighash: *const [c_uchar; 32],
|
sighash: *const [c_uchar; 32],
|
||||||
result: *mut [c_uchar; 64],
|
result: *mut [c_uchar; 64],
|
||||||
) -> bool {
|
) -> bool {
|
||||||
// Grab the current `bsk` from the context
|
|
||||||
let bsk = redjubjub::PrivateKey::<Bls12>(unsafe { &*ctx }.bsk);
|
|
||||||
|
|
||||||
// Grab the `bvk` using DerivePublic.
|
|
||||||
let bvk = redjubjub::PublicKey::from_private(
|
|
||||||
&bsk,
|
|
||||||
FixedGenerators::ValueCommitmentRandomness,
|
|
||||||
&JUBJUB,
|
|
||||||
);
|
|
||||||
|
|
||||||
// In order to check internal consistency, let's use the accumulated value
|
|
||||||
// commitments (as the verifier would) and apply valuebalance to compare
|
|
||||||
// against our derived bvk.
|
|
||||||
{
|
|
||||||
// Compute value balance
|
|
||||||
let mut value_balance = match compute_value_balance(value_balance) {
|
|
||||||
Some(a) => a,
|
|
||||||
None => return false,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Subtract value_balance from current bvk to get final bvk
|
|
||||||
value_balance = value_balance.negate();
|
|
||||||
let mut tmp = unsafe { &*ctx }.bvk.clone();
|
|
||||||
tmp = tmp.add(&value_balance, &JUBJUB);
|
|
||||||
|
|
||||||
// The result should be the same, unless the provided valueBalance is wrong.
|
|
||||||
if bvk.0 != tmp {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Construct signature message
|
|
||||||
let mut data_to_be_signed = [0u8; 64];
|
|
||||||
bvk.0
|
|
||||||
.write(&mut data_to_be_signed[0..32])
|
|
||||||
.expect("message buffer should be 32 bytes");
|
|
||||||
(&mut data_to_be_signed[32..64]).copy_from_slice(&(unsafe { &*sighash })[..]);
|
|
||||||
|
|
||||||
// Sign
|
// Sign
|
||||||
let mut rng = OsRng::new().expect("should be able to construct RNG");
|
let sig = match unsafe { &*ctx }.binding_sig(value_balance, unsafe { &*sighash }, &JUBJUB) {
|
||||||
let sig = bsk.sign(
|
Ok(s) => s,
|
||||||
&data_to_be_signed,
|
Err(_) => return false,
|
||||||
&mut rng,
|
};
|
||||||
FixedGenerators::ValueCommitmentRandomness,
|
|
||||||
&JUBJUB,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Write out signature
|
// Write out signature
|
||||||
sig.write(&mut (unsafe { &mut *result })[..])
|
sig.write(&mut (unsafe { &mut *result })[..])
|
||||||
@@ -1377,26 +1134,6 @@ pub extern "system" fn librustzcash_sapling_spend_proof(
|
|||||||
rk_out: *mut [c_uchar; 32],
|
rk_out: *mut [c_uchar; 32],
|
||||||
zkproof: *mut [c_uchar; GROTH_PROOF_SIZE],
|
zkproof: *mut [c_uchar; GROTH_PROOF_SIZE],
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let mut rng = OsRng::new().expect("should be able to construct RNG");
|
|
||||||
|
|
||||||
// We create the randomness of the value commitment
|
|
||||||
let rcv = Fs::rand(&mut rng);
|
|
||||||
|
|
||||||
// Accumulate the value commitment randomness in the context
|
|
||||||
{
|
|
||||||
let mut tmp = rcv.clone();
|
|
||||||
tmp.add_assign(&unsafe { &*ctx }.bsk);
|
|
||||||
|
|
||||||
// Update the context
|
|
||||||
unsafe { &mut *ctx }.bsk = tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Construct the value commitment
|
|
||||||
let value_commitment = ValueCommitment::<Bls12> {
|
|
||||||
value: value,
|
|
||||||
randomness: rcv,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Grab `ak` from the caller, which should be a point.
|
// Grab `ak` from the caller, which should be a point.
|
||||||
let ak = match edwards::Point::<Bls12, Unknown>::read(&(unsafe { &*ak })[..], &JUBJUB) {
|
let ak = match edwards::Point::<Bls12, Unknown>::read(&(unsafe { &*ak })[..], &JUBJUB) {
|
||||||
Ok(p) => p,
|
Ok(p) => p,
|
||||||
@@ -1421,18 +1158,9 @@ pub extern "system" fn librustzcash_sapling_spend_proof(
|
|||||||
nsk,
|
nsk,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Construct the viewing key
|
|
||||||
let viewing_key = proof_generation_key.into_viewing_key(&JUBJUB);
|
|
||||||
|
|
||||||
// Grab the diversifier from the caller
|
// Grab the diversifier from the caller
|
||||||
let diversifier = sapling_crypto::primitives::Diversifier(unsafe { *diversifier });
|
let diversifier = sapling_crypto::primitives::Diversifier(unsafe { *diversifier });
|
||||||
|
|
||||||
// Construct the payment address with the viewing key / diversifier
|
|
||||||
let payment_address = match viewing_key.into_payment_address(diversifier, &JUBJUB) {
|
|
||||||
Some(p) => p,
|
|
||||||
None => return false,
|
|
||||||
};
|
|
||||||
|
|
||||||
// The caller chooses the note randomness
|
// 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,
|
Ok(p) => p,
|
||||||
@@ -1445,17 +1173,6 @@ pub extern "system" fn librustzcash_sapling_spend_proof(
|
|||||||
Err(_) => return false,
|
Err(_) => return false,
|
||||||
};
|
};
|
||||||
|
|
||||||
// This is the result of the re-randomization, we compute it for the caller
|
|
||||||
let rk = redjubjub::PublicKey::<Bls12>(ak.into()).randomize(
|
|
||||||
ar,
|
|
||||||
FixedGenerators::SpendingKeyGenerator,
|
|
||||||
&JUBJUB,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Write out `rk` to the caller
|
|
||||||
rk.write(&mut unsafe { &mut *rk_out }[..])
|
|
||||||
.expect("should be able to write to rk_out");
|
|
||||||
|
|
||||||
// We need to compute the anchor of the Spend.
|
// 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_le(unsafe { &(&*anchor)[..] })) {
|
||||||
Ok(p) => p,
|
Ok(p) => p,
|
||||||
@@ -1464,140 +1181,26 @@ pub extern "system" fn librustzcash_sapling_spend_proof(
|
|||||||
|
|
||||||
// The witness contains the incremental tree witness information, in a
|
// The witness contains the incremental tree witness information, in a
|
||||||
// weird serialized format.
|
// weird serialized format.
|
||||||
let mut witness = unsafe { &(&*witness)[..] };
|
let witness = match CommitmentTreeWitness::from_slice(unsafe { &(&*witness)[..] }) {
|
||||||
|
Ok(w) => w,
|
||||||
// Skip the first byte, which should be "32" to signify the length of
|
Err(_) => return false,
|
||||||
// the following vector of Pedersen hashes.
|
|
||||||
assert_eq!(witness[0], SAPLING_TREE_DEPTH as u8);
|
|
||||||
witness = &witness[1..];
|
|
||||||
|
|
||||||
// Begin to construct the authentication path
|
|
||||||
let mut auth_path = vec![None; SAPLING_TREE_DEPTH];
|
|
||||||
|
|
||||||
// The vector works in reverse
|
|
||||||
for i in (0..SAPLING_TREE_DEPTH).rev() {
|
|
||||||
// skip length of inner vector
|
|
||||||
assert_eq!(witness[0], 32); // the length of a pedersen hash
|
|
||||||
witness = &witness[1..];
|
|
||||||
|
|
||||||
// Grab the sibling node at this depth in the tree
|
|
||||||
let mut sibling = [0u8; 32];
|
|
||||||
sibling.copy_from_slice(&witness[0..32]);
|
|
||||||
witness = &witness[32..];
|
|
||||||
|
|
||||||
// Sibling node should be an element of Fr
|
|
||||||
let sibling = match Fr::from_repr(read_le(&sibling)) {
|
|
||||||
Ok(p) => p,
|
|
||||||
Err(_) => return false,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Set the value in the auth path; we put false here
|
|
||||||
// for now (signifying the position bit) which we'll
|
|
||||||
// fill in later.
|
|
||||||
auth_path[i] = Some((sibling, false));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read the position from the witness
|
|
||||||
let mut position = witness
|
|
||||||
.read_u64::<LittleEndian>()
|
|
||||||
.expect("should have had index at the end");
|
|
||||||
|
|
||||||
// Let's compute the nullifier while we have the position
|
|
||||||
let note = sapling_crypto::primitives::Note {
|
|
||||||
value: value,
|
|
||||||
g_d: diversifier
|
|
||||||
.g_d::<Bls12>(&JUBJUB)
|
|
||||||
.expect("was a valid diversifier before"),
|
|
||||||
pk_d: payment_address.pk_d.clone(),
|
|
||||||
r: rcm,
|
|
||||||
};
|
|
||||||
|
|
||||||
let nullifier = note.nf(&viewing_key, position, &JUBJUB);
|
|
||||||
|
|
||||||
// Given the position, let's finish constructing the authentication
|
|
||||||
// path
|
|
||||||
for i in 0..SAPLING_TREE_DEPTH {
|
|
||||||
auth_path[i].as_mut().map(|p| p.1 = (position & 1) == 1);
|
|
||||||
|
|
||||||
position >>= 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The witness should be empty now; if it wasn't, the caller would
|
|
||||||
// have provided more information than they should have, indicating
|
|
||||||
// a bug downstream
|
|
||||||
assert_eq!(witness.len(), 0);
|
|
||||||
|
|
||||||
// We now have the full witness for our circuit
|
|
||||||
let instance = sapling_crypto::circuit::sapling::Spend {
|
|
||||||
params: &*JUBJUB,
|
|
||||||
value_commitment: Some(value_commitment.clone()),
|
|
||||||
proof_generation_key: Some(proof_generation_key),
|
|
||||||
payment_address: Some(payment_address),
|
|
||||||
commitment_randomness: Some(rcm),
|
|
||||||
ar: Some(ar),
|
|
||||||
auth_path: auth_path,
|
|
||||||
anchor: Some(anchor),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create proof
|
// Create proof
|
||||||
let proof = create_random_proof(
|
let (proof, value_commitment, rk) = unsafe { &mut *ctx }
|
||||||
instance,
|
.spend_proof(
|
||||||
unsafe { SAPLING_SPEND_PARAMS.as_ref() }.unwrap(),
|
proof_generation_key,
|
||||||
&mut rng,
|
diversifier,
|
||||||
).expect("proving should not fail");
|
rcm,
|
||||||
|
ar,
|
||||||
// Try to verify the proof:
|
value,
|
||||||
// Construct public input for circuit
|
anchor,
|
||||||
let mut public_input = [Fr::zero(); 7];
|
witness,
|
||||||
{
|
unsafe { SAPLING_SPEND_PARAMS.as_ref() }.unwrap(),
|
||||||
let (x, y) = rk.0.into_xy();
|
unsafe { SAPLING_SPEND_VK.as_ref() }.unwrap(),
|
||||||
public_input[0] = x;
|
&JUBJUB,
|
||||||
public_input[1] = y;
|
)
|
||||||
}
|
.expect("proving should not fail");
|
||||||
{
|
|
||||||
let (x, y) = value_commitment.cm(&JUBJUB).into_xy();
|
|
||||||
public_input[2] = x;
|
|
||||||
public_input[3] = y;
|
|
||||||
}
|
|
||||||
public_input[4] = anchor;
|
|
||||||
|
|
||||||
// Add the nullifier through multiscalar packing
|
|
||||||
{
|
|
||||||
let nullifier = multipack::bytes_to_bits_le(&nullifier);
|
|
||||||
let nullifier = multipack::compute_multipacking::<Bls12>(&nullifier);
|
|
||||||
|
|
||||||
assert_eq!(nullifier.len(), 2);
|
|
||||||
|
|
||||||
public_input[5] = nullifier[0];
|
|
||||||
public_input[6] = nullifier[1];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify the proof
|
|
||||||
match verify_proof(
|
|
||||||
unsafe { SAPLING_SPEND_VK.as_ref() }.unwrap(),
|
|
||||||
&proof,
|
|
||||||
&public_input[..],
|
|
||||||
) {
|
|
||||||
// No error, and proof verification successful
|
|
||||||
Ok(true) => {}
|
|
||||||
|
|
||||||
// Any other case
|
|
||||||
_ => {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compute value commitment
|
|
||||||
let value_commitment: edwards::Point<Bls12, Unknown> = value_commitment.cm(&JUBJUB).into();
|
|
||||||
|
|
||||||
// Accumulate the value commitment in the context
|
|
||||||
{
|
|
||||||
let mut tmp = value_commitment.clone();
|
|
||||||
tmp = tmp.add(&unsafe { &*ctx }.bvk, &JUBJUB);
|
|
||||||
|
|
||||||
// Update the context
|
|
||||||
unsafe { &mut *ctx }.bvk = tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write value commitment to caller
|
// Write value commitment to caller
|
||||||
value_commitment
|
value_commitment
|
||||||
@@ -1609,15 +1212,16 @@ pub extern "system" fn librustzcash_sapling_spend_proof(
|
|||||||
.write(&mut (unsafe { &mut *zkproof })[..])
|
.write(&mut (unsafe { &mut *zkproof })[..])
|
||||||
.expect("should be able to serialize a proof");
|
.expect("should be able to serialize a proof");
|
||||||
|
|
||||||
|
// Write out `rk` to the caller
|
||||||
|
rk.write(&mut unsafe { &mut *rk_out }[..])
|
||||||
|
.expect("should be able to write to rk_out");
|
||||||
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "system" fn librustzcash_sapling_proving_ctx_init() -> *mut SaplingProvingContext {
|
pub extern "system" fn librustzcash_sapling_proving_ctx_init() -> *mut SaplingProvingContext {
|
||||||
let ctx = Box::new(SaplingProvingContext {
|
let ctx = Box::new(SaplingProvingContext::new());
|
||||||
bsk: Fs::zero(),
|
|
||||||
bvk: edwards::Point::zero(),
|
|
||||||
});
|
|
||||||
|
|
||||||
Box::into_raw(ctx)
|
Box::into_raw(ctx)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,6 +31,8 @@ use super::blake2s;
|
|||||||
use super::num;
|
use super::num;
|
||||||
use super::multipack;
|
use super::multipack;
|
||||||
|
|
||||||
|
pub const TREE_DEPTH: usize = 32;
|
||||||
|
|
||||||
/// This is an instance of the `Spend` circuit.
|
/// This is an instance of the `Spend` circuit.
|
||||||
pub struct Spend<'a, E: JubjubEngine> {
|
pub struct Spend<'a, E: JubjubEngine> {
|
||||||
pub params: &'a E::Params,
|
pub params: &'a E::Params,
|
||||||
|
|||||||
@@ -109,11 +109,12 @@ fn single_output_hash(tx_out: &TxOut) -> Vec<u8> {
|
|||||||
|
|
||||||
fn joinsplits_hash(tx: &TransactionData) -> Vec<u8> {
|
fn joinsplits_hash(tx: &TransactionData) -> Vec<u8> {
|
||||||
let mut data = Vec::with_capacity(
|
let mut data = Vec::with_capacity(
|
||||||
tx.joinsplits.len() * if tx.version < SAPLING_TX_VERSION {
|
tx.joinsplits.len()
|
||||||
1802 // JSDescription with PHGR13 proof
|
* if tx.version < SAPLING_TX_VERSION {
|
||||||
} else {
|
1802 // JSDescription with PHGR13 proof
|
||||||
1698 // JSDescription with Groth16 proof
|
} else {
|
||||||
},
|
1698 // JSDescription with Groth16 proof
|
||||||
|
},
|
||||||
);
|
);
|
||||||
for js in &tx.joinsplits {
|
for js in &tx.joinsplits {
|
||||||
js.write(&mut data).unwrap();
|
js.write(&mut data).unwrap();
|
||||||
|
|||||||
@@ -6,3 +6,8 @@ authors = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
bellman = { path = "../bellman" }
|
||||||
|
byteorder = "1"
|
||||||
|
pairing = { path = "../pairing" }
|
||||||
|
rand = "0.4"
|
||||||
|
sapling-crypto = { path = "../sapling-crypto" }
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#[cfg(test)]
|
extern crate bellman;
|
||||||
mod tests {
|
extern crate byteorder;
|
||||||
#[test]
|
extern crate pairing;
|
||||||
fn it_works() {
|
extern crate rand;
|
||||||
assert_eq!(2 + 2, 4);
|
extern crate sapling_crypto;
|
||||||
}
|
|
||||||
}
|
pub mod sapling;
|
||||||
|
|||||||
39
zcash_proofs/src/sapling/mod.rs
Normal file
39
zcash_proofs/src/sapling/mod.rs
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
use pairing::bls12_381::Bls12;
|
||||||
|
use sapling_crypto::jubjub::{
|
||||||
|
edwards, fs::FsRepr, FixedGenerators, JubjubBls12, JubjubParams, Unknown,
|
||||||
|
};
|
||||||
|
|
||||||
|
mod prover;
|
||||||
|
mod verifier;
|
||||||
|
|
||||||
|
pub use self::prover::{CommitmentTreeWitness, SaplingProvingContext};
|
||||||
|
pub use self::verifier::SaplingVerificationContext;
|
||||||
|
|
||||||
|
// This function computes `value` in the exponent of the value commitment base
|
||||||
|
fn compute_value_balance(
|
||||||
|
value: i64,
|
||||||
|
params: &JubjubBls12,
|
||||||
|
) -> Option<edwards::Point<Bls12, Unknown>> {
|
||||||
|
// Compute the absolute value (failing if -i64::MAX is
|
||||||
|
// the value)
|
||||||
|
let abs = match value.checked_abs() {
|
||||||
|
Some(a) => a as u64,
|
||||||
|
None => return None,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Is it negative? We'll have to negate later if so.
|
||||||
|
let is_negative = value.is_negative();
|
||||||
|
|
||||||
|
// Compute it in the exponent
|
||||||
|
let mut value_balance = params
|
||||||
|
.generator(FixedGenerators::ValueCommitmentValue)
|
||||||
|
.mul(FsRepr::from(abs), params);
|
||||||
|
|
||||||
|
// Negate if necessary
|
||||||
|
if is_negative {
|
||||||
|
value_balance = value_balance.negate();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert to unknown order point
|
||||||
|
Some(value_balance.into())
|
||||||
|
}
|
||||||
365
zcash_proofs/src/sapling/prover.rs
Normal file
365
zcash_proofs/src/sapling/prover.rs
Normal file
@@ -0,0 +1,365 @@
|
|||||||
|
use bellman::groth16::{
|
||||||
|
create_random_proof, verify_proof, Parameters, PreparedVerifyingKey, Proof,
|
||||||
|
};
|
||||||
|
use byteorder::{LittleEndian, ReadBytesExt};
|
||||||
|
use pairing::{
|
||||||
|
bls12_381::{Bls12, Fr, FrRepr},
|
||||||
|
Field, PrimeField, PrimeFieldRepr,
|
||||||
|
};
|
||||||
|
use rand::{OsRng, Rand};
|
||||||
|
use sapling_crypto::{
|
||||||
|
circuit::{
|
||||||
|
multipack,
|
||||||
|
sapling::{Output, Spend, TREE_DEPTH},
|
||||||
|
},
|
||||||
|
jubjub::{edwards, fs::Fs, FixedGenerators, JubjubBls12, Unknown},
|
||||||
|
primitives::{Diversifier, Note, PaymentAddress, ProofGenerationKey, ValueCommitment},
|
||||||
|
redjubjub::{PrivateKey, PublicKey, Signature},
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::compute_value_balance;
|
||||||
|
|
||||||
|
/// A witness to a path from a postion in a particular Sapling commitment tree
|
||||||
|
/// to the root of that tree.
|
||||||
|
pub struct CommitmentTreeWitness {
|
||||||
|
auth_path: Vec<Option<(Fr, bool)>>,
|
||||||
|
position: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CommitmentTreeWitness {
|
||||||
|
pub fn from_slice(mut witness: &[u8]) -> Result<Self, ()> {
|
||||||
|
// Skip the first byte, which should be "32" to signify the length of
|
||||||
|
// the following vector of Pedersen hashes.
|
||||||
|
assert_eq!(witness[0], TREE_DEPTH as u8);
|
||||||
|
witness = &witness[1..];
|
||||||
|
|
||||||
|
// Begin to construct the authentication path
|
||||||
|
let mut auth_path = vec![None; TREE_DEPTH];
|
||||||
|
|
||||||
|
// The vector works in reverse
|
||||||
|
for i in (0..TREE_DEPTH).rev() {
|
||||||
|
// skip length of inner vector
|
||||||
|
assert_eq!(witness[0], 32); // the length of a pedersen hash
|
||||||
|
witness = &witness[1..];
|
||||||
|
|
||||||
|
// Grab the sibling node at this depth in the tree
|
||||||
|
let mut sibling = [0u8; 32];
|
||||||
|
sibling.copy_from_slice(&witness[0..32]);
|
||||||
|
witness = &witness[32..];
|
||||||
|
|
||||||
|
// Sibling node should be an element of Fr
|
||||||
|
let sibling = match {
|
||||||
|
let mut repr = FrRepr::default();
|
||||||
|
repr.read_le(&sibling[..]).expect("length is 32 bytes");
|
||||||
|
Fr::from_repr(repr)
|
||||||
|
} {
|
||||||
|
Ok(p) => p,
|
||||||
|
Err(_) => return Err(()),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Set the value in the auth path; we put false here
|
||||||
|
// for now (signifying the position bit) which we'll
|
||||||
|
// fill in later.
|
||||||
|
auth_path[i] = Some((sibling, false));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the position from the witness
|
||||||
|
let position = witness
|
||||||
|
.read_u64::<LittleEndian>()
|
||||||
|
.expect("should have had index at the end");
|
||||||
|
|
||||||
|
// Given the position, let's finish constructing the authentication
|
||||||
|
// path
|
||||||
|
let mut tmp = position;
|
||||||
|
for i in 0..TREE_DEPTH {
|
||||||
|
auth_path[i].as_mut().map(|p| p.1 = (tmp & 1) == 1);
|
||||||
|
|
||||||
|
tmp >>= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The witness should be empty now; if it wasn't, the caller would
|
||||||
|
// have provided more information than they should have, indicating
|
||||||
|
// a bug downstream
|
||||||
|
assert_eq!(witness.len(), 0);
|
||||||
|
|
||||||
|
Ok(CommitmentTreeWitness {
|
||||||
|
auth_path,
|
||||||
|
position,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A context object for creating the Sapling components of a Zcash transaction.
|
||||||
|
pub struct SaplingProvingContext {
|
||||||
|
bsk: Fs,
|
||||||
|
bvk: edwards::Point<Bls12, Unknown>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SaplingProvingContext {
|
||||||
|
/// Construct a new context to be used with a single transaction.
|
||||||
|
pub fn new() -> Self {
|
||||||
|
SaplingProvingContext {
|
||||||
|
bsk: Fs::zero(),
|
||||||
|
bvk: edwards::Point::zero(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create the value commitment, re-randomized key, and proof for a Sapling
|
||||||
|
/// SpendDescription, while accumulating its value commitment randomness
|
||||||
|
/// inside the context for later use.
|
||||||
|
pub fn spend_proof(
|
||||||
|
&mut self,
|
||||||
|
proof_generation_key: ProofGenerationKey<Bls12>,
|
||||||
|
diversifier: Diversifier,
|
||||||
|
rcm: Fs,
|
||||||
|
ar: Fs,
|
||||||
|
value: u64,
|
||||||
|
anchor: Fr,
|
||||||
|
witness: CommitmentTreeWitness,
|
||||||
|
proving_key: &Parameters<Bls12>,
|
||||||
|
verifying_key: &PreparedVerifyingKey<Bls12>,
|
||||||
|
params: &JubjubBls12,
|
||||||
|
) -> Result<
|
||||||
|
(
|
||||||
|
Proof<Bls12>,
|
||||||
|
edwards::Point<Bls12, Unknown>,
|
||||||
|
PublicKey<Bls12>,
|
||||||
|
),
|
||||||
|
(),
|
||||||
|
> {
|
||||||
|
// Initialize secure RNG
|
||||||
|
let mut rng = OsRng::new().expect("should be able to construct RNG");
|
||||||
|
|
||||||
|
// We create the randomness of the value commitment
|
||||||
|
let rcv = Fs::rand(&mut rng);
|
||||||
|
|
||||||
|
// Accumulate the value commitment randomness in the context
|
||||||
|
{
|
||||||
|
let mut tmp = rcv.clone();
|
||||||
|
tmp.add_assign(&self.bsk);
|
||||||
|
|
||||||
|
// Update the context
|
||||||
|
self.bsk = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Construct the value commitment
|
||||||
|
let value_commitment = ValueCommitment::<Bls12> {
|
||||||
|
value: value,
|
||||||
|
randomness: rcv,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Construct the viewing key
|
||||||
|
let viewing_key = proof_generation_key.into_viewing_key(params);
|
||||||
|
|
||||||
|
// Construct the payment address with the viewing key / diversifier
|
||||||
|
let payment_address = match viewing_key.into_payment_address(diversifier, params) {
|
||||||
|
Some(p) => p,
|
||||||
|
None => return Err(()),
|
||||||
|
};
|
||||||
|
|
||||||
|
// This is the result of the re-randomization, we compute it for the caller
|
||||||
|
let rk = PublicKey::<Bls12>(proof_generation_key.ak.clone().into()).randomize(
|
||||||
|
ar,
|
||||||
|
FixedGenerators::SpendingKeyGenerator,
|
||||||
|
params,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Let's compute the nullifier while we have the position
|
||||||
|
let note = Note {
|
||||||
|
value: value,
|
||||||
|
g_d: diversifier
|
||||||
|
.g_d::<Bls12>(params)
|
||||||
|
.expect("was a valid diversifier before"),
|
||||||
|
pk_d: payment_address.pk_d.clone(),
|
||||||
|
r: rcm,
|
||||||
|
};
|
||||||
|
|
||||||
|
let nullifier = note.nf(&viewing_key, witness.position, params);
|
||||||
|
|
||||||
|
// We now have the full witness for our circuit
|
||||||
|
let instance = Spend {
|
||||||
|
params,
|
||||||
|
value_commitment: Some(value_commitment.clone()),
|
||||||
|
proof_generation_key: Some(proof_generation_key),
|
||||||
|
payment_address: Some(payment_address),
|
||||||
|
commitment_randomness: Some(rcm),
|
||||||
|
ar: Some(ar),
|
||||||
|
auth_path: witness.auth_path,
|
||||||
|
anchor: Some(anchor),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create proof
|
||||||
|
let proof =
|
||||||
|
create_random_proof(instance, proving_key, &mut rng).expect("proving should not fail");
|
||||||
|
|
||||||
|
// Try to verify the proof:
|
||||||
|
// Construct public input for circuit
|
||||||
|
let mut public_input = [Fr::zero(); 7];
|
||||||
|
{
|
||||||
|
let (x, y) = rk.0.into_xy();
|
||||||
|
public_input[0] = x;
|
||||||
|
public_input[1] = y;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
let (x, y) = value_commitment.cm(params).into_xy();
|
||||||
|
public_input[2] = x;
|
||||||
|
public_input[3] = y;
|
||||||
|
}
|
||||||
|
public_input[4] = anchor;
|
||||||
|
|
||||||
|
// Add the nullifier through multiscalar packing
|
||||||
|
{
|
||||||
|
let nullifier = multipack::bytes_to_bits_le(&nullifier);
|
||||||
|
let nullifier = multipack::compute_multipacking::<Bls12>(&nullifier);
|
||||||
|
|
||||||
|
assert_eq!(nullifier.len(), 2);
|
||||||
|
|
||||||
|
public_input[5] = nullifier[0];
|
||||||
|
public_input[6] = nullifier[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify the proof
|
||||||
|
match verify_proof(verifying_key, &proof, &public_input[..]) {
|
||||||
|
// No error, and proof verification successful
|
||||||
|
Ok(true) => {}
|
||||||
|
|
||||||
|
// Any other case
|
||||||
|
_ => {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute value commitment
|
||||||
|
let value_commitment: edwards::Point<Bls12, Unknown> = value_commitment.cm(params).into();
|
||||||
|
|
||||||
|
// Accumulate the value commitment in the context
|
||||||
|
{
|
||||||
|
let mut tmp = value_commitment.clone();
|
||||||
|
tmp = tmp.add(&self.bvk, params);
|
||||||
|
|
||||||
|
// Update the context
|
||||||
|
self.bvk = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok((proof, value_commitment, rk))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create the value commitment and proof for a Sapling OutputDescription,
|
||||||
|
/// while accumulating its value commitment randomness inside the context
|
||||||
|
/// for later use.
|
||||||
|
pub fn output_proof(
|
||||||
|
&mut self,
|
||||||
|
esk: Fs,
|
||||||
|
payment_address: PaymentAddress<Bls12>,
|
||||||
|
rcm: Fs,
|
||||||
|
value: u64,
|
||||||
|
proving_key: &Parameters<Bls12>,
|
||||||
|
params: &JubjubBls12,
|
||||||
|
) -> (Proof<Bls12>, edwards::Point<Bls12, Unknown>) {
|
||||||
|
// Initialize secure RNG
|
||||||
|
let mut rng = OsRng::new().expect("should be able to construct RNG");
|
||||||
|
|
||||||
|
// We construct ephemeral randomness for the value commitment. This
|
||||||
|
// randomness is not given back to the caller, but the synthetic
|
||||||
|
// blinding factor `bsk` is accumulated in the context.
|
||||||
|
let rcv = Fs::rand(&mut rng);
|
||||||
|
|
||||||
|
// Accumulate the value commitment randomness in the context
|
||||||
|
{
|
||||||
|
let mut tmp = rcv.clone();
|
||||||
|
tmp.negate(); // Outputs subtract from the total.
|
||||||
|
tmp.add_assign(&self.bsk);
|
||||||
|
|
||||||
|
// Update the context
|
||||||
|
self.bsk = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Construct the value commitment for the proof instance
|
||||||
|
let value_commitment = ValueCommitment::<Bls12> {
|
||||||
|
value: value,
|
||||||
|
randomness: rcv,
|
||||||
|
};
|
||||||
|
|
||||||
|
// We now have a full witness for the output proof.
|
||||||
|
let instance = Output {
|
||||||
|
params,
|
||||||
|
value_commitment: Some(value_commitment.clone()),
|
||||||
|
payment_address: Some(payment_address.clone()),
|
||||||
|
commitment_randomness: Some(rcm),
|
||||||
|
esk: Some(esk.clone()),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create proof
|
||||||
|
let proof =
|
||||||
|
create_random_proof(instance, proving_key, &mut rng).expect("proving should not fail");
|
||||||
|
|
||||||
|
// Compute the actual value commitment
|
||||||
|
let value_commitment: edwards::Point<Bls12, Unknown> = value_commitment.cm(params).into();
|
||||||
|
|
||||||
|
// Accumulate the value commitment in the context. We do this to check internal consistency.
|
||||||
|
{
|
||||||
|
let mut tmp = value_commitment.clone();
|
||||||
|
tmp = tmp.negate(); // Outputs subtract from the total.
|
||||||
|
tmp = tmp.add(&self.bvk, params);
|
||||||
|
|
||||||
|
// Update the context
|
||||||
|
self.bvk = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
(proof, value_commitment)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create the bindingSig for a Sapling transaction. All calls to spend_proof()
|
||||||
|
/// and output_proof() must be completed before calling this function.
|
||||||
|
pub fn binding_sig(
|
||||||
|
&self,
|
||||||
|
value_balance: i64,
|
||||||
|
sighash: &[u8; 32],
|
||||||
|
params: &JubjubBls12,
|
||||||
|
) -> Result<Signature, ()> {
|
||||||
|
// Initialize secure RNG
|
||||||
|
let mut rng = OsRng::new().expect("should be able to construct RNG");
|
||||||
|
|
||||||
|
// Grab the current `bsk` from the context
|
||||||
|
let bsk = PrivateKey::<Bls12>(self.bsk);
|
||||||
|
|
||||||
|
// Grab the `bvk` using DerivePublic.
|
||||||
|
let bvk = PublicKey::from_private(&bsk, FixedGenerators::ValueCommitmentRandomness, params);
|
||||||
|
|
||||||
|
// In order to check internal consistency, let's use the accumulated value
|
||||||
|
// commitments (as the verifier would) and apply valuebalance to compare
|
||||||
|
// against our derived bvk.
|
||||||
|
{
|
||||||
|
// Compute value balance
|
||||||
|
let mut value_balance = match compute_value_balance(value_balance, params) {
|
||||||
|
Some(a) => a,
|
||||||
|
None => return Err(()),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Subtract value_balance from current bvk to get final bvk
|
||||||
|
value_balance = value_balance.negate();
|
||||||
|
let mut tmp = self.bvk.clone();
|
||||||
|
tmp = tmp.add(&value_balance, params);
|
||||||
|
|
||||||
|
// The result should be the same, unless the provided valueBalance is wrong.
|
||||||
|
if bvk.0 != tmp {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Construct signature message
|
||||||
|
let mut data_to_be_signed = [0u8; 64];
|
||||||
|
bvk.0
|
||||||
|
.write(&mut data_to_be_signed[0..32])
|
||||||
|
.expect("message buffer should be 32 bytes");
|
||||||
|
(&mut data_to_be_signed[32..64]).copy_from_slice(&sighash[..]);
|
||||||
|
|
||||||
|
// Sign
|
||||||
|
Ok(bsk.sign(
|
||||||
|
&data_to_be_signed,
|
||||||
|
&mut rng,
|
||||||
|
FixedGenerators::ValueCommitmentRandomness,
|
||||||
|
params,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
207
zcash_proofs/src/sapling/verifier.rs
Normal file
207
zcash_proofs/src/sapling/verifier.rs
Normal file
@@ -0,0 +1,207 @@
|
|||||||
|
use bellman::groth16::{verify_proof, PreparedVerifyingKey, Proof};
|
||||||
|
use pairing::{
|
||||||
|
bls12_381::{Bls12, Fr},
|
||||||
|
Field,
|
||||||
|
};
|
||||||
|
use sapling_crypto::{
|
||||||
|
circuit::multipack,
|
||||||
|
jubjub::{edwards, FixedGenerators, JubjubBls12, Unknown},
|
||||||
|
redjubjub::{PublicKey, Signature},
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::compute_value_balance;
|
||||||
|
|
||||||
|
fn is_small_order<Order>(p: &edwards::Point<Bls12, Order>, params: &JubjubBls12) -> bool {
|
||||||
|
p.double(params).double(params).double(params) == edwards::Point::zero()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A context object for verifying the Sapling components of a Zcash transaction.
|
||||||
|
pub struct SaplingVerificationContext {
|
||||||
|
bvk: edwards::Point<Bls12, Unknown>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SaplingVerificationContext {
|
||||||
|
/// Construct a new context to be used with a single transaction.
|
||||||
|
pub fn new() -> Self {
|
||||||
|
SaplingVerificationContext {
|
||||||
|
bvk: edwards::Point::zero(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Perform consensus checks on a Sapling SpendDescription, while
|
||||||
|
/// accumulating its value commitment inside the context for later use.
|
||||||
|
pub fn check_spend(
|
||||||
|
&mut self,
|
||||||
|
cv: edwards::Point<Bls12, Unknown>,
|
||||||
|
anchor: Fr,
|
||||||
|
nullifier: &[u8; 32],
|
||||||
|
rk: PublicKey<Bls12>,
|
||||||
|
sighash_value: &[u8; 32],
|
||||||
|
spend_auth_sig: Signature,
|
||||||
|
zkproof: Proof<Bls12>,
|
||||||
|
verifying_key: &PreparedVerifyingKey<Bls12>,
|
||||||
|
params: &JubjubBls12,
|
||||||
|
) -> bool {
|
||||||
|
if is_small_order(&cv, params) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if is_small_order(&rk.0, params) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Accumulate the value commitment in the context
|
||||||
|
{
|
||||||
|
let mut tmp = cv.clone();
|
||||||
|
tmp = tmp.add(&self.bvk, params);
|
||||||
|
|
||||||
|
// Update the context
|
||||||
|
self.bvk = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Grab the nullifier as a sequence of bytes
|
||||||
|
let nullifier = &nullifier[..];
|
||||||
|
|
||||||
|
// Compute the signature's message for rk/spend_auth_sig
|
||||||
|
let mut data_to_be_signed = [0u8; 64];
|
||||||
|
rk.0.write(&mut data_to_be_signed[0..32])
|
||||||
|
.expect("message buffer should be 32 bytes");
|
||||||
|
(&mut data_to_be_signed[32..64]).copy_from_slice(&sighash_value[..]);
|
||||||
|
|
||||||
|
// Verify the spend_auth_sig
|
||||||
|
if !rk.verify(
|
||||||
|
&data_to_be_signed,
|
||||||
|
&spend_auth_sig,
|
||||||
|
FixedGenerators::SpendingKeyGenerator,
|
||||||
|
params,
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Construct public input for circuit
|
||||||
|
let mut public_input = [Fr::zero(); 7];
|
||||||
|
{
|
||||||
|
let (x, y) = rk.0.into_xy();
|
||||||
|
public_input[0] = x;
|
||||||
|
public_input[1] = y;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
let (x, y) = cv.into_xy();
|
||||||
|
public_input[2] = x;
|
||||||
|
public_input[3] = y;
|
||||||
|
}
|
||||||
|
public_input[4] = anchor;
|
||||||
|
|
||||||
|
// Add the nullifier through multiscalar packing
|
||||||
|
{
|
||||||
|
let nullifier = multipack::bytes_to_bits_le(nullifier);
|
||||||
|
let nullifier = multipack::compute_multipacking::<Bls12>(&nullifier);
|
||||||
|
|
||||||
|
assert_eq!(nullifier.len(), 2);
|
||||||
|
|
||||||
|
public_input[5] = nullifier[0];
|
||||||
|
public_input[6] = nullifier[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify the proof
|
||||||
|
match verify_proof(verifying_key, &zkproof, &public_input[..]) {
|
||||||
|
// No error, and proof verification successful
|
||||||
|
Ok(true) => true,
|
||||||
|
|
||||||
|
// Any other case
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Perform consensus checks on a Sapling OutputDescription, while
|
||||||
|
/// accumulating its value commitment inside the context for later use.
|
||||||
|
pub fn check_output(
|
||||||
|
&mut self,
|
||||||
|
cv: edwards::Point<Bls12, Unknown>,
|
||||||
|
cm: Fr,
|
||||||
|
epk: edwards::Point<Bls12, Unknown>,
|
||||||
|
zkproof: Proof<Bls12>,
|
||||||
|
verifying_key: &PreparedVerifyingKey<Bls12>,
|
||||||
|
params: &JubjubBls12,
|
||||||
|
) -> bool {
|
||||||
|
if is_small_order(&cv, params) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if is_small_order(&epk, params) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Accumulate the value commitment in the context
|
||||||
|
{
|
||||||
|
let mut tmp = cv.clone();
|
||||||
|
tmp = tmp.negate(); // Outputs subtract from the total.
|
||||||
|
tmp = tmp.add(&self.bvk, params);
|
||||||
|
|
||||||
|
// Update the context
|
||||||
|
self.bvk = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Construct public input for circuit
|
||||||
|
let mut public_input = [Fr::zero(); 5];
|
||||||
|
{
|
||||||
|
let (x, y) = cv.into_xy();
|
||||||
|
public_input[0] = x;
|
||||||
|
public_input[1] = y;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
let (x, y) = epk.into_xy();
|
||||||
|
public_input[2] = x;
|
||||||
|
public_input[3] = y;
|
||||||
|
}
|
||||||
|
public_input[4] = cm;
|
||||||
|
|
||||||
|
// Verify the proof
|
||||||
|
match verify_proof(verifying_key, &zkproof, &public_input[..]) {
|
||||||
|
// No error, and proof verification successful
|
||||||
|
Ok(true) => true,
|
||||||
|
|
||||||
|
// Any other case
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Perform consensus checks on the valueBalance and bindingSig parts of a
|
||||||
|
/// Sapling transaction. All SpendDescriptions and OutputDescriptions must
|
||||||
|
/// have been checked before calling this function.
|
||||||
|
pub fn final_check(
|
||||||
|
&self,
|
||||||
|
value_balance: i64,
|
||||||
|
sighash_value: &[u8; 32],
|
||||||
|
binding_sig: Signature,
|
||||||
|
params: &JubjubBls12,
|
||||||
|
) -> bool {
|
||||||
|
// Obtain current bvk from the context
|
||||||
|
let mut bvk = PublicKey(self.bvk.clone());
|
||||||
|
|
||||||
|
// Compute value balance
|
||||||
|
let mut value_balance = match compute_value_balance(value_balance, params) {
|
||||||
|
Some(a) => a,
|
||||||
|
None => return false,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Subtract value_balance from current bvk to get final bvk
|
||||||
|
value_balance = value_balance.negate();
|
||||||
|
bvk.0 = bvk.0.add(&value_balance, params);
|
||||||
|
|
||||||
|
// Compute the signature's message for bvk/binding_sig
|
||||||
|
let mut data_to_be_signed = [0u8; 64];
|
||||||
|
bvk.0
|
||||||
|
.write(&mut data_to_be_signed[0..32])
|
||||||
|
.expect("bvk is 32 bytes");
|
||||||
|
(&mut data_to_be_signed[32..64]).copy_from_slice(&sighash_value[..]);
|
||||||
|
|
||||||
|
// Verify the binding_sig
|
||||||
|
bvk.verify(
|
||||||
|
&data_to_be_signed,
|
||||||
|
&binding_sig,
|
||||||
|
FixedGenerators::ValueCommitmentRandomness,
|
||||||
|
params,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -92,13 +92,13 @@ impl<E: JubjubEngine> ExpandedSpendingKey<E> {
|
|||||||
pub fn read<R: Read>(mut reader: R) -> io::Result<Self> {
|
pub fn read<R: Read>(mut reader: R) -> io::Result<Self> {
|
||||||
let mut ask_repr = <E::Fs as PrimeField>::Repr::default();
|
let mut ask_repr = <E::Fs as PrimeField>::Repr::default();
|
||||||
ask_repr.read_le(&mut reader)?;
|
ask_repr.read_le(&mut reader)?;
|
||||||
let ask =
|
let ask = E::Fs::from_repr(ask_repr)
|
||||||
E::Fs::from_repr(ask_repr).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
|
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
|
||||||
|
|
||||||
let mut nsk_repr = <E::Fs as PrimeField>::Repr::default();
|
let mut nsk_repr = <E::Fs as PrimeField>::Repr::default();
|
||||||
nsk_repr.read_le(&mut reader)?;
|
nsk_repr.read_le(&mut reader)?;
|
||||||
let nsk =
|
let nsk = E::Fs::from_repr(nsk_repr)
|
||||||
E::Fs::from_repr(nsk_repr).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
|
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
|
||||||
|
|
||||||
let mut ovk = [0; 32];
|
let mut ovk = [0; 32];
|
||||||
reader.read_exact(&mut ovk)?;
|
reader.read_exact(&mut ovk)?;
|
||||||
@@ -324,9 +324,11 @@ impl DiversifierKey {
|
|||||||
// Return (j, d_j) if valid, else increment j and try again
|
// Return (j, d_j) if valid, else increment j and try again
|
||||||
match d_j.g_d::<Bls12>(&JUBJUB) {
|
match d_j.g_d::<Bls12>(&JUBJUB) {
|
||||||
Some(_) => return Ok((j, d_j)),
|
Some(_) => return Ok((j, d_j)),
|
||||||
None => if j.increment().is_err() {
|
None => {
|
||||||
return Err(());
|
if j.increment().is_err() {
|
||||||
},
|
return Err(());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user