walletd_hd_key 0.2.0

WalletD HD Key library for handling HD keys
Documentation
use slip44::{Coin, Symbol};
use std::str::FromStr;
use walletd_hd_key::{
    Error, ExtendedPrivateKey, ExtendedPublicKey, HDKey, HDNetworkType, HDPath, HDPurpose, Seed,
};

#[test]
fn test_network_type() {
    assert_eq!(HDNetworkType::MainNet.to_string(), "mainnet");
    assert_eq!(HDNetworkType::TestNet.to_string(), "testnet");
}

#[test]
fn test_new_func_bip32_first_account() -> Result<(), Error> {
    let dt = HDPurpose::BIP32;
    let keys = HDKey::new(
        Seed::new(vec![
            162, 253, 156, 5, 34, 216, 77, 82, 238, 76, 133, 51, 220, 2, 212, 182, 155, 77, 249,
            182, 37, 94, 26, 242, 12, 159, 29, 77, 105, 22, 137, 242, 163, 134, 55, 235, 30, 199,
            120, 151, 43, 248, 69, 195, 45, 90, 232, 60, 117, 54, 153, 155, 86, 102, 57, 122, 195,
            32, 33, 178, 30, 10, 204, 238,
        ]),
        HDNetworkType::MainNet,
        &format!("m/{}/{}'/0'", dt, Coin::from(Symbol::BTC).id()),
    )?;

    assert_eq!(
        keys,
        HDKey {
            master_seed: Seed::new(vec![
                162, 253, 156, 5, 34, 216, 77, 82, 238, 76, 133, 51, 220, 2, 212, 182, 155, 77,
                249, 182, 37, 94, 26, 242, 12, 159, 29, 77, 105, 22, 137, 242, 163, 134, 55, 235,
                30, 199, 120, 151, 43, 248, 69, 195, 45, 90, 232, 60, 117, 54, 153, 155, 86, 102,
                57, 122, 195, 32, 33, 178, 30, 10, 204, 238
            ]),
            derivation_path: HDPath::from_str("m/0'/0'/0'")?,
            chain_code: [
                232, 52, 107, 14, 44, 22, 8, 59, 174, 66, 87, 0, 203, 147, 163, 167, 84, 231, 203,
                92, 107, 241, 154, 155, 115, 40, 57, 109, 88, 159, 240, 240
            ],
            depth: 3,
            parent_fingerprint: [107, 29, 72, 246],
            extended_private_key: Some(ExtendedPrivateKey::from_slice(&[
                192, 250, 8, 248, 220, 160, 148, 114, 210, 240, 91, 48, 42, 71, 243, 28, 64, 173,
                186, 85, 26, 141, 214, 240, 128, 27, 225, 155, 145, 56, 237, 101
            ])?),
            extended_public_key: Some(ExtendedPublicKey::from_slice(&[
                2, 134, 68, 19, 216, 122, 40, 153, 49, 141, 8, 93, 145, 229, 90, 54, 99, 218, 63,
                46, 66, 210, 6, 3, 180, 128, 2, 30, 250, 181, 84, 87, 185
            ])?),
            child_index: 2147483648,
            network: HDNetworkType::MainNet,
            derivation_purpose: HDPurpose::BIP32,
        }
    );
    Ok(())
}

#[test]
fn test_bip32_first_account_derive() -> Result<(), Error> {
    let dt = HDPurpose::BIP32;
    let keys = HDKey::new_master(
        Seed::new(vec![
            162, 253, 156, 5, 34, 216, 77, 82, 238, 76, 133, 51, 220, 2, 212, 182, 155, 77, 249,
            182, 37, 94, 26, 242, 12, 159, 29, 77, 105, 22, 137, 242, 163, 134, 55, 235, 30, 199,
            120, 151, 43, 248, 69, 195, 45, 90, 232, 60, 117, 54, 153, 155, 86, 102, 57, 122, 195,
            32, 33, 178, 30, 10, 204, 238,
        ]),
        HDNetworkType::MainNet,
    )?;
    assert_eq!(
        keys.derive(&format!("m/{}/{}'/0'", dt, Coin::from(Symbol::BTC).id()))?,
        HDKey {
            master_seed: Seed::new(vec![
                162, 253, 156, 5, 34, 216, 77, 82, 238, 76, 133, 51, 220, 2, 212, 182, 155, 77,
                249, 182, 37, 94, 26, 242, 12, 159, 29, 77, 105, 22, 137, 242, 163, 134, 55, 235,
                30, 199, 120, 151, 43, 248, 69, 195, 45, 90, 232, 60, 117, 54, 153, 155, 86, 102,
                57, 122, 195, 32, 33, 178, 30, 10, 204, 238
            ]),
            derivation_path: HDPath::from_str("m/0'/0'/0'")?,
            chain_code: [
                232, 52, 107, 14, 44, 22, 8, 59, 174, 66, 87, 0, 203, 147, 163, 167, 84, 231, 203,
                92, 107, 241, 154, 155, 115, 40, 57, 109, 88, 159, 240, 240
            ],
            depth: 3,
            parent_fingerprint: [107, 29, 72, 246],
            extended_private_key: Some(ExtendedPrivateKey::from_slice(&[
                192, 250, 8, 248, 220, 160, 148, 114, 210, 240, 91, 48, 42, 71, 243, 28, 64, 173,
                186, 85, 26, 141, 214, 240, 128, 27, 225, 155, 145, 56, 237, 101
            ])?),
            extended_public_key: Some(ExtendedPublicKey::from_slice(&[
                2, 134, 68, 19, 216, 122, 40, 153, 49, 141, 8, 93, 145, 229, 90, 54, 99, 218, 63,
                46, 66, 210, 6, 3, 180, 128, 2, 30, 250, 181, 84, 87, 185
            ])?),
            child_index: 2147483648,
            network: HDNetworkType::MainNet,
            derivation_purpose: HDPurpose::BIP32,
        }
    );

    Ok(())
}

#[test]
fn test_new_func_first_bip32_address() -> Result<(), Error> {
    let dt = HDPurpose::BIP32;
    let keys = HDKey::new(
        Seed::new(vec![
            162, 253, 156, 5, 34, 216, 77, 82, 238, 76, 133, 51, 220, 2, 212, 182, 155, 77, 249,
            182, 37, 94, 26, 242, 12, 159, 29, 77, 105, 22, 137, 242, 163, 134, 55, 235, 30, 199,
            120, 151, 43, 248, 69, 195, 45, 90, 232, 60, 117, 54, 153, 155, 86, 102, 57, 122, 195,
            32, 33, 178, 30, 10, 204, 238,
        ]),
        HDNetworkType::MainNet,
        &dt.default_path_specify(Coin::Bitcoin.id(), 0, 0, 0),
    )?;

    assert_eq!(
        keys,
        HDKey {
            master_seed: Seed::new(vec![
                162, 253, 156, 5, 34, 216, 77, 82, 238, 76, 133, 51, 220, 2, 212, 182, 155, 77,
                249, 182, 37, 94, 26, 242, 12, 159, 29, 77, 105, 22, 137, 242, 163, 134, 55, 235,
                30, 199, 120, 151, 43, 248, 69, 195, 45, 90, 232, 60, 117, 54, 153, 155, 86, 102,
                57, 122, 195, 32, 33, 178, 30, 10, 204, 238
            ]),
            derivation_path: HDPath::from_str("m/0'/0'/0'/0/0")?,
            chain_code: [
                77, 157, 183, 97, 179, 135, 148, 182, 249, 135, 66, 7, 35, 20, 70, 206, 27, 66, 0,
                133, 246, 255, 179, 36, 121, 22, 245, 17, 169, 178, 56, 73
            ],
            depth: 5,
            parent_fingerprint: [252, 17, 0, 152],
            extended_private_key: Some(ExtendedPrivateKey::from_slice(&[
                37, 137, 71, 12, 145, 160, 177, 51, 192, 93, 77, 95, 253, 188, 73, 141, 60, 223,
                118, 144, 156, 92, 95, 18, 7, 104, 131, 208, 25, 158, 233, 219
            ])?),
            extended_public_key: Some(ExtendedPublicKey::from_slice(&[
                2, 232, 62, 185, 87, 185, 189, 35, 206, 203, 149, 71, 11, 176, 241, 36, 100, 0,
                201, 165, 200, 202, 72, 77, 132, 229, 128, 178, 82, 207, 191, 60, 8
            ])?),
            child_index: 0,
            network: HDNetworkType::MainNet,
            derivation_purpose: HDPurpose::BIP32,
        }
    );

    Ok(())
}

#[test]
fn test_derive_first_bip32_address() -> Result<(), Error> {
    let dt = HDPurpose::BIP32;
    let keys = HDKey::new_master(
        Seed::new(vec![
            162, 253, 156, 5, 34, 216, 77, 82, 238, 76, 133, 51, 220, 2, 212, 182, 155, 77, 249,
            182, 37, 94, 26, 242, 12, 159, 29, 77, 105, 22, 137, 242, 163, 134, 55, 235, 30, 199,
            120, 151, 43, 248, 69, 195, 45, 90, 232, 60, 117, 54, 153, 155, 86, 102, 57, 122, 195,
            32, 33, 178, 30, 10, 204, 238,
        ]),
        HDNetworkType::MainNet,
    )?;

    assert_eq!(
        keys.derive(&dt.default_path_specify(Coin::Bitcoin.id(), 0, 0, 0))?,
        HDKey {
            master_seed: Seed::new(vec![
                162, 253, 156, 5, 34, 216, 77, 82, 238, 76, 133, 51, 220, 2, 212, 182, 155, 77,
                249, 182, 37, 94, 26, 242, 12, 159, 29, 77, 105, 22, 137, 242, 163, 134, 55, 235,
                30, 199, 120, 151, 43, 248, 69, 195, 45, 90, 232, 60, 117, 54, 153, 155, 86, 102,
                57, 122, 195, 32, 33, 178, 30, 10, 204, 238
            ]),
            derivation_path: HDPath::from_str("m/0'/0'/0'/0/0")?,
            chain_code: [
                77, 157, 183, 97, 179, 135, 148, 182, 249, 135, 66, 7, 35, 20, 70, 206, 27, 66, 0,
                133, 246, 255, 179, 36, 121, 22, 245, 17, 169, 178, 56, 73
            ],
            depth: 5,
            parent_fingerprint: [252, 17, 0, 152],
            extended_private_key: Some(ExtendedPrivateKey::from_slice(&[
                37, 137, 71, 12, 145, 160, 177, 51, 192, 93, 77, 95, 253, 188, 73, 141, 60, 223,
                118, 144, 156, 92, 95, 18, 7, 104, 131, 208, 25, 158, 233, 219
            ])?),
            extended_public_key: Some(ExtendedPublicKey::from_slice(&[
                2, 232, 62, 185, 87, 185, 189, 35, 206, 203, 149, 71, 11, 176, 241, 36, 100, 0,
                201, 165, 200, 202, 72, 77, 132, 229, 128, 178, 82, 207, 191, 60, 8
            ])?),
            child_index: 0,
            network: HDNetworkType::MainNet,
            derivation_purpose: HDPurpose::BIP32,
        }
    );

    Ok(())
}

#[test]
fn test_derive_change_internal_chain() -> Result<(), Error> {
    let dt = HDPurpose::BIP32;
    let derived = HDKey::new(
        Seed::new(vec![
            162, 253, 156, 5, 34, 216, 77, 82, 238, 76, 133, 51, 220, 2, 212, 182, 155, 77, 249,
            182, 37, 94, 26, 242, 12, 159, 29, 77, 105, 22, 137, 242, 163, 134, 55, 235, 30, 199,
            120, 151, 43, 248, 69, 195, 45, 90, 232, 60, 117, 54, 153, 155, 86, 102, 57, 122, 195,
            32, 33, 178, 30, 10, 204, 238,
        ]),
        HDNetworkType::MainNet,
        &dt.default_path_specify(Coin::Bitcoin.id(), 0, 1, 0),
    )?;
    assert_eq!(
        derived.derivation_path.to_string(),
        "m/0'/0'/0'/1/0".to_string()
    );
    assert_eq!(&derived.extended_private_key_serialized()?, "xprvA47jwGZNLdTnKuMGfLdeYMV7dgAF9gCjYUNYGeAjJuXrRbj1MULdePjyC5nH7Pp2GTRqnXqkumeJC29fRVVSJmbrWDUENyRG22n1tJdn5b7");
    assert_eq!(&derived.extended_public_key_serialized()?, "xpub6H76Ln6GB125YPRjmNAeuVRrBhzjZ8vauhJ952aLsF4qJQ49u1etCC4T3KkvysShJwgdPL3B5fEsiZJCeymY1Z2wfUNXN77ksN9oqLP9PU3");
    assert_eq!(
        &derived.to_wif()?,
        "L36tbAQoqCpU4rHQuyhYmRHscbcrSc31HXefsrMUaXco8Wqfpaqf"
    );
    assert_eq!(
        format!("{:x}", derived.extended_public_key()?),
        "0224c0180e484ca64cea39fc471a02bf286196e12d10f08dfa18bdc995f0707cad"
    );
    Ok(())
}

#[test]
fn test_eth_bip44() -> Result<(), Error> {
    let dpath = HDPath::builder()
        .purpose_index(HDPurpose::BIP44.to_shortform_num())
        .coin_type_index(Coin::from(Symbol::ETH).id())
        .build()
        .to_string();

    let derived_key = HDKey::new(
        Seed::new(vec![
            162, 253, 156, 5, 34, 216, 77, 82, 238, 76, 133, 51, 220, 2, 212, 182, 155, 77, 249,
            182, 37, 94, 26, 242, 12, 159, 29, 77, 105, 22, 137, 242, 163, 134, 55, 235, 30, 199,
            120, 151, 43, 248, 69, 195, 45, 90, 232, 60, 117, 54, 153, 155, 86, 102, 57, 122, 195,
            32, 33, 178, 30, 10, 204, 238,
        ]),
        HDNetworkType::MainNet,
        &dpath,
    )?;

    assert_eq!(
        derived_key,
        HDKey {
            master_seed: Seed::new(vec![
                162, 253, 156, 5, 34, 216, 77, 82, 238, 76, 133, 51, 220, 2, 212, 182, 155, 77,
                249, 182, 37, 94, 26, 242, 12, 159, 29, 77, 105, 22, 137, 242, 163, 134, 55, 235,
                30, 199, 120, 151, 43, 248, 69, 195, 45, 90, 232, 60, 117, 54, 153, 155, 86, 102,
                57, 122, 195, 32, 33, 178, 30, 10, 204, 238
            ]),
            derivation_path: HDPath::from_str("m/44h/60h/0h/0/0")?,
            chain_code: [
                109, 150, 159, 21, 145, 38, 169, 238, 94, 27, 158, 36, 221, 164, 167, 226, 84, 253,
                81, 90, 210, 254, 84, 178, 233, 164, 217, 131, 149, 75, 168, 105
            ],
            depth: 5,
            parent_fingerprint: [219, 127, 235, 119],
            extended_private_key: Some(ExtendedPrivateKey::from_slice(&[
                165, 220, 218, 239, 160, 128, 19, 9, 44, 163, 125, 63, 96, 212, 111, 39, 81, 13,
                248, 119, 122, 58, 125, 214, 161, 185, 243, 115, 53, 44, 170, 117
            ])?),
            extended_public_key: Some(ExtendedPublicKey::from_slice(&[
                3, 237, 181, 7, 68, 80, 173, 147, 6, 71, 14, 89, 107, 91, 14, 126, 178, 36, 245,
                197, 197, 57, 113, 112, 101, 150, 46, 195, 101, 233, 63, 6, 97
            ])?),
            child_index: 0,
            network: HDNetworkType::MainNet,
            derivation_purpose: HDPurpose::BIP44,
        }
    );
    Ok(())
}

#[test]
fn test_bip49_first_account() -> Result<(), Error> {
    let mut path_builder = HDPath::builder();
    path_builder
        .purpose_index(HDPurpose::BIP49.to_shortform_num())
        .coin_type_index(Coin::from(Symbol::BTC).id())
        .no_change_index()
        .no_address_index();

    let master_key = HDKey::new_master(
        Seed::new(vec![
            162, 253, 156, 5, 34, 216, 77, 82, 238, 76, 133, 51, 220, 2, 212, 182, 155, 77, 249,
            182, 37, 94, 26, 242, 12, 159, 29, 77, 105, 22, 137, 242, 163, 134, 55, 235, 30, 199,
            120, 151, 43, 248, 69, 195, 45, 90, 232, 60, 117, 54, 153, 155, 86, 102, 57, 122, 195,
            32, 33, 178, 30, 10, 204, 238,
        ]),
        HDNetworkType::MainNet,
    )?;

    assert_eq!(master_key.derivation_path.to_string(), "m".to_string());

    let first_account = master_key.derive(&path_builder.build().to_string())?;
    assert_eq!(
        first_account.derivation_path.to_string(),
        "m/49'/0'/0'".to_string()
    );
    assert_eq!(&first_account.extended_private_key_serialized()?, "yprvAKG7rKYNjGJwgbJsjEADohvBMDupqc4J3hAopkwqyfKk73voyQzBNuVFLJFUPVrpm3ei2H1cQCBP1oiAaasyNc9UoPzasnScRfeZxFDT4Tf");
    assert_eq!(&first_account.extended_public_key_serialized()?, "ypub6YFUFq5GZdsEu5PLqFhEAqruuFkKF4n9Qv6Qd9MTXzriyrFxWxJRvhojBZgZ7Wj5Ak9ow69A8QXhiZoRu41FvXdK7rv8cXJo5PoLwqNywF5");
    Ok(())
}

#[test]

fn test_bip49_address_one() -> Result<(), Error> {
    let mut path_builder = HDPath::builder();
    path_builder
        .purpose_index(HDPurpose::BIP49.to_shortform_num())
        .coin_type_index(Coin::from(Symbol::BTC).id())
        .address_index(1);

    let derived = HDKey::new(
        Seed::new(vec![
            162, 253, 156, 5, 34, 216, 77, 82, 238, 76, 133, 51, 220, 2, 212, 182, 155, 77, 249,
            182, 37, 94, 26, 242, 12, 159, 29, 77, 105, 22, 137, 242, 163, 134, 55, 235, 30, 199,
            120, 151, 43, 248, 69, 195, 45, 90, 232, 60, 117, 54, 153, 155, 86, 102, 57, 122, 195,
            32, 33, 178, 30, 10, 204, 238,
        ]),
        HDNetworkType::MainNet,
        &path_builder.build().to_string(),
    )?;

    assert_eq!(
        derived.derivation_path.to_string(),
        "m/49'/0'/0'/0/1".to_string()
    );
    assert_eq!(
        &derived.to_wif()?,
        "KzzMux1HnhZCAiCLScSpUDtXtsHgjts4RJLadDDi2zgxU2qq3g53"
    );
    assert_eq!(
        format!("{:x}", derived.extended_public_key()?),
        "02b9a730f83f85b77c7cf2f444d6cf76b144e11370bb96c6cbc624072f2d8e94cc"
    );
    Ok(())
}