Do not confuse a NoteCommitment for U(NoteCommitment)

This commit is contained in:
Deirdre Connolly 2020-07-14 04:56:53 -04:00 committed by Deirdre Connolly
parent 0f46a9b6a8
commit 33730a05cb
10 changed files with 189 additions and 62 deletions

View File

@ -3,6 +3,9 @@ mod memo;
pub mod sapling; pub mod sapling;
pub mod sprout; pub mod sprout;
#[cfg(test)]
mod tests;
/// The randomness used in the Pedersen Hash for note commitment. /// The randomness used in the Pedersen Hash for note commitment.
#[derive(Copy, Clone, Debug, PartialEq)] #[derive(Copy, Clone, Debug, PartialEq)]
pub struct NoteCommitmentRandomness(pub [u8; 32]); pub struct NoteCommitmentRandomness(pub [u8; 32]);

View File

@ -15,7 +15,7 @@ use crate::{
}; };
/// A Nullifier for Sapling transactions /// A Nullifier for Sapling transactions
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
#[cfg_attr(test, derive(proptest_derive::Arbitrary))] #[cfg_attr(test, derive(proptest_derive::Arbitrary))]
pub struct Nullifier([u8; 32]); pub struct Nullifier([u8; 32]);
@ -41,7 +41,7 @@ impl ZcashSerialize for Nullifier {
/// The randomness used in the Pedersen Hash for note commitment. /// The randomness used in the Pedersen Hash for note commitment.
#[derive(Copy, Clone, Debug, PartialEq)] #[derive(Copy, Clone, Debug, PartialEq)]
pub struct NoteCommitmentRandomness(redjubjub::Randomizer); pub struct CommitmentRandomness(redjubjub::Randomizer);
/// A Note represents that a value is spendable by the recipient who /// A Note represents that a value is spendable by the recipient who
/// holds the spending key corresponding to a given shielded payment /// holds the spending key corresponding to a given shielded payment
@ -50,7 +50,7 @@ pub struct Note {
diversifier: Diversifier, diversifier: Diversifier,
transmission_key: TransmissionKey, transmission_key: TransmissionKey,
value: Amount<NonNegative>, value: Amount<NonNegative>,
rcm: NoteCommitmentRandomness, rcm: CommitmentRandomness,
} }
impl Note { impl Note {
@ -60,23 +60,60 @@ impl Note {
/// ///
/// https://zips.z.cash/protocol/protocol.pdf#concretewindowedcommit /// https://zips.z.cash/protocol/protocol.pdf#concretewindowedcommit
pub fn commit(&self) -> NoteCommitment { pub fn commit(&self) -> NoteCommitment {
// Windowed Pedersen Commitment
// NoteCommitment()
unimplemented!() unimplemented!()
} }
} }
/// ///
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Deserialize, PartialEq, Serialize)]
//#[cfg_attr(test, derive(proptest_derive::Arbitrary))] //#[cfg_attr(test, derive(proptest_derive::Arbitrary))]
pub struct NoteCommitment(jubjub::ExtendedPoint); pub struct NoteCommitment(#[serde(with = "serde_helpers::AffinePoint")] pub jubjub::AffinePoint);
impl fmt::Debug for NoteCommitment {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("NoteCommitment")
.field("u", &hex::encode(self.0.get_u().to_bytes()))
.field("v", &hex::encode(self.0.get_v().to_bytes()))
.finish()
}
}
impl From<[u8; 32]> for NoteCommitment {
fn from(bytes: [u8; 32]) -> Self {
Self(jubjub::AffinePoint::from_bytes(bytes).unwrap())
}
}
impl Eq for NoteCommitment {}
impl From<NoteCommitment> for [u8; 32] {
fn from(cm: NoteCommitment) -> [u8; 32] {
cm.0.to_bytes()
}
}
impl ZcashSerialize for NoteCommitment {
// The u-coordinate of the note commitment, for the output note
// LEBS2OSP256(cm_u) where cm_u = Extract_J(r)(cm). ???
fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
writer.write_all(&self.0.to_bytes())?;
Ok(())
}
}
impl ZcashDeserialize for NoteCommitment {
fn zcash_deserialize<R: io::Read>(mut reader: R) -> Result<Self, SerializationError> {
Ok(Self(
jubjub::AffinePoint::from_bytes(reader.read_32_bytes()?).unwrap(),
))
}
}
/// The decrypted form of encrypted Sapling notes on the blockchain. /// The decrypted form of encrypted Sapling notes on the blockchain.
pub struct NotePlaintext { pub struct NotePlaintext {
diversifier: Diversifier, diversifier: Diversifier,
value: Amount<NonNegative>, value: Amount<NonNegative>,
rcm: NoteCommitmentRandomness, rcm: CommitmentRandomness,
memo: memo::Memo, memo: memo::Memo,
} }
@ -212,8 +249,51 @@ impl Arbitrary for OutCiphertext {
/// Spend and Output Descriptions. /// Spend and Output Descriptions.
/// ///
/// https://zips.z.cash/protocol/protocol.pdf#concretehomomorphiccommit /// https://zips.z.cash/protocol/protocol.pdf#concretehomomorphiccommit
#[derive(Clone, Copy, Debug)] #[derive(Clone, Deserialize, PartialEq, Serialize)]
pub struct ValueCommitment(pub jubjub::ExtendedPoint); //#[cfg_attr(test, derive(proptest_derive::Arbitrary))]
pub struct ValueCommitment(#[serde(with = "serde_helpers::AffinePoint")] pub jubjub::AffinePoint);
impl fmt::Debug for ValueCommitment {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("ValueCommitment")
.field("u", &hex::encode(self.0.get_u().to_bytes()))
.field("v", &hex::encode(self.0.get_v().to_bytes()))
.finish()
}
}
impl From<[u8; 32]> for ValueCommitment {
fn from(bytes: [u8; 32]) -> Self {
Self(jubjub::AffinePoint::from_bytes(bytes).unwrap())
}
}
impl Eq for ValueCommitment {}
impl From<ValueCommitment> for [u8; 32] {
fn from(cm: ValueCommitment) -> [u8; 32] {
cm.0.to_bytes()
}
}
/// LEBS2OSP256(repr_J(cv))
///
/// https://zips.z.cash/protocol/protocol.pdf#spendencoding
/// https://zips.z.cash/protocol/protocol.pdf#jubjub
impl ZcashSerialize for ValueCommitment {
fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
writer.write_all(&self.0.to_bytes())?;
Ok(())
}
}
impl ZcashDeserialize for ValueCommitment {
fn zcash_deserialize<R: io::Read>(mut reader: R) -> Result<Self, SerializationError> {
Ok(Self(
jubjub::AffinePoint::from_bytes(reader.read_32_bytes()?).unwrap(),
))
}
}
#[cfg(test)] #[cfg(test)]
proptest! { proptest! {

View File

@ -96,9 +96,9 @@ impl ZcashSerialize for Nullifier {
/// The randomness used in the Pedersen Hash for note commitment. /// The randomness used in the Pedersen Hash for note commitment.
#[derive(Copy, Clone, Debug, PartialEq)] #[derive(Copy, Clone, Debug, PartialEq)]
#[cfg_attr(test, derive(proptest_derive::Arbitrary))] #[cfg_attr(test, derive(proptest_derive::Arbitrary))]
pub struct NoteCommitmentRandomness(pub [u8; 32]); pub struct CommitmentRandomness(pub [u8; 32]);
impl AsRef<[u8]> for NoteCommitmentRandomness { impl AsRef<[u8]> for CommitmentRandomness {
fn as_ref(&self) -> &[u8] { fn as_ref(&self) -> &[u8] {
&self.0 &self.0
} }
@ -125,7 +125,7 @@ pub struct Note {
/// Input to PRF^nf to derive the nullifier of the note /// Input to PRF^nf to derive the nullifier of the note
rho: NullifierSeed, rho: NullifierSeed,
/// A random commitment trapdoor /// A random commitment trapdoor
rcm: NoteCommitmentRandomness, rcm: CommitmentRandomness,
} }
impl Note { impl Note {
@ -148,7 +148,7 @@ impl Note {
pub struct NotePlaintext { pub struct NotePlaintext {
value: Amount<NonNegative>, value: Amount<NonNegative>,
rho: NullifierSeed, rho: NullifierSeed,
rcm: NoteCommitmentRandomness, rcm: CommitmentRandomness,
memo: Memo, memo: Memo,
} }

View File

@ -0,0 +1 @@
mod arbitrary;

View File

@ -0,0 +1,33 @@
use crate::notes::sapling;
use proptest::{array, prelude::*};
impl Arbitrary for sapling::NoteCommitment {
type Parameters = ();
fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
array::uniform32(any::<u8>())
.prop_filter("Valid jubjub::AffinePoint", |b| {
jubjub::AffinePoint::from_bytes(*b).is_some().unwrap_u8() == 1
})
.prop_map(Self::from)
.boxed()
}
type Strategy = BoxedStrategy<Self>;
}
impl Arbitrary for sapling::ValueCommitment {
type Parameters = ();
fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
array::uniform32(any::<u8>())
.prop_filter("Valid jubjub::AffinePoint", |b| {
jubjub::AffinePoint::from_bytes(*b).is_some().unwrap_u8() == 1
})
.prop_map(Self::from)
.boxed()
}
type Strategy = BoxedStrategy<Self>;
}

View File

@ -23,6 +23,19 @@ impl From<AffinePoint> for jubjub::AffinePoint {
} }
} }
#[derive(Deserialize, Serialize)]
#[serde(remote = "jubjub::Fq")]
pub(crate) struct Fq {
#[serde(getter = "jubjub::Fq::to_bytes")]
bytes: [u8; 32],
}
impl From<Fq> for jubjub::Fq {
fn from(local: Fq) -> Self {
jubjub::Fq::from_bytes(&local.bytes).unwrap()
}
}
#[derive(Deserialize, Serialize)] #[derive(Deserialize, Serialize)]
#[serde(remote = "futures::future::Either")] #[serde(remote = "futures::future::Either")]
pub(crate) enum Either<A, B> { pub(crate) enum Either<A, B> {

View File

@ -21,8 +21,6 @@ pub struct JoinSplit<P: ZkSnarkProof> {
/// XXX refine type /// XXX refine type
pub anchor: [u8; 32], pub anchor: [u8; 32],
/// A nullifier for the input notes. /// A nullifier for the input notes.
///
/// XXX refine type to [T; 2] -- there are two nullifiers
pub nullifiers: [crate::notes::sprout::Nullifier; 2], pub nullifiers: [crate::notes::sprout::Nullifier; 2],
/// A note commitment for this output note. /// A note commitment for this output note.
/// ///

View File

@ -8,12 +8,14 @@ use std::{
sync::Arc, sync::Arc,
}; };
use crate::notes; use crate::{
use crate::proofs::ZkSnarkProof; notes,
use crate::serialization::{ proofs::ZkSnarkProof,
ReadZcashExt, SerializationError, WriteZcashExt, ZcashDeserialize, ZcashSerialize, serialization::{
ReadZcashExt, SerializationError, WriteZcashExt, ZcashDeserialize, ZcashSerialize,
},
types,
}; };
use crate::types::Script;
use super::*; use super::*;
@ -211,7 +213,7 @@ impl ZcashDeserialize for TransparentInput {
hash: TransactionHash(bytes), hash: TransactionHash(bytes),
index: reader.read_u32::<LittleEndian>()?, index: reader.read_u32::<LittleEndian>()?,
}, },
unlock_script: Script::zcash_deserialize(&mut reader)?, unlock_script: types::Script::zcash_deserialize(&mut reader)?,
sequence: reader.read_u32::<LittleEndian>()?, sequence: reader.read_u32::<LittleEndian>()?,
}) })
} }
@ -230,7 +232,7 @@ impl ZcashDeserialize for TransparentOutput {
fn zcash_deserialize<R: io::Read>(mut reader: R) -> Result<Self, SerializationError> { fn zcash_deserialize<R: io::Read>(mut reader: R) -> Result<Self, SerializationError> {
Ok(TransparentOutput { Ok(TransparentOutput {
value: reader.read_u64::<LittleEndian>()?.try_into()?, value: reader.read_u64::<LittleEndian>()?.try_into()?,
lock_script: Script::zcash_deserialize(&mut reader)?, lock_script: tyoes::Script::zcash_deserialize(&mut reader)?,
}) })
} }
} }
@ -262,15 +264,15 @@ impl<P: ZkSnarkProof> ZcashDeserialize for JoinSplit<P> {
vpub_new: reader.read_u64::<LittleEndian>()?.try_into()?, vpub_new: reader.read_u64::<LittleEndian>()?.try_into()?,
anchor: reader.read_32_bytes()?, anchor: reader.read_32_bytes()?,
nullifiers: [ nullifiers: [
crate::notes::sprout::Nullifier::zcash_deserialize(&mut reader)?, notes::sprout::Nullifier::zcash_deserialize(&mut reader)?,
crate::notes::sprout::Nullifier::zcash_deserialize(&mut reader)?, notes::sprout::Nullifier::zcash_deserialize(&mut reader)?,
], ],
commitments: [reader.read_32_bytes()?, reader.read_32_bytes()?], commitments: [reader.read_32_bytes()?, reader.read_32_bytes()?],
ephemeral_key: x25519_dalek::PublicKey::from(reader.read_32_bytes()?), ephemeral_key: x25519_dalek::PublicKey::from(reader.read_32_bytes()?),
random_seed: reader.read_32_bytes()?, random_seed: reader.read_32_bytes()?,
vmacs: [ vmacs: [
crate::types::MAC::zcash_deserialize(&mut reader)?, types::MAC::zcash_deserialize(&mut reader)?,
crate::types::MAC::zcash_deserialize(&mut reader)?, types::MAC::zcash_deserialize(&mut reader)?,
], ],
zkproof: P::zcash_deserialize(&mut reader)?, zkproof: P::zcash_deserialize(&mut reader)?,
enc_ciphertexts: [ enc_ciphertexts: [
@ -319,7 +321,7 @@ impl<P: ZkSnarkProof> ZcashDeserialize for Option<JoinSplitData<P>> {
impl ZcashSerialize for Spend { impl ZcashSerialize for Spend {
fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> { fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
writer.write_all(&self.cv[..])?; self.cv.zcash_serialize(&mut writer)?;
writer.write_all(&self.anchor.0[..])?; writer.write_all(&self.anchor.0[..])?;
self.nullifier.zcash_serialize(&mut writer)?; self.nullifier.zcash_serialize(&mut writer)?;
writer.write_all(&<[u8; 32]>::from(self.rk)[..])?; writer.write_all(&<[u8; 32]>::from(self.rk)[..])?;
@ -333,9 +335,9 @@ impl ZcashDeserialize for Spend {
fn zcash_deserialize<R: io::Read>(mut reader: R) -> Result<Self, SerializationError> { fn zcash_deserialize<R: io::Read>(mut reader: R) -> Result<Self, SerializationError> {
use crate::treestate::note_commitment_tree::SaplingNoteTreeRootHash; use crate::treestate::note_commitment_tree::SaplingNoteTreeRootHash;
Ok(Spend { Ok(Spend {
cv: reader.read_32_bytes()?, cv: notes::sapling::ValueCommitment::zcash_deserialize(&mut reader)?,
anchor: SaplingNoteTreeRootHash(reader.read_32_bytes()?), anchor: SaplingNoteTreeRootHash(reader.read_32_bytes()?),
nullifier: crate::notes::sapling::Nullifier::zcash_deserialize(&mut reader)?, nullifier: notes::sapling::Nullifier::zcash_deserialize(&mut reader)?,
rk: reader.read_32_bytes()?.into(), rk: reader.read_32_bytes()?.into(),
zkproof: Groth16Proof::zcash_deserialize(&mut reader)?, zkproof: Groth16Proof::zcash_deserialize(&mut reader)?,
spend_auth_sig: reader.read_64_bytes()?.into(), spend_auth_sig: reader.read_64_bytes()?.into(),
@ -345,8 +347,8 @@ impl ZcashDeserialize for Spend {
impl ZcashSerialize for Output { impl ZcashSerialize for Output {
fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> { fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
writer.write_all(&self.cv[..])?; self.cv.zcash_serialize(&mut writer)?;
writer.write_all(&self.cmu[..])?; writer.write_all(&self.cm_u.to_bytes())?;
writer.write_all(&self.ephemeral_key.to_bytes())?; writer.write_all(&self.ephemeral_key.to_bytes())?;
self.enc_ciphertext.zcash_serialize(&mut writer)?; self.enc_ciphertext.zcash_serialize(&mut writer)?;
self.out_ciphertext.zcash_serialize(&mut writer)?; self.out_ciphertext.zcash_serialize(&mut writer)?;
@ -358,8 +360,8 @@ impl ZcashSerialize for Output {
impl ZcashDeserialize for Output { impl ZcashDeserialize for Output {
fn zcash_deserialize<R: io::Read>(mut reader: R) -> Result<Self, SerializationError> { fn zcash_deserialize<R: io::Read>(mut reader: R) -> Result<Self, SerializationError> {
Ok(Output { Ok(Output {
cv: reader.read_32_bytes()?, cv: notes::sapling::ValueCommitment::zcash_deserialize(&mut reader)?,
cmu: reader.read_32_bytes()?, cm_u: jubjub::Fq::from_bytes(&reader.read_32_bytes()?).unwrap(),
ephemeral_key: jubjub::AffinePoint::from_bytes(reader.read_32_bytes()?).unwrap(), ephemeral_key: jubjub::AffinePoint::from_bytes(reader.read_32_bytes()?).unwrap(),
enc_ciphertext: notes::sapling::EncryptedCiphertext::zcash_deserialize(&mut reader)?, enc_ciphertext: notes::sapling::EncryptedCiphertext::zcash_deserialize(&mut reader)?,
out_ciphertext: notes::sapling::OutCiphertext::zcash_deserialize(&mut reader)?, out_ciphertext: notes::sapling::OutCiphertext::zcash_deserialize(&mut reader)?,

View File

@ -1,8 +1,10 @@
use crate::notes::sapling; use crate::{
use crate::proofs::Groth16Proof; notes,
use crate::redjubjub::{self, Binding, SpendAuth}; proofs::Groth16Proof,
use crate::serde_helpers; redjubjub::{self, Binding, SpendAuth},
use crate::treestate::note_commitment_tree::SaplingNoteTreeRootHash; serde_helpers,
treestate::note_commitment_tree::SaplingNoteTreeRootHash,
};
use futures::future::Either; use futures::future::Either;
/// A _Spend Description_, as described in [protocol specification §7.3][ps]. /// A _Spend Description_, as described in [protocol specification §7.3][ps].
@ -11,13 +13,11 @@ use futures::future::Either;
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct Spend { pub struct Spend {
/// A value commitment to the value of the input note. /// A value commitment to the value of the input note.
/// pub cv: notes::sapling::ValueCommitment,
/// XXX refine to a specific type.
pub cv: [u8; 32],
/// A root of the Sapling note commitment tree at some block height in the past. /// A root of the Sapling note commitment tree at some block height in the past.
pub anchor: SaplingNoteTreeRootHash, pub anchor: SaplingNoteTreeRootHash,
/// The nullifier of the input note. /// The nullifier of the input note.
pub nullifier: crate::notes::sapling::Nullifier, pub nullifier: notes::sapling::Nullifier,
/// The randomized public key for `spend_auth_sig`. /// The randomized public key for `spend_auth_sig`.
pub rk: redjubjub::VerificationKeyBytes<SpendAuth>, pub rk: redjubjub::VerificationKeyBytes<SpendAuth>,
/// The ZK spend proof. /// The ZK spend proof.
@ -32,20 +32,17 @@ pub struct Spend {
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct Output { pub struct Output {
/// A value commitment to the value of the input note. /// A value commitment to the value of the input note.
/// pub cv: notes::sapling::ValueCommitment,
/// XXX refine to a specific type.
pub cv: [u8; 32],
/// The u-coordinate of the note commitment for the output note. /// The u-coordinate of the note commitment for the output note.
/// #[serde(with = "serde_helpers::Fq")]
/// XXX refine to a specific type. pub cm_u: jubjub::Fq,
pub cmu: [u8; 32],
/// An encoding of an ephemeral Jubjub public key. /// An encoding of an ephemeral Jubjub public key.
#[serde(with = "serde_helpers::AffinePoint")] #[serde(with = "serde_helpers::AffinePoint")]
pub ephemeral_key: jubjub::AffinePoint, pub ephemeral_key: jubjub::AffinePoint,
/// A ciphertext component for the encrypted output note. /// A ciphertext component for the encrypted output note.
pub enc_ciphertext: sapling::EncryptedCiphertext, pub enc_ciphertext: notes::sapling::EncryptedCiphertext,
/// A ciphertext component for the encrypted output note. /// A ciphertext component for the encrypted output note.
pub out_ciphertext: sapling::OutCiphertext, pub out_ciphertext: notes::sapling::OutCiphertext,
/// The ZK output proof. /// The ZK output proof.
pub zkproof: Groth16Proof, pub zkproof: Groth16Proof,
} }

View File

@ -22,7 +22,7 @@ impl<P: ZkSnarkProof + Arbitrary + 'static> Arbitrary for JoinSplit<P> {
any::<Amount<NonNegative>>(), any::<Amount<NonNegative>>(),
any::<Amount<NonNegative>>(), any::<Amount<NonNegative>>(),
array::uniform32(any::<u8>()), array::uniform32(any::<u8>()),
array::uniform2(any::<crate::notes::sprout::Nullifier>()), array::uniform2(any::<sprout::Nullifier>()),
array::uniform2(array::uniform32(any::<u8>())), array::uniform2(array::uniform32(any::<u8>())),
array::uniform32(any::<u8>()), array::uniform32(any::<u8>()),
array::uniform32(any::<u8>()), array::uniform32(any::<u8>()),
@ -94,8 +94,8 @@ impl Arbitrary for Output {
fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy { fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
( (
array::uniform32(any::<u8>()), any::<sapling::ValueCommitment>(),
array::uniform32(any::<u8>()), any::<sapling::NoteCommitment>(),
array::uniform32(any::<u8>()).prop_filter("Valid jubjub::AffinePoint", |b| { array::uniform32(any::<u8>()).prop_filter("Valid jubjub::AffinePoint", |b| {
jubjub::AffinePoint::from_bytes(*b).is_some().unwrap_u8() == 1 jubjub::AffinePoint::from_bytes(*b).is_some().unwrap_u8() == 1
}), }),
@ -104,9 +104,9 @@ impl Arbitrary for Output {
any::<Groth16Proof>(), any::<Groth16Proof>(),
) )
.prop_map( .prop_map(
|(cv, cmu, ephemeral_key_bytes, enc_ciphertext, out_ciphertext, zkproof)| Self { |(cv, cm, ephemeral_key_bytes, enc_ciphertext, out_ciphertext, zkproof)| Self {
cv, cv,
cmu, cm_u: cm.0.get_u(),
ephemeral_key: jubjub::AffinePoint::from_bytes(ephemeral_key_bytes).unwrap(), ephemeral_key: jubjub::AffinePoint::from_bytes(ephemeral_key_bytes).unwrap(),
enc_ciphertext, enc_ciphertext,
out_ciphertext, out_ciphertext,
@ -153,18 +153,18 @@ impl Arbitrary for Spend {
fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy { fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
( (
array::uniform32(any::<u8>()),
any::<SaplingNoteTreeRootHash>(), any::<SaplingNoteTreeRootHash>(),
any::<crate::notes::sapling::Nullifier>(), any::<sapling::ValueCommitment>(),
any::<sapling::Nullifier>(),
array::uniform32(any::<u8>()), array::uniform32(any::<u8>()),
any::<Groth16Proof>(), any::<Groth16Proof>(),
vec(any::<u8>(), 64), vec(any::<u8>(), 64),
) )
.prop_map( .prop_map(
|(cv_bytes, anchor, nullifier_bytes, rpk_bytes, proof, sig_bytes)| Self { |(anchor, cv, nullifier, rpk_bytes, proof, sig_bytes)| Self {
anchor, anchor,
cv: cv_bytes, cv,
nullifier: nullifier_bytes, nullifier,
rk: redjubjub::VerificationKeyBytes::from(rpk_bytes), rk: redjubjub::VerificationKeyBytes::from(rpk_bytes),
zkproof: proof, zkproof: proof,
spend_auth_sig: redjubjub::Signature::from({ spend_auth_sig: redjubjub::Signature::from({