mirror of
https://github.com/Qortal/pirate-librustzcash.git
synced 2025-07-31 12:31:22 +00:00
6
Cargo.lock
generated
6
Cargo.lock
generated
@@ -121,7 +121,7 @@ dependencies = [
|
|||||||
"libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"pairing 0.14.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"pairing 0.14.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"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 (git+https://github.com/zcash-hackworks/sapling-crypto?rev=6abfcca25ae233922ecc18a4d2d0b5cb7aab7c8c)",
|
"sapling-crypto 0.0.1 (git+https://github.com/zcash-hackworks/sapling-crypto?rev=c2862a43829632d64f0654e8a9cde9680ba48076)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -159,7 +159,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "sapling-crypto"
|
name = "sapling-crypto"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
source = "git+https://github.com/zcash-hackworks/sapling-crypto?rev=6abfcca25ae233922ecc18a4d2d0b5cb7aab7c8c#6abfcca25ae233922ecc18a4d2d0b5cb7aab7c8c"
|
source = "git+https://github.com/zcash-hackworks/sapling-crypto?rev=c2862a43829632d64f0654e8a9cde9680ba48076#c2862a43829632d64f0654e8a9cde9680ba48076"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bellman 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"bellman 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"blake2-rfc 0.2.18 (git+https://github.com/gtank/blake2-rfc?rev=7a5b5fc99ae483a0043db7547fb79a6fa44b88a9)",
|
"blake2-rfc 0.2.18 (git+https://github.com/gtank/blake2-rfc?rev=7a5b5fc99ae483a0043db7547fb79a6fa44b88a9)",
|
||||||
@@ -214,7 +214,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
"checksum num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c51a3322e4bca9d212ad9a158a02abc6934d005490c054a2778df73a70aa0a30"
|
"checksum num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c51a3322e4bca9d212ad9a158a02abc6934d005490c054a2778df73a70aa0a30"
|
||||||
"checksum pairing 0.14.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ceda21136251c6d5a422d3d798d8ac22515a6e8d3521bb60c59a8349d36d0d57"
|
"checksum pairing 0.14.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ceda21136251c6d5a422d3d798d8ac22515a6e8d3521bb60c59a8349d36d0d57"
|
||||||
"checksum rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "eba5f8cb59cc50ed56be8880a5c7b496bfd9bd26394e176bc67884094145c2c5"
|
"checksum rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "eba5f8cb59cc50ed56be8880a5c7b496bfd9bd26394e176bc67884094145c2c5"
|
||||||
"checksum sapling-crypto 0.0.1 (git+https://github.com/zcash-hackworks/sapling-crypto?rev=6abfcca25ae233922ecc18a4d2d0b5cb7aab7c8c)" = "<none>"
|
"checksum sapling-crypto 0.0.1 (git+https://github.com/zcash-hackworks/sapling-crypto?rev=c2862a43829632d64f0654e8a9cde9680ba48076)" = "<none>"
|
||||||
"checksum typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "612d636f949607bdf9b123b4a6f6d966dedf3ff669f7f045890d3a4a73948169"
|
"checksum typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "612d636f949607bdf9b123b4a6f6d966dedf3ff669f7f045890d3a4a73948169"
|
||||||
"checksum winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "04e3bd221fcbe8a271359c04f21a76db7d0c6028862d1bb5512d85e1e2eb5bb3"
|
"checksum winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "04e3bd221fcbe8a271359c04f21a76db7d0c6028862d1bb5512d85e1e2eb5bb3"
|
||||||
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||||
|
@@ -27,7 +27,7 @@ rev = "7a5b5fc99ae483a0043db7547fb79a6fa44b88a9"
|
|||||||
|
|
||||||
[dependencies.sapling-crypto]
|
[dependencies.sapling-crypto]
|
||||||
git = "https://github.com/zcash-hackworks/sapling-crypto"
|
git = "https://github.com/zcash-hackworks/sapling-crypto"
|
||||||
rev = "6abfcca25ae233922ecc18a4d2d0b5cb7aab7c8c"
|
rev = "c2862a43829632d64f0654e8a9cde9680ba48076"
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
lto = true
|
lto = true
|
||||||
|
@@ -60,6 +60,68 @@ extern "C" {
|
|||||||
unsigned char *result
|
unsigned char *result
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/// 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.
|
||||||
|
bool librustzcash_sapling_spend_sig(
|
||||||
|
const unsigned char *ask,
|
||||||
|
const unsigned char *ar,
|
||||||
|
const unsigned char *sighash,
|
||||||
|
unsigned char *result
|
||||||
|
);
|
||||||
|
|
||||||
|
/// Creates a Sapling proving context. Please free this when you're done.
|
||||||
|
void * librustzcash_sapling_proving_ctx_init();
|
||||||
|
|
||||||
|
/// 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.
|
||||||
|
bool librustzcash_sapling_spend_proof(
|
||||||
|
void *ctx,
|
||||||
|
const unsigned char *ak,
|
||||||
|
const unsigned char *nsk,
|
||||||
|
const unsigned char *diversifier,
|
||||||
|
const unsigned char *rcm,
|
||||||
|
const unsigned char *ar,
|
||||||
|
const uint64_t value,
|
||||||
|
const unsigned char *anchor,
|
||||||
|
const unsigned char *witness,
|
||||||
|
unsigned char *cv,
|
||||||
|
unsigned char *rk,
|
||||||
|
unsigned char *zkproof
|
||||||
|
);
|
||||||
|
|
||||||
|
/// This function (using the proving context) constructs an Output
|
||||||
|
/// proof given the necessary witness information. It outputs `cv`
|
||||||
|
/// and the `zkproof`.
|
||||||
|
bool librustzcash_sapling_output_proof(
|
||||||
|
void *ctx,
|
||||||
|
const unsigned char *esk,
|
||||||
|
const unsigned char *diversifier,
|
||||||
|
const unsigned char *pk_d,
|
||||||
|
const unsigned char *rcm,
|
||||||
|
const uint64_t value,
|
||||||
|
unsigned char *cv,
|
||||||
|
unsigned char *zkproof
|
||||||
|
);
|
||||||
|
|
||||||
|
/// This function (using the proving context) constructs a binding
|
||||||
|
/// signature. You must provide the intended valueBalance so that
|
||||||
|
/// we can internally check consistency.
|
||||||
|
bool librustzcash_sapling_binding_sig(
|
||||||
|
const void *ctx,
|
||||||
|
int64_t valueBalance,
|
||||||
|
const unsigned char *sighash,
|
||||||
|
unsigned char *result
|
||||||
|
);
|
||||||
|
|
||||||
|
/// Frees a Sapling proving context returned from
|
||||||
|
/// `librustzcash_sapling_proving_ctx_init`.
|
||||||
|
void librustzcash_sapling_proving_ctx_free(void *);
|
||||||
|
|
||||||
/// Creates a Sapling verification context. Please free this
|
/// Creates a Sapling verification context. Please free this
|
||||||
/// when you're done.
|
/// when you're done.
|
||||||
void * librustzcash_sapling_verification_ctx_init();
|
void * librustzcash_sapling_verification_ctx_init();
|
||||||
|
498
src/rustzcash.rs
498
src/rustzcash.rs
@@ -23,6 +23,8 @@ use sapling_crypto::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
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,
|
||||||
@@ -33,7 +35,7 @@ use blake2_rfc::blake2s::Blake2s;
|
|||||||
|
|
||||||
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
||||||
|
|
||||||
use rand::{OsRng, Rng};
|
use rand::{OsRng, Rand, Rng};
|
||||||
use std::io::BufReader;
|
use std::io::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};
|
||||||
@@ -41,7 +43,7 @@ use std::ffi::CStr;
|
|||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::slice;
|
use std::slice;
|
||||||
|
|
||||||
use sapling_crypto::primitives::ViewingKey;
|
use sapling_crypto::primitives::{ProofGenerationKey, ValueCommitment, ViewingKey};
|
||||||
|
|
||||||
pub mod equihash;
|
pub mod equihash;
|
||||||
|
|
||||||
@@ -693,7 +695,7 @@ pub extern "system" fn librustzcash_sapling_check_output(
|
|||||||
// Accumulate the value commitment in the context
|
// Accumulate the value commitment in the context
|
||||||
{
|
{
|
||||||
let mut tmp = cv.clone();
|
let mut tmp = cv.clone();
|
||||||
tmp.negate(); // Outputs subtract from the total.
|
tmp = tmp.negate(); // Outputs subtract from the total.
|
||||||
tmp = tmp.add(&unsafe { &*ctx }.bvk, &JUBJUB);
|
tmp = tmp.add(&unsafe { &*ctx }.bvk, &JUBJUB);
|
||||||
|
|
||||||
// Update the context
|
// Update the context
|
||||||
@@ -1019,3 +1021,493 @@ pub extern "system" fn librustzcash_sprout_verify(
|
|||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct SaplingProvingContext {
|
||||||
|
bsk: Fs,
|
||||||
|
bvk: edwards::Point<Bls12, Unknown>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "system" fn librustzcash_sapling_output_proof(
|
||||||
|
ctx: *mut SaplingProvingContext,
|
||||||
|
esk: *const [c_uchar; 32],
|
||||||
|
diversifier: *const [c_uchar; 11],
|
||||||
|
pk_d: *const [c_uchar; 32],
|
||||||
|
rcm: *const [c_uchar; 32],
|
||||||
|
value: uint64_t,
|
||||||
|
cv: *mut [c_uchar; 32],
|
||||||
|
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 })[..])) {
|
||||||
|
Ok(p) => p,
|
||||||
|
Err(_) => return false,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Grab the diversifier from the caller.
|
||||||
|
let diversifier = sapling_crypto::primitives::Diversifier(unsafe { *diversifier });
|
||||||
|
|
||||||
|
// Grab pk_d from the caller.
|
||||||
|
let pk_d = match edwards::Point::<Bls12, Unknown>::read(&(unsafe { &*pk_d })[..], &JUBJUB) {
|
||||||
|
Ok(p) => p,
|
||||||
|
Err(_) => return false,
|
||||||
|
};
|
||||||
|
|
||||||
|
// pk_d should be prime order.
|
||||||
|
let pk_d = match pk_d.as_prime_order(&JUBJUB) {
|
||||||
|
Some(p) => p,
|
||||||
|
None => return false,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Construct a payment address
|
||||||
|
let payment_address = sapling_crypto::primitives::PaymentAddress {
|
||||||
|
pk_d: pk_d,
|
||||||
|
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
|
||||||
|
let rcm = match Fs::from_repr(read_fs(&(unsafe { &*rcm })[..])) {
|
||||||
|
Ok(p) => p,
|
||||||
|
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
|
||||||
|
let proof = create_random_proof(
|
||||||
|
instance,
|
||||||
|
unsafe { SAPLING_OUTPUT_PARAMS.as_ref() }.unwrap(),
|
||||||
|
&mut rng,
|
||||||
|
).expect("proving should not fail");
|
||||||
|
|
||||||
|
// Write the proof out to the caller
|
||||||
|
proof
|
||||||
|
.write(&mut (unsafe { &mut *zkproof })[..])
|
||||||
|
.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
|
||||||
|
value_commitment
|
||||||
|
.write(&mut (unsafe { &mut *cv })[..])
|
||||||
|
.expect("should be able to serialize rcv");
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "system" 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 })[..])) {
|
||||||
|
Ok(p) => p,
|
||||||
|
Err(_) => return false,
|
||||||
|
};
|
||||||
|
|
||||||
|
// The caller provides `ask`, the spend authorizing key.
|
||||||
|
let ask = match redjubjub::PrivateKey::<Bls12>::read(&(unsafe { &*ask })[..]) {
|
||||||
|
Ok(p) => p,
|
||||||
|
Err(_) => return false,
|
||||||
|
};
|
||||||
|
|
||||||
|
// We compute `rsk`...
|
||||||
|
let rsk = ask.randomize(ar);
|
||||||
|
|
||||||
|
// We compute `rk` from there (needed for key prefixing)
|
||||||
|
let rk =
|
||||||
|
redjubjub::PublicKey::from_private(&rsk, FixedGenerators::SpendingKeyGenerator, &JUBJUB);
|
||||||
|
|
||||||
|
// 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(&(unsafe { &*sighash })[..]);
|
||||||
|
|
||||||
|
// Do the signing
|
||||||
|
let mut rng = OsRng::new().expect("should be able to construct RNG");
|
||||||
|
let sig = rsk.sign(
|
||||||
|
&data_to_be_signed,
|
||||||
|
&mut rng,
|
||||||
|
FixedGenerators::SpendingKeyGenerator,
|
||||||
|
&JUBJUB,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Write out the signature
|
||||||
|
sig.write(&mut (unsafe { &mut *result })[..])
|
||||||
|
.expect("result should be 64 bytes");
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "system" fn librustzcash_sapling_binding_sig(
|
||||||
|
ctx: *const SaplingProvingContext,
|
||||||
|
value_balance: int64_t,
|
||||||
|
sighash: *const [c_uchar; 32],
|
||||||
|
result: *mut [c_uchar; 64],
|
||||||
|
) -> 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
|
||||||
|
let mut rng = OsRng::new().expect("should be able to construct RNG");
|
||||||
|
let sig = bsk.sign(
|
||||||
|
&data_to_be_signed,
|
||||||
|
&mut rng,
|
||||||
|
FixedGenerators::ValueCommitmentRandomness,
|
||||||
|
&JUBJUB,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Write out signature
|
||||||
|
sig.write(&mut (unsafe { &mut *result })[..])
|
||||||
|
.expect("result should be 64 bytes");
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "system" fn librustzcash_sapling_spend_proof(
|
||||||
|
ctx: *mut SaplingProvingContext,
|
||||||
|
ak: *const [c_uchar; 32],
|
||||||
|
nsk: *const [c_uchar; 32],
|
||||||
|
diversifier: *const [c_uchar; 11],
|
||||||
|
rcm: *const [c_uchar; 32],
|
||||||
|
ar: *const [c_uchar; 32],
|
||||||
|
value: uint64_t,
|
||||||
|
anchor: *const [c_uchar; 32],
|
||||||
|
witness: *const [c_uchar; 1 + 33 * SAPLING_TREE_DEPTH + 8],
|
||||||
|
cv: *mut [c_uchar; 32],
|
||||||
|
rk_out: *mut [c_uchar; 32],
|
||||||
|
zkproof: *mut [c_uchar; GROTH_PROOF_SIZE],
|
||||||
|
) -> 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.
|
||||||
|
let ak = match edwards::Point::<Bls12, Unknown>::read(&(unsafe { &*ak })[..], &JUBJUB) {
|
||||||
|
Ok(p) => p,
|
||||||
|
Err(_) => return false,
|
||||||
|
};
|
||||||
|
|
||||||
|
// `ak` should be prime order.
|
||||||
|
let ak = match ak.as_prime_order(&JUBJUB) {
|
||||||
|
Some(p) => p,
|
||||||
|
None => return false,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Grab `nsk` from the caller
|
||||||
|
let nsk = match Fs::from_repr(read_fs(&(unsafe { &*nsk })[..])) {
|
||||||
|
Ok(p) => p,
|
||||||
|
Err(_) => return false,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Construct the proof generation key
|
||||||
|
let proof_generation_key = ProofGenerationKey {
|
||||||
|
ak: ak.clone(),
|
||||||
|
nsk,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Construct the viewing key
|
||||||
|
let viewing_key = proof_generation_key.into_viewing_key(&JUBJUB);
|
||||||
|
|
||||||
|
// Grab the diversifier from the caller
|
||||||
|
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
|
||||||
|
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 })[..])) {
|
||||||
|
Ok(p) => p,
|
||||||
|
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.
|
||||||
|
let anchor = match Fr::from_repr(read_le(unsafe { &(&*anchor)[..] })) {
|
||||||
|
Ok(p) => p,
|
||||||
|
Err(_) => return false,
|
||||||
|
};
|
||||||
|
|
||||||
|
// 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),
|
||||||
|
};
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write value commitment to caller
|
||||||
|
value_commitment
|
||||||
|
.write(&mut unsafe { &mut *cv }[..])
|
||||||
|
.expect("should be able to serialize cv");
|
||||||
|
|
||||||
|
// Write proof out to caller
|
||||||
|
proof
|
||||||
|
.write(&mut (unsafe { &mut *zkproof })[..])
|
||||||
|
.expect("should be able to serialize a proof");
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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(),
|
||||||
|
});
|
||||||
|
|
||||||
|
Box::into_raw(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "system" fn librustzcash_sapling_proving_ctx_free(ctx: *mut SaplingProvingContext) {
|
||||||
|
drop(unsafe { Box::from_raw(ctx) });
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user