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 xrpl {
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<xrpl::TokenType, std::size_t>
34{
35 assert(i < numTokenTypeIndexes);
36
37 switch (i)
38 {
39 using enum xrpl::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<xrpl::TokenType, std::size_t>
67{
68 using namespace xrpl;
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 == 0u)
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] == std::numeric_limits<std::uint64_t>::max())
185 {
186 bigInt[bigInt.size() - 1] -= 1; // Prevent overflow
187 }
188 auto const boostBigInt = multiprecision_utils::toBoostMP(
189 std::span<std::uint64_t>(bigInt.data(), bigInt.size()));
190
191 auto const refAdd = boostBigInt + d;
192
193 auto const result = b58_fast::detail::inplace_bigint_add(
194 std::span<uint64_t>(bigInt.data(), bigInt.size()), d);
195 BEAST_EXPECT(result == TokenCodecErrc::success);
196 auto const foundAdd = multiprecision_utils::toBoostMP(bigInt);
197 BEAST_EXPECT(refAdd == foundAdd);
198 }
199 for (int i = 0; i < iters; ++i)
200 {
201 std::uint64_t const d = dist1(eng);
202 // Force overflow
204
205 auto const boostBigInt = multiprecision_utils::toBoostMP(
206 std::span<std::uint64_t>(bigInt.data(), bigInt.size()));
207
208 auto const refAdd = boostBigInt + d;
209
210 auto const result = b58_fast::detail::inplace_bigint_add(
211 std::span<uint64_t>(bigInt.data(), bigInt.size()), d);
212 BEAST_EXPECT(result == TokenCodecErrc::overflowAdd);
213 auto const foundAdd = multiprecision_utils::toBoostMP(bigInt);
214 BEAST_EXPECT(refAdd != foundAdd);
215 }
216 for (int i = 0; i < iters; ++i)
217 {
218 std::uint64_t const d = dist(eng);
219 auto bigInt = multiprecision_utils::randomBigInt(/* minSize */ 2);
220 // inplace mul requires the most significant coeff to be zero to
221 // hold the result.
222 bigInt[bigInt.size() - 1] = 0;
223 auto const boostBigInt = multiprecision_utils::toBoostMP(
224 std::span<std::uint64_t>(bigInt.data(), bigInt.size()));
225
226 auto const refMul = boostBigInt * d;
227
228 auto const result = b58_fast::detail::inplace_bigint_mul(
229 std::span<uint64_t>(bigInt.data(), bigInt.size()), d);
230 BEAST_EXPECT(result == TokenCodecErrc::success);
231 auto const foundMul = multiprecision_utils::toBoostMP(bigInt);
232 BEAST_EXPECT(refMul == foundMul);
233 }
234 for (int i = 0; i < iters; ++i)
235 {
236 std::uint64_t const d = dist1(eng);
237 // Force overflow
239 auto const boostBigInt = multiprecision_utils::toBoostMP(
240 std::span<std::uint64_t>(bigInt.data(), bigInt.size()));
241
242 auto const refMul = boostBigInt * d;
243
244 auto const result = b58_fast::detail::inplace_bigint_mul(
245 std::span<uint64_t>(bigInt.data(), bigInt.size()), d);
246 BEAST_EXPECT(result == TokenCodecErrc::inputTooLarge);
247 auto const foundMul = multiprecision_utils::toBoostMP(bigInt);
248 BEAST_EXPECT(refMul != foundMul);
249 }
250 }
251
252 void
253 testFastMatchesRef()
254 {
255 testcase("fast_matches_ref");
256 auto testRawEncode = [&](std::span<std::uint8_t> const& b256Data) {
257 std::array<std::uint8_t, 64> b58ResultBuf[2];
259
260 std::array<std::uint8_t, 64> b256ResultBuf[2];
262 for (int i = 0; i < 2; ++i)
263 {
264 std::span const outBuf{b58ResultBuf[i]};
265 if (i == 0)
266 {
267 auto const r = xrpl::b58_fast::detail::b256_to_b58_be(b256Data, outBuf);
268 BEAST_EXPECT(r);
269 b58Result[i] = r.value();
270 }
271 else
272 {
275 b256Data.data(), b256Data.size(), tmpBuf.data(), tmpBuf.size());
276 BEAST_EXPECT(s.size());
277 b58Result[i] = outBuf.subspan(0, s.size());
278 std::copy(s.begin(), s.end(), b58Result[i].begin());
279 }
280 }
281 if (BEAST_EXPECT(b58Result[0].size() == b58Result[1].size()))
282 {
283 if (!BEAST_EXPECT(
284 memcmp(b58Result[0].data(), b58Result[1].data(), b58Result[0].size()) == 0))
285 {
286 printAsChar(b58Result[0], b58Result[1]);
287 }
288 }
289
290 for (int i = 0; i < 2; ++i)
291 {
292 std::span const outBuf{b256ResultBuf[i].data(), b256ResultBuf[i].size()};
293 if (i == 0)
294 {
295 std::string const in(
296 b58Result[i].data(), b58Result[i].data() + b58Result[i].size());
297 auto const r = xrpl::b58_fast::detail::b58_to_b256_be(in, outBuf);
298 BEAST_EXPECT(r);
299 b256Result[i] = r.value();
300 }
301 else
302 {
303 std::string const st(b58Result[i].begin(), b58Result[i].end());
305 BEAST_EXPECT(s.size());
306 b256Result[i] = outBuf.subspan(0, s.size());
307 std::copy(s.begin(), s.end(), b256Result[i].begin());
308 }
309 }
310
311 if (BEAST_EXPECT(b256Result[0].size() == b256Result[1].size()))
312 {
313 if (!BEAST_EXPECT(
314 memcmp(b256Result[0].data(), b256Result[1].data(), b256Result[0].size()) ==
315 0))
316 {
317 printAsInt(b256Result[0], b256Result[1]);
318 }
319 }
320 };
321
322 auto testTokenEncode = [&](xrpl::TokenType const tokType,
323 std::span<std::uint8_t> const& b256Data) {
324 std::array<std::uint8_t, 64> b58ResultBuf[2];
326
327 std::array<std::uint8_t, 64> b256ResultBuf[2];
329 for (int i = 0; i < 2; ++i)
330 {
331 std::span const outBuf{b58ResultBuf[i].data(), b58ResultBuf[i].size()};
332 if (i == 0)
333 {
334 auto const r = xrpl::b58_fast::encodeBase58Token(tokType, b256Data, outBuf);
335 BEAST_EXPECT(r);
336 b58Result[i] = r.value();
337 }
338 else
339 {
340 std::string const s =
341 xrpl::b58_ref::encodeBase58Token(tokType, b256Data.data(), b256Data.size());
342 BEAST_EXPECT(s.size());
343 b58Result[i] = outBuf.subspan(0, s.size());
344 std::copy(s.begin(), s.end(), b58Result[i].begin());
345 }
346 }
347 if (BEAST_EXPECT(b58Result[0].size() == b58Result[1].size()))
348 {
349 if (!BEAST_EXPECT(
350 memcmp(b58Result[0].data(), b58Result[1].data(), b58Result[0].size()) == 0))
351 {
352 printAsChar(b58Result[0], b58Result[1]);
353 }
354 }
355
356 for (int i = 0; i < 2; ++i)
357 {
358 std::span const outBuf{b256ResultBuf[i].data(), b256ResultBuf[i].size()};
359 if (i == 0)
360 {
361 std::string const in(
362 b58Result[i].data(), b58Result[i].data() + b58Result[i].size());
363 auto const r = xrpl::b58_fast::decodeBase58Token(tokType, in, outBuf);
364 BEAST_EXPECT(r);
365 b256Result[i] = r.value();
366 }
367 else
368 {
369 std::string const st(b58Result[i].begin(), b58Result[i].end());
370 std::string const s = xrpl::b58_ref::decodeBase58Token(st, tokType);
371 BEAST_EXPECT(s.size());
372 b256Result[i] = outBuf.subspan(0, s.size());
373 std::copy(s.begin(), s.end(), b256Result[i].begin());
374 }
375 }
376
377 if (BEAST_EXPECT(b256Result[0].size() == b256Result[1].size()))
378 {
379 if (!BEAST_EXPECT(
380 memcmp(b256Result[0].data(), b256Result[1].data(), b256Result[0].size()) ==
381 0))
382 {
383 printAsInt(b256Result[0], b256Result[1]);
384 }
385 }
386 };
387
388 auto testIt = [&](xrpl::TokenType const tokType, std::span<std::uint8_t> const& b256Data) {
389 testRawEncode(b256Data);
390 testTokenEncode(tokType, b256Data);
391 };
392
393 // test every token type with data where every byte is the same and the
394 // bytes range from 0-255
395 for (int i = 0; i < numTokenTypeIndexes; ++i)
396 {
397 std::array<std::uint8_t, 128> b256DataBuf{};
398 auto const [tokType, tokSize] = tokenTypeAndSize(i);
399 for (int d = 0; d <= 255; ++d)
400 {
401 memset(b256DataBuf.data(), d, tokSize);
402 testIt(tokType, std::span(b256DataBuf.data(), tokSize));
403 }
404 }
405
406 // test with random data
407 constexpr std::size_t iters = 100000;
408 for (int i = 0; i < iters; ++i)
409 {
410 std::array<std::uint8_t, 128> b256DataBuf{};
411 auto const [tokType, b256Data] = randomB256TestData(b256DataBuf);
412 testIt(tokType, b256Data);
413 }
414 }
415
416 void
417 run() override
418 {
419 testMultiprecision();
420 testFastMatchesRef();
421 }
422};
423
424BEAST_DEFINE_TESTSUITE(base58, basics, xrpl);
425
426} // namespace test
427} // namespace xrpl
428#endif // _MSC_VER
T begin(T... args)
A testsuite class.
Definition suite.h:51
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:248
std::string encodeBase58(void const *message, std::size_t size, void *temp, std::size_t temp_size)
Definition tokens.cpp:201
std::string encodeBase58Token(TokenType type, void const *token, std::size_t size)
Definition tokens.cpp:296
std::string decodeBase58Token(std::string const &s, TokenType type)
Definition tokens.cpp:319
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:5
int run(int argc, char **argv)
Definition Main.cpp:332
base_uint< 160, detail::AccountIDTag > AccountID
A 160-bit unsigned that uniquely identifies an account.
Definition AccountID.h:28
TokenType
Definition tokens.h:18
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)