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