Task: Bitcoin_address validation




Go: package main

import (
    "bytes"
    "crypto/sha256"
    "errors"
    "os"
)









type A25 [25]byte
    
func (a *A25) Version() byte {
    return a[0]
}
    
func (a *A25) EmbeddedChecksum() (c [4]byte) {
    copy(c[:], a[21:])
    return
}





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])
}



func (a *A25) ComputeChecksum() (c [4]byte) {
    copy(c[:], a.doubleSHA256())
    return
} 



var tmpl = []byte("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz")





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
}






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
}



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)
}



PHP: function validate($address){
        $decoded = decodeBase58($address);

        $d1 = hash("sha256", substr($decoded,0,21), true);
        $d2 = hash("sha256", $d1, true);

        if(substr_compare($decoded, $d2, 21, 4)){
                throw new \Exception("bad digest");
        }
        return true;
}
function decodeBase58($input) {
        $alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";

        $out = array_fill(0, 25, 0);
        for($i=0;$i<strlen($input);$i++){
                if(($p=strpos($alphabet, $input[$i]))===false){
                        throw new \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){
                    throw new \Exception("address too long");
                }
        }

        $result = "";
        foreach($out as $val){
                $result .= chr($val);
        }

        return $result;
}

function main () {
  $s = array(
                "1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nK9",
                "1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i",
                "1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nJ9",
                "1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62I",
        );
  foreach($s as $btc){
    $message = "OK";
    try{
        validate($btc);
    }catch(\Exception $e){ $message = $e->getMessage(); }
    echo "$btc: $message\n";
  }
}

main();