sapling_crypto/
zip32.rs

1//! Sapling key derivation according to ZIP 32 and ZIP 316
2//!
3//! Implements [section 4.2.2] of the Zcash Protocol Specification.
4//!
5//! [section 4.2.2]: https://zips.z.cash/protocol/protocol.pdf#saplingkeycomponents
6
7use aes::Aes256;
8use blake2b_simd::Params as Blake2bParams;
9use fpe::ff1::{BinaryNumeralString, FF1};
10use subtle::CtOption;
11use zcash_spec::PrfExpand;
12use zip32::{ChainCode, ChildIndex, DiversifierIndex, Scope};
13
14use core::ops::AddAssign;
15use core2::io::{self, Read, Write};
16
17use super::{Diversifier, NullifierDerivingKey, PaymentAddress, ViewingKey};
18use crate::note_encryption::PreparedIncomingViewingKey;
19use crate::{
20    constants::PROOF_GENERATION_KEY_GENERATOR,
21    keys::{
22        DecodingError, ExpandedSpendingKey, FullViewingKey, OutgoingViewingKey, SpendAuthorizingKey,
23    },
24    SaplingIvk,
25};
26
27pub const ZIP32_SAPLING_MASTER_PERSONALIZATION: &[u8; 16] = b"ZcashIP32Sapling";
28pub const ZIP32_SAPLING_FVFP_PERSONALIZATION: &[u8; 16] = b"ZcashSaplingFVFP";
29pub const ZIP32_SAPLING_INT_PERSONALIZATION: &[u8; 16] = b"Zcash_SaplingInt";
30
31/// Attempt to produce a payment address given the specified diversifier
32/// index, and return None if the specified index does not produce a valid
33/// diversifier.
34pub fn sapling_address(
35    fvk: &FullViewingKey,
36    dk: &DiversifierKey,
37    j: DiversifierIndex,
38) -> Option<PaymentAddress> {
39    dk.diversifier(j)
40        .and_then(|d_j| fvk.vk.to_payment_address(d_j))
41}
42
43/// Search the diversifier space starting at diversifier index `j` for
44/// one which will produce a valid diversifier, and return the payment address
45/// constructed using that diversifier along with the index at which the
46/// valid diversifier was found.
47pub fn sapling_find_address(
48    fvk: &FullViewingKey,
49    dk: &DiversifierKey,
50    j: DiversifierIndex,
51) -> Option<(DiversifierIndex, PaymentAddress)> {
52    let (j, d_j) = dk.find_diversifier(j)?;
53    fvk.vk.to_payment_address(d_j).map(|addr| (j, addr))
54}
55
56/// Returns the payment address corresponding to the smallest valid diversifier
57/// index, along with that index.
58pub fn sapling_default_address(
59    fvk: &FullViewingKey,
60    dk: &DiversifierKey,
61) -> (DiversifierIndex, PaymentAddress) {
62    // This unwrap is safe, if you have to search the 2^88 space of
63    // diversifiers it'll never return anyway.
64    sapling_find_address(fvk, dk, DiversifierIndex::new()).unwrap()
65}
66
67/// Convenience function for child OVK derivation
68fn derive_child_ovk(parent: &OutgoingViewingKey, i_l: &[u8]) -> OutgoingViewingKey {
69    let mut ovk = [0u8; 32];
70    ovk.copy_from_slice(&PrfExpand::SAPLING_ZIP32_CHILD_OVK.with(i_l, &parent.0)[..32]);
71    OutgoingViewingKey(ovk)
72}
73
74/// Returns the internal full viewing key and diversifier key
75/// for the provided external FVK = (ak, nk, ovk) and dk encoded
76/// in a [Unified FVK].
77///
78/// [Unified FVK]: https://zips.z.cash/zip-0316#encoding-of-unified-full-incoming-viewing-keys
79pub fn sapling_derive_internal_fvk(
80    fvk: &FullViewingKey,
81    dk: &DiversifierKey,
82) -> (FullViewingKey, DiversifierKey) {
83    let i = {
84        let mut h = Blake2bParams::new()
85            .hash_length(32)
86            .personal(ZIP32_SAPLING_INT_PERSONALIZATION)
87            .to_state();
88        h.update(&fvk.to_bytes());
89        h.update(&dk.0);
90        h.finalize()
91    };
92    let i_nsk =
93        jubjub::Fr::from_bytes_wide(&PrfExpand::SAPLING_ZIP32_INTERNAL_NSK.with(i.as_bytes()));
94    let r = PrfExpand::SAPLING_ZIP32_INTERNAL_DK_OVK.with(i.as_bytes());
95    // PROOF_GENERATION_KEY_GENERATOR = \mathcal{H}^Sapling
96    let nk_internal = NullifierDerivingKey(PROOF_GENERATION_KEY_GENERATOR * i_nsk + fvk.vk.nk.0);
97    let dk_internal = DiversifierKey(r[..32].try_into().unwrap());
98    let ovk_internal = OutgoingViewingKey(r[32..].try_into().unwrap());
99
100    (
101        FullViewingKey {
102            vk: ViewingKey {
103                ak: fvk.vk.ak.clone(),
104                nk: nk_internal,
105            },
106            ovk: ovk_internal,
107        },
108        dk_internal,
109    )
110}
111
112/// A Sapling full viewing key fingerprint
113struct FvkFingerprint([u8; 32]);
114
115impl From<&FullViewingKey> for FvkFingerprint {
116    fn from(fvk: &FullViewingKey) -> Self {
117        let mut h = Blake2bParams::new()
118            .hash_length(32)
119            .personal(ZIP32_SAPLING_FVFP_PERSONALIZATION)
120            .to_state();
121        h.update(&fvk.to_bytes());
122        let mut fvfp = [0u8; 32];
123        fvfp.copy_from_slice(h.finalize().as_bytes());
124        FvkFingerprint(fvfp)
125    }
126}
127
128impl FvkFingerprint {
129    fn tag(&self) -> FvkTag {
130        let mut tag = [0u8; 4];
131        tag.copy_from_slice(&self.0[..4]);
132        FvkTag(tag)
133    }
134}
135
136/// A Sapling full viewing key tag
137#[derive(Clone, Copy, Debug, PartialEq)]
138struct FvkTag([u8; 4]);
139
140impl FvkTag {
141    fn master() -> Self {
142        FvkTag([0u8; 4])
143    }
144
145    fn as_bytes(&self) -> &[u8; 4] {
146        &self.0
147    }
148}
149
150/// A key used to derive diversifiers for a particular child key
151#[derive(Clone, Copy, Debug, PartialEq, Eq)]
152pub struct DiversifierKey([u8; 32]);
153
154impl DiversifierKey {
155    pub fn master(sk_m: &[u8]) -> Self {
156        let mut dk_m = [0u8; 32];
157        dk_m.copy_from_slice(&PrfExpand::SAPLING_ZIP32_MASTER_DK.with(sk_m)[..32]);
158        DiversifierKey(dk_m)
159    }
160
161    /// Constructs the diversifier key from its constituent bytes.
162    pub fn from_bytes(key: [u8; 32]) -> Self {
163        DiversifierKey(key)
164    }
165
166    /// Returns the byte representation of the diversifier key.
167    pub fn as_bytes(&self) -> &[u8; 32] {
168        &self.0
169    }
170
171    fn derive_child(&self, i_l: &[u8]) -> Self {
172        let mut dk = [0u8; 32];
173        dk.copy_from_slice(&PrfExpand::SAPLING_ZIP32_CHILD_DK.with(i_l, &self.0)[..32]);
174        DiversifierKey(dk)
175    }
176
177    fn try_diversifier_internal(ff: &FF1<Aes256>, j: DiversifierIndex) -> Option<Diversifier> {
178        // Generate d_j
179        let enc = ff
180            .encrypt(&[], &BinaryNumeralString::from_bytes_le(j.as_bytes()))
181            .unwrap();
182        let mut d_j = [0; 11];
183        d_j.copy_from_slice(&enc.to_bytes_le());
184        let diversifier = Diversifier(d_j);
185
186        // validate that the generated diversifier maps to a jubjub subgroup point.
187        diversifier.g_d().map(|_| diversifier)
188    }
189
190    /// Attempts to produce a diversifier at the given index. Returns None
191    /// if the index does not produce a valid diversifier.
192    pub fn diversifier(&self, j: DiversifierIndex) -> Option<Diversifier> {
193        let ff = FF1::<Aes256>::new(&self.0, 2).unwrap();
194        Self::try_diversifier_internal(&ff, j)
195    }
196
197    /// Returns the diversifier index to which this key maps the given diversifier.
198    ///
199    /// This method cannot be used to verify whether the diversifier was originally
200    /// generated with this diversifier key, because all valid diversifiers can be
201    /// produced by all diversifier keys.
202    pub fn diversifier_index(&self, d: &Diversifier) -> DiversifierIndex {
203        let ff = FF1::<Aes256>::new(&self.0, 2).unwrap();
204        let dec = ff
205            .decrypt(&[], &BinaryNumeralString::from_bytes_le(&d.0[..]))
206            .unwrap();
207        DiversifierIndex::from(<[u8; 11]>::try_from(&dec.to_bytes_le()[..]).unwrap())
208    }
209
210    /// Returns the first index starting from j that generates a valid
211    /// diversifier, along with the corresponding diversifier. Returns
212    /// `None` if the diversifier space contains no valid diversifiers
213    /// at or above the specified diversifier index.
214    pub fn find_diversifier(
215        &self,
216        mut j: DiversifierIndex,
217    ) -> Option<(DiversifierIndex, Diversifier)> {
218        let ff = FF1::<Aes256>::new(&self.0, 2).unwrap();
219        loop {
220            match Self::try_diversifier_internal(&ff, j) {
221                Some(d_j) => return Some((j, d_j)),
222                None => {
223                    if j.increment().is_err() {
224                        return None;
225                    }
226                }
227            }
228        }
229    }
230}
231
232/// The derivation index associated with a key.
233///
234/// Master keys are never derived via the ZIP 32 child derivation process, but they have
235/// an index in their encoding. This type allows the encoding to be represented, while
236/// also enabling the derivation methods to only accept [`ChildIndex`].
237#[derive(Clone, Copy, Debug, PartialEq, Eq)]
238enum KeyIndex {
239    Master,
240    Child(ChildIndex),
241}
242
243impl KeyIndex {
244    fn new(depth: u8, i: u32) -> Option<Self> {
245        match (depth == 0, i) {
246            (true, 0) => Some(KeyIndex::Master),
247            (false, _) => ChildIndex::from_index(i).map(KeyIndex::Child),
248            _ => None,
249        }
250    }
251
252    fn index(&self) -> u32 {
253        match self {
254            KeyIndex::Master => 0,
255            KeyIndex::Child(i) => i.index(),
256        }
257    }
258}
259
260/// A Sapling extended spending key
261#[derive(Clone)]
262pub struct ExtendedSpendingKey {
263    depth: u8,
264    parent_fvk_tag: FvkTag,
265    child_index: KeyIndex,
266    chain_code: ChainCode,
267    pub expsk: ExpandedSpendingKey,
268    dk: DiversifierKey,
269}
270
271impl core::cmp::PartialEq for ExtendedSpendingKey {
272    fn eq(&self, rhs: &ExtendedSpendingKey) -> bool {
273        self.depth == rhs.depth
274            && self.parent_fvk_tag == rhs.parent_fvk_tag
275            && self.child_index == rhs.child_index
276            && self.chain_code == rhs.chain_code
277            && self.expsk.ask == rhs.expsk.ask
278            && self.expsk.nsk == rhs.expsk.nsk
279            && self.expsk.ovk == rhs.expsk.ovk
280            && self.dk == rhs.dk
281    }
282}
283
284impl core::fmt::Debug for ExtendedSpendingKey {
285    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
286        write!(
287            f,
288            "ExtendedSpendingKey(d = {}, tag_p = {:?}, i = {:?})",
289            self.depth, self.parent_fvk_tag, self.child_index
290        )
291    }
292}
293
294impl ExtendedSpendingKey {
295    pub fn master(seed: &[u8]) -> Self {
296        let i = Blake2bParams::new()
297            .hash_length(64)
298            .personal(ZIP32_SAPLING_MASTER_PERSONALIZATION)
299            .hash(seed);
300
301        let sk_m = &i.as_bytes()[..32];
302        let mut c_m = [0u8; 32];
303        c_m.copy_from_slice(&i.as_bytes()[32..]);
304
305        ExtendedSpendingKey {
306            depth: 0,
307            parent_fvk_tag: FvkTag::master(),
308            child_index: KeyIndex::Master,
309            chain_code: ChainCode::new(c_m),
310            expsk: ExpandedSpendingKey::from_spending_key(sk_m),
311            dk: DiversifierKey::master(sk_m),
312        }
313    }
314
315    /// Decodes the extended spending key from its serialized representation as defined in
316    /// [ZIP 32](https://zips.z.cash/zip-0032)
317    pub fn from_bytes(b: &[u8]) -> Result<Self, DecodingError> {
318        if b.len() != 169 {
319            return Err(DecodingError::LengthInvalid {
320                expected: 169,
321                actual: b.len(),
322            });
323        }
324
325        let depth = b[0];
326
327        let mut parent_fvk_tag = FvkTag([0; 4]);
328        parent_fvk_tag.0[..].copy_from_slice(&b[1..5]);
329
330        let mut ci_bytes = [0u8; 4];
331        ci_bytes[..].copy_from_slice(&b[5..9]);
332        let child_index = KeyIndex::new(depth, u32::from_le_bytes(ci_bytes))
333            .ok_or(DecodingError::UnsupportedChildIndex)?;
334
335        let mut c = [0u8; 32];
336        c[..].copy_from_slice(&b[9..41]);
337
338        let expsk = ExpandedSpendingKey::from_bytes(&b[41..137])?;
339
340        let mut dk = DiversifierKey([0u8; 32]);
341        dk.0[..].copy_from_slice(&b[137..169]);
342
343        Ok(ExtendedSpendingKey {
344            depth,
345            parent_fvk_tag,
346            child_index,
347            chain_code: ChainCode::new(c),
348            expsk,
349            dk,
350        })
351    }
352
353    /// Reads and decodes the encoded form of the extended spending key as defined in
354    /// [ZIP 32](https://zips.z.cash/zip-0032) from the provided reader.
355    pub fn read<R: Read>(mut reader: R) -> io::Result<Self> {
356        let mut depth = [0; 1];
357        reader.read_exact(&mut depth)?;
358        let depth = depth[0];
359        let mut tag = [0; 4];
360        reader.read_exact(&mut tag)?;
361        let mut child_index_bytes = [0; 4];
362        reader.read_exact(&mut child_index_bytes)?;
363        let child_index =
364            KeyIndex::new(depth, u32::from_le_bytes(child_index_bytes)).ok_or_else(|| {
365                core2::io::Error::new(
366                    core2::io::ErrorKind::InvalidData,
367                    "Unsupported child index in encoding",
368                )
369            })?;
370        let mut c = [0; 32];
371        reader.read_exact(&mut c)?;
372        let expsk = ExpandedSpendingKey::read(&mut reader)?;
373        let mut dk = [0; 32];
374        reader.read_exact(&mut dk)?;
375
376        Ok(ExtendedSpendingKey {
377            depth,
378            parent_fvk_tag: FvkTag(tag),
379            child_index,
380            chain_code: ChainCode::new(c),
381            expsk,
382            dk: DiversifierKey(dk),
383        })
384    }
385
386    /// Encodes the extended spending key to its serialized representation as defined in
387    /// [ZIP 32](https://zips.z.cash/zip-0032)
388    pub fn to_bytes(&self) -> [u8; 169] {
389        let mut result = [0u8; 169];
390        result[0] = self.depth;
391        result[1..5].copy_from_slice(&self.parent_fvk_tag.as_bytes()[..]);
392        result[5..9].copy_from_slice(&self.child_index.index().to_le_bytes()[..]);
393        result[9..41].copy_from_slice(&self.chain_code.as_bytes()[..]);
394        result[41..137].copy_from_slice(&self.expsk.to_bytes()[..]);
395        result[137..169].copy_from_slice(&self.dk.as_bytes()[..]);
396        result
397    }
398
399    /// Writes the encoded form of the extended spending key as defined in
400    /// [ZIP 32](https://zips.z.cash/zip-0032) to the provided writer.
401    pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
402        writer.write_all(&self.to_bytes())
403    }
404
405    /// Returns the child key corresponding to the path derived from the master key
406    pub fn from_path(master: &ExtendedSpendingKey, path: &[ChildIndex]) -> Self {
407        let mut xsk = master.clone();
408        for &i in path.iter() {
409            xsk = xsk.derive_child(i);
410        }
411        xsk
412    }
413
414    /// Derives the child key at the given (hardened) index.
415    ///
416    /// # Panics
417    ///
418    /// Panics if the child key has `ask = 0`. This has a negligible probability of
419    /// occurring.
420    #[must_use]
421    pub fn derive_child(&self, i: ChildIndex) -> Self {
422        let fvk = FullViewingKey::from_expanded_spending_key(&self.expsk);
423        let tmp = {
424            let le_i = i.index().to_le_bytes();
425            PrfExpand::SAPLING_ZIP32_CHILD_HARDENED.with(
426                self.chain_code.as_bytes(),
427                &self.expsk.to_bytes(),
428                &self.dk.0,
429                &le_i,
430            )
431        };
432        let i_l = &tmp[..32];
433        let mut c_i = [0u8; 32];
434        c_i.copy_from_slice(&tmp[32..]);
435
436        ExtendedSpendingKey {
437            depth: self.depth + 1,
438            parent_fvk_tag: FvkFingerprint::from(&fvk).tag(),
439            child_index: KeyIndex::Child(i),
440            chain_code: ChainCode::new(c_i),
441            expsk: {
442                let mut ask =
443                    jubjub::Fr::from_bytes_wide(&PrfExpand::SAPLING_ZIP32_CHILD_I_ASK.with(i_l));
444                let mut nsk =
445                    jubjub::Fr::from_bytes_wide(&PrfExpand::SAPLING_ZIP32_CHILD_I_NSK.with(i_l));
446                ask.add_assign(self.expsk.ask.to_scalar());
447                nsk.add_assign(&self.expsk.nsk);
448                let ovk = derive_child_ovk(&self.expsk.ovk, i_l);
449                ExpandedSpendingKey {
450                    ask: SpendAuthorizingKey::from_scalar(ask)
451                        .expect("negligible chance of ask == 0"),
452                    nsk,
453                    ovk,
454                }
455            },
456            dk: self.dk.derive_child(i_l),
457        }
458    }
459
460    /// Returns the address with the lowest valid diversifier index, along with
461    /// the diversifier index that generated that address.
462    pub fn default_address(&self) -> (DiversifierIndex, PaymentAddress) {
463        self.to_diversifiable_full_viewing_key().default_address()
464    }
465
466    /// Derives an internal spending key given an external spending key.
467    ///
468    /// Specified in [ZIP 32](https://zips.z.cash/zip-0032#deriving-a-sapling-internal-spending-key).
469    #[must_use]
470    pub fn derive_internal(&self) -> Self {
471        let i = {
472            let fvk = FullViewingKey::from_expanded_spending_key(&self.expsk);
473            let mut h = Blake2bParams::new()
474                .hash_length(32)
475                .personal(ZIP32_SAPLING_INT_PERSONALIZATION)
476                .to_state();
477            h.update(&fvk.to_bytes());
478            h.update(&self.dk.0);
479            h.finalize()
480        };
481        let i_nsk =
482            jubjub::Fr::from_bytes_wide(&PrfExpand::SAPLING_ZIP32_INTERNAL_NSK.with(i.as_bytes()));
483        let r = PrfExpand::SAPLING_ZIP32_INTERNAL_DK_OVK.with(i.as_bytes());
484        let nsk_internal = i_nsk + self.expsk.nsk;
485        let dk_internal = DiversifierKey(r[..32].try_into().unwrap());
486        let ovk_internal = OutgoingViewingKey(r[32..].try_into().unwrap());
487
488        ExtendedSpendingKey {
489            depth: self.depth,
490            parent_fvk_tag: self.parent_fvk_tag,
491            child_index: self.child_index,
492            chain_code: self.chain_code,
493            expsk: ExpandedSpendingKey {
494                ask: self.expsk.ask.clone(),
495                nsk: nsk_internal,
496                ovk: ovk_internal,
497            },
498            dk: dk_internal,
499        }
500    }
501
502    #[deprecated(note = "Use `to_diversifiable_full_viewing_key` instead.")]
503    pub fn to_extended_full_viewing_key(&self) -> ExtendedFullViewingKey {
504        ExtendedFullViewingKey {
505            depth: self.depth,
506            parent_fvk_tag: self.parent_fvk_tag,
507            child_index: self.child_index,
508            chain_code: self.chain_code,
509            fvk: FullViewingKey::from_expanded_spending_key(&self.expsk),
510            dk: self.dk,
511        }
512    }
513
514    pub fn to_diversifiable_full_viewing_key(&self) -> DiversifiableFullViewingKey {
515        DiversifiableFullViewingKey {
516            fvk: FullViewingKey::from_expanded_spending_key(&self.expsk),
517            dk: self.dk,
518        }
519    }
520}
521
522// A Sapling extended full viewing key
523#[derive(Clone)]
524pub struct ExtendedFullViewingKey {
525    depth: u8,
526    parent_fvk_tag: FvkTag,
527    child_index: KeyIndex,
528    chain_code: ChainCode,
529    pub fvk: FullViewingKey,
530    pub(crate) dk: DiversifierKey,
531}
532
533impl core::cmp::PartialEq for ExtendedFullViewingKey {
534    fn eq(&self, rhs: &ExtendedFullViewingKey) -> bool {
535        self.depth == rhs.depth
536            && self.parent_fvk_tag == rhs.parent_fvk_tag
537            && self.child_index == rhs.child_index
538            && self.chain_code == rhs.chain_code
539            && self.fvk.vk.ak == rhs.fvk.vk.ak
540            && self.fvk.vk.nk == rhs.fvk.vk.nk
541            && self.fvk.ovk == rhs.fvk.ovk
542            && self.dk == rhs.dk
543    }
544}
545
546impl core::fmt::Debug for ExtendedFullViewingKey {
547    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
548        write!(
549            f,
550            "ExtendedFullViewingKey(d = {}, tag_p = {:?}, i = {:?})",
551            self.depth, self.parent_fvk_tag, self.child_index
552        )
553    }
554}
555
556impl ExtendedFullViewingKey {
557    pub fn read<R: Read>(mut reader: R) -> io::Result<Self> {
558        let mut depth = [0; 1];
559        reader.read_exact(&mut depth)?;
560        let depth = depth[0];
561        let mut tag = [0; 4];
562        reader.read_exact(&mut tag)?;
563        let mut child_index_bytes = [0; 4];
564        reader.read_exact(&mut child_index_bytes)?;
565        let child_index =
566            KeyIndex::new(depth, u32::from_le_bytes(child_index_bytes)).ok_or_else(|| {
567                core2::io::Error::new(
568                    core2::io::ErrorKind::InvalidData,
569                    "Unsupported child index in encoding",
570                )
571            })?;
572        let mut c = [0; 32];
573        reader.read_exact(&mut c)?;
574        let fvk = FullViewingKey::read(&mut reader)?;
575        let mut dk = [0; 32];
576        reader.read_exact(&mut dk)?;
577
578        Ok(ExtendedFullViewingKey {
579            depth,
580            parent_fvk_tag: FvkTag(tag),
581            child_index,
582            chain_code: ChainCode::new(c),
583            fvk,
584            dk: DiversifierKey(dk),
585        })
586    }
587
588    pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
589        writer.write_all(&[self.depth])?;
590        writer.write_all(&self.parent_fvk_tag.0)?;
591        writer.write_all(&self.child_index.index().to_le_bytes())?;
592        writer.write_all(self.chain_code.as_bytes())?;
593        writer.write_all(&self.fvk.to_bytes())?;
594        writer.write_all(&self.dk.0)?;
595
596        Ok(())
597    }
598
599    /// Attempt to produce a payment address given the specified diversifier
600    /// index, and return None if the specified index does not produce a valid
601    /// diversifier.
602    pub fn address(&self, j: DiversifierIndex) -> Option<PaymentAddress> {
603        sapling_address(&self.fvk, &self.dk, j)
604    }
605
606    /// Search the diversifier space starting at diversifier index `j` for
607    /// one which will produce a valid diversifier, and return the payment address
608    /// constructed using that diversifier along with the index at which the
609    /// valid diversifier was found.
610    pub fn find_address(&self, j: DiversifierIndex) -> Option<(DiversifierIndex, PaymentAddress)> {
611        sapling_find_address(&self.fvk, &self.dk, j)
612    }
613
614    /// Returns the payment address corresponding to the smallest valid diversifier
615    /// index, along with that index.
616    pub fn default_address(&self) -> (DiversifierIndex, PaymentAddress) {
617        sapling_default_address(&self.fvk, &self.dk)
618    }
619
620    /// Derives an internal full viewing key used for internal operations such
621    /// as change and auto-shielding. The internal FVK has the same spend authority
622    /// (the private key corresponding to ak) as the original, but viewing authority
623    /// only for internal transfers.
624    ///
625    /// Specified in [ZIP 32](https://zips.z.cash/zip-0032#deriving-a-sapling-internal-full-viewing-key).
626    #[must_use]
627    pub fn derive_internal(&self) -> Self {
628        let (fvk_internal, dk_internal) = sapling_derive_internal_fvk(&self.fvk, &self.dk);
629
630        ExtendedFullViewingKey {
631            depth: self.depth,
632            parent_fvk_tag: self.parent_fvk_tag,
633            child_index: self.child_index,
634            chain_code: self.chain_code,
635            fvk: fvk_internal,
636            dk: dk_internal,
637        }
638    }
639
640    pub fn to_diversifiable_full_viewing_key(&self) -> DiversifiableFullViewingKey {
641        DiversifiableFullViewingKey {
642            fvk: self.fvk.clone(),
643            dk: self.dk,
644        }
645    }
646}
647
648/// A Sapling key that provides the capability to view incoming and outgoing transactions.
649///
650/// This key is useful anywhere you need to maintain accurate balance, but do not want the
651/// ability to spend funds (such as a view-only wallet).
652///
653/// It comprises the subset of the ZIP 32 extended full viewing key that is used for the
654/// Sapling item in a [ZIP 316 Unified Full Viewing Key][zip-0316-ufvk].
655///
656/// [zip-0316-ufvk]: https://zips.z.cash/zip-0316#encoding-of-unified-full-incoming-viewing-keys
657#[derive(Clone, Debug)]
658pub struct DiversifiableFullViewingKey {
659    fvk: FullViewingKey,
660    dk: DiversifierKey,
661}
662
663impl From<ExtendedFullViewingKey> for DiversifiableFullViewingKey {
664    fn from(extfvk: ExtendedFullViewingKey) -> Self {
665        DiversifiableFullViewingKey {
666            fvk: extfvk.fvk,
667            dk: extfvk.dk,
668        }
669    }
670}
671
672impl From<&ExtendedFullViewingKey> for DiversifiableFullViewingKey {
673    fn from(extfvk: &ExtendedFullViewingKey) -> Self {
674        extfvk.to_diversifiable_full_viewing_key()
675    }
676}
677
678impl DiversifiableFullViewingKey {
679    /// Parses a `DiversifiableFullViewingKey` from its raw byte encoding.
680    ///
681    /// Returns `None` if the bytes do not contain a valid encoding of a diversifiable
682    /// Sapling full viewing key.
683    pub fn from_bytes(bytes: &[u8; 128]) -> Option<Self> {
684        FullViewingKey::read(&bytes[..96]).ok().map(|fvk| Self {
685            fvk,
686            dk: DiversifierKey::from_bytes(bytes[96..].try_into().unwrap()),
687        })
688    }
689
690    /// Returns the raw encoding of this `DiversifiableFullViewingKey`.
691    pub fn to_bytes(&self) -> [u8; 128] {
692        let mut bytes = [0; 128];
693        self.fvk
694            .write(&mut bytes[..96])
695            .expect("slice should be the correct length");
696        bytes[96..].copy_from_slice(&self.dk.as_bytes()[..]);
697        bytes
698    }
699
700    /// Derives the internal `DiversifiableFullViewingKey` corresponding to `self` (which
701    /// is assumed here to be an external DFVK).
702    fn derive_internal(&self) -> Self {
703        let (fvk, dk) = sapling_derive_internal_fvk(&self.fvk, &self.dk);
704        Self { fvk, dk }
705    }
706
707    /// Exposes the external [`FullViewingKey`] component of this diversifiable full viewing key.
708    pub fn fvk(&self) -> &FullViewingKey {
709        &self.fvk
710    }
711
712    /// Returns the internal [`FullViewingKey`] component of this diversifiable full viewing key.
713    pub fn to_internal_fvk(&self) -> FullViewingKey {
714        self.derive_internal().fvk
715    }
716
717    /// Derives a nullifier-deriving key for the provided scope.
718    ///
719    /// This API is provided so that nullifiers for change notes can be correctly computed.
720    pub fn to_nk(&self, scope: Scope) -> NullifierDerivingKey {
721        match scope {
722            Scope::External => self.fvk.vk.nk,
723            Scope::Internal => self.derive_internal().fvk.vk.nk,
724        }
725    }
726
727    /// Derives an incoming viewing key corresponding to this full viewing key.
728    // TODO: This should be renamed "prepare" and return a PreparedIncomingViewingKey
729    pub fn to_ivk(&self, scope: Scope) -> SaplingIvk {
730        match scope {
731            Scope::External => self.fvk.vk.ivk(),
732            Scope::Internal => self.derive_internal().fvk.vk.ivk(),
733        }
734    }
735
736    /// Derives the external diversifiable incoming viewing key corresponding to this full viewing key.
737    pub fn to_external_ivk(&self) -> IncomingViewingKey {
738        IncomingViewingKey {
739            dk: self.dk,
740            ivk: self.to_ivk(Scope::External),
741        }
742    }
743
744    /// Derives an outgoing viewing key corresponding to this full viewing key.
745    pub fn to_ovk(&self, scope: Scope) -> OutgoingViewingKey {
746        match scope {
747            Scope::External => self.fvk.ovk,
748            Scope::Internal => self.derive_internal().fvk.ovk,
749        }
750    }
751
752    /// Attempts to produce a valid payment address for the given diversifier index.
753    ///
754    /// Returns `None` if the diversifier index does not produce a valid diversifier for
755    /// this `DiversifiableFullViewingKey`.
756    pub fn address(&self, j: DiversifierIndex) -> Option<PaymentAddress> {
757        self.to_external_ivk().address_at(j)
758    }
759
760    /// Finds the next valid payment address starting from the given diversifier index.
761    ///
762    /// This searches the diversifier space starting at `j` and incrementing, to find an
763    /// index which will produce a valid diversifier (a 50% probability for each index).
764    ///
765    /// Returns the index at which the valid diversifier was found along with the payment
766    /// address constructed using that diversifier, or `None` if the maximum index was
767    /// reached and no valid diversifier was found.
768    pub fn find_address(&self, j: DiversifierIndex) -> Option<(DiversifierIndex, PaymentAddress)> {
769        self.to_external_ivk().find_address(j)
770    }
771
772    /// Returns the payment address corresponding to the smallest valid diversifier index,
773    /// along with that index.
774    pub fn default_address(&self) -> (DiversifierIndex, PaymentAddress) {
775        self.to_external_ivk().find_address(0u32).unwrap()
776    }
777
778    /// Returns the payment address corresponding to the specified diversifier, if any.
779    ///
780    /// In general, it is preferable to use `find_address` instead, but this method is
781    /// useful in some cases for matching keys to existing payment addresses.
782    pub fn diversified_address(&self, diversifier: Diversifier) -> Option<PaymentAddress> {
783        self.to_external_ivk().address(diversifier)
784    }
785
786    /// Returns the internal address corresponding to the smallest valid diversifier index,
787    /// along with that index.
788    ///
789    /// This address **MUST NOT** be encoded and exposed to end users. User interfaces
790    /// should instead mark these notes as "change notes" or "internal wallet operations".
791    pub fn change_address(&self) -> (DiversifierIndex, PaymentAddress) {
792        let internal_dfvk = self.derive_internal();
793        sapling_default_address(&internal_dfvk.fvk, &internal_dfvk.dk)
794    }
795
796    /// Returns the change address corresponding to the specified diversifier, if any.
797    ///
798    /// In general, it is preferable to use `change_address` instead, but this method is
799    /// useful in some cases for matching keys to existing payment addresses.
800    pub fn diversified_change_address(&self, diversifier: Diversifier) -> Option<PaymentAddress> {
801        self.derive_internal()
802            .fvk
803            .vk
804            .to_payment_address(diversifier)
805    }
806
807    /// Attempts to decrypt the given address's diversifier with this full viewing key.
808    ///
809    /// This method extracts the diversifier from the given address and decrypts it as a
810    /// diversifier index, then verifies that this diversifier index produces the same
811    /// address. Decryption is attempted using both the internal and external parts of the
812    /// full viewing key.
813    ///
814    /// Returns the decrypted diversifier index and its scope, or `None` if the address
815    /// was not generated from this key.
816    pub fn decrypt_diversifier(&self, addr: &PaymentAddress) -> Option<(DiversifierIndex, Scope)> {
817        let j_external = self.dk.diversifier_index(addr.diversifier());
818        if self.address(j_external).as_ref() == Some(addr) {
819            return Some((j_external, Scope::External));
820        }
821
822        let j_internal = self
823            .derive_internal()
824            .dk
825            .diversifier_index(addr.diversifier());
826        if self.address(j_internal).as_ref() == Some(addr) {
827            return Some((j_internal, Scope::Internal));
828        }
829
830        None
831    }
832}
833
834/// A Sapling key that provides the capability to decrypt incoming notes and generate diversified
835/// Sapling payment addresses.
836///
837/// This key is useful for detecting received funds and memos, but is not sufficient
838/// for determining balance.
839///
840/// It comprises the subset of the ZIP 32 extended full viewing key that is used for the
841/// Sapling item in a [ZIP 316 Unified Incoming Viewing Key][zip-0316-ufvk].
842///
843/// [zip-0316-ufvk]: https://zips.z.cash/zip-0316#encoding-of-unified-full-incoming-viewing-keys
844#[derive(Clone, Debug)]
845pub struct IncomingViewingKey {
846    dk: DiversifierKey,
847    ivk: SaplingIvk,
848}
849
850impl IncomingViewingKey {
851    /// Parses a `IncomingViewingKey` from its raw byte encoding.
852    ///
853    /// Returns `None` if the bytes do not contain a valid encoding of a diversifiable
854    /// Sapling incoming viewing key.
855    pub fn from_bytes(bytes: &[u8; 64]) -> CtOption<Self> {
856        jubjub::Fr::from_bytes(&bytes[32..].try_into().unwrap()).map(|fr| IncomingViewingKey {
857            dk: DiversifierKey::from_bytes(bytes[..32].try_into().unwrap()),
858            ivk: SaplingIvk(fr),
859        })
860    }
861
862    /// Returns the raw encoding of this `IncomingViewingKey`.
863    pub fn to_bytes(&self) -> [u8; 64] {
864        let mut bytes = [0; 64];
865        bytes[..32].copy_from_slice(&self.dk.as_bytes()[..]);
866        bytes[32..].copy_from_slice(&self.ivk.0.to_bytes()[..]);
867        bytes
868    }
869
870    /// Returns the prepared incoming viewing key for this IVK.
871    pub fn prepare(&self) -> PreparedIncomingViewingKey {
872        PreparedIncomingViewingKey::new(&self.ivk)
873    }
874
875    /// Attempts to produce a valid payment address for the given diversifier index.
876    ///
877    /// Returns `None` if the diversifier index does not produce a valid diversifier for
878    /// this `IncomingViewingKey`.
879    pub fn address_at(&self, j: impl Into<DiversifierIndex>) -> Option<PaymentAddress> {
880        self.dk
881            .diversifier(j.into())
882            .and_then(|d_j| self.ivk.to_payment_address(d_j))
883    }
884
885    /// Returns the payment address corresponding to the specified diversifier, if any.
886    ///
887    /// In general, it is preferable to use `find_address` instead, but this method is
888    /// useful in some cases for matching keys to existing payment addresses.
889    pub fn address(&self, diversifier: Diversifier) -> Option<PaymentAddress> {
890        self.ivk.to_payment_address(diversifier)
891    }
892
893    /// Finds the next valid payment address starting from the given diversifier index.
894    ///
895    /// This searches the diversifier space starting at `j` and incrementing, to find an
896    /// index which will produce a valid diversifier (a 50% probability for each index).
897    ///
898    /// Returns the index at which the valid diversifier was found along with the payment
899    /// address constructed using that diversifier, or `None` if the maximum index was
900    /// reached and no valid diversifier was found.
901    pub fn find_address(
902        &self,
903        j: impl Into<DiversifierIndex>,
904    ) -> Option<(DiversifierIndex, PaymentAddress)> {
905        let (j, d_j) = self.dk.find_diversifier(j.into())?;
906        self.ivk.to_payment_address(d_j).map(|addr| (j, addr))
907    }
908
909    /// Attempts to decrypt the given address's diversifier with this incoming viewing key.
910    ///
911    /// This method extracts the diversifier from the given address and decrypts it as a
912    /// diversifier index, then verifies that this diversifier index produces the same
913    /// address. Decryption is attempted using both the internal and external parts of the
914    /// full viewing key.
915    ///
916    /// Returns the decrypted diversifier index and its scope, or `None` if the address
917    /// was not generated from this key.
918    pub fn decrypt_diversifier(&self, addr: &PaymentAddress) -> Option<DiversifierIndex> {
919        let j = self.dk.diversifier_index(addr.diversifier());
920        if self.address_at(j).as_ref() == Some(addr) {
921            Some(j)
922        } else {
923            None
924        }
925    }
926}
927
928#[cfg(test)]
929mod tests {
930    use super::*;
931
932    use super::{DiversifiableFullViewingKey, ExtendedSpendingKey};
933    use ff::PrimeField;
934    use group::GroupEncoding;
935
936    #[test]
937    #[allow(deprecated)]
938    fn derive_hardened_child() {
939        let seed = [0; 32];
940        let xsk_m = ExtendedSpendingKey::master(&seed);
941
942        let i_5h = ChildIndex::hardened(5);
943        let _ = xsk_m.derive_child(i_5h);
944    }
945
946    #[test]
947    fn path() {
948        let seed = [0; 32];
949        let xsk_m = ExtendedSpendingKey::master(&seed);
950
951        let xsk_5h = xsk_m.derive_child(ChildIndex::hardened(5));
952        assert_eq!(
953            ExtendedSpendingKey::from_path(&xsk_m, &[ChildIndex::hardened(5)]),
954            xsk_5h
955        );
956
957        let xsk_5h_7 = xsk_5h.derive_child(ChildIndex::hardened(7));
958        assert_eq!(
959            ExtendedSpendingKey::from_path(
960                &xsk_m,
961                &[ChildIndex::hardened(5), ChildIndex::hardened(7)]
962            ),
963            xsk_5h_7
964        );
965    }
966
967    #[test]
968    fn diversifier() {
969        let dk = DiversifierKey([0; 32]);
970        let j_0 = DiversifierIndex::new();
971        let j_1 = DiversifierIndex::from([1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
972        let j_2 = DiversifierIndex::from([2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
973        let j_3 = DiversifierIndex::from([3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
974        // Computed using this Rust implementation
975        let d_0 = [220, 231, 126, 188, 236, 10, 38, 175, 214, 153, 140];
976        let d_3 = [60, 253, 170, 8, 171, 147, 220, 31, 3, 144, 34];
977
978        // j = 0
979        let d_j = dk.diversifier(j_0).unwrap();
980        assert_eq!(d_j.0, d_0);
981        assert_eq!(dk.diversifier_index(&Diversifier(d_0)), j_0);
982
983        // j = 1
984        assert_eq!(dk.diversifier(j_1), None);
985
986        // j = 2
987        assert_eq!(dk.diversifier(j_2), None);
988
989        // j = 3
990        let d_j = dk.diversifier(j_3).unwrap();
991        assert_eq!(d_j.0, d_3);
992        assert_eq!(dk.diversifier_index(&Diversifier(d_3)), j_3);
993    }
994
995    #[test]
996    fn diversifier_index_from() {
997        let di32: u32 = 0xa0b0c0d0;
998        assert_eq!(
999            DiversifierIndex::from(di32),
1000            DiversifierIndex::from([0xd0, 0xc0, 0xb0, 0xa0, 0, 0, 0, 0, 0, 0, 0])
1001        );
1002        let di64: u64 = 0x0102030405060708;
1003        assert_eq!(
1004            DiversifierIndex::from(di64),
1005            DiversifierIndex::from([8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0])
1006        );
1007    }
1008
1009    #[test]
1010    fn find_diversifier() {
1011        let dk = DiversifierKey([0; 32]);
1012        let j_0 = DiversifierIndex::new();
1013        let j_1 = DiversifierIndex::from([1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
1014        let j_2 = DiversifierIndex::from([2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
1015        let j_3 = DiversifierIndex::from([3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
1016        // Computed using this Rust implementation
1017        let d_0 = [220, 231, 126, 188, 236, 10, 38, 175, 214, 153, 140];
1018        let d_3 = [60, 253, 170, 8, 171, 147, 220, 31, 3, 144, 34];
1019
1020        // j = 0
1021        let (j, d_j) = dk.find_diversifier(j_0).unwrap();
1022        assert_eq!(j, j_0);
1023        assert_eq!(d_j.0, d_0);
1024
1025        // j = 1
1026        let (j, d_j) = dk.find_diversifier(j_1).unwrap();
1027        assert_eq!(j, j_3);
1028        assert_eq!(d_j.0, d_3);
1029
1030        // j = 2
1031        let (j, d_j) = dk.find_diversifier(j_2).unwrap();
1032        assert_eq!(j, j_3);
1033        assert_eq!(d_j.0, d_3);
1034
1035        // j = 3
1036        let (j, d_j) = dk.find_diversifier(j_3).unwrap();
1037        assert_eq!(j, j_3);
1038        assert_eq!(d_j.0, d_3);
1039    }
1040
1041    #[test]
1042    fn dfvk_round_trip() {
1043        let dfvk = {
1044            let extsk = ExtendedSpendingKey::master(&[]);
1045            #[allow(deprecated)]
1046            let extfvk = extsk.to_extended_full_viewing_key();
1047            DiversifiableFullViewingKey::from(extfvk)
1048        };
1049
1050        // Check value -> bytes -> parsed round trip.
1051        let dfvk_bytes = dfvk.to_bytes();
1052        let dfvk_parsed = DiversifiableFullViewingKey::from_bytes(&dfvk_bytes).unwrap();
1053        assert_eq!(dfvk_parsed.fvk.vk.ak, dfvk.fvk.vk.ak);
1054        assert_eq!(dfvk_parsed.fvk.vk.nk, dfvk.fvk.vk.nk);
1055        assert_eq!(dfvk_parsed.fvk.ovk, dfvk.fvk.ovk);
1056        assert_eq!(dfvk_parsed.dk, dfvk.dk);
1057
1058        // Check bytes -> parsed -> bytes round trip.
1059        assert_eq!(dfvk_parsed.to_bytes(), dfvk_bytes);
1060    }
1061
1062    #[test]
1063    fn ivk_round_trip() {
1064        let ivk = {
1065            let extsk = ExtendedSpendingKey::master(&[]);
1066            #[allow(deprecated)]
1067            let extfvk = extsk.to_extended_full_viewing_key();
1068            DiversifiableFullViewingKey::from(extfvk).to_external_ivk()
1069        };
1070
1071        // Check value -> bytes -> parsed round trip.
1072        let ivk_bytes = ivk.to_bytes();
1073        let ivk_parsed = IncomingViewingKey::from_bytes(&ivk_bytes).unwrap();
1074        assert_eq!(ivk_parsed.dk, ivk.dk);
1075        assert_eq!(ivk_parsed.ivk.0, ivk.ivk.0);
1076
1077        // Check bytes -> parsed -> bytes round trip.
1078        assert_eq!(ivk_parsed.to_bytes(), ivk_bytes);
1079    }
1080
1081    #[test]
1082    fn address() {
1083        let seed = [0; 32];
1084        let xsk_m = ExtendedSpendingKey::master(&seed);
1085        let xfvk_m = xsk_m.to_diversifiable_full_viewing_key();
1086        let j_0 = DiversifierIndex::new();
1087        let addr_m = xfvk_m.address(j_0).unwrap();
1088        assert_eq!(
1089            addr_m.diversifier().0,
1090            // Computed using this Rust implementation
1091            [59, 246, 250, 31, 131, 191, 69, 99, 200, 167, 19]
1092        );
1093
1094        let j_1 = DiversifierIndex::from([1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
1095        assert_eq!(xfvk_m.address(j_1), None);
1096    }
1097
1098    #[test]
1099    fn default_address() {
1100        let seed = [0; 32];
1101        let xsk_m = ExtendedSpendingKey::master(&seed);
1102        let (j_m, addr_m) = xsk_m.default_address();
1103        assert_eq!(j_m.as_bytes(), &[0; 11]);
1104        assert_eq!(
1105            addr_m.diversifier().0,
1106            // Computed using this Rust implementation
1107            [59, 246, 250, 31, 131, 191, 69, 99, 200, 167, 19]
1108        );
1109    }
1110
1111    #[test]
1112    #[allow(deprecated)]
1113    fn read_write() {
1114        let seed = [0; 32];
1115        let xsk = ExtendedSpendingKey::master(&seed);
1116        let fvk = xsk.to_extended_full_viewing_key();
1117
1118        let mut ser = vec![];
1119        xsk.write(&mut ser).unwrap();
1120        let xsk2 = ExtendedSpendingKey::read(&ser[..]).unwrap();
1121        assert_eq!(xsk2, xsk);
1122
1123        let mut ser = vec![];
1124        fvk.write(&mut ser).unwrap();
1125        let fvk2 = ExtendedFullViewingKey::read(&ser[..]).unwrap();
1126        assert_eq!(fvk2, fvk);
1127    }
1128
1129    #[test]
1130    #[allow(deprecated)]
1131    fn test_vectors() {
1132        struct TestVector {
1133            ask: Option<[u8; 32]>,
1134            nsk: Option<[u8; 32]>,
1135            ovk: [u8; 32],
1136            dk: [u8; 32],
1137            c: [u8; 32],
1138            ak: [u8; 32],
1139            nk: [u8; 32],
1140            ivk: [u8; 32],
1141            xsk: Option<[u8; 169]>,
1142            xfvk: [u8; 169],
1143            fp: [u8; 32],
1144            d0: Option<[u8; 11]>,
1145            d1: Option<[u8; 11]>,
1146            d2: Option<[u8; 11]>,
1147            dmax: Option<[u8; 11]>,
1148            internal_nsk: Option<[u8; 32]>,
1149            internal_ovk: [u8; 32],
1150            internal_dk: [u8; 32],
1151            internal_nk: [u8; 32],
1152            internal_ivk: [u8; 32],
1153            internal_xsk: Option<[u8; 169]>,
1154            internal_xfvk: [u8; 169],
1155            internal_fp: [u8; 32],
1156        }
1157
1158        // From https://github.com/zcash-hackworks/zcash-test-vectors/blob/master/sapling_zip32.py
1159        let test_vectors = [
1160            TestVector {
1161                ask: Some([
1162                    0xb6, 0xc0, 0x0c, 0x93, 0xd3, 0x60, 0x32, 0xb9, 0xa2, 0x68, 0xe9, 0x9e, 0x86,
1163                    0xa8, 0x60, 0x77, 0x65, 0x60, 0xbf, 0x0e, 0x83, 0xc1, 0xa1, 0x0b, 0x51, 0xf6,
1164                    0x07, 0xc9, 0x54, 0x74, 0x25, 0x06,
1165                ]),
1166                nsk: Some([
1167                    0x82, 0x04, 0xed, 0xe8, 0x3b, 0x2f, 0x1f, 0xbd, 0x84, 0xf9, 0xb4, 0x5d, 0x7f,
1168                    0x99, 0x6e, 0x2e, 0xbd, 0x0a, 0x03, 0x0a, 0xd2, 0x43, 0xb4, 0x8e, 0xd3, 0x9f,
1169                    0x74, 0x8a, 0x88, 0x21, 0xea, 0x06,
1170                ]),
1171                ovk: [
1172                    0x39, 0x58, 0x84, 0x89, 0x03, 0x23, 0xb9, 0xd4, 0x93, 0x3c, 0x02, 0x1d, 0xb8,
1173                    0x9b, 0xcf, 0x76, 0x7d, 0xf2, 0x19, 0x77, 0xb2, 0xff, 0x06, 0x83, 0x84, 0x83,
1174                    0x21, 0xa4, 0xdf, 0x4a, 0xfb, 0x21,
1175                ],
1176                dk: [
1177                    0x77, 0xc1, 0x7c, 0xb7, 0x5b, 0x77, 0x96, 0xaf, 0xb3, 0x9f, 0x0f, 0x3e, 0x91,
1178                    0xc9, 0x24, 0x60, 0x7d, 0xa5, 0x6f, 0xa9, 0xa2, 0x0e, 0x28, 0x35, 0x09, 0xbc,
1179                    0x8a, 0x3e, 0xf9, 0x96, 0xa1, 0x72,
1180                ],
1181                c: [
1182                    0xd0, 0x94, 0x7c, 0x4b, 0x03, 0xbf, 0x72, 0xa3, 0x7a, 0xb4, 0x4f, 0x72, 0x27,
1183                    0x6d, 0x1c, 0xf3, 0xfd, 0xcd, 0x7e, 0xbf, 0x3e, 0x73, 0x34, 0x8b, 0x7e, 0x55,
1184                    0x0d, 0x75, 0x20, 0x18, 0x66, 0x8e,
1185                ],
1186                ak: [
1187                    0x93, 0x44, 0x2e, 0x5f, 0xef, 0xfb, 0xff, 0x16, 0xe7, 0x21, 0x72, 0x02, 0xdc,
1188                    0x73, 0x06, 0x72, 0x9f, 0xff, 0xfe, 0x85, 0xaf, 0x56, 0x83, 0xbc, 0xe2, 0x64,
1189                    0x2e, 0x3e, 0xeb, 0x5d, 0x38, 0x71,
1190                ],
1191                nk: [
1192                    0xdc, 0xe8, 0xe7, 0xed, 0xec, 0xe0, 0x4b, 0x89, 0x50, 0x41, 0x7f, 0x85, 0xba,
1193                    0x57, 0x69, 0x1b, 0x78, 0x3c, 0x45, 0xb1, 0xa2, 0x74, 0x22, 0xdb, 0x16, 0x93,
1194                    0xdc, 0xeb, 0x67, 0xb1, 0x01, 0x06,
1195                ],
1196                ivk: [
1197                    0x48, 0x47, 0xa1, 0x30, 0xe7, 0x99, 0xd3, 0xdb, 0xea, 0x36, 0xa1, 0xc1, 0x64,
1198                    0x67, 0xd6, 0x21, 0xfb, 0x2d, 0x80, 0xe3, 0x0b, 0x3b, 0x1d, 0x1a, 0x42, 0x68,
1199                    0x93, 0x41, 0x5d, 0xad, 0x66, 0x01,
1200                ],
1201                xsk: Some([
1202                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd0, 0x94, 0x7c, 0x4b,
1203                    0x03, 0xbf, 0x72, 0xa3, 0x7a, 0xb4, 0x4f, 0x72, 0x27, 0x6d, 0x1c, 0xf3, 0xfd,
1204                    0xcd, 0x7e, 0xbf, 0x3e, 0x73, 0x34, 0x8b, 0x7e, 0x55, 0x0d, 0x75, 0x20, 0x18,
1205                    0x66, 0x8e, 0xb6, 0xc0, 0x0c, 0x93, 0xd3, 0x60, 0x32, 0xb9, 0xa2, 0x68, 0xe9,
1206                    0x9e, 0x86, 0xa8, 0x60, 0x77, 0x65, 0x60, 0xbf, 0x0e, 0x83, 0xc1, 0xa1, 0x0b,
1207                    0x51, 0xf6, 0x07, 0xc9, 0x54, 0x74, 0x25, 0x06, 0x82, 0x04, 0xed, 0xe8, 0x3b,
1208                    0x2f, 0x1f, 0xbd, 0x84, 0xf9, 0xb4, 0x5d, 0x7f, 0x99, 0x6e, 0x2e, 0xbd, 0x0a,
1209                    0x03, 0x0a, 0xd2, 0x43, 0xb4, 0x8e, 0xd3, 0x9f, 0x74, 0x8a, 0x88, 0x21, 0xea,
1210                    0x06, 0x39, 0x58, 0x84, 0x89, 0x03, 0x23, 0xb9, 0xd4, 0x93, 0x3c, 0x02, 0x1d,
1211                    0xb8, 0x9b, 0xcf, 0x76, 0x7d, 0xf2, 0x19, 0x77, 0xb2, 0xff, 0x06, 0x83, 0x84,
1212                    0x83, 0x21, 0xa4, 0xdf, 0x4a, 0xfb, 0x21, 0x77, 0xc1, 0x7c, 0xb7, 0x5b, 0x77,
1213                    0x96, 0xaf, 0xb3, 0x9f, 0x0f, 0x3e, 0x91, 0xc9, 0x24, 0x60, 0x7d, 0xa5, 0x6f,
1214                    0xa9, 0xa2, 0x0e, 0x28, 0x35, 0x09, 0xbc, 0x8a, 0x3e, 0xf9, 0x96, 0xa1, 0x72,
1215                ]),
1216                xfvk: [
1217                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd0, 0x94, 0x7c, 0x4b,
1218                    0x03, 0xbf, 0x72, 0xa3, 0x7a, 0xb4, 0x4f, 0x72, 0x27, 0x6d, 0x1c, 0xf3, 0xfd,
1219                    0xcd, 0x7e, 0xbf, 0x3e, 0x73, 0x34, 0x8b, 0x7e, 0x55, 0x0d, 0x75, 0x20, 0x18,
1220                    0x66, 0x8e, 0x93, 0x44, 0x2e, 0x5f, 0xef, 0xfb, 0xff, 0x16, 0xe7, 0x21, 0x72,
1221                    0x02, 0xdc, 0x73, 0x06, 0x72, 0x9f, 0xff, 0xfe, 0x85, 0xaf, 0x56, 0x83, 0xbc,
1222                    0xe2, 0x64, 0x2e, 0x3e, 0xeb, 0x5d, 0x38, 0x71, 0xdc, 0xe8, 0xe7, 0xed, 0xec,
1223                    0xe0, 0x4b, 0x89, 0x50, 0x41, 0x7f, 0x85, 0xba, 0x57, 0x69, 0x1b, 0x78, 0x3c,
1224                    0x45, 0xb1, 0xa2, 0x74, 0x22, 0xdb, 0x16, 0x93, 0xdc, 0xeb, 0x67, 0xb1, 0x01,
1225                    0x06, 0x39, 0x58, 0x84, 0x89, 0x03, 0x23, 0xb9, 0xd4, 0x93, 0x3c, 0x02, 0x1d,
1226                    0xb8, 0x9b, 0xcf, 0x76, 0x7d, 0xf2, 0x19, 0x77, 0xb2, 0xff, 0x06, 0x83, 0x84,
1227                    0x83, 0x21, 0xa4, 0xdf, 0x4a, 0xfb, 0x21, 0x77, 0xc1, 0x7c, 0xb7, 0x5b, 0x77,
1228                    0x96, 0xaf, 0xb3, 0x9f, 0x0f, 0x3e, 0x91, 0xc9, 0x24, 0x60, 0x7d, 0xa5, 0x6f,
1229                    0xa9, 0xa2, 0x0e, 0x28, 0x35, 0x09, 0xbc, 0x8a, 0x3e, 0xf9, 0x96, 0xa1, 0x72,
1230                ],
1231                fp: [
1232                    0x14, 0xc2, 0x71, 0x3a, 0xdc, 0xe9, 0x3a, 0x83, 0x0e, 0xa8, 0x3a, 0x05, 0x19,
1233                    0x08, 0xb7, 0x44, 0x77, 0x83, 0xf5, 0xd1, 0x06, 0xc0, 0x98, 0x5e, 0x02, 0x55,
1234                    0x0e, 0x42, 0x6f, 0x27, 0x59, 0x7c,
1235                ],
1236                d0: Some([
1237                    0xd8, 0x62, 0x1b, 0x98, 0x1c, 0xf3, 0x00, 0xe9, 0xd4, 0xcc, 0x89,
1238                ]),
1239                d1: Some([
1240                    0x48, 0xea, 0x17, 0xa1, 0x99, 0xc8, 0x4b, 0xd1, 0xba, 0xa5, 0xd4,
1241                ]),
1242                d2: None,
1243                dmax: None,
1244                internal_nsk: Some([
1245                    0x51, 0x12, 0x33, 0x63, 0x6b, 0x95, 0xfd, 0x0a, 0xfb, 0x6b, 0xf8, 0x19, 0x3a,
1246                    0x7d, 0x8f, 0x49, 0xef, 0xd7, 0x36, 0xa9, 0x88, 0x77, 0x5c, 0x54, 0xf9, 0x56,
1247                    0x68, 0x76, 0x46, 0xea, 0xab, 0x07,
1248                ]),
1249                internal_ovk: [
1250                    0x9d, 0xc4, 0x77, 0xfe, 0x1e, 0x7d, 0x28, 0x29, 0x13, 0xf6, 0x51, 0x65, 0x4d,
1251                    0x39, 0x85, 0xf0, 0x9d, 0x53, 0xc2, 0xd3, 0xb5, 0x76, 0x3d, 0x7a, 0x72, 0x3b,
1252                    0xcb, 0xd6, 0xee, 0x05, 0x3d, 0x5a,
1253                ],
1254                internal_dk: [
1255                    0x40, 0xdd, 0xc5, 0x6e, 0x69, 0x75, 0x13, 0x8c, 0x08, 0x39, 0xe5, 0x80, 0xb5,
1256                    0x4d, 0x6d, 0x99, 0x9d, 0xc6, 0x16, 0x84, 0x3c, 0xfe, 0x04, 0x1e, 0x8f, 0x38,
1257                    0x8b, 0x12, 0x4e, 0xf7, 0xb5, 0xed,
1258                ],
1259                internal_nk: [
1260                    0xa3, 0x83, 0x1a, 0x5c, 0x69, 0x33, 0xf8, 0xec, 0x6a, 0xa5, 0xce, 0x31, 0x6c,
1261                    0x50, 0x8b, 0x79, 0x91, 0xcd, 0x94, 0xd3, 0xbd, 0xb7, 0x00, 0xa1, 0xc4, 0x27,
1262                    0xa6, 0xae, 0x15, 0xe7, 0x2f, 0xb5,
1263                ],
1264                internal_ivk: [
1265                    0x79, 0x05, 0x77, 0x32, 0x1c, 0x51, 0x18, 0x04, 0x63, 0x6e, 0xe6, 0xba, 0xa4,
1266                    0xee, 0xa7, 0x79, 0xb4, 0xa4, 0x6a, 0x5a, 0x12, 0xf8, 0x5d, 0x36, 0x50, 0x74,
1267                    0xa0, 0x9d, 0x05, 0x4f, 0x34, 0x01,
1268                ],
1269                internal_xsk: Some([
1270                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd0, 0x94, 0x7c, 0x4b,
1271                    0x03, 0xbf, 0x72, 0xa3, 0x7a, 0xb4, 0x4f, 0x72, 0x27, 0x6d, 0x1c, 0xf3, 0xfd,
1272                    0xcd, 0x7e, 0xbf, 0x3e, 0x73, 0x34, 0x8b, 0x7e, 0x55, 0x0d, 0x75, 0x20, 0x18,
1273                    0x66, 0x8e, 0xb6, 0xc0, 0x0c, 0x93, 0xd3, 0x60, 0x32, 0xb9, 0xa2, 0x68, 0xe9,
1274                    0x9e, 0x86, 0xa8, 0x60, 0x77, 0x65, 0x60, 0xbf, 0x0e, 0x83, 0xc1, 0xa1, 0x0b,
1275                    0x51, 0xf6, 0x07, 0xc9, 0x54, 0x74, 0x25, 0x06, 0x51, 0x12, 0x33, 0x63, 0x6b,
1276                    0x95, 0xfd, 0x0a, 0xfb, 0x6b, 0xf8, 0x19, 0x3a, 0x7d, 0x8f, 0x49, 0xef, 0xd7,
1277                    0x36, 0xa9, 0x88, 0x77, 0x5c, 0x54, 0xf9, 0x56, 0x68, 0x76, 0x46, 0xea, 0xab,
1278                    0x07, 0x9d, 0xc4, 0x77, 0xfe, 0x1e, 0x7d, 0x28, 0x29, 0x13, 0xf6, 0x51, 0x65,
1279                    0x4d, 0x39, 0x85, 0xf0, 0x9d, 0x53, 0xc2, 0xd3, 0xb5, 0x76, 0x3d, 0x7a, 0x72,
1280                    0x3b, 0xcb, 0xd6, 0xee, 0x05, 0x3d, 0x5a, 0x40, 0xdd, 0xc5, 0x6e, 0x69, 0x75,
1281                    0x13, 0x8c, 0x08, 0x39, 0xe5, 0x80, 0xb5, 0x4d, 0x6d, 0x99, 0x9d, 0xc6, 0x16,
1282                    0x84, 0x3c, 0xfe, 0x04, 0x1e, 0x8f, 0x38, 0x8b, 0x12, 0x4e, 0xf7, 0xb5, 0xed,
1283                ]),
1284                internal_xfvk: [
1285                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd0, 0x94, 0x7c, 0x4b,
1286                    0x03, 0xbf, 0x72, 0xa3, 0x7a, 0xb4, 0x4f, 0x72, 0x27, 0x6d, 0x1c, 0xf3, 0xfd,
1287                    0xcd, 0x7e, 0xbf, 0x3e, 0x73, 0x34, 0x8b, 0x7e, 0x55, 0x0d, 0x75, 0x20, 0x18,
1288                    0x66, 0x8e, 0x93, 0x44, 0x2e, 0x5f, 0xef, 0xfb, 0xff, 0x16, 0xe7, 0x21, 0x72,
1289                    0x02, 0xdc, 0x73, 0x06, 0x72, 0x9f, 0xff, 0xfe, 0x85, 0xaf, 0x56, 0x83, 0xbc,
1290                    0xe2, 0x64, 0x2e, 0x3e, 0xeb, 0x5d, 0x38, 0x71, 0xa3, 0x83, 0x1a, 0x5c, 0x69,
1291                    0x33, 0xf8, 0xec, 0x6a, 0xa5, 0xce, 0x31, 0x6c, 0x50, 0x8b, 0x79, 0x91, 0xcd,
1292                    0x94, 0xd3, 0xbd, 0xb7, 0x00, 0xa1, 0xc4, 0x27, 0xa6, 0xae, 0x15, 0xe7, 0x2f,
1293                    0xb5, 0x9d, 0xc4, 0x77, 0xfe, 0x1e, 0x7d, 0x28, 0x29, 0x13, 0xf6, 0x51, 0x65,
1294                    0x4d, 0x39, 0x85, 0xf0, 0x9d, 0x53, 0xc2, 0xd3, 0xb5, 0x76, 0x3d, 0x7a, 0x72,
1295                    0x3b, 0xcb, 0xd6, 0xee, 0x05, 0x3d, 0x5a, 0x40, 0xdd, 0xc5, 0x6e, 0x69, 0x75,
1296                    0x13, 0x8c, 0x08, 0x39, 0xe5, 0x80, 0xb5, 0x4d, 0x6d, 0x99, 0x9d, 0xc6, 0x16,
1297                    0x84, 0x3c, 0xfe, 0x04, 0x1e, 0x8f, 0x38, 0x8b, 0x12, 0x4e, 0xf7, 0xb5, 0xed,
1298                ],
1299                internal_fp: [
1300                    0x82, 0x64, 0xed, 0xec, 0x63, 0xb1, 0x55, 0x00, 0x1d, 0x84, 0x96, 0x68, 0x5c,
1301                    0xc7, 0xc2, 0x1e, 0xa9, 0x57, 0xc6, 0xf5, 0x91, 0x09, 0x0a, 0x1c, 0x20, 0xe5,
1302                    0x2a, 0x41, 0x89, 0xb8, 0xbb, 0x96,
1303                ],
1304            },
1305            TestVector {
1306                ask: Some([
1307                    0xd5, 0xf7, 0xe9, 0x2e, 0xfb, 0x7a, 0xbe, 0x04, 0xdc, 0x8c, 0x14, 0x8b, 0x0b,
1308                    0x3b, 0x0f, 0xc2, 0x3e, 0x04, 0x29, 0xf0, 0x02, 0x08, 0xff, 0x93, 0xb6, 0x8d,
1309                    0x21, 0xa6, 0xe1, 0x31, 0xbd, 0x04,
1310                ]),
1311                nsk: Some([
1312                    0x37, 0x2a, 0x7c, 0x68, 0x22, 0xcb, 0xe6, 0x03, 0xf3, 0x46, 0x5c, 0x4b, 0x9b,
1313                    0x65, 0x58, 0xf3, 0xa3, 0x51, 0x2d, 0xec, 0xd4, 0x34, 0x01, 0x2e, 0x67, 0xbf,
1314                    0xfc, 0xf6, 0x57, 0xe5, 0x75, 0x0a,
1315                ]),
1316                ovk: [
1317                    0x25, 0x30, 0x76, 0x19, 0x33, 0x34, 0x8c, 0x1f, 0xcf, 0x14, 0x35, 0x54, 0x33,
1318                    0xa8, 0xd2, 0x91, 0x16, 0x7f, 0xbb, 0x37, 0xb2, 0xce, 0x37, 0xca, 0x97, 0x16,
1319                    0x0a, 0x47, 0xec, 0x33, 0x1c, 0x69,
1320                ],
1321                dk: [
1322                    0xf2, 0x88, 0x40, 0x0f, 0xd6, 0x5f, 0x9a, 0xdf, 0xe3, 0xa7, 0xc3, 0x72, 0x0a,
1323                    0xce, 0xee, 0x0d, 0xae, 0x05, 0x0d, 0x0a, 0x81, 0x9d, 0x61, 0x9f, 0x92, 0xe9,
1324                    0xe2, 0xcb, 0x44, 0x34, 0xd5, 0x26,
1325                ],
1326                c: [
1327                    0x6f, 0xcc, 0xaa, 0x45, 0xa8, 0x20, 0x6b, 0x06, 0x3e, 0xbb, 0x68, 0xc6, 0x10,
1328                    0xe0, 0x59, 0x27, 0xaa, 0x94, 0xd6, 0x1b, 0xe9, 0x3e, 0xc2, 0x5e, 0xb4, 0xf8,
1329                    0x2e, 0xfd, 0x68, 0xca, 0xae, 0xdb,
1330                ],
1331                ak: [
1332                    0xcf, 0xca, 0x79, 0xd3, 0x37, 0xbc, 0x68, 0x98, 0x13, 0xe4, 0x09, 0xa5, 0x4e,
1333                    0x3e, 0x72, 0xad, 0x8e, 0x2f, 0x70, 0x3a, 0xe6, 0xf8, 0x22, 0x3c, 0x9b, 0xec,
1334                    0xbd, 0xe9, 0xa8, 0xa3, 0x5f, 0x53,
1335                ],
1336                nk: [
1337                    0x51, 0x3d, 0xe6, 0x40, 0x85, 0xd3, 0x5a, 0x3a, 0xdf, 0x23, 0xd8, 0x9d, 0x5a,
1338                    0x21, 0xcd, 0xee, 0x4d, 0xb4, 0xc6, 0x25, 0xbd, 0x6a, 0x3c, 0x3c, 0x62, 0x4b,
1339                    0xef, 0x43, 0x44, 0x14, 0x1d, 0xeb,
1340                ],
1341                ivk: [
1342                    0xf6, 0xe7, 0x5c, 0xd9, 0x80, 0xc3, 0x0e, 0xab, 0xc6, 0x1f, 0x49, 0xac, 0x68,
1343                    0xf4, 0x88, 0x57, 0x3a, 0xb3, 0xe6, 0xaf, 0xe1, 0x53, 0x76, 0x37, 0x5d, 0x34,
1344                    0xe4, 0x06, 0x70, 0x2f, 0xfd, 0x02,
1345                ],
1346                xsk: Some([
1347                    0x01, 0x14, 0xc2, 0x71, 0x3a, 0x01, 0x00, 0x00, 0x80, 0x6f, 0xcc, 0xaa, 0x45,
1348                    0xa8, 0x20, 0x6b, 0x06, 0x3e, 0xbb, 0x68, 0xc6, 0x10, 0xe0, 0x59, 0x27, 0xaa,
1349                    0x94, 0xd6, 0x1b, 0xe9, 0x3e, 0xc2, 0x5e, 0xb4, 0xf8, 0x2e, 0xfd, 0x68, 0xca,
1350                    0xae, 0xdb, 0xd5, 0xf7, 0xe9, 0x2e, 0xfb, 0x7a, 0xbe, 0x04, 0xdc, 0x8c, 0x14,
1351                    0x8b, 0x0b, 0x3b, 0x0f, 0xc2, 0x3e, 0x04, 0x29, 0xf0, 0x02, 0x08, 0xff, 0x93,
1352                    0xb6, 0x8d, 0x21, 0xa6, 0xe1, 0x31, 0xbd, 0x04, 0x37, 0x2a, 0x7c, 0x68, 0x22,
1353                    0xcb, 0xe6, 0x03, 0xf3, 0x46, 0x5c, 0x4b, 0x9b, 0x65, 0x58, 0xf3, 0xa3, 0x51,
1354                    0x2d, 0xec, 0xd4, 0x34, 0x01, 0x2e, 0x67, 0xbf, 0xfc, 0xf6, 0x57, 0xe5, 0x75,
1355                    0x0a, 0x25, 0x30, 0x76, 0x19, 0x33, 0x34, 0x8c, 0x1f, 0xcf, 0x14, 0x35, 0x54,
1356                    0x33, 0xa8, 0xd2, 0x91, 0x16, 0x7f, 0xbb, 0x37, 0xb2, 0xce, 0x37, 0xca, 0x97,
1357                    0x16, 0x0a, 0x47, 0xec, 0x33, 0x1c, 0x69, 0xf2, 0x88, 0x40, 0x0f, 0xd6, 0x5f,
1358                    0x9a, 0xdf, 0xe3, 0xa7, 0xc3, 0x72, 0x0a, 0xce, 0xee, 0x0d, 0xae, 0x05, 0x0d,
1359                    0x0a, 0x81, 0x9d, 0x61, 0x9f, 0x92, 0xe9, 0xe2, 0xcb, 0x44, 0x34, 0xd5, 0x26,
1360                ]),
1361                xfvk: [
1362                    0x01, 0x14, 0xc2, 0x71, 0x3a, 0x01, 0x00, 0x00, 0x80, 0x6f, 0xcc, 0xaa, 0x45,
1363                    0xa8, 0x20, 0x6b, 0x06, 0x3e, 0xbb, 0x68, 0xc6, 0x10, 0xe0, 0x59, 0x27, 0xaa,
1364                    0x94, 0xd6, 0x1b, 0xe9, 0x3e, 0xc2, 0x5e, 0xb4, 0xf8, 0x2e, 0xfd, 0x68, 0xca,
1365                    0xae, 0xdb, 0xcf, 0xca, 0x79, 0xd3, 0x37, 0xbc, 0x68, 0x98, 0x13, 0xe4, 0x09,
1366                    0xa5, 0x4e, 0x3e, 0x72, 0xad, 0x8e, 0x2f, 0x70, 0x3a, 0xe6, 0xf8, 0x22, 0x3c,
1367                    0x9b, 0xec, 0xbd, 0xe9, 0xa8, 0xa3, 0x5f, 0x53, 0x51, 0x3d, 0xe6, 0x40, 0x85,
1368                    0xd3, 0x5a, 0x3a, 0xdf, 0x23, 0xd8, 0x9d, 0x5a, 0x21, 0xcd, 0xee, 0x4d, 0xb4,
1369                    0xc6, 0x25, 0xbd, 0x6a, 0x3c, 0x3c, 0x62, 0x4b, 0xef, 0x43, 0x44, 0x14, 0x1d,
1370                    0xeb, 0x25, 0x30, 0x76, 0x19, 0x33, 0x34, 0x8c, 0x1f, 0xcf, 0x14, 0x35, 0x54,
1371                    0x33, 0xa8, 0xd2, 0x91, 0x16, 0x7f, 0xbb, 0x37, 0xb2, 0xce, 0x37, 0xca, 0x97,
1372                    0x16, 0x0a, 0x47, 0xec, 0x33, 0x1c, 0x69, 0xf2, 0x88, 0x40, 0x0f, 0xd6, 0x5f,
1373                    0x9a, 0xdf, 0xe3, 0xa7, 0xc3, 0x72, 0x0a, 0xce, 0xee, 0x0d, 0xae, 0x05, 0x0d,
1374                    0x0a, 0x81, 0x9d, 0x61, 0x9f, 0x92, 0xe9, 0xe2, 0xcb, 0x44, 0x34, 0xd5, 0x26,
1375                ],
1376                fp: [
1377                    0x76, 0x84, 0x23, 0xcb, 0x88, 0xd2, 0x2d, 0xee, 0x91, 0xb5, 0xb7, 0x66, 0x1e,
1378                    0x72, 0xed, 0x00, 0x95, 0x57, 0xeb, 0xa1, 0x44, 0xc7, 0x8d, 0x1a, 0xa7, 0x1a,
1379                    0x3e, 0x88, 0xb6, 0x91, 0x06, 0x96,
1380                ],
1381                d0: None,
1382                d1: Some([
1383                    0xbc, 0xc3, 0x23, 0xe8, 0xda, 0x39, 0xb4, 0x96, 0xc0, 0x50, 0x51,
1384                ]),
1385                d2: None,
1386                dmax: Some([
1387                    0x25, 0x14, 0x32, 0x0d, 0x33, 0x9c, 0x66, 0x6a, 0x25, 0x4c, 0x06,
1388                ]),
1389                internal_nsk: Some([
1390                    0x5d, 0x47, 0x0f, 0x97, 0x79, 0xcc, 0x25, 0x7f, 0x21, 0x28, 0x8f, 0x50, 0x5a,
1391                    0x4e, 0x65, 0xb3, 0x8e, 0xb8, 0x53, 0xf1, 0xa2, 0x45, 0x63, 0xb9, 0xf6, 0x74,
1392                    0x17, 0x26, 0xf4, 0xd3, 0x01, 0x03,
1393                ]),
1394                internal_ovk: [
1395                    0x78, 0x64, 0xe8, 0xc7, 0x9c, 0xea, 0xab, 0x97, 0xe6, 0xae, 0x5b, 0xca, 0x10,
1396                    0xf7, 0xd5, 0x1d, 0xf4, 0x20, 0x9a, 0xd0, 0xd4, 0x6e, 0x80, 0xac, 0x18, 0x0d,
1397                    0x50, 0xd3, 0xff, 0x09, 0xa6, 0x70,
1398                ],
1399                internal_dk: [
1400                    0x54, 0xf1, 0x1e, 0x3f, 0xa3, 0x0d, 0x34, 0xd6, 0xa7, 0x4d, 0xef, 0x1e, 0x6d,
1401                    0x5d, 0xaf, 0x58, 0xcf, 0xc7, 0xd7, 0x8b, 0x27, 0xcb, 0x07, 0x15, 0xc1, 0xaf,
1402                    0xfa, 0x29, 0xae, 0x39, 0x92, 0xfa,
1403                ],
1404                internal_nk: [
1405                    0x31, 0x42, 0x48, 0x75, 0xd6, 0xa5, 0xed, 0x75, 0xde, 0x20, 0x0b, 0xb5, 0xc1,
1406                    0xd8, 0x1a, 0xec, 0x4d, 0xff, 0x16, 0x50, 0xb7, 0x8b, 0xb0, 0xca, 0xde, 0x3c,
1407                    0x8c, 0x7a, 0xb0, 0x3d, 0xf1, 0x11,
1408                ],
1409                internal_ivk: [
1410                    0xe5, 0x42, 0x6b, 0x5b, 0x80, 0xb1, 0x18, 0x67, 0x97, 0x01, 0x65, 0x80, 0xc1,
1411                    0xf4, 0x1c, 0x34, 0x19, 0x68, 0x3a, 0xac, 0x77, 0xcf, 0x8d, 0xe0, 0x2f, 0x2f,
1412                    0x98, 0x07, 0xd1, 0x50, 0xb4, 0x02,
1413                ],
1414                internal_xsk: Some([
1415                    0x01, 0x14, 0xc2, 0x71, 0x3a, 0x01, 0x00, 0x00, 0x80, 0x6f, 0xcc, 0xaa, 0x45,
1416                    0xa8, 0x20, 0x6b, 0x06, 0x3e, 0xbb, 0x68, 0xc6, 0x10, 0xe0, 0x59, 0x27, 0xaa,
1417                    0x94, 0xd6, 0x1b, 0xe9, 0x3e, 0xc2, 0x5e, 0xb4, 0xf8, 0x2e, 0xfd, 0x68, 0xca,
1418                    0xae, 0xdb, 0xd5, 0xf7, 0xe9, 0x2e, 0xfb, 0x7a, 0xbe, 0x04, 0xdc, 0x8c, 0x14,
1419                    0x8b, 0x0b, 0x3b, 0x0f, 0xc2, 0x3e, 0x04, 0x29, 0xf0, 0x02, 0x08, 0xff, 0x93,
1420                    0xb6, 0x8d, 0x21, 0xa6, 0xe1, 0x31, 0xbd, 0x04, 0x5d, 0x47, 0x0f, 0x97, 0x79,
1421                    0xcc, 0x25, 0x7f, 0x21, 0x28, 0x8f, 0x50, 0x5a, 0x4e, 0x65, 0xb3, 0x8e, 0xb8,
1422                    0x53, 0xf1, 0xa2, 0x45, 0x63, 0xb9, 0xf6, 0x74, 0x17, 0x26, 0xf4, 0xd3, 0x01,
1423                    0x03, 0x78, 0x64, 0xe8, 0xc7, 0x9c, 0xea, 0xab, 0x97, 0xe6, 0xae, 0x5b, 0xca,
1424                    0x10, 0xf7, 0xd5, 0x1d, 0xf4, 0x20, 0x9a, 0xd0, 0xd4, 0x6e, 0x80, 0xac, 0x18,
1425                    0x0d, 0x50, 0xd3, 0xff, 0x09, 0xa6, 0x70, 0x54, 0xf1, 0x1e, 0x3f, 0xa3, 0x0d,
1426                    0x34, 0xd6, 0xa7, 0x4d, 0xef, 0x1e, 0x6d, 0x5d, 0xaf, 0x58, 0xcf, 0xc7, 0xd7,
1427                    0x8b, 0x27, 0xcb, 0x07, 0x15, 0xc1, 0xaf, 0xfa, 0x29, 0xae, 0x39, 0x92, 0xfa,
1428                ]),
1429                internal_xfvk: [
1430                    0x01, 0x14, 0xc2, 0x71, 0x3a, 0x01, 0x00, 0x00, 0x80, 0x6f, 0xcc, 0xaa, 0x45,
1431                    0xa8, 0x20, 0x6b, 0x06, 0x3e, 0xbb, 0x68, 0xc6, 0x10, 0xe0, 0x59, 0x27, 0xaa,
1432                    0x94, 0xd6, 0x1b, 0xe9, 0x3e, 0xc2, 0x5e, 0xb4, 0xf8, 0x2e, 0xfd, 0x68, 0xca,
1433                    0xae, 0xdb, 0xcf, 0xca, 0x79, 0xd3, 0x37, 0xbc, 0x68, 0x98, 0x13, 0xe4, 0x09,
1434                    0xa5, 0x4e, 0x3e, 0x72, 0xad, 0x8e, 0x2f, 0x70, 0x3a, 0xe6, 0xf8, 0x22, 0x3c,
1435                    0x9b, 0xec, 0xbd, 0xe9, 0xa8, 0xa3, 0x5f, 0x53, 0x31, 0x42, 0x48, 0x75, 0xd6,
1436                    0xa5, 0xed, 0x75, 0xde, 0x20, 0x0b, 0xb5, 0xc1, 0xd8, 0x1a, 0xec, 0x4d, 0xff,
1437                    0x16, 0x50, 0xb7, 0x8b, 0xb0, 0xca, 0xde, 0x3c, 0x8c, 0x7a, 0xb0, 0x3d, 0xf1,
1438                    0x11, 0x78, 0x64, 0xe8, 0xc7, 0x9c, 0xea, 0xab, 0x97, 0xe6, 0xae, 0x5b, 0xca,
1439                    0x10, 0xf7, 0xd5, 0x1d, 0xf4, 0x20, 0x9a, 0xd0, 0xd4, 0x6e, 0x80, 0xac, 0x18,
1440                    0x0d, 0x50, 0xd3, 0xff, 0x09, 0xa6, 0x70, 0x54, 0xf1, 0x1e, 0x3f, 0xa3, 0x0d,
1441                    0x34, 0xd6, 0xa7, 0x4d, 0xef, 0x1e, 0x6d, 0x5d, 0xaf, 0x58, 0xcf, 0xc7, 0xd7,
1442                    0x8b, 0x27, 0xcb, 0x07, 0x15, 0xc1, 0xaf, 0xfa, 0x29, 0xae, 0x39, 0x92, 0xfa,
1443                ],
1444                internal_fp: [
1445                    0x12, 0x87, 0xc3, 0x79, 0x02, 0x35, 0x69, 0x16, 0x4e, 0xb5, 0x23, 0xff, 0xdd,
1446                    0x72, 0x03, 0x1c, 0x35, 0xe1, 0x85, 0x9f, 0x3e, 0xf8, 0xd4, 0x83, 0x0a, 0x29,
1447                    0x91, 0xba, 0x7e, 0x15, 0xde, 0x2d,
1448                ],
1449            },
1450            TestVector {
1451                ask: Some([
1452                    0x7f, 0xf3, 0x5d, 0xb6, 0x9e, 0x13, 0xc3, 0x6f, 0x59, 0xad, 0x9c, 0x08, 0xd3,
1453                    0x2d, 0x52, 0x27, 0x37, 0x8d, 0xa0, 0xcf, 0xf9, 0x71, 0xfd, 0x42, 0x4b, 0xae,
1454                    0xf9, 0xa6, 0x33, 0x2f, 0x51, 0x06,
1455                ]),
1456                nsk: Some([
1457                    0x77, 0x9c, 0x6e, 0xe4, 0xa0, 0x39, 0x44, 0xeb, 0xa2, 0x8b, 0xc9, 0xbd, 0xc1,
1458                    0x32, 0x9a, 0x39, 0x14, 0x07, 0xf4, 0x8c, 0x41, 0x0d, 0x5a, 0xe0, 0xa3, 0x64,
1459                    0xf5, 0x99, 0x59, 0xbf, 0xde, 0x00,
1460                ]),
1461                ovk: [
1462                    0xd9, 0xfc, 0x71, 0x01, 0xbf, 0x90, 0x7f, 0x41, 0x88, 0x6a, 0x73, 0x30, 0xa5,
1463                    0xd6, 0xa7, 0xbd, 0x23, 0x53, 0x5e, 0x30, 0x5e, 0xb7, 0x67, 0x9b, 0xc2, 0x3d,
1464                    0x76, 0x05, 0x93, 0x61, 0x85, 0xac,
1465                ],
1466                dk: [
1467                    0xe4, 0x69, 0x9e, 0x9a, 0x86, 0xe0, 0x31, 0xc5, 0x4b, 0x21, 0xcd, 0xd0, 0x96,
1468                    0x0a, 0xc1, 0x8d, 0xdd, 0x61, 0xec, 0x9f, 0x7a, 0xe9, 0x8d, 0x55, 0x82, 0xa6,
1469                    0xfa, 0xf6, 0x5f, 0x32, 0x48, 0xd1,
1470                ],
1471                c: [
1472                    0x44, 0x79, 0x08, 0x6c, 0x75, 0xd0, 0x80, 0x79, 0x60, 0x20, 0xf5, 0x00, 0xc1,
1473                    0xe3, 0x0a, 0x54, 0xcf, 0xe2, 0x9d, 0xda, 0x36, 0xf2, 0x14, 0x4f, 0xb3, 0x3a,
1474                    0x50, 0x80, 0x6f, 0xbe, 0xf7, 0xda,
1475                ],
1476                ak: [
1477                    0x9a, 0x85, 0x3f, 0x95, 0x44, 0x71, 0x37, 0x97, 0xe0, 0x85, 0x17, 0x64, 0xda,
1478                    0x39, 0x2e, 0x68, 0x53, 0x4b, 0x1d, 0x94, 0x8d, 0xae, 0x47, 0x42, 0xee, 0x76,
1479                    0x5c, 0x72, 0x75, 0x72, 0xab, 0x4e,
1480                ],
1481                nk: [
1482                    0xf1, 0x66, 0xa2, 0x8a, 0x4f, 0x88, 0xce, 0xc1, 0x21, 0x41, 0xa8, 0x2d, 0x21,
1483                    0x20, 0xbd, 0x6d, 0x8c, 0xaf, 0x87, 0x9c, 0x9a, 0x1b, 0x3a, 0xd2, 0x11, 0x85,
1484                    0x01, 0x36, 0x4f, 0x5d, 0x4f, 0xbe,
1485                ],
1486                ivk: [
1487                    0x33, 0xbd, 0x46, 0x01, 0x5a, 0x2c, 0xad, 0x17, 0xd6, 0xe0, 0x15, 0xeb, 0x88,
1488                    0x86, 0x1b, 0x0c, 0x91, 0x77, 0x96, 0x24, 0x65, 0x70, 0x52, 0x1c, 0x9e, 0x1a,
1489                    0xe4, 0xb1, 0xc8, 0x31, 0x1d, 0x06,
1490                ],
1491                xsk: Some([
1492                    0x02, 0x76, 0x84, 0x23, 0xcb, 0x02, 0x00, 0x00, 0x80, 0x44, 0x79, 0x08, 0x6c,
1493                    0x75, 0xd0, 0x80, 0x79, 0x60, 0x20, 0xf5, 0x00, 0xc1, 0xe3, 0x0a, 0x54, 0xcf,
1494                    0xe2, 0x9d, 0xda, 0x36, 0xf2, 0x14, 0x4f, 0xb3, 0x3a, 0x50, 0x80, 0x6f, 0xbe,
1495                    0xf7, 0xda, 0x7f, 0xf3, 0x5d, 0xb6, 0x9e, 0x13, 0xc3, 0x6f, 0x59, 0xad, 0x9c,
1496                    0x08, 0xd3, 0x2d, 0x52, 0x27, 0x37, 0x8d, 0xa0, 0xcf, 0xf9, 0x71, 0xfd, 0x42,
1497                    0x4b, 0xae, 0xf9, 0xa6, 0x33, 0x2f, 0x51, 0x06, 0x77, 0x9c, 0x6e, 0xe4, 0xa0,
1498                    0x39, 0x44, 0xeb, 0xa2, 0x8b, 0xc9, 0xbd, 0xc1, 0x32, 0x9a, 0x39, 0x14, 0x07,
1499                    0xf4, 0x8c, 0x41, 0x0d, 0x5a, 0xe0, 0xa3, 0x64, 0xf5, 0x99, 0x59, 0xbf, 0xde,
1500                    0x00, 0xd9, 0xfc, 0x71, 0x01, 0xbf, 0x90, 0x7f, 0x41, 0x88, 0x6a, 0x73, 0x30,
1501                    0xa5, 0xd6, 0xa7, 0xbd, 0x23, 0x53, 0x5e, 0x30, 0x5e, 0xb7, 0x67, 0x9b, 0xc2,
1502                    0x3d, 0x76, 0x05, 0x93, 0x61, 0x85, 0xac, 0xe4, 0x69, 0x9e, 0x9a, 0x86, 0xe0,
1503                    0x31, 0xc5, 0x4b, 0x21, 0xcd, 0xd0, 0x96, 0x0a, 0xc1, 0x8d, 0xdd, 0x61, 0xec,
1504                    0x9f, 0x7a, 0xe9, 0x8d, 0x55, 0x82, 0xa6, 0xfa, 0xf6, 0x5f, 0x32, 0x48, 0xd1,
1505                ]),
1506                xfvk: [
1507                    0x02, 0x76, 0x84, 0x23, 0xcb, 0x02, 0x00, 0x00, 0x80, 0x44, 0x79, 0x08, 0x6c,
1508                    0x75, 0xd0, 0x80, 0x79, 0x60, 0x20, 0xf5, 0x00, 0xc1, 0xe3, 0x0a, 0x54, 0xcf,
1509                    0xe2, 0x9d, 0xda, 0x36, 0xf2, 0x14, 0x4f, 0xb3, 0x3a, 0x50, 0x80, 0x6f, 0xbe,
1510                    0xf7, 0xda, 0x9a, 0x85, 0x3f, 0x95, 0x44, 0x71, 0x37, 0x97, 0xe0, 0x85, 0x17,
1511                    0x64, 0xda, 0x39, 0x2e, 0x68, 0x53, 0x4b, 0x1d, 0x94, 0x8d, 0xae, 0x47, 0x42,
1512                    0xee, 0x76, 0x5c, 0x72, 0x75, 0x72, 0xab, 0x4e, 0xf1, 0x66, 0xa2, 0x8a, 0x4f,
1513                    0x88, 0xce, 0xc1, 0x21, 0x41, 0xa8, 0x2d, 0x21, 0x20, 0xbd, 0x6d, 0x8c, 0xaf,
1514                    0x87, 0x9c, 0x9a, 0x1b, 0x3a, 0xd2, 0x11, 0x85, 0x01, 0x36, 0x4f, 0x5d, 0x4f,
1515                    0xbe, 0xd9, 0xfc, 0x71, 0x01, 0xbf, 0x90, 0x7f, 0x41, 0x88, 0x6a, 0x73, 0x30,
1516                    0xa5, 0xd6, 0xa7, 0xbd, 0x23, 0x53, 0x5e, 0x30, 0x5e, 0xb7, 0x67, 0x9b, 0xc2,
1517                    0x3d, 0x76, 0x05, 0x93, 0x61, 0x85, 0xac, 0xe4, 0x69, 0x9e, 0x9a, 0x86, 0xe0,
1518                    0x31, 0xc5, 0x4b, 0x21, 0xcd, 0xd0, 0x96, 0x0a, 0xc1, 0x8d, 0xdd, 0x61, 0xec,
1519                    0x9f, 0x7a, 0xe9, 0x8d, 0x55, 0x82, 0xa6, 0xfa, 0xf6, 0x5f, 0x32, 0x48, 0xd1,
1520                ],
1521                fp: [
1522                    0x0b, 0xdc, 0x2d, 0x2b, 0x6e, 0xb1, 0xf9, 0x27, 0xcb, 0xab, 0xdb, 0xb9, 0xd4,
1523                    0x3d, 0xb8, 0xde, 0x85, 0x7b, 0xb7, 0x16, 0xdf, 0x86, 0xce, 0xcf, 0x08, 0x1e,
1524                    0x1a, 0x2b, 0x74, 0xfc, 0xad, 0x55,
1525                ],
1526                d0: None,
1527                d1: None,
1528                d2: None,
1529                dmax: None,
1530                internal_nsk: Some([
1531                    0x7b, 0x17, 0x17, 0x65, 0x27, 0xf9, 0x17, 0x99, 0x0f, 0x9f, 0x51, 0x79, 0xcb,
1532                    0x23, 0xc1, 0x6e, 0xc0, 0xa9, 0x26, 0xed, 0xc4, 0x1a, 0xb2, 0xba, 0x42, 0x13,
1533                    0x7b, 0xef, 0x5c, 0x20, 0x9f, 0x09,
1534                ]),
1535                internal_ovk: [
1536                    0xc6, 0x12, 0xcb, 0xc9, 0x77, 0x30, 0x7e, 0x53, 0x52, 0xa1, 0x58, 0x8b, 0xd7,
1537                    0x0f, 0x41, 0xaf, 0x11, 0xe7, 0x3b, 0x7b, 0xc6, 0xbc, 0xbc, 0x73, 0x2a, 0xa3,
1538                    0x06, 0xc2, 0x1c, 0xd0, 0x0f, 0x3a,
1539                ],
1540                internal_dk: [
1541                    0x35, 0xef, 0x4d, 0x26, 0x59, 0x51, 0xdc, 0xaa, 0xec, 0x26, 0xef, 0x8f, 0xbd,
1542                    0xf8, 0x4c, 0x92, 0xb7, 0x90, 0x04, 0x9d, 0x09, 0x93, 0x77, 0x2e, 0xfb, 0x43,
1543                    0x97, 0xf0, 0x49, 0x30, 0xf1, 0x67,
1544                ],
1545                internal_nk: [
1546                    0x8d, 0x05, 0x55, 0xe8, 0xe0, 0x20, 0xc9, 0xd3, 0x60, 0x68, 0x5d, 0x24, 0x2f,
1547                    0x2b, 0xa9, 0xf7, 0x74, 0x61, 0x3f, 0xa0, 0x94, 0x01, 0xf1, 0x25, 0xbc, 0xa9,
1548                    0x29, 0xec, 0xa4, 0x86, 0xa3, 0xd1,
1549                ],
1550                internal_ivk: [
1551                    0x7f, 0x7c, 0xee, 0xfa, 0x65, 0x42, 0x8e, 0x8b, 0x70, 0x76, 0x19, 0x1a, 0x23,
1552                    0x93, 0x95, 0x7b, 0x9c, 0x09, 0x50, 0x61, 0xd8, 0xcc, 0xe1, 0x28, 0x3d, 0xd1,
1553                    0x5c, 0x2b, 0x5e, 0x8f, 0xc3, 0x05,
1554                ],
1555                internal_xsk: Some([
1556                    0x02, 0x76, 0x84, 0x23, 0xcb, 0x02, 0x00, 0x00, 0x80, 0x44, 0x79, 0x08, 0x6c,
1557                    0x75, 0xd0, 0x80, 0x79, 0x60, 0x20, 0xf5, 0x00, 0xc1, 0xe3, 0x0a, 0x54, 0xcf,
1558                    0xe2, 0x9d, 0xda, 0x36, 0xf2, 0x14, 0x4f, 0xb3, 0x3a, 0x50, 0x80, 0x6f, 0xbe,
1559                    0xf7, 0xda, 0x7f, 0xf3, 0x5d, 0xb6, 0x9e, 0x13, 0xc3, 0x6f, 0x59, 0xad, 0x9c,
1560                    0x08, 0xd3, 0x2d, 0x52, 0x27, 0x37, 0x8d, 0xa0, 0xcf, 0xf9, 0x71, 0xfd, 0x42,
1561                    0x4b, 0xae, 0xf9, 0xa6, 0x33, 0x2f, 0x51, 0x06, 0x7b, 0x17, 0x17, 0x65, 0x27,
1562                    0xf9, 0x17, 0x99, 0x0f, 0x9f, 0x51, 0x79, 0xcb, 0x23, 0xc1, 0x6e, 0xc0, 0xa9,
1563                    0x26, 0xed, 0xc4, 0x1a, 0xb2, 0xba, 0x42, 0x13, 0x7b, 0xef, 0x5c, 0x20, 0x9f,
1564                    0x09, 0xc6, 0x12, 0xcb, 0xc9, 0x77, 0x30, 0x7e, 0x53, 0x52, 0xa1, 0x58, 0x8b,
1565                    0xd7, 0x0f, 0x41, 0xaf, 0x11, 0xe7, 0x3b, 0x7b, 0xc6, 0xbc, 0xbc, 0x73, 0x2a,
1566                    0xa3, 0x06, 0xc2, 0x1c, 0xd0, 0x0f, 0x3a, 0x35, 0xef, 0x4d, 0x26, 0x59, 0x51,
1567                    0xdc, 0xaa, 0xec, 0x26, 0xef, 0x8f, 0xbd, 0xf8, 0x4c, 0x92, 0xb7, 0x90, 0x04,
1568                    0x9d, 0x09, 0x93, 0x77, 0x2e, 0xfb, 0x43, 0x97, 0xf0, 0x49, 0x30, 0xf1, 0x67,
1569                ]),
1570                internal_xfvk: [
1571                    0x02, 0x76, 0x84, 0x23, 0xcb, 0x02, 0x00, 0x00, 0x80, 0x44, 0x79, 0x08, 0x6c,
1572                    0x75, 0xd0, 0x80, 0x79, 0x60, 0x20, 0xf5, 0x00, 0xc1, 0xe3, 0x0a, 0x54, 0xcf,
1573                    0xe2, 0x9d, 0xda, 0x36, 0xf2, 0x14, 0x4f, 0xb3, 0x3a, 0x50, 0x80, 0x6f, 0xbe,
1574                    0xf7, 0xda, 0x9a, 0x85, 0x3f, 0x95, 0x44, 0x71, 0x37, 0x97, 0xe0, 0x85, 0x17,
1575                    0x64, 0xda, 0x39, 0x2e, 0x68, 0x53, 0x4b, 0x1d, 0x94, 0x8d, 0xae, 0x47, 0x42,
1576                    0xee, 0x76, 0x5c, 0x72, 0x75, 0x72, 0xab, 0x4e, 0x8d, 0x05, 0x55, 0xe8, 0xe0,
1577                    0x20, 0xc9, 0xd3, 0x60, 0x68, 0x5d, 0x24, 0x2f, 0x2b, 0xa9, 0xf7, 0x74, 0x61,
1578                    0x3f, 0xa0, 0x94, 0x01, 0xf1, 0x25, 0xbc, 0xa9, 0x29, 0xec, 0xa4, 0x86, 0xa3,
1579                    0xd1, 0xc6, 0x12, 0xcb, 0xc9, 0x77, 0x30, 0x7e, 0x53, 0x52, 0xa1, 0x58, 0x8b,
1580                    0xd7, 0x0f, 0x41, 0xaf, 0x11, 0xe7, 0x3b, 0x7b, 0xc6, 0xbc, 0xbc, 0x73, 0x2a,
1581                    0xa3, 0x06, 0xc2, 0x1c, 0xd0, 0x0f, 0x3a, 0x35, 0xef, 0x4d, 0x26, 0x59, 0x51,
1582                    0xdc, 0xaa, 0xec, 0x26, 0xef, 0x8f, 0xbd, 0xf8, 0x4c, 0x92, 0xb7, 0x90, 0x04,
1583                    0x9d, 0x09, 0x93, 0x77, 0x2e, 0xfb, 0x43, 0x97, 0xf0, 0x49, 0x30, 0xf1, 0x67,
1584                ],
1585                internal_fp: [
1586                    0xe0, 0xba, 0xa5, 0xdb, 0xb8, 0x06, 0xc7, 0x21, 0x33, 0x3c, 0x63, 0x08, 0x34,
1587                    0x5f, 0xc5, 0x1c, 0x2d, 0xc1, 0xe0, 0x09, 0xda, 0x04, 0x47, 0x78, 0xa3, 0xc3,
1588                    0x29, 0x4d, 0x68, 0x17, 0xa3, 0xc4,
1589                ],
1590            },
1591            TestVector {
1592                ask: Some([
1593                    0x45, 0x93, 0xd2, 0x4d, 0x21, 0xe3, 0x59, 0x37, 0xf1, 0x52, 0xcf, 0x90, 0x46,
1594                    0x1c, 0x33, 0x2f, 0x69, 0x50, 0x3c, 0x10, 0x45, 0x81, 0xd6, 0x83, 0xe0, 0xac,
1595                    0x29, 0xf8, 0x4d, 0xec, 0xaf, 0x07,
1596                ]),
1597                nsk: Some([
1598                    0x1a, 0xc8, 0x7e, 0xc2, 0x12, 0x3f, 0x50, 0x57, 0xe3, 0xc0, 0xf8, 0x58, 0xe8,
1599                    0x0d, 0xfa, 0x0e, 0xe4, 0x55, 0x3d, 0xed, 0x27, 0xb7, 0xb5, 0xab, 0xfb, 0xb6,
1600                    0xfa, 0x6e, 0xff, 0xa7, 0xbb, 0x0b,
1601                ]),
1602                ovk: [
1603                    0x1e, 0x36, 0xea, 0x0c, 0xf2, 0xbe, 0x2e, 0x9d, 0x6c, 0xe3, 0x80, 0xa8, 0xaf,
1604                    0x18, 0xe7, 0x5d, 0xa9, 0x22, 0x55, 0x51, 0xfb, 0xef, 0x8b, 0x98, 0x31, 0x1b,
1605                    0x5c, 0x9c, 0x1b, 0x4b, 0x9e, 0xe3,
1606                ],
1607                dk: [
1608                    0x57, 0xfc, 0x6c, 0x59, 0xa4, 0xf3, 0xad, 0x5a, 0x6f, 0x60, 0x9d, 0xb6, 0x71,
1609                    0xd2, 0x8c, 0xbf, 0x70, 0x3f, 0x0d, 0x14, 0xdc, 0x36, 0x3a, 0xaa, 0xed, 0x70,
1610                    0x72, 0x9c, 0x10, 0x7b, 0xbb, 0x6a,
1611                ],
1612                c: [
1613                    0x33, 0xdc, 0x01, 0x2d, 0x76, 0x90, 0xce, 0xd2, 0xcd, 0x2b, 0xcb, 0x2c, 0xc3,
1614                    0xe4, 0x63, 0xe2, 0x8d, 0x8c, 0x29, 0xef, 0x3b, 0x01, 0xbe, 0x59, 0xb2, 0xbd,
1615                    0xfc, 0x38, 0x5b, 0xbd, 0xc7, 0x4b,
1616                ],
1617                ak: [
1618                    0x9c, 0x6d, 0x85, 0x9a, 0x75, 0x2c, 0x30, 0x5d, 0x62, 0x63, 0xde, 0x95, 0xf2,
1619                    0xfc, 0xf7, 0x34, 0xb1, 0x26, 0xdf, 0x24, 0x56, 0xc7, 0xd3, 0x1b, 0xc6, 0x01,
1620                    0xc8, 0xdd, 0xec, 0x40, 0x91, 0x12,
1621                ],
1622                nk: [
1623                    0xd3, 0xee, 0x41, 0xf8, 0x4b, 0x5a, 0x95, 0x08, 0xb6, 0x1d, 0x29, 0xb2, 0xfb,
1624                    0x45, 0x63, 0x6d, 0x19, 0xaa, 0x10, 0xd7, 0x82, 0xcd, 0x97, 0x8c, 0xfe, 0x67,
1625                    0x15, 0x49, 0x2f, 0xcd, 0x22, 0x4e,
1626                ],
1627                ivk: [
1628                    0xd1, 0x38, 0xe1, 0x37, 0xc6, 0x67, 0x1d, 0xe7, 0x82, 0xfb, 0x01, 0xba, 0x91,
1629                    0x1d, 0x98, 0x64, 0xbe, 0xbc, 0x44, 0x36, 0xcc, 0xb3, 0x88, 0xb4, 0xc1, 0xce,
1630                    0x02, 0x56, 0xa8, 0xdb, 0x74, 0x01,
1631                ],
1632                xsk: Some([
1633                    0x03, 0x0b, 0xdc, 0x2d, 0x2b, 0x03, 0x00, 0x00, 0x80, 0x33, 0xdc, 0x01, 0x2d,
1634                    0x76, 0x90, 0xce, 0xd2, 0xcd, 0x2b, 0xcb, 0x2c, 0xc3, 0xe4, 0x63, 0xe2, 0x8d,
1635                    0x8c, 0x29, 0xef, 0x3b, 0x01, 0xbe, 0x59, 0xb2, 0xbd, 0xfc, 0x38, 0x5b, 0xbd,
1636                    0xc7, 0x4b, 0x45, 0x93, 0xd2, 0x4d, 0x21, 0xe3, 0x59, 0x37, 0xf1, 0x52, 0xcf,
1637                    0x90, 0x46, 0x1c, 0x33, 0x2f, 0x69, 0x50, 0x3c, 0x10, 0x45, 0x81, 0xd6, 0x83,
1638                    0xe0, 0xac, 0x29, 0xf8, 0x4d, 0xec, 0xaf, 0x07, 0x1a, 0xc8, 0x7e, 0xc2, 0x12,
1639                    0x3f, 0x50, 0x57, 0xe3, 0xc0, 0xf8, 0x58, 0xe8, 0x0d, 0xfa, 0x0e, 0xe4, 0x55,
1640                    0x3d, 0xed, 0x27, 0xb7, 0xb5, 0xab, 0xfb, 0xb6, 0xfa, 0x6e, 0xff, 0xa7, 0xbb,
1641                    0x0b, 0x1e, 0x36, 0xea, 0x0c, 0xf2, 0xbe, 0x2e, 0x9d, 0x6c, 0xe3, 0x80, 0xa8,
1642                    0xaf, 0x18, 0xe7, 0x5d, 0xa9, 0x22, 0x55, 0x51, 0xfb, 0xef, 0x8b, 0x98, 0x31,
1643                    0x1b, 0x5c, 0x9c, 0x1b, 0x4b, 0x9e, 0xe3, 0x57, 0xfc, 0x6c, 0x59, 0xa4, 0xf3,
1644                    0xad, 0x5a, 0x6f, 0x60, 0x9d, 0xb6, 0x71, 0xd2, 0x8c, 0xbf, 0x70, 0x3f, 0x0d,
1645                    0x14, 0xdc, 0x36, 0x3a, 0xaa, 0xed, 0x70, 0x72, 0x9c, 0x10, 0x7b, 0xbb, 0x6a,
1646                ]),
1647                xfvk: [
1648                    0x03, 0x0b, 0xdc, 0x2d, 0x2b, 0x03, 0x00, 0x00, 0x80, 0x33, 0xdc, 0x01, 0x2d,
1649                    0x76, 0x90, 0xce, 0xd2, 0xcd, 0x2b, 0xcb, 0x2c, 0xc3, 0xe4, 0x63, 0xe2, 0x8d,
1650                    0x8c, 0x29, 0xef, 0x3b, 0x01, 0xbe, 0x59, 0xb2, 0xbd, 0xfc, 0x38, 0x5b, 0xbd,
1651                    0xc7, 0x4b, 0x9c, 0x6d, 0x85, 0x9a, 0x75, 0x2c, 0x30, 0x5d, 0x62, 0x63, 0xde,
1652                    0x95, 0xf2, 0xfc, 0xf7, 0x34, 0xb1, 0x26, 0xdf, 0x24, 0x56, 0xc7, 0xd3, 0x1b,
1653                    0xc6, 0x01, 0xc8, 0xdd, 0xec, 0x40, 0x91, 0x12, 0xd3, 0xee, 0x41, 0xf8, 0x4b,
1654                    0x5a, 0x95, 0x08, 0xb6, 0x1d, 0x29, 0xb2, 0xfb, 0x45, 0x63, 0x6d, 0x19, 0xaa,
1655                    0x10, 0xd7, 0x82, 0xcd, 0x97, 0x8c, 0xfe, 0x67, 0x15, 0x49, 0x2f, 0xcd, 0x22,
1656                    0x4e, 0x1e, 0x36, 0xea, 0x0c, 0xf2, 0xbe, 0x2e, 0x9d, 0x6c, 0xe3, 0x80, 0xa8,
1657                    0xaf, 0x18, 0xe7, 0x5d, 0xa9, 0x22, 0x55, 0x51, 0xfb, 0xef, 0x8b, 0x98, 0x31,
1658                    0x1b, 0x5c, 0x9c, 0x1b, 0x4b, 0x9e, 0xe3, 0x57, 0xfc, 0x6c, 0x59, 0xa4, 0xf3,
1659                    0xad, 0x5a, 0x6f, 0x60, 0x9d, 0xb6, 0x71, 0xd2, 0x8c, 0xbf, 0x70, 0x3f, 0x0d,
1660                    0x14, 0xdc, 0x36, 0x3a, 0xaa, 0xed, 0x70, 0x72, 0x9c, 0x10, 0x7b, 0xbb, 0x6a,
1661                ],
1662                fp: [
1663                    0xdf, 0x0a, 0x89, 0xbd, 0x88, 0x35, 0x39, 0xc0, 0x7b, 0x89, 0xe0, 0x4c, 0x92,
1664                    0x76, 0x4e, 0xc2, 0xd1, 0x59, 0x69, 0x0f, 0x5a, 0xd5, 0xdd, 0x3d, 0x0a, 0xd8,
1665                    0xac, 0x29, 0x69, 0xde, 0x22, 0xc8,
1666                ],
1667                d0: None,
1668                d1: None,
1669                d2: None,
1670                dmax: Some([
1671                    0xb8, 0x31, 0xc2, 0x96, 0x5a, 0x86, 0x0a, 0xd7, 0x60, 0xec, 0x2a,
1672                ]),
1673                internal_nsk: Some([
1674                    0x9c, 0x39, 0x3c, 0x5b, 0xd7, 0x66, 0x4d, 0x63, 0xef, 0xa1, 0xba, 0xea, 0x99,
1675                    0xfc, 0x6d, 0xc4, 0x74, 0xfe, 0xa7, 0x53, 0xce, 0x84, 0xc8, 0x81, 0xd9, 0xef,
1676                    0x28, 0x77, 0x86, 0x75, 0xb1, 0x05,
1677                ]),
1678                internal_ovk: [
1679                    0x69, 0xaa, 0xb0, 0x2e, 0xa6, 0x43, 0x57, 0x9d, 0x4d, 0x85, 0x2a, 0xf8, 0xb4,
1680                    0x32, 0xb8, 0x8d, 0x1c, 0xa0, 0x00, 0x44, 0x4a, 0xb0, 0x73, 0x7a, 0x41, 0x15,
1681                    0xe0, 0x63, 0xf1, 0x48, 0xd2, 0x72,
1682                ],
1683                internal_dk: [
1684                    0x88, 0x26, 0xa9, 0x3c, 0x65, 0xc6, 0x6e, 0x75, 0x54, 0x32, 0x74, 0xe6, 0x72,
1685                    0xad, 0xf5, 0x59, 0xf7, 0xd7, 0x26, 0x5e, 0x99, 0xcc, 0x11, 0xda, 0x4a, 0x14,
1686                    0x20, 0xa3, 0x7b, 0x92, 0xf7, 0xab,
1687                ],
1688                internal_nk: [
1689                    0x59, 0xba, 0xa9, 0x0f, 0x83, 0x4a, 0x66, 0x1b, 0xf2, 0xbe, 0x42, 0x46, 0xa4,
1690                    0x3d, 0x18, 0x9c, 0x7d, 0x0e, 0x17, 0xa8, 0x24, 0x7b, 0x4f, 0xd9, 0xd2, 0xe1,
1691                    0x53, 0xa5, 0x97, 0x3d, 0xc8, 0xec,
1692                ],
1693                internal_ivk: [
1694                    0x8a, 0x86, 0xfb, 0x27, 0x81, 0xfe, 0x6f, 0x24, 0xd9, 0x60, 0xdd, 0xdb, 0x2f,
1695                    0x78, 0x13, 0xc0, 0x31, 0xfe, 0xc5, 0x5d, 0x26, 0xcc, 0xde, 0xe1, 0xf7, 0x18,
1696                    0x2a, 0x3e, 0xc6, 0x83, 0xcf, 0x04,
1697                ],
1698                internal_xsk: Some([
1699                    0x03, 0x0b, 0xdc, 0x2d, 0x2b, 0x03, 0x00, 0x00, 0x80, 0x33, 0xdc, 0x01, 0x2d,
1700                    0x76, 0x90, 0xce, 0xd2, 0xcd, 0x2b, 0xcb, 0x2c, 0xc3, 0xe4, 0x63, 0xe2, 0x8d,
1701                    0x8c, 0x29, 0xef, 0x3b, 0x01, 0xbe, 0x59, 0xb2, 0xbd, 0xfc, 0x38, 0x5b, 0xbd,
1702                    0xc7, 0x4b, 0x45, 0x93, 0xd2, 0x4d, 0x21, 0xe3, 0x59, 0x37, 0xf1, 0x52, 0xcf,
1703                    0x90, 0x46, 0x1c, 0x33, 0x2f, 0x69, 0x50, 0x3c, 0x10, 0x45, 0x81, 0xd6, 0x83,
1704                    0xe0, 0xac, 0x29, 0xf8, 0x4d, 0xec, 0xaf, 0x07, 0x9c, 0x39, 0x3c, 0x5b, 0xd7,
1705                    0x66, 0x4d, 0x63, 0xef, 0xa1, 0xba, 0xea, 0x99, 0xfc, 0x6d, 0xc4, 0x74, 0xfe,
1706                    0xa7, 0x53, 0xce, 0x84, 0xc8, 0x81, 0xd9, 0xef, 0x28, 0x77, 0x86, 0x75, 0xb1,
1707                    0x05, 0x69, 0xaa, 0xb0, 0x2e, 0xa6, 0x43, 0x57, 0x9d, 0x4d, 0x85, 0x2a, 0xf8,
1708                    0xb4, 0x32, 0xb8, 0x8d, 0x1c, 0xa0, 0x00, 0x44, 0x4a, 0xb0, 0x73, 0x7a, 0x41,
1709                    0x15, 0xe0, 0x63, 0xf1, 0x48, 0xd2, 0x72, 0x88, 0x26, 0xa9, 0x3c, 0x65, 0xc6,
1710                    0x6e, 0x75, 0x54, 0x32, 0x74, 0xe6, 0x72, 0xad, 0xf5, 0x59, 0xf7, 0xd7, 0x26,
1711                    0x5e, 0x99, 0xcc, 0x11, 0xda, 0x4a, 0x14, 0x20, 0xa3, 0x7b, 0x92, 0xf7, 0xab,
1712                ]),
1713                internal_xfvk: [
1714                    0x03, 0x0b, 0xdc, 0x2d, 0x2b, 0x03, 0x00, 0x00, 0x80, 0x33, 0xdc, 0x01, 0x2d,
1715                    0x76, 0x90, 0xce, 0xd2, 0xcd, 0x2b, 0xcb, 0x2c, 0xc3, 0xe4, 0x63, 0xe2, 0x8d,
1716                    0x8c, 0x29, 0xef, 0x3b, 0x01, 0xbe, 0x59, 0xb2, 0xbd, 0xfc, 0x38, 0x5b, 0xbd,
1717                    0xc7, 0x4b, 0x9c, 0x6d, 0x85, 0x9a, 0x75, 0x2c, 0x30, 0x5d, 0x62, 0x63, 0xde,
1718                    0x95, 0xf2, 0xfc, 0xf7, 0x34, 0xb1, 0x26, 0xdf, 0x24, 0x56, 0xc7, 0xd3, 0x1b,
1719                    0xc6, 0x01, 0xc8, 0xdd, 0xec, 0x40, 0x91, 0x12, 0x59, 0xba, 0xa9, 0x0f, 0x83,
1720                    0x4a, 0x66, 0x1b, 0xf2, 0xbe, 0x42, 0x46, 0xa4, 0x3d, 0x18, 0x9c, 0x7d, 0x0e,
1721                    0x17, 0xa8, 0x24, 0x7b, 0x4f, 0xd9, 0xd2, 0xe1, 0x53, 0xa5, 0x97, 0x3d, 0xc8,
1722                    0xec, 0x69, 0xaa, 0xb0, 0x2e, 0xa6, 0x43, 0x57, 0x9d, 0x4d, 0x85, 0x2a, 0xf8,
1723                    0xb4, 0x32, 0xb8, 0x8d, 0x1c, 0xa0, 0x00, 0x44, 0x4a, 0xb0, 0x73, 0x7a, 0x41,
1724                    0x15, 0xe0, 0x63, 0xf1, 0x48, 0xd2, 0x72, 0x88, 0x26, 0xa9, 0x3c, 0x65, 0xc6,
1725                    0x6e, 0x75, 0x54, 0x32, 0x74, 0xe6, 0x72, 0xad, 0xf5, 0x59, 0xf7, 0xd7, 0x26,
1726                    0x5e, 0x99, 0xcc, 0x11, 0xda, 0x4a, 0x14, 0x20, 0xa3, 0x7b, 0x92, 0xf7, 0xab,
1727                ],
1728                internal_fp: [
1729                    0x3f, 0x63, 0x16, 0x1d, 0x5b, 0x43, 0x72, 0x04, 0xf7, 0x01, 0x2a, 0x3a, 0x1d,
1730                    0x36, 0x58, 0x1d, 0xab, 0x39, 0x7a, 0x84, 0x3b, 0x2c, 0x58, 0x98, 0x11, 0xed,
1731                    0xcc, 0x5b, 0x50, 0x1c, 0xd4, 0xeb,
1732                ],
1733            },
1734        ];
1735
1736        let seed = [
1737            0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
1738            24, 25, 26, 27, 28, 29, 30, 31,
1739        ];
1740
1741        let i1h = ChildIndex::hardened(1);
1742        let i2h = ChildIndex::hardened(2);
1743        let i3h = ChildIndex::hardened(3);
1744
1745        let m = ExtendedSpendingKey::master(&seed);
1746        let m_1h = m.derive_child(i1h);
1747        let m_1h_2h = ExtendedSpendingKey::from_path(&m, &[i1h, i2h]);
1748        let m_1h_2h_3h = m_1h_2h.derive_child(i3h);
1749
1750        let xfvks = [
1751            m.to_extended_full_viewing_key(),
1752            m_1h.to_extended_full_viewing_key(),
1753            m_1h_2h.to_extended_full_viewing_key(),
1754            m_1h_2h_3h.to_extended_full_viewing_key(),
1755        ];
1756        assert_eq!(test_vectors.len(), xfvks.len());
1757
1758        let xsks = [m, m_1h, m_1h_2h, m_1h_2h_3h];
1759
1760        for (xsk, tv) in xsks.iter().zip(test_vectors.iter()) {
1761            assert_eq!(xsk.expsk.ask.to_bytes(), tv.ask.unwrap());
1762            assert_eq!(xsk.expsk.nsk.to_repr().as_ref(), tv.nsk.unwrap());
1763
1764            assert_eq!(xsk.expsk.ovk.0, tv.ovk);
1765            assert_eq!(xsk.dk.0, tv.dk);
1766            assert_eq!(xsk.chain_code.as_bytes(), &tv.c);
1767
1768            let mut ser = vec![];
1769            xsk.write(&mut ser).unwrap();
1770            assert_eq!(&ser[..], &tv.xsk.unwrap()[..]);
1771
1772            let internal_xsk = xsk.derive_internal();
1773            assert_eq!(internal_xsk.expsk.ask.to_bytes(), tv.ask.unwrap());
1774            assert_eq!(
1775                internal_xsk.expsk.nsk.to_repr().as_ref(),
1776                tv.internal_nsk.unwrap()
1777            );
1778
1779            assert_eq!(internal_xsk.expsk.ovk.0, tv.internal_ovk);
1780            assert_eq!(internal_xsk.dk.0, tv.internal_dk);
1781            assert_eq!(internal_xsk.chain_code.as_bytes(), &tv.c);
1782
1783            let mut ser = vec![];
1784            internal_xsk.write(&mut ser).unwrap();
1785            assert_eq!(&ser[..], &tv.internal_xsk.unwrap()[..]);
1786        }
1787
1788        for (xfvk, tv) in xfvks.iter().zip(test_vectors.iter()) {
1789            assert_eq!(xfvk.fvk.vk.ak.to_bytes(), tv.ak);
1790            assert_eq!(xfvk.fvk.vk.nk.0.to_bytes(), tv.nk);
1791
1792            assert_eq!(xfvk.fvk.ovk.0, tv.ovk);
1793            assert_eq!(xfvk.dk.0, tv.dk);
1794            assert_eq!(xfvk.chain_code.as_bytes(), &tv.c);
1795
1796            assert_eq!(xfvk.fvk.vk.ivk().to_repr().as_ref(), tv.ivk);
1797
1798            let mut ser = vec![];
1799            xfvk.write(&mut ser).unwrap();
1800            assert_eq!(&ser[..], &tv.xfvk[..]);
1801            assert_eq!(FvkFingerprint::from(&xfvk.fvk).0, tv.fp);
1802
1803            // d0
1804            let mut di = DiversifierIndex::new();
1805            match xfvk.dk.find_diversifier(di).unwrap() {
1806                (l, d) if l == di => assert_eq!(d.0, tv.d0.unwrap()),
1807                (_, _) => assert!(tv.d0.is_none()),
1808            }
1809
1810            // d1
1811            di.increment().unwrap();
1812            match xfvk.dk.find_diversifier(di).unwrap() {
1813                (l, d) if l == di => assert_eq!(d.0, tv.d1.unwrap()),
1814                (_, _) => assert!(tv.d1.is_none()),
1815            }
1816
1817            // d2
1818            di.increment().unwrap();
1819            match xfvk.dk.find_diversifier(di).unwrap() {
1820                (l, d) if l == di => assert_eq!(d.0, tv.d2.unwrap()),
1821                (_, _) => assert!(tv.d2.is_none()),
1822            }
1823
1824            // dmax
1825            let dmax = DiversifierIndex::from([0xff; 11]);
1826            match xfvk.dk.find_diversifier(dmax) {
1827                Some((l, d)) if l == dmax => assert_eq!(d.0, tv.dmax.unwrap()),
1828                Some((_, _)) => panic!(),
1829                None => assert!(tv.dmax.is_none()),
1830            }
1831
1832            let internal_xfvk = xfvk.derive_internal();
1833            assert_eq!(internal_xfvk.fvk.vk.ak.to_bytes(), tv.ak);
1834            assert_eq!(internal_xfvk.fvk.vk.nk.0.to_bytes(), tv.internal_nk);
1835
1836            assert_eq!(internal_xfvk.fvk.ovk.0, tv.internal_ovk);
1837            assert_eq!(internal_xfvk.dk.0, tv.internal_dk);
1838            assert_eq!(internal_xfvk.chain_code.as_bytes(), &tv.c);
1839
1840            assert_eq!(
1841                internal_xfvk.fvk.vk.ivk().to_repr().as_ref(),
1842                tv.internal_ivk
1843            );
1844
1845            let mut ser = vec![];
1846            internal_xfvk.write(&mut ser).unwrap();
1847            assert_eq!(&ser[..], &tv.internal_xfvk[..]);
1848            assert_eq!(FvkFingerprint::from(&internal_xfvk.fvk).0, tv.internal_fp);
1849
1850            let dfvk = xfvk.to_diversifiable_full_viewing_key();
1851            let ivk = dfvk.to_external_ivk();
1852            let ivk_bytes = ivk.to_bytes();
1853            assert_eq!(&ivk_bytes[..32], tv.dk);
1854            assert_eq!(&ivk_bytes[32..], tv.ivk);
1855
1856            let ivk_rt = IncomingViewingKey::from_bytes(&ivk_bytes).unwrap();
1857            assert_eq!(ivk.dk, ivk_rt.dk);
1858            assert_eq!(ivk.ivk.0, ivk_rt.ivk.0);
1859        }
1860    }
1861}
1862
1863#[cfg(any(test, feature = "test-dependencies"))]
1864#[cfg_attr(docsrs, doc(cfg(feature = "test-dependencies")))]
1865pub mod testing {
1866    use proptest::collection::vec;
1867    use proptest::prelude::{any, prop_compose};
1868
1869    use super::ExtendedSpendingKey;
1870
1871    prop_compose! {
1872        pub fn arb_extended_spending_key()(v in vec(any::<u8>(), 32..252)) -> ExtendedSpendingKey {
1873            ExtendedSpendingKey::master(&v)
1874        }
1875    }
1876}