1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.commons.codec.binary;
19
20 import static org.junit.jupiter.api.Assertions.assertArrayEquals;
21 import static org.junit.jupiter.api.Assertions.assertEquals;
22 import static org.junit.jupiter.api.Assertions.assertFalse;
23 import static org.junit.jupiter.api.Assertions.assertNull;
24 import static org.junit.jupiter.api.Assertions.assertThrows;
25 import static org.junit.jupiter.api.Assertions.assertTrue;
26
27 import java.io.IOException;
28 import java.nio.ByteBuffer;
29 import java.nio.charset.Charset;
30 import java.nio.charset.StandardCharsets;
31 import java.util.Arrays;
32 import java.util.Random;
33 import java.util.concurrent.atomic.AtomicInteger;
34
35 import org.apache.commons.codec.DecoderException;
36 import org.apache.commons.codec.EncoderException;
37 import org.apache.commons.lang3.ArrayFill;
38 import org.junit.jupiter.api.Test;
39 import org.junit.jupiter.params.ParameterizedTest;
40 import org.junit.jupiter.params.provider.ValueSource;
41
42
43
44
45 public class Base58Test {
46
47 private static final int BOUND = 10_000;
48
49 private static final Charset CHARSET_UTF8 = StandardCharsets.UTF_8;
50
51 private static void assertArrayEqualsAt(final byte[] data, final byte[] dec, final int i) {
52 final AtomicInteger counter = new AtomicInteger(i);
53 assertArrayEquals(data, dec, () -> String.format("Failed for length %,d: %s", counter.get(), Arrays.toString(data)));
54 }
55
56 private final Random random = new Random();
57
58 @Test
59 void testBase58() {
60 final String content = "Hello World";
61 final byte[] encodedBytes = new Base58().encode(StringUtils.getBytesUtf8(content));
62 final String encodedContent = StringUtils.newStringUtf8(encodedBytes);
63 assertEquals("JxF12TrwUP45BMd", encodedContent, "encoding hello world");
64 final byte[] decodedBytes = new Base58().decode(encodedBytes);
65 final String decodedContent = StringUtils.newStringUtf8(decodedBytes);
66 assertEquals(content, decodedContent, "decoding hello world");
67 }
68
69 @Test
70 void testEmptyBase58() {
71 byte[] empty = {};
72 byte[] result = new Base58().encode(empty);
73 assertEquals(0, result.length, "empty Base58 encode");
74 assertNull(new Base58().encode(null), "empty Base58 encode");
75 empty = new byte[0];
76 result = new Base58().decode(empty);
77 assertEquals(0, result.length, "empty Base58 decode");
78 assertNull(new Base58().decode((byte[]) null), "empty Base58 decode");
79 }
80
81 @Test
82 void testEncodeDecode() {
83 for (int i = 1; i < 5; i++) {
84 final byte[] data = new byte[random.nextInt(BOUND) + 1];
85 Arrays.fill(data, (byte) i);
86 final byte[] enc = new Base58().encode(data);
87 final byte[] dec = new Base58().decode(enc);
88 assertArrayEqualsAt(data, dec, i);
89 }
90 }
91
92 @Test
93 void testEncodeDecodeRandom() {
94 for (int i = 1; i < 5; i++) {
95 final byte[] data = new byte[random.nextInt(BOUND) + 1];
96 random.nextBytes(data);
97 final byte[] enc = new Base58().encode(data);
98 final byte[] dec = new Base58().decode(enc);
99 assertArrayEqualsAt(data, dec, i);
100 }
101 }
102
103 @Test
104 void testEncodeDecodeSmall() {
105 for (int i = 0; i < 12; i++) {
106 final byte[] data = new byte[i];
107 Arrays.fill(data, (byte) i);
108 final byte[] enc = new Base58().encode(data);
109 final byte[] dec = new Base58().decode(enc);
110 assertArrayEqualsAt(data, dec, i);
111 }
112 }
113
114 @Test
115 void testEncodeDecodeSmallRandom() {
116 for (int i = 0; i < 12; i++) {
117 final byte[] data = new byte[i];
118 random.nextBytes(data);
119 final byte[] enc = new Base58().encode(data);
120 final byte[] dec = new Base58().decode(enc);
121 assertArrayEqualsAt(data, dec, i);
122 }
123 }
124
125 @Test
126 void testHexEncoding() {
127 final String hexString = "48656c6c6f20576f726c6421";
128 final byte[] encoded = new Base58().encode(StringUtils.getBytesUtf8(hexString));
129 final byte[] decoded = new Base58().decode(StringUtils.newStringUtf8(encoded));
130 assertEquals("5m7UdtXCfQxGvX2K9dLrkNs7AFMS98qn8", StringUtils.newStringUtf8(encoded), "Hex encoding failed");
131 assertEquals(hexString, StringUtils.newStringUtf8(decoded), "Hex decoding failed");
132 }
133
134 @Test
135 void testInvalidCharacters() {
136
137 final byte[] invalidChars = "0OIl".getBytes(CHARSET_UTF8);
138 assertThrows(IllegalArgumentException.class, () -> new Base58().decode(invalidChars));
139 }
140
141 @Test
142 void testIsInAlphabet() {
143 final Base58 base58 = new Base58();
144
145 for (char c = '1'; c <= '9'; c++) {
146 assertTrue(base58.isInAlphabet((byte) c), "char " + c);
147 }
148 for (char c = 'A'; c <= 'H'; c++) {
149 assertTrue(base58.isInAlphabet((byte) c), "char " + c);
150 }
151 for (char c = 'J'; c <= 'N'; c++) {
152 assertTrue(base58.isInAlphabet((byte) c), "char " + c);
153 }
154 for (char c = 'P'; c <= 'Z'; c++) {
155 assertTrue(base58.isInAlphabet((byte) c), "char " + c);
156 }
157 for (char c = 'a'; c <= 'k'; c++) {
158 assertTrue(base58.isInAlphabet((byte) c), "char " + c);
159 }
160 for (char c = 'm'; c <= 'z'; c++) {
161 assertTrue(base58.isInAlphabet((byte) c), "char " + c);
162 }
163
164 assertFalse(base58.isInAlphabet((byte) '0'), "char 0");
165 assertFalse(base58.isInAlphabet((byte) 'O'), "char O");
166 assertFalse(base58.isInAlphabet((byte) 'I'), "char I");
167 assertFalse(base58.isInAlphabet((byte) 'l'), "char l");
168
169 assertFalse(base58.isInAlphabet((byte) -1));
170 assertFalse(base58.isInAlphabet((byte) 0));
171 assertFalse(base58.isInAlphabet((byte) 128));
172 assertFalse(base58.isInAlphabet((byte) 255));
173 }
174
175 @Test
176 void testLeadingZeros() {
177
178 final byte[] input = { 0, 0, 1, 2, 3 };
179 final byte[] encoded = new Base58().encode(input);
180 final String encodedStr = new String(encoded);
181
182 assertTrue(encodedStr.startsWith("11"), "Leading zeros should encode as '1' characters");
183
184 final byte[] decoded = new Base58().decode(encoded);
185 assertArrayEquals(input, decoded, "Decoded should match original including leading zeros");
186 }
187
188 @Test
189 void testObjectDecodeWithInvalidParameter() {
190 assertThrows(DecoderException.class, () -> new Base58().decode(Integer.valueOf(5)));
191 }
192
193 @Test
194 void testObjectDecodeWithValidParameter() throws Exception {
195 final String original = "Hello World!";
196 final Object o = new Base58().encode(original.getBytes(CHARSET_UTF8));
197 final Base58 base58 = new Base58();
198 final Object oDecoded = base58.decode(o);
199 final byte[] baDecoded = (byte[]) oDecoded;
200 final String dest = new String(baDecoded);
201 assertEquals(original, dest, "dest string does not equal original");
202 }
203
204 @Test
205 void testObjectEncodeWithInvalidParameter() {
206 assertThrows(EncoderException.class, () -> new Base58().encode("Yadayadayada"));
207 }
208
209 @Test
210 void testObjectEncodeWithValidParameter() throws Exception {
211 final String original = "Hello World!";
212 final Object origObj = original.getBytes(CHARSET_UTF8);
213 final Object oEncoded = new Base58().encode(origObj);
214 final byte[] bArray = new Base58().decode((byte[]) oEncoded);
215 final String dest = new String(bArray);
216 assertEquals(original, dest, "dest string does not equal original");
217 }
218
219 @Test
220 void testRoundTrip() {
221 final String[] testStrings = { "", "a", "ab", "abc", "abcd", "abcde", "abcdef", "Hello World", "The quick brown fox jumps over the lazy dog",
222 "1234567890", "!@#$%^&*()" };
223 for (final String test : testStrings) {
224 final byte[] input = test.getBytes(CHARSET_UTF8);
225 final byte[] encoded = new Base58().encode(input);
226 final byte[] decoded = new Base58().decode(encoded);
227 assertArrayEquals(input, decoded, "Round trip failed for: " + test);
228 }
229 }
230
231 @ParameterizedTest
232 @ValueSource(ints = { 0, 1, 2, 3, 4 })
233 void testRoundtripByte0(final int len) throws IOException {
234
235 final byte[] zeros = new byte[len];
236 final byte[] encoded0s = ArrayFill.fill(zeros.clone(), (byte) '1');
237 assertArrayEquals(encoded0s, Base58.builder().get().encode(zeros));
238 final byte[] decoded = Base58.builder().get().decode(encoded0s);
239 assertArrayEquals(zeros, decoded, () -> String.format("zeros=%s, decoded=%s", Arrays.toString(zeros), Arrays.toString(decoded)));
240 }
241
242 @Test
243 void testSingleBytes() {
244
245 for (int i = 1; i <= 255; i++) {
246 final byte[] data = { (byte) i };
247 final byte[] enc = new Base58().encode(data);
248 final byte[] dec = new Base58().decode(enc);
249 assertArrayEquals(data, dec, "Failed for byte value: " + i);
250 }
251 }
252
253 @Test
254 void testTestVectors() {
255 final String content = "Hello World!";
256 final String content1 = "The quick brown fox jumps over the lazy dog.";
257 final long content2 = 0x0000287fb4cdL;
258 final byte[] encodedBytes = new Base58().encode(StringUtils.getBytesUtf8(content));
259 final byte[] encodedBytes1 = new Base58().encode(StringUtils.getBytesUtf8(content1));
260 final byte[] content2Bytes = ByteBuffer.allocate(8).putLong(content2).array();
261 final byte[] content2Trimmed = new byte[6];
262 System.arraycopy(content2Bytes, 2, content2Trimmed, 0, 6);
263 final byte[] encodedBytes2 = new Base58().encode(content2Trimmed);
264 final String encodedContent = StringUtils.newStringUtf8(encodedBytes);
265 final String encodedContent1 = StringUtils.newStringUtf8(encodedBytes1);
266 final String encodedContent2 = StringUtils.newStringUtf8(encodedBytes2);
267 assertEquals("2NEpo7TZRRrLZSi2U", encodedContent, "encoding hello world");
268 assertEquals("USm3fpXnKG5EUBx2ndxBDMPVciP5hGey2Jh4NDv6gmeo1LkMeiKrLJUUBk6Z", encodedContent1);
269 assertEquals("11233QC4", encodedContent2, "encoding 0x0000287fb4cd");
270 final byte[] decodedBytes = new Base58().decode(encodedBytes);
271 final byte[] decodedBytes1 = new Base58().decode(encodedBytes1);
272 final byte[] decodedBytes2 = new Base58().decode(encodedBytes2);
273 final String decodedContent = StringUtils.newStringUtf8(decodedBytes);
274 final String decodedContent1 = StringUtils.newStringUtf8(decodedBytes1);
275 assertEquals(content, decodedContent, "decoding hello world");
276 assertEquals(content1, decodedContent1);
277 assertArrayEquals(content2Trimmed, decodedBytes2, "decoding 0x0000287fb4cd");
278 }
279 }