Files

177 lines
8.1 KiB
Markdown
Raw Permalink Normal View History

2020-03-12 00:35:35 +01:00
# 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
2020-03-27 21:55:48 +01:00
Base58Check is used to encode a **payload** and a **version** byte. It is done by following the steps described below.
2020-03-12 00:35:35 +01:00
2020-03-27 21:55:48 +01:00
1. Take the version byte and the payload bytes, and concatenate them together (bytewise):
2020-03-12 00:35:35 +01:00
```
version || payload
```
2020-03-27 21:55:48 +01:00
2. Compute the checksum by taking the first four bytes of the double SHA256 hash function of this concatenation.
2020-03-12 00:35:35 +01:00
```
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
```
2020-03-27 21:55:48 +01:00
2. Take the version byte corresponding to it (`0x80` for mainnet, `0xef` for testnet), and concatenate them together:
2020-03-12 00:35:35 +01:00
```
801e99423a4ed27608a15a2616a2b0e9e52ced330ac530edcc32c8ffc6a526aedd
```
3. Compute the checksum by performing the double SHA256 on it, and by taking the first four bytes of this hash:
```
2020-03-27 21:55:48 +01:00
SHA256( SHA256( 801e99423a4ed27608a15a2616a2b0e9e52ced330ac530edcc32c8ffc6a526aedd ) ) = c47e83ffafda3ba4396e1bc6a648515e5fc9aa95910af6a4429537b87fb7b474
2020-03-12 00:35:35 +01:00
```
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:
```
2020-03-27 21:55:48 +01:00
SHA256( SHA256( 00211b74ca4686f81efda5641767fc84ef16dafe0b ) ) = 388c8d1d3f70ec351abf400fadf7756418e6b3835c01fe78206b39ec1ab8a37a
2020-03-12 00:35:35 +01:00
```
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.