Skip to main content

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