Generate Bitcoin wallet keys using mnemonic phrase.
Bitcoin key generation has changed over the years and has nuances that are covered in following specs:
In particular, you may have seen bitcoin addresses in one of the following forms:
- 1H1RmnHvTsgLBxvGke9XDPP74w7K9uVT9c, i.e., starting with
1
- 3LZoyoAc9TDXLGrGoWsmv3dtJNbBwm1HKz, i.e., starting with
3
- bc1q87e8lc7523q67qqjdnah6yk4g547nh50vavhlc, i.e. starting with
bc1
These addresses can all be traced back to single mnemonic sentence and are
therefore, "derived" from a master key. In particular, addresses starting with
1
correspond to the spec BIP-44
, whereas addresses starting with 3
correspond to the spec BIP-49
and the ones starting with bc1
correspond
to the spec BIP-84
.
More info on how to go about generating these keys is below.
The use of this tool does not guarantee security or usability for any particular purpose. Please review the code and use at your own risk.
Don't trust, verify
This step assumes you have Go compiler toolchain installed on your system.
go install github.com/kubetrail/bip32@latest
Add autocompletion for bash
to your .bashrc
source <(bip32 completion bash)
Keys can be generated using a mnemonic sentence, which is collection, typically of 12 to 24, random words.
All mnemonic and seed generation can be handled by bip39 CLI tool
For example, a 12 word mnemonic can be generated as follows:
bip39 gen --length=12
surround disagree build occur pluck main ignore define hurdle excess chicken gold
Export mnemonic as an environment variable for use in next steps.
export MNEMONIC="surround disagree build occur pluck main ignore define hurdle excess chicken gold"
Please note that mnemonic generation occurs using a cryptographic source of randomness that is internal to the tool. Hence, security of such mnemonic is inherently limited by the security of the random generator used.
All keys are derived from mnemonic in a deterministic way, therefore, the security of Bitcoin keys is essentially limited by security of mnemonic.
Needless to say, please only use the mnemonic shown above for experimental purposes and not for performing actual transactions. Generate a new mnemonic and do not share with anyone!
The high level sequence of events during a key generation is as follows:
- A random generator is used to generate so-called
entropy
entropy
is used to generate a mnemonic sentence- Mnemonic sentence is combined with optional passphrase to generate a
seed
- If a mnemonic language is different from english, it is first translated to english
- Seed is used to generate the master
ECDSA
key pair (private and public keys) - These are wrapped into so-called extended keys
- Private extended key is used to generate the private WIF key (more info on WIF below)
- Private WIF key is used to generate a public hex key
- Public hex key is used to generate the address
- Address type governs the type of address generated, i.e., either staring with
1
,3
, orbc1
. It also decided the prefix of extended keys, which can be eitherxprv
,yprv
orzprv
- Similarly, choice of network, i.e., either
mainnet
ortestnet
also governs the form of extended keys.
Assuming you stored mnemonic in an environment variable, private key and public address can be generated using a few defaults as follows:
bip32 gen ${MNEMONIC}
prvKeyWif: Ky7kJQEFQDCRhShHaZs7TSCEa1UqGhVZB6BhXUHf3T9pAG6q7987
addr: 1MJ9PojuE1rA1E8wtrdQXjxaqZdsgddhoh
Please note that passing mnemonic as command line arguments is not secure since it may get captured in the shell command history. To avoid this do not pass mnemonic as command line args and instead let the tool ask for it as an input on STDIN
In order to explore the keys better, let's output all keys that are
part of this step. This is done using --show-all-keys
flag
bip32 gen --show-all-keys ${MNEMONIC}
seed: bf4848ce9688a8fc5e149aa038c454885a6727e0c7de50754cef7e506fb3d8a80d4c92b7cd77da542b1b6764a2513899c311ab2c7d9d192546e2d6442ac99e11
xPrv: xprvA4HQhA4Br6afRrswRBRydnSPqHGAVFptd3iiMp8kuMMsuso4vUuPSb91AizescB7NwS8uHijxBK4L1J5nJj98be4cSTKthrPraRZiFttSPj
xPub: xpub6HGm6fb5gU8xeLxQXCxyzvP8PK6etiYjzGeKACYNTgtrng8DU2DdzPTV1yvcwsjZ9o1UAUYq39RhLXBmJu66xMmAGGnDRGb4iLkP99rLmL5
pubKeyHex: 022b70459564b65102394e088bdb68f8a80d386939e39319363377b00f23b21cc4
prvKeyWif: Ky7kJQEFQDCRhShHaZs7TSCEa1UqGhVZB6BhXUHf3T9pAG6q7987
addr: 1MJ9PojuE1rA1E8wtrdQXjxaqZdsgddhoh
addrType: legacy
derivationPath: m/44h/0h/0h/0/0
coinType: btc
network: mainnet
seed
is the hexadecimal seed used for generating master key.
Please note that the seed is the raw cryptographic secret material that by itself is sufficient to generate master key. Unlike a mnemonic with passphrase, a seed is not protected using a passphrase
xPrv
and xPub
are extended private and public keys, respectively. Extended keys
contain not just the raw key material, but also metadata such as key version and
child derivation details. These are base58
encoded. Learn more about
extended keys
prvKeyWif
is the wallet import format (WIF) compatible private key. WIF keys are
essentially private key material combined with network info, so-called compression
byte and a checksum. Learn more about WIF private key
pubKeyHex
is a hexadecimal formatted public key bytes. These are raw bytes without
any network info or key version in it.
addr
is the legacy bitcoin address for receiving transactions that correspond to the private
key. addrType
indicates type of the address, i.e., either legacy
, segwit-compatibe
or
segwit-native
etc.
network
and coinType
indicate the blockchain on which these keys will work.
Below are aliases for address types. These aliases can be used as values for
--addr-type
flag:
Address type Aliases
-------------------------------------------------------
p2pkh-or-p2sh legacy, bip44
p2wpkh-p2sh segwit-compatible, p2sh, bip49
p2wsh-p2sh
p2wpkh segwit-native, bech32, bip84
p2wsh
Read more about address types here
A secret passphrase can be additionally used in conjunction with mnemonic to apply an
additional layer of security. Use of passphrase
is enabled using --use-passphrase
flag.
A passphrase
input is only allowed via STDIN
and the user has to confirm it by entering
it again.
bip32 gen --use-passphrase ${MNEMONIC}
Enter secret passphrase:
Enter secret passphrase again:
prvKeyWif: KzjwJtryjybsJjVvse22oG7f1pfQbYcj2E8gxbM6bivai3pLrczh
addr: 181mk5LncqeKPEz1Z1KihzykXD5XKLTTr2
As mentioned before, three types of addresses can be derived from mnemonic.
For instance legacy
P2PKH
addresses can be generated as follows, which is also the
default:
bip32 gen --addr-type=legacy ${MNEMONIC}
prvKeyWif: Ky7kJQEFQDCRhShHaZs7TSCEa1UqGhVZB6BhXUHf3T9pAG6q7987
addr: 1MJ9PojuE1rA1E8wtrdQXjxaqZdsgddhoh
segwit
compatible P2SH
addresses can be generated as follows:
bip32 gen --addr-type=segwit-compatible ${MNEMONIC}
prvKeyWif: L5Nx5ePGYjN2TzoadVXorDQXrchWEtwuDQEnsC4SSztz9b4tcCWQ
addr: 37vznvAgCmaKERDZmYaw3X4ArHracgVUfa
And finally, segwit
native Bech32
addresses can be generated as follows:
bip32 gen --addr-type=segwit-native ${MNEMONIC}
prvKeyWif: L125eeMMWH93ZVb6NXEpQ5qXYPiKDF6Qy3AHE8UcBmh6SdcBvEfQ
addr: bc1qsah54m5u94ktfymcv4jf656rqnu9dxnuhcjvx8
At this point, it might be a good idea to verify these addresses match those produced by external wallet apps such as Mycelium
After installing the app, select the option to restore the wallet from backup instead of creating a new one.
Select 12 words as mnemonic length option and then enter your mnemonic that was used so far.
Once the master key generation is done, go to the Balance
tab and tap on
qrcode to rotate through different key types and verify they are the same
as generated in previous step.
Keys generated so far used defaults for the so-called derivation-path
, which
governs how child keys are to be derived from the master key. It is a commandline
flag --derivation-path
and a few examples of such paths are as follows:
- m
- m/44h/0h/0h
- m/49h/0h/0h/0/0
- m/49'/0'/0'/0/0 and so on
m
stands for master key and when there is nothing else after m
, no key derivation
is performed and master key itself is used to derive the public address.
The default value of derivation-path
is auto
, which signifies that the path will be
chosen according to the addr-type
flag. In particular, below is the mapping between
addr-type
and the derivation-path
:
For mainnet
:
addr type derivation path
--------------------------------------------------
legacy m/44'/0'/0'/0/0
segwit compatible P2SH m/49'/0'/0'/0/0
segwit native Bech32 m/84'/0'/0'/0/0
--------------------------------------------------
For testnet
:
addr type derivation path
--------------------------------------------------
legacy m/44'/1'/0'/0/0
segwit compatible P2SH m/49'/1'/0'/0/0
segwit native Bech32 m/84'/1'/0'/0/0
--------------------------------------------------
The way to read these derivation paths is as follows:
m / purpose / coin type / account / change / index
where, '
or h
or H
denotes so-called hardened
value. Read more about it
on BIP-44 spec.
Thus, master private extended keys correspond to the derivation paths that do not
account change
and index
values, such as m/44'/0'/0'
or m/49'/0'/0'
etc.
Let's generate private extended keys for different address types:
Make sure to use purpose value of 44
with address type of legacy
:
bip32 gen \
--output-format=json \
--addr-type=legacy \
--show-all-keys \
--derivation-path=m/44h/0h/0h \
${MNEMONIC} \
| jq '{xPub: .xPub, xPrv: .xPrv}'
{
"xPub": "xpub6D2evtM5oHGd4MxbT4oDhrgKAVXWgoRBLPQgPScQYS1LN1NUqaQ5jJ4azZxfbiUh9EUDurfuaZkFewCoNYyzXm84BDMp2PbS9mvcFwtvfLm",
"xPrv": "xprv9z3JXNpBxuiKqst8M3GDLijacTh2HLhKyAV5b4Cnz6UMVD3LJ35qBVk79JpMs6XoygJoaEmVd4vDNQrsfdBaXngkCrvvmSSLiExb2usfLG1"
}
Please use purpose of 49
when using address type segwit-compatible
bip32 gen \
--output-format=json \
--addr-type=segwit-compatible \
--show-all-keys \
--derivation-path=m/49h/0h/0h \
${MNEMONIC} \
| jq '{xPub: .xPub, xPrv: .xPrv}'
{
"xPub": "ypub6X1LhQqze25nBqKDhxpMUYueRM6VdALocQomFPPEtryESNvQ9d1mo8VsqyWMsZiHszffJXHPfgdBv2tS38Sk2FB7hbjYEMCp3TvZSr2CDaj",
"xPrv": "yprvAJ1zHuK6oeXUyMEkbwHM7QxusKG1DhcxFBtASzydLXSFZabFc5hXFLBPzij6CSnXoiPawEqBxYV8ZKSVuBoCzCEhZ3Rksjrc6WSJggMaTpa"
}
Similarly, please use purpose of 84
when using address type of segwit-native
bip32 gen \
--output-format=json \
--addr-type=segwit-native \
--show-all-keys \
--derivation-path=m/84h/0h/0h \
${MNEMONIC} \
| jq '{xPub: .xPub, xPrv: .xPrv}'
{
"xPub": "zpub6rWTAsb9uWGq6RBefNvtyMtXdscHxQb5xLPPmyPN9p43978FzZqmxsPQ7eYu8qJb6xkPnDfP9ciZC5hoNv6ZoFKspuDgHuR1ad5SmSZkw7R",
"xPrv": "zprvAdX6mN4G58iXsw7BZMPtcDwo5qmoYwsEb7TnyaykbUX4GJo7T2XXR54vGM6DbnNMM9f9WEpsGhXABP2eyvNwQXzuwywQJH4opQNEwC1ZAmX"
}
Again, please ensure that these are indeed the private key values that you can export out of
an external wallet app such as Mycelium
.
Bitcoin networks mainnet
(default) and testnet
can be selected using --network
flag.
Example below shows generation for mainnet
using a hex seed
echo 3ddd5602285899a946114506157c7997e5444528f3003f6134712147db19b678 \
| bip32 gen --input-hex-seed --show-all-keys --network=mainnet --output-format=json \
| jq '.'
{
"seed": "3ddd5602285899a946114506157c7997e5444528f3003f6134712147db19b678",
"xPrv": "xprvA2X4DRgATz4wyfc3rjYT2o24MDk2s6hDsU6Xpzj7N5zqi2mGwkcojjDhmz8acg2zHDHWkL4rdavReSrJTWJNtsnmcU23qKCJeFixAdy27Y8",
"xPub": "xpub6FWQcwD4JMdFC9gWxm5TPvxnuFaXGZR5Eh28dP8ivRXpaq6RVHw4HXYBdFL69c4YbLETQnxh2Qo4j4VKDdYsPuttZNMC3dauvKKhqNLzWRb",
"pubKeyHex": "024bf0776f0b553c9acfe6d47206849012eae6c81bd8e9fc645b4e84cabcfddc76",
"prvKeyWif": "Ky2KivUN8WQ9Rdd9FyacLPmfqWy3mUMq4uqJPnXNoctFu15A79BH",
"addr": "16XvtkkJio36xFbuXjLMvEGRuLFB15ScKY",
"addrType": "legacy",
"derivationPath": "m/44h/0h/0h/0/0",
"coinType": "btc",
"network": "mainnet"
}
Same hex seed used for generation on testnet
results in different keys.
echo 3ddd5602285899a946114506157c7997e5444528f3003f6134712147db19b678 \
| bip32 gen --input-hex-seed --show-all-keys --network=testnet --output-format=json \
| jq '.'
{
"seed": "3ddd5602285899a946114506157c7997e5444528f3003f6134712147db19b678",
"xPrv": "tprv8kFu8KvA1aeUGDaNtevxyVFzUMNzkrjuEL8qn7Py4ASGoGkZbpkZEExR4UCizwwPVeUGp1SBcpaDMZhA8GiPFvPDfr8o4rnx3JfrjS1C9cz",
"xPub": "tpubDGwwGjxQ9xL99gcAnJbZNtv73NtvvBvoodjd4dSGUSEfdm1LEDa9QjaHEebkg2VrhvU7Za9wsTDXyZNokb93TTbmMkNrt256Ex6yAgRRz3L",
"pubKeyHex": "03c67617201c583f63f0e35997f49e935ba3dd6d4954f9a58590734623c82a21b5",
"prvKeyWif": "cMamanoqSi2Bq82yqgKFSVVvVcWHSCnZEpbSUDqu6CFmJQUEYFPH",
"addr": "mtJygtHbYmq7kauFn6mVwuay2SWBmka4NJ",
"addrType": "legacy",
"derivationPath": "m/44h/1h/0h/0/0",
"coinType": "btc",
"network": "testnet"
}
Child keys can be derived using parent private or public keys and derivation paths.
For instance, if we start with private extended key that was compared against
exported key from Mycelium
in previous steps, and derive the child key for
change=0
and index=0
, it would correspond to the derivation path of m/0/0
since the private extended key was already derived from the master key using
derivatation path of m/44h/0h/0h
:
bip32 derive \
--derivation-path=m/0/0 \
xprv9z3JXNpBxuiKqst8M3GDLijacTh2HLhKyAV5b4Cnz6UMVD3LJ35qBVk79JpMs6XoygJoaEmVd4vDNQrsfdBaXngkCrvvmSSLiExb2usfLG1
xPrv: xprvA4HQhA4Br6afRrswRBRydnSPqHGAVFptd3iiMp8kuMMsuso4vUuPSb91AizescB7NwS8uHijxBK4L1J5nJj98be4cSTKthrPraRZiFttSPj
xPub: xpub6HGm6fb5gU8xeLxQXCxyzvP8PK6etiYjzGeKACYNTgtrng8DU2DdzPTV1yvcwsjZ9o1UAUYq39RhLXBmJu66xMmAGGnDRGb4iLkP99rLmL5
pubKeyHex: 022b70459564b65102394e088bdb68f8a80d386939e39319363377b00f23b21cc4
prvKeyWif: Ky7kJQEFQDCRhShHaZs7TSCEa1UqGhVZB6BhXUHf3T9pAG6q7987
addr: 1MJ9PojuE1rA1E8wtrdQXjxaqZdsgddhoh
coinType: btc
network: mainnet
As you can see the address 1MJ9PojuE1rA1E8wtrdQXjxaqZdsgddhoh
matches the one
produced above using command:
bip32 gen --addr-type=legacy ${MNEMONIC}
prvKeyWif: Ky7kJQEFQDCRhShHaZs7TSCEa1UqGhVZB6BhXUHf3T9pAG6q7987
addr: 1MJ9PojuE1rA1E8wtrdQXjxaqZdsgddhoh
Similarly, extended keys from other address types can be used:
bip32 derive \
--derivation-path=m/0/0 \
yprvAJ1zHuK6oeXUyMEkbwHM7QxusKG1DhcxFBtASzydLXSFZabFc5hXFLBPzij6CSnXoiPawEqBxYV8ZKSVuBoCzCEhZ3Rksjrc6WSJggMaTpa
xPrv: yprvAMnfCAc35Zzftqq3WA8yhmD7juNk1UykJBUtc6QdWJkAoQJUajnCgxEc6KJwwMYTYzETw986nt6qVLmEMAbUYMiRcWuS8kvgAZKUdwHT5aC
xPub: ypub6an1bg8vuwYy7KuWcBfz4u9rHwDEQwhbfQQVQUpF4eH9gCdd8H6TEkZ5wasZcRfWY1U8ZYBTHquqSJbxMg1r6WNC3Zwxmwnt8SCxmirsu2G
pubKeyHex: 03397f9677279f78472b1c9528a760eb84ebb3c6019a5dfa6bbeb971cf58ae173b
prvKeyWif: L5Nx5ePGYjN2TzoadVXorDQXrchWEtwuDQEnsC4SSztz9b4tcCWQ
addr: 37vznvAgCmaKERDZmYaw3X4ArHracgVUfa
addrType: segwit-compatible, p2sh
coinType: btc
network: mainnet
As you can see the address 37vznvAgCmaKERDZmYaw3X4ArHracgVUfa
is the same as that generated
previously using following command for address type of segwit-compatible
bip32 gen --addr-type=segwit-compatible ${MNEMONIC}
prvKeyWif: L5Nx5ePGYjN2TzoadVXorDQXrchWEtwuDQEnsC4SSztz9b4tcCWQ
addr: 37vznvAgCmaKERDZmYaw3X4ArHracgVUfa
Generation of hardened keys is only allowed for parent private keys.
While derive
command is used for deriving child keys, decode
works with a variety of key inputs:
- Extended keys (both private and public)
- Private WIF keys
- Public HEX keys
echo xprv9xenovaMSsLaNKX8Yz2K1TEZ1b8VymYyji1SL6URcvAMT4EXKQTQxySayFFk2CA6BrhVaBkXWuzTSfNHMEuu1a6gCxZhdc5t9afpx7YRdq4 \
| bip32 decode --output-format=json \
| jq '.'
{
"xPrv": "xprv9xenovaMSsLaNKX8Yz2K1TEZ1b8VymYyji1SL6URcvAMT4EXKQTQxySayFFk2CA6BrhVaBkXWuzTSfNHMEuu1a6gCxZhdc5t9afpx7YRdq4",
"xPub": "xpub6Be9DS7FHEtsaobbf1ZKNbBHZcxzPEGq6vw38Ut3BFhLKrZfrwmfWmm4pWbqVMyPauABhiVdazRtW9ZBT7fpKR9Pbw5puUAsZaTSRhshGU4",
"prvKeyWif": "KzeBeJFoxNctzErrKH9GqBS8VGakySW2bQ33sE43X64aRSjxh7Ei",
"pubKeyHex": "028fb7e34b1d9f41c1b7c4a9f93d75a18b4bf0ef9537a270a4018e43214448ac0d",
"addr": "19q3NgbmofP5eB62zNawU68pxVwopdHDZ2",
"network": "mainnet",
"coinType": "btc"
}
Similarly, a public key can be decoded as follows:
echo xpub661MyMwAqRbcGczjuMoRm6dXaLDEhW1u34gKenbeYqAix21mdUKJyuyu5F1rzYGVxyL6tmgBUAEPrEz92mBXjByMRiJdba9wpnN37RLLAXa \
| bip32 decode --output-format=json \
| jq '.'
{
"xPub": "xpub661MyMwAqRbcGczjuMoRm6dXaLDEhW1u34gKenbeYqAix21mdUKJyuyu5F1rzYGVxyL6tmgBUAEPrEz92mBXjByMRiJdba9wpnN37RLLAXa",
"pubKeyHex": "026f6fedc9240f61daa9c7144b682a430a3a1366576f840bf2d070101fcbc9a02d",
"addr": "1GpWFBBE37FQumRkrVUL6HB1bqSWCuYsKt",
"network": "mainnet",
"coinType": "btc"
}
Decode private WIF key:
bip32 decode KzeBeJFoxNctzErrKH9GqBS8VGakySW2bQ33sE43X64aRSjxh7Ei --output-format=json \
| jq '.'
{
"prvKeyWif": "KzeBeJFoxNctzErrKH9GqBS8VGakySW2bQ33sE43X64aRSjxh7Ei",
"pubKeyHex": "028fb7e34b1d9f41c1b7c4a9f93d75a18b4bf0ef9537a270a4018e43214448ac0d",
"addr": "19q3NgbmofP5eB62zNawU68pxVwopdHDZ2",
"network": "mainnet",
"coinType": "btc"
}
Decode public HEX key.
Please note that assumption is made about the network to be
mainnet
for cointype of BTC since public hex keys do not encode key versions and therefore it is not possible to decode which network these keys were originally meant for.
bip32 decode 028fb7e34b1d9f41c1b7c4a9f93d75a18b4bf0ef9537a270a4018e43214448ac0d --output-format=json \
| jq '.'
{
"pubKeyHex": "028fb7e34b1d9f41c1b7c4a9f93d75a18b4bf0ef9537a270a4018e43214448ac0d",
"addr": "19q3NgbmofP5eB62zNawU68pxVwopdHDZ2",
"network": "mainnet",
"coinType": "btc"
}
Validity of the keys can be checked (for the most part)
For instance, key below is valid
bip32 validate xpub661MyMwAqRbcGczjuMoRm6dXaLDEhW1u34gKenbeYqAix21mdUKJyuyu5F1rzYGVxyL6tmgBUAEPrEz92mBXjByMRiJdba9wpnN37RLLAXa
key is valid
However, this key is invalid.
bip32 validate xpub661MyMwAqRbcEYS8w7XLSVeEsBXy79zSzH1J8vCdxAZningWLdN3zgtU6LBpB85b3D2yc8sfvZU521AAwdZafEz7mnzBBsz4wKY5fTtTQBm
Error: private key with public key version mismatch
Usage:
bip32 validate [flags]
Flags:
-h, --help help for validate
Global Flags:
--config string config file (default is $HOME/.bip32.yaml)
Error: private key with public key version mismatch
See test vector 5 for more examples of invalid keys.
Following tests pass except for one at the time of writing this doc.
One of the test cases in test vector 5 related to invalid public key is currently not getting detected.
./test/test.sh
https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki#test-vector-1
ok ok ok ok ok ok ok ok ok ok ok ok
https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki#test-vector-2
ok ok ok ok ok ok ok ok ok ok ok ok
https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki#test-vector-3
ok ok ok ok
https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki#test-vector-4
ok ok ok ok ok ok
https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki#test-vector-5
ok ok ok ok ok ok ok ok ok ok ok ok ok ok ok validation failed
passed bip84 test matrix
passed bip49 test matrix
passed bip44 test matrix
passed bip32 test matrix
First generate a mnemonic using bip39 and then secure it on Google cloud secret engine using mksecret
You may also need qrcode to display Bitcoin addresses as qr codes for easier workflow with cellphone based wallets
Finally, you will need btcio to inspect blocks, transactions and UTXO's
mksecret set --name=my-bitcoin-testnet-mnemonic-phrase $(bip39 gen --length=12)
canvas title buffalo digital cash cement cabin capable satisfy axis mandate heavy
Generate qrcode for receiving addresses. Generate all three types of addresses,
i.e., legacy
, segwit compatible
P2SH
and segwit native
Bech32
formatted
addresses:
Legacy address:
mksecret get my-bitcoin-testnet-mnemonic-phrase \
| bip32 gen \
--network=testnet \
--addr-type=legacy \
--output-format=json \
| jq -r '.addr' \
| qrcode gen
P2SH SegWit compatible address:
mksecret get my-bitcoin-testnet-mnemonic-phrase \
| bip32 gen \
--network=testnet \
--addr-type=segwit-compatible \
--output-format=json \
| jq -r '.addr' \
| qrcode gen
SegWit native Bech32 address:
mksecret get my-bitcoin-testnet-mnemonic-phrase \
| bip32 gen \
--network=testnet \
--addr-type=segwit-native \
--output-format=json \
| jq -r '.addr' \
| qrcode gen
Using a funded testnet wallet send Bitcoins on these addresses. Then verify on explorer to ensure funds have been transferred and confirmed.
Find out transaction hash for each of these transactions. Also make sure you have a Bitcoin testnet node running on localhost and it is synced up with the peers to the latest block.
Inspect UTXO's for the addresses in these transactions
btcio utxo --tx-hash=<your-transaction-hash> | jq '.'
Below is a typical scenario of how a wallet would rotate addresses making use of chain derivation paths.
Let's say you created a new wallet using a new mnemonic that was never used before and then receive funds into that wallet using three separate transactions. Typically, a wallet would rotate addresses after every transaction using following derivation paths:
Transaction Number Derivation path Amount BTC Received
--------------------------------------------------------------------
0 m/84h/0h/0h/0/0 0.12
1 m/84h/0h/0h/0/1 0.13
2 m/84h/0h/0h/0/2 0.15
--------------------------------------------------------------------
This will reflect as total account balance of 0.2
, which is scattered
among three separate addresses that are all derived using address index
value as shown above.
Now if we were to send an amount of 0.18
to an external address, we don't have
sufficient funds in any one single address. This will require pooling funds from
more than one addresses. For the case shown above, it can be done using pooling
funds from last two addresses or using all three addresses.
Each transaction has a set of addresses grouped as inputs
and outputs
.
So typically a wallet would group all addresses in inputs required to
pool the sufficient funds.
However, a new address may get created where the remaining balance is transferred. This new address would typically be generated using following derivation path
Transaction Number Derivation path Received Sent
--------------------------------------------------------------------
3 m/84h/0h/0h/1/0 0.02
3 m/84h/0h/0h/0/0 0.12
3 m/84h/0h/0h/0/1 0.13
3 m/84h/0h/0h/0/2 0.15
--------------------------------------------------------------------
As you can see all these addresses are part of the same transaction and
the remaining balance is accumulated in the so-called change
address