Generate child keys in HD Wallet
val seed = fun(mnemonic)
val master private key and chain code = fun(seed)
val master public key = fun(private key)
val child public key and chain code = fun(master public key + chain code + path of child key)
Using extended public account key
private fun print() {
// mnemonic -> seed -> master key -> child keys -> grandchild keys
// https://iancoleman.io/bip39/
val passphrase = ""
val deterministicSeed = DeterministicSeed(mnemonic, null, passphrase, 0)
val masterKey = HDKeyDerivation.createMasterPrivateKey(deterministicSeed.seedBytes)
val extendedPubKey = masterKey.serializePubB58(NETWORK_PARAM)
Log.d(TAG, "BIP39 Mnemonic: $mnemonic")
Log.d(TAG, "BIP39 Passphrase : $passphrase")
Log.d(TAG, "BIP39 Seed: ${deterministicSeed.toHexString()}")
Log.d(TAG, "Coin: ${NETWORK_PARAM.id}")
Log.d(TAG, "master key path: ${masterKey.pathAsString}")
Log.d(TAG, "BIP32 Root(extended private) Key(xprv): ${masterKey.serializePrivB58(NETWORK_PARAM)}")
Log.d(TAG, "root extended public key(xpub): $extendedPubKey")
Log.d(TAG, "master private key as hex: ${masterKey.privateKeyAsHex}")
Log.d(TAG, "master public key as hex: ${masterKey.publicKeyAsHex}")
Log.i(TAG, "*********** Derivation Path(m/44H'/1H'/0H') in BIP44 ***********")
val coinKey = DeterministicHierarchy(masterKey).get(HDUtils.parsePath("44H/1H"), true, true)
val accountKey = HDKeyDerivation.deriveChildKey(coinKey, ChildNumber(0, true))
Log.d(TAG, "coin key path: ${coinKey.pathAsString}")
Log.d(TAG, "Account 0 Path: ${accountKey.pathAsString}")
Log.d(TAG, "Account 0 Extended Public Key: ${accountKey.serializePubB58(NETWORK_PARAM)}")
if (!accountKey.isPubKeyOnly) {
Log.d(TAG, "Account 0 Extended Private Key: ${accountKey.serializePrivB58(NETWORK_PARAM)} ")
}
Log.i(TAG, "*********** Derivation Path(m/44H'/1H'/0H') in BIP44 from account extended public key ***********")
val key = DeterministicKey.deserializeB58(accountKey.serializePubB58(NETWORK_PARAM), NETWORK_PARAM)
toBip32ExtendedExternalKey(key)
toBip32ExtendedInternalKey(key)
}
private fun toBip32ExtendedExternalKey(accountKey: DeterministicKey) {
val key = HDKeyDerivation.deriveChildKey(accountKey, ZERO)
Log.d(TAG, "BIP32 Derivation Path: ${key.pathAsString}")
Log.d(TAG, "BIP32 Extended Public Key: ${key.serializePubB58(NETWORK_PARAM)}")
if (!key.isPubKeyOnly) {
Log.d(TAG, "BIP32 Extended Private Key: ${key.serializePrivB58(NETWORK_PARAM)}")
}
index(key)
}
private fun toBip32ExtendedInternalKey(accountKey: DeterministicKey) {
val key = HDKeyDerivation.deriveChildKey(accountKey, ONE)
Log.d(TAG, "BIP32 Derivation Path: ${key.pathAsString}")
Log.d(TAG, "BIP32 Extended Public Key: ${key.serializePubB58(NETWORK_PARAM)}")
if (!key.isPubKeyOnly) {
Log.d(TAG, "BIP32 Extended Private Key: ${key.serializePrivB58(NETWORK_PARAM)}")
}
index(key)
}
private fun index(key: DeterministicKey) {
for (index in 0 until 2) {
val subKey = HDKeyDerivation.deriveChildKey(key, ChildNumber(index, false))
Log.d(TAG, "#$index ${subKey.publicKeyAsHex}")
Log.d(TAG, "#$index ${LegacyAddress.fromPubKeyHash(NETWORK_PARAM, subKey.pubKeyHash).toBase58()}")
}
}
- LogCat
D/Key: BIP39 Mnemonic: try food throw quote crystal opera unveil advance sister improve deny team
D/Key: BIP39 Passphrase :
D/Key: BIP39 Seed: 2899ab70709c05cbd988949cd8f553e93e598a9de0774000808ee3f9fb914474bca1bae031972a4c2bce74878634519f05ee53ec9dd59050725b582a4953ba3c
D/Key: Coin: org.bitcoin.test
D/Key: master key path: M
D/Key: BIP32 Root(extended private) Key(xprv): tprv8ZgxMBicQKsPde8f74FzdbrCPgbXXSW8GE7HD8mKcm2BVmR6CJrUot6x4LAezrJgnXR7i2n39QTUM9Tyw3v4MejEhC2g5J4kPgZu6zUhMBk
D/Key: root extended public key(xpub): tpubD6NzVbkrYhZ4X7ASzhvb31WJxi7Tgmh2qXi4Veod32paLFfrphg4zNipEU8JmBzC5tU2gULJMKLHV9H4MSkB8XLiUwCyegtR4XpHUfKrJJ2
D/Key: master private key as hex: f3878526fdf274073368235f898378f66b440aaa7979a6d55daf246d86cf2e69
D/Key: master public key as hex: 0378f0b74dc20ac0b9f19aac892817f024ecbe2cd35b9412e48e0ce9916c4f05ef
I/Key: *********** Derivation Path(m/44H'/1H'/0H') in BIP44 ***********
D/Key: coin key path: M/44H/1H
D/Key: Account 0 Path: M/44H/1H/0H
D/Key: Account 0 Extended Public Key: tpubDD5zzoPYBfUmGpzBTC9A4duYLkDzzYyggcrizbaTXtnA8fVYP6K7EGKEuQS3Vb2fbQVSCauy6QByCUCqoSj9FrFFa12nhY7NodRGvTDWbva
D/Key: Account 0 Extended Private Key: tprv8gPxrPMJ3Ho6PMxPZYUZfEFRmii4qDnn7KFwi5YA7cymJBEmkhVX3mhNjFWn2QKU1X2YfDJgdTJkVjNPD6mDjDnGMEALBTZravHZR5ZmCDG
I/Key: *********** Derivation Path(m/44H'/1H'/0H') in BIP44 from account extended public key ***********
D/Key: BIP32 Derivation Path: M/0H/0 // what happen?
D/Key: BIP32 Extended Public Key: tpubDEj5yioiXvjWw2CM89aQmMrzUn6UVbhCVM2MkaXjTpHdtZkgst5WcPBnc4tuzoe6zipyAAwFwA4i59G9VAFBEoohKagokFDoJjHbKT7JYQM
D/Key: #0 03b04c220ae19ea44a0cb7fd94e666c78ec7ec4418513ab7159955488c8d09bc15
D/Key: #0 mrYkGBV53ChLcf1uF3qnLHdHnKbtH94B8n
D/Key: #1 03405b717edba320ba8923862fdfb4e48704790ac83723c0be10b2ee4edeff0c64
D/Key: #1 miwLBNAwqo4BBJPhLUTugbUQ4nYNky9e1m
D/Key: BIP32 Derivation Path: M/0H/1 // what happen?
D/Key: BIP32 Extended Public Key: tpubDEj5yioiXvjWx3VPKCEpdLox4TCjS53gVjkCMoAb7kLrNUTNRBkH4gDA832hYT7MY4rHUPtWiR7YGrf3WxRgHFWAWiKquYxpMmXHms9hwXU
D/Key: #0 023919a545f2d16e604250fb39ef174945bc746912895c0281c65021d9209dd4f2
D/Key: #0 mqzLAZZZ8tFvbs6d2tK2fAYQrrPyVLrifH
D/Key: #1 031f1c53dc75a1e716c4d698ef89a71044ff76f4125c769e446b805bf8801b097d
D/Key: #1 mttwgLNAr1Kw8UnxE7hBBB71THb9zXEBgm
Using master extend public key
I want to use master extend public key to generate account key and its child, but failed.
private fun test() {
val passphrase = ""
val deterministicSeed = DeterministicSeed(mnemonic, null, passphrase, 0)
val masterKey = HDKeyDerivation.createMasterPrivateKey(deterministicSeed.seedBytes)
val extendedPubKey = masterKey.serializePubB58(NETWORK_PARAM)
Log.i(TAG, "*********** deserialize extended public key ***********")
val normalPubKey = DeterministicKey.deserializeB58(extendedPubKey, NETWORK_PARAM)
Log.d(TAG, "normal public key path: ${normalPubKey.pathAsString}")
Log.d(TAG, "isPubKeyOnly: ${normalPubKey.isPubKeyOnly}")
Log.d(TAG, "normal public key as hex: ${normalPubKey.publicKeyAsHex}")
val accountKeyFromXPubKey = DeterministicHierarchy(normalPubKey)
.get(HDUtils.parsePath("44H/1H/0H"), true, true)
}
- LogCat
I/Key: *********** deserialize extended public key ***********
D/Key: normal public key path: M
D/Key: isPubKeyOnly: true
D/Key: normal public key as hex: 0378f0b74dc20ac0b9f19aac892817f024ecbe2cd35b9412e48e0ce9916c4f05ef
Caused by: java.lang.IllegalArgumentException: Hardened derivation is unsupported (44H).
at com.google.common.base.Preconditions.checkArgument(Preconditions.java:217)
at org.bitcoinj.crypto.HDKeyDerivation.deriveChildKeyBytesFromPublic(HDKeyDerivation.java:189)
at org.bitcoinj.crypto.HDKeyDerivation.deriveChildKey(HDKeyDerivation.java:142)
at org.bitcoinj.crypto.DeterministicHierarchy.get(DeterministicHierarchy.java:93)
at org.bitcoinj.crypto.DeterministicHierarchy.get(DeterministicHierarchy.java:92)
at org.bitcoinj.crypto.DeterministicHierarchy.get(DeterministicHierarchy.java:92)
Using master private key
private fun test() {
val passphrase = ""
val deterministicSeed = DeterministicSeed(mnemonic, null, passphrase, 0)
val masterKey = HDKeyDerivation.createMasterPrivateKey(deterministicSeed.seedBytes)
subFromRootPrivateKey(masterKey)
}
private fun subFromRootPrivateKey(rootPrivateKey: DeterministicKey) {
Log.i(TAG, "**********use root private key to generate sub private/public key and address****************")
val deterministicHierarchy = DeterministicHierarchy(rootPrivateKey)
val path = HDUtils.parsePath("44H/1H/0H")
Log.d(TAG, "is isHardened: ${path[0].isHardened}")
val accountKey = deterministicHierarchy.get(path, true, true)
Log.d(TAG, "Account 0 Path: ${accountKey.pathAsString}")
Log.d(TAG, "Account 0 Extended Public Key: ${accountKey.serializePubB58(NETWORK_PARAM)}")
if (!accountKey.isPubKeyOnly) {
Log.d(TAG, "Account 0 Extended Private Key: ${accountKey.serializePrivB58(NETWORK_PARAM)} ")
}
// m/44'/1'/0'/0 account zero external sub path
Log.i(TAG, "***********************account zero external sub path*************************")
toBip32ExtendedExternalKey(accountKey)
// m/44'/1'/0'/1 account zero internal sub path
Log.i(TAG, "******************account zero internal sub path******************************")
toBip32ExtendedInternalKey(accountKey)
}
- LogCat
I/Key: **********use root private key to generate sub private/public key and address****************
D/Key: is isHardened: true
D/Key: Account 0 Path: M/44H/1H/0H
D/Key: Account 0 Extended Public Key: tpubDD5zzoPYBfUmGpzBTC9A4duYLkDzzYyggcrizbaTXtnA8fVYP6K7EGKEuQS3Vb2fbQVSCauy6QByCUCqoSj9FrFFa12nhY7NodRGvTDWbva
D/Key: Account 0 Extended Private Key: tprv8gPxrPMJ3Ho6PMxPZYUZfEFRmii4qDnn7KFwi5YA7cymJBEmkhVX3mhNjFWn2QKU1X2YfDJgdTJkVjNPD6mDjDnGMEALBTZravHZR5ZmCDG
I/Key: ***********************account zero external sub path*************************
D/Key: BIP32 Derivation Path: M/44H/1H/0H/0
D/Key: BIP32 Extended Public Key: tpubDEj5yioiXvjWw2CM89aQmMrzUn6UVbhCVM2MkaXjTpHdtZkgst5WcPBnc4tuzoe6zipyAAwFwA4i59G9VAFBEoohKagokFDoJjHbKT7JYQM
D/Key: BIP32 Extended Private Key: tprv8i33qJmUPZ3r3ZAZEVupMxCsukaYLGWHv3RaU4VS3YVF45VvFVFvRtZvRuDveQ37Y6WDUmNTEFTuHzL7qtiR51Meyv23TK1DBfkHnjKnEyn
D/Key: #0 03b04c220ae19ea44a0cb7fd94e666c78ec7ec4418513ab7159955488c8d09bc15
D/Key: #0 mrYkGBV53ChLcf1uF3qnLHdHnKbtH94B8n
D/Key: #1 03405b717edba320ba8923862fdfb4e48704790ac83723c0be10b2ee4edeff0c64
D/Key: #1 miwLBNAwqo4BBJPhLUTugbUQ4nYNky9e1m
I/Key: ******************account zero internal sub path******************************
D/Key: BIP32 Derivation Path: M/44H/1H/0H/1
D/Key: BIP32 Extended Public Key: tpubDEj5yioiXvjWx3VPKCEpdLox4TCjS53gVjkCMoAb7kLrNUTNRBkH4gDA832hYT7MY4rHUPtWiR7YGrf3WxRgHFWAWiKquYxpMmXHms9hwXU
D/Key: BIP32 Extended Private Key: tprv8i33qJmUPZ3r4aTbRYaEDw9qVRgoGjrmvS9R5H8HhUYTXzCbnnvgtBbHwthZCZHC84hCyc4Nm4d1rQ7WfgVwmXPNHYGfW5t2Fw5ocXLWChj
D/Key: #0 023919a545f2d16e604250fb39ef174945bc746912895c0281c65021d9209dd4f2
D/Key: #0 mqzLAZZZ8tFvbs6d2tK2fAYQrrPyVLrifH
D/Key: #1 031f1c53dc75a1e716c4d698ef89a71044ff76f4125c769e446b805bf8801b097d
D/Key: #1 mttwgLNAr1Kw8UnxE7hBBB71THb9zXEBgm