libbitcoin / libbitcoin-system

Bitcoin Cross-Platform C++ Development Toolkit

Home Page:https://libbitcoin.info/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

How to get a Bitcoin address from a mnemonic?

evoskuil opened this issue · comments

Attempt posted by @alonargel

#include <cstdlib>
#include <string>
#include <bitcoin/system.hpp>

BC_USE_LIBBITCOIN_MAIN
LIBBITCOIN_SYSTEM_WALLET_MNEMONICS_MNEMONIC_HPP

int bc::system::main(int argc, char* argv[])
{
    using namespace bc;
    using namespace bc::system;
    using namespace bc::system::wallet;

    std::string mnemonic_phrase = "bunker churn kangaroo melt bleak chalk vacant alert reason exit forward language";
    libbitcoin::system::wallet::mnemonic mnemonic(mnemonic_phrase);

    libbitcoin::system::wallet::hd_private key = mnemonic.to_key();
    system::cout << mnemonic.to_key() << std::endl;

    return 0;
}

The following example demonstrates:

  • derivation of recovery seed from BIP39 mnemonic phrase
  • derivation of account private key from recovery seed
  • derivation of hd hard/soft keys along arbitrary private path
  • derivation of corresponding hd public keys from hd private keys
  • derivation of corresponding ec private keys from hd private keys
  • derivation of corresponding ec public keys from hd private keys
  • derivation of p2pkh and witness addresses from ec private keys
  • derivation of corresponding hd public keys along public path
  • derivation of corresponding ec public keys from hd public keys
  • derivation of p2pkh and witness addresses from ec public keys
  • prefix, chain, version, and compression context for address construction
#include <cstdlib>
#include <bitcoin/system.hpp>

BC_USE_LIBBITCOIN_MAIN

int bc::system::main(int, char*[])
{
    using namespace bc;
    using namespace bc::system;
    using namespace bc::system::wallet;

    set_utf8_stdio();

    // Recovery seed derivation.
    const auto mnemonic_phrase = "bunker churn kangaroo melt bleak chalk vacant alert reason exit forward language";
    const mnemonic recovery_seed(mnemonic_phrase);
    if (!recovery_seed)
    {
        system::cerr << "Invalid mnemonic phrase." << std::endl;
        return EXIT_FAILURE;
    }

    const auto compressed = true;
    const auto context = ctx::btc::main::p2pkh;

    // Account private key derivation.
    const auto passphrase = "";
    const auto account_private_key = recovery_seed.to_key(passphrase, context);
    if (!account_private_key)
    {
        system::cerr << "Unexpected error." << std::endl;
        return EXIT_FAILURE;
    }

    // private
    // ------------------------------------------------------------------------

    // Some hd private key derivations.
    const auto& m = account_private_key;
    const auto m0h = m.derive_private(hd_first_hardened_key);
    const auto m0h1 = m0h.derive_private(1);
    const auto m0h12h = m0h1.derive_private(2 + hd_first_hardened_key);
    const auto m0h12h2 = m0h12h.derive_private(2);
    const auto m0h12h2x = m0h12h2.derive_private(1000000000);

    system::cout << m << std::endl;
    system::cout << m0h << std::endl;
    system::cout << m0h1 << std::endl;
    system::cout << m0h12h << std::endl;
    system::cout << m0h12h2 << std::endl;
    system::cout << m0h12h2x << std::endl;
    system::cout << std::endl;

    // Corresponding hd public key derivations.
    const auto hd_p = m.to_public();
    const auto hd0h_p = m0h.to_public();
    const auto hd0h1_p = m0h1.to_public();
    const auto hd0h12h_p = m0h12h.to_public();
    const auto hd0h12h2_p = m0h12h2.to_public();
    const auto hd0h12h2x_p = m0h12h2x.to_public();

    system::cout << hd_p << std::endl;
    system::cout << hd0h_p << std::endl;
    system::cout << hd0h1_p << std::endl;
    system::cout << hd0h12h_p << std::endl;
    system::cout << hd0h12h2_p  << std::endl;
    system::cout << hd0h12h2x_p << std::endl;
    system::cout << std::endl;

    // Corresponding ec private key derivations.
    const auto ec = m.secret();
    const auto ec0h = m0h.secret();
    const auto ec0h1 = m0h1.secret();
    const auto ec0h12h = m0h12h.secret();
    const auto ec0h12h2 = m0h12h2.secret();
    const auto ec0h12h2x = m0h12h2x.secret();

    system::cout << encode_base16(ec) << std::endl;
    system::cout << encode_base16(ec0h) << std::endl;
    system::cout << encode_base16(ec0h1) << std::endl;
    system::cout << encode_base16(ec0h12h) << std::endl;
    system::cout << encode_base16(ec0h12h2) << std::endl;
    system::cout << encode_base16(ec0h12h2x) << std::endl;
    system::cout << std::endl;

    // Corresponding ec public keys from private.
    const auto ec_p = m.point();
    const auto ec0h_p = m0h.point();
    const auto ec0h1_p = m0h1.point();
    const auto ec0h12h_p = m0h12h.point();
    const auto ec0h12h2_p = m0h12h2.point();
    const auto ec0h12h2x_p = m0h12h2x.point();

    system::cout << encode_base16(ec_p) << std::endl;
    system::cout << encode_base16(ec0h_p) << std::endl;
    system::cout << encode_base16(ec0h1_p) << std::endl;
    system::cout << encode_base16(ec0h12h_p) << std::endl;
    system::cout << encode_base16(ec0h12h2_p) << std::endl;
    system::cout << encode_base16(ec0h12h2x_p) << std::endl;
    system::cout << std::endl;

    // Corresponding witness address derivations from ec private keys.
    // ec_private{} prevents ec_secret (vector) from falling into hash_digest
    // (vector) constructor, as these are byte vectors of the same length.
    const auto witness = witness_address(ec_private{ ec }, context.witness_prefix);
    const auto witness0h = witness_address(ec_private{ ec0h }, context.witness_prefix);
    const auto witness0h1 = witness_address(ec_private{ ec0h1 }, context.witness_prefix);
    const auto witness0h12h = witness_address(ec_private{ ec0h12h }, context.witness_prefix);
    const auto witness0h12h2 = witness_address(ec_private{ ec0h12h2 }, context.witness_prefix);
    const auto witness0h12h2x = witness_address(ec_private{ ec0h12h2x }, context.witness_prefix);

    system::cout << witness << std::endl;
    system::cout << witness0h << std::endl;
    system::cout << witness0h1 << std::endl;
    system::cout << witness0h12h << std::endl;
    system::cout << witness0h12h2 << std::endl;
    system::cout << witness0h12h2x << std::endl;
    system::cout << std::endl;

    // Corresponding payment address derivations from ec private keys.
    // ec_private allows for passage of context (prevents context loss in some situations).
    const auto address = payment_address(ec_private{ ec, context.versions(), compressed });
    const auto address0h = payment_address(ec_private{ ec0h, context.versions(), compressed });
    const auto address0h1 = payment_address(ec_private{ ec0h1, context.versions(), compressed });
    const auto address0h12h = payment_address(ec_private{ ec0h12h, context.versions(), compressed });
    const auto address0h12h2 = payment_address(ec_private{ ec0h12h2, context.versions(), compressed });
    const auto address0h12h2x = payment_address(ec_private{ ec0h12h2x, context.versions(), compressed });

    system::cout << address << std::endl;
    system::cout << address0h << std::endl;
    system::cout << address0h1 << std::endl;
    system::cout << address0h12h << std::endl;
    system::cout << address0h12h2 << std::endl;
    system::cout << address0h12h2x << std::endl;
    system::cout << std::endl;

    // public
    // ------------------------------------------------------------------------

    // Corresponding hd public key derivations.
    const auto m_pub = m.to_public();
    const auto m0h_pub = m.derive_public(hd_first_hardened_key);
    const auto m0h1_pub = m0h.derive_public(1);
    const auto m0h12h_pub = m0h1.derive_public(2 + hd_first_hardened_key);
    const auto m0h12h2_pub = m0h12h.derive_public(2);
    const auto m0h12h2x_pub = m0h12h2.derive_public(1000000000);

    system::cout << m_pub << std::endl;
    system::cout << m0h_pub << std::endl;
    system::cout << m0h1_pub << std::endl;
    system::cout << m0h12h_pub << std::endl;
    system::cout << m0h12h2_pub << std::endl;
    system::cout << m0h12h2x_pub << std::endl;
    system::cout << std::endl;

    // Corresponding ec public key derivations.
    const auto ec_pub = m_pub.point();
    const auto ec0h_pub = m0h_pub.point();
    const auto ec0h1_pub = m0h1_pub.point();
    const auto ec0h12h_pub = m0h12h_pub.point();
    const auto ec0h12h2_pub = m0h12h2_pub.point();
    const auto ec0h12h2x_pub = m0h12h2x_pub.point();

    system::cout << encode_base16(ec_pub) << std::endl;
    system::cout << encode_base16(ec0h_pub) << std::endl;
    system::cout << encode_base16(ec0h1_pub) << std::endl;
    system::cout << encode_base16(ec0h12h_pub) << std::endl;
    system::cout << encode_base16(ec0h12h2_pub) << std::endl;
    system::cout << encode_base16(ec0h12h2x_pub) << std::endl;
    system::cout << std::endl;

    // Corresponding witness address derivations from ec public keys.
    // ec_public{} unnecessary, as ec_compressed is unambiguous 33 byte vector.
    // ec_public does not provide for passage of context (can always be passed locally).
    const auto witness_pub = witness_address(ec_public{ ec_pub, compressed }, context.witness_prefix);
    const auto witness0h_pub = witness_address(ec_public{ ec0h_pub, compressed }, context.witness_prefix);
    const auto witness0h1_pub = witness_address(ec_public{ ec0h1_pub, compressed }, context.witness_prefix);
    const auto witness0h12h_pub = witness_address(ec_public{ ec0h12h_pub, compressed }, context.witness_prefix);
    const auto witness0h12h2_pub = witness_address(ec_public{ ec0h12h2_pub, compressed }, context.witness_prefix);
    const auto witness0h12h2x_pub = witness_address(ec_public{ ec0h12h2x_pub, compressed }, context.witness_prefix);

    system::cout << witness_pub << std::endl;
    system::cout << witness0h_pub << std::endl;
    system::cout << witness0h1_pub << std::endl;
    system::cout << witness0h12h_pub << std::endl;
    system::cout << witness0h12h2_pub << std::endl;
    system::cout << witness0h12h2x_pub << std::endl;
    system::cout << std::endl;

    // Corresponding payment address derivations from ec public keys.
    const auto address_pub = payment_address(ec_pub, context.address_version);
    const auto address0h_pub = payment_address(ec0h_pub, context.address_version);
    const auto address0h1_pub = payment_address(ec0h1_pub, context.address_version);
    const auto address0h12h_pub = payment_address(ec0h12h_pub, context.address_version);
    const auto address0h12h2_pub = payment_address(ec0h12h2_pub, context.address_version);
    const auto address0h12h2x_pub = payment_address(ec0h12h2x_pub, context.address_version);

    system::cout << address_pub << std::endl;
    system::cout << address0h_pub << std::endl;
    system::cout << address0h1_pub << std::endl;
    system::cout << address0h12h_pub << std::endl;
    system::cout << address0h12h2_pub << std::endl;
    system::cout << address0h12h2x_pub << std::endl;
    system::cout << std::endl;

    return EXIT_SUCCESS;
}
xprv9s21ZrQH143K3S4c2rZ8jL6PLrSw69LKiGnGdXykiF9yVSqXWQFQ6uPE6QTyaxNyWmQnBSZ2nFKMmX8mRfeL2rGKSxhC72HW6N42mSgvwRz
xprv9uURCks92ms5ETYyB3CgpY6gV2C3bT4Ze86vF8ekPBS2UF43xR4ZzgmHTzDB6pjcVe9Hz9xCQt2QgYaL9xniuGAt9vTsocwi1oMXcBwes8V
xprv9xCDbKykuy5b4HYLj18eLDbiwujRngafYkuHacrAgWpn5q5F49qYJvpTAy146QpwrPz89KEDkSp2cxX7kgG8E6o6nHPjrSHW9HbYJ6H6Tav
xprv9yiNTpgmSu9BfbkcjVYCabLeF4mHGT6uxU2Xx57We4FrHMCZqbYGvqwUpN82kSTSFTjmnhzmQo7oSxv2jRWHur8qSvFEekR21jWYkTv2CWS
xprvA1n5PUpZwt9wwYp1DjMQw59pZyw9prETammVbiVoPL4AdVwjUzBebQ5AHWxyUbLTFXz1UgN3793eni2gk5zURBuY32qfYAxDBiX6yq3SC5W
xprvA3TYEnBkxVA5aX1PmNHKrnoVpWpZCUZoDXW8d3L7LcRZ1otCaTQT1je3HfwRXDu8ELvz8BFoKjyknMv51HwyTyBMDTdyEhHkS7bQqogwyH8

xpub661MyMwAqRbcFv958t696U37ttHRVc4B5VhsRvPNGagxNFAg3wZeehhhwhPwPF1q3Zd4Q3SsF2WHpGzg2sLiFfVKFeY1BufSxuxosiE1Skg
xpub68TmcGQ2s9RNSwdSH4jhBg3R342XzunR1M2X3X4MwWy1M3PCVxNpYV5mKHyi9D27W216hHUX4FuH74Afjzps3Hgz5F5HYxzWK77UeFJRSLH
xpub6BBZzqWekLdtGmcoq2fehMYTVwZvC9JWuyptP1FnErMkxdQPbh9nrj8w2G9mfw9HmzQRKu9cGkQYRZVU4K9wZ9gWB33ULwfRPeFrqFP1Fn8
xpub6ChisLDfHGhUt5q5qX5CwjHNo6bmfupmKgx8kTX8CPnqA9XiP8rXUeFxffCAAtWDaFX1o93hafMF2LJ6YJpTrYZH7JDH8XDtedE5XwAkfvD
xpub6EmRnzMTnFiFA2tUKktRJD6Z81meEJxJwzh6Q6uQwfb9WJGt2XVu9CPe8ps5GjNUWheuAHHehLJeP2jpqYyyctMZ4adF6HhyqcczidRLgn7
xpub6GSteHienriNo15rsPpLDvkENYf3bwHeakRjRRjitwxXtcDM7zihZXxX8vGEaZj9BoVweFwjmTqZ6pQHASmUQqiT7u4JFaDLnab16HhRgGe

c23932abcb20b452a9d53fb91ac645a01df8837edf7cab2fc548f40e7769e0b8
173337395ee8f54d2b641a35d31f9ee78be08d88d6628f630e631cc4ebe36e2a
619994c2e11c7360436ed2d16a491ee9fa462271c01f5117def0e7bc038e216f
7b9fd9d95fd34cae5dfef12be5d650e3ed8d50ab59a839f0db761a138a7d8851
41e111436ee0b922dc187125a6502cac4b65f0da6d6a49f58948051542aad5bd
768404f5c62b4b400d0524137204f3fe0577bfb90e9eab6b536634cf93bff294

03bc6fbbbb8afb24c57993ab60b470e3f15233e29de804dae5c0ff2cb8b970fc7d
037fb4527a4ace545f48afdcd926c6704188315217f01cd70002c618c78cde8341
0378c63bb6850732f1c5dc95301cc08cd1b38fb916259c5e147f6e6a4ba6a231e6
038861116bd52faa10c52d8158d186c85ca15a9d85174e8f9e39bd4aa58e2ebfd5
03bb8f653b178280dfa2c33e19c8f120f96f31a84b39e924433b0d8ce13efb4bd4
021985c7e0c4214441103e71955a2ba2015afe650f0dbc2ffff6c801773e412c4d

bc1qfc97ta46c4ffuvlw2nsms0fhlpdstkanh0lstn
bc1qcqewxpecw4xese2qcsmjhc55980ehgc3r30unv
bc1q3madwg8ke5cexrwdckqx63hyqkezumj99yrjqn
bc1q5lrr27n9wrzcrg85s4x0ukxmkp0wpv5s3kx68t
bc1q33jd92n56d3w7dc9pw8ya2f44phv3qmk9wjzg9
bc1q4yz4dyr29vjqxuwlrjk3chdyrd7xpkq8tlna6s

187fxfupn4SPrpnL6tmfvXYz89GY7NKHRP
1JXFhWbeTiZZ977T7XeqhN7uJfK17cb7pM
1E31Sw6o2eiW3HGast6BGZEVbaj6zwTcGn
1GJ7HqDrvdrCqteRoRQwvroNjW2sxAcdwA
1DoLP9zkGcL9hV2NMRybSowXY7gFqsLcGi
1GQhbKZ6gaVfWCdBwv6kXK3UnbhFwagwAq

xpub661MyMwAqRbcFv958t696U37ttHRVc4B5VhsRvPNGagxNFAg3wZeehhhwhPwPF1q3Zd4Q3SsF2WHpGzg2sLiFfVKFeY1BufSxuxosiE1Skg
xpub68TmcGQ2s9RNSwdSH4jhBg3R342XzunR1M2X3X4MwWy1M3PCVxNpYV5mKHyi9D27W216hHUX4FuH74Afjzps3Hgz5F5HYxzWK77UeFJRSLH
xpub6BBZzqWekLdtGmcoq2fehMYTVwZvC9JWuyptP1FnErMkxdQPbh9nrj8w2G9mfw9HmzQRKu9cGkQYRZVU4K9wZ9gWB33ULwfRPeFrqFP1Fn8
xpub6ChisLDfHGhUt5q5qX5CwjHNo6bmfupmKgx8kTX8CPnqA9XiP8rXUeFxffCAAtWDaFX1o93hafMF2LJ6YJpTrYZH7JDH8XDtedE5XwAkfvD
xpub6EmRnzMTnFiFA2tUKktRJD6Z81meEJxJwzh6Q6uQwfb9WJGt2XVu9CPe8ps5GjNUWheuAHHehLJeP2jpqYyyctMZ4adF6HhyqcczidRLgn7
xpub6GSteHienriNo15rsPpLDvkENYf3bwHeakRjRRjitwxXtcDM7zihZXxX8vGEaZj9BoVweFwjmTqZ6pQHASmUQqiT7u4JFaDLnab16HhRgGe

03bc6fbbbb8afb24c57993ab60b470e3f15233e29de804dae5c0ff2cb8b970fc7d
037fb4527a4ace545f48afdcd926c6704188315217f01cd70002c618c78cde8341
0378c63bb6850732f1c5dc95301cc08cd1b38fb916259c5e147f6e6a4ba6a231e6
038861116bd52faa10c52d8158d186c85ca15a9d85174e8f9e39bd4aa58e2ebfd5
03bb8f653b178280dfa2c33e19c8f120f96f31a84b39e924433b0d8ce13efb4bd4
021985c7e0c4214441103e71955a2ba2015afe650f0dbc2ffff6c801773e412c4d

bc1qfc97ta46c4ffuvlw2nsms0fhlpdstkanh0lstn
bc1qcqewxpecw4xese2qcsmjhc55980ehgc3r30unv
bc1q3madwg8ke5cexrwdckqx63hyqkezumj99yrjqn
bc1q5lrr27n9wrzcrg85s4x0ukxmkp0wpv5s3kx68t
bc1q33jd92n56d3w7dc9pw8ya2f44phv3qmk9wjzg9
bc1q4yz4dyr29vjqxuwlrjk3chdyrd7xpkq8tlna6s

187fxfupn4SPrpnL6tmfvXYz89GY7NKHRP
1JXFhWbeTiZZ977T7XeqhN7uJfK17cb7pM
1E31Sw6o2eiW3HGast6BGZEVbaj6zwTcGn
1GJ7HqDrvdrCqteRoRQwvroNjW2sxAcdwA
1DoLP9zkGcL9hV2NMRybSowXY7gFqsLcGi
1GQhbKZ6gaVfWCdBwv6kXK3UnbhFwagwAq

There are examples of these in the corresponding unit test cases. Hope this helps.

There are examples of these in the corresponding unit test cases. Hope this helps.

Thank you very much for your example, everything compiled perfectly, but there is one thing, addresses do not match the mnemonic phrase in the example, maybe I don’t understand something? I checked with https://iancoleman.io/bip39/
BIP44
bunker churn kangaroo melt bleak chalk vacant alert reason exit forward language

m/44'/0'/0'/0/0 16FdACJs8hU8yZYLPP85KX5WH5wqHmsh4r 02ab0375a86623124a63483f439fe46026ff2ef0165687be7d06618450c37b54f4 L1aceRm4wqdEfFW3dCtLJhFEjRu7cmBHXRLQ4rkbkq144aw1cYky

m/44'/0'/0'/0/1 1P8AaRKzWSTVaTGZA46Vh9J7vzp6r4q4eT 0221ce5e67d16ffc619bfbfa8f0f392c3c6e09ce7358ed1cb63b6de695336a5b36 L5TBqmGv6P4ecBCH6P9ZaC888FLPwzGeUqQxRey1pKCND5GpMTLj

m/44'/0'/0'/0/2 1Mc6Nig43ppeVn1p7PeVkY2gaDNx8CovuF 02499e0fcc1d8382ed2ddbe71211feddcfd18f5d518a6215581e179fe34659e246 L1BXqL6HDZ7uFdcTLFxTrHJjAPa4aYdnVMa9BmFgwQZhQ2JBtpA3

m/44'/0'/0'/0/3 192vVrJzKcPGXf2oDDecC1G2XFjmBVf5sc 026f8a4814af6e4e8175303c48d2574b6b472d114e1114a1a4832cc65fa2b3c193 L5TtRy6agRVVLxrw98R4B4ALgWwMgyJL8W4kphNk5Kehbr3aC7CW

These are HD derivation paths, the example shows how to navigate these. The path m/44'/0'/0'/0/3 represents a derivation of: 44 hardened, 0 hardened, 0 hardened, 0, 3. There is presently no BIP44 helper to convert the path and master key to a derived key, but it is trivially walked once you have a parsed path.

This might also help, though it's for v3 it's still very close. https://github.com/libbitcoin/libbitcoin-system/wiki/Addresses-and-HD-Wallets

The full path in the sample code above is m/0'/1/2'/2/1000000000 and the output matches that on https://iancoleman.io/bip39/

image

I eliminated some unnecessary copies, added WIF conversions/output, and added your first derivation as the last output of each section. You can see that this matches your intended output.

#include <cstdlib>
#include <bitcoin/system.hpp>

BC_USE_LIBBITCOIN_MAIN

int bc::system::main(int, char*[])
{
    using namespace bc;
    using namespace bc::system;
    using namespace bc::system::wallet;

    set_utf8_stdio();

    // Recovery seed derivation.
    const auto mnemonic_phrase = "bunker churn kangaroo melt bleak chalk vacant alert reason exit forward language";
    const mnemonic recovery_seed(mnemonic_phrase);
    if (!recovery_seed)
    {
        system::cerr << "Invalid mnemonic phrase." << std::endl;
        return EXIT_FAILURE;
    }

    const auto compressed = true;
    const auto context = ctx::btc::main::p2pkh;

    // Account private key derivation.
    const auto passphrase = "";
    const auto account_private_key = recovery_seed.to_key(passphrase, context);
    if (!account_private_key)
    {
        system::cerr << "Unexpected error." << std::endl;
        return EXIT_FAILURE;
    }

    // private
    // ------------------------------------------------------------------------

    // Some hd private key derivations.
    const auto& m = account_private_key;
    const auto m0h = m.derive_private(hd_first_hardened_key);
    const auto m0h1 = m0h.derive_private(1);
    const auto m0h12h = m0h1.derive_private(2 + hd_first_hardened_key);
    const auto m0h12h2 = m0h12h.derive_private(2);
    const auto m0h12h2x = m0h12h2.derive_private(1000000000);
    const auto m44h0h0h00 = m
        .derive_private(44 + hd_first_hardened_key)
        .derive_private(0 + hd_first_hardened_key)
        .derive_private(0 + hd_first_hardened_key)
        .derive_private(0)
        .derive_private(0);

    system::cout << m << std::endl;
    system::cout << m0h << std::endl;
    system::cout << m0h1 << std::endl;
    system::cout << m0h12h << std::endl;
    system::cout << m0h12h2 << std::endl;
    system::cout << m0h12h2x << std::endl;
    system::cout << m44h0h0h00 << std::endl;
    system::cout << std::endl;

    // Corresponding hd public key derivations.
    const auto hd_p = m.to_public();
    const auto hd0h_p = m0h.to_public();
    const auto hd0h1_p = m0h1.to_public();
    const auto hd0h12h_p = m0h12h.to_public();
    const auto hd0h12h2_p = m0h12h2.to_public();
    const auto hd0h12h2x_p = m0h12h2x.to_public();
    const auto m44h0h0h00_p = m44h0h0h00.to_public();

    system::cout << hd_p << std::endl;
    system::cout << hd0h_p << std::endl;
    system::cout << hd0h1_p << std::endl;
    system::cout << hd0h12h_p << std::endl;
    system::cout << hd0h12h2_p  << std::endl;
    system::cout << hd0h12h2x_p << std::endl;
    system::cout << m44h0h0h00_p << std::endl;
    system::cout << std::endl;

    // Corresponding ec private key derivations.
    const auto& ec = m.secret();
    const auto& ec0h = m0h.secret();
    const auto& ec0h1 = m0h1.secret();
    const auto& ec0h12h = m0h12h.secret();
    const auto& ec0h12h2 = m0h12h2.secret();
    const auto& ec0h12h2x = m0h12h2x.secret();
    const auto& ec44h0h0h00 = m44h0h0h00.secret();

    system::cout << encode_base16(ec) << std::endl;
    system::cout << encode_base16(ec0h) << std::endl;
    system::cout << encode_base16(ec0h1) << std::endl;
    system::cout << encode_base16(ec0h12h) << std::endl;
    system::cout << encode_base16(ec0h12h2) << std::endl;
    system::cout << encode_base16(ec0h12h2x) << std::endl;
    system::cout << encode_base16(ec44h0h0h00) << std::endl;
    system::cout << std::endl;

    // Corresponding WIF (ec_private) key conversions.
    const ec_private wif{ ec };
    const ec_private wif0h{ ec0h };
    const ec_private wif0h1{ ec0h1 };
    const ec_private wif0h12h{ ec0h12h };
    const ec_private wif0h12h2{ ec0h12h2 };
    const ec_private wif0h12h2x{ ec0h12h2x };
    const ec_private wif44h0h0h00{ ec44h0h0h00 };

    system::cout << wif << std::endl;
    system::cout << wif0h << std::endl;
    system::cout << wif0h1 << std::endl;
    system::cout << wif0h12h << std::endl;
    system::cout << wif0h12h2 << std::endl;
    system::cout << wif0h12h2x << std::endl;
    system::cout << wif44h0h0h00 << std::endl;
    system::cout << std::endl;

    // Corresponding ec public keys from private.
    const auto& ec_p = m.point();
    const auto& ec0h_p = m0h.point();
    const auto& ec0h1_p = m0h1.point();
    const auto& ec0h12h_p = m0h12h.point();
    const auto& ec0h12h2_p = m0h12h2.point();
    const auto& ec0h12h2x_p = m0h12h2x.point();
    const auto& ec44h0h0h00_p = m44h0h0h00.point();

    system::cout << encode_base16(ec_p) << std::endl;
    system::cout << encode_base16(ec0h_p) << std::endl;
    system::cout << encode_base16(ec0h1_p) << std::endl;
    system::cout << encode_base16(ec0h12h_p) << std::endl;
    system::cout << encode_base16(ec0h12h2_p) << std::endl;
    system::cout << encode_base16(ec0h12h2x_p) << std::endl;
    system::cout << encode_base16(ec44h0h0h00_p) << std::endl;
    system::cout << std::endl;

    // Corresponding witness address derivations from WIF (ec private) keys.
    const witness_address witness{ wif, context.witness_prefix };
    const witness_address witness0h{ wif0h, context.witness_prefix };
    const witness_address witness0h1{ wif0h1, context.witness_prefix };
    const witness_address witness0h12h{ wif0h12h, context.witness_prefix };
    const witness_address witness0h12h2{ wif0h12h2, context.witness_prefix };
    const witness_address witness0h12h2x{ wif0h12h2x, context.witness_prefix };
    const witness_address witness44h0h0h00{ wif44h0h0h00, context.witness_prefix };

    system::cout << witness << std::endl;
    system::cout << witness0h << std::endl;
    system::cout << witness0h1 << std::endl;
    system::cout << witness0h12h << std::endl;
    system::cout << witness0h12h2 << std::endl;
    system::cout << witness0h12h2x << std::endl;
    system::cout << witness44h0h0h00 << std::endl;
    system::cout << std::endl;

    // Corresponding payment address derivations from ec private keys.
    // ec_private allows for passage of context (prevents context loss in some situations).
    const payment_address address{ ec_private{ ec, context.versions(), compressed } };
    const payment_address address0h{ ec_private{ ec0h, context.versions(), compressed } };
    const payment_address address0h1{ ec_private{ ec0h1, context.versions(), compressed } };
    const payment_address address0h12h{ ec_private{ ec0h12h, context.versions(), compressed } };
    const payment_address address0h12h2{ ec_private{ ec0h12h2, context.versions(), compressed } };
    const payment_address address0h12h2x{ ec_private{ ec0h12h2x, context.versions(), compressed } };
    const payment_address address44h0h0h00{ ec_private{ ec44h0h0h00, context.versions(), compressed } };

    system::cout << address << std::endl;
    system::cout << address0h << std::endl;
    system::cout << address0h1 << std::endl;
    system::cout << address0h12h << std::endl;
    system::cout << address0h12h2 << std::endl;
    system::cout << address0h12h2x << std::endl;
    system::cout << address44h0h0h00 << std::endl;
    system::cout << std::endl;

    // public
    // ------------------------------------------------------------------------

    // Corresponding hd public key derivations .
    const auto m_pub = m.to_public();
    const auto m0h_pub = m.derive_public(hd_first_hardened_key);
    const auto m0h1_pub = m0h.derive_public(1);
    const auto m0h12h_pub = m0h1.derive_public(2 + hd_first_hardened_key);
    const auto m0h12h2_pub = m0h12h.derive_public(2);
    const auto m0h12h2x_pub = m0h12h2.derive_public(1000000000);
    const auto m44h0h0h00_pub = m
        .derive_private(44 + hd_first_hardened_key) // hardened requires private derivation
        .derive_private(0 + hd_first_hardened_key)  // hardened requires private derivation
        .derive_public(0 + hd_first_hardened_key)
        .derive_public(0)
        .derive_public(0);

    system::cout << m_pub << std::endl;
    system::cout << m0h_pub << std::endl;
    system::cout << m0h1_pub << std::endl;
    system::cout << m0h12h_pub << std::endl;
    system::cout << m0h12h2_pub << std::endl;
    system::cout << m0h12h2x_pub << std::endl;
    system::cout << m44h0h0h00_pub << std::endl;
    system::cout << std::endl;

    // Corresponding ec public key derivations.
    const auto& ec_pub = m_pub.point();
    const auto& ec0h_pub = m0h_pub.point();
    const auto& ec0h1_pub = m0h1_pub.point();
    const auto& ec0h12h_pub = m0h12h_pub.point();
    const auto& ec0h12h2_pub = m0h12h2_pub.point();
    const auto& ec0h12h2x_pub = m0h12h2x_pub.point();
    const auto& ec44h0h0h00_pub = m44h0h0h00_pub.point();

    system::cout << encode_base16(ec_pub) << std::endl;
    system::cout << encode_base16(ec0h_pub) << std::endl;
    system::cout << encode_base16(ec0h1_pub) << std::endl;
    system::cout << encode_base16(ec0h12h_pub) << std::endl;
    system::cout << encode_base16(ec0h12h2_pub) << std::endl;
    system::cout << encode_base16(ec0h12h2x_pub) << std::endl;
    system::cout << encode_base16(ec44h0h0h00_pub) << std::endl;
    system::cout << std::endl;

    // Corresponding witness address derivations from ec public keys.
    // ec_public{} unnecessary, as ec_compressed is unambiguous 33 byte vector.
    // ec_public does not provide for passage of context (can always be passed locally).
    const witness_address witness_pub{ ec_public{ ec_pub, compressed }, context.witness_prefix };
    const witness_address witness0h_pub{ ec_public{ ec0h_pub, compressed }, context.witness_prefix };
    const witness_address witness0h1_pub{ ec_public{ ec0h1_pub, compressed }, context.witness_prefix };
    const witness_address witness0h12h_pub{ ec_public{ ec0h12h_pub, compressed }, context.witness_prefix };
    const witness_address witness0h12h2_pub{ ec_public{ ec0h12h2_pub, compressed }, context.witness_prefix };
    const witness_address witness0h12h2x_pub{ ec_public{ ec0h12h2x_pub, compressed }, context.witness_prefix };
    const witness_address witness44h0h0h00_pub{ ec_public{ ec44h0h0h00_pub, compressed }, context.witness_prefix };

    system::cout << witness_pub << std::endl;
    system::cout << witness0h_pub << std::endl;
    system::cout << witness0h1_pub << std::endl;
    system::cout << witness0h12h_pub << std::endl;
    system::cout << witness0h12h2_pub << std::endl;
    system::cout << witness0h12h2x_pub << std::endl;
    system::cout << witness44h0h0h00_pub << std::endl;
    system::cout << std::endl;

    // Corresponding payment address derivations from ec public keys.
    const payment_address address_pub{ ec_pub, context.address_version };
    const payment_address address0h_pub{ ec0h_pub, context.address_version };
    const payment_address address0h1_pub{ ec0h1_pub, context.address_version };
    const payment_address address0h12h_pub{ ec0h12h_pub, context.address_version };
    const payment_address address0h12h2_pub{ ec0h12h2_pub, context.address_version };
    const payment_address address0h12h2x_pub{ ec0h12h2x_pub, context.address_version };
    const payment_address address44h0h0h00_pub{ ec44h0h0h00_pub, context.address_version };

    system::cout << address_pub << std::endl;
    system::cout << address0h_pub << std::endl;
    system::cout << address0h1_pub << std::endl;
    system::cout << address0h12h_pub << std::endl;
    system::cout << address0h12h2_pub << std::endl;
    system::cout << address0h12h2x_pub << std::endl;
    system::cout << address44h0h0h00_pub << std::endl;
    system::cout << std::endl;

    return EXIT_SUCCESS;
}
xprv9s21ZrQH143K3S4c2rZ8jL6PLrSw69LKiGnGdXykiF9yVSqXWQFQ6uPE6QTyaxNyWmQnBSZ2nFKMmX8mRfeL2rGKSxhC72HW6N42mSgvwRz
xprv9uURCks92ms5ETYyB3CgpY6gV2C3bT4Ze86vF8ekPBS2UF43xR4ZzgmHTzDB6pjcVe9Hz9xCQt2QgYaL9xniuGAt9vTsocwi1oMXcBwes8V
xprv9xCDbKykuy5b4HYLj18eLDbiwujRngafYkuHacrAgWpn5q5F49qYJvpTAy146QpwrPz89KEDkSp2cxX7kgG8E6o6nHPjrSHW9HbYJ6H6Tav
xprv9yiNTpgmSu9BfbkcjVYCabLeF4mHGT6uxU2Xx57We4FrHMCZqbYGvqwUpN82kSTSFTjmnhzmQo7oSxv2jRWHur8qSvFEekR21jWYkTv2CWS
xprvA1n5PUpZwt9wwYp1DjMQw59pZyw9prETammVbiVoPL4AdVwjUzBebQ5AHWxyUbLTFXz1UgN3793eni2gk5zURBuY32qfYAxDBiX6yq3SC5W
xprvA3TYEnBkxVA5aX1PmNHKrnoVpWpZCUZoDXW8d3L7LcRZ1otCaTQT1je3HfwRXDu8ELvz8BFoKjyknMv51HwyTyBMDTdyEhHkS7bQqogwyH8
xprvA2tpJb7dbomdXh6ZrBA5fsKQqJszjkBvqPFFiWcM82xnPghGuhcfncpgGevd2oYAJV4YRrbz1KmfyRwKYjpkH2Wb3mYgBDnNqidFA6njN1b

xpub661MyMwAqRbcFv958t696U37ttHRVc4B5VhsRvPNGagxNFAg3wZeehhhwhPwPF1q3Zd4Q3SsF2WHpGzg2sLiFfVKFeY1BufSxuxosiE1Skg
xpub68TmcGQ2s9RNSwdSH4jhBg3R342XzunR1M2X3X4MwWy1M3PCVxNpYV5mKHyi9D27W216hHUX4FuH74Afjzps3Hgz5F5HYxzWK77UeFJRSLH
xpub6BBZzqWekLdtGmcoq2fehMYTVwZvC9JWuyptP1FnErMkxdQPbh9nrj8w2G9mfw9HmzQRKu9cGkQYRZVU4K9wZ9gWB33ULwfRPeFrqFP1Fn8
xpub6ChisLDfHGhUt5q5qX5CwjHNo6bmfupmKgx8kTX8CPnqA9XiP8rXUeFxffCAAtWDaFX1o93hafMF2LJ6YJpTrYZH7JDH8XDtedE5XwAkfvD
xpub6EmRnzMTnFiFA2tUKktRJD6Z81meEJxJwzh6Q6uQwfb9WJGt2XVu9CPe8ps5GjNUWheuAHHehLJeP2jpqYyyctMZ4adF6HhyqcczidRLgn7
xpub6GSteHienriNo15rsPpLDvkENYf3bwHeakRjRRjitwxXtcDM7zihZXxX8vGEaZj9BoVweFwjmTqZ6pQHASmUQqiT7u4JFaDLnab16HhRgGe
xpub6FtAi6eXSBKvkBB2xCh631G9PLiV9CunCcArWu1xgNVmGV2RTEvvLR9A7vGQTi7GV8U7MgDuCxrsMd7nWc86u4h4wfCbtLajMusEESeQc5H

c23932abcb20b452a9d53fb91ac645a01df8837edf7cab2fc548f40e7769e0b8
173337395ee8f54d2b641a35d31f9ee78be08d88d6628f630e631cc4ebe36e2a
619994c2e11c7360436ed2d16a491ee9fa462271c01f5117def0e7bc038e216f
7b9fd9d95fd34cae5dfef12be5d650e3ed8d50ab59a839f0db761a138a7d8851
41e111436ee0b922dc187125a6502cac4b65f0da6d6a49f58948051542aad5bd
768404f5c62b4b400d0524137204f3fe0577bfb90e9eab6b536634cf93bff294
821a894fdd3a85366c04b0dc9990a246957ee7e4e3ed29b046efa493b36b648a

L3jFnvgkrxqTnVBDtGQ3Uxr378RsnNXcbbzTGWJts6Uc1MwRBYsq
KwzorxC64BjnDEKcBEz1SL8m1JBvHSogtH8gxsucxkjZPVyqhowt
KzVS2nxBEsDD76hDfMXViTcd4xcck1e8VsRDchDKiJQfeW1PJHkX
L1N29Y13dfoMJXYUuhchbrRkYswoS7Tu9WKUbsWJas5zQwga42Ti
KyRmhAqaizSHoYmvB6G2zsMjCz4qfsZTAH8byimdb2KTuDB9SJVj
L1C6AjSBWr7Ps2Vvk6KxT5XkbH9H3d4NCNW379rsyRvWA4CqFwYM
L1aceRm4wqdEfFW3dCtLJhFEjRu7cmBHXRLQ4rkbkq144aw1cYky

03bc6fbbbb8afb24c57993ab60b470e3f15233e29de804dae5c0ff2cb8b970fc7d
037fb4527a4ace545f48afdcd926c6704188315217f01cd70002c618c78cde8341
0378c63bb6850732f1c5dc95301cc08cd1b38fb916259c5e147f6e6a4ba6a231e6
038861116bd52faa10c52d8158d186c85ca15a9d85174e8f9e39bd4aa58e2ebfd5
03bb8f653b178280dfa2c33e19c8f120f96f31a84b39e924433b0d8ce13efb4bd4
021985c7e0c4214441103e71955a2ba2015afe650f0dbc2ffff6c801773e412c4d
02ab0375a86623124a63483f439fe46026ff2ef0165687be7d06618450c37b54f4

bc1qfc97ta46c4ffuvlw2nsms0fhlpdstkanh0lstn
bc1qcqewxpecw4xese2qcsmjhc55980ehgc3r30unv
bc1q3madwg8ke5cexrwdckqx63hyqkezumj99yrjqn
bc1q5lrr27n9wrzcrg85s4x0ukxmkp0wpv5s3kx68t
bc1q33jd92n56d3w7dc9pw8ya2f44phv3qmk9wjzg9
bc1q4yz4dyr29vjqxuwlrjk3chdyrd7xpkq8tlna6s
bc1q8xw295tt0pjdsm30act84f2xqzhweluur4rvt3

187fxfupn4SPrpnL6tmfvXYz89GY7NKHRP
1JXFhWbeTiZZ977T7XeqhN7uJfK17cb7pM
1E31Sw6o2eiW3HGast6BGZEVbaj6zwTcGn
1GJ7HqDrvdrCqteRoRQwvroNjW2sxAcdwA
1DoLP9zkGcL9hV2NMRybSowXY7gFqsLcGi
1GQhbKZ6gaVfWCdBwv6kXK3UnbhFwagwAq
16FdACJs8hU8yZYLPP85KX5WH5wqHmsh4r

xpub661MyMwAqRbcFv958t696U37ttHRVc4B5VhsRvPNGagxNFAg3wZeehhhwhPwPF1q3Zd4Q3SsF2WHpGzg2sLiFfVKFeY1BufSxuxosiE1Skg
xpub68TmcGQ2s9RNSwdSH4jhBg3R342XzunR1M2X3X4MwWy1M3PCVxNpYV5mKHyi9D27W216hHUX4FuH74Afjzps3Hgz5F5HYxzWK77UeFJRSLH
xpub6BBZzqWekLdtGmcoq2fehMYTVwZvC9JWuyptP1FnErMkxdQPbh9nrj8w2G9mfw9HmzQRKu9cGkQYRZVU4K9wZ9gWB33ULwfRPeFrqFP1Fn8
xpub6ChisLDfHGhUt5q5qX5CwjHNo6bmfupmKgx8kTX8CPnqA9XiP8rXUeFxffCAAtWDaFX1o93hafMF2LJ6YJpTrYZH7JDH8XDtedE5XwAkfvD
xpub6EmRnzMTnFiFA2tUKktRJD6Z81meEJxJwzh6Q6uQwfb9WJGt2XVu9CPe8ps5GjNUWheuAHHehLJeP2jpqYyyctMZ4adF6HhyqcczidRLgn7
xpub6GSteHienriNo15rsPpLDvkENYf3bwHeakRjRRjitwxXtcDM7zihZXxX8vGEaZj9BoVweFwjmTqZ6pQHASmUQqiT7u4JFaDLnab16HhRgGe
xpub6FtAi6eXSBKvkBB2xCh631G9PLiV9CunCcArWu1xgNVmGV2RTEvvLR9A7vGQTi7GV8U7MgDuCxrsMd7nWc86u4h4wfCbtLajMusEESeQc5H

03bc6fbbbb8afb24c57993ab60b470e3f15233e29de804dae5c0ff2cb8b970fc7d
037fb4527a4ace545f48afdcd926c6704188315217f01cd70002c618c78cde8341
0378c63bb6850732f1c5dc95301cc08cd1b38fb916259c5e147f6e6a4ba6a231e6
038861116bd52faa10c52d8158d186c85ca15a9d85174e8f9e39bd4aa58e2ebfd5
03bb8f653b178280dfa2c33e19c8f120f96f31a84b39e924433b0d8ce13efb4bd4
021985c7e0c4214441103e71955a2ba2015afe650f0dbc2ffff6c801773e412c4d
02ab0375a86623124a63483f439fe46026ff2ef0165687be7d06618450c37b54f4

bc1qfc97ta46c4ffuvlw2nsms0fhlpdstkanh0lstn
bc1qcqewxpecw4xese2qcsmjhc55980ehgc3r30unv
bc1q3madwg8ke5cexrwdckqx63hyqkezumj99yrjqn
bc1q5lrr27n9wrzcrg85s4x0ukxmkp0wpv5s3kx68t
bc1q33jd92n56d3w7dc9pw8ya2f44phv3qmk9wjzg9
bc1q4yz4dyr29vjqxuwlrjk3chdyrd7xpkq8tlna6s
bc1q8xw295tt0pjdsm30act84f2xqzhweluur4rvt3

187fxfupn4SPrpnL6tmfvXYz89GY7NKHRP
1JXFhWbeTiZZ977T7XeqhN7uJfK17cb7pM
1E31Sw6o2eiW3HGast6BGZEVbaj6zwTcGn
1GJ7HqDrvdrCqteRoRQwvroNjW2sxAcdwA
1DoLP9zkGcL9hV2NMRybSowXY7gFqsLcGi
1GQhbKZ6gaVfWCdBwv6kXK3UnbhFwagwAq
16FdACJs8hU8yZYLPP85KX5WH5wqHmsh4r

Thank you very much for adding the derivation example BIP44! Its matches the example of iancoleman.io Now I understand how derivation paths work.