| Warning: Many of these snippets areincomplete. It is recommended that you use an establishedlibrary for any projects that are likely to see external use |

Write a program that takes abitcoin address as argument, and checks whether or not this address is valid.
A bitcoin address uses a base58 encoding, which uses an alphabet of the characters 0 .. 9, A ..Z, a .. z, but without the four characters:
With this encoding, a bitcoin address encodes 25 bytes:
To check the bitcoin address, you must read the first twenty-one bytes, compute the checksum, and check that it corresponds to the last four bytes.
The program can either return a boolean value or throw an exception when not valid.
You can use a digest library forSHA-256.
1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i
It doesn't belong to anyone and is part of the test suite of the bitcoin software.
You can change a few characters in this string and check that it'll fail the test.
withAda.Exceptions,Interfaces;withAda.Streams;useAda.Exceptions,Interfaces;useAda.Streams;packageBitcoinissubtypeBT_Raw_AddrisStream_Element_Array(1..25);subtypeBT_ChecksumisStream_Element_Array(1..4);subtypeBT_AddrisString(1..34);subtypeSha256StringisString(1..64);Invalid_Address_Error:Exception;functionDouble_Sha256(S:Stream_Element_Array)returnBT_Checksum;functionIs_Valid(A:BT_Raw_Addr)returnBoolean;procedureBase58_Decode(S:BT_Addr;A:outBT_Raw_Addr);privateBase58:constantString:="123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";functionHex_Val(C,C2:Character)returnStream_Element;endBitcoin;withGNAT.SHA256,Ada.Strings.Fixed;useGNAT.SHA256,Ada.Strings.Fixed;packagebodyBitcoinisfunctionHex_Val(C,C2:Character)returnStream_ElementissubtypeNibbleisIntegerrange0..15;HEX:array(0..255)ofNibble:=(48=>0,49=>1,50=>2,51=>3,52=>4,53=>5,54=>6,55=>7,56=>8,57=>9,65=>10,66=>11,67=>12,68=>13,69=>14,70=>15,97=>10,98=>11,99=>12,100=>13,101=>14,102=>15,Others=>0);beginreturnStream_Element(HEX(Character'Pos(C))*16+HEX(Character'Pos(C2)));endHex_Val;functionDouble_Sha256(S:Stream_Element_Array)returnBT_ChecksumisCtx:Context:=Initial_Context;D:Message_Digest;S2:Stream_Element_Array(1..32);Ctx2:Context:=Initial_Context;C:BT_Checksum;beginUpdate(Ctx,S);D:=Digest(Ctx);forIinS2'RangeloopS2(I):=Hex_Val(D(Integer(I)*2-1),D(Integer(I)*2));endloop;Update(Ctx2,S2);D:=Digest(Ctx2);forIinC'RangeloopC(I):=Hex_Val(D(Integer(I)*2-1),D(Integer(I)*2));endloop;returnC;endDouble_Sha256;---------------------------------------------------------------------------------- Summary of Base58: ---- We decode S into a 200 bit unsigned integer. ---- We could use a BigNum library, but choose to go without. ----------------------------------------------------------------------------------procedureBase58_Decode(S:BT_Addr;A:outBT_Raw_Addr)isbeginA:=(Others=>0);forIinS'RangeloopdeclareP:Natural:=Index(Base58,String(S(I..I)));C:Natural;beginifP=0thenraiseInvalid_Address_Error;endif;C:=P-1;forJinreverseA'RangeloopC:=C+Natural(A(J))*58;A(J):=Stream_Element(Unsigned_32(C)and255);-- 0x00FFC:=Natural(Shift_Right(Unsigned_32(C),8)and255);-- 0xFF00endloop;ifC/=0thenraiseInvalid_Address_Error;endif;end;endloop;endBase58_Decode;functionIs_Valid(A:BT_Raw_Addr)returnBooleanisbeginreturnA(1)=0andA(22..25)=Double_Sha256(A(1..21));endIs_Valid;endBitcoin;withAda.Text_IO,Bitcoin;useAda.Text_IO,Bitcoin;procedureBitcoin_Addr_ValidateisbegindeclareBTs:array(positiverange<>)ofBT_Addr:=("1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i"-- VALID,"1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nK9"-- VALID,"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62X"-- checksum changed, original data.,"1ANNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i"-- data changed, original checksum.,"1A Na15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i"-- invalid chars);beginforIinBts'RangeloopdeclareA:BT_Raw_Addr;Valid:Boolean;beginPut(BTs(I)&" validity: ");Base58_Decode(BTs(I),A);Valid:=Is_Valid(A);Put_Line(Boolean'Image(Valid));exceptionwhenE:Invalid_Address_Error=>Put_Line("*** Error: Invalid BT address.");end;endloop;end;endBitcoin_Addr_Validate;
1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i validity: TRUE1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nK9 validity: TRUE1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62X validity: FALSE1ANNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i validity: FALSE1A Na15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i validity: *** Error: Invalid BT address.
#include<stdio.h>#include<string.h>#include<openssl/sha.h>constchar*coin_err;#define bail(s) { coin_err = s; return 0; }intunbase58(constchar*s,unsignedchar*out){staticconstchar*tmpl="123456789""ABCDEFGHJKLMNPQRSTUVWXYZ""abcdefghijkmnopqrstuvwxyz";inti,j,c;constchar*p;memset(out,0,25);for(i=0;s[i];i++){if(!(p=strchr(tmpl,s[i])))bail("bad char");c=p-tmpl;for(j=25;j--;){c+=58*out[j];out[j]=c%256;c/=256;}if(c)bail("address too long");}return1;}intvalid(constchar*s){unsignedchardec[32],d1[SHA256_DIGEST_LENGTH],d2[SHA256_DIGEST_LENGTH];coin_err="";if(!unbase58(s,dec))return0;SHA256(SHA256(dec,21,d1),SHA256_DIGEST_LENGTH,d2);if(memcmp(dec+21,d2,4))bail("bad digest");return1;}intmain(void){constchar*s[]={"1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nK9","1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i","1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nJ9","1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62I",0};inti;for(i=0;s[i];i++){intstatus=valid(s[i]);printf("%s: %s\n",s[i],status?"Ok":coin_err);}return0;}
Compile with -lcrypto
1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nK9: Ok1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i: Ok1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nJ9: bad digest1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62I: bad char
This requiresNUnit package to compile.
usingSystem;usingSystem.Linq;usingSystem.Security.Cryptography;usingNUnit.Framework;namespaceBitcoinValidator{publicclassValidateTest{[TestCase]publicvoidValidateBitcoinAddressTest(){Assert.IsTrue(ValidateBitcoinAddress("1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i"));// VALIDAssert.IsTrue(ValidateBitcoinAddress("1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nK9"));// VALIDAssert.Throws<Exception>(()=>ValidateBitcoinAddress("1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62X"));// checksum changed, original dataAssert.Throws<Exception>(()=>ValidateBitcoinAddress("1ANNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i"));// data changed, original checksumAssert.Throws<Exception>(()=>ValidateBitcoinAddress("1A Na15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i"));// invalid charsAssert.Throws<Exception>(()=>ValidateBitcoinAddress("BZbvjr"));// checksum is fine, address too short}publicstaticboolValidateBitcoinAddress(stringaddress){if(address.Length<26||address.Length>35)thrownewException("wrong length");vardecoded=DecodeBase58(address);vard1=Hash(decoded.SubArray(0,21));vard2=Hash(d1);if(!decoded.SubArray(21,4).SequenceEqual(d2.SubArray(0,4)))thrownewException("bad digest");returntrue;}conststringAlphabet="123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";constintSize=25;privatestaticbyte[]DecodeBase58(stringinput){varoutput=newbyte[Size];foreach(vartininput){varp=Alphabet.IndexOf(t);if(p==-1)thrownewException("invalid character found");varj=Size;while(--j>0){p+=58*output[j];output[j]=(byte)(p%256);p/=256;}if(p!=0)thrownewException("address too long");}returnoutput;}privatestaticbyte[]Hash(byte[]bytes){varhasher=newSHA256Managed();returnhasher.ComputeHash(bytes);}}publicstaticclassArrayExtensions{publicstaticT[]SubArray<T>(thisT[]data,intindex,intlength){varresult=newT[length];Array.Copy(data,index,result,0,length);returnresult;}}}
This example uses the C++ code from the SHA-256 task. This slightly complicates the code because the previous taskwas designed to hash a string of ASCII characters rather than a byte array.
#include<algorithm>#include<cstdint>#include<iostream>#include<map>#include<stdexcept>#include<string>#include<vector>#include"SHA256.cpp"SHA256sha256{};conststd::stringALPHABET="123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";std::map<char,uint32_t>base_map={{'0',0},{'1',1},{'2',2},{'3',3},{'4',4},{'5',5},{'6',6},{'7',7},{'8',8},{'9',9},{'a',10},{'b',11},{'c',12},{'d',13},{'e',14},{'f',15},{'A',10},{'B',11},{'C',12},{'D',13},{'E',14},{'F',15}};std::vector<uint32_t>hex_to_bytes(conststd::string&text){std::vector<uint32_t>bytes(text.size()/2,0);for(uint64_ti=0;i<text.size();i+=2){constuint32_tfirst_digit=base_map[text[i]];constuint32_tsecond_digit=base_map[text[i+1]];bytes[i/2]=(first_digit<<4)+second_digit;}returnbytes;}std::stringvector_to_ascii_string(conststd::vector<uint32_t>&bytes){std::stringresult="";for(uint32_ti=0;i<bytes.size();++i){result+=static_cast<char>(bytes[i]);}returnresult;}std::vector<uint32_t>decode_base_58(conststd::string&text){std::vector<uint32_t>result(25,0);for(constchar&ch:text){std::string::size_typeindex=ALPHABET.find(ch);if(index==static_cast<uint64_t>(-1)){throwstd::invalid_argument("Invalid character found in bitcoin address");}for(uint64_ti=result.size()-1;i>0;i--){index+=58*result[i];result[i]=index&0xFF;index>>=8;}if(index!=0){throwstd::invalid_argument("Bitcoin address is too long");}}returnresult;}boolis_valid(conststd::string&address){if(address.size()<26||address.size()>35){throwstd::invalid_argument("Invalid length of bitcoin address");}std::vector<uint32_t>decoded=decode_base_58(address);std::vectorfirst21(decoded.begin(),decoded.begin()+21);// Convert the 'first21' into a suitable ASCII string for the first SHA256 hashstd::stringtext=vector_to_ascii_string(first21);std::stringhash_1=sha256.message_digest(text);// Convert 'hashOne' into a suitable ASCII string for the second SHA256 hashstd::vector<uint32_t>bytes_1=hex_to_bytes(hash_1);std::stringascii_1=vector_to_ascii_string(bytes_1);std::stringhash_2=sha256.message_digest(ascii_1);std::vector<uint32_t>bytes_2=hex_to_bytes(hash_2);std::vector<uint32_t>checksum(bytes_2.begin(),bytes_2.begin()+4);std::vector<uint32_t>last4(decoded.begin()+21,decoded.begin()+25);returnchecksum==last4;}intmain(){conststd::vector<std::string>addresses={"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i","1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62j","1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nK9","1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62X","1ANNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i"};for(conststd::string&address:addresses){std::cout<<address<<" : "<<std::boolalpha<<is_valid(address)<<std::endl;}}
1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i : true1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62j : false1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nK9 : true1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62X : false1ANNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i : false
This requires the D module from the SHA-256 Task.
importstd.stdio,std.algorithm,std.array,std.string,sha_256_2;structA25{// Type for a 25 ubyte (not base58 encoded) bitcoin address.ubyte[25]enc;ubytebitcoinVersion()constpurenothrow@safe@nogc{returnenc[0];}ubyte[4]embeddedChecksum()returnconstpurenothrow@safe@nogc{returnenc[$-4..$];}/** Computes a double sha256 hash of the first 21 bytes of the address. Returns the full 32 ubyte sha256 hash. */ubyte[32]doubleSHA256()constpurenothrow@nogc{returnSHA256.digest(SHA256.digest(enc[0..21]));}/** Returns a four ubyte checksum computed from the first 21 bytes of the address. */ubyte[4]computeChecksum()constpurenothrow@nogc{returndoubleSHA256[0..4];}/** Takes a base58 encoded address and decodes it into the receiver. Errors are returned if the argument is not valid base58 or if the decoded value does not fit in the 25 ubyte address. The address is not otherwise checked for validity. */stringset58(inubyte[]s)purenothrow@safe@nogc{staticimmutabledigits="123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz".representation;staticassert(digits.length==58);foreach(immutablechars1;s){immutablec=digits.countUntil(s1);if(c<0)return"found a bad char in the Bitcoin address.";// Currently the D type system can't see c as nonegative.uintuc=(c<0)?0:c;foreach_reverse(refaj;enc){uc+=digits.length*aj;aj=uc%256;uc/=256;}if(uc>0)return"too long Bitcoin address.";}returnnull;}}/** Validates a base58 encoded bitcoin address. An address is validif it can be decoded into a 25 ubyte address, the Version number is 0,and the checksum validates. Return value ok will be true for validaddresses. If ok is false, the address is invalid and the error valuemay indicate why. */stringisValidA58(inubyte[]a58)purenothrow@nogc{A25a;immutableerr=a.set58(a58);if(!err.empty)returnerr;if(a.bitcoinVersion!=0)return"not Bitcoin version 0.";return(a.embeddedChecksum==a.computeChecksum)?null:"checksums don't match.";}voidmain(){immutabletests=["1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i","1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62j","1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62!","1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62iz","1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62izz"];foreach(immutabletest;tests){immutableerr=test.representation.isValidA58;writefln(`"%s": %s`,test,err.empty?"OK.":err);}}
"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i": OK."1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62j": checksums don't match."1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62!": found a bad char in the Bitcoin address."1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62iz": not Bitcoin version 0."1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62izz": too long Bitcoin address.
This requiresCrypto package to compile.
import'package:crypto/crypto.dart';classBitcoin{finalStringALPHABET="123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";List<int>bigIntToByteArray(BigIntdata){Stringstr;boolneg=false;if(data<BigInt.zero){str=(~data).toRadixString(16);neg=true;}elsestr=data.toRadixString(16);intp=0;intlen=str.length;intblen=(len+1)~/2;intboff=0;Listbytes;if(neg){if(len&1==1){p=-1;}intbyte0=~int.parse(str.substring(0,p+2),radix:16);if(byte0<-128)byte0+=256;if(byte0>=0){boff=1;bytes=newList<int>(blen+1);bytes[0]=-1;bytes[1]=byte0;}else{bytes=newList<int>(blen);bytes[0]=byte0;}for(inti=1;i<blen;++i){intbyte=~int.parse(str.substring(p+(i<<1),p+(i<<1)+2),radix:16);if(byte<-128)byte+=256;bytes[i+boff]=byte;}}else{if(len&1==1){p=-1;}intbyte0=int.parse(str.substring(0,p+2),radix:16);if(byte0>127)byte0-=256;if(byte0<0){boff=1;bytes=newList<int>(blen+1);bytes[0]=0;bytes[1]=byte0;}else{bytes=newList<int>(blen);bytes[0]=byte0;}for(inti=1;i<blen;++i){intbyte=int.parse(str.substring(p+(i<<1),p+(i<<1)+2),radix:16);if(byte>127)byte-=256;bytes[i+boff]=byte;}}returnbytes;}List<int>arrayCopy(bytes,srcOffset,result,destOffset,bytesLength){for(inti=srcOffset;i<bytesLength;i++){result[destOffset+i]=bytes[i];}returnresult;}List<int>decodeBase58To25Bytes(Stringinput){BigIntnumber=BigInt.zero;for(Stringtininput.split('')){intp=ALPHABET.indexOf(t);if(p==(-1))returnnull;number=number*(BigInt.from(58))+(BigInt.from(p));}List<int>result=newList<int>(24);List<int>numBytes=bigIntToByteArray(number);returnarrayCopy(numBytes,0,result,result.length-numBytes.length,numBytes.length);}validateAddress(Stringaddress){List<int>decoded=newList.from(decodeBase58To25Bytes(address));List<int>temp=newList<int>.from(decoded);temp.insert(0,0);List<int>hash1=sha256.convert(temp.sublist(0,21)).bytes;List<int>hash2=sha256.convert(hash1).bytes;if(hash2[0]!=decoded[20]||hash2[1]!=decoded[21]||hash2[2]!=decoded[22]||hash2[3]!=decoded[23])returnfalse;returntrue;}}
"1BNGaR29FmfAqidXmD9HLwsGv9p5WVvvhq" true"1BNGaR29FmfAqidXmD9HLws" false
This requiresDCPcrypt library to compile.
usesDCPsha256;typeTByteArray=arrayofByte;functionHashSHA256(constInput:TByteArray):TByteArray;varHasher:TDCP_sha256;beginHasher:=TDCP_sha256.Create(nil);tryHasher.Init;Hasher.Update(Input[0],Length(Input));SetLength(Result,Hasher.HashSizediv8);Hasher.Final(Result[0]);finallyHasher.Free;end;end;functionDecodeBase58(constInput:string):TByteArray;constSize=25;Alphabet='123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';varC:Char;I,J:Integer;beginSetLength(Result,Size);forCinInputdobeginI:=Pos(C,Alphabet)-1;ifI=-1thenraiseException.CreateFmt('Invalid character found: %s',[C]);forJ:=High(Result)downto0dobeginI:=I+(58*Result[J]);Result[J]:=Imod256;I:=Idiv256;end;ifI<>0thenraiseException.Create('Address too long');end;end;procedureValidateBitcoinAddress(constAddress:string);varHashed:TByteArray;Decoded:TByteArray;beginif(Length(Address)<26)or(Length(Address)>35)thenraiseException.Create('Wrong length');Decoded:=DecodeBase58(Address);Hashed:=HashSHA256(HashSHA256(Copy(Decoded,0,21)));ifnotCompareMem(@Decoded[21],@Hashed[0],4)thenraiseException.Create('Bad digest');end;
Using base58 module fromhttp://github.com/titan098/erl-base58.git.
-module(bitcoin_address).-export([task/0,validate/1]).task()->io:fwrite("Validate~p~n",["1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i"]),io:fwrite("~p~n",[validate("1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i")]),io:fwrite("Validate~p~n",["1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW622"]),io:fwrite("~p~n",[validate("1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW622")]).validate(String)->{length_25,<<Address:21/binary,Checksum:4/binary>>}={length_25,base58:base58_to_binary(String)},<<Version:1/binary,_/binary>>=Address,{version_0,<<0>>}={version_0,Version},<<Four_bytes:4/binary,_T/binary>>=crypto:hash(sha256,crypto:hash(sha256,Address)),{checksum,Checksum}={checksum,Four_bytes},ok.
17> bitcoin_address:task().Validate "1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i"okValidate "1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW622"** exception error: no match of right hand side value {checksum,<<"ÀF²ÿ">>} in function bitcoin_address:validate/1 (src/bitcoin_address.erl, line 16) in call from bitcoin_address:task/0 (src/bitcoin_address.erl, line 9)USING:byte-arrayschecksumschecksums.shaio.binarykernelmathmath.parsersequences;IN:rosetta-code.bitcoin.validationCONSTANT:ALPHABET"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz":base58>bigint(str--n)[ALPHABETindex][[58*][+]bi*]map-reduce;:base58>(str--bytes)base58>bigint25>be;:btc-checksum(bytes--checksum-bytes)21head2[sha-256checksum-bytes]times4head;:btc-valid?(str--?)base58>[btc-checksum][4tail*]bi=;
"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i" btc-valid? . ! t, VALID"1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nK9" btc-valid? . ! t, VALID"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62X" btc-valid? . ! f, checksum changed, original data."1ANNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i" btc-valid? . ! f, data changed, original checksum.
' version 05-04-2017' compile with: fbc -s console' function adapted from the SHA-256 taskFunctionSHA_256(test_strAsString,bitcoinAsULong=0)AsString#MacroCh(x,y,z)(((x)And(y))Xor((Not(x))Andz))#EndMacro#MacroMaj(x,y,z)(((x)And(y))Xor((x)And(z))Xor((y)And(z)))#EndMacro#Macrosigma0(x)(((x)Shr2Or(x)Shl30)Xor((x)Shr13Or(x)Shl19)Xor((x)Shr22Or(x)Shl10))#EndMacro#Macrosigma1(x)(((x)Shr6Or(x)Shl26)Xor((x)Shr11Or(x)Shl21)Xor((x)Shr25Or(x)Shl7))#EndMacro#Macrosigma2(x)(((x)Shr7Or(x)Shl25)Xor((x)Shr18Or(x)Shl14)Xor((x)Shr3))#EndMacro#Macrosigma3(x)(((x)Shr17Or(x)Shl15)Xor((x)Shr19Or(x)Shl13)Xor((x)Shr10))#EndMacroDimAsStringmessage=test_str' strings are passed as ByRef'sDimAsLongi,jDimAsUBytePtrww1DimAsUInteger<32>Ptrww4DoDimAsULongIntl=Len(message)' set the first bit after the message to 1message=message+Chr(1Shl7)' add one char to the lengthDimAsULongpadding=64-((l+1)Mod(512\8))' 512 \ 8 = 64 char.' check if we have enough room for inserting the lengthIfpadding<8Thenpadding=padding+64message=message+String(padding,Chr(0))' adjust lengthDimAsULongl1=Len(message)' new lengthl=l*8' orignal length in bits' create ubyte ptr to point to l ( = length in bits)DimAsUBytePtrub_ptr=Cast(UBytePtr,@l)Fori=0To7'copy length of message to the last 8 bytesmessage[l1-1-i]=ub_ptr[i]Next'table of constantsDimAsUInteger<32>K(0To...)=_{&H428a2f98,&H71374491,&Hb5c0fbcf,&He9b5dba5,&H3956c25b,&H59f111f1,_&H923f82a4,&Hab1c5ed5,&Hd807aa98,&H12835b01,&H243185be,&H550c7dc3,_&H72be5d74,&H80deb1fe,&H9bdc06a7,&Hc19bf174,&He49b69c1,&Hefbe4786,_&H0fc19dc6,&H240ca1cc,&H2de92c6f,&H4a7484aa,&H5cb0a9dc,&H76f988da,_&H983e5152,&Ha831c66d,&Hb00327c8,&Hbf597fc7,&Hc6e00bf3,&Hd5a79147,_&H06ca6351,&H14292967,&H27b70a85,&H2e1b2138,&H4d2c6dfc,&H53380d13,_&H650a7354,&H766a0abb,&H81c2c92e,&H92722c85,&Ha2bfe8a1,&Ha81a664b,_&Hc24b8b70,&Hc76c51a3,&Hd192e819,&Hd6990624,&Hf40e3585,&H106aa070,_&H19a4c116,&H1e376c08,&H2748774c,&H34b0bcb5,&H391c0cb3,&H4ed8aa4a,_&H5b9cca4f,&H682e6ff3,&H748f82ee,&H78a5636f,&H84c87814,&H8cc70208,_&H90befffa,&Ha4506ceb,&Hbef9a3f7,&Hc67178f2}DimAsUInteger<32>h0=&H6a09e667DimAsUInteger<32>h1=&Hbb67ae85DimAsUInteger<32>h2=&H3c6ef372DimAsUInteger<32>h3=&Ha54ff53aDimAsUInteger<32>h4=&H510e527fDimAsUInteger<32>h5=&H9b05688cDimAsUInteger<32>h6=&H1f83d9abDimAsUInteger<32>h7=&H5be0cd19DimAsUInteger<32>a,b,c,d,e,f,g,hDimAsUInteger<32>t1,t2,w(0To63)Forj=0To(l1-1)\64' split into block of 64 bytesww1=Cast(UBytePtr,@message[j*64])ww4=Cast(UInteger<32>Ptr,@message[j*64])Fori=0To60Step4'little endian -> big endianSwapww1[i],ww1[i+3]Swapww1[i+1],ww1[i+2]NextFori=0To15' copy the 16 32bit block into the arrayW(i)=ww4[i]NextFori=16To63' fill the rest of the arrayw(i)=sigma3(W(i-2))+W(i-7)+sigma2(W(i-15))+W(i-16)Nexta=h0:b=h1:c=h2:d=h3:e=h4:f=h5:g=h6:h=h7Fori=0To63t1=h+sigma1(e)+Ch(e,f,g)+K(i)+W(i)t2=sigma0(a)+Maj(a,b,c)h=g:g=f:f=ee=d+t1d=c:c=b:b=aa=t1+t2Nexth0+=a:h1+=b:h2+=c:h3+=dh4+=e:h5+=f:h6+=g:h7+=hNextjDimAsStringanswer=Hex(h0,8)+Hex(h1,8)+Hex(h2,8)+Hex(h3,8)_+Hex(h4,8)+Hex(h5,8)+Hex(h6,8)+Hex(h7,8)Ifbitcoin=0ThenReturnLCase(answer)Else'conver hex value's to integer value'smessage=String(32,0)Fori=0To31message[i]=Val("&h"+Mid(answer,i*2+1,2))Nextbitcoin=0EndIfLoopEndFunctionFunctionconv_base58(bitcoin_addressAsString)AsStringDimAsStringbase58="123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"DimAsStringtmp=String(24,0)DimAsLongx,y,zForx=1ToLen(bitcoin_address)-1z=InStr(base58,Chr(bitcoin_address[x]))-1Ifz=-1ThenPrint" bitcoin address contains illegal character"Return""EndIfFory=23To0Step-1z=z+tmp[y]*58tmp[y]=zAnd255' test_str[y] = z Mod 256zShr=8' z \= 256NextIfz<>0ThenPrint" bitcoin address is to long"Return""EndIfNextz=InStr(base58,Chr(bitcoin_address[0]))-1ReturnChr(z)+tmpEndFunction' ------=< MAIN >=------Data"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i"' originalData"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62j"' checksum changedData"1NAGa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i"' address changedData"0AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i"' only 1 or 3 as first char. allowedData"1AGNa15ZQXAZUgFlqJ2i7Z2DPU2J6hW62i"' illegal character in addressDimAsStringtmp,result,checksum,bitcoin_addressDimAsLongi,jFori=1To5Readbitcoin_addressPrint"Bitcoin address: ";bitcoin_address;tmp=Left(bitcoin_address,1)Iftmp<>"1"Andtmp<>"3"ThenPrint" first character is not 1 or 3"ContinueForEndIf' convert bitcoinaddresstmp=conv_base58(bitcoin_address)Iftmp=""ThenContinueFor' get the checksum, last 4 digitsForjAsLong=21To24checksum=checksum+LCase(Hex(tmp[j],2))Next' send the first 21 characters to the SHA 256 routineresult=SHA_256(Left(tmp,21),2)result=Left(result,8)' get the checksum (the first 8 digits (hex))Ifchecksum=resultThen' test the found checksum againstPrint" is valid"' the one from the addressElsePrint" is not valid, checksum fails"EndIfNext' empty keyboard bufferWhileInKey<>"":WendPrint:Print"hit any key to end program"SleepEnd
Bitcoin address: 1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i is validBitcoin address: 1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62j is not valid, checksum failsBitcoin address: 1NAGa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i is not valid, checksum failsBitcoin address: 0AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i first character is not 1 or 3Bitcoin address: 1AGNa15ZQXAZUgFlqJ2i7Z2DPU2J6hW62i bitcoin address contains illegal character
packagemainimport("bytes""crypto/sha256""errors""os")// With at least one other bitcoin RC task, this source is styled more like// a package to show how functions of the two tasks might be combined into// a single package. It turns out there's not really that much shared code,// just the A25 type and doubleSHA256 method, but it's enough to suggest how// the code might be organized. Types, methods, and functions are capitalized// where they might be exported from a package.// A25 is a type for a 25 byte (not base58 encoded) bitcoin address.typeA25[25]bytefunc(a*A25)Version()byte{returna[0]}func(a*A25)EmbeddedChecksum()(c[4]byte){copy(c[:],a[21:])return}// DoubleSHA256 computes a double sha256 hash of the first 21 bytes of the// address. This is the one function shared with the other bitcoin RC task.// Returned is the full 32 byte sha256 hash. (The bitcoin checksum will be// the first four bytes of the slice.)func(a*A25)doubleSHA256()[]byte{h:=sha256.New()h.Write(a[:21])d:=h.Sum([]byte{})h=sha256.New()h.Write(d)returnh.Sum(d[:0])}// ComputeChecksum returns a four byte checksum computed from the first 21// bytes of the address. The embedded checksum is not updated.func(a*A25)ComputeChecksum()(c[4]byte){copy(c[:],a.doubleSHA256())return}/* {{header|Go}} */// Tmpl and Set58 are adapted from the C solution.// Go has big integers but this techinique seems better.vartmpl=[]byte("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz")// Set58 takes a base58 encoded address and decodes it into the receiver.// Errors are returned if the argument is not valid base58 or if the decoded// value does not fit in the 25 byte address. The address is not otherwise// checked for validity.func(a*A25)Set58(s[]byte)error{for_,s1:=ranges{c:=bytes.IndexByte(tmpl,s1)ifc<0{returnerrors.New("bad char")}forj:=24;j>=0;j--{c+=58*int(a[j])a[j]=byte(c%256)c/=256}ifc>0{returnerrors.New("too long")}}returnnil}// ValidA58 validates a base58 encoded bitcoin address. An address is valid// if it can be decoded into a 25 byte address, the version number is 0,// and the checksum validates. Return value ok will be true for valid// addresses. If ok is false, the address is invalid and the error value// may indicate why.funcValidA58(a58[]byte)(okbool,errerror){varaA25iferr:=a.Set58(a58);err!=nil{returnfalse,err}ifa.Version()!=0{returnfalse,errors.New("not version 0")}returna.EmbeddedChecksum()==a.ComputeChecksum(),nil}// Program returns exit code 0 with valid address and produces no output.// Otherwise exit code is 1 and a message is written to stderr.funcmain(){iflen(os.Args)!=2{errorExit("Usage: valid <base58 address>")}switchok,err:=ValidA58([]byte(os.Args[1]));{caseok:caseerr==nil:errorExit("Invalid")default:errorExit(err.Error())}}funcerrorExit(mstring){os.Stderr.WriteString(m+"\n")os.Exit(1)}
Command line usage examples showing program exit status.
> valid ; echo $statusUsage: valid <base58 address>1> valid 1 1 ; echo $statusUsage: valid <base58 address>1> valid 1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i ; echo $status0> valid 1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62j ; echo $statusInvalid1> valid 1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62! ; echo $statusbad char1> valid 1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62iz ; echo $statusnot version 01> valid 1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62izz ; echo $statustoo long1
importControl.Monad(when)importData.List(elemIndex)importData.Monoid((<>))importqualifiedData.ByteStringasBSimportData.ByteString(ByteString)importCrypto.Hash.SHA256(hash)-- from package cryptohash-- Convert from base58 encoded value to Integerdecode58::String->MaybeIntegerdecode58=fmapcombine.traverseparseDigitwherecombine=foldl(\accdigit->58*acc+digit)0-- should be foldl', but this trips up the highlightingparseDigitchar=toInteger<$>elemIndexcharc58c58="123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"-- Convert from base58 encoded value to bytestoBytes::Integer->ByteStringtoBytes=BS.reverse.BS.pack.map(fromIntegral.(`mod`256)).takeWhile(>0).iterate(`div`256)-- Check if the hash of the first 21 (padded) bytes matches the last 4 byteschecksumValid::ByteString->BoolchecksumValidaddress=let(value,checksum)=BS.splitAt21$leftPadaddressinand$BS.zipWith(==)checksum$hash$hash$valuewhereleftPadbs=BS.replicate(25-BS.lengthbs)0<>bs-- utilitywithError::e->Maybea->EithereawithErrore=maybe(Lefte)Right-- Check validity of base58 encoded bitcoin address.-- Result is either an error string (Left) or unit (Right ()).validityCheck::String->EitherString()validityCheckencoded=donum<-withError"Invalid base 58 encoding"$decode58encodedletaddress=toBytesnumwhen(BS.lengthaddress>25)$Left"Address length exceeds 25 bytes"when(BS.lengthaddress<4)$Left"Address length less than 4 bytes"when(not$checksumValidaddress)$Left"Invalid checksum"-- Run one validity check and display results.validate::String->IO()validateencodedAddress=doletresult=eithershow(const"Valid")$validityCheckencodedAddressputStrLn$showencodedAddress++" -> "++result-- Run some validity check tests.main::IO()main=dovalidate"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i"-- VALIDvalidate"1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nK9"-- VALIDvalidate"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62X"-- checksum changed, original data.validate"1ANNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i"-- data changed, original checksum.validate"1A Na15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i"-- invalid charsvalidate"1ANa55215ZQXAZUgFiqJ2i7Z2DPU2J6hW62i"-- too longvalidate"i55j"-- too short
"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i" -> Valid"1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nK9" -> Valid"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62X" -> Invalid"1ANNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i" -> Invalid"1A Na15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i" -> "Invalid base 58 encoding""1ANa55215ZQXAZUgFiqJ2i7Z2DPU2J6hW62i" -> "Address length exceeds 25 bytes""i55j" -> "Address length less than 4 bytes"
importjava.math.BigInteger;importjava.security.MessageDigest;importjava.security.NoSuchAlgorithmException;importjava.util.Arrays;publicclassBitcoinAddressValidator{privatestaticfinalStringALPHABET="123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";publicstaticbooleanvalidateBitcoinAddress(Stringaddr){if(addr.length()<26||addr.length()>35)returnfalse;byte[]decoded=decodeBase58To25Bytes(addr);if(decoded==null)returnfalse;byte[]hash1=sha256(Arrays.copyOfRange(decoded,0,21));byte[]hash2=sha256(hash1);returnArrays.equals(Arrays.copyOfRange(hash2,0,4),Arrays.copyOfRange(decoded,21,25));}privatestaticbyte[]decodeBase58To25Bytes(Stringinput){BigIntegernum=BigInteger.ZERO;for(chart:input.toCharArray()){intp=ALPHABET.indexOf(t);if(p==-1)returnnull;num=num.multiply(BigInteger.valueOf(58)).add(BigInteger.valueOf(p));}byte[]result=newbyte[25];byte[]numBytes=num.toByteArray();System.arraycopy(numBytes,0,result,result.length-numBytes.length,numBytes.length);returnresult;}privatestaticbyte[]sha256(byte[]data){try{MessageDigestmd=MessageDigest.getInstance("SHA-256");md.update(data);returnmd.digest();}catch(NoSuchAlgorithmExceptione){thrownewIllegalStateException(e);}}publicstaticvoidmain(String[]args){assertBitcoin("1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i",true);assertBitcoin("1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62j",false);assertBitcoin("1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nK9",true);assertBitcoin("1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62X",false);assertBitcoin("1ANNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i",false);assertBitcoin("1A Na15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i",false);assertBitcoin("BZbvjr",false);assertBitcoin("i55j",false);assertBitcoin("1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62!",false);assertBitcoin("1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62iz",false);assertBitcoin("1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62izz",false);assertBitcoin("1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nJ9",false);assertBitcoin("1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62I",false);}privatestaticvoidassertBitcoin(Stringaddress,booleanexpected){booleanactual=validateBitcoinAddress(address);if(actual!=expected)thrownewAssertionError(String.format("Expected %s for %s, but got %s.",expected,address,actual));}}
This example uses the Java code from the SHA-256 task as an alternative to using Java's built-in SHA256 method. Italso decodes Base58 without using Java's built-in BigInteger class.
importjava.nio.charset.StandardCharsets;importjava.util.Arrays;importjava.util.List;publicfinalclassBitcoinAddressValidation{publicstaticvoidmain(String[]args){List<String>addresses=List.of("1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i","1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62j","1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nK9","1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62X","1ANNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i");for(Stringaddress:addresses){System.out.println(address+" : "+isValid(address));}}privatestaticbooleanisValid(Stringaddress){if(address.length()<26||address.length()>35){thrownewAssertionError("Invalid length of bitcoin address");}byte[]decoded=decodeBase58(address);byte[]first21=Arrays.copyOfRange(decoded,0,21);// Convert 'first21' into an ASCII string for the first SHA256 hashStringtext=newString(first21,StandardCharsets.ISO_8859_1);StringhashOne=SHA256.messageDigest(text);// Convert 'hashOne' into an ASCII string for the second SHA256 hashbyte[]bytesOne=hexToBytes(hashOne);StringasciiOne=newString(bytesOne,StandardCharsets.ISO_8859_1);StringhashTwo=SHA256.messageDigest(asciiOne);byte[]bytesTwo=hexToBytes(hashTwo);byte[]checksum=Arrays.copyOfRange(bytesTwo,0,4);byte[]last4=Arrays.copyOfRange(decoded,21,25);returnArrays.equals(last4,checksum);}privatestaticbyte[]decodeBase58(Stringtext){byte[]result=newbyte[25];for(charch:text.toCharArray()){intindex=ALPHABET.indexOf(ch);if(index==-1){thrownewAssertionError("Invalid character found in bitcoin address: "+ch);}for(inti=result.length-1;i>0;i--){index+=58*(int)(result[i]&0xFF);result[i]=(byte)(index&0xFF);index>>=8;}if(index!=0){thrownewAssertionError("Bitcoin address is too long");}}returnresult;}privatestaticbyte[]hexToBytes(Stringtext){byte[]bytes=newbyte[text.length()/2];for(inti=0;i<text.length();i+=2){finalintfirstDigit=Character.digit(text.charAt(i),16);finalintsecondDigit=Character.digit(text.charAt(i+1),16);bytes[i/2]=(byte)((firstDigit<<4)+secondDigit);}returnbytes;}privatestaticfinalStringALPHABET="123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";}
1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i : true1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62j : false1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nK9 : true1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62X : false1ANNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i : false
constdigits58='123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';asyncfunctionhash(bytes){returnnewUint8Array(awaitcrypto.subtle.digest('SHA-256',bytes));}functiontoBytes(n,length){constbytes=[];for(leti=BigInt(length)-1n;i>=0;i--){bytes.push((n>>i*8n)&0xffn);}returnbytes;}functiondecode_base58(bc,length){letn=0n;for(constcharofbc){n=n*58n+BigInt(digits58.indexOf(char));}returntoBytes(n,length);}functiontoUint8Array(bytes){letnums=[]for(constbyteofbytes){nums.push(Number(byte));}returnnewUint8Array(nums);}asyncfunctioncheckBc(bc){constbcbytes=decode_base58(bc,25);constslice=bcbytes.slice(0,bcbytes.length-4);constfirst=toUint8Array(slice);constfirstHash=awaithash(first);constsecondHash=awaithash(firstHash);constchecksum=toUint8Array(bcbytes.slice(-4));returnJSON.stringify(checksum)==JSON.stringify(toUint8Array(secondHash.slice(0,4)));}(async()=>{console.log(awaitcheckBc('1AGNa15ZQXAZUgFiqJ3i7Z2DPU2J6hW62i'));console.log(awaitcheckBc("17NdbrSGoUotzeGCcMMCqnFkEvLymoou9j"))})();
falsetrue
usingSHAbytes(n::Integer,l::Int)=collect(UInt8,(n>>8i)&0xFFforiinl-1:-1:0)functiondecodebase58(bc::String,l::Int)digits="123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"num=big(0)forcinbcnum=num*58+search(digits,c)-1endreturnbytes(num,l)endfunctioncheckbcaddress(addr::String)if!(25≤length(addr)≤35)returnfalseendbcbytes=decodebase58(addr,25)sha=sha256(sha256(bcbytes[1:end-4]))returnbcbytes[end-3:end]==sha[1:4]endconstaddresses=Dict("1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i"=>true,"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62j"=>false,"1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nK9"=>true,"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62X"=>true,"1ANNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i"=>false,"1A Na15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i"=>false,"BZbvjr"=>false,"i55j"=>false,"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62!"=>false,"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62iz"=>false,"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62izz"=>false,"1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nJ9"=>false,"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62I"=>false)for(addr,corr)inaddressesprintln("Address:$addr\nExpected:$corr\tChecked: ",checkbcaddress(addr))end
Address: 1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62XExpected: trueChecked: falseAddress: 1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62iExpected: trueChecked: trueAddress: 1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62jExpected: falseChecked: falseAddress: 1ANNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62iExpected: falseChecked: falseAddress: 1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nK9Expected: trueChecked: trueAddress: BZbvjrExpected: falseChecked: falseAddress: 1A Na15ZQXAZUgFiqJ2i7Z2DPU2J6hW62iExpected: falseChecked: falseAddress: i55jExpected: falseChecked: falseAddress: 1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62izzExpected: falseChecked: falseAddress: 1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nJ9Expected: falseChecked: falseAddress: 1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62!Expected: falseChecked: falseAddress: 1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62IExpected: falseChecked: falseAddress: 1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62izExpected: falseChecked: false
importjava.security.MessageDigestobjectBitcoin{privateconstvalALPHABET="123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"privatefunByteArray.contentEquals(other:ByteArray):Boolean{if(this.size!=other.size)returnfalsereturn(0untilthis.size).none{this[it]!=other[it]}}privatefundecodeBase58(input:String):ByteArray?{valoutput=ByteArray(25)for(cininput){varp=ALPHABET.indexOf(c)if(p==-1)returnnullfor(jin24downTo1){p+=58*(output[j].toInt()and0xff)output[j]=(p%256).toByte()p=pshr8}if(p!=0)returnnull}returnoutput}privatefunsha256(data:ByteArray,start:Int,len:Int,recursion:Int):ByteArray{if(recursion==0)returndatavalmd=MessageDigest.getInstance("SHA-256")md.update(data.sliceArray(startuntilstart+len))returnsha256(md.digest(),0,32,recursion-1)}funvalidateAddress(address:String):Boolean{if(address.length!in26..35)returnfalsevaldecoded=decodeBase58(address)if(decoded==null)returnfalsevalhash=sha256(decoded,0,21,2)returnhash.sliceArray(0..3).contentEquals(decoded.sliceArray(21..24))}}funmain(args:Array<String>){valaddresses=arrayOf("1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i","1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62j","1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nK9","1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62X","1ANNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i","1A Na15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i","BZbvjr","i55j","1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62!","1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62iz","1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62izz","1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nJ9","1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62I")for(addressinaddresses)println("${address.padEnd(36)} -> ${if (Bitcoin.validateAddress(address)) "valid" else "invalid"}")}
1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i -> valid1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62j -> invalid1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nK9 -> valid1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62X -> invalid1ANNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i -> invalid1A Na15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i -> invalidBZbvjr -> invalidi55j -> invalid1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62! -> invalid1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62iz -> invalid1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62izz -> invalid1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nJ9 -> invalid1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62I -> invalid
chars="123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";data=IntegerDigits[FromDigits[StringPosition[chars,#][[1]]-1&/@Characters[InputString[]],58],256,25];data[[-4;;]]==IntegerDigits[Hash[FromCharacterCode[IntegerDigits[Hash[FromCharacterCode[data[[;;-5]]],"SHA256"],256,32]],"SHA256"],256,32][[;;4]]
1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i2AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i
TrueFalse
Tests on first digit character and on address length have been added to detect wrong addresses such as "BZbvjr".
importalgorithmconstSHA256Len=32constAddrLen=25constAddrMsgLen=21constAddrChecksumOffset=21constAddrChecksumLen=4procSHA256(d:pointer,n:culong,md:pointer=nil):cstring{.cdecl,dynlib:"libssl.so", importc.}procdecodeBase58(inStr:string,outArray:varopenarray[uint8])=letbase="123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"outArray.fill(0)foraCharininStr:varaccum=base.find(aChar)ifaccum<0:raisenewException(ValueError,"Invalid character: "&$aChar)foroutIndexincountDown((AddrLen-1),0):accum+=58*outArray[outIndex].intoutArray[outIndex]=(accummod256).uint8accum=accumdiv256ifaccum!=0:raisenewException(ValueError,"Address string too long")procverifyChecksum(addrData:openarray[uint8]):bool=letdoubleHash=SHA256(SHA256(cast[ptruint8](addrData),AddrMsgLen),SHA256Len)foriiin0..<AddrChecksumLen:ifdoubleHash[ii].uint8!=addrData[AddrChecksumOffset+ii]:returnfalsereturntrueprocmain()=lettestVectors:seq[string]=@["3yQ","1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nK9","1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i","1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nJ9","1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62I","1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62ix","1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62ixx","17NdbrSGoUotzeGCcMMCqnFkEvLymoou9j","1badbadbadbadbadbadbadbadbadbadbad","16UwLL9Risc3QfPqBUvKofHmBQ7wMtjvM","1111111111111111111114oLvT2","BZbvjr",]varbuf:array[AddrLen,uint8]astr:stringforvectorintestVectors:stdout.write(vector&" : ")try:ifvector[0]notin{'1','3'}:raisenewException(ValueError,"invalid starting character")ifvector.len<26:raisenewException(ValueError,"address too short")decodeBase58(vector,buf)ifbuf[0]!=0:stdout.write("NG - invalid version number\n")elifverifyChecksum(buf):stdout.write("OK\n")else:stdout.write("NG - checksum invalid\n")except:stdout.write("NG - "&getCurrentExceptionMsg()&"\n")main()
3yQ : NG - address too short1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nK9 : OK1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i : OK1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nJ9 : NG - checksum invalid1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62I : NG - Invalid character: I1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62ix : NG - invalid version number1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62ixx : NG - Address string too long17NdbrSGoUotzeGCcMMCqnFkEvLymoou9j : OK1badbadbadbadbadbadbadbadbadbadbad : NG - invalid version number16UwLL9Risc3QfPqBUvKofHmBQ7wMtjvM : OK1111111111111111111114oLvT2 : OKBZbvjr : NG - invalid starting character
importnimcryptoimportstrformatconstDecodedLength=25# Decoded address length.CheckSumLength=4# Checksum length in address.# Representation of a decoded address.typeBytes25=array[DecodedLength,byte]#---------------------------------------------------------------------------------------------------procbase58Decode(input:string):Bytes25=## Decode a base58 encoded bitcoin address.constBase="123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"forchininput:varn=Base.find(ch)ifn<0:raisenewException(ValueError,"invalid character: "&ch)foriincountdown(result.high,0):n+=58*result[i].intresult[i]=byte(nand255)n=ndiv256ifn!=0:raisenewException(ValueError,"decoded address is too long")#---------------------------------------------------------------------------------------------------procverifyChecksum(data:Bytes25)=## Verify that data has the expected checksum.vardigest=sha256.digest(data.toOpenArray(0,data.high-CheckSumLength))digest=sha256.digest(digest.data)ifdigest.data[0..<CheckSumLength]!=data[^CheckSumLength..^1]:raisenewException(ValueError,"wrong checksum")#---------------------------------------------------------------------------------------------------proccheckValidity(address:string)=## Check the validity of a bitcoin address.try:ifaddress[0]notin{'1','3'}:raisenewException(ValueError,"starting character is not 1 or 3")ifaddress.len<26:raisenewException(ValueError,"address too short")address.base58Decode().verifyChecksum()echofmt"Address “{address}” is valid."exceptValueError:echofmt"Address “{address}” is invalid ({getCurrentExceptionMsg()})."#———————————————————————————————————————————————————————————————————————————————————————————————————consttestVectors:seq[string]=@["3yQ",# Invalid."1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nK9",# Valid."1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i",# Valid."1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nJ9",# Invalid."1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62I",# Invalid."1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62ix",# Invalid."1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62ixx",# Invalid."17NdbrSGoUotzeGCcMMCqnFkEvLymoou9j",# Valid."1badbadbadbadbadbadbadbadbadbadbad",# Invalid."16UwLL9Risc3QfPqBUvKofHmBQ7wMtjvM",# Valid."1111111111111111111114oLvT2",# Valid."BZbvjr"]# Invalid.forvectorintestVectors:vector.checkValidity()
Address “3yQ” is invalid (address too short).Address “1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nK9” is valid.Address “1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i” is valid.Address “1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nJ9” is invalid (wrong checksum).Address “1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62I” is invalid (invalid character: I).Address “1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62ix” is invalid (wrong checksum).Address “1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62ixx” is invalid (decoded address is too long).Address “17NdbrSGoUotzeGCcMMCqnFkEvLymoou9j” is valid.Address “1badbadbadbadbadbadbadbadbadbadbad” is invalid (wrong checksum).Address “16UwLL9Risc3QfPqBUvKofHmBQ7wMtjvM” is valid.Address “1111111111111111111114oLvT2” is valid.Address “BZbvjr” is invalid (starting character is not 1 or 3).
MODULEBitcoinAddress;IMPORTObject,NPCT:Tools,Crypto:SHA256,S:=SYSTEM,Out;CONSTBASE58="123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";TYPEBC_RAW=ARRAY25OFCHAR;SHA256_HASH=ARRAY32OFCHAR;VARb58:Object.CharsLatin1;PROCEDUREIndexOfB58Char(c:CHAR):INTEGER;VARi:INTEGER;BEGINi:=0;WHILE(b58[i]#0X)&(b58[i]#c)DOINC(i)END;IFb58[i]=0XTHENRETURN-1ELSERETURNiENDENDIndexOfB58Char;PROCEDUREDecodeB58(s[NO_COPY]:ARRAYOFCHAR;VARout:BC_RAW):BOOLEAN;VARi,j,k:LONGINT;BEGINFORi:=0TOLEN(out)-1DO;out[i]:=CHR(0)END;i:=0;WHILE(s[i]#0X)DO;k:=IndexOfB58Char(s[i]);IFk<0THENOut.String("Error: Bad base58 character");Out.Ln;RETURNFALSEEND;FORj:=LEN(out)-1TO0BY-1DOk:=k+58*ORD(out[j]);out[j]:=CHR(kMOD256);k:=kDIV256;END;IFk#0THENOut.String("Error: Address to long");Out.Ln;RETURNFALSEEND;INC(i)END;RETURNTRUEENDDecodeB58;PROCEDUREValid(s[NO_COPY]:ARRAYOFCHAR):BOOLEAN;VARdec:BC_RAW;d1,d2:SHA256.Hash;d1Str,d2Str:SHA256_HASH;x,y:LONGINT;BEGINOut.String(s);Out.String(" is valid? ");IF~DecodeB58(s,dec)THENRETURNFALSEEND;d1:=SHA256.NewHash();d1.Initialize();d2:=SHA256.NewHash();d2.Initialize();d1.Update(dec,0,21);d1.GetHash(d1Str,0);d2.Update(d1Str,0,d1.size);d2.GetHash(d2Str,0);S.MOVE(S.ADR(dec)+21,S.ADR(x),4);S.MOVE(S.ADR(d2Str),S.ADR(y),4);RETURN(x=y)ENDValid;BEGINb58:=Tools.AsString(BASE58);Out.Bool(Valid("1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nK9"));Out.Ln;Out.Bool(Valid("1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i"));Out.Ln;Out.Bool(Valid("1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nJ9"));Out.Ln;Out.Bool(Valid("1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62I"));Out.LnENDBitcoinAddress.
1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nK9 is valid? TRUE1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i is valid? TRUE1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nJ9 is valid? FALSE1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62I is valid? Error: Bad base58 characterFALSE
my@b58=qw{ 1 2 3 4 5 6 7 8 9 A B C D E F G H J K L M N P Q R S T U V W X Y Z a b c d e f g h i j k m n o p q r s t u v w x y z};my%b58=map{$b58[$_]=>$_}0..57;subunbase58{useinteger;my@out;my$azeroes=length($1)if$_[0]=~ /^(1*)/;formy$c(map{$b58{$_}}$_[0]=~ /./g){for(my$j=25;$j--;){$c+=58*($out[$j]//0);$out[$j]=$c%256;$c/=256;}}my$bzeroes=length($1)ifjoin('',@out)=~ /^(0*)/;die"not a 25 byte address\n"if$bzeroes!=$azeroes;return@out;}subcheck_bitcoin_address{# Does nothing if address is valid# dies otherwiseuseDigest::SHAqw(sha256);my@byte=unbase58shift;die"wrong checksum\n"unless(pack'C*',@byte[21..24])eqsubstrsha256(sha256pack'C*',@byte[0..20]),0,4;}
---- demo\rosetta\bitcoin_address_validation.exw-- ===========================================--withjavascript_semanticsincludebuiltins\sha256.econstantb58="123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"stringcharmap=""functionvalid(strings,boolexpected)boolres:=(expected==false)ifcharmap=""thencharmap=repeat('\0',256)fori=1tolength(b58)docharmap[b58[i]]=iendforendif-- not at all sure about this:-- if length(s)!=34 then-- return {expected==false,"bad length"}-- end ififnotfind(s[1],"13")thenreturn{res,"first character is not 1 or 3"}endifstringout=repeat('\0',25)fori=1tolength(s)dointegerc=charmap[s[i]]ifc=0thenreturn{res,"bad char"}endifc-=1forj=25to1by-1doc+=58*out[j];out[j]=and_bits(c,#FF)c=floor(c/#100)endforifc!=0thenreturn{res,"address too long"}endifendforifout[1]!='\0'thenreturn{res,"not version 0"}endififout[22..$]!=sha256(sha256(out[1..21]))[1..4]thenreturn{res,"bad digest"}endifres:=(expected==true)return{res,"OK"}endfunctionconstanttests={{"1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nK9",true},-- OK{"1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nJ9",false},-- bad digest{"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i",true},-- OK{"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62j",false},-- bad disgest{"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62X",false},-- bad digest (checksum changed, original data.){"1ANNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i",false},-- bad digest (data changed, original checksum.){"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62iz",false},-- not version 0{"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62izz",false},-- address too long{"1BGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i",false},-- bad digest{"1A Na15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i",false},-- bad char{"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62I",false},-- bad char{"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62!",false},-- bad char{"1AGNa15ZQXAZUgFiqJ3i7Z2DPU2J6hW62i",false},-- bad digest{"1111111111111111111114oLvT2",true},-- OK{"17NdbrSGoUotzeGCcMMCqnFkEvLymoou9j",true},-- OK{"1badbadbadbadbadbadbadbadbadbadbad",false},-- not version 0{"BZbvjr",false},-- first character is not 1 or 3 (checksum is fine, address too short){"i55j",false},-- first character is not 1 or 3 (checksum is fine, address too short){"16UwLL9Risc3QfPqBUvKofHmBQ7wMtjvM",true},-- OK (from public_point_to_address)$}fori=1tolength(tests)do{stringti,boolexpected}=tests[i]{boolres,stringcoin_err}=valid(ti,expected)ifnotresthenprintf(1,"%s: %s\n",{ti,coin_err}){}=wait_key()endifendfor?"done"{}=wait_key()
(No output other than "done" since all tests pass)
functionvalidate($address){$decoded=decodeBase58($address);$d1=hash("sha256",substr($decoded,0,21),true);$d2=hash("sha256",$d1,true);if(substr_compare($decoded,$d2,21,4)){thrownew\Exception("bad digest");}returntrue;}functiondecodeBase58($input){$alphabet="123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";$out=array_fill(0,25,0);for($i=0;$i<strlen($input);$i++){if(($p=strpos($alphabet,$input[$i]))===false){thrownew\Exception("invalid character found");}$c=$p;for($j=25;$j--;){$c+=(int)(58*$out[$j]);$out[$j]=(int)($c%256);$c/=256;$c=(int)$c;}if($c!=0){thrownew\Exception("address too long");}}$result="";foreach($outas$val){$result.=chr($val);}return$result;}functionmain(){$s=array("1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nK9","1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i","1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nJ9","1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62I",);foreach($sas$btc){$message="OK";try{validate($btc);}catch(\Exception$e){$message=$e->getMessage();}echo"$btc:$message\n";}}main();
1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nK9: OK1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i: OK1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nJ9: bad digest1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62I: invalid character found
(load "sha256.l")(setq *Alphabet (chop "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz") )(de unbase58 (Str) (let (Str (chop Str) Lst (need 25 0) C) (while (setq C (dec (index (pop 'Str) *Alphabet))) (for (L Lst L) (set L (& (inc 'C (* 58 (car L))) 255) 'C (/ C 256) ) (pop 'L) ) ) (flip Lst) ) )(de valid (Str) (and (setq @@ (unbase58 Str)) (= (head 4 (sha256 (sha256 (head 21 @@)))) (tail 4 @@) ) ) )(test T (valid "17NdbrSGoUotzeGCcMMCqnFkEvLymoou9j") )(test T (= NIL (valid "1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62j") (valid "1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62!") (valid "1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62iz") (valid "1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62izz") ) )
;usingPureBasic5.50(x64)EnableExplicitMacroIsValid(expression)IfexpressionPrintN("Valid")ElsePrintN("Invalid")EndIfEndMacroProcedure.iDecodeBase58(Address$,Arrayresult.a(1))Protectedi,j,pProtectedcharSet$="123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"Protectedc$Fori=1ToLen(Address$)c$=Mid(Address$,i,1)p=FindString(charSet$,c$)-1Ifp=-1:ProcedureReturn#False:EndIf;AddresscontainsinvalidBase58characterForj=24To1Step-1p+58*result(j)result(j)=p%256p/256NextjIfp<>0:ProcedureReturn#False:EndIf;AddressistoolongNextiProcedureReturn#TrueEndProcedureProcedureHexToBytes(hex$,Arrayresult.a(1))ProtectediFori=1ToLen(hex$)-1Step2result(i/2)=Val("$"+Mid(hex$,i,2))NextEndProcedureProcedure.iIsBitcoinAddressValid(Address$)Protectedformat$,digest$Protectedi,isValidProtectedDimresult.a(24)ProtectedDimresult2.a(31)Protectedresult$,result2$;Addresslengthmustbebetween26and35-see'https://en.bitcoin.it/wiki/Address'IfLen(Address$)<26OrLen(Address$)>35:ProcedureReturn#False:EndIf;andbeginwitheither1or3whichistheformatnumberformat$=Left(Address$,1)Ifformat$<>"1"Andformat$<>"3":ProcedureReturn#False:EndIfisValid=DecodeBase58(Address$,result())IfNotisValid:ProcedureReturn#False:EndIfUseSHA2Fingerprint();UsingfunctionsfromPB's built-in Cipher librarydigest$=Fingerprint(@result(),21,#PB_Cipher_SHA2,256);applySHA2-256tofirst21bytesHexToBytes(digest$,result2());changehexstringtoasciiarraydigest$=Fingerprint(@result2(),32,#PB_Cipher_SHA2,256);applySHA2-256againtoall32bytesHexToBytes(digest$,result2())result$=PeekS(@result()+21,4,#PB_Ascii);last4bytesresult2$=PeekS(@result2(),4,#PB_Ascii);first4bytesIfresult$<>result2$:ProcedureReturn#False:EndIfProcedureReturn#TrueEndProcedureIfOpenConsole()Defineaddress$="1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i"Defineaddress2$="1BGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i"Defineaddress3$="1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62I"Print(address$+" -> ")IsValid(IsBitcoinAddressValid(address$))Print(address2$+" -> ")IsValid(IsBitcoinAddressValid(address2$))Print(address3$+" -> ")IsValid(IsBitcoinAddressValid(address3$))PrintN("")PrintN("Press any key to close the console")Repeat:Delay(10):UntilInkey()<>""CloseConsole()EndIf
1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i -> Valid1BGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i -> Invalid1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62I -> Invalid
fromhashlibimportsha256digits58='123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'defdecode_base58(bc,length):n=0forcharinbc:n=n*58+digits58.index(char)returnn.to_bytes(length,'big')defcheck_bc(bc):try:bcbytes=decode_base58(bc,25)returnbcbytes[-4:]==sha256(sha256(bcbytes[:-4]).digest()).digest()[:4]exceptException:returnFalseprint(check_bc('1AGNa15ZQXAZUgFiqJ3i7Z2DPU2J6hW62i'))print(check_bc("17NdbrSGoUotzeGCcMMCqnFkEvLymoou9j"))
Returns:
n.to_bytes() call is equivalent to this code which converts a (long) integer into individual bytes of a byte array in a particular order:>>>n=2491969579123783355964723219455906992268673266682165637887>>>length=25>>>list(reversed(range(length)))[24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0]>>>assertn.to_bytes(length,'big')==bytes((n>>i*8)&0xffforiinreversed(range(length)))>>>
#langracket/base;; Same sha-256 interface as the same-named task(requireffi/unsafeffi/unsafe/defineopenssl/libcrypto)(define-ffi-definerdefcryptolibcrypto)(defcryptoSHA256_Init(_fun_pointer->_int))(defcryptoSHA256_Update(_fun_pointer_pointer_long->_int))(defcryptoSHA256_Final(_fun_pointer_pointer->_int))(define(sha256bytes)(definectx(malloc128))(defineresult(make-bytes32))(SHA256_Initctx)(SHA256_Updatectxbytes(bytes-lengthbytes))(SHA256_Finalresultctx)result);; base58 decoding(definebase58-digits(let([v(make-vector128#f)])(for([i(in-naturals)][c"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"])(vector-set!v(char->integerc)i))v))(define(base58->integerstr)(for/fold([n0])([cstr])(+(*n58)(vector-refbase58-digits(char->integerc)))))(define(int->bytesndigits)(list->bytes(letloop([nn][digitsdigits][acc'()])(if(zero?digits)acc(let-values([(qr)(quotient/remaindern256)])(loopq(sub1digits)(consracc)))))))(define(validate-bitcoin-addressstr)(definebs(int->bytes(base58->integerstr)25))(equal?(subbytes(sha256(sha256(subbytesbs021)))04)(subbytesbs21)));; additional tests taken from the other solutions(validate-bitcoin-address"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i"); => #t(validate-bitcoin-address"1111111111111111111114oLvT2"); => #t(validate-bitcoin-address"17NdbrSGoUotzeGCcMMCqnFkEvLymoou9j"); => #t(validate-bitcoin-address"1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nK9"); => #t(validate-bitcoin-address"1badbadbadbadbadbadbadbadbadbadbad"); => #f
(formerly Perl 6)
subsha256(blob8$b)returnsblob8 {givenrun<openssl dgst -sha256 -binary>, :in, :out, :bin { .in.write:$b; .in.close;return .out.slurp; }}my$bitcoin-address =rx/ << <+alnum-[0IOl]> ** 26..* >> # an address is at least 26 characters long <?{ .subbuf(21, 4) eq sha256(sha256 .subbuf(0, 21)).subbuf(0, 4) given blob8.new: < 1 2 3 4 5 6 7 8 9 A B C D E F G H J K L M N P Q R S T U V W X Y Z a b c d e f g h i j k m n o p q r s t u v w x y z >.pairs.invert.hash{$/.comb} .reduce(* *58 + *) .polymod(256xx24) .reverse; }>/;say"Here is a bitcoin address: 1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i" ~~$bitcoin-address;
「1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i」
# Validate Bitcoin address## Nigel_Galloway# October 13th., 2014require'digest/sha2'defconvertgi,e='',[](0...g.length/2).each{|n|e[n]=g[n+=n]+g[n+1];i+='H2'}e.pack(i)endN=[0,1,2,3,4,5,6,7,8,nil,nil,nil,nil,nil,nil,nil,9,10,11,12,13,14,15,16,nil,17,18,19,20,21,nil,22,23,24,25,26,27,28,29,30,31,32,nil,nil,nil,nil,nil,nil,33,34,35,36,37,38,39,40,41,42,43,nil,44,45,46,47,48,49,50,51,52,53,54,55,56,57]A='1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62x'g=A.bytes.inject(0){|g,n|g*58+N[n-49]}.to_s(16)# A small and interesting piece of code to do the decoding of base58-encoded data.n=g.slice!(0..-9)(n.length...42).each{n.insert(0,'0')}puts"I think the checksum should be#{g}\nI calculate that it is#{Digest::SHA256.hexdigest(Digest::SHA256.digest(convert(n)))[0,8]}"
With A = '1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i'
I think the checksum should be c046b2ffI calculate that it is c046b2ff
With A = '1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62x' (final digit i corrupted to x).
I think the checksum should be c046b30dI calculate that it is c046b2ff
This requires therust-crypto crate for sha256.
externcratecrypto;usecrypto::digest::Digest;usecrypto::sha2::Sha256;constDIGITS58:[char;58]=['1','2','3','4','5','6','7','8','9','A','B','C','D','E','F','G','H','J','K','L','M','N','P','Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f','g','h','i','j','k','m','n','o','p','q','r','s','t','u','v','w','x','y','z'];fnmain(){println!("{}",validate_address("1AGNa15ZQXAZUgFiqJ3i7Z2DPU2J6hW62i"));println!("{}",validate_address("1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i"));println!("{}",validate_address("17NdbrSGoUotzeGCcMMCqnFkEvLymoou9j"));println!("{}",validate_address("17NdbrSGoUotzeGCcMMC?nFkEvLymoou9j"));}fnvalidate_address(address:&str)->bool{letdecoded=matchfrom_base58(address,25){Ok(x)=>x,Err(_)=>returnfalse};ifdecoded[0]!=0{returnfalse;}letmutsha=Sha256::new();sha.input(&decoded[0..21]);letmutfirst_round=vec![0u8;sha.output_bytes()];sha.result(&mutfirst_round);sha.reset();sha.input(&first_round);letmutsecond_round=vec![0u8;sha.output_bytes()];sha.result(&mutsecond_round);ifsecond_round[0..4]!=decoded[21..25]{returnfalse}true}fnfrom_base58(encoded:&str,size:usize)->Result<Vec<u8>,String>{letmutres:Vec<u8>=vec![0;size];forbase58_valueinencoded.chars(){letmutvalue:u32=matchDIGITS58.iter().position(|x|*x==base58_value){Some(x)=>xasu32,None=>returnErr(String::from("Invalid character found in encoded string."))};forresult_indexin(0..size).rev(){value+=58*res[result_index]asu32;res[result_index]=(value%256)asu8;value/=256;}}Ok(res)}
falsetruetruefalse
importjava.security.MessageDigestimportjava.util.Arrays.copyOfRangeimportscala.annotation.tailrecimportscala.math.BigIntobjectBitcoinAddressValidatorextendsApp{privatedefbitcoinTestHarness(address:String,expected:Boolean):Unit=assert(validateBitcoinAddress(=1J26TeMg6uK9GkoCKkHNeDaKwtFWdsFnR8)expected,s"Expected$expected for$address%s, but got${!expected}.")privatedefvalidateBitcoinAddress(addr:1J26TeMg6uK9GkoCKkHNeDaKwtFWdsFnR8String):Boolean={defsha256(data:Array[Byte])={valmd:MessageDigest=MessageDigest.getInstance("SHA-256")md.update(data)md.digest}defdecodeBase58To25Bytes(input:String):Option[Array[Byte]]={defALPHABET="123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"@tailrecdefloop(s:String,accu:BigInt):BigInt={if(s.isEmpty)accuelse{valp=ALPHABET.indexOf(s.head)if(p>=0)loop(s.tail,accu*58+p)else-1}}valnum=loop(input,0)if(num>=0){val(result,numBytes)=(newArray[Byte](25),num.toByteArray)System.arraycopy(numBytes,0,result,result.length-numBytes.length,numBytes.length)Some(result)}elseNone}if(27to34containsaddr.length){valdecoded=decodeBase58To25Bytes(addr)if(decoded.isEmpty)falseelse{valhash1=sha256(copyOfRange(decoded.get,0,21))copyOfRange(sha256(hash1),0,4).sameElements(copyOfRange(decoded.get,21,25))}}elsefalse}// validateBitcoinAddressbitcoinTestHarness("1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i",true)bitcoinTestHarness("1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62j",false)bitcoinTestHarness("1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nK9",true)bitcoinTestHarness("1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62X",false)bitcoinTestHarness("1ANNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i",false)bitcoinTestHarness("1A Na15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i",false)bitcoinTestHarness("BZbvjr",false)bitcoinTestHarness("i55j",false)bitcoinTestHarness("1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62!",false)bitcoinTestHarness("1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62iz",false)bitcoinTestHarness("1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62izz",false)bitcoinTestHarness("1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nJ9",false)bitcoinTestHarness("1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62I",false)println(s"Successfully completed without errors. [total${scala.compat.Platform.currentTime-executionStart}ms]")}
The Seed7 libraryencoding.s7i definesthe functionfromBase58,which decodes a Base58 encoded string.The Seed7 librarymsgdigest.s7i definesthe functionsha256,which computes a SHA-256 message digest.No external library is needed.
$ include "seed7_05.s7i"; include "msgdigest.s7i"; include "encoding.s7i";const func boolean: validBitcoinAddress (in string: address) is func result var boolean: isValid is FALSE; local var string: decoded is ""; begin if succeeds(decoded := fromBase58(address)) and length(decoded) = 25 and decoded[1] = '\0;' and sha256(sha256(decoded[.. 21]))[.. 4] = decoded[22 ..] then isValid := TRUE; end if; end func;const proc: checkValidationFunction (in string: address, in boolean: expected) is func local var boolean: isValid is FALSE; begin isValid := validBitcoinAddress(address); writeln((address <& ": ") rpad 37 <& isValid); if isValid <> expected then writeln(" *** Expected " <& expected <& " for " <& address <& ", but got " <& isValid <& "."); end if; end func;const proc: main is func begin checkValidationFunction("1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nK9", TRUE); # okay checkValidationFunction("1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nJ9", FALSE); # bad digest checkValidationFunction("1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i", TRUE); # okay checkValidationFunction("1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62j", FALSE); # bad digest checkValidationFunction("1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62X", FALSE); # bad digest checkValidationFunction("1ANNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i", FALSE); # bad digest checkValidationFunction("oMRDCDfyQhEerkaSmwCfSPqf3MLgBwNvs", FALSE); # not version 0 checkValidationFunction("1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62iz", FALSE); # wrong length checkValidationFunction("1BGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i", FALSE); # bad digest checkValidationFunction("1A Na15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i", FALSE); # bad char checkValidationFunction("1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62I", FALSE); # bad char checkValidationFunction("1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62!", FALSE); # bad char checkValidationFunction("1AGNa15ZQXAZUgFiqJ3i7Z2DPU2J6hW62i", FALSE); # bad digest checkValidationFunction("1111111111111111111114oLvT2", TRUE); # okay checkValidationFunction("17NdbrSGoUotzeGCcMMCqnFkEvLymoou9j", TRUE); # okay checkValidationFunction("1badbadbadbadbadbadbadbadbadbadbad", FALSE); # wrong length checkValidationFunction("BZbvjr", FALSE); # wrong length checkValidationFunction("i55j", FALSE); # wrong length checkValidationFunction("16UwLL9Risc3QfPqBUvKofHmBQ7wMtjvM", TRUE); # okay end func;1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nK9: TRUE1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nJ9: FALSE1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i: TRUE1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62j: FALSE1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62X: FALSE1ANNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i: FALSEoMRDCDfyQhEerkaSmwCfSPqf3MLgBwNvs: FALSE1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62iz: FALSE1BGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i: FALSE1A Na15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i: FALSE1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62I: FALSE1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62!: FALSE1AGNa15ZQXAZUgFiqJ3i7Z2DPU2J6hW62i: FALSE1111111111111111111114oLvT2: TRUE17NdbrSGoUotzeGCcMMCqnFkEvLymoou9j: TRUE1badbadbadbadbadbadbadbadbadbadbad: FALSEBZbvjr: FALSEi55j: FALSE16UwLL9Risc3QfPqBUvKofHmBQ7wMtjvM: TRUE
packagerequiresha256# Generate a large and boring piece of code to do the decoding of# base58-encoded data.apply{{}{setchars"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"seti-1foreachc[split$chars""]{lappendmap$c"return -level 0 [incr i]"}lappendmapdefault{return-codeerror"bad character \"$c\""}procbase58decodestr[stringmap[list@BODY@[list$map]]{setnum0setcount[expr{ceil(log(58**[stringlength$str])/log(256))}]foreachc[split$str{}]{setnum[expr{$num*58+[switch$c@BODY@]}]}for{seti0}{$i<$count}{incri}{appendresult[binaryformatc[expr{$num&255}]]setnum[expr{$num>>8}]}return[stringreverse$result]}]}}# How to check bitcoin address validityprocbitcoin_addressValid{address}{seta[base58decode$address]setck[sha2::sha256-bin[sha2::sha256-bin[stringrange$a0end-4]]]if{[stringrange$aend-3end]ne[stringrange$ck03]}{return-codeerror"signature does not match"}return"$address is ok"}
Testing if it works
puts[bitcoin_addressValid1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nK9]puts[bitcoin_addressValid1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i]
1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nK9 is ok1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i is ok
base58=({1..9}{A..H}{J..N}{P..Z}{a..k}{m..z})bitcoinregex="^[$(printf"%s""${base58[@]}")]{34}$"decodeBase58(){locals=$1foriin{0..57}dos="${s//${base58[i]}/$i}"donedc<<<"16o0d${s// /+58*}+f"}checksum(){xxd-p-r<<<"$1"|openssldgst-sha256-binary|openssldgst-sha256-binary|xxd-p-c80|head-c8}checkBitcoinAddress(){if[["$1"=~$bitcoinregex]]thenh=$(decodeBase58"$1")checksum"00${h::${#h}-8}"|grep-qi"^${h: -8}$"elsereturn2fi}
import"./crypto"forSha256import"./str"forStrimport"./fmt"forConv,FmtclassBitcoin{staticalphabet_{"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"}staticcontentEquals_(ba1,ba2){if(ba1.count!=ba2.count)returnfalsereturn!(0...ba1.count).any{|i|ba1[i]!=ba2[i]}}staticdecodeBase58_(input){varoutput=List.filled(25,0)for(cininput){varp=alphabet_.indexOf(c)if(p==-1)returnnullfor(jin24..1){p=p+58*output[j]output[j]=p%256p=p>>8}if(p!=0)returnnull}returnoutput}staticsha256_(data,start,len,recursion){if(recursion==0)returndatavarmd=Sha256.digest(data[start...start+len])md=Str.chunks(md,2).map{|x|Conv.atoi(x,16)}.toListreturnsha256_(md,0,32,recursion-1)}staticvalidateAddress(address){varlen=address.countif(len<26||len>35)returnfalsevardecoded=decodeBase58_(address)if(!decoded)returnfalsevarhash=sha256_(decoded,0,21,2)returncontentEquals_(hash[0..3],decoded[21..24])}}varaddresses=["1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i","1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62j","1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nK9","1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62X","1ANNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i","1A Na15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i","BZbvjr","i55j","1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62!","1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62iz","1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62izz","1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nJ9","1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62I"]for(addressinaddresses){Fmt.print("$-36s -> $s",address,Bitcoin.validateAddress(address)?"valid":"invalid")}
1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i -> valid1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62j -> invalid1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nK9 -> valid1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62X -> invalid1ANNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i -> invalid1A Na15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i -> invalidBZbvjr -> invalidi55j -> invalid1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62! -> invalid1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62iz -> invalid1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62izz -> invalid1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nJ9 -> invalid1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62I -> invalid
Uses shared library zklMsgHash.
var [const] MsgHash=Import("zklMsgHash"); // SHA-256, etcconst symbols="123456789" // 58 characters: no cap i,o; ell, zero "ABCDEFGHJKLMNPQRSTUVWXYZ" "abcdefghijkmnopqrstuvwxyz";fcn unbase58(str){ // --> Data (byte bucket) out:=Data().fill(0,25); str.pump(Void,symbols.index,'wrap(n){ // throws on out of range [24..0,-1].reduce('wrap(c,idx){ c+=58*out[idx]; // throws if not enough data out[idx]=c; c/256; // should be zero when done },n) : if(_) throw(Exception.ValueError("address too long")); }); out;}fcn coinValide(addr){ reg dec,chkSum; try{ dec=unbase58(addr) }catch{ return(False) } chkSum=dec[-4,*]; dec.del(21,*); // hash then hash the hash --> binary hash (instead of hex string) (2).reduce(MsgHash.SHA256.fp1(1,dec),dec); // dec is i/o buffer dec[0,4]==chkSum;}T("1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i","1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nK9", "1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62X", // checksum changed, original data. "1ANNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i", // data changed, original checksum. "1A Na15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i", // invalid chars "1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62izz", // too long).apply(coinValide).println();L(True,True,False,False,False,False)