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.

  • Why from base256? 0x04 0x88 0xAD 0xE4 should directly go into base58 IMO. Jul 30 '15 at 16:55
  • @JonasSchnelli only because the pybitcointools library defines it that way. It makes no difference if it's encoded as hex Jul 30 '15 at 17:10

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 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].

  • So it only works for 78 byte structures? If so, I understand Jul 31 '15 at 2:45
  • @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
    Jul 31 '15 at 11:06
  • 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. :) Jul 31 '15 at 12:59
  • @WizardOfOzzie, good point! I've updated my answer.
    – morsecoder
    Jul 31 '15 at 13:26
  • For future reference: all bitcoin extended key version bytes can be found at electrum.readthedocs.io/en/latest/xpub_version_bytes.html
    – Thalis K.
    Feb 16 '20 at 15:26

To complement the answer above, the Bitcoin chainparams.cpp file contains the following information to assist in achieving BIP 32 compliance:


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> >();


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

    checksum 3287061687
    payload 3583940000000000000000004b0dc73821a026c0c71d07a7655968352b52d7e5896dd3907df121f914f9743900a6b53d2f0e384dc6ced2caeb36f0c9cc6f3ab0677f73c31aca87b62bbcc9fe78
    version 4

% echo '0' | bx base16-encode | bx sha512 | bx hd-new | bx base58-decode 043583940000000000000000004b0dc73821a026c0c71d07a7655968352b52d7e5896dd3907df121f914f9743900a6b53d2f0e384dc6ced2caeb36f0c9cc6f3ab0677f73c31aca87b62bbcc9fe78b794ecc3

% 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 043587cf01cf633b1c00000000c023d310564ead16b50e678b2ff20e2d0a0f210fcf9c603ebd16f17d0510663802605d719a28b2707dcc9d7a2b804ba70242330e7010161aec4f86a0771d9909e504b705bf

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.