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"
|
ff = "0.9"
|
||||||
fpe = "0.4"
|
fpe = "0.4"
|
||||||
group = "0.9"
|
group = "0.9"
|
||||||
|
proptest = { version = "1.0.0", optional = true }
|
||||||
rand = "0.8"
|
rand = "0.8"
|
||||||
rand_7 = { package = "rand", version = "0.7" }
|
rand_7 = { package = "rand", version = "0.7" }
|
||||||
nonempty = "0.6"
|
nonempty = "0.6"
|
||||||
|
@ -45,11 +46,13 @@ rev = "f1e76dbc9abf2b68cc609e874fe39f2a15b75b12"
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
criterion = "0.3"
|
criterion = "0.3"
|
||||||
hex = "0.4"
|
hex = "0.4"
|
||||||
proptest = "1.0.0"
|
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
bench = false
|
bench = false
|
||||||
|
|
||||||
|
[features]
|
||||||
|
test-dependencies = ["proptest"]
|
||||||
|
|
||||||
[[bench]]
|
[[bench]]
|
||||||
name = "small"
|
name = "small"
|
||||||
harness = false
|
harness = false
|
||||||
|
|
|
@ -15,7 +15,7 @@ use crate::{
|
||||||
/// let sk = SpendingKey::from_bytes([7; 32]).unwrap();
|
/// let sk = SpendingKey::from_bytes([7; 32]).unwrap();
|
||||||
/// let address = FullViewingKey::from(&sk).default_address();
|
/// let address = FullViewingKey::from(&sk).default_address();
|
||||||
/// ```
|
/// ```
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Address {
|
pub struct Address {
|
||||||
d: Diversifier,
|
d: Diversifier,
|
||||||
pk_d: DiversifiedTransmissionKey,
|
pk_d: DiversifiedTransmissionKey,
|
||||||
|
@ -38,3 +38,21 @@ impl Address {
|
||||||
&self.pk_d
|
&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},
|
primitives::redpallas::{self, Binding, SpendAuth},
|
||||||
tree::{Anchor, MerklePath},
|
tree::{Anchor, MerklePath},
|
||||||
value::{self, NoteValue, ValueCommitTrapdoor, ValueCommitment, ValueSum},
|
value::{self, NoteValue, ValueCommitTrapdoor, ValueCommitment, ValueSum},
|
||||||
Address, TransmittedNoteCiphertext, Note,
|
Address, Note, TransmittedNoteCiphertext,
|
||||||
};
|
};
|
||||||
|
|
||||||
const MIN_ACTIONS: usize = 2;
|
const MIN_ACTIONS: usize = 2;
|
||||||
|
@ -292,8 +292,10 @@ impl Builder {
|
||||||
.into_bsk();
|
.into_bsk();
|
||||||
|
|
||||||
// Create the actions.
|
// Create the actions.
|
||||||
let (actions, circuits): (Vec<_>, Vec<_>) =
|
let (actions, circuits): (Vec<_>, Vec<_>) = pre_actions
|
||||||
pre_actions.into_iter().map(|a| a.build(&mut rng, PhantomData::<V>)).unzip();
|
.into_iter()
|
||||||
|
.map(|a| a.build(&mut rng, PhantomData::<V>))
|
||||||
|
.unzip();
|
||||||
|
|
||||||
// Verify that bsk and bvk are consistent.
|
// Verify that bsk and bvk are consistent.
|
||||||
let bvk = (actions.iter().map(|a| a.cv_net()).sum::<ValueCommitment>()
|
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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use rand::rngs::OsRng;
|
use rand::rngs::OsRng;
|
||||||
|
|
||||||
use super::Builder;
|
use super::Builder;
|
||||||
use crate::{
|
use crate::{
|
||||||
bundle::Flags,
|
bundle::{Authorized, Bundle, Flags},
|
||||||
circuit::ProvingKey,
|
circuit::ProvingKey,
|
||||||
keys::{FullViewingKey, SpendingKey},
|
keys::{FullViewingKey, SpendingKey},
|
||||||
tree::Anchor,
|
tree::Anchor,
|
||||||
value::{NoteValue, ValueSum},
|
value::NoteValue,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -469,16 +531,16 @@ mod tests {
|
||||||
let fvk = FullViewingKey::from(&sk);
|
let fvk = FullViewingKey::from(&sk);
|
||||||
let recipient = fvk.default_address();
|
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
|
builder
|
||||||
.add_recipient(None, recipient, NoteValue::from_raw(5000), None)
|
.add_recipient(None, recipient, NoteValue::from_raw(5000), None)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let bundle = dbg!(builder
|
let bundle: Bundle<Authorized, i64> = dbg!(builder
|
||||||
.build(&mut rng, &pk)
|
.build(&mut rng, &pk)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.prepare(rand_7::rngs::OsRng, [0; 32]))
|
.prepare(rand_7::rngs::OsRng, [0; 32]))
|
||||||
.finalize()
|
.finalize()
|
||||||
.unwrap();
|
.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;
|
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.
|
/// Authorizing data for a bundle of actions, ready to be committed to the ledger.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Authorized {
|
pub struct Authorized {
|
||||||
|
@ -342,41 +334,41 @@ pub enum BundleAuthError<E> {
|
||||||
AuthLengthMismatch(usize, usize),
|
AuthLengthMismatch(usize, usize),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<V> Bundle<Unauthorized, V> {
|
//impl<V> Bundle<Unauthorized, V> {
|
||||||
/// Compute the authorizing data for a bundle and apply it to the bundle, returning the
|
// /// Compute the authorizing data for a bundle and apply it to the bundle, returning the
|
||||||
/// authorized result.
|
// /// authorized result.
|
||||||
pub fn with_auth<E, F: FnOnce(&Self) -> Result<BundleAuth, E>>(
|
// pub fn with_auth<E, F: FnOnce(&Self) -> Result<BundleAuth, E>>(
|
||||||
self,
|
// self,
|
||||||
f: F,
|
// f: F,
|
||||||
) -> Result<Bundle<Authorized, V>, BundleAuthError<E>> {
|
// ) -> Result<Bundle<Authorized, V>, BundleAuthError<E>> {
|
||||||
let auth = f(&self).map_err(BundleAuthError::Wrapped)?;
|
// let auth = f(&self).map_err(BundleAuthError::Wrapped)?;
|
||||||
let actions_len = self.actions.len();
|
// let actions_len = self.actions.len();
|
||||||
|
//
|
||||||
if actions_len != auth.action_authorizations.len() {
|
// if actions_len != auth.action_authorizations.len() {
|
||||||
Err(BundleAuthError::AuthLengthMismatch(
|
// Err(BundleAuthError::AuthLengthMismatch(
|
||||||
actions_len,
|
// actions_len,
|
||||||
auth.action_authorizations.len(),
|
// auth.action_authorizations.len(),
|
||||||
))
|
// ))
|
||||||
} else {
|
// } else {
|
||||||
let actions = NonEmpty::from_vec(
|
// let actions = NonEmpty::from_vec(
|
||||||
self.actions
|
// self.actions
|
||||||
.into_iter()
|
// .into_iter()
|
||||||
.zip(auth.action_authorizations.into_iter())
|
// .zip(auth.action_authorizations.into_iter())
|
||||||
.map(|(act, a)| act.map(|_| a))
|
// .map(|(act, a)| act.map(|_| a))
|
||||||
.collect(),
|
// .collect(),
|
||||||
)
|
// )
|
||||||
.ok_or(BundleAuthError::AuthLengthMismatch(actions_len, 0))?;
|
// .ok_or(BundleAuthError::AuthLengthMismatch(actions_len, 0))?;
|
||||||
|
//
|
||||||
Ok(Bundle {
|
// Ok(Bundle {
|
||||||
actions,
|
// actions,
|
||||||
flags: self.flags,
|
// flags: self.flags,
|
||||||
value_balance: self.value_balance,
|
// value_balance: self.value_balance,
|
||||||
anchor: self.anchor,
|
// anchor: self.anchor,
|
||||||
authorization: auth.authorization,
|
// authorization: auth.authorization,
|
||||||
})
|
// })
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
|
|
||||||
impl Authorized {
|
impl Authorized {
|
||||||
/// Constructs the authorizing data for a bundle of actions from its constituent parts.
|
/// 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.
|
/// A commitment to the authorizing data within a bundle of actions.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct BundleAuthorizingCommitment;
|
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 {},
|
Circuit {},
|
||||||
Instance {
|
Instance {
|
||||||
anchor: Anchor,
|
anchor: Anchor([0; 32]),
|
||||||
cv_net,
|
cv_net,
|
||||||
nf_old,
|
nf_old,
|
||||||
rk,
|
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.
|
/// $\mathsf{ak}$ but stored here as a RedPallas verification key.
|
||||||
///
|
///
|
||||||
/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
|
/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct SpendValidatingKey(redpallas::VerificationKey<SpendAuth>);
|
pub struct SpendValidatingKey(redpallas::VerificationKey<SpendAuth>);
|
||||||
|
|
||||||
impl From<&SpendAuthorizingKey> for SpendValidatingKey {
|
impl From<&SpendAuthorizingKey> for SpendValidatingKey {
|
||||||
|
@ -138,7 +138,7 @@ impl SpendValidatingKey {
|
||||||
/// [`Nullifier`]: crate::note::Nullifier
|
/// [`Nullifier`]: crate::note::Nullifier
|
||||||
/// [`Note`]: crate::note::Note
|
/// [`Note`]: crate::note::Note
|
||||||
/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
|
/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone)]
|
||||||
pub(crate) struct NullifierDerivingKey(pallas::Base);
|
pub(crate) struct NullifierDerivingKey(pallas::Base);
|
||||||
|
|
||||||
impl From<&SpendingKey> for NullifierDerivingKey {
|
impl From<&SpendingKey> for NullifierDerivingKey {
|
||||||
|
@ -158,7 +158,7 @@ impl NullifierDerivingKey {
|
||||||
/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
|
/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
|
||||||
///
|
///
|
||||||
/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
|
/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone)]
|
||||||
struct CommitIvkRandomness(pallas::Scalar);
|
struct CommitIvkRandomness(pallas::Scalar);
|
||||||
|
|
||||||
impl From<&SpendingKey> for CommitIvkRandomness {
|
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].
|
/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
|
||||||
///
|
///
|
||||||
/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
|
/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct FullViewingKey {
|
pub struct FullViewingKey {
|
||||||
ak: SpendValidatingKey,
|
ak: SpendValidatingKey,
|
||||||
nk: NullifierDerivingKey,
|
nk: NullifierDerivingKey,
|
||||||
|
@ -287,7 +287,7 @@ impl DiversifierKey {
|
||||||
/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
|
/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
|
||||||
///
|
///
|
||||||
/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
|
/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Diversifier([u8; 11]);
|
pub struct Diversifier([u8; 11]);
|
||||||
|
|
||||||
impl Diversifier {
|
impl Diversifier {
|
||||||
|
@ -343,7 +343,7 @@ impl IncomingViewingKey {
|
||||||
/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
|
/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
|
||||||
///
|
///
|
||||||
/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
|
/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct OutgoingViewingKey([u8; 32]);
|
pub struct OutgoingViewingKey([u8; 32]);
|
||||||
|
|
||||||
impl From<&FullViewingKey> for OutgoingViewingKey {
|
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].
|
/// Defined in [Zcash Protocol Spec § 4.2.3: Orchard Key Components][orchardkeycomponents].
|
||||||
///
|
///
|
||||||
/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
|
/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone)]
|
||||||
pub(crate) struct DiversifiedTransmissionKey(pallas::Point);
|
pub(crate) struct DiversifiedTransmissionKey(pallas::Point);
|
||||||
|
|
||||||
impl DiversifiedTransmissionKey {
|
impl DiversifiedTransmissionKey {
|
||||||
|
@ -374,3 +374,25 @@ impl DiversifiedTransmissionKey {
|
||||||
self.0.to_bytes()
|
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 mod value;
|
||||||
|
|
||||||
pub use address::Address;
|
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 circuit::Proof;
|
||||||
pub use note::{
|
pub use note::{
|
||||||
ExtractedNoteCommitment, Note, NoteCommitment, Nullifier, TransmittedNoteCiphertext,
|
ExtractedNoteCommitment, Note, NoteCommitment, Nullifier, TransmittedNoteCiphertext,
|
||||||
|
|
39
src/note.rs
39
src/note.rs
|
@ -9,10 +9,10 @@ use crate::{
|
||||||
Address,
|
Address,
|
||||||
};
|
};
|
||||||
|
|
||||||
mod commitment;
|
pub(crate) mod commitment;
|
||||||
pub use self::commitment::{ExtractedNoteCommitment, NoteCommitment};
|
pub use self::commitment::{ExtractedNoteCommitment, NoteCommitment};
|
||||||
|
|
||||||
mod nullifier;
|
pub(crate) mod nullifier;
|
||||||
pub use self::nullifier::Nullifier;
|
pub use self::nullifier::Nullifier;
|
||||||
|
|
||||||
/// The ZIP 212 seed randomness for a note.
|
/// The ZIP 212 seed randomness for a note.
|
||||||
|
@ -144,3 +144,38 @@ pub struct TransmittedNoteCiphertext {
|
||||||
/// key for the note to recover the note plaintext.
|
/// key for the note to recover the note plaintext.
|
||||||
pub out_ciphertext: [u8; 80],
|
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.
|
/// A unique nullifier for a note.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Nullifier(pub(super) pallas::Base);
|
pub struct Nullifier(pub(crate) pallas::Base);
|
||||||
|
|
||||||
impl Nullifier {
|
impl Nullifier {
|
||||||
/// Generates a dummy nullifier for use as $\rho$ in dummy spent notes.
|
/// 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)))
|
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.
|
/// A RedPallas signing key.
|
||||||
#[derive(Debug)]
|
#[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] {
|
impl<T: SigType> From<SigningKey<T>> for [u8; 32] {
|
||||||
fn from(sk: SigningKey<T>) -> [u8; 32] {
|
fn from(sk: SigningKey<T>) -> [u8; 32] {
|
||||||
|
@ -117,3 +117,49 @@ pub(crate) mod private {
|
||||||
|
|
||||||
impl Sealed for Binding {}
|
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 {
|
impl Sub for NoteValue {
|
||||||
type Output = Option<ValueSum>;
|
type Output = Option<ValueSum>;
|
||||||
|
|
||||||
|
@ -227,20 +226,21 @@ impl ValueCommitment {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
/// Generators for property testing.
|
||||||
mod tests {
|
#[cfg(any(test, feature = "test-dependencies"))]
|
||||||
|
pub mod testing {
|
||||||
use pasta_curves::{arithmetic::FieldExt, pallas};
|
use pasta_curves::{arithmetic::FieldExt, pallas};
|
||||||
use proptest::prelude::*;
|
use proptest::prelude::*;
|
||||||
|
|
||||||
use super::{OverflowError, ValueCommitTrapdoor, ValueCommitment, ValueSum};
|
use super::{NoteValue, ValueCommitTrapdoor, ValueSum};
|
||||||
use crate::primitives::redpallas;
|
|
||||||
|
|
||||||
/// Zcash's maximum money amount. Used as a bound in proptests so we don't artifically
|
/// Zcash's maximum money amount. Used as a bound in proptests so we don't artifically
|
||||||
/// overflow `ValueSum`'s size.
|
/// 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! {
|
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.
|
// Instead of rejecting out-of-range bytes, let's reduce them.
|
||||||
let mut buf = [0; 64];
|
let mut buf = [0; 64];
|
||||||
buf[..32].copy_from_slice(&bytes);
|
buf[..32].copy_from_slice(&bytes);
|
||||||
|
@ -249,17 +249,45 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
prop_compose! {
|
prop_compose! {
|
||||||
fn arb_value_sum(bound: i64)(value in -bound..bound) -> ValueSum {
|
/// Generate an arbitrary [`ValueSum`] in the range of valid Zcash values.
|
||||||
ValueSum(value)
|
pub fn arb_value_sum(bound: i64)(value in -bound..bound) -> ValueSum {
|
||||||
|
ValueSum(value as i128)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
prop_compose! {
|
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)
|
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! {
|
proptest! {
|
||||||
#[test]
|
#[test]
|
||||||
fn bsk_consistent_with_bvk(
|
fn bsk_consistent_with_bvk(
|
||||||
|
|
Loading…
Reference in New Issue