1#include <xrpl/beast/unit_test/suite.h>
2#include <xrpl/protocol/detail/token_errors.h>
4#include <boost/multiprecision/cpp_int.hpp>
19#include <xrpl/protocol/detail/b58_utils.h>
20#include <xrpl/protocol/tokens.h>
31[[nodiscard]]
inline auto
32randEngine() -> std::mt19937&
34 static std::mt19937 kR = [] {
35 std::random_device rd;
36 return std::mt19937{rd()};
41constexpr int kNumTokenTypeIndexes = 9;
43[[nodiscard]]
inline auto
44tokenTypeAndSize(
int i) -> std::tuple<xrpl::TokenType, std::size_t>
46 assert(i < kNumTokenTypeIndexes);
70 throw std::invalid_argument(
71 "Invalid token selection passed to tokenTypeAndSize() "
76[[nodiscard]]
inline auto
77randomTokenTypeAndSize() -> std::tuple<xrpl::TokenType, std::size_t>
80 auto& rng = randEngine();
81 std::uniform_int_distribution<> d(0, 8);
82 return tokenTypeAndSize(d(rng));
86[[nodiscard]]
inline auto
87randomB256TestData(std::span<std::uint8_t> d)
88 -> std::tuple<xrpl::TokenType, std::span<std::uint8_t>>
90 auto& rng = randEngine();
91 std::uniform_int_distribution<std::uint8_t> dist(0, 255);
92 auto [tokType, tokSize] = randomTokenTypeAndSize();
93 std::generate(d.begin(), d.begin() + tokSize, [&] { return dist(rng); });
94 return {tokType, d.subspan(0, tokSize)};
98printAsChar(std::span<std::uint8_t> a, std::span<std::uint8_t> b)
100 auto asString = [](std::span<std::uint8_t> s) {
106 auto sa = asString(a);
107 auto sb = asString(b);
108 std::cerr <<
"\n\n" << sa <<
"\n" << sb <<
"\n";
112printAsInt(std::span<std::uint8_t> a, std::span<std::uint8_t> b)
114 auto asString = [](std::span<std::uint8_t> s) -> std::string {
115 std::stringstream sstr;
122 auto sa = asString(a);
123 auto sb = asString(b);
124 std::cerr <<
"\n\n" << sa <<
"\n" << sb <<
"\n";
129namespace multiprecision_utils {
131boost::multiprecision::checked_uint512_t
132toBoostMP(std::span<std::uint64_t> in)
134 boost::multiprecision::checked_uint512_t mbp = 0;
135 for (
auto i = in.
rbegin(); i != in.
rend(); ++i)
143std::vector<std::uint64_t>
144randomBigInt(std::uint8_t minSize = 1, std::uint8_t maxSize = 5)
146 auto eng = randEngine();
147 std::uniform_int_distribution<std::uint8_t> numCoeffDist(minSize, maxSize);
148 std::uniform_int_distribution<std::uint64_t> dist;
149 auto const numCoeff = numCoeffDist(eng);
150 std::vector<std::uint64_t> coeffs;
152 for (
int i = 0; i < numCoeff; ++i)
160class base58_test :
public beast::unit_test::Suite
165 testcase(
"b58_multiprecision");
167 using namespace boost::multiprecision;
169 static constexpr std::size_t kIters = 100000;
170 auto eng = randEngine();
171 std::uniform_int_distribution<std::uint64_t> dist;
172 std::uniform_int_distribution<std::uint64_t> dist1(1);
173 for (
int i = 0; i < kIters; ++i)
175 std::uint64_t
const d = dist(eng);
178 auto bigInt = multiprecision_utils::randomBigInt();
179 auto const boostBigInt = multiprecision_utils::toBoostMP(
180 std::span<std::uint64_t>(bigInt.data(), bigInt.size()));
182 auto const refDiv = boostBigInt / d;
183 auto const refMod = boostBigInt % d;
185 auto const mod = b58_fast::detail::inplaceBigintDivRem(
186 std::span<uint64_t>(bigInt.data(), bigInt.size()), d);
187 auto const foundDiv = multiprecision_utils::toBoostMP(bigInt);
188 BEAST_EXPECT(refMod.convert_to<std::uint64_t>() == mod);
189 BEAST_EXPECT(foundDiv == refDiv);
191 for (
int i = 0; i < kIters; ++i)
193 std::uint64_t
const d = dist(eng);
194 auto bigInt = multiprecision_utils::randomBigInt( 2);
197 bigInt[bigInt.size() - 1] -= 1;
199 auto const boostBigInt = multiprecision_utils::toBoostMP(
200 std::span<std::uint64_t>(bigInt.data(), bigInt.size()));
202 auto const refAdd = boostBigInt + d;
204 auto const result = b58_fast::detail::inplaceBigintAdd(
205 std::span<uint64_t>(bigInt.data(), bigInt.size()), d);
206 BEAST_EXPECT(result == TokenCodecErrc::Success);
207 auto const foundAdd = multiprecision_utils::toBoostMP(bigInt);
208 BEAST_EXPECT(refAdd == foundAdd);
210 for (
int i = 0; i < kIters; ++i)
212 std::uint64_t
const d = dist1(eng);
216 auto const boostBigInt = multiprecision_utils::toBoostMP(
217 std::span<std::uint64_t>(bigInt.data(), bigInt.size()));
219 auto const refAdd = boostBigInt + d;
221 auto const result = b58_fast::detail::inplaceBigintAdd(
222 std::span<uint64_t>(bigInt.data(), bigInt.size()), d);
223 BEAST_EXPECT(result == TokenCodecErrc::OverflowAdd);
224 auto const foundAdd = multiprecision_utils::toBoostMP(bigInt);
225 BEAST_EXPECT(refAdd != foundAdd);
227 for (
int i = 0; i < kIters; ++i)
229 std::uint64_t
const d = dist(eng);
230 auto bigInt = multiprecision_utils::randomBigInt( 2);
233 bigInt[bigInt.size() - 1] = 0;
234 auto const boostBigInt = multiprecision_utils::toBoostMP(
235 std::span<std::uint64_t>(bigInt.data(), bigInt.size()));
237 auto const refMul = boostBigInt * d;
239 auto const result = b58_fast::detail::inplaceBigintMul(
240 std::span<uint64_t>(bigInt.data(), bigInt.size()), d);
241 BEAST_EXPECT(result == TokenCodecErrc::Success);
242 auto const foundMul = multiprecision_utils::toBoostMP(bigInt);
243 BEAST_EXPECT(refMul == foundMul);
245 for (
int i = 0; i < kIters; ++i)
247 std::uint64_t
const d = dist1(eng);
250 auto const boostBigInt = multiprecision_utils::toBoostMP(
251 std::span<std::uint64_t>(bigInt.data(), bigInt.size()));
253 auto const refMul = boostBigInt * d;
255 auto const result = b58_fast::detail::inplaceBigintMul(
256 std::span<uint64_t>(bigInt.data(), bigInt.size()), d);
257 BEAST_EXPECT(result == TokenCodecErrc::InputTooLarge);
258 auto const foundMul = multiprecision_utils::toBoostMP(bigInt);
259 BEAST_EXPECT(refMul != foundMul);
266 testcase(
"fast_matches_ref");
267 auto testRawEncode = [&](std::span<std::uint8_t>
const& b256Data) {
268 std::array<std::uint8_t, 64> b58ResultBuf[2];
269 std::array<std::span<std::uint8_t>, 2> b58Result;
271 std::array<std::uint8_t, 64> b256ResultBuf[2];
272 std::array<std::span<std::uint8_t>, 2> b256Result;
273 for (
int i = 0; i < 2; ++i)
275 std::span
const outBuf{b58ResultBuf[i]};
278 auto const r = xrpl::b58_fast::detail::b256ToB58Be(b256Data, outBuf);
280 b58Result[i] = r.value();
284 std::array<std::uint8_t, 128> tmpBuf{};
286 b256Data.data(), b256Data.size(), tmpBuf.
data(), tmpBuf.
size());
287 BEAST_EXPECT(s.
size());
292 if (BEAST_EXPECT(b58Result[0].
size() == b58Result[1].
size()))
297 printAsChar(b58Result[0], b58Result[1]);
301 for (
int i = 0; i < 2; ++i)
303 std::span
const outBuf{b256ResultBuf[i].
data(), b256ResultBuf[i].
size()};
306 std::string
const in(
307 b58Result[i].
data(), b58Result[i].
data() + b58Result[i].
size());
308 auto const r = xrpl::b58_fast::detail::b58ToB256Be(in, outBuf);
310 b256Result[i] = r.value();
314 std::string
const st(b58Result[i].
begin(), b58Result[i].
end());
316 BEAST_EXPECT(s.
size());
322 if (BEAST_EXPECT(b256Result[0].
size() == b256Result[1].
size()))
328 printAsInt(b256Result[0], b256Result[1]);
334 std::span<std::uint8_t>
const& b256Data) {
335 std::array<std::uint8_t, 64> b58ResultBuf[2];
336 std::array<std::span<std::uint8_t>, 2> b58Result;
338 std::array<std::uint8_t, 64> b256ResultBuf[2];
339 std::array<std::span<std::uint8_t>, 2> b256Result;
340 for (
int i = 0; i < 2; ++i)
342 std::span
const outBuf{b58ResultBuf[i].
data(), b58ResultBuf[i].
size()};
345 auto const r = xrpl::b58_fast::encodeBase58Token(tokType, b256Data, outBuf);
347 b58Result[i] = r.value();
351 std::string
const s =
353 BEAST_EXPECT(s.
size());
358 if (BEAST_EXPECT(b58Result[0].
size() == b58Result[1].
size()))
363 printAsChar(b58Result[0], b58Result[1]);
367 for (
int i = 0; i < 2; ++i)
369 std::span
const outBuf{b256ResultBuf[i].
data(), b256ResultBuf[i].
size()};
372 std::string
const in(
373 b58Result[i].
data(), b58Result[i].
data() + b58Result[i].
size());
374 auto const r = xrpl::b58_fast::decodeBase58Token(tokType, in, outBuf);
376 b256Result[i] = r.value();
380 std::string
const st(b58Result[i].
begin(), b58Result[i].
end());
382 BEAST_EXPECT(s.
size());
388 if (BEAST_EXPECT(b256Result[0].
size() == b256Result[1].
size()))
394 printAsInt(b256Result[0], b256Result[1]);
399 auto testIt = [&](
xrpl::TokenType const tokType, std::span<std::uint8_t>
const& b256Data) {
400 testRawEncode(b256Data);
401 testTokenEncode(tokType, b256Data);
406 for (
int i = 0; i < kNumTokenTypeIndexes; ++i)
408 std::array<std::uint8_t, 128> b256DataBuf{};
409 auto const [tokType, tokSize] = tokenTypeAndSize(i);
410 for (
int d = 0; d <= 255; ++d)
413 testIt(tokType, std::span(b256DataBuf.
data(), tokSize));
418 static constexpr std::size_t kIters = 100000;
419 for (
int i = 0; i < kIters; ++i)
421 std::array<std::uint8_t, 128> b256DataBuf{};
422 auto const [tokType, b256Data] = randomB256TestData(b256DataBuf);
423 testIt(tokType, b256Data);
430 testMultiprecision();
431 testFastMatchesRef();
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)
BEAST_DEFINE_TESTSUITE(AMMClawback, app, xrpl)
int run(int argc, char **argv)
BaseUInt< 160, detail::AccountIDTag > AccountID
A 160-bit unsigned that uniquely identifies an account.