Derivation Paths
How HD wallets derive keys.
The cool thing about extended keys is that they can derive children, and these child keys can derive more children, and so on. This allows you to create a tree of extended keys, with each key having its own unique derivation path from the master key.
You can derive keys in any way you want. But to help with compatibility between wallets, we have a set structure for how we derive keys for use in a hierarchical deterministic wallet.
The most common derivation paths used are:
- BIP 44:
m/44'/0'/0'
(for1addresses
) - BIP 49:
m/49'/0'/0'
(for3addresses
) - BIP 84:
m/84'/0'/0'
(forbc1addresses
)
1. Notation
First of all, we have a basic notation for describing the derivation path for a specific extended key. For example:
m/0/1/3'
The slashes /
indicate a new level in the tree (a new child). The numbers (e.g. 0
) indicate the child number from the parent.
The only other thing you need to know about the notation is that a '
or h
indicates a hardened child extended private key
(which means that the public keys it creates cannot be derived by the extended public key
):
0
- Normal Child (index0
)0'
- Hardened Child (index2147483648
)
This '
just saves us having to write the full index numbers for hardened children. For example, 3'
means index 2147483651
.
Tip: You can derive up to 4294967296
children from a single extended key. The first half are for normal children, and the second half are for hardened children.
Note: Deriving hardened children is the default. Normal children are only derived when it would be useful to have a corresponding extended public key for deriving the same public keys.
2. Wallet Structure
To help with consistency between wallets, BIP 44 introduced the following structure:
m / purpose' / coin_type' / account' / change / index

m
: Master
The master key (created from a seed).
m/44'
: Purpose (hardened)
This specifies the upcoming wallet structure.
There are three schemes currently being used by wallets:
Tip: The number reflects the number of the BIP. New schemes can use different BIP numbers.
m/44'/0'
: Coin Type (hardened)
The cryptocurrency the keys will be used for.
Different cryptocurrencies can use the same private keys
and public keys
derived from a seed. So instead of having separate seeds for different currencies (or using the same public keys on different chains), we can use the same seed with different derivation paths instead.
0' = Bitcoin
1' = Bitcoin (Testnet)
2' = Litecoin
3' = Dogecoin
...
Full list: https://github.com/satoshilabs/slips/blob/master/slip-0044.md
This is useful in hardware wallets, where you can have a single seed and use it for holding a variety of different coins.
m/44'/0'/0'
: Account (hardened)
This allows you to create separate accounts for funds. The default account is 0'
.
For example, you can use the same seed yet still create separate “pots” for receiving payments. The coins in these separate accounts will never be mixed.
Tip: You could obviously create many different accounts at various indexes at this level. But to keep recovery simple, a wallet should create accounts in sequential order, and not create a new account if a previous one has not been used.
m/44'/0'/0'/0
: Change
The keys and addresses we use are separated in to “Receiving” and “Change”.
- Receiving =
0
- Addresses that we will give out to people for receiving payments. - Change =
1
- Addresses we use for sending change back to ourselves when we make transactions.
This means you will always be able to identify the coins that arrived from external payments.
m/44'/0'/0'/0/0
: Index
These are the extended keys that you use for their private keys
and public keys
for actual usage in the wallet.
So as you can see, the first few levels in the path are just used to structure the hierarchical deterministic wallet in a practical way.
The actual keys used for addresses are in lowest level of the tree.
3. Derivation Paths
Here are some actual derivation paths used by wallets.
BIP 32: m/0'/0/0
(deprecated)
This is the original derivation path specification in BIP 32.
It just used the first child for accounts, and the next two children below for separating external and internal addresses. The children of these are used for their actual private keys
and public keys
to create addresses
.
m / account' / external / index
This is a nice and simple derivation path, but it doesn’t allow for the option of creating alternative derivation path schemes.
This is where BIP 44, BIP 49, and BIP 84 come in.
BIP 44: m/44'/0'/0'/0/0
BIP 44 builds upon the original BIP 32 scheme to include a purpose1 (which is like a version number to identify the upcoming scheme), as well as a coin type so that the same seed can be used to generate keys for different cryptocurrencies.
The public keys
are encoded to 1addresses
(P2PKH).
BIP 49: m/49'/0'/0'/0/0
BIP 49 uses the same structure as BIP 44, but is used to indicate that the public keys
should be encoded in to 3addresses
(P2WPKH-P2SH).
BIP 49 Serialization
The extended keys in the BIP 49 derivation path use the version bytes 049d7878
“yprv” or 049d7cb2
“ypub” during serialization. This allows you to identify an extended key when it is part of the BIP 49 scheme.
For example:
yprvABrGsX5C9jant45o1Au7iHH54A8GXQH9SGhK5vkYKPUBDYsFy6KNUWX24moUE6KxoCh2qtZ8UpLaDWQiqt4aPdvvgjszQ4VrbLpfp5patGg
BIP 84: m/84'/0'/0'/0/0
BIP 84 uses the same structure as BIP 44, but is used to indicate that the public keys
should be encoded in to bc1addresses
(P2WPKH).
BIP 84 Serialization
The extended keys in the BIP 84 derivation path use the version bytes 04b2430c
“zprv” or 04b24746
“zpub” during serialization. This allows you to identify an extended key when it is part of the BIP 84 scheme.
For example:
zprvAWgYBBk7JR8GjMGuqXgjvNNaE8GiU2GeMPDXsKeRhPr4GegVDkUw6aBA5ym4DzytCqoqbN9gwUh86o2HZaUbBscXZ5aQyyKLs4tKCeThpsa
4. Example
The following takes a seed (mnemonic sentence or hex) and derivation path, and shows you the address
for the private extended key
in that path (along with the next few children too).
Try it! - Derivation Paths
Addresses:
m/44h/0h/0h/0/0: 1AZnveys2k5taGCCF743RtrWGwc58UMeq
m/44h/0h/0h/0/1: 1AMYJTJyV4o1hwNACJtfdXBW6BiD1f5FXb
m/44h/0h/0h/0/2: 1NPFFtSiFRatoeUf35rwYb8j8C1u7sVhGa
m/44h/0h/0h/0/3: 1L44VTYEzWesp8cxnXcPGbUzuwTYoSW9at
m/44h/0h/0h/0/4: 1FK85vpZavzZu6oBCvBcmD4FWXQT5fVYRu
m/44h/0h/0h/0/5: 12QaHfWLtyuMwNXuap3FscMY434bw4TS6n
m/44h/0h/0h/0/6: 1NeFG5BYAR9bnjAG72SDYKvNZBH4kPa8r1
m/44h/0h/0h/0/7: 1yF3BiHqbQKL4aRfNYHQt4ZpgNagC4nQe
m/44h/0h/0h/0/8: 144vmUhuAZJsV3m2GsP5Kqp55Pmzwx2gna
m/44h/0h/0h/0/9: 1DQM5w6C7gNaCKBxQV3rXKftcamRKDPQ2M
m/44h/0h/0h/0/10: 17XRvBac5xpgMVr6LbsDA56fgsaAed4oEV
m/44h/0h/0h/0/11: 1BSQC3Qn38UT2WVfcM6LdybkfE7tTGW5M2
m/44h/0h/0h/0/12: 1KUG4EDePnG97xQNXtuU9Xmp4sThqFvSoS
m/44h/0h/0h/0/13: 18sXnPcBnXBRFBYbqr85aKPPNpwT4f52a8
m/44h/0h/0h/0/14: 15S2gpAVvprN1GPE44oXCdtkA4L7yQtBkX
m/44h/0h/0h/0/15: 1FvC2STfbj7dcr2ApAPhagnSCP5Dmy79nH
m/44h/0h/0h/0/16: 15VZHWTEjnQuJSvUHzS7K6gmYjNv4A5cVJ
m/44h/0h/0h/0/17: 1N4S7Z43gb22PDCcpjHhX25cgDSLxegdWm
m/44h/0h/0h/0/18: 1MzS2BktGqokVM4kDuB6VavjLuib72W2je
m/44h/0h/0h/0/19: 1GDLeWJ4FcK2uiTFvLshtVcBArA7M9ECxq
Gap Limit: When recovering a wallet from a seed, you should only check the first 20 receiving addresses for a balance. If none have been used in the past, you can consider the account as unused.
Never enter your actual seed in to a website. Websites can save what you enter and use it to steal your coins.
5. Code
To save on code, these snippets use handy libraries (bitcoin-ruby, hdkeychain) for deriving extended keys.
Ruby
'bitcoin' # sum gem install bitcoin-ruby
require
"67f93560761e20617de26e0cb84f7234aaf373ed2e66295c3d7397e6d7ebe882ea396d5d293808b0defd7edd2babd4c091ad942e6a9351e6d075a29d4df872af"
seed =
# ------
# BIP 44
# ------
# Note: Hardened keys start at 2**31 (the second half of the 2**32 possible children).
Bitcoin::ExtKey.generate_master(seed.htb) # convert hex to binary
m = 2**31+44) # m/44'
purpose = m.derive(2**31+44).derive(2**31+0) # m/44'/0'
coin_type = m.derive(2**31+44).derive(2**31+0).derive(2**31+0) # m/44'/0'/0'
account = m.derive(2**31+44).derive(2**31+0).derive(2**31+0).derive(0) # m/44'/0'/0'/0
receiving = m.derive(
20.times do |i|
# m/44'/0'/0'/0/*
puts receiving.derive(i).addr end
Go
package main
import (
"encoding/hex" // byte array to hex string
"fmt"
"github.com/btcsuite/btcd/chaincfg" // chaincfg.MainNetParams
"github.com/btcsuite/btcutil/hdkeychain" // https://godoc.org/github.com/btcsuite/btcutil/hdkeychain
)
func main() {
// From Seed
"67f93560761e20617de26e0cb84f7234aaf373ed2e66295c3d7397e6d7ebe882ea396d5d293808b0defd7edd2babd4c091ad942e6a9351e6d075a29d4df872af"
seedhex := // hex to bytes
seed, _ := hex.DecodeString(seedhex) // fmt.Println("seed: ", seedhex)
// Generate Seed
// seed, _ := hdkeychain.GenerateSeed(uint8(16))
// fmt.Println(hex.EncodeToString(seed)) // bytes to hex
// ------
// BIP 44
// ------
// m
m, _ := hdkeychain.NewMaster(seed, &chaincfg.MainNetParams)
// m/44h
44)
purpose, _ := m.Child(hdkeychain.HardenedKeyStart +
// m/44h/0h
0)
coin, _ := purpose.Child(hdkeychain.HardenedKeyStart +
// m/44h/0h/0h
0)
account, _ := coin.Child(hdkeychain.HardenedKeyStart +
// m/44h/0h/0h/0
0) // 0 = receiving, 1 = change
receiving, _ := account.Child(
// m/44h/0h/0h/0/*
for i := 0; i < 20; i++ {
uint32(i)) // takes an unsigned integer
index, _ := receiving.Child(
address, _ := index.Address(&chaincfg.MainNetParams)
fmt.Println(address)
} }
Links
- BIP 44 (Marek Palatinus, Pavol Rusnak)
- BIP 49 (Daniel Weigl)
- BIP 84 (Pavol Rusnak)
- Ian Coleman BIP39 Tool (Fantastic interactive tool for path derivation from a seed.)