Did you know that you can navigate the posts by swiping left and right?
In this part we will talk about Bitcoin’s addresses. We will explain how they are created, some of their properties as well as provide examples. The tutorial is aimed for people who already have some knowledge of how Bitcoin works at a high-level and want to understand how it works at a low-level.
Addresses can be shared to anyone who wants to sent you money. They are typically generated from the public key, consist of a sequence of characters and digits and start with 1 for the mainnet and with m or n for testnet1. This will be further explained below.
An address typically represents the owner of a private/public pair but it can also represent an arbitrary complex script, called P2SH script, as we will see in a future post.
Notice that we do not share the public key as one would expect in public key cryptography but rather the address, which derives from the public key. Some benefits are:
- shorter addresses
- arguably, quantum computer resistance2
An address is really just the hash of the public key3, called public key hash (or PKH). That is how it is represented on the blockchain. The way we format addresses to display them (starting with 1, m/n, etc.) are just for our convenience. The format that we use is Base58Check encoding4 of the public key hash; Base58 with version prefix to specify the network and a 32-bit checksum.
The following is pseudocode of the process that converts the public key to public key hash and then address:
version = (1 byte version number) keyHash = RIPEMD-160( SHA-256( publicKey ) ) data = version + keyHash dataHash = SHA-256( SHA-256( data ) ) checksum = (first 4 bytes of dataHash) address = Base58CheckEncode( data + checksum )
Note that all the above functions operate on big-endian bytes. The network prefix specifies the Bitcoin network that this address would be used. The Base58 address first character depends on the network prefix, as follows:
>>> from bitcoinutils.setup import setup >>> from bitcoinutils.keys import PrivateKey, PublicKey, P2pkhAddress >>> setup('testnet') 'testnet' >>> priv = PrivateKey.from_wif('91h2ReUJRwJhTNd828zhc8RRVMU4krX9q3LNi4nVfiVwkMPfA9p') >>> pub = priv.get_public_key() >>> addr1 = pub.get_address() >>> addr2 = pub.get_address(compressed=False) >>> addr1.to_string() 'n42m3hGC52QTChUbXq3QAPVU6nWkG9xuWj' >>> addr2.to_string() 'n2JjAgC6UqFf8DvsZXhWcyNzm8w8YKj7MQ'
The actual python implepmentation of converting a public key hash (the application of SHA256 and then RIPEMD160, also called HASH160) can be found at _to_hash160() on github. For code to convert from public key hash to address consult to_string(). Feel free to consult the rest of the code and/or the examples in the repository for segwit address creation, etc.
Segregated Witness (segwit) is a consensus change that was activated in August 2017 and introduces an update on how transactions are constructed. It introduces two new transaction types, Pay-to-Witness-Public-Key-Hash (P2WPKH) and Pay-to-Witness-Script-Hash (P2WSH). These new transaction types are going to be explained in detail in a future post.
Native segwit addresses use a different format to display the public key hash called Bech32 encoding (instead of Base58check). For code implementation look at the python reference implementation by Pieter Wuille here.
The network prefix specifies the Bitcoin network that this address would be used:
>>> from bitcoinutils.setup import setup >>> from bitcoinutils.keys import PrivateKey, PublicKey, P2wpkhAddress >>> setup('testnet') 'testnet' >>> priv = PrivateKey.from_wif('91h2ReUJRwJhTNd828zhc8RRVMU4krX9q3LNi4nVfiVwkMPfA9p') >>> pub = priv.get_public_key() >>> addr = pub.get_segwit_address() >>> addr.to_string() 'tb1q7m6ak6k050sxzxjjekhey73k0f3rqnxsqa08k2'
When segwit was introduced a lot of wallets did not support the new bech32 addresses so users could not use those to send funds to segwit addresses. To remedy that, nested segwit addresses could be used.
Effectively you could nest or wrap a segwit address into a P2SH address. As already mentioned P2SH addresses can be created from arbitrary scripts and thus could also include a witness script. Again, P2SH and segwit are outside our scope now and will be explained in more detail in a later post.
After the segwit upgrade one needs to choose what type of address to create (e.g. with
getnewaddress. The supported types where
bech32. The default, starting from version 0.16.0, is nested addresses (
Or for segwit addresses bc and tb for mainnet and testnet respectively. ↩
Until one spends from an address the public key will never appear on the blockchain and thus to potential attackers and since the address is hashed from the public key not even quantum computers could (arguably) brute force to get the public key and then the private key. Note, however, that even if that is the case the majority of addresses would be hacked thus destroying trust in (and the value of) the network anyway! ↩
More precisely two hashing functions are used: RIPEMD-160( SHA-256( publicKey ) ) ↩
Pay to Public Key Hash - this is a typical legacy address. ↩
Pay to Script Hash - this is a typical script hash address. ↩
Since the public key hash of a compressed public key would be different from an uncompressed public key we have two distinct legacy addresses. ↩
Pay to Witness Public Key Hash - this is a segwit address. ↩
Pay to Witness Script Hash - this is a segwit script hash address. ↩