rippled
Loading...
Searching...
No Matches
base58_test.cpp
1#ifndef _MSC_VER
2
3#include <xrpl/beast/unit_test.h>
4#include <xrpl/protocol/detail/b58_utils.h>
5#include <xrpl/protocol/tokens.h>
6
7#include <boost/multiprecision/cpp_int.hpp>
8#include <boost/random.hpp>
9
10#include <array>
11#include <cstddef>
12#include <random>
13#include <span>
14#include <sstream>
15
16namespace ripple {
17namespace test {
18namespace {
19
20[[nodiscard]] inline auto
21randEngine() -> std::mt19937&
22{
23 static std::mt19937 r = [] {
25 return std::mt19937{rd()};
26 }();
27 return r;
28}
29
30constexpr int numTokenTypeIndexes = 9;
31
32[[nodiscard]] inline auto
33tokenTypeAndSize(int i) -> std::tuple<ripple::TokenType, std::size_t>
34{
35 assert(i < numTokenTypeIndexes);
36
37 switch (i)
38 {
39 using enum ripple::TokenType;
40 case 0:
41 return {None, 20};
42 case 1:
43 return {NodePublic, 32};
44 case 2:
45 return {NodePublic, 33};
46 case 3:
47 return {NodePrivate, 32};
48 case 4:
49 return {AccountID, 20};
50 case 5:
51 return {AccountPublic, 32};
52 case 6:
53 return {AccountPublic, 33};
54 case 7:
55 return {AccountSecret, 32};
56 case 8:
57 return {FamilySeed, 16};
58 default:
60 "Invalid token selection passed to tokenTypeAndSize() "
61 "in " __FILE__);
62 }
63}
64
65[[nodiscard]] inline auto
66randomTokenTypeAndSize() -> std::tuple<ripple::TokenType, std::size_t>
67{
68 using namespace ripple;
69 auto& rng = randEngine();
71 return tokenTypeAndSize(d(rng));
72}
73
74// Return the token type and subspan of `d` to use as test data.
75[[nodiscard]] inline auto
76randomB256TestData(std::span<std::uint8_t> d)
78{
79 auto& rng = randEngine();
81 auto [tokType, tokSize] = randomTokenTypeAndSize();
82 std::generate(d.begin(), d.begin() + tokSize, [&] { return dist(rng); });
83 return {tokType, d.subspan(0, tokSize)};
84}
85
86inline void
88{
89 auto asString = [](std::span<std::uint8_t> s) {
91 r.resize(s.size());
92 std::copy(s.begin(), s.end(), r.begin());
93 return r;
94 };
95 auto sa = asString(a);
96 auto sb = asString(b);
97 std::cerr << "\n\n" << sa << "\n" << sb << "\n";
98}
99
100inline void
102{
103 auto asString = [](std::span<std::uint8_t> s) -> std::string {
105 for (auto i : s)
106 {
107 sstr << std::setw(3) << int(i) << ',';
108 }
109 return sstr.str();
110 };
111 auto sa = asString(a);
112 auto sb = asString(b);
113 std::cerr << "\n\n" << sa << "\n" << sb << "\n";
114}
115
116} // namespace
117
118namespace multiprecision_utils {
119
120boost::multiprecision::checked_uint512_t
121toBoostMP(std::span<std::uint64_t> in)
122{
123 boost::multiprecision::checked_uint512_t mbp = 0;
124 for (auto i = in.rbegin(); i != in.rend(); ++i)
125 {
126 mbp <<= 64;
127 mbp += *i;
128 }
129 return mbp;
130}
131
133randomBigInt(std::uint8_t minSize = 1, std::uint8_t maxSize = 5)
134{
135 auto eng = randEngine();
136 std::uniform_int_distribution<std::uint8_t> numCoeffDist(minSize, maxSize);
138 auto const numCoeff = numCoeffDist(eng);
140 coeffs.reserve(numCoeff);
141 for (int i = 0; i < numCoeff; ++i)
142 {
143 coeffs.push_back(dist(eng));
144 }
145 return coeffs;
146}
147} // namespace multiprecision_utils
148
149class base58_test : public beast::unit_test::suite
150{
151 void
152 testMultiprecision()
153 {
154 testcase("b58_multiprecision");
155
156 using namespace boost::multiprecision;
157
158 constexpr std::size_t iters = 100000;
159 auto eng = randEngine();
162 for (int i = 0; i < iters; ++i)
163 {
164 std::uint64_t const d = dist(eng);
165 if (!d)
166 continue;
167 auto bigInt = multiprecision_utils::randomBigInt();
168 auto const boostBigInt = multiprecision_utils::toBoostMP(
169 std::span<std::uint64_t>(bigInt.data(), bigInt.size()));
170
171 auto const refDiv = boostBigInt / d;
172 auto const refMod = boostBigInt % d;
173
174 auto const mod = b58_fast::detail::inplace_bigint_div_rem(
175 std::span<uint64_t>(bigInt.data(), bigInt.size()), d);
176 auto const foundDiv = multiprecision_utils::toBoostMP(bigInt);
177 BEAST_EXPECT(refMod.convert_to<std::uint64_t>() == mod);
178 BEAST_EXPECT(foundDiv == refDiv);
179 }
180 for (int i = 0; i < iters; ++i)
181 {
182 std::uint64_t const d = dist(eng);
183 auto bigInt = multiprecision_utils::randomBigInt(/*minSize*/ 2);
184 if (bigInt[bigInt.size() - 1] ==
186 {
187 bigInt[bigInt.size() - 1] -= 1; // Prevent overflow
188 }
189 auto const boostBigInt = multiprecision_utils::toBoostMP(
190 std::span<std::uint64_t>(bigInt.data(), bigInt.size()));
191
192 auto const refAdd = boostBigInt + d;
193
194 auto const result = b58_fast::detail::inplace_bigint_add(
195 std::span<uint64_t>(bigInt.data(), bigInt.size()), d);
196 BEAST_EXPECT(result == TokenCodecErrc::success);
197 auto const foundAdd = multiprecision_utils::toBoostMP(bigInt);
198 BEAST_EXPECT(refAdd == foundAdd);
199 }
200 for (int i = 0; i < iters; ++i)
201 {
202 std::uint64_t const d = dist1(eng);
203 // Force overflow
206
207 auto const boostBigInt = multiprecision_utils::toBoostMP(
208 std::span<std::uint64_t>(bigInt.data(), bigInt.size()));
209
210 auto const refAdd = boostBigInt + d;
211
212 auto const result = b58_fast::detail::inplace_bigint_add(
213 std::span<uint64_t>(bigInt.data(), bigInt.size()), d);
214 BEAST_EXPECT(result == TokenCodecErrc::overflowAdd);
215 auto const foundAdd = multiprecision_utils::toBoostMP(bigInt);
216 BEAST_EXPECT(refAdd != foundAdd);
217 }
218 for (int i = 0; i < iters; ++i)
219 {
220 std::uint64_t const d = dist(eng);
221 auto bigInt = multiprecision_utils::randomBigInt(/* minSize */ 2);
222 // inplace mul requires the most significant coeff to be zero to
223 // hold the result.
224 bigInt[bigInt.size() - 1] = 0;
225 auto const boostBigInt = multiprecision_utils::toBoostMP(
226 std::span<std::uint64_t>(bigInt.data(), bigInt.size()));
227
228 auto const refMul = boostBigInt * d;
229
230 auto const result = b58_fast::detail::inplace_bigint_mul(
231 std::span<uint64_t>(bigInt.data(), bigInt.size()), d);
232 BEAST_EXPECT(result == TokenCodecErrc::success);
233 auto const foundMul = multiprecision_utils::toBoostMP(bigInt);
234 BEAST_EXPECT(refMul == foundMul);
235 }
236 for (int i = 0; i < iters; ++i)
237 {
238 std::uint64_t const d = dist1(eng);
239 // Force overflow
242 auto const boostBigInt = multiprecision_utils::toBoostMP(
243 std::span<std::uint64_t>(bigInt.data(), bigInt.size()));
244
245 auto const refMul = boostBigInt * d;
246
247 auto const result = b58_fast::detail::inplace_bigint_mul(
248 std::span<uint64_t>(bigInt.data(), bigInt.size()), d);
249 BEAST_EXPECT(result == TokenCodecErrc::inputTooLarge);
250 auto const foundMul = multiprecision_utils::toBoostMP(bigInt);
251 BEAST_EXPECT(refMul != foundMul);
252 }
253 }
254
255 void
256 testFastMatchesRef()
257 {
258 testcase("fast_matches_ref");
259 auto testRawEncode = [&](std::span<std::uint8_t> const& b256Data) {
260 std::array<std::uint8_t, 64> b58ResultBuf[2];
262
263 std::array<std::uint8_t, 64> b256ResultBuf[2];
265 for (int i = 0; i < 2; ++i)
266 {
267 std::span const outBuf{b58ResultBuf[i]};
268 if (i == 0)
269 {
270 auto const r = ripple::b58_fast::detail::b256_to_b58_be(
271 b256Data, outBuf);
272 BEAST_EXPECT(r);
273 b58Result[i] = r.value();
274 }
275 else
276 {
279 b256Data.data(),
280 b256Data.size(),
281 tmpBuf.data(),
282 tmpBuf.size());
283 BEAST_EXPECT(s.size());
284 b58Result[i] = outBuf.subspan(0, s.size());
285 std::copy(s.begin(), s.end(), b58Result[i].begin());
286 }
287 }
288 if (BEAST_EXPECT(b58Result[0].size() == b58Result[1].size()))
289 {
290 if (!BEAST_EXPECT(
291 memcmp(
292 b58Result[0].data(),
293 b58Result[1].data(),
294 b58Result[0].size()) == 0))
295 {
296 printAsChar(b58Result[0], b58Result[1]);
297 }
298 }
299
300 for (int i = 0; i < 2; ++i)
301 {
302 std::span const outBuf{
303 b256ResultBuf[i].data(), b256ResultBuf[i].size()};
304 if (i == 0)
305 {
306 std::string const in(
307 b58Result[i].data(),
308 b58Result[i].data() + b58Result[i].size());
309 auto const r =
310 ripple::b58_fast::detail::b58_to_b256_be(in, outBuf);
311 BEAST_EXPECT(r);
312 b256Result[i] = r.value();
313 }
314 else
315 {
316 std::string const st(
317 b58Result[i].begin(), b58Result[i].end());
318 std::string const s =
320 BEAST_EXPECT(s.size());
321 b256Result[i] = outBuf.subspan(0, s.size());
322 std::copy(s.begin(), s.end(), b256Result[i].begin());
323 }
324 }
325
326 if (BEAST_EXPECT(b256Result[0].size() == b256Result[1].size()))
327 {
328 if (!BEAST_EXPECT(
329 memcmp(
330 b256Result[0].data(),
331 b256Result[1].data(),
332 b256Result[0].size()) == 0))
333 {
334 printAsInt(b256Result[0], b256Result[1]);
335 }
336 }
337 };
338
339 auto testTokenEncode = [&](ripple::TokenType const tokType,
340 std::span<std::uint8_t> const& b256Data) {
341 std::array<std::uint8_t, 64> b58ResultBuf[2];
343
344 std::array<std::uint8_t, 64> b256ResultBuf[2];
346 for (int i = 0; i < 2; ++i)
347 {
348 std::span const outBuf{
349 b58ResultBuf[i].data(), b58ResultBuf[i].size()};
350 if (i == 0)
351 {
352 auto const r = ripple::b58_fast::encodeBase58Token(
353 tokType, b256Data, outBuf);
354 BEAST_EXPECT(r);
355 b58Result[i] = r.value();
356 }
357 else
358 {
360 tokType, b256Data.data(), b256Data.size());
361 BEAST_EXPECT(s.size());
362 b58Result[i] = outBuf.subspan(0, s.size());
363 std::copy(s.begin(), s.end(), b58Result[i].begin());
364 }
365 }
366 if (BEAST_EXPECT(b58Result[0].size() == b58Result[1].size()))
367 {
368 if (!BEAST_EXPECT(
369 memcmp(
370 b58Result[0].data(),
371 b58Result[1].data(),
372 b58Result[0].size()) == 0))
373 {
374 printAsChar(b58Result[0], b58Result[1]);
375 }
376 }
377
378 for (int i = 0; i < 2; ++i)
379 {
380 std::span const outBuf{
381 b256ResultBuf[i].data(), b256ResultBuf[i].size()};
382 if (i == 0)
383 {
384 std::string const in(
385 b58Result[i].data(),
386 b58Result[i].data() + b58Result[i].size());
387 auto const r = ripple::b58_fast::decodeBase58Token(
388 tokType, in, outBuf);
389 BEAST_EXPECT(r);
390 b256Result[i] = r.value();
391 }
392 else
393 {
394 std::string const st(
395 b58Result[i].begin(), b58Result[i].end());
396 std::string const s =
398 BEAST_EXPECT(s.size());
399 b256Result[i] = outBuf.subspan(0, s.size());
400 std::copy(s.begin(), s.end(), b256Result[i].begin());
401 }
402 }
403
404 if (BEAST_EXPECT(b256Result[0].size() == b256Result[1].size()))
405 {
406 if (!BEAST_EXPECT(
407 memcmp(
408 b256Result[0].data(),
409 b256Result[1].data(),
410 b256Result[0].size()) == 0))
411 {
412 printAsInt(b256Result[0], b256Result[1]);
413 }
414 }
415 };
416
417 auto testIt = [&](ripple::TokenType const tokType,
418 std::span<std::uint8_t> const& b256Data) {
419 testRawEncode(b256Data);
420 testTokenEncode(tokType, b256Data);
421 };
422
423 // test every token type with data where every byte is the same and the
424 // bytes range from 0-255
425 for (int i = 0; i < numTokenTypeIndexes; ++i)
426 {
428 auto const [tokType, tokSize] = tokenTypeAndSize(i);
429 for (int d = 0; d <= 255; ++d)
430 {
431 memset(b256DataBuf.data(), d, tokSize);
432 testIt(tokType, std::span(b256DataBuf.data(), tokSize));
433 }
434 }
435
436 // test with random data
437 constexpr std::size_t iters = 100000;
438 for (int i = 0; i < iters; ++i)
439 {
441 auto const [tokType, b256Data] = randomB256TestData(b256DataBuf);
442 testIt(tokType, b256Data);
443 }
444 }
445
446 void
447 run() override
448 {
449 testMultiprecision();
450 testFastMatchesRef();
451 }
452};
453
454BEAST_DEFINE_TESTSUITE(base58, basics, ripple);
455
456} // namespace test
457} // namespace ripple
458#endif // _MSC_VER
T begin(T... args)
A testsuite class.
Definition suite.h:52
T copy(T... args)
T data(T... args)
T end(T... args)
T generate(T... args)
T memcmp(T... args)
T memset(T... args)
std::string decodeBase58(std::string const &s)
Definition tokens.cpp:257
std::string encodeBase58(void const *message, std::size_t size, void *temp, std::size_t temp_size)
Definition tokens.cpp:205
std::string encodeBase58Token(TokenType type, void const *token, std::size_t size)
Definition tokens.cpp:307
std::string decodeBase58Token(std::string const &s, TokenType type)
Definition tokens.cpp:331
auto const data
General field definitions, or fields used in multiple transaction namespaces.
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:6
base_uint< 160, detail::AccountIDTag > AccountID
A 160-bit unsigned that uniquely identifies an account.
Definition AccountID.h:29
TokenType
Definition tokens.h:19
int run(int argc, char **argv)
Definition Main.cpp:330
T push_back(T... args)
T reserve(T... args)
T resize(T... args)
T setw(T... args)
T size(T... args)
T str(T... args)