1#include <xrpl/protocol/detail/token_errors.h>
3#include <boost/multiprecision/cpp_int.hpp>
5#include <gtest/gtest.h>
21#include <xrpl/protocol/detail/b58_utils.h>
22#include <xrpl/protocol/tokens.h>
33[[nodiscard]]
inline auto
34randEngine() -> std::mt19937&
36 static std::mt19937 kR = [] {
37 std::random_device rd;
38 return std::mt19937{rd()};
43constexpr int kNumTokenTypeIndexes = 9;
45[[nodiscard]]
inline auto
46tokenTypeAndSize(
int i) -> std::tuple<xrpl::TokenType, std::size_t>
48 assert(i < kNumTokenTypeIndexes);
72 throw std::invalid_argument(
73 "Invalid token selection passed to tokenTypeAndSize() "
78[[nodiscard]]
inline auto
79randomTokenTypeAndSize() -> std::tuple<xrpl::TokenType, std::size_t>
82 auto& rng = randEngine();
83 std::uniform_int_distribution<> d(0, 8);
84 return tokenTypeAndSize(d(rng));
88[[nodiscard]]
inline auto
89randomB256TestData(std::span<std::uint8_t> d)
90 -> std::tuple<xrpl::TokenType, std::span<std::uint8_t>>
92 auto& rng = randEngine();
93 std::uniform_int_distribution<std::uint8_t> dist(0, 255);
94 auto [tokType, tokSize] = randomTokenTypeAndSize();
95 std::generate(d.begin(), d.begin() + tokSize, [&] { return dist(rng); });
96 return {tokType, d.subspan(0, tokSize)};
100printAsChar(std::span<std::uint8_t> a, std::span<std::uint8_t> b)
102 auto asString = [](std::span<std::uint8_t> s) {
108 auto sa = asString(a);
109 auto sb = asString(b);
110 std::cerr <<
"\n\n" << sa <<
"\n" << sb <<
"\n";
114printAsInt(std::span<std::uint8_t> a, std::span<std::uint8_t> b)
116 auto asString = [](std::span<std::uint8_t> s) -> std::string {
117 std::stringstream sstr;
124 auto sa = asString(a);
125 auto sb = asString(b);
126 std::cerr <<
"\n\n" << sa <<
"\n" << sb <<
"\n";
131namespace multiprecision_utils {
133boost::multiprecision::checked_uint512_t
134toBoostMP(std::span<std::uint64_t> in)
136 boost::multiprecision::checked_uint512_t mbp = 0;
137 for (
auto const& i : std::views::reverse(in))
145std::vector<std::uint64_t>
146randomBigInt(std::uint8_t minSize = 1, std::uint8_t maxSize = 5)
148 auto eng = randEngine();
149 std::uniform_int_distribution<std::uint8_t> numCoeffDist(minSize, maxSize);
150 std::uniform_int_distribution<std::uint64_t> dist;
151 auto const numCoeff = numCoeffDist(eng);
152 std::vector<std::uint64_t> coeffs;
154 for (
auto i = 0uz; i < numCoeff; ++i)
162TEST(Base58Test, multiprecision)
164 using namespace boost::multiprecision;
166 constexpr std::size_t kIters = 100000;
167 auto eng = randEngine();
168 std::uniform_int_distribution<std::uint64_t> dist;
169 std::uniform_int_distribution<std::uint64_t> dist1(1);
170 for (
auto i = 0uz; i < kIters; ++i)
172 std::uint64_t
const d = dist(eng);
175 auto bigInt = multiprecision_utils::randomBigInt();
176 auto const boostBigInt =
177 multiprecision_utils::toBoostMP(std::span<std::uint64_t>(bigInt.data(), bigInt.size()));
179 auto const refDiv = boostBigInt / d;
180 auto const refMod = boostBigInt % d;
182 auto const mod = b58_fast::detail::inplaceBigintDivRem(
183 std::span<uint64_t>(bigInt.data(), bigInt.size()), d);
184 auto const foundDiv = multiprecision_utils::toBoostMP(bigInt);
185 EXPECT_EQ(refMod.convert_to<std::uint64_t>(), mod);
186 EXPECT_EQ(foundDiv, refDiv);
188 for (
auto i = 0uz; i < kIters; ++i)
190 std::uint64_t
const d = dist(eng);
191 auto bigInt = multiprecision_utils::randomBigInt( 2);
194 bigInt[bigInt.size() - 1] -= 1;
196 auto const boostBigInt =
197 multiprecision_utils::toBoostMP(std::span<std::uint64_t>(bigInt.data(), bigInt.size()));
199 auto const refAdd = boostBigInt + d;
201 auto const result = b58_fast::detail::inplaceBigintAdd(
202 std::span<uint64_t>(bigInt.data(), bigInt.size()), d);
204 auto const foundAdd = multiprecision_utils::toBoostMP(bigInt);
205 EXPECT_EQ(refAdd, foundAdd);
207 for (
auto i = 0uz; i < kIters; ++i)
209 std::uint64_t
const d = dist1(eng);
213 auto const boostBigInt =
214 multiprecision_utils::toBoostMP(std::span<std::uint64_t>(bigInt.data(), bigInt.size()));
216 auto const refAdd = boostBigInt + d;
218 auto const result = b58_fast::detail::inplaceBigintAdd(
219 std::span<uint64_t>(bigInt.data(), bigInt.size()), d);
221 auto const foundAdd = multiprecision_utils::toBoostMP(bigInt);
222 EXPECT_NE(refAdd, foundAdd);
224 for (
auto i = 0uz; i < kIters; ++i)
226 std::uint64_t
const d = dist(eng);
227 auto bigInt = multiprecision_utils::randomBigInt( 2);
230 bigInt[bigInt.size() - 1] = 0;
231 auto const boostBigInt =
232 multiprecision_utils::toBoostMP(std::span<std::uint64_t>(bigInt.data(), bigInt.size()));
234 auto const refMul = boostBigInt * d;
236 auto const result = b58_fast::detail::inplaceBigintMul(
237 std::span<uint64_t>(bigInt.data(), bigInt.size()), d);
239 auto const foundMul = multiprecision_utils::toBoostMP(bigInt);
240 EXPECT_EQ(refMul, foundMul);
242 for (
auto i = 0uz; i < kIters; ++i)
244 std::uint64_t
const d = dist1(eng);
247 auto const boostBigInt =
248 multiprecision_utils::toBoostMP(std::span<std::uint64_t>(bigInt.data(), bigInt.size()));
250 auto const refMul = boostBigInt * d;
252 auto const result = b58_fast::detail::inplaceBigintMul(
253 std::span<uint64_t>(bigInt.data(), bigInt.size()), d);
255 auto const foundMul = multiprecision_utils::toBoostMP(bigInt);
256 EXPECT_NE(refMul, foundMul);
260TEST(Base58Test, fast_matches_ref)
262 auto testRawEncode = [&](std::span<std::uint8_t>
const& b256Data) {
263 std::array<std::uint8_t, 64> b58ResultBuf[2];
264 std::array<std::span<std::uint8_t>, 2> b58Result;
266 std::array<std::uint8_t, 64> b256ResultBuf[2];
267 std::array<std::span<std::uint8_t>, 2> b256Result;
268 for (
auto i = 0uz; i < 2; ++i)
270 std::span
const outBuf{b58ResultBuf[i]};
273 auto const r = xrpl::b58_fast::detail::b256ToB58Be(b256Data, outBuf);
275 b58Result[i] = r.value();
279 std::array<std::uint8_t, 128> tmpBuf{};
281 b256Data.data(), b256Data.size(), tmpBuf.
data(), tmpBuf.
size());
282 EXPECT_TRUE(s.
size());
287 auto const rawB58SameSize = b58Result[0].
size() == b58Result[1].
size();
288 EXPECT_TRUE(rawB58SameSize);
291 auto const rawB58SameData =
293 EXPECT_TRUE(rawB58SameData);
296 printAsChar(b58Result[0], b58Result[1]);
300 for (
auto i = 0uz; i < 2; ++i)
302 std::span
const outBuf{b256ResultBuf[i].
data(), b256ResultBuf[i].
size()};
305 std::string
const in(
306 b58Result[i].
data(), b58Result[i].
data() + b58Result[i].
size());
307 auto const r = xrpl::b58_fast::detail::b58ToB256Be(in, outBuf);
309 b256Result[i] = r.value();
313 std::string
const st(b58Result[i].
begin(), b58Result[i].
end());
315 EXPECT_TRUE(s.
size());
321 auto const rawB256SameSize = b256Result[0].
size() == b256Result[1].
size();
322 EXPECT_TRUE(rawB256SameSize);
325 auto const rawB256SameData =
327 EXPECT_TRUE(rawB256SameData);
328 if (!rawB256SameData)
330 printAsInt(b256Result[0], b256Result[1]);
336 std::span<std::uint8_t>
const& b256Data) {
337 std::array<std::uint8_t, 64> b58ResultBuf[2];
338 std::array<std::span<std::uint8_t>, 2> b58Result;
340 std::array<std::uint8_t, 64> b256ResultBuf[2];
341 std::array<std::span<std::uint8_t>, 2> b256Result;
342 for (
auto i = 0uz; i < 2; ++i)
344 std::span
const outBuf{b58ResultBuf[i].
data(), b58ResultBuf[i].
size()};
347 auto const r = xrpl::b58_fast::encodeBase58Token(tokType, b256Data, outBuf);
349 b58Result[i] = r.value();
353 std::string
const s =
355 EXPECT_TRUE(s.
size());
360 auto const tokenB58SameSize = b58Result[0].
size() == b58Result[1].
size();
361 EXPECT_TRUE(tokenB58SameSize);
362 if (tokenB58SameSize)
364 auto const tokenB58SameData =
366 EXPECT_TRUE(tokenB58SameData);
367 if (!tokenB58SameData)
369 printAsChar(b58Result[0], b58Result[1]);
373 for (
auto i = 0uz; i < 2; ++i)
375 std::span
const outBuf{b256ResultBuf[i].
data(), b256ResultBuf[i].
size()};
378 std::string
const in(
379 b58Result[i].
data(), b58Result[i].
data() + b58Result[i].
size());
380 auto const r = xrpl::b58_fast::decodeBase58Token(tokType, in, outBuf);
382 b256Result[i] = r.value();
386 std::string
const st(b58Result[i].
begin(), b58Result[i].
end());
388 EXPECT_TRUE(s.
size());
394 auto const tokenB256SameSize = b256Result[0].
size() == b256Result[1].
size();
395 EXPECT_TRUE(tokenB256SameSize);
396 if (tokenB256SameSize)
398 auto const tokenB256SameData =
400 EXPECT_TRUE(tokenB256SameData);
401 if (!tokenB256SameData)
403 printAsInt(b256Result[0], b256Result[1]);
408 auto testIt = [&](
xrpl::TokenType const tokType, std::span<std::uint8_t>
const& b256Data) {
409 testRawEncode(b256Data);
410 testTokenEncode(tokType, b256Data);
415 for (
int i = 0; i < kNumTokenTypeIndexes; ++i)
417 std::array<std::uint8_t, 128> b256DataBuf{};
418 auto const [tokType, tokSize] = tokenTypeAndSize(i);
419 for (
int d = 0; d <= 255; ++d)
422 testIt(tokType, std::span(b256DataBuf.
data(), tokSize));
427 constexpr std::size_t kIters = 100000;
428 for (
auto i = 0uz; i < kIters; ++i)
430 std::array<std::uint8_t, 128> b256DataBuf{};
431 auto const [tokType, b256Data] = randomB256TestData(b256DataBuf);
432 testIt(tokType, b256Data);
std::string decodeBase58(std::string const &s)
std::string encodeBase58(void const *message, std::size_t size, void *temp, std::size_t tempSize)
std::string encodeBase58Token(TokenType type, void const *token, std::size_t size)
std::string decodeBase58Token(std::string const &s, TokenType type)
BaseUInt< 160, detail::AccountIDTag > AccountID
A 160-bit unsigned that uniquely identifies an account.