Base58Check encoding
The popular encoding of small and medium-sized checksums is base16, that is more compact than usual base10 and is human readable... For checksums resulting in hash digests bigger than ~100 bits, the base16 is too long: base58 is shorter and (when using good alphabet) preserves secure human readability. The most popular alphabet of base58 is the variant used in bitcoin address (see Bitcoin/address validation), so it is the "default base58 alphabet".
Write a program that takes a checksum (resultant hash digest) integer binary representation as argument, and converts (encode it) into base58 with the standard Bitcoin alphabet — which uses an alphabet of the characters 0 .. 9, A ..Z, a .. z, but without the four characters 0, O, I and l.
The reference algorithm is at the Bitcoin's Base58Check page.
Haskell
<lang haskell>import Numeric (showIntAtBase)
chars = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
base58Encode :: Integer -> String base58Encode n = showIntAtBase 58 (chars !!) n ""
main :: IO () main = mapM_ (putStrLn . base58Encode)
[25420294593250030202636073700053352635053786165627414518, 0x61, 0x626262, 0x636363, 0x73696d706c792061206c6f6e6720737472696e67, 0x516b6fcd0f, 0xbf4f89001e670274dd, 0x572e4794, 0xecac89cad93923c02321, 0x10c8511e]</lang>
- Output:
6UwLL9Risc3QfPqBUvKofHmBQ7wMtjvM 2g a3gV aPEr 2cFupjhnEsSn59qHXstmK2ffpLv2 ABnLTmg 3SEo3LWLoPntC 3EFU7m EJDM8drfXA6uyA Rt5zm
Perl 6
<lang perl6>sub encode_Base58 ( Int $x ) {
constant @codes = < 1 2 3 4 5 6 7 8 9 A B C D E F G H J K L M N P Q R S T U V W X Y Z a b c d e f g h i j k m n o p q r s t u v w x y z >;
return @codes[ $x.polymod( 58 xx * ) ].join.flip;
}
my @tests =
25420294593250030202636073700053352635053786165627414518 => '6UwLL9Risc3QfPqBUvKofHmBQ7wMtjvM', 0x61 => '2g', 0x626262 => 'a3gV', 0x636363 => 'aPEr', 0x73696d706c792061206c6f6e6720737472696e67 => '2cFupjhnEsSn59qHXstmK2ffpLv2', 0x516b6fcd0f => 'ABnLTmg', 0xbf4f89001e670274dd => '3SEo3LWLoPntC', 0x572e4794 => '3EFU7m', 0xecac89cad93923c02321 => 'EJDM8drfXA6uyA', 0x10c8511e => 'Rt5zm',
use Test; for @tests {
is encode_Base58(.key), .value, "{.key} encodes to {.value}";
} </lang>
REXX
version 1
Following the description in https://www.anintegratedworld.com/how-to-manually-calculate-base58check-encoding/ I get the result expected there. Apart for the leading 1 the program works also for the inputs shown above. <lang rexx>/* REXX */ s="123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz" Numeric Digits 100 k='00010966776006953D5567439E5E39F86A0D273BEED61967F6'x n=c2d(k) o= Do Until n=0
rem=n//58 n=n%58 o=o||substr(s,rem+1,1) End
o=o||substr(s,1,1) Say reverse(o)</lang>
- Output:
16UwLL9Risc3QfPqBUvKofHmBQ7wMtjvM
version 2
does what the others do <lang>/* REXX */ s="123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz" Numeric Digits 1000 cnt_ok=0 Call test 'N',25420294593250030202636073700053352635053786165627414518,,
'6UwLL9Risc3QfPqBUvKofHmBQ7wMtjvM'
Call test 'X','61'x ,'2g' Call test 'X','626262'x ,'a3gV' Call test 'X','636363'x ,'aPEr' Call test 'X','73696d706c792061206c6f6e6720737472696e67'x,,
'2cFupjhnEsSn59qHXstmK2ffpLv2'
Call test 'X','516b6fcd0f'x ,'ABnLTmg' Call test 'X','bf4f89001e670274dd'x ,'3SEo3LWLoPntC' Call test 'X','572e4794'x ,'3EFU7m' Call test 'X','ecac89cad93923c02321'x ,'EJDM8drfXA6uyA' Call test 'X','10c8511e'x ,'Rt5zm' Call test 'X','10c8511e'x ,'check_error_handlimng' Say cnt_ok 'tests ok' Exit test:
Parse Arg how,k,res If how='X' Then k=c2d(k) o= Do Until k=0 rem=k//58 k=k%58 o=o||substr(s,rem+1,1) End o=reverse(o) If o=res Then cnt_ok+=1 Else Do Say 'expected:' res Say 'found :' o End Return</lang>
- Output:
expected: check_error_handlimng found : Rt5zm 10 tests ok