Bitcoin Forum
June 23, 2024, 01:08:50 PM *
News: Latest Bitcoin Core release: 27.0 [Torrent]
 
  Home Help Search Login Register More  
  Show Posts
Pages: [1]
1  Bitcoin / Development & Technical Discussion / Re: Python code for validating bitcoin address on: February 25, 2014, 11:47:14 PM
Special thanks to Gavin Anderson

So I re-worked Gavin's code just a little bit, so I could basically type cast a Bitcoin address.

Below is my code.  Please let me know if you find any issues.

Code:
#!/usr/bin/env python
"""
# Special thanks to Gavin Andresen from bitcointalk.org
# in reference to: https://bitcointalk.org/index.php?topic=1026.0
# Edited By/Author Josh Lee PyTis.com, I also release my changes into the
# public domain.
"""

class BTCaddress(str):
""" Custom Bitcoin Address Verify-turend into a type for type-casting.
"""
import re

address_pattern = "[a-zA-Z1-9]{27,35}$"
address_expr = re.compile(r"%s"%address_pattern)
__b58chars = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
__b58base = len(__b58chars)
_valid = False
_version = None

def __new__(cls,val):
if val is None:
# allow None to be passed in, so that we may clear out bitcoin addresses
# if you want, youc an raise an error instead, so that None doesn't pass silently
inst = str.__new__(cls, None)
inst._valid = False
inst._version = None
return inst

# I don't know if there could be a valid address with only numbers, but just in-case
if isinstance(val, int): val = str(val)

# remove preceading and trailing whitespace
if isinstance(val, basestring):
val = val.strip()
if not val:
# again, allow '' to be passed in, so that we may clear out bitcoin addresses
# if you want, you can raise an error instead, so that None doesn't pass silently
inst = str.__new__(cls, None)
inst._valid = False
inst._version = None
return inst
else:
if not cls.address_expr.match(val):
raise ValueError('Invalid Bitcoin address. Bad RE match r"%s".' % cls.address_pattern)
version = cls.getVersion(val)
if version is None:
raise ValueError('Invalid Bitcoin address. Version Missmatch: %r' % version)
else:
inst = str.__new__(cls, val)
inst._valid = True
inst._version = version
return inst
else:
raise TypeError('Invalid Bitcoin address. Not a string.')

valid = property(lambda s: s._valid)
version = property(lambda s: s._version)

@classmethod
def getVersion(cls, strAddress):
""" Returns None if strAddress is invalid. Otherwise returns integer version of address. """
from Crypto.Hash import SHA256
addr = cls.b58decode(strAddress,25)
if addr is None: return None
version = addr[0]
checksum = addr[-4:]
vh160 = addr[:-4] # Version plus hash160 is what is checksummed
h3=SHA256.new(SHA256.new(vh160).digest()).digest()
if h3[0:4] == checksum:
return ord(version)
return None

@classmethod
def b58encode(cls, v):
""" encode v, which is a string of bytes, to base58.
"""
long_value = 0L
for (i, c) in enumerate(v[::-1]):
long_value += (256**i) * ord(c)
result = ''
while long_value >= cls.__b58base:
div, mod = divmod(long_value, cls.__b58base)
result = cls.__b58chars[mod] + result
long_value = div
result = cls.__b58chars[long_value] + result

# Bitcoin does a little leading-zero-compression:
# leading 0-bytes in the input become leading-1s
nPad = 0
for c in v:
if c == '\0': nPad += 1
else: break

return (cls.__b58chars[0]*nPad) + result

@classmethod
def b58decode(cls, v, length):
""" decode v into a string of len bytes
"""
long_value = 0L
for (i, c) in enumerate(v[::-1]):
long_value += cls.__b58chars.find(c) * (cls.__b58base**i)

result = ''
while long_value >= 256:
div, mod = divmod(long_value, 256)
result = chr(mod) + result
long_value = div
result = chr(long_value) + result

nPad = 0
for c in v:
if c == cls.__b58chars[0]: nPad += 1
else: break

result = chr(0)*nPad + result
if length is not None and len(result) != length:
return None

return result

# - - - - - - - - - - - - - -
# END OF Custom Type class.
#



#
# tests below
# I found a page on a bitcoin wiki to explain to me different bitcoin address
# versions, I decided to test these, and other things that could go wrong.
# I then altered my code above, to get desired results.
#
# For more information on bitcoin address versions, please follow link:
# https://en.bitcoin.it/wiki/List_of_address_prefixes

tests = [
'17VZNX1SN5NtKa8UQFxwQbFeFc3iqRYhem', # version 0 || TEST 1
u'3EktnHQD7RiAE6uzMj2ZifT9YgRrkSgzQX', # version 5 || TEST 2
'LhK2kQwiaAvhjWY799cZvMyYwnQAcxkarr', # vesrion 48 || TEST 3
'NATX6zEUNfxfvgVwz8qVnnw3hLhhYXhgQn', # version 52 || TEST 4
'mipcBbFg9gMiCh81Kj8tqqdgoZub1ZJRfn', # version 111 || TEST 5
'5Hwgr3u458GLafKBgxtssHSPqJnYoGrSzgQsPwLFhLNYskDPyyA', # version 128 || TEST 6
'92Pg46rUhgTT7romnV7iGW6W1gbGdeezqdbJCzShkCsYNzyyNcc', # version 239 || TEST 7
None, # testing None, No version || TEST 8
' 1111111111111111111114oLvT2', # test from https://bitcointalk.org/index.php?topic=1026.0 || TEST 9
float(123456789.01), #|| TEST 10
bool(True), # || TEST 11
'', # || TEST 12
'1NnZEPLmBEK2HryY4heeJQhQPB8LLbNiPe' # my bitcoin address :-) || TEST 13
]

for i, test in enumerate(tests):
print '-'*80
print 'typeof test: ', type(test)
print 'original value: ', test
try:
t = BTCaddress(test)
except TypeError, e:
print "TypeError: %s" % str(e)
except  ValueError, e:
print "ValueError: %s" % str(e)
else:
print 'test %s' % i, t
print 'test %s.type' % i, type(t)
print 'test %s.valid' % i, t.valid
print 'test %s.version' % i, t.version
print 't.getVersion()', t.getVersion(str(t))

print


-- fixed

Test output below:
Quote
--------------------------------------------------------------------------------
typeof test:  <type 'str'>
original value:  17VZNX1SN5NtKa8UQFxwQbFeFc3iqRYhem
test 0 17VZNX1SN5NtKa8UQFxwQbFeFc3iqRYhem
test 0.type <class '__main__.BTCaddress'>
test 0.valid True
test 0.version 0
--------------------------------------------------------------------------------
typeof test:  <type 'unicode'>
original value:  3EktnHQD7RiAE6uzMj2ZifT9YgRrkSgzQX
test 1 3EktnHQD7RiAE6uzMj2ZifT9YgRrkSgzQX
test 1.type <class '__main__.BTCaddress'>
test 1.valid True
test 1.version 5
--------------------------------------------------------------------------------
typeof test:  <type 'str'>
original value:  LhK2kQwiaAvhjWY799cZvMyYwnQAcxkarr
test 2 LhK2kQwiaAvhjWY799cZvMyYwnQAcxkarr
test 2.type <class '__main__.BTCaddress'>
test 2.valid True
test 2.version 48
--------------------------------------------------------------------------------
typeof test:  <type 'str'>
original value:  NATX6zEUNfxfvgVwz8qVnnw3hLhhYXhgQn
test 3 NATX6zEUNfxfvgVwz8qVnnw3hLhhYXhgQn
test 3.type <class '__main__.BTCaddress'>
test 3.valid True
test 3.version 52
--------------------------------------------------------------------------------
typeof test:  <type 'str'>
original value:  mipcBbFg9gMiCh81Kj8tqqdgoZub1ZJRfn
test 4 mipcBbFg9gMiCh81Kj8tqqdgoZub1ZJRfn
test 4.type <class '__main__.BTCaddress'>
test 4.valid True
test 4.version 111
--------------------------------------------------------------------------------
typeof test:  <type 'str'>
original value:  5Hwgr3u458GLafKBgxtssHSPqJnYoGrSzgQsPwLFhLNYskDPyyA
ValueError: Invalid Bitcoin address. Bad RE match r"[a-zA-Z1-9]{27,35}$".
--------------------------------------------------------------------------------
typeof test:  <type 'str'>
original value:  92Pg46rUhgTT7romnV7iGW6W1gbGdeezqdbJCzShkCsYNzyyNcc
ValueError: Invalid Bitcoin address. Bad RE match r"[a-zA-Z1-9]{27,35}$".
--------------------------------------------------------------------------------
typeof test:  <type 'NoneType'>
original value:  None
test 7 None
test 7.type <class '__main__.BTCaddress'>
test 7.valid False
test 7.version None
--------------------------------------------------------------------------------
typeof test:  <type 'str'>
original value:   1111111111111111111114oLvT2
test 8 1111111111111111111114oLvT2
test 8.type <class '__main__.BTCaddress'>
test 8.valid True
test 8.version 0
--------------------------------------------------------------------------------
typeof test:  <type 'float'>
original value:  123456789.01
TypeError: Invalid Bitcoin address. Not a string.
--------------------------------------------------------------------------------
typeof test:  <type 'bool'>
original value:  True
ValueError: Invalid Bitcoin address. Bad RE match r"[a-zA-Z1-9]{27,35}$".
--------------------------------------------------------------------------------
typeof test:  <type 'str'>
original value:  
test 11 None
test 11.type <class '__main__.BTCaddress'>
test 11.valid False
test 11.version None
--------------------------------------------------------------------------------
typeof test:  <type 'str'>
original value:  1NnZEPLmBEK2HryY4heeJQhQPB8LLbNiPe
test 12 1NnZEPLmBEK2HryY4heeJQhQPB8LLbNiPe
test 12.type <class '__main__.BTCaddress'>
test 12.valid True
test 12.version 0

-- last fix was just a typo in a code's comment
Pages: [1]
Powered by MySQL Powered by PHP Powered by SMF 1.1.19 | SMF © 2006-2009, Simple Machines Valid XHTML 1.0! Valid CSS!