<p>The <a href="https://github.com/jimeh/node-base58" rel="nofollow noreferrer">base58 module</a> is designed for Flickr; it uses a different alphabet ordering than Bitcoin, and only handles fairly small numbers not the size Bitcoin needs. For Bitcoin use <a href="https://github.com/cryptocoinjs/bs58" rel="nofollow noreferrer">bs58 module</a> plus one other fix: the checksum is not the whole SHA256d but only the first 4 bytes, or since you store it in hex the first 8 chars:</p>
<pre><code>const bs58 = require ('bs58');
... same except 
var data_with_checksum = data + sha_256_2.substring(0,8);
var result = bs58.encode(Buffer.from(data_with_checksum,'hex'));
-&gt;
xprv9s21ZrQH143K3mfksXFtCXmQBeuA4wJqhcEJKgxzocJqadRRZZZvW2quiv9XiRw9BipJgvjbGLipAH5ZT9868uA4LFwJexUaJus2EVe4FXq
</code></pre>
<p>However, as the bs58 page says, there is a <a href="https://github.com/bitcoinjs/bs58check" rel="nofollow noreferrer">bs58check module</a> that combines the checksum and encode for you:</p>
<pre><code>const bs58check = require ('bs58check');
... first two parts only, computing data
var result = bs58check.encode(Buffer.from(data,'hex'));
-&gt; same
</code></pre>
<p>And the <a href="https://github.com/bitcoinjs" rel="nofollow noreferrer">same github user</a> also has <code>bip32</code> and <code>bip32-utils</code> modules that probably do the whole job for you, although the readme's are fairly limited and you'll probably need to look at the code (I didn't have time).</p>

CC BY-SA 4.0
2021-01-27T09:33:19.550Z
2021-01-27T09:33:19.550Z
dave_thompson_085
<p>I'm trying to learn how HD wallets work internally by writing javascript code that can derive child keys from parent keys. So far, the only thing I've been able to do is convert a BIP39-generated seed to a master key and chain code. I can do that successfully.</p>
<p>Now I would like to convert the master key to an extended key, the long string that starts with <code>xprv</code>. I'd also like to know how to derive child keys from these, and obtain the private key for signing with any of the accounts.</p>
<p>I've been using this confusing <a href="https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki" rel="nofollow noreferrer">BIP32 document</a> as a guide so far, but I'm getting nowhere with it. As far as I can tell, before you derive any keys, you have to serialize the ones you already have.</p>
<p>The serialization process is described like this:</p>
<blockquote>
<p>Extended public and private keys are serialized as follows:</p>
<ul>
<li>4 byte: version bytes (mainnet: 0x0488B21E public, 0x0488ADE4 private;
testnet: 0x043587CF public, 0x04358394 private)</li>
<li>1 byte: depth: 0x00 for master nodes, 0x01 for level-1 derived keys, ....</li>
<li>4 bytes: the fingerprint of the parent's key (0x00000000 if master key)</li>
<li>4 bytes: child number. This is ser32(i) for i in xi = xpar/i, with xi the key
being serialized. (0x00000000 if master key)</li>
<li>32 bytes: the chain code</li>
<li>33 bytes: the public key or private key data (serP(K) for public keys,
0x00 || ser256(k) for private keys)<br /></li>
</ul>
<p>This 78 byte structure can be
encoded like other Bitcoin data in Base58, by first adding 32 checksum
bits (derived from the double SHA-256 checksum), and then converting
to the Base58 representation. This results in a Base58-encoded string
of up to 112 characters. Because of the choice of the version bytes,
the Base58 representation will start with &quot;xprv&quot; or &quot;xpub&quot; on mainnet,
&quot;tprv&quot; or &quot;tpub&quot; on testnet.</p>
</blockquote>
<p>Here is the javascript code I wrote in attempting to follow these instructions.</p>
<pre><code>const crypto = require ('crypto');
const base58 = require ('base58');

const master_key_hex = '5a02ee5cb622f15a4e7ab3dfe7faa9861738da2ae7bb936c685af4dd7b1fbdee';
const chain_code_hex = 'abfbc73691eaad7ca4eb5e25bc51c5804af221cc27a23460634498497f92c92c';

// the 78 bytes of data
var data = '0488ade4';
data += '00';
data += '00000000';
data += '00000000';
data += chain_code_hex;
data += '00' + master_key_hex;

// add the checksum
const hash_1 = crypto.createHash ('sha256');
var sha_256_1 = hash_1.update (data, 'hex').digest ('hex');
const hash_2 = crypto.createHash ('sha256');
var sha_256_2 = hash_2.update (sha_256_1, 'hex').digest ('hex');

var data_with_checksum = data + sha_256_2;

//data = Buffer.from (data_with_checksum, 'hex');
var result = base58.encode (data_with_checksum);
console.log (result);
</code></pre>
<p>I tried it with a string and with a Buffer and it gave me the same error message both times. The error message is:</p>
<pre><code>Error: Value passed is not a non-negative safe integer.
</code></pre>
<p>I have a feeling I am not even close to doing this right. Does anyone know how it should be coded to derive child keys from a master key?</p>

<bip32-hd-wallets><node.js>
How to convert HD wallet master key to extended key and child keys in javascript?
user114637
# How to convert HD wallet master key to extended key and child keys in javascript?

(Asked by: username_0 on 2021-01-26)

I'm trying to learn how HD wallets work internally by writing javascript code that can derive child keys from parent keys. So far, the only thing I've been able to do is convert a BIP39-generated seed to a master key and chain code. I can do that successfully.

Now I would like to convert the master key to an extended key, the long string that starts with `xprv`. I'd also like to know how to derive child keys from these, and obtain the private key for signing with any of the accounts.

I've been using this confusing [BIP32 document](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki) as a guide so far, but I'm getting nowhere with it. As far as I can tell, before you derive any keys, you have to serialize the ones you already have.

The serialization process is described like this:

> Extended public and private keys are serialized as follows:
>
> * 4 byte: version bytes (mainnet: 0x0488B21E public, 0x0488ADE4 private;
>   testnet: 0x043587CF public, 0x04358394 private)
> * 1 byte: depth: 0x00 for master nodes, 0x01 for level-1 derived keys, ....
> * 4 bytes: the fingerprint of the parent's key (0x00000000 if master key)
> * 4 bytes: child number. This is ser32(i) for i in xi = xpar/i, with xi the key
>   being serialized. (0x00000000 if master key)
> * 32 bytes: the chain code
> * 33 bytes: the public key or private key data (serP(K) for public keys,
>   0x00 || ser256(k) for private keys)
>
> This 78 byte structure can be
> encoded like other Bitcoin data in Base58, by first adding 32 checksum
> bits (derived from the double SHA-256 checksum), and then converting
> to the Base58 representation. This results in a Base58-encoded string
> of up to 112 characters. Because of the choice of the version bytes,
> the Base58 representation will start with "xprv" or "xpub" on mainnet,
> "tprv" or "tpub" on testnet.

Here is the javascript code I wrote in attempting to follow these instructions.

```
const crypto = require ('crypto');
const base58 = require ('base58');

const master_key_hex = '5a02ee5cb622f15a4e7ab3dfe7faa9861738da2ae7bb936c685af4dd7b1fbdee';
const chain_code_hex = 'abfbc73691eaad7ca4eb5e25bc51c5804af221cc27a23460634498497f92c92c';

// the 78 bytes of data
var data = '0488ade4';
data += '00';
data += '00000000';
data += '00000000';
data += chain_code_hex;
data += '00' + master_key_hex;

// add the checksum
const hash_1 = crypto.createHash ('sha256');
var sha_256_1 = hash_1.update (data, 'hex').digest ('hex');
const hash_2 = crypto.createHash ('sha256');
var sha_256_2 = hash_2.update (sha_256_1, 'hex').digest ('hex');

var data_with_checksum = data + sha_256_2;

//data = Buffer.from (data_with_checksum, 'hex');
var result = base58.encode (data_with_checksum);
console.log (result);

```

I tried it with a string and with a Buffer and it gave me the same error message both times. The error message is:

```
Error: Value passed is not a non-negative safe integer.

```

I have a feeling I am not even close to doing this right. Does anyone know how it should be coded to derive child keys from a master key?

**Tags:** bip32-hd-wallets, node.js


---

## 1 answers

### Answer by username_1 on 2021-01-27. Score: 1

The [base58 module](https://github.com/jimeh/node-base58) is designed for Flickr; it uses a different alphabet ordering than Bitcoin, and only handles fairly small numbers not the size Bitcoin needs. For Bitcoin use [bs58 module](https://github.com/cryptocoinjs/bs58) plus one other fix: the checksum is not the whole SHA256d but only the first 4 bytes, or since you store it in hex the first 8 chars:

```
const bs58 = require ('bs58');
... same except 
var data_with_checksum = data + sha_256_2.substring(0,8);
var result = bs58.encode(Buffer.from(data_with_checksum,'hex'));
->
xprv9s21ZrQH143K3mfksXFtCXmQBeuA4wJqhcEJKgxzocJqadRRZZZvW2quiv9XiRw9BipJgvjbGLipAH5ZT9868uA4LFwJexUaJus2EVe4FXq

```

However, as the bs58 page says, there is a [bs58check module](https://github.com/bitcoinjs/bs58check) that combines the checksum and encode for you:

```
const bs58check = require ('bs58check');
... first two parts only, computing data
var result = bs58check.encode(Buffer.from(data,'hex'));
-> same

```

And the [same github user](https://github.com/bitcoinjs) also has `bip32` and `bip32-utils` modules that probably do the whole job for you, although the readme's are fairly limited and you'll probably need to look at the code (I didn't have time).


---

