mirror of https://github.com/zcash/orchard.git
Add proptest generators for action and bundle types.
This commit is contained in:
parent
75573d331a
commit
4d89d45332
|
@ -25,6 +25,7 @@ blake2b_simd = "0.5"
|
|||
ff = "0.9"
|
||||
fpe = "0.4"
|
||||
group = "0.9"
|
||||
proptest = { version = "1.0.0", optional = true }
|
||||
rand = "0.8"
|
||||
rand_7 = { package = "rand", version = "0.7" }
|
||||
nonempty = "0.6"
|
||||
|
@ -45,11 +46,13 @@ rev = "f1e76dbc9abf2b68cc609e874fe39f2a15b75b12"
|
|||
[dev-dependencies]
|
||||
criterion = "0.3"
|
||||
hex = "0.4"
|
||||
proptest = "1.0.0"
|
||||
|
||||
[lib]
|
||||
bench = false
|
||||
|
||||
[features]
|
||||
test-dependencies = ["proptest"]
|
||||
|
||||
[[bench]]
|
||||
name = "small"
|
||||
harness = false
|
||||
|
|
|
@ -15,7 +15,7 @@ use crate::{
|
|||
/// let sk = SpendingKey::from_bytes([7; 32]).unwrap();
|
||||
/// let address = FullViewingKey::from(&sk).default_address();
|
||||
/// ```
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Address {
|
||||
d: Diversifier,
|
||||
pk_d: DiversifiedTransmissionKey,
|
||||
|
@ -38,3 +38,21 @@ impl Address {
|
|||
&self.pk_d
|
||||
}
|
||||
}
|
||||
|
||||
/// Generators for property testing.
|
||||
#[cfg(any(test, feature = "test-dependencies"))]
|
||||
pub mod testing {
|
||||
use proptest::prelude::*;
|
||||
|
||||
use crate::keys::{testing::arb_spending_key, FullViewingKey};
|
||||
|
||||
use super::Address;
|
||||
|
||||
prop_compose! {
|
||||
/// Generate an arbitrary random seed
|
||||
pub(crate) fn arb_address()(sk in arb_spending_key()) -> Address {
|
||||
let fvk = FullViewingKey::from(&sk);
|
||||
fvk.default_address()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ use crate::{
|
|||
primitives::redpallas::{self, Binding, SpendAuth},
|
||||
tree::{Anchor, MerklePath},
|
||||
value::{self, NoteValue, ValueCommitTrapdoor, ValueCommitment, ValueSum},
|
||||
Address, TransmittedNoteCiphertext, Note,
|
||||
Address, Note, TransmittedNoteCiphertext,
|
||||
};
|
||||
|
||||
const MIN_ACTIONS: usize = 2;
|
||||
|
@ -292,8 +292,10 @@ impl Builder {
|
|||
.into_bsk();
|
||||
|
||||
// Create the actions.
|
||||
let (actions, circuits): (Vec<_>, Vec<_>) =
|
||||
pre_actions.into_iter().map(|a| a.build(&mut rng, PhantomData::<V>)).unzip();
|
||||
let (actions, circuits): (Vec<_>, Vec<_>) = pre_actions
|
||||
.into_iter()
|
||||
.map(|a| a.build(&mut rng, PhantomData::<V>))
|
||||
.unzip();
|
||||
|
||||
// Verify that bsk and bvk are consistent.
|
||||
let bvk = (actions.iter().map(|a| a.cv_net()).sum::<ValueCommitment>()
|
||||
|
@ -447,17 +449,77 @@ impl<V> Bundle<PartiallyAuthorized, V> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Generators for property testing.
|
||||
#[cfg(any(test, feature = "test-dependencies"))]
|
||||
pub mod testing {
|
||||
use rand::rngs::OsRng;
|
||||
|
||||
use proptest::collection::vec;
|
||||
use proptest::prelude::*;
|
||||
|
||||
//use pasta_curves::{pallas};
|
||||
|
||||
use crate::{
|
||||
address::testing::arb_address,
|
||||
bundle::{Authorized, Bundle, Flags},
|
||||
circuit::ProvingKey,
|
||||
keys::{FullViewingKey, OutgoingViewingKey, SpendingKey},
|
||||
note::testing::arb_note,
|
||||
tree::{Anchor, MerklePath},
|
||||
value::testing::{arb_positive_note_value, MAX_MONEY},
|
||||
};
|
||||
|
||||
use super::Builder;
|
||||
|
||||
prop_compose! {
|
||||
/// Produce a random valid Orchard bundle.
|
||||
pub fn arb_bundle(sk: SpendingKey)(
|
||||
anchor in prop::array::uniform32(prop::num::u8::ANY).prop_map(Anchor),
|
||||
// generate note values that we're certain won't exceed MAX_MONEY in total
|
||||
notes in vec(arb_positive_note_value(MAX_MONEY as u64 / 10000).prop_flat_map(arb_note), 1..30),
|
||||
recipient_amounts in vec(
|
||||
arb_address().prop_flat_map(
|
||||
|a| arb_positive_note_value(MAX_MONEY as u64 / 10000).prop_map(move |v| (a.clone(), v))
|
||||
),
|
||||
1..30
|
||||
),
|
||||
) -> Bundle<Authorized, i64> {
|
||||
let fvk = FullViewingKey::from(&sk);
|
||||
let ovk = OutgoingViewingKey::from(&fvk);
|
||||
let flags = Flags::from_parts(true, true);
|
||||
let mut builder = Builder::new(flags, anchor);
|
||||
|
||||
for note in notes.into_iter() {
|
||||
builder.add_spend(fvk.clone(), note, MerklePath).unwrap();
|
||||
}
|
||||
|
||||
for (addr, value) in recipient_amounts.into_iter() {
|
||||
builder.add_recipient(Some(ovk.clone()), addr, value, None).unwrap();
|
||||
}
|
||||
|
||||
let mut rng = OsRng;
|
||||
let pk = ProvingKey::build();
|
||||
builder
|
||||
.build(&mut rng, &pk)
|
||||
.unwrap()
|
||||
.prepare(rand_7::rngs::OsRng, [0; 32])
|
||||
.finalize()
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use rand::rngs::OsRng;
|
||||
|
||||
use super::Builder;
|
||||
use crate::{
|
||||
bundle::Flags,
|
||||
bundle::{Authorized, Bundle, Flags},
|
||||
circuit::ProvingKey,
|
||||
keys::{FullViewingKey, SpendingKey},
|
||||
tree::Anchor,
|
||||
value::{NoteValue, ValueSum},
|
||||
value::NoteValue,
|
||||
};
|
||||
|
||||
#[test]
|
||||
|
@ -469,16 +531,16 @@ mod tests {
|
|||
let fvk = FullViewingKey::from(&sk);
|
||||
let recipient = fvk.default_address();
|
||||
|
||||
let mut builder = Builder::new(Flags::from_parts(true, true), Anchor);
|
||||
let mut builder = Builder::new(Flags::from_parts(true, true), Anchor([0; 32]));
|
||||
builder
|
||||
.add_recipient(None, recipient, NoteValue::from_raw(5000), None)
|
||||
.unwrap();
|
||||
let bundle = dbg!(builder
|
||||
let bundle: Bundle<Authorized, i64> = dbg!(builder
|
||||
.build(&mut rng, &pk)
|
||||
.unwrap()
|
||||
.prepare(rand_7::rngs::OsRng, [0; 32]))
|
||||
.finalize()
|
||||
.unwrap();
|
||||
assert_eq!(bundle.value_balance(), &ValueSum::from_raw(-5000))
|
||||
assert_eq!(bundle.value_balance(), &(-5000))
|
||||
}
|
||||
}
|
||||
|
|
177
src/bundle.rs
177
src/bundle.rs
|
@ -172,14 +172,6 @@ pub trait Authorization {
|
|||
type SpendAuth;
|
||||
}
|
||||
|
||||
/// Marker for an unauthorized bundle with no proofs or signatures.
|
||||
#[derive(Debug)]
|
||||
pub struct Unauthorized {}
|
||||
|
||||
impl Authorization for Unauthorized {
|
||||
type SpendAuth = ();
|
||||
}
|
||||
|
||||
/// Authorizing data for a bundle of actions, ready to be committed to the ledger.
|
||||
#[derive(Debug)]
|
||||
pub struct Authorized {
|
||||
|
@ -342,41 +334,41 @@ pub enum BundleAuthError<E> {
|
|||
AuthLengthMismatch(usize, usize),
|
||||
}
|
||||
|
||||
impl<V> Bundle<Unauthorized, V> {
|
||||
/// Compute the authorizing data for a bundle and apply it to the bundle, returning the
|
||||
/// authorized result.
|
||||
pub fn with_auth<E, F: FnOnce(&Self) -> Result<BundleAuth, E>>(
|
||||
self,
|
||||
f: F,
|
||||
) -> Result<Bundle<Authorized, V>, BundleAuthError<E>> {
|
||||
let auth = f(&self).map_err(BundleAuthError::Wrapped)?;
|
||||
let actions_len = self.actions.len();
|
||||
|
||||
if actions_len != auth.action_authorizations.len() {
|
||||
Err(BundleAuthError::AuthLengthMismatch(
|
||||
actions_len,
|
||||
auth.action_authorizations.len(),
|
||||
))
|
||||
} else {
|
||||
let actions = NonEmpty::from_vec(
|
||||
self.actions
|
||||
.into_iter()
|
||||
.zip(auth.action_authorizations.into_iter())
|
||||
.map(|(act, a)| act.map(|_| a))
|
||||
.collect(),
|
||||
)
|
||||
.ok_or(BundleAuthError::AuthLengthMismatch(actions_len, 0))?;
|
||||
|
||||
Ok(Bundle {
|
||||
actions,
|
||||
flags: self.flags,
|
||||
value_balance: self.value_balance,
|
||||
anchor: self.anchor,
|
||||
authorization: auth.authorization,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
//impl<V> Bundle<Unauthorized, V> {
|
||||
// /// Compute the authorizing data for a bundle and apply it to the bundle, returning the
|
||||
// /// authorized result.
|
||||
// pub fn with_auth<E, F: FnOnce(&Self) -> Result<BundleAuth, E>>(
|
||||
// self,
|
||||
// f: F,
|
||||
// ) -> Result<Bundle<Authorized, V>, BundleAuthError<E>> {
|
||||
// let auth = f(&self).map_err(BundleAuthError::Wrapped)?;
|
||||
// let actions_len = self.actions.len();
|
||||
//
|
||||
// if actions_len != auth.action_authorizations.len() {
|
||||
// Err(BundleAuthError::AuthLengthMismatch(
|
||||
// actions_len,
|
||||
// auth.action_authorizations.len(),
|
||||
// ))
|
||||
// } else {
|
||||
// let actions = NonEmpty::from_vec(
|
||||
// self.actions
|
||||
// .into_iter()
|
||||
// .zip(auth.action_authorizations.into_iter())
|
||||
// .map(|(act, a)| act.map(|_| a))
|
||||
// .collect(),
|
||||
// )
|
||||
// .ok_or(BundleAuthError::AuthLengthMismatch(actions_len, 0))?;
|
||||
//
|
||||
// Ok(Bundle {
|
||||
// actions,
|
||||
// flags: self.flags,
|
||||
// value_balance: self.value_balance,
|
||||
// anchor: self.anchor,
|
||||
// authorization: auth.authorization,
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
impl Authorized {
|
||||
/// Constructs the authorizing data for a bundle of actions from its constituent parts.
|
||||
|
@ -407,3 +399,102 @@ pub struct BundleCommitment;
|
|||
/// A commitment to the authorizing data within a bundle of actions.
|
||||
#[derive(Debug)]
|
||||
pub struct BundleAuthorizingCommitment;
|
||||
|
||||
/// Generators for property testing.
|
||||
#[cfg(any(test, feature = "test-dependencies"))]
|
||||
pub mod testing {
|
||||
use nonempty::NonEmpty;
|
||||
|
||||
use proptest::collection::vec;
|
||||
use proptest::prelude::*;
|
||||
|
||||
//use pasta_curves::{pallas};
|
||||
|
||||
use crate::{
|
||||
note::{
|
||||
commitment::ExtractedNoteCommitment, nullifier::testing::arb_nullifier,
|
||||
testing::arb_note, TransmittedNoteCiphertext,
|
||||
},
|
||||
primitives::redpallas::testing::arb_spendauth_verification_key,
|
||||
value::{
|
||||
testing::{arb_note_value},
|
||||
NoteValue, ValueCommitTrapdoor, ValueCommitment, ValueSum,
|
||||
},
|
||||
Anchor,
|
||||
};
|
||||
|
||||
use super::{Action, Authorization, Bundle, Flags};
|
||||
|
||||
/// Marker for an unauthorized bundle with no proofs or signatures.
|
||||
#[derive(Debug)]
|
||||
pub struct Unauthorized;
|
||||
|
||||
impl Authorization for Unauthorized {
|
||||
type SpendAuth = ();
|
||||
}
|
||||
|
||||
prop_compose! {
|
||||
/// Generate an action without authorization data.
|
||||
pub fn arb_unauthorized_action(value: NoteValue)(
|
||||
nf in arb_nullifier(),
|
||||
rk in arb_spendauth_verification_key(),
|
||||
note in arb_note(value),
|
||||
) -> Action<()> {
|
||||
let cmx = ExtractedNoteCommitment::from(note.commitment());
|
||||
let cv_net = ValueCommitment::derive(
|
||||
(note.value() - NoteValue::zero()).unwrap(),
|
||||
ValueCommitTrapdoor::zero()
|
||||
);
|
||||
// FIXME: make a real one from the note.
|
||||
let encrypted_note = TransmittedNoteCiphertext {
|
||||
epk_bytes: [0u8; 32],
|
||||
enc_ciphertext: [0u8; 580],
|
||||
out_ciphertext: [0u8; 80]
|
||||
};
|
||||
Action {
|
||||
nf,
|
||||
rk,
|
||||
cmx,
|
||||
encrypted_note,
|
||||
cv_net,
|
||||
authorization: ()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
prop_compose! {
|
||||
/// Create an arbitrary set of flags.
|
||||
pub fn arb_flags()(spends_enabled in prop::bool::ANY, outputs_enabled in prop::bool::ANY) -> Flags {
|
||||
Flags::from_parts(spends_enabled, outputs_enabled)
|
||||
}
|
||||
}
|
||||
|
||||
prop_compose! {
|
||||
/// Generate an arbitrary unauthorized bundle. This bundle does not
|
||||
/// necessarily respect consensus rules; for that use
|
||||
/// [`crate::builder::testing::arb_bundle`]
|
||||
pub fn arb_unauthorized_bundle()(
|
||||
acts in vec(
|
||||
arb_note_value().prop_flat_map(|v|
|
||||
arb_unauthorized_action(v).prop_map(move |a| (v, a))
|
||||
),
|
||||
1..10
|
||||
),
|
||||
flags in arb_flags(),
|
||||
anchor in prop::array::uniform32(prop::num::u8::ANY).prop_map(Anchor)
|
||||
) -> Bundle<Unauthorized, ValueSum> {
|
||||
let (values, actions): (Vec<NoteValue>, Vec<Action<()>>) = acts.into_iter().unzip();
|
||||
|
||||
Bundle::from_parts(
|
||||
NonEmpty::from_vec(actions).unwrap(),
|
||||
flags,
|
||||
values.into_iter().fold(
|
||||
ValueSum::zero(),
|
||||
|acc, cv| (acc + (cv - NoteValue::zero()).unwrap()).unwrap()
|
||||
),
|
||||
anchor,
|
||||
Unauthorized
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -207,7 +207,7 @@ mod tests {
|
|||
(
|
||||
Circuit {},
|
||||
Instance {
|
||||
anchor: Anchor,
|
||||
anchor: Anchor([0; 32]),
|
||||
cv_net,
|
||||
nf_old,
|
||||
rk,
|
||||
|
|
36
src/keys.rs
36
src/keys.rs
|
@ -109,7 +109,7 @@ impl From<&SpendingKey> for SpendAuthorizingKey {
|
|||
/// $\mathsf{ak}$ but stored here as a RedPallas verification key.
|
||||
///
|
||||
/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SpendValidatingKey(redpallas::VerificationKey<SpendAuth>);
|
||||
|
||||
impl From<&SpendAuthorizingKey> for SpendValidatingKey {
|
||||
|
@ -138,7 +138,7 @@ impl SpendValidatingKey {
|
|||
/// [`Nullifier`]: crate::note::Nullifier
|
||||
/// [`Note`]: crate::note::Note
|
||||
/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct NullifierDerivingKey(pallas::Base);
|
||||
|
||||
impl From<&SpendingKey> for NullifierDerivingKey {
|
||||
|
@ -158,7 +158,7 @@ impl NullifierDerivingKey {
|
|||
/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
|
||||
///
|
||||
/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
struct CommitIvkRandomness(pallas::Scalar);
|
||||
|
||||
impl From<&SpendingKey> for CommitIvkRandomness {
|
||||
|
@ -175,7 +175,7 @@ impl From<&SpendingKey> for CommitIvkRandomness {
|
|||
/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
|
||||
///
|
||||
/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct FullViewingKey {
|
||||
ak: SpendValidatingKey,
|
||||
nk: NullifierDerivingKey,
|
||||
|
@ -287,7 +287,7 @@ impl DiversifierKey {
|
|||
/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
|
||||
///
|
||||
/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Diversifier([u8; 11]);
|
||||
|
||||
impl Diversifier {
|
||||
|
@ -343,7 +343,7 @@ impl IncomingViewingKey {
|
|||
/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
|
||||
///
|
||||
/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct OutgoingViewingKey([u8; 32]);
|
||||
|
||||
impl From<&FullViewingKey> for OutgoingViewingKey {
|
||||
|
@ -357,7 +357,7 @@ impl From<&FullViewingKey> for OutgoingViewingKey {
|
|||
/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
|
||||
///
|
||||
/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct DiversifiedTransmissionKey(pallas::Point);
|
||||
|
||||
impl DiversifiedTransmissionKey {
|
||||
|
@ -374,3 +374,25 @@ impl DiversifiedTransmissionKey {
|
|||
self.0.to_bytes()
|
||||
}
|
||||
}
|
||||
|
||||
/// Generators for property testing.
|
||||
#[cfg(any(test, feature = "test-dependencies"))]
|
||||
pub mod testing {
|
||||
use proptest::prelude::*;
|
||||
|
||||
use super::SpendingKey;
|
||||
|
||||
prop_compose! {
|
||||
/// Generate a uniformly distributed fake note commitment value.
|
||||
pub fn arb_spending_key()(
|
||||
key in prop::array::uniform32(prop::num::u8::ANY).
|
||||
prop_map(SpendingKey::from_bytes).
|
||||
prop_filter(
|
||||
"Values must correspond to valid Orchard spending keys.",
|
||||
|opt| bool::from(opt.is_some())
|
||||
)
|
||||
) -> SpendingKey {
|
||||
key.unwrap()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ mod tree;
|
|||
pub mod value;
|
||||
|
||||
pub use address::Address;
|
||||
pub use bundle::{Action, Authorization, Authorized, Bundle, Unauthorized};
|
||||
pub use bundle::{Action, Authorization, Authorized, Bundle};
|
||||
pub use circuit::Proof;
|
||||
pub use note::{
|
||||
ExtractedNoteCommitment, Note, NoteCommitment, Nullifier, TransmittedNoteCiphertext,
|
||||
|
|
39
src/note.rs
39
src/note.rs
|
@ -9,10 +9,10 @@ use crate::{
|
|||
Address,
|
||||
};
|
||||
|
||||
mod commitment;
|
||||
pub(crate) mod commitment;
|
||||
pub use self::commitment::{ExtractedNoteCommitment, NoteCommitment};
|
||||
|
||||
mod nullifier;
|
||||
pub(crate) mod nullifier;
|
||||
pub use self::nullifier::Nullifier;
|
||||
|
||||
/// The ZIP 212 seed randomness for a note.
|
||||
|
@ -144,3 +144,38 @@ pub struct TransmittedNoteCiphertext {
|
|||
/// key for the note to recover the note plaintext.
|
||||
pub out_ciphertext: [u8; 80],
|
||||
}
|
||||
|
||||
/// Generators for property testing.
|
||||
#[cfg(any(test, feature = "test-dependencies"))]
|
||||
pub mod testing {
|
||||
use proptest::prelude::*;
|
||||
|
||||
use crate::{
|
||||
address::testing::arb_address, note::nullifier::testing::arb_nullifier, value::NoteValue,
|
||||
};
|
||||
|
||||
use super::{Note, RandomSeed};
|
||||
|
||||
prop_compose! {
|
||||
/// Generate an arbitrary random seed
|
||||
pub(crate) fn arb_rseed()(elems in prop::array::uniform32(prop::num::u8::ANY)) -> RandomSeed {
|
||||
RandomSeed(elems)
|
||||
}
|
||||
}
|
||||
|
||||
prop_compose! {
|
||||
/// Generate an action without authorization data.
|
||||
pub fn arb_note(value: NoteValue)(
|
||||
recipient in arb_address(),
|
||||
rho in arb_nullifier(),
|
||||
rseed in arb_rseed(),
|
||||
) -> Note {
|
||||
Note {
|
||||
recipient,
|
||||
value,
|
||||
rho,
|
||||
rseed,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,8 +11,8 @@ use crate::{
|
|||
};
|
||||
|
||||
/// A unique nullifier for a note.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Nullifier(pub(super) pallas::Base);
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Nullifier(pub(crate) pallas::Base);
|
||||
|
||||
impl Nullifier {
|
||||
/// Generates a dummy nullifier for use as $\rho$ in dummy spent notes.
|
||||
|
@ -57,3 +57,20 @@ impl Nullifier {
|
|||
Nullifier(extract_p(&(k * mod_r_p(nk.prf_nf(rho) + psi) + cm.0)))
|
||||
}
|
||||
}
|
||||
|
||||
/// Generators for property testing.
|
||||
#[cfg(any(test, feature = "test-dependencies"))]
|
||||
pub mod testing {
|
||||
use proptest::prelude::*;
|
||||
|
||||
use pasta_curves::pallas;
|
||||
|
||||
use super::Nullifier;
|
||||
|
||||
prop_compose! {
|
||||
/// Generate a uniformly distributed nullifier value.
|
||||
pub fn arb_nullifier()(elems in prop::array::uniform4(prop::num::u64::ANY)) -> Nullifier {
|
||||
Nullifier(pallas::Base::from_raw(elems))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ impl SigType for Binding {}
|
|||
|
||||
/// A RedPallas signing key.
|
||||
#[derive(Debug)]
|
||||
pub struct SigningKey<T: SigType>(reddsa::SigningKey<T>);
|
||||
pub struct SigningKey<T: SigType>(pub(crate) reddsa::SigningKey<T>);
|
||||
|
||||
impl<T: SigType> From<SigningKey<T>> for [u8; 32] {
|
||||
fn from(sk: SigningKey<T>) -> [u8; 32] {
|
||||
|
@ -117,3 +117,49 @@ pub(crate) mod private {
|
|||
|
||||
impl Sealed for Binding {}
|
||||
}
|
||||
|
||||
/// Generators for property testing.
|
||||
#[cfg(any(test, feature = "test-dependencies"))]
|
||||
pub mod testing {
|
||||
use std::convert::TryFrom;
|
||||
|
||||
use proptest::prelude::*;
|
||||
|
||||
use super::{Binding, SigningKey, SpendAuth, VerificationKey};
|
||||
|
||||
prop_compose! {
|
||||
/// Generate a uniformly distributed nullifier value.
|
||||
pub fn arb_spendauth_signing_key()(
|
||||
sk in prop::array::uniform32(prop::num::u8::ANY)
|
||||
.prop_map(reddsa::SigningKey::try_from)
|
||||
.prop_filter("Values must be parseable as valid signing keys", |r| r.is_ok())
|
||||
) -> SigningKey<SpendAuth> {
|
||||
SigningKey(sk.unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
prop_compose! {
|
||||
/// Generate a uniformly distributed nullifier value.
|
||||
pub fn arb_binding_signing_key()(
|
||||
sk in prop::array::uniform32(prop::num::u8::ANY)
|
||||
.prop_map(reddsa::SigningKey::try_from)
|
||||
.prop_filter("Values must be parseable as valid signing keys", |r| r.is_ok())
|
||||
) -> SigningKey<Binding> {
|
||||
SigningKey(sk.unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
prop_compose! {
|
||||
/// Generate a uniformly distributed nullifier value.
|
||||
pub fn arb_spendauth_verification_key()(sk in arb_spendauth_signing_key()) -> VerificationKey<SpendAuth> {
|
||||
VerificationKey::from(&sk)
|
||||
}
|
||||
}
|
||||
|
||||
prop_compose! {
|
||||
/// Generate a uniformly distributed nullifier value.
|
||||
pub fn arb_binding_verification_key()(sk in arb_binding_signing_key()) -> VerificationKey<Binding> {
|
||||
VerificationKey::from(&sk)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
48
src/value.rs
48
src/value.rs
|
@ -66,7 +66,6 @@ impl NoteValue {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
impl Sub for NoteValue {
|
||||
type Output = Option<ValueSum>;
|
||||
|
||||
|
@ -227,20 +226,21 @@ impl ValueCommitment {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
/// Generators for property testing.
|
||||
#[cfg(any(test, feature = "test-dependencies"))]
|
||||
pub mod testing {
|
||||
use pasta_curves::{arithmetic::FieldExt, pallas};
|
||||
use proptest::prelude::*;
|
||||
|
||||
use super::{OverflowError, ValueCommitTrapdoor, ValueCommitment, ValueSum};
|
||||
use crate::primitives::redpallas;
|
||||
use super::{NoteValue, ValueCommitTrapdoor, ValueSum};
|
||||
|
||||
/// Zcash's maximum money amount. Used as a bound in proptests so we don't artifically
|
||||
/// overflow `ValueSum`'s size.
|
||||
const MAX_MONEY: i64 = 21_000_000 * 1_0000_0000;
|
||||
pub const MAX_MONEY: i64 = 21_000_000 * 1_0000_0000;
|
||||
|
||||
prop_compose! {
|
||||
fn arb_scalar()(bytes in prop::array::uniform32(0u8..)) -> pallas::Scalar {
|
||||
/// Generate an arbitrary Pallas scalar.
|
||||
pub fn arb_scalar()(bytes in prop::array::uniform32(0u8..)) -> pallas::Scalar {
|
||||
// Instead of rejecting out-of-range bytes, let's reduce them.
|
||||
let mut buf = [0; 64];
|
||||
buf[..32].copy_from_slice(&bytes);
|
||||
|
@ -249,17 +249,45 @@ mod tests {
|
|||
}
|
||||
|
||||
prop_compose! {
|
||||
fn arb_value_sum(bound: i64)(value in -bound..bound) -> ValueSum {
|
||||
ValueSum(value)
|
||||
/// Generate an arbitrary [`ValueSum`] in the range of valid Zcash values.
|
||||
pub fn arb_value_sum(bound: i64)(value in -bound..bound) -> ValueSum {
|
||||
ValueSum(value as i128)
|
||||
}
|
||||
}
|
||||
|
||||
prop_compose! {
|
||||
fn arb_trapdoor()(rcv in arb_scalar()) -> ValueCommitTrapdoor {
|
||||
/// Generate an arbitrary ValueCommitTrapdoor
|
||||
pub fn arb_trapdoor()(rcv in arb_scalar()) -> ValueCommitTrapdoor {
|
||||
ValueCommitTrapdoor(rcv)
|
||||
}
|
||||
}
|
||||
|
||||
prop_compose! {
|
||||
/// Generate an arbitrary value in the range of valid nonnegative Zcash amounts.
|
||||
pub fn arb_note_value()(value in 0u64..(MAX_MONEY as u64)) -> NoteValue {
|
||||
NoteValue(value)
|
||||
}
|
||||
}
|
||||
|
||||
prop_compose! {
|
||||
/// Generate an arbitrary value in the range of valid positive Zcash amounts
|
||||
/// less than a specified value.
|
||||
pub fn arb_positive_note_value(max: u64)(value in 1u64..max) -> NoteValue {
|
||||
NoteValue(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use proptest::prelude::*;
|
||||
|
||||
use super::{
|
||||
testing::{arb_trapdoor, arb_value_sum, MAX_MONEY},
|
||||
OverflowError, ValueCommitTrapdoor, ValueCommitment, ValueSum
|
||||
};
|
||||
use crate::primitives::redpallas;
|
||||
|
||||
proptest! {
|
||||
#[test]
|
||||
fn bsk_consistent_with_bvk(
|
||||
|
|
Loading…
Reference in New Issue