nima
|
|
December 09, 2012, 10:07:13 PM |
|
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. 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. 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. BCAddressField.py: # # DJango field type for a Bitcoin Address # import re from django import forms from django.forms.util import ValidationError from Crypto.Hash import SHA256
class BCAddressField(forms.CharField): default_error_messages = { 'invalid': 'Invalid Bitcoin address.', }
def __init__(self, *args, **kwargs): super(BCAddressField, self).__init__(*args, **kwargs)
def clean(self, value): value = value.strip() if re.match(r"[a-zA-Z1-9]{27,35}$", value) is None: raise ValidationError(self.error_messages['invalid']) version = get_bcaddress_version(value) if version is None: raise ValidationError(self.error_messages['invalid']) return value
import math
__b58chars = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz' __b58base = len(__b58chars)
def b58encode(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 >= __b58base: div, mod = divmod(long_value, __b58base) result = __b58chars[mod] + result long_value = div result = __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 (__b58chars[0]*nPad) + result
def b58decode(v, length): """ decode v into a string of len bytes """ long_value = 0L for (i, c) in enumerate(v[::-1]): long_value += __b58chars.find(c) * (__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 == __b58chars[0]: nPad += 1 else: break
result = chr(0)*nPad + result if length is not None and len(result) != length: return None
return result
def get_bcaddress_version(strAddress): """ Returns None if strAddress is invalid. Otherwise returns integer version of address. """ addr = 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
October 20: Fixed bug with bitcoin addresses with leading-1's. Hi Gavin, Thanks for the code. I used it in a python script to validate a bunch of addresses. One invalid address managed to escape the validation (miwxGypTcHDXT3m4avmrMMC4co7XWqbG9r). Would you please confirm if the address says to be valid when you check it with your code? Thank you
|
|
|
|
cande
Member
Offline
Activity: 107
Merit: 10
https://bt.cx
|
|
February 17, 2013, 10:23:46 AM Last edit: February 18, 2013, 09:08:17 AM by cande |
|
Thanks for this contribution! I ran the test fixtures for valid and invalid public keys listed below, with a few of my own known keys added; I got the following result: There was 1 failure: 1) Lmh\BitcoinTransferBundle\Tests\BitcoinAddressValidatorTest::testInvalid Test invalid public key '37qgekLpCCHrQuSjvX3fs496FWTGsHFHizjJAs6NPcR47aefnnCWECAhHV6E3g4YN7u7Yuwod5Y' returns 1 error. Failed asserting that 0 matches expected 1. /Users/matmar10/Projects/bitcoinbymobile/src/Lmh/BitcoinTransferBundle/Tests/BitcoinAddressValidatorTest.php:69 FAILURES! Tests: 2, Assertions: 22, Failures: 1. By the way, your PHP code had an undefined variable throwing an error and also an unused variable; I'll send you a git push request. Hi, using the PHP code I can see that there has been no update on that gitrepo for the past 8 months, what correction did you find? can you post it here? false negative address I found: 13saQdsv2XbkL9ef9FFT92m2zDaBGKwVGM
|
|
|
|
Rotsor
Full Member
Offline
Activity: 309
Merit: 102
Presale is live!
|
|
May 03, 2013, 06:10:52 PM |
|
Haskell translation: module Main where
import Data.List import Data.Maybe import Data.Word import Data.Tuple import Control.Arrow import Control.Monad import qualified Crypto.Hash.SHA256 as SHA256 import qualified Data.ByteString as B
fromBase :: Integral a => Integer -> [a] -> Integer fromBase b = foldl (\a c -> fromIntegral c + a * b) 0
toBase :: Num a => Integer -> Integer -> [a] toBase b = reverse . unfoldr f where f 0 = Nothing f n = Just $ first fromInteger $ swap $ n `divMod` b
b58chars = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
b58encode :: [Word8] -> [Char] b58encode l = replicate (length $ takeWhile(==0) l) '1' ++ (map (b58chars!!) . toBase 58 . fromBase 256) l
fromb58char c = fst . fromJust . find ((==c) . snd) $ zip [0..] b58chars
b58decode v = case span (== b58chars !! 0) v of (ones, v) -> replicate (length ones) 0 ++ (toBase 256 . fromBase 58 . map fromb58char) v
b58decodeL l v = let res = b58decode v in guard (length res == l) >> return res
sha256x2 = B.unpack . SHA256.hash . SHA256.hash . B.pack where
checksum = take 4 . sha256x2
mkAddress :: Word8 -> [Word8] -> [Char] mkAddress v d = b58encode $ v : d ++ checksum (v : d)
validateAddress :: [Char] -> Maybe (Word8, [Word8]) validateAddress addr = do (version : d) <- b58decodeL 25 addr case splitAt 20 d of (d, check) -> do guard $ checksum (version : d) == check return (version, d)
The code is under WTFPL.
|
|
|
|
grondilu
Legendary
Offline
Activity: 1288
Merit: 1080
|
|
May 03, 2013, 06:15:42 PM |
|
BTW did I tell you guys that there is a rosetta code entry for bitcoin address validation?
|
|
|
|
Viceroy
|
|
May 07, 2013, 05:05:44 PM Last edit: May 09, 2013, 09:02:09 PM by Viceroy |
|
I REALLY did want to use your code, grondilu, but it lacks documentation. I tried to use the perl version and the bash version but couldn't get either to work. I'm sure it's a lack of understanding on my part.
Instead I grabbed Xenland's modification of MagicalTux's php script and made a couple changes:
EDIT: THIS CODE GIVES FALSE NEGATIVES SHOWING PERFECTLY GOOD ADDRESSES AS BAD!!!
[code retracted]
working code a few posts down the thread
that way | | | V
|
|
|
|
grondilu
Legendary
Offline
Activity: 1288
Merit: 1080
|
|
May 07, 2013, 10:22:33 PM |
|
I REALLY did want to use your code, grondilu, but it lacks documentation. I tried to use the perl version and the bash version but couldn't get either to work. I'm sure it's a lack of understanding on my part.
Could you report your error message here or on the rosettacode talk page please?
|
|
|
|
Viceroy
|
|
May 08, 2013, 11:25:37 PM |
|
I REALLY did want to use your code, grondilu, but it lacks documentation. I tried to use the perl version and the bash version but couldn't get either to work. I'm sure it's a lack of understanding on my part.
Could you report your error message here or on the rosettacode talk page please? lol, it would look like this: I'm too dumb to figure out how to use a bash script and I forget what issue it was that caused me to not want to use the perl, oh yea... I had no idea how to"feed" the address into the program. So I gave up. No reason to noise up your page with that. now I'm looking at http://blockexplorer.com/q/checkaddress as I found the above php useless.... it just gives false negatives. why is there no simple tool like http://blockexplorer.com/q/checkaddressoh, wait, maybe there is over at http://blockexplorer.com/q/checkaddress.. I'll report back (unless I just get lost in smoke and code). Oh, I should probably start with my objective.... but finishing with it will suffice: I am trying to figure out how to check bitcoin addresses and make bitcoin addresses and valid data in an online php form.
|
|
|
|
theymos
Administrator
Legendary
Offline
Activity: 5376
Merit: 13348
|
|
May 08, 2013, 11:48:35 PM |
|
/q/checkaddress uses this plus some extra checking. Note that the checkAddress function assumes that the input is valid base58. Do something like: if(preg_match('/^[1-9A-HJ-NP-Za-km-z]+$/', $address) && strlen($address) <= 34 && checkAddress($address)) address is valid
|
1NXYoJ5xU91Jp83XfVMHwwTUyZFK64BoAD
|
|
|
Viceroy
|
|
May 09, 2013, 12:24:48 AM Last edit: May 09, 2013, 09:01:49 PM by Viceroy |
|
thanks!
EDIT
working code:
Shows Good Addresses as bad.
[retracted code]
working code a few posts down the thread
that way | | | V
|
|
|
|
jackjack
Legendary
Offline
Activity: 1176
Merit: 1280
May Bitcoin be touched by his Noodly Appendage
|
|
May 09, 2013, 02:21:08 PM |
|
Did you try pywallet's code? Look for DecodeBase58Check or something like that
|
Own address: 19QkqAza7BHFTuoz9N8UQkryP4E9jHo4N3 - Pywallet support: 1AQDfx22pKGgXnUZFL1e4UKos3QqvRzNh5 - Bitcointalk++ script support: 1Pxeccscj1ygseTdSV1qUqQCanp2B2NMM2 Pywallet: instructions. Encrypted wallet support, export/import keys/addresses, backup wallets, export/import CSV data from/into wallet, merge wallets, delete/import addresses and transactions, recover altcoins sent to bitcoin addresses, sign/verify messages and files with Bitcoin addresses, recover deleted wallets, etc.
|
|
|
Viceroy
|
|
May 09, 2013, 02:21:19 PM Last edit: May 09, 2013, 02:37:06 PM by Viceroy |
|
I'm not getting the same results I see from http://blockexplorer.com/q/checkaddress/ with the above php code. Checking further... Are there any commercial use restrictions on grabbing data from blockexplore.com? ps. got a link for that, jack?
|
|
|
|
jackjack
Legendary
Offline
Activity: 1176
Merit: 1280
May Bitcoin be touched by his Noodly Appendage
|
|
May 09, 2013, 02:36:15 PM |
|
|
Own address: 19QkqAza7BHFTuoz9N8UQkryP4E9jHo4N3 - Pywallet support: 1AQDfx22pKGgXnUZFL1e4UKos3QqvRzNh5 - Bitcointalk++ script support: 1Pxeccscj1ygseTdSV1qUqQCanp2B2NMM2 Pywallet: instructions. Encrypted wallet support, export/import keys/addresses, backup wallets, export/import CSV data from/into wallet, merge wallets, delete/import addresses and transactions, recover altcoins sent to bitcoin addresses, sign/verify messages and files with Bitcoin addresses, recover deleted wallets, etc.
|
|
|
Viceroy
|
|
May 09, 2013, 02:37:18 PM Last edit: May 09, 2013, 02:56:31 PM by Viceroy |
|
thank you thank you. dang dude, I can't dive into a 1700 line program right now. what's that do with all those lines? fwiw my code is down to 7 lines, and two of those are comments. <?php // use with validator.php?refund=btc-address-here $validateBTCURL = 'http://blockexplorer.com/q/checkaddress/' . $_GET["refund"]; //echo $validateBTCURL; $validBTC = file_get_contents($validateBTCURL); //echo $validBTC; if($validBTC == "00"){ //do something } else { //do something else } ?>
|
|
|
|
jackjack
Legendary
Offline
Activity: 1176
Merit: 1280
May Bitcoin be touched by his Noodly Appendage
|
|
May 09, 2013, 03:45:08 PM |
|
That's why I told you to look for the definition of DecodeBase58Check in the code, that's the function to decode address to hash160
|
Own address: 19QkqAza7BHFTuoz9N8UQkryP4E9jHo4N3 - Pywallet support: 1AQDfx22pKGgXnUZFL1e4UKos3QqvRzNh5 - Bitcointalk++ script support: 1Pxeccscj1ygseTdSV1qUqQCanp2B2NMM2 Pywallet: instructions. Encrypted wallet support, export/import keys/addresses, backup wallets, export/import CSV data from/into wallet, merge wallets, delete/import addresses and transactions, recover altcoins sent to bitcoin addresses, sign/verify messages and files with Bitcoin addresses, recover deleted wallets, etc.
|
|
|
theymos
Administrator
Legendary
Offline
Activity: 5376
Merit: 13348
|
|
May 09, 2013, 08:15:36 PM |
|
In what cases? It should be identical other than /q/checkaddress's additional info about why the address failed.
|
1NXYoJ5xU91Jp83XfVMHwwTUyZFK64BoAD
|
|
|
Viceroy
|
|
May 09, 2013, 08:57:53 PM |
|
after further testing it appears to be working as expected. Here's the working code: called with: http://yourdomain/btcvalidate.php?address="btc-address-here" btcvalidate.php: <?php include("base58.php"); ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <!--- - - - - - - - - - - - - - - - - - - - - - ---> <!--- name this file btcvalidate.php on webserver ---> <!--- ---> <!--- place base58.php in same directory ---> <!--- ---> <!--- call with btc address as get function: ---> <!--- btcvalidate.php?address="btc-address-here" ---> <!--- ---> <!--- warning: no checking on input ---> <!--- - - - - - - - - - - - - - - - - - - - - - ---> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>btc address validator</title> </head> <body> <?php $address = $_GET["address"]; if(preg_match('/^[1-9A-HJ-NP-Za-km-z]+$/', $address) && strlen($address) <= 34 && checkAddress($address)){ echo "<p>address is valid</p>"; } else { echo "<p>address is NOT valid</p>"; } ?> </body> </html>
base58.php: <?php /* <!--- - - - - - - - - - - - - - - - - - - - - - ---> <!--- name this file base58.php on webserver ---> <!--- - - - - - - - - - - - - - - - - - - - - - ---> */ //hex input must be in uppercase, with no leading 0x define("ADDRESSVERSION","00"); //this is a hex byte
function decodeHex($hex){ $hex=strtoupper($hex); $chars="0123456789ABCDEF"; $return="0"; for($i=0;$i<strlen($hex);$i++){ $current=(string)strpos($chars,$hex[$i]); $return=(string)bcmul($return,"16",0); $return=(string)bcadd($return,$current,0); } return $return; }
function encodeHex($dec){ $chars="0123456789ABCDEF"; $return=""; while (bccomp($dec,0)==1){ $dv=(string)bcdiv($dec,"16",0); $rem=(integer)bcmod($dec,"16"); $dec=$dv; $return=$return.$chars[$rem]; } return strrev($return); }
function decodeBase58($base58){ $origbase58=$base58; $chars="123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; $return="0"; for($i=0;$i<strlen($base58);$i++){ $current=(string)strpos($chars,$base58[$i]); $return=(string)bcmul($return,"58",0); $return=(string)bcadd($return,$current,0); } $return=encodeHex($return); //leading zeros for($i=0;$i<strlen($origbase58)&&$origbase58[$i]=="1";$i++){ $return="00".$return; } if(strlen($return)%2!=0){ $return="0".$return; } return $return; }
function encodeBase58($hex){ if(strlen($hex)%2!=0){ die("encodeBase58: uneven number of hex characters"); } $orighex=$hex; $chars="123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; $hex=decodeHex($hex); $return=""; while (bccomp($hex,0)==1){ $dv=(string)bcdiv($hex,"58",0); $rem=(integer)bcmod($hex,"58"); $hex=$dv; $return=$return.$chars[$rem]; } $return=strrev($return); //leading zeros for($i=0;$i<strlen($orighex)&&substr($orighex,$i,2)=="00";$i+=2){ $return="1".$return; } return $return; }
function hash160ToAddress($hash160,$addressversion=ADDRESSVERSION){ $hash160=$addressversion.$hash160; $check=pack("H*" , $hash160); $check=hash("sha256",hash("sha256",$check,true)); $check=substr($check,0,8); $hash160=strtoupper($hash160.$check); return encodeBase58($hash160); }
function addressToHash160($addr){ $addr=decodeBase58($addr); $addr=substr($addr,2,strlen($addr)-10); return $addr; }
function checkAddress($addr,$addressversion=ADDRESSVERSION){ $addr=decodeBase58($addr); if(strlen($addr)!=50){ return false; } $version=substr($addr,0,2); if(hexdec($version)>hexdec($addressversion)){ return false; } $check=substr($addr,0,strlen($addr)-8); $check=pack("H*" , $check); $check=strtoupper(hash("sha256",hash("sha256",$check,true))); $check=substr($check,0,8); return $check==substr($addr,strlen($addr)-8); }
function hash160($data){ $data=pack("H*" , $data); return strtoupper(hash("ripemd160",hash("sha256",$data,true))); }
function pubKeyToAddress($pubkey){ return hash160ToAddress(hash160($pubkey)); }
function remove0x($string){ if(substr($string,0,2)=="0x"||substr($string,0,2)=="0X"){ $string=substr($string,2); } return $string; }
?>
|
|
|
|
Eisenhower34
Legendary
Offline
Activity: 906
Merit: 1002
|
|
May 27, 2013, 12:16:27 PM |
|
The code mentioned in the original post returns true for BTC and LTC addresses, because get_bcaddress_version(address)
returns 0 for BTC addresses and 48 for LTC addresses, so both is not None. I wouldnt call this a bug, more a feature that allowes to check other coin addresses.
|
|
|
|
Melbustus
Legendary
Offline
Activity: 1722
Merit: 1004
|
|
July 08, 2013, 08:29:18 AM |
|
/q/checkaddress uses this plus some extra checking. Note that the checkAddress function assumes that the input is valid base58. Do something like: if(preg_match('/^[1-9A-HJ-NP-Za-km-z]+$/', $address) && strlen($address) <= 34 && checkAddress($address)) address is valid theymos - that pastebin-linked code is yours, right, from: http://code.gogulski.com/bitcoin-php/Just want to make sure the public domain license applies before I integrate. Thanks!
|
Bitcoin is the first monetary system to credibly offer perfect information to all economic participants.
|
|
|
theymos
Administrator
Legendary
Offline
Activity: 5376
Merit: 13348
|
|
July 08, 2013, 01:37:29 PM |
|
The pastebin code is mine and public domain. That bitcoin-php library is a separate thing that I wasn't involved with.
|
1NXYoJ5xU91Jp83XfVMHwwTUyZFK64BoAD
|
|
|
Melbustus
Legendary
Offline
Activity: 1722
Merit: 1004
|
|
July 08, 2013, 08:07:42 PM |
|
The pastebin code is mine and public domain. That bitcoin-php library is a separate thing that I wasn't involved with.
Great, thanks. I just glanced at the function sigs and assumed they were by the same author. In any event, I just integrated your code from that pastebin link. That was easy - thanks very much!
|
Bitcoin is the first monetary system to credibly offer perfect information to all economic participants.
|
|
|
|