HD Wallets

Hierarchical Deterministic Wallets (HD Wallets)

A Hierarchical Deterministic (HD) wallet is the term used to describe a wallet which uses a single seed to derive public and private keys. Approach to create HD Wallets was defined under BIP-32. Not using BIP-32 means that you need to create a separate seed every time you created a normal receiving address. Nowadays all popular wallets use this approach to generate receiving addresses.

Bitcoin has different types of addresses: P2PKH, P2WSH (wrapped segwit), P2WPKH (native segwit). That is why additional BIPs were created to define standards what kind of derivation paths should be used to generate specific address types.

Read BIP-32.

BIP-44: derive P2PKH addresses

According to BIP 44, wallets which generate P2PKH addresses should use a derivation path beginning with m/44'/. This means the first address generated by a mainnet Bitcoin wallet will have a derivation path of m/44'/0'/0'/0/0.

In addition, all such wallets which use mainnet Bitcoin should use the xpub or xprv prefix for extended public and private keys respectively. Extended keys using the BIP 44 standard are thus called xpubs and xprvs respectively. If the wallet is a testnet wallet, tpub and tprv prefixes are used instead.

Read BIP-44.

 1import hashlib
 2from bip_utils import Bip44Changes, Bip44Coins, Bip44
 3
 4# in previous examples we use sha256 but here we use sha512 due to the min seed length requirement
 5# that bip_utils enforces
 6h = hashlib.sha512(b'very bad brainwallet').digest()
 7
 8bip44 = Bip44.FromSeed(h, Bip44Coins.BITCOIN)
 9bip44_acc = bip44.Purpose().Coin().Account(0)
10
11for index in range(5):
12    # CHAIN_EXT means it's an external address (receiving address). CHAIN_INT constant indicates an
13    # internal address (used for change)
14    bip44_addr = bip44_acc.Change(Bip44Changes.CHAIN_EXT).AddressIndex(index)
15    address = bip44_addr.PublicKey().ToAddress()
16    print(f"Address {index}: {address}")
17
18# Outputs:
19# Address 0: 1MAjFH9X7C94QGFGcFgegjTko8n1vUCu4A
20# Address 1: 18mEkkjvTPty8uH1LX3hnnZTstS7pbwqrV
21# Address 2: 1PLRhwriNYopoNDsLrKRpiHTfHFdNAkipx
22# Address 3: 19ZyE5du3WeRyWoPuKDw3Lq4e9EuDDeQgK
23# Address 4: 1GXEGfsFwJUAQn1XYV3EYwgJ4hMiKTo8Yg

BIP-49: derive wrapped SegWit addresses

According to BIP 49, wallets which generate wrapped SegWit addresses should use a derivation path beginning with m/49'/. This means the first address generated by a mainnet Bitcoin wallet will have a derivation path of m/49'/0'/0'/0/0.

In addition, all such wallets which use mainnet Bitcoin should use ypub or yprv prefixes for extended public and private keys respectively. Extended keys following the BIP 49 standard are thus called ypubs and yprvs respectively. If the wallet is a testnet wallet, upub and uprv prefixes are used instead.

Read BIP-49.

 1import hashlib
 2from bip_utils import Bip44Changes, Bip49Coins, Bip49
 3
 4# in previous examples we use sha256 but here we use sha512 due to the min seed length requirement
 5# that bip_utils enforces
 6h = hashlib.sha512(b'very bad brainwallet').digest()
 7
 8bip49 = Bip49.FromSeed(h, Bip49Coins.BITCOIN)
 9bip49_acc = bip49.Purpose().Coin().Account(0)
10
11for index in range(5):
12    # CHAIN_EXT means it's an external address (receiving address). CHAIN_INT constant indicates an
13    # internal address (used for change)
14    bip49_addr = bip49_acc.Change(Bip44Changes.CHAIN_EXT).AddressIndex(index)
15    address = bip49_addr.PublicKey().ToAddress()
16    print(f"Address {index}: {address}")
17
18# Outputs:
19# Address 0: 3LjNLKmKAj6KzWz1XkSmhm7LGxaC58sm5Y
20# Address 1: 37yj44W3Y8MjxDZgGmm5bosvUUp3e7oLov
21# Address 2: 32AyxeR6TdCjfSJhoT9jk8ppDnmUSj9KVA
22# Address 3: 3K2TxNH1Xk2oehm3xu7jWZBmJvA6o6ZBFg
23# Address 4: 3K7vkaW2NMDn813mca9Fikd4k6F3jQ1h1t

BIP-84: derive native SegWit addresses

According to BIP 84, wallets which generate native SegWit addresses should use a derivation path beginning with m/84'/. This means the first address generated by a mainnet Bitcoin wallet will have a derivation path of m/84'/0'/0'/0/0.

In addition, BIP 84 states that all wallets which use native SegWit on mainnet should use zpub or zprv prefixes for extended public and private keys respectively. Extended keys following the BIP 84 standard are thus called zpubs and zprvs respectively. If the wallet is a testnet wallet, vpub and vprv prefixes are used instead.

Read BIP-84.

 1import hashlib
 2from bip_utils import Bip44Changes, Bip84Coins, Bip84
 3
 4# in previous examples we use sha256 but here we use sha512 due to the min seed length requirement
 5# that bip_utils enforces
 6h = hashlib.sha512(b'very bad brainwallet').digest()
 7
 8bip84 = Bip84.FromSeed(h, Bip84Coins.BITCOIN)
 9bip84_acc = bip84.Purpose().Coin().Account(0)
10
11for index in range(5):
12    # CHAIN_EXT means it's an external address (receiving address). CHAIN_INT constant indicates an
13    # internal address (used for change)
14    bip84_addr = bip84_acc.Change(Bip44Changes.CHAIN_EXT).AddressIndex(index)
15    address = bip84_addr.PublicKey().ToAddress()
16    print(f"Address {index}: {address}")
17
18# Outputs:
19# Address 0: bc1q3lusppjzw8jqxwlnq8nu2ua8spnzka3gzh52ha
20# Address 1: bc1q3et59vv6qa8ulr8a0vzjykgh9jkvcvltvd5k7p
21# Address 2: bc1qge23sdr36vqn3sca8760y4sy3vsjxe2wuju263
22# Address 3: bc1qz3rnqmueq8jej33a9zh68zm6n58ngfajgrr0f5
23# Address 4: bc1qtsnmsfhd4jkzeml3u5wjjmfe0587vntg6nc6gx

BIP-86: derive single key P2TR outputs

TODO

More info.


Sources: