Data update
This commit is contained in:
parent
4bb20c9b71
commit
cbaf4c4b64
12390 changed files with 318560 additions and 27248 deletions
|
|
@ -0,0 +1,128 @@
|
|||
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;
|
||||
|
|
@ -12,77 +12,77 @@ SHA256 sha256{ };
|
|||
const std::string ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
|
||||
|
||||
std::map<char, uint32_t> base_map =
|
||||
{ { '0', 0 }, { '1', 1 }, { '2', 2 }, { '3', 3 }, { '4', 4 }, { '5', 5 }, { '6', 6 }, { '7', 7 },
|
||||
{ '8', 8 }, { '9', 9 }, { 'a', 10 }, { 'b', 11 }, { 'c', 12 }, { 'd', 13 }, { 'e', 14 }, { 'f', 15 },
|
||||
{ 'A', 10 }, { 'B', 11 }, { 'C', 12 }, { 'D', 13 }, { 'E', 14 }, { 'F', 15 } };
|
||||
{ { '0', 0 }, { '1', 1 }, { '2', 2 }, { '3', 3 }, { '4', 4 }, { '5', 5 }, { '6', 6 }, { '7', 7 },
|
||||
{ '8', 8 }, { '9', 9 }, { 'a', 10 }, { 'b', 11 }, { 'c', 12 }, { 'd', 13 }, { 'e', 14 }, { 'f', 15 },
|
||||
{ 'A', 10 }, { 'B', 11 }, { 'C', 12 }, { 'D', 13 }, { 'E', 14 }, { 'F', 15 } };
|
||||
|
||||
std::vector<uint32_t> hex_to_bytes(const std::string& text) {
|
||||
std::vector<uint32_t> bytes(text.size() / 2, 0);
|
||||
for ( uint64_t i = 0; i < text.size(); i += 2 ) {
|
||||
const uint32_t first_digit = base_map[text[i]];
|
||||
const uint32_t second_digit = base_map[text[i + 1]];
|
||||
bytes[i / 2] = ( first_digit << 4 ) + second_digit;
|
||||
}
|
||||
return bytes;
|
||||
std::vector<uint32_t> bytes(text.size() / 2, 0);
|
||||
for ( uint64_t i = 0; i < text.size(); i += 2 ) {
|
||||
const uint32_t first_digit = base_map[text[i]];
|
||||
const uint32_t second_digit = base_map[text[i + 1]];
|
||||
bytes[i / 2] = ( first_digit << 4 ) + second_digit;
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
std::string vector_to_ascii_string(const std::vector<uint32_t>& bytes) {
|
||||
std::string result = "";
|
||||
for ( uint32_t i = 0; i < bytes.size(); ++i ) {
|
||||
result += static_cast<char>(bytes[i]);
|
||||
}
|
||||
return result;
|
||||
std::string result = "";
|
||||
for ( uint32_t i = 0; i < bytes.size(); ++i ) {
|
||||
result += static_cast<char>(bytes[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<uint32_t> decode_base_58(const std::string& text) {
|
||||
std::vector<uint32_t> result(25, 0);
|
||||
for ( const char& ch : text ) {
|
||||
std::string::size_type index = ALPHABET.find(ch);
|
||||
if ( index == static_cast<uint64_t>(-1) ) {
|
||||
throw std::invalid_argument("Invalid character found in bitcoin address");
|
||||
}
|
||||
for ( uint64_t i = result.size() - 1; i > 0; i-- ) {
|
||||
index += 58 * result[i];
|
||||
result[i] = index & 0xFF;
|
||||
index >>= 8;
|
||||
}
|
||||
if ( index != 0 ) {
|
||||
throw std::invalid_argument("Bitcoin address is too long");
|
||||
}
|
||||
}
|
||||
return result;
|
||||
std::vector<uint32_t> result(25, 0);
|
||||
for ( const char& ch : text ) {
|
||||
std::string::size_type index = ALPHABET.find(ch);
|
||||
if ( index == static_cast<uint64_t>(-1) ) {
|
||||
throw std::invalid_argument("Invalid character found in bitcoin address");
|
||||
}
|
||||
for ( uint64_t i = result.size() - 1; i > 0; i-- ) {
|
||||
index += 58 * result[i];
|
||||
result[i] = index & 0xFF;
|
||||
index >>= 8;
|
||||
}
|
||||
if ( index != 0 ) {
|
||||
throw std::invalid_argument("Bitcoin address is too long");
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool is_valid(const std::string& address) {
|
||||
if ( address.size() < 26 || address.size() > 35 ) {
|
||||
throw std::invalid_argument("Invalid length of bitcoin address");
|
||||
}
|
||||
if ( address.size() < 26 || address.size() > 35 ) {
|
||||
throw std::invalid_argument("Invalid length of bitcoin address");
|
||||
}
|
||||
|
||||
std::vector<uint32_t> decoded = decode_base_58(address);
|
||||
std::vector first21(decoded.begin(), decoded.begin() + 21);
|
||||
std::vector<uint32_t> decoded = decode_base_58(address);
|
||||
std::vector first21(decoded.begin(), decoded.begin() + 21);
|
||||
|
||||
// Convert the 'first21' into a suitable ASCII string for the first SHA256 hash
|
||||
std::string text = vector_to_ascii_string(first21);
|
||||
std::string hash_1 = sha256.message_digest(text);
|
||||
// Convert 'hashOne' into a suitable ASCII string for the second SHA256 hash
|
||||
std::vector<uint32_t> bytes_1 = hex_to_bytes(hash_1);
|
||||
std::string ascii_1 = vector_to_ascii_string(bytes_1);
|
||||
std::string hash_2 = sha256.message_digest(ascii_1);
|
||||
// Convert the 'first21' into a suitable ASCII string for the first SHA256 hash
|
||||
std::string text = vector_to_ascii_string(first21);
|
||||
std::string hash_1 = sha256.message_digest(text);
|
||||
// Convert 'hashOne' into a suitable ASCII string for the second SHA256 hash
|
||||
std::vector<uint32_t> bytes_1 = hex_to_bytes(hash_1);
|
||||
std::string ascii_1 = vector_to_ascii_string(bytes_1);
|
||||
std::string hash_2 = sha256.message_digest(ascii_1);
|
||||
|
||||
std::vector<uint32_t> bytes_2 = hex_to_bytes(hash_2);
|
||||
std::vector<uint32_t> checksum(bytes_2.begin(), bytes_2.begin() + 4);
|
||||
std::vector<uint32_t> last4(decoded.begin() + 21, decoded.begin() + 25);
|
||||
return checksum == last4;
|
||||
std::vector<uint32_t> bytes_2 = hex_to_bytes(hash_2);
|
||||
std::vector<uint32_t> checksum(bytes_2.begin(), bytes_2.begin() + 4);
|
||||
std::vector<uint32_t> last4(decoded.begin() + 21, decoded.begin() + 25);
|
||||
return checksum == last4;
|
||||
}
|
||||
|
||||
int main() {
|
||||
const std::vector<std::string> addresses = { "1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i",
|
||||
"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62j",
|
||||
"1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nK9",
|
||||
"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62X",
|
||||
"1ANNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i" };
|
||||
const std::vector<std::string> addresses = { "1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i",
|
||||
"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62j",
|
||||
"1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nK9",
|
||||
"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62X",
|
||||
"1ANNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i" };
|
||||
|
||||
for ( const std::string& address : addresses ) {
|
||||
std::cout << address << " : " << std::boolalpha << is_valid(address) << std::endl;
|
||||
}
|
||||
for ( const std::string& address : addresses ) {
|
||||
std::cout << address << " : " << std::boolalpha << is_valid(address) << std::endl;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,56 +6,56 @@ 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;
|
||||
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");
|
||||
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;
|
||||
}
|
||||
c = p - tmpl;
|
||||
for (j = 25; j--; ) {
|
||||
c += 58 * out[j];
|
||||
out[j] = c % 256;
|
||||
c /= 256;
|
||||
}
|
||||
|
||||
if (c) bail("address too long");
|
||||
}
|
||||
if (c) bail("address too long");
|
||||
}
|
||||
|
||||
return 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int valid(const char *s) {
|
||||
unsigned char dec[32], d1[SHA256_DIGEST_LENGTH], d2[SHA256_DIGEST_LENGTH];
|
||||
unsigned char dec[32], d1[SHA256_DIGEST_LENGTH], d2[SHA256_DIGEST_LENGTH];
|
||||
|
||||
coin_err = "";
|
||||
if (!unbase58(s, dec)) return 0;
|
||||
coin_err = "";
|
||||
if (!unbase58(s, dec)) return 0;
|
||||
|
||||
SHA256(SHA256(dec, 21, d1), SHA256_DIGEST_LENGTH, d2);
|
||||
SHA256(SHA256(dec, 21, d1), SHA256_DIGEST_LENGTH, d2);
|
||||
|
||||
if (memcmp(dec + 21, d2, 4))
|
||||
bail("bad digest");
|
||||
if (memcmp(dec + 21, d2, 4))
|
||||
bail("bad digest");
|
||||
|
||||
return 1;
|
||||
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);
|
||||
}
|
||||
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;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,15 +3,15 @@
|
|||
-export( [task/0, validate/1] ).
|
||||
|
||||
task() ->
|
||||
io:fwrite( "Validate ~p~n", ["1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i"] ),
|
||||
io:fwrite( "~p~n", [validate("1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i")] ),
|
||||
io:fwrite( "Validate ~p~n", ["1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW622"] ),
|
||||
io:fwrite( "~p~n", [validate("1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW622")] ).
|
||||
io:fwrite( "Validate ~p~n", ["1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i"] ),
|
||||
io:fwrite( "~p~n", [validate("1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i")] ),
|
||||
io:fwrite( "Validate ~p~n", ["1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW622"] ),
|
||||
io:fwrite( "~p~n", [validate("1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW622")] ).
|
||||
|
||||
validate( String ) ->
|
||||
{length_25, <<Address:21/binary, Checksum:4/binary>>} = {length_25, base58:base58_to_binary( String )},
|
||||
<<Version:1/binary, _/binary>> = Address,
|
||||
{version_0, <<0>>} = {version_0, Version},
|
||||
<<Four_bytes:4/binary, _T/binary>> = crypto:hash( sha256, crypto:hash(sha256, Address) ),
|
||||
{checksum, Checksum} = {checksum, Four_bytes},
|
||||
ok.
|
||||
{length_25, <<Address:21/binary, Checksum:4/binary>>} = {length_25, base58:base58_to_binary( String )},
|
||||
<<Version:1/binary, _/binary>> = Address,
|
||||
{version_0, <<0>>} = {version_0, Version},
|
||||
<<Four_bytes:4/binary, _T/binary>> = crypto:hash( sha256, crypto:hash(sha256, Address) ),
|
||||
{checksum, Checksum} = {checksum, Four_bytes},
|
||||
ok.
|
||||
|
|
|
|||
|
|
@ -4,45 +4,45 @@ import java.util.List;
|
|||
|
||||
public final class BitcoinAddressValidation {
|
||||
|
||||
public static void main(String[] args) {
|
||||
List<String> addresses = List.of ( "1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i",
|
||||
"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62j",
|
||||
"1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nK9",
|
||||
"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62X",
|
||||
"1ANNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i" );
|
||||
|
||||
for ( String address : addresses ) {
|
||||
System.out.println(address + " : " + isValid(address));
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isValid(String address) {
|
||||
if ( address.length() < 26 || address.length() > 35 ) {
|
||||
throw new AssertionError("Invalid length of bitcoin address");
|
||||
}
|
||||
|
||||
byte[] decoded = decodeBase58(address);
|
||||
byte[] first21 = Arrays.copyOfRange(decoded, 0, 21);
|
||||
// Convert 'first21' into an ASCII string for the first SHA256 hash
|
||||
String text = new String(first21, StandardCharsets.ISO_8859_1);
|
||||
String hashOne = SHA256.messageDigest(text);
|
||||
// Convert 'hashOne' into an ASCII string for the second SHA256 hash
|
||||
byte[] bytesOne = hexToBytes(hashOne);
|
||||
String asciiOne = new String(bytesOne, StandardCharsets.ISO_8859_1);
|
||||
String hashTwo = SHA256.messageDigest(asciiOne);
|
||||
|
||||
byte[] bytesTwo = hexToBytes(hashTwo);
|
||||
byte[] checksum = Arrays.copyOfRange(bytesTwo, 0, 4);
|
||||
byte[] last4 = Arrays.copyOfRange(decoded, 21, 25);
|
||||
return Arrays.equals(last4, checksum);
|
||||
}
|
||||
|
||||
private static byte[] decodeBase58(String text) {
|
||||
public static void main(String[] args) {
|
||||
List<String> addresses = List.of ( "1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i",
|
||||
"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62j",
|
||||
"1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nK9",
|
||||
"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62X",
|
||||
"1ANNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i" );
|
||||
|
||||
for ( String address : addresses ) {
|
||||
System.out.println(address + " : " + isValid(address));
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isValid(String address) {
|
||||
if ( address.length() < 26 || address.length() > 35 ) {
|
||||
throw new AssertionError("Invalid length of bitcoin address");
|
||||
}
|
||||
|
||||
byte[] decoded = decodeBase58(address);
|
||||
byte[] first21 = Arrays.copyOfRange(decoded, 0, 21);
|
||||
// Convert 'first21' into an ASCII string for the first SHA256 hash
|
||||
String text = new String(first21, StandardCharsets.ISO_8859_1);
|
||||
String hashOne = SHA256.messageDigest(text);
|
||||
// Convert 'hashOne' into an ASCII string for the second SHA256 hash
|
||||
byte[] bytesOne = hexToBytes(hashOne);
|
||||
String asciiOne = new String(bytesOne, StandardCharsets.ISO_8859_1);
|
||||
String hashTwo = SHA256.messageDigest(asciiOne);
|
||||
|
||||
byte[] bytesTwo = hexToBytes(hashTwo);
|
||||
byte[] checksum = Arrays.copyOfRange(bytesTwo, 0, 4);
|
||||
byte[] last4 = Arrays.copyOfRange(decoded, 21, 25);
|
||||
return Arrays.equals(last4, checksum);
|
||||
}
|
||||
|
||||
private static byte[] decodeBase58(String text) {
|
||||
byte[] result = new byte[25];
|
||||
for ( char ch : text.toCharArray() ) {
|
||||
int index = ALPHABET.indexOf(ch);
|
||||
if ( index == -1 ) {
|
||||
throw new AssertionError("Invalid character found in bitcoin address: " + ch);
|
||||
throw new AssertionError("Invalid character found in bitcoin address: " + ch);
|
||||
}
|
||||
for ( int i = result.length - 1; i > 0; i-- ) {
|
||||
index += 58 * (int) ( result[i] & 0xFF );
|
||||
|
|
@ -50,22 +50,22 @@ public final class BitcoinAddressValidation {
|
|||
index >>= 8;
|
||||
}
|
||||
if ( index != 0 ) {
|
||||
throw new AssertionError("Bitcoin address is too long");
|
||||
throw new AssertionError("Bitcoin address is too long");
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static byte[] hexToBytes(String text) {
|
||||
byte[] bytes = new byte[text.length() / 2];
|
||||
for ( int i = 0; i < text.length(); i += 2 ) {
|
||||
final int firstDigit = Character.digit(text.charAt(i), 16);
|
||||
final int secondDigit = Character.digit(text.charAt(i + 1), 16);
|
||||
bytes[i / 2] = (byte) ( ( firstDigit << 4 ) + secondDigit );
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
private static final String ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
|
||||
private static byte[] hexToBytes(String text) {
|
||||
byte[] bytes = new byte[text.length() / 2];
|
||||
for ( int i = 0; i < text.length(); i += 2 ) {
|
||||
final int firstDigit = Character.digit(text.charAt(i), 16);
|
||||
final int secondDigit = Character.digit(text.charAt(i + 1), 16);
|
||||
bytes[i / 2] = (byte) ( ( firstDigit << 4 ) + secondDigit );
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
private static final String ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,59 @@
|
|||
require "table2"
|
||||
local crypto = require "crypto"
|
||||
local fmt = require "fmt"
|
||||
|
||||
$define ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
|
||||
|
||||
local function decode_base58(input)
|
||||
local output = table.rep(25, 0)
|
||||
for i = 1, #input do
|
||||
local c = input[i]
|
||||
local p = ALPHABET:find(c, 1, true)
|
||||
if !p then return nil end
|
||||
p -= 1
|
||||
for j = 25, 2, -1 do
|
||||
p += 58 * output[j]
|
||||
output[j] = p % 256
|
||||
p >>= 8
|
||||
end
|
||||
if p != 0 then return nil end
|
||||
end
|
||||
return output
|
||||
end
|
||||
|
||||
local function sha256(data, start, len, recursion)
|
||||
if recursion == 0 then return data end
|
||||
local ds = data:slice(start, start + len - 1)
|
||||
local md = crypto.sha256(string.char(ds:unpack()))
|
||||
md = md:split(""):chunk(2):map(|x| -> tonumber(x:concat(), 16))
|
||||
return sha256(md, 1, 32, recursion - 1)
|
||||
end
|
||||
|
||||
local function validate_address(address)
|
||||
local len = #address
|
||||
if len < 26 or len > 35 then return false end
|
||||
local decoded = decode_base58(address)
|
||||
if !decoded then return false end
|
||||
local hash = sha256(decoded, 1, 21, 2)
|
||||
return table.same(hash:slice(1, 4), decoded:slice(22, 25))
|
||||
end
|
||||
|
||||
local addresses = {
|
||||
"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i",
|
||||
"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62j",
|
||||
"1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nK9",
|
||||
"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62X",
|
||||
"1ANNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i",
|
||||
"1A Na15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i",
|
||||
"BZbvjr",
|
||||
"i55j",
|
||||
"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62!",
|
||||
"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62iz",
|
||||
"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62izz",
|
||||
"1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nJ9",
|
||||
"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62I"
|
||||
}
|
||||
|
||||
for addresses as address do
|
||||
fmt.print("%-36s -> %s", address, validate_address(address) ? "valid" : "invalid")
|
||||
end
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
Rebol [
|
||||
title: "Rosetta code: Bitcoin_address_validation"
|
||||
file: %Bitcoin_address_validation.r3
|
||||
url: https://rosettacode.org/wiki/Bitcoin/address_validation
|
||||
]
|
||||
|
||||
decode-base58: function [
|
||||
"Decodes a Base58-encoded string into a 25-byte binary value."
|
||||
input [string!]
|
||||
][
|
||||
size: 25
|
||||
alphabet: "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
|
||||
result: make binary! size
|
||||
append/dup result 0 size
|
||||
foreach c input [
|
||||
;; Find character position (1-based). Returns none if not in alphabet.
|
||||
i: index? find/case alphabet c
|
||||
unless i [return none] ;; Invalid character found
|
||||
i: i - 1 ;; Convert to 0-based index
|
||||
;; Treat result as a base-256 big-endian number and multiply by 58,
|
||||
;; adding the new digit. Work right-to-left to propagate carries.
|
||||
for j size 1 -1 [
|
||||
i: i + (58 * to integer! result/:j)
|
||||
result/:j: i % 256 ;; Store the low byte
|
||||
i: to integer! (i / 256) ;; Carry the remainder leftward
|
||||
]
|
||||
if i != 0 [return none] ;; Address too long
|
||||
]
|
||||
result
|
||||
]
|
||||
valid-bitcoin?: function [
|
||||
"Returns true if a Bitcoin address has a valid checksum, false otherwise."
|
||||
address [string!]
|
||||
][
|
||||
did all [
|
||||
bin: decode-base58 address ;; Decode to 25 raw bytes (21 payload + 4 checksum)
|
||||
sum: checksum/part bin 'sha256 21 ;; SHA256 over the first 21 bytes (payload only)
|
||||
sum: checksum sum 'sha256 ;; SHA256 again (double-hash)
|
||||
equal? (skip bin 21) (copy/part sum 4) ;; Compare last 4 bytes of address to first 4 of hash
|
||||
]
|
||||
]
|
||||
|
||||
foreach address [
|
||||
"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i" ; VALID
|
||||
"1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nK9" ; VALID
|
||||
"1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62X" ; checksum changed, original data.
|
||||
"1ANNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i" ; data changed, original checksum.
|
||||
"1A Na15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i" ; invalid chars
|
||||
][
|
||||
print [address 'is pick ["valid." "invalid!"] valid-bitcoin? address]
|
||||
]
|
||||
|
|
@ -6,20 +6,20 @@ apply {{} {
|
|||
set chars "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
|
||||
set i -1
|
||||
foreach c [split $chars ""] {
|
||||
lappend map $c "return -level 0 [incr i]"
|
||||
lappend map $c "return -level 0 [incr i]"
|
||||
}
|
||||
lappend map default {return -code error "bad character \"$c\""}
|
||||
proc base58decode str [string map [list @BODY@ [list $map]] {
|
||||
set num 0
|
||||
set count [expr {ceil(log(58**[string length $str])/log(256))}]
|
||||
foreach c [split $str {}] {
|
||||
set num [expr {$num*58+[switch $c @BODY@]}]
|
||||
}
|
||||
for {set i 0} {$i < $count} {incr i} {
|
||||
append result [binary format c [expr {$num & 255}]]
|
||||
set num [expr {$num >> 8}]
|
||||
}
|
||||
return [string reverse $result]
|
||||
set num 0
|
||||
set count [expr {ceil(log(58**[string length $str])/log(256))}]
|
||||
foreach c [split $str {}] {
|
||||
set num [expr {$num*58+[switch $c @BODY@]}]
|
||||
}
|
||||
for {set i 0} {$i < $count} {incr i} {
|
||||
append result [binary format c [expr {$num & 255}]]
|
||||
set num [expr {$num >> 8}]
|
||||
}
|
||||
return [string reverse $result]
|
||||
}]
|
||||
}}
|
||||
|
||||
|
|
@ -28,7 +28,7 @@ proc bitcoin_addressValid {address} {
|
|||
set a [base58decode $address]
|
||||
set ck [sha2::sha256 -bin [sha2::sha256 -bin [string range $a 0 end-4]]]
|
||||
if {[string range $a end-3 end] ne [string range $ck 0 3]} {
|
||||
return -code error "signature does not match"
|
||||
return -code error "signature does not match"
|
||||
}
|
||||
return "$address is ok"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,68 @@
|
|||
import crypto.sha256
|
||||
|
||||
const test = ["1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i", "1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nK9",
|
||||
"1badbadbadbadbadbadbadbadbadbadbad", "1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62I"]
|
||||
|
||||
struct A25 {
|
||||
mut:
|
||||
data [25]u8
|
||||
tmpl []u8 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz".bytes()
|
||||
}
|
||||
|
||||
fn (a &A25) version() u8 {
|
||||
return a.data[0]
|
||||
}
|
||||
|
||||
fn (a &A25) embedded_checksum() [4]u8 {
|
||||
mut c := [4]u8{}
|
||||
for i in 0 .. 4 {
|
||||
c[i] = a.data[21 + i]
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
fn (a &A25) double_sha256() []u8 {
|
||||
h1 := sha256.sum(a.data[0..21])
|
||||
h2 := sha256.sum(h1)
|
||||
return h2
|
||||
}
|
||||
|
||||
fn (a &A25) compute_checksum() [4]u8 {
|
||||
d := a.double_sha256()
|
||||
mut c := [4]u8{}
|
||||
for i in 0 .. 4 {
|
||||
c[i] = d[i]
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
fn (mut a A25) set58(s []u8) ! {
|
||||
for s1 in s {
|
||||
mut c := a.tmpl.index(s1)
|
||||
if c < 0 { return error("bad char") }
|
||||
for j := 24; j >= 0; j-- {
|
||||
c += 58 * int(a.data[j])
|
||||
a.data[j] = u8(c % 256)
|
||||
c /= 256
|
||||
}
|
||||
if c > 0 { return error("too long") }
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
fn valid_a58(a58 []u8) !bool {
|
||||
mut a := A25{}
|
||||
a.set58(a58) or { return false }
|
||||
if a.version() != 0 { return false }
|
||||
return a.embedded_checksum() == a.compute_checksum()
|
||||
}
|
||||
|
||||
fn main() {
|
||||
for val in test {
|
||||
try := val.bytes()
|
||||
match valid_a58(try)! {
|
||||
true { println("'${try.bytestr()}' is valid") }
|
||||
false { println("'${try.bytestr()}' is not valid") }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
const std = @import("std");
|
||||
const Sha256 = std.crypto.hash.sha2.Sha256;
|
||||
|
||||
pub fn isValid(string: []const u8) bool {
|
||||
const chars = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
|
||||
|
||||
var val: u256 = 0;
|
||||
|
||||
for (string) |char| {
|
||||
const index = std.mem.indexOf(u8, chars, &[1]u8{char});
|
||||
if (index == null) {
|
||||
return false;
|
||||
}
|
||||
val = val * 58 + index.?;
|
||||
}
|
||||
|
||||
var bytes: [25]u8 = undefined;
|
||||
|
||||
var i: i8 = 24;
|
||||
var actualI: usize = 0;
|
||||
|
||||
while (i >= 0) : (i -= 1) {
|
||||
const castI: u8 = @intCast(i);
|
||||
bytes[actualI] = @intCast((val >> castI * 8) & 0xFF);
|
||||
actualI += 1;
|
||||
}
|
||||
|
||||
var hash: [Sha256.digest_length]u8 = undefined;
|
||||
const first21 = bytes[0..21];
|
||||
|
||||
Sha256.hash(first21, &hash, .{});
|
||||
|
||||
var hash2: [Sha256.digest_length]u8 = undefined;
|
||||
Sha256.hash(&hash, &hash2, .{});
|
||||
|
||||
const firstFourHash = hash2[0..4];
|
||||
const lastFour = bytes[21..25];
|
||||
|
||||
return std.mem.eql(u8, firstFourHash, lastFour);
|
||||
}
|
||||
|
||||
pub fn main() !void {
|
||||
std.debug.print("{}\n", .{isValid("1AGNa15ZQXAZUgFiqJ3i7Z2DPU2J6hW62i")});
|
||||
std.debug.print("{}\n", .{isValid("17NdbrSGoUotzeGCcMMCqnFkEvLymoou9j")});
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
var [const] MsgHash=Import("zklMsgHash"); // SHA-256, etc
|
||||
const symbols="123456789" // 58 characters: no cap i,o; ell, zero
|
||||
"ABCDEFGHJKLMNPQRSTUVWXYZ"
|
||||
"abcdefghijkmnopqrstuvwxyz";
|
||||
"ABCDEFGHJKLMNPQRSTUVWXYZ"
|
||||
"abcdefghijkmnopqrstuvwxyz";
|
||||
|
||||
fcn unbase58(str){ // --> Data (byte bucket)
|
||||
out:=Data().fill(0,25);
|
||||
|
|
@ -9,7 +9,7 @@ fcn unbase58(str){ // --> Data (byte bucket)
|
|||
[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
|
||||
c/256; // should be zero when done
|
||||
},n) : if(_) throw(Exception.ValueError("address too long"));
|
||||
});
|
||||
out;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue