I am puzzled by the hardened key derivation from a watch-only BIP44 wallet.

Let me first explain what I am trying to do.

  • I am implementing a 2-of-3 multisig scheme
  • I am using Bcoin node and wallet
  • All 3 keys are externally generated
  • I want to use Bcoin watch-only wallet

ts to create addresses and scripts

This is what I have done so far:

  1. Created 3 xprv keys and xpubs keys using this tool here
  2. I used custome path m/44'/0'/0' to have hardened keys because Bcoin wallet requires BIP44 hardened keys
  3. I created 3 watch-only wallets using the xpub keys.
  4. Then I added each of the keys as shared keys to each other (I only really have to do this for one wallet)

The code is as below:

const HSM_accountKey = "xpub_HSM...."
const IC_accountKey = "xpub_IC..."
const Paper_accountKey = "xpub_Paper..."

const result_wallet_HSM = await walletClient.createWallet('wallet_HSM', options_wallet);
const result_wallet_IC = await walletClient.createWallet('wallet_IC', options_wallet);
const result_wallet_Paper = await walletClient.createWallet('wallet_Paper', options_wallet);

const wallet_IC_account = await wallet_IC.getAccount('default');
const wallet_Paper_account = await wallet_Paper.getAccount('default');
const result_addkey1 = await wallet_HSM.addSharedKey('default', wallet_IC_account.accountKey);
const result_addkey2 = await wallet_HSM.addSharedKey('default', wallet_Paper_account.accountKey);

Now I have created a watch-only 2-of-3 multisig wallet. What I want to do now is to create addresses. I do not want to derive more child extended keys. I only need one derivation level. Now I want to create multisig addresses for my users.

// Create addresses from each wallet
const wallet_HSM_address = await wallet_HSM.createAddress('default');
const wallet_IC_address = await wallet_IC.createAddress('default');
const wallet_Paper_address = await wallet_Paper.createAddress('default');

// Keyrings for script generation
const keyring_HSM = KeyRing.fromJSON(wallet_HSM_address);
const keyring_IC = KeyRing.fromJSON(wallet_IC_address);
const keyring_Paper = KeyRing.fromJSON(wallet_Paper_address);

//Get the pubkeys from each wallet
const pubKeys = [keyring_HSM.publicKey, keyring_HSM.publicKey, keyring_Paper.publicKey];

const multiSigScript = Script.fromMultisig(m, n, pubKeys);
const receivingAddress = multiSigScript.getAddress().toBase58(network); 

I have a feeling that I am going about this step in a round-about way but you get what I am trying to do.

To re-state, what I want to do now is to create individual receiving addresses for my users and the corresponding P2SH scripts.

Now, this is what I need help understanding.

  • According to BIP32, you cannot derive hardened public keys with CDKpub. You need the xprv to derive child keys.
  • However, from what I have done, I can derive child public keys. I have sent tesetnet coins to these addresses (receivingAddress in the code above) and they work. The wallet receives coins.
  • How then, was the watch-only wallet able to create hardened child public keys? I did not input xprv to the wallet.
  • Or, is it that I am NOT creating hardened child public keys when I used the path m/44'/0'/0'? Is the receivingAddress I am creating in the above code not a hardened one? Meaning that if someone is able to find the child private keys of one user and the xpub, they will be able to derive all the chile private keys?

My apologies for the long question. I am a bit confused as to how the wallets are able to create child public keys without having any private key in the wallet.

Thank you.

  • 1
    I don't see anywhere in your code where you are deriving child keys. All I see is that you've generated 3 private/public keypairs and made a multisig out of them. That's nothing special.
    – Ava Chow
    Jul 24, 2019 at 15:49
  • Thanks Andrew. What is the difference between deriving child keys and generating child keys? What I am trying to do is to generate child keys for users of the system for them to have a receiving address. What is my misunderstanding? And then later on I need to generate the corresponding child private keys to spend the coins received to those address. So I want to be able to generate those private keys from the index.
    – madu
    Jul 25, 2019 at 0:05
  • 1
    deriving child keys and generating child keys mean the same thing. But, AFAICT, you aren't doing that in your code at all.
    – Ava Chow
    Jul 25, 2019 at 2:16
  • @AndrewChow Thanks so much. Could you kindly tell me what it is happening at createAddress()? Is it not generating a child key? Each time I call createAddress() it generates a new child address with a certain index. What am I misunderstanding here.
    – madu
    Jul 25, 2019 at 2:18
  • 1
    I was not aware that createAddress() is creating a new address. If that is the case, it is not deriving hardened addresses, especially if you only gave it an xpub.
    – Ava Chow
    Jul 25, 2019 at 2:34

1 Answer 1


You are not deriving hardened child keys. You cannot derive hardened keys from an xpub. Just because the xpub itself is hardened does not mean that its children are.

  • Thank you Andrew. That is probably the reason. If you have idea how to explicitly generate hardened child keys please share.
    – madu
    Jul 25, 2019 at 2:41

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.