Skip to content

Commit

Permalink
Merge branch 'master' into develop
Browse files Browse the repository at this point in the history
  • Loading branch information
Bronek committed Jul 31, 2024
2 parents f5a3495 + e6ef0fc commit 8b181ed
Show file tree
Hide file tree
Showing 20 changed files with 403 additions and 52 deletions.
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 @@ carrying_add(std::uint64_t a, std::uint64_t b)
// (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 @@ inplace_bigint_add(std::span<std::uint64_t> a, std::uint64_t b)
{
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 @@ inplace_bigint_mul(std::span<std::uint64_t> a, std::uint64_t b)
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 @@ inplace_bigint_div_rem(std::span<uint64_t> numerator, std::uint64_t divisor)
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

0 comments on commit 8b181ed

Please sign in to comment.