Data update

This commit is contained in:
Ingy döt Net 2026-04-30 12:34:36 -04:00
parent 4bb20c9b71
commit cbaf4c4b64
12390 changed files with 318560 additions and 27248 deletions

View file

@ -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;

View file

@ -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;
}
}

View file

@ -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;
}

View file

@ -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.

View file

@ -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";
}

View file

@ -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

View file

@ -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]
]

View file

@ -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"
}

View file

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

View file

@ -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")});
}

View file

@ -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;