Task: Bitcoin_address validation

C: #include <stdio.h>
#include <string.h>
#include <openssl/sha.h>

const char *coin_err;
#define bail(s) { coin_err = s; return 0; }

int unbase58(const char *s, unsigned char *out) {
	static const char *tmpl = "123456789"
		"ABCDEFGHJKLMNPQRSTUVWXYZ"
		"abcdefghijkmnopqrstuvwxyz";
	int i, j, c;
	const char *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");
	}

	return 1;
}

int valid(const char *s) {
	unsigned char dec[32], d1[SHA256_DIGEST_LENGTH], d2[SHA256_DIGEST_LENGTH];

	coin_err = "";
	if (!unbase58(s, dec)) return 0;

	SHA256(SHA256(dec, 21, d1), SHA256_DIGEST_LENGTH, d2);

	if (memcmp(dec + 21, d2, 4))
		bail("bad digest");

	return 1;
}

int main (void) {
	const char *s[] = {
		"1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nK9",
		"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i",
		"1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nJ9",
		"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62I",
		0 };
	int i;
	for (i = 0; s[i]; i++) {
		int status = valid(s[i]);
		printf("%s: %s\n", s[i], status ? "Ok" : coin_err);
	}

	return 0;
}











Ada: with Ada.Exceptions, Interfaces;
with Ada.Streams;
use Ada.Exceptions, Interfaces;
use Ada.Streams;

package Bitcoin is
    subtype BT_Raw_Addr is Stream_Element_Array(1..25);
    subtype BT_Checksum is Stream_Element_Array(1..4);
    subtype BT_Addr is String(1..34);
    subtype Sha256String is String(1..64);
    Invalid_Address_Error : Exception;

    function Double_Sha256(S : Stream_Element_Array) return BT_Checksum;
    function Is_Valid(A : BT_Raw_Addr) return Boolean;
    procedure Base58_Decode(S : BT_Addr; A : out BT_Raw_Addr) ;
private
    Base58 : constant String := "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
    function Hex_Val (C, C2 : Character) return Stream_Element;
end Bitcoin;


with GNAT.SHA256, Ada.Strings.Fixed;
use  GNAT.SHA256, Ada.Strings.Fixed;

package body Bitcoin is

function Hex_Val (C, C2 : Character) return Stream_Element is
    subtype Nibble is Integer range 0..15;
    HEX : array (0..255) of Nibble := (
          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
    );
begin
    return Stream_Element(HEX(Character'Pos(C)) * 16 + HEX(Character'Pos(C2)));
end Hex_Val;

function Double_Sha256(S : Stream_Element_Array) return BT_Checksum is
    Ctx  : Context := Initial_Context;
    D : Message_Digest;
    S2 : Stream_Element_Array(1..32);
    Ctx2 : Context := Initial_Context;
    C : BT_Checksum;
begin
    Update(Ctx, S);
    D := Digest(Ctx);
    for I in S2'Range loop
        S2(I) := Hex_Val(D(Integer(I)*2-1), D(Integer(I)*2));
    end loop;
    Update(Ctx2, S2);
    D := Digest(Ctx2);
    for I in C'Range loop
        C(I) := Hex_Val(D(Integer(I)*2-1), D(Integer(I)*2));
    end loop;
    return C;

end Double_Sha256;







procedure Base58_Decode(S : BT_Addr; A : out BT_Raw_Addr) is
begin
    A := (Others => 0);
    for I in S'Range loop
        declare
            P : Natural := Index(Base58, String(S(I..I)));
            C : Natural;
        begin
            if P = 0 then
                raise Invalid_Address_Error;
            end if;
            C := P - 1;
            for J in reverse A'Range loop
                C    := C + Natural(A(J)) * 58;
                A(J) := Stream_Element(Unsigned_32(C) and 255);         
                C    := Natural(Shift_Right(Unsigned_32(C),8) and 255); 
            end loop;
            if C /= 0 then
                raise Invalid_Address_Error;
            end if;
        end;
    end loop;
end Base58_Decode;


function Is_Valid(A : BT_Raw_Addr) return Boolean is
begin
    return A(1) = 0 and A(22..25) = Double_Sha256(A(1..21));
end Is_Valid;


end Bitcoin;

with Ada.Text_IO, Bitcoin;
use Ada.Text_IO, Bitcoin;

procedure Bitcoin_Addr_Validate is
begin
    declare
        BTs : array (positive range <>) of BT_Addr := (
              "1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i"  
            , "1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nK9"  
            , "1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62X"  
            , "1ANNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i"  
            , "1A Na15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i"  
        );
    begin
        for I in Bts'Range loop
            declare
                A : BT_Raw_Addr;
                Valid : Boolean;
            begin
                Put(BTs(I) & " validity: ");
                Base58_Decode(BTs(I), A);
                Valid := Is_Valid(A);
                Put_Line(Boolean'Image(Valid));
            exception
                when E : Invalid_Address_Error  =>
                    Put_Line ("*** Error: Invalid BT address.");
            end;
        end loop;
    end;
end Bitcoin_Addr_Validate;