Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge master into develop #5085

Merged
merged 4 commits into from
Jul 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 12 additions & 12 deletions include/xrpl/protocol/detail/b58_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#define RIPPLE_PROTOCOL_B58_UTILS_H_INCLUDED

#include <xrpl/basics/contract.h>
#include <xrpl/protocol/detail/token_errors.h>

#include <boost/outcome.hpp>
#include <boost/outcome/result.hpp>
Expand Down Expand Up @@ -71,12 +72,12 @@
// (i.e a[0] is the 2^0 coefficient, a[n] is the 2^(64*n) coefficient)
// panics if overflows (this is a specialized adder for b58 decoding.
// it should never overflow).
inline void
[[nodiscard]] inline TokenCodecErrc
inplace_bigint_add(std::span<std::uint64_t> a, std::uint64_t b)
{
if (a.size() <= 1)
{
ripple::LogicError("Input span too small for inplace_bigint_add");
return TokenCodecErrc::inputTooSmall;

Check warning on line 80 in include/xrpl/protocol/detail/b58_utils.h

View check run for this annotation

Codecov / codecov/patch

include/xrpl/protocol/detail/b58_utils.h#L80

Added line #L80 was not covered by tests
}

std::uint64_t carry;
Expand All @@ -86,28 +87,29 @@
{
if (!carry)
{
return;
return TokenCodecErrc::success;
}
std::tie(v, carry) = carrying_add(v, 1);
}
if (carry)
{
LogicError("Overflow in inplace_bigint_add");
return TokenCodecErrc::overflowAdd;
}
return TokenCodecErrc::success;
}

inline void
[[nodiscard]] inline TokenCodecErrc
inplace_bigint_mul(std::span<std::uint64_t> a, std::uint64_t b)
{
if (a.empty())
{
LogicError("Empty span passed to inplace_bigint_mul");
return TokenCodecErrc::inputTooSmall;

Check warning on line 106 in include/xrpl/protocol/detail/b58_utils.h

View check run for this annotation

Codecov / codecov/patch

include/xrpl/protocol/detail/b58_utils.h#L106

Added line #L106 was not covered by tests
}

auto const last_index = a.size() - 1;
if (a[last_index] != 0)
{
LogicError("Non-zero element in inplace_bigint_mul last index");
return TokenCodecErrc::inputTooLarge;
}

std::uint64_t carry = 0;
Expand All @@ -116,7 +118,9 @@
std::tie(coeff, carry) = carrying_mul(coeff, b, carry);
}
a[last_index] = carry;
return TokenCodecErrc::success;
}

// divide a "big uint" value inplace and return the mod
// numerator is stored so smallest coefficients come first
[[nodiscard]] inline std::uint64_t
Expand Down Expand Up @@ -166,11 +170,7 @@
b58_10_to_b58_be(std::uint64_t input)
{
constexpr std::uint64_t B_58_10 = 430804206899405824; // 58^10;
if (input >= B_58_10)
{
LogicError("Input to b58_10_to_b58_be equals or exceeds 58^10.");
}

assert(input < B_58_10);
constexpr std::size_t resultSize = 10;
std::array<std::uint8_t, resultSize> result{};
int i = 0;
Expand Down
1 change: 1 addition & 0 deletions include/xrpl/protocol/detail/token_errors.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ enum class TokenCodecErrc {
mismatchedTokenType,
mismatchedChecksum,
invalidEncodingChar,
overflowAdd,
unknown,
};
}
Expand Down
26 changes: 22 additions & 4 deletions src/libxrpl/protocol/tokens.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -467,6 +467,11 @@ b256_to_b58_be(std::span<std::uint8_t const> input, std::span<std::uint8_t> out)
{
continue;
}
static constexpr std::uint64_t B_58_10 = 430804206899405824; // 58^10;
if (base_58_10_coeff[i] >= B_58_10)
{
return Unexpected(TokenCodecErrc::inputTooLarge);
}
std::array<std::uint8_t, 10> const b58_be =
ripple::b58_fast::detail::b58_10_to_b58_be(base_58_10_coeff[i]);
std::size_t to_skip = 0;
Expand Down Expand Up @@ -565,10 +570,23 @@ b58_to_b256_be(std::string_view input, std::span<std::uint8_t> out)
for (int i = 1; i < num_b_58_10_coeffs; ++i)
{
std::uint64_t const c = b_58_10_coeff[i];
ripple::b58_fast::detail::inplace_bigint_mul(
std::span(&result[0], cur_result_size + 1), B_58_10);
ripple::b58_fast::detail::inplace_bigint_add(
std::span(&result[0], cur_result_size + 1), c);

{
auto code = ripple::b58_fast::detail::inplace_bigint_mul(
std::span(&result[0], cur_result_size + 1), B_58_10);
if (code != TokenCodecErrc::success)
{
return Unexpected(code);
}
}
{
auto code = ripple::b58_fast::detail::inplace_bigint_add(
std::span(&result[0], cur_result_size + 1), c);
if (code != TokenCodecErrc::success)
{
return Unexpected(code);
}
}
if (result[cur_result_size] != 0)
{
cur_result_size += 1;
Expand Down
19 changes: 19 additions & 0 deletions src/test/app/PayChan_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -873,6 +873,25 @@ struct PayChan_test : public beast::unit_test::suite
auto const chan1Str = to_string(channel(alice, bob, env.seq(alice)));
env(create(alice, bob, channelFunds, settleDelay, pk));
env.close();
{
// test account non-string
auto testInvalidAccountParam = [&](auto const& param) {
Json::Value params;
params[jss::account] = param;
auto jrr = env.rpc(
"json", "account_channels", to_string(params))[jss::result];
BEAST_EXPECT(jrr[jss::error] == "invalidParams");
BEAST_EXPECT(
jrr[jss::error_message] == "Invalid field 'account'.");
};

testInvalidAccountParam(1);
testInvalidAccountParam(1.1);
testInvalidAccountParam(true);
testInvalidAccountParam(Json::Value(Json::nullValue));
testInvalidAccountParam(Json::Value(Json::objectValue));
testInvalidAccountParam(Json::Value(Json::arrayValue));
}
{
auto const r =
env.rpc("account_channels", alice.human(), bob.human());
Expand Down
42 changes: 40 additions & 2 deletions src/test/basics/base58_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ class base58_test : public beast::unit_test::suite
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);
Expand Down Expand Up @@ -209,12 +210,31 @@ class base58_test : public beast::unit_test::suite

auto const refAdd = boostBigInt + d;

b58_fast::detail::inplace_bigint_add(
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);
Expand All @@ -226,11 +246,29 @@ class base58_test : public beast::unit_test::suite

auto const refMul = boostBigInt * d;

b58_fast::detail::inplace_bigint_mul(
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
Expand Down
47 changes: 46 additions & 1 deletion src/test/rpc/AccountCurrencies_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ class AccountCurrencies_test : public beast::unit_test::suite

{ // invalid ledger (hash)
Json::Value params;
params[jss::account] = Account{"bob"}.human();
params[jss::ledger_hash] = 1;
auto const result = env.rpc(
"json",
Expand All @@ -56,6 +57,50 @@ class AccountCurrencies_test : public beast::unit_test::suite
result[jss::error_message] == "Missing field 'account'.");
}

{
// test account non-string
auto testInvalidAccountParam = [&](auto const& param) {
Json::Value params;
params[jss::account] = param;
auto jrr = env.rpc(
"json",
"account_currencies",
to_string(params))[jss::result];
BEAST_EXPECT(jrr[jss::error] == "invalidParams");
BEAST_EXPECT(
jrr[jss::error_message] == "Invalid field 'account'.");
};

testInvalidAccountParam(1);
testInvalidAccountParam(1.1);
testInvalidAccountParam(true);
testInvalidAccountParam(Json::Value(Json::nullValue));
testInvalidAccountParam(Json::Value(Json::objectValue));
testInvalidAccountParam(Json::Value(Json::arrayValue));
}

{
// test ident non-string
auto testInvalidIdentParam = [&](auto const& param) {
Json::Value params;
params[jss::ident] = param;
auto jrr = env.rpc(
"json",
"account_currencies",
to_string(params))[jss::result];
BEAST_EXPECT(jrr[jss::error] == "invalidParams");
BEAST_EXPECT(
jrr[jss::error_message] == "Invalid field 'ident'.");
};

testInvalidIdentParam(1);
testInvalidIdentParam(1.1);
testInvalidIdentParam(true);
testInvalidIdentParam(Json::Value(Json::nullValue));
testInvalidIdentParam(Json::Value(Json::objectValue));
testInvalidIdentParam(Json::Value(Json::arrayValue));
}

{
Json::Value params;
params[jss::account] =
Expand Down Expand Up @@ -198,6 +243,6 @@ class AccountCurrencies_test : public beast::unit_test::suite
}
};

BEAST_DEFINE_TESTSUITE(AccountCurrencies, app, ripple);
BEAST_DEFINE_TESTSUITE(AccountCurrencies, rpc, ripple);

} // namespace ripple
47 changes: 46 additions & 1 deletion src/test/rpc/AccountInfo_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ class AccountInfo_test : public beast::unit_test::suite
void
testErrors()
{
testcase("Errors");
using namespace jtx;
Env env(*this);
{
Expand Down Expand Up @@ -78,12 +79,53 @@ class AccountInfo_test : public beast::unit_test::suite
BEAST_EXPECT(
info[jss::result][jss::error_message] == "Account malformed.");
}
{
// Cannot pass a non-string into the `account` param

auto testInvalidAccountParam = [&](auto const& param) {
Json::Value params;
params[jss::account] = param;
auto jrr = env.rpc(
"json", "account_info", to_string(params))[jss::result];
BEAST_EXPECT(jrr[jss::error] == "invalidParams");
BEAST_EXPECT(
jrr[jss::error_message] == "Invalid field 'account'.");
};

testInvalidAccountParam(1);
testInvalidAccountParam(1.1);
testInvalidAccountParam(true);
testInvalidAccountParam(Json::Value(Json::nullValue));
testInvalidAccountParam(Json::Value(Json::objectValue));
testInvalidAccountParam(Json::Value(Json::arrayValue));
}
{
// Cannot pass a non-string into the `ident` param

auto testInvalidIdentParam = [&](auto const& param) {
Json::Value params;
params[jss::ident] = param;
auto jrr = env.rpc(
"json", "account_info", to_string(params))[jss::result];
BEAST_EXPECT(jrr[jss::error] == "invalidParams");
BEAST_EXPECT(
jrr[jss::error_message] == "Invalid field 'ident'.");
};

testInvalidIdentParam(1);
testInvalidIdentParam(1.1);
testInvalidIdentParam(true);
testInvalidIdentParam(Json::Value(Json::nullValue));
testInvalidIdentParam(Json::Value(Json::objectValue));
testInvalidIdentParam(Json::Value(Json::arrayValue));
}
}

// Test the "signer_lists" argument in account_info.
void
testSignerLists()
{
testcase("Signer lists");
using namespace jtx;
Env env(*this);
Account const alice{"alice"};
Expand Down Expand Up @@ -205,6 +247,7 @@ class AccountInfo_test : public beast::unit_test::suite
void
testSignerListsApiVersion2()
{
testcase("Signer lists APIv2");
using namespace jtx;
Env env{*this};
Account const alice{"alice"};
Expand Down Expand Up @@ -326,6 +369,7 @@ class AccountInfo_test : public beast::unit_test::suite
void
testSignerListsV2()
{
testcase("Signer lists v2");
using namespace jtx;
Env env(*this);
Account const alice{"alice"};
Expand Down Expand Up @@ -515,6 +559,7 @@ class AccountInfo_test : public beast::unit_test::suite
void
testAccountFlags(FeatureBitset const& features)
{
testcase("Account flags");
using namespace jtx;

Env env(*this, features);
Expand Down Expand Up @@ -652,7 +697,7 @@ class AccountInfo_test : public beast::unit_test::suite
}
};

BEAST_DEFINE_TESTSUITE(AccountInfo, app, ripple);
BEAST_DEFINE_TESTSUITE(AccountInfo, rpc, ripple);

} // namespace test
} // namespace ripple
Loading
Loading