From 00280488009ffaef37cbc91be5c6748866000b2f Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Mon, 4 Nov 2019 17:48:44 +0000 Subject: [PATCH 01/10] Use C calling convention for librustzcash The Rust-to-C++ interface speaks the C ABI. The "system" ABI happens to be equivalent to the C ABI on the platforms we currently target (in particular, we don't target win32 with an x86 architecture, which would use the stdcall ABI). --- librustzcash/src/rustzcash.rs | 78 +++++++++++++++-------------------- 1 file changed, 33 insertions(+), 45 deletions(-) diff --git a/librustzcash/src/rustzcash.rs b/librustzcash/src/rustzcash.rs index fc8d3ef..8f13713 100644 --- a/librustzcash/src/rustzcash.rs +++ b/librustzcash/src/rustzcash.rs @@ -115,7 +115,7 @@ fn fixed_scalar_mult(from: &[u8], p_g: FixedGenerators) -> edwards::Point::uncommitted().into_repr(); // Should be okay, caller is responsible for ensuring the pointer @@ -249,7 +249,7 @@ pub extern "system" fn librustzcash_tree_uncommitted(result: *mut [c_uchar; 32]) } #[no_mangle] -pub extern "system" fn librustzcash_merkle_hash( +pub extern "C" fn librustzcash_merkle_hash( depth: size_t, a: *const [c_uchar; 32], b: *const [c_uchar; 32], @@ -275,10 +275,7 @@ pub extern "system" fn librustzcash_merkle_hash( } #[no_mangle] // ToScalar -pub extern "system" fn librustzcash_to_scalar( - input: *const [c_uchar; 64], - result: *mut [c_uchar; 32], -) { +pub extern "C" fn librustzcash_to_scalar(input: *const [c_uchar; 64], result: *mut [c_uchar; 32]) { // Should be okay, because caller is responsible for ensuring // the pointer is a valid pointer to 32 bytes, and that is the // size of the representation @@ -292,10 +289,7 @@ pub extern "system" fn librustzcash_to_scalar( } #[no_mangle] -pub extern "system" fn librustzcash_ask_to_ak( - ask: *const [c_uchar; 32], - result: *mut [c_uchar; 32], -) { +pub extern "C" fn librustzcash_ask_to_ak(ask: *const [c_uchar; 32], result: *mut [c_uchar; 32]) { let ask = unsafe { &*ask }; let ak = fixed_scalar_mult(ask, FixedGenerators::SpendingKeyGenerator); @@ -305,10 +299,7 @@ pub extern "system" fn librustzcash_ask_to_ak( } #[no_mangle] -pub extern "system" fn librustzcash_nsk_to_nk( - nsk: *const [c_uchar; 32], - result: *mut [c_uchar; 32], -) { +pub extern "C" fn librustzcash_nsk_to_nk(nsk: *const [c_uchar; 32], result: *mut [c_uchar; 32]) { let nsk = unsafe { &*nsk }; let nk = fixed_scalar_mult(nsk, FixedGenerators::ProofGenerationKey); @@ -318,7 +309,7 @@ pub extern "system" fn librustzcash_nsk_to_nk( } #[no_mangle] -pub extern "system" fn librustzcash_crh_ivk( +pub extern "C" fn librustzcash_crh_ivk( ak: *const [c_uchar; 32], nk: *const [c_uchar; 32], result: *mut [c_uchar; 32], @@ -343,13 +334,13 @@ pub extern "system" fn librustzcash_crh_ivk( } #[no_mangle] -pub extern "system" fn librustzcash_check_diversifier(diversifier: *const [c_uchar; 11]) -> bool { +pub extern "C" fn librustzcash_check_diversifier(diversifier: *const [c_uchar; 11]) -> bool { let diversifier = Diversifier(unsafe { *diversifier }); diversifier.g_d::(&JUBJUB).is_some() } #[no_mangle] -pub extern "system" fn librustzcash_ivk_to_pkd( +pub extern "C" fn librustzcash_ivk_to_pkd( ivk: *const [c_uchar; 32], diversifier: *const [c_uchar; 11], result: *mut [c_uchar; 32], @@ -390,7 +381,7 @@ fn test_gen_r() { /// Return 32 byte random scalar, uniformly. #[no_mangle] -pub extern "system" fn librustzcash_sapling_generate_r(result: *mut [c_uchar; 32]) { +pub extern "C" fn librustzcash_sapling_generate_r(result: *mut [c_uchar; 32]) { // create random 64 byte buffer let mut rng = OsRng; let mut buffer = [0u8; 64]; @@ -445,7 +436,7 @@ fn priv_get_note( /// Compute Sapling note nullifier. #[no_mangle] -pub extern "system" fn librustzcash_sapling_compute_nf( +pub extern "C" fn librustzcash_sapling_compute_nf( diversifier: *const [c_uchar; 11], pk_d: *const [c_uchar; 32], value: u64, @@ -490,7 +481,7 @@ pub extern "system" fn librustzcash_sapling_compute_nf( /// Compute Sapling note commitment. #[no_mangle] -pub extern "system" fn librustzcash_sapling_compute_cm( +pub extern "C" fn librustzcash_sapling_compute_cm( diversifier: *const [c_uchar; 11], pk_d: *const [c_uchar; 32], value: u64, @@ -509,7 +500,7 @@ pub extern "system" fn librustzcash_sapling_compute_cm( } #[no_mangle] -pub extern "system" fn librustzcash_sapling_ka_agree( +pub extern "C" fn librustzcash_sapling_ka_agree( p: *const [c_uchar; 32], sk: *const [c_uchar; 32], result: *mut [c_uchar; 32], @@ -537,7 +528,7 @@ pub extern "system" fn librustzcash_sapling_ka_agree( } #[no_mangle] -pub extern "system" fn librustzcash_sapling_ka_derivepublic( +pub extern "C" fn librustzcash_sapling_ka_derivepublic( diversifier: *const [c_uchar; 11], esk: *const [c_uchar; 32], result: *mut [c_uchar; 32], @@ -565,7 +556,7 @@ pub extern "system" fn librustzcash_sapling_ka_derivepublic( } #[no_mangle] -pub extern "system" fn librustzcash_eh_isvalid( +pub extern "C" fn librustzcash_eh_isvalid( n: u32, k: u32, input: *const c_uchar, @@ -585,17 +576,14 @@ pub extern "system" fn librustzcash_eh_isvalid( } #[no_mangle] -pub extern "system" fn librustzcash_sapling_verification_ctx_init( -) -> *mut SaplingVerificationContext { +pub extern "C" fn librustzcash_sapling_verification_ctx_init() -> *mut SaplingVerificationContext { let ctx = Box::new(SaplingVerificationContext::new()); Box::into_raw(ctx) } #[no_mangle] -pub extern "system" fn librustzcash_sapling_verification_ctx_free( - ctx: *mut SaplingVerificationContext, -) { +pub extern "C" fn librustzcash_sapling_verification_ctx_free(ctx: *mut SaplingVerificationContext) { drop(unsafe { Box::from_raw(ctx) }); } @@ -604,7 +592,7 @@ const GROTH_PROOF_SIZE: usize = 48 // π_A + 48; // π_C #[no_mangle] -pub extern "system" fn librustzcash_sapling_check_spend( +pub extern "C" fn librustzcash_sapling_check_spend( ctx: *mut SaplingVerificationContext, cv: *const [c_uchar; 32], anchor: *const [c_uchar; 32], @@ -659,7 +647,7 @@ pub extern "system" fn librustzcash_sapling_check_spend( } #[no_mangle] -pub extern "system" fn librustzcash_sapling_check_output( +pub extern "C" fn librustzcash_sapling_check_output( ctx: *mut SaplingVerificationContext, cv: *const [c_uchar; 32], cm: *const [c_uchar; 32], @@ -702,7 +690,7 @@ pub extern "system" fn librustzcash_sapling_check_output( } #[no_mangle] -pub extern "system" fn librustzcash_sapling_final_check( +pub extern "C" fn librustzcash_sapling_final_check( ctx: *mut SaplingVerificationContext, value_balance: i64, binding_sig: *const [c_uchar; 64], @@ -728,7 +716,7 @@ pub extern "system" fn librustzcash_sapling_final_check( } #[no_mangle] -pub extern "system" fn librustzcash_sprout_prove( +pub extern "C" fn librustzcash_sprout_prove( proof_out: *mut [c_uchar; GROTH_PROOF_SIZE], phi: *const [c_uchar; 32], @@ -877,7 +865,7 @@ pub extern "system" fn librustzcash_sprout_prove( } #[no_mangle] -pub extern "system" fn librustzcash_sprout_verify( +pub extern "C" fn librustzcash_sprout_verify( proof: *const [c_uchar; GROTH_PROOF_SIZE], rt: *const [c_uchar; 32], h_sig: *const [c_uchar; 32], @@ -926,7 +914,7 @@ pub extern "system" fn librustzcash_sprout_verify( } #[no_mangle] -pub extern "system" fn librustzcash_sapling_output_proof( +pub extern "C" fn librustzcash_sapling_output_proof( ctx: *mut SaplingProvingContext, esk: *const [c_uchar; 32], payment_address: *const [c_uchar; 43], @@ -978,7 +966,7 @@ pub extern "system" fn librustzcash_sapling_output_proof( } #[no_mangle] -pub extern "system" fn librustzcash_sapling_spend_sig( +pub extern "C" fn librustzcash_sapling_spend_sig( ask: *const [c_uchar; 32], ar: *const [c_uchar; 32], sighash: *const [c_uchar; 32], @@ -1010,7 +998,7 @@ pub extern "system" fn librustzcash_sapling_spend_sig( } #[no_mangle] -pub extern "system" fn librustzcash_sapling_binding_sig( +pub extern "C" fn librustzcash_sapling_binding_sig( ctx: *const SaplingProvingContext, value_balance: i64, sighash: *const [c_uchar; 32], @@ -1035,7 +1023,7 @@ pub extern "system" fn librustzcash_sapling_binding_sig( } #[no_mangle] -pub extern "system" fn librustzcash_sapling_spend_proof( +pub extern "C" fn librustzcash_sapling_spend_proof( ctx: *mut SaplingProvingContext, ak: *const [c_uchar; 32], nsk: *const [c_uchar; 32], @@ -1135,19 +1123,19 @@ pub extern "system" fn librustzcash_sapling_spend_proof( } #[no_mangle] -pub extern "system" fn librustzcash_sapling_proving_ctx_init() -> *mut SaplingProvingContext { +pub extern "C" fn librustzcash_sapling_proving_ctx_init() -> *mut SaplingProvingContext { let ctx = Box::new(SaplingProvingContext::new()); Box::into_raw(ctx) } #[no_mangle] -pub extern "system" fn librustzcash_sapling_proving_ctx_free(ctx: *mut SaplingProvingContext) { +pub extern "C" fn librustzcash_sapling_proving_ctx_free(ctx: *mut SaplingProvingContext) { drop(unsafe { Box::from_raw(ctx) }); } #[no_mangle] -pub extern "system" fn librustzcash_zip32_xsk_master( +pub extern "C" fn librustzcash_zip32_xsk_master( seed: *const c_uchar, seedlen: size_t, xsk_master: *mut [c_uchar; 169], @@ -1161,7 +1149,7 @@ pub extern "system" fn librustzcash_zip32_xsk_master( } #[no_mangle] -pub extern "system" fn librustzcash_zip32_xsk_derive( +pub extern "C" fn librustzcash_zip32_xsk_derive( xsk_parent: *const [c_uchar; 169], i: u32, xsk_i: *mut [c_uchar; 169], @@ -1177,7 +1165,7 @@ pub extern "system" fn librustzcash_zip32_xsk_derive( } #[no_mangle] -pub extern "system" fn librustzcash_zip32_xfvk_derive( +pub extern "C" fn librustzcash_zip32_xfvk_derive( xfvk_parent: *const [c_uchar; 169], i: u32, xfvk_i: *mut [c_uchar; 169], @@ -1198,7 +1186,7 @@ pub extern "system" fn librustzcash_zip32_xfvk_derive( } #[no_mangle] -pub extern "system" fn librustzcash_zip32_xfvk_address( +pub extern "C" fn librustzcash_zip32_xfvk_address( xfvk: *const [c_uchar; 169], j: *const [c_uchar; 11], j_ret: *mut [c_uchar; 11], From 7722b1a50bff0de0c76ca7671f286bd5b78766a4 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Mon, 4 Nov 2019 23:48:49 +0000 Subject: [PATCH 02/10] Inline write_le --- librustzcash/src/rustzcash.rs | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/librustzcash/src/rustzcash.rs b/librustzcash/src/rustzcash.rs index 8f13713..daf29ad 100644 --- a/librustzcash/src/rustzcash.rs +++ b/librustzcash/src/rustzcash.rs @@ -72,13 +72,6 @@ static mut SAPLING_SPEND_PARAMS: Option> = None; static mut SAPLING_OUTPUT_PARAMS: Option> = None; static mut SPROUT_GROTH16_PARAMS_PATH: Option = None; -/// Writes an FrRepr to [u8] of length 32 -fn write_le(f: FrRepr, to: &mut [u8]) { - assert_eq!(to.len(), 32); - - f.write_le(to).expect("length is 32 bytes"); -} - /// Reads an FrRepr from a [u8] of length 32. /// This will panic (abort) if length provided is /// not correct. @@ -244,8 +237,7 @@ pub extern "C" fn librustzcash_tree_uncommitted(result: *mut [c_uchar; 32]) { // Should be okay, caller is responsible for ensuring the pointer // is a valid pointer to 32 bytes that can be mutated. let result = unsafe { &mut *result }; - - write_le(tmp, &mut result[..]); + tmp.write_le(&mut result[..]).expect("length is 32 bytes"); } #[no_mangle] @@ -270,8 +262,7 @@ pub extern "C" fn librustzcash_merkle_hash( // Should be okay, caller is responsible for ensuring the pointer // is a valid pointer to 32 bytes that can be mutated. let result = unsafe { &mut *result }; - - write_le(tmp, &mut result[..]); + tmp.write_le(&mut result[..]).expect("length is 32 bytes"); } #[no_mangle] // ToScalar @@ -494,7 +485,10 @@ pub extern "C" fn librustzcash_sapling_compute_cm( }; let result = unsafe { &mut *result }; - write_le(note.cm(&JUBJUB).into_repr(), &mut result[..]); + note.cm(&JUBJUB) + .into_repr() + .write_le(&mut result[..]) + .expect("length is 32 bytes"); true } From 5d036194660836b064e70d74dd223ab110db333a Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Mon, 4 Nov 2019 23:50:51 +0000 Subject: [PATCH 03/10] Rename read_le to read_fr to match read_fs --- librustzcash/src/rustzcash.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/librustzcash/src/rustzcash.rs b/librustzcash/src/rustzcash.rs index daf29ad..e96dc4d 100644 --- a/librustzcash/src/rustzcash.rs +++ b/librustzcash/src/rustzcash.rs @@ -75,7 +75,7 @@ static mut SPROUT_GROTH16_PARAMS_PATH: Option = None; /// Reads an FrRepr from a [u8] of length 32. /// This will panic (abort) if length provided is /// not correct. -fn read_le(from: &[u8]) -> FrRepr { +fn read_fr(from: &[u8]) -> FrRepr { assert_eq!(from.len(), 32); let mut f = FrRepr::default(); @@ -250,12 +250,12 @@ pub extern "C" fn librustzcash_merkle_hash( // Should be okay, because caller is responsible for ensuring // the pointer is a valid pointer to 32 bytes, and that is the // size of the representation - let a_repr = read_le(unsafe { &(&*a)[..] }); + let a_repr = read_fr(unsafe { &(&*a)[..] }); // Should be okay, because caller is responsible for ensuring // the pointer is a valid pointer to 32 bytes, and that is the // size of the representation - let b_repr = read_le(unsafe { &(&*b)[..] }); + let b_repr = read_fr(unsafe { &(&*b)[..] }); let tmp = merkle_hash(depth, &a_repr, &b_repr); @@ -604,7 +604,7 @@ pub extern "C" fn librustzcash_sapling_check_spend( // Deserialize the anchor, which should be an element // of Fr. - let anchor = match Fr::from_repr(read_le(&(unsafe { &*anchor })[..])) { + let anchor = match Fr::from_repr(read_fr(&(unsafe { &*anchor })[..])) { Ok(a) => a, Err(_) => return false, }; @@ -656,7 +656,7 @@ pub extern "C" fn librustzcash_sapling_check_output( // Deserialize the commitment, which should be an element // of Fr. - let cm = match Fr::from_repr(read_le(&(unsafe { &*cm })[..])) { + let cm = match Fr::from_repr(read_fr(&(unsafe { &*cm })[..])) { Ok(a) => a, Err(_) => return false, }; @@ -1071,7 +1071,7 @@ pub extern "C" fn librustzcash_sapling_spend_proof( }; // We need to compute the anchor of the Spend. - let anchor = match Fr::from_repr(read_le(unsafe { &(&*anchor)[..] })) { + let anchor = match Fr::from_repr(read_fr(unsafe { &(&*anchor)[..] })) { Ok(p) => p, Err(_) => return false, }; From 7181d603be42d5dc13ca12269a7ba76673155535 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Mon, 4 Nov 2019 23:54:04 +0000 Subject: [PATCH 04/10] Explicitly pass [u8; 32] into read_fr and read_fs --- librustzcash/src/rustzcash.rs | 56 ++++++++++++++--------------------- 1 file changed, 22 insertions(+), 34 deletions(-) diff --git a/librustzcash/src/rustzcash.rs b/librustzcash/src/rustzcash.rs index e96dc4d..85a1306 100644 --- a/librustzcash/src/rustzcash.rs +++ b/librustzcash/src/rustzcash.rs @@ -72,35 +72,23 @@ static mut SAPLING_SPEND_PARAMS: Option> = None; static mut SAPLING_OUTPUT_PARAMS: Option> = None; static mut SPROUT_GROTH16_PARAMS_PATH: Option = None; -/// Reads an FrRepr from a [u8] of length 32. -/// This will panic (abort) if length provided is -/// not correct. -fn read_fr(from: &[u8]) -> FrRepr { - assert_eq!(from.len(), 32); - +/// Reads an FrRepr from a [u8; 32]. +fn read_fr(from: &[u8; 32]) -> FrRepr { let mut f = FrRepr::default(); - f.read_le(from).expect("length is 32 bytes"); - + f.read_le(&from[..]).expect("length is 32 bytes"); f } -/// Reads an FsRepr from [u8] of length 32 -/// This will panic (abort) if length provided is -/// not correct -fn read_fs(from: &[u8]) -> FsRepr { - assert_eq!(from.len(), 32); - +/// Reads an FsRepr from a [u8; 32]. +fn read_fs(from: &[u8; 32]) -> FsRepr { let mut f = <::Fs as PrimeField>::Repr::default(); - f.read_le(from).expect("length is 32 bytes"); - + f.read_le(&from[..]).expect("length is 32 bytes"); f } -/// Reads an FsRepr from [u8] of length 32 +/// Reads an FsRepr from a [u8; 32] /// and multiplies it by the given base. -/// This will panic (abort) if length provided is -/// not correct -fn fixed_scalar_mult(from: &[u8], p_g: FixedGenerators) -> edwards::Point { +fn fixed_scalar_mult(from: &[u8; 32], p_g: FixedGenerators) -> edwards::Point { let f = read_fs(from); JUBJUB.generator(p_g).mul(f, &JUBJUB) @@ -250,12 +238,12 @@ pub extern "C" fn librustzcash_merkle_hash( // Should be okay, because caller is responsible for ensuring // the pointer is a valid pointer to 32 bytes, and that is the // size of the representation - let a_repr = read_fr(unsafe { &(&*a)[..] }); + let a_repr = read_fr(unsafe { &*a }); // Should be okay, because caller is responsible for ensuring // the pointer is a valid pointer to 32 bytes, and that is the // size of the representation - let b_repr = read_fr(unsafe { &(&*b)[..] }); + let b_repr = read_fr(unsafe { &*b }); let tmp = merkle_hash(depth, &a_repr, &b_repr); @@ -410,7 +398,7 @@ fn priv_get_note( }; // Deserialize randomness - let r = match Fs::from_repr(read_fs(&(unsafe { &*r })[..])) { + let r = match Fs::from_repr(read_fs(unsafe { &*r })) { Ok(r) => r, Err(_) => return Err(()), }; @@ -506,7 +494,7 @@ pub extern "C" fn librustzcash_sapling_ka_agree( }; // Deserialize sk - let sk = match Fs::from_repr(read_fs(&(unsafe { &*sk })[..])) { + let sk = match Fs::from_repr(read_fs(unsafe { &*sk })) { Ok(p) => p, Err(_) => return false, }; @@ -536,7 +524,7 @@ pub extern "C" fn librustzcash_sapling_ka_derivepublic( }; // Deserialize esk - let esk = match Fs::from_repr(read_fs(&(unsafe { &*esk })[..])) { + let esk = match Fs::from_repr(read_fs(unsafe { &*esk })) { Ok(p) => p, Err(_) => return false, }; @@ -604,7 +592,7 @@ pub extern "C" fn librustzcash_sapling_check_spend( // Deserialize the anchor, which should be an element // of Fr. - let anchor = match Fr::from_repr(read_fr(&(unsafe { &*anchor })[..])) { + let anchor = match Fr::from_repr(read_fr(unsafe { &*anchor })) { Ok(a) => a, Err(_) => return false, }; @@ -656,7 +644,7 @@ pub extern "C" fn librustzcash_sapling_check_output( // Deserialize the commitment, which should be an element // of Fr. - let cm = match Fr::from_repr(read_fr(&(unsafe { &*cm })[..])) { + let cm = match Fr::from_repr(read_fr(unsafe { &*cm })) { Ok(a) => a, Err(_) => return false, }; @@ -918,7 +906,7 @@ pub extern "C" fn librustzcash_sapling_output_proof( zkproof: *mut [c_uchar; GROTH_PROOF_SIZE], ) -> bool { // Grab `esk`, which the caller should have constructed for the DH key exchange. - let esk = match Fs::from_repr(read_fs(&(unsafe { &*esk })[..])) { + let esk = match Fs::from_repr(read_fs(unsafe { &*esk })) { Ok(p) => p, Err(_) => return false, }; @@ -931,7 +919,7 @@ pub extern "C" fn librustzcash_sapling_output_proof( }; // The caller provides the commitment randomness for the output note - let rcm = match Fs::from_repr(read_fs(&(unsafe { &*rcm })[..])) { + let rcm = match Fs::from_repr(read_fs(unsafe { &*rcm })) { Ok(p) => p, Err(_) => return false, }; @@ -967,7 +955,7 @@ pub extern "C" fn librustzcash_sapling_spend_sig( result: *mut [c_uchar; 64], ) -> bool { // The caller provides the re-randomization of `ak`. - let ar = match Fs::from_repr(read_fs(&(unsafe { &*ar })[..])) { + let ar = match Fs::from_repr(read_fs(unsafe { &*ar })) { Ok(p) => p, Err(_) => return false, }; @@ -1044,7 +1032,7 @@ pub extern "C" fn librustzcash_sapling_spend_proof( }; // Grab `nsk` from the caller - let nsk = match Fs::from_repr(read_fs(&(unsafe { &*nsk })[..])) { + let nsk = match Fs::from_repr(read_fs(unsafe { &*nsk })) { Ok(p) => p, Err(_) => return false, }; @@ -1059,19 +1047,19 @@ pub extern "C" fn librustzcash_sapling_spend_proof( let diversifier = Diversifier(unsafe { *diversifier }); // The caller chooses the note randomness - let rcm = match Fs::from_repr(read_fs(&(unsafe { &*rcm })[..])) { + let rcm = match Fs::from_repr(read_fs(unsafe { &*rcm })) { Ok(p) => p, Err(_) => return false, }; // The caller also chooses the re-randomization of ak - let ar = match Fs::from_repr(read_fs(&(unsafe { &*ar })[..])) { + let ar = match Fs::from_repr(read_fs(unsafe { &*ar })) { Ok(p) => p, Err(_) => return false, }; // We need to compute the anchor of the Spend. - let anchor = match Fr::from_repr(read_fr(unsafe { &(&*anchor)[..] })) { + let anchor = match Fr::from_repr(read_fr(unsafe { &*anchor })) { Ok(p) => p, Err(_) => return false, }; From 9cb8c0b3c4756afacd4d9d6ee7d490afcec94319 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Tue, 5 Nov 2019 01:04:40 +0000 Subject: [PATCH 05/10] Pull librustzcash.h documentation into crate --- librustzcash/src/rustzcash.rs | 71 +++++++++++++++++++++++++++++++++-- 1 file changed, 68 insertions(+), 3 deletions(-) diff --git a/librustzcash/src/rustzcash.rs b/librustzcash/src/rustzcash.rs index 85a1306..0168e0d 100644 --- a/librustzcash/src/rustzcash.rs +++ b/librustzcash/src/rustzcash.rs @@ -94,6 +94,8 @@ fn fixed_scalar_mult(from: &[u8; 32], p_g: FixedGenerators) -> edwards::Point::uncommitted().into_repr(); @@ -228,6 +235,13 @@ pub extern "C" fn librustzcash_tree_uncommitted(result: *mut [c_uchar; 32]) { tmp.write_le(&mut result[..]).expect("length is 32 bytes"); } +/// Computes a merkle tree hash for a given depth. The `depth` parameter should +/// not be larger than 62. +/// +/// `a` and `b` each must be of length 32, and must each be scalars of BLS12-381. +/// +/// The result of the merkle tree hash is placed in `result`, which must also be +/// of length 32. #[no_mangle] pub extern "C" fn librustzcash_merkle_hash( depth: size_t, @@ -358,7 +372,7 @@ fn test_gen_r() { let _ = Fs::from_repr(repr).unwrap(); } -/// Return 32 byte random scalar, uniformly. +/// Generate uniformly random scalar in Jubjub. The result is of length 32. #[no_mangle] pub extern "C" fn librustzcash_sapling_generate_r(result: *mut [c_uchar; 32]) { // create random 64 byte buffer @@ -413,7 +427,12 @@ fn priv_get_note( Ok(note) } -/// Compute Sapling note nullifier. +/// Compute a Sapling nullifier. +/// +/// The `diversifier` parameter must be 11 bytes in length. +/// The `pk_d`, `r`, `ak` and `nk` parameters must be of length 32. +/// The result is also of length 32 and placed in `result`. +/// Returns false if `diversifier` or `pk_d` is not valid. #[no_mangle] pub extern "C" fn librustzcash_sapling_compute_nf( diversifier: *const [c_uchar; 11], @@ -458,7 +477,12 @@ pub extern "C" fn librustzcash_sapling_compute_nf( true } -/// Compute Sapling note commitment. +/// Compute a Sapling commitment. +/// +/// The `diversifier` parameter must be 11 bytes in length. +/// The `pk_d` and `r` parameters must be of length 32. +/// The result is also of length 32 and placed in `result`. +/// Returns false if `diversifier` or `pk_d` is not valid. #[no_mangle] pub extern "C" fn librustzcash_sapling_compute_cm( diversifier: *const [c_uchar; 11], @@ -481,6 +505,10 @@ pub extern "C" fn librustzcash_sapling_compute_cm( true } +/// Compute [sk] [8] P for some 32-byte point P, and 32-byte Fs. +/// +/// If P or sk are invalid, returns false. Otherwise, the result is written to +/// the 32-byte `result` buffer. #[no_mangle] pub extern "C" fn librustzcash_sapling_ka_agree( p: *const [c_uchar; 32], @@ -509,6 +537,9 @@ pub extern "C" fn librustzcash_sapling_ka_agree( true } +/// Compute g_d = GH(diversifier) and returns false if the diversifier is +/// invalid. Computes [esk] g_d and writes the result to the 32-byte `result` +/// buffer. Returns false if `esk` is not a valid scalar. #[no_mangle] pub extern "C" fn librustzcash_sapling_ka_derivepublic( diversifier: *const [c_uchar; 11], @@ -537,6 +568,8 @@ pub extern "C" fn librustzcash_sapling_ka_derivepublic( true } +/// Validates the provided Equihash solution against the given parameters, input +/// and nonce. #[no_mangle] pub extern "C" fn librustzcash_eh_isvalid( n: u32, @@ -557,6 +590,7 @@ pub extern "C" fn librustzcash_eh_isvalid( equihash::is_valid_solution(n, k, rs_input, rs_nonce, rs_soln) } +/// Creates a Sapling verification context. Please free this when you're done. #[no_mangle] pub extern "C" fn librustzcash_sapling_verification_ctx_init() -> *mut SaplingVerificationContext { let ctx = Box::new(SaplingVerificationContext::new()); @@ -564,6 +598,8 @@ pub extern "C" fn librustzcash_sapling_verification_ctx_init() -> *mut SaplingVe Box::into_raw(ctx) } +/// Frees a Sapling verification context returned from +/// [`librustzcash_sapling_verification_ctx_init`]. #[no_mangle] pub extern "C" fn librustzcash_sapling_verification_ctx_free(ctx: *mut SaplingVerificationContext) { drop(unsafe { Box::from_raw(ctx) }); @@ -573,6 +609,8 @@ const GROTH_PROOF_SIZE: usize = 48 // π_A + 96 // π_B + 48; // π_C +/// Check the validity of a Sapling Spend description, accumulating the value +/// commitment into the context. #[no_mangle] pub extern "C" fn librustzcash_sapling_check_spend( ctx: *mut SaplingVerificationContext, @@ -628,6 +666,8 @@ pub extern "C" fn librustzcash_sapling_check_spend( ) } +/// Check the validity of a Sapling Output description, accumulating the value +/// commitment into the context. #[no_mangle] pub extern "C" fn librustzcash_sapling_check_output( ctx: *mut SaplingVerificationContext, @@ -671,6 +711,8 @@ pub extern "C" fn librustzcash_sapling_check_output( ) } +/// Finally checks the validity of the entire Sapling transaction given +/// valueBalance and the binding signature. #[no_mangle] pub extern "C" fn librustzcash_sapling_final_check( ctx: *mut SaplingVerificationContext, @@ -697,6 +739,7 @@ pub extern "C" fn librustzcash_sapling_final_check( ) } +/// Sprout JoinSplit proof generation. #[no_mangle] pub extern "C" fn librustzcash_sprout_prove( proof_out: *mut [c_uchar; GROTH_PROOF_SIZE], @@ -846,6 +889,7 @@ pub extern "C" fn librustzcash_sprout_prove( .expect("should be able to serialize a proof"); } +/// Sprout JoinSplit proof verification. #[no_mangle] pub extern "C" fn librustzcash_sprout_verify( proof: *const [c_uchar; GROTH_PROOF_SIZE], @@ -895,6 +939,8 @@ pub extern "C" fn librustzcash_sprout_verify( } } +/// This function (using the proving context) constructs an Output proof given +/// the necessary witness information. It outputs `cv` and the `zkproof`. #[no_mangle] pub extern "C" fn librustzcash_sapling_output_proof( ctx: *mut SaplingProvingContext, @@ -947,6 +993,11 @@ pub extern "C" fn librustzcash_sapling_output_proof( true } +/// Computes the signature for each Spend description, given the key `ask`, the +/// re-randomization `ar`, the 32-byte sighash `sighash`, and an output `result` +/// buffer of 64-bytes for the signature. +/// +/// This function will fail if the provided `ask` or `ar` are invalid. #[no_mangle] pub extern "C" fn librustzcash_sapling_spend_sig( ask: *const [c_uchar; 32], @@ -979,6 +1030,10 @@ pub extern "C" fn librustzcash_sapling_spend_sig( true } +/// This function (using the proving context) constructs a binding signature. +/// +/// You must provide the intended valueBalance so that we can internally check +/// consistency. #[no_mangle] pub extern "C" fn librustzcash_sapling_binding_sig( ctx: *const SaplingProvingContext, @@ -1004,6 +1059,9 @@ pub extern "C" fn librustzcash_sapling_binding_sig( true } +/// This function (using the proving context) constructs a Spend proof given the +/// necessary witness information. It outputs `cv` (the value commitment) and +/// `rk` (so that you don't have to compute it) along with the proof. #[no_mangle] pub extern "C" fn librustzcash_sapling_spend_proof( ctx: *mut SaplingProvingContext, @@ -1104,6 +1162,7 @@ pub extern "C" fn librustzcash_sapling_spend_proof( true } +/// Creates a Sapling proving context. Please free this when you're done. #[no_mangle] pub extern "C" fn librustzcash_sapling_proving_ctx_init() -> *mut SaplingProvingContext { let ctx = Box::new(SaplingProvingContext::new()); @@ -1111,11 +1170,14 @@ pub extern "C" fn librustzcash_sapling_proving_ctx_init() -> *mut SaplingProving Box::into_raw(ctx) } +/// Frees a Sapling proving context returned from +/// [`librustzcash_sapling_proving_ctx_init`]. #[no_mangle] pub extern "C" fn librustzcash_sapling_proving_ctx_free(ctx: *mut SaplingProvingContext) { drop(unsafe { Box::from_raw(ctx) }); } +/// Derive the master ExtendedSpendingKey from a seed. #[no_mangle] pub extern "C" fn librustzcash_zip32_xsk_master( seed: *const c_uchar, @@ -1130,6 +1192,7 @@ pub extern "C" fn librustzcash_zip32_xsk_master( .expect("should be able to serialize an ExtendedSpendingKey"); } +/// Derive a child ExtendedSpendingKey from a parent. #[no_mangle] pub extern "C" fn librustzcash_zip32_xsk_derive( xsk_parent: *const [c_uchar; 169], @@ -1146,6 +1209,7 @@ pub extern "C" fn librustzcash_zip32_xsk_derive( .expect("should be able to serialize an ExtendedSpendingKey"); } +/// Derive a child ExtendedFullViewingKey from a parent. #[no_mangle] pub extern "C" fn librustzcash_zip32_xfvk_derive( xfvk_parent: *const [c_uchar; 169], @@ -1167,6 +1231,7 @@ pub extern "C" fn librustzcash_zip32_xfvk_derive( true } +/// Derive a PaymentAddress from an ExtendedFullViewingKey. #[no_mangle] pub extern "C" fn librustzcash_zip32_xfvk_address( xfvk: *const [c_uchar; 169], From 8651bb41cea564f27e8bc02be6719e92ff5e1d14 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Tue, 5 Nov 2019 02:49:13 +0000 Subject: [PATCH 06/10] Clean up librustzcash imports --- librustzcash/src/rustzcash.rs | 44 ++++++++++++++--------------------- 1 file changed, 18 insertions(+), 26 deletions(-) diff --git a/librustzcash/src/rustzcash.rs b/librustzcash/src/rustzcash.rs index 0168e0d..f166bd9 100644 --- a/librustzcash/src/rustzcash.rs +++ b/librustzcash/src/rustzcash.rs @@ -1,38 +1,20 @@ // Catch documentation errors caused by code changes. #![deny(intra_doc_link_resolution_failure)] -use lazy_static; - -use ff::{PrimeField, PrimeFieldRepr}; -use pairing::bls12_381::{Bls12, Fr, FrRepr}; - -use zcash_primitives::{ - constants::CRH_IVK_PERSONALIZATION, - jubjub::{ - edwards, - fs::{Fs, FsRepr}, - FixedGenerators, JubjubEngine, JubjubParams, PrimeOrder, ToUniform, Unknown, - }, +use bellman::{ + gadgets::multipack, + groth16::{create_random_proof, verify_proof, Parameters, PreparedVerifyingKey, Proof}, }; - -use zcash_proofs::circuit::sapling::TREE_DEPTH as SAPLING_TREE_DEPTH; -use zcash_proofs::circuit::sprout::{self, TREE_DEPTH as SPROUT_TREE_DEPTH}; - -use bellman::gadgets::multipack; -use bellman::groth16::{ - create_random_proof, verify_proof, Parameters, PreparedVerifyingKey, Proof, -}; - use blake2s_simd::Params as Blake2sParams; - use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; - -use rand_core::{OsRng, RngCore}; -use std::io::BufReader; - +use ff::{PrimeField, PrimeFieldRepr}; +use lazy_static; use libc::{c_char, c_uchar, size_t}; +use pairing::bls12_381::{Bls12, Fr, FrRepr}; +use rand_core::{OsRng, RngCore}; use std::ffi::CStr; use std::fs::File; +use std::io::BufReader; use std::path::{Path, PathBuf}; use std::slice; @@ -48,6 +30,12 @@ use std::os::windows::ffi::OsStringExt; use zcash_primitives::{ block::equihash, + constants::CRH_IVK_PERSONALIZATION, + jubjub::{ + edwards, + fs::{Fs, FsRepr}, + FixedGenerators, JubjubEngine, JubjubParams, PrimeOrder, ToUniform, Unknown, + }, merkle_tree::CommitmentTreeWitness, note_encryption::sapling_ka_agree, primitives::{Diversifier, Note, PaymentAddress, ProofGenerationKey, ViewingKey}, @@ -57,6 +45,10 @@ use zcash_primitives::{ zip32, JUBJUB, }; use zcash_proofs::{ + circuit::{ + sapling::TREE_DEPTH as SAPLING_TREE_DEPTH, + sprout::{self, TREE_DEPTH as SPROUT_TREE_DEPTH}, + }, load_parameters, sapling::{SaplingProvingContext, SaplingVerificationContext}, }; From de5943aea4a44d7e748ac4b038512bf7e734fb6e Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Tue, 5 Nov 2019 02:50:42 +0000 Subject: [PATCH 07/10] Ignore clippy::not_unsafe_ptr_arg_deref lint --- librustzcash/src/rustzcash.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/librustzcash/src/rustzcash.rs b/librustzcash/src/rustzcash.rs index f166bd9..83d24d0 100644 --- a/librustzcash/src/rustzcash.rs +++ b/librustzcash/src/rustzcash.rs @@ -1,5 +1,19 @@ // Catch documentation errors caused by code changes. #![deny(intra_doc_link_resolution_failure)] +// Clippy has a default-deny lint to prevent dereferencing raw pointer arguments +// in a non-unsafe function. However, declaring a function as unsafe has the +// side-effect that the entire function body is treated as an unsafe {} block, +// and rustc will not enforce full safety checks on the parts of the function +// that would otherwise be safe. +// +// The functions in this crate are all for FFI usage, so it's obvious to the +// caller (which is only ever zcashd) that the arguments must satisfy the +// necessary assumptions. We therefore ignore this lint to retain the benefit of +// explicitly annotating the parts of each function that must themselves satisfy +// assumptions of underlying code. +// +// See https://github.com/rust-lang/rfcs/pull/2585 for more background. +#![allow(clippy::not_unsafe_ptr_arg_deref)] use bellman::{ gadgets::multipack, From d1bc61800c162839dbbbf6e2e4fb55d33388fdde Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Wed, 6 Nov 2019 08:58:42 +0000 Subject: [PATCH 08/10] Move Sprout proof logic into zcash_proofs --- Cargo.lock | 1 - librustzcash/Cargo.toml | 1 - librustzcash/src/rustzcash.rs | 177 ++++++++------------------------- zcash_proofs/src/lib.rs | 1 + zcash_proofs/src/sprout.rs | 179 ++++++++++++++++++++++++++++++++++ 5 files changed, 222 insertions(+), 137 deletions(-) create mode 100644 zcash_proofs/src/sprout.rs diff --git a/Cargo.lock b/Cargo.lock index d9cbf75..3ab00a0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -362,7 +362,6 @@ dependencies = [ "bellman 0.2.0", "blake2b_simd 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)", "blake2s_simd 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "ff 0.5.0", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/librustzcash/Cargo.toml b/librustzcash/Cargo.toml index 432222b..8f33896 100644 --- a/librustzcash/Cargo.toml +++ b/librustzcash/Cargo.toml @@ -27,7 +27,6 @@ ff = { version = "0.5.0", path = "../ff" } libc = "0.2" pairing = { version = "0.15.0", path = "../pairing" } lazy_static = "1" -byteorder = "1" rand_core = "0.5.1" zcash_primitives = { version = "0.1.0", path = "../zcash_primitives" } zcash_proofs = { version = "0.1.0", path = "../zcash_proofs" } diff --git a/librustzcash/src/rustzcash.rs b/librustzcash/src/rustzcash.rs index 83d24d0..98c9ff7 100644 --- a/librustzcash/src/rustzcash.rs +++ b/librustzcash/src/rustzcash.rs @@ -15,12 +15,8 @@ // See https://github.com/rust-lang/rfcs/pull/2585 for more background. #![allow(clippy::not_unsafe_ptr_arg_deref)] -use bellman::{ - gadgets::multipack, - groth16::{create_random_proof, verify_proof, Parameters, PreparedVerifyingKey, Proof}, -}; +use bellman::groth16::{Parameters, PreparedVerifyingKey, Proof}; use blake2s_simd::Params as Blake2sParams; -use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; use ff::{PrimeField, PrimeFieldRepr}; use lazy_static; use libc::{c_char, c_uchar, size_t}; @@ -59,12 +55,10 @@ use zcash_primitives::{ zip32, JUBJUB, }; use zcash_proofs::{ - circuit::{ - sapling::TREE_DEPTH as SAPLING_TREE_DEPTH, - sprout::{self, TREE_DEPTH as SPROUT_TREE_DEPTH}, - }, + circuit::sapling::TREE_DEPTH as SAPLING_TREE_DEPTH, load_parameters, sapling::{SaplingProvingContext, SaplingVerificationContext}, + sprout, }; #[cfg(test)] @@ -759,14 +753,14 @@ pub extern "C" fn librustzcash_sprout_prove( in_value1: u64, in_rho1: *const [c_uchar; 32], in_r1: *const [c_uchar; 32], - in_auth1: *const [c_uchar; 1 + 33 * SPROUT_TREE_DEPTH + 8], + in_auth1: *const [c_uchar; sprout::WITNESS_PATH_SIZE], // Second input in_sk2: *const [c_uchar; 32], in_value2: u64, in_rho2: *const [c_uchar; 32], in_r2: *const [c_uchar; 32], - in_auth2: *const [c_uchar; 1 + 33 * SPROUT_TREE_DEPTH + 8], + in_auth2: *const [c_uchar; sprout::WITNESS_PATH_SIZE], // First output out_pk1: *const [c_uchar; 32], @@ -782,94 +776,6 @@ pub extern "C" fn librustzcash_sprout_prove( vpub_old: u64, vpub_new: u64, ) { - let phi = unsafe { *phi }; - let rt = unsafe { *rt }; - let h_sig = unsafe { *h_sig }; - let in_sk1 = unsafe { *in_sk1 }; - let in_rho1 = unsafe { *in_rho1 }; - let in_r1 = unsafe { *in_r1 }; - let in_auth1 = unsafe { *in_auth1 }; - let in_sk2 = unsafe { *in_sk2 }; - let in_rho2 = unsafe { *in_rho2 }; - let in_r2 = unsafe { *in_r2 }; - let in_auth2 = unsafe { *in_auth2 }; - let out_pk1 = unsafe { *out_pk1 }; - let out_r1 = unsafe { *out_r1 }; - let out_pk2 = unsafe { *out_pk2 }; - let out_r2 = unsafe { *out_r2 }; - - let mut inputs = Vec::with_capacity(2); - { - let mut handle_input = |sk, value, rho, r, mut auth: &[u8]| { - let value = Some(value); - let rho = Some(sprout::UniqueRandomness(rho)); - let r = Some(sprout::CommitmentRandomness(r)); - let a_sk = Some(sprout::SpendingKey(sk)); - - // skip the first byte - assert_eq!(auth[0], SPROUT_TREE_DEPTH as u8); - auth = &auth[1..]; - - let mut auth_path = [None; SPROUT_TREE_DEPTH]; - for i in (0..SPROUT_TREE_DEPTH).rev() { - // skip length of inner vector - assert_eq!(auth[0], 32); - auth = &auth[1..]; - - let mut sibling = [0u8; 32]; - sibling.copy_from_slice(&auth[0..32]); - auth = &auth[32..]; - - auth_path[i] = Some((sibling, false)); - } - - let mut position = auth - .read_u64::() - .expect("should have had index at the end"); - - for i in 0..SPROUT_TREE_DEPTH { - auth_path[i].as_mut().map(|p| p.1 = (position & 1) == 1); - - position >>= 1; - } - - inputs.push(sprout::JSInput { - value: value, - a_sk: a_sk, - rho: rho, - r: r, - auth_path: auth_path, - }); - }; - - handle_input(in_sk1, in_value1, in_rho1, in_r1, &in_auth1[..]); - handle_input(in_sk2, in_value2, in_rho2, in_r2, &in_auth2[..]); - } - - let mut outputs = Vec::with_capacity(2); - { - let mut handle_output = |a_pk, value, r| { - outputs.push(sprout::JSOutput { - value: Some(value), - a_pk: Some(sprout::PayingKey(a_pk)), - r: Some(sprout::CommitmentRandomness(r)), - }); - }; - - handle_output(out_pk1, out_value1, out_r1); - handle_output(out_pk2, out_value2, out_r2); - } - - let js = sprout::JoinSplit { - vpub_old: Some(vpub_old), - vpub_new: Some(vpub_new), - h_sig: Some(h_sig), - phi: Some(phi), - inputs: inputs, - outputs: outputs, - rt: Some(rt), - }; - // Load parameters from disk let sprout_fs = File::open( unsafe { &SPROUT_GROTH16_PARAMS_PATH } @@ -885,10 +791,30 @@ pub extern "C" fn librustzcash_sprout_prove( drop(sprout_fs); - // Initialize secure RNG - let mut rng = OsRng; - - let proof = create_random_proof(js, ¶ms, &mut rng).expect("proving should not fail"); + let proof = sprout::create_proof( + unsafe { *phi }, + unsafe { *rt }, + unsafe { *h_sig }, + unsafe { *in_sk1 }, + in_value1, + unsafe { *in_rho1 }, + unsafe { *in_r1 }, + unsafe { &*in_auth1 }, + unsafe { *in_sk2 }, + in_value2, + unsafe { *in_rho2 }, + unsafe { *in_r2 }, + unsafe { &*in_auth2 }, + unsafe { *out_pk1 }, + out_value1, + unsafe { *out_r1 }, + unsafe { *out_pk2 }, + out_value2, + unsafe { *out_r2 }, + vpub_old, + vpub_new, + ¶ms, + ); proof .write(&mut (unsafe { &mut *proof_out })[..]) @@ -910,39 +836,20 @@ pub extern "C" fn librustzcash_sprout_verify( vpub_old: u64, vpub_new: u64, ) -> bool { - // Prepare the public input for the verifier - let mut public_input = Vec::with_capacity((32 * 8) + (8 * 2)); - public_input.extend(unsafe { &(&*rt)[..] }); - public_input.extend(unsafe { &(&*h_sig)[..] }); - public_input.extend(unsafe { &(&*nf1)[..] }); - public_input.extend(unsafe { &(&*mac1)[..] }); - public_input.extend(unsafe { &(&*nf2)[..] }); - public_input.extend(unsafe { &(&*mac2)[..] }); - public_input.extend(unsafe { &(&*cm1)[..] }); - public_input.extend(unsafe { &(&*cm2)[..] }); - public_input.write_u64::(vpub_old).unwrap(); - public_input.write_u64::(vpub_new).unwrap(); - - let public_input = multipack::bytes_to_bits(&public_input); - let public_input = multipack::compute_multipacking::(&public_input); - - let proof = match Proof::read(unsafe { &(&*proof)[..] }) { - Ok(p) => p, - Err(_) => return false, - }; - - // Verify the proof - match verify_proof( + sprout::verify_proof( + unsafe { &*proof }, + unsafe { &*rt }, + unsafe { &*h_sig }, + unsafe { &*mac1 }, + unsafe { &*mac2 }, + unsafe { &*nf1 }, + unsafe { &*nf2 }, + unsafe { &*cm1 }, + unsafe { &*cm2 }, + vpub_old, + vpub_new, unsafe { SPROUT_GROTH16_VK.as_ref() }.expect("parameters should have been initialized"), - &proof, - &public_input[..], - ) { - // No error, and proof verification successful - Ok(true) => true, - - // Any other case - _ => false, - } + ) } /// This function (using the proving context) constructs an Output proof given diff --git a/zcash_proofs/src/lib.rs b/zcash_proofs/src/lib.rs index 0a1f9f2..1e8ceb2 100644 --- a/zcash_proofs/src/lib.rs +++ b/zcash_proofs/src/lib.rs @@ -15,6 +15,7 @@ use std::path::Path; pub mod circuit; mod hashreader; pub mod sapling; +pub mod sprout; #[cfg(feature = "local-prover")] pub mod prover; diff --git a/zcash_proofs/src/sprout.rs b/zcash_proofs/src/sprout.rs new file mode 100644 index 0000000..30ec821 --- /dev/null +++ b/zcash_proofs/src/sprout.rs @@ -0,0 +1,179 @@ +//! APIs for creating and verifying Sprout proofs. + +use bellman::{ + gadgets::multipack, + groth16::{self, create_random_proof, Parameters, PreparedVerifyingKey, Proof}, +}; +use pairing::bls12_381::Bls12; +use rand_core::OsRng; + +use crate::circuit::sprout::*; + +const GROTH_PROOF_SIZE: usize = 48 // π_A + + 96 // π_B + + 48; // π_C +pub const WITNESS_PATH_SIZE: usize = 1 + 33 * TREE_DEPTH + 8; + +/// Sprout JoinSplit proof generation. +pub fn create_proof( + phi: [u8; 32], + rt: [u8; 32], + h_sig: [u8; 32], + + // First input + in_sk1: [u8; 32], + in_value1: u64, + in_rho1: [u8; 32], + in_r1: [u8; 32], + in_auth1: &[u8; WITNESS_PATH_SIZE], + + // Second input + in_sk2: [u8; 32], + in_value2: u64, + in_rho2: [u8; 32], + in_r2: [u8; 32], + in_auth2: &[u8; WITNESS_PATH_SIZE], + + // First output + out_pk1: [u8; 32], + out_value1: u64, + out_r1: [u8; 32], + + // Second output + out_pk2: [u8; 32], + out_value2: u64, + out_r2: [u8; 32], + + // Public value + vpub_old: u64, + vpub_new: u64, + + proving_key: &Parameters, +) -> Proof { + let mut inputs = Vec::with_capacity(2); + { + let mut handle_input = |sk, value, rho, r, mut auth: &[u8]| { + let value = Some(value); + let rho = Some(UniqueRandomness(rho)); + let r = Some(CommitmentRandomness(r)); + let a_sk = Some(SpendingKey(sk)); + + // skip the first byte + assert_eq!(auth[0], TREE_DEPTH as u8); + auth = &auth[1..]; + + let mut auth_path = [None; TREE_DEPTH]; + for i in (0..TREE_DEPTH).rev() { + // skip length of inner vector + assert_eq!(auth[0], 32); + auth = &auth[1..]; + + let mut sibling = [0u8; 32]; + sibling.copy_from_slice(&auth[0..32]); + auth = &auth[32..]; + + auth_path[i] = Some((sibling, false)); + } + + let mut position = { + let mut bytes = [0; 8]; + bytes.copy_from_slice(&auth[0..8]); + u64::from_le_bytes(bytes) + }; + + for entry in auth_path.iter_mut() { + if let Some(p) = entry { + p.1 = (position & 1) == 1; + } + + position >>= 1; + } + + inputs.push(JSInput { + value, + a_sk, + rho, + r, + auth_path, + }); + }; + + handle_input(in_sk1, in_value1, in_rho1, in_r1, &in_auth1[..]); + handle_input(in_sk2, in_value2, in_rho2, in_r2, &in_auth2[..]); + } + + let mut outputs = Vec::with_capacity(2); + { + let mut handle_output = |a_pk, value, r| { + outputs.push(JSOutput { + value: Some(value), + a_pk: Some(PayingKey(a_pk)), + r: Some(CommitmentRandomness(r)), + }); + }; + + handle_output(out_pk1, out_value1, out_r1); + handle_output(out_pk2, out_value2, out_r2); + } + + let js = JoinSplit { + vpub_old: Some(vpub_old), + vpub_new: Some(vpub_new), + h_sig: Some(h_sig), + phi: Some(phi), + inputs, + outputs, + rt: Some(rt), + }; + + // Initialize secure RNG + let mut rng = OsRng; + + create_random_proof(js, proving_key, &mut rng).expect("proving should not fail") +} + +/// Sprout JoinSplit proof verification. +pub fn verify_proof( + proof: &[u8; GROTH_PROOF_SIZE], + rt: &[u8; 32], + h_sig: &[u8; 32], + mac1: &[u8; 32], + mac2: &[u8; 32], + nf1: &[u8; 32], + nf2: &[u8; 32], + cm1: &[u8; 32], + cm2: &[u8; 32], + vpub_old: u64, + vpub_new: u64, + verifying_key: &PreparedVerifyingKey, +) -> bool { + // Prepare the public input for the verifier + let mut public_input = Vec::with_capacity((32 * 8) + (8 * 2)); + public_input.extend(rt); + public_input.extend(h_sig); + public_input.extend(nf1); + public_input.extend(mac1); + public_input.extend(nf2); + public_input.extend(mac2); + public_input.extend(cm1); + public_input.extend(cm2); + public_input.extend(&vpub_old.to_le_bytes()); + public_input.extend(&vpub_new.to_le_bytes()); + + let public_input = multipack::bytes_to_bits(&public_input); + let public_input = multipack::compute_multipacking::(&public_input); + + let proof = match Proof::read(&proof[..]) { + Ok(p) => p, + Err(_) => return false, + }; + + // Verify the proof + match groth16::verify_proof(verifying_key, &proof, &public_input[..]) { + // No error, and proof verification successful + Ok(true) => true, + + // Any other case + _ => false, + } +} From 4ae238ea1fe0bc4f4ea1a68523095b65b889864b Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Mon, 11 Nov 2019 10:21:38 +0000 Subject: [PATCH 09/10] librustzcash crate doc --- librustzcash/src/rustzcash.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/librustzcash/src/rustzcash.rs b/librustzcash/src/rustzcash.rs index 98c9ff7..068fb77 100644 --- a/librustzcash/src/rustzcash.rs +++ b/librustzcash/src/rustzcash.rs @@ -1,3 +1,7 @@ +//! FFI between the C++ zcashd codebase and the Rust Zcash crates. +//! +//! This is internal to zcashd and is not an officially-supported API. + // Catch documentation errors caused by code changes. #![deny(intra_doc_link_resolution_failure)] // Clippy has a default-deny lint to prevent dereferencing raw pointer arguments From 60eac4e8b72a39cebd336151a85bb63938332ee1 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Mon, 11 Nov 2019 10:29:36 +0000 Subject: [PATCH 10/10] Escape non-link square brackets in comments --- librustzcash/src/rustzcash.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/librustzcash/src/rustzcash.rs b/librustzcash/src/rustzcash.rs index 068fb77..9c199dd 100644 --- a/librustzcash/src/rustzcash.rs +++ b/librustzcash/src/rustzcash.rs @@ -509,7 +509,7 @@ pub extern "C" fn librustzcash_sapling_compute_cm( true } -/// Compute [sk] [8] P for some 32-byte point P, and 32-byte Fs. +/// Computes \[sk\] \[8\] P for some 32-byte point P, and 32-byte Fs. /// /// If P or sk are invalid, returns false. Otherwise, the result is written to /// the 32-byte `result` buffer. @@ -542,7 +542,7 @@ pub extern "C" fn librustzcash_sapling_ka_agree( } /// Compute g_d = GH(diversifier) and returns false if the diversifier is -/// invalid. Computes [esk] g_d and writes the result to the 32-byte `result` +/// invalid. Computes \[esk\] g_d and writes the result to the 32-byte `result` /// buffer. Returns false if `esk` is not a valid scalar. #[no_mangle] pub extern "C" fn librustzcash_sapling_ka_derivepublic(