Pure functions docs

This commit is contained in:
4tochka 2018-06-19 01:22:26 +04:00
parent bf906e24e2
commit 4ecdadc0f4
5 changed files with 301 additions and 142 deletions

View File

@ -19,6 +19,7 @@ sys.path.insert(0, os.path.abspath('.'))
sys.path.insert(0, os.path.abspath('..')) sys.path.insert(0, os.path.abspath('..'))
sys.path.insert(0, os.path.abspath('../_static/')) sys.path.insert(0, os.path.abspath('../_static/'))
sys.path.insert(0, os.path.abspath('../../_static/')) sys.path.insert(0, os.path.abspath('../../_static/'))
sys.path.insert(0, os.path.abspath('../../pybtc/'))
sys.path.insert(0, os.path.abspath('./_static/')) sys.path.insert(0, os.path.abspath('./_static/'))

View File

@ -0,0 +1,49 @@
==============
Pure functions
==============
Base function primitives implemeted in functional programming paradigm.
Key management
==============
Tools for private and public key managment
Private key
-----------
.. autofunction:: pybtc.create_private_key
.. autofunction:: pybtc.private_key_to_wif
.. autofunction:: pybtc.wif_to_private_key
.. autofunction:: pybtc.is_wif_valid
Public key
----------
.. WARNING::
Using uncompressed public keys is
`deprecated <https://github.com/bitcoin/bips/blob/master/bip-0143.mediawiki#restrictions-on-public-key-type>`_
in a new SEGWIT address format.
To avoid potential future funds loss, users MUST NOT use uncompressed keys
in version 0 witness programs. Use uncompressed keys only for backward
compatibilitylegacy in legacy address format (PUBKEY, P2PKH).
.. autofunction:: pybtc.private_to_public_key
.. autofunction:: pybtc.is_public_key_valid
Addresses
=========
.. autofunction:: pybtc.hash_to_address
.. autofunction:: pybtc.address_to_hash
.. autofunction:: pybtc.public_key_to_address
.. autofunction:: pybtc.address_type
.. autofunction:: pybtc.address_to_script
.. autofunction:: pybtc.is_address_valid

View File

@ -111,4 +111,4 @@ Table Of Contents
:name: mastertoc :name: mastertoc
:maxdepth: 2 :maxdepth: 2
tools.rst functional.rst

View File

@ -1,5 +0,0 @@
Tools
======
.. autofunction:: pybtc.tools.sign_message

View File

@ -9,33 +9,53 @@ import math
import io import io
# Bitcoin keys # Key management
#
def create_private_key(compressed=True, testnet=False, wif=True): def create_private_key(compressed=True, testnet=False, wif=True, hex=False):
""" """
:return: 32 bytes private key Create private key
:param compressed: (optional) Type of public key, by default set to compressed.
Using uncompressed public keys is deprecated in new SEGWIT addresses,
use this option only for backward compatibility.
:param testnet: (optional) flag for testnet network, by default is False.
:param wif: (optional) If set to True return key in WIF format, by default is True.
:param hex: (optional) If set to True return key in HEX format, by default is False.
:return: Private key in wif format (default), hex encoded byte string in case of hex flag or
raw bytes string in case wif and hex flags set to False.
""" """
a = random.SystemRandom().randint(0, MAX_INT_PRIVATE_KEY) a = random.SystemRandom().randint(0, MAX_INT_PRIVATE_KEY)
i = int((time.time() % 0.01)*100000) i = int((time.time() % 0.01)*100000)
h = a.to_bytes(32, byteorder="big") h = a.to_bytes(32, byteorder="big")
while True: # more entropy from system timer and sha256 derivation
while i:
h = hashlib.sha256(h).digest() h = hashlib.sha256(h).digest()
if i > 1: i -= 1
i -= 1 if not i and int.from_bytes(h, byteorder="big") > MAX_INT_PRIVATE_KEY:
else: i += 1
if int.from_bytes(h, byteorder="big") < MAX_INT_PRIVATE_KEY: if wif:
break
if hex:
return private_key_to_wif(h) return private_key_to_wif(h)
elif hex:
return hexlify(h).decode()
return h return h
def private_key_to_wif(h, compressed=True, testnet=False): def private_key_to_wif(h, compressed=True, testnet=False):
# uncompressed: 0x80 + [32-byte secret] + [4 bytes of Hash() of previous 33 bytes], base58 encoded """
# compressed: 0x80 + [32-byte secret] + 0x01 + [4 bytes of Hash() previous 34 bytes], base58 encoded Encode private key in HEX or RAW bytes format to WIF format.
if type(h) == str:
:param h: private key 32 byte string or HEX encoded string.
:param compressed: (optional) flag of public key compressed format, by default set to True.
:param testnet: (optional) flag for testnet network, by default is False.
:return: Private key in WIF format
"""
# uncompressed: 0x80 + [32-byte secret] + [4 bytes of Hash() of previous 33 bytes], base58 encoded.
# compressed: 0x80 + [32-byte secret] + 0x01 + [4 bytes of Hash() previous 34 bytes], base58 encoded.
if isinstance(h, str):
h = unhexlify(h) h = unhexlify(h)
assert len(h) == 32 if len(h) != 32 and isinstance(h, bytes):
raise TypeError("private key must be a 32 bytes or hex encoded string")
if testnet: if testnet:
h = TESTNET_PRIVATE_KEY_BYTE_PREFIX + h h = TESTNET_PRIVATE_KEY_BYTE_PREFIX + h
else: else:
@ -46,8 +66,15 @@ def private_key_to_wif(h, compressed=True, testnet=False):
return encode_base58(h) return encode_base58(h)
def wif_to_private_key(h, hex=False): def wif_to_private_key(h, hex=True):
assert is_wif_valid(h) """
Decode WIF private key to bytes string or HEX encoded string
:param hex: (optional) if set to True return key in HEX format, by default is True.
:return: Private key HEX encoded string or raw bytes string.
"""
if not is_wif_valid(h):
raise TypeError("invalid wif key")
h = decode_base58(h) h = decode_base58(h)
if hex: if hex:
return hexlify(h[1:33]).decode() return hexlify(h[1:33]).decode()
@ -55,7 +82,14 @@ def wif_to_private_key(h, hex=False):
def is_wif_valid(wif): def is_wif_valid(wif):
assert type(wif) == str """
Check is private key in WIF format string is valid.
:param wif: private key in WIF format string.
:return: boolean
"""
if not isinstance(wif, str):
raise TypeError("invalid wif key")
if wif[0] not in PRIVATE_KEY_PREFIX_LIST: if wif[0] not in PRIVATE_KEY_PREFIX_LIST:
return False return False
try: try:
@ -75,10 +109,20 @@ def is_wif_valid(wif):
def private_to_public_key(private_key, compressed=True, hex=True): def private_to_public_key(private_key, compressed=True, hex=True):
if type(private_key)!= bytes: """
if type(private_key) == bytearray: Get public key from private key using ECDSA secp256k1
:param private_key: private key in WIF, HEX or bytes.
:param compressed: (optional) flag of public key compressed format, by default set to True.
In case private_key in WIF format, this flag is set in accordance with
the key format specified in WIF string.
:param hex: (optional) if set to True return key in HEX format, by default is True.
:return: 33/65 bytes public key in HEX or bytes string
"""
if not isinstance(private_key, bytes):
if isinstance(private_key, bytearray):
private_key = bytes(private_key) private_key = bytes(private_key)
elif type(private_key) == str: elif isinstance(private_key, str):
if not is_wif_valid(private_key): if not is_wif_valid(private_key):
private_key = unhexlify(private_key) private_key = unhexlify(private_key)
else: else:
@ -90,18 +134,26 @@ def private_to_public_key(private_key, compressed=True, hex=True):
raise TypeError("private key must be a bytes or WIF or hex encoded string") raise TypeError("private key must be a bytes or WIF or hex encoded string")
pubkey_ptr = ffi.new('secp256k1_pubkey *') pubkey_ptr = ffi.new('secp256k1_pubkey *')
r = secp256k1.secp256k1_ec_pubkey_create(ECDSA_CONTEXT_ALL, pubkey_ptr, private_key) r = secp256k1.secp256k1_ec_pubkey_create(ECDSA_CONTEXT_ALL, pubkey_ptr, private_key)
assert r == 1 if not r:
raise RuntimeError("secp256k1 error")
len_key = 33 if compressed else 65 len_key = 33 if compressed else 65
pubkey = ffi.new('char [%d]' % len_key) pubkey = ffi.new('char [%d]' % len_key)
outlen = ffi.new('size_t *', len_key) outlen = ffi.new('size_t *', len_key)
compflag = EC_COMPRESSED if compressed else EC_UNCOMPRESSED compflag = EC_COMPRESSED if compressed else EC_UNCOMPRESSED
r = secp256k1.secp256k1_ec_pubkey_serialize(ECDSA_CONTEXT_VERIFY, pubkey, outlen, pubkey_ptr, compflag) r = secp256k1.secp256k1_ec_pubkey_serialize(ECDSA_CONTEXT_VERIFY, pubkey, outlen, pubkey_ptr, compflag)
assert r == 1
pub = bytes(ffi.buffer(pubkey, len_key)) pub = bytes(ffi.buffer(pubkey, len_key))
if not r:
raise RuntimeError("secp256k1 error")
return hexlify(pub).decode() if hex else pub return hexlify(pub).decode() if hex else pub
def is_valid_public_key(key): def is_public_key_valid(key):
"""
Check public key is valid.
:param key: public key in HEX or bytes string format.
:return: boolean
"""
if isinstance(key, str): if isinstance(key, str):
key = unhexlify(key) key = unhexlify(key)
if len(key) < 33: if len(key) < 33:
@ -114,18 +166,30 @@ def is_valid_public_key(key):
return True return True
# # Addresses
# Bitcoin addresses
#
def hash_to_address(address_hash, testnet=False, script_hash=False, witness_version=0):
"""
Get address from public key/script hash. In case PUBKEY, P2PKH, P2PKH public key/script hash is SHA256+RIPEMD160,
P2WSH script hash is SHA256.
def hash_to_address(address_hash, testnet=False, :param address_hash: public key hash or script hash in HEX or bytes string format.
script_hash=False, witness_version=0): :param testnet: (optional) flag for testnet network, by default is False.
if type(address_hash) == str: :param script_hash: (optional) flag for script hash (P2SH address), by default is False.
:param witness_version: (optional) witness program version, by default is 0, for legacy
address format use None.
:return: address in base58 or bech32 format.
"""
if isinstance(address_hash, str):
address_hash = unhexlify(address_hash) address_hash = unhexlify(address_hash)
if not isinstance(address_hash, bytes):
raise TypeError("address hash must be HEX encoded string or bytes")
if not script_hash: if not script_hash:
if witness_version is None: if witness_version is None:
assert len(address_hash) == 20 if len(address_hash) != 20:
raise TypeError("address hash length incorrect")
if testnet: if testnet:
prefix = TESTNET_ADDRESS_BYTE_PREFIX prefix = TESTNET_ADDRESS_BYTE_PREFIX
else: else:
@ -134,7 +198,9 @@ def hash_to_address(address_hash, testnet=False,
address_hash += double_sha256(address_hash)[:4] address_hash += double_sha256(address_hash)[:4]
return encode_base58(address_hash) return encode_base58(address_hash)
else: else:
assert len(address_hash) in (20,32) if len(address_hash) not in (20, 32):
raise TypeError("address hash length incorrect")
if witness_version is None: if witness_version is None:
if testnet: if testnet:
prefix = TESTNET_SCRIPT_ADDRESS_BYTE_PREFIX prefix = TESTNET_SCRIPT_ADDRESS_BYTE_PREFIX
@ -143,19 +209,59 @@ def hash_to_address(address_hash, testnet=False,
address_hash = prefix + address_hash address_hash = prefix + address_hash
address_hash += double_sha256(address_hash)[:4] address_hash += double_sha256(address_hash)[:4]
return encode_base58(address_hash) return encode_base58(address_hash)
if testnet: if testnet:
prefix = TESTNET_SEGWIT_ADDRESS_BYTE_PREFIX prefix = TESTNET_SEGWIT_ADDRESS_BYTE_PREFIX
hrp = TESTNET_SEGWIT_ADDRESS_PREFIX hrp = TESTNET_SEGWIT_ADDRESS_PREFIX
else: else:
prefix = MAINNET_SEGWIT_ADDRESS_BYTE_PREFIX prefix = MAINNET_SEGWIT_ADDRESS_BYTE_PREFIX
hrp = MAINNET_SEGWIT_ADDRESS_PREFIX hrp = MAINNET_SEGWIT_ADDRESS_PREFIX
address_hash = witness_version.to_bytes(1, "big") + rebase_8_to_5(address_hash) address_hash = witness_version.to_bytes(1, "big") + rebase_8_to_5(address_hash)
checksum = bech32_polymod(prefix + address_hash + b"\x00" * 6) checksum = bech32_polymod(prefix + address_hash + b"\x00" * 6)
checksum = rebase_8_to_5(checksum.to_bytes(5, "big"))[2:] checksum = rebase_8_to_5(checksum.to_bytes(5, "big"))[2:]
return "%s1%s" % (hrp, rebase_5_to_32(address_hash + checksum).decode()) return "%s1%s" % (hrp, rebase_5_to_32(address_hash + checksum).decode())
def address_to_hash(address, hex=False): def public_key_to_address(pubkey, testnet=False, p2sh_p2wpkh=False, witness_version=0):
"""
Get address from public key/script hash. In case PUBKEY, P2PKH, P2PKH public key/script hash is SHA256+RIPEMD160,
P2WSH script hash is SHA256.
:param pubkey: public key HEX or bytes string format.
:param testnet: (optional) flag for testnet network, by default is False.
:param p2sh_p2wpkh: (optional) flag for P2WPKH inside P2SH address, by default is False.
:param witness_version: (optional) witness program version, by default is 0, for legacy
address format use None.
:return: address in base58 or bech32 format.
"""
if isinstance(pubkey, str):
pubkey = unhexlify(pubkey)
if not isinstance(pubkey, bytes):
raise TypeError("public key invalid")
if p2sh_p2wpkh:
if len(pubkey) != 33:
raise TypeError("public key invalid")
h = hash160(b'\x00\x14' + hash160(pubkey))
witness_version = None
else:
if witness_version is not None:
if len(pubkey) != 33:
raise TypeError("public key invalid")
h = hash160(pubkey)
return hash_to_address(h, testnet=testnet,
script_hash=p2sh_p2wpkh,
witness_version=witness_version)
def address_to_hash(address, hex=True):
"""
Get address hash from base58 or bech32 address format.
:param address: address in base58 or bech32 format.
:param hex: (optional) If set to True return key in HEX format, by default is True.
:return: script in HEX or bytes string
"""
if address[0] in ADDRESS_PREFIX_LIST: if address[0] in ADDRESS_PREFIX_LIST:
h = decode_base58(address)[1:-4] h = decode_base58(address)[1:-4]
elif address[:2] in (MAINNET_SEGWIT_ADDRESS_PREFIX, elif address[:2] in (MAINNET_SEGWIT_ADDRESS_PREFIX,
@ -164,19 +270,17 @@ def address_to_hash(address, hex=False):
h = rebase_5_to_8(rebase_32_to_5(address)[1:-6], False) h = rebase_5_to_8(rebase_32_to_5(address)[1:-6], False)
else: else:
return None return None
if hex: return h.hex() if hex else h
return h.hex()
else:
return h
def get_witness_version(address):
address = address.split("1")[1]
h = rebase_32_to_5(address)
return h[0]
def address_type(address, num=False): def address_type(address, num=False):
"""
Get address type.
:param address: address in base58 or bech32 format.
:param num: (optional) If set to True return type in numeric format, by default is False.
:return: address type in string or numeric format.
"""
if address[0] in (TESTNET_SCRIPT_ADDRESS_PREFIX, if address[0] in (TESTNET_SCRIPT_ADDRESS_PREFIX,
MAINNET_SCRIPT_ADDRESS_PREFIX): MAINNET_SCRIPT_ADDRESS_PREFIX):
t = 'P2SH' t = 'P2SH'
@ -198,6 +302,12 @@ def address_type(address, num=False):
def address_net_type(address): def address_net_type(address):
"""
Get address network type.
:param address: address in base58 or bech32 format.
:return: address network type in string format or None.
"""
if address[0] in (MAINNET_SCRIPT_ADDRESS_PREFIX, if address[0] in (MAINNET_SCRIPT_ADDRESS_PREFIX,
MAINNET_ADDRESS_PREFIX): MAINNET_ADDRESS_PREFIX):
return "mainnet" return "mainnet"
@ -212,16 +322,14 @@ def address_net_type(address):
return None return None
def script_to_hash(s, witness=False, hex=False):
if type(s) == str:
s = unhexlify(s)
if witness:
return sha256(s, hex)
else:
return hash160(s, hex)
def address_to_script(address, hex=False): def address_to_script(address, hex=False):
"""
Get public key script from address.
:param address: address in base58 or bech32 format.
:param hex: (optional) If set to True return key in HEX format, by default is True.
:return: public key script in HEX or bytes string
"""
if address[0] in (TESTNET_SCRIPT_ADDRESS_PREFIX, if address[0] in (TESTNET_SCRIPT_ADDRESS_PREFIX,
MAINNET_SCRIPT_ADDRESS_PREFIX): MAINNET_SCRIPT_ADDRESS_PREFIX):
s = [BYTE_OPCODE["OP_HASH160"], s = [BYTE_OPCODE["OP_HASH160"],
@ -244,7 +352,7 @@ def address_to_script(address, hex=False):
bytes([len(h)]), bytes([len(h)]),
h] h]
else: else:
assert False raise TypeError("address invalid")
s = b''.join(s) s = b''.join(s)
return hexlify(s).decode() if hex else s return hexlify(s).decode() if hex else s
@ -254,27 +362,84 @@ def public_key_to_p2sh_p2wpkh_script(pubkey):
return b'\x00\x14' + hash160(pubkey) return b'\x00\x14' + hash160(pubkey)
def public_key_to_address(pubkey, testnet=False, def is_address_valid(address, testnet=False):
p2sh_p2wpkh=False, """
witness_version=0): Check is address valid.
if type(pubkey) == str:
pubkey = unhexlify(pubkey)
if p2sh_p2wpkh:
assert len(pubkey) == 33
h = hash160(b'\x00\x14' + hash160(pubkey))
witness_version = None
else:
if witness_version is not None:
assert len(pubkey) == 33
h = hash160(pubkey)
return hash_to_address(h, testnet=testnet,
script_hash=p2sh_p2wpkh,
witness_version=witness_version)
:param address: address in base58 or bech32 format.
:param testnet: (optional) flag for testnet network, by default is False.
:return: boolean
"""
if not address or type(address) != str:
return False
if address[0] in (MAINNET_ADDRESS_PREFIX,
MAINNET_SCRIPT_ADDRESS_PREFIX,
TESTNET_ADDRESS_PREFIX,
TESTNET_ADDRESS_PREFIX_2,
TESTNET_SCRIPT_ADDRESS_PREFIX):
if testnet:
if address[0] not in (TESTNET_ADDRESS_PREFIX,
TESTNET_ADDRESS_PREFIX_2,
TESTNET_SCRIPT_ADDRESS_PREFIX):
return False
else:
if address[0] not in (MAINNET_ADDRESS_PREFIX,
MAINNET_SCRIPT_ADDRESS_PREFIX):
return False
h = decode_base58(address)
if len(h) != 25:
return False
checksum = h[-4:]
if double_sha256(h[:-4])[:4] != checksum:
return False
return True
elif address[:2].lower() in (TESTNET_SEGWIT_ADDRESS_PREFIX,
MAINNET_SEGWIT_ADDRESS_PREFIX):
if len(address) not in (42, 62):
return False
try:
prefix, payload = address.split('1')
except:
return False
upp = True if prefix[0].isupper() else False
for i in payload[1:]:
if upp:
if not i.isupper() or i not in base32charset_upcase:
return False
else:
if i.isupper() or i not in base32charset:
return False
payload = payload.lower()
prefix = prefix.lower()
if testnet:
if prefix != TESTNET_SEGWIT_ADDRESS_PREFIX:
return False
stripped_prefix = TESTNET_SEGWIT_ADDRESS_BYTE_PREFIX
else:
if prefix != MAINNET_SEGWIT_ADDRESS_PREFIX:
return False
stripped_prefix = MAINNET_SEGWIT_ADDRESS_BYTE_PREFIX
d = rebase_32_to_5(payload)
address_hash = d[:-6]
checksum = d[-6:]
checksum2 = bech32_polymod(stripped_prefix + address_hash + b"\x00" * 6)
checksum2 = rebase_8_to_5(checksum2.to_bytes(5, "big"))[2:]
if checksum != checksum2:
return False
return True
def get_witness_version(address):
address = address.split("1")[1]
h = rebase_32_to_5(address)
return h[0]
# Script
def parse_script(script, segwit=True): def parse_script(script, segwit=True):
if not script: if not script:
return {"nType": 7, "type": "NON_STANDARD", "reqSigs": 0, "script": b""} return {"nType": 7, "type": "NON_STANDARD", "reqSigs": 0, "script": b""}
if type(script) == str: if type(script) == str:
try: try:
script = unhexlify(script) script = unhexlify(script)
@ -288,12 +453,12 @@ def parse_script(script, segwit=True):
if l == 34 and script[0] == 0: if l == 34 and script[0] == 0:
return {"nType": 6, "type": "P2WSH", "reqSigs": None, "addressHash": script[2:]} return {"nType": 6, "type": "P2WSH", "reqSigs": None, "addressHash": script[2:]}
if l == 25 and \ if l == 25 and \
script[:2] == b"\x76\xa9" and \ script[:2] == b"\x76\xa9" and \
script[-2:] == b"\x88\xac": script[-2:] == b"\x88\xac":
return {"nType": 0, "type": "P2PKH", "reqSigs": 1, "addressHash": script[3:-2]} return {"nType": 0, "type": "P2PKH", "reqSigs": 1, "addressHash": script[3:-2]}
if l == 23 and \ if l == 23 and \
script[0] == 169 and \ script[0] == 169 and \
script[-1] == 135: script[-1] == 135:
return {"nType": 1, "type": "P2SH", "reqSigs": None, "addressHash": script[2:-1]} return {"nType": 1, "type": "P2SH", "reqSigs": None, "addressHash": script[2:-1]}
if l == 67 and script[-1] == 172: if l == 67 and script[-1] == 172:
return {"nType": 2, "type": "PUBKEY", "reqSigs": 1, "addressHash": hash160(script[1:-1])} return {"nType": 2, "type": "PUBKEY", "reqSigs": 1, "addressHash": hash160(script[1:-1])}
@ -316,7 +481,7 @@ def parse_script(script, segwit=True):
break break
s += 1 s += 1
if c == script[-2] - 80: if c == script[-2] - 80:
return {"nType": 4, "type": "MULTISIG", "reqSigs": script[0] - 80, "script": script} return {"nType": 4, "type": "MULTISIG", "reqSigs": script[0] - 80, "script": script}
s, m, n, last, req_sigs = 0, 0, 0, 0, 0 s, m, n, last, req_sigs = 0, 0, 0, 0, 0
while l - s > 0: while l - s > 0:
@ -364,7 +529,7 @@ def parse_script(script, segwit=True):
if last: if last:
last -= 1 last -= 1
s += 1 s += 1
return {"nType": 7, "type": "NON_STANDARD", "reqSigs": req_sigs, "script": script} return {"nType": 7, "type": "NON_STANDARD", "reqSigs": req_sigs, "script": script}
def decode_script(script, asm=False): def decode_script(script, asm=False):
@ -380,7 +545,7 @@ def decode_script(script, asm=False):
while l - s > 0: while l - s > 0:
if script[s] < 0x4c and script[s]: if script[s] < 0x4c and script[s]:
if asm: if asm:
result.append(hexlify(script[s+1:s+1 +script[s]]).decode()) result.append(hexlify(script[s + 1:s + 1 + script[s]]).decode())
else: else:
result.append('[%s]' % script[s]) result.append('[%s]' % script[s])
s += script[s] + 1 s += script[s] + 1
@ -445,7 +610,7 @@ def delete_from_script(script, sub_script):
k = s k = s
else: else:
t = stack.pop(0) t = stack.pop(0)
result.append(script[k:k+t]) result.append(script[k:k + t])
k += t k += t
if script[k:s][:ls] == sub_script: if script[k:s][:ls] == sub_script:
if s - k > ls: if s - k > ls:
@ -456,64 +621,13 @@ def delete_from_script(script, sub_script):
return b''.join(result) if not s_hex else hexlify(b''.join(result)).decode() return b''.join(result) if not s_hex else hexlify(b''.join(result)).decode()
def is_address_valid(address, testnet=False): def script_to_hash(s, witness=False, hex=False):
if not address or type(address) != str: if type(s) == str:
return False s = unhexlify(s)
if address[0] in (MAINNET_ADDRESS_PREFIX, if witness:
MAINNET_SCRIPT_ADDRESS_PREFIX, return sha256(s, hex)
TESTNET_ADDRESS_PREFIX, else:
TESTNET_ADDRESS_PREFIX_2, return hash160(s, hex)
TESTNET_SCRIPT_ADDRESS_PREFIX):
if testnet:
if address[0] not in (TESTNET_ADDRESS_PREFIX,
TESTNET_ADDRESS_PREFIX_2,
TESTNET_SCRIPT_ADDRESS_PREFIX):
return False
else:
if address[0] not in (MAINNET_ADDRESS_PREFIX,
MAINNET_SCRIPT_ADDRESS_PREFIX):
return False
h = decode_base58(address)
if len(h) != 25:
return False
checksum = h[-4:]
if double_sha256(h[:-4])[:4] != checksum:
return False
return True
elif address[:2].lower() in (TESTNET_SEGWIT_ADDRESS_PREFIX,
MAINNET_SEGWIT_ADDRESS_PREFIX):
if len(address) not in (42, 62):
return False
try:
prefix, payload = address.split('1')
except:
return False
upp = True if prefix[0].isupper() else False
for i in payload[1:]:
if upp:
if not i.isupper() or i not in base32charset_upcase:
return False
else:
if i.isupper() or i not in base32charset:
return False
payload = payload.lower()
prefix = prefix.lower()
if testnet:
if prefix != TESTNET_SEGWIT_ADDRESS_PREFIX:
return False
stripped_prefix = TESTNET_SEGWIT_ADDRESS_BYTE_PREFIX
else:
if prefix != MAINNET_SEGWIT_ADDRESS_PREFIX:
return False
stripped_prefix = MAINNET_SEGWIT_ADDRESS_BYTE_PREFIX
d = rebase_32_to_5(payload)
address_hash = d[:-6]
checksum = d[-6:]
checksum2 = bech32_polymod(stripped_prefix + address_hash + b"\x00" * 6)
checksum2 = rebase_8_to_5(checksum2.to_bytes(5, "big"))[2:]
if checksum != checksum2:
return False
return True
# #