zebra_chain/sapling/
arbitrary.rs

1//! Randomised data generation for sapling types.
2
3use group::Group;
4use jubjub::{AffinePoint, ExtendedPoint};
5use rand::SeedableRng;
6use rand_chacha::ChaChaRng;
7
8use proptest::{collection::vec, prelude::*};
9
10use crate::primitives::Groth16Proof;
11
12use super::{
13    keys::{self, ValidatingKey},
14    note, tree, FieldNotPresent, NoteCommitment, Output, OutputInTransactionV4, PerSpendAnchor,
15    SharedAnchor, Spend,
16};
17
18impl Arbitrary for Spend<PerSpendAnchor> {
19    type Parameters = ();
20
21    fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
22        (
23            any::<tree::Root>(),
24            any::<note::Nullifier>(),
25            spendauth_verification_key_bytes(),
26            any::<Groth16Proof>(),
27            vec(any::<u8>(), 64),
28        )
29            .prop_map(|(per_spend_anchor, nullifier, rk, proof, sig_bytes)| Self {
30                per_spend_anchor,
31                cv: ExtendedPoint::generator().try_into().unwrap(),
32                nullifier,
33                rk,
34                zkproof: proof,
35                spend_auth_sig: redjubjub::Signature::from({
36                    let mut b = [0u8; 64];
37                    b.copy_from_slice(sig_bytes.as_slice());
38                    b
39                }),
40            })
41            .boxed()
42    }
43
44    type Strategy = BoxedStrategy<Self>;
45}
46
47impl Arbitrary for Spend<SharedAnchor> {
48    type Parameters = ();
49
50    fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
51        (
52            any::<note::Nullifier>(),
53            spendauth_verification_key_bytes(),
54            any::<Groth16Proof>(),
55            vec(any::<u8>(), 64),
56        )
57            .prop_map(|(nullifier, rk, proof, sig_bytes)| Self {
58                per_spend_anchor: FieldNotPresent,
59                cv: ExtendedPoint::generator().try_into().unwrap(),
60                nullifier,
61                rk,
62                zkproof: proof,
63                spend_auth_sig: redjubjub::Signature::from({
64                    let mut b = [0u8; 64];
65                    b.copy_from_slice(sig_bytes.as_slice());
66                    b
67                }),
68            })
69            .boxed()
70    }
71
72    type Strategy = BoxedStrategy<Self>;
73}
74
75impl Arbitrary for Output {
76    type Parameters = ();
77
78    fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
79        (
80            any::<note::EncryptedNote>(),
81            any::<note::WrappedNoteKey>(),
82            any::<Groth16Proof>(),
83        )
84            .prop_map(|(enc_ciphertext, out_ciphertext, zkproof)| Self {
85                cv: ExtendedPoint::generator().try_into().unwrap(),
86                cm_u: NoteCommitment(AffinePoint::identity()).extract_u(),
87                ephemeral_key: keys::EphemeralPublicKey(ExtendedPoint::generator().into()),
88                enc_ciphertext,
89                out_ciphertext,
90                zkproof,
91            })
92            .boxed()
93    }
94
95    type Strategy = BoxedStrategy<Self>;
96}
97
98impl Arbitrary for OutputInTransactionV4 {
99    type Parameters = ();
100
101    fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
102        any::<Output>().prop_map(OutputInTransactionV4).boxed()
103    }
104
105    type Strategy = BoxedStrategy<Self>;
106}
107
108/// Creates Strategy for generation VerificationKeyBytes, since the `redjubjub`
109/// crate does not provide an Arbitrary implementation for it.
110fn spendauth_verification_key_bytes() -> impl Strategy<Value = ValidatingKey> {
111    prop::array::uniform32(any::<u8>()).prop_map(|bytes| {
112        let rng = ChaChaRng::from_seed(bytes);
113        let sk = redjubjub::SigningKey::<redjubjub::SpendAuth>::new(rng);
114        redjubjub::VerificationKey::<redjubjub::SpendAuth>::from(&sk)
115            .try_into()
116            .unwrap()
117    })
118}
119
120fn jubjub_base_strat() -> BoxedStrategy<jubjub::Base> {
121    (vec(any::<u8>(), 64))
122        .prop_map(|bytes| {
123            let bytes = bytes.try_into().expect("vec is the correct length");
124            jubjub::Base::from_bytes_wide(&bytes)
125        })
126        .boxed()
127}
128
129impl Arbitrary for tree::Root {
130    type Parameters = ();
131
132    fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
133        jubjub_base_strat().prop_map(tree::Root).boxed()
134    }
135
136    type Strategy = BoxedStrategy<Self>;
137}
138
139impl Arbitrary for tree::Node {
140    type Parameters = ();
141
142    fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
143        jubjub_base_strat().prop_map(tree::Node::from).boxed()
144    }
145
146    type Strategy = BoxedStrategy<Self>;
147}