zebra_chain/sapling/
arbitrary.rs

1//! Randomised data generation for sapling types.
2
3use group::Group;
4use jubjub::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, Output, OutputInTransactionV4, PerSpendAnchor, SharedAnchor,
15    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().into(),
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().into(),
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().into(),
86                cm_u: sapling_crypto::note::ExtractedNoteCommitment::from_bytes(&[0u8; 32])
87                    .unwrap(),
88                ephemeral_key: keys::EphemeralPublicKey(ExtendedPoint::generator().into()),
89                enc_ciphertext,
90                out_ciphertext,
91                zkproof,
92            })
93            .boxed()
94    }
95
96    type Strategy = BoxedStrategy<Self>;
97}
98
99impl Arbitrary for OutputInTransactionV4 {
100    type Parameters = ();
101
102    fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
103        any::<Output>().prop_map(OutputInTransactionV4).boxed()
104    }
105
106    type Strategy = BoxedStrategy<Self>;
107}
108
109/// Creates Strategy for generation VerificationKeyBytes, since the `redjubjub`
110/// crate does not provide an Arbitrary implementation for it.
111fn spendauth_verification_key_bytes() -> impl Strategy<Value = ValidatingKey> {
112    prop::array::uniform32(any::<u8>()).prop_map(|bytes| {
113        let rng = ChaChaRng::from_seed(bytes);
114        let sk = redjubjub::SigningKey::<redjubjub::SpendAuth>::new(rng);
115        redjubjub::VerificationKey::<redjubjub::SpendAuth>::from(&sk)
116            .try_into()
117            .unwrap()
118    })
119}
120
121fn jubjub_base_strat() -> BoxedStrategy<jubjub::Base> {
122    (vec(any::<u8>(), 64))
123        .prop_map(|bytes| {
124            let bytes = bytes.try_into().expect("vec is the correct length");
125            jubjub::Base::from_bytes_wide(&bytes)
126        })
127        .boxed()
128}
129
130impl Arbitrary for tree::Root {
131    type Parameters = ();
132
133    fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
134        jubjub_base_strat().prop_map(tree::Root).boxed()
135    }
136
137    type Strategy = BoxedStrategy<Self>;
138}
139
140impl Arbitrary for tree::legacy::Node {
141    type Parameters = ();
142
143    fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
144        jubjub_base_strat()
145            .prop_map(tree::legacy::Node::from)
146            .boxed()
147    }
148
149    type Strategy = BoxedStrategy<Self>;
150}
151
152impl From<jubjub::Fq> for tree::legacy::Node {
153    fn from(x: jubjub::Fq) -> Self {
154        let node = sapling_crypto::Node::from_bytes(x.to_bytes());
155        if node.is_some().into() {
156            tree::legacy::Node(node.unwrap())
157        } else {
158            sapling_crypto::Node::from_bytes([0; 32]).unwrap().into()
159        }
160    }
161}