Bitcoin/address validation

You are encouraged to solve this task according to the task description, using any language you may know.


Task

Write a program that takes a bitcoin 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:

   0   zero
   O   uppercase oh
   I   uppercase eye
   l   lowercase ell


With this encoding, a bitcoin address encodes 25 bytes:

 the first byte is the version number, which will be zero for this task ;
 the next twenty bytes are a RIPEMD-160 digest, but you don't have to know that for this task: you can consider them a pure arbitrary data ;
 the last four bytes are a checksum check. They are the first four bytes of a double SHA-256 digest of the previous 21 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 for SHA-256.



Example of a bitcoin address



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.




var [const] MsgHash=Import("zklMsgHash"); // SHA-256, etc
const 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;
}