1+ package ch .admin .bj .swiyu .didtoolbox ;
2+
3+ import java .util .Arrays ;
4+
5+ /**
6+ * Yet another Java implementation of <a href="https://bitcoinwiki.org/wiki/base58#Base58_Encode_and_Decode">base58btc</a> encoder/decoder.
7+ * <p>
8+ * Base58 is a way to encode Bitcoin addresses (or arbitrary data) as alphanumeric strings.
9+ *
10+ * <p>Note that this is different from the base58 as used by Flickr, which you may find referenced around
11+ * the Internet.
12+ *
13+ * <p>Satoshi explains: why base-58 instead of standard base-64 encoding?
14+ *
15+ * <ul>
16+ * <li>Don't want 0OIl characters that look the same in some fonts and could be used to create
17+ * visually identical looking account numbers.
18+ * <li>A string with non-alphanumeric characters is not as easily accepted as an account number.
19+ * <li>E-mail usually won't line-break if there's no punctuation to break at.
20+ * <li>Doubleclicking selects the whole number as one word if it's all alphanumeric.
21+ * </ul>
22+ *
23+ * <p>However, note that the encoding/decoding runs in O(n²) time, so it is not useful for
24+ * large data.
25+ *
26+ * <p>The basic idea of the encoding is to treat the data bytes as a large number represented using
27+ * base-256 digits, convert the number to be represented using base-58 digits, preserve the exact
28+ * number of leading zeros (which are otherwise lost during the mathematical operations on the
29+ * numbers), and finally represent the resulting base-58 digits as alphanumeric ASCII characters.
30+ */
31+ public final class Base58 {
32+
33+ public static final char [] ALPHABET =
34+ "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz" .toCharArray ();
35+ private static final char ENCODED_ZERO = ALPHABET [0 ];
36+ private static final int [] INDEXES = new int [128 ];
37+
38+ static {
39+ Arrays .fill (INDEXES , -1 );
40+ for (int i = 0 ; i < ALPHABET .length ; i ++) {
41+ INDEXES [ALPHABET [i ]] = i ;
42+ }
43+ }
44+
45+ private Base58 () {
46+ }
47+
48+ /**
49+ * Encodes the given bytes as a base58 string (no checksum is appended).
50+ *
51+ * @param input the bytes to encode
52+ * @return the base58-encoded string
53+ */
54+ public static String encode (byte [] input ) {
55+ if (input .length == 0 ) {
56+ return "" ;
57+ }
58+
59+ // Count leading zeros.
60+ int zeros = 0 ;
61+ while (zeros < input .length && input [zeros ] == 0 ) {
62+ ++zeros ;
63+ }
64+
65+ // Convert base-256 digits to base-58 digits (plus conversion to ASCII characters)
66+ var in = Arrays .copyOf (input , input .length ); // since we modify it in-place
67+ char [] encoded = new char [in .length * 2 ]; // upper bound
68+ int outputStart = encoded .length ;
69+ for (int inputStart = zeros ; inputStart < in .length ; ) {
70+ encoded [--outputStart ] = ALPHABET [divmod (in , inputStart , 256 , 58 )];
71+ if (in [inputStart ] == 0 ) {
72+ ++inputStart ; // optimization - skip leading zeros
73+ }
74+ }
75+
76+ // Preserve exactly as many leading encoded zeros in output as there were leading zeros in input.
77+ while (outputStart < encoded .length && encoded [outputStart ] == ENCODED_ZERO ) {
78+ ++outputStart ;
79+ }
80+
81+ while (--zeros >= 0 ) {
82+ encoded [--outputStart ] = ENCODED_ZERO ;
83+ }
84+
85+ // Return encoded string (including encoded leading zeros).
86+ return new String (encoded , outputStart , encoded .length - outputStart );
87+ }
88+
89+ /**
90+ * Decodes the given base58 string into the original data bytes.
91+ *
92+ * @param input the base58-encoded string to decode
93+ * @return the decoded data bytes
94+ */
95+ @ SuppressWarnings ({"PMD.CyclomaticComplexity" })
96+ public static byte [] decode (String input ) {
97+ if (input .isEmpty ()) {
98+ return new byte [0 ];
99+ }
100+
101+ // Convert the base58-encoded ASCII chars to a base58 byte sequence (base58 digits).
102+ byte [] input58 = new byte [input .length ()];
103+ for (int i = 0 ; i < input .length (); ++i ) {
104+ char c = input .charAt (i );
105+ int digit = c < 128 ? INDEXES [c ] : -1 ;
106+ if (digit < 0 ) {
107+ throw new IllegalArgumentException (
108+ String .format ("Invalid character in Base58: 0x%04x" , (int ) c ));
109+ }
110+ input58 [i ] = (byte ) digit ;
111+ }
112+
113+ // Count leading zeros.
114+ int zeros = 0 ;
115+ while (zeros < input58 .length && input58 [zeros ] == 0 ) {
116+ ++zeros ;
117+ }
118+
119+ // Convert base-58 digits to base-256 digits.
120+ byte [] decoded = new byte [input .length ()];
121+ int outputStart = decoded .length ;
122+ for (int inputStart = zeros ; inputStart < input58 .length ; ) {
123+ decoded [--outputStart ] = divmod (input58 , inputStart , 58 , 256 );
124+ if (input58 [inputStart ] == 0 ) {
125+ ++inputStart ; // optimization - skip leading zeros
126+ }
127+ }
128+
129+ // Ignore extra leading zeroes that were added during the calculation.
130+ while (outputStart < decoded .length && decoded [outputStart ] == 0 ) {
131+ ++outputStart ;
132+ }
133+
134+ // Return decoded data (including original number of leading zeros).
135+ return Arrays .copyOfRange (decoded , outputStart - zeros , decoded .length );
136+ }
137+
138+ /**
139+ * Divides a number, represented as an array of bytes each containing a single digit in the
140+ * specified base, by the given divisor. The given number is modified in-place to contain the
141+ * quotient, and the return value is the remainder.
142+ *
143+ * @param number the number to divide
144+ * @param firstDigit the index within the array of the first non-zero digit (this is used for
145+ * optimization by skipping the leading zeros)
146+ * @param base the base in which the number's digits are represented (up to 256)
147+ * @param divisor the number to divide by (up to 256)
148+ * @return the remainder of the division operation
149+ */
150+ private static byte divmod (byte [] number , int firstDigit , int base , int divisor ) {
151+ // this is just long division which accounts for the base of the input digits
152+ int remainder = 0 ;
153+ for (int i = firstDigit ; i < number .length ; i ++) {
154+ int digit = (int ) number [i ] & 0xFF ;
155+ int temp = remainder * base + digit ;
156+ number [i ] = (byte ) (temp / divisor );
157+ remainder = temp % divisor ;
158+ }
159+ return (byte ) remainder ;
160+ }
161+ }
0 commit comments