Skip to content

Instantly share code, notes, and snippets.

@matthewdowney
Created October 13, 2017 09:42
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save matthewdowney/9e556fc1a3ad5d331f388062163decc5 to your computer and use it in GitHub Desktop.
Save matthewdowney/9e556fc1a3ad5d331f388062163decc5 to your computer and use it in GitHub Desktop.
P2WPKH-P2SH (SegWit) Wallet Address Generation
"""
Implementation of "Pay to Witness Public Key Hash nested in BIP16 Pay to Script Hash" (P2WPKH-P2SH) address generation.
Described in BIP 141 (SegWit) https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki#P2WPKH_nested_in_BIP16_P2SH
I.e.: Public key -> SegWit address
"""
import hashlib
from hashlib import sha256
# MIT License
# pip install git+https://github.com/prusnak/bip32utils
from bip32utils import Base58
def pk_to_p2wpkh_as_p2sh_addr(pk_as_hex_string, testnet=False):
pk_bytes = bytes.fromhex(pk_as_hex_string)
assert len(pk_bytes) == 33 and (pk_bytes.startswith(b"\x02") or pk_bytes.startswith(b"\x03")), \
"Only compressed public keys are compatible with p2sh-p2wpkh addresses. " \
"See https://github.com/bitcoin/bips/blob/master/bip-0049.mediawiki."
pk_hash = hash160_bytes(pk_bytes)
push_20 = bytes.fromhex("0014")
script_sig = push_20 + pk_hash
address_bytes = hash160_bytes(script_sig)
prefix = b"\xc4" if testnet else b"\x05"
address = Base58.check_encode(prefix + address_bytes)
return address
def hash160_bytes(byte_input):
return hashlib.new('ripemd160', sha256(byte_input).digest()).digest()
# Test vector from https://github.com/bitcoin/bips/blob/master/bip-0049.mediawiki
public_key_hex = "03a1af804ac108a8a51782198c2d034b28bf90c8803f5a53f76276fa69a4eae77f"
testnet_address = pk_to_p2wpkh_as_p2sh_addr(public_key_hex, testnet=True)
print(testnet_address)
assert testnet_address == "2Mww8dCYPUpKHofjgcXcBCEGmniw9CoaiD2", "Generated address matches BIP44 test vector"
mainnet_address = pk_to_p2wpkh_as_p2sh_addr(public_key_hex)
print(mainnet_address)
assert mainnet_address == "36NvZTcMsMowbt78wPzJaHHWaNiyR73Y4g", "Generated address matches /my/ test vector"
@nyg
Copy link

nyg commented Feb 11, 2024

Amazing, thank you very much for this!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment