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:
- Created 3 xprv keys and xpubs keys using this tool here
- I used custome path
m/44'/0'/0'
to have hardened keys because Bcoin wallet requires BIP44 hardened keys - I created 3 watch-only wallets using the
xpub
keys. - 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 thereceivingAddress
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 thexpub
, 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.
createAddress()
? Is it not generating a child key? Each time I callcreateAddress()
it generates a new child address with a certain index. What am I misunderstanding here.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.