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.




package main
 
import (
    "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.
type A25 [25]byte
 
func (a *A25) Version() byte {
    return a[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)
    return h.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.
var tmpl = []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 := range s {
        c := bytes.IndexByte(tmpl, s1)
        if c < 0 {
            return errors.New("bad char")
        }
        for j := 24; j >= 0; j-- {
            c += 58 * int(a[j])
            a[j] = byte(c % 256)
            c /= 256
        }
        if c > 0 {
            return errors.New("too long")
        }
    }
    return nil
}
 
// 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.
func ValidA58(a58 []byte) (ok bool, err error) {
    var a A25
    if err := a.Set58(a58); err != nil {
        return false, err
    }
    if a.Version() != 0 {
        return false, errors.New("not version 0")
    }
    return a.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.
func main() {
    if len(os.Args) != 2 {
        errorExit("Usage: valid <base58 address>")
    }
    switch ok, err := ValidA58([]byte(os.Args[1])); {
    case ok:
    case err == nil:
        errorExit("Invalid")
    default:
        errorExit(err.Error())
    }
}
 
func errorExit(m string) {
    os.Stderr.WriteString(m + "\n")
    os.Exit(1)
}