HD Key

WalletD features BIP32 compliant HD keys to facilitate Hierarchical Deterministic (HD) wallets. In this section, we'll guide you on how to use WalletD to access BIP39 mnemonics.

Imports needed for this section

use walletd::walletd_hd_key::prelude::*;
use walletd::walletd_hd_key::slip44::{Coin, Symbol};

Master HD Key from a Seed

You can create a new master HDKey from a Seed and also derive a child HDKey.

Here's how you can import a master hd key based on a seed hex:

Example

 let seed_hex = "a2fd9c0522d84d52ee4c8533dc02d4b69b4df9b6255e1af20c9f1d4d691689f2a38637eb1ec778972bf845c32d5ae83c7536999b5666397ac32021b21e0accee";
 let seed = Seed::from_str(seed_hex)?;
 // creating a new master key for a testnet network based on the above seed
 let master_hd_key = HDKey::new_master(seed, HDNetworkType::TestNet)?;
 // the master hd key should have a depth of 0
 println!("master hd key depth {}", master_hd_key.depth());

Setting a network type on the HDKey is required, you should select HDNetworkType::TestNet during development and testing purposes and to avoid using real funds and HDNetworkType::MainNet for production level code with caution. Be sure to be consistent with HDNetworkType when connecting to the blockchain, make sure to use a compatible blockchain for the specified network type category.

HD Key Serialization, WIF

The Wallet Import Format (WIF) is a standard way to encode private keys. The extended public key and extended private key can also be serialized using a standard serialized format.

How to display these key formats for an HDKey:

Example

// use the to_wif method 
println!("wif of master hd key {}", master_hd_key.to_wif().unwrap());

// can display the serialized for mat of the extended private and public keys
println!(
     "master hd key extended public key: {}",
     master_hd_key.extended_public_key_serialized()?
);
println!(
     "master hd key extended private key: {}",
     master_hd_key.extended_private_key_serialized()?
);

Specify Derivation Path

You flexibly specify the derivation path using the HDPathBuilder. The default HDPathBuilder which can be accessed through HDPath::builder() does not specify the purpose, or coin type, and uses the hardened indices for the purpose, coin type, and account indices.

Example

let default_deriv_path = HDPath::builder().build().to_string();
// without specifying the purpose, the default derivation path is "m
assert_eq!(default_deriv_path, "m");
println!("default derivation path: {}", default_deriv_path);

// can flexibly specify the derivation path using the HDPathBuilder
let account_deriv_path = HDPath::builder()
.purpose_index(HDPurpose::BIP44.to_shortform_num())
.coin_type_index(Coin::from(Symbol::ETH).id())
.account_index(0)
.no_change_index()
.no_address_index()
.build().to_string();

// checking this is what we are expecting 
assert_eq!(account_deriv_path, "m/44'/60'/0'");

Derive Child HD Key

Can derive a child key from the master key or a parent key using the HDKey::derive method.

Example

 let eth_first_account_key = master_hd_key.derive(account_deriv_path.to_string())?;
 // the master seed should be the same for a child HD Key and it's parent HD Key
 assert_eq!(
 eth_first_account_key.master_seed(),
 master_hd_key.master_seed()
 );
 
 println!(
 "eth_first_account_key depth {}",
 eth_first_account_key.depth()
 );
 assert_eq!(eth_first_account_key.depth(), 3);

Can also use a string directly to represent the derivation path, ' or h can be used to denote a hardened index.

Example

let account_deriv_path = HDPath::builder()
.purpose_index(HDPurpose::BIP44.to_shortform_num())
.coin_type_index(Coin::from(Symbol::ETH).id())
.account_index(0)
.no_change_index()
.no_address_index()
.build().to_string();
 let compare_account_key = master_hd_key.derive("m/44h/60h/0h".to_string())?;
 assert_eq!(eth_first_account_key, compare_account_key);

 let address_key1 = master_hd_key.derive("m/44h/60h/0h/0/0".to_string())?;
 let address_key2 = eth_first_account_key.derive("m/44'/60'/0'/0/0".to_string())?;
 assert_eq!(address_key1, address_key2);

A shortcut way to get a derived HDKey directly from a master seed, with a specified HDNetworkType and a derivation path is to use the HDKey::new method.

Example

 let custom_key_path = HDPath::builder()
 .purpose_index(HDPurpose::BIP84.to_shortform_num())
 .coin_type_index(Coin::Testnet.id())
 .account_index(0)
 .change_index(1)
 .address_index(0)
 .hardened_address()
 .build()
 .to_string();

 assert_eq!(custom_key_path, "m/84'/1'/0'/1/0'");
 let derived_key = HDKey::new(
    seed,
    HDNetworkType::TestNet,
    custom_key_path,
 )?;
println!("derived_key: {:?}", derived_key);
 println!("derived_key depth: {}", derived_key.depth());
println!("derived_key wif: {}", derived_key.to_wif()?);
println!(
   "derived_key public key: {}",
   derived_key.extended_public_key_serialized()?
);
println!(
   "derived_key private key: {}",
  derived_key.extended_private_key_serialized()?
);
  // Can also just display the bytes in the extended private and public keys as a hex
 println!(
   "derived_key public key hex: {:x}",
  derived_key.extended_public_key()?);
 println!(
     "derived_key private key hex: {:x}",
    derived_key.extended_private_key()?);