Skip to content

Commit d26f9dd

Browse files
author
Vizeet Srivastava
committed
Fixed address verification and bech32
1 parent d2fbf72 commit d26f9dd

File tree

5 files changed

+187
-17
lines changed

5 files changed

+187
-17
lines changed

base58.py

+13-6
Original file line numberDiff line numberDiff line change
@@ -50,15 +50,22 @@ def base58checkEncode(prefix: bytes, h: bytes):
5050
print('encoded base58 = %s' % encode)
5151
return encode
5252

53-
def base58checkVerify(prefix: bytes, val: str):
53+
def base58checkVerify(prefix: str, val: str):
5454
decoded_val = base58_decode(val)
55-
postfix = binascii.unhexlify('%x' % decoded_val)[-4:]
55+
decoded_prefix = base58_decode(prefix)
56+
print('decoded prefix = %02x' % decoded_prefix)
57+
val_str = '%02x' % decoded_val
58+
if len(val_str) % 2 == 1:
59+
val_str = '0' + val_str
60+
print('decoded val = %s' % val_str)
61+
postfix = binascii.unhexlify(val_str)[-4:]
5662
print('hash from value = %s' % bytes.decode(binascii.hexlify(postfix)))
57-
val_without_postfix = binascii.unhexlify('%x' % decoded_val)[0:-4]
63+
val_without_postfix = binascii.unhexlify(val_str)[0:-4]
5864
print('value = %s' % bytes.decode(binascii.hexlify(val_without_postfix)))
59-
val_with_prefix = prefix + val_without_postfix
60-
print('value = %s' % bytes.decode(binascii.hexlify(val_with_prefix)))
61-
h = hash256(val_with_prefix)[0:4]
65+
if decoded_prefix == 0x00:
66+
val_without_postfix = b'\x00' + val_without_postfix
67+
print('value = %s' % bytes.decode(binascii.hexlify(val_without_postfix)))
68+
h = hash256(val_without_postfix)[0:4]
6269
print('hash of value = %s' % bytes.decode(binascii.hexlify(h)))
6370
if h == postfix:
6471
return True

bitcoin_base58.py

+22-3
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,34 @@
2020
}
2121
}
2222

23+
address_prefixes = {
24+
"Mainnet": {
25+
"PKH": "1",
26+
"SH": "3",
27+
"WIF_Uncompressed": 0x80,
28+
"WIF_Compressed": 0x80,
29+
"BIP32_Pubkey": 0x0488B21E,
30+
"BIP32_Privkey": 0x0488ADE4
31+
},
32+
"Testnet": {
33+
"PKH": "m",
34+
"SH": "2",
35+
"WIF_Uncompressed": 0xEF,
36+
"WIF_Compressed": 0xEF,
37+
"BIP32_Pubkey": 0x043587CF,
38+
"BIP32_Privkey": 0x04358394
39+
}
40+
}
41+
2342
def forAddress(h: bytes, is_testnet: bool, is_script: bool):
2443
prefix = base58_prefixes[("Mainnet", "Testnet")[is_testnet == True]][("PKH", "SH")[is_script == True]]
2544
print('address prefix before encoding = %02x' % prefix)
2645
address = base58.base58checkEncode(binascii.unhexlify('%02x' % prefix), h)
2746
return address
2847

29-
def addressVerify(address: str, is_testnet: bool, is_script: bool):
30-
prefix = base58_prefixes[("Mainnet", "Testnet")[is_testnet == True]][("PKH", "SH")[is_script == True]]
31-
is_valid = base58.base58checkVerify(binascii.unhexlify('%02x' % prefix), address)
48+
def addressVerify(address: str):
49+
prefix = address[0:1]
50+
is_valid = base58.base58checkVerify(prefix, address)
3251
return is_valid
3352

3453
def forWifPrivkey(h: bytes, is_testnet: bool, for_compressed_pubkey: bool):

bitcoin_bech32.py

+123
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
# Copyright (c) 2017 Pieter Wuille
2+
#
3+
# Permission is hereby granted, free of charge, to any person obtaining a copy
4+
# of this software and associated documentation files (the "Software"), to deal
5+
# in the Software without restriction, including without limitation the rights
6+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7+
# copies of the Software, and to permit persons to whom the Software is
8+
# furnished to do so, subject to the following conditions:
9+
#
10+
# The above copyright notice and this permission notice shall be included in
11+
# all copies or substantial portions of the Software.
12+
#
13+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19+
# THE SOFTWARE.
20+
21+
"""Reference implementation for Bech32 and segwit addresses."""
22+
23+
24+
CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"
25+
26+
27+
def bech32_polymod(values):
28+
"""Internal function that computes the Bech32 checksum."""
29+
generator = [0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3]
30+
chk = 1
31+
for value in values:
32+
top = chk >> 25
33+
chk = (chk & 0x1ffffff) << 5 ^ value
34+
for i in range(5):
35+
chk ^= generator[i] if ((top >> i) & 1) else 0
36+
return chk
37+
38+
39+
def bech32_hrp_expand(hrp):
40+
"""Expand the HRP into values for checksum computation."""
41+
return [ord(x) >> 5 for x in hrp] + [0] + [ord(x) & 31 for x in hrp]
42+
43+
44+
def bech32_verify_checksum(hrp, data):
45+
"""Verify a checksum given HRP and converted data characters."""
46+
return bech32_polymod(bech32_hrp_expand(hrp) + data) == 1
47+
48+
49+
def bech32_create_checksum(hrp, data):
50+
"""Compute the checksum values given HRP and data."""
51+
values = bech32_hrp_expand(hrp) + data
52+
polymod = bech32_polymod(values + [0, 0, 0, 0, 0, 0]) ^ 1
53+
return [(polymod >> 5 * (5 - i)) & 31 for i in range(6)]
54+
55+
56+
def bech32_encode(hrp, data):
57+
"""Compute a Bech32 string given HRP and data values."""
58+
combined = data + bech32_create_checksum(hrp, data)
59+
return hrp + '1' + ''.join([CHARSET[d] for d in combined])
60+
61+
62+
def bech32_decode(bech):
63+
"""Validate a Bech32 string, and determine HRP and data."""
64+
if ((any(ord(x) < 33 or ord(x) > 126 for x in bech)) or
65+
(bech.lower() != bech and bech.upper() != bech)):
66+
return (None, None)
67+
bech = bech.lower()
68+
pos = bech.rfind('1')
69+
if pos < 1 or pos + 7 > len(bech) or len(bech) > 90:
70+
return (None, None)
71+
if not all(x in CHARSET for x in bech[pos+1:]):
72+
return (None, None)
73+
hrp = bech[:pos]
74+
data = [CHARSET.find(x) for x in bech[pos+1:]]
75+
if not bech32_verify_checksum(hrp, data):
76+
return (None, None)
77+
return (hrp, data[:-6])
78+
79+
80+
def convertbits(data, frombits, tobits, pad=True):
81+
"""General power-of-2 base conversion."""
82+
acc = 0
83+
bits = 0
84+
ret = []
85+
maxv = (1 << tobits) - 1
86+
max_acc = (1 << (frombits + tobits - 1)) - 1
87+
for value in data:
88+
if value < 0 or (value >> frombits):
89+
return None
90+
acc = ((acc << frombits) | value) & max_acc
91+
bits += frombits
92+
while bits >= tobits:
93+
bits -= tobits
94+
ret.append((acc >> bits) & maxv)
95+
if pad:
96+
if bits:
97+
ret.append((acc << (tobits - bits)) & maxv)
98+
elif bits >= frombits or ((acc << (tobits - bits)) & maxv):
99+
return None
100+
return ret
101+
102+
103+
def decode(hrp, addr):
104+
"""Decode a segwit address."""
105+
hrpgot, data = bech32_decode(addr)
106+
if hrpgot != hrp:
107+
return (None, None)
108+
decoded = convertbits(data[1:], 5, 8, False)
109+
if decoded is None or len(decoded) < 2 or len(decoded) > 40:
110+
return (None, None)
111+
if data[0] > 16:
112+
return (None, None)
113+
if data[0] == 0 and len(decoded) != 20 and len(decoded) != 32:
114+
return (None, None)
115+
return (data[0], decoded)
116+
117+
118+
def encode(hrp, witver, witprog):
119+
"""Encode a segwit address."""
120+
ret = bech32_encode(hrp, [witver] + convertbits(witprog, 8, 5))
121+
if decode(hrp, ret) == (None, None):
122+
return None
123+
return ret

pubkey_address.py

+24-4
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import bitcoin_base58
55
import base58
66
import hash_utils
7+
import bitcoin_bech32
78

89
def compressPubkey(pubkey: bytes):
910
x_b = pubkey[1:33]
@@ -53,13 +54,20 @@ def sh2address(sh: bytes, is_testnet: bool):
5354

5455
def redeemScript2address(script: bytes, is_testnet: bool):
5556
sh = hash_utils.hash160(script)
56-
address = bitcoin_base58.forAddress(sh, is_testnet, True)
57+
address = sh2address(sh, is_testnet)
5758
return address
5859

59-
def addressCheckVerify(address: str, is_testnet: bool, is_script: bool):
60-
is_valid = bitcoin_base58.addressVerify(address, is_testnet, is_script)
60+
def addressCheckVerify(address: str):
61+
is_valid = False
62+
if address[0] in ['1', '3', 'm', 'n', '2']:
63+
is_valid = bitcoin_base58.addressVerify(address)
64+
elif address[0:3] in ['bc1', 'tb1']:
65+
is_valid = bitcoin_bech32.addressVerify(address)
6166
return is_valid
6267

68+
def witnessProgram2address(hrp: str, witver: int, witprog: bytes):
69+
return bitcoin_bech32.encode(hrp, witver, witprog)
70+
6371
if __name__ == '__main__':
6472
pubkey = privkey2pubkey(0x18e14a7b6a307f426a94f8114701e7c8e774e7f9a47e2c2035db29a206321725, False)
6573
print ('Full pubkey = %s' % bytes.decode(binascii.hexlify(pubkey)))
@@ -69,8 +77,20 @@ def addressCheckVerify(address: str, is_testnet: bool, is_script: bool):
6977
print('address = %s' % address)
7078
pubkey = uncompressPubkey(pubkey)
7179
print ('uncompressed pubkey = %s' % bytes.decode(binascii.hexlify(pubkey)))
72-
is_valid = addressCheckVerify(address, False, False)
80+
is_valid = addressCheckVerify(address)
7381
print('Is Address valid: %r' % is_valid)
7482
h160 = 'e9c3dd0c07aac76179ebc76a6c78d4d67c6c160a'
7583
address = sh2address(binascii.unhexlify(h160), False)
7684
print ('P2SH address = %s' % address)
85+
is_valid = addressCheckVerify(address)
86+
print('Is Address valid: %r' % is_valid)
87+
witprog = binascii.unhexlify('701a8d401c84fb13e6baf169d59684e17abd9fa216c8cc5b9fc63d622ff8c58d')
88+
witver = 0x00
89+
hrp = 'bc'
90+
address = witnessProgram2address(hrp, witver, witprog)
91+
print('WSH witness address = %s' % address)
92+
witprog = binascii.unhexlify('04411aab1f36d417d6e96da77cc708d6c703f067')
93+
witver = 0x00
94+
hrp = 'bc'
95+
address = witnessProgram2address(hrp, witver, witprog)
96+
print('WPKH witness address = %s' % address)

script_parser.py

+5-4
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,10 @@
2020
#txn_hash = 'a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d'
2121
#txn_hash = '8ca45aed169b0434ad5a117804cdf6eec715208d57f13396c0ba18fb5a327e30' # P2PKH
2222
#txn_hash = '40eee3ae1760e3a8532263678cdf64569e6ad06abc133af64f735e52562bccc8'
23-
txn_hash = '7edb32d4ffd7a385b763c7a8e56b6358bcd729e747290624e18acdbe6209fc45' # P2SH
24-
#txn_hash = '581cff2eab901adce9e62f08ee7e8306d7a6d678a1974c029d33ec9426d0b14f' #Bare P2WSH
23+
#txn_hash = '7edb32d4ffd7a385b763c7a8e56b6358bcd729e747290624e18acdbe6209fc45' # P2SH
24+
txn_hash = '581cff2eab901adce9e62f08ee7e8306d7a6d678a1974c029d33ec9426d0b14f' #Bare P2WSH
2525
#txn_hash = '7f48d5fd4c993305d5fe027c5ddc23b95a0757657f91de75cf1cd2dc732f284c' #P2SH-P2WPKH
26+
#txn_hash = '714e0aac85acd5748094c1a9b792ee847cbe2e9544e5d4f73bdbd0437550cda4'
2627
#block_hash = '0000000000000000009a8aa7b36b0e37a28bf98956097b7b844e172692e604e1' # Pre-Segwit
2728
#block_hash = '0000000000000000000e377cd4083945678ad30c533a8729198bf3b12a8e9315' # Segwit block
2829

@@ -1135,5 +1136,5 @@ def bareP2WSH():
11351136
# scriptCode='210321a1b4858bb05350c68190a2b4517aeac6b4ec72e9a9a059e543083c63915423'
11361137
# print('scriptCode = %s' % scriptCode)
11371138

1138-
block_hash_bigendian_b = binascii.unhexlify(block_hash)[::-1]
1139-
validate_all_transactions_of_block(block_hash_bigendian_b)
1139+
# block_hash_bigendian_b = binascii.unhexlify(block_hash)[::-1]
1140+
# validate_all_transactions_of_block(block_hash_bigendian_b)

0 commit comments

Comments
 (0)