mirror of
https://github.com/XRPLF/clio.git
synced 2025-12-06 17:27:58 +00:00
@@ -24,7 +24,7 @@ class Clio(ConanFile):
|
||||
'fmt/10.0.0',
|
||||
'grpc/1.50.1',
|
||||
'openssl/1.1.1u',
|
||||
'xrpl/1.12.0-b2',
|
||||
'xrpl/1.12.0',
|
||||
]
|
||||
|
||||
default_options = {
|
||||
|
||||
@@ -19,10 +19,15 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <util/JsonUtils.h>
|
||||
|
||||
#include <ripple/protocol/jss.h>
|
||||
|
||||
/** @brief Helper macro for borrowing from ripple::jss static (J)son (S)trings. */
|
||||
#define JS(x) ripple::jss::x.c_str()
|
||||
|
||||
/** @brief Access the lower case copy of a static (J)son (S)tring. */
|
||||
#define JSL(x) util::toLower(JS(x))
|
||||
|
||||
/** @brief Provides access to (SF)ield name (S)trings. */
|
||||
#define SFS(x) ripple::x.jsonName.c_str()
|
||||
|
||||
@@ -141,6 +141,7 @@ accountFromStringStrict(std::string const& account)
|
||||
else
|
||||
return {};
|
||||
}
|
||||
|
||||
std::pair<std::shared_ptr<ripple::STTx const>, std::shared_ptr<ripple::STObject const>>
|
||||
deserializeTxPlusMeta(data::TransactionAndMetadata const& blobs)
|
||||
{
|
||||
|
||||
@@ -22,6 +22,9 @@
|
||||
#include <rpc/common/Concepts.h>
|
||||
#include <rpc/common/Specs.h>
|
||||
#include <rpc/common/Types.h>
|
||||
#include <util/JsonUtils.h>
|
||||
|
||||
#include <string_view>
|
||||
|
||||
namespace rpc::modifiers {
|
||||
|
||||
@@ -68,4 +71,32 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Convert input string to lower case.
|
||||
*
|
||||
* Note: the conversion is only performed if the input value is a string.
|
||||
*/
|
||||
struct ToLower final
|
||||
{
|
||||
/**
|
||||
* @brief Update the input string to lower case.
|
||||
*
|
||||
* @param value The JSON value representing the outer object
|
||||
* @param key The key used to retrieve the modified value from the outer object
|
||||
* @return Possibly an error
|
||||
*/
|
||||
[[nodiscard]] MaybeError
|
||||
modify(boost::json::value& value, std::string_view key) const
|
||||
{
|
||||
if (not value.is_object() or not value.as_object().contains(key.data()))
|
||||
return {}; // ignore. field does not exist, let 'required' fail instead
|
||||
|
||||
if (not value.as_object().at(key.data()).is_string())
|
||||
return {}; // ignore for non-string types
|
||||
|
||||
value.as_object()[key.data()] = util::toLower(value.as_object().at(key.data()).as_string().c_str());
|
||||
return {};
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace rpc::modifiers
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
|
||||
namespace rpc {
|
||||
|
||||
// found here : https://xrpl.org/ledger_entry.html#:~:text=valid%20fields%20are%3A-,index,-account_root
|
||||
std::unordered_map<std::string, ripple::LedgerEntryType> const AccountObjectsHandler::TYPESMAP{
|
||||
{"state", ripple::ltRIPPLE_STATE},
|
||||
{"ticket", ripple::ltTICKET},
|
||||
|
||||
@@ -18,10 +18,50 @@
|
||||
//==============================================================================
|
||||
|
||||
#include <rpc/handlers/AccountTx.h>
|
||||
#include <util/JsonUtils.h>
|
||||
#include <util/Profiler.h>
|
||||
|
||||
namespace rpc {
|
||||
|
||||
// found here : https://xrpl.org/transaction-types.html
|
||||
// TODO [https://github.com/XRPLF/clio/issues/856]: add AMMBid, AMMCreate, AMMDelete, AMMDeposit, AMMVote, AMMWithdraw
|
||||
std::unordered_map<std::string, ripple::TxType> const AccountTxHandler::TYPESMAP{
|
||||
{JSL(AccountSet), ripple::ttACCOUNT_SET},
|
||||
{JSL(AccountDelete), ripple::ttACCOUNT_DELETE},
|
||||
{JSL(CheckCancel), ripple::ttCHECK_CANCEL},
|
||||
{JSL(CheckCash), ripple::ttCHECK_CASH},
|
||||
{JSL(CheckCreate), ripple::ttCHECK_CREATE},
|
||||
{JSL(Clawback), ripple::ttCLAWBACK},
|
||||
{JSL(DepositPreauth), ripple::ttDEPOSIT_PREAUTH},
|
||||
{JSL(EscrowCancel), ripple::ttESCROW_CANCEL},
|
||||
{JSL(EscrowCreate), ripple::ttESCROW_CREATE},
|
||||
{JSL(EscrowFinish), ripple::ttESCROW_FINISH},
|
||||
{JSL(NFTokenAcceptOffer), ripple::ttNFTOKEN_ACCEPT_OFFER},
|
||||
{JSL(NFTokenBurn), ripple::ttNFTOKEN_BURN},
|
||||
{JSL(NFTokenCancelOffer), ripple::ttNFTOKEN_CANCEL_OFFER},
|
||||
{JSL(NFTokenCreateOffer), ripple::ttNFTOKEN_CREATE_OFFER},
|
||||
{JSL(NFTokenMint), ripple::ttNFTOKEN_MINT},
|
||||
{JSL(OfferCancel), ripple::ttOFFER_CANCEL},
|
||||
{JSL(OfferCreate), ripple::ttOFFER_CREATE},
|
||||
{JSL(Payment), ripple::ttPAYMENT},
|
||||
{JSL(PaymentChannelClaim), ripple::ttPAYCHAN_CLAIM},
|
||||
{JSL(PaymentChannelCreate), ripple::ttCHECK_CREATE},
|
||||
{JSL(PaymentChannelFund), ripple::ttPAYCHAN_FUND},
|
||||
{JSL(SetRegularKey), ripple::ttREGULAR_KEY_SET},
|
||||
{JSL(SignerListSet), ripple::ttSIGNER_LIST_SET},
|
||||
{JSL(TicketCreate), ripple::ttTICKET_CREATE},
|
||||
{JSL(TrustSet), ripple::ttTRUST_SET},
|
||||
};
|
||||
|
||||
// TODO: should be std::views::keys when clang supports it
|
||||
std::unordered_set<std::string> const AccountTxHandler::TYPES_KEYS = [] {
|
||||
std::unordered_set<std::string> keys;
|
||||
std::transform(TYPESMAP.begin(), TYPESMAP.end(), std::inserter(keys, keys.begin()), [](auto const& pair) {
|
||||
return pair.first;
|
||||
});
|
||||
return keys;
|
||||
}();
|
||||
|
||||
// TODO: this is currently very similar to nft_history but its own copy for time
|
||||
// being. we should aim to reuse common logic in some way in the future.
|
||||
AccountTxHandler::Result
|
||||
@@ -116,6 +156,18 @@ AccountTxHandler::process(AccountTxHandler::Input input, Context const& ctx) con
|
||||
auto [txn, meta] = toExpandedJson(txnPlusMeta, NFTokenjson::ENABLE);
|
||||
obj[JS(meta)] = std::move(meta);
|
||||
obj[JS(tx)] = std::move(txn);
|
||||
|
||||
if (obj[JS(tx)].as_object().contains(JS(TransactionType)))
|
||||
{
|
||||
auto const objTransactionType = obj[JS(tx)].as_object()[JS(TransactionType)];
|
||||
auto const strType = util::toLower(objTransactionType.as_string().c_str());
|
||||
|
||||
// if transactionType does not match
|
||||
if (input.transactionType.has_value() && AccountTxHandler::TYPESMAP.contains(strType) &&
|
||||
AccountTxHandler::TYPESMAP.at(strType) != input.transactionType.value())
|
||||
continue;
|
||||
}
|
||||
|
||||
obj[JS(tx)].as_object()[JS(ledger_index)] = txnPlusMeta.ledgerSequence;
|
||||
obj[JS(tx)].as_object()[JS(date)] = txnPlusMeta.date;
|
||||
}
|
||||
@@ -208,6 +260,12 @@ tag_invoke(boost::json::value_to_tag<AccountTxHandler::Input>, boost::json::valu
|
||||
jsonObject.at(JS(marker)).as_object().at(JS(ledger)).as_int64(),
|
||||
jsonObject.at(JS(marker)).as_object().at(JS(seq)).as_int64()};
|
||||
|
||||
if (jsonObject.contains("tx_type"))
|
||||
{
|
||||
auto objTransactionType = jsonObject.at("tx_type");
|
||||
input.transactionType = AccountTxHandler::TYPESMAP.at(objTransactionType.as_string().c_str());
|
||||
}
|
||||
|
||||
return input;
|
||||
}
|
||||
|
||||
|
||||
@@ -39,6 +39,9 @@ class AccountTxHandler
|
||||
util::Logger log_{"RPC"};
|
||||
std::shared_ptr<BackendInterface> sharedPtrBackend_;
|
||||
|
||||
static std::unordered_map<std::string, ripple::TxType> const TYPESMAP;
|
||||
static const std::unordered_set<std::string> TYPES_KEYS;
|
||||
|
||||
public:
|
||||
// no max limit
|
||||
static auto constexpr LIMIT_MIN = 1;
|
||||
@@ -77,6 +80,7 @@ public:
|
||||
bool forward = false;
|
||||
std::optional<uint32_t> limit;
|
||||
std::optional<Marker> marker;
|
||||
std::optional<ripple::TxType> transactionType;
|
||||
};
|
||||
|
||||
using Result = HandlerReturnType<Output>;
|
||||
@@ -109,6 +113,12 @@ public:
|
||||
{JS(ledger), validation::Required{}, validation::Type<uint32_t>{}},
|
||||
{JS(seq), validation::Required{}, validation::Type<uint32_t>{}},
|
||||
}},
|
||||
{
|
||||
"tx_type",
|
||||
validation::Type<std::string>{},
|
||||
modifiers::ToLower{},
|
||||
validation::OneOf<std::string>(TYPES_KEYS.cbegin(), TYPES_KEYS.cend()),
|
||||
},
|
||||
};
|
||||
|
||||
return rpcSpec;
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
|
||||
#include <boost/json.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <string>
|
||||
|
||||
/**
|
||||
@@ -28,6 +30,13 @@
|
||||
*/
|
||||
namespace util {
|
||||
|
||||
inline std::string
|
||||
toLower(std::string str)
|
||||
{
|
||||
std::transform(std::begin(str), std::end(str), std::begin(str), [](unsigned char c) { return std::tolower(c); });
|
||||
return str;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Removes any detected secret information from a response JSON object.
|
||||
*
|
||||
|
||||
@@ -568,3 +568,25 @@ TEST_F(RPCBaseTest, ClampingModifier)
|
||||
ASSERT_TRUE(spec.process(passingInput3));
|
||||
ASSERT_EQ(passingInput3.at("amount").as_uint64(), 20u); // clamped
|
||||
}
|
||||
|
||||
TEST_F(RPCBaseTest, ToLowerModifier)
|
||||
{
|
||||
auto spec = RpcSpec{
|
||||
{"str", ToLower{}},
|
||||
};
|
||||
|
||||
auto passingInput = json::parse(R"({ "str": "TesT" })");
|
||||
ASSERT_TRUE(spec.process(passingInput));
|
||||
ASSERT_EQ(passingInput.at("str").as_string(), "test");
|
||||
|
||||
auto passingInput2 = json::parse(R"({ "str2": "TesT" })");
|
||||
ASSERT_TRUE(spec.process(passingInput2)); // no str no problem
|
||||
|
||||
auto passingInput3 = json::parse(R"({ "str": "already lower case" })");
|
||||
ASSERT_TRUE(spec.process(passingInput3));
|
||||
ASSERT_EQ(passingInput3.at("str").as_string(), "already lower case");
|
||||
|
||||
auto passingInput4 = json::parse(R"({ "str": "" })");
|
||||
ASSERT_TRUE(spec.process(passingInput4)); // empty str no problem
|
||||
ASSERT_EQ(passingInput4.at("str").as_string(), "");
|
||||
}
|
||||
|
||||
@@ -311,7 +311,7 @@ TEST_F(RPCAccountTxHandlerTest, IndexSpecificForwardTrue)
|
||||
auto const handler = AnyHandler{AccountTxHandler{mockBackendPtr}};
|
||||
auto const static input = json::parse(fmt::format(
|
||||
R"({{
|
||||
"account":"{}",
|
||||
"account": "{}",
|
||||
"ledger_index_min": {},
|
||||
"ledger_index_max": {},
|
||||
"forward": true
|
||||
@@ -324,7 +324,7 @@ TEST_F(RPCAccountTxHandlerTest, IndexSpecificForwardTrue)
|
||||
EXPECT_EQ(output->at("account").as_string(), ACCOUNT);
|
||||
EXPECT_EQ(output->at("ledger_index_min").as_uint64(), MINSEQ + 1);
|
||||
EXPECT_EQ(output->at("ledger_index_max").as_uint64(), MAXSEQ - 1);
|
||||
EXPECT_EQ(output->at("marker").as_object(), json::parse(R"({"ledger":12,"seq":34})"));
|
||||
EXPECT_EQ(output->at("marker").as_object(), json::parse(R"({"ledger": 12, "seq": 34})"));
|
||||
EXPECT_EQ(output->at("transactions").as_array().size(), 2);
|
||||
EXPECT_FALSE(output->as_object().contains("limit"));
|
||||
});
|
||||
@@ -352,7 +352,7 @@ TEST_F(RPCAccountTxHandlerTest, IndexSpecificForwardFalse)
|
||||
auto const handler = AnyHandler{AccountTxHandler{mockBackendPtr}};
|
||||
auto const static input = json::parse(fmt::format(
|
||||
R"({{
|
||||
"account":"{}",
|
||||
"account": "{}",
|
||||
"ledger_index_min": {},
|
||||
"ledger_index_max": {},
|
||||
"forward": false
|
||||
@@ -365,7 +365,7 @@ TEST_F(RPCAccountTxHandlerTest, IndexSpecificForwardFalse)
|
||||
EXPECT_EQ(output->at("account").as_string(), ACCOUNT);
|
||||
EXPECT_EQ(output->at("ledger_index_min").as_uint64(), MINSEQ + 1);
|
||||
EXPECT_EQ(output->at("ledger_index_max").as_uint64(), MAXSEQ - 1);
|
||||
EXPECT_EQ(output->at("marker").as_object(), json::parse(R"({"ledger":12,"seq":34})"));
|
||||
EXPECT_EQ(output->at("marker").as_object(), json::parse(R"({"ledger": 12, "seq": 34})"));
|
||||
EXPECT_EQ(output->at("transactions").as_array().size(), 2);
|
||||
EXPECT_FALSE(output->as_object().contains("limit"));
|
||||
});
|
||||
@@ -393,7 +393,7 @@ TEST_F(RPCAccountTxHandlerTest, IndexNotSpecificForwardTrue)
|
||||
auto const handler = AnyHandler{AccountTxHandler{mockBackendPtr}};
|
||||
auto const static input = json::parse(fmt::format(
|
||||
R"({{
|
||||
"account":"{}",
|
||||
"account": "{}",
|
||||
"ledger_index_min": {},
|
||||
"ledger_index_max": {},
|
||||
"forward": true
|
||||
@@ -406,7 +406,7 @@ TEST_F(RPCAccountTxHandlerTest, IndexNotSpecificForwardTrue)
|
||||
EXPECT_EQ(output->at("account").as_string(), ACCOUNT);
|
||||
EXPECT_EQ(output->at("ledger_index_min").as_uint64(), MINSEQ);
|
||||
EXPECT_EQ(output->at("ledger_index_max").as_uint64(), MAXSEQ);
|
||||
EXPECT_EQ(output->at("marker").as_object(), json::parse(R"({"ledger":12,"seq":34})"));
|
||||
EXPECT_EQ(output->at("marker").as_object(), json::parse(R"({"ledger": 12, "seq": 34})"));
|
||||
EXPECT_EQ(output->at("transactions").as_array().size(), 2);
|
||||
EXPECT_FALSE(output->as_object().contains("limit"));
|
||||
});
|
||||
@@ -434,7 +434,7 @@ TEST_F(RPCAccountTxHandlerTest, IndexNotSpecificForwardFalse)
|
||||
auto const handler = AnyHandler{AccountTxHandler{mockBackendPtr}};
|
||||
auto const static input = json::parse(fmt::format(
|
||||
R"({{
|
||||
"account":"{}",
|
||||
"account": "{}",
|
||||
"ledger_index_min": {},
|
||||
"ledger_index_max": {},
|
||||
"forward": false
|
||||
@@ -447,7 +447,7 @@ TEST_F(RPCAccountTxHandlerTest, IndexNotSpecificForwardFalse)
|
||||
EXPECT_EQ(output->at("account").as_string(), ACCOUNT);
|
||||
EXPECT_EQ(output->at("ledger_index_min").as_uint64(), MINSEQ);
|
||||
EXPECT_EQ(output->at("ledger_index_max").as_uint64(), MAXSEQ);
|
||||
EXPECT_EQ(output->at("marker").as_object(), json::parse(R"({"ledger":12,"seq":34})"));
|
||||
EXPECT_EQ(output->at("marker").as_object(), json::parse(R"({"ledger": 12, "seq": 34})"));
|
||||
EXPECT_EQ(output->at("transactions").as_array().size(), 2);
|
||||
EXPECT_FALSE(output->as_object().contains("limit"));
|
||||
});
|
||||
@@ -475,7 +475,7 @@ TEST_F(RPCAccountTxHandlerTest, BinaryTrue)
|
||||
auto const handler = AnyHandler{AccountTxHandler{mockBackendPtr}};
|
||||
auto const static input = json::parse(fmt::format(
|
||||
R"({{
|
||||
"account":"{}",
|
||||
"account": "{}",
|
||||
"ledger_index_min": {},
|
||||
"ledger_index_max": {},
|
||||
"binary": true
|
||||
@@ -488,7 +488,7 @@ TEST_F(RPCAccountTxHandlerTest, BinaryTrue)
|
||||
EXPECT_EQ(output->at("account").as_string(), ACCOUNT);
|
||||
EXPECT_EQ(output->at("ledger_index_min").as_uint64(), MINSEQ);
|
||||
EXPECT_EQ(output->at("ledger_index_max").as_uint64(), MAXSEQ);
|
||||
EXPECT_EQ(output->at("marker").as_object(), json::parse(R"({"ledger":12,"seq":34})"));
|
||||
EXPECT_EQ(output->at("marker").as_object(), json::parse(R"({"ledger": 12, "seq": 34})"));
|
||||
EXPECT_EQ(output->at("transactions").as_array().size(), 2);
|
||||
EXPECT_EQ(
|
||||
output->at("transactions").as_array()[0].as_object().at("meta").as_string(),
|
||||
@@ -524,7 +524,7 @@ TEST_F(RPCAccountTxHandlerTest, LimitAndMarker)
|
||||
auto const handler = AnyHandler{AccountTxHandler{mockBackendPtr}};
|
||||
auto const static input = json::parse(fmt::format(
|
||||
R"({{
|
||||
"account":"{}",
|
||||
"account": "{}",
|
||||
"ledger_index_min": {},
|
||||
"ledger_index_max": {},
|
||||
"limit": 2,
|
||||
@@ -540,7 +540,7 @@ TEST_F(RPCAccountTxHandlerTest, LimitAndMarker)
|
||||
EXPECT_EQ(output->at("ledger_index_min").as_uint64(), MINSEQ);
|
||||
EXPECT_EQ(output->at("ledger_index_max").as_uint64(), MAXSEQ);
|
||||
EXPECT_EQ(output->at("limit").as_uint64(), 2);
|
||||
EXPECT_EQ(output->at("marker").as_object(), json::parse(R"({"ledger":12,"seq":34})"));
|
||||
EXPECT_EQ(output->at("marker").as_object(), json::parse(R"({"ledger": 12, "seq": 34})"));
|
||||
EXPECT_EQ(output->at("transactions").as_array().size(), 2);
|
||||
});
|
||||
}
|
||||
@@ -572,8 +572,8 @@ TEST_F(RPCAccountTxHandlerTest, SpecificLedgerIndex)
|
||||
auto const handler = AnyHandler{AccountTxHandler{mockBackendPtr}};
|
||||
auto const static input = json::parse(fmt::format(
|
||||
R"({{
|
||||
"account":"{}",
|
||||
"ledger_index":{}
|
||||
"account": "{}",
|
||||
"ledger_index": {}
|
||||
}})",
|
||||
ACCOUNT,
|
||||
MAXSEQ - 1));
|
||||
@@ -601,8 +601,8 @@ TEST_F(RPCAccountTxHandlerTest, SpecificNonexistLedgerIntIndex)
|
||||
auto const handler = AnyHandler{AccountTxHandler{mockBackendPtr}};
|
||||
auto const static input = json::parse(fmt::format(
|
||||
R"({{
|
||||
"account":"{}",
|
||||
"ledger_index":{}
|
||||
"account": "{}",
|
||||
"ledger_index": {}
|
||||
}})",
|
||||
ACCOUNT,
|
||||
MAXSEQ - 1));
|
||||
@@ -627,8 +627,8 @@ TEST_F(RPCAccountTxHandlerTest, SpecificNonexistLedgerStringIndex)
|
||||
auto const handler = AnyHandler{AccountTxHandler{mockBackendPtr}};
|
||||
auto const static input = json::parse(fmt::format(
|
||||
R"({{
|
||||
"account":"{}",
|
||||
"ledger_index":"{}"
|
||||
"account": "{}",
|
||||
"ledger_index": "{}"
|
||||
}})",
|
||||
ACCOUNT,
|
||||
MAXSEQ - 1));
|
||||
@@ -667,8 +667,8 @@ TEST_F(RPCAccountTxHandlerTest, SpecificLedgerHash)
|
||||
auto const handler = AnyHandler{AccountTxHandler{mockBackendPtr}};
|
||||
auto const static input = json::parse(fmt::format(
|
||||
R"({{
|
||||
"account":"{}",
|
||||
"ledger_hash":"{}"
|
||||
"account": "{}",
|
||||
"ledger_hash": "{}"
|
||||
}})",
|
||||
ACCOUNT,
|
||||
LEDGERHASH));
|
||||
@@ -710,8 +710,8 @@ TEST_F(RPCAccountTxHandlerTest, SpecificLedgerIndexValidated)
|
||||
auto const handler = AnyHandler{AccountTxHandler{mockBackendPtr}};
|
||||
auto const static input = json::parse(fmt::format(
|
||||
R"({{
|
||||
"account":"{}",
|
||||
"ledger_index":"validated"
|
||||
"account": "{}",
|
||||
"ledger_index": "validated"
|
||||
}})",
|
||||
ACCOUNT));
|
||||
auto const output = handler.process(input, Context{yield});
|
||||
@@ -747,7 +747,7 @@ TEST_F(RPCAccountTxHandlerTest, TxLessThanMinSeq)
|
||||
auto const handler = AnyHandler{AccountTxHandler{mockBackendPtr}};
|
||||
auto const static input = json::parse(fmt::format(
|
||||
R"({{
|
||||
"account":"{}",
|
||||
"account": "{}",
|
||||
"ledger_index_min": {},
|
||||
"ledger_index_max": {},
|
||||
"forward": false
|
||||
@@ -788,7 +788,7 @@ TEST_F(RPCAccountTxHandlerTest, TxLargerThanMaxSeq)
|
||||
auto const handler = AnyHandler{AccountTxHandler{mockBackendPtr}};
|
||||
auto const static input = json::parse(fmt::format(
|
||||
R"({{
|
||||
"account":"{}",
|
||||
"account": "{}",
|
||||
"ledger_index_min": {},
|
||||
"ledger_index_max": {},
|
||||
"forward": false
|
||||
@@ -803,7 +803,7 @@ TEST_F(RPCAccountTxHandlerTest, TxLargerThanMaxSeq)
|
||||
EXPECT_EQ(output->at("ledger_index_max").as_uint64(), MAXSEQ - 2);
|
||||
EXPECT_EQ(output->at("transactions").as_array().size(), 1);
|
||||
EXPECT_FALSE(output->as_object().contains("limit"));
|
||||
EXPECT_EQ(output->at("marker").as_object(), json::parse(R"({"ledger":12,"seq":34})"));
|
||||
EXPECT_EQ(output->at("marker").as_object(), json::parse(R"({"ledger": 12, "seq": 34})"));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -909,8 +909,8 @@ TEST_F(RPCAccountTxHandlerTest, NFTTxs)
|
||||
"date": 2
|
||||
},
|
||||
"validated": true
|
||||
},
|
||||
{
|
||||
},
|
||||
{
|
||||
"meta":
|
||||
{
|
||||
"AffectedNodes":
|
||||
@@ -1031,3 +1031,309 @@ TEST_F(RPCAccountTxHandlerTest, NFTTxs)
|
||||
EXPECT_EQ(*output, json::parse(OUT));
|
||||
});
|
||||
}
|
||||
|
||||
struct AccountTxTransactionBundle
|
||||
{
|
||||
std::string testName;
|
||||
std::string testJson;
|
||||
std::string result;
|
||||
};
|
||||
|
||||
// parameterized test cases for parameters check
|
||||
struct AccountTxTransactionTypeTest : public RPCAccountTxHandlerTest,
|
||||
public WithParamInterface<AccountTxTransactionBundle>
|
||||
{
|
||||
struct NameGenerator
|
||||
{
|
||||
template <class ParamType>
|
||||
std::string
|
||||
operator()(const testing::TestParamInfo<ParamType>& info) const
|
||||
{
|
||||
auto bundle = static_cast<AccountTxTransactionBundle>(info.param);
|
||||
return bundle.testName;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
static auto
|
||||
generateTransactionTypeTestValues()
|
||||
{
|
||||
return std::vector<AccountTxTransactionBundle>{
|
||||
AccountTxTransactionBundle{
|
||||
"AccountSet",
|
||||
R"({
|
||||
"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
||||
"ledger_index": "validated",
|
||||
"tx_type": "AccountSet"
|
||||
})",
|
||||
"[]"},
|
||||
AccountTxTransactionBundle{
|
||||
"AccountDelete",
|
||||
R"({
|
||||
"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
||||
"ledger_index": "validated",
|
||||
"tx_type": "AccountDelete"
|
||||
})",
|
||||
"[]"},
|
||||
AccountTxTransactionBundle{
|
||||
"CheckCancel",
|
||||
R"({
|
||||
"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
||||
"ledger_index": "validated",
|
||||
"tx_type": "CheckCancel"
|
||||
})",
|
||||
"[]"},
|
||||
AccountTxTransactionBundle{
|
||||
"CheckCash",
|
||||
R"({
|
||||
"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
||||
"ledger_index": "validated",
|
||||
"tx_type": "CheckCash"
|
||||
})",
|
||||
"[]"},
|
||||
AccountTxTransactionBundle{
|
||||
"CheckCreate",
|
||||
R"({
|
||||
"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
||||
"ledger_index": "validated",
|
||||
"tx_type": "CheckCreate"
|
||||
})",
|
||||
"[]"},
|
||||
AccountTxTransactionBundle{
|
||||
"Clawback",
|
||||
R"({
|
||||
"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
||||
"ledger_index": "validated",
|
||||
"tx_type": "Clawback"
|
||||
})",
|
||||
"[]"},
|
||||
AccountTxTransactionBundle{
|
||||
"DepositPreauth",
|
||||
R"({
|
||||
"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
||||
"ledger_index": "validated",
|
||||
"tx_type": "DepositPreauth"
|
||||
})",
|
||||
"[]"},
|
||||
AccountTxTransactionBundle{
|
||||
"EscrowCancel",
|
||||
R"({
|
||||
"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
||||
"ledger_index": "validated",
|
||||
"tx_type": "EscrowCancel"
|
||||
})",
|
||||
"[]"},
|
||||
AccountTxTransactionBundle{
|
||||
"EscrowCreate",
|
||||
R"({
|
||||
"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
||||
"ledger_index": "validated",
|
||||
"tx_type": "EscrowCreate"
|
||||
})",
|
||||
"[]"},
|
||||
AccountTxTransactionBundle{
|
||||
"EscrowFinish",
|
||||
R"({
|
||||
"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
||||
"ledger_index": "validated",
|
||||
"tx_type": "EscrowFinish"
|
||||
})",
|
||||
"[]"},
|
||||
AccountTxTransactionBundle{
|
||||
"NFTokenAcceptOffer",
|
||||
R"({
|
||||
"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
||||
"ledger_index": "validated",
|
||||
"tx_type": "NFTokenAcceptOffer"
|
||||
})",
|
||||
"[]"},
|
||||
AccountTxTransactionBundle{
|
||||
"NFTokenBurn",
|
||||
R"({
|
||||
"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
||||
"ledger_index": "validated",
|
||||
"tx_type": "NFTokenBurn"
|
||||
})",
|
||||
"[]"},
|
||||
AccountTxTransactionBundle{
|
||||
"NFTokenCancelOffer",
|
||||
R"({
|
||||
"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
||||
"ledger_index": "validated",
|
||||
"tx_type": "NFTokenCancelOffer"
|
||||
})",
|
||||
"[]"},
|
||||
AccountTxTransactionBundle{
|
||||
"NFTokenCreateOffer",
|
||||
R"({
|
||||
"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
||||
"ledger_index": "validated",
|
||||
"tx_type": "NFTokenCreateOffer"
|
||||
})",
|
||||
"[]"},
|
||||
AccountTxTransactionBundle{
|
||||
"NFTokenMint",
|
||||
R"({
|
||||
"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
||||
"ledger_index": "validated",
|
||||
"tx_type": "NFTokenMint"
|
||||
})",
|
||||
"[]"},
|
||||
AccountTxTransactionBundle{
|
||||
"OfferCancel",
|
||||
R"({
|
||||
"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
||||
"ledger_index": "validated",
|
||||
"tx_type": "OfferCancel"
|
||||
})",
|
||||
"[]"},
|
||||
AccountTxTransactionBundle{
|
||||
"OfferCreate",
|
||||
R"({
|
||||
"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
||||
"ledger_index": "validated",
|
||||
"tx_type": "OfferCreate"
|
||||
})",
|
||||
"[]"},
|
||||
AccountTxTransactionBundle{
|
||||
"Payment",
|
||||
R"({
|
||||
"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
||||
"ledger_index": "validated",
|
||||
"tx_type": "Payment"
|
||||
})",
|
||||
R"([
|
||||
{
|
||||
"meta": {
|
||||
"AffectedNodes": [
|
||||
{
|
||||
"ModifiedNode": {
|
||||
"FinalFields": {
|
||||
"Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
||||
"Balance": "22"
|
||||
},
|
||||
"LedgerEntryType": "AccountRoot"
|
||||
}
|
||||
},
|
||||
{
|
||||
"ModifiedNode": {
|
||||
"FinalFields": {
|
||||
"Account": "rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun",
|
||||
"Balance": "23"
|
||||
},
|
||||
"LedgerEntryType": "AccountRoot"
|
||||
}
|
||||
}],
|
||||
"TransactionIndex": 0,
|
||||
"TransactionResult": "tesSUCCESS",
|
||||
"delivered_amount": "unavailable"
|
||||
},
|
||||
"tx": {
|
||||
"Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
||||
"Amount": "1",
|
||||
"Destination": "rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun",
|
||||
"Fee": "1",
|
||||
"Sequence": 32,
|
||||
"SigningPubKey": "74657374",
|
||||
"TransactionType": "Payment",
|
||||
"hash": "51D2AAA6B8E4E16EF22F6424854283D8391B56875858A711B8CE4D5B9A422CC2",
|
||||
"ledger_index": 30,
|
||||
"date": 1
|
||||
},
|
||||
"validated": true
|
||||
}
|
||||
])"},
|
||||
AccountTxTransactionBundle{
|
||||
"PaymentChannelClaim",
|
||||
R"({
|
||||
"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
||||
"ledger_index": "validated",
|
||||
"tx_type": "PaymentChannelClaim"
|
||||
})",
|
||||
"[]"},
|
||||
AccountTxTransactionBundle{
|
||||
"PaymentChannelCreate",
|
||||
R"({
|
||||
"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
||||
"ledger_index": "validated",
|
||||
"tx_type": "PaymentChannelCreate"
|
||||
})",
|
||||
"[]"},
|
||||
AccountTxTransactionBundle{
|
||||
"PaymentChannelFund",
|
||||
R"({
|
||||
"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
||||
"ledger_index": "validated",
|
||||
"tx_type": "PaymentChannelFund"
|
||||
})",
|
||||
"[]"},
|
||||
AccountTxTransactionBundle{
|
||||
"SetRegularKey",
|
||||
R"({
|
||||
"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
||||
"ledger_index": "validated",
|
||||
"tx_type": "SetRegularKey"
|
||||
})",
|
||||
"[]"},
|
||||
AccountTxTransactionBundle{
|
||||
"SignerListSet",
|
||||
R"({
|
||||
"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
||||
"ledger_index": "validated",
|
||||
"tx_type": "SignerListSet"
|
||||
})",
|
||||
"[]"},
|
||||
AccountTxTransactionBundle{
|
||||
"TicketCreate",
|
||||
R"({
|
||||
"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
||||
"ledger_index": "validated",
|
||||
"tx_type": "TicketCreate"
|
||||
})",
|
||||
"[]"},
|
||||
AccountTxTransactionBundle{
|
||||
"TrustSet",
|
||||
R"({
|
||||
"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
||||
"ledger_index": "validated",
|
||||
"tx_type": "TrustSet"
|
||||
})",
|
||||
"[]"},
|
||||
};
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
RPCAccountTxTransactionTypeTest,
|
||||
AccountTxTransactionTypeTest,
|
||||
ValuesIn(generateTransactionTypeTestValues()),
|
||||
AccountTxTransactionTypeTest::NameGenerator{});
|
||||
|
||||
TEST_P(AccountTxTransactionTypeTest, SpecificTransactionType)
|
||||
{
|
||||
mockBackendPtr->updateRange(MINSEQ); // min
|
||||
mockBackendPtr->updateRange(MAXSEQ); // max
|
||||
MockBackend* rawBackendPtr = static_cast<MockBackend*>(mockBackendPtr.get());
|
||||
|
||||
auto const transactions = genTransactions(MAXSEQ, MAXSEQ - 1);
|
||||
auto const transCursor = TransactionsAndCursor{transactions, TransactionsCursor{12, 34}};
|
||||
ON_CALL(*rawBackendPtr, fetchAccountTransactions).WillByDefault(Return(transCursor));
|
||||
EXPECT_CALL(
|
||||
*rawBackendPtr, fetchAccountTransactions(_, _, false, Optional(Eq(TransactionsCursor{MAXSEQ, INT32_MAX})), _))
|
||||
.Times(1);
|
||||
|
||||
auto const ledgerinfo = CreateLedgerInfo(LEDGERHASH, MAXSEQ);
|
||||
EXPECT_CALL(*rawBackendPtr, fetchLedgerBySequence).Times(1);
|
||||
ON_CALL(*rawBackendPtr, fetchLedgerBySequence(MAXSEQ, _)).WillByDefault(Return(ledgerinfo));
|
||||
|
||||
auto const testBundle = GetParam();
|
||||
runSpawn([&, this](auto yield) {
|
||||
auto const handler = AnyHandler{AccountTxHandler{mockBackendPtr}};
|
||||
auto const req = json::parse(testBundle.testJson);
|
||||
auto const output = handler.process(req, Context{yield});
|
||||
EXPECT_TRUE(output);
|
||||
|
||||
auto const transactions = output->at("transactions").as_array();
|
||||
auto const jsonObject = json::parse(testBundle.result);
|
||||
EXPECT_EQ(jsonObject, transactions);
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user