4

I am using ethereumjs-wallet npm package to generate a unique wallet address for each user. However instead of using the mnemonic/seed, a public extended key xpub will be used instead to keep the private keys secure.

However the following code which I used is throwing the error

AssertionError [ERR_ASSERTION]: Could not derive hardened child key

The code works fine if we were to replace

const hdWallet = hdkey.fromExtendedKey("xpub...")

with

const hdWallet = hdkey.fromSeed("seed or mnemonic ...")

It appears to be a problem with the hdPath, but which exactly is the problem? And how can we solve it?

JS Code

var hdkey = require('ethereumjs-wallet/hdkey')

const hdWallet = hdkey.fromExtendedKey("xpub...")
const hdPath  = "m/44'/60'/0'/1/0"
const node = hdWallet.derivePath(hdPath)
const nodeWallet = node.getWallet()
const address = nodeWallet.getAddressString()
return address
1

I know its not exactly what you're after, but you can derive form non hardened:

var hdkey = require('ethereumjs-wallet/hdkey')

const hdWallet = hdkey.fromExtendedKey("xpub6Cy7dUR4ZKF22HEuVq7epRgRsoXfL2MK1RE81CSvp1ZySySoYGXk5PUY9y9Cc5ExpnSwXyimQAsVhyyPDNDrfj4xjDsKZJNYgsHXoEPNCYQ")
const hdPath  = "m/44/60/0/1/0"
const node = hdWallet.derivePath(hdPath)
const nodeWallet = node.getWallet()
const address = nodeWallet.getAddressString()
console.log(address)
1

Hardened child keys are the ones using apostrophes (m/44'/60'/0'). By design, hardened child keys cannot be derived from extended public keys, only from private (xprv).

To get around this, you derive the part that includes hardened children directly from your private key and leave the last non-hardened part of the path to be derived for each user.

var privateRoot = hdkey.fromMasterSeed("0x0bad5eed")
var publicParentExtendedKey = privateRoot
      .derivePath("m/44'/60'/0'/1") //missing the final 0
      .publicExtendedKey()

console.log(publicParentExtendedKey) //xpub6Eaz...M6tmjr7t

You can store the publicParentExtendedKey on the machine that generates user addresses. Then, for each user, you generate an address like so:

var userIndex = 0
var derivedRoot = hdkey.fromExtendedKey(publicParentExtendedKey)
var derivedChild = derivedRoot.deriveChild(userIndex)
console.log(derivedChild.getWallet().getChecksumAddressString())
//0x345e0aDfc2ABf06C0B1dEf36CD556089Cd0c76fC

Then to actually access the funds, you start from your private root and derive the full path.

var userPrivKey = privateRoot
      .derivePath(`m/44'/60'/0'/1/${userIndex}`)
      .getWallet()
      .getPrivateKey()

EDIT: This is essentially what @dalton-hsu answered but with code samples.

0

Reference to this tool https://iancoleman.io/bip39/ if you want to derive the path m/44'/60'/0'/1/0 using the extended public key.

you can derive m/44'/60'/0'/1 first by using the BIP32 root key, then derive the m/0 using the extended public key of m/44'/60'/0'/1

here is the example code:

const hdkey = require('ethereumjs-wallet/hdkey');
const bip39 = require('bip39');

// This mnemonic code is generate for the example code, don't use this in your project
const mnemonic = 'traffic farm flock path ethics blast barrel sweet jealous note village flash write job pattern impose cash elevator all audit relax omit visa inspire';

bip39.mnemonicToSeed(mnemonic).then(function(seed){
    const hdWallet = hdkey.fromMasterSeed(seed);
    /*
        Ethereum derivePath m/44'/60'/0'/0
        Ledger derivePath m/44'/60'/0'
    */
    const masterNode = hdWallet.derivePath("m/44'/60'/0'");
    const masterExtendedPublicKey = masterNode.publicExtendedKey();

    /* Notice: for security reason, above code must run in offline enviroment
               just keep extended public key to the online code
    */
    const myWallet = hdkey.fromExtendedKey(masterExtendedPublicKey);

    for(let i=0;i<5;i++){
        const node = myWallet.derivePath("m/"+i);
        const nodeWallet = node.getWallet();
        const address = nodeWallet.getAddressString();
        console.log(address);
    }

});

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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