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

results matching ""

    No results matching ""