From Key to Address
A Bitcoin address is simply a human-readable encoding of a script template. The path from a private key to an address looks like this:
An address encodes a hash of a public key (or script), not the key itself. The actual public key is only revealed when spending, providing an extra layer of security against potential future attacks on elliptic curve cryptography.
Base58Check Encoding
The original Bitcoin address format (still used for P2PKH and P2SH). Base58 is like Base64 but removes ambiguous characters:
Removed Characters
0(zero) andO(uppercase O): look alikeI(uppercase I) andl(lowercase L): look alike+and/: problematic in URLs
Encoding Steps
- Take the raw data (e.g., 20-byte pubkey hash)
- Prepend a version byte (0x00 for P2PKH mainnet, 0x05 for P2SH)
- Compute checksum: first 4 bytes of
SHA256d(version + data) - Append checksum to get
version + data + checksum - Encode the result in Base58
// Example: P2PKH address creation
pubkey_hash = RIPEMD160(SHA256(public_key)) // 20 bytes
versioned = 0x00 || pubkey_hash // 21 bytes
checksum = SHA256d(versioned)[0:4] // 4 bytes
address = Base58(versioned || checksum) // e.g., "1A1zP1eP..."
Base58Check addresses are case-sensitive, don't detect transposition errors reliably, and are slow to decode. These issues led to the development of Bech32.
Bech32 & Bech32m
Bech32 (BIP 173) and Bech32m (BIP 350) are modern address formats designed to fix Base58's shortcomings:
Case-Insensitive
Addresses use only lowercase (or only uppercase). No more confusion between similar characters.
Better Error Detection
Uses a BCH code that detects up to 4 errors and catches all single-character errors.
QR-Code Friendly
When all-uppercase, Bech32 uses the efficient alphanumeric QR encoding mode, producing smaller QR codes.
Format
// Bech32 address structure:
bc1 q r4g5fcm3k0v8vm0smlpv3he5xtfnqmv50f // P2WPKH
โ โ โโโ data (witness program in 5-bit groups) + checksum
โ โโโโโ witness version (q = 0)
โโโโโโโโโ human-readable part (bc = mainnet, tb = testnet)
Bech32 vs. Bech32m
- Bech32 (BIP 173), used for witness version 0 (P2WPKH, P2WSH)
- Bech32m (BIP 350), used for witness version 1+ (P2TR / Taproot). Fixes a subtle weakness in Bech32's error detection for certain edge cases.
Address Types Summary
New wallets should default to P2TR (Taproot) addresses. They provide the best combination of privacy, efficiency, and functionality. P2WPKH is also widely used and well-supported.
The key_io Layer
In Bitcoin Core, address encoding/decoding is handled by src/key_io.cpp. Two key functions:
EncodeDestination()
Converts a CTxDestination (internal destination variant) to a string address:
// CTxDestination is a std::variant holding one of:
// - PKHash โ encodes as P2PKH (1...)
// - ScriptHash โ encodes as P2SH (3...)
// - WitnessV0KeyHash โ encodes as P2WPKH (bc1q...)
// - WitnessV0ScriptHash โ encodes as P2WSH (bc1q...)
// - WitnessV1Taproot โ encodes as P2TR (bc1p...)
std::string address = EncodeDestination(destination);
DecodeDestination()
Parses a string address back into a CTxDestination:
CTxDestination dest = DecodeDestination("bc1p...");
// Automatically detects address type
// Returns CNoDestination if invalid
WIF: Wallet Import Format
Private keys can also be encoded as strings using WIF (Base58Check with a different version byte):
- Take the 32-byte private key
- Prepend version byte (0x80 for mainnet)
- If compressed, append 0x01 flag byte
- Apply Base58Check encoding
WIF keys start with 5 (uncompressed) or K/L (compressed) on mainnet. This format is used for importprivkey RPC and backup/restore operations.