In creating bitcoins, Satoshi created a P2PKH (Pay to Public Key Hash) address. These addresses are used to identify the wallet to be paid, and links to the public key of the owner. Basically when a payer signs a transaction with their private key, this signature can be checked again the wallet address.The original addresses started with a '1'. In order to support the sending of bitcoins to and from multiple addresses, Bitcoin was upgraded with SegWit (defined in BIP141). The wallet address then integrates the pay to witness public key hash (P2WPKH). These addresses start with a '3'. In this case we will generate a private key, and derive the public key from this (based on secp256k1), and then create the two types of Bitcoin wallet addresses [article].
Bitcoin: Pay to witness public key hash (P2WPKH) |
Theory
In creating bitcoins, Satoshi created a P2PKH (Pay to Public Key Hash) address. These addresses are used to identify the wallet to be paid, and links to the public key of the owner. Basically when a payer signs a transaction with their private key, this signature can be checked again the wallet address.The original addresses started with a '1', such as [here]:
In order to support the sending of bitcoins to and from multiple addresses, Bitcoin was upgraded with SegWit (defined in BIP141). The wallet address then integrates the pay to witness public key hash (P2WPKH). These addresses start with a '3', such as [here]:
Here is the first transaction to use the P2SH address [here]:
In generating both types of addresses, we start with the creation of a random 256-bit private key (\(sk\)). As we use the sepc256k1 curve, we compute the public key (\(pk\)) from \(pk = sk.G\), and where \(G\) is the base point on the curve. We then generate a compressed public key point. This starts with a '0x04' (to identify it is a compressed point) and followed by the hex value for the x-coordinate and then the hex value of the y-coordinate. For example, the public key point of:
(d48244894b841891152ad0c319d756535d3ecdf165a67b604e243cbda686ad8b, 4bdac1c339a1f589ea6445a59bd4448e6b3c0a6dbf048391e687bcd32c27f30d)
will have a compressed point value of:
04d48244894b841891152ad0c319d756535d3ecdf165a67b604e243cbda686ad8b4bdac1c339a1f589ea6445a59bd4448e6b3c0a6dbf048391e687bcd32c27f30d
The compressed public key will thus have 130 hex characters, as we use two characters for the '04' part, and then each co-ordinate value is a 256-bit value (64 hex characters). For the private key, we covert to a Wif value, and which is stored in the wallet. This is the key which is used to sign a transaction and is basically a Base-58 version of the private key. Base-58 takes six bits at a time and converts the output as Base-64, but where we remove: 0 (zero), O (capital o), I (capital i) and l (lower case L), and non-alphanumeric characters of + (plus) and / (slash). The Base-58 alphabet is then:
123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz
Next we compute the wallet address based on the public key, and is created a SHA-256 hash of the public key, and then hashing again with a 160-bit RIPEMD-160 hash:
Address='1'+RIPEMD160(SHA-256(pub))
For SegWit, we basically define a version of '3', a PUSH(20) value (0x0014), and then the same hash as before:
Address='3'+RIPEMD160(SHA-256('0014'+pub))
An overview of this process is:
Code
The following is an outline is:
from bip32utils import Base58 import hashlib import binascii import ecdsa import random def hash160(x): # Both accepts & returns bytes return hashlib.new('ripemd160', hashlib.sha256(x).digest()).digest() def SegWit_address(pk, testnet=False): # The Script sig is PUSH(20) and then Hash160(pk) and where pk is the compressed public key push_20 = bytes.fromhex("0014") script_sig = push_20 + hash160(bytes.fromhex(pk)) prefix = b"\xc4" if testnet else b"\x05" address = Base58.check_encode(prefix + hash160(script_sig)) return address def pubKeyToAddr(s): ripemd160 = hash160(bytes.fromhex(s)) return '1'+Base58.check_encode(ripemd160) def privateKeyToWif(key_hex): return Base58.check_encode(bytes.fromhex(key_hex)) def privateKeyToPublicKey(s): pk = ecdsa.SigningKey.from_string(bytes.fromhex(s), curve=ecdsa.SECP256k1) pk=pk.get_verifying_key().to_string() pk="04"+binascii.b2a_hex(pk).decode() return (pk) private_key = ''.join(['%x' % random.randrange(16) for x in range(0, 64)]) print ('Private key: ',private_key) pub = privateKeyToPublicKey(private_key) print ('\nPublic key: ',pub) print ('\nWif: ',privateKeyToWif(private_key)) print ('\nAddress: ',pubKeyToAddr(private_key)) print ("\n===BIP 141===") print ("SegWit address: ",SegWit_address(pub)) print ("Test net: ",SegWit_address(pub, testnet=True))
A sample run is:
Private key: 004290d3940a179420b7c5f4d37a1480c652473cd337bd3bbd115bc9e4947084 Public key: 042a32ac582016213e91f22dd40e78cd00a6bb79fb527958067a4414e284b74f01c4dcbf6aacfe8638c7f4b7510d4c34031c4021153aeb5facae7b756ed331f242 Wif: 17eERopZsEWvTjqtQi9SeUxTz8W7KeZHwvGL1BBx61Z9Hpo9r Address: 13Jkybvi3qV3YJAFetAZP9CwJtA3LP3hUG ===BIP 141=== SegWit address: 38xNaCLWdqtTag6WmrJbKzvrxGt2Uj9vyL Test net: 2MzWadwGYFJPonTj4SyvTwwv8Ad6CHRJTvs