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.
Take the version byte and the payload bytes, and concatenate them together (bytewise):
version || payload
Compute the checksum by taking the first four bytes of the double SHA256 hash function of this concatenation.
checksum = SHA256( SHA256( version || payload ) )[:4]
Concatenate all three of them together:
version || payload || checksum
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:
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
Take the version byte corresponding to it (
0x80
for mainnet,0xef
for testnet), and concatenate them together:801e99423a4ed27608a15a2616a2b0e9e52ced330ac530edcc32c8ffc6a526aedd
Compute the checksum by performing the double SHA256 on it, and by taking the first four bytes of this hash:
SHA256( SHA256( 801e99423a4ed27608a15a2616a2b0e9e52ced330ac530edcc32c8ffc6a526aedd ) ) = c47e83ffafda3ba4396e1bc6a648515e5fc9aa95910af6a4429537b87fb7b474
Concatenate the result from step 2 and the checksum together:
801e99423a4ed27608a15a2616a2b0e9e52ced330ac530edcc32c8ffc6a526aeddc47e83ff
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:
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
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
Compute the checksum by performing the double SHA256 on it, and by taking the first four bytes of this hash:
SHA256( SHA256( 00211b74ca4686f81efda5641767fc84ef16dafe0b ) ) = 388c8d1d3f70ec351abf400fadf7756418e6b3835c01fe78206b39ec1ab8a37a
Concatenate the result from step 2 and the checksum together:
00211b74ca4686f81efda5641767fc84ef16dafe0b388c8d1d
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 a1
.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.