mirror of
https://github.com/XRPLF/rippled.git
synced 2025-12-06 17:27:55 +00:00
This change fixes the suite names all around the test files, to make them match to the folder name in which this test files are located. Also, the RCL test files are relocated to the consensus folder, because they are testing consensus functionality.
478 lines
16 KiB
C++
478 lines
16 KiB
C++
//------------------------------------------------------------------------------
|
|
/*
|
|
This file is part of rippled: https://github.com/ripple/rippled
|
|
Copyright (c) 2022 Ripple Labs Inc.
|
|
|
|
Permission to use, copy, modify, and/or distribute this software for any
|
|
purpose with or without fee is hereby granted, provided that the above
|
|
copyright notice and this permission notice appear in all copies.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
//==============================================================================
|
|
|
|
#ifndef _MSC_VER
|
|
|
|
#include <xrpl/beast/unit_test.h>
|
|
#include <xrpl/protocol/detail/b58_utils.h>
|
|
#include <xrpl/protocol/tokens.h>
|
|
|
|
#include <boost/multiprecision/cpp_int.hpp>
|
|
#include <boost/random.hpp>
|
|
|
|
#include <array>
|
|
#include <cstddef>
|
|
#include <random>
|
|
#include <span>
|
|
#include <sstream>
|
|
|
|
namespace ripple {
|
|
namespace test {
|
|
namespace {
|
|
|
|
[[nodiscard]] inline auto
|
|
randEngine() -> std::mt19937&
|
|
{
|
|
static std::mt19937 r = [] {
|
|
std::random_device rd;
|
|
return std::mt19937{rd()};
|
|
}();
|
|
return r;
|
|
}
|
|
|
|
constexpr int numTokenTypeIndexes = 9;
|
|
|
|
[[nodiscard]] inline auto
|
|
tokenTypeAndSize(int i) -> std::tuple<ripple::TokenType, std::size_t>
|
|
{
|
|
assert(i < numTokenTypeIndexes);
|
|
|
|
switch (i)
|
|
{
|
|
using enum ripple::TokenType;
|
|
case 0:
|
|
return {None, 20};
|
|
case 1:
|
|
return {NodePublic, 32};
|
|
case 2:
|
|
return {NodePublic, 33};
|
|
case 3:
|
|
return {NodePrivate, 32};
|
|
case 4:
|
|
return {AccountID, 20};
|
|
case 5:
|
|
return {AccountPublic, 32};
|
|
case 6:
|
|
return {AccountPublic, 33};
|
|
case 7:
|
|
return {AccountSecret, 32};
|
|
case 8:
|
|
return {FamilySeed, 16};
|
|
default:
|
|
throw std::invalid_argument(
|
|
"Invalid token selection passed to tokenTypeAndSize() "
|
|
"in " __FILE__);
|
|
}
|
|
}
|
|
|
|
[[nodiscard]] inline auto
|
|
randomTokenTypeAndSize() -> std::tuple<ripple::TokenType, std::size_t>
|
|
{
|
|
using namespace ripple;
|
|
auto& rng = randEngine();
|
|
std::uniform_int_distribution<> d(0, 8);
|
|
return tokenTypeAndSize(d(rng));
|
|
}
|
|
|
|
// Return the token type and subspan of `d` to use as test data.
|
|
[[nodiscard]] inline auto
|
|
randomB256TestData(std::span<std::uint8_t> d)
|
|
-> std::tuple<ripple::TokenType, std::span<std::uint8_t>>
|
|
{
|
|
auto& rng = randEngine();
|
|
std::uniform_int_distribution<std::uint8_t> dist(0, 255);
|
|
auto [tokType, tokSize] = randomTokenTypeAndSize();
|
|
std::generate(d.begin(), d.begin() + tokSize, [&] { return dist(rng); });
|
|
return {tokType, d.subspan(0, tokSize)};
|
|
}
|
|
|
|
inline void
|
|
printAsChar(std::span<std::uint8_t> a, std::span<std::uint8_t> b)
|
|
{
|
|
auto asString = [](std::span<std::uint8_t> s) {
|
|
std::string r;
|
|
r.resize(s.size());
|
|
std::copy(s.begin(), s.end(), r.begin());
|
|
return r;
|
|
};
|
|
auto sa = asString(a);
|
|
auto sb = asString(b);
|
|
std::cerr << "\n\n" << sa << "\n" << sb << "\n";
|
|
}
|
|
|
|
inline void
|
|
printAsInt(std::span<std::uint8_t> a, std::span<std::uint8_t> b)
|
|
{
|
|
auto asString = [](std::span<std::uint8_t> s) -> std::string {
|
|
std::stringstream sstr;
|
|
for (auto i : s)
|
|
{
|
|
sstr << std::setw(3) << int(i) << ',';
|
|
}
|
|
return sstr.str();
|
|
};
|
|
auto sa = asString(a);
|
|
auto sb = asString(b);
|
|
std::cerr << "\n\n" << sa << "\n" << sb << "\n";
|
|
}
|
|
|
|
} // namespace
|
|
|
|
namespace multiprecision_utils {
|
|
|
|
boost::multiprecision::checked_uint512_t
|
|
toBoostMP(std::span<std::uint64_t> in)
|
|
{
|
|
boost::multiprecision::checked_uint512_t mbp = 0;
|
|
for (auto i = in.rbegin(); i != in.rend(); ++i)
|
|
{
|
|
mbp <<= 64;
|
|
mbp += *i;
|
|
}
|
|
return mbp;
|
|
}
|
|
|
|
std::vector<std::uint64_t>
|
|
randomBigInt(std::uint8_t minSize = 1, std::uint8_t maxSize = 5)
|
|
{
|
|
auto eng = randEngine();
|
|
std::uniform_int_distribution<std::uint8_t> numCoeffDist(minSize, maxSize);
|
|
std::uniform_int_distribution<std::uint64_t> dist;
|
|
auto const numCoeff = numCoeffDist(eng);
|
|
std::vector<std::uint64_t> coeffs;
|
|
coeffs.reserve(numCoeff);
|
|
for (int i = 0; i < numCoeff; ++i)
|
|
{
|
|
coeffs.push_back(dist(eng));
|
|
}
|
|
return coeffs;
|
|
}
|
|
} // namespace multiprecision_utils
|
|
|
|
class base58_test : public beast::unit_test::suite
|
|
{
|
|
void
|
|
testMultiprecision()
|
|
{
|
|
testcase("b58_multiprecision");
|
|
|
|
using namespace boost::multiprecision;
|
|
|
|
constexpr std::size_t iters = 100000;
|
|
auto eng = randEngine();
|
|
std::uniform_int_distribution<std::uint64_t> dist;
|
|
std::uniform_int_distribution<std::uint64_t> dist1(1);
|
|
for (int i = 0; i < iters; ++i)
|
|
{
|
|
std::uint64_t const d = dist(eng);
|
|
if (!d)
|
|
continue;
|
|
auto bigInt = multiprecision_utils::randomBigInt();
|
|
auto const boostBigInt = multiprecision_utils::toBoostMP(
|
|
std::span<std::uint64_t>(bigInt.data(), bigInt.size()));
|
|
|
|
auto const refDiv = boostBigInt / d;
|
|
auto const refMod = boostBigInt % d;
|
|
|
|
auto const mod = b58_fast::detail::inplace_bigint_div_rem(
|
|
std::span<uint64_t>(bigInt.data(), bigInt.size()), d);
|
|
auto const foundDiv = multiprecision_utils::toBoostMP(bigInt);
|
|
BEAST_EXPECT(refMod.convert_to<std::uint64_t>() == mod);
|
|
BEAST_EXPECT(foundDiv == refDiv);
|
|
}
|
|
for (int i = 0; i < iters; ++i)
|
|
{
|
|
std::uint64_t const d = dist(eng);
|
|
auto bigInt = multiprecision_utils::randomBigInt(/*minSize*/ 2);
|
|
if (bigInt[bigInt.size() - 1] ==
|
|
std::numeric_limits<std::uint64_t>::max())
|
|
{
|
|
bigInt[bigInt.size() - 1] -= 1; // Prevent overflow
|
|
}
|
|
auto const boostBigInt = multiprecision_utils::toBoostMP(
|
|
std::span<std::uint64_t>(bigInt.data(), bigInt.size()));
|
|
|
|
auto const refAdd = boostBigInt + d;
|
|
|
|
auto const result = b58_fast::detail::inplace_bigint_add(
|
|
std::span<uint64_t>(bigInt.data(), bigInt.size()), d);
|
|
BEAST_EXPECT(result == TokenCodecErrc::success);
|
|
auto const foundAdd = multiprecision_utils::toBoostMP(bigInt);
|
|
BEAST_EXPECT(refAdd == foundAdd);
|
|
}
|
|
for (int i = 0; i < iters; ++i)
|
|
{
|
|
std::uint64_t const d = dist1(eng);
|
|
// Force overflow
|
|
std::vector<std::uint64_t> bigInt(
|
|
5, std::numeric_limits<std::uint64_t>::max());
|
|
|
|
auto const boostBigInt = multiprecision_utils::toBoostMP(
|
|
std::span<std::uint64_t>(bigInt.data(), bigInt.size()));
|
|
|
|
auto const refAdd = boostBigInt + d;
|
|
|
|
auto const result = b58_fast::detail::inplace_bigint_add(
|
|
std::span<uint64_t>(bigInt.data(), bigInt.size()), d);
|
|
BEAST_EXPECT(result == TokenCodecErrc::overflowAdd);
|
|
auto const foundAdd = multiprecision_utils::toBoostMP(bigInt);
|
|
BEAST_EXPECT(refAdd != foundAdd);
|
|
}
|
|
for (int i = 0; i < iters; ++i)
|
|
{
|
|
std::uint64_t const d = dist(eng);
|
|
auto bigInt = multiprecision_utils::randomBigInt(/* minSize */ 2);
|
|
// inplace mul requires the most significant coeff to be zero to
|
|
// hold the result.
|
|
bigInt[bigInt.size() - 1] = 0;
|
|
auto const boostBigInt = multiprecision_utils::toBoostMP(
|
|
std::span<std::uint64_t>(bigInt.data(), bigInt.size()));
|
|
|
|
auto const refMul = boostBigInt * d;
|
|
|
|
auto const result = b58_fast::detail::inplace_bigint_mul(
|
|
std::span<uint64_t>(bigInt.data(), bigInt.size()), d);
|
|
BEAST_EXPECT(result == TokenCodecErrc::success);
|
|
auto const foundMul = multiprecision_utils::toBoostMP(bigInt);
|
|
BEAST_EXPECT(refMul == foundMul);
|
|
}
|
|
for (int i = 0; i < iters; ++i)
|
|
{
|
|
std::uint64_t const d = dist1(eng);
|
|
// Force overflow
|
|
std::vector<std::uint64_t> bigInt(
|
|
5, std::numeric_limits<std::uint64_t>::max());
|
|
auto const boostBigInt = multiprecision_utils::toBoostMP(
|
|
std::span<std::uint64_t>(bigInt.data(), bigInt.size()));
|
|
|
|
auto const refMul = boostBigInt * d;
|
|
|
|
auto const result = b58_fast::detail::inplace_bigint_mul(
|
|
std::span<uint64_t>(bigInt.data(), bigInt.size()), d);
|
|
BEAST_EXPECT(result == TokenCodecErrc::inputTooLarge);
|
|
auto const foundMul = multiprecision_utils::toBoostMP(bigInt);
|
|
BEAST_EXPECT(refMul != foundMul);
|
|
}
|
|
}
|
|
|
|
void
|
|
testFastMatchesRef()
|
|
{
|
|
testcase("fast_matches_ref");
|
|
auto testRawEncode = [&](std::span<std::uint8_t> const& b256Data) {
|
|
std::array<std::uint8_t, 64> b58ResultBuf[2];
|
|
std::array<std::span<std::uint8_t>, 2> b58Result;
|
|
|
|
std::array<std::uint8_t, 64> b256ResultBuf[2];
|
|
std::array<std::span<std::uint8_t>, 2> b256Result;
|
|
for (int i = 0; i < 2; ++i)
|
|
{
|
|
std::span const outBuf{b58ResultBuf[i]};
|
|
if (i == 0)
|
|
{
|
|
auto const r = ripple::b58_fast::detail::b256_to_b58_be(
|
|
b256Data, outBuf);
|
|
BEAST_EXPECT(r);
|
|
b58Result[i] = r.value();
|
|
}
|
|
else
|
|
{
|
|
std::array<std::uint8_t, 128> tmpBuf;
|
|
std::string const s = ripple::b58_ref::detail::encodeBase58(
|
|
b256Data.data(),
|
|
b256Data.size(),
|
|
tmpBuf.data(),
|
|
tmpBuf.size());
|
|
BEAST_EXPECT(s.size());
|
|
b58Result[i] = outBuf.subspan(0, s.size());
|
|
std::copy(s.begin(), s.end(), b58Result[i].begin());
|
|
}
|
|
}
|
|
if (BEAST_EXPECT(b58Result[0].size() == b58Result[1].size()))
|
|
{
|
|
if (!BEAST_EXPECT(
|
|
memcmp(
|
|
b58Result[0].data(),
|
|
b58Result[1].data(),
|
|
b58Result[0].size()) == 0))
|
|
{
|
|
printAsChar(b58Result[0], b58Result[1]);
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < 2; ++i)
|
|
{
|
|
std::span const outBuf{
|
|
b256ResultBuf[i].data(), b256ResultBuf[i].size()};
|
|
if (i == 0)
|
|
{
|
|
std::string const in(
|
|
b58Result[i].data(),
|
|
b58Result[i].data() + b58Result[i].size());
|
|
auto const r =
|
|
ripple::b58_fast::detail::b58_to_b256_be(in, outBuf);
|
|
BEAST_EXPECT(r);
|
|
b256Result[i] = r.value();
|
|
}
|
|
else
|
|
{
|
|
std::string const st(
|
|
b58Result[i].begin(), b58Result[i].end());
|
|
std::string const s =
|
|
ripple::b58_ref::detail::decodeBase58(st);
|
|
BEAST_EXPECT(s.size());
|
|
b256Result[i] = outBuf.subspan(0, s.size());
|
|
std::copy(s.begin(), s.end(), b256Result[i].begin());
|
|
}
|
|
}
|
|
|
|
if (BEAST_EXPECT(b256Result[0].size() == b256Result[1].size()))
|
|
{
|
|
if (!BEAST_EXPECT(
|
|
memcmp(
|
|
b256Result[0].data(),
|
|
b256Result[1].data(),
|
|
b256Result[0].size()) == 0))
|
|
{
|
|
printAsInt(b256Result[0], b256Result[1]);
|
|
}
|
|
}
|
|
};
|
|
|
|
auto testTokenEncode = [&](ripple::TokenType const tokType,
|
|
std::span<std::uint8_t> const& b256Data) {
|
|
std::array<std::uint8_t, 64> b58ResultBuf[2];
|
|
std::array<std::span<std::uint8_t>, 2> b58Result;
|
|
|
|
std::array<std::uint8_t, 64> b256ResultBuf[2];
|
|
std::array<std::span<std::uint8_t>, 2> b256Result;
|
|
for (int i = 0; i < 2; ++i)
|
|
{
|
|
std::span const outBuf{
|
|
b58ResultBuf[i].data(), b58ResultBuf[i].size()};
|
|
if (i == 0)
|
|
{
|
|
auto const r = ripple::b58_fast::encodeBase58Token(
|
|
tokType, b256Data, outBuf);
|
|
BEAST_EXPECT(r);
|
|
b58Result[i] = r.value();
|
|
}
|
|
else
|
|
{
|
|
std::string const s = ripple::b58_ref::encodeBase58Token(
|
|
tokType, b256Data.data(), b256Data.size());
|
|
BEAST_EXPECT(s.size());
|
|
b58Result[i] = outBuf.subspan(0, s.size());
|
|
std::copy(s.begin(), s.end(), b58Result[i].begin());
|
|
}
|
|
}
|
|
if (BEAST_EXPECT(b58Result[0].size() == b58Result[1].size()))
|
|
{
|
|
if (!BEAST_EXPECT(
|
|
memcmp(
|
|
b58Result[0].data(),
|
|
b58Result[1].data(),
|
|
b58Result[0].size()) == 0))
|
|
{
|
|
printAsChar(b58Result[0], b58Result[1]);
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < 2; ++i)
|
|
{
|
|
std::span const outBuf{
|
|
b256ResultBuf[i].data(), b256ResultBuf[i].size()};
|
|
if (i == 0)
|
|
{
|
|
std::string const in(
|
|
b58Result[i].data(),
|
|
b58Result[i].data() + b58Result[i].size());
|
|
auto const r = ripple::b58_fast::decodeBase58Token(
|
|
tokType, in, outBuf);
|
|
BEAST_EXPECT(r);
|
|
b256Result[i] = r.value();
|
|
}
|
|
else
|
|
{
|
|
std::string const st(
|
|
b58Result[i].begin(), b58Result[i].end());
|
|
std::string const s =
|
|
ripple::b58_ref::decodeBase58Token(st, tokType);
|
|
BEAST_EXPECT(s.size());
|
|
b256Result[i] = outBuf.subspan(0, s.size());
|
|
std::copy(s.begin(), s.end(), b256Result[i].begin());
|
|
}
|
|
}
|
|
|
|
if (BEAST_EXPECT(b256Result[0].size() == b256Result[1].size()))
|
|
{
|
|
if (!BEAST_EXPECT(
|
|
memcmp(
|
|
b256Result[0].data(),
|
|
b256Result[1].data(),
|
|
b256Result[0].size()) == 0))
|
|
{
|
|
printAsInt(b256Result[0], b256Result[1]);
|
|
}
|
|
}
|
|
};
|
|
|
|
auto testIt = [&](ripple::TokenType const tokType,
|
|
std::span<std::uint8_t> const& b256Data) {
|
|
testRawEncode(b256Data);
|
|
testTokenEncode(tokType, b256Data);
|
|
};
|
|
|
|
// test every token type with data where every byte is the same and the
|
|
// bytes range from 0-255
|
|
for (int i = 0; i < numTokenTypeIndexes; ++i)
|
|
{
|
|
std::array<std::uint8_t, 128> b256DataBuf;
|
|
auto const [tokType, tokSize] = tokenTypeAndSize(i);
|
|
for (int d = 0; d <= 255; ++d)
|
|
{
|
|
memset(b256DataBuf.data(), d, tokSize);
|
|
testIt(tokType, std::span(b256DataBuf.data(), tokSize));
|
|
}
|
|
}
|
|
|
|
// test with random data
|
|
constexpr std::size_t iters = 100000;
|
|
for (int i = 0; i < iters; ++i)
|
|
{
|
|
std::array<std::uint8_t, 128> b256DataBuf;
|
|
auto const [tokType, b256Data] = randomB256TestData(b256DataBuf);
|
|
testIt(tokType, b256Data);
|
|
}
|
|
}
|
|
|
|
void
|
|
run() override
|
|
{
|
|
testMultiprecision();
|
|
testFastMatchesRef();
|
|
}
|
|
};
|
|
|
|
BEAST_DEFINE_TESTSUITE(base58, basics, ripple);
|
|
|
|
} // namespace test
|
|
} // namespace ripple
|
|
#endif // _MSC_VER
|