1use alloc::collections::BTreeMap;
2use alloc::string::String;
3use alloc::vec::Vec;
4
5use ff::PrimeField;
6use zcash_note_encryption::{EphemeralKeyBytes, OutgoingCipherKey};
7use zip32::ChildIndex;
8
9use super::{Bundle, Output, Spend, Zip32Derivation};
10use crate::{
11 bundle::GrothProofBytes,
12 keys::{SpendAuthorizingKey, SpendValidatingKey},
13 note::ExtractedNoteCommitment,
14 value::{NoteValue, ValueCommitTrapdoor, ValueCommitment, ValueSum},
15 Anchor, MerklePath, Node, Nullifier, PaymentAddress, ProofGenerationKey, Rseed,
16};
17
18impl Bundle {
19 pub fn parse(
21 spends: Vec<Spend>,
22 outputs: Vec<Output>,
23 value_sum: i128,
24 anchor: [u8; 32],
25 bsk: Option<[u8; 32]>,
26 ) -> Result<Self, ParseError> {
27 let value_sum = ValueSum::from_raw(value_sum);
28
29 let anchor = Anchor::from_bytes(anchor)
30 .into_option()
31 .ok_or(ParseError::InvalidAnchor)?;
32
33 let bsk = bsk
34 .map(redjubjub::SigningKey::try_from)
35 .transpose()
36 .map_err(|_| ParseError::InvalidBindingSignatureSigningKey)?;
37
38 Ok(Self {
39 spends,
40 outputs,
41 value_sum,
42 anchor,
43 bsk,
44 })
45 }
46}
47
48impl Spend {
49 #[allow(clippy::too_many_arguments)]
51 pub fn parse(
52 cv: [u8; 32],
53 nullifier: [u8; 32],
54 rk: [u8; 32],
55 zkproof: Option<GrothProofBytes>,
56 spend_auth_sig: Option<[u8; 64]>,
57 recipient: Option<[u8; 43]>,
58 value: Option<u64>,
59 rcm: Option<[u8; 32]>,
60 rseed: Option<[u8; 32]>,
61 rcv: Option<[u8; 32]>,
62 proof_generation_key: Option<([u8; 32], [u8; 32])>,
63 witness: Option<(u32, [[u8; 32]; 32])>,
64 alpha: Option<[u8; 32]>,
65 zip32_derivation: Option<Zip32Derivation>,
66 dummy_ask: Option<[u8; 32]>,
67 proprietary: BTreeMap<String, Vec<u8>>,
68 ) -> Result<Self, ParseError> {
69 let cv = ValueCommitment::from_bytes_not_small_order(&cv)
70 .into_option()
71 .ok_or(ParseError::InvalidValueCommitment)?;
72
73 let nullifier = Nullifier(nullifier);
74
75 let rk = redjubjub::VerificationKey::try_from(rk)
76 .map_err(|_| ParseError::InvalidRandomizedKey)?;
77
78 let spend_auth_sig = spend_auth_sig.map(redjubjub::Signature::from);
79
80 let recipient = recipient
81 .as_ref()
82 .map(|r| PaymentAddress::from_bytes(r).ok_or(ParseError::InvalidRecipient))
83 .transpose()?;
84
85 let value = value.map(NoteValue::from_raw);
86
87 let rseed = match (rcm, rseed) {
88 (None, None) => Ok(None),
89 (Some(rcm), None) => jubjub::Scalar::from_repr(rcm)
90 .into_option()
91 .ok_or(ParseError::InvalidNoteCommitRandomness)
92 .map(Rseed::BeforeZip212)
93 .map(Some),
94 (None, Some(rseed)) => Ok(Some(Rseed::AfterZip212(rseed))),
95 (Some(_), Some(_)) => Err(ParseError::MixedNoteCommitRandomnessAndRseed),
96 }?;
97
98 let rcv = rcv
99 .map(|rcv| {
100 ValueCommitTrapdoor::from_bytes(rcv)
101 .into_option()
102 .ok_or(ParseError::InvalidValueCommitTrapdoor)
103 })
104 .transpose()?;
105
106 let proof_generation_key = proof_generation_key
107 .map(|(ak, nsk)| {
108 Ok(ProofGenerationKey {
109 ak: SpendValidatingKey::from_bytes(&ak)
110 .ok_or(ParseError::InvalidProofGenerationKey)?,
111 nsk: jubjub::Scalar::from_repr(nsk)
112 .into_option()
113 .ok_or(ParseError::InvalidProofGenerationKey)?,
114 })
115 })
116 .transpose()?;
117
118 let witness = witness
119 .map(|(position, auth_path_bytes)| {
120 let path_elems = auth_path_bytes
121 .into_iter()
122 .map(|hash| {
123 Node::from_bytes(hash)
124 .into_option()
125 .ok_or(ParseError::InvalidWitness)
126 })
127 .collect::<Result<Vec<_>, _>>()?;
128
129 MerklePath::from_parts(path_elems, u64::from(position).into())
130 .map_err(|()| ParseError::InvalidWitness)
131 })
132 .transpose()?;
133
134 let alpha = alpha
135 .map(|alpha| {
136 jubjub::Scalar::from_repr(alpha)
137 .into_option()
138 .ok_or(ParseError::InvalidSpendAuthRandomizer)
139 })
140 .transpose()?;
141
142 let dummy_ask = dummy_ask
143 .map(|dummy_ask| {
144 SpendAuthorizingKey::from_bytes(&dummy_ask)
145 .ok_or(ParseError::InvalidDummySpendAuthorizingKey)
146 })
147 .transpose()?;
148
149 Ok(Self {
150 cv,
151 nullifier,
152 rk,
153 zkproof,
154 spend_auth_sig,
155 recipient,
156 value,
157 rseed,
158 rcv,
159 proof_generation_key,
160 witness,
161 alpha,
162 zip32_derivation,
163 dummy_ask,
164 proprietary,
165 })
166 }
167}
168
169impl Output {
170 #[allow(clippy::too_many_arguments)]
172 pub fn parse(
173 cv: [u8; 32],
174 cmu: [u8; 32],
175 ephemeral_key: [u8; 32],
176 enc_ciphertext: Vec<u8>,
177 out_ciphertext: Vec<u8>,
178 zkproof: Option<GrothProofBytes>,
179 recipient: Option<[u8; 43]>,
180 value: Option<u64>,
181 rseed: Option<[u8; 32]>,
182 rcv: Option<[u8; 32]>,
183 ock: Option<[u8; 32]>,
184 zip32_derivation: Option<Zip32Derivation>,
185 user_address: Option<String>,
186 proprietary: BTreeMap<String, Vec<u8>>,
187 ) -> Result<Self, ParseError> {
188 let cv = ValueCommitment::from_bytes_not_small_order(&cv)
189 .into_option()
190 .ok_or(ParseError::InvalidValueCommitment)?;
191
192 let cmu = ExtractedNoteCommitment::from_bytes(&cmu)
193 .into_option()
194 .ok_or(ParseError::InvalidExtractedNoteCommitment)?;
195
196 let ephemeral_key = EphemeralKeyBytes(ephemeral_key);
197
198 let enc_ciphertext = enc_ciphertext
199 .as_slice()
200 .try_into()
201 .map_err(|_| ParseError::InvalidEncCiphertext)?;
202
203 let out_ciphertext = out_ciphertext
204 .as_slice()
205 .try_into()
206 .map_err(|_| ParseError::InvalidOutCiphertext)?;
207
208 let recipient = recipient
209 .as_ref()
210 .map(|r| PaymentAddress::from_bytes(r).ok_or(ParseError::InvalidRecipient))
211 .transpose()?;
212
213 let value = value.map(NoteValue::from_raw);
214
215 let rcv = rcv
216 .map(|rcv| {
217 ValueCommitTrapdoor::from_bytes(rcv)
218 .into_option()
219 .ok_or(ParseError::InvalidValueCommitTrapdoor)
220 })
221 .transpose()?;
222
223 let ock = ock.map(OutgoingCipherKey);
224
225 Ok(Self {
226 cv,
227 cmu,
228 ephemeral_key,
229 enc_ciphertext,
230 out_ciphertext,
231 zkproof,
232 recipient,
233 value,
234 rseed,
235 rcv,
236 ock,
237 zip32_derivation,
238 user_address,
239 proprietary,
240 })
241 }
242}
243
244impl Zip32Derivation {
245 pub fn parse(
250 seed_fingerprint: [u8; 32],
251 derivation_path: Vec<u32>,
252 ) -> Result<Self, ParseError> {
253 Ok(Self {
254 seed_fingerprint,
255 derivation_path: derivation_path
256 .into_iter()
257 .map(|i| ChildIndex::from_index(i).ok_or(ParseError::InvalidZip32Derivation))
258 .collect::<Result<_, _>>()?,
259 })
260 }
261}
262
263#[derive(Debug)]
265pub enum ParseError {
266 InvalidAnchor,
268 InvalidBindingSignatureSigningKey,
270 InvalidDummySpendAuthorizingKey,
272 InvalidEncCiphertext,
274 InvalidExtractedNoteCommitment,
276 InvalidNoteCommitRandomness,
278 InvalidOutCiphertext,
280 InvalidProofGenerationKey,
282 InvalidRandomizedKey,
284 InvalidRecipient,
286 InvalidSpendAuthRandomizer,
288 InvalidValueCommitment,
290 InvalidValueCommitTrapdoor,
292 InvalidWitness,
294 InvalidZip32Derivation,
296 MixedNoteCommitRandomnessAndRseed,
298}