zcash-sync/src/zip32.rs

79 lines
2.6 KiB
Rust

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,
})
}