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
77{
78 auto& rng = randEngine();
80 auto [tokType, tokSize] = randomTokenTypeAndSize();
81 std::generate(d.begin(), d.begin() + tokSize, [&] { return dist(rng); });
82 return {tokType, d.subspan(0, tokSize)};
83}
84
85inline void
87{
88 auto asString = [](std::span<std::uint8_t> s) {
90 r.resize(s.size());
91 std::copy(s.begin(), s.end(), r.begin());
92 return r;
93 };
94 auto sa = asString(a);
95 auto sb = asString(b);
96 std::cerr << "\n\n" << sa << "\n" << sb << "\n";
97}
98
99inline void
101{
102 auto asString = [](std::span<std::uint8_t> s) -> std::string {
104 for (auto i : s)
105 {
106 sstr << std::setw(3) << int(i) << ',';
107 }
108 return sstr.str();
109 };
110 auto sa = asString(a);
111 auto sb = asString(b);
112 std::cerr << "\n\n" << sa << "\n" << sb << "\n";
113}
114
115} // namespace
116
117namespace multiprecision_utils {
118
119boost::multiprecision::checked_uint512_t
120toBoostMP(std::span<std::uint64_t> in)
121{
122 boost::multiprecision::checked_uint512_t mbp = 0;
123 for (auto i = in.rbegin(); i != in.rend(); ++i)
124 {
125 mbp <<= 64;
126 mbp += *i;
127 }
128 return mbp;
129}
130
132randomBigInt(std::uint8_t minSize = 1, std::uint8_t maxSize = 5)
133{
134 auto eng = randEngine();
135 std::uniform_int_distribution<std::uint8_t> numCoeffDist(minSize, maxSize);
137 auto const numCoeff = numCoeffDist(eng);
139 coeffs.reserve(numCoeff);
140 for (int i = 0; i < numCoeff; ++i)
141 {
142 coeffs.push_back(dist(eng));
143 }
144 return coeffs;
145}
146} // namespace multiprecision_utils
147
148class base58_test : public beast::unit_test::suite
149{
150 void
151 testMultiprecision()
152 {
153 testcase("b58_multiprecision");
154
155 using namespace boost::multiprecision;
156
157 constexpr std::size_t iters = 100000;
158 auto eng = randEngine();
161 for (int i = 0; i < iters; ++i)
162 {
163 std::uint64_t const d = dist(eng);
164 if (!d)
165 continue;
166 auto bigInt = multiprecision_utils::randomBigInt();
167 auto const boostBigInt =
168 multiprecision_utils::toBoostMP(std::span<std::uint64_t>(bigInt.data(), bigInt.size()));
169
170 auto const refDiv = boostBigInt / d;
171 auto const refMod = boostBigInt % d;
172
173 auto const mod =
174 b58_fast::detail::inplace_bigint_div_rem(std::span<uint64_t>(bigInt.data(), bigInt.size()), d);
175 auto const foundDiv = multiprecision_utils::toBoostMP(bigInt);
176 BEAST_EXPECT(refMod.convert_to<std::uint64_t>() == mod);
177 BEAST_EXPECT(foundDiv == refDiv);
178 }
179 for (int i = 0; i < iters; ++i)
180 {
181 std::uint64_t const d = dist(eng);
182 auto bigInt = multiprecision_utils::randomBigInt(/*minSize*/ 2);
183 if (bigInt[bigInt.size() - 1] == std::numeric_limits<std::uint64_t>::max())
184 {
185 bigInt[bigInt.size() - 1] -= 1; // Prevent overflow
186 }
187 auto const boostBigInt =
188 multiprecision_utils::toBoostMP(std::span<std::uint64_t>(bigInt.data(), bigInt.size()));
189
190 auto const refAdd = boostBigInt + d;
191
192 auto const result =
193 b58_fast::detail::inplace_bigint_add(std::span<uint64_t>(bigInt.data(), bigInt.size()), d);
194 BEAST_EXPECT(result == TokenCodecErrc::success);
195 auto const foundAdd = multiprecision_utils::toBoostMP(bigInt);
196 BEAST_EXPECT(refAdd == foundAdd);
197 }
198 for (int i = 0; i < iters; ++i)
199 {
200 std::uint64_t const d = dist1(eng);
201 // Force overflow
203
204 auto const boostBigInt =
205 multiprecision_utils::toBoostMP(std::span<std::uint64_t>(bigInt.data(), bigInt.size()));
206
207 auto const refAdd = boostBigInt + d;
208
209 auto const result =
210 b58_fast::detail::inplace_bigint_add(std::span<uint64_t>(bigInt.data(), bigInt.size()), d);
211 BEAST_EXPECT(result == TokenCodecErrc::overflowAdd);
212 auto const foundAdd = multiprecision_utils::toBoostMP(bigInt);
213 BEAST_EXPECT(refAdd != foundAdd);
214 }
215 for (int i = 0; i < iters; ++i)
216 {
217 std::uint64_t const d = dist(eng);
218 auto bigInt = multiprecision_utils::randomBigInt(/* minSize */ 2);
219 // inplace mul requires the most significant coeff to be zero to
220 // hold the result.
221 bigInt[bigInt.size() - 1] = 0;
222 auto const boostBigInt =
223 multiprecision_utils::toBoostMP(std::span<std::uint64_t>(bigInt.data(), bigInt.size()));
224
225 auto const refMul = boostBigInt * d;
226
227 auto const result =
228 b58_fast::detail::inplace_bigint_mul(std::span<uint64_t>(bigInt.data(), bigInt.size()), d);
229 BEAST_EXPECT(result == TokenCodecErrc::success);
230 auto const foundMul = multiprecision_utils::toBoostMP(bigInt);
231 BEAST_EXPECT(refMul == foundMul);
232 }
233 for (int i = 0; i < iters; ++i)
234 {
235 std::uint64_t const d = dist1(eng);
236 // Force overflow
238 auto const boostBigInt =
239 multiprecision_utils::toBoostMP(std::span<std::uint64_t>(bigInt.data(), bigInt.size()));
240
241 auto const refMul = boostBigInt * d;
242
243 auto const result =
244 b58_fast::detail::inplace_bigint_mul(std::span<uint64_t>(bigInt.data(), bigInt.size()), d);
245 BEAST_EXPECT(result == TokenCodecErrc::inputTooLarge);
246 auto const foundMul = multiprecision_utils::toBoostMP(bigInt);
247 BEAST_EXPECT(refMul != foundMul);
248 }
249 }
250
251 void
252 testFastMatchesRef()
253 {
254 testcase("fast_matches_ref");
255 auto testRawEncode = [&](std::span<std::uint8_t> const& b256Data) {
256 std::array<std::uint8_t, 64> b58ResultBuf[2];
258
259 std::array<std::uint8_t, 64> b256ResultBuf[2];
261 for (int i = 0; i < 2; ++i)
262 {
263 std::span const outBuf{b58ResultBuf[i]};
264 if (i == 0)
265 {
266 auto const r = xrpl::b58_fast::detail::b256_to_b58_be(b256Data, outBuf);
267 BEAST_EXPECT(r);
268 b58Result[i] = r.value();
269 }
270 else
271 {
274 b256Data.data(), b256Data.size(), tmpBuf.data(), tmpBuf.size());
275 BEAST_EXPECT(s.size());
276 b58Result[i] = outBuf.subspan(0, s.size());
277 std::copy(s.begin(), s.end(), b58Result[i].begin());
278 }
279 }
280 if (BEAST_EXPECT(b58Result[0].size() == b58Result[1].size()))
281 {
282 if (!BEAST_EXPECT(memcmp(b58Result[0].data(), b58Result[1].data(), b58Result[0].size()) == 0))
283 {
284 printAsChar(b58Result[0], b58Result[1]);
285 }
286 }
287
288 for (int i = 0; i < 2; ++i)
289 {
290 std::span const outBuf{b256ResultBuf[i].data(), b256ResultBuf[i].size()};
291 if (i == 0)
292 {
293 std::string const in(b58Result[i].data(), b58Result[i].data() + b58Result[i].size());
294 auto const r = xrpl::b58_fast::detail::b58_to_b256_be(in, outBuf);
295 BEAST_EXPECT(r);
296 b256Result[i] = r.value();
297 }
298 else
299 {
300 std::string const st(b58Result[i].begin(), b58Result[i].end());
302 BEAST_EXPECT(s.size());
303 b256Result[i] = outBuf.subspan(0, s.size());
304 std::copy(s.begin(), s.end(), b256Result[i].begin());
305 }
306 }
307
308 if (BEAST_EXPECT(b256Result[0].size() == b256Result[1].size()))
309 {
310 if (!BEAST_EXPECT(memcmp(b256Result[0].data(), b256Result[1].data(), b256Result[0].size()) == 0))
311 {
312 printAsInt(b256Result[0], b256Result[1]);
313 }
314 }
315 };
316
317 auto testTokenEncode = [&](xrpl::TokenType const tokType, std::span<std::uint8_t> const& b256Data) {
318 std::array<std::uint8_t, 64> b58ResultBuf[2];
320
321 std::array<std::uint8_t, 64> b256ResultBuf[2];
323 for (int i = 0; i < 2; ++i)
324 {
325 std::span const outBuf{b58ResultBuf[i].data(), b58ResultBuf[i].size()};
326 if (i == 0)
327 {
328 auto const r = xrpl::b58_fast::encodeBase58Token(tokType, b256Data, outBuf);
329 BEAST_EXPECT(r);
330 b58Result[i] = r.value();
331 }
332 else
333 {
334 std::string const s = xrpl::b58_ref::encodeBase58Token(tokType, b256Data.data(), b256Data.size());
335 BEAST_EXPECT(s.size());
336 b58Result[i] = outBuf.subspan(0, s.size());
337 std::copy(s.begin(), s.end(), b58Result[i].begin());
338 }
339 }
340 if (BEAST_EXPECT(b58Result[0].size() == b58Result[1].size()))
341 {
342 if (!BEAST_EXPECT(memcmp(b58Result[0].data(), b58Result[1].data(), b58Result[0].size()) == 0))
343 {
344 printAsChar(b58Result[0], b58Result[1]);
345 }
346 }
347
348 for (int i = 0; i < 2; ++i)
349 {
350 std::span const outBuf{b256ResultBuf[i].data(), b256ResultBuf[i].size()};
351 if (i == 0)
352 {
353 std::string const in(b58Result[i].data(), b58Result[i].data() + b58Result[i].size());
354 auto const r = xrpl::b58_fast::decodeBase58Token(tokType, in, outBuf);
355 BEAST_EXPECT(r);
356 b256Result[i] = r.value();
357 }
358 else
359 {
360 std::string const st(b58Result[i].begin(), b58Result[i].end());
361 std::string const s = xrpl::b58_ref::decodeBase58Token(st, tokType);
362 BEAST_EXPECT(s.size());
363 b256Result[i] = outBuf.subspan(0, s.size());
364 std::copy(s.begin(), s.end(), b256Result[i].begin());
365 }
366 }
367
368 if (BEAST_EXPECT(b256Result[0].size() == b256Result[1].size()))
369 {
370 if (!BEAST_EXPECT(memcmp(b256Result[0].data(), b256Result[1].data(), b256Result[0].size()) == 0))
371 {
372 printAsInt(b256Result[0], b256Result[1]);
373 }
374 }
375 };
376
377 auto testIt = [&](xrpl::TokenType const tokType, std::span<std::uint8_t> const& b256Data) {
378 testRawEncode(b256Data);
379 testTokenEncode(tokType, b256Data);
380 };
381
382 // test every token type with data where every byte is the same and the
383 // bytes range from 0-255
384 for (int i = 0; i < numTokenTypeIndexes; ++i)
385 {
387 auto const [tokType, tokSize] = tokenTypeAndSize(i);
388 for (int d = 0; d <= 255; ++d)
389 {
390 memset(b256DataBuf.data(), d, tokSize);
391 testIt(tokType, std::span(b256DataBuf.data(), tokSize));
392 }
393 }
394
395 // test with random data
396 constexpr std::size_t iters = 100000;
397 for (int i = 0; i < iters; ++i)
398 {
400 auto const [tokType, b256Data] = randomB256TestData(b256DataBuf);
401 testIt(tokType, b256Data);
402 }
403 }
404
405 void
406 run() override
407 {
408 testMultiprecision();
409 testFastMatchesRef();
410 }
411};
412
413BEAST_DEFINE_TESTSUITE(base58, basics, xrpl);
414
415} // namespace test
416} // namespace xrpl
417#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:247
std::string encodeBase58(void const *message, std::size_t size, void *temp, std::size_t temp_size)
Definition tokens.cpp:200
std::string encodeBase58Token(TokenType type, void const *token, std::size_t size)
Definition tokens.cpp:295
std::string decodeBase58Token(std::string const &s, TokenType type)
Definition tokens.cpp:318
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
int run(int argc, char **argv)
Definition Main.cpp:322
base_uint< 160, detail::AccountIDTag > AccountID
A 160-bit unsigned that uniquely identifies an account.
Definition AccountID.h:29
TokenType
Definition tokens.h:19
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)