mirror of
https://github.com/XRPLF/clio.git
synced 2025-11-11 23:35:52 +00:00
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
665fab183a | ||
|
|
b65ac67d17 | ||
|
|
7b18e28c47 | ||
|
|
4940d463dc | ||
|
|
c795cf371a | ||
|
|
e135aa49d5 |
@@ -24,6 +24,7 @@
|
|||||||
#include "rpc/Errors.hpp"
|
#include "rpc/Errors.hpp"
|
||||||
#include "rpc/JS.hpp"
|
#include "rpc/JS.hpp"
|
||||||
#include "rpc/common/Types.hpp"
|
#include "rpc/common/Types.hpp"
|
||||||
|
#include "util/AccountUtils.hpp"
|
||||||
#include "util/Profiler.hpp"
|
#include "util/Profiler.hpp"
|
||||||
#include "util/log/Logger.hpp"
|
#include "util/log/Logger.hpp"
|
||||||
#include "web/Context.hpp"
|
#include "web/Context.hpp"
|
||||||
@@ -186,14 +187,14 @@ accountFromStringStrict(std::string const& account)
|
|||||||
if (blob && ripple::publicKeyType(ripple::makeSlice(*blob))) {
|
if (blob && ripple::publicKeyType(ripple::makeSlice(*blob))) {
|
||||||
publicKey = ripple::PublicKey(ripple::Slice{blob->data(), blob->size()});
|
publicKey = ripple::PublicKey(ripple::Slice{blob->data(), blob->size()});
|
||||||
} else {
|
} else {
|
||||||
publicKey = ripple::parseBase58<ripple::PublicKey>(ripple::TokenType::AccountPublic, account);
|
publicKey = util::parseBase58Wrapper<ripple::PublicKey>(ripple::TokenType::AccountPublic, account);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<ripple::AccountID> result;
|
std::optional<ripple::AccountID> result;
|
||||||
if (publicKey) {
|
if (publicKey) {
|
||||||
result = ripple::calcAccountID(*publicKey);
|
result = ripple::calcAccountID(*publicKey);
|
||||||
} else {
|
} else {
|
||||||
result = ripple::parseBase58<ripple::AccountID>(account);
|
result = util::parseBase58Wrapper<ripple::AccountID>(account);
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
@@ -799,7 +800,7 @@ getAccountsFromTransaction(boost::json::object const& transaction)
|
|||||||
auto inObject = getAccountsFromTransaction(value.as_object());
|
auto inObject = getAccountsFromTransaction(value.as_object());
|
||||||
accounts.insert(accounts.end(), inObject.begin(), inObject.end());
|
accounts.insert(accounts.end(), inObject.begin(), inObject.end());
|
||||||
} else if (value.is_string()) {
|
} else if (value.is_string()) {
|
||||||
auto const account = ripple::parseBase58<ripple::AccountID>(boost::json::value_to<std::string>(value));
|
auto const account = util::parseBase58Wrapper<ripple::AccountID>(boost::json::value_to<std::string>(value));
|
||||||
if (account) {
|
if (account) {
|
||||||
accounts.push_back(*account);
|
accounts.push_back(*account);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ struct ReturnType {
|
|||||||
* @param warnings The warnings generated by the RPC call
|
* @param warnings The warnings generated by the RPC call
|
||||||
*/
|
*/
|
||||||
ReturnType(std::expected<boost::json::value, Status> result, boost::json::array warnings = {})
|
ReturnType(std::expected<boost::json::value, Status> result, boost::json::array warnings = {})
|
||||||
: result{std::move(result)}, warnings{std::move(warnings)}
|
: result{std::move(result)}, warnings(std::move(warnings))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
#include "rpc/Errors.hpp"
|
#include "rpc/Errors.hpp"
|
||||||
#include "rpc/RPCHelpers.hpp"
|
#include "rpc/RPCHelpers.hpp"
|
||||||
#include "rpc/common/Types.hpp"
|
#include "rpc/common/Types.hpp"
|
||||||
|
#include "util/AccountUtils.hpp"
|
||||||
|
|
||||||
#include <boost/json/object.hpp>
|
#include <boost/json/object.hpp>
|
||||||
#include <boost/json/value.hpp>
|
#include <boost/json/value.hpp>
|
||||||
@@ -29,6 +30,7 @@
|
|||||||
#include <fmt/core.h>
|
#include <fmt/core.h>
|
||||||
#include <ripple/basics/base_uint.h>
|
#include <ripple/basics/base_uint.h>
|
||||||
#include <ripple/protocol/AccountID.h>
|
#include <ripple/protocol/AccountID.h>
|
||||||
|
#include <ripple/protocol/ErrorCodes.h>
|
||||||
#include <ripple/protocol/UintTypes.h>
|
#include <ripple/protocol/UintTypes.h>
|
||||||
#include <ripple/protocol/tokens.h>
|
#include <ripple/protocol/tokens.h>
|
||||||
|
|
||||||
@@ -113,7 +115,7 @@ CustomValidator AccountBase58Validator =
|
|||||||
if (!value.is_string())
|
if (!value.is_string())
|
||||||
return Error{Status{RippledError::rpcINVALID_PARAMS, std::string(key) + "NotString"}};
|
return Error{Status{RippledError::rpcINVALID_PARAMS, std::string(key) + "NotString"}};
|
||||||
|
|
||||||
auto const account = ripple::parseBase58<ripple::AccountID>(boost::json::value_to<std::string>(value));
|
auto const account = util::parseBase58Wrapper<ripple::AccountID>(boost::json::value_to<std::string>(value));
|
||||||
if (!account || account->isZero())
|
if (!account || account->isZero())
|
||||||
return Error{Status{ClioError::rpcMALFORMED_ADDRESS}};
|
return Error{Status{ClioError::rpcMALFORMED_ADDRESS}};
|
||||||
|
|
||||||
@@ -140,8 +142,12 @@ CustomValidator CurrencyValidator =
|
|||||||
if (!value.is_string())
|
if (!value.is_string())
|
||||||
return Error{Status{RippledError::rpcINVALID_PARAMS, std::string(key) + "NotString"}};
|
return Error{Status{RippledError::rpcINVALID_PARAMS, std::string(key) + "NotString"}};
|
||||||
|
|
||||||
|
auto const currencyStr = boost::json::value_to<std::string>(value);
|
||||||
|
if (currencyStr.empty())
|
||||||
|
return Error{Status{RippledError::rpcINVALID_PARAMS, std::string(key) + "IsEmpty"}};
|
||||||
|
|
||||||
ripple::Currency currency;
|
ripple::Currency currency;
|
||||||
if (!ripple::to_currency(currency, boost::json::value_to<std::string>(value)))
|
if (!ripple::to_currency(currency, currencyStr))
|
||||||
return Error{Status{ClioError::rpcMALFORMED_CURRENCY, "malformedCurrency"}};
|
return Error{Status{ClioError::rpcMALFORMED_CURRENCY, "malformedCurrency"}};
|
||||||
|
|
||||||
return MaybeError{};
|
return MaybeError{};
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
#include "rpc/common/Specs.hpp"
|
#include "rpc/common/Specs.hpp"
|
||||||
#include "rpc/common/Types.hpp"
|
#include "rpc/common/Types.hpp"
|
||||||
#include "rpc/common/Validators.hpp"
|
#include "rpc/common/Validators.hpp"
|
||||||
|
#include "util/AccountUtils.hpp"
|
||||||
|
|
||||||
#include <boost/json/array.hpp>
|
#include <boost/json/array.hpp>
|
||||||
#include <boost/json/conversion.hpp>
|
#include <boost/json/conversion.hpp>
|
||||||
@@ -116,14 +117,14 @@ public:
|
|||||||
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};
|
||||||
auto const getAccountID = [](auto const& j) -> std::optional<ripple::AccountID> {
|
auto const getAccountID = [](auto const& j) -> std::optional<ripple::AccountID> {
|
||||||
if (j.is_string()) {
|
if (j.is_string()) {
|
||||||
auto const pk = ripple::parseBase58<ripple::PublicKey>(
|
auto const pk = util::parseBase58Wrapper<ripple::PublicKey>(
|
||||||
ripple::TokenType::AccountPublic, boost::json::value_to<std::string>(j)
|
ripple::TokenType::AccountPublic, boost::json::value_to<std::string>(j)
|
||||||
);
|
);
|
||||||
|
|
||||||
if (pk)
|
if (pk)
|
||||||
return ripple::calcAccountID(*pk);
|
return ripple::calcAccountID(*pk);
|
||||||
|
|
||||||
return ripple::parseBase58<ripple::AccountID>(boost::json::value_to<std::string>(j));
|
return util::parseBase58Wrapper<ripple::AccountID>(boost::json::value_to<std::string>(j));
|
||||||
}
|
}
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
#include "rpc/JS.hpp"
|
#include "rpc/JS.hpp"
|
||||||
#include "rpc/RPCHelpers.hpp"
|
#include "rpc/RPCHelpers.hpp"
|
||||||
#include "rpc/common/Types.hpp"
|
#include "rpc/common/Types.hpp"
|
||||||
|
#include "util/AccountUtils.hpp"
|
||||||
|
|
||||||
#include <boost/asio/spawn.hpp>
|
#include <boost/asio/spawn.hpp>
|
||||||
#include <boost/bimap/bimap.hpp>
|
#include <boost/bimap/bimap.hpp>
|
||||||
@@ -263,7 +264,7 @@ tag_invoke(boost::json::value_to_tag<GetAggregatePriceHandler::Input>, boost::js
|
|||||||
for (auto const& oracle : jsonObject.at(JS(oracles)).as_array()) {
|
for (auto const& oracle : jsonObject.at(JS(oracles)).as_array()) {
|
||||||
input.oracles.push_back(GetAggregatePriceHandler::Oracle{
|
input.oracles.push_back(GetAggregatePriceHandler::Oracle{
|
||||||
.documentId = boost::json::value_to<std::uint64_t>(oracle.as_object().at(JS(oracle_document_id))),
|
.documentId = boost::json::value_to<std::uint64_t>(oracle.as_object().at(JS(oracle_document_id))),
|
||||||
.account = *ripple::parseBase58<ripple::AccountID>(
|
.account = *util::parseBase58Wrapper<ripple::AccountID>(
|
||||||
boost::json::value_to<std::string>(oracle.as_object().at(JS(account)))
|
boost::json::value_to<std::string>(oracle.as_object().at(JS(account)))
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -163,10 +163,12 @@ public:
|
|||||||
static auto const rpcSpec = RpcSpec{
|
static auto const rpcSpec = RpcSpec{
|
||||||
{JS(ledger_hash), validation::Uint256HexStringValidator},
|
{JS(ledger_hash), validation::Uint256HexStringValidator},
|
||||||
{JS(ledger_index), validation::LedgerIndexValidator},
|
{JS(ledger_index), validation::LedgerIndexValidator},
|
||||||
// validate quoteAsset in accordance to the currency code found in XRPL doc:
|
// validate quoteAsset and base_asset in accordance to the currency code found in XRPL doc:
|
||||||
// https://xrpl.org/docs/references/protocol/data-types/currency-formats#currency-codes
|
// https://xrpl.org/docs/references/protocol/data-types/currency-formats#currency-codes
|
||||||
// usually Clio returns rpcMALFORMED_CURRENCY , return InvalidParam here just to mimic rippled
|
// usually Clio returns rpcMALFORMED_CURRENCY , return InvalidParam here just to mimic rippled
|
||||||
{JS(base_asset), validation::Required{}, validation::Type<std::string>{}},
|
{JS(base_asset),
|
||||||
|
validation::Required{},
|
||||||
|
meta::WithCustomError{validation::CurrencyValidator, Status(RippledError::rpcINVALID_PARAMS)}},
|
||||||
{JS(quote_asset),
|
{JS(quote_asset),
|
||||||
validation::Required{},
|
validation::Required{},
|
||||||
meta::WithCustomError{validation::CurrencyValidator, Status(RippledError::rpcINVALID_PARAMS)}},
|
meta::WithCustomError{validation::CurrencyValidator, Status(RippledError::rpcINVALID_PARAMS)}},
|
||||||
|
|||||||
@@ -70,9 +70,8 @@ public:
|
|||||||
* - ledger
|
* - ledger
|
||||||
* - type
|
* - type
|
||||||
*
|
*
|
||||||
* Clio will throw an error when `queue` is set to `true`
|
* Clio will throw an error when `queue`, `full` or `accounts` is set to `true`.
|
||||||
* or if `full` or `accounts` are used.
|
* @see https://github.com/XRPLF/clio/issues/603 and https://github.com/XRPLF/clio/issues/1537
|
||||||
* @see https://github.com/XRPLF/clio/issues/603
|
|
||||||
*/
|
*/
|
||||||
struct Input {
|
struct Input {
|
||||||
std::optional<std::string> ledgerHash;
|
std::optional<std::string> ledgerHash;
|
||||||
@@ -105,9 +104,9 @@ public:
|
|||||||
spec([[maybe_unused]] uint32_t apiVersion)
|
spec([[maybe_unused]] uint32_t apiVersion)
|
||||||
{
|
{
|
||||||
static auto const rpcSpec = RpcSpec{
|
static auto const rpcSpec = RpcSpec{
|
||||||
{JS(full), validation::NotSupported{}},
|
{JS(full), validation::Type<bool>{}, validation::NotSupported{true}},
|
||||||
{JS(full), check::Deprecated{}},
|
{JS(full), check::Deprecated{}},
|
||||||
{JS(accounts), validation::NotSupported{}},
|
{JS(accounts), validation::Type<bool>{}, validation::NotSupported{true}},
|
||||||
{JS(accounts), check::Deprecated{}},
|
{JS(accounts), check::Deprecated{}},
|
||||||
{JS(owner_funds), validation::Type<bool>{}},
|
{JS(owner_funds), validation::Type<bool>{}},
|
||||||
{JS(queue), validation::Type<bool>{}, validation::NotSupported{true}},
|
{JS(queue), validation::Type<bool>{}, validation::NotSupported{true}},
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
#include "rpc/JS.hpp"
|
#include "rpc/JS.hpp"
|
||||||
#include "rpc/RPCHelpers.hpp"
|
#include "rpc/RPCHelpers.hpp"
|
||||||
#include "rpc/common/Types.hpp"
|
#include "rpc/common/Types.hpp"
|
||||||
|
#include "util/AccountUtils.hpp"
|
||||||
|
|
||||||
#include <boost/json/conversion.hpp>
|
#include <boost/json/conversion.hpp>
|
||||||
#include <boost/json/object.hpp>
|
#include <boost/json/object.hpp>
|
||||||
@@ -62,9 +63,9 @@ LedgerEntryHandler::process(LedgerEntryHandler::Input input, Context const& ctx)
|
|||||||
if (input.index) {
|
if (input.index) {
|
||||||
key = ripple::uint256{std::string_view(*(input.index))};
|
key = ripple::uint256{std::string_view(*(input.index))};
|
||||||
} else if (input.accountRoot) {
|
} else if (input.accountRoot) {
|
||||||
key = ripple::keylet::account(*ripple::parseBase58<ripple::AccountID>(*(input.accountRoot))).key;
|
key = ripple::keylet::account(*util::parseBase58Wrapper<ripple::AccountID>(*(input.accountRoot))).key;
|
||||||
} else if (input.did) {
|
} else if (input.did) {
|
||||||
key = ripple::keylet::did(*ripple::parseBase58<ripple::AccountID>(*(input.did))).key;
|
key = ripple::keylet::did(*util::parseBase58Wrapper<ripple::AccountID>(*(input.did))).key;
|
||||||
} else if (input.directory) {
|
} else if (input.directory) {
|
||||||
auto const keyOrStatus = composeKeyFromDirectory(*input.directory);
|
auto const keyOrStatus = composeKeyFromDirectory(*input.directory);
|
||||||
if (auto const status = std::get_if<Status>(&keyOrStatus))
|
if (auto const status = std::get_if<Status>(&keyOrStatus))
|
||||||
@@ -73,13 +74,14 @@ LedgerEntryHandler::process(LedgerEntryHandler::Input input, Context const& ctx)
|
|||||||
key = std::get<ripple::uint256>(keyOrStatus);
|
key = std::get<ripple::uint256>(keyOrStatus);
|
||||||
} else if (input.offer) {
|
} else if (input.offer) {
|
||||||
auto const id =
|
auto const id =
|
||||||
ripple::parseBase58<ripple::AccountID>(boost::json::value_to<std::string>(input.offer->at(JS(account))));
|
util::parseBase58Wrapper<ripple::AccountID>(boost::json::value_to<std::string>(input.offer->at(JS(account)))
|
||||||
|
);
|
||||||
key = ripple::keylet::offer(*id, boost::json::value_to<std::uint32_t>(input.offer->at(JS(seq)))).key;
|
key = ripple::keylet::offer(*id, boost::json::value_to<std::uint32_t>(input.offer->at(JS(seq)))).key;
|
||||||
} else if (input.rippleStateAccount) {
|
} else if (input.rippleStateAccount) {
|
||||||
auto const id1 = ripple::parseBase58<ripple::AccountID>(
|
auto const id1 = util::parseBase58Wrapper<ripple::AccountID>(
|
||||||
boost::json::value_to<std::string>(input.rippleStateAccount->at(JS(accounts)).as_array().at(0))
|
boost::json::value_to<std::string>(input.rippleStateAccount->at(JS(accounts)).as_array().at(0))
|
||||||
);
|
);
|
||||||
auto const id2 = ripple::parseBase58<ripple::AccountID>(
|
auto const id2 = util::parseBase58Wrapper<ripple::AccountID>(
|
||||||
boost::json::value_to<std::string>(input.rippleStateAccount->at(JS(accounts)).as_array().at(1))
|
boost::json::value_to<std::string>(input.rippleStateAccount->at(JS(accounts)).as_array().at(1))
|
||||||
);
|
);
|
||||||
auto const currency =
|
auto const currency =
|
||||||
@@ -88,20 +90,22 @@ LedgerEntryHandler::process(LedgerEntryHandler::Input input, Context const& ctx)
|
|||||||
key = ripple::keylet::line(*id1, *id2, currency).key;
|
key = ripple::keylet::line(*id1, *id2, currency).key;
|
||||||
} else if (input.escrow) {
|
} else if (input.escrow) {
|
||||||
auto const id =
|
auto const id =
|
||||||
ripple::parseBase58<ripple::AccountID>(boost::json::value_to<std::string>(input.escrow->at(JS(owner))));
|
util::parseBase58Wrapper<ripple::AccountID>(boost::json::value_to<std::string>(input.escrow->at(JS(owner)))
|
||||||
|
);
|
||||||
key = ripple::keylet::escrow(*id, input.escrow->at(JS(seq)).as_int64()).key;
|
key = ripple::keylet::escrow(*id, input.escrow->at(JS(seq)).as_int64()).key;
|
||||||
} else if (input.depositPreauth) {
|
} else if (input.depositPreauth) {
|
||||||
auto const owner = ripple::parseBase58<ripple::AccountID>(
|
auto const owner = util::parseBase58Wrapper<ripple::AccountID>(
|
||||||
boost::json::value_to<std::string>(input.depositPreauth->at(JS(owner)))
|
boost::json::value_to<std::string>(input.depositPreauth->at(JS(owner)))
|
||||||
);
|
);
|
||||||
auto const authorized = ripple::parseBase58<ripple::AccountID>(
|
auto const authorized = util::parseBase58Wrapper<ripple::AccountID>(
|
||||||
boost::json::value_to<std::string>(input.depositPreauth->at(JS(authorized)))
|
boost::json::value_to<std::string>(input.depositPreauth->at(JS(authorized)))
|
||||||
);
|
);
|
||||||
|
|
||||||
key = ripple::keylet::depositPreauth(*owner, *authorized).key;
|
key = ripple::keylet::depositPreauth(*owner, *authorized).key;
|
||||||
} else if (input.ticket) {
|
} else if (input.ticket) {
|
||||||
auto const id =
|
auto const id =
|
||||||
ripple::parseBase58<ripple::AccountID>(boost::json::value_to<std::string>(input.ticket->at(JS(account))));
|
util::parseBase58Wrapper<ripple::AccountID>(boost::json::value_to<std::string>(input.ticket->at(JS(account))
|
||||||
|
));
|
||||||
|
|
||||||
key = ripple::getTicketIndex(*id, input.ticket->at(JS(ticket_seq)).as_int64());
|
key = ripple::getTicketIndex(*id, input.ticket->at(JS(ticket_seq)).as_int64());
|
||||||
} else if (input.amm) {
|
} else if (input.amm) {
|
||||||
@@ -112,7 +116,8 @@ LedgerEntryHandler::process(LedgerEntryHandler::Input input, Context const& ctx)
|
|||||||
return ripple::xrpIssue();
|
return ripple::xrpIssue();
|
||||||
}
|
}
|
||||||
auto const issuer =
|
auto const issuer =
|
||||||
ripple::parseBase58<ripple::AccountID>(boost::json::value_to<std::string>(assetJson.at(JS(issuer))));
|
util::parseBase58Wrapper<ripple::AccountID>(boost::json::value_to<std::string>(assetJson.at(JS(issuer)))
|
||||||
|
);
|
||||||
return ripple::Issue{currency, *issuer};
|
return ripple::Issue{currency, *issuer};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -125,7 +130,7 @@ LedgerEntryHandler::process(LedgerEntryHandler::Input input, Context const& ctx)
|
|||||||
return Error{Status{ClioError::rpcMALFORMED_REQUEST}};
|
return Error{Status{ClioError::rpcMALFORMED_REQUEST}};
|
||||||
|
|
||||||
if (input.bridgeAccount) {
|
if (input.bridgeAccount) {
|
||||||
auto const bridgeAccount = ripple::parseBase58<ripple::AccountID>(*(input.bridgeAccount));
|
auto const bridgeAccount = util::parseBase58Wrapper<ripple::AccountID>(*(input.bridgeAccount));
|
||||||
auto const chainType = ripple::STXChainBridge::srcChain(bridgeAccount == input.bridge->lockingChainDoor());
|
auto const chainType = ripple::STXChainBridge::srcChain(bridgeAccount == input.bridge->lockingChainDoor());
|
||||||
|
|
||||||
if (bridgeAccount != input.bridge->door(chainType))
|
if (bridgeAccount != input.bridge->door(chainType))
|
||||||
@@ -201,7 +206,7 @@ LedgerEntryHandler::composeKeyFromDirectory(boost::json::object const& directory
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto const ownerID =
|
auto const ownerID =
|
||||||
ripple::parseBase58<ripple::AccountID>(boost::json::value_to<std::string>(directory.at(JS(owner))));
|
util::parseBase58Wrapper<ripple::AccountID>(boost::json::value_to<std::string>(directory.at(JS(owner))));
|
||||||
return ripple::keylet::page(ripple::keylet::ownerDir(*ownerID), subIndex).key;
|
return ripple::keylet::page(ripple::keylet::ownerDir(*ownerID), subIndex).key;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -262,10 +267,10 @@ tag_invoke(boost::json::value_to_tag<LedgerEntryHandler::Input>, boost::json::va
|
|||||||
};
|
};
|
||||||
|
|
||||||
auto const parseBridgeFromJson = [](boost::json::value const& bridgeJson) {
|
auto const parseBridgeFromJson = [](boost::json::value const& bridgeJson) {
|
||||||
auto const lockingDoor = *ripple::parseBase58<ripple::AccountID>(
|
auto const lockingDoor = *util::parseBase58Wrapper<ripple::AccountID>(
|
||||||
boost::json::value_to<std::string>(bridgeJson.at(ripple::sfLockingChainDoor.getJsonName().c_str()))
|
boost::json::value_to<std::string>(bridgeJson.at(ripple::sfLockingChainDoor.getJsonName().c_str()))
|
||||||
);
|
);
|
||||||
auto const issuingDoor = *ripple::parseBase58<ripple::AccountID>(
|
auto const issuingDoor = *util::parseBase58Wrapper<ripple::AccountID>(
|
||||||
boost::json::value_to<std::string>(bridgeJson.at(ripple::sfIssuingChainDoor.getJsonName().c_str()))
|
boost::json::value_to<std::string>(bridgeJson.at(ripple::sfIssuingChainDoor.getJsonName().c_str()))
|
||||||
);
|
);
|
||||||
auto const lockingIssue =
|
auto const lockingIssue =
|
||||||
@@ -278,7 +283,7 @@ tag_invoke(boost::json::value_to_tag<LedgerEntryHandler::Input>, boost::json::va
|
|||||||
|
|
||||||
auto const parseOracleFromJson = [](boost::json::value const& json) {
|
auto const parseOracleFromJson = [](boost::json::value const& json) {
|
||||||
auto const account =
|
auto const account =
|
||||||
ripple::parseBase58<ripple::AccountID>(boost::json::value_to<std::string>(json.at(JS(account))));
|
util::parseBase58Wrapper<ripple::AccountID>(boost::json::value_to<std::string>(json.at(JS(account))));
|
||||||
auto const documentId = boost::json::value_to<uint32_t>(json.at(JS(oracle_document_id)));
|
auto const documentId = boost::json::value_to<uint32_t>(json.at(JS(oracle_document_id)));
|
||||||
|
|
||||||
return ripple::keylet::oracle(*account, documentId).key;
|
return ripple::keylet::oracle(*account, documentId).key;
|
||||||
|
|||||||
@@ -28,6 +28,7 @@
|
|||||||
#include "rpc/common/Specs.hpp"
|
#include "rpc/common/Specs.hpp"
|
||||||
#include "rpc/common/Types.hpp"
|
#include "rpc/common/Types.hpp"
|
||||||
#include "rpc/common/Validators.hpp"
|
#include "rpc/common/Validators.hpp"
|
||||||
|
#include "util/AccountUtils.hpp"
|
||||||
|
|
||||||
#include <boost/json/conversion.hpp>
|
#include <boost/json/conversion.hpp>
|
||||||
#include <boost/json/object.hpp>
|
#include <boost/json/object.hpp>
|
||||||
@@ -136,9 +137,11 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto const id1 =
|
auto const id1 =
|
||||||
ripple::parseBase58<ripple::AccountID>(boost::json::value_to<std::string>(value.as_array()[0]));
|
util::parseBase58Wrapper<ripple::AccountID>(boost::json::value_to<std::string>(value.as_array()[0])
|
||||||
|
);
|
||||||
auto const id2 =
|
auto const id2 =
|
||||||
ripple::parseBase58<ripple::AccountID>(boost::json::value_to<std::string>(value.as_array()[1]));
|
util::parseBase58Wrapper<ripple::AccountID>(boost::json::value_to<std::string>(value.as_array()[1])
|
||||||
|
);
|
||||||
|
|
||||||
if (!id1 || !id2)
|
if (!id1 || !id2)
|
||||||
return Error{Status{ClioError::rpcMALFORMED_ADDRESS, "malformedAddresses"}};
|
return Error{Status{ClioError::rpcMALFORMED_ADDRESS, "malformedAddresses"}};
|
||||||
|
|||||||
67
src/util/AccountUtils.hpp
Normal file
67
src/util/AccountUtils.hpp
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
This file is part of clio: https://github.com/XRPLF/clio
|
||||||
|
Copyright (c) 2024, 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 <ripple/protocol/tokens.h>
|
||||||
|
|
||||||
|
#include <cctype>
|
||||||
|
#include <optional>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace util {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A wrapper of parseBase58 function. It adds the check if all characters in the input string are alphanumeric.
|
||||||
|
* If not, it returns an empty optional, instead of calling the parseBase58 function.
|
||||||
|
*
|
||||||
|
* @tparam T The type of the value to parse to.
|
||||||
|
* @param str The string to parse.
|
||||||
|
* @return An optional with the parsed value, or an empty optional if the parse fails.
|
||||||
|
*/
|
||||||
|
template <class T>
|
||||||
|
[[nodiscard]] std::optional<T>
|
||||||
|
parseBase58Wrapper(std::string const& str)
|
||||||
|
{
|
||||||
|
if (!std::all_of(std::begin(str), std::end(str), [](unsigned char c) { return std::isalnum(c); }))
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
return ripple::parseBase58<T>(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A wrapper of parseBase58 function. It add the check if all characters in the input string are alphanumeric. If
|
||||||
|
* not, it returns an empty optional, instead of calling the parseBase58 function.
|
||||||
|
*
|
||||||
|
* @tparam T The type of the value to parse to.
|
||||||
|
* @param type The type of the token to parse.
|
||||||
|
* @param str The string to parse.
|
||||||
|
* @return An optional with the parsed value, or an empty optional if the parse fails.
|
||||||
|
*/
|
||||||
|
template <class T>
|
||||||
|
[[nodiscard]] std::optional<T>
|
||||||
|
parseBase58Wrapper(ripple::TokenType type, std::string const& str)
|
||||||
|
{
|
||||||
|
if (!std::all_of(std::begin(str), std::end(str), [](unsigned char c) { return std::isalnum(c); }))
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
return ripple::parseBase58<T>(type, str);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace util
|
||||||
@@ -2,6 +2,7 @@ add_library(clio_testing_common)
|
|||||||
|
|
||||||
target_sources(
|
target_sources(
|
||||||
clio_testing_common PRIVATE util/StringUtils.cpp util/TestHttpServer.cpp util/TestWsServer.cpp util/TestObject.cpp
|
clio_testing_common PRIVATE util/StringUtils.cpp util/TestHttpServer.cpp util/TestWsServer.cpp util/TestObject.cpp
|
||||||
|
util/AssignRandomPort.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
include(deps/gtest)
|
include(deps/gtest)
|
||||||
|
|||||||
45
tests/common/util/AssignRandomPort.cpp
Normal file
45
tests/common/util/AssignRandomPort.cpp
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
This file is part of clio: https://github.com/XRPLF/clio
|
||||||
|
Copyright (c) 2024, 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 "util/AssignRandomPort.hpp"
|
||||||
|
|
||||||
|
#include <boost/asio/io_context.hpp>
|
||||||
|
#include <boost/asio/ip/tcp.hpp>
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
using tcp = boost::asio::ip::tcp;
|
||||||
|
|
||||||
|
namespace tests::util {
|
||||||
|
|
||||||
|
uint32_t
|
||||||
|
generateFreePort()
|
||||||
|
{
|
||||||
|
boost::asio::io_context io_context;
|
||||||
|
tcp::acceptor acceptor(io_context);
|
||||||
|
tcp::endpoint const endpoint(tcp::v4(), 0);
|
||||||
|
|
||||||
|
acceptor.open(endpoint.protocol());
|
||||||
|
acceptor.set_option(tcp::acceptor::reuse_address(true));
|
||||||
|
acceptor.bind(endpoint);
|
||||||
|
|
||||||
|
return acceptor.local_endpoint().port();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace tests::util
|
||||||
29
tests/common/util/AssignRandomPort.hpp
Normal file
29
tests/common/util/AssignRandomPort.hpp
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
This file is part of clio: https://github.com/XRPLF/clio
|
||||||
|
Copyright (c) 2024, 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 <cstdint>
|
||||||
|
|
||||||
|
namespace tests::util {
|
||||||
|
|
||||||
|
uint32_t
|
||||||
|
generateFreePort();
|
||||||
|
|
||||||
|
} // namespace tests::util
|
||||||
@@ -82,7 +82,7 @@ struct WithMockXrpLedgerAPIService : virtual ::testing::Test {
|
|||||||
WithMockXrpLedgerAPIService(std::string serverAddress)
|
WithMockXrpLedgerAPIService(std::string serverAddress)
|
||||||
{
|
{
|
||||||
grpc::ServerBuilder builder;
|
grpc::ServerBuilder builder;
|
||||||
builder.AddListeningPort(serverAddress, grpc::InsecureServerCredentials());
|
builder.AddListeningPort(serverAddress, grpc::InsecureServerCredentials(), &port_);
|
||||||
builder.RegisterService(&mockXrpLedgerAPIService);
|
builder.RegisterService(&mockXrpLedgerAPIService);
|
||||||
server_ = builder.BuildAndStart();
|
server_ = builder.BuildAndStart();
|
||||||
serverThread_ = std::thread([this] { server_->Wait(); });
|
serverThread_ = std::thread([this] { server_->Wait(); });
|
||||||
@@ -94,11 +94,17 @@ struct WithMockXrpLedgerAPIService : virtual ::testing::Test {
|
|||||||
serverThread_.join();
|
serverThread_.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
getXRPLMockPort() const
|
||||||
|
{
|
||||||
|
return port_;
|
||||||
|
}
|
||||||
MockXrpLedgerAPIService mockXrpLedgerAPIService;
|
MockXrpLedgerAPIService mockXrpLedgerAPIService;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<grpc::Server> server_;
|
std::unique_ptr<grpc::Server> server_;
|
||||||
std::thread serverThread_;
|
std::thread serverThread_;
|
||||||
|
int port_{};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace tests::util
|
} // namespace tests::util
|
||||||
|
|||||||
@@ -105,9 +105,9 @@ doSession(
|
|||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
TestHttpServer::TestHttpServer(boost::asio::io_context& context, std::string host, int const port) : acceptor_(context)
|
TestHttpServer::TestHttpServer(boost::asio::io_context& context, std::string host) : acceptor_(context)
|
||||||
{
|
{
|
||||||
boost::asio::ip::tcp::endpoint const endpoint(boost::asio::ip::make_address(host), port);
|
boost::asio::ip::tcp::endpoint const endpoint(boost::asio::ip::make_address(host), 0);
|
||||||
acceptor_.open(endpoint.protocol());
|
acceptor_.open(endpoint.protocol());
|
||||||
acceptor_.set_option(asio::socket_base::reuse_address(true));
|
acceptor_.set_option(asio::socket_base::reuse_address(true));
|
||||||
acceptor_.bind(endpoint);
|
acceptor_.bind(endpoint);
|
||||||
@@ -134,3 +134,9 @@ TestHttpServer::handleRequest(TestHttpServer::RequestHandler handler, bool const
|
|||||||
boost::asio::detached
|
boost::asio::detached
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string
|
||||||
|
TestHttpServer::port() const
|
||||||
|
{
|
||||||
|
return std::to_string(acceptor_.local_endpoint().port());
|
||||||
|
}
|
||||||
|
|||||||
@@ -41,9 +41,8 @@ public:
|
|||||||
*
|
*
|
||||||
* @param context boost::asio::io_context to use for networking
|
* @param context boost::asio::io_context to use for networking
|
||||||
* @param host host to bind to
|
* @param host host to bind to
|
||||||
* @param port port to bind to
|
|
||||||
*/
|
*/
|
||||||
TestHttpServer(boost::asio::io_context& context, std::string host, int port);
|
TestHttpServer(boost::asio::io_context& context, std::string host);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Start the server
|
* @brief Start the server
|
||||||
@@ -56,6 +55,14 @@ public:
|
|||||||
void
|
void
|
||||||
handleRequest(RequestHandler handler, bool allowToFail = false);
|
handleRequest(RequestHandler handler, bool allowToFail = false);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Return the port HTTP server is connected to
|
||||||
|
*
|
||||||
|
* @return string port number
|
||||||
|
*/
|
||||||
|
std::string
|
||||||
|
port() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
boost::asio::ip::tcp::acceptor acceptor_;
|
boost::asio::ip::tcp::acceptor acceptor_;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
|
|
||||||
#include "data/DBHelpers.hpp"
|
#include "data/DBHelpers.hpp"
|
||||||
#include "data/Types.hpp"
|
#include "data/Types.hpp"
|
||||||
|
#include "util/AccountUtils.hpp"
|
||||||
#include "util/Assert.hpp"
|
#include "util/Assert.hpp"
|
||||||
|
|
||||||
#include <ripple/basics/Blob.h>
|
#include <ripple/basics/Blob.h>
|
||||||
@@ -60,7 +61,7 @@ constexpr static auto INDEX1 = "1B8590C01B0006EDFA9ED60296DD052DC5E90F99659B2501
|
|||||||
ripple::AccountID
|
ripple::AccountID
|
||||||
GetAccountIDWithString(std::string_view id)
|
GetAccountIDWithString(std::string_view id)
|
||||||
{
|
{
|
||||||
return ripple::parseBase58<ripple::AccountID>(std::string(id)).value();
|
return util::parseBase58Wrapper<ripple::AccountID>(std::string(id)).value();
|
||||||
}
|
}
|
||||||
|
|
||||||
ripple::uint256
|
ripple::uint256
|
||||||
@@ -154,11 +155,11 @@ CreatePaymentTransactionObject(
|
|||||||
{
|
{
|
||||||
ripple::STObject obj(ripple::sfTransaction);
|
ripple::STObject obj(ripple::sfTransaction);
|
||||||
obj.setFieldU16(ripple::sfTransactionType, ripple::ttPAYMENT);
|
obj.setFieldU16(ripple::sfTransactionType, ripple::ttPAYMENT);
|
||||||
auto account = ripple::parseBase58<ripple::AccountID>(std::string(accountId1));
|
auto account = util::parseBase58Wrapper<ripple::AccountID>(std::string(accountId1));
|
||||||
obj.setAccountID(ripple::sfAccount, account.value());
|
obj.setAccountID(ripple::sfAccount, account.value());
|
||||||
obj.setFieldAmount(ripple::sfAmount, ripple::STAmount(amount, false));
|
obj.setFieldAmount(ripple::sfAmount, ripple::STAmount(amount, false));
|
||||||
obj.setFieldAmount(ripple::sfFee, ripple::STAmount(fee, false));
|
obj.setFieldAmount(ripple::sfFee, ripple::STAmount(fee, false));
|
||||||
auto account2 = ripple::parseBase58<ripple::AccountID>(std::string(accountId2));
|
auto account2 = util::parseBase58Wrapper<ripple::AccountID>(std::string(accountId2));
|
||||||
obj.setAccountID(ripple::sfDestination, account2.value());
|
obj.setAccountID(ripple::sfDestination, account2.value());
|
||||||
obj.setFieldU32(ripple::sfSequence, seq);
|
obj.setFieldU32(ripple::sfSequence, seq);
|
||||||
char const* key = "test";
|
char const* key = "test";
|
||||||
@@ -258,14 +259,14 @@ CreateCreateOfferTransactionObject(
|
|||||||
{
|
{
|
||||||
ripple::STObject obj(ripple::sfTransaction);
|
ripple::STObject obj(ripple::sfTransaction);
|
||||||
obj.setFieldU16(ripple::sfTransactionType, ripple::ttOFFER_CREATE);
|
obj.setFieldU16(ripple::sfTransactionType, ripple::ttOFFER_CREATE);
|
||||||
auto account = ripple::parseBase58<ripple::AccountID>(std::string(accountId));
|
auto account = util::parseBase58Wrapper<ripple::AccountID>(std::string(accountId));
|
||||||
obj.setAccountID(ripple::sfAccount, account.value());
|
obj.setAccountID(ripple::sfAccount, account.value());
|
||||||
auto amount = ripple::STAmount(fee, false);
|
auto amount = ripple::STAmount(fee, false);
|
||||||
obj.setFieldAmount(ripple::sfFee, amount);
|
obj.setFieldAmount(ripple::sfFee, amount);
|
||||||
obj.setFieldU32(ripple::sfSequence, seq);
|
obj.setFieldU32(ripple::sfSequence, seq);
|
||||||
// add amount
|
// add amount
|
||||||
ripple::Issue const issue1(
|
ripple::Issue const issue1(
|
||||||
ripple::Currency{currency}, ripple::parseBase58<ripple::AccountID>(std::string(issuer)).value()
|
ripple::Currency{currency}, util::parseBase58Wrapper<ripple::AccountID>(std::string(issuer)).value()
|
||||||
);
|
);
|
||||||
if (reverse) {
|
if (reverse) {
|
||||||
obj.setFieldAmount(ripple::sfTakerPays, ripple::STAmount(issue1, takerGets));
|
obj.setFieldAmount(ripple::sfTakerPays, ripple::STAmount(issue1, takerGets));
|
||||||
@@ -288,11 +289,11 @@ GetIssue(std::string_view currency, std::string_view issuerId)
|
|||||||
if (currency.size() == 3) {
|
if (currency.size() == 3) {
|
||||||
return ripple::Issue(
|
return ripple::Issue(
|
||||||
ripple::to_currency(std::string(currency)),
|
ripple::to_currency(std::string(currency)),
|
||||||
ripple::parseBase58<ripple::AccountID>(std::string(issuerId)).value()
|
util::parseBase58Wrapper<ripple::AccountID>(std::string(issuerId)).value()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return ripple::Issue(
|
return ripple::Issue(
|
||||||
ripple::Currency{currency}, ripple::parseBase58<ripple::AccountID>(std::string(issuerId)).value()
|
ripple::Currency{currency}, util::parseBase58Wrapper<ripple::AccountID>(std::string(issuerId)).value()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -636,7 +637,7 @@ CreateMintNFTTxWithMetadata(
|
|||||||
// tx
|
// tx
|
||||||
ripple::STObject tx(ripple::sfTransaction);
|
ripple::STObject tx(ripple::sfTransaction);
|
||||||
tx.setFieldU16(ripple::sfTransactionType, ripple::ttNFTOKEN_MINT);
|
tx.setFieldU16(ripple::sfTransactionType, ripple::ttNFTOKEN_MINT);
|
||||||
auto account = ripple::parseBase58<ripple::AccountID>(std::string(accountId));
|
auto account = util::parseBase58Wrapper<ripple::AccountID>(std::string(accountId));
|
||||||
tx.setAccountID(ripple::sfAccount, account.value());
|
tx.setAccountID(ripple::sfAccount, account.value());
|
||||||
auto amount = ripple::STAmount(fee, false);
|
auto amount = ripple::STAmount(fee, false);
|
||||||
tx.setFieldAmount(ripple::sfFee, amount);
|
tx.setFieldAmount(ripple::sfFee, amount);
|
||||||
@@ -693,7 +694,7 @@ CreateAcceptNFTOfferTxWithMetadata(std::string_view accountId, uint32_t seq, uin
|
|||||||
// tx
|
// tx
|
||||||
ripple::STObject tx(ripple::sfTransaction);
|
ripple::STObject tx(ripple::sfTransaction);
|
||||||
tx.setFieldU16(ripple::sfTransactionType, ripple::ttNFTOKEN_ACCEPT_OFFER);
|
tx.setFieldU16(ripple::sfTransactionType, ripple::ttNFTOKEN_ACCEPT_OFFER);
|
||||||
auto account = ripple::parseBase58<ripple::AccountID>(std::string(accountId));
|
auto account = util::parseBase58Wrapper<ripple::AccountID>(std::string(accountId));
|
||||||
tx.setAccountID(ripple::sfAccount, account.value());
|
tx.setAccountID(ripple::sfAccount, account.value());
|
||||||
auto amount = ripple::STAmount(fee, false);
|
auto amount = ripple::STAmount(fee, false);
|
||||||
tx.setFieldAmount(ripple::sfFee, amount);
|
tx.setFieldAmount(ripple::sfFee, amount);
|
||||||
@@ -737,7 +738,7 @@ CreateCancelNFTOffersTxWithMetadata(
|
|||||||
// tx
|
// tx
|
||||||
ripple::STObject tx(ripple::sfTransaction);
|
ripple::STObject tx(ripple::sfTransaction);
|
||||||
tx.setFieldU16(ripple::sfTransactionType, ripple::ttNFTOKEN_CANCEL_OFFER);
|
tx.setFieldU16(ripple::sfTransactionType, ripple::ttNFTOKEN_CANCEL_OFFER);
|
||||||
auto account = ripple::parseBase58<ripple::AccountID>(std::string(accountId));
|
auto account = util::parseBase58Wrapper<ripple::AccountID>(std::string(accountId));
|
||||||
tx.setAccountID(ripple::sfAccount, account.value());
|
tx.setAccountID(ripple::sfAccount, account.value());
|
||||||
auto amount = ripple::STAmount(fee, false);
|
auto amount = ripple::STAmount(fee, false);
|
||||||
tx.setFieldAmount(ripple::sfFee, amount);
|
tx.setFieldAmount(ripple::sfFee, amount);
|
||||||
@@ -791,7 +792,7 @@ CreateCreateNFTOfferTxWithMetadata(
|
|||||||
// tx
|
// tx
|
||||||
ripple::STObject tx(ripple::sfTransaction);
|
ripple::STObject tx(ripple::sfTransaction);
|
||||||
tx.setFieldU16(ripple::sfTransactionType, ripple::ttNFTOKEN_CREATE_OFFER);
|
tx.setFieldU16(ripple::sfTransactionType, ripple::ttNFTOKEN_CREATE_OFFER);
|
||||||
auto account = ripple::parseBase58<ripple::AccountID>(std::string(accountId));
|
auto account = util::parseBase58Wrapper<ripple::AccountID>(std::string(accountId));
|
||||||
tx.setAccountID(ripple::sfAccount, account.value());
|
tx.setAccountID(ripple::sfAccount, account.value());
|
||||||
auto amount = ripple::STAmount(fee, false);
|
auto amount = ripple::STAmount(fee, false);
|
||||||
tx.setFieldAmount(ripple::sfFee, amount);
|
tx.setFieldAmount(ripple::sfFee, amount);
|
||||||
@@ -839,7 +840,7 @@ CreateOracleSetTxWithMetadata(
|
|||||||
// tx
|
// tx
|
||||||
ripple::STObject tx(ripple::sfTransaction);
|
ripple::STObject tx(ripple::sfTransaction);
|
||||||
tx.setFieldU16(ripple::sfTransactionType, ripple::ttORACLE_SET);
|
tx.setFieldU16(ripple::sfTransactionType, ripple::ttORACLE_SET);
|
||||||
auto account = ripple::parseBase58<ripple::AccountID>(std::string(accountId));
|
auto account = util::parseBase58Wrapper<ripple::AccountID>(std::string(accountId));
|
||||||
tx.setAccountID(ripple::sfAccount, account.value());
|
tx.setAccountID(ripple::sfAccount, account.value());
|
||||||
auto amount = ripple::STAmount(fee, false);
|
auto amount = ripple::STAmount(fee, false);
|
||||||
tx.setFieldAmount(ripple::sfFee, amount);
|
tx.setFieldAmount(ripple::sfFee, amount);
|
||||||
@@ -909,7 +910,7 @@ CreateAMMObject(
|
|||||||
amm.setFieldIssue(ripple::sfAsset2, ripple::STIssue{ripple::sfAsset2, GetIssue(asset2Currency, asset2Issuer)});
|
amm.setFieldIssue(ripple::sfAsset2, ripple::STIssue{ripple::sfAsset2, GetIssue(asset2Currency, asset2Issuer)});
|
||||||
ripple::Issue const issue1(
|
ripple::Issue const issue1(
|
||||||
ripple::Currency{lpTokenBalanceIssueCurrency},
|
ripple::Currency{lpTokenBalanceIssueCurrency},
|
||||||
ripple::parseBase58<ripple::AccountID>(std::string(accountId)).value()
|
util::parseBase58Wrapper<ripple::AccountID>(std::string(accountId)).value()
|
||||||
);
|
);
|
||||||
amm.setFieldAmount(ripple::sfLPTokenBalance, ripple::STAmount(issue1, lpTokenBalanceIssueAmount));
|
amm.setFieldAmount(ripple::sfLPTokenBalance, ripple::STAmount(issue1, lpTokenBalanceIssueAmount));
|
||||||
amm.setFieldU32(ripple::sfFlags, 0);
|
amm.setFieldU32(ripple::sfFlags, 0);
|
||||||
|
|||||||
@@ -105,14 +105,20 @@ TestWsConnection::headers() const
|
|||||||
return headers_;
|
return headers_;
|
||||||
}
|
}
|
||||||
|
|
||||||
TestWsServer::TestWsServer(asio::io_context& context, std::string const& host, int port) : acceptor_(context)
|
TestWsServer::TestWsServer(asio::io_context& context, std::string const& host) : acceptor_(context)
|
||||||
{
|
{
|
||||||
auto endpoint = asio::ip::tcp::endpoint(boost::asio::ip::make_address(host), port);
|
auto endpoint = asio::ip::tcp::endpoint(boost::asio::ip::make_address(host), 0);
|
||||||
acceptor_.open(endpoint.protocol());
|
acceptor_.open(endpoint.protocol());
|
||||||
acceptor_.set_option(asio::socket_base::reuse_address(true));
|
acceptor_.set_option(asio::socket_base::reuse_address(true));
|
||||||
acceptor_.bind(endpoint);
|
acceptor_.bind(endpoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string
|
||||||
|
TestWsServer::port() const
|
||||||
|
{
|
||||||
|
return std::to_string(this->acceptor_.local_endpoint().port());
|
||||||
|
}
|
||||||
|
|
||||||
std::expected<TestWsConnection, util::requests::RequestError>
|
std::expected<TestWsConnection, util::requests::RequestError>
|
||||||
TestWsServer::acceptConnection(asio::yield_context yield)
|
TestWsServer::acceptConnection(asio::yield_context yield)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -70,7 +70,10 @@ class TestWsServer {
|
|||||||
boost::asio::ip::tcp::acceptor acceptor_;
|
boost::asio::ip::tcp::acceptor acceptor_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
TestWsServer(boost::asio::io_context& context, std::string const& host, int port);
|
TestWsServer(boost::asio::io_context& context, std::string const& host);
|
||||||
|
|
||||||
|
std::string
|
||||||
|
port() const;
|
||||||
|
|
||||||
std::expected<TestWsConnection, util::requests::RequestError>
|
std::expected<TestWsConnection, util::requests::RequestError>
|
||||||
acceptConnection(boost::asio::yield_context yield);
|
acceptConnection(boost::asio::yield_context yield);
|
||||||
|
|||||||
@@ -92,6 +92,7 @@ target_sources(
|
|||||||
rpc/JsonBoolTests.cpp
|
rpc/JsonBoolTests.cpp
|
||||||
rpc/RPCHelpersTests.cpp
|
rpc/RPCHelpersTests.cpp
|
||||||
rpc/WorkQueueTests.cpp
|
rpc/WorkQueueTests.cpp
|
||||||
|
util/AccountUtilsTests.cpp
|
||||||
util/AssertTests.cpp
|
util/AssertTests.cpp
|
||||||
# Async framework
|
# Async framework
|
||||||
util/async/AnyExecutionContextTests.cpp
|
util/async/AnyExecutionContextTests.cpp
|
||||||
|
|||||||
@@ -37,8 +37,13 @@
|
|||||||
using namespace etl::impl;
|
using namespace etl::impl;
|
||||||
|
|
||||||
struct ForwardingSourceTests : SyncAsioContextTest {
|
struct ForwardingSourceTests : SyncAsioContextTest {
|
||||||
TestWsServer server_{ctx, "0.0.0.0", 11114};
|
TestWsServer server_{ctx, "0.0.0.0"};
|
||||||
ForwardingSource forwardingSource{"127.0.0.1", "11114", std::chrono::milliseconds{1}, std::chrono::milliseconds{1}};
|
ForwardingSource forwardingSource{
|
||||||
|
"127.0.0.1",
|
||||||
|
server_.port(),
|
||||||
|
std::chrono::milliseconds{1},
|
||||||
|
std::chrono::milliseconds{1}
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
TEST_F(ForwardingSourceTests, ConnectionFailed)
|
TEST_F(ForwardingSourceTests, ConnectionFailed)
|
||||||
|
|||||||
@@ -42,9 +42,9 @@ using namespace etl::impl;
|
|||||||
|
|
||||||
struct GrpcSourceTests : NoLoggerFixture, util::prometheus::WithPrometheus, tests::util::WithMockXrpLedgerAPIService {
|
struct GrpcSourceTests : NoLoggerFixture, util::prometheus::WithPrometheus, tests::util::WithMockXrpLedgerAPIService {
|
||||||
GrpcSourceTests()
|
GrpcSourceTests()
|
||||||
: WithMockXrpLedgerAPIService("localhost:55051")
|
: WithMockXrpLedgerAPIService("localhost:0")
|
||||||
, mockBackend_(std::make_shared<testing::StrictMock<MockBackend>>(util::Config{}))
|
, mockBackend_(std::make_shared<testing::StrictMock<MockBackend>>(util::Config{}))
|
||||||
, grpcSource_("127.0.0.1", "55051", mockBackend_)
|
, grpcSource_("127.0.0.1", std::to_string(getXRPLMockPort()), mockBackend_)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
//==============================================================================
|
//==============================================================================
|
||||||
|
|
||||||
#include "etl/impl/SubscriptionSource.hpp"
|
#include "etl/impl/SubscriptionSource.hpp"
|
||||||
|
#include "util/AssignRandomPort.hpp"
|
||||||
#include "util/Fixtures.hpp"
|
#include "util/Fixtures.hpp"
|
||||||
#include "util/MockNetworkValidatedLedgers.hpp"
|
#include "util/MockNetworkValidatedLedgers.hpp"
|
||||||
#include "util/MockSubscriptionManager.hpp"
|
#include "util/MockSubscriptionManager.hpp"
|
||||||
@@ -31,6 +32,7 @@
|
|||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
#include <cstdint>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
@@ -46,8 +48,7 @@ struct SubscriptionSourceConnectionTests : public NoLoggerFixture {
|
|||||||
}
|
}
|
||||||
|
|
||||||
boost::asio::io_context ioContext_;
|
boost::asio::io_context ioContext_;
|
||||||
|
TestWsServer wsServer_{ioContext_, "0.0.0.0"};
|
||||||
TestWsServer wsServer_{ioContext_, "0.0.0.0", 11113};
|
|
||||||
|
|
||||||
StrictMockNetworkValidatedLedgersPtr networkValidatedLedgers_;
|
StrictMockNetworkValidatedLedgersPtr networkValidatedLedgers_;
|
||||||
StrictMockSubscriptionManagerSharedPtr subscriptionManager_;
|
StrictMockSubscriptionManagerSharedPtr subscriptionManager_;
|
||||||
@@ -59,7 +60,7 @@ struct SubscriptionSourceConnectionTests : public NoLoggerFixture {
|
|||||||
SubscriptionSource subscriptionSource_{
|
SubscriptionSource subscriptionSource_{
|
||||||
ioContext_,
|
ioContext_,
|
||||||
"127.0.0.1",
|
"127.0.0.1",
|
||||||
"11113",
|
wsServer_.port(),
|
||||||
networkValidatedLedgers_,
|
networkValidatedLedgers_,
|
||||||
subscriptionManager_,
|
subscriptionManager_,
|
||||||
onConnectHook_.AsStdFunction(),
|
onConnectHook_.AsStdFunction(),
|
||||||
|
|||||||
@@ -426,6 +426,9 @@ TEST_F(RPCBaseTest, AccountValidator)
|
|||||||
failingInput = json::parse(R"({ "account": "02000000000000000000000000000000000000000000000000000000000000000" })");
|
failingInput = json::parse(R"({ "account": "02000000000000000000000000000000000000000000000000000000000000000" })");
|
||||||
ASSERT_FALSE(spec.process(failingInput));
|
ASSERT_FALSE(spec.process(failingInput));
|
||||||
|
|
||||||
|
failingInput = json::parse(R"({ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jp?" })");
|
||||||
|
ASSERT_FALSE(spec.process(failingInput));
|
||||||
|
|
||||||
auto passingInput = json::parse(R"({ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn" })");
|
auto passingInput = json::parse(R"({ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn" })");
|
||||||
ASSERT_TRUE(spec.process(passingInput));
|
ASSERT_TRUE(spec.process(passingInput));
|
||||||
|
|
||||||
@@ -434,6 +437,28 @@ TEST_F(RPCBaseTest, AccountValidator)
|
|||||||
ASSERT_TRUE(spec.process(passingInput));
|
ASSERT_TRUE(spec.process(passingInput));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(RPCBaseTest, AccountBase58Validator)
|
||||||
|
{
|
||||||
|
auto spec = RpcSpec{
|
||||||
|
{"account", validation::AccountBase58Validator},
|
||||||
|
};
|
||||||
|
auto failingInput = json::parse(R"({ "account": 256 })");
|
||||||
|
ASSERT_FALSE(spec.process(failingInput));
|
||||||
|
|
||||||
|
failingInput = json::parse(R"({ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jp" })");
|
||||||
|
ASSERT_FALSE(spec.process(failingInput));
|
||||||
|
|
||||||
|
failingInput =
|
||||||
|
json::parse(R"({ "account": "020000000000000000000000000000000000000000000000000000000000000000" })");
|
||||||
|
ASSERT_FALSE(spec.process(failingInput));
|
||||||
|
|
||||||
|
failingInput = json::parse(R"({ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jp?" })");
|
||||||
|
ASSERT_FALSE(spec.process(failingInput));
|
||||||
|
|
||||||
|
auto passingInput = json::parse(R"({ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn" })");
|
||||||
|
ASSERT_TRUE(spec.process(passingInput));
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(RPCBaseTest, AccountMarkerValidator)
|
TEST_F(RPCBaseTest, AccountMarkerValidator)
|
||||||
{
|
{
|
||||||
auto spec = RpcSpec{
|
auto spec = RpcSpec{
|
||||||
|
|||||||
@@ -20,6 +20,8 @@
|
|||||||
#include "rpc/Errors.hpp"
|
#include "rpc/Errors.hpp"
|
||||||
#include "rpc/common/Types.hpp"
|
#include "rpc/common/Types.hpp"
|
||||||
|
|
||||||
|
#include <boost/json/array.hpp>
|
||||||
|
#include <boost/json/value.hpp>
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
#include <expected>
|
#include <expected>
|
||||||
@@ -34,3 +36,46 @@ TEST(MaybeErrorTest, OperatorEquals)
|
|||||||
EXPECT_EQ(MaybeError{std::unexpected{Status{"Error"}}}, MaybeError{std::unexpected{Status{"Error"}}});
|
EXPECT_EQ(MaybeError{std::unexpected{Status{"Error"}}}, MaybeError{std::unexpected{Status{"Error"}}});
|
||||||
EXPECT_NE(MaybeError{std::unexpected{Status{"Error"}}}, MaybeError{std::unexpected{Status{"Another_error"}}});
|
EXPECT_NE(MaybeError{std::unexpected{Status{"Error"}}}, MaybeError{std::unexpected{Status{"Another_error"}}});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(ReturnTypeTests, Constructor)
|
||||||
|
{
|
||||||
|
boost::json::value const value{42};
|
||||||
|
|
||||||
|
{
|
||||||
|
ReturnType const r{value};
|
||||||
|
ASSERT_TRUE(r.result);
|
||||||
|
EXPECT_EQ(r.result.value(), value);
|
||||||
|
EXPECT_EQ(r.warnings, boost::json::array{});
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
boost::json::array const warnings{1, 2, 3};
|
||||||
|
ReturnType const r{value, warnings};
|
||||||
|
ASSERT_TRUE(r.result);
|
||||||
|
EXPECT_EQ(r.result.value(), value);
|
||||||
|
EXPECT_EQ(r.warnings, warnings);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
Status const status{"Error"};
|
||||||
|
|
||||||
|
ReturnType const r{std::unexpected{status}};
|
||||||
|
ASSERT_FALSE(r.result);
|
||||||
|
EXPECT_EQ(r.result.error(), status);
|
||||||
|
EXPECT_EQ(r.warnings, boost::json::array{});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ReturnTypeTests, operatorBool)
|
||||||
|
{
|
||||||
|
{
|
||||||
|
boost::json::value const value{42};
|
||||||
|
ReturnType const r{value};
|
||||||
|
EXPECT_TRUE(r);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
Status const status{"Error"};
|
||||||
|
ReturnType const r{std::unexpected{status}};
|
||||||
|
EXPECT_FALSE(r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -144,6 +144,54 @@ generateTestValuesForParametersTest()
|
|||||||
"invalidParams",
|
"invalidParams",
|
||||||
"Required field 'base_asset' missing"
|
"Required field 'base_asset' missing"
|
||||||
},
|
},
|
||||||
|
GetAggregatePriceParamTestCaseBundle{
|
||||||
|
"invalid_base_asset",
|
||||||
|
R"({
|
||||||
|
"quote_asset" : "USD",
|
||||||
|
"base_asset": "asdf",
|
||||||
|
"oracles":
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"account": "rGh1VZCRBJY6rJiaFpD4LZtyHiuCkC8aeD",
|
||||||
|
"oracle_document_id": 2
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})",
|
||||||
|
"invalidParams",
|
||||||
|
"Invalid parameters."
|
||||||
|
},
|
||||||
|
GetAggregatePriceParamTestCaseBundle{
|
||||||
|
"emtpy_base_asset",
|
||||||
|
R"({
|
||||||
|
"quote_asset" : "USD",
|
||||||
|
"base_asset": "",
|
||||||
|
"oracles":
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"account": "rGh1VZCRBJY6rJiaFpD4LZtyHiuCkC8aeD",
|
||||||
|
"oracle_document_id": 2
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})",
|
||||||
|
"invalidParams",
|
||||||
|
"Invalid parameters."
|
||||||
|
},
|
||||||
|
GetAggregatePriceParamTestCaseBundle{
|
||||||
|
"invalid_base_asset2",
|
||||||
|
R"({
|
||||||
|
"quote_asset" : "USD",
|
||||||
|
"base_asset": "+aa",
|
||||||
|
"oracles":
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"account": "rGh1VZCRBJY6rJiaFpD4LZtyHiuCkC8aeD",
|
||||||
|
"oracle_document_id": 2
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})",
|
||||||
|
"invalidParams",
|
||||||
|
"Invalid parameters."
|
||||||
|
},
|
||||||
GetAggregatePriceParamTestCaseBundle{
|
GetAggregatePriceParamTestCaseBundle{
|
||||||
"no_quote_asset",
|
"no_quote_asset",
|
||||||
R"({
|
R"({
|
||||||
@@ -175,6 +223,22 @@ generateTestValuesForParametersTest()
|
|||||||
"invalidParams",
|
"invalidParams",
|
||||||
"Invalid parameters."
|
"Invalid parameters."
|
||||||
},
|
},
|
||||||
|
GetAggregatePriceParamTestCaseBundle{
|
||||||
|
"empty_quote_asset",
|
||||||
|
R"({
|
||||||
|
"quote_asset" : "",
|
||||||
|
"base_asset": "USD",
|
||||||
|
"oracles":
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"account": "rGh1VZCRBJY6rJiaFpD4LZtyHiuCkC8aeD",
|
||||||
|
"oracle_document_id": 2
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})",
|
||||||
|
"invalidParams",
|
||||||
|
"Invalid parameters."
|
||||||
|
},
|
||||||
GetAggregatePriceParamTestCaseBundle{
|
GetAggregatePriceParamTestCaseBundle{
|
||||||
"invalid_quote_asset2",
|
"invalid_quote_asset2",
|
||||||
R"({
|
R"({
|
||||||
|
|||||||
@@ -79,25 +79,25 @@ generateTestValuesForParametersTest()
|
|||||||
"AccountsInvalidBool",
|
"AccountsInvalidBool",
|
||||||
R"({"accounts": true})",
|
R"({"accounts": true})",
|
||||||
"notSupported",
|
"notSupported",
|
||||||
"Not supported field 'accounts'",
|
"Not supported field 'accounts's value 'true'",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"AccountsInvalidInt",
|
"AccountsInvalidInt",
|
||||||
R"({"accounts": 123})",
|
R"({"accounts": 123})",
|
||||||
"notSupported",
|
"invalidParams",
|
||||||
"Not supported field 'accounts'",
|
"Invalid parameters.",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"FullInvalidBool",
|
"FullInvalidBool",
|
||||||
R"({"full": true})",
|
R"({"full": true})",
|
||||||
"notSupported",
|
"notSupported",
|
||||||
"Not supported field 'full'",
|
"Not supported field 'full's value 'true'",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"FullInvalidInt",
|
"FullInvalidInt",
|
||||||
R"({"full": 123})",
|
R"({"full": 123})",
|
||||||
"notSupported",
|
"invalidParams",
|
||||||
"Not supported field 'full'",
|
"Invalid parameters.",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"QueueExist",
|
"QueueExist",
|
||||||
@@ -304,6 +304,8 @@ TEST_F(RPCLedgerHandlerTest, ConditionallyNotSupportedFieldsDefaultValue)
|
|||||||
auto const handler = AnyHandler{LedgerHandler{backend}};
|
auto const handler = AnyHandler{LedgerHandler{backend}};
|
||||||
auto const req = json::parse(
|
auto const req = json::parse(
|
||||||
R"({
|
R"({
|
||||||
|
"full": false,
|
||||||
|
"accounts": false,
|
||||||
"queue": false
|
"queue": false
|
||||||
})"
|
})"
|
||||||
);
|
);
|
||||||
|
|||||||
40
tests/unit/util/AccountUtilsTests.cpp
Normal file
40
tests/unit/util/AccountUtilsTests.cpp
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
This file is part of clio: https://github.com/XRPLF/clio
|
||||||
|
Copyright (c) 2024, 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 "util/AccountUtils.hpp"
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
#include <ripple/protocol/AccountID.h>
|
||||||
|
#include <ripple/protocol/SecretKey.h>
|
||||||
|
#include <ripple/protocol/tokens.h>
|
||||||
|
|
||||||
|
constexpr static auto ACCOUNT = "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn";
|
||||||
|
|
||||||
|
TEST(AccountUtils, parseBase58Wrapper)
|
||||||
|
{
|
||||||
|
EXPECT_FALSE(util::parseBase58Wrapper<ripple::AccountID>("rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jp!"));
|
||||||
|
EXPECT_TRUE(util::parseBase58Wrapper<ripple::AccountID>(ACCOUNT));
|
||||||
|
|
||||||
|
EXPECT_TRUE(util::parseBase58Wrapper<ripple::SecretKey>(
|
||||||
|
ripple::TokenType::NodePrivate, "paQmjZ37pKKPMrgadBLsuf9ab7Y7EUNzh27LQrZqoexpAs31nJi"
|
||||||
|
));
|
||||||
|
EXPECT_FALSE(util::parseBase58Wrapper<ripple::SecretKey>(
|
||||||
|
ripple::TokenType::NodePrivate, "??paQmjZ37pKKPMrgadBLsuf9ab7Y7EUNzh27LQrZqoexpAs31n"
|
||||||
|
));
|
||||||
|
}
|
||||||
@@ -17,6 +17,7 @@
|
|||||||
*/
|
*/
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
|
|
||||||
|
#include "util/AssignRandomPort.hpp"
|
||||||
#include "util/Fixtures.hpp"
|
#include "util/Fixtures.hpp"
|
||||||
#include "util/TestHttpServer.hpp"
|
#include "util/TestHttpServer.hpp"
|
||||||
#include "util/requests/RequestBuilder.hpp"
|
#include "util/requests/RequestBuilder.hpp"
|
||||||
@@ -31,6 +32,7 @@
|
|||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
#include <cstdint>
|
||||||
#include <expected>
|
#include <expected>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
@@ -50,8 +52,8 @@ struct RequestBuilderTestBundle {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct RequestBuilderTestBase : SyncAsioContextTest {
|
struct RequestBuilderTestBase : SyncAsioContextTest {
|
||||||
TestHttpServer server{ctx, "0.0.0.0", 11111};
|
TestHttpServer server{ctx, "0.0.0.0"};
|
||||||
RequestBuilder builder{"localhost", "11111"};
|
RequestBuilder builder{"localhost", server.port()};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct RequestBuilderTest : RequestBuilderTestBase, testing::WithParamInterface<RequestBuilderTestBundle> {};
|
struct RequestBuilderTest : RequestBuilderTestBase, testing::WithParamInterface<RequestBuilderTestBundle> {};
|
||||||
@@ -182,7 +184,7 @@ TEST_F(RequestBuilderTest, ResolveError)
|
|||||||
|
|
||||||
TEST_F(RequestBuilderTest, ConnectionError)
|
TEST_F(RequestBuilderTest, ConnectionError)
|
||||||
{
|
{
|
||||||
builder = RequestBuilder{"localhost", "11112"};
|
builder = RequestBuilder{"localhost", std::to_string(tests::util::generateFreePort())};
|
||||||
builder.setTimeout(std::chrono::milliseconds{1});
|
builder.setTimeout(std::chrono::milliseconds{1});
|
||||||
runSpawn([this](asio::yield_context yield) {
|
runSpawn([this](asio::yield_context yield) {
|
||||||
auto const response = builder.getPlain(yield);
|
auto const response = builder.getPlain(yield);
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
*/
|
*/
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
|
|
||||||
|
#include "util/AssignRandomPort.hpp"
|
||||||
#include "util/Fixtures.hpp"
|
#include "util/Fixtures.hpp"
|
||||||
#include "util/TestWsServer.hpp"
|
#include "util/TestWsServer.hpp"
|
||||||
#include "util/requests/Types.hpp"
|
#include "util/requests/Types.hpp"
|
||||||
@@ -29,6 +30,7 @@
|
|||||||
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
|
#include <cstdint>
|
||||||
#include <expected>
|
#include <expected>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
@@ -41,8 +43,8 @@ namespace asio = boost::asio;
|
|||||||
namespace http = boost::beast::http;
|
namespace http = boost::beast::http;
|
||||||
|
|
||||||
struct WsConnectionTestsBase : SyncAsioContextTest {
|
struct WsConnectionTestsBase : SyncAsioContextTest {
|
||||||
WsConnectionBuilder builder{"localhost", "11112"};
|
TestWsServer server{ctx, "0.0.0.0"};
|
||||||
TestWsServer server{ctx, "0.0.0.0", 11112};
|
WsConnectionBuilder builder{"localhost", server.port()};
|
||||||
|
|
||||||
template <typename T, typename E>
|
template <typename T, typename E>
|
||||||
T
|
T
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
*/
|
*/
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
|
|
||||||
|
#include "util/AssignRandomPort.hpp"
|
||||||
#include "util/Fixtures.hpp"
|
#include "util/Fixtures.hpp"
|
||||||
#include "util/MockPrometheus.hpp"
|
#include "util/MockPrometheus.hpp"
|
||||||
#include "util/TestHttpSyncClient.hpp"
|
#include "util/TestHttpSyncClient.hpp"
|
||||||
@@ -43,13 +44,17 @@
|
|||||||
#include <fmt/core.h>
|
#include <fmt/core.h>
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
|
#include <cstdint>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include <iostream>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
@@ -57,37 +62,48 @@
|
|||||||
using namespace util;
|
using namespace util;
|
||||||
using namespace web::impl;
|
using namespace web::impl;
|
||||||
using namespace web;
|
using namespace web;
|
||||||
|
using namespace boost::json;
|
||||||
|
|
||||||
constexpr static auto JSONData = R"JSON(
|
std::string
|
||||||
{
|
generateJSONWithDynamicPort(std::string_view port)
|
||||||
"server":{
|
{
|
||||||
"ip":"0.0.0.0",
|
return fmt::format(
|
||||||
"port":8888
|
R"JSON({{
|
||||||
},
|
"server": {{
|
||||||
"dos_guard": {
|
"ip": "0.0.0.0",
|
||||||
"max_fetches": 100,
|
"port": {}
|
||||||
"sweep_interval": 1000,
|
}},
|
||||||
"max_connections": 2,
|
"dos_guard": {{
|
||||||
"max_requests": 3,
|
"max_fetches": 100,
|
||||||
"whitelist": ["127.0.0.1"]
|
"sweep_interval": 1000,
|
||||||
}
|
"max_connections": 2,
|
||||||
}
|
"max_requests": 3,
|
||||||
)JSON";
|
"whitelist": ["127.0.0.1"]
|
||||||
|
}}
|
||||||
|
}})JSON",
|
||||||
|
port
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
constexpr static auto JSONDataOverload = R"JSON(
|
std::string
|
||||||
{
|
generateJSONDataOverload(std::string_view port)
|
||||||
"server":{
|
{
|
||||||
"ip":"0.0.0.0",
|
return fmt::format(
|
||||||
"port":8888
|
R"JSON({{
|
||||||
},
|
"server": {{
|
||||||
"dos_guard": {
|
"ip": "0.0.0.0",
|
||||||
"max_fetches": 100,
|
"port": {}
|
||||||
"sweep_interval": 1000,
|
}},
|
||||||
"max_connections": 2,
|
"dos_guard": {{
|
||||||
"max_requests": 1
|
"max_fetches": 100,
|
||||||
}
|
"sweep_interval": 1000,
|
||||||
}
|
"max_connections": 2,
|
||||||
)JSON";
|
"max_requests": 1
|
||||||
|
}}
|
||||||
|
}})JSON",
|
||||||
|
port
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// for testing, we use a self-signed certificate
|
// for testing, we use a self-signed certificate
|
||||||
std::optional<ssl::context>
|
std::optional<ssl::context>
|
||||||
@@ -168,12 +184,13 @@ protected:
|
|||||||
|
|
||||||
// this ctx is for dos timer
|
// this ctx is for dos timer
|
||||||
boost::asio::io_context ctxSync;
|
boost::asio::io_context ctxSync;
|
||||||
Config cfg{boost::json::parse(JSONData)};
|
std::string const port = std::to_string(tests::util::generateFreePort());
|
||||||
|
Config cfg{parse(generateJSONWithDynamicPort(port))};
|
||||||
IntervalSweepHandler sweepHandler = web::IntervalSweepHandler{cfg, ctxSync};
|
IntervalSweepHandler sweepHandler = web::IntervalSweepHandler{cfg, ctxSync};
|
||||||
WhitelistHandler whitelistHandler = web::WhitelistHandler{cfg};
|
WhitelistHandler whitelistHandler = web::WhitelistHandler{cfg};
|
||||||
DOSGuard dosGuard = web::DOSGuard{cfg, whitelistHandler, sweepHandler};
|
DOSGuard dosGuard = web::DOSGuard{cfg, whitelistHandler, sweepHandler};
|
||||||
|
|
||||||
Config cfgOverload{boost::json::parse(JSONDataOverload)};
|
Config cfgOverload{parse(generateJSONDataOverload(port))};
|
||||||
IntervalSweepHandler sweepHandlerOverload = web::IntervalSweepHandler{cfgOverload, ctxSync};
|
IntervalSweepHandler sweepHandlerOverload = web::IntervalSweepHandler{cfgOverload, ctxSync};
|
||||||
WhitelistHandler whitelistHandlerOverload = web::WhitelistHandler{cfgOverload};
|
WhitelistHandler whitelistHandlerOverload = web::WhitelistHandler{cfgOverload};
|
||||||
DOSGuard dosGuardOverload = web::DOSGuard{cfgOverload, whitelistHandlerOverload, sweepHandlerOverload};
|
DOSGuard dosGuardOverload = web::DOSGuard{cfgOverload, whitelistHandlerOverload, sweepHandlerOverload};
|
||||||
@@ -250,7 +267,7 @@ TEST_F(WebServerTest, Http)
|
|||||||
{
|
{
|
||||||
auto e = std::make_shared<EchoExecutor>();
|
auto e = std::make_shared<EchoExecutor>();
|
||||||
auto const server = makeServerSync(cfg, ctx, std::nullopt, dosGuard, e);
|
auto const server = makeServerSync(cfg, ctx, std::nullopt, dosGuard, e);
|
||||||
auto const res = HttpSyncClient::syncPost("localhost", "8888", R"({"Hello":1})");
|
auto const res = HttpSyncClient::syncPost("localhost", port, R"({"Hello":1})");
|
||||||
EXPECT_EQ(res, R"({"Hello":1})");
|
EXPECT_EQ(res, R"({"Hello":1})");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -259,7 +276,7 @@ TEST_F(WebServerTest, Ws)
|
|||||||
auto e = std::make_shared<EchoExecutor>();
|
auto e = std::make_shared<EchoExecutor>();
|
||||||
auto const server = makeServerSync(cfg, ctx, std::nullopt, dosGuard, e);
|
auto const server = makeServerSync(cfg, ctx, std::nullopt, dosGuard, e);
|
||||||
WebSocketSyncClient wsClient;
|
WebSocketSyncClient wsClient;
|
||||||
wsClient.connect("localhost", "8888");
|
wsClient.connect("localhost", port);
|
||||||
auto const res = wsClient.syncPost(R"({"Hello":1})");
|
auto const res = wsClient.syncPost(R"({"Hello":1})");
|
||||||
EXPECT_EQ(res, R"({"Hello":1})");
|
EXPECT_EQ(res, R"({"Hello":1})");
|
||||||
wsClient.disconnect();
|
wsClient.disconnect();
|
||||||
@@ -269,7 +286,7 @@ TEST_F(WebServerTest, HttpInternalError)
|
|||||||
{
|
{
|
||||||
auto e = std::make_shared<ExceptionExecutor>();
|
auto e = std::make_shared<ExceptionExecutor>();
|
||||||
auto const server = makeServerSync(cfg, ctx, std::nullopt, dosGuard, e);
|
auto const server = makeServerSync(cfg, ctx, std::nullopt, dosGuard, e);
|
||||||
auto const res = HttpSyncClient::syncPost("localhost", "8888", R"({})");
|
auto const res = HttpSyncClient::syncPost("localhost", port, R"({})");
|
||||||
EXPECT_EQ(
|
EXPECT_EQ(
|
||||||
res,
|
res,
|
||||||
R"({"error":"internal","error_code":73,"error_message":"Internal error.","status":"error","type":"response"})"
|
R"({"error":"internal","error_code":73,"error_message":"Internal error.","status":"error","type":"response"})"
|
||||||
@@ -281,7 +298,7 @@ TEST_F(WebServerTest, WsInternalError)
|
|||||||
auto e = std::make_shared<ExceptionExecutor>();
|
auto e = std::make_shared<ExceptionExecutor>();
|
||||||
auto const server = makeServerSync(cfg, ctx, std::nullopt, dosGuard, e);
|
auto const server = makeServerSync(cfg, ctx, std::nullopt, dosGuard, e);
|
||||||
WebSocketSyncClient wsClient;
|
WebSocketSyncClient wsClient;
|
||||||
wsClient.connect("localhost", "8888");
|
wsClient.connect("localhost", port);
|
||||||
auto const res = wsClient.syncPost(R"({"id":"id1"})");
|
auto const res = wsClient.syncPost(R"({"id":"id1"})");
|
||||||
wsClient.disconnect();
|
wsClient.disconnect();
|
||||||
EXPECT_EQ(
|
EXPECT_EQ(
|
||||||
@@ -295,7 +312,7 @@ TEST_F(WebServerTest, WsInternalErrorNotJson)
|
|||||||
auto e = std::make_shared<ExceptionExecutor>();
|
auto e = std::make_shared<ExceptionExecutor>();
|
||||||
auto const server = makeServerSync(cfg, ctx, std::nullopt, dosGuard, e);
|
auto const server = makeServerSync(cfg, ctx, std::nullopt, dosGuard, e);
|
||||||
WebSocketSyncClient wsClient;
|
WebSocketSyncClient wsClient;
|
||||||
wsClient.connect("localhost", "8888");
|
wsClient.connect("localhost", port);
|
||||||
auto const res = wsClient.syncPost("not json");
|
auto const res = wsClient.syncPost("not json");
|
||||||
wsClient.disconnect();
|
wsClient.disconnect();
|
||||||
EXPECT_EQ(
|
EXPECT_EQ(
|
||||||
@@ -310,7 +327,7 @@ TEST_F(WebServerTest, Https)
|
|||||||
auto sslCtx = parseCertsForTest();
|
auto sslCtx = parseCertsForTest();
|
||||||
auto const ctxSslRef = sslCtx ? std::optional<std::reference_wrapper<ssl::context>>{sslCtx.value()} : std::nullopt;
|
auto const ctxSslRef = sslCtx ? std::optional<std::reference_wrapper<ssl::context>>{sslCtx.value()} : std::nullopt;
|
||||||
auto const server = makeServerSync(cfg, ctx, ctxSslRef, dosGuard, e);
|
auto const server = makeServerSync(cfg, ctx, ctxSslRef, dosGuard, e);
|
||||||
auto const res = HttpsSyncClient::syncPost("localhost", "8888", R"({"Hello":1})");
|
auto const res = HttpsSyncClient::syncPost("localhost", port, R"({"Hello":1})");
|
||||||
EXPECT_EQ(res, R"({"Hello":1})");
|
EXPECT_EQ(res, R"({"Hello":1})");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -322,7 +339,7 @@ TEST_F(WebServerTest, Wss)
|
|||||||
|
|
||||||
auto server = makeServerSync(cfg, ctx, ctxSslRef, dosGuard, e);
|
auto server = makeServerSync(cfg, ctx, ctxSslRef, dosGuard, e);
|
||||||
WebServerSslSyncClient wsClient;
|
WebServerSslSyncClient wsClient;
|
||||||
wsClient.connect("localhost", "8888");
|
wsClient.connect("localhost", port);
|
||||||
auto const res = wsClient.syncPost(R"({"Hello":1})");
|
auto const res = wsClient.syncPost(R"({"Hello":1})");
|
||||||
EXPECT_EQ(res, R"({"Hello":1})");
|
EXPECT_EQ(res, R"({"Hello":1})");
|
||||||
wsClient.disconnect();
|
wsClient.disconnect();
|
||||||
@@ -332,9 +349,9 @@ TEST_F(WebServerTest, HttpRequestOverload)
|
|||||||
{
|
{
|
||||||
auto e = std::make_shared<EchoExecutor>();
|
auto e = std::make_shared<EchoExecutor>();
|
||||||
auto const server = makeServerSync(cfg, ctx, std::nullopt, dosGuardOverload, e);
|
auto const server = makeServerSync(cfg, ctx, std::nullopt, dosGuardOverload, e);
|
||||||
auto res = HttpSyncClient::syncPost("localhost", "8888", R"({})");
|
auto res = HttpSyncClient::syncPost("localhost", port, R"({})");
|
||||||
EXPECT_EQ(res, "{}");
|
EXPECT_EQ(res, "{}");
|
||||||
res = HttpSyncClient::syncPost("localhost", "8888", R"({})");
|
res = HttpSyncClient::syncPost("localhost", port, R"({})");
|
||||||
EXPECT_EQ(
|
EXPECT_EQ(
|
||||||
res,
|
res,
|
||||||
R"({"error":"slowDown","error_code":10,"error_message":"You are placing too much load on the server.","status":"error","type":"response"})"
|
R"({"error":"slowDown","error_code":10,"error_message":"You are placing too much load on the server.","status":"error","type":"response"})"
|
||||||
@@ -346,12 +363,12 @@ TEST_F(WebServerTest, WsRequestOverload)
|
|||||||
auto e = std::make_shared<EchoExecutor>();
|
auto e = std::make_shared<EchoExecutor>();
|
||||||
auto const server = makeServerSync(cfg, ctx, std::nullopt, dosGuardOverload, e);
|
auto const server = makeServerSync(cfg, ctx, std::nullopt, dosGuardOverload, e);
|
||||||
WebSocketSyncClient wsClient;
|
WebSocketSyncClient wsClient;
|
||||||
wsClient.connect("localhost", "8888");
|
wsClient.connect("localhost", port);
|
||||||
auto res = wsClient.syncPost(R"({})");
|
auto res = wsClient.syncPost(R"({})");
|
||||||
wsClient.disconnect();
|
wsClient.disconnect();
|
||||||
EXPECT_EQ(res, "{}");
|
EXPECT_EQ(res, "{}");
|
||||||
WebSocketSyncClient wsClient2;
|
WebSocketSyncClient wsClient2;
|
||||||
wsClient2.connect("localhost", "8888");
|
wsClient2.connect("localhost", port);
|
||||||
res = wsClient2.syncPost(R"({})");
|
res = wsClient2.syncPost(R"({})");
|
||||||
wsClient2.disconnect();
|
wsClient2.disconnect();
|
||||||
EXPECT_EQ(
|
EXPECT_EQ(
|
||||||
@@ -365,7 +382,7 @@ TEST_F(WebServerTest, HttpPayloadOverload)
|
|||||||
std::string const s100(100, 'a');
|
std::string const s100(100, 'a');
|
||||||
auto e = std::make_shared<EchoExecutor>();
|
auto e = std::make_shared<EchoExecutor>();
|
||||||
auto server = makeServerSync(cfg, ctx, std::nullopt, dosGuardOverload, e);
|
auto server = makeServerSync(cfg, ctx, std::nullopt, dosGuardOverload, e);
|
||||||
auto const res = HttpSyncClient::syncPost("localhost", "8888", fmt::format(R"({{"payload":"{}"}})", s100));
|
auto const res = HttpSyncClient::syncPost("localhost", port, fmt::format(R"({{"payload":"{}"}})", s100));
|
||||||
EXPECT_EQ(
|
EXPECT_EQ(
|
||||||
res,
|
res,
|
||||||
R"({"payload":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","warning":"load","warnings":[{"id":2003,"message":"You are about to be rate limited"}]})"
|
R"({"payload":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","warning":"load","warnings":[{"id":2003,"message":"You are about to be rate limited"}]})"
|
||||||
@@ -378,7 +395,7 @@ TEST_F(WebServerTest, WsPayloadOverload)
|
|||||||
auto e = std::make_shared<EchoExecutor>();
|
auto e = std::make_shared<EchoExecutor>();
|
||||||
auto server = makeServerSync(cfg, ctx, std::nullopt, dosGuardOverload, e);
|
auto server = makeServerSync(cfg, ctx, std::nullopt, dosGuardOverload, e);
|
||||||
WebSocketSyncClient wsClient;
|
WebSocketSyncClient wsClient;
|
||||||
wsClient.connect("localhost", "8888");
|
wsClient.connect("localhost", port);
|
||||||
auto const res = wsClient.syncPost(fmt::format(R"({{"payload":"{}"}})", s100));
|
auto const res = wsClient.syncPost(fmt::format(R"({{"payload":"{}"}})", s100));
|
||||||
wsClient.disconnect();
|
wsClient.disconnect();
|
||||||
EXPECT_EQ(
|
EXPECT_EQ(
|
||||||
@@ -393,13 +410,13 @@ TEST_F(WebServerTest, WsTooManyConnection)
|
|||||||
auto server = makeServerSync(cfg, ctx, std::nullopt, dosGuardOverload, e);
|
auto server = makeServerSync(cfg, ctx, std::nullopt, dosGuardOverload, e);
|
||||||
// max connection is 2, exception should happen when the third connection is made
|
// max connection is 2, exception should happen when the third connection is made
|
||||||
WebSocketSyncClient wsClient1;
|
WebSocketSyncClient wsClient1;
|
||||||
wsClient1.connect("localhost", "8888");
|
wsClient1.connect("localhost", port);
|
||||||
WebSocketSyncClient wsClient2;
|
WebSocketSyncClient wsClient2;
|
||||||
wsClient2.connect("localhost", "8888");
|
wsClient2.connect("localhost", port);
|
||||||
bool exceptionThrown = false;
|
bool exceptionThrown = false;
|
||||||
try {
|
try {
|
||||||
WebSocketSyncClient wsClient3;
|
WebSocketSyncClient wsClient3;
|
||||||
wsClient3.connect("localhost", "8888");
|
wsClient3.connect("localhost", port);
|
||||||
} catch (boost::system::system_error const& ex) {
|
} catch (boost::system::system_error const& ex) {
|
||||||
exceptionThrown = true;
|
exceptionThrown = true;
|
||||||
EXPECT_EQ(ex.code(), boost::beast::websocket::error::upgrade_declined);
|
EXPECT_EQ(ex.code(), boost::beast::websocket::error::upgrade_declined);
|
||||||
@@ -409,45 +426,65 @@ TEST_F(WebServerTest, WsTooManyConnection)
|
|||||||
EXPECT_TRUE(exceptionThrown);
|
EXPECT_TRUE(exceptionThrown);
|
||||||
}
|
}
|
||||||
|
|
||||||
static auto constexpr JSONServerConfigWithAdminPassword = R"JSON(
|
std::string
|
||||||
{
|
JSONServerConfigWithAdminPassword(uint32_t const port)
|
||||||
"server":{
|
{
|
||||||
"ip": "0.0.0.0",
|
return fmt::format(
|
||||||
"port": 8888,
|
R"JSON({{
|
||||||
"admin_password": "secret"
|
"server": {{
|
||||||
}
|
"ip": "0.0.0.0",
|
||||||
}
|
"port": {},
|
||||||
)JSON";
|
"admin_password": "secret"
|
||||||
|
}}
|
||||||
|
}})JSON",
|
||||||
|
port
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
static auto constexpr JSONServerConfigWithLocalAdmin = R"JSON(
|
std::string
|
||||||
{
|
JSONServerConfigWithLocalAdmin(uint32_t const port)
|
||||||
"server":{
|
{
|
||||||
"ip": "0.0.0.0",
|
return fmt::format(
|
||||||
"port": 8888,
|
R"JSON({{
|
||||||
"local_admin": true
|
"server": {{
|
||||||
}
|
"ip": "0.0.0.0",
|
||||||
}
|
"port": {},
|
||||||
)JSON";
|
"local_admin": true
|
||||||
|
}}
|
||||||
|
}})JSON",
|
||||||
|
port
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
static auto constexpr JSONServerConfigWithBothAdminPasswordAndLocalAdminFalse = R"JSON(
|
std::string
|
||||||
{
|
JSONServerConfigWithBothAdminPasswordAndLocalAdminFalse(uint32_t const port)
|
||||||
"server":{
|
{
|
||||||
"ip": "0.0.0.0",
|
return fmt::format(
|
||||||
"port": 8888,
|
R"JSON({{
|
||||||
"admin_password": "secret",
|
"server": {{
|
||||||
"local_admin": false
|
"ip": "0.0.0.0",
|
||||||
}
|
"port": {},
|
||||||
}
|
"admin_password": "secret",
|
||||||
)JSON";
|
"local_admin": false
|
||||||
|
}}
|
||||||
|
}})JSON",
|
||||||
|
port
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
static auto constexpr JSONServerConfigWithNoSpecifiedAdmin = R"JSON(
|
std::string
|
||||||
{
|
JSONServerConfigWithNoSpecifiedAdmin(uint32_t const port)
|
||||||
"server":{
|
{
|
||||||
"ip": "0.0.0.0",
|
return fmt::format(
|
||||||
"port": 8888
|
R"JSON({{
|
||||||
}
|
"server": {{
|
||||||
}
|
"ip": "0.0.0.0",
|
||||||
)JSON";
|
"port": {}
|
||||||
|
}}
|
||||||
|
}})JSON",
|
||||||
|
port
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// get this value from online sha256 generator
|
// get this value from online sha256 generator
|
||||||
static auto constexpr SecertSha256 = "2bb80d537b1da3e38bd30361aa855686bde0eacd7162fef6a25fe97bf527a25b";
|
static auto constexpr SecertSha256 = "2bb80d537b1da3e38bd30361aa855686bde0eacd7162fef6a25fe97bf527a25b";
|
||||||
@@ -478,10 +515,11 @@ class WebServerAdminTest : public WebServerTest, public ::testing::WithParamInte
|
|||||||
TEST_P(WebServerAdminTest, WsAdminCheck)
|
TEST_P(WebServerAdminTest, WsAdminCheck)
|
||||||
{
|
{
|
||||||
auto e = std::make_shared<AdminCheckExecutor>();
|
auto e = std::make_shared<AdminCheckExecutor>();
|
||||||
Config const serverConfig{boost::json::parse(GetParam().config)};
|
Config const serverConfig{parse(GetParam().config)};
|
||||||
auto server = makeServerSync(serverConfig, ctx, std::nullopt, dosGuardOverload, e);
|
auto server = makeServerSync(serverConfig, ctx, std::nullopt, dosGuardOverload, e);
|
||||||
WebSocketSyncClient wsClient;
|
WebSocketSyncClient wsClient;
|
||||||
wsClient.connect("localhost", "8888", GetParam().headers);
|
uint32_t webServerPort = serverConfig.value<uint32_t>("server.port");
|
||||||
|
wsClient.connect("localhost", std::to_string(webServerPort), GetParam().headers);
|
||||||
std::string const request = "Why hello";
|
std::string const request = "Why hello";
|
||||||
auto const res = wsClient.syncPost(request);
|
auto const res = wsClient.syncPost(request);
|
||||||
wsClient.disconnect();
|
wsClient.disconnect();
|
||||||
@@ -491,10 +529,11 @@ TEST_P(WebServerAdminTest, WsAdminCheck)
|
|||||||
TEST_P(WebServerAdminTest, HttpAdminCheck)
|
TEST_P(WebServerAdminTest, HttpAdminCheck)
|
||||||
{
|
{
|
||||||
auto e = std::make_shared<AdminCheckExecutor>();
|
auto e = std::make_shared<AdminCheckExecutor>();
|
||||||
Config const serverConfig{boost::json::parse(GetParam().config)};
|
Config const serverConfig{parse(GetParam().config)};
|
||||||
auto server = makeServerSync(serverConfig, ctx, std::nullopt, dosGuardOverload, e);
|
auto server = makeServerSync(serverConfig, ctx, std::nullopt, dosGuardOverload, e);
|
||||||
std::string const request = "Why hello";
|
std::string const request = "Why hello";
|
||||||
auto const res = HttpSyncClient::syncPost("localhost", "8888", request, GetParam().headers);
|
uint32_t webServerPort = serverConfig.value<uint32_t>("server.port");
|
||||||
|
auto const res = HttpSyncClient::syncPost("localhost", std::to_string(webServerPort), request, GetParam().headers);
|
||||||
EXPECT_EQ(res, fmt::format("{} {}", request, GetParam().expectedResponse));
|
EXPECT_EQ(res, fmt::format("{} {}", request, GetParam().expectedResponse));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -503,27 +542,27 @@ INSTANTIATE_TEST_CASE_P(
|
|||||||
WebServerAdminTest,
|
WebServerAdminTest,
|
||||||
::testing::Values(
|
::testing::Values(
|
||||||
WebServerAdminTestParams{
|
WebServerAdminTestParams{
|
||||||
.config = JSONServerConfigWithAdminPassword,
|
.config = JSONServerConfigWithAdminPassword(tests::util::generateFreePort()),
|
||||||
.headers = {},
|
.headers = {},
|
||||||
.expectedResponse = "user"
|
.expectedResponse = "user"
|
||||||
},
|
},
|
||||||
WebServerAdminTestParams{
|
WebServerAdminTestParams{
|
||||||
.config = JSONServerConfigWithAdminPassword,
|
.config = JSONServerConfigWithAdminPassword(tests::util::generateFreePort()),
|
||||||
.headers = {WebHeader(http::field::authorization, "")},
|
.headers = {WebHeader(http::field::authorization, "")},
|
||||||
.expectedResponse = "user"
|
.expectedResponse = "user"
|
||||||
},
|
},
|
||||||
WebServerAdminTestParams{
|
WebServerAdminTestParams{
|
||||||
.config = JSONServerConfigWithAdminPassword,
|
.config = JSONServerConfigWithAdminPassword(tests::util::generateFreePort()),
|
||||||
.headers = {WebHeader(http::field::authorization, "s")},
|
.headers = {WebHeader(http::field::authorization, "s")},
|
||||||
.expectedResponse = "user"
|
.expectedResponse = "user"
|
||||||
},
|
},
|
||||||
WebServerAdminTestParams{
|
WebServerAdminTestParams{
|
||||||
.config = JSONServerConfigWithAdminPassword,
|
.config = JSONServerConfigWithAdminPassword(tests::util::generateFreePort()),
|
||||||
.headers = {WebHeader(http::field::authorization, SecertSha256)},
|
.headers = {WebHeader(http::field::authorization, SecertSha256)},
|
||||||
.expectedResponse = "user"
|
.expectedResponse = "user"
|
||||||
},
|
},
|
||||||
WebServerAdminTestParams{
|
WebServerAdminTestParams{
|
||||||
.config = JSONServerConfigWithAdminPassword,
|
.config = JSONServerConfigWithAdminPassword(tests::util::generateFreePort()),
|
||||||
.headers = {WebHeader(
|
.headers = {WebHeader(
|
||||||
http::field::authorization,
|
http::field::authorization,
|
||||||
fmt::format("{}{}", PasswordAdminVerificationStrategy::passwordPrefix, SecertSha256)
|
fmt::format("{}{}", PasswordAdminVerificationStrategy::passwordPrefix, SecertSha256)
|
||||||
@@ -531,12 +570,12 @@ INSTANTIATE_TEST_CASE_P(
|
|||||||
.expectedResponse = "admin"
|
.expectedResponse = "admin"
|
||||||
},
|
},
|
||||||
WebServerAdminTestParams{
|
WebServerAdminTestParams{
|
||||||
.config = JSONServerConfigWithBothAdminPasswordAndLocalAdminFalse,
|
.config = JSONServerConfigWithBothAdminPasswordAndLocalAdminFalse(tests::util::generateFreePort()),
|
||||||
.headers = {WebHeader(http::field::authorization, SecertSha256)},
|
.headers = {WebHeader(http::field::authorization, SecertSha256)},
|
||||||
.expectedResponse = "user"
|
.expectedResponse = "user"
|
||||||
},
|
},
|
||||||
WebServerAdminTestParams{
|
WebServerAdminTestParams{
|
||||||
.config = JSONServerConfigWithBothAdminPasswordAndLocalAdminFalse,
|
.config = JSONServerConfigWithBothAdminPasswordAndLocalAdminFalse(tests::util::generateFreePort()),
|
||||||
.headers = {WebHeader(
|
.headers = {WebHeader(
|
||||||
http::field::authorization,
|
http::field::authorization,
|
||||||
fmt::format("{}{}", PasswordAdminVerificationStrategy::passwordPrefix, SecertSha256)
|
fmt::format("{}{}", PasswordAdminVerificationStrategy::passwordPrefix, SecertSha256)
|
||||||
@@ -544,16 +583,20 @@ INSTANTIATE_TEST_CASE_P(
|
|||||||
.expectedResponse = "admin"
|
.expectedResponse = "admin"
|
||||||
},
|
},
|
||||||
WebServerAdminTestParams{
|
WebServerAdminTestParams{
|
||||||
.config = JSONServerConfigWithAdminPassword,
|
.config = JSONServerConfigWithAdminPassword(tests::util::generateFreePort()),
|
||||||
.headers = {WebHeader(
|
.headers = {WebHeader(
|
||||||
http::field::authentication_info,
|
http::field::authentication_info,
|
||||||
fmt::format("{}{}", PasswordAdminVerificationStrategy::passwordPrefix, SecertSha256)
|
fmt::format("{}{}", PasswordAdminVerificationStrategy::passwordPrefix, SecertSha256)
|
||||||
)},
|
)},
|
||||||
.expectedResponse = "user"
|
.expectedResponse = "user"
|
||||||
},
|
},
|
||||||
WebServerAdminTestParams{.config = JSONServerConfigWithLocalAdmin, .headers = {}, .expectedResponse = "admin"},
|
|
||||||
WebServerAdminTestParams{
|
WebServerAdminTestParams{
|
||||||
.config = JSONServerConfigWithNoSpecifiedAdmin,
|
.config = JSONServerConfigWithLocalAdmin(tests::util::generateFreePort()),
|
||||||
|
.headers = {},
|
||||||
|
.expectedResponse = "admin"
|
||||||
|
},
|
||||||
|
WebServerAdminTestParams{
|
||||||
|
.config = JSONServerConfigWithNoSpecifiedAdmin(tests::util::generateFreePort()),
|
||||||
.headers = {},
|
.headers = {},
|
||||||
.expectedResponse = "admin"
|
.expectedResponse = "admin"
|
||||||
}
|
}
|
||||||
@@ -563,36 +606,40 @@ INSTANTIATE_TEST_CASE_P(
|
|||||||
|
|
||||||
TEST_F(WebServerTest, AdminErrorCfgTestBothAdminPasswordAndLocalAdminSet)
|
TEST_F(WebServerTest, AdminErrorCfgTestBothAdminPasswordAndLocalAdminSet)
|
||||||
{
|
{
|
||||||
static auto constexpr JSONServerConfigWithBothAdminPasswordAndLocalAdmin = R"JSON(
|
uint32_t webServerPort = tests::util::generateFreePort();
|
||||||
{
|
std::string JSONServerConfigWithBothAdminPasswordAndLocalAdmin = fmt::format(
|
||||||
"server":{
|
R"JSON({{
|
||||||
|
"server":{{
|
||||||
"ip": "0.0.0.0",
|
"ip": "0.0.0.0",
|
||||||
"port": 8888,
|
"port": {},
|
||||||
"admin_password": "secret",
|
"admin_password": "secret",
|
||||||
"local_admin": true
|
"local_admin": true
|
||||||
}
|
}}
|
||||||
}
|
}})JSON",
|
||||||
)JSON";
|
webServerPort
|
||||||
|
);
|
||||||
|
|
||||||
auto e = std::make_shared<AdminCheckExecutor>();
|
auto e = std::make_shared<AdminCheckExecutor>();
|
||||||
Config const serverConfig{boost::json::parse(JSONServerConfigWithBothAdminPasswordAndLocalAdmin)};
|
Config const serverConfig{parse(JSONServerConfigWithBothAdminPasswordAndLocalAdmin)};
|
||||||
EXPECT_THROW(web::make_HttpServer(serverConfig, ctx, std::nullopt, dosGuardOverload, e), std::logic_error);
|
EXPECT_THROW(web::make_HttpServer(serverConfig, ctx, std::nullopt, dosGuardOverload, e), std::logic_error);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(WebServerTest, AdminErrorCfgTestBothAdminPasswordAndLocalAdminFalse)
|
TEST_F(WebServerTest, AdminErrorCfgTestBothAdminPasswordAndLocalAdminFalse)
|
||||||
{
|
{
|
||||||
static auto constexpr JSONServerConfigWithNoAdminPasswordAndLocalAdminFalse = R"JSON(
|
uint32_t webServerPort = tests::util::generateFreePort();
|
||||||
{
|
std::string JSONServerConfigWithNoAdminPasswordAndLocalAdminFalse = fmt::format(
|
||||||
"server":{
|
R"JSON({{
|
||||||
"ip": "0.0.0.0",
|
"server": {{
|
||||||
"port": 8888,
|
"ip": "0.0.0.0",
|
||||||
"local_admin": false
|
"port": {},
|
||||||
}
|
"local_admin": false
|
||||||
}
|
}}
|
||||||
)JSON";
|
}})JSON",
|
||||||
|
webServerPort
|
||||||
|
);
|
||||||
|
|
||||||
auto e = std::make_shared<AdminCheckExecutor>();
|
auto e = std::make_shared<AdminCheckExecutor>();
|
||||||
Config const serverConfig{boost::json::parse(JSONServerConfigWithNoAdminPasswordAndLocalAdminFalse)};
|
Config const serverConfig{parse(JSONServerConfigWithNoAdminPasswordAndLocalAdminFalse)};
|
||||||
EXPECT_THROW(web::make_HttpServer(serverConfig, ctx, std::nullopt, dosGuardOverload, e), std::logic_error);
|
EXPECT_THROW(web::make_HttpServer(serverConfig, ctx, std::nullopt, dosGuardOverload, e), std::logic_error);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -601,32 +648,35 @@ struct WebServerPrometheusTest : util::prometheus::WithPrometheus, WebServerTest
|
|||||||
TEST_F(WebServerPrometheusTest, rejectedWithoutAdminPassword)
|
TEST_F(WebServerPrometheusTest, rejectedWithoutAdminPassword)
|
||||||
{
|
{
|
||||||
auto e = std::make_shared<EchoExecutor>();
|
auto e = std::make_shared<EchoExecutor>();
|
||||||
Config const serverConfig{boost::json::parse(JSONServerConfigWithAdminPassword)};
|
uint32_t webServerPort = tests::util::generateFreePort();
|
||||||
|
Config const serverConfig{parse(JSONServerConfigWithAdminPassword(webServerPort))};
|
||||||
auto server = makeServerSync(serverConfig, ctx, std::nullopt, dosGuard, e);
|
auto server = makeServerSync(serverConfig, ctx, std::nullopt, dosGuard, e);
|
||||||
auto const res = HttpSyncClient::syncGet("localhost", "8888", "", "/metrics");
|
auto const res = HttpSyncClient::syncGet("localhost", std::to_string(webServerPort), "", "/metrics");
|
||||||
EXPECT_EQ(res, "Only admin is allowed to collect metrics");
|
EXPECT_EQ(res, "Only admin is allowed to collect metrics");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(WebServerPrometheusTest, rejectedIfPrometheusIsDisabled)
|
TEST_F(WebServerPrometheusTest, rejectedIfPrometheusIsDisabled)
|
||||||
{
|
{
|
||||||
static auto constexpr JSONServerConfigWithDisabledPrometheus = R"JSON(
|
uint32_t webServerPort = tests::util::generateFreePort();
|
||||||
{
|
std::string JSONServerConfigWithDisabledPrometheus = fmt::format(
|
||||||
"server": {
|
R"JSON({{
|
||||||
|
"server":{{
|
||||||
"ip": "0.0.0.0",
|
"ip": "0.0.0.0",
|
||||||
"port": 8888,
|
"port": {},
|
||||||
"admin_password": "secret"
|
"admin_password": "secret"
|
||||||
},
|
}},
|
||||||
"prometheus": { "enabled": false }
|
"prometheus": {{ "enabled": false }}
|
||||||
}
|
}})JSON",
|
||||||
)JSON";
|
webServerPort
|
||||||
|
);
|
||||||
|
|
||||||
auto e = std::make_shared<EchoExecutor>();
|
auto e = std::make_shared<EchoExecutor>();
|
||||||
Config const serverConfig{boost::json::parse(JSONServerConfigWithDisabledPrometheus)};
|
Config const serverConfig{parse(JSONServerConfigWithDisabledPrometheus)};
|
||||||
PrometheusService::init(serverConfig);
|
PrometheusService::init(serverConfig);
|
||||||
auto server = makeServerSync(serverConfig, ctx, std::nullopt, dosGuard, e);
|
auto server = makeServerSync(serverConfig, ctx, std::nullopt, dosGuard, e);
|
||||||
auto const res = HttpSyncClient::syncGet(
|
auto const res = HttpSyncClient::syncGet(
|
||||||
"localhost",
|
"localhost",
|
||||||
"8888",
|
std::to_string(webServerPort),
|
||||||
"",
|
"",
|
||||||
"/metrics",
|
"/metrics",
|
||||||
{WebHeader(
|
{WebHeader(
|
||||||
@@ -639,14 +689,15 @@ TEST_F(WebServerPrometheusTest, rejectedIfPrometheusIsDisabled)
|
|||||||
|
|
||||||
TEST_F(WebServerPrometheusTest, validResponse)
|
TEST_F(WebServerPrometheusTest, validResponse)
|
||||||
{
|
{
|
||||||
|
uint32_t webServerPort = tests::util::generateFreePort();
|
||||||
auto& testCounter = PrometheusService::counterInt("test_counter", util::prometheus::Labels());
|
auto& testCounter = PrometheusService::counterInt("test_counter", util::prometheus::Labels());
|
||||||
++testCounter;
|
++testCounter;
|
||||||
auto e = std::make_shared<EchoExecutor>();
|
auto e = std::make_shared<EchoExecutor>();
|
||||||
Config const serverConfig{boost::json::parse(JSONServerConfigWithAdminPassword)};
|
Config const serverConfig{parse(JSONServerConfigWithAdminPassword(webServerPort))};
|
||||||
auto server = makeServerSync(serverConfig, ctx, std::nullopt, dosGuard, e);
|
auto server = makeServerSync(serverConfig, ctx, std::nullopt, dosGuard, e);
|
||||||
auto const res = HttpSyncClient::syncGet(
|
auto const res = HttpSyncClient::syncGet(
|
||||||
"localhost",
|
"localhost",
|
||||||
"8888",
|
std::to_string(webServerPort),
|
||||||
"",
|
"",
|
||||||
"/metrics",
|
"/metrics",
|
||||||
{WebHeader(
|
{WebHeader(
|
||||||
|
|||||||
Reference in New Issue
Block a user