129 lines
4.1 KiB
Ada
129 lines
4.1 KiB
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;
|
|
|
|
|
|
--------------------------------------------------------------------------------
|
|
-- Summary of Base58: --
|
|
-- We decode S into a 200 bit unsigned integer. --
|
|
-- We could use a BigNum library, but choose to go without. --
|
|
--------------------------------------------------------------------------------
|
|
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); -- 0x00FF
|
|
C := Natural(Shift_Right(Unsigned_32(C),8) and 255); -- 0xFF00
|
|
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" -- VALID
|
|
, "1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nK9" -- VALID
|
|
, "1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62X" -- checksum changed, original data.
|
|
, "1ANNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i" -- data changed, original checksum.
|
|
, "1A Na15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i" -- invalid chars
|
|
);
|
|
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;
|