# Gavin Andresen # 2010-09-13 12:38:24 # https://bitcointalk.org/index.php?topic=1026.msg12557#msg12557 This bitcoind address validator is a subclass of the Django forms.CharField class, but could easily be adapted to other frameworks or to be standalone code. @p{par} It does a "deep" validation, checking that the checksum built into every bitcoin address matches the address. It needs the PyCrypto library for the SHA256 function. @p{par} I hereby release this code into the public domain, do with it what you will. And please let me know if you find any bugs in it. @p{par} BCAddressField.py: Code: # @p{brk} # DJango field type for a Bitcoin Address @p{brk} # @p{brk} import re @p{brk} from django import forms @p{brk} from django.forms.util import ValidationError @p{brk} from Crypto.Hash import SHA256 @p{par} class BCAddressField(forms.CharField): @p{brk} default_error_messages = { @p{brk} 'invalid': 'Invalid Bitcoin address.', @p{brk} } @p{par} def __init__(self, *args, **kwargs): @p{brk} super(BCAddressField, self).__init__(*args, **kwargs) @p{par} def clean(self, value): @p{brk} value = value.strip() @p{brk} if re.match(r"[a-zA-Z1-9]{27,35}$", value) is None: @p{brk} raise ValidationError(self.error_messages['invalid']) @p{brk} version = get_bcaddress_version(value) @p{brk} if version is None: @p{brk} raise ValidationError(self.error_messages['invalid']) @p{brk} return value @p{par} import math @p{par} __b58chars = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz' @p{brk} __b58base = len(__b58chars) @p{par} def b58encode(v): @p{brk} """ encode v, which is a string of bytes, to base58. @p{brk} """ @p{par} long_value = 0L @p{brk} for (i, c) in enumerate(v[::-1]): @p{brk} long_value += (256**i) * ord(c) @p{par} result = '' @p{brk} while long_value @s{gt}= __b58base: @p{brk} div, mod = divmod(long_value, __b58base) @p{brk} result = __b58chars[mod] + result @p{brk} long_value = div @p{brk} result = __b58chars[long_value] + result @p{par} # Bitcoin does a little leading-zero-compression: @p{brk} # leading 0-bytes in the input become leading-1s @p{brk} nPad = 0 @p{brk} for c in v: @p{brk} if c == '\0': nPad += 1 @p{brk} else: break @p{par} return (__b58chars[0]*nPad) + result @p{par} def b58decode(v, length): @p{brk} """ decode v into a string of len bytes @p{brk} """ @p{brk} long_value = 0L @p{brk} for (i, c) in enumerate(v[::-1]): @p{brk} long_value += __b58chars.find(c) * (__b58base**i) @p{par} result = '' @p{brk} while long_value @s{gt}= 256: @p{brk} div, mod = divmod(long_value, 256) @p{brk} result = chr(mod) + result @p{brk} long_value = div @p{brk} result = chr(long_value) + result @p{par} nPad = 0 @p{brk} for c in v: @p{brk} if c == __b58chars[0]: nPad += 1 @p{brk} else: break @p{par} result = chr(0)*nPad + result @p{brk} if length is not None and len(result) != length: @p{brk} return None @p{par} return result @p{par} def get_bcaddress_version(strAddress): @p{brk} """ Returns None if strAddress is invalid. Otherwise returns integer version of address. """ @p{brk} addr = b58decode(strAddress,25) @p{brk} if addr is None: return None @p{brk} version = addr[0] @p{brk} checksum = addr[-4:] @p{brk} vh160 = addr[:-4] # Version plus hash160 is what is checksummed @p{brk} h3=SHA256.new(SHA256.new(vh160).digest()).digest() @p{brk} if h3[0:4] == checksum: @p{brk} return ord(version) @p{brk} return None @p{brk} @p{(bf}October 20:@p{bf)} Fixed bug with bitcoin addresses with leading-1's.