Understanding how blockchain wallet addresses are generated is essential for anyone exploring cryptocurrency development or security. At the heart of this process lie cryptographic principles, elliptic curve mathematics, and encoding techniques that ensure secure and verifiable transactions. This guide walks you through each step—from private keys to public key compression and final address encoding—using Bitcoin as a reference model.
The Role of Private and Public Keys in Value Transfer
In blockchain networks like Bitcoin, wallets serve as digital identities that enable value transfer. For any transaction to be valid, two critical components are required: proof of ownership and a destination identifier.
- The private key proves ownership. It's a randomly generated number used to digitally sign transactions. Only someone with access to the private key can authorize the movement of funds.
- The public key, derived from the private key via elliptic curve multiplication, acts like a bank account number—it identifies where funds should be sent.
👉 Discover how secure digital asset management starts with understanding wallet cryptography.
Mathematically, given a generator point $ G $ on the secp256k1 elliptic curve and a private key $ N $, the corresponding public key $ P $ is calculated as:
$$ P = N \times G $$
This operation is irreversible—while it's easy to compute $ P $ from $ N $, deriving $ N $ from $ P $ is computationally infeasible due to the discrete logarithm problem.
Public Key Formats: Uncompressed vs Compressed
A public key represents a point on the elliptic curve with coordinates $ (x, y) $, each 32 bytes long. There are two standard formats for encoding this data:
Uncompressed Format
In uncompressed mode, both coordinates are stored in full:
- Prepend
0x04to indicate an uncompressed key. - Concatenate 32-byte big-endian representations of $ x $ and $ y $.
def sec(self, compressed=False):
if not compressed:
return b'\x04' + self.x.num.to_bytes(32, 'big') + self.y.num.to_bytes(32, 'big')Resulting size: 65 bytes.
Compressed Format
Because elliptic curves are symmetric about the x-axis, we only need $ x $ and the parity (even/odd) of $ y $. This allows us to reconstruct the full point when needed.
- If $ y $ is even → use prefix
0x02 - If $ y $ is odd → use prefix
0x03
def sec(self, compressed=True):
if compressed:
if self.y.num % 2 == 0:
return b'\x02' + self.x.num.to_bytes(32, 'big')
else:
return b'\x03' + self.x.num.to_bytes(32, 'big')Resulting size: 33 bytes, reducing bandwidth and storage requirements by nearly 50%.
Recovering Points from Compressed Keys
Since only $ x $ is stored in compressed form, we must reconstruct $ y $ using the curve equation:
$$ y^2 = x^3 + 7 \mod p $$
To solve for $ y $ without square roots, we leverage modular exponentiation. Given that Bitcoin’s prime modulus $ p \equiv 3 \mod 4 $, we apply Fermat’s Little Theorem:
$$ y = (x^3 + 7)^{(p+1)/4} \mod p $$
This yields one valid solution; the other is $ p - y $. We select based on the prefix byte (0x02 → even, 0x03 → odd).
class S256Field:
def sqrt(self):
return self ** ((P + 1) // 4)
@classmethod
def parse(cls, sec_bin):
is_even = sec_bin[0] in [0x02, 0x03]
x = S256Field(int.from_bytes(sec_bin[1:], 'big'))
y_squared = x**3 + S256Field(7)
y = y_squared.sqrt()
# Choose correct y based on parity
...This method ensures full reversibility—compressed keys retain all necessary information.
Hashing: From Public Key to Hash160
Before generating an address, the public key undergoes a two-step hashing process known as hash160:
- Apply SHA-256
- Then apply RIPEMD-160
The result is a 20-byte digest that uniquely represents the public key while being more compact.
def hash160(s):
return hashlib.new('ripemd160', hashlib.sha256(s).digest()).digest()This step enhances security and prepares the data for network-specific addressing.
Address Construction and Checksum Validation
Final wallet addresses are built using Base58Check encoding—a format designed to prevent transcription errors.
Step-by-step Address Generation:
- Get
hash160of the serialized public key (compressed or not). Add a version byte:
0x00for mainnet → starts with "1"0x6ffor testnet → starts with "m" or "n"
- Compute double SHA-256 (hash256) of the result and take the first 4 bytes as a checksum.
- Append the checksum.
- Encode the entire payload using Base58.
def encode_base58_checksum(payload):
checksum = hash256(payload)[:4]
return encode_base58(payload + checksum)Base58 excludes ambiguous characters (0, O, I, l) to reduce human error.
👉 Learn how modern wallets simplify complex cryptography into user-friendly interfaces.
Final Output: A Working Bitcoin Address
Using a sample private key:
privKey = 0x038109007313a5807b2eccc082c8c3fbb988a973cacf1a7df9ce725c31b14776We generate its compressed public key, apply hash160, add mainnet prefix 0x00, append checksum, and encode:
point.address(compressed=True, testnet=False)
# Output: 1PRTTaJesdNovgne6Ehcdu1fpEdX7913CKThis is your Bitcoin mainnet wallet address, ready to receive funds securely.
Frequently Asked Questions (FAQ)
What is a blockchain wallet address?
A wallet address is a hashed version of a public key, encoded for safe sharing. It allows others to send cryptocurrency to your wallet without exposing sensitive cryptographic material.
Why use compressed public keys?
Compressed keys reduce data size from 65 to 33 bytes, improving efficiency in storage and transmission across the network. They're now standard in most modern wallets.
How does Base58Check prevent errors?
By including a 4-byte checksum derived from double SHA-256, Base58Check detects typos or incorrect character sequences during address entry.
Can you derive a private key from a wallet address?
No. The cryptographic hash functions (SHA-256 and RIPEMD-160) and elliptic curve math make reverse-engineering impossible with current technology.
What’s the difference between mainnet and testnet addresses?
Mainnet addresses handle real value; testnet addresses use experimental coins with no monetary worth. They differ only by version byte: 0x00 vs 0x6f.
Are wallet addresses reusable?
While technically possible, reusing addresses compromises privacy and security. Best practice is to generate a new address for each incoming transaction.
👉 See how leading platforms implement secure address generation at scale.
Core Keywords
- blockchain wallet
- wallet address generation
- public key compression
- elliptic curve cryptography
- Base58Check encoding
- hash160
- private key security
- Bitcoin address format
This technical foundation underpins every cryptocurrency transaction. By understanding how addresses are formed—from random number to human-readable string—you gain deeper insight into the trustless architecture of decentralized systems.