<p>To complement the answer above, the Bitcoin <a href="https://github.com/bitcoin/bitcoin/blob/master/src/chainparams.cpp" rel="nofollow">chainparams.cpp</a> file contains the following information to assist in achieving BIP 32 compliance:</p>

<p>Mainnet:</p>

<pre><code>base58Prefixes[EXT_PUBLIC_KEY] = boost::assign::list_of(0x04)(0x88)(0xB2)(0x1E).convert_to_container&lt;std::vector&lt;unsigned char&gt; &gt;();
base58Prefixes[EXT_SECRET_KEY] = boost::assign::list_of(0x04)(0x88)(0xAD)(0xE4).convert_to_container&lt;std::vector&lt;unsigned char&gt; &gt;();
</code></pre>

<p>Testnet:</p>

<pre><code>base58Prefixes[EXT_PUBLIC_KEY] = boost::assign::list_of(0x04)(0x35)(0x87)(0xCF).convert_to_container&lt;std::vector&lt;unsigned char&gt; &gt;();
base58Prefixes[EXT_SECRET_KEY] = boost::assign::list_of(0x04)(0x35)(0x83)(0x94).convert_to_container&lt;std::vector&lt;unsigned char&gt; &gt;();
</code></pre>

<p>The bitcoin-explorer (bx) command is used below, with the 3rd worst possible brain wallet seed in the world. Additionally, bx was compiled for using testnet not mainnet. Hence, focus on where <strong>04358394</strong> for <strong>tprv</strong> and <strong>043587cf</strong> for <strong>tpub</strong> that appears in the examples below.</p>

<p>% echo '0' | bx base16-encode | bx sha512 | bx hd-new 
<code>tprv8ZgxMBicQKsPdcvudXaExR6Wdz5VgdjGeZHsw5bjnypoxrCxnYsyVq2v9cPTzDsnyLAL1v4Z2tM3Rp2AA6vv9WDbNBtui5QEZTWYucefZox</code></p>

<p>% echo '0' | bx base16-encode | bx sha512 | bx hd-new | bx base58check-decode</p>

<pre><code>wrapper
{
    checksum 3287061687
    payload 3583940000000000000000004b0dc73821a026c0c71d07a7655968352b52d7e5896dd3907df121f914f9743900a6b53d2f0e384dc6ced2caeb36f0c9cc6f3ab0677f73c31aca87b62bbcc9fe78
    version 4
}
</code></pre>

<p>% echo '0' | bx base16-encode | bx sha512 | bx hd-new | bx base58-decode
<code>04358394</code>0000000000000000004b0dc73821a026c0c71d07a7655968352b52d7e5896dd3907df121f914f9743900a6b53d2f0e384dc6ced2caeb36f0c9cc6f3ab0677f73c31aca87b62bbcc9fe78<code>b794ecc3</code></p>

<p>% echo '0' | bx base16-encode | bx sha512 | bx hd-new | bx hd-public
<code>tpubD9nYDjxx7QwpsEhUiS7MDJ68p25nu9yxEt1AofKmEjmL3kozwA3z8G4Cm556YCYYJwfnix2GLVTtCHZq79R8UsRZBJVRPtD9HKNQBzDZVo4</code></p>

<p>% echo '0' | bx base16-encode | bx sha512 | bx hd-new | bx hd-public | bx base58-decode
<code>043587cf</code>01cf633b1c00000000c023d310564ead16b50e678b2ff20e2d0a0f210fcf9c603ebd16f17d0510663802605d719a28b2707dcc9d7a2b804ba70242330e7010161aec4f86a0771d9909e504b705bf</p>

CC BY-SA 3.0
2015-07-31T01:42:42.110Z
2015-07-31T01:42:42.110Z
skaht
<p>Just like in regular bitcoin addresses (or anything base 58 encoded), the version bytes don't get encoded by themselves. As described in the <a href="https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki#serialization-format" rel="nofollow">Serialization format</a> section, there is 78 byte payload that gets versioned and checksumed before then being encoded into base 58:</p>

<blockquote>
  <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)</li>
  </ul>
</blockquote>

<p>There's not a byte-to-character mapping when you do base58 serialization of a multi-byte structure. It's not like 0x04 becomes 'x', 0x88 becomes 'p', etc. Rather, the whole 86 (78 payload + 4 version + 4 checksum) byte structure gets encoded to something that starts with 'xpriv' when the 4 most significant bytes of the 86 bytes structure are [0x04, 0x88, 0xad, 0xe4].</p>

CC BY-SA 3.0
2015-07-31T02:45:46.043Z
So it only works for 78 byte structures? If so, I understand
Wizard Of Ozzie
CC BY-SA 3.0
2015-07-31T11:06:39.760Z
@WizardOfOzzie it's likely that a different sequence of 4 bytes could get a 50 byte structure to start with xpriv, for example, but yes, those exact bytes were chosen for a 78 byte structure.
morsecoder
CC BY-SA 3.0
2015-07-31T12:59:49.950Z
I tried and tried again and it wouldn't work. Then I realised it's not 78, it's actually **82 bytes** when you include the 4 byte checksum. In pybitcointools; `changebase(TESTNET_PRIVATE + bytes(bytearray(78)), 256, 58) >>> 'tprv8ZgxMBicQKsPcsbCVeqqF1KVdH7gwDJbxbzpCxDUsoXHdb6SnTPYxdwSAKDC6KKJzv7khnNWRAJQsRA8BBQyiSfYnRt6zuu4vZQGKhCz4Jb`. :)
Wizard Of Ozzie
CC BY-SA 3.0
2015-07-31T13:26:59.540Z
@WizardOfOzzie, good point! I've updated my answer.
morsecoder
CC BY-SA 4.0
2020-02-16T15:26:26.590Z
For future reference: all bitcoin extended key version bytes can be found at https://electrum.readthedocs.io/en/latest/xpub_version_bytes.html
thalisk
CC BY-SA 3.0
2015-07-30T17:15:49.333Z
2015-07-31T13:25:59.993Z
2015-07-31T13:25:59.993Z
morsecoder
<p>Converting the BIP32 version bytes - <code>x04\x88\xad\xe4</code> - from base256 to base58 and base58Check returns <code>7irrX</code> and <code>1kz713TZjju</code>, respectively.</p>

<p>I thought the version bytes represented <code>xprv</code>. What am I missing?</p>

<p>I'm using <em>pybitcointools</em>, if that makes a difference.</p>

CC BY-SA 3.0
2015-07-30T16:55:12.760Z
Why from base256? 0x04 0x88 0xAD 0xE4 should directly go into base58 IMO.
Jonas Schnelli
CC BY-SA 3.0
2015-07-30T17:10:24.187Z
@JonasSchnelli only because the pybitcointools library defines it that way. It makes no difference if it's encoded as hex
Wizard Of Ozzie
<bip32-hd-wallets><python><base58><base58check>
How does the bip32 version bytes convert to base58?
Wizard Of Ozzie
# How does the bip32 version bytes convert to base58?

(Asked by: username_0 on 2015-07-30)

Converting the BIP32 version bytes - `x04\x88\xad\xe4` - from base256 to base58 and base58Check returns `7irrX` and `1kz713TZjju`, respectively.

I thought the version bytes represented `xprv`. What am I missing?

I'm using *pybitcointools*, if that makes a difference.

**Tags:** bip32-hd-wallets, python, base58, base58check

> **Comments:**
> **username_1**: Why from base256? 0x04 0x88 0xAD 0xE4 should directly go into base58 IMO.  
> **username_0**: @username_1 only because the pybitcointools library defines it that way. It makes no difference if it's encoded as hex  

---

## 2 answers

### Answer by username_3 on 2015-07-30. Score: 6 | Accepted answer

Just like in regular bitcoin addresses (or anything base 58 encoded), the version bytes don't get encoded by themselves. As described in the [Serialization format](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki#serialization-format) section, there is 78 byte payload that gets versioned and checksumed before then being encoded into base 58:

> * 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)

There's not a byte-to-character mapping when you do base58 serialization of a multi-byte structure. It's not like 0x04 becomes 'x', 0x88 becomes 'p', etc. Rather, the whole 86 (78 payload + 4 version + 4 checksum) byte structure gets encoded to something that starts with 'xpriv' when the 4 most significant bytes of the 86 bytes structure are [0x04, 0x88, 0xad, 0xe4].

> **Comments:**
> **username_0**: So it only works for 78 byte structures? If so, I understand  
> **username_3**: @username_0 it's likely that a different sequence of 4 bytes could get a 50 byte structure to start with xpriv, for example, but yes, those exact bytes were chosen for a 78 byte structure.  
> **username_0**: I tried and tried again and it wouldn't work. Then I realised it's not 78, it's actually **82 bytes** when you include the 4 byte checksum. In pybitcointools; `changebase(TESTNET_PRIVATE + bytes(bytearray(78)), 256, 58) >>> 'tprv8ZgxMBicQKsPcsbCVeqqF1KVdH7gwDJbxbzpCxDUsoXHdb6SnTPYxdwSAKDC6KKJzv7khnNWRAJQsRA8BBQyiSfYnRt6zuu4vZQGKhCz4Jb`. :)  
> **username_3**: @username_0, good point! I've updated my answer.  
> **username_4**: For future reference: all bitcoin extended key version bytes can be found at https://electrum.readthedocs.io/en/latest/xpub_version_bytes.html  

---

### Answer by username_2 on 2015-07-31. Score: 1

To complement the answer above, the Bitcoin [chainparams.cpp](https://github.com/bitcoin/bitcoin/blob/master/src/chainparams.cpp) file contains the following information to assist in achieving BIP 32 compliance:

Mainnet:

```
base58Prefixes[EXT_PUBLIC_KEY] = boost::assign::list_of(0x04)(0x88)(0xB2)(0x1E).convert_to_container<std::vector<unsigned char> >();
base58Prefixes[EXT_SECRET_KEY] = boost::assign::list_of(0x04)(0x88)(0xAD)(0xE4).convert_to_container<std::vector<unsigned char> >();

```

Testnet:

```
base58Prefixes[EXT_PUBLIC_KEY] = boost::assign::list_of(0x04)(0x35)(0x87)(0xCF).convert_to_container<std::vector<unsigned char> >();
base58Prefixes[EXT_SECRET_KEY] = boost::assign::list_of(0x04)(0x35)(0x83)(0x94).convert_to_container<std::vector<unsigned char> >();

```

The bitcoin-explorer (bx) command is used below, with the 3rd worst possible brain wallet seed in the world. Additionally, bx was compiled for using testnet not mainnet. Hence, focus on where **04358394** for **tprv** and **043587cf** for **tpub** that appears in the examples below.

% echo '0' | bx base16-encode | bx sha512 | bx hd-new
`tprv8ZgxMBicQKsPdcvudXaExR6Wdz5VgdjGeZHsw5bjnypoxrCxnYsyVq2v9cPTzDsnyLAL1v4Z2tM3Rp2AA6vv9WDbNBtui5QEZTWYucefZox`

% echo '0' | bx base16-encode | bx sha512 | bx hd-new | bx base58check-decode

```
wrapper
{
    checksum 3287061687
    payload 3583940000000000000000004b0dc73821a026c0c71d07a7655968352b52d7e5896dd3907df121f914f9743900a6b53d2f0e384dc6ced2caeb36f0c9cc6f3ab0677f73c31aca87b62bbcc9fe78
    version 4
}

```

% echo '0' | bx base16-encode | bx sha512 | bx hd-new | bx base58-decode
`04358394`0000000000000000004b0dc73821a026c0c71d07a7655968352b52d7e5896dd3907df121f914f9743900a6b53d2f0e384dc6ced2caeb36f0c9cc6f3ab0677f73c31aca87b62bbcc9fe78`b794ecc3`

% echo '0' | bx base16-encode | bx sha512 | bx hd-new | bx hd-public
`tpubD9nYDjxx7QwpsEhUiS7MDJ68p25nu9yxEt1AofKmEjmL3kozwA3z8G4Cm556YCYYJwfnix2GLVTtCHZq79R8UsRZBJVRPtD9HKNQBzDZVo4`

% echo '0' | bx base16-encode | bx sha512 | bx hd-new | bx hd-public | bx base58-decode
`043587cf`01cf633b1c00000000c023d310564ead16b50e678b2ff20e2d0a0f210fcf9c603ebd16f17d0510663802605d719a28b2707dcc9d7a2b804ba70242330e7010161aec4f86a0771d9909e504b705bf


---

