mirror of
https://github.com/Qortal/pirate-librustzcash.git
synced 2025-07-30 03:51:22 +00:00
Move Sapling proving and binding signature into zcash_proofs crate
This commit is contained in:
2
Cargo.lock
generated
2
Cargo.lock
generated
@@ -719,7 +719,9 @@ name = "zcash_proofs"
|
||||
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",
|
||||
]
|
||||
|
||||
|
@@ -15,7 +15,7 @@ extern crate lazy_static;
|
||||
|
||||
use pairing::{
|
||||
bls12_381::{Bls12, Fr, FrRepr},
|
||||
BitIterator, Field, PrimeField, PrimeFieldRepr,
|
||||
BitIterator, PrimeField, PrimeFieldRepr,
|
||||
};
|
||||
|
||||
use sapling_crypto::{
|
||||
@@ -30,9 +30,8 @@ use sapling_crypto::{
|
||||
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};
|
||||
// TODO: make these consistent
|
||||
const SAPLING_TREE_DEPTH: usize = 32;
|
||||
|
||||
use bellman::groth16::{
|
||||
create_random_proof, prepare_verifying_key, verify_proof, Parameters, PreparedVerifyingKey,
|
||||
@@ -43,7 +42,7 @@ use blake2_rfc::blake2s::Blake2s;
|
||||
|
||||
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
||||
|
||||
use rand::{OsRng, Rand, Rng};
|
||||
use rand::{OsRng, Rng};
|
||||
use std::io::{self, BufReader};
|
||||
|
||||
use libc::{c_char, c_uchar, int64_t, size_t, uint32_t, uint64_t};
|
||||
@@ -62,8 +61,10 @@ use std::ffi::OsString;
|
||||
#[cfg(target_os = "windows")]
|
||||
use std::os::windows::ffi::OsStringExt;
|
||||
|
||||
use sapling_crypto::primitives::{ProofGenerationKey, ValueCommitment, ViewingKey};
|
||||
use zcash_proofs::sapling::{compute_value_balance, SaplingVerificationContext};
|
||||
use sapling_crypto::primitives::{ProofGenerationKey, ViewingKey};
|
||||
use zcash_proofs::sapling::{
|
||||
CommitmentTreeWitness, SaplingProvingContext, SaplingVerificationContext,
|
||||
};
|
||||
|
||||
pub mod equihash;
|
||||
|
||||
@@ -981,11 +982,6 @@ pub extern "system" fn librustzcash_sprout_verify(
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SaplingProvingContext {
|
||||
bsk: Fs,
|
||||
bvk: edwards::Point<Bls12, Unknown>,
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "system" fn librustzcash_sapling_output_proof(
|
||||
ctx: *mut SaplingProvingContext,
|
||||
@@ -1030,58 +1026,15 @@ pub extern "system" fn librustzcash_sapling_output_proof(
|
||||
Err(_) => return false,
|
||||
};
|
||||
|
||||
// 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(&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
|
||||
let proof = create_random_proof(
|
||||
instance,
|
||||
let (proof, value_commitment) = unsafe { &mut *ctx }.output_proof(
|
||||
esk,
|
||||
payment_address,
|
||||
rcm,
|
||||
value,
|
||||
unsafe { SAPLING_OUTPUT_PARAMS.as_ref() }.unwrap(),
|
||||
&mut rng,
|
||||
).expect("proving should not fail");
|
||||
|
||||
// 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;
|
||||
}
|
||||
&JUBJUB,
|
||||
);
|
||||
|
||||
// Write the proof out to the caller
|
||||
proof
|
||||
@@ -1152,54 +1105,11 @@ pub extern "system" fn librustzcash_sapling_binding_sig(
|
||||
sighash: *const [c_uchar; 32],
|
||||
result: *mut [c_uchar; 64],
|
||||
) -> bool {
|
||||
// Initialize secure RNG
|
||||
let mut rng = OsRng::new().expect("should be able to construct RNG");
|
||||
|
||||
// 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, &JUBJUB) {
|
||||
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
|
||||
let sig = bsk.sign(
|
||||
&data_to_be_signed,
|
||||
&mut rng,
|
||||
FixedGenerators::ValueCommitmentRandomness,
|
||||
&JUBJUB,
|
||||
);
|
||||
let sig = match unsafe { &*ctx }.binding_sig(value_balance, unsafe { &*sighash }, &JUBJUB) {
|
||||
Ok(s) => s,
|
||||
Err(_) => return false,
|
||||
};
|
||||
|
||||
// Write out signature
|
||||
sig.write(&mut (unsafe { &mut *result })[..])
|
||||
@@ -1262,43 +1172,6 @@ pub extern "system" fn librustzcash_sapling_spend_proof(
|
||||
Err(_) => return false,
|
||||
};
|
||||
|
||||
// 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(&unsafe { &*ctx }.bsk);
|
||||
|
||||
// Update the context
|
||||
unsafe { &mut *ctx }.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(&JUBJUB);
|
||||
|
||||
// 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,
|
||||
};
|
||||
|
||||
// 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,
|
||||
);
|
||||
|
||||
// We need to compute the anchor of the Spend.
|
||||
let anchor = match Fr::from_repr(read_le(unsafe { &(&*anchor)[..] })) {
|
||||
Ok(p) => p,
|
||||
@@ -1307,140 +1180,25 @@ pub extern "system" fn librustzcash_sapling_spend_proof(
|
||||
|
||||
// The witness contains the incremental tree witness information, in a
|
||||
// weird serialized format.
|
||||
let mut witness = unsafe { &(&*witness)[..] };
|
||||
|
||||
// Skip the first byte, which should be "32" to signify the length of
|
||||
// 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),
|
||||
let witness = match CommitmentTreeWitness::from_slice(unsafe { &(&*witness)[..] }) {
|
||||
Ok(w) => w,
|
||||
Err(_) => return false,
|
||||
};
|
||||
|
||||
// Create proof
|
||||
let proof = create_random_proof(
|
||||
instance,
|
||||
unsafe { SAPLING_SPEND_PARAMS.as_ref() }.unwrap(),
|
||||
&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(&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;
|
||||
}
|
||||
let (proof, value_commitment, rk) = unsafe { &mut *ctx }
|
||||
.spend_proof(
|
||||
proof_generation_key,
|
||||
diversifier,
|
||||
rcm,
|
||||
ar,
|
||||
value,
|
||||
anchor,
|
||||
witness,
|
||||
unsafe { SAPLING_SPEND_PARAMS.as_ref() }.unwrap(),
|
||||
unsafe { SAPLING_SPEND_VK.as_ref() }.unwrap(),
|
||||
&JUBJUB,
|
||||
).expect("proving should not fail");
|
||||
|
||||
// Write value commitment to caller
|
||||
value_commitment
|
||||
@@ -1461,10 +1219,7 @@ pub extern "system" fn librustzcash_sapling_spend_proof(
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "system" fn librustzcash_sapling_proving_ctx_init() -> *mut SaplingProvingContext {
|
||||
let ctx = Box::new(SaplingProvingContext {
|
||||
bsk: Fs::zero(),
|
||||
bvk: edwards::Point::zero(),
|
||||
});
|
||||
let ctx = Box::new(SaplingProvingContext::new());
|
||||
|
||||
Box::into_raw(ctx)
|
||||
}
|
||||
|
@@ -31,6 +31,8 @@ use super::blake2s;
|
||||
use super::num;
|
||||
use super::multipack;
|
||||
|
||||
pub const TREE_DEPTH: usize = 32;
|
||||
|
||||
/// This is an instance of the `Spend` circuit.
|
||||
pub struct Spend<'a, E: JubjubEngine> {
|
||||
pub params: &'a E::Params,
|
||||
|
@@ -7,5 +7,7 @@ authors = [
|
||||
|
||||
[dependencies]
|
||||
bellman = { path = "../bellman" }
|
||||
byteorder = "1"
|
||||
pairing = { path = "../pairing" }
|
||||
rand = "0.4"
|
||||
sapling-crypto = { path = "../sapling-crypto" }
|
||||
|
@@ -1,5 +1,7 @@
|
||||
extern crate bellman;
|
||||
extern crate byteorder;
|
||||
extern crate pairing;
|
||||
extern crate rand;
|
||||
extern crate sapling_crypto;
|
||||
|
||||
pub mod sapling;
|
||||
|
@@ -3,12 +3,14 @@ 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
|
||||
pub fn compute_value_balance(
|
||||
fn compute_value_balance(
|
||||
value: i64,
|
||||
params: &JubjubBls12,
|
||||
) -> Option<edwards::Point<Bls12, Unknown>> {
|
||||
|
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,
|
||||
))
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user