mirror of
https://github.com/XRPLF/clio.git
synced 2025-11-20 11:45:53 +00:00
@@ -58,6 +58,7 @@ target_sources(clio PRIVATE
|
|||||||
src/rpc/common/Validators.cpp
|
src/rpc/common/Validators.cpp
|
||||||
## NextGen RPC handler
|
## NextGen RPC handler
|
||||||
src/rpc/ngHandlers/AccountChannels.cpp
|
src/rpc/ngHandlers/AccountChannels.cpp
|
||||||
|
src/rpc/ngHandlers/AccountCurrencies.cpp
|
||||||
## RPC Methods
|
## RPC Methods
|
||||||
# Account
|
# Account
|
||||||
src/rpc/handlers/AccountChannels.cpp
|
src/rpc/handlers/AccountChannels.cpp
|
||||||
@@ -116,6 +117,7 @@ if(BUILD_TESTS)
|
|||||||
unittests/rpc/BaseTests.cpp
|
unittests/rpc/BaseTests.cpp
|
||||||
unittests/rpc/RPCHelpersTest.cpp
|
unittests/rpc/RPCHelpersTest.cpp
|
||||||
unittests/rpc/handlers/TestHandlerTests.cpp
|
unittests/rpc/handlers/TestHandlerTests.cpp
|
||||||
|
unittests/rpc/handlers/AccountCurrenciesTest.cpp
|
||||||
unittests/rpc/handlers/DefaultProcessorTests.cpp
|
unittests/rpc/handlers/DefaultProcessorTests.cpp
|
||||||
unittests/rpc/handlers/PingTest.cpp
|
unittests/rpc/handlers/PingTest.cpp
|
||||||
unittests/rpc/handlers/AccountChannelsTest.cpp)
|
unittests/rpc/handlers/AccountChannelsTest.cpp)
|
||||||
|
|||||||
@@ -606,8 +606,10 @@ getLedgerInfoFromHashOrSeq(
|
|||||||
RPC::Status{RPC::RippledError::rpcLGR_NOT_FOUND, "ledgerNotFound"};
|
RPC::Status{RPC::RippledError::rpcLGR_NOT_FOUND, "ledgerNotFound"};
|
||||||
if (ledgerHash)
|
if (ledgerHash)
|
||||||
{
|
{
|
||||||
lgrInfo =
|
// invoke uint256's constructor to parse the hex string , instead of
|
||||||
backend.fetchLedgerByHash(ripple::uint256{*ledgerHash}, yield);
|
// copying buffer
|
||||||
|
ripple::uint256 ledgerHash256{std::string_view(*ledgerHash)};
|
||||||
|
lgrInfo = backend.fetchLedgerByHash(ledgerHash256, yield);
|
||||||
if (!lgrInfo || lgrInfo->seq > maxSeq)
|
if (!lgrInfo || lgrInfo->seq > maxSeq)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
|
|||||||
@@ -75,7 +75,8 @@ public:
|
|||||||
|
|
||||||
using Result = RPCng::HandlerReturnType<Output>;
|
using Result = RPCng::HandlerReturnType<Output>;
|
||||||
|
|
||||||
AccountChannelsHandler(std::shared_ptr<BackendInterface>& sharedPtrBackend)
|
AccountChannelsHandler(
|
||||||
|
std::shared_ptr<BackendInterface> const& sharedPtrBackend)
|
||||||
: sharedPtrBackend_(sharedPtrBackend)
|
: sharedPtrBackend_(sharedPtrBackend)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|||||||
129
src/rpc/ngHandlers/AccountCurrencies.cpp
Normal file
129
src/rpc/ngHandlers/AccountCurrencies.cpp
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
This file is part of clio: https://github.com/XRPLF/clio
|
||||||
|
Copyright (c) 2023, the clio developers.
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and 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.
|
||||||
|
*/
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
#include <rpc/RPCHelpers.h>
|
||||||
|
#include <rpc/ngHandlers/AccountCurrencies.h>
|
||||||
|
|
||||||
|
namespace RPCng {
|
||||||
|
AccountCurrenciesHandler::Result
|
||||||
|
AccountCurrenciesHandler::process(
|
||||||
|
AccountCurrenciesHandler::Input input,
|
||||||
|
boost::asio::yield_context& yield) const
|
||||||
|
{
|
||||||
|
auto const range = sharedPtrBackend_->fetchLedgerRange();
|
||||||
|
auto const lgrInfoOrStatus = RPC::getLedgerInfoFromHashOrSeq(
|
||||||
|
*sharedPtrBackend_,
|
||||||
|
yield,
|
||||||
|
input.ledgerHash,
|
||||||
|
input.ledgerIndex,
|
||||||
|
range->maxSequence);
|
||||||
|
|
||||||
|
if (auto const status = std::get_if<RPC::Status>(&lgrInfoOrStatus))
|
||||||
|
return Error{*status};
|
||||||
|
|
||||||
|
auto const lgrInfo = std::get<ripple::LedgerInfo>(lgrInfoOrStatus);
|
||||||
|
|
||||||
|
auto const accountID = RPC::accountFromStringStrict(input.account);
|
||||||
|
|
||||||
|
auto const accountLedgerObject = sharedPtrBackend_->fetchLedgerObject(
|
||||||
|
ripple::keylet::account(*accountID).key, lgrInfo.seq, yield);
|
||||||
|
if (!accountLedgerObject)
|
||||||
|
return Error{RPC::Status{
|
||||||
|
RPC::RippledError::rpcACT_NOT_FOUND, "accountNotFound"}};
|
||||||
|
|
||||||
|
Output response;
|
||||||
|
auto const addToResponse = [&](ripple::SLE&& sle) {
|
||||||
|
if (sle.getType() == ripple::ltRIPPLE_STATE)
|
||||||
|
{
|
||||||
|
ripple::STAmount balance = sle.getFieldAmount(ripple::sfBalance);
|
||||||
|
auto const lowLimit = sle.getFieldAmount(ripple::sfLowLimit);
|
||||||
|
auto const highLimit = sle.getFieldAmount(ripple::sfHighLimit);
|
||||||
|
bool const viewLowest = (lowLimit.getIssuer() == accountID);
|
||||||
|
auto const lineLimit = viewLowest ? lowLimit : highLimit;
|
||||||
|
auto const lineLimitPeer = !viewLowest ? lowLimit : highLimit;
|
||||||
|
if (!viewLowest)
|
||||||
|
balance.negate();
|
||||||
|
if (balance < lineLimit)
|
||||||
|
response.receiveCurrencies.insert(
|
||||||
|
ripple::to_string(balance.getCurrency()));
|
||||||
|
if ((-balance) < lineLimitPeer)
|
||||||
|
response.sendCurrencies.insert(
|
||||||
|
ripple::to_string(balance.getCurrency()));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
// traverse all owned nodes, limit->max, marker->empty
|
||||||
|
RPC::ngTraverseOwnedNodes(
|
||||||
|
*sharedPtrBackend_,
|
||||||
|
*accountID,
|
||||||
|
lgrInfo.seq,
|
||||||
|
std::numeric_limits<std::uint32_t>::max(),
|
||||||
|
{},
|
||||||
|
yield,
|
||||||
|
addToResponse);
|
||||||
|
|
||||||
|
response.ledgerHash = ripple::strHex(lgrInfo.hash);
|
||||||
|
response.ledgerIndex = lgrInfo.seq;
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
tag_invoke(
|
||||||
|
boost::json::value_from_tag,
|
||||||
|
boost::json::value& jv,
|
||||||
|
AccountCurrenciesHandler::Output output)
|
||||||
|
{
|
||||||
|
jv = {
|
||||||
|
{"ledger_hash", output.ledgerHash},
|
||||||
|
{"ledger_index", output.ledgerIndex},
|
||||||
|
{"validated", output.validated},
|
||||||
|
{"receive_currencies",
|
||||||
|
boost::json::value_from(output.receiveCurrencies)},
|
||||||
|
{"send_currencies", boost::json::value_from(output.sendCurrencies)}};
|
||||||
|
}
|
||||||
|
|
||||||
|
AccountCurrenciesHandler::Input
|
||||||
|
tag_invoke(
|
||||||
|
boost::json::value_to_tag<AccountCurrenciesHandler::Input>,
|
||||||
|
boost::json::value const& jv)
|
||||||
|
{
|
||||||
|
auto const& jsonObject = jv.as_object();
|
||||||
|
AccountCurrenciesHandler::Input input;
|
||||||
|
input.account = jv.at("account").as_string().c_str();
|
||||||
|
if (jsonObject.contains("ledger_hash"))
|
||||||
|
{
|
||||||
|
input.ledgerHash = jv.at("ledger_hash").as_string().c_str();
|
||||||
|
}
|
||||||
|
if (jsonObject.contains("ledger_index"))
|
||||||
|
{
|
||||||
|
if (!jsonObject.at("ledger_index").is_string())
|
||||||
|
{
|
||||||
|
input.ledgerIndex = jv.at("ledger_index").as_int64();
|
||||||
|
}
|
||||||
|
else if (jsonObject.at("ledger_index").as_string() != "validated")
|
||||||
|
{
|
||||||
|
input.ledgerIndex =
|
||||||
|
std::stoi(jv.at("ledger_index").as_string().c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace RPCng
|
||||||
87
src/rpc/ngHandlers/AccountCurrencies.h
Normal file
87
src/rpc/ngHandlers/AccountCurrencies.h
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
This file is part of clio: https://github.com/XRPLF/clio
|
||||||
|
Copyright (c) 2023, the clio developers.
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and 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.
|
||||||
|
*/
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <backend/BackendInterface.h>
|
||||||
|
#include <rpc/common/Types.h>
|
||||||
|
#include <rpc/common/Validators.h>
|
||||||
|
|
||||||
|
#include <boost/asio/spawn.hpp>
|
||||||
|
|
||||||
|
#include <set>
|
||||||
|
|
||||||
|
namespace RPCng {
|
||||||
|
class AccountCurrenciesHandler
|
||||||
|
{
|
||||||
|
// dependencies
|
||||||
|
std::shared_ptr<BackendInterface> sharedPtrBackend_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
struct Output
|
||||||
|
{
|
||||||
|
std::string ledgerHash;
|
||||||
|
uint32_t ledgerIndex;
|
||||||
|
std::set<std::string> receiveCurrencies;
|
||||||
|
std::set<std::string> sendCurrencies;
|
||||||
|
// validated should be sent via framework
|
||||||
|
bool validated = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO:we did not implement the "strict" field
|
||||||
|
struct Input
|
||||||
|
{
|
||||||
|
std::string account;
|
||||||
|
std::optional<std::string> ledgerHash;
|
||||||
|
std::optional<uint32_t> ledgerIndex;
|
||||||
|
};
|
||||||
|
|
||||||
|
using Result = RPCng::HandlerReturnType<Output>;
|
||||||
|
|
||||||
|
AccountCurrenciesHandler(
|
||||||
|
std::shared_ptr<BackendInterface> const& sharedPtrBackend)
|
||||||
|
: sharedPtrBackend_(sharedPtrBackend)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
RpcSpecConstRef
|
||||||
|
spec() const
|
||||||
|
{
|
||||||
|
static const RpcSpec rpcSpec = {
|
||||||
|
{"account", validation::Required{}, validation::AccountValidator},
|
||||||
|
{"ledger_hash", validation::LedgerHashValidator},
|
||||||
|
{"ledger_index", validation::LedgerIndexValidator}};
|
||||||
|
return rpcSpec;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result
|
||||||
|
process(Input input, boost::asio::yield_context& yield) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
void
|
||||||
|
tag_invoke(
|
||||||
|
boost::json::value_from_tag,
|
||||||
|
boost::json::value& jv,
|
||||||
|
AccountCurrenciesHandler::Output output);
|
||||||
|
|
||||||
|
AccountCurrenciesHandler::Input
|
||||||
|
tag_invoke(
|
||||||
|
boost::json::value_to_tag<AccountCurrenciesHandler::Input>,
|
||||||
|
boost::json::value const& jv);
|
||||||
|
} // namespace RPCng
|
||||||
@@ -40,20 +40,8 @@ constexpr static auto INDEX2 =
|
|||||||
constexpr static auto TXNID =
|
constexpr static auto TXNID =
|
||||||
"05FB0EB4B899F056FA095537C5817163801F544BAFCEA39C995D76DB4D16F9DD";
|
"05FB0EB4B899F056FA095537C5817163801F544BAFCEA39C995D76DB4D16F9DD";
|
||||||
|
|
||||||
class RPCAccountHandlerTest : public SyncAsioContextTest, public MockBackendTest
|
class RPCAccountHandlerTest : public HandlerBaseTest
|
||||||
{
|
{
|
||||||
void
|
|
||||||
SetUp() override
|
|
||||||
{
|
|
||||||
SyncAsioContextTest::SetUp();
|
|
||||||
MockBackendTest::SetUp();
|
|
||||||
}
|
|
||||||
void
|
|
||||||
TearDown() override
|
|
||||||
{
|
|
||||||
MockBackendTest::TearDown();
|
|
||||||
SyncAsioContextTest::TearDown();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
TEST_F(RPCAccountHandlerTest, NonHexLedgerHash)
|
TEST_F(RPCAccountHandlerTest, NonHexLedgerHash)
|
||||||
@@ -250,7 +238,7 @@ TEST_F(RPCAccountHandlerTest, NonExistLedgerViaLedgerHash)
|
|||||||
MockBackend* rawBackendPtr =
|
MockBackend* rawBackendPtr =
|
||||||
static_cast<MockBackend*>(mockBackendPtr.get());
|
static_cast<MockBackend*>(mockBackendPtr.get());
|
||||||
// mock fetchLedgerByHash return empty
|
// mock fetchLedgerByHash return empty
|
||||||
ON_CALL(*rawBackendPtr, fetchLedgerByHash)
|
ON_CALL(*rawBackendPtr, fetchLedgerByHash(ripple::uint256{LEDGERHASH}, _))
|
||||||
.WillByDefault(Return(std::optional<ripple::LedgerInfo>{}));
|
.WillByDefault(Return(std::optional<ripple::LedgerInfo>{}));
|
||||||
EXPECT_CALL(*rawBackendPtr, fetchLedgerByHash).Times(1);
|
EXPECT_CALL(*rawBackendPtr, fetchLedgerByHash).Times(1);
|
||||||
|
|
||||||
@@ -311,7 +299,7 @@ TEST_F(RPCAccountHandlerTest, NonExistLedgerViaLedgerHash2)
|
|||||||
mockBackendPtr->updateRange(30); // max
|
mockBackendPtr->updateRange(30); // max
|
||||||
// mock fetchLedgerByHash return ledger but seq is 31 > 30
|
// mock fetchLedgerByHash return ledger but seq is 31 > 30
|
||||||
auto ledgerinfo = CreateLedgerInfo(LEDGERHASH, 31);
|
auto ledgerinfo = CreateLedgerInfo(LEDGERHASH, 31);
|
||||||
ON_CALL(*rawBackendPtr, fetchLedgerByHash)
|
ON_CALL(*rawBackendPtr, fetchLedgerByHash(ripple::uint256{LEDGERHASH}, _))
|
||||||
.WillByDefault(Return(ledgerinfo));
|
.WillByDefault(Return(ledgerinfo));
|
||||||
EXPECT_CALL(*rawBackendPtr, fetchLedgerByHash).Times(1);
|
EXPECT_CALL(*rawBackendPtr, fetchLedgerByHash).Times(1);
|
||||||
auto const input = json::parse(fmt::format(
|
auto const input = json::parse(fmt::format(
|
||||||
@@ -367,7 +355,7 @@ TEST_F(RPCAccountHandlerTest, NonExistAccount)
|
|||||||
mockBackendPtr->updateRange(10); // min
|
mockBackendPtr->updateRange(10); // min
|
||||||
mockBackendPtr->updateRange(30); // max
|
mockBackendPtr->updateRange(30); // max
|
||||||
auto ledgerinfo = CreateLedgerInfo(LEDGERHASH, 30);
|
auto ledgerinfo = CreateLedgerInfo(LEDGERHASH, 30);
|
||||||
ON_CALL(*rawBackendPtr, fetchLedgerByHash)
|
ON_CALL(*rawBackendPtr, fetchLedgerByHash(ripple::uint256{LEDGERHASH}, _))
|
||||||
.WillByDefault(Return(ledgerinfo));
|
.WillByDefault(Return(ledgerinfo));
|
||||||
EXPECT_CALL(*rawBackendPtr, fetchLedgerByHash).Times(1);
|
EXPECT_CALL(*rawBackendPtr, fetchLedgerByHash).Times(1);
|
||||||
// fetch account object return emtpy
|
// fetch account object return emtpy
|
||||||
|
|||||||
295
unittests/rpc/handlers/AccountCurrenciesTest.cpp
Normal file
295
unittests/rpc/handlers/AccountCurrenciesTest.cpp
Normal file
@@ -0,0 +1,295 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
This file is part of clio: https://github.com/XRPLF/clio
|
||||||
|
Copyright (c) 2023, the clio developers.
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and 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.
|
||||||
|
*/
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
#include <rpc/common/AnyHandler.h>
|
||||||
|
#include <rpc/ngHandlers/AccountCurrencies.h>
|
||||||
|
#include <util/Fixtures.h>
|
||||||
|
#include <util/TestObject.h>
|
||||||
|
|
||||||
|
#include <fmt/core.h>
|
||||||
|
|
||||||
|
using namespace RPCng;
|
||||||
|
namespace json = boost::json;
|
||||||
|
using namespace testing;
|
||||||
|
|
||||||
|
constexpr static auto ACCOUNT = "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn";
|
||||||
|
constexpr static auto ACCOUNT2 = "rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun";
|
||||||
|
constexpr static auto ISSUER = "rK9DrarGKnVEo2nYp5MfVRXRYf5yRX3mwD";
|
||||||
|
constexpr static auto LEDGERHASH =
|
||||||
|
"4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A652";
|
||||||
|
constexpr static auto INDEX1 =
|
||||||
|
"1B8590C01B0006EDFA9ED60296DD052DC5E90F99659B25014D08E1BC983515BC";
|
||||||
|
constexpr static auto INDEX2 =
|
||||||
|
"E6DBAFC99223B42257915A63DFC6B0C032D4070F9A574B255AD97466726FC321";
|
||||||
|
constexpr static auto TXNID =
|
||||||
|
"E3FE6EA3D48F0C2B639448020EA4F03D4F4F8FFDB243A852A0F59177921B4879";
|
||||||
|
|
||||||
|
class RPCAccountCurrenciesHandlerTest : public HandlerBaseTest
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_F(RPCAccountCurrenciesHandlerTest, AccountNotExsit)
|
||||||
|
{
|
||||||
|
auto const rawBackendPtr = static_cast<MockBackend*>(mockBackendPtr.get());
|
||||||
|
mockBackendPtr->updateRange(10); // min
|
||||||
|
mockBackendPtr->updateRange(30); // max
|
||||||
|
auto const ledgerinfo = CreateLedgerInfo(LEDGERHASH, 30);
|
||||||
|
EXPECT_CALL(*rawBackendPtr, fetchLedgerBySequence).Times(1);
|
||||||
|
|
||||||
|
ON_CALL(*rawBackendPtr, fetchLedgerBySequence)
|
||||||
|
.WillByDefault(Return(ledgerinfo));
|
||||||
|
ON_CALL(*rawBackendPtr, doFetchLedgerObject)
|
||||||
|
.WillByDefault(Return(std::optional<Blob>{}));
|
||||||
|
EXPECT_CALL(*rawBackendPtr, doFetchLedgerObject).Times(1);
|
||||||
|
|
||||||
|
auto const static input = boost::json::parse(fmt::format(
|
||||||
|
R"({{
|
||||||
|
"account":"{}"
|
||||||
|
}})",
|
||||||
|
ACCOUNT));
|
||||||
|
auto const handler = AnyHandler{AccountCurrenciesHandler{mockBackendPtr}};
|
||||||
|
boost::asio::spawn(ctx, [&](boost::asio::yield_context yield) {
|
||||||
|
auto const output = handler.process(input, yield);
|
||||||
|
ASSERT_FALSE(output);
|
||||||
|
auto const err = RPC::makeError(output.error());
|
||||||
|
EXPECT_EQ(err.at("error").as_string(), "actNotFound");
|
||||||
|
EXPECT_EQ(err.at("error_message").as_string(), "accountNotFound");
|
||||||
|
});
|
||||||
|
ctx.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(RPCAccountCurrenciesHandlerTest, LedgerNonExistViaSequence)
|
||||||
|
{
|
||||||
|
auto const rawBackendPtr = static_cast<MockBackend*>(mockBackendPtr.get());
|
||||||
|
mockBackendPtr->updateRange(10); // min
|
||||||
|
mockBackendPtr->updateRange(30); // max
|
||||||
|
EXPECT_CALL(*rawBackendPtr, fetchLedgerBySequence).Times(1);
|
||||||
|
// return empty ledgerinfo
|
||||||
|
ON_CALL(*rawBackendPtr, fetchLedgerBySequence(30, _))
|
||||||
|
.WillByDefault(Return(std::optional<ripple::LedgerInfo>{}));
|
||||||
|
|
||||||
|
auto const static input = boost::json::parse(fmt::format(
|
||||||
|
R"({{
|
||||||
|
"account":"{}"
|
||||||
|
}})",
|
||||||
|
ACCOUNT));
|
||||||
|
auto const handler = AnyHandler{AccountCurrenciesHandler{mockBackendPtr}};
|
||||||
|
boost::asio::spawn(ctx, [&](boost::asio::yield_context yield) {
|
||||||
|
auto const output = handler.process(input, yield);
|
||||||
|
ASSERT_FALSE(output);
|
||||||
|
auto const err = RPC::makeError(output.error());
|
||||||
|
EXPECT_EQ(err.at("error").as_string(), "lgrNotFound");
|
||||||
|
EXPECT_EQ(err.at("error_message").as_string(), "ledgerNotFound");
|
||||||
|
});
|
||||||
|
ctx.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(RPCAccountCurrenciesHandlerTest, LedgerNonExistViaHash)
|
||||||
|
{
|
||||||
|
auto const rawBackendPtr = static_cast<MockBackend*>(mockBackendPtr.get());
|
||||||
|
mockBackendPtr->updateRange(10); // min
|
||||||
|
mockBackendPtr->updateRange(30); // max
|
||||||
|
EXPECT_CALL(*rawBackendPtr, fetchLedgerByHash).Times(1);
|
||||||
|
// return empty ledgerinfo
|
||||||
|
ON_CALL(*rawBackendPtr, fetchLedgerByHash(ripple::uint256{LEDGERHASH}, _))
|
||||||
|
.WillByDefault(Return(std::optional<ripple::LedgerInfo>{}));
|
||||||
|
|
||||||
|
auto const static input = boost::json::parse(fmt::format(
|
||||||
|
R"({{
|
||||||
|
"account":"{}",
|
||||||
|
"ledger_hash":"{}"
|
||||||
|
}})",
|
||||||
|
ACCOUNT,
|
||||||
|
LEDGERHASH));
|
||||||
|
auto const handler = AnyHandler{AccountCurrenciesHandler{mockBackendPtr}};
|
||||||
|
boost::asio::spawn(ctx, [&](boost::asio::yield_context yield) {
|
||||||
|
auto const output = handler.process(input);
|
||||||
|
ASSERT_FALSE(output);
|
||||||
|
auto const err = RPC::makeError(output.error());
|
||||||
|
EXPECT_EQ(err.at("error").as_string(), "lgrNotFound");
|
||||||
|
EXPECT_EQ(err.at("error_message").as_string(), "ledgerNotFound");
|
||||||
|
});
|
||||||
|
ctx.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(RPCAccountCurrenciesHandlerTest, DefaultParameter)
|
||||||
|
{
|
||||||
|
auto constexpr static OUTPUT = R"({
|
||||||
|
"ledger_hash":"4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A652",
|
||||||
|
"ledger_index":30,
|
||||||
|
"validated":true,
|
||||||
|
"receive_currencies":[
|
||||||
|
"EUR",
|
||||||
|
"JPY"
|
||||||
|
],
|
||||||
|
"send_currencies":[
|
||||||
|
"EUR",
|
||||||
|
"USD"
|
||||||
|
]
|
||||||
|
})";
|
||||||
|
auto const rawBackendPtr = static_cast<MockBackend*>(mockBackendPtr.get());
|
||||||
|
mockBackendPtr->updateRange(10); // min
|
||||||
|
mockBackendPtr->updateRange(30); // max
|
||||||
|
// return valid ledgerinfo
|
||||||
|
auto const ledgerinfo = CreateLedgerInfo(LEDGERHASH, 30);
|
||||||
|
EXPECT_CALL(*rawBackendPtr, fetchLedgerBySequence).Times(1);
|
||||||
|
ON_CALL(*rawBackendPtr, fetchLedgerBySequence(30, _))
|
||||||
|
.WillByDefault(Return(ledgerinfo));
|
||||||
|
// return valid account
|
||||||
|
auto const accountKk =
|
||||||
|
ripple::keylet::account(GetAccountIDWithString(ACCOUNT)).key;
|
||||||
|
ON_CALL(*rawBackendPtr, doFetchLedgerObject(accountKk, 30, _))
|
||||||
|
.WillByDefault(Return(Blob{'f', 'a', 'k', 'e'}));
|
||||||
|
|
||||||
|
auto const ownerDir = CreateOwnerDirLedgerObject(
|
||||||
|
{ripple::uint256{INDEX1},
|
||||||
|
ripple::uint256{INDEX2},
|
||||||
|
ripple::uint256{INDEX2}},
|
||||||
|
INDEX1);
|
||||||
|
auto const ownerDirKk =
|
||||||
|
ripple::keylet::ownerDir(GetAccountIDWithString(ACCOUNT)).key;
|
||||||
|
ON_CALL(*rawBackendPtr, doFetchLedgerObject(ownerDirKk, 30, _))
|
||||||
|
.WillByDefault(Return(ownerDir.getSerializer().peekData()));
|
||||||
|
EXPECT_CALL(*rawBackendPtr, doFetchLedgerObject).Times(2);
|
||||||
|
|
||||||
|
// ACCOUNT can receive USD 10 from ACCOUNT2 and send USD 20 to ACCOUNT2, now
|
||||||
|
// the balance is 100, ACCOUNT can only send USD to ACCOUNT2
|
||||||
|
auto const line1 = CreateRippleStateLedgerObject(
|
||||||
|
ACCOUNT, "USD", ISSUER, 100, ACCOUNT, 10, ACCOUNT2, 20, TXNID, 123);
|
||||||
|
// ACCOUNT2 can receive JPY 10 from ACCOUNT and send JPY 20 to ACCOUNT, now
|
||||||
|
// the balance is 100, ACCOUNT2 can only send JPY to ACCOUNT
|
||||||
|
auto const line2 = CreateRippleStateLedgerObject(
|
||||||
|
ACCOUNT, "JPY", ISSUER, 100, ACCOUNT2, 10, ACCOUNT, 20, TXNID, 123);
|
||||||
|
// ACCOUNT can receive EUR 10 from ACCOUNT and send EUR 20 to ACCOUNT2, now
|
||||||
|
// the balance is 8, ACCOUNT can receive/send EUR to/from ACCOUNT2
|
||||||
|
auto const line3 = CreateRippleStateLedgerObject(
|
||||||
|
ACCOUNT, "EUR", ISSUER, 8, ACCOUNT, 10, ACCOUNT2, 20, TXNID, 123);
|
||||||
|
std::vector<Blob> bbs;
|
||||||
|
bbs.push_back(line1.getSerializer().peekData());
|
||||||
|
bbs.push_back(line2.getSerializer().peekData());
|
||||||
|
bbs.push_back(line3.getSerializer().peekData());
|
||||||
|
|
||||||
|
ON_CALL(*rawBackendPtr, doFetchLedgerObjects).WillByDefault(Return(bbs));
|
||||||
|
EXPECT_CALL(*rawBackendPtr, doFetchLedgerObjects).Times(1);
|
||||||
|
auto const static input = boost::json::parse(fmt::format(
|
||||||
|
R"({{
|
||||||
|
"account":"{}"
|
||||||
|
}})",
|
||||||
|
ACCOUNT));
|
||||||
|
auto const handler = AnyHandler{AccountCurrenciesHandler{mockBackendPtr}};
|
||||||
|
boost::asio::spawn(ctx, [&](boost::asio::yield_context yield) {
|
||||||
|
auto const output = handler.process(input, yield);
|
||||||
|
ASSERT_TRUE(output);
|
||||||
|
EXPECT_EQ(*output, json::parse(OUTPUT));
|
||||||
|
});
|
||||||
|
ctx.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(RPCAccountCurrenciesHandlerTest, RequestViaLegderHash)
|
||||||
|
{
|
||||||
|
auto const rawBackendPtr = static_cast<MockBackend*>(mockBackendPtr.get());
|
||||||
|
mockBackendPtr->updateRange(10); // min
|
||||||
|
mockBackendPtr->updateRange(30); // max
|
||||||
|
// return valid ledgerinfo
|
||||||
|
auto const ledgerinfo = CreateLedgerInfo(LEDGERHASH, 30);
|
||||||
|
EXPECT_CALL(*rawBackendPtr, fetchLedgerByHash).Times(1);
|
||||||
|
ON_CALL(*rawBackendPtr, fetchLedgerByHash(ripple::uint256{LEDGERHASH}, _))
|
||||||
|
.WillByDefault(Return(ledgerinfo));
|
||||||
|
// return valid account
|
||||||
|
auto const accountKk =
|
||||||
|
ripple::keylet::account(GetAccountIDWithString(ACCOUNT)).key;
|
||||||
|
ON_CALL(*rawBackendPtr, doFetchLedgerObject(accountKk, 30, _))
|
||||||
|
.WillByDefault(Return(Blob{'f', 'a', 'k', 'e'}));
|
||||||
|
|
||||||
|
auto const ownerDir =
|
||||||
|
CreateOwnerDirLedgerObject({ripple::uint256{INDEX1}}, INDEX1);
|
||||||
|
auto const ownerDirKk =
|
||||||
|
ripple::keylet::ownerDir(GetAccountIDWithString(ACCOUNT)).key;
|
||||||
|
ON_CALL(*rawBackendPtr, doFetchLedgerObject(ownerDirKk, 30, _))
|
||||||
|
.WillByDefault(Return(ownerDir.getSerializer().peekData()));
|
||||||
|
EXPECT_CALL(*rawBackendPtr, doFetchLedgerObject).Times(2);
|
||||||
|
std::vector<Blob> bbs;
|
||||||
|
auto const line1 = CreateRippleStateLedgerObject(
|
||||||
|
ACCOUNT, "USD", ISSUER, 100, ACCOUNT, 10, ACCOUNT2, 20, TXNID, 123);
|
||||||
|
bbs.push_back(line1.getSerializer().peekData());
|
||||||
|
|
||||||
|
ON_CALL(*rawBackendPtr, doFetchLedgerObjects).WillByDefault(Return(bbs));
|
||||||
|
EXPECT_CALL(*rawBackendPtr, doFetchLedgerObjects).Times(1);
|
||||||
|
auto const static input = boost::json::parse(fmt::format(
|
||||||
|
R"({{
|
||||||
|
"account":"{}",
|
||||||
|
"ledger_hash":"{}"
|
||||||
|
}})",
|
||||||
|
ACCOUNT,
|
||||||
|
LEDGERHASH));
|
||||||
|
auto const handler = AnyHandler{AccountCurrenciesHandler{mockBackendPtr}};
|
||||||
|
boost::asio::spawn(ctx, [&](boost::asio::yield_context yield) {
|
||||||
|
auto const output = handler.process(input, yield);
|
||||||
|
ASSERT_TRUE(output);
|
||||||
|
});
|
||||||
|
ctx.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(RPCAccountCurrenciesHandlerTest, RequestViaLegderSeq)
|
||||||
|
{
|
||||||
|
auto const rawBackendPtr = static_cast<MockBackend*>(mockBackendPtr.get());
|
||||||
|
mockBackendPtr->updateRange(10); // min
|
||||||
|
mockBackendPtr->updateRange(30); // max
|
||||||
|
auto const ledgerSeq = 29;
|
||||||
|
// return valid ledgerinfo
|
||||||
|
auto const ledgerinfo = CreateLedgerInfo(LEDGERHASH, ledgerSeq);
|
||||||
|
EXPECT_CALL(*rawBackendPtr, fetchLedgerBySequence).Times(1);
|
||||||
|
ON_CALL(*rawBackendPtr, fetchLedgerBySequence(ledgerSeq, _))
|
||||||
|
.WillByDefault(Return(ledgerinfo));
|
||||||
|
// return valid account
|
||||||
|
auto const accountKk =
|
||||||
|
ripple::keylet::account(GetAccountIDWithString(ACCOUNT)).key;
|
||||||
|
ON_CALL(*rawBackendPtr, doFetchLedgerObject(accountKk, ledgerSeq, _))
|
||||||
|
.WillByDefault(Return(Blob{'f', 'a', 'k', 'e'}));
|
||||||
|
|
||||||
|
auto const ownerDir =
|
||||||
|
CreateOwnerDirLedgerObject({ripple::uint256{INDEX1}}, INDEX1);
|
||||||
|
auto const ownerDirKk =
|
||||||
|
ripple::keylet::ownerDir(GetAccountIDWithString(ACCOUNT)).key;
|
||||||
|
ON_CALL(*rawBackendPtr, doFetchLedgerObject(ownerDirKk, ledgerSeq, _))
|
||||||
|
.WillByDefault(Return(ownerDir.getSerializer().peekData()));
|
||||||
|
EXPECT_CALL(*rawBackendPtr, doFetchLedgerObject).Times(2);
|
||||||
|
std::vector<Blob> bbs;
|
||||||
|
auto const line1 = CreateRippleStateLedgerObject(
|
||||||
|
ACCOUNT, "USD", ISSUER, 100, ACCOUNT, 10, ACCOUNT2, 20, TXNID, 123);
|
||||||
|
bbs.push_back(line1.getSerializer().peekData());
|
||||||
|
|
||||||
|
ON_CALL(*rawBackendPtr, doFetchLedgerObjects).WillByDefault(Return(bbs));
|
||||||
|
EXPECT_CALL(*rawBackendPtr, doFetchLedgerObjects).Times(1);
|
||||||
|
auto const static input = boost::json::parse(fmt::format(
|
||||||
|
R"({{
|
||||||
|
"account":"{}",
|
||||||
|
"ledger_index":{}
|
||||||
|
}})",
|
||||||
|
ACCOUNT,
|
||||||
|
ledgerSeq));
|
||||||
|
auto const handler = AnyHandler{AccountCurrenciesHandler{mockBackendPtr}};
|
||||||
|
boost::asio::spawn(ctx, [&](boost::asio::yield_context yield) {
|
||||||
|
auto const output = handler.process(input, yield);
|
||||||
|
ASSERT_TRUE(output);
|
||||||
|
EXPECT_EQ(
|
||||||
|
(*output).as_object().at("ledger_index").as_uint64(), ledgerSeq);
|
||||||
|
});
|
||||||
|
ctx.run();
|
||||||
|
}
|
||||||
@@ -177,3 +177,23 @@ struct MockBackendTest : virtual public NoLoggerFixture
|
|||||||
protected:
|
protected:
|
||||||
std::shared_ptr<BackendInterface> mockBackendPtr;
|
std::shared_ptr<BackendInterface> mockBackendPtr;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Fixture with an mock backend and an embedded boost::asio context
|
||||||
|
* Handler unittest base class
|
||||||
|
*/
|
||||||
|
class HandlerBaseTest : public MockBackendTest, public SyncAsioContextTest
|
||||||
|
{
|
||||||
|
void
|
||||||
|
SetUp() override
|
||||||
|
{
|
||||||
|
MockBackendTest::SetUp();
|
||||||
|
SyncAsioContextTest::SetUp();
|
||||||
|
}
|
||||||
|
void
|
||||||
|
TearDown() override
|
||||||
|
{
|
||||||
|
SyncAsioContextTest::TearDown();
|
||||||
|
MockBackendTest::TearDown();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|||||||
@@ -153,6 +153,12 @@ CreateCreateOfferTransactionObject(
|
|||||||
ripple::Issue
|
ripple::Issue
|
||||||
GetIssue(std::string_view currency, std::string_view issuerId)
|
GetIssue(std::string_view currency, std::string_view issuerId)
|
||||||
{
|
{
|
||||||
|
// standard currency
|
||||||
|
if (currency.size() == 3)
|
||||||
|
return ripple::Issue(
|
||||||
|
ripple::to_currency(std::string(currency)),
|
||||||
|
ripple::parseBase58<ripple::AccountID>(std::string(issuerId))
|
||||||
|
.value());
|
||||||
return ripple::Issue(
|
return ripple::Issue(
|
||||||
ripple::Currency{currency},
|
ripple::Currency{currency},
|
||||||
ripple::parseBase58<ripple::AccountID>(std::string(issuerId)).value());
|
ripple::parseBase58<ripple::AccountID>(std::string(issuerId)).value());
|
||||||
@@ -285,3 +291,33 @@ CreatePaymentChannelLedgerObject(
|
|||||||
channel.setFieldVL(ripple::sfPublicKey, slice);
|
channel.setFieldVL(ripple::sfPublicKey, slice);
|
||||||
return channel;
|
return channel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] ripple::STObject
|
||||||
|
CreateRippleStateLedgerObject(
|
||||||
|
std::string_view accountId,
|
||||||
|
std::string_view currency,
|
||||||
|
std::string_view issuerId,
|
||||||
|
int balance,
|
||||||
|
std::string_view lowNodeAccountId,
|
||||||
|
int lowLimit,
|
||||||
|
std::string_view highNodeAccountId,
|
||||||
|
int highLimit,
|
||||||
|
std::string_view previousTxnId,
|
||||||
|
uint32_t previousTxnSeq)
|
||||||
|
{
|
||||||
|
auto line = ripple::STObject(ripple::sfLedgerEntry);
|
||||||
|
line.setFieldU16(ripple::sfLedgerEntryType, ripple::ltRIPPLE_STATE);
|
||||||
|
line.setFieldU32(ripple::sfFlags, 0);
|
||||||
|
line.setFieldAmount(
|
||||||
|
ripple::sfBalance,
|
||||||
|
ripple::STAmount(GetIssue(currency, issuerId), balance));
|
||||||
|
line.setFieldAmount(
|
||||||
|
ripple::sfHighLimit,
|
||||||
|
ripple::STAmount(GetIssue(currency, highNodeAccountId), highLimit));
|
||||||
|
line.setFieldAmount(
|
||||||
|
ripple::sfLowLimit,
|
||||||
|
ripple::STAmount(GetIssue(currency, lowNodeAccountId), lowLimit));
|
||||||
|
line.setFieldH256(ripple::sfPreviousTxnID, ripple::uint256{previousTxnId});
|
||||||
|
line.setFieldU32(ripple::sfPreviousTxnLgrSeq, previousTxnSeq);
|
||||||
|
return line;
|
||||||
|
}
|
||||||
|
|||||||
@@ -156,3 +156,16 @@ CreatePaymentChannelLedgerObject(
|
|||||||
uint32_t settleDelay,
|
uint32_t settleDelay,
|
||||||
std::string_view previousTxnId,
|
std::string_view previousTxnId,
|
||||||
uint32_t previousTxnSeq);
|
uint32_t previousTxnSeq);
|
||||||
|
|
||||||
|
[[nodiscard]] ripple::STObject
|
||||||
|
CreateRippleStateLedgerObject(
|
||||||
|
std::string_view accountId,
|
||||||
|
std::string_view currency,
|
||||||
|
std::string_view issuerId,
|
||||||
|
int balance,
|
||||||
|
std::string_view lowNodeAccountId,
|
||||||
|
int lowLimit,
|
||||||
|
std::string_view highNodeAccountId,
|
||||||
|
int highLimit,
|
||||||
|
std::string_view previousTxnId,
|
||||||
|
uint32_t previousTxnSeq);
|
||||||
|
|||||||
Reference in New Issue
Block a user