1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
use anyhow::anyhow;
use base58check::ToBase58Check;
use bip39::{Language, Mnemonic, Seed};
use ripemd::{Digest, Ripemd160};
use secp256k1::{All, PublicKey, Secp256k1, SecretKey};
use serde::Serialize;
use sha2::Sha256;
use tiny_hderive::bip32::ExtendedPrivKey;
use zcash_client_backend::encoding::{
    encode_extended_spending_key, encode_payment_address, encode_transparent_address,
};
use zcash_primitives::consensus::{Network, Parameters};
use zcash_primitives::legacy::TransparentAddress;
use zcash_primitives::zip32::{ChildIndex, ExtendedSpendingKey};

#[derive(Serialize)]
pub struct KeyPack {
    pub t_addr: String,
    pub t_key: String,
    pub z_addr: String,
    pub z_key: String,
}

pub fn derive_zip32(
    network: &Network,
    phrase: &str,
    account_index: u32,
    external: u32,
    address_index: Option<u32>,
) -> anyhow::Result<KeyPack> {
    let mnemonic = Mnemonic::from_phrase(phrase, Language::English)?;
    let seed = Seed::new(&mnemonic, "");
    let master = ExtendedSpendingKey::master(seed.as_bytes());
    let mut z_path = vec![
        ChildIndex::Hardened(32),
        ChildIndex::Hardened(network.coin_type()),
        ChildIndex::Hardened(account_index),
    ];
    if let Some(address_index) = address_index {
        z_path.push(ChildIndex::Hardened(address_index));
    }
    let extsk = ExtendedSpendingKey::from_path(&master, &z_path);
    let z_key = encode_extended_spending_key(network.hrp_sapling_extended_spending_key(), &extsk);
    let (_, pa) = extsk.default_address();
    let z_addr = encode_payment_address(network.hrp_sapling_payment_address(), &pa);

    let addr_index = address_index.unwrap_or(0);
    let t_path = format!(
        "m/44'/{}'/{}'/{}/{}",
        network.coin_type(),
        account_index,
        external,
        addr_index
    );
    let ext = ExtendedPrivKey::derive(seed.as_bytes(), &*t_path)
        .map_err(|_| anyhow!("Invalid derivation path"))?;
    let secret_key = SecretKey::from_slice(&ext.secret())?;
    let secp = Secp256k1::<All>::new();
    let pub_key = PublicKey::from_secret_key(&secp, &secret_key);
    let pub_key = pub_key.serialize();
    let pub_key = Ripemd160::digest(&Sha256::digest(&pub_key));
    let t_addr = TransparentAddress::PublicKey(pub_key.into());
    let t_addr = encode_transparent_address(
        &network.b58_pubkey_address_prefix(),
        &network.b58_script_address_prefix(),
        &t_addr,
    );
    let mut sk = secret_key.serialize_secret().to_vec();
    sk.push(0x01);
    let t_key = sk.to_base58check(0x80);

    Ok(KeyPack {
        z_key,
        z_addr,
        t_key,
        t_addr,
    })
}