Base58check encoding

In Bitcoin Cash, an encoding used for keys and addresses is Base58Check. This is a Base58 encoding format that unambiguously encodes the type of data in the first few characters (the version) and includes an error detection code in the last few characters (the checksum). Its goal is to make it easier to copy and to share information, by using a QR code for instance.

To encode a Bitcoin Cash address, it is however recommended to use the CashAddr encoding instead, because it prevents confusion with Bitcoin-BTC addresses.

Base58

Base58's goal is to avoid copy error and enable doublecliking selection. That is why it uses all the alphanumeric symbols excluding 0, O, I and l, these last characters being hard to ditinguish from one another in some fonts.

Base58 alphabet:

123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz

Base58 symbol chart:

Value Character Value Character Value Character Value Character
0 1 15 G 30 X 45 n
1 2 16 H 31 Y 46 o
2 3 17 J 32 Z 47 p
3 4 18 K 33 a 48 q
4 5 19 L 34 b 49 r
5 6 20 M 35 c 50 s
6 7 21 N 36 d 51 t
7 8 22 P 37 e 52 u
8 9 23 Q 38 f 53 v
9 A 24 R 39 g 54 w
10 B 25 S 40 h 55 x
11 C 26 T 41 i 56 y
12 D 27 U 42 j 57 z
13 E 28 V 43 k
14 F 29 W 44 m

Version bytes

In the Base58Check encoding, the version byte indicates the type of the data encoded. The mainnet version bytes are:

Type Hex value Decimal value Base58 prefix
P2PKH address 0x00 0 1
P2SH address 0x05 5 3
Private key (WIF) 0x80 128 5
Private key (WIF-compressed) 0x80 128 K or L
Extended private key 0x0488ade4 76066276 xpub
Extended public key 0x0488b21e 76067358 xprv

The testnet version bytes are:

Type Hex value Decimal value Base58 prefix
Testnet P2PKH address 0x6f 111 m or n
Testnet P2SH address 0xc4 196 2
Testnet private key (WIF) 0xef 239 9
Testnet private key (WIF-compressed) 0xef 239 c
Testnet extended private key 0x043587cf 70617039 tpub
Testnet extended public key 0x04358394 70615956 tprv

Encoding

Base58Check is used to encode a payload and a version byte. It is done by following the steps described below.

  1. Take the version byte and the payload bytes, and concatenate them together (bytewise):

    version || payload

  2. Compute the checksum by taking the first four bytes of the double SHA256 hash function of this concatenation.

    checksum = SHA256( SHA256( version || payload ) )[:4]

  3. Concatenate all three of them together:

    version || payload || checksum

  4. Encode the result with Base58. Note that each leading zero bytes are encoded with the character 1 which is added to the string.

    Base58( version || payload || checksum )

Encoding a private key (Wallet Import Format)

Private keys in Bitcoin Cash are usually encoded with Base58Check. This is known as Wallet Import Format (WIF).

Steps to encode a private key:

  1. Take a private key, i.e., a number between 0 and the order of the generator point (G) of secp256k1. Let's consider the following private key (32-byte array):

    1e99423a4ed27608a15a2616a2b0e9e52ced330ac530edcc32c8ffc6a526aedd

  2. Take the version byte corresponding to it (0x80 for mainnet, 0xef for testnet), and concatenate them together:

    801e99423a4ed27608a15a2616a2b0e9e52ced330ac530edcc32c8ffc6a526aedd

  3. Compute the checksum by performing the double SHA256 on it, and by taking the first four bytes of this hash:

    SHA256( SHA256( 801e99423a4ed27608a15a2616a2b0e9e52ced330ac530edcc32c8ffc6a526aedd ) ) = c47e83ffafda3ba4396e1bc6a648515e5fc9aa95910af6a4429537b87fb7b474

  4. Concatenate the result from step 2 and the checksum together:

    801e99423a4ed27608a15a2616a2b0e9e52ced330ac530edcc32c8ffc6a526aeddc47e83ff

  5. Encode it with Base58. The result is the Wallet Import Format, or WIF, of the private key. If it is a mainnet (uncompressed) private key, it will always start with a 5.

    5J3mBbAH58CpQ3Y5RNJpUKPE62SQ5tfcvU2JpbnkeyhfsYB1Jcn

If you want to derive a compressed public key from this private key, which is usually done in every modern wallets, simply add the prefix 0x01 to the private key bytes:

1e99423a4ed27608a15a2616a2b0e9e52ced330ac530edcc32c8ffc6a526aedd01

and follow the steps (2-5) described above, to get the encoded private key:

KxFC1jmwwCoACiCAWZ3eXa96mBM6tb3TYzGmf6YwgdGWZgawvrtJ

It is known as the WIF-compressed format. Even if the private key is not compressed, the wallet will take this encoding into account and will derive a compressed public key from it. Note that WIF-compressed private keys always start with a K or a L.

Encoding a Bitcoin Cash address

Addresses in Bitcoin Cash can sometimes be encoded with Base58Check. These encoded addresses are called legacy address. Even if this format is still supported by various wallets, it is strongly recommended to use CashAddr encoding instead.

Steps to encode a legacy address:

  1. Take an address payload, i.e., the hash of a public key (P2PKH) or a redeem script (P2SH) which is a 20-byte array:

    211b74ca4686f81efda5641767fc84ef16dafe0b

  2. Take the corresponding version byte (0x00 for mainnet P2PKH, 0x05 for mainnet P2SH, 0x6f for testnet P2PKH, 0xc4 for testnet P2SH), and concatenate them together. In our case, this is a mainnet P2PKH address:

    00211b74ca4686f81efda5641767fc84ef16dafe0b

  3. Compute the checksum by performing the double SHA256 on it, and by taking the first four bytes of this hash:

    SHA256( SHA256( 00211b74ca4686f81efda5641767fc84ef16dafe0b ) ) = 388c8d1d3f70ec351abf400fadf7756418e6b3835c01fe78206b39ec1ab8a37a

  4. Concatenate the result from step 2 and the checksum together:

    00211b74ca4686f81efda5641767fc84ef16dafe0b388c8d1d

  5. Encode it with Base58. Note that each leading zero byte must be encoded with the prefix 1 which is appended to the beginning of the string. Thus, if it is a mainnet P2PKH legacy address (0x00 version byte), it will always start with a 1.

    1424C2F4bC9JidNjjTUZCbUxv6Sa1Mt62x

Important notice. Please do not use this encoding for P2SH address. It can (and should) be deactivated by wallets in order to prevent to send funds to P2SH-embedded SegWit addresses, which are not supported by the Bitcoin Cash protocol.