mirror of
https://github.com/XRPLF/clio.git
synced 2025-11-18 02:35:51 +00:00
Port of #1839 into 2.3.1. Fixes #1832. rippled code: https://github.com/XRPLF/rippled/blob/develop/src/xrpld/rpc/handlers/GatewayBalances.cpp#L129
This commit is contained in:
@@ -108,10 +108,11 @@ public:
|
|||||||
static RpcSpecConstRef
|
static RpcSpecConstRef
|
||||||
spec([[maybe_unused]] uint32_t apiVersion)
|
spec([[maybe_unused]] uint32_t apiVersion)
|
||||||
{
|
{
|
||||||
static auto const hotWalletValidator =
|
auto const getHotWalletValidator = [](RippledError errCode) {
|
||||||
validation::CustomValidator{[](boost::json::value const& value, std::string_view key) -> MaybeError {
|
return validation::CustomValidator{
|
||||||
|
[errCode](boost::json::value const& value, std::string_view key) -> MaybeError {
|
||||||
if (!value.is_string() && !value.is_array())
|
if (!value.is_string() && !value.is_array())
|
||||||
return Error{Status{RippledError::rpcINVALID_PARAMS, std::string(key) + "NotStringOrArray"}};
|
return Error{Status{errCode, std::string(key) + "NotStringOrArray"}};
|
||||||
|
|
||||||
// wallet needs to be an valid accountID or public key
|
// wallet needs to be an valid accountID or public key
|
||||||
auto const wallets = value.is_array() ? value.as_array() : boost::json::array{value};
|
auto const wallets = value.is_array() ? value.as_array() : boost::json::array{value};
|
||||||
@@ -132,20 +133,26 @@ public:
|
|||||||
|
|
||||||
for (auto const& wallet : wallets) {
|
for (auto const& wallet : wallets) {
|
||||||
if (!getAccountID(wallet))
|
if (!getAccountID(wallet))
|
||||||
return Error{Status{RippledError::rpcINVALID_PARAMS, std::string(key) + "Malformed"}};
|
return Error{Status{errCode, std::string(key) + "Malformed"}};
|
||||||
}
|
}
|
||||||
|
|
||||||
return MaybeError{};
|
return MaybeError{};
|
||||||
}};
|
}
|
||||||
|
};
|
||||||
static auto const rpcSpec = RpcSpec{
|
|
||||||
{JS(account), validation::Required{}, validation::CustomValidators::AccountValidator},
|
|
||||||
{JS(ledger_hash), validation::CustomValidators::Uint256HexStringValidator},
|
|
||||||
{JS(ledger_index), validation::CustomValidators::LedgerIndexValidator},
|
|
||||||
{JS(hotwallet), hotWalletValidator}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return rpcSpec;
|
static auto const kSPEC_COMMON = RpcSpec{
|
||||||
|
{JS(account), validation::Required{}, validation::CustomValidators::AccountValidator},
|
||||||
|
{JS(ledger_hash), validation::CustomValidators::Uint256HexStringValidator},
|
||||||
|
{JS(ledger_index), validation::CustomValidators::LedgerIndexValidator}
|
||||||
|
};
|
||||||
|
|
||||||
|
auto static const kSPEC_V1 =
|
||||||
|
RpcSpec{kSPEC_COMMON, {{JS(hotwallet), getHotWalletValidator(ripple::rpcINVALID_HOTWALLET)}}};
|
||||||
|
auto static const kSPEC_V2 =
|
||||||
|
RpcSpec{kSPEC_COMMON, {{JS(hotwallet), getHotWalletValidator(ripple::rpcINVALID_PARAMS)}}};
|
||||||
|
|
||||||
|
return apiVersion == 1 ? kSPEC_V1 : kSPEC_V2;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -50,24 +50,19 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
constexpr static auto ACCOUNT = "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn";
|
namespace {
|
||||||
constexpr static auto ACCOUNT2 = "rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun";
|
constexpr auto ACCOUNT = "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn";
|
||||||
|
constexpr auto ACCOUNT2 = "rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun";
|
||||||
|
|
||||||
constexpr static auto LEDGERHASH = "4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A652";
|
constexpr auto LEDGERHASH = "4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A652";
|
||||||
constexpr static auto INDEX1 = "1B8590C01B0006EDFA9ED60296DD052DC5E90F99659B25014D08E1BC983515BC";
|
constexpr auto INDEX1 = "1B8590C01B0006EDFA9ED60296DD052DC5E90F99659B25014D08E1BC983515BC";
|
||||||
constexpr static auto INDEX2 = "E6DBAFC99223B42257915A63DFC6B0C032D4070F9A574B255AD97466726FC321";
|
constexpr auto INDEX2 = "E6DBAFC99223B42257915A63DFC6B0C032D4070F9A574B255AD97466726FC321";
|
||||||
// 20 USD : 10 XRP
|
// 20 USD : 10 XRP
|
||||||
constexpr static auto PAYS20USDGETS10XRPBOOKDIR = "43B83ADC452B85FCBADA6CAEAC5181C255A213630D58FFD455071AFD498D0000";
|
constexpr auto PAYS20USDGETS10XRPBOOKDIR = "43B83ADC452B85FCBADA6CAEAC5181C255A213630D58FFD455071AFD498D0000";
|
||||||
// 20 XRP : 10 USD
|
// 20 XRP : 10 USD
|
||||||
constexpr static auto PAYS20XRPGETS10USDBOOKDIR = "7B1767D41DBCE79D9585CF9D0262A5FEC45E5206FF524F8B55071AFD498D0000";
|
constexpr auto PAYS20XRPGETS10USDBOOKDIR = "7B1767D41DBCE79D9585CF9D0262A5FEC45E5206FF524F8B55071AFD498D0000";
|
||||||
// transfer rate x2
|
// transfer rate x2
|
||||||
constexpr static auto TRANSFERRATEX2 = 2000000000;
|
constexpr auto TRANSFERRATEX2 = 2000000000;
|
||||||
|
|
||||||
using namespace rpc;
|
|
||||||
namespace json = boost::json;
|
|
||||||
using namespace testing;
|
|
||||||
|
|
||||||
class RPCBookOffersHandlerTest : public HandlerBaseTest {};
|
|
||||||
|
|
||||||
struct ParameterTestBundle {
|
struct ParameterTestBundle {
|
||||||
std::string testName;
|
std::string testName;
|
||||||
@@ -76,7 +71,15 @@ struct ParameterTestBundle {
|
|||||||
std::string expectedErrorMessage;
|
std::string expectedErrorMessage;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct RPCBookOffersParameterTest : public RPCBookOffersHandlerTest, public WithParamInterface<ParameterTestBundle> {};
|
} // namespace
|
||||||
|
|
||||||
|
using namespace rpc;
|
||||||
|
namespace json = boost::json;
|
||||||
|
using namespace testing;
|
||||||
|
|
||||||
|
class RPCBookOffersHandlerTest : public HandlerBaseTest {};
|
||||||
|
|
||||||
|
struct RPCBookOffersParameterTest : RPCBookOffersHandlerTest, WithParamInterface<ParameterTestBundle> {};
|
||||||
|
|
||||||
TEST_P(RPCBookOffersParameterTest, CheckError)
|
TEST_P(RPCBookOffersParameterTest, CheckError)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -49,22 +49,30 @@ using namespace rpc;
|
|||||||
namespace json = boost::json;
|
namespace json = boost::json;
|
||||||
using namespace testing;
|
using namespace testing;
|
||||||
|
|
||||||
constexpr static auto ACCOUNT = "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn";
|
namespace {
|
||||||
constexpr static auto ACCOUNT2 = "rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun";
|
constexpr auto ACCOUNT = "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn";
|
||||||
constexpr static auto ACCOUNT3 = "raHGBERMka3KZsfpTQUAtumxmvpqhFLyrk";
|
constexpr auto ACCOUNT2 = "rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun";
|
||||||
constexpr static auto ISSUER = "rK9DrarGKnVEo2nYp5MfVRXRYf5yRX3mwD";
|
constexpr auto ACCOUNT3 = "raHGBERMka3KZsfpTQUAtumxmvpqhFLyrk";
|
||||||
constexpr static auto LEDGERHASH = "4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A652";
|
constexpr auto ISSUER = "rK9DrarGKnVEo2nYp5MfVRXRYf5yRX3mwD";
|
||||||
constexpr static auto INDEX1 = "1B8590C01B0006EDFA9ED60296DD052DC5E90F99659B25014D08E1BC983515BC";
|
constexpr auto LEDGERHASH = "4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A652";
|
||||||
constexpr static auto INDEX2 = "E6DBAFC99223B42257915A63DFC6B0C032D4070F9A574B255AD97466726FC321";
|
constexpr auto INDEX1 = "1B8590C01B0006EDFA9ED60296DD052DC5E90F99659B25014D08E1BC983515BC";
|
||||||
constexpr static auto TXNID = "E3FE6EA3D48F0C2B639448020EA4F03D4F4F8FFDB243A852A0F59177921B4879";
|
constexpr auto INDEX2 = "E6DBAFC99223B42257915A63DFC6B0C032D4070F9A574B255AD97466726FC321";
|
||||||
|
constexpr auto TXNID = "E3FE6EA3D48F0C2B639448020EA4F03D4F4F8FFDB243A852A0F59177921B4879";
|
||||||
class RPCGatewayBalancesHandlerTest : public HandlerBaseTest {};
|
|
||||||
|
|
||||||
struct ParameterTestBundle {
|
struct ParameterTestBundle {
|
||||||
std::string testName;
|
std::string testName;
|
||||||
std::string testJson;
|
std::string testJson;
|
||||||
std::string expectedError;
|
std::string expectedError;
|
||||||
std::string expectedErrorMessage;
|
std::string expectedErrorMessage;
|
||||||
|
std::uint32_t apiVersion = 1u;
|
||||||
|
};
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
struct RPCGatewayBalancesHandlerTest : HandlerBaseTest {
|
||||||
|
RPCGatewayBalancesHandlerTest()
|
||||||
|
{
|
||||||
|
backend->setRange(10, 300);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ParameterTest : public RPCGatewayBalancesHandlerTest, public WithParamInterface<ParameterTestBundle> {};
|
struct ParameterTest : public RPCGatewayBalancesHandlerTest, public WithParamInterface<ParameterTestBundle> {};
|
||||||
@@ -74,7 +82,8 @@ TEST_P(ParameterTest, CheckError)
|
|||||||
auto bundle = GetParam();
|
auto bundle = GetParam();
|
||||||
auto const handler = AnyHandler{GatewayBalancesHandler{backend}};
|
auto const handler = AnyHandler{GatewayBalancesHandler{backend}};
|
||||||
runSpawn([&](auto yield) {
|
runSpawn([&](auto yield) {
|
||||||
auto const output = handler.process(json::parse(bundle.testJson), Context{yield});
|
auto const output =
|
||||||
|
handler.process(json::parse(bundle.testJson), Context{.yield = yield, .apiVersion = bundle.apiVersion});
|
||||||
ASSERT_FALSE(output);
|
ASSERT_FALSE(output);
|
||||||
auto const err = rpc::makeError(output.result.error());
|
auto const err = rpc::makeError(output.result.error());
|
||||||
EXPECT_EQ(err.at("error").as_string(), bundle.expectedError);
|
EXPECT_EQ(err.at("error").as_string(), bundle.expectedError);
|
||||||
@@ -146,52 +155,104 @@ generateParameterTestBundles()
|
|||||||
"ledger_hashNotString"
|
"ledger_hashNotString"
|
||||||
},
|
},
|
||||||
ParameterTestBundle{
|
ParameterTestBundle{
|
||||||
"WalletsNotStringOrArray",
|
.testName = "WalletsNotStringOrArrayV1",
|
||||||
fmt::format(
|
.testJson = fmt::format(
|
||||||
R"({{
|
R"({{
|
||||||
"account": "{}",
|
"account": "{}",
|
||||||
"hotwallet": 12
|
"hotwallet": 12
|
||||||
}})",
|
}})",
|
||||||
ACCOUNT
|
ACCOUNT
|
||||||
),
|
),
|
||||||
"invalidParams",
|
.expectedError = "invalidHotWallet",
|
||||||
"hotwalletNotStringOrArray"
|
.expectedErrorMessage = "hotwalletNotStringOrArray"
|
||||||
},
|
},
|
||||||
ParameterTestBundle{
|
ParameterTestBundle{
|
||||||
"WalletsNotStringAccount",
|
.testName = "WalletsNotStringAccountV1",
|
||||||
fmt::format(
|
.testJson = fmt::format(
|
||||||
R"({{
|
R"({{
|
||||||
"account": "{}",
|
"account": "{}",
|
||||||
"hotwallet": [12]
|
"hotwallet": [12]
|
||||||
}})",
|
}})",
|
||||||
ACCOUNT
|
ACCOUNT
|
||||||
),
|
),
|
||||||
"invalidParams",
|
.expectedError = "invalidHotWallet",
|
||||||
"hotwalletMalformed"
|
.expectedErrorMessage = "hotwalletMalformed"
|
||||||
},
|
},
|
||||||
ParameterTestBundle{
|
ParameterTestBundle{
|
||||||
"WalletsInvalidAccount",
|
.testName = "WalletsInvalidAccountV1",
|
||||||
fmt::format(
|
.testJson = fmt::format(
|
||||||
R"({{
|
R"({{
|
||||||
"account": "{}",
|
"account": "{}",
|
||||||
"hotwallet": ["12"]
|
"hotwallet": ["12"]
|
||||||
}})",
|
}})",
|
||||||
ACCOUNT
|
ACCOUNT
|
||||||
),
|
),
|
||||||
"invalidParams",
|
.expectedError = "invalidHotWallet",
|
||||||
"hotwalletMalformed"
|
.expectedErrorMessage = "hotwalletMalformed"
|
||||||
},
|
},
|
||||||
ParameterTestBundle{
|
ParameterTestBundle{
|
||||||
"WalletInvalidAccount",
|
.testName = "WalletInvalidAccountV1",
|
||||||
fmt::format(
|
.testJson = fmt::format(
|
||||||
R"({{
|
R"({{
|
||||||
"account": "{}",
|
"account": "{}",
|
||||||
"hotwallet": "12"
|
"hotwallet": "12"
|
||||||
}})",
|
}})",
|
||||||
ACCOUNT
|
ACCOUNT
|
||||||
),
|
),
|
||||||
"invalidParams",
|
.expectedError = "invalidHotWallet",
|
||||||
"hotwalletMalformed"
|
.expectedErrorMessage = "hotwalletMalformed"
|
||||||
|
},
|
||||||
|
ParameterTestBundle{
|
||||||
|
.testName = "WalletsNotStringOrArrayV2",
|
||||||
|
.testJson = fmt::format(
|
||||||
|
R"({{
|
||||||
|
"account": "{}",
|
||||||
|
"hotwallet": 12
|
||||||
|
}})",
|
||||||
|
ACCOUNT
|
||||||
|
),
|
||||||
|
.expectedError = "invalidParams",
|
||||||
|
.expectedErrorMessage = "hotwalletNotStringOrArray",
|
||||||
|
.apiVersion = 2u
|
||||||
|
},
|
||||||
|
ParameterTestBundle{
|
||||||
|
.testName = "WalletsNotStringAccountV2",
|
||||||
|
.testJson = fmt::format(
|
||||||
|
R"({{
|
||||||
|
"account": "{}",
|
||||||
|
"hotwallet": [12]
|
||||||
|
}})",
|
||||||
|
ACCOUNT
|
||||||
|
),
|
||||||
|
.expectedError = "invalidParams",
|
||||||
|
.expectedErrorMessage = "hotwalletMalformed",
|
||||||
|
.apiVersion = 2u
|
||||||
|
},
|
||||||
|
ParameterTestBundle{
|
||||||
|
.testName = "WalletsInvalidAccountV2",
|
||||||
|
.testJson = fmt::format(
|
||||||
|
R"({{
|
||||||
|
"account": "{}",
|
||||||
|
"hotwallet": ["12"]
|
||||||
|
}})",
|
||||||
|
ACCOUNT
|
||||||
|
),
|
||||||
|
.expectedError = "invalidParams",
|
||||||
|
.expectedErrorMessage = "hotwalletMalformed",
|
||||||
|
.apiVersion = 2u
|
||||||
|
},
|
||||||
|
ParameterTestBundle{
|
||||||
|
.testName = "WalletInvalidAccountV2",
|
||||||
|
.testJson = fmt::format(
|
||||||
|
R"({{
|
||||||
|
"account": "{}",
|
||||||
|
"hotwallet": "12"
|
||||||
|
}})",
|
||||||
|
ACCOUNT
|
||||||
|
),
|
||||||
|
.expectedError = "invalidParams",
|
||||||
|
.expectedErrorMessage = "hotwalletMalformed",
|
||||||
|
.apiVersion = 2u
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -207,7 +268,6 @@ TEST_F(RPCGatewayBalancesHandlerTest, LedgerNotFoundViaStringIndex)
|
|||||||
{
|
{
|
||||||
auto const seq = 123;
|
auto const seq = 123;
|
||||||
|
|
||||||
backend->setRange(10, 300);
|
|
||||||
EXPECT_CALL(*backend, fetchLedgerBySequence).Times(1);
|
EXPECT_CALL(*backend, fetchLedgerBySequence).Times(1);
|
||||||
// return empty ledgerHeader
|
// return empty ledgerHeader
|
||||||
ON_CALL(*backend, fetchLedgerBySequence(seq, _)).WillByDefault(Return(std::optional<ripple::LedgerHeader>{}));
|
ON_CALL(*backend, fetchLedgerBySequence(seq, _)).WillByDefault(Return(std::optional<ripple::LedgerHeader>{}));
|
||||||
@@ -236,7 +296,6 @@ TEST_F(RPCGatewayBalancesHandlerTest, LedgerNotFoundViaIntIndex)
|
|||||||
{
|
{
|
||||||
auto const seq = 123;
|
auto const seq = 123;
|
||||||
|
|
||||||
backend->setRange(10, 300);
|
|
||||||
EXPECT_CALL(*backend, fetchLedgerBySequence).Times(1);
|
EXPECT_CALL(*backend, fetchLedgerBySequence).Times(1);
|
||||||
// return empty ledgerHeader
|
// return empty ledgerHeader
|
||||||
ON_CALL(*backend, fetchLedgerBySequence(seq, _)).WillByDefault(Return(std::optional<ripple::LedgerHeader>{}));
|
ON_CALL(*backend, fetchLedgerBySequence(seq, _)).WillByDefault(Return(std::optional<ripple::LedgerHeader>{}));
|
||||||
@@ -263,7 +322,6 @@ TEST_F(RPCGatewayBalancesHandlerTest, LedgerNotFoundViaIntIndex)
|
|||||||
|
|
||||||
TEST_F(RPCGatewayBalancesHandlerTest, LedgerNotFoundViaHash)
|
TEST_F(RPCGatewayBalancesHandlerTest, LedgerNotFoundViaHash)
|
||||||
{
|
{
|
||||||
backend->setRange(10, 300);
|
|
||||||
EXPECT_CALL(*backend, fetchLedgerByHash).Times(1);
|
EXPECT_CALL(*backend, fetchLedgerByHash).Times(1);
|
||||||
// return empty ledgerHeader
|
// return empty ledgerHeader
|
||||||
ON_CALL(*backend, fetchLedgerByHash(ripple::uint256{LEDGERHASH}, _))
|
ON_CALL(*backend, fetchLedgerByHash(ripple::uint256{LEDGERHASH}, _))
|
||||||
@@ -293,7 +351,6 @@ TEST_F(RPCGatewayBalancesHandlerTest, AccountNotFound)
|
|||||||
{
|
{
|
||||||
auto const seq = 300;
|
auto const seq = 300;
|
||||||
|
|
||||||
backend->setRange(10, seq);
|
|
||||||
EXPECT_CALL(*backend, fetchLedgerBySequence).Times(1);
|
EXPECT_CALL(*backend, fetchLedgerBySequence).Times(1);
|
||||||
// return valid ledgerHeader
|
// return valid ledgerHeader
|
||||||
auto const ledgerHeader = CreateLedgerHeader(LEDGERHASH, seq);
|
auto const ledgerHeader = CreateLedgerHeader(LEDGERHASH, seq);
|
||||||
@@ -337,7 +394,6 @@ TEST_P(NormalPathTest, CheckOutput)
|
|||||||
auto const& bundle = GetParam();
|
auto const& bundle = GetParam();
|
||||||
auto const seq = 300;
|
auto const seq = 300;
|
||||||
|
|
||||||
backend->setRange(10, seq);
|
|
||||||
EXPECT_CALL(*backend, fetchLedgerBySequence).Times(1);
|
EXPECT_CALL(*backend, fetchLedgerBySequence).Times(1);
|
||||||
// return valid ledgerHeader
|
// return valid ledgerHeader
|
||||||
auto const ledgerHeader = CreateLedgerHeader(LEDGERHASH, seq);
|
auto const ledgerHeader = CreateLedgerHeader(LEDGERHASH, seq);
|
||||||
|
|||||||
Reference in New Issue
Block a user